diff --git a/includes/ajaxHandler/filter.class.php b/includes/ajaxHandler/filter.class.php new file mode 100644 index 00000000..0b790eff --- /dev/null +++ b/includes/ajaxHandler/filter.class.php @@ -0,0 +1,96 @@ +page = $p[0]; + + if (isset($p[1])) + $this->cat[] = $p[1]; + + if (count($params) > 1) + for ($i = 1; $i < count($params); $i++) + $this->cat[] = $params[$i]; + + $opts = ['parentCats' => $this->cat]; + + switch ($p[0]) + { + case 'achievements': + $this->filter = (new AchievementListFilter(true, $opts)); + break; + case 'enchantments': + $this->filter = (new EnchantmentListFilter(true, $opts)); + break; + case 'icons': + $this->filter = (new IconListFilter(true, $opts)); + break; + case 'items': + $this->filter = (new ItemListFilter(true, $opts)); + break; + case 'itemsets': + $this->filter = (new ItemsetListFilter(true, $opts)); + break; + case 'npcs': + $this->filter = (new CreatureListFilter(true, $opts)); + break; + case 'objects': + $this->filter = (new GameObjectListFilter(true, $opts)); + break; + case 'quests': + $this->filter = (new QuestListFilter(true, $opts)); + break; + case 'sounds': + $this->filter = (new SoundListFilter(true, $opts)); + break; + case 'spells': + $this->filter = (new SpellListFilter(true, $opts)); + break; + default: + return; + } + + parent::__construct($params); + + // always this one + $this->handler = 'handleFilter'; + } + + protected function handleFilter() + { + $url = '?'.$this->page; + + if ($this->cat) + $url .= '='.implode('.', $this->cat); + + $fi = []; + if ($x = $this->filter->getFilterString()) + $url .= '&filter='.$x; + + if ($this->filter->error) + $_SESSION['fiError'] = get_class($this->filter); + + if ($fi) + $url .= '&filter='.implode(';', $fi); + + // do get request + return $url; + } + +} \ No newline at end of file diff --git a/includes/basetype.class.php b/includes/basetype.class.php index 1218e27a..c01dfb22 100644 --- a/includes/basetype.class.php +++ b/includes/basetype.class.php @@ -754,16 +754,16 @@ trait spawnHelper abstract class Filter { - private static $pattern = "/[\p{C}]/ui"; // delete unprintable characters - private static $wCards = ['*' => '%', '?' => '_']; - private static $criteria = ['cr', 'crs', 'crv']; // [cr]iterium, [cr].[s]ign, [cr].[v]alue + private static $wCards = ['*' => '%', '?' => '_']; - public $error = false; // erronous search fields + public $error = false; // erronous search fields - private $cndSet = []; + private $cndSet = []; - protected $fiData = ['c' => [], 'v' =>[]]; - protected $formData = array( // data to fill form fields + protected $parentCats = []; // used to validate ty-filter + protected $inputFields = []; // list of input fields defined per page + protected $fiData = ['c' => [], 'v' =>[]]; + protected $formData = array( // data to fill form fields 'form' => [], // base form - unsanitized 'setCriteria' => [], // dynamic criteria list - index checked 'setWeights' => [], // dynamic weights list - index checked @@ -771,130 +771,24 @@ abstract class Filter 'reputationCols' => [] // simlar and exclusive to extraCols - added as required ); - // parse the provided request into a usable format; recall self with GET-params if nessecary - public function __construct() + // parse the provided request into a usable format + public function __construct($fromPOST = false, $opts = []) { - // prefer POST over GET, translate to url - if (!empty($_POST)) + if (!empty($opts['parentCats'])) + $this->parentCats = $opts['parentCats']; + + if ($fromPOST) + $this->evaluatePOST(); + else { - foreach ($_POST as $k => $v) + // an error occured, while processing POST + if (isset($_SESSION['fiError'])) { - if (is_array($v)) // array -> max depths:1 - { - if (in_array($k, ['cr', 'wt']) && empty($v[0])) - continue; - - $sub = []; - foreach ($v as $sk => $sv) - $sub[$sk] = Util::checkNumeric($sv) ? $sv : urlencode($sv); - - if (!empty($sub) && in_array($k, self::$criteria)) - $this->fiData['c'][$k] = $sub; - else if (!empty($sub)) - $this->fiData['v'][$k] = $sub; - } - else // stings and integer - { - if (in_array($k, self::$criteria)) - $this->fiData['c'][$k] = Util::checkNumeric($v) ? $v : urlencode($v); - else - $this->fiData['v'][$k] = Util::checkNumeric($v) ? $v : urlencode($v); - } + $this->error = $_SESSION['fiError'] == get_class($this); + unset($_SESSION['fiError']); } - // do get request - header('Location: '.HOST_URL.'?'.$_SERVER['QUERY_STRING'].'='.$this->urlize(), true, 302); - } - // sanitize input and build sql - else if (!empty($_GET['filter'])) - { - $tmp = explode(';', $_GET['filter']); - $cr = $crs = $crv = []; - - foreach (self::$criteria as $c) - { - foreach ($tmp as $i => $term) - { - if (strpos($term, $c.'=') === 0) - { - $$c = explode(':', explode('=', $term)[1]); - unset($tmp[$i]); - } - } - } - - // handle erronous input - if (count($cr) != count($crv) || count($cr) != count($crs)) - { - // use min provided criterion as basis - $min = min(count($cr), count($crv), count($crs)); - if (count($cr) > $min) - array_splice($cr, $min); - - if (count($crv) > $min) - array_splice($crv, $min); - - if (count($crs) > $min) - array_splice($crs, $min); - - $this->error = true; - } - - foreach (self::$criteria as $c) - $this->formData['setCriteria'][$c] = $$c; - - for ($i = 0; $i < count($cr); $i++) - { - if (!isset($cr[$i]) || !isset($crs[$i]) || !isset($crv[$i]) || - !intVal($cr[$i]) || $crs[$i] === '' || $crv[$i] === '') - { - $this->error = true; - continue; - } - - $this->sanitize($crv[$i]); - - if ($crv[$i] !== '') - { - $this->fiData['c']['cr'][] = intVal($cr[$i]); - $this->fiData['c']['crs'][] = intVal($crs[$i]); - $this->fiData['c']['crv'][] = $crv[$i]; - } - else - $this->error = true; - - } - - foreach ($tmp as $v) - { - if (!strstr($v, '=')) - continue; - - $w = explode('=', $v); - - if (strstr($w[1], ':')) - { - $tmp2 = explode(':', $w[1]); - - $this->formData['form'][$w[0]] = $tmp2; - - array_walk($tmp2, function(&$v) { - $v = intVal($v); - }); - $this->fiData['v'][$w[0]] = $tmp2; - } - else - { - $this->formData['form'][$w[0]] = $w[1]; - - $this->sanitize($w[1]); - - if ($w[1] !== '') - $this->fiData['v'][$w[0]] = $w[1]; - else - $this->error = true; - } - } + $this->evaluateGET(); } } @@ -904,7 +798,25 @@ abstract class Filter return ['formData']; } - public function urlize(array $override = [], array $addCr = []) + private function &criteriaIterator() + { + if (!$this->fiData['c']) + return; + + for ($i = 0; $i < count($this->fiData['c']['cr']); $i++) + { + // throws a notice if yielded directly "Only variable references should be yielded by reference" + $v = [&$this->fiData['c']['cr'][$i], &$this->fiData['c']['crs'][$i], &$this->fiData['c']['crv'][$i]]; + yield $i => $v; + } + } + + + /***********************/ + /* get prepared values */ + /***********************/ + + public function getFilterString(array $override = [], array $addCr = []) { $_ = []; foreach (array_merge($this->fiData['c'], $this->fiData['v'], $override) as $k => $v) @@ -932,79 +844,315 @@ abstract class Filter return implode(';', $_); } - // todo: kill data, that is unexpected or points to wrong indizes - private function evaluateFilter() + public function getExtraCols() { - // values - $this->cndSet = $this->createSQLForValues(); - - // criteria - foreach ($this->criteriaIterator() as &$_cr) - $this->cndSet[] = $this->createSQLForCriterium($_cr); - - if ($this->cndSet) - array_unshift($this->cndSet, empty($this->fiData['v']['ma']) ? 'AND' : 'OR'); + return $this->formData['extraCols']; } - public function getForm($key = null, $raw = false) + public function getSetCriteria() { - $form = []; + return $this->formData['setCriteria']; + } - if (!$this->formData) - return $form; + public function getSetWeights() + { + return $this->formData['setWeights']; + } - foreach ($this->formData as $name => $data) - { - if (!$data || ($key && $name != $key)) - continue; + public function getReputationCols() + { + return $this->formData['reputationCols']; + } - switch ($name) - { - case 'setCriteria': - if ($data || $raw) - $form[$name] = $raw ? $data : 'fi_setCriteria('.Util::toJSON($data['cr']).', '.Util::toJSON($data['crs']).', '.Util::toJSON($data['crv']).');'; - else - $form[$name] = 'fi_setCriteria([], [], []);'; - break; - case 'extraCols': - $form[$name] = $raw ? $data : 'fi_extraCols = '.Util::toJSON(array_unique($data)).';'; - break; - case 'setWeights': - $form[$name] = $raw ? $data : 'fi_setWeights('.Util::toJSON($data).', 0, 1, 1);'; - break; - case 'form': - case 'reputationCols': - if ($key == $name) // only if explicitely specified - $form[$name] = $data; - break; - default: - break; - } - } - - return $key ? (empty($form[$key]) ? [] : $form[$key]) : $form; + public function getForm() + { + return $this->formData['form']; } public function getConditions() { if (!$this->cndSet) - $this->evaluateFilter(); + { + // values + $this->cndSet = $this->createSQLForValues(); + + // criteria + foreach ($this->criteriaIterator() as &$_cr) + $this->cndSet[] = $this->createSQLForCriterium($_cr); + + if ($this->cndSet) + array_unshift($this->cndSet, empty($this->fiData['v']['ma']) ? 'AND' : 'OR'); + } return $this->cndSet; } - // santas little helper.. - private function &criteriaIterator() + + /**********************/ + /* input sanitization */ + /**********************/ + + private function evaluatePOST() { - if (!$this->fiData['c']) + // doesn't need to set formData['form']; this happens in GET-step + + foreach ($this->inputFields as $inp => list($type, $valid, $asArray)) + { + if (!isset($_POST[$inp]) || $_POST[$inp] === '') + continue; + + $val = $_POST[$inp]; + $k = in_array($inp, ['cr', 'crs', 'crv']) ? 'c' : 'v'; + + if ($asArray) + { + $buff = []; + foreach ((array)$val as $v) + if ($v !== '' && $this->checkInput($type, $valid, $v)) + $buff[] = $v; + + if ($buff) + $this->fiData[$k][$inp] = $buff; + } + else if ($this->checkInput($type, $valid, $val)) + $this->fiData[$k][$inp] = $val; + } + + $this->setWeights(); + $this->setCriteria(); + } + + private function evaluateGET() + { + if (empty($_GET['filter'])) return; - for ($i = 0; $i < count($this->fiData['c']['cr']); $i++) + // squash into usable format + $post = []; + foreach (explode(';', $_GET['filter']) as $f) { - // throws a notice if yielded directly "Only variable references should be yielded by reference" - $v = [&$this->fiData['c']['cr'][$i], &$this->fiData['c']['crs'][$i], &$this->fiData['c']['crv'][$i]]; - yield $i => $v; + if (!strstr($f, '=')) + { + $this->error = true; + continue; + } + + $_ = explode('=', $f); + $post[$_[0]] = $_[1]; } + + $cr = $crs = $crv = []; + foreach ($this->inputFields as $inp => list($type, $valid, $asArray)) + { + if (!isset($post[$inp]) || $post[$inp] === '') + continue; + + $val = $post[$inp]; + $k = in_array($inp, ['cr', 'crs', 'crv']) ? 'c' : 'v'; + + if ($asArray) + { + $buff = []; + foreach (explode(':', $val) as $v) + if ($v !== '' && $this->checkInput($type, $valid, $v)) + $buff[] = $v; + + if ($buff) + { + if ($k == 'v') + $this->formData['form'][$inp] = $buff; + + $this->fiData[$k][$inp] = array_map(function ($x) { return strtr($x, Filter::$wCards); }, $buff); + } + } + else if ($this->checkInput($type, $valid, $val)) + { + if ($k == 'v') + $this->formData['form'][$inp] = $val; + + $this->fiData[$k][$inp] = strtr($val, Filter::$wCards); + } + } + + $this->setWeights(); + $this->setCriteria(); + } + + private function setCriteria() // [cr]iterium, [cr].[s]ign, [cr].[v]alue + { + if (empty($this->fiData['c']['cr']) && empty($this->fiData['c']['crs']) && empty($this->fiData['c']['crv'])) + return; + else if (empty($this->fiData['c']['cr']) || empty($this->fiData['c']['crs']) || empty($this->fiData['c']['crv'])) + { + unset($this->fiData['c']['cr']); + unset($this->fiData['c']['crs']); + unset($this->fiData['c']['crv']); + + $this->error = true; + + return; + } + + $_cr = &$this->fiData['c']['cr']; + $_crs = &$this->fiData['c']['crs']; + $_crv = &$this->fiData['c']['crv']; + + if (count($_cr) != count($_crv) || count($_cr) != count($_crs)) + { + // use min provided criterion as basis + $min = min(count($_cr), count($_crv), count($_crs)); + if (count($_cr) > $min) + array_splice($_cr, $min); + + if (count($_crv) > $min) + array_splice($_crv, $min); + + if (count($_crs) > $min) + array_splice($_crs, $min); + + $this->error = true; + } + + for ($i = 0; $i < count($_cr); $i++) + { + // conduct filter specific checks & casts here + $unsetme = false; + if (isset($this->genericFilter[$_cr[$i]])) + { + $gf = $this->genericFilter[$_cr[$i]]; + switch ($gf[0]) + { + case FILTER_CR_NUMERIC: + $_ = $_crs[$i]; + if (!Util::checkNumeric($_crv[$i], $gf[2]) || !$this->int2Op($_)) + $unsetme = true; + break; + case FILTER_CR_BOOLEAN: + case FILTER_CR_FLAG: + case FILTER_CR_STAFFFLAG: + $_ = $_crs[$i]; + if (!$this->int2Bool($_)) + $unsetme = true; + break; + case FILTER_CR_ENUM: + if (!Util::checkNumeric($_crs[$i], NUM_REQ_INT)) + $unsetme = true; + break; + } + } + + if (!$unsetme && intval($_cr[$i]) && $_crs[$i] !== '' && $_crv[$i] !== '') + continue; + + unset($_cr[$i]); + unset($_crs[$i]); + unset($_crv[$i]); + + $this->error = true; + } + + $this->formData['setCriteria'] = array( + 'cr' => $_cr, + 'crs' => $_crs, + 'crv' => $_crv + ); + } + + private function setWeights() + { + if (empty($this->fiData['v']['wt']) && empty($this->fiData['v']['wtv'])) + return; + + $_wt = &$this->fiData['v']['wt']; + $_wtv = &$this->fiData['v']['wtv']; + + if (empty($_wt) && !empty($_wtv)) + { + unset($_wtv); + $this->error = true; + return; + } + + if (empty($_wtv) && !empty($_wt)) + { + unset($_wt); + $this->error = true; + return; + } + + $nwt = count($_wt); + $nwtv = count($_wtv); + + if ($nwt > $nwtv) + { + array_splice($_wt, $nwtv); + $this->error = true; + } + else if ($nwtv > $nwt) + { + array_splice($_wtv, $nwt); + $this->error = true; + } + + $this->formData['setWeights'] = [$_wt, $_wtv]; + } + + protected function checkInput($type, $valid, &$val, $recursive = false) + { + switch ($type) + { + case FILTER_V_EQUAL: + if (gettype($valid) == 'integer') + $val = intval($val); + else if (gettype($valid) == 'double') + $val = floatval($val); + else /* if (gettype($valid) == 'string') */ + $var = strval($val); + + if ($valid == $val) + return true; + + break; + case FILTER_V_LIST: + if (!Util::checkNumeric($val, NUM_CAST_INT)) + return false; + + foreach ($valid as $k => $v) + { + if (gettype($v) != 'array') + continue; + + if ($this->checkInput(FILTER_V_RANGE, $v, $val, true)) + return true; + + unset($valid[$k]); + } + + if (in_array($val, $valid)) + return true; + + break; + case FILTER_V_RANGE: + if (Util::checkNumeric($val, NUM_CAST_INT) && $val >= $valid[0] && $val <= $valid[1]) + return true; + + break; + case FILTER_V_CALLBACK: + if ($this->$valid($val)) + return true; + + break; + case FILTER_V_REGEX: + if (!preg_match($valid, $val)) + return true; + + break; + } + + if (!$recursive) + $this->error = true; + + return false; } protected function modularizeString(array $fields, $string = '', $exact = false) @@ -1072,36 +1220,22 @@ abstract class Filter } } - protected function list2Mask($list, $noOffset = false) + protected function list2Mask(array $list, $noOffset = false) { $mask = 0x0; $o = $noOffset ? 0 : 1; // schoolMask requires this..? - if (!is_array($list)) - $mask = (1 << (intVal($list) - $o)); - else - foreach ($list as $itm) - $mask += (1 << (intVal($itm) - $o)); + foreach ($list as $itm) + $mask += (1 << (intval($itm) - $o)); return $mask; } - protected function isSaneNumeric(&$val, $castInt = true) - { - if ($castInt && is_float($val)) - $val = intVal($val); - if (is_int($val) || (is_float($val) && $val >= 0.0)) - return true; - - return false; - } - - private function sanitize(&$str) - { - $str = preg_replace(Filter::$pattern, '', trim($str)); - $str = Util::checkNumeric($str) ? $str : strtr($str, Filter::$wCards); - } + /**************************/ + /* create conditions from */ + /* generic criteria */ + /**************************/ private function genericBoolean($field, $op, $isString) { @@ -1134,7 +1268,7 @@ abstract class Filter private function genericNumeric($field, &$value, $op, $castInt) { - if (!$this->isSaneNumeric($value, $castInt)) + if (!Util::checkNumeric($value, $castInt)) return null; if ($this->int2Op($op)) @@ -1165,7 +1299,7 @@ abstract class Filter switch ($gen[0]) { case FILTER_CR_NUMERIC: - $result = $this->genericNumeric($gen[1], $cr[2], $cr[1], empty($gen[2])); + $result = $this->genericNumeric($gen[1], $cr[2], $cr[1], $gen[2]); break; case FILTER_CR_FLAG: $result = $this->genericBooleanFlags($gen[1], $gen[2], $cr[1]); @@ -1183,17 +1317,40 @@ abstract class Filter case FILTER_CR_ENUM: if (isset($this->enums[$cr[0]][$cr[1]])) $result = $this->genericEnum($gen[1], $this->enums[$cr[0]][$cr[1]]); - else if (intVal($cr[1]) != 0) - $result = $this->genericEnum($gen[1], intVal($cr[1])); + else if (intval($cr[1]) != 0) + $result = $this->genericEnum($gen[1], intval($cr[1])); break; + case FILTER_CR_CALLBACK: + $result = $this->{$gen[1]}($cr, $gen[2], $gen[3]); + break; + case FILTER_CR_NYI_PH: // do not limit with not implemented filters + if (is_int($gen[2])) + return [$gen[2]]; + + // for nonsensical values; compare against 0 + if ($this->int2Op($cr[1]) && Util::checkNumeric($cr[2])) + { + if ($cr[1] == '=') + $cr[1] = '=='; + + return eval('return ('.$cr[2].' '.$cr[1].' 0);') ? [1] : [0]; + } + else + return [0]; } - if ($result && !empty($gen[3])) + if ($result && $gen[0] == FILTER_CR_NUMERIC && !empty($gen[3])) $this->formData['extraCols'][] = $cr[0]; return $result; } + + /***********************************/ + /* create conditions from */ + /* non-generic values and criteria */ + /***********************************/ + abstract protected function createSQLForCriterium(&$cr); abstract protected function createSQLForValues(); } diff --git a/includes/defines.php b/includes/defines.php index 1fb32a75..9ebb95a1 100644 --- a/includes/defines.php +++ b/includes/defines.php @@ -7,6 +7,8 @@ if (!defined('AOWOW_REVISION')) * Page */ +define('E_AOWOW', E_ALL & ~(E_DEPRECATED | E_USER_DEPRECATED | E_STRICT)); + // TypeIds define('TYPE_NPC', 1); define('TYPE_OBJECT', 2); @@ -171,6 +173,13 @@ define('FILTER_CR_NUMERIC', 3); define('FILTER_CR_STRING', 4); define('FILTER_CR_ENUM', 5); define('FILTER_CR_STAFFFLAG', 6); +define('FILTER_CR_CALLBACK', 7); +define('FILTER_CR_NYI_PH', 999); +define('FILTER_V_EQUAL', 8); +define('FILTER_V_RANGE', 9); +define('FILTER_V_LIST', 10); +define('FILTER_V_CALLBACK', 11); +define('FILTER_V_REGEX', 12); define('FILTER_ENUM_ANY', -2323); define('FILTER_ENUM_NONE', -2324); @@ -214,6 +223,12 @@ define('CONTRIBUTE_SS', 0x2); define('CONTRIBUTE_VI', 0x4); define('CONTRIBUTE_ANY', CONTRIBUTE_CO | CONTRIBUTE_SS | CONTRIBUTE_VI); +define('NUM_ANY', 0); +define('NUM_CAST_INT', 1); +define('NUM_CAST_FLOAT', 2); +define('NUM_REQ_INT', 3); +define('NUM_REQ_FLOAT', 4); + /* * Game */ diff --git a/includes/kernel.php b/includes/kernel.php index 937895d2..ec2f7131 100644 --- a/includes/kernel.php +++ b/includes/kernel.php @@ -26,7 +26,7 @@ require_once 'pages/genericPage.class.php'; // autoload List-classes, associated filters and pages spl_autoload_register(function ($class) { - $class = strtolower(str_replace('Filter', '', $class)); + $class = strtolower(str_replace('ListFilter', 'List', $class)); if (class_exists($class)) // already registered return; @@ -117,7 +117,7 @@ foreach ($sets as $k => $v) // handle non-fatal errors and notices -error_reporting(!empty($AoWoWconf['aowow']) && CFG_DEBUG ? (E_ALL & ~(E_DEPRECATED | E_USER_DEPRECATED | E_STRICT)) : 0); +error_reporting(!empty($AoWoWconf['aowow']) && CFG_DEBUG ? E_AOWOW : 0); set_error_handler(function($errNo, $errStr, $errFile, $errLine) { $errName = 'unknown error'; // errors not in this list can not be handled by set_error_handler (as per documentation) or are ignored @@ -149,7 +149,7 @@ set_error_handler(function($errNo, $errStr, $errFile, $errLine) ); return true; -}, E_ALL & ~(E_DEPRECATED | E_USER_DEPRECATED | E_STRICT)); +}, E_AOWOW); // handle exceptions set_exception_handler(function ($ex) diff --git a/includes/shared.php b/includes/shared.php index 1ba7825b..d1626f6a 100644 --- a/includes/shared.php +++ b/includes/shared.php @@ -1,6 +1,6 @@ -1, 424 => -1, 301 => -1 ) ); + protected $genericFilter = array( // misc (bool): _NUMERIC => useFloat; _STRING => localized; _FLAG => match Value; _BOOLEAN => stringSet - 2 => [FILTER_CR_BOOLEAN, 'reward_loc0', true ], // givesreward - 3 => [FILTER_CR_STRING, 'reward', true ], // rewardtext - 7 => [FILTER_CR_BOOLEAN, 'chainId', ], // partseries - 9 => [FILTER_CR_NUMERIC, 'id', null, true], // id - 10 => [FILTER_CR_STRING, 'ic.name', ], // icon - 18 => [FILTER_CR_STAFFFLAG, 'flags', ], // flags - 14 => [FILTER_CR_FLAG, 'cuFlags', CUSTOM_HAS_COMMENT ], // hascomments - 15 => [FILTER_CR_FLAG, 'cuFlags', CUSTOM_HAS_SCREENSHOT ], // hasscreenshots - 16 => [FILTER_CR_FLAG, 'cuFlags', CUSTOM_HAS_VIDEO ], // hasvideos + 2 => [FILTER_CR_BOOLEAN, 'reward_loc0', true ], // givesreward + 3 => [FILTER_CR_STRING, 'reward', true ], // rewardtext + 4 => [FILTER_CR_NYI_PH, null, 1, ], // location [enum] + 5 => [FILTER_CR_CALLBACK, 'cbSeries', ACHIEVEMENT_CU_FIRST_SERIES, null], // first in series [yn] + 6 => [FILTER_CR_CALLBACK, 'cbSeries', ACHIEVEMENT_CU_LAST_SERIES, null], // last in series [yn] + 7 => [FILTER_CR_BOOLEAN, 'chainId', ], // partseries + 9 => [FILTER_CR_NUMERIC, 'id', NUM_CAST_INT, true], // id + 10 => [FILTER_CR_STRING, 'ic.name', ], // icon + 11 => [FILTER_CR_CALLBACK, 'cbRelEvent', null, null], // related event [enum] + 14 => [FILTER_CR_FLAG, 'cuFlags', CUSTOM_HAS_COMMENT ], // hascomments + 15 => [FILTER_CR_FLAG, 'cuFlags', CUSTOM_HAS_SCREENSHOT ], // hasscreenshots + 16 => [FILTER_CR_FLAG, 'cuFlags', CUSTOM_HAS_VIDEO ], // hasvideos + 18 => [FILTER_CR_STAFFFLAG, 'flags', ] // flags + ); + + // fieldId => [checkType, checkValue[, fieldIsArray]] + protected $inputFields = array( + 'cr' => [FILTER_V_RANGE, [2, 18], true ], // criteria ids + 'crs' => [FILTER_V_LIST, [FILTER_ENUM_NONE, FILTER_ENUM_ANY, [0, 99999]], true ], // criteria operators + 'crv' => [FILTER_V_REGEX, '/[\p{C};:]/ui', true ], // criteria values - only printable chars, no delimiters + 'na' => [FILTER_V_REGEX, '/[\p{C};]/ui', false], // name / description - only printable chars, no delimiter + 'ex' => [FILTER_V_EQUAL, 'on', false], // extended name search + 'ma' => [FILTER_V_EQUAL, 1, false], // match any / all filter + 'si' => [FILTER_V_LIST, [1, 2, 3, -1, -2], false], // side + 'minpt' => [FILTER_V_RANGE, [1, 99], false], // required level min + 'maxpt' => [FILTER_V_RANGE, [1, 99], false] // required level max ); 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 4: // location [enum] -/* todo */ return [1]; // no plausible locations parsed yet - case 5: // first in series [yn] - if ($this->int2Bool($cr[1])) - return $cr[1] ? ['AND', ['chainId', 0, '!'], ['cuFlags', ACHIEVEMENT_CU_FIRST_SERIES, '&']] : ['AND', ['chainId', 0, '!'], [['cuFlags', ACHIEVEMENT_CU_FIRST_SERIES, '&'], 0]]; - - break; - case 6: // last in series [yn] - if ($this->int2Bool($cr[1])) - return $cr[1] ? ['AND', ['chainId', 0, '!'], ['cuFlags', ACHIEVEMENT_CU_LAST_SERIES, '&']] : ['AND', ['chainId', 0, '!'], [['cuFlags', ACHIEVEMENT_CU_LAST_SERIES, '&'], 0]]; - - break; - case 11: // Related Event [enum] - $_ = isset($this->enums[$cr[0]][$cr[1]]) ? $this->enums[$cr[0]][$cr[1]] : null; - if ($_ !== null) - { - if (is_int($_)) - return ($_ > 0) ? ['category', $_] : ['id', abs($_)]; - else - { - $ids = array_filter($this->enums[$cr[0]], function($x) { - return is_int($x) && $x > 0; - }); - - return ['category', $ids, $_ ? null : '!']; - } - } - break; - } - unset($cr); $this->error = true; return [1]; @@ -362,45 +342,57 @@ class AchievementListFilter extends Filter // points min if (isset($_v['minpt'])) - { - if ($this->isSaneNumeric($_v['minpt'])) - $parts[] = ['points', $_v['minpt'], '>=']; - else - unset($_v['minpt']); - } + $parts[] = ['points', $_v['minpt'], '>=']; // points max if (isset($_v['maxpt'])) - { - if ($this->isSaneNumeric($_v['maxpt'])) - $parts[] = ['points', $_v['maxpt'], '<=']; - else - unset($_v['maxpt']); - } + $parts[] = ['points', $_v['maxpt'], '<=']; // faction (side) if (isset($_v['si'])) { switch ($_v['si']) { - case 3: // both - $parts[] = ['faction', 0]; - break; case -1: // faction, exclusive both case -2: $parts[] = ['faction', -$_v['si']]; break; case 1: // faction, inclusive both case 2: - $parts[] = ['OR', ['faction', 0], ['faction', $_v['si']]]; + case 3: // both + $parts[] = ['faction', $_v['si'], '&']; break; - default: - unset($_v['si']); } } return $parts; } + + protected function cbRelEvent($cr, $value) + { + if (!isset($this->enums[$cr[0]][$cr[1]])) + return false; + + $_ = $this->enums[$cr[0]][$cr[1]]; + if (is_int($_)) + return ($_ > 0) ? ['category', $_] : ['id', abs($_)]; + else + { + $ids = array_filter($this->enums[$cr[0]], function($x) { return is_int($x) && $x > 0; }); + + return ['category', $ids, $_ ? null : '!']; + } + + return false; + } + + protected function cbSeries($cr, $value) + { + if ($this->int2Bool($cr[1])) + return $cr[1] ? ['AND', ['chainId', 0, '!'], ['cuFlags', $value, '&']] : ['AND', ['chainId', 0, '!'], [['cuFlags', $value, '&'], 0]]; + + return false; + } } ?> diff --git a/includes/types/creature.class.php b/includes/types/creature.class.php index 738a0a76..191f14ec 100644 --- a/includes/types/creature.class.php +++ b/includes/types/creature.class.php @@ -169,7 +169,7 @@ class CreatureList extends BaseType $data = []; $rewRep = []; - if ($addInfoMask & NPCINFO_REP) + if ($addInfoMask & NPCINFO_REP && $this->getFoundIDs()) { $rewRep = DB::World()->selectCol(' SELECT creature_id AS ARRAY_KEY, RewOnKillRepFaction1 AS ARRAY_KEY2, RewOnKillRepValue1 FROM creature_onkill_reputation WHERE creature_id IN (?a) AND RewOnKillRepFaction1 > 0 UNION @@ -179,6 +179,7 @@ class CreatureList extends BaseType ); } + foreach ($this->iterate() as $__) { if ($addInfoMask & NPCINFO_MODEL) @@ -221,6 +222,7 @@ class CreatureList extends BaseType 'react' => [$this->curTpl['A'], $this->curTpl['H']], ); + if ($this->getField('startsQuests')) $data[$this->id]['hasQuests'] = 1; @@ -275,6 +277,7 @@ class CreatureList extends BaseType public function addRewardsToJScript(&$refs) { } + } @@ -290,234 +293,66 @@ class CreatureListFilter extends Filter // cr => [type, field, misc, extraCol] protected $genericFilter = array( // misc (bool): _NUMERIC => useFloat; _STRING => localized; _FLAG => match Value; _BOOLEAN => stringSet - 5 => [FILTER_CR_FLAG, 'npcflag', NPC_FLAG_REPAIRER ], // canrepair - 6 => [FILTER_CR_ENUM, 's.areaId', null ], // foundin - 9 => [FILTER_CR_BOOLEAN, 'lootId', ], // lootable - 11 => [FILTER_CR_BOOLEAN, 'pickpocketLootId', ], // pickpocketable - 18 => [FILTER_CR_FLAG, 'npcflag', NPC_FLAG_AUCTIONEER ], // auctioneer - 19 => [FILTER_CR_FLAG, 'npcflag', NPC_FLAG_BANKER ], // banker - 20 => [FILTER_CR_FLAG, 'npcflag', NPC_FLAG_BATTLEMASTER ], // battlemaster - 21 => [FILTER_CR_FLAG, 'npcflag', NPC_FLAG_FLIGHT_MASTER ], // flightmaster - 22 => [FILTER_CR_FLAG, 'npcflag', NPC_FLAG_GUILD_MASTER ], // guildmaster - 23 => [FILTER_CR_FLAG, 'npcflag', NPC_FLAG_INNKEEPER ], // innkeeper - 24 => [FILTER_CR_FLAG, 'npcflag', NPC_FLAG_CLASS_TRAINER ], // talentunlearner - 25 => [FILTER_CR_FLAG, 'npcflag', NPC_FLAG_GUILD_MASTER ], // tabardvendor - 27 => [FILTER_CR_FLAG, 'npcflag', NPC_FLAG_STABLE_MASTER ], // stablemaster - 28 => [FILTER_CR_FLAG, 'npcflag', NPC_FLAG_TRAINER ], // trainer - 29 => [FILTER_CR_FLAG, 'npcflag', NPC_FLAG_VENDOR ], // vendor - 19 => [FILTER_CR_FLAG, 'npcflag', NPC_FLAG_BANKER ], // banker - 37 => [FILTER_CR_NUMERIC, 'id', null, true], // id - 35 => [FILTER_CR_STRING, 'textureString' ], // useskin - 32 => [FILTER_CR_FLAG, 'cuFlags', NPC_CU_INSTANCE_BOSS ], // instanceboss - 33 => [FILTER_CR_FLAG, 'cuFlags', CUSTOM_HAS_COMMENT ], // hascomments - 31 => [FILTER_CR_FLAG, 'cuFlags', CUSTOM_HAS_SCREENSHOT ], // hasscreenshots - 40 => [FILTER_CR_FLAG, 'cuFlags', CUSTOM_HAS_VIDEO ], // hasvideos + 1 => [FILTER_CR_CALLBACK, 'cbHealthMana', 'healthMax', 'healthMin'], // health [num] + 2 => [FILTER_CR_CALLBACK, 'cbHealthMana', 'manaMin', 'manaMax' ], // mana [num] + 3 => [FILTER_CR_CALLBACK, 'cbFaction', null, null ], // faction [enum] + 5 => [FILTER_CR_FLAG, 'npcflag', NPC_FLAG_REPAIRER ], // canrepair + 6 => [FILTER_CR_ENUM, 's.areaId', null ], // foundin + 7 => [FILTER_CR_CALLBACK, 'cbQuestRelation', 'startsQuests', 0x1 ], // startsquest [enum] + 8 => [FILTER_CR_CALLBACK, 'cbQuestRelation', 'endsQuests', 0x2 ], // endsquest [enum] + 9 => [FILTER_CR_BOOLEAN, 'lootId', ], // lootable + 10 => [FILTER_CR_BOOLEAN, 'cbRegularSkinLoot', NPC_TYPEFLAG_SPECIALLOOT ], // skinnable [yn] + 11 => [FILTER_CR_BOOLEAN, 'pickpocketLootId', ], // pickpocketable + 12 => [FILTER_CR_CALLBACK, 'cbMoneyDrop', null, null ], // averagemoneydropped [op] [int] + 15 => [FILTER_CR_CALLBACK, 'cbSpecialSkinLoot', NPC_TYPEFLAG_HERBLOOT, null ], // gatherable [yn] + 16 => [FILTER_CR_CALLBACK, 'cbSpecialSkinLoot', NPC_TYPEFLAG_ENGINEERLOOT, null ], // minable [yn] + 18 => [FILTER_CR_FLAG, 'npcflag', NPC_FLAG_AUCTIONEER ], // auctioneer + 19 => [FILTER_CR_FLAG, 'npcflag', NPC_FLAG_BANKER ], // banker + 20 => [FILTER_CR_FLAG, 'npcflag', NPC_FLAG_BATTLEMASTER ], // battlemaster + 21 => [FILTER_CR_FLAG, 'npcflag', NPC_FLAG_FLIGHT_MASTER ], // flightmaster + 22 => [FILTER_CR_FLAG, 'npcflag', NPC_FLAG_GUILD_MASTER ], // guildmaster + 23 => [FILTER_CR_FLAG, 'npcflag', NPC_FLAG_INNKEEPER ], // innkeeper + 24 => [FILTER_CR_FLAG, 'npcflag', NPC_FLAG_CLASS_TRAINER ], // talentunlearner + 25 => [FILTER_CR_FLAG, 'npcflag', NPC_FLAG_GUILD_MASTER ], // tabardvendor + 27 => [FILTER_CR_FLAG, 'npcflag', NPC_FLAG_STABLE_MASTER ], // stablemaster + 28 => [FILTER_CR_FLAG, 'npcflag', NPC_FLAG_TRAINER ], // trainer + 29 => [FILTER_CR_FLAG, 'npcflag', NPC_FLAG_VENDOR ], // vendor + 31 => [FILTER_CR_FLAG, 'cuFlags', CUSTOM_HAS_SCREENSHOT ], // hasscreenshots + 32 => [FILTER_CR_FLAG, 'cuFlags', NPC_CU_INSTANCE_BOSS ], // instanceboss + 33 => [FILTER_CR_FLAG, 'cuFlags', CUSTOM_HAS_COMMENT ], // hascomments + 34 => [FILTER_CR_NYI_PH, 1, null ], // usemodel [str] - displayId -> id:creatureDisplayInfo.dbc/model -> id:cratureModelData.dbc/modelPath + 35 => [FILTER_CR_STRING, 'textureString' ], // useskin + 37 => [FILTER_CR_NUMERIC, 'id', NUM_CAST_INT, true ], // id + 38 => [FILTER_CR_CALLBACK, 'cbRelEvent', null, null ], // relatedevent [enum] + 40 => [FILTER_CR_FLAG, 'cuFlags', CUSTOM_HAS_VIDEO ], // hasvideos + 41 => [FILTER_CR_NYI_PH, 1, null ], // haslocation [yn] [staff] + 42 => [FILTER_CR_CALLBACK, 'cbReputation', '>', null ], // increasesrepwith [enum] + 43 => [FILTER_CR_CALLBACK, 'cbReputation', '<', null ], // decreasesrepwith [enum] + 44 => [FILTER_CR_CALLBACK, 'cbSpecialSkinLoot', NPC_TYPEFLAG_MININGLOOT, null ] // salvageable [yn] + ); + + // fieldId => [checkType, checkValue[, fieldIsArray]] + protected $inputFields = array( + 'cr' => [FILTER_V_LIST, [[1, 3],[5, 12], 15, 16, [18, 25], [27, 29], [31, 35], 37, 38, [40, 44]], true ], // criteria ids + 'crs' => [FILTER_V_LIST, [FILTER_ENUM_NONE, FILTER_ENUM_ANY, [0, 9999]], true ], // criteria operators + 'crv' => [FILTER_V_REGEX, '/[\p{C}:;]/ui', true ], // criteria values - only printable chars, no delimiter + 'na' => [FILTER_V_REGEX, '/[\p{C};]/ui', false], // name / subname - only printable chars, no delimiter + 'ex' => [FILTER_V_EQUAL, 'on', false], // also match subname + 'ma' => [FILTER_V_EQUAL, 1, false], // match any / all filter + 'fa' => [FILTER_V_CALLBACK, 'cbPetFamily', true ], // pet family [list] - cat[0] == 1 + 'minle' => [FILTER_V_RANGE, [1, 99], false], // min level [int] + 'maxle' => [FILTER_V_RANGE, [1, 99], false], // max level [int] + 'cl' => [FILTER_V_RANGE, [0, 4], true ], // classification [list] + 'ra' => [FILTER_V_LIST, [-1, 0, 1], false], // react alliance [int] + 'rh' => [FILTER_V_LIST, [-1, 0, 1], false] // react horde [int] ); 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 1: // health [num] - if (!$this->isSaneNumeric($cr[2]) || !$this->int2Op($cr[1])) - break; - - // remap OP for this special case - switch ($cr[1]) - { - case '=': // min > max is totally possible - $this->extraOpts['ct']['h'][] = 'healthMin = healthMax AND healthMin = '.$cr[2]; - break; - case '>': - $this->extraOpts['ct']['h'][] = 'IF(healthMin > healthMax, healthMax, healthMin) > '.$cr[2]; - break; - case '>=': - $this->extraOpts['ct']['h'][] = 'IF(healthMin > healthMax, healthMax, healthMin) >= '.$cr[2]; - break; - case '<': - $this->extraOpts['ct']['h'][] = 'IF(healthMin > healthMax, healthMin, healthMax) < '.$cr[2]; - break; - case '<=': - $this->extraOpts['ct']['h'][] = 'IF(healthMin > healthMax, healthMin, healthMax) <= '.$cr[2]; - break; - } - return [1]; // always true, use post-filter - case 2: // mana [num] - if (!$this->isSaneNumeric($cr[2]) || !$this->int2Op($cr[1])) - break; - - // remap OP for this special case - switch ($cr[1]) - { - case '=': - $this->extraOpts['ct']['h'][] = 'manaMin = manaMax AND manaMin = '.$cr[2]; - break; - case '>': - $this->extraOpts['ct']['h'][] = 'IF(manaMin > manaMax, manaMin, manaMax) > '.$cr[2]; - break; - case '>=': - $this->extraOpts['ct']['h'][] = 'IF(manaMin > manaMax, manaMin, manaMax) >= '.$cr[2]; - break; - case '<': - $this->extraOpts['ct']['h'][] = 'IF(manaMin > manaMax, manaMax, manaMin) < '.$cr[2]; - break; - case '<=': - $this->extraOpts['ct']['h'][] = 'IF(manaMin > manaMax, manaMax, manaMin) <= '.$cr[2]; - break; - } - return [1]; // always true, use post-filter - case 7: // startsquest [enum] - switch ($cr[1]) - { - case 1: // any - return ['AND', ['qse.method', 0x1, '&'], ['qse.questId', null, '!']]; - case 2: // alliance - return ['AND', ['qse.method', 0x1, '&'], ['qse.questId', null, '!'], [['qt.reqRaceMask', RACE_MASK_HORDE, '&'], 0], ['qt.reqRaceMask', RACE_MASK_ALLIANCE, '&']]; - case 3: // horde - return ['AND', ['qse.method', 0x1, '&'], ['qse.questId', null, '!'], [['qt.reqRaceMask', RACE_MASK_ALLIANCE, '&'], 0], ['qt.reqRaceMask', RACE_MASK_HORDE, '&']]; - case 4: // both - return ['AND', ['qse.method', 0x1, '&'], ['qse.questId', null, '!'], ['OR', ['AND', ['qt.reqRaceMask', RACE_MASK_ALLIANCE, '&'], ['qt.reqRaceMask', RACE_MASK_HORDE, '&']], ['qt.reqRaceMask', 0]]]; - case 5: // none - $this->extraOpts['ct']['h'][] = 'startsQuests = 0'; - return [1]; - } - break; - case 8: // endsquest [enum] - switch ($cr[1]) - { - case 1: // any - return ['AND', ['qse.method', 0x2, '&'], ['qse.questId', null, '!']]; - case 2: // alliance - return ['AND', ['qse.method', 0x2, '&'], ['qse.questId', null, '!'], [['qt.reqRaceMask', RACE_MASK_HORDE, '&'], 0], ['qt.reqRaceMask', RACE_MASK_ALLIANCE, '&']]; - case 3: // horde - return ['AND', ['qse.method', 0x2, '&'], ['qse.questId', null, '!'], [['qt.reqRaceMask', RACE_MASK_ALLIANCE, '&'], 0], ['qt.reqRaceMask', RACE_MASK_HORDE, '&']]; - case 4: // both - return ['AND', ['qse.method', 0x2, '&'], ['qse.questId', null, '!'], ['OR', ['AND', ['qt.reqRaceMask', RACE_MASK_ALLIANCE, '&'], ['qt.reqRaceMask', RACE_MASK_HORDE, '&']], ['qt.reqRaceMask', 0]]]; - case 5: // none - $this->extraOpts['ct']['h'][] = 'endsQuests = 0'; - return [1]; - } - break; - case 3: // faction [enum] - if (in_array($cr[1], $this->enums[$cr[0]])) - { - $facTpls = []; - $facs = new FactionList(array('OR', ['parentFactionId', $cr[1]], ['id', $cr[1]])); - foreach ($facs->iterate() as $__) - $facTpls = array_merge($facTpls, $facs->getField('templateIds')); - - if (!$facTpls) - return [0]; - - return ['faction', $facTpls]; - } - break; - case 38; // relatedevent - if (!$this->isSaneNumeric($cr[1])) - break; - - if ($cr[1] == FILTER_ENUM_ANY) - { - $eventIds = DB::Aowow()->selectCol('SELECT id FROM ?_events WHERE holidayId <> 0'); - $cGuids = DB::World()->selectCol('SELECT DISTINCT guid FROM game_event_creature WHERE eventEntry IN (?a)', $eventIds); - return ['s.guid', $cGuids]; - } - else if ($cr[1] == FILTER_ENUM_NONE) - { - $eventIds = DB::Aowow()->selectCol('SELECT id FROM ?_events WHERE holidayId <> 0'); - $cGuids = DB::World()->selectCol('SELECT DISTINCT guid FROM game_event_creature WHERE eventEntry IN (?a)', $eventIds); - return ['s.guid', $cGuids, '!']; - } - else if ($cr[1]) - { - $eventIds = DB::Aowow()->selectCol('SELECT id FROM ?_events WHERE holidayId = ?d', $cr[1]); - $cGuids = DB::World()->selectCol('SELECT DISTINCT guid FROM game_event_creature WHERE eventEntry IN (?a)', $eventIds); - return ['s.guid', $cGuids]; - } - - break; - case 42: // increasesrepwith [enum] - if (in_array($cr[1], $this->enums[3])) // reuse - { - if ($_ = DB::Aowow()->selectRow('SELECT * FROM ?_factions WHERE id = ?d', $cr[1])) - $this->formData['reputationCols'][] = [$cr[1], Util::localizedString($_, 'name')]; - - if ($cIds = DB::World()->selectCol('SELECT creature_id FROM creature_onkill_reputation WHERE (RewOnKillRepFaction1 = ?d AND RewOnKillRepValue1 > 0) OR (RewOnKillRepFaction2 = ?d AND RewOnKillRepValue2 > 0)', $cr[1], $cr[1])) - return ['id', $cIds]; - else - return [0]; - } - - break; - case 43: // decreasesrepwith [enum] - if (in_array($cr[1], $this->enums[3])) // reuse - { - if ($_ = DB::Aowow()->selectRow('SELECT * FROM ?_factions WHERE id = ?d', $cr[1])) - $this->formData['reputationCols'][] = [$cr[1], Util::localizedString($_, 'name')]; - - if ($cIds = DB::World()->selectCol('SELECT creature_id FROM creature_onkill_reputation WHERE (RewOnKillRepFaction1 = ?d AND RewOnKillRepValue1 < 0) OR (RewOnKillRepFaction2 = ?d AND RewOnKillRepValue2 < 0)', $cr[1], $cr[1])) - return ['id', $cIds]; - else - return [0]; - } - - break; - case 12: // averagemoneydropped [op] [int] - if (!$this->isSaneNumeric($cr[2]) || !$this->int2Op($cr[1])) - break; - - return ['AND', ['((minGold + maxGold) / 2)', $cr[2], $cr[1]]]; - case 15: // gatherable [yn] - if ($this->int2Bool($cr[1])) - { - if ($cr[1]) - return ['AND', ['skinLootId', 0, '>'], ['typeFlags', NPC_TYPEFLAG_HERBLOOT, '&']]; - else - return ['OR', ['skinLootId', 0], [['typeFlags', NPC_TYPEFLAG_HERBLOOT, '&'], 0]]; - } - break; - case 44: // salvageable [yn] - if ($this->int2Bool($cr[1])) - { - if ($cr[1]) - return ['AND', ['skinLootId', 0, '>'], ['typeFlags', NPC_TYPEFLAG_ENGINEERLOOT, '&']]; - else - return ['OR', ['skinLootId', 0], [['typeFlags', NPC_TYPEFLAG_ENGINEERLOOT, '&'], 0]]; - } - break; - case 16: // minable [yn] - if ($this->int2Bool($cr[1])) - { - if ($cr[1]) - return ['AND', ['skinLootId', 0, '>'], ['typeFlags', NPC_TYPEFLAG_MININGLOOT, '&']]; - else - return ['OR', ['skinLootId', 0], [['typeFlags', NPC_TYPEFLAG_MININGLOOT, '&'], 0]]; - } - break; - case 10: // skinnable [yn] - if ($this->int2Bool($cr[1])) - { - if ($cr[1]) - return ['AND', ['skinLootId', 0, '>'], [['typeFlags', NPC_TYPEFLAG_SPECIALLOOT, '&'], 0]]; - else - return ['OR', ['skinLootId', 0], [['typeFlags', NPC_TYPEFLAG_SPECIALLOOT, '&'], 0, '!']]; - } - break; - case 34: // usemodel [str] // displayId -> id:creatureDisplayInfo.dbc/model -> id:cratureModelData.dbc/modelPath - case 41: // haslocation [yn] [staff] -/* todo */ return [1]; - } - unset($cr); $this->error = true; return [1]; @@ -543,65 +378,176 @@ class CreatureListFilter extends Filter // pet family [list] if (isset($_v['fa'])) - { - $_ = (array)$_v['fa']; - if (!array_diff($_, [1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 12, 20, 21, 24, 25, 26, 27, 30, 31, 32, 33, 34, 35, 37, 38, 39, 41, 42, 43, 44, 45, 46])) - $parts[] = ['family', $_]; - else - unset($_v['cl']); - } + $parts[] = ['family', $_v['fa']]; // creatureLevel min [int] if (isset($_v['minle'])) - { - if (is_int($_v['minle']) && $_v['minle'] > 0) - $parts[] = ['minLevel', $_v['minle'], '>=']; - else - unset($_v['minle']); - } + $parts[] = ['minLevel', $_v['minle'], '>=']; // creatureLevel max [int] if (isset($_v['maxle'])) - { - if (is_int($_v['maxle']) && $_v['maxle'] > 0) - $parts[] = ['maxLevel', $_v['maxle'], '<=']; - else - unset($_v['maxle']); - } + $parts[] = ['maxLevel', $_v['maxle'], '<=']; // classification [list] if (isset($_v['cl'])) - { - $_ = (array)$_v['cl']; - if (!array_diff($_, [0, 1, 2, 3, 4])) - $parts[] = ['rank', $_]; - else - unset($_v['cl']); - } + $parts[] = ['rank', $_v['cl']]; // react Alliance [int] if (isset($_v['ra'])) - { - $_ = (int)$_v['ra']; - if (in_array($_, [-1, 0, 1])) - $parts[] = ['ft.A', $_]; - else - unset($_v['ra']); - } + $parts[] = ['ft.A', $_v['ra']]; // react Horde [int] if (isset($_v['rh'])) - { - $_ = (int)$_v['rh']; - if (in_array($_, [-1, 0, 1])) - $parts[] = ['ft.H', $_]; - else - unset($_v['rh']); - } + $parts[] = ['ft.H', $_v['rh']]; return $parts; } + protected function cbPetFamily(&$val) + { + if (!$this->parentCats || $this->parentCats[0] != 1) + return false; + + if (!Util::checkNumeric($val, NUM_REQ_INT)) + return false; + + $type = FILTER_V_LIST; + $valid = [[1, 9], 11, 12, 20, 21, [24, 27], [30, 35], [37, 39], [41, 46]]; + + return $this->checkInput($type, $valid, $val); + } + + protected function cbRelEvent($cr) + { + if (!Util::checkNumeric($cr[1], NUM_REQ_INT)) + return false; + + if ($cr[1] == FILTER_ENUM_ANY) + { + $eventIds = DB::Aowow()->selectCol('SELECT id FROM ?_events WHERE holidayId <> 0'); + $cGuids = DB::World()->selectCol('SELECT DISTINCT guid FROM game_event_creature WHERE eventEntry IN (?a)', $eventIds); + return ['s.guid', $cGuids]; + } + else if ($cr[1] == FILTER_ENUM_NONE) + { + $eventIds = DB::Aowow()->selectCol('SELECT id FROM ?_events WHERE holidayId <> 0'); + $cGuids = DB::World()->selectCol('SELECT DISTINCT guid FROM game_event_creature WHERE eventEntry IN (?a)', $eventIds); + return ['s.guid', $cGuids, '!']; + } + else if ($cr[1]) + { + $eventIds = DB::Aowow()->selectCol('SELECT id FROM ?_events WHERE holidayId = ?d', $cr[1]); + $cGuids = DB::World()->selectCol('SELECT DISTINCT guid FROM game_event_creature WHERE eventEntry IN (?a)', $eventIds); + return ['s.guid', $cGuids]; + } + + return false; + } + + protected function cbMoneyDrop($cr) + { + if (!Util::checkNumeric($cr[2], NUM_CAST_INT) || !$this->int2Op($cr[1])) + return false; + + return ['AND', ['((minGold + maxGold) / 2)', $cr[2], $cr[1]]]; + } + + protected function cbQuestRelation($cr, $field, $val) + { + switch ($cr[1]) + { + case 1: // any + return ['AND', ['qse.method', $val, '&'], ['qse.questId', null, '!']]; + case 2: // alliance + return ['AND', ['qse.method', $val, '&'], ['qse.questId', null, '!'], [['qt.reqRaceMask', RACE_MASK_HORDE, '&'], 0], ['qt.reqRaceMask', RACE_MASK_ALLIANCE, '&']]; + case 3: // horde + return ['AND', ['qse.method', $val, '&'], ['qse.questId', null, '!'], [['qt.reqRaceMask', RACE_MASK_ALLIANCE, '&'], 0], ['qt.reqRaceMask', RACE_MASK_HORDE, '&']]; + case 4: // both + return ['AND', ['qse.method', $val, '&'], ['qse.questId', null, '!'], ['OR', ['AND', ['qt.reqRaceMask', RACE_MASK_ALLIANCE, '&'], ['qt.reqRaceMask', RACE_MASK_HORDE, '&']], ['qt.reqRaceMask', 0]]]; + case 5: // none + $this->extraOpts['ct']['h'][] = $field.' = 0'; + return [1]; + } + + return false; + } + + protected function cbHealthMana($cr, $minField, $maxField) + { + if (!Util::checkNumeric($cr[2], NUM_CAST_INT) || !$this->int2Op($cr[1])) + return false; + + // remap OP for this special case + switch ($cr[1]) + { + case '=': // min > max is totally possible + $this->extraOpts['ct']['h'][] = $minField.' = '.$maxField.' AND '.$minField.' = '.$cr[2]; + break; + case '>': + case '>=': + case '<': + case '<=': + $this->extraOpts['ct']['h'][] = 'IF('.$minField.' > '.$maxField.', '.$maxField.', '.$minField.') '.$cr[1].' '.$cr[2]; + break; + } + + + return [1]; // always true, use post-filter + } + + protected function cbSpecialSkinLoot($cr, $typeFlag) + { + if (!$this->int2Bool($cr[1])) + return false; + + + if ($cr[1]) + return ['AND', ['skinLootId', 0, '>'], ['typeFlags', $typeFlag, '&']]; + else + return ['OR', ['skinLootId', 0], [['typeFlags', $typeFlag, '&'], 0]]; + } + + protected function cbRegularSkinLoot($cr, $typeFlag) + { + if (!$this->int2Bool($cr[1])) + return false; + + if ($cr[1]) + return ['AND', ['skinLootId', 0, '>'], [['typeFlags', $typeFlag, '&'], 0]]; + else + return ['OR', ['skinLootId', 0], ['typeFlags', $typeFlag, '&']]; + } + + protected function cbReputation($cr, $op) + { + if (!in_array($cr[1], $this->enums[3])) // reuse + return false; + + if ($_ = DB::Aowow()->selectRow('SELECT * FROM ?_factions WHERE id = ?d', $cr[1])) + $this->formData['reputationCols'][] = [$cr[1], Util::localizedString($_, 'name')]; + + if ($cIds = DB::World()->selectCol('SELECT creature_id FROM creature_onkill_reputation WHERE (RewOnKillRepFaction1 = ?d AND RewOnKillRepValue1 '.$op.' 0) OR (RewOnKillRepFaction2 = ?d AND RewOnKillRepValue2 '.$op.' 0)', $cr[1], $cr[1])) + return ['id', $cIds]; + else + return [0]; + } + + protected function cbFaction($cr) + { + if (!Util::checkNumeric($cr[1], NUM_REQ_INT)) + return false; + + if (!in_array($cr[1], $this->enums[$cr[0]])) + return false; + + + $facTpls = []; + $facs = new FactionList(array('OR', ['parentFactionId', $cr[1]], ['id', $cr[1]])); + foreach ($facs->iterate() as $__) + $facTpls = array_merge($facTpls, $facs->getField('templateIds')); + + return $facTpls ? ['faction', $facTpls] : [0]; + } } ?> diff --git a/includes/types/enchantment.class.php b/includes/types/enchantment.class.php index cab4d95f..cb245818 100644 --- a/includes/types/enchantment.class.php +++ b/includes/types/enchantment.class.php @@ -244,78 +244,86 @@ class EnchantmentListFilter extends Filter ); protected $genericFilter = array( // misc (bool): _NUMERIC => useFloat; _STRING => localized; _FLAG => match Value; _BOOLEAN => stringSet - 2 => [FILTER_CR_NUMERIC, 'id', null, true], // id + 2 => [FILTER_CR_NUMERIC, 'id', NUM_CAST_INT, true], // id 3 => [FILTER_CR_ENUM, 'skillLine' ], // requiresprof - 4 => [FILTER_CR_NUMERIC, 'skillLevel', ], // reqskillrank + 4 => [FILTER_CR_NUMERIC, 'skillLevel', NUM_CAST_INT ], // reqskillrank 5 => [FILTER_CR_BOOLEAN, 'conditionId' ], // hascondition 10 => [FILTER_CR_FLAG, 'cuFlags', CUSTOM_HAS_COMMENT ], // hascomments 11 => [FILTER_CR_FLAG, 'cuFlags', CUSTOM_HAS_SCREENSHOT ], // hasscreenshots 12 => [FILTER_CR_FLAG, 'cuFlags', CUSTOM_HAS_VIDEO ], // hasvideos - 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 - 34 => [FILTER_CR_NUMERIC, 'is.dmg', true, true], // dmg - 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 + 20 => [FILTER_CR_NUMERIC, 'is.str', NUM_CAST_INT, true], // str + 21 => [FILTER_CR_NUMERIC, 'is.agi', NUM_CAST_INT, true], // agi + 22 => [FILTER_CR_NUMERIC, 'is.sta', NUM_CAST_INT, true], // sta + 23 => [FILTER_CR_NUMERIC, 'is.int', NUM_CAST_INT, true], // int + 24 => [FILTER_CR_NUMERIC, 'is.spi', NUM_CAST_INT, true], // spi + 25 => [FILTER_CR_NUMERIC, 'is.arcres', NUM_CAST_INT, true], // arcres + 26 => [FILTER_CR_NUMERIC, 'is.firres', NUM_CAST_INT, true], // firres + 27 => [FILTER_CR_NUMERIC, 'is.natres', NUM_CAST_INT, true], // natres + 28 => [FILTER_CR_NUMERIC, 'is.frores', NUM_CAST_INT, true], // frores + 29 => [FILTER_CR_NUMERIC, 'is.shares', NUM_CAST_INT, true], // shares + 30 => [FILTER_CR_NUMERIC, 'is.holres', NUM_CAST_INT, true], // holres + 32 => [FILTER_CR_NUMERIC, 'is.dps', NUM_CAST_FLOAT, true], // dps + 34 => [FILTER_CR_NUMERIC, 'is.dmg', NUM_CAST_FLOAT, true], // dmg + 37 => [FILTER_CR_NUMERIC, 'is.mleatkpwr', NUM_CAST_INT, true], // mleatkpwr + 38 => [FILTER_CR_NUMERIC, 'is.rgdatkpwr', NUM_CAST_INT, true], // rgdatkpwr + 39 => [FILTER_CR_NUMERIC, 'is.rgdhitrtng', NUM_CAST_INT, true], // rgdhitrtng + 40 => [FILTER_CR_NUMERIC, 'is.rgdcritstrkrtng', NUM_CAST_INT, true], // rgdcritstrkrtng + 41 => [FILTER_CR_NUMERIC, 'is.armor' , NUM_CAST_INT, true], // armor + 42 => [FILTER_CR_NUMERIC, 'is.defrtng', NUM_CAST_INT, true], // defrtng + 43 => [FILTER_CR_NUMERIC, 'is.block', NUM_CAST_INT, true], // block + 44 => [FILTER_CR_NUMERIC, 'is.blockrtng', NUM_CAST_INT, true], // blockrtng + 45 => [FILTER_CR_NUMERIC, 'is.dodgertng', NUM_CAST_INT, true], // dodgertng + 46 => [FILTER_CR_NUMERIC, 'is.parryrtng', NUM_CAST_INT, true], // parryrtng + 48 => [FILTER_CR_NUMERIC, 'is.splhitrtng', NUM_CAST_INT, true], // splhitrtng + 49 => [FILTER_CR_NUMERIC, 'is.splcritstrkrtng', NUM_CAST_INT, true], // splcritstrkrtng + 50 => [FILTER_CR_NUMERIC, 'is.splheal', NUM_CAST_INT, true], // splheal + 51 => [FILTER_CR_NUMERIC, 'is.spldmg', NUM_CAST_INT, true], // spldmg + 52 => [FILTER_CR_NUMERIC, 'is.arcsplpwr', NUM_CAST_INT, true], // arcsplpwr + 53 => [FILTER_CR_NUMERIC, 'is.firsplpwr', NUM_CAST_INT, true], // firsplpwr + 54 => [FILTER_CR_NUMERIC, 'is.frosplpwr', NUM_CAST_INT, true], // frosplpwr + 55 => [FILTER_CR_NUMERIC, 'is.holsplpwr', NUM_CAST_INT, true], // holsplpwr + 56 => [FILTER_CR_NUMERIC, 'is.natsplpwr', NUM_CAST_INT, true], // natsplpwr + 57 => [FILTER_CR_NUMERIC, 'is.shasplpwr', NUM_CAST_INT, true], // shasplpwr + 60 => [FILTER_CR_NUMERIC, 'is.healthrgn', NUM_CAST_INT, true], // healthrgn + 61 => [FILTER_CR_NUMERIC, 'is.manargn', NUM_CAST_INT, true], // manargn + 77 => [FILTER_CR_NUMERIC, 'is.atkpwr', NUM_CAST_INT, true], // atkpwr + 78 => [FILTER_CR_NUMERIC, 'is.mlehastertng', NUM_CAST_INT, true], // mlehastertng + 79 => [FILTER_CR_NUMERIC, 'is.resirtng', NUM_CAST_INT, true], // resirtng + 84 => [FILTER_CR_NUMERIC, 'is.mlecritstrkrtng', NUM_CAST_INT, true], // mlecritstrkrtng + 94 => [FILTER_CR_NUMERIC, 'is.splpen', NUM_CAST_INT, true], // splpen + 95 => [FILTER_CR_NUMERIC, 'is.mlehitrtng', NUM_CAST_INT, true], // mlehitrtng + 96 => [FILTER_CR_NUMERIC, 'is.critstrkrtng', NUM_CAST_INT, true], // critstrkrtng + 97 => [FILTER_CR_NUMERIC, 'is.feratkpwr', NUM_CAST_INT, true], // feratkpwr + 101 => [FILTER_CR_NUMERIC, 'is.rgdhastertng', NUM_CAST_INT, true], // rgdhastertng + 102 => [FILTER_CR_NUMERIC, 'is.splhastertng', NUM_CAST_INT, true], // splhastertng + 103 => [FILTER_CR_NUMERIC, 'is.hastertng', NUM_CAST_INT, true], // hastertng + 114 => [FILTER_CR_NUMERIC, 'is.armorpenrtng', NUM_CAST_INT, true], // armorpenrtng + 115 => [FILTER_CR_NUMERIC, 'is.health', NUM_CAST_INT, true], // health + 116 => [FILTER_CR_NUMERIC, 'is.mana', NUM_CAST_INT, true], // mana + 117 => [FILTER_CR_NUMERIC, 'is.exprtng', NUM_CAST_INT, true], // exprtng + 119 => [FILTER_CR_NUMERIC, 'is.hitrtng', NUM_CAST_INT, true], // hitrtng + 123 => [FILTER_CR_NUMERIC, 'is.splpwr', NUM_CAST_INT, true] // splpwr + ); + + // fieldId => [checkType, checkValue[, fieldIsArray]] + protected $inputFields = array( + 'cr' => [FILTER_V_RANGE, [2, 123], true ], // criteria ids + 'crs' => [FILTER_V_RANGE, [1, 15], true ], // criteria operators + 'crv' => [FILTER_V_RANGE, [0, 99999], true ], // criteria values - only numerals + 'na' => [FILTER_V_REGEX, '/[\p{C};]/ui', false], // name - only printable chars, no delimiter + 'ma' => [FILTER_V_EQUAL, 1, false], // match any / all filter + 'ty' => [FILTER_V_RANGE, [1, 8], true ] // types ); 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]; - } + unset($cr); + $this->error = true; + return [1]; } protected function createSQLForValues() diff --git a/includes/types/gameobject.class.php b/includes/types/gameobject.class.php index e6cf6bb2..7838ac21 100644 --- a/includes/types/gameobject.class.php +++ b/includes/types/gameobject.class.php @@ -142,98 +142,36 @@ class GameObjectListFilter extends Filter public $extraOpts = []; protected $genericFilter = array( - 1 => [FILTER_CR_ENUM, 's.areaId', null ], // foundin - 7 => [FILTER_CR_NUMERIC, 'reqSkill', null ], // requiredskilllevel - 11 => [FILTER_CR_FLAG, 'cuFlags', CUSTOM_HAS_SCREENSHOT], // hasscreenshots - 13 => [FILTER_CR_FLAG, 'cuFlags', CUSTOM_HAS_COMMENT ], // hascomments - 15 => [FILTER_CR_NUMERIC, 'id', null ], // id - 18 => [FILTER_CR_FLAG, 'cuFlags', CUSTOM_HAS_VIDEO ], // hasvideos + 1 => [FILTER_CR_ENUM, 's.areaId', null ], // foundin + 2 => [FILTER_CR_CALLBACK, 'cbQuestRelation', 'startsQuests', 0x1 ], // startsquest [side] + 3 => [FILTER_CR_CALLBACK, 'cbQuestRelation', 'endsQuests', 0x2 ], // endsquest [side] + 4 => [FILTER_CR_CALLBACK, 'cbOpenable', null, null], // openable [yn] + 5 => [FILTER_CR_NYI_PH, null, null ], // averagemoneycontained [op] [int] - GOs don't contain money, match against 0 + 7 => [FILTER_CR_NUMERIC, 'reqSkill', NUM_CAST_INT ], // requiredskilllevel + 11 => [FILTER_CR_FLAG, 'cuFlags', CUSTOM_HAS_SCREENSHOT ], // hasscreenshots + 13 => [FILTER_CR_FLAG, 'cuFlags', CUSTOM_HAS_COMMENT ], // hascomments + 15 => [FILTER_CR_NUMERIC, 'id', NUM_CAST_INT ], // id + 16 => [FILTER_CR_CALLBACK, 'cbRelEvent', null, null], // relatedevent (ignore removed by event) + 18 => [FILTER_CR_FLAG, 'cuFlags', CUSTOM_HAS_VIDEO ] // hasvideos + ); + + // fieldId => [checkType, checkValue[, fieldIsArray]] + protected $inputFields = array( + 'cr' => [FILTER_V_LIST, [[1, 5], 7, 11, 13, 15, 16, 18], true ], // criteria ids + 'crs' => [FILTER_V_LIST, [FILTER_ENUM_NONE, FILTER_ENUM_ANY, [0, 5000]], true ], // criteria operators + 'crv' => [FILTER_V_RANGE, [0, 99999], true ], // criteria values - only numeric input values expected + 'na' => [FILTER_V_REGEX, '/[\p{C};]/ui', false], // name - only printable chars, no delimiter + 'ma' => [FILTER_V_EQUAL, 1, false] // match any / all filter ); 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 4: - if (!$this->int2Bool($cr[1])) - break; - - return $cr[1] ? ['OR', ['flags', 0x2, '&'], ['type', 3]] : ['AND', [['flags', 0x2, '&'], 0], ['type', 3, '!']]; - case 5: // averagemoneycontained [op] [int] GOs don't contain money .. eval to 0 == true - if (!$this->isSaneNumeric($cr[2], false) || !$this->int2Op($cr[1])) - break; - - return eval('return ('.$cr[2].' '.$cr[1].' 0)') ? [1] : [0]; - case 2: // startsquest [side] - switch ($cr[1]) - { - case 1: // any - return ['AND', ['qse.method', 0x1, '&'], ['qse.questId', null, '!']]; - case 2: // alliance only - return ['AND', ['qse.method', 0x1, '&'], ['qse.questId', null, '!'], [['qt.reqRaceMask', RACE_MASK_HORDE, '&'], 0], ['qt.reqRaceMask', RACE_MASK_ALLIANCE, '&']]; - case 3: // horde only - return ['AND', ['qse.method', 0x1, '&'], ['qse.questId', null, '!'], [['qt.reqRaceMask', RACE_MASK_ALLIANCE, '&'], 0], ['qt.reqRaceMask', RACE_MASK_HORDE, '&']]; - case 4: // both - return ['AND', ['qse.method', 0x1, '&'], ['qse.questId', null, '!'], ['OR', ['AND', ['qt.reqRaceMask', RACE_MASK_ALLIANCE, '&'], ['qt.reqRaceMask', RACE_MASK_HORDE, '&']], ['qt.reqRaceMask', 0]]]; - case 5: // none - $this->extraOpts['o']['h'][] = 'startsQuests = 0'; - return [1]; - } - break; - case 3: // endsquest [side] - switch ($cr[1]) - { - case 1: // any - return ['AND', ['qse.method', 0x2, '&'], ['qse.questId', null, '!']]; - case 2: // alliance only - return ['AND', ['qse.method', 0x2, '&'], ['qse.questId', null, '!'], [['qt.reqRaceMask', RACE_MASK_HORDE, '&'], 0], ['qt.reqRaceMask', RACE_MASK_ALLIANCE, '&']]; - case 3: // horde only - return ['AND', ['qse.method', 0x2, '&'], ['qse.questId', null, '!'], [['qt.reqRaceMask', RACE_MASK_ALLIANCE, '&'], 0], ['qt.reqRaceMask', RACE_MASK_HORDE, '&']]; - case 4: // both - return ['AND', ['qse.method', 0x2, '&'], ['qse.questId', null, '!'], ['OR', ['AND', ['qt.reqRaceMask', RACE_MASK_ALLIANCE, '&'], ['qt.reqRaceMask', RACE_MASK_HORDE, '&']], ['qt.reqRaceMask', 0]]]; - case 5: // none todo: broken, if entry starts and ends quests... - $this->extraOpts['o']['h'][] = 'endsQuests = 0'; - return [1]; - } - break; - case 16; // relatedevent (ignore removed by event) - if (!$this->isSaneNumeric($cr[1])) - break; - - if ($cr[1] == FILTER_ENUM_ANY) - { - $eventIds = DB::Aowow()->selectCol('SELECT id FROM ?_events WHERE holidayId <> 0'); - $goGuids = DB::World()->selectCol('SELECT DISTINCT guid FROM game_event_gameobject WHERE eventEntry IN (?a)', $eventIds); - return ['s.guid', $goGuids]; - } - else if ($cr[1] == FILTER_ENUM_NONE) - { - $eventIds = DB::Aowow()->selectCol('SELECT id FROM ?_events WHERE holidayId <> 0'); - $goGuids = DB::World()->selectCol('SELECT DISTINCT guid FROM game_event_gameobject WHERE eventEntry IN (?a)', $eventIds); - return ['s.guid', $goGuids, '!']; - } - else if ($cr[1]) - { - $eventIds = DB::Aowow()->selectCol('SELECT id FROM ?_events WHERE holidayId = ?d', $cr[1]); - $goGuids = DB::World()->selectCol('SELECT DISTINCT guid FROM game_event_gameobject WHERE eventEntry IN (?a)', $eventIds); - return ['s.guid', $goGuids]; - } - - break; - } - unset($cr); - $this->error = 1; + $this->error = true; return [1]; } @@ -249,6 +187,61 @@ class GameObjectListFilter extends Filter return $parts; } + + protected function cbOpenable($cr) + { + if ($this->int2Bool($cr[1])) + return $cr[1] ? ['OR', ['flags', 0x2, '&'], ['type', 3]] : ['AND', [['flags', 0x2, '&'], 0], ['type', 3, '!']]; + + return false; + } + + protected function cbQuestRelation($cr, $field, $value) + { + switch ($cr[1]) + { + case 1: // any + return ['AND', ['qse.method', $value, '&'], ['qse.questId', null, '!']]; + case 2: // alliance only + return ['AND', ['qse.method', $value, '&'], ['qse.questId', null, '!'], [['qt.reqRaceMask', RACE_MASK_HORDE, '&'], 0], ['qt.reqRaceMask', RACE_MASK_ALLIANCE, '&']]; + case 3: // horde only + return ['AND', ['qse.method', $value, '&'], ['qse.questId', null, '!'], [['qt.reqRaceMask', RACE_MASK_ALLIANCE, '&'], 0], ['qt.reqRaceMask', RACE_MASK_HORDE, '&']]; + case 4: // both + return ['AND', ['qse.method', $value, '&'], ['qse.questId', null, '!'], ['OR', ['AND', ['qt.reqRaceMask', RACE_MASK_ALLIANCE, '&'], ['qt.reqRaceMask', RACE_MASK_HORDE, '&']], ['qt.reqRaceMask', 0]]]; + case 5: // none todo (low): broken, if entry starts and ends quests... + $this->extraOpts['o']['h'][] = $field.' = 0'; + return [1]; + } + + return false; + } + + protected function cbRelEvent($cr) + { + if (!Util::checkNumeric($cr[1], NUM_REQ_INT)) + return false;; + + if ($cr[1] == FILTER_ENUM_ANY) + { + $eventIds = DB::Aowow()->selectCol('SELECT id FROM ?_events WHERE holidayId <> 0'); + $goGuids = DB::World()->selectCol('SELECT DISTINCT guid FROM game_event_gameobject WHERE eventEntry IN (?a)', $eventIds); + return ['s.guid', $goGuids]; + } + else if ($cr[1] == FILTER_ENUM_NONE) + { + $eventIds = DB::Aowow()->selectCol('SELECT id FROM ?_events WHERE holidayId <> 0'); + $goGuids = DB::World()->selectCol('SELECT DISTINCT guid FROM game_event_gameobject WHERE eventEntry IN (?a)', $eventIds); + return ['s.guid', $goGuids, '!']; + } + else if ($cr[1]) + { + $eventIds = DB::Aowow()->selectCol('SELECT id FROM ?_events WHERE holidayId = ?d', $cr[1]); + $goGuids = DB::World()->selectCol('SELECT DISTINCT guid FROM game_event_gameobject WHERE eventEntry IN (?a)', $eventIds); + return ['s.guid', $goGuids]; + } + + return false; + } } ?> diff --git a/includes/types/icon.class.php b/includes/types/icon.class.php index 7e4909f2..94ce25c0 100644 --- a/includes/types/icon.class.php +++ b/includes/types/icon.class.php @@ -120,6 +120,25 @@ class IconListFilter extends Filter ); private $totalUses = []; + protected $genericFilter = array( + 1 => [FILTER_CR_CALLBACK, 'cbUseAny' ], // items [num] + 2 => [FILTER_CR_CALLBACK, 'cbUseAny' ], // spells [num] + 3 => [FILTER_CR_CALLBACK, 'cbUseAny' ], // achievements [num] + 6 => [FILTER_CR_CALLBACK, 'cbUseAny' ], // currencies [num] + 9 => [FILTER_CR_CALLBACK, 'cbUseAny' ], // hunterpets [num] + 11 => [FILTER_CR_NYI_PH, null, null], // classes [num] + 13 => [FILTER_CR_CALLBACK, 'cbUseAll' ] // used [num] + ); + + // fieldId => [checkType, checkValue[, fieldIsArray]] + protected $inputFields = array( + 'cr' => [FILTER_V_LIST, [1, 2, 3, 6, 9, 11, 13], true ], // criteria ids + 'crs' => [FILTER_V_RANGE, [1, 6], true ], // criteria operators + 'crv' => [FILTER_V_RANGE, [0, 99999], true ], // criteria values - all criteria are numeric here + 'na' => [FILTER_V_REGEX, '/[\p{C};]/ui', false], // name - only printable chars, no delimiter + 'ma' => [FILTER_V_EQUAL, 1, false] // match any / all filter + ); + private function _getCnd($op, $val, $tbl) { switch ($op) @@ -147,47 +166,11 @@ class IconListFilter extends Filter return $ids ? ['id', array_keys($ids), '!'] : [1]; } - protected function createSQLForCriterium(&$cr) { - if (isset($this->criterion2field[$cr[0]])) - { - if ($cr[0] == 13 && $this->isSaneNumeric($cr[2]) && $this->int2Op($cr[1])) - { - if (!$this->totalUses) - { - foreach ($this->criterion2field as $tbl) - { - if (!$tbl) - continue; - - $res = DB::Aowow()->selectCol('SELECT iconId AS ARRAY_KEY, COUNT(*) AS n FROM ?# GROUP BY iconId', $tbl); - Util::arraySumByKey($this->totalUses, $res); - } - } - - if ($cr[1] == '=') - $cr[1] = '=='; - - $op = $cr[1]; - if ($cr[1] == '<=' && $cr[2]) - $op = '>'; - else if ($cr[1] == '<' && $cr[2]) - $op = '>='; - else if ($cr[1] == '!=' && $cr[2]) - $op = '=='; - $ids = array_filter($this->totalUses, function ($x) use ($op, $cr) { return eval('return '.$x.' '.$op.' '.$cr[2].';'); }); - - if ($cr[1] != $op) - return $ids ? ['id', array_keys($ids), '!'] : [1]; - else - return $ids ? ['id', array_keys($ids)] : ['id', array_keys($this->totalUses), '!']; - } - else if ($cr[0] == 11) - return [0]; - else if ($this->isSaneNumeric($cr[2]) && $this->int2Op($cr[1])) - return $this->_getCnd($cr[1], $cr[2], $this->criterion2field[$cr[0]]); - } + if (in_array($cr[0], array_keys($this->genericFilter))) + if ($genCr = $this->genericCriterion($cr)) + return $genCr; unset($cr); $this->error = true; @@ -206,6 +189,49 @@ class IconListFilter extends Filter return $parts; } + + protected function cbUseAny($cr, $value) + { + if (Util::checkNumeric($cr[2], NUM_CAST_INT) && $this->int2Op($cr[1])) + return $this->_getCnd($cr[1], $cr[2], $this->criterion2field[$cr[0]]); + + return false; + } + + protected function cbUseAll($cr) + { + if (!Util::checkNumeric($cr[2], NUM_CAST_INT) || !$this->int2Op($cr[1])) + return false; + + if (!$this->totalUses) + { + foreach ($this->criterion2field as $tbl) + { + if (!$tbl) + continue; + + $res = DB::Aowow()->selectCol('SELECT iconId AS ARRAY_KEY, COUNT(*) AS n FROM ?# GROUP BY iconId', $tbl); + Util::arraySumByKey($this->totalUses, $res); + } + } + + if ($cr[1] == '=') + $cr[1] = '=='; + + $op = $cr[1]; + if ($cr[1] == '<=' && $cr[2]) + $op = '>'; + else if ($cr[1] == '<' && $cr[2]) + $op = '>='; + else if ($cr[1] == '!=' && $cr[2]) + $op = '=='; + $ids = array_filter($this->totalUses, function ($x) use ($op, $cr) { return eval('return '.$x.' '.$op.' '.$cr[2].';'); }); + + if ($cr[1] != $op) + return $ids ? ['id', array_keys($ids), '!'] : [1]; + else + return $ids ? ['id', array_keys($ids)] : ['id', array_keys($this->totalUses), '!']; + } } ?> diff --git a/includes/types/item.class.php b/includes/types/item.class.php index cb579482..41bfde33 100644 --- a/includes/types/item.class.php +++ b/includes/types/item.class.php @@ -54,10 +54,12 @@ class ItemList extends BaseType $_ = &$_curTpl['requiredClass']; if ($_ < 0 || ($_ & CLASS_MASK_ALL) == CLASS_MASK_ALL) $_ = 0; + unset($_); $_ = &$_curTpl['requiredRace']; if ($_ < 0 || ($_ & RACE_MASK_ALL) == RACE_MASK_ALL) $_ = 0; + unset($_); // sources for ($i = 1; $i < 25; $i++) @@ -1705,111 +1707,189 @@ class ItemListFilter extends Filter // cr => [type, field, misc, extraCol] protected $genericFilter = array( // misc (bool): _NUMERIC => useFloat; _STRING => localized; _FLAG => match Value; _BOOLEAN => stringSet - 9 => [FILTER_CR_FLAG, 'flags', ITEM_FLAG_CONJURED ], // conjureditem - 11 => [FILTER_CR_FLAG, 'flags', ITEM_FLAG_OPENABLE ], // openable - 83 => [FILTER_CR_FLAG, 'flags', ITEM_FLAG_UNIQUEEQUIPPED ], // uniqueequipped - 89 => [FILTER_CR_FLAG, 'flags', ITEM_FLAG_PROSPECTABLE ], // prospectable - 98 => [FILTER_CR_FLAG, 'flags', ITEM_FLAG_PARTYLOOT ], // partyloot - 133 => [FILTER_CR_FLAG, 'flags', ITEM_FLAG_ACCOUNTBOUND ], // accountbound - 146 => [FILTER_CR_FLAG, 'flags', ITEM_FLAG_HEROIC ], // heroic - 154 => [FILTER_CR_FLAG, 'flags', ITEM_FLAG_REFUNDABLE ], // refundable - 155 => [FILTER_CR_FLAG, 'flags', ITEM_FLAG_USABLE_ARENA ], // usableinarenas - 156 => [FILTER_CR_FLAG, 'flags', ITEM_FLAG_USABLE_SHAPED ], // usablewhenshapeshifted - 157 => [FILTER_CR_FLAG, 'flags', ITEM_FLAG_SMARTLOOT ], // smartloot - 159 => [FILTER_CR_FLAG, 'flags', ITEM_FLAG_MILLABLE ], // millable - 162 => [FILTER_CR_FLAG, 'flags', ITEM_FLAG_DEPRECATED ], // deprecated - 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, 'e.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 - 10 => [FILTER_CR_BOOLEAN, 'lockId' ], // locked - 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, 'ic.name', ], // icon - 12 => [FILTER_CR_BOOLEAN, 'itemset', ], // partofset - 13 => [FILTER_CR_BOOLEAN, 'randomEnchant', ], // randomlyenchanted - 14 => [FILTER_CR_BOOLEAN, 'pageTextId', ], // readable - 63 => [FILTER_CR_NUMERIC, 'buyPrice', null, true], // buyprice - 64 => [FILTER_CR_NUMERIC, 'sellPrice', null, true], // sellprice - 165 => [FILTER_CR_NUMERIC, 'repairPrice', null, true], // repaircost - 91 => [FILTER_CR_ENUM, 'totemCategory' ], // tool - 176 => [FILTER_CR_STAFFFLAG, 'flags' ], // flags - 177 => [FILTER_CR_STAFFFLAG, 'flagsExtra' ], // flags2 - 71 => [FILTER_CR_FLAG, 'cuFlags', ITEM_CU_OT_ITEMLOOT ], // otitemopening - 74 => [FILTER_CR_FLAG, 'cuFlags', ITEM_CU_OT_OBJECTLOOT ], // otobjectopening - 130 => [FILTER_CR_FLAG, 'cuFlags', CUSTOM_HAS_COMMENT ], // hascomments - 113 => [FILTER_CR_FLAG, 'cuFlags', CUSTOM_HAS_SCREENSHOT ], // hasscreenshots - 167 => [FILTER_CR_FLAG, 'cuFlags', CUSTOM_HAS_VIDEO ], // hasvideos + 2 => [FILTER_CR_CALLBACK, 'cbFieldHasVal', 'bonding', 1 ], // bindonpickup [yn] + 3 => [FILTER_CR_CALLBACK, 'cbFieldHasVal', 'bonding', 2 ], // bindonequip [yn] + 4 => [FILTER_CR_CALLBACK, 'cbFieldHasVal', 'bonding', 3 ], // bindonuse [yn] + 5 => [FILTER_CR_CALLBACK, 'cbFieldHasVal', 'bonding', [4, 5] ], // questitem [yn] + 6 => [FILTER_CR_CALLBACK, 'cbQuestRelation', null, null ], // startsquest [side] + 7 => [FILTER_CR_BOOLEAN, 'description_loc0', true ], // hasflavortext + 8 => [FILTER_CR_BOOLEAN, 'requiredDisenchantSkill' ], // disenchantable + 9 => [FILTER_CR_FLAG, 'flags', ITEM_FLAG_CONJURED ], // conjureditem + 10 => [FILTER_CR_BOOLEAN, 'lockId' ], // locked + 11 => [FILTER_CR_FLAG, 'flags', ITEM_FLAG_OPENABLE ], // openable + 12 => [FILTER_CR_BOOLEAN, 'itemset' ], // partofset + 13 => [FILTER_CR_BOOLEAN, 'randomEnchant' ], // randomlyenchanted + 14 => [FILTER_CR_BOOLEAN, 'pageTextId' ], // readable + 15 => [FILTER_CR_CALLBACK, 'cbFieldHasVal', 'maxCount', 1 ], // unique [yn] + 16 => [FILTER_CR_NYI_PH, null, 1, ], // dropsin [zone] + 17 => [FILTER_CR_ENUM, 'requiredFaction' ], // requiresrepwith + 18 => [FILTER_CR_CALLBACK, 'cbFactionQuestReward', null, null ], // rewardedbyfactionquest [side] + 20 => [FILTER_CR_NUMERIC, 'is.str', NUM_CAST_INT, true ], // str + 21 => [FILTER_CR_NUMERIC, 'is.agi', NUM_CAST_INT, true ], // agi + 22 => [FILTER_CR_NUMERIC, 'is.sta', NUM_CAST_INT, true ], // sta + 23 => [FILTER_CR_NUMERIC, 'is.int', NUM_CAST_INT, true ], // int + 24 => [FILTER_CR_NUMERIC, 'is.spi', NUM_CAST_INT, true ], // spi + 25 => [FILTER_CR_NUMERIC, 'is.arcres', NUM_CAST_INT, true ], // arcres + 26 => [FILTER_CR_NUMERIC, 'is.firres', NUM_CAST_INT, true ], // firres + 27 => [FILTER_CR_NUMERIC, 'is.natres', NUM_CAST_INT, true ], // natres + 28 => [FILTER_CR_NUMERIC, 'is.frores', NUM_CAST_INT, true ], // frores + 29 => [FILTER_CR_NUMERIC, 'is.shares', NUM_CAST_INT, true ], // shares + 30 => [FILTER_CR_NUMERIC, 'is.holres', NUM_CAST_INT, true ], // holres + 32 => [FILTER_CR_NUMERIC, 'is.dps', NUM_CAST_FLOAT, true ], // dps + 33 => [FILTER_CR_NUMERIC, 'is.dmgmin1', NUM_CAST_INT, true ], // dmgmin1 + 34 => [FILTER_CR_NUMERIC, 'is.dmgmax1', NUM_CAST_INT, true ], // dmgmax1 + 35 => [FILTER_CR_CALLBACK, 'cbDamageType', null, null ], // damagetype [enum] + 36 => [FILTER_CR_NUMERIC, 'is.speed', NUM_CAST_FLOAT, true ], // speed + 37 => [FILTER_CR_NUMERIC, 'is.mleatkpwr', NUM_CAST_INT, true ], // mleatkpwr + 38 => [FILTER_CR_NUMERIC, 'is.rgdatkpwr', NUM_CAST_INT, true ], // rgdatkpwr + 39 => [FILTER_CR_NUMERIC, 'is.rgdhitrtng', NUM_CAST_INT, true ], // rgdhitrtng + 40 => [FILTER_CR_NUMERIC, 'is.rgdcritstrkrtng', NUM_CAST_INT, true ], // rgdcritstrkrtng + 41 => [FILTER_CR_NUMERIC, 'is.armor' , NUM_CAST_INT, true ], // armor + 42 => [FILTER_CR_NUMERIC, 'is.defrtng', NUM_CAST_INT, true ], // defrtng + 43 => [FILTER_CR_NUMERIC, 'is.block', NUM_CAST_INT, true ], // block + 44 => [FILTER_CR_NUMERIC, 'is.blockrtng', NUM_CAST_INT, true ], // blockrtng + 45 => [FILTER_CR_NUMERIC, 'is.dodgertng', NUM_CAST_INT, true ], // dodgertng + 46 => [FILTER_CR_NUMERIC, 'is.parryrtng', NUM_CAST_INT, true ], // parryrtng + 48 => [FILTER_CR_NUMERIC, 'is.splhitrtng', NUM_CAST_INT, true ], // splhitrtng + 49 => [FILTER_CR_NUMERIC, 'is.splcritstrkrtng', NUM_CAST_INT, true ], // splcritstrkrtng + 50 => [FILTER_CR_NUMERIC, 'is.splheal', NUM_CAST_INT, true ], // splheal + 51 => [FILTER_CR_NUMERIC, 'is.spldmg', NUM_CAST_INT, true ], // spldmg + 52 => [FILTER_CR_NUMERIC, 'is.arcsplpwr', NUM_CAST_INT, true ], // arcsplpwr + 53 => [FILTER_CR_NUMERIC, 'is.firsplpwr', NUM_CAST_INT, true ], // firsplpwr + 54 => [FILTER_CR_NUMERIC, 'is.frosplpwr', NUM_CAST_INT, true ], // frosplpwr + 55 => [FILTER_CR_NUMERIC, 'is.holsplpwr', NUM_CAST_INT, true ], // holsplpwr + 56 => [FILTER_CR_NUMERIC, 'is.natsplpwr', NUM_CAST_INT, true ], // natsplpwr + 57 => [FILTER_CR_NUMERIC, 'is.shasplpwr', NUM_CAST_INT, true ], // shasplpwr + 59 => [FILTER_CR_NUMERIC, 'durability', NUM_CAST_INT, true ], // dura + 60 => [FILTER_CR_NUMERIC, 'is.healthrgn', NUM_CAST_INT, true ], // healthrgn + 61 => [FILTER_CR_NUMERIC, 'is.manargn', NUM_CAST_INT, true ], // manargn + 62 => [FILTER_CR_CALLBACK, 'cbCooldown', null, null ], // cooldown [op] [int] + 63 => [FILTER_CR_NUMERIC, 'buyPrice', NUM_CAST_INT, true ], // buyprice + 64 => [FILTER_CR_NUMERIC, 'sellPrice', NUM_CAST_INT, true ], // sellprice + 65 => [FILTER_CR_CALLBACK, 'cbAvgMoneyContent', null, null ], // avgmoney [op] [int] + 66 => [FILTER_CR_ENUM, 'requiredSpell' ], // requiresprofspec + 68 => [FILTER_CR_CALLBACK, 'cbObtainedBy', null, null ], // otdisenchanting [yn] + 69 => [FILTER_CR_CALLBACK, 'cbObtainedBy', null, null ], // otfishing [yn] + 70 => [FILTER_CR_CALLBACK, 'cbObtainedBy', null, null ], // otherbgathering [yn] + 71 => [FILTER_CR_FLAG, 'cuFlags', ITEM_CU_OT_ITEMLOOT ], // otitemopening [yn] + 72 => [FILTER_CR_CALLBACK, 'cbObtainedBy', null, null ], // otlooting [yn] + 73 => [FILTER_CR_CALLBACK, 'cbObtainedBy', null, null ], // otmining [yn] + 74 => [FILTER_CR_FLAG, 'cuFlags', ITEM_CU_OT_OBJECTLOOT ], // otobjectopening [yn] + 75 => [FILTER_CR_CALLBACK, 'cbObtainedBy', null, null ], // otpickpocketing [yn] + 76 => [FILTER_CR_CALLBACK, 'cbObtainedBy', null, null ], // otskinning [yn] + 77 => [FILTER_CR_NUMERIC, 'is.atkpwr', NUM_CAST_INT, true ], // atkpwr + 78 => [FILTER_CR_NUMERIC, 'is.mlehastertng', NUM_CAST_INT, true ], // mlehastertng + 79 => [FILTER_CR_NUMERIC, 'is.resirtng', NUM_CAST_INT, true ], // resirtng + 80 => [FILTER_CR_CALLBACK, 'cbHasSockets', null, null ], // has sockets [enum] + 81 => [FILTER_CR_CALLBACK, 'cbFitsGemSlot', null, null ], // fits gem slot [enum] + 83 => [FILTER_CR_FLAG, 'flags', ITEM_FLAG_UNIQUEEQUIPPED ], // uniqueequipped + 84 => [FILTER_CR_NUMERIC, 'is.mlecritstrkrtng', NUM_CAST_INT, true ], // mlecritstrkrtng + 85 => [FILTER_CR_CALLBACK, 'cbObjectiveOfQuest', null, null ], // objectivequest [side] + 86 => [FILTER_CR_CALLBACK, 'cbCraftedByProf', null, null ], // craftedprof [enum] + 87 => [FILTER_CR_CALLBACK, 'cbReagentForAbility', null, null ], // reagentforability [enum] + 88 => [FILTER_CR_CALLBACK, 'cbObtainedBy', null, null ], // otprospecting [yn] + 89 => [FILTER_CR_FLAG, 'flags', ITEM_FLAG_PROSPECTABLE ], // prospectable + 90 => [FILTER_CR_CALLBACK, 'cbAvgBuyout', null, null ], // avgbuyout [op] [int] + 91 => [FILTER_CR_ENUM, 'totemCategory' ], // tool + 92 => [FILTER_CR_CALLBACK, 'cbObtainedBy', null, null ], // soldbyvendor [yn] + 93 => [FILTER_CR_CALLBACK, 'cbObtainedBy', null, null ], // otpvp [pvp] + 94 => [FILTER_CR_NUMERIC, 'is.splpen', NUM_CAST_INT, true ], // splpen + 95 => [FILTER_CR_NUMERIC, 'is.mlehitrtng', NUM_CAST_INT, true ], // mlehitrtng + 96 => [FILTER_CR_NUMERIC, 'is.critstrkrtng', NUM_CAST_INT, true ], // critstrkrtng + 97 => [FILTER_CR_NUMERIC, 'is.feratkpwr', NUM_CAST_INT, true ], // feratkpwr + 98 => [FILTER_CR_FLAG, 'flags', ITEM_FLAG_PARTYLOOT ], // partyloot + 99 => [FILTER_CR_ENUM, 'requiredSkill' ], // requiresprof + 100 => [FILTER_CR_NUMERIC, 'is.nsockets', NUM_CAST_INT ], // nsockets + 101 => [FILTER_CR_NUMERIC, 'is.rgdhastertng', NUM_CAST_INT, true ], // rgdhastertng + 102 => [FILTER_CR_NUMERIC, 'is.splhastertng', NUM_CAST_INT, true ], // splhastertng + 103 => [FILTER_CR_NUMERIC, 'is.hastertng', NUM_CAST_INT, true ], // hastertng + 104 => [FILTER_CR_STRING, 'description', true ], // flavortext + 105 => [FILTER_CR_NYI_PH, null, 1, ], // dropsinnormal [heroicdungeon-any] + 106 => [FILTER_CR_NYI_PH, null, 1, ], // dropsinheroic [heroicdungeon-any] + 107 => [FILTER_CR_NYI_PH, null, 1, ], // effecttext [str] not yet parsed ['effectsParsed_loc'.User::$localeId, $cr[2]] + 109 => [FILTER_CR_CALLBACK, 'cbArmorBonus', null, null ], // armorbonus [op] [int] + 111 => [FILTER_CR_NUMERIC, 'requiredSkillRank', NUM_CAST_INT, true ], // reqskillrank + 113 => [FILTER_CR_FLAG, 'cuFlags', CUSTOM_HAS_SCREENSHOT ], // hasscreenshots + 114 => [FILTER_CR_NUMERIC, 'is.armorpenrtng', NUM_CAST_INT, true ], // armorpenrtng + 115 => [FILTER_CR_NUMERIC, 'is.health', NUM_CAST_INT, true ], // health + 116 => [FILTER_CR_NUMERIC, 'is.mana', NUM_CAST_INT, true ], // mana + 117 => [FILTER_CR_NUMERIC, 'is.exprtng', NUM_CAST_INT, true ], // exprtng + 118 => [FILTER_CR_CALLBACK, 'cbPurchasableWith', null, null ], // purchasablewithitem [enum] + 119 => [FILTER_CR_NUMERIC, 'is.hitrtng', NUM_CAST_INT, true ], // hitrtng + 123 => [FILTER_CR_NUMERIC, 'is.splpwr', NUM_CAST_INT, true ], // splpwr + 124 => [FILTER_CR_CALLBACK, 'cbHasRandEnchant', null, null ], // randomenchants [str] + 125 => [FILTER_CR_CALLBACK, 'cbReqArenaRating', null, null ], // reqarenartng [op] [int] todo (low): 'find out, why "IN (W, X, Y) AND IN (X, Y, Z)" doesn't result in "(X, Y)" + 126 => [FILTER_CR_CALLBACK, 'cbQuestRewardIn', null, null ], // rewardedbyquestin [zone-any] + 128 => [FILTER_CR_CALLBACK, 'cbSource', null, null ], // source [enum] + 129 => [FILTER_CR_CALLBACK, 'cbSoldByNPC', null, null ], // soldbynpc [str-small] + 130 => [FILTER_CR_FLAG, 'cuFlags', CUSTOM_HAS_COMMENT ], // hascomments + 132 => [FILTER_CR_CALLBACK, 'cbGlyphType', null, null ], // glyphtype [enum] + 133 => [FILTER_CR_FLAG, 'flags', ITEM_FLAG_ACCOUNTBOUND ], // accountbound + 134 => [FILTER_CR_NUMERIC, 'is.mledps', NUM_CAST_FLOAT, true ], // mledps + 135 => [FILTER_CR_NUMERIC, 'is.mledmgmin', NUM_CAST_INT, true ], // mledmgmin + 136 => [FILTER_CR_NUMERIC, 'is.mledmgmax', NUM_CAST_INT, true ], // mledmgmax + 137 => [FILTER_CR_NUMERIC, 'is.mlespeed', NUM_CAST_FLOAT, true ], // mlespeed + 138 => [FILTER_CR_NUMERIC, 'is.rgddps', NUM_CAST_FLOAT, true ], // rgddps + 139 => [FILTER_CR_NUMERIC, 'is.rgddmgmin', NUM_CAST_INT, true ], // rgddmgmin + 140 => [FILTER_CR_NUMERIC, 'is.rgddmgmax', NUM_CAST_INT, true ], // rgddmgmax + 141 => [FILTER_CR_NUMERIC, 'is.rgdspeed', NUM_CAST_FLOAT, true ], // rgdspeed + 142 => [FILTER_CR_STRING, 'ic.name' ], // icon + 143 => [FILTER_CR_CALLBACK, 'cbObtainedBy', null, null ], // otmilling [yn] + 144 => [FILTER_CR_CALLBACK, 'cbPvpPurchasable', 'reqHonorPoints', null ], // purchasablewithhonor [yn] + 145 => [FILTER_CR_CALLBACK, 'cbPvpPurchasable', 'reqHonorPoints', null ], // purchasablewitharena [yn] + 146 => [FILTER_CR_FLAG, 'flags', ITEM_FLAG_HEROIC ], // heroic + 147 => [FILTER_CR_NYI_PH, null, 1, ], // dropsinnormal10 [multimoderaid-any] + 148 => [FILTER_CR_NYI_PH, null, 1, ], // dropsinnormal25 [multimoderaid-any] + 149 => [FILTER_CR_NYI_PH, null, 1, ], // dropsinheroic10 [heroicraid-any] + 150 => [FILTER_CR_NYI_PH, null, 1, ], // dropsinheroic25 [heroicraid-any] + 151 => [FILTER_CR_NUMERIC, 'id', NUM_CAST_INT, true ], // id + 152 => [FILTER_CR_CALLBACK, 'cbClassRaceSpec', 'requiredClass', CLASS_MASK_ALL], // classspecific [enum] + 153 => [FILTER_CR_CALLBACK, 'cbClassRaceSpec', 'requiredRace', RACE_MASK_ALL ], // racespecific [enum] + 154 => [FILTER_CR_FLAG, 'flags', ITEM_FLAG_REFUNDABLE ], // refundable + 155 => [FILTER_CR_FLAG, 'flags', ITEM_FLAG_USABLE_ARENA ], // usableinarenas + 156 => [FILTER_CR_FLAG, 'flags', ITEM_FLAG_USABLE_SHAPED ], // usablewhenshapeshifted + 157 => [FILTER_CR_FLAG, 'flags', ITEM_FLAG_SMARTLOOT ], // smartloot + 158 => [FILTER_CR_CALLBACK, 'cbPurchasableWith', null, null ], // purchasablewithcurrency [enum] + 159 => [FILTER_CR_FLAG, 'flags', ITEM_FLAG_MILLABLE ], // millable + 160 => [FILTER_CR_NYI_PH, null, 1, ], // relatedevent [enum] like 169 .. crawl though npc_vendor and loot_templates of event-related spawns + 161 => [FILTER_CR_CALLBACK, 'cbAvailable', null, null ], // availabletoplayers [yn] + 162 => [FILTER_CR_FLAG, 'flags', ITEM_FLAG_DEPRECATED ], // deprecated + 163 => [FILTER_CR_CALLBACK, 'cbDisenchantsInto', null, null ], // disenchantsinto [disenchanting] + 165 => [FILTER_CR_NUMERIC, 'repairPrice', NUM_CAST_INT, true ], // repaircost + 167 => [FILTER_CR_FLAG, 'cuFlags', CUSTOM_HAS_VIDEO ], // hasvideos + 168 => [FILTER_CR_CALLBACK, 'cbFieldHasVal', 'spellId1', [483, 55884] ], // teachesspell [yn] - 483: learn recipe; 55884: learn mount/pet + 169 => [FILTER_CR_ENUM, 'e.holidayId' ], // requiresevent + 171 => [FILTER_CR_CALLBACK, 'cbObtainedBy', null, null ], // otredemption [yn] + 172 => [FILTER_CR_CALLBACK, 'cbObtainedBy', null, null ], // rewardedbyachievement [yn] + 176 => [FILTER_CR_STAFFFLAG, 'flags' ], // flags + 177 => [FILTER_CR_STAFFFLAG, 'flagsExtra' ], // flags2 ); - public function __construct() + // fieldId => [checkType, checkValue[, fieldIsArray]] + protected $inputFields = array( + 'wt' => [FILTER_V_CALLBACK, 'cbWeightKeyCheck', true ], // weight keys + 'wtv' => [FILTER_V_RANGE, [1, 999], true ], // weight values + 'jc' => [FILTER_V_LIST, [1], false], // use jewelcrafter gems for weight calculation + 'gm' => [FILTER_V_LIST, [2, 3, 4], false], // gem rarity for weight calculation + 'cr' => [FILTER_V_RANGE, [1, 177], true ], // criteria ids + 'crs' => [FILTER_V_LIST, [FILTER_ENUM_NONE, FILTER_ENUM_ANY, [0, 99999]], true ], // criteria operators + 'crv' => [FILTER_V_REGEX, '/[\p{C};:]/ui', true ], // criteria values - only printable chars, no delimiters + 'upg' => [FILTER_V_RANGE, [1, 999999], true ], // upgrade item ids + 'gb' => [FILTER_V_LIST, [0, 1, 2, 3], false], // search result grouping + 'na' => [FILTER_V_REGEX, '/[\p{C};]/ui', false], // name - only printable chars, no delimiter + 'ma' => [FILTER_V_EQUAL, 1, false], // match any / all filter + 'ub' => [FILTER_V_LIST, [[1, 9], 11], false], // usable by classId + 'qu' => [FILTER_V_RANGE, [0, 7], true ], // quality ids + 'ty' => [FILTER_V_CALLBACK, 'cbTypeCheck', true ], // item type - dynamic by current group + 'sl' => [FILTER_V_CALLBACK, 'cbSlotCheck', true ], // item slot - dynamic by current group + 'si' => [FILTER_V_LIST, [1, 2, 3, -1, -2], false], // side + 'minle' => [FILTER_V_RANGE, [1, 999], false], // item level min + 'maxle' => [FILTER_V_RANGE, [1, 999], false], // item level max + 'minrl' => [FILTER_V_RANGE, [1, MAX_LEVEL], false], // required level min + 'maxrl' => [FILTER_V_RANGE, [1, MAX_LEVEL], false] // required level max + ); + + public function __construct($fromPOST = false, $opts = []) { $classes = new CharClassList(); foreach ($classes->iterate() as $cId => $_tpl) @@ -1826,40 +1906,32 @@ class ItemListFilter extends Filter $this->ubFilter[$cId][ITEM_CLASS_ARMOR][] = $i; } - parent::__construct(); + parent::__construct($fromPOST, $opts); } - public function createConditionsForWeights(&$data) + public function createConditionsForWeights() { - if (!$data['wt'] || !$data['wtv'] || count($data['wt']) != count($data['wtv'])) + if (!$this->fiData['v']['wt']) return null; $this->wtCnd = []; $select = []; $wtSum = 0; - foreach ($data['wt'] as $k => $v) + foreach ($this->fiData['v']['wt'] as $k => $v) { - $str = isset(Util::$itemFilter[$v]) ? Util::$itemFilter[$v] : null; - $qty = intVal($data['wtv'][$k]); + $str = Util::$itemFilter[$v]; + $qty = intVal($this->fiData['v']['wtv'][$k]); - if ($str && $qty) - { - if ($str == 'rgdspeed') // dont need no duplicate column - $str = 'speed'; + if ($str == 'rgdspeed') // dont need no duplicate column + $str = 'speed'; - if ($str == 'mledps') // todo (med): unify rngdps and mledps to dps - $str = 'dps'; + if ($str == 'mledps') // todo (med): unify rngdps and mledps to dps + $str = 'dps'; - $select[] = '(`is`.`'.$str.'` * '.$qty.')'; - $this->wtCnd[] = ['is.'.$str, 0, '>']; - $wtSum += $qty; - } - else // well look at that.. erronous indizes or zero-weights - { - unset($data['wt'][$k]); - unset($data['wtv'][$k]); - } + $select[] = '(`is`.`'.$str.'` * '.$qty.')'; + $this->wtCnd[] = ['is.'.$str, 0, '>']; + $wtSum += $qty; } if (count($this->wtCnd) > 1) @@ -1882,401 +1954,11 @@ class ItemListFilter extends Filter 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: // bindonuse [yn] - if ($this->int2Bool($cr[1])) - return ['bonding', 3, $cr[1] ? null : '!']; - break; - case 5: // questitem [yn] - if ($this->int2Bool($cr[1])) - return ['bonding', [4, 5], $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 161: // availabletoplayers [yn] - if ($this->int2Bool($cr[1])) - return [['cuFlags', CUSTOM_UNAVAILABLE, '&'], 0, $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 107: // effecttext [str] not yet parsed ['effectsParsed_loc'.User::$localeId, $cr[2]] -/* todo */ return [1]; - case 132: // glyphtype [enum] - switch ($cr[1]) - { - case 1: // Major - case 2: // Minor - return ['AND', ['class', 16], ['subSubClass', $cr[1]]]; - } - break; - case 124: // randomenchants [str] - $randIds = DB::Aowow()->selectCol('SELECT id AS ARRAY_KEY, ABS(id) FROM ?_itemrandomenchant WHERE name_loc?d LIKE ?', User::$localeId, '%'.$cr[2].'%'); - $tplIds = $randIds ? DB::World()->select('SELECT entry, ench FROM item_enchantment_template WHERE ench IN (?a)', $randIds) : []; - foreach ($tplIds as $k => &$set) - if (array_search($set['ench'], $randIds) < 0) - $set['entry'] *= -1; - - if ($tplIds) - return ['randomEnchant', array_column($tplIds, 'entry')]; - else - return [0]; // no results aren't really input errors - case 125: // reqarenartng [op] [int] todo (low): find out, why "IN (W, X, Y) AND IN (X, Y, Z)" doesn't result in "(X, Y)" - if (!$this->isSaneNumeric($cr[2]) || !$this->int2Op($cr[1])) - break; - - $this->formData['extraCols'][] = $cr[0]; - $costs = DB::Aowow()->selectCol('SELECT id FROM ?_itemextendedcost WHERE reqPersonalrating '.$cr[1].' '.$cr[2]); - $items = DB::World()->selectCol($this->extCostQuery, $costs, $costs); - return ['id', $items]; - case 160: // relatedevent [enum] like 169 .. crawl though npc_vendor and loot_templates of event-related spawns -/* todo */ return [1]; - case 152: // classspecific [enum] - $_ = isset($this->enums[$cr[0]][$cr[1]]) ? $this->enums[$cr[0]][$cr[1]] : null; - 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] - $_ = isset($this->enums[$cr[0]][$cr[1]]) ? $this->enums[$cr[0]][$cr[1]] : null; - 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) || !$this->int2Op($cr[1])) - break; - - $this->formData['extraCols'][] = $cr[0]; - return ['AND', ['armordamagemodifier', $cr[2], $cr[1]], ['class', ITEM_CLASS_ARMOR]]; - case 86: // craftedprof [enum] - $_ = isset($this->enums[99][$cr[1]]) ? $this->enums[99][$cr[1]] : null; - if (is_bool($_)) - return ['src.src1', null, $_ ? '!' : null]; - else if (is_int($_)) - return ['s.skillLine1', $_]; - - break; - 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] - case 69: // otfishing [yn] - case 70: // otherbgathering [yn] - // case 71: // otitemopening [yn] [implemented via cuFlags] - case 72: // otlooting [yn] - case 73: // otmining [yn] - // case 74: // otobjectopening [yn] [implemented via cuFlags] - case 75: // otpickpocketing [yn] - case 76: // otskinning [yn] - case 88: // otprospecting [yn] - case 93: // otpvp [pvp] - case 143: // otmilling [yn] - case 171: // otredemption [yn] - case 172: // rewardedbyachievement [yn] - case 92: // soldbyvendor [yn] - if ($this->int2Bool($cr[1])) - return ['src.src'.$this->otFields[$cr[0]], null, $cr[1] ? '!' : null]; - - break; - case 18: // rewardedbyfactionquest [side] - $field = 'src.src'.$this->otFields[$cr[0]]; - switch ($cr[1]) - { - case 1: // Yes - return [$field, null, '!']; - case 2: // Alliance - return [$field, 1]; - case 3: // Horde - return [$field, 2]; - case 4: // Both - return [$field, 3]; - case 5: // No - return [$field, null]; - } - - break; - case 126: // rewardedbyquestin [zone-any] - if (in_array($cr[1], $this->enums[$cr[0]])) - return ['AND', ['src.src4', null, '!'], ['src.moreZoneId', $cr[1]]]; - else if ($cr[1] == FILTER_ENUM_ANY) - return ['src.src4', null, '!']; // well, this seems a bit redundant.. - - break; - case 158: // purchasablewithcurrency [enum] - case 118: // purchasablewithitem [enum] - if (in_array($cr[1], $this->enums[$cr[0]])) - $_ = (array)$cr[1]; - else if ($cr[1] == FILTER_ENUM_ANY) - $_ = $this->enums[$cr[0]]; - else - break; - - $costs = DB::Aowow()->selectCol( - 'SELECT id FROM ?_itemextendedcost WHERE reqItemId1 IN (?a) OR reqItemId2 IN (?a) OR reqItemId3 IN (?a) OR reqItemId4 IN (?a) OR reqItemId5 IN (?a)', - $_, $_, $_, $_, $_ - ); - if ($items = DB::World()->selectCol($this->extCostQuery, $costs, $costs)) - return ['id', $items]; - - break; - case 144: // purchasablewithhonor [yn] - if ($this->int2Bool($cr[1])) - { - $costs = DB::Aowow()->selectCol('SELECT id FROM ?_itemextendedcost WHERE reqHonorPoints > 0'); - if ($items = DB::World()->selectCol($this->extCostQuery, $costs, $costs)) - return ['id', $items, $cr[1] ? null : '!']; - } - break; - case 145: // purchasablewitharena [yn] - if ($this->int2Bool($cr[1])) - { - $costs = DB::Aowow()->selectCol('SELECT id FROM ?_itemextendedcost WHERE reqArenaPoints > 0'); - if ($items = DB::World()->selectCol($this->extCostQuery, $costs, $costs)) - return ['id', $items, $cr[1] ? null : '!']; - } - break; - case 129: // soldbynpc [str-small] - if (!$this->isSaneNumeric($cr[2], true)) - break; - - if ($iIds = DB::World()->selectCol('SELECT item FROM npc_vendor WHERE entry = ?d UNION SELECT item FROM game_event_npc_vendor v JOIN creature c ON c.guid = v.guid WHERE c.id = ?d', $cr[2], $cr[2])) - return ['i.id', $iIds]; - else - return [0]; - case 90: // avgbuyout [op] [int] - if (!$this->isSaneNumeric($cr[2]) || !$this->int2Op($cr[1])) - break; - - foreach (Util::getRealms() as $rId => $__) - { - // todo: do something sensible.. - // // todo (med): get the avgbuyout into the listview - // if ($_ = DB::Characters()->select('SELECT ii.itemEntry AS ARRAY_KEY, AVG(ah.buyoutprice / ii.count) AS buyout FROM auctionhouse ah JOIN item_instance ii ON ah.itemguid = ii.guid GROUP BY ii.itemEntry HAVING avgbuyout '.$cr[1].' ?f', $c[1])) - // return ['i.id', array_keys($_)]; - // else - // return [0]; - return [1]; - } - - return [0]; - case 65: // avgmoney [op] [int] - if (!$this->isSaneNumeric($cr[2]) || !$this->int2Op($cr[1])) - break; - - $this->formData['extraCols'][] = $cr[0]; - return ['AND', ['flags', ITEM_FLAG_OPENABLE, '&'], ['((minMoneyLoot + maxMoneyLoot) / 2)', $cr[2], $cr[1]]]; - case 62: // cooldown [op] [int] fuck it .. too complex atm - if (!$this->isSaneNumeric($cr[2]) || !$this->int2Op($cr[1])) - break; - - $cr[2] *= 1000; // field supplied in milliseconds - - $this->formData['extraCols'][] = $cr[0]; - $this->extraOpts['is']['s'][] = ', IF(spellCooldown1 > 1, spellCooldown1, IF(spellCooldown2 > 1, spellCooldown2, IF(spellCooldown3 > 1, spellCooldown3, IF(spellCooldown4 > 1, spellCooldown4, IF(spellCooldown5 > 1, spellCooldown5,))))) AS cooldown'; - - return [ - 'OR', - ['AND', ['spellTrigger1', 0], ['spellId1', 0, '!'], ['spellCooldown1', 0, '>'], ['spellCooldown1', $cr[2], $cr[1]]], - ['AND', ['spellTrigger2', 0], ['spellId2', 0, '!'], ['spellCooldown2', 0, '>'], ['spellCooldown2', $cr[2], $cr[1]]], - ['AND', ['spellTrigger3', 0], ['spellId3', 0, '!'], ['spellCooldown3', 0, '>'], ['spellCooldown3', $cr[2], $cr[1]]], - ['AND', ['spellTrigger4', 0], ['spellId4', 0, '!'], ['spellCooldown4', 0, '>'], ['spellCooldown4', $cr[2], $cr[1]]], - ['AND', ['spellTrigger5', 0], ['spellId5', 0, '!'], ['spellCooldown5', 0, '>'], ['spellCooldown5', $cr[2], $cr[1]]], - ]; - case 163: // disenchantsinto [disenchanting] - if (!$this->isSaneNumeric($cr[1])) - break; - - if (!in_array($cr[1], $this->enums[$cr[0]])) - break; - - $refResults = []; - $newRefs = DB::World()->selectCol('SELECT entry FROM ?# WHERE item = ?d AND reference = 0', LOOT_REFERENCE, $cr[1]); - while ($newRefs) - { - $refResults += $newRefs; - $newRefs = DB::World()->selectCol('SELECT entry FROM ?# WHERE reference IN (?a)', LOOT_REFERENCE, $newRefs); - } - - $lootIds = DB::World()->selectCol('SELECT entry FROM ?# WHERE {reference IN (?a) OR }(reference = 0 AND item = ?d)', LOOT_DISENCHANT, $refResults ?: DBSIMPLE_SKIP, $cr[1]); - - return $lootIds ? ['disenchantId', $lootIds] : [0]; - case 85: // objectivequest [side] - $w = ''; - switch ($cr[1]) - { - case 1: // Yes - case 5: // No - $w = 1; - break; - case 2: // Alliance - $w = 'reqRaceMask & '.RACE_MASK_ALLIANCE.' AND (reqRaceMask & '.RACE_MASK_HORDE.') = 0'; - break; - case 3: // Horde - $w = 'reqRaceMask & '.RACE_MASK_HORDE.' AND (reqRaceMask & '.RACE_MASK_ALLIANCE.') = 0'; - break; - case 4: // Both - $w = '(reqRaceMask & '.RACE_MASK_ALLIANCE.' AND reqRaceMask & '.RACE_MASK_HORDE.') OR reqRaceMask = 0'; - break; - default: - break 2; - } - - $itemIds = DB::Aowow()->selectCol(sprintf(' - SELECT reqItemId1 FROM ?_quests WHERE %1$s UNION SELECT reqItemId2 FROM ?_quests WHERE %1$s UNION - SELECT reqItemId3 FROM ?_quests WHERE %1$s UNION SELECT reqItemId4 FROM ?_quests WHERE %1$s UNION - SELECT reqItemId5 FROM ?_quests WHERE %1$s UNION SELECT reqItemId6 FROM ?_quests WHERE %1$s', $w) - ); - - if ($itemIds) - return ['id', $itemIds, $cr[1] == 5 ? '!' : null]; - - return [0]; - case 87: // reagentforability [enum] - $_ = isset($this->enums[99][$cr[1]]) ? $this->enums[99][$cr[1]] : null; - if ($_ !== null) - { - $ids = []; - $spells = DB::Aowow()->select( // todo (med): hmm, selecting all using SpellList would exhaust 128MB of memory :x .. see, that we only select the fields that are really needed - 'SELECT reagent1, reagent2, reagent3, reagent4, reagent5, reagent6, reagent7, reagent8, - reagentCount1, reagentCount2, reagentCount3, reagentCount4, reagentCount5, reagentCount6, reagentCount7, reagentCount8 - FROM ?_spell - WHERE skillLine1 IN (?a)', - is_bool($_) ? array_filter($this->enums[99], "is_numeric") : $_ - ); - foreach ($spells as $spell) - for ($i = 1; $i < 9; $i++) - if ($spell['reagent'.$i] > 0 && $spell['reagentCount'.$i] > 0) - $ids[] = $spell['reagent'.$i]; - - if (empty($ids)) - return [0]; - else if ($_) - return ['id', $ids]; - else - return ['id', $ids, '!']; - } - break; - case 6: // startsquest [side] - switch ($cr[1]) - { - case 1: // any - return ['startQuest', 0, '>']; - case 2: // exclude horde only - return ['AND', ['startQuest', 0, '>'], [['flagsExtra', 0x3, '&'], 2]]; - case 3: // exclude alliance only - return ['AND', ['startQuest', 0, '>'], [['flagsExtra', 0x3, '&'], 1]]; - case 4: // both - return ['AND', ['startQuest', 0, '>'], [['flagsExtra', 0x3, '&'], 0]]; - case 5: // none - return ['startQuest', 0]; - } - break; - case 128: // source [enum] - $_ = isset($this->enums[$cr[0]][$cr[1]]) ? $this->enums[$cr[0]][$cr[1]] : null; - if ($_ !== null) - { - if (is_int($_)) // specific - return ['src.src'.$_, null, '!']; - else if ($_) // any - { - $foo = ['OR']; - foreach ($this->enums[$cr[0]] as $bar) - if (is_int($bar)) - $foo[] = ['src.src'.$bar, null, '!']; - - return $foo; - } - else if (!$_) // none - { - $foo = ['AND']; - foreach ($this->enums[$cr[0]] as $bar) - if (is_int($bar)) - $foo[] = ['src.src'.$bar, null]; - - return $foo; - } - } - break; - } - unset($cr); - $this->error = 1; + $this->error = true; return [1]; } @@ -2291,53 +1973,32 @@ class ItemListFilter extends Filter // 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->createConditionsForWeights($_v); + $parts[] = $this->createConditionsForWeights(); 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']) && !is_array($_v['upg'])) + $_ = DB::Aowow()->selectCol('SELECT id as ARRAY_KEY, slot FROM ?_items WHERE class IN (2, 3, 4) AND id IN (?a)', (array)$_v['upg']); + if ($_ === null) { - unset($this->formData['form']['upg']); unset($_v['upg']); + unset($this->formData['form']['upg']); } else { - $_ = DB::Aowow()->selectCol('SELECT id as ARRAY_KEY, slot FROM ?_items WHERE class IN (2, 3, 4) AND id IN (?a)', (array)$_v['upg']); - if ($_ === null) - { - unset($_v['upg']); - unset($this->formData['form']['upg']); - } - else - { - $this->formData['form']['upg'] = $_; - if ($_) - $parts[] = ['slot', $_]; - } + $this->formData['form']['upg'] = $_; + if ($_) + $parts[] = ['slot', $_]; } } // 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']); - } + $this->formData['form']['gb'] = $_v['gb']; // name if (isset($_v['na'])) @@ -2347,48 +2008,29 @@ class ItemListFilter extends Filter // 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']); + $parts[] = array( + 'AND', + ['OR', ['requiredClass', 0], ['requiredClass', $this->list2Mask((array)$_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]]] + ] + ); } // quality [list] if (isset($_v['qu'])) - { - $_ = (array)$_v['qu']; - if (!array_diff($_, array_keys(Util::$rarityColorStings))) - $parts[] = ['quality', $_]; - else - unset($_v['qu']); - } + $parts[] = ['quality', $_v['qu']]; // type if (isset($_v['ty'])) - { - // should be contextual to 'class' - $_ = (array)$_v['ty']; - $parts[] = ['subclass', $_]; - } + $parts[] = ['subclass', $_v['ty']]; // slot if (isset($_v['sl'])) - { - // should be contextual - $_ = (array)$_v['sl']; - $parts[] = ['slot', $_]; - } + $parts[] = ['slot', $_v['sl']]; // side if (isset($_v['si'])) @@ -2413,49 +2055,493 @@ class ItemListFilter extends Filter case -1: $parts[] = ['OR', [['flagsExtra', 0x3, '&'], 2], ['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']); - } + $parts[] = ['itemLevel', $_v['minle'], '>=']; // itemLevel max if (isset($_v['maxle'])) - { - if (is_int($_v['maxle']) && $_v['maxle'] > 0) - $parts[] = ['itemLevel', $_v['maxle'], '<=']; - else - unset($_v['maxle']); - } + $parts[] = ['itemLevel', $_v['maxle'], '<=']; // reqLevel min if (isset($_v['minrl'])) - { - if (is_int($_v['minrl']) && $_v['minrl'] > 0) - $parts[] = ['requiredLevel', $_v['minrl'], '>=']; - else - unset($_v['minrl']); - } + $parts[] = ['requiredLevel', $_v['minrl'], '>=']; // reqLevel max if (isset($_v['maxrl'])) - { - if (is_int($_v['maxrl']) && $_v['maxrl'] > 0) - $parts[] = ['requiredLevel', $_v['maxrl'], '<=']; - else - unset($_v['maxrl']); - } + $parts[] = ['requiredLevel', $_v['maxrl'], '<=']; return $parts; } + + protected function cbFactionQuestReward($cr) + { + if (!isset($this->otFields[$cr[0]])) + return false; + + $field = 'src.src'.$this->otFields[$cr[0]]; + switch ($cr[1]) + { + case 1: // Yes + return [$field, null, '!']; + case 2: // Alliance + return [$field, 1]; + case 3: // Horde + return [$field, 2]; + case 4: // Both + return [$field, 3]; + case 5: // No + return [$field, null]; + } + + return false; + } + + protected function cbAvailable($cr) + { + if ($this->int2Bool($cr[1])) + return [['cuFlags', CUSTOM_UNAVAILABLE, '&'], 0, $cr[1] ? null : '!']; + + return false; + } + + protected function cbHasSockets($cr) + { + 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]]; + } + + return false; + } + + protected function cbFitsGemSlot($cr) + { + 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, '&']]; + } + } + + protected function cbGlyphType($cr) + { + switch ($cr[1]) + { + case 1: // Major + case 2: // Minor + return ['AND', ['class', 16], ['subSubClass', $cr[1]]]; + } + + return false; + } + + protected function cbHasRandEnchant($cr) + { + $randIds = DB::Aowow()->selectCol('SELECT id AS ARRAY_KEY, ABS(id) FROM ?_itemrandomenchant WHERE name_loc?d LIKE ?', User::$localeId, '%'.$cr[2].'%'); + $tplIds = $randIds ? DB::World()->select('SELECT entry, ench FROM item_enchantment_template WHERE ench IN (?a)', $randIds) : []; + foreach ($tplIds as $k => &$set) + if (array_search($set['ench'], $randIds) < 0) + $set['entry'] *= -1; + + if ($tplIds) + return ['randomEnchant', array_column($tplIds, 'entry')]; + else + return [0]; // no results aren't really input errors + } + + protected function cbReqArenaRating($cr) + { + if (!Util::checkNumeric($cr[2], NUM_CAST_INT) || !$this->int2Op($cr[1])) + return false; + + $this->formData['extraCols'][] = $cr[0]; + + $costs = DB::Aowow()->selectCol('SELECT id FROM ?_itemextendedcost WHERE reqPersonalrating '.$cr[1].' '.$cr[2]); + $items = DB::World()->selectCol($this->extCostQuery, $costs, $costs); + return ['id', $items]; + } + + protected function cbClassRaceSpec($cr, $field, $mask) + { + if (!isset($this->enums[$cr[0]][$cr[1]])) + return false; + + $_ = $this->enums[$cr[0]][$cr[1]]; + if (is_bool($_)) + return $_ ? ['AND', [[$field, $mask, '&'], $mask, '!'], [$field, 0, '>']] : ['OR', [[$field, $mask, '&'], $mask], [$field, 0]]; + else if (is_int($_)) + return ['AND', [[$field, $mask, '&'], $mask, '!'], [$field, 1 << ($_ - 1), '&']]; + + return false; + } + + protected function cbDamageType($cr) + { + if (!$this->checkInput(FILTER_V_RANGE, [0, 6], $cr[1])) + return false; + + return ['OR', ['dmgType1', $cr[1]], ['dmgType2', $cr[1]]]; + } + + protected function cbArmorBonus($cr) + { + if (!Util::checkNumeric($cr[2], NUM_CAST_FLOAT) || !$this->int2Op($cr[1])) + return false; + + $this->formData['extraCols'][] = $cr[0]; + return ['AND', ['armordamagemodifier', $cr[2], $cr[1]], ['class', ITEM_CLASS_ARMOR]]; + } + + protected function cbCraftedByProf($cr) + { + if (!isset($this->enums[99][$cr[1]])) + return false; + + $_ = $this->enums[99][$cr[1]]; + if (is_bool($_)) + return ['src.src1', null, $_ ? '!' : null]; + else if (is_int($_)) + return ['s.skillLine1', $_]; + + return false; + } + + protected function cbQuestRewardIn($cr) + { + if (in_array($cr[1], $this->enums[$cr[0]])) + return ['AND', ['src.src4', null, '!'], ['src.moreZoneId', $cr[1]]]; + else if ($cr[1] == FILTER_ENUM_ANY) + return ['src.src4', null, '!']; // well, this seems a bit redundant.. + + return false; + } + + protected function cbPurchasableWith($cr) + { + if (in_array($cr[1], $this->enums[$cr[0]])) + $_ = (array)$cr[1]; + else if ($cr[1] == FILTER_ENUM_ANY) + $_ = $this->enums[$cr[0]]; + else + return false; + + $costs = DB::Aowow()->selectCol( + 'SELECT id FROM ?_itemextendedcost WHERE reqItemId1 IN (?a) OR reqItemId2 IN (?a) OR reqItemId3 IN (?a) OR reqItemId4 IN (?a) OR reqItemId5 IN (?a)', + $_, $_, $_, $_, $_ + ); + if ($items = DB::World()->selectCol($this->extCostQuery, $costs, $costs)) + return ['id', $items]; + } + + protected function cbSoldByNPC($cr) + { + if (!Util::checkNumeric($cr[2], NUM_CAST_INT)) + return false; + + if ($iIds = DB::World()->selectCol('SELECT item FROM npc_vendor WHERE entry = ?d UNION SELECT item FROM game_event_npc_vendor v JOIN creature c ON c.guid = v.guid WHERE c.id = ?d', $cr[2], $cr[2])) + return ['i.id', $iIds]; + else + return [0]; + } + + protected function cbAvgBuyout($cr) + { + if (!Util::checkNumeric($cr[2], NUM_CAST_INT) || !$this->int2Op($cr[1])) + return false; + + foreach (Util::getRealms() as $rId => $__) + { + // todo: do something sensible.. + // // todo (med): get the avgbuyout into the listview + // if ($_ = DB::Characters()->select('SELECT ii.itemEntry AS ARRAY_KEY, AVG(ah.buyoutprice / ii.count) AS buyout FROM auctionhouse ah JOIN item_instance ii ON ah.itemguid = ii.guid GROUP BY ii.itemEntry HAVING avgbuyout '.$cr[1].' ?f', $c[1])) + // return ['i.id', array_keys($_)]; + // else + // return [0]; + return [1]; + } + + return [0]; + } + + protected function cbAvgMoneyContent($cr) + { + if (!Util::checkNumeric($cr[2], NUM_CAST_INT) || !$this->int2Op($cr[1])) + return false; + + $this->formData['extraCols'][] = $cr[0]; + return ['AND', ['flags', ITEM_FLAG_OPENABLE, '&'], ['((minMoneyLoot + maxMoneyLoot) / 2)', $cr[2], $cr[1]]]; + } + + protected function cbCooldown($cr) + { + if (!Util::checkNumeric($cr[2], NUM_CAST_INT) || !$this->int2Op($cr[1])) + return false; + + $cr[2] *= 1000; // field supplied in milliseconds + + $this->formData['extraCols'][] = $cr[0]; + $this->extraOpts['is']['s'][] = ', IF(spellCooldown1 > 1, spellCooldown1, IF(spellCooldown2 > 1, spellCooldown2, IF(spellCooldown3 > 1, spellCooldown3, IF(spellCooldown4 > 1, spellCooldown4, IF(spellCooldown5 > 1, spellCooldown5,))))) AS cooldown'; + + return [ + 'OR', + ['AND', ['spellTrigger1', 0], ['spellId1', 0, '!'], ['spellCooldown1', 0, '>'], ['spellCooldown1', $cr[2], $cr[1]]], + ['AND', ['spellTrigger2', 0], ['spellId2', 0, '!'], ['spellCooldown2', 0, '>'], ['spellCooldown2', $cr[2], $cr[1]]], + ['AND', ['spellTrigger3', 0], ['spellId3', 0, '!'], ['spellCooldown3', 0, '>'], ['spellCooldown3', $cr[2], $cr[1]]], + ['AND', ['spellTrigger4', 0], ['spellId4', 0, '!'], ['spellCooldown4', 0, '>'], ['spellCooldown4', $cr[2], $cr[1]]], + ['AND', ['spellTrigger5', 0], ['spellId5', 0, '!'], ['spellCooldown5', 0, '>'], ['spellCooldown5', $cr[2], $cr[1]]], + ]; + } + + protected function cbQuestRelation($cr) + { + switch ($cr[1]) + { + case 1: // any + return ['startQuest', 0, '>']; + case 2: // exclude horde only + return ['AND', ['startQuest', 0, '>'], [['flagsExtra', 0x3, '&'], 2]]; + case 3: // exclude alliance only + return ['AND', ['startQuest', 0, '>'], [['flagsExtra', 0x3, '&'], 1]]; + case 4: // both + return ['AND', ['startQuest', 0, '>'], [['flagsExtra', 0x3, '&'], 0]]; + case 5: // none + return ['startQuest', 0]; + } + + return false; + } + + protected function cbFieldHasVal($cr, $field, $val) + { + if ($this->int2Bool($cr[1])) + return [$field, $val, $cr[1] ? null : '!']; + + return false; + } + + protected function cbObtainedBy($cr, $field) + { + if ($this->int2Bool($cr[1])) + return ['src.src'.$this->otFields[$cr[0]], null, $cr[1] ? '!' : null]; + + return false; + } + + protected function cbPvpPurchasable($cr, $field) + { + if (!$this->int2Bool($cr[1])) + return false; + + $costs = DB::Aowow()->selectCol('SELECT id FROM ?_itemextendedcost WHERE ?# > 0', $field); + if ($items = DB::World()->selectCol($this->extCostQuery, $costs, $costs)) + return ['id', $items, $cr[1] ? null : '!']; + + return false; + } + + protected function cbDisenchantsInto($cr) + { + if (!Util::checkNumeric($cr[1], NUM_REQ_INT)) + return false; + + if (!in_array($cr[1], $this->enums[$cr[0]])) + return false; + + $refResults = []; + $newRefs = DB::World()->selectCol('SELECT entry FROM ?# WHERE item = ?d AND reference = 0', LOOT_REFERENCE, $cr[1]); + while ($newRefs) + { + $refResults += $newRefs; + $newRefs = DB::World()->selectCol('SELECT entry FROM ?# WHERE reference IN (?a)', LOOT_REFERENCE, $newRefs); + } + + $lootIds = DB::World()->selectCol('SELECT entry FROM ?# WHERE {reference IN (?a) OR }(reference = 0 AND item = ?d)', LOOT_DISENCHANT, $refResults ?: DBSIMPLE_SKIP, $cr[1]); + + return $lootIds ? ['disenchantId', $lootIds] : [0]; + } + + protected function cbObjectiveOfQuest($cr) + { + $w = ''; + switch ($cr[1]) + { + case 1: // Yes + case 5: // No + $w = 1; + return; + case 2: // Alliance + $w = 'reqRaceMask & '.RACE_MASK_ALLIANCE.' AND (reqRaceMask & '.RACE_MASK_HORDE.') = 0'; + break; + case 3: // Horde + $w = 'reqRaceMask & '.RACE_MASK_HORDE.' AND (reqRaceMask & '.RACE_MASK_ALLIANCE.') = 0'; + break; + case 4: // Both + $w = '(reqRaceMask & '.RACE_MASK_ALLIANCE.' AND reqRaceMask & '.RACE_MASK_HORDE.') OR reqRaceMask = 0'; + break; + default: + return false; + } + + $itemIds = DB::Aowow()->selectCol(sprintf(' + SELECT reqItemId1 FROM ?_quests WHERE %1$s UNION SELECT reqItemId2 FROM ?_quests WHERE %1$s UNION + SELECT reqItemId3 FROM ?_quests WHERE %1$s UNION SELECT reqItemId4 FROM ?_quests WHERE %1$s UNION + SELECT reqItemId5 FROM ?_quests WHERE %1$s UNION SELECT reqItemId6 FROM ?_quests WHERE %1$s', + $w + )); + + if ($itemIds) + return ['id', $itemIds, $cr[1] == 5 ? '!' : null]; + + return [0]; + } + + protected function cbReagentForAbility($cr) + { + if (!isset($this->enums[99][$cr[1]])) + return false; + + $_ = $this->enums[99][$cr[1]]; + if ($_ === null) + return false; + + $ids = []; + $spells = DB::Aowow()->select( // todo (med): hmm, selecting all using SpellList would exhaust 128MB of memory :x .. see, that we only select the fields that are really needed + 'SELECT reagent1, reagent2, reagent3, reagent4, reagent5, reagent6, reagent7, reagent8, + reagentCount1, reagentCount2, reagentCount3, reagentCount4, reagentCount5, reagentCount6, reagentCount7, reagentCount8 + FROM ?_spell + WHERE skillLine1 IN (?a)', + is_bool($_) ? array_filter($this->enums[99], "is_numeric") : $_ + ); + foreach ($spells as $spell) + for ($i = 1; $i < 9; $i++) + if ($spell['reagent'.$i] > 0 && $spell['reagentCount'.$i] > 0) + $ids[] = $spell['reagent'.$i]; + + if (empty($ids)) + return [0]; + else if ($_) + return ['id', $ids]; + else + return ['id', $ids, '!']; + } + + protected function cbSource($cr) + { + if (!isset($this->enums[$cr[0]][$cr[1]])) + return false; + + $_ = $this->enums[$cr[0]][$cr[1]]; + if (is_int($_)) // specific + return ['src.src'.$_, null, '!']; + else if ($_) // any + { + $foo = ['OR']; + foreach ($this->enums[$cr[0]] as $bar) + if (is_int($bar)) + $foo[] = ['src.src'.$bar, null, '!']; + + return $foo; + } + else // none + { + $foo = ['AND']; + foreach ($this->enums[$cr[0]] as $bar) + if (is_int($bar)) + $foo[] = ['src.src'.$bar, null]; + + return $foo; + } + } + + protected function cbTypeCheck(&$v) + { + if (!$this->parentCats) + return false; + + if (!Util::checkNumeric($v, NUM_REQ_INT)) + return false; + + $c = $this->parentCats; + + if (isset($c[2]) && is_array(Lang::item('cat', $c[0], 1, $c[1]))) + $catList = Lang::item('cat', $c[0], 1, $c[1], 1, $c[2]); + else if (isset($c[1]) && is_array(Lang::item('cat', $c[0]))) + $catList = Lang::item('cat', $c[0], 1, $c[1]); + else + $catList = Lang::item('cat', $c[0]); + + // consumables - always + if ($c[0] == 0) + return in_array($v, array_keys(Lang::item('cat', 0, 1))); + // weapons - only if parent + else if ($c[0] == 2 && !isset($c[1])) + return in_array($v, array_keys(Lang::spell('weaponSubClass'))); + // armor - only if parent + else if ($c[0] == 4 && !isset($c[1])) + return in_array($v, array_keys(Lang::item('cat', 4, 1))); + // uh ... other stuff... + else if (in_array($c[0], [1, 3, 7, 9, 15]) && !isset($c[1])) + return in_array($v, array_keys($catList[1])); + + return false; + } + + protected function cbSlotCheck(&$v) + { + if (!Util::checkNumeric($v, NUM_REQ_INT)) + return false; + + // todo (low): limit to concrete slots + $sl = array_keys(Lang::item('inventoryType')); + $c = $this->parentCats; + + // no selection + if (!isset($c[0])) + return in_array($v, $sl); + + // consumables - any; perm / temp item enhancements + else if ($c[0] == 0 && (!isset($c[1]) || in_array($c[1], [3, -6]))) + return in_array($v, $sl); + + // weapons - always + else if ($c[0] == 2) + return in_array($v, $sl); + + // armor - any; any armor + else if ($c[0] == 4 && (!isset($c[1]) || in_array($c[1], [1, 2, 3, 4]))) + return in_array($v, $sl); + + return false; + } + + protected function cbWeightKeyCheck(&$v) + { + if (preg_match('/\W/i', $v)) + return false; + + return isset(Util::$itemFilter[$val]); + } } ?> diff --git a/includes/types/itemset.class.php b/includes/types/itemset.class.php index 0653f14f..9bfce499 100644 --- a/includes/types/itemset.class.php +++ b/includes/types/itemset.class.php @@ -171,34 +171,40 @@ class ItemsetListFilter extends Filter { // cr => [type, field, misc, extraCol] protected $genericFilter = array( // misc (bool): _NUMERIC => useFloat; _STRING => localized; _FLAG => match Value; _BOOLEAN => stringSet - 2 => [FILTER_CR_NUMERIC, 'id', null, true], // id - 3 => [FILTER_CR_NUMERIC, 'npieces', ], // pieces - 4 => [FILTER_CR_STRING, 'bonusText', true ], // bonustext - 5 => [FILTER_CR_BOOLEAN, 'heroic', ], // heroic - 6 => [FILTER_CR_ENUM, 'e.holidayId', ], // relatedevent - 8 => [FILTER_CR_FLAG, 'cuFlags', CUSTOM_HAS_COMMENT ], // hascomments - 9 => [FILTER_CR_FLAG, 'cuFlags', CUSTOM_HAS_SCREENSHOT ], // hasscreenshots - 10 => [FILTER_CR_FLAG, 'cuFlags', CUSTOM_HAS_VIDEO ], // hasvideos + 2 => [FILTER_CR_NUMERIC, 'id', NUM_CAST_INT, true], // id + 3 => [FILTER_CR_NUMERIC, 'npieces', NUM_CAST_INT ], // pieces + 4 => [FILTER_CR_STRING, 'bonusText', true ], // bonustext + 5 => [FILTER_CR_BOOLEAN, 'heroic', ], // heroic + 6 => [FILTER_CR_ENUM, 'e.holidayId', ], // relatedevent + 8 => [FILTER_CR_FLAG, 'cuFlags', CUSTOM_HAS_COMMENT ], // hascomments + 9 => [FILTER_CR_FLAG, 'cuFlags', CUSTOM_HAS_SCREENSHOT ], // hasscreenshots + 10 => [FILTER_CR_FLAG, 'cuFlags', CUSTOM_HAS_VIDEO ], // hasvideos + 12 => [FILTER_CR_NYI_PH, null, 1 ] // available to players [yn] - ugh .. scan loot, quest and vendor templates and write to ?_itemset + ); + + // fieldId => [checkType, checkValue[, fieldIsArray]] + protected $inputFields = array( + 'cr' => [FILTER_V_RANGE, [2, 12], true ], // criteria ids + 'crs' => [FILTER_V_LIST, [FILTER_ENUM_NONE, FILTER_ENUM_ANY, [0, 424]], true ], // criteria operators + 'crv' => [FILTER_V_REGEX, '/[\p{C};:]/ui', true ], // criteria values - only printable chars, no delimiters + 'na' => [FILTER_V_REGEX, '/[\p{C};]/ui', false], // name / description - only printable chars, no delimiter + 'ma' => [FILTER_V_EQUAL, 1, false], // match any / all filter + 'qu' => [FILTER_V_RANGE, [0, 7], true ], // quality + 'ty' => [FILTER_V_RANGE, [1, 12], true ], // set type + 'minle' => [FILTER_V_RANGE, [1, 999], false], // min item level + 'maxle' => [FILTER_V_RANGE, [1, 999], false], // max itemlevel + 'minrl' => [FILTER_V_RANGE, [1, MAX_LEVEL], false], // min required level + 'maxrl' => [FILTER_V_RANGE, [1, MAX_LEVEL], false], // max required level + 'cl' => [FILTER_V_LIST, [[1, 9], 11], false], // class + 'ta' => [FILTER_V_RANGE, [1, 30], false] // tag / content group ); 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 12: // available to players [yn] ugh .. scan loot, quest and vendor templates and write to ?_itemset -/* todo */ return [1]; - } - unset($cr); $this->error = true; return [1]; @@ -216,65 +222,35 @@ class ItemsetListFilter extends Filter // quality [enum] if (isset($_v['qu'])) - $parts[] = ['quality', (array)$_v['qu']]; + $parts[] = ['quality', $_v['qu']]; // type [enum] if (isset($_v['ty'])) - $parts[] = ['type', (array)$_v['ty']]; + $parts[] = ['type', $_v['ty']]; // itemLevel min [int] if (isset($_v['minle'])) - { - if (is_int($_v['minle']) && $_v['minle'] > 0) - $parts[] = ['minLevel', $_v['minle'], '>=']; - else - unset($_v['minle']); - } + $parts[] = ['minLevel', $_v['minle'], '>=']; // itemLevel max [int] if (isset($_v['maxle'])) - { - if (is_int($_v['maxle']) && $_v['maxle'] > 0) - $parts[] = ['maxLevel', $_v['maxle'], '<=']; - else - unset($_v['maxle']); - } + $parts[] = ['maxLevel', $_v['maxle'], '<=']; // reqLevel min [int] if (isset($_v['minrl'])) - { - if (is_int($_v['minrl']) && $_v['minrl'] > 0) - $parts[] = ['reqLevel', $_v['minrl'], '>=']; - else - unset($_v['minrl']); - } + $parts[] = ['reqLevel', $_v['minrl'], '>=']; // reqLevel max [int] if (isset($_v['maxrl'])) - { - if (is_int($_v['maxrl']) && $_v['maxrl'] > 0) - $parts[] = ['reqLevel', $_v['maxrl'], '<=']; - else - unset($_v['maxrl']); - } + $parts[] = ['reqLevel', $_v['maxrl'], '<=']; // class [enum] if (isset($_v['cl'])) - { - if (in_array($_v['cl'], [1, 2, 3, 4, 5, 6, 7, 8, 9, 11])) - $parts[] = ['classMask', $this->list2Mask($_v['cl']), '&']; - else - unset($_v['cl']); - } + $parts[] = ['classMask', $this->list2Mask([$_v['cl']]), '&']; // tag [enum] if (isset($_v['ta'])) - { - if ($_v['ta'] > 0 && $_v['ta'] < 31) - $parts[] = ['contentGroup', intVal($_v['ta'])]; - else - unset($_v['ta']); - } + $parts[] = ['contentGroup', intVal($_v['ta'])]; return $parts; } diff --git a/includes/types/quest.class.php b/includes/types/quest.class.php index 435d8cb2..417d8797 100644 --- a/includes/types/quest.class.php +++ b/includes/types/quest.class.php @@ -430,195 +430,64 @@ class QuestListFilter extends Filter 38 => [null, 1, 2, 3, 4, 5, 6, 7, 8, null, 10, 11, true, false], ); protected $genericFilter = array( + 1 => [FILTER_CR_CALLBACK, 'cbReputation', '>', null], // increasesrepwith + 2 => [FILTER_CR_NUMERIC, 'rewardXP', NUM_CAST_INT ], // experiencegained + 3 => [FILTER_CR_NUMERIC, 'rewardOrReqMoney', NUM_CAST_INT ], // moneyrewarded + 4 => [FILTER_CR_CALLBACK, 'cbSpellRewards', null, null], // spellrewarded [yn] + 5 => [FILTER_CR_FLAG, 'flags', QUEST_FLAG_SHARABLE ], // sharable + 6 => [FILTER_CR_NUMERIC, 'timeLimit', NUM_CAST_INT ], // timer + 7 => [FILTER_CR_NYI_PH, null, 1 ], // firstquestseries + 9 => [FILTER_CR_CALLBACK, 'cbEarnReputation', null, null], // objectiveearnrepwith [enum] + 10 => [FILTER_CR_CALLBACK, 'cbReputation', '<', null], // decreasesrepwith + 11 => [FILTER_CR_NUMERIC, 'suggestedPlayers', NUM_CAST_INT ], // suggestedplayers + 15 => [FILTER_CR_NYI_PH, null, 1 ], // lastquestseries + 16 => [FILTER_CR_NYI_PH, null, 1 ], // partseries + 18 => [FILTER_CR_FLAG, 'cuFlags', CUSTOM_HAS_SCREENSHOT ], // hasscreenshots + 19 => [FILTER_CR_CALLBACK, 'cbQuestRelation', 0x1, null], // startsfrom [enum] + 21 => [FILTER_CR_CALLBACK, 'cbQuestRelation', 0x2, null], // endsat [enum] + 22 => [FILTER_CR_CALLBACK, 'cbItemRewards', null, null], // itemrewards [op] [int] + 23 => [FILTER_CR_CALLBACK, 'cbItemChoices', null, null], // itemchoices [op] [int] + 24 => [FILTER_CR_CALLBACK, 'cbLacksStartEnd', null, null], // lacksstartend [yn] + 25 => [FILTER_CR_FLAG, 'cuFlags', CUSTOM_HAS_COMMENT ], // hascomments 27 => [FILTER_CR_FLAG, 'flags', QUEST_FLAG_DAILY ], // daily 28 => [FILTER_CR_FLAG, 'flags', QUEST_FLAG_WEEKLY ], // weekly 29 => [FILTER_CR_FLAG, 'flags', QUEST_FLAG_REPEATABLE ], // repeatable - 30 => [FILTER_CR_NUMERIC, 'id', null, true], // id - 5 => [FILTER_CR_FLAG, 'flags', QUEST_FLAG_SHARABLE ], // sharable - 11 => [FILTER_CR_NUMERIC, 'suggestedPlayers', ], // suggestedplayers - 6 => [FILTER_CR_NUMERIC, 'timeLimit', ], // timer - 42 => [FILTER_CR_STAFFFLAG, 'flags', ], // flags - 45 => [FILTER_CR_BOOLEAN, 'rewardTitleId', ], // titlerewarded - 2 => [FILTER_CR_NUMERIC, 'rewardXP', ], // experiencegained - 3 => [FILTER_CR_NUMERIC, 'rewardOrReqMoney', ], // moneyrewarded - 33 => [FILTER_CR_ENUM, 'e.holidayId', ], // relatedevent - 25 => [FILTER_CR_FLAG, 'cuFlags', CUSTOM_HAS_COMMENT ], // hascomments - 18 => [FILTER_CR_FLAG, 'cuFlags', CUSTOM_HAS_SCREENSHOT ], // hasscreenshots + 30 => [FILTER_CR_NUMERIC, 'id', NUM_CAST_INT, true], // id + 33 => [FILTER_CR_ENUM, 'e.holidayId' ], // relatedevent + 34 => [FILTER_CR_CALLBACK, 'cbAvailable', null, null], // availabletoplayers [yn] 36 => [FILTER_CR_FLAG, 'cuFlags', CUSTOM_HAS_VIDEO ], // hasvideos + 37 => [FILTER_CR_CALLBACK, 'cbClassSpec', null, null], // classspecific [enum] + 37 => [FILTER_CR_CALLBACK, 'cbRaceSpec', null, null], // racespecific [enum] + 42 => [FILTER_CR_STAFFFLAG, 'flags' ], // flags + 43 => [FILTER_CR_CALLBACK, 'cbCurrencyReward', null, null], // currencyrewarded [enum] + 44 => [FILTER_CR_CALLBACK, 'cbLoremaster', null, null], // countsforloremaster_stc [yn] + 45 => [FILTER_CR_BOOLEAN, 'rewardTitleId' ] // titlerewarded + ); + + // fieldId => [checkType, checkValue[, fieldIsArray]] + protected $inputFields = array( + 'cr' => [FILTER_V_RANGE, [1, 45], true ], // criteria ids + 'crs' => [FILTER_V_LIST, [FILTER_ENUM_NONE, FILTER_ENUM_ANY, [0, 99999]], true ], // criteria operators + 'crv' => [FILTER_V_REGEX, '/\D/', true ], // criteria values - only numerals + 'na' => [FILTER_V_REGEX, '/[\p{C};]/ui', false], // name / text - only printable chars, no delimiter + 'ex' => [FILTER_V_EQUAL, 'on', false], // also match subname + 'ma' => [FILTER_V_EQUAL, 1, false], // match any / all filter + 'minle' => [FILTER_V_RANGE, [1, 99], false], // min quest level + 'maxle' => [FILTER_V_RANGE, [1, 99], false], // max quest level + 'minrl' => [FILTER_V_RANGE, [1, 99], false], // min required level + 'maxrl' => [FILTER_V_RANGE, [1, 99], false], // max required level + 'si' => [FILTER_V_LIST, [-2, -1, 1, 2, 3], false], // siede + 'ty' => [FILTER_V_LIST, [0, 1, 21, 41, 62, [81, 85], 88, 89], true ] // type ); 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 1: // increasesrepwith - if ($this->isSaneNumeric($cr[1]) && $cr[1] > 0) - { - if ($_ = DB::Aowow()->selectRow('SELECT * FROM ?_factions WHERE id = ?d', $cr[1])) - $this->formData['reputationCols'][] = [$cr[1], Util::localizedString($_, 'name')]; - - return [ - 'OR', - ['AND', ['rewardFactionId1', $cr[1]], ['rewardFactionValue1', 0, '>']], - ['AND', ['rewardFactionId2', $cr[1]], ['rewardFactionValue2', 0, '>']], - ['AND', ['rewardFactionId3', $cr[1]], ['rewardFactionValue3', 0, '>']], - ['AND', ['rewardFactionId4', $cr[1]], ['rewardFactionValue4', 0, '>']], - ['AND', ['rewardFactionId5', $cr[1]], ['rewardFactionValue5', 0, '>']] - ]; - } - break; - case 10: // decreasesrepwith - if ($this->isSaneNumeric($cr[1]) && $cr[1] > 0) - { - if ($_ = DB::Aowow()->selectRow('SELECT * FROM ?_factions WHERE id = ?d', $cr[1])) - $this->formData['reputationCols'][] = [$cr[1], Util::localizedString($_, 'name')]; - - return [ - 'OR', - ['AND', ['rewardFactionId1', $cr[1]], ['rewardFactionValue1', 0, '<']], - ['AND', ['rewardFactionId2', $cr[1]], ['rewardFactionValue2', 0, '<']], - ['AND', ['rewardFactionId3', $cr[1]], ['rewardFactionValue3', 0, '<']], - ['AND', ['rewardFactionId4', $cr[1]], ['rewardFactionValue4', 0, '<']], - ['AND', ['rewardFactionId5', $cr[1]], ['rewardFactionValue5', 0, '<']] - ]; - } - break; - case 43: // currencyrewarded - if ($this->isSaneNumeric($cr[1]) && $cr[1] > 0) - { - return [ - 'OR', - ['rewardItemId1', $cr[1]], ['rewardItemId2', $cr[1]], ['rewardItemId3', $cr[1]], ['rewardItemId4', $cr[1]], - ['rewardChoiceItemId1', $cr[1]], ['rewardChoiceItemId2', $cr[1]], ['rewardChoiceItemId3', $cr[1]], ['rewardChoiceItemId4', $cr[1]], ['rewardChoiceItemId5', $cr[1]], ['rewardChoiceItemId6', $cr[1]] - ]; - } - break; - case 34: // availabletoplayers - if ($this->int2Bool($cr[1])) - { - if ($cr[1]) - return [['cuFlags', CUSTOM_UNAVAILABLE | CUSTOM_DISABLED, '&'], 0]; - else - return ['cuFlags', CUSTOM_UNAVAILABLE | CUSTOM_DISABLED, '&']; - } - break; - case 23: // itemchoices [op] [int] - if (!$this->isSaneNumeric($cr[2], false) || !$this->int2Op($cr[1])) - break; - - $this->extraOpts['q']['s'][] = ', (IF(rewardChoiceItemId1, 1, 0) + IF(rewardChoiceItemId2, 1, 0) + IF(rewardChoiceItemId3, 1, 0) + IF(rewardChoiceItemId4, 1, 0) + IF(rewardChoiceItemId5, 1, 0) + IF(rewardChoiceItemId6, 1, 0)) as numChoices'; - $this->extraOpts['q']['h'][] = 'numChoices '.$cr[1].' '.$cr[2]; - return [1]; - case 22: // itemrewards [op] [int] - if (!$this->isSaneNumeric($cr[2], false) || !$this->int2Op($cr[1])) - break; - - $this->extraOpts['q']['s'][] = ', (IF(rewardItemId1, 1, 0) + IF(rewardItemId2, 1, 0) + IF(rewardItemId3, 1, 0) + IF(rewardItemId4, 1, 0)) as numRewards'; - $this->extraOpts['q']['h'][] = 'numRewards '.$cr[1].' '.$cr[2]; - return [1]; - case 44: // countsforloremaster_stc [bool] - if ($this->int2Bool($cr[1])) - { - if ($cr[1]) - return ['AND', ['zoneOrSort', 0, '>'], [['flags', QUEST_FLAG_DAILY | QUEST_FLAG_WEEKLY | QUEST_FLAG_REPEATABLE , '&'], 0], [['specialFlags', QUEST_FLAG_SPECIAL_REPEATABLE | QUEST_FLAG_SPECIAL_MONTHLY , '&'], 0]]; - else - return ['OR', ['zoneOrSort', 0, '<'], ['flags', QUEST_FLAG_DAILY | QUEST_FLAG_WEEKLY | QUEST_FLAG_REPEATABLE , '&'], ['specialFlags', QUEST_FLAG_SPECIAL_REPEATABLE | QUEST_FLAG_SPECIAL_MONTHLY , '&']];; - } - - break; - case 4: // spellrewarded [bool] - if ($this->int2Bool($cr[1])) - { - if ($cr[1]) - return ['OR', ['sourceSpellId', 0, '>'], ['rewardSpell', 0, '>'], ['rsc.effect1Id', SpellList::$effects['teach']], ['rsc.effect2Id', SpellList::$effects['teach']], ['rsc.effect3Id', SpellList::$effects['teach']]]; - else - return ['AND', ['sourceSpellId', 0], ['rewardSpell', 0], ['rewardSpellCast', 0]]; - } - break; - case 9: // objectiveearnrepwith [enum] - $_ = intVal($cr[1]); - if ($_ > 0) - return ['OR', ['reqFactionId1', $_], ['reqFactionId2', $_]]; - else if ($cr[1] == FILTER_ENUM_ANY) // any - return ['OR', ['reqFactionId1', 0, '>'], ['reqFactionId2', 0, '>']]; - else if ($cr[1] == FILTER_ENUM_NONE) // none - return ['AND', ['reqFactionId1', 0], ['reqFactionId2', 0]]; - - break; - case 37: // classspecific [enum] - $_ = isset($this->enums[$cr[0]][$cr[1]]) ? $this->enums[$cr[0]][$cr[1]] : null; - if ($_ !== null) - { - if ($_ === true) - return ['AND', ['reqClassMask', 0, '!'], [['reqClassMask', CLASS_MASK_ALL, '&'], CLASS_MASK_ALL, '!']]; - else if ($_ === false) - return ['OR', ['reqClassMask', 0], [['reqClassMask', CLASS_MASK_ALL, '&'], CLASS_MASK_ALL]]; - else if (is_int($_)) - return ['AND', ['reqClassMask', (1 << ($_ - 1)), '&'], [['reqClassMask', CLASS_MASK_ALL, '&'], CLASS_MASK_ALL, '!']]; - } - break; - case 38: // racespecific [enum] - $_ = isset($this->enums[$cr[0]][$cr[1]]) ? $this->enums[$cr[0]][$cr[1]] : null; - if ($_ !== null) - { - if ($_ === true) - return ['AND', ['reqRaceMask', 0, '!'], [['reqRaceMask', RACE_MASK_ALL, '&'], RACE_MASK_ALL, '!'], [['reqRaceMask', RACE_MASK_ALLIANCE, '&'], RACE_MASK_ALLIANCE, '!'], [['reqRaceMask', RACE_MASK_HORDE, '&'], RACE_MASK_HORDE, '!']]; - else if ($_ === false) - return ['OR', ['reqRaceMask', 0], ['reqRaceMask', RACE_MASK_ALL], ['reqRaceMask', RACE_MASK_ALLIANCE], ['reqRaceMask', RACE_MASK_HORDE]]; - else if (is_int($_)) - return ['AND', ['reqRaceMask', (1 << ($_ - 1)), '&'], [['reqRaceMask', RACE_MASK_ALLIANCE, '&'], RACE_MASK_ALLIANCE, '!'], [['reqRaceMask', RACE_MASK_HORDE, '&'], RACE_MASK_HORDE, '!']]; - } - break; - case 19: // startsfrom [enum] - switch ($cr[1]) - { - case 1: // npc - return ['AND', ['qse.type', TYPE_NPC], ['qse.method', 0x1, '&']]; - case 2: // object - return ['AND', ['qse.type', TYPE_OBJECT], ['qse.method', 0x1, '&']]; - case 3: // item - return ['AND', ['qse.type', TYPE_ITEM], ['qse.method', 0x1, '&']]; - } - break; - case 21: // endsat [enum] - switch ($cr[1]) - { - case 1: // npc - return ['AND', ['qse.type', TYPE_NPC], ['qse.method', 0x2, '&']]; - case 2: // object - return ['AND', ['qse.type', TYPE_OBJECT], ['qse.method', 0x2, '&']]; - } - break; - case 24: // lacksstartend [bool] - $missing = DB::Aowow()->selectCol('SELECT questId, max(method) a, min(method) b FROM ?_quests_startend GROUP BY questId HAVING (a | b) <> 3'); - if ($this->int2Bool($cr[1])) - { - if ($cr[1]) - return ['id', $missing]; - else - return ['id', $missing, '!']; - } - break; - case 7: // firstquestseries - case 15: // lastquestseries - case 16: // partseries -/* todo */ return [1]; // self-joining eats substential amounts of time: should restructure that and also incorporate reqQ and openQ cases from infobox - default: - break; - } - unset($cr); - $this->error = 1; + $this->error = true; return [1]; } @@ -642,39 +511,19 @@ class QuestListFilter extends Filter // level min if (isset($_v['minle'])) - { - if (is_int($_v['minle']) && $_v['minle'] > 0) - $parts[] = ['level', $_v['minle'], '>=']; // not considering quests that are always at player level (-1) - else - unset($_v['minle']); - } + $parts[] = ['level', $_v['minle'], '>=']; // not considering quests that are always at player level (-1) // level max if (isset($_v['maxle'])) - { - if (is_int($_v['maxle']) && $_v['maxle'] > 0) - $parts[] = ['level', $_v['maxle'], '<=']; - else - unset($_v['maxle']); - } + $parts[] = ['level', $_v['maxle'], '<=']; // reqLevel min if (isset($_v['minrl'])) - { - if (is_int($_v['minrl']) && $_v['minrl'] > 0) - $parts[] = ['minLevel', $_v['minrl'], '>='];// ignoring maxLevel - else - unset($_v['minrl']); - } + $parts[] = ['minLevel', $_v['minrl'], '>=']; // ignoring maxLevel // reqLevel max if (isset($_v['maxrl'])) - { - if (is_int($_v['maxrl']) && $_v['maxrl'] > 0) - $parts[] = ['minLevel', $_v['maxrl'], '<='];// ignoring maxLevel - else - unset($_v['maxrl']); - } + $parts[] = ['minLevel', $_v['maxrl'], '<=']; // ignoring maxLevel // side if (isset($_v['si'])) @@ -699,23 +548,172 @@ class QuestListFilter extends Filter case -1: $parts[] = ['AND', $ex, ['reqRaceMask', RACE_MASK_ALLIANCE, '&']]; break; - default: - unset($_v['si']); } } // type [list] if (isset($_v['ty'])) - { - $_ = (array)$_v['ty']; - if (!array_diff($_, [0, 1, 21, 41, 62, 81, 82, 83, 84, 85, 88, 89])) - $parts[] = ['type', $_]; - else - unset($_v['ty']); - } + $parts[] = ['type', $_v['ty']]; return $parts; } + + protected function cbReputation($cr, $sign) + { + if (!Util::checkNumeric($cr[1], NUM_REQ_INT) || $cr[1] <= 0) + return false; + + if ($_ = DB::Aowow()->selectRow('SELECT * FROM ?_factions WHERE id = ?d', $cr[1])) + $this->formData['reputationCols'][] = [$cr[1], Util::localizedString($_, 'name')]; + + return [ + 'OR', + ['AND', ['rewardFactionId1', $cr[1]], ['rewardFactionValue1', 0, $sign]], + ['AND', ['rewardFactionId2', $cr[1]], ['rewardFactionValue2', 0, $sign]], + ['AND', ['rewardFactionId3', $cr[1]], ['rewardFactionValue3', 0, $sign]], + ['AND', ['rewardFactionId4', $cr[1]], ['rewardFactionValue4', 0, $sign]], + ['AND', ['rewardFactionId5', $cr[1]], ['rewardFactionValue5', 0, $sign]] + ]; + } + + protected function cbQuestRelation($cr, $flags) + { + switch ($cr[1]) + { + case 1: // npc + return ['AND', ['qse.type', TYPE_NPC], ['qse.method', $flags, '&']]; + case 2: // object + return ['AND', ['qse.type', TYPE_OBJECT], ['qse.method', $flags, '&']]; + case 3: // item + return ['AND', ['qse.type', TYPE_ITEM], ['qse.method', $flags, '&']]; + } + + return false; + } + + protected function cbCurrencyReward($cr) + { + if (!Util::checkNumeric($cr[1], NUM_REQ_INT) || $cr[1] <= 0) + return false; + + return [ + 'OR', + ['rewardItemId1', $cr[1]], ['rewardItemId2', $cr[1]], ['rewardItemId3', $cr[1]], ['rewardItemId4', $cr[1]], + ['rewardChoiceItemId1', $cr[1]], ['rewardChoiceItemId2', $cr[1]], ['rewardChoiceItemId3', $cr[1]], ['rewardChoiceItemId4', $cr[1]], ['rewardChoiceItemId5', $cr[1]], ['rewardChoiceItemId6', $cr[1]] + ]; + } + + protected function cbAvailable($cr) + { + if (!$this->int2Bool($cr[1])) + return false; + + if ($cr[1]) + return [['cuFlags', CUSTOM_UNAVAILABLE | CUSTOM_DISABLED, '&'], 0]; + else + return ['cuFlags', CUSTOM_UNAVAILABLE | CUSTOM_DISABLED, '&']; + } + + protected function cbItemChoices($cr) + { + if (!Util::checkNumeric($cr[2], NUM_CAST_INT) || !$this->int2Op($cr[1])) + return false; + + $this->extraOpts['q']['s'][] = ', (IF(rewardChoiceItemId1, 1, 0) + IF(rewardChoiceItemId2, 1, 0) + IF(rewardChoiceItemId3, 1, 0) + IF(rewardChoiceItemId4, 1, 0) + IF(rewardChoiceItemId5, 1, 0) + IF(rewardChoiceItemId6, 1, 0)) as numChoices'; + $this->extraOpts['q']['h'][] = 'numChoices '.$cr[1].' '.$cr[2]; + return [1]; + } + + protected function cbItemRewards($cr) + { + if (!Util::checkNumeric($cr[2], NUM_CAST_INT) || !$this->int2Op($cr[1])) + return false; + + $this->extraOpts['q']['s'][] = ', (IF(rewardItemId1, 1, 0) + IF(rewardItemId2, 1, 0) + IF(rewardItemId3, 1, 0) + IF(rewardItemId4, 1, 0)) as numRewards'; + $this->extraOpts['q']['h'][] = 'numRewards '.$cr[1].' '.$cr[2]; + return [1]; + } + + protected function cbLoremaster($cr) + { + if (!$this->int2Bool($cr[1])) + return false; + + if ($cr[1]) + return ['AND', ['zoneOrSort', 0, '>'], [['flags', QUEST_FLAG_DAILY | QUEST_FLAG_WEEKLY | QUEST_FLAG_REPEATABLE , '&'], 0], [['specialFlags', QUEST_FLAG_SPECIAL_REPEATABLE | QUEST_FLAG_SPECIAL_MONTHLY , '&'], 0]]; + else + return ['OR', ['zoneOrSort', 0, '<'], ['flags', QUEST_FLAG_DAILY | QUEST_FLAG_WEEKLY | QUEST_FLAG_REPEATABLE , '&'], ['specialFlags', QUEST_FLAG_SPECIAL_REPEATABLE | QUEST_FLAG_SPECIAL_MONTHLY , '&']];; + } + + protected function cbSpellRewards($cr) + { + if (!$this->int2Bool($cr[1])) + return false; + + if ($cr[1]) + return ['OR', ['sourceSpellId', 0, '>'], ['rewardSpell', 0, '>'], ['rsc.effect1Id', SpellList::$effects['teach']], ['rsc.effect2Id', SpellList::$effects['teach']], ['rsc.effect3Id', SpellList::$effects['teach']]]; + else + return ['AND', ['sourceSpellId', 0], ['rewardSpell', 0], ['rewardSpellCast', 0]]; + } + + protected function cbEarnReputation($cr) + { + if (!Util::checkNumeric($cr[1], NUM_REQ_INT)) + return false; + + if ($cr[1] > 0) + return ['OR', ['reqFactionId1', $cr[1]], ['reqFactionId2', $cr[1]]]; + else if ($cr[1] == FILTER_ENUM_ANY) // any + return ['OR', ['reqFactionId1', 0, '>'], ['reqFactionId2', 0, '>']]; + else if ($cr[1] == FILTER_ENUM_NONE) // none + return ['AND', ['reqFactionId1', 0], ['reqFactionId2', 0]]; + + return false; + } + + protected function cbClassSpec($cr) + { + if (!isset($this->enums[$cr[0]][$cr[1]])) + return false; + + $_ = $this->enums[$cr[0]][$cr[1]]; + if ($_ === true) + return ['AND', ['reqClassMask', 0, '!'], [['reqClassMask', CLASS_MASK_ALL, '&'], CLASS_MASK_ALL, '!']]; + else if ($_ === false) + return ['OR', ['reqClassMask', 0], [['reqClassMask', CLASS_MASK_ALL, '&'], CLASS_MASK_ALL]]; + else if (is_int($_)) + return ['AND', ['reqClassMask', (1 << ($_ - 1)), '&'], [['reqClassMask', CLASS_MASK_ALL, '&'], CLASS_MASK_ALL, '!']]; + + return false; + } + + protected function cbRaceSpec($cr) + { + if (!isset($this->enums[$cr[0]][$cr[1]])) + return false; + + $_ = $this->enums[$cr[0]][$cr[1]]; + if ($_ === true) + return ['AND', ['reqRaceMask', 0, '!'], [['reqRaceMask', RACE_MASK_ALL, '&'], RACE_MASK_ALL, '!'], [['reqRaceMask', RACE_MASK_ALLIANCE, '&'], RACE_MASK_ALLIANCE, '!'], [['reqRaceMask', RACE_MASK_HORDE, '&'], RACE_MASK_HORDE, '!']]; + else if ($_ === false) + return ['OR', ['reqRaceMask', 0], ['reqRaceMask', RACE_MASK_ALL], ['reqRaceMask', RACE_MASK_ALLIANCE], ['reqRaceMask', RACE_MASK_HORDE]]; + else if (is_int($_)) + return ['AND', ['reqRaceMask', (1 << ($_ - 1)), '&'], [['reqRaceMask', RACE_MASK_ALLIANCE, '&'], RACE_MASK_ALLIANCE, '!'], [['reqRaceMask', RACE_MASK_HORDE, '&'], RACE_MASK_HORDE, '!']]; + + return false; + } + + protected function cbLacksStartEnd($cr) + { + if (!$this->int2Bool($cr[1])) + return false; + + $missing = DB::Aowow()->selectCol('SELECT questId, max(method) a, min(method) b FROM ?_quests_startend GROUP BY questId HAVING (a | b) <> 3'); + if ($cr[1]) + return ['id', $missing]; + else + return ['id', $missing, '!']; + } } diff --git a/includes/types/sound.class.php b/includes/types/sound.class.php index a7fa0440..86001e76 100644 --- a/includes/types/sound.class.php +++ b/includes/types/sound.class.php @@ -109,6 +109,12 @@ class SoundListFilter extends Filter return [1]; } + // fieldId => [checkType, checkValue[, fieldIsArray]] + protected $inputFields = array( + 'na' => [FILTER_V_REGEX, '/[\p{C};]/ui', false], // name - only printable chars, no delimiter + 'ty' => [FILTER_V_LIST, [[1, 4], 6, 9, 10, 12, 13, 14, 16, 17, [19, 23], [25, 31], 50, 52, 53], true ] // type + ); + protected function createSQLForValues() { $parts = []; @@ -121,15 +127,7 @@ class SoundListFilter extends Filter // type [list] if (isset($_v['ty'])) - { - if ($_ = array_intersect((array)$_v['ty'], [1, 2, 3, 4, 6, 9, 10, 12, 13, 14, 16, 17, 19, 20, 21, 22, 23, 25, 26, 27, 28, 29, 30, 31, 50, 52, 53])) - $parts[] = ['cat', $_]; - else - { - $this->error = true; - unset($_v['ty']); - } - } + $parts[] = ['cat', $_v['ty']]; return $parts; } diff --git a/includes/types/spell.class.php b/includes/types/spell.class.php index fc1f5d6c..3f1534d3 100644 --- a/includes/types/spell.class.php +++ b/includes/types/spell.class.php @@ -2159,66 +2159,51 @@ class SpellListFilter extends Filter // cr => [type, field, misc, extraCol] protected $genericFilter = array( // misc (bool): _NUMERIC => useFloat; _STRING => localized; _FLAG => match Value; _BOOLEAN => stringSet - 2 => [FILTER_CR_NUMERIC, 'powerCostPercent', ], // prcntbasemanarequired - 3 => [FILTER_CR_BOOLEAN, 'spellFocusObject' ], // requiresnearbyobject - 4 => [FILTER_CR_NUMERIC, 'trainingcost' ], // trainingcost - 5 => [FILTER_CR_BOOLEAN, 'reqSpellId' ], // requiresprofspec - 10 => [FILTER_CR_FLAG, 'cuFlags', SPELL_CU_FIRST_RANK ], // firstrank - 12 => [FILTER_CR_FLAG, 'cuFlags', SPELL_CU_LAST_RANK ], // lastrank - 13 => [FILTER_CR_NUMERIC, 'rankNo', ], // rankno - 14 => [FILTER_CR_NUMERIC, 'id', null, true], // id - 15 => [FILTER_CR_STRING, 'ic.name', ], // icon - 19 => [FILTER_CR_FLAG, 'attributes0', 0x80000 ], // scaling - 25 => [FILTER_CR_BOOLEAN, 'skillLevelYellow' ], // rewardsskillups - 11 => [FILTER_CR_FLAG, 'cuFlags', CUSTOM_HAS_COMMENT ], // hascomments - 8 => [FILTER_CR_FLAG, 'cuFlags', CUSTOM_HAS_SCREENSHOT ], // hasscreenshots - 17 => [FILTER_CR_FLAG, 'cuFlags', CUSTOM_HAS_VIDEO ], // hasvideos + 1 => [FILTER_CR_CALLBACK, 'cbCost', null, null], // costAbs [op] [int] + 2 => [FILTER_CR_NUMERIC, 'powerCostPercent', NUM_CAST_INT ], // prcntbasemanarequired + 3 => [FILTER_CR_BOOLEAN, 'spellFocusObject' ], // requiresnearbyobject + 4 => [FILTER_CR_NUMERIC, 'trainingcost', NUM_CAST_INT ], // trainingcost + 5 => [FILTER_CR_BOOLEAN, 'reqSpellId' ], // requiresprofspec + 8 => [FILTER_CR_FLAG, 'cuFlags', CUSTOM_HAS_SCREENSHOT ], // hasscreenshots + 9 => [FILTER_CR_CALLBACK, 'cbSource', null, null], // source [enum] + 10 => [FILTER_CR_FLAG, 'cuFlags', SPELL_CU_FIRST_RANK ], // firstrank + 11 => [FILTER_CR_FLAG, 'cuFlags', CUSTOM_HAS_COMMENT ], // hascomments + 12 => [FILTER_CR_FLAG, 'cuFlags', SPELL_CU_LAST_RANK ], // lastrank + 13 => [FILTER_CR_NUMERIC, 'rankNo', NUM_CAST_INT ], // rankno + 14 => [FILTER_CR_NUMERIC, 'id', NUM_CAST_INT, true], // id + 15 => [FILTER_CR_STRING, 'ic.name', ], // icon + 17 => [FILTER_CR_FLAG, 'cuFlags', CUSTOM_HAS_VIDEO ], // hasvideos + 19 => [FILTER_CR_FLAG, 'attributes0', 0x80000 ], // scaling + 20 => [FILTER_CR_CALLBACK, 'cbReagents', null, null], // has Reagents [yn] + 25 => [FILTER_CR_BOOLEAN, 'skillLevelYellow' ] // rewardsskillups + ); + + // fieldId => [checkType, checkValue[, fieldIsArray]] + protected $inputFields = array( + 'cr' => [FILTER_V_RANGE, [1, 25], true ], // criteria ids + 'crs' => [FILTER_V_LIST, [FILTER_ENUM_NONE, FILTER_ENUM_ANY, [0, 99999]], true ], // criteria operators + 'crv' => [FILTER_V_REGEX, '/[\p{C};:]/ui', true ], // criteria values - only printable chars, no delimiters + 'na' => [FILTER_V_REGEX, '/[\p{C};]/ui', false], // name / text - only printable chars, no delimiter + 'ex' => [FILTER_V_EQUAL, 'on', false], // extended name search + 'ma' => [FILTER_V_EQUAL, 1, false], // match any / all filter + 'minle' => [FILTER_V_RANGE, [1, 99], false], // spell level min + 'maxle' => [FILTER_V_RANGE, [1, 99], false], // spell level max + 'minrs' => [FILTER_V_RANGE, [1, 999], false], // required skill level min + 'maxrs' => [FILTER_V_RANGE, [1, 999], false], // required skill level max + 'ra' => [FILTER_V_LIST, [[1, 8], 10, 11], false], // races + 'cl' => [FILTER_V_CALLBACK, 'cbClasses', true ], // classes + 'gl' => [FILTER_V_CALLBACK, 'cbGlyphs', true ], // glyph type + 'sc' => [FILTER_V_RANGE, [0, 6], true ], // magic schools + 'dt' => [FILTER_V_LIST, [[1, 6], 9], false], // dispel types + 'me' => [FILTER_V_RANGE, [1, 31], false] // mechanics ); 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 1: // costAbs [op] [int] - if (!$this->isSaneNumeric($cr[2])) - break; - - if (!$this->int2Op($cr[1])) - break; - - return ['OR', ['AND', ['powerType', [1, 6]], ['powerCost', (10 * $cr[2]), $cr[1]]], ['AND', ['powerType', [1, 6], '!'], ['powerCost', $cr[2], $cr[1]]]]; - case 9: // source [enum] - $_ = isset($this->enums[$cr[0]][$cr[1]]) ? $this->enums[$cr[0]][$cr[1]] : null; - if ($_ !== null) - { - if (is_int($_)) // specific - return ['src.src'.$_, null, '!']; - else if ($_) // any - return ['OR', ['src.src1', null, '!'], ['src.src2', null, '!'], ['src.src4', null, '!'], ['src.src5', null, '!'], ['src.src6', null, '!'], ['src.src7', null, '!'], ['src.src9', null, '!']]; - else if (!$_) // none - return ['AND', ['src.src1', null], ['src.src2', null], ['src.src4', null], ['src.src5', null], ['src.src6', null], ['src.src7', null], ['src.src9', null]]; - } - break; - case 20: // has Reagents [yn] - if ($this->int2Bool($cr[1])) - { - if ($cr[1]) - return ['OR', ['reagent1', 0, '>'], ['reagent2', 0, '>'], ['reagent3', 0, '>'], ['reagent4', 0, '>'], ['reagent5', 0, '>'], ['reagent6', 0, '>'], ['reagent7', 0, '>'], ['reagent8', 0, '>']]; - else - return ['AND', ['reagent1', 0], ['reagent2', 0], ['reagent3', 0], ['reagent4', 0], ['reagent5', 0], ['reagent6', 0], ['reagent7', 0], ['reagent8', 0]]; - } - } - unset($cr); $this->error = true; return [1]; @@ -2244,98 +2229,109 @@ class SpellListFilter extends Filter // spellLevel min todo (low): talentSpells (typeCat -2) commonly have spellLevel 1 (and talentLevel >1) -> query is inaccurate if (isset($_v['minle'])) - { - if (is_int($_v['minle']) && $_v['minle'] > 0) - $parts[] = ['spellLevel', $_v['minle'], '>=']; - else - unset($_v['minle']); - } + $parts[] = ['spellLevel', $_v['minle'], '>=']; // spellLevel max if (isset($_v['maxle'])) - { - if (is_int($_v['maxle']) && $_v['maxle'] > 0) - $parts[] = ['spellLevel', $_v['maxle'], '<=']; - else - unset($_v['maxle']); - } + $parts[] = ['spellLevel', $_v['maxle'], '<=']; // skillLevel min if (isset($_v['minrs'])) - { - if (is_int($_v['minrs']) && $_v['minrs'] > 0) - $parts[] = ['learnedAt', $_v['minrs'], '>=']; - else - unset($_v['minrs']); - } + $parts[] = ['learnedAt', $_v['minrs'], '>=']; // skillLevel max if (isset($_v['maxrs'])) - { - if (is_int($_v['maxrs']) && $_v['maxrs'] > 0) - $parts[] = ['learnedAt', $_v['maxrs'], '<=']; - else - unset($_v['maxrs']); - } + $parts[] = ['learnedAt', $_v['maxrs'], '<=']; // race if (isset($_v['ra'])) - { - if (in_array($_v['ra'], [1, 2, 3, 4, 5, 6, 7, 8, 10, 11])) - $parts[] = ['AND', [['reqRaceMask', RACE_MASK_ALL, '&'], RACE_MASK_ALL, '!'], ['reqRaceMask', $this->list2Mask($_v['ra']), '&']]; - else - unset($_v['ra']); - } + $parts[] = ['AND', [['reqRaceMask', RACE_MASK_ALL, '&'], RACE_MASK_ALL, '!'], ['reqRaceMask', $this->list2Mask([$_v['ra']]), '&']]; // class [list] if (isset($_v['cl'])) - { - $_ = (array)$_v['cl']; - if (!array_diff($_, [1, 2, 3, 4, 5, 6, 7, 8, 9, 11])) - $parts[] = ['reqClassMask', $this->list2Mask($_), '&']; - else - unset($_v['cl']); - } + $parts[] = ['reqClassMask', $this->list2Mask($_v['cl']), '&']; // school [list] if (isset($_v['sc'])) - { - $_ = (array)$_v['sc']; - if (!array_diff($_, [0, 1, 2, 3, 4, 5, 6])) - $parts[] = ['schoolMask', $this->list2Mask($_, true), '&']; - else - unset($_v['sc']); - } + $parts[] = ['schoolMask', $this->list2Mask($_v['sc'], true), '&']; // glyph type [list] wonky, admittedly, but consult SPELL_CU_* in defines and it makes sense if (isset($_v['gl'])) - { - if (in_array($_v['gl'], [1, 2])) - $parts[] = ['cuFlags', ($this->list2Mask($_v['gl']) << 6), '&']; - else - unset($_v['gl']); - } + $parts[] = ['cuFlags', ($this->list2Mask($_v['gl']) << 6), '&']; // dispel type if (isset($_v['dt'])) - { - if (in_array($_v['dt'], [1, 2, 3, 4, 5, 6, 9])) - $parts[] = ['dispelType', $_v['dt']]; - else - unset($_v['dt']); - } + $parts[] = ['dispelType', $_v['dt']]; // mechanic if (isset($_v['me'])) - { - if ($_v['me'] > 0 && $_v['me'] < 32) - $parts[] = ['OR', ['mechanic', $_v['me']], ['effect1Mechanic', $_v['me']], ['effect2Mechanic', $_v['me']], ['effect3Mechanic', $_v['me']]]; - else - unset($_v['me']); - } + $parts[] = ['OR', ['mechanic', $_v['me']], ['effect1Mechanic', $_v['me']], ['effect2Mechanic', $_v['me']], ['effect3Mechanic', $_v['me']]]; return $parts; } + + protected function cbClasses(&$val) + { + if (!$this->parentCats || !in_array($this->parentCats[0], [-13, -2, 7])) + return false; + + if (!Util::checkNumeric($val, NUM_REQ_INT)) + return false; + + $type = FILTER_V_LIST; + $valid = [[1, 9], 11]; + + return $this->checkInput($type, $valid, $val); + } + + protected function cbGlyphs(&$val) + { + if (!$this->parentCats || $this->parentCats[0] != -13) + return false; + + if (!Util::checkNumeric($val, NUM_REQ_INT)) + return false; + + $type = FILTER_V_LIST; + $valid = [1, 2]; + + return $this->checkInput($type, $valid, $val); + } + + protected function cbCost($cr) + { + if (!Util::checkNumeric($cr[2], NUM_CAST_INT) || !$this->int2Op($cr[1])) + return false; + + return ['OR', ['AND', ['powerType', [1, 6]], ['powerCost', (10 * $cr[2]), $cr[1]]], ['AND', ['powerType', [1, 6], '!'], ['powerCost', $cr[2], $cr[1]]]]; + } + + protected function cbSource($cr) + { + if (!isset($this->enums[$cr[0]][$cr[1]])) + return false; + + $_ = $this->enums[$cr[0]][$cr[1]]; + if (is_int($_)) // specific + return ['src.src'.$_, null, '!']; + else if ($_) // any + return ['OR', ['src.src1', null, '!'], ['src.src2', null, '!'], ['src.src4', null, '!'], ['src.src5', null, '!'], ['src.src6', null, '!'], ['src.src7', null, '!'], ['src.src9', null, '!']]; + else if (!$_) // none + return ['AND', ['src.src1', null], ['src.src2', null], ['src.src4', null], ['src.src5', null], ['src.src6', null], ['src.src7', null], ['src.src9', null]]; + + return false; + } + + protected function cbReagents($cr) + { + if (!$this->int2Bool($cr[1])) + return false; + + if ($cr[1]) + return ['OR', ['reagent1', 0, '>'], ['reagent2', 0, '>'], ['reagent3', 0, '>'], ['reagent4', 0, '>'], ['reagent5', 0, '>'], ['reagent6', 0, '>'], ['reagent7', 0, '>'], ['reagent8', 0, '>']]; + else + return ['AND', ['reagent1', 0], ['reagent2', 0], ['reagent3', 0], ['reagent4', 0], ['reagent5', 0], ['reagent6', 0], ['reagent7', 0], ['reagent8', 0]]; + } } ?> diff --git a/includes/utilities.php b/includes/utilities.php index 35ba033f..f2e4096f 100644 --- a/includes/utilities.php +++ b/includes/utilities.php @@ -96,7 +96,6 @@ class Util public static $tryFilteringString = '$$WH.sprintf(%s, %s, %s) + LANG.dash + LANG.lvnote_tryfiltering.replace(\'\', \'\')'; public static $tryFilteringEntityString = '$$WH.sprintf(LANG.lvnote_entitiesfound, %s, %s, %s) + LANG.dash + LANG.lvnote_tryfiltering.replace(\'\', \'\')'; public static $tryNarrowingString = '$$WH.sprintf(%s, %s, %s) + LANG.dash + LANG.lvnote_trynarrowing'; - public static $setCriteriaString = "fi_setCriteria(%s, %s, %s);\n"; public static $dfnString = '%s'; @@ -496,31 +495,37 @@ class Util } // note: valid integer > 32bit are returned as float - public static function checkNumeric(&$data) + public static function checkNumeric(&$data, $typeCast = NUM_ANY) { if ($data === null) return false; else if (!is_array($data)) { $data = trim($data); + if (preg_match('/^-?\d*,\d+$/', $data)) + $data = strtr($data, ',', '.'); if (is_numeric($data)) { - $data += 0; - return true; - } - else if (preg_match('/^\d*,\d+$/', $data)) - { - $data = floatVal(strtr($data, ',', '.')); + $data += 0; // becomes float or int + + if ((is_float($data) && $typeCast == NUM_REQ_INT) || + (is_int($data) && $typeCast == NUM_REQ_FLOAT)) + return false; + + if (is_float($data) && $typeCast == NUM_CAST_INT) + $data = intval($data); + + if (is_int($data) && $typeCast == NUM_CAST_FLOAT) + $data = floatval($data); + return true; } return false; } - array_walk($data, function(&$item, $key) { - self::checkNumeric($item); - }); + array_walk($data, function(&$x) use($typeCast) { self::checkNumeric($x, $typeCast); }); return false; // always false for passed arrays } diff --git a/index.php b/index.php index 8fb9ca48..abe3e368 100644 --- a/index.php +++ b/index.php @@ -82,7 +82,7 @@ switch ($pageCall) case 'cookie': // lossless cookies and user settings case 'contactus': case 'comment': - // case 'filter': // just a note: this would be accessed from filtrable pages as ?filter=typeStr (with POST-data) and forwards back to page with GET-data .. why? Hell if i know.. + case 'filter': // pre-evaluate filter POST-data; sanitize and forward as GET-data case 'go-to-comment': // find page the comment is on and forward case 'locale': // subdomain-workaround, change the language $cleanName = str_replace(['-', '_'], '', ucFirst($altClass ?: $pageCall)); diff --git a/pages/achievements.php b/pages/achievements.php index 0c92f28b..f1cde8e0 100644 --- a/pages/achievements.php +++ b/pages/achievements.php @@ -42,8 +42,8 @@ class AchievementsPage extends GenericPage public function __construct($pageCall, $pageParam) { - $this->filterObj = new AchievementListFilter(); $this->getCategoryFromUrl($pageParam); + $this->filterObj = new AchievementListFilter(false, $this->category); parent::__construct($pageCall, $pageParam); @@ -63,9 +63,12 @@ class AchievementsPage extends GenericPage $conditions[] = ['category', (int)end($this->category)]; // recreate form selection - $this->filter = $this->filterObj->getForm('form'); + $this->filter = $this->filterObj->getForm(); $this->filter['query'] = isset($_GET['filter']) ? $_GET['filter'] : null; - $this->filter['fi'] = $this->filterObj->getForm(); + $this->filter['initData'] = ['init' => 'achievements']; + + if ($x = $this->filterObj->getSetCriteria()) + $this->filter['initData']['sc'] = $x; if ($fiCnd = $this->filterObj->getConditions()) $conditions[] = $fiCnd; diff --git a/pages/enchantments.php b/pages/enchantments.php index 44a73f75..5f14e89c 100644 --- a/pages/enchantments.php +++ b/pages/enchantments.php @@ -19,8 +19,8 @@ class EnchantmentsPage extends GenericPage public function __construct($pageCall, $pageParam) { - $this->filterObj = new EnchantmentListFilter(); $this->getCategoryFromUrl($pageParam);; + $this->filterObj = new EnchantmentListFilter(false, ['parentCats' => $this->category]); parent::__construct($pageCall, $pageParam); @@ -49,11 +49,14 @@ class EnchantmentsPage extends GenericPage $this->extendGlobalData($ench->getJSGlobals()); // recreate form selection - $this->filter = array_merge($this->filterObj->getForm('form'), $this->filter); - $this->filter['query'] = isset($_GET['filter']) ? $_GET['filter'] : NULL; - $this->filter['fi'] = $this->filterObj->getForm(); + $this->filter = $this->filterObj->getForm(); + $this->filter['query'] = isset($_GET['filter']) ? $_GET['filter'] : NULL; + $this->filter['initData'] = ['init' => 'enchantments']; - $xCols = $this->filterObj->getForm('extraCols', true); + if ($x = $this->filterObj->getSetCriteria()) + $this->filter['initData']['sc'] = $x; + + $xCols = $this->filterObj->getExtraCols(); foreach (Util::$itemFilter as $fiId => $str) if (array_column($tabData['data'], $str)) $xCols[] = $fiId; @@ -62,9 +65,9 @@ class EnchantmentsPage extends GenericPage $xCols[] = 34; if ($xCols) - $this->filter['fi']['extraCols'] = "fi_extraCols = ".Util::toJSON(array_values(array_unique($xCols))).";"; + $this->filter['initData']['ec'] = array_values(array_unique($xCols)); - if (!empty($this->filter['fi']['extraCols'])) + if ($xCols) $tabData['extraCols'] = '$fi_getExtraCols(fi_extraCols, 0, 0)'; if ($ench->getMatches() > CFG_SQL_LIMIT_DEFAULT) diff --git a/pages/icons.php b/pages/icons.php index d6b8e6c2..c20f7580 100644 --- a/pages/icons.php +++ b/pages/icons.php @@ -48,9 +48,12 @@ class IconsPage extends GenericPage $this->extendGlobalData($icons->getJSGlobals()); // recreate form selection - $this->filter = array_merge($this->filterObj->getForm('form'), $this->filter); - $this->filter['query'] = isset($_GET['filter']) ? $_GET['filter'] : null; - $this->filter['fi'] = $this->filterObj->getForm(); + $this->filter = $this->filterObj->getForm(); + $this->filter['query'] = isset($_GET['filter']) ? $_GET['filter'] : null; + $this->filter['initData'] = ['init' => 'icons']; + + if ($x = $this->filterObj->getSetCriteria()) + $this->filter['initData']['sc'] = $x; if ($icons->getMatches() > $sqlLimit) { @@ -66,7 +69,7 @@ class IconsPage extends GenericPage protected function generateTitle() { - $setCrt = $this->filterObj->getForm('setCriteria', true); + $setCrt = $this->filterObj->getSetCriteria(); $title = $this->name; if (isset($setCrt['cr']) && count($setCrt['cr']) == 1) { @@ -98,7 +101,7 @@ class IconsPage extends GenericPage protected function generatePath() { - $setCrt = $this->filterObj->getForm('setCriteria', true); + $setCrt = $this->filterObj->getSetCriteria(); if (isset($setCrt['cr']) && count($setCrt['cr']) == 1) $this->path[] = $setCrt['cr'][0]; } diff --git a/pages/items.php b/pages/items.php index dfb5a494..6e96fc87 100644 --- a/pages/items.php +++ b/pages/items.php @@ -81,13 +81,14 @@ class ItemsPage extends GenericPage public function __construct($pageCall, $pageParam) { - $this->filterObj = new ItemListFilter(); $this->getCategoryFromUrl($pageParam); + $this->filterObj = new ItemListFilter(false, ['parentCats' => $this->category]); + parent::__construct($pageCall, $pageParam); $this->name = Util::ucFirst(Lang::game('items')); - $this->subCat = $pageParam !== NULL ? '='.$pageParam : ''; + $this->subCat = $pageParam !== null ? '='.$pageParam : ''; } protected function generateContent() @@ -107,9 +108,19 @@ class ItemsPage extends GenericPage if ($_ = $this->filterObj->getConditions()) $conditions[] = $_; - $this->filter = array_merge($this->filterObj->getForm('form'), $this->filter); - $this->filter['query'] = isset($_GET['filter']) ? $_GET['filter'] : null; - $this->filter['fi'] = $this->filterObj->getForm(); + $this->filter = $this->filterObj->getForm(); + $this->filter['query'] = isset($_GET['filter']) ? $_GET['filter'] : null; + $this->filter['initData'] = ['init' => 'items']; + + if ($x = $this->filterObj->getSetCriteria()) + $this->filter['initData']['sc'] = $x; + + $xCols = $this->filterObj->getExtraCols(); + if ($xCols) + $this->filter['initData']['ec'] = $xCols; + + if ($x = $this->filterObj->getSetWeights()) + $this->filter['initData']['sw'] = $x; $menu = $this->createExtraMenus(); foreach ($menu['type'][0] as $k => $str) @@ -123,13 +134,12 @@ class ItemsPage extends GenericPage if (isset($this->filter['slot'][INVTYPE_SHIELD])) // "Off Hand" => "Shield" $this->filter['slot'][INVTYPE_SHIELD] = Lang::item('armorSubClass', 6); - $xCols = $this->filterObj->getForm('extraCols', true); $infoMask = ITEMINFO_JSON; if (array_intersect([63, 64, 125], $xCols)) // 63:buyPrice; 64:sellPrice; 125:reqarenartng $infoMask |= ITEMINFO_VENDOR; - if (!empty($this->filter['fi']['extraCols'])) + if ($xCols) $this->sharedLV['extraCols'] = '$fi_getExtraCols(fi_extraCols, '.(isset($this->filter['gm']) ? $this->filter['gm'] : 0).', '.(array_intersect([63], $xCols) ? 1 : 0).')'; if ($this->filterObj->error) @@ -157,7 +167,7 @@ class ItemsPage extends GenericPage /*************************/ $upgItemData = []; - if (!empty($this->filter['upg']) && !empty($this->filter['fi']['setWeights'])) + if (!empty($this->filter['upg']) && !empty($this->filterObj->getSetWeights())) { $upgItems = new ItemList(array(['id', array_keys($this->filter['upg'])]), ['extraOpts' => $this->filterObj->extraOpts]); if (!$upgItems->error) @@ -352,7 +362,7 @@ class ItemsPage extends GenericPage $tabData['tabs'] = '$tabsGroups'; } - if (!empty($this->filter['fi']['setWeights'])) + if (!empty($this->filterObj->getSetWeights())) if ($items->hasSetFields(['armor'])) $tabData['visibleCols'][] = 'armor'; @@ -449,7 +459,7 @@ class ItemsPage extends GenericPage $this->path[] = $c; // if slot-dropdown is available && Armor && $path points to Armor-Class - $form = $this->filterObj->getForm('form'); + $form = $this->filterObj->getForm(); if (count($this->path) == 4 && $this->category[0] == 4 && isset($form['sl']) && !is_array($form['sl'])) $this->path[] = $form['sl']; else if (!empty($this->category[0]) && $this->category[0] == 0 && isset($form['ty']) && !is_array($form['ty'])) @@ -461,7 +471,7 @@ class ItemsPage extends GenericPage { $gemScores = []; - if (empty($this->filter['fi']['setWeights'])) + if (empty($this->filterObj->getSetWeights())) return []; if (!empty($this->filter['gm'])) diff --git a/pages/itemsets.php b/pages/itemsets.php index e67e6c07..80d9553a 100644 --- a/pages/itemsets.php +++ b/pages/itemsets.php @@ -19,8 +19,8 @@ class ItemsetsPage extends GenericPage public function __construct($pageCall, $pageParam) { - $this->filterObj = new ItemsetListFilter(); $this->getCategoryFromUrl($pageParam); + $this->filterObj = new ItemsetListFilter(false, ['parentCats' => $this->category]); parent::__construct($pageCall, $pageParam); @@ -29,6 +29,8 @@ class ItemsetsPage extends GenericPage protected function generateContent() { + $this->addJS('?data=weight-presets&locale='.User::$localeId.'&t='.$_SESSION['dataKey']); + $conditions = []; if (!User::isInGroup(U_GROUP_EMPLOYEE)) @@ -41,15 +43,20 @@ class ItemsetsPage extends GenericPage $this->extendGlobalData($itemsets->getJSGlobals()); // recreate form selection - $this->filter = array_merge($this->filterObj->getForm('form'), $this->filter); - $this->filter['query'] = isset($_GET['filter']) ? $_GET['filter'] : NULL; - $this->filter['fi'] = $this->filterObj->getForm(); + $this->filter = $this->filterObj->getForm(); + $this->filter['query'] = isset($_GET['filter']) ? $_GET['filter'] : NULL; + $this->filter['initData'] = ['init' => 'itemsets']; - $this->addJS('?data=weight-presets&locale='.User::$localeId.'&t='.$_SESSION['dataKey']); + if ($x = $this->filterObj->getSetCriteria()) + $this->filter['initData']['sc'] = $x; + + $xCols = $this->filterObj->getExtraCols(); + if ($xCols) + $this->filter['initData']['ec'] = $xCols; $tabData = ['data' => array_values($itemsets->getListviewData())]; - if (!empty($this->filter['fi']['extraCols'])) + if ($xCols) $tabData['extraCols'] = '$fi_getExtraCols(fi_extraCols, 0, 0)'; // create note if search limit was exceeded diff --git a/pages/npcs.php b/pages/npcs.php index 8e341346..3bbbb998 100644 --- a/pages/npcs.php +++ b/pages/npcs.php @@ -20,8 +20,8 @@ class NpcsPage extends GenericPage public function __construct($pageCall, $pageParam) { - $this->filterObj = new CreatureListFilter(); $this->getCategoryFromUrl($pageParam);; + $this->filterObj = new CreatureListFilter(false, ['parentCats' => $this->category]); parent::__construct($pageCall, $pageParam); @@ -53,19 +53,26 @@ class NpcsPage extends GenericPage $npcs = new CreatureList($conditions, ['extraOpts' => $this->filterObj->extraOpts]); // recreate form selection - $this->filter = array_merge($this->filterObj->getForm('form'), $this->filter); - $this->filter['query'] = isset($_GET['filter']) ? $_GET['filter'] : NULL; - $this->filter['fi'] = $this->filterObj->getForm(); + $this->filter = $this->filterObj->getForm(); + $this->filter['query'] = isset($_GET['filter']) ? $_GET['filter'] : null; + $this->filter['initData'] = ['init' => 'npcs']; - $repCols = $this->filterObj->getForm('reputationCols'); + $rCols = $this->filterObj->getReputationCols(); + $xCols = $this->filterObj->getExtraCols(); + if ($rCols) + $this->filter['initData']['rc'] = $rCols; - $tabData = array( - 'data' => array_values($npcs->getListviewData($repCols ? NPCINFO_REP : 0x0)), - ); + if ($xCols) + $this->filter['initData']['ec'] = $xCols; - if ($repCols) - $tabData['extraCols'] = '$fi_getReputationCols('.Util::toJSON($repCols).')'; - else if (!empty($this->filter['fi']['extraCols'])) + if ($x = $this->filterObj->getSetCriteria()) + $this->filter['initData']['sc'] = $x; + + $tabData = ['data' => array_values($npcs->getListviewData($rCols ? NPCINFO_REP : 0x0))]; + + if ($rCols) // never use pretty-print + $tabData['extraCols'] = '$fi_getReputationCols('.Util::toJSON($rCols, JSON_NUMERIC_CHECK | JSON_UNESCAPED_UNICODE).')'; + else if ($xCols) $tabData['extraCols'] = '$fi_getExtraCols(fi_extraCols, 0, 0)'; if ($this->category) diff --git a/pages/objects.php b/pages/objects.php index a8f7eac3..27717b14 100644 --- a/pages/objects.php +++ b/pages/objects.php @@ -20,8 +20,8 @@ class ObjectsPage extends GenericPage public function __construct($pageCall, $pageParam) { - $this->filterObj = new GameObjectListFilter(); $this->getCategoryFromUrl($pageParam);; + $this->filterObj = new GameObjectListFilter(false, ['parentCats' => $this->category]); parent::__construct($pageCall, $pageParam); @@ -42,9 +42,12 @@ class ObjectsPage extends GenericPage $conditions[] = ['typeCat', (int)$this->category[0]]; // recreate form selection - $this->filter = $this->filterObj->getForm('form'); - $this->filter['query'] = isset($_GET['filter']) ? $_GET['filter'] : null; - $this->filter['fi'] = $this->filterObj->getForm(); + $this->filter = $this->filterObj->getForm(); + $this->filter['query'] = isset($_GET['filter']) ? $_GET['filter'] : null; + $this->filter['initData'] = ['init' => 'objects']; + + if ($x = $this->filterObj->getSetCriteria()) + $this->filter['initData']['sc'] = $x; if ($_ = $this->filterObj->getConditions()) $conditions[] = $_; diff --git a/pages/quests.php b/pages/quests.php index 424dd1a9..478c1072 100644 --- a/pages/quests.php +++ b/pages/quests.php @@ -21,8 +21,8 @@ class QuestsPage extends GenericPage { $this->validCats = Game::$questClasses; // needs reviewing (not allowed to set this as default) - $this->filterObj = new QuestListFilter(); $this->getCategoryFromUrl($pageParam); + $this->filterObj = new QuestListFilter(false, ['parentCats' => $this->category]); parent::__construct($pageCall, $pageParam); @@ -50,15 +50,26 @@ class QuestsPage extends GenericPage $this->extendGlobalData($quests->getJSGlobals()); // recreate form selection - $this->filter = array_merge($this->filterObj->getForm('form'), $this->filter); - $this->filter['query'] = isset($_GET['filter']) ? $_GET['filter'] : NULL; - $this->filter['fi'] = $this->filterObj->getForm(); + $this->filter = $this->filterObj->getForm(); + $this->filter['query'] = isset($_GET['filter']) ? $_GET['filter'] : null; + $this->filter['initData'] = ['init' => 'quests']; + + $rCols = $this->filterObj->getReputationCols(); + $xCols = $this->filterObj->getExtraCols(); + if ($rCols) + $this->filter['initData']['rc'] = $rCols; + + if ($xCols) + $this->filter['initData']['ec'] = $xCols; + + if ($x = $this->filterObj->getSetCriteria()) + $this->filter['initData']['sc'] = $x; $tabData = ['data' => array_values($quests->getListviewData())]; - if ($_ = $this->filterObj->getForm('reputationCols')) - $tabData['extraCols'] = '$fi_getReputationCols('.json_encode($_, JSON_NUMERIC_CHECK).')'; - else if (!empty($this->filter['fi']['extraCols'])) + if ($rCols) + $tabData['extraCols'] = '$fi_getReputationCols('.json_encode($rCols, JSON_NUMERIC_CHECK | JSON_UNESCAPED_UNICODE).')'; + else if ($xCols) $tabData['extraCols'] = '$fi_getExtraCols(fi_extraCols, 0, 0)'; // create note if search limit was exceeded diff --git a/pages/screenshot.php b/pages/screenshot.php index 2d074e74..f5a6388f 100644 --- a/pages/screenshot.php +++ b/pages/screenshot.php @@ -198,7 +198,7 @@ class ScreenshotPage extends GenericPage if (count($dims) != 4) return 3; - Util::checkNumeric($dims); + Util::checkNumeric($dims, NUM_REQ_INT); // actually crop the image $srcImg = imagecreatefromjpeg($fullPath); diff --git a/pages/search.php b/pages/search.php index 6bf38065..24ea3bd1 100644 --- a/pages/search.php +++ b/pages/search.php @@ -40,7 +40,6 @@ class SearchPage extends GenericPage protected $search = ''; // output protected $invalid = []; - private $statWeight = ['wt' => null, 'wtv' => null]; private $maxResults = CFG_SQL_LIMIT_SEARCH; private $searchMask = 0x0; private $query = ''; // lookup @@ -64,15 +63,6 @@ class SearchPage extends GenericPage if ($this->reqUGroup && !User::isInGroup($this->reqUGroup)) $this->error(); - // statWeight for JSON-search - if (isset($_GET['wt']) && isset($_GET['wtv'])) - { - $this->statWeight = array( - 'wt' => explode(':', $_GET['wt']), - 'wtv' => explode(':', $_GET['wtv']) - ); - } - // select search mode if (isset($_GET['json'])) { @@ -577,8 +567,12 @@ class SearchPage extends GenericPage if ($_ = array_filter($slots)) $cnd[] = ['slot', $_]; + // trick ItemListFilter into evaluating weights + if (isset($_GET['wt']) && isset($_GET['wtv'])) + $_GET['filter'] = 'wt='.$_GET['wt'].';wtv='.$_GET['wtv']; + $itemFilter = new ItemListFilter(); - if ($_ = $itemFilter->createConditionsForWeights($this->statWeight)) + if ($_ = $itemFilter->createConditionsForWeights()) { $miscData['extraOpts'] = $itemFilter->extraOpts; $cnd = array_merge($cnd, [$_]); diff --git a/pages/sounds.php b/pages/sounds.php index 17ff3e37..b778c329 100644 --- a/pages/sounds.php +++ b/pages/sounds.php @@ -39,9 +39,8 @@ class SoundsPage extends GenericPage if ($_ = $this->filterObj->getConditions()) $conditions[] = $_; - $this->filter = array_merge($this->filterObj->getForm('form'), $this->filter); + $this->filter = $this->filterObj->getForm(); $this->filter['query'] = isset($_GET['filter']) ? $_GET['filter'] : null; - $this->filter['fi'] = $this->filterObj->getForm(); $sounds = new SoundList($conditions); $tabData = []; @@ -66,15 +65,15 @@ class SoundsPage extends GenericPage protected function generateTitle() { - $form = $this->filterObj->getForm('form'); - if (isset($form['ty']) && !is_array($form['ty'])) - array_unshift($this->title, Lang::sound('cat', $form['ty'])); + $form = $this->filterObj->getForm(); + if (isset($form['ty']) && count($form['ty']) == 1) + array_unshift($this->title, Lang::sound('cat', $form['ty'][0])); } protected function generatePath() { - $form = $this->filterObj->getForm('form'); - if (isset($form['ty']) && !is_array($form['ty'])) + $form = $this->filterObj->getForm(); + if (isset($form['ty']) && count($form['ty']) == 1) $this->path[] = $form['ty']; } } diff --git a/pages/spell.php b/pages/spell.php index 268bce11..6cf8176e 100644 --- a/pages/spell.php +++ b/pages/spell.php @@ -630,11 +630,8 @@ class SpellPage extends GenericPage $lv[$bar]['condition'][0][$this->typeId][] = [[CND_SPELL, $extraItem['requiredSpecialization']]]; $this->extendGlobalIds(TYPE_SPELL, $extraItem['requiredSpecialization']); $extraCols[] = '$Listview.extraCols.condition'; - if ($max = $extraItem['additionalMaxNum']) - { - $lv[$bar]['mincount'] = 1; - $lv[$bar]['maxcount'] = $max; - } + if ($max = ($extraItem['additionalMaxNum'] - 1)) + $lv[$bar]['stack'] = [1, $max]; break; // skill_extra_item_template can only contain 1 item } diff --git a/pages/spells.php b/pages/spells.php index 33a25b55..9c080d34 100644 --- a/pages/spells.php +++ b/pages/spells.php @@ -85,14 +85,16 @@ class SpellsPage extends GenericPage public function __construct($pageCall, $pageParam) { - $this->filterObj = new SpellListFilter(); $this->getCategoryFromUrl($pageParam);; + $this->filterObj = new SpellListFilter(false, ['parentCats' => $this->category]); parent::__construct($pageCall, $pageParam); $this->name = Util::ucFirst(Lang::game('spells')); $this->subCat = $pageParam !== null ? '='.$pageParam : ''; - $this->filter = ['classPanel' => false, 'glyphPanel' => false]; + + $this->classPanel = false; + $this->glyphPanel = false; } protected function generateContent() @@ -108,7 +110,7 @@ class SpellsPage extends GenericPage switch ($this->category[0]) { case -2: // Character Talents - $this->filter['classPanel'] = true; + $this->classPanel = true; array_push($visibleCols, 'singleclass', 'level', 'schools', 'tier'); @@ -218,8 +220,8 @@ class SpellsPage extends GenericPage break; case -13: // Glyphs - $this->filter['classPanel'] = true; - $this->filter['glyphPanel'] = true; + $this->classPanel = true; + $this->glyphPanel = true; array_push($visibleCols, 'singleclass', 'glyphtype'); @@ -230,7 +232,7 @@ class SpellsPage extends GenericPage break; case 7: // Abilities - $this->filter['classPanel'] = true; + $this->classPanel = true; array_push($visibleCols, 'level', 'singleclass', 'schools'); @@ -366,12 +368,24 @@ class SpellsPage extends GenericPage $tabData['data'] = array_values($spells->getListviewData()); // recreate form selection - $this->filter = array_merge($this->filterObj->getForm('form'), $this->filter); - $this->filter['query'] = isset($_GET['filter']) ? $_GET['filter'] : NULL; - $this->filter['fi'] = $this->filterObj->getForm(); + $this->filter = $this->filterObj->getForm(); + $this->filter['query'] = isset($_GET['filter']) ? $_GET['filter'] : NULL; + $this->filter['initData'] = ['init' => 'spells']; - if (!empty($this->filter['fi']['extraCols'])) + if ($ec = $this->filterObj->getExtraCols()) + { + $this->filter['initData']['ec'] = $ec; $tabData['extraCols'] = '$fi_getExtraCols(fi_extraCols, 0, 0)'; + } + + if ($sc = $this->filterObj->getSetCriteria()) + { + $this->filter['initData']['sc'] = $sc; + + // add source to cols if explicitly searching for it + if (in_array(9, $sc['cr']) && !in_array('source', $visibleCols)) + $visibleCols[] = 'source'; + } // create note if search limit was exceeded; overwriting 'note' is intentional if ($spells->getMatches() > CFG_SQL_LIMIT_DEFAULT) @@ -383,10 +397,6 @@ class SpellsPage extends GenericPage if ($this->filterObj->error) $tabData['_errors'] = 1; - // add source to cols if explicitly searching for it - if ($_ = $this->filterObj->getForm('setCriteria', true)) - if (in_array(9, $_['cr']) && !in_array('source', $visibleCols)) - $visibleCols[] = 'source'; $mask = $spells->hasSetFields(['reagent1', 'skillLines', 'trainingCost', 'reqClassMask']); if ($mask & 0x1) @@ -444,8 +454,8 @@ class SpellsPage extends GenericPage foreach ($this->category as $c) $this->path[] = $c; - $form = $this->filterObj->getForm('form'); - if (count($this->path) == 4 && $this->category[0] == -13 && isset($form['gl']) && !is_array($form['gl'])) + $form = $this->filterObj->getForm(); + if (count($this->path) == 4 && $this->category[0] == -13 && isset($form['gl']) && count($form['gl']) == 1) $this->path[] = $form['gl']; } } diff --git a/template/bricks/filter.tpl.php b/template/bricks/filter.tpl.php new file mode 100644 index 00000000..042370ec --- /dev/null +++ b/template/bricks/filter.tpl.php @@ -0,0 +1,14 @@ + diff --git a/template/pages/achievements.tpl.php b/template/pages/achievements.tpl.php index 32fa30d5..f6011b16 100644 --- a/template/pages/achievements.tpl.php +++ b/template/pages/achievements.tpl.php @@ -14,7 +14,7 @@ $this->brick('pageTemplate', ['fi' => empty($f['query']) ? null : ['query' => $f ?>
-
+ @@ -63,14 +63,7 @@ endforeach;
- +brick('filter', ['fi' => $f['initData']]); ?> brick('lvTabs'); ?> diff --git a/template/pages/enchantments.tpl.php b/template/pages/enchantments.tpl.php index 2621a9cf..bd652622 100644 --- a/template/pages/enchantments.tpl.php +++ b/template/pages/enchantments.tpl.php @@ -13,11 +13,11 @@ $this->brick('announcement'); $this->brick('pageTemplate', ['fi' => empty($f['query']) ? null : ['query' => $f['query'], 'menuItem' => 101]]); ?> -
- +
+
-
- +
+
- +
- +
 /> />
-
+
- /> /> + /> />
- - + +
- +brick('filter', ['fi' => $f['initData']]); ?> brick('lvTabs'); ?> diff --git a/template/pages/icons.tpl.php b/template/pages/icons.tpl.php index 1d38e0ea..3d077a7d 100644 --- a/template/pages/icons.tpl.php +++ b/template/pages/icons.tpl.php @@ -13,44 +13,37 @@ $this->brick('announcement'); $this->brick('pageTemplate', ['fi' => empty($f['query']) ? null : ['query' => $f['query'], 'menuItem' => 101]]); ?> -
-
+
+ - +
- +
 /> />
-
+
- /> /> + /> />
- - + +
- +brick('filter', ['fi' => $f['initData']]); ?> brick('lvTabs'); ?> diff --git a/template/pages/items.tpl.php b/template/pages/items.tpl.php index 0548a857..77e16270 100644 --- a/template/pages/items.tpl.php +++ b/template/pages/items.tpl.php @@ -13,11 +13,11 @@ $this->brick('announcement'); $this->brick('pageTemplate', ['fi' => empty($f['query']) ? null : ['query' => $f['query'], 'menuItem' => 0]]); ?> -
-
+
+
-
- +
+
+ + /> + +  /> - -  /> - /> + +  /> - /> - - + +
    /> - />    /> - />
- +  />/> +
+ />/>
-

+

@@ -172,11 +172,11 @@ endforeach;
- - + +
- /> + />
@@ -184,14 +184,7 @@ endforeach;
- +brick('filter', ['fi' => $f['initData']]); ?> brick('lvTabs'); ?> diff --git a/template/pages/itemsets.tpl.php b/template/pages/itemsets.tpl.php index 57e46876..67cb2242 100644 --- a/template/pages/itemsets.tpl.php +++ b/template/pages/itemsets.tpl.php @@ -13,11 +13,11 @@ $this->brick('announcement'); $this->brick('pageTemplate', ['fi' => empty($f['query']) ? null : ['query' => $f['query'], 'menuItem' => 2]]); ?> -
- +
+
-
- +
+
- -  /> + +  /> - -  /> - /> + +  /> - /> - - + +
    /> - />    /> - />
- +   -
+
-
- /> /> +
+ /> />
- - + +
- +brick('filter', ['fi' => $f['initData']]); ?> brick('lvTabs'); ?> diff --git a/template/pages/npcs.tpl.php b/template/pages/npcs.tpl.php index bc4d632d..7f153b5e 100644 --- a/template/pages/npcs.tpl.php +++ b/template/pages/npcs.tpl.php @@ -13,11 +13,11 @@ $this->brick('announcement'); $this->brick('pageTemplate', ['fi' => empty($f['query']) ? null : ['query' => $f['query'], 'menuItem' => 4]]); ?> -
-
+
+
-
- +
+
- + - - + +
- - - + + +
 />  /> />  />
 /> - /> /> - /> - - +
         - > - - - + + +
@@ -78,32 +78,25 @@ endforeach;
-
+
-
- /> /> +
+ /> />
- - + +
- +brick('filter', ['fi' => $f['initData']]); ?> brick('lvTabs'); ?> diff --git a/template/pages/objects.tpl.php b/template/pages/objects.tpl.php index ce5b2a45..9e042468 100644 --- a/template/pages/objects.tpl.php +++ b/template/pages/objects.tpl.php @@ -13,38 +13,31 @@ $this->brick('announcement'); $this->brick('pageTemplate', ['fi' => empty($f['query']) ? null : ['query' => $f['query'], 'menuItem' => 5]]); ?> -
-
+
+ - +
 />
 />
-
+
-
- /> /> +
+ /> />
- - + +
- +brick('filter', ['fi' => $f['initData']]); ?> brick('lvTabs'); ?> diff --git a/template/pages/profiles.tpl.php b/template/pages/profiles.tpl.php index 0b716ebf..039fd170 100644 --- a/template/pages/profiles.tpl.php +++ b/template/pages/profiles.tpl.php @@ -15,7 +15,7 @@ $this->brick('pageTemplate'); # for some arcane reason a newline (\n) means, the first childNode is a text instead of the form for the following div ?>
+ action="?filter=profiles" method="post" name="fi" onsubmit="return fi_submit(this)" onreset="return fi_reset(this)">
diff --git a/template/pages/quests.tpl.php b/template/pages/quests.tpl.php index a4ee2f10..803ed842 100644 --- a/template/pages/quests.tpl.php +++ b/template/pages/quests.tpl.php @@ -13,10 +13,10 @@ $this->brick('announcement'); $this->brick('pageTemplate', ['fi' => empty($f['query']) ? null : ['query' => $f['query'], 'menuItem' => 3]]); ?> -
- +
+
-
+
/> -   /> - +  /> +   /> + - -  /> - /> + +  /> - /> - - + +
    /> - />    /> - />
- +  /> /> +
+ /> />
- - + +
- +brick('filter', ['fi' => $f['initData']]); ?> brick('lvTabs'); ?> diff --git a/template/pages/sounds.tpl.php b/template/pages/sounds.tpl.php index 32103ff6..cbbf5376 100644 --- a/template/pages/sounds.tpl.php +++ b/template/pages/sounds.tpl.php @@ -13,11 +13,11 @@ $this->brick('announcement'); $this->brick('pageTemplate', ['fi' => empty($f['query']) ? null : ['query' => $f['query'], 'menuItem' => 101]]); ?>

name; ?> brick('redButtons'); ?>

-
-
+
+
-
- +
+
/> +  /> @@ -43,8 +43,8 @@ endforeach;
- - + +
diff --git a/template/pages/spells.tpl.php b/template/pages/spells.tpl.php index df671fd9..21a990dc 100644 --- a/template/pages/spells.tpl.php +++ b/template/pages/spells.tpl.php @@ -13,11 +13,11 @@ $this->brick('announcement'); $this->brick('pageTemplate', ['fi' => empty($f['query']) ? null : ['query' => $f['query'], 'menuItem' => 1]]); ?> -
-
+
+
-
- +
+
- +classPanel): ?>
-
- +
+
- + - - + + - + - +
- - - + + +
 />  /> />  />
 /> - /> /> - /> - - + +
    /> - />    /> - />
 
  - +
        
-
+
-
- /> /> +
+ /> />
- - + +
- +brick('filter', ['fi' => $f['initData']]); ?> brick('lvTabs'); ?>