diff --git a/.gitignore b/.gitignore index 17e15022..5faf86ee 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,6 @@ -# smarty cache -/cache/*.tmp -/cache/*.php +# cache +/cache/template/* +/cache/alphaMaps/*.png # generated files /static/js/profile_all.js diff --git a/cache/data/index.html b/cache/data/index.html deleted file mode 100644 index fa6d84e8..00000000 --- a/cache/data/index.html +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/cache/images/index.html b/cache/images/index.html deleted file mode 100644 index fa6d84e8..00000000 --- a/cache/images/index.html +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/includes/defines.php b/includes/defines.php index 3b87eba1..ecf37a72 100644 --- a/includes/defines.php +++ b/includes/defines.php @@ -25,12 +25,11 @@ define('TYPE_RACE', 14); define('TYPE_SKILL', 15); define('TYPE_CURRENCY', 17); -define('CACHETYPE_PAGE', 0); -define('CACHETYPE_TOOLTIP', 1); -define('CACHETYPE_BUFF', 2); // only used by spells obviously +define('CACHETYPE_NONE', 0); // page will not be cached +define('CACHETYPE_PAGE', 1); +define('CACHETYPE_TOOLTIP', 2); define('CACHETYPE_SEARCH', 3); define('CACHETYPE_XML', 4); // only used by items -define('CACHETYPE_NONE', 5); // page will not be cached define('SEARCH_TYPE_REGULAR', 0x10000000); define('SEARCH_TYPE_OPEN', 0x20000000); diff --git a/includes/genericPage.class.php b/includes/genericPage.class.php index 6a2bb92b..a8c5a948 100644 --- a/includes/genericPage.class.php +++ b/includes/genericPage.class.php @@ -7,13 +7,14 @@ if (!defined('AOWOW_REVISION')) trait DetailPage { protected $hasComContent = true; + protected $category = null; // not used on detail pages - function generateCacheKey($cacheType, $params = '-1') + private $subject = null; // so it will not get cached + + function generateCacheKey() { - $key = [$cacheType, $this->type, '-1', $params]; - - if ($this->isLocalized) - $key[] = User::$localeId; + // mode, type, typeId, localeId, category, filter + $key = [$this->mode, $this->type, $this->typeId, User::$localeId, '-1', '-1']; return implode('_', $key); } @@ -23,15 +24,19 @@ trait DetailPage trait ListPage { protected $category = null; - protected $validCats = []; protected $typeId = 0; + protected $filter = []; - function generateCacheKey($cacheType) + function generateCacheKey() { - $key = [$cacheType, $this->type, $this->typeId, '-1']; + // mode, type, typeId, localeId, + $key = [$this->mode, $this->type, '-1', User::$localeId]; - if ($this->isLocalized) - $key[] = User::$localeId; + //category + $key[] = $this->category ? implode('.', $this->category) : '-1'; + + // filter + $key[] = $this->filter ? md5(serialize($this->filter)) : '-1'; return implode('_', $key); } @@ -42,24 +47,23 @@ class GenericPage { protected $tpl = ''; protected $restrictedGroups = U_GROUP_NONE; + protected $mode = CACHETYPE_NONE; protected $jsGlobals = []; - protected $jsgBuffer = []; - protected $isCachable = true; - protected $isLocalized = false; - protected $hasCacheFile = false; protected $lvData = []; protected $title = [CFG_NAME]; // for title-Element protected $name = ''; // for h1-Element protected $tabId = 0; - protected $community = ['co' => [], 'sc' => [], 'vi' => []]; - protected $js = []; protected $css = []; + // private vars don't get cached + private $cacheDir = 'cache/template/'; + private $jsgBuffer = []; private $gLocale = []; - protected $gPageInfo = []; + private $gPageInfo = []; private $gUser = []; + private $community = ['co' => [], 'sc' => [], 'vi' => []]; protected function generatePath() {} protected function generateTitle() {} @@ -76,20 +80,25 @@ class GenericPage else if (CFG_MAINTENANCE && User::isInGroup(U_GROUP_EMPLOYEE)) Util::addNote(U_GROUP_EMPLOYEE, 'Maintenance mode enabled!'); - if (isset($this->validCats) && !Util::isValidPage($this->validCats, $this->category)) - $this->error(); + // display modes + if (isset($_GET['power']) && method_exists($this, 'generateTooltip')) + $this->mode = CACHETYPE_TOOLTIP; + else if (isset($_GET['xml']) && method_exists($this, 'generateXML')) + $this->mode = CACHETYPE_XML; + else + { + if (!$this->isValidPage() || !$this->tpl) + $this->error(); - $this->gUser = User::getUserGlobals(); - $this->gLocale = array( - 'id' => User::$localeId, - 'name' => User::$localeString - ); - - if (!$this->tpl) - return; + $this->gUser = User::getUserGlobals(); + $this->gLocale = array( + 'id' => User::$localeId, + 'name' => User::$localeString + ); + } } - private function prepare() + private function prepareContent() { if (!$this->loadCache()) { @@ -113,15 +122,47 @@ class GenericPage public function display($override = '') { - if (!$override) - $this->prepare(); + if ($override) + { + $this->addAnnouncements(); - if (!$override && !$this->isSaneInclude('template/pages/', $this->tpl)) - die(User::isInGroup(U_GROUP_STAFF) ? 'Error: nonexistant template requestst: template/pages/'.$this->tpl.'.tpl.php' : null); + include('template/pages/'.$override.'.tpl.php'); + die(); + } + else if ($this->mode == CACHETYPE_TOOLTIP) + { + if (!$this->loadCache($tt)) + { + $tt = $this->generateTooltip(); + $this->saveCache($tt); + } - $this->addAnnouncements(); + header('Content-type: application/x-javascript; charsetUTF-8'); + die($tt); + } + else if ($this->mode == CACHETYPE_XML) + { + if (!$this->loadCache($xml)) + { + $xml = $this->generateXML(); + $this->saveCache($xml); + } - include('template/pages/'.($override ? $override : $this->tpl).'.tpl.php'); + header('Content-type: text/xml; charsetUTF-8'); + die($xml); + } + else + { + $this->prepareContent(); + + if (!$this->isSaneInclude('template/pages/', $this->tpl)) + die(User::isInGroup(U_GROUP_STAFF) ? 'Error: nonexistant template requested: template/pages/'.$this->tpl.'.tpl.php' : null); + + $this->addAnnouncements(); + + include('template/pages/'.$this->tpl.'.tpl.php'); + die(); + } } public function gBrick($file, array $localVars = []) @@ -130,7 +171,7 @@ class GenericPage $$n = $v; if (!$this->isSaneInclude('template/globals/', $file)) - echo !User::isInGroup(U_GROUP_STAFF) ? "\n\nError: nonexistant template requestst: template/globals/".$file.".tpl.php\n\n" : null; + echo !User::isInGroup(U_GROUP_STAFF) ? "\n\nError: nonexistant template requested: template/globals/".$file.".tpl.php\n\n" : null; else include('template/globals/'.$file.'.tpl.php'); } @@ -141,7 +182,7 @@ class GenericPage $$n = $v; if (!$this->isSaneInclude('template/bricks/', $file)) - echo User::isInGroup(U_GROUP_STAFF) ? "\n\nError: nonexistant template requestst: template/bricks/".$file.".tpl.php\n\n" : null; + echo User::isInGroup(U_GROUP_STAFF) ? "\n\nError: nonexistant template requested: template/bricks/".$file.".tpl.php\n\n" : null; else include('template/bricks/'.$file.'.tpl.php'); } @@ -152,7 +193,7 @@ class GenericPage $$n = $v; if (!$this->isSaneInclude('template/listviews/', $file)) - echo User::isInGroup(U_GROUP_STAFF) ? "\n\nError: nonexistant template requestst: template/listviews/".$file.".tpl.php\n\n" : null; + echo User::isInGroup(U_GROUP_STAFF) ? "\n\nError: nonexistant template requested: template/listviews/".$file.".tpl.php\n\n" : null; else include('template/listviews/'.$file.'.tpl.php'); } @@ -168,6 +209,33 @@ class GenericPage return true; } + private function isValidPage() + { + if ($this->category === null || empty($this->validCats)) + return true; + + switch (count($this->category)) + { + case 0: // no params works always + return true; + case 1: // null is avalid || value in a 1-dim-array || key in a n-dim-array + return $this->category[0] === null || in_array($this->category[0], $this->validCats) || (isset($this->validCats[$this->category[0]]) && is_array($this->validCats[$this->category[0]])); + case 2: // first param has to be a key. otherwise invalid + if (!isset($this->validCats[$this->category[0]])) + return false; + + // check if the sub-array is n-imensional + if (count($this->validCats[$this->category[0]]) == count($this->validCats[$this->category[0]], COUNT_RECURSIVE)) + return in_array($this->category[1], $this->validCats[$this->category[0]]); // second param is value in second level array + else + return isset($this->validCats[$this->category[0]][$this->category[1]]); // check if params is key of another array + case 3: // 3 params MUST point to a specific value + return isset($this->validCats[$this->category[0]][$this->category[1]]) && in_array($this->category[2], $this->validCats[$this->category[0]][$this->category[1]]); + } + + return false; + } + public function addJS($name, $unshift = false) { if (is_array($name)) @@ -395,10 +463,18 @@ class GenericPage public function notFound($subject) { - $this->subject = $subject; - $this->mysql = DB::Aowow()->getStatistics(); + if ($this->mode == CACHETYPE_TOOLTIP) + echo $this->generateTooltip(true); + else if ($this->mode == CACHETYPE_XML) + echo $this->generateXML(true); + else + { + $this->subject = $subject; + $this->mysql = DB::Aowow()->getStatistics(); + + $this->display('text-page-generic'); + } - $this->display('text-page-generic'); exit(); } @@ -424,44 +500,76 @@ class GenericPage } // creates the cache file - public function saveCache(/*$key, $data, $filter = null*/) + public function saveCache($saveString = null) { - // if (CFG_DEBUG) + if ($this->mode == CACHETYPE_NONE) + return false; + + if (CFG_DEBUG) return; - $file = $this->cache_dir.'data/'.$key; + $file = CWD.$this->cacheDir.$this->generateCacheKey(); + $data = time()." ".AOWOW_REVISION." ".($saveString ? '1' : '0')."\n"; + if (!$saveString) + { + $cache = []; + foreach ($this as $key => $val) + { + try + { + // public, protected and an undocumented flag added to properties created on the fly..? + if ((new ReflectionProperty($this, $key))->getModifiers() & 0x1300) + $cache[$key] = $val; + } + catch (ReflectionException $e) { } // shut up! + } - $cacheData = time()." ".AOWOW_REVISION."\n"; - $cacheData .= serialize(str_replace(["\n", "\t"], ['\n', '\t'], $data)); + $data .= serialize($cache); + } + else + $data .= (string)$saveString; - if ($filter) - $cacheData .= "\n".serialize($filter); - - file_put_contents($file, $cacheData); + file_put_contents($file, $data); } // loads and evaluates the cache file - public function loadCache(/*$key, &$data, &$filter = null*/) + public function loadCache(&$saveVar = null) { - // if (CFG_DEBUG) + if ($this->mode == CACHETYPE_NONE) return false; - $cache = @file_get_contents($this->cache_dir.'data/'.$key); + if (CFG_DEBUG) + return false; + + $file = CWD.$this->cacheDir.$this->generateCacheKey(); + if (!file_exists($file)) + return false; + + $cache = file_get_contents($file); if (!$cache) return false; - $cache = explode("\n", $cache); + $cache = explode("\n", $cache, 2); - @list($time, $rev) = explode(' ', $cache[0]); - $expireTime = $time + CFG_CACHE_DECAY; - if ($expireTime <= time() || $rev < AOWOW_REVISION) + @list($time, $rev, $type) = explode(' ', $cache[0]); + if ($time + CFG_CACHE_DECAY <= time() || $rev < AOWOW_REVISION) return false; - $data = str_replace(['\n', '\t'], ["\n", "\t"], unserialize($cache[1])); - if (isset($cache[2])) - $filter = unserialize($cache[2]); + if ($type == '0') + { + $data = unserialize($cache[1]); + foreach ($data as $k => $v) + $this->$k = $v; - return true; + return true; + } + else if ($type == '1') + { + $saveVar = $cache[1]; + return true; + } + + return false;; } } diff --git a/includes/loot.class.php b/includes/loot.class.php new file mode 100644 index 00000000..644f9c89 --- /dev/null +++ b/includes/loot.class.php @@ -0,0 +1,573 @@ +select('SELECT * FROM ?# WHERE entry = ?d{ AND groupid = ?d}', $tableName, abs($lootId), $groupId ? $groupId : DBSIMPLE_SKIP); + if (!$rows) + return null; + + $groupChances = []; + $nGroupEquals = []; + foreach ($rows as $entry) + { + $set = array( + 'quest' => $entry['ChanceOrQuestChance'] < 0, + 'group' => $entry['groupid'], + 'reference' => $lootId < 0 ? abs($lootId) : 0, + 'realChanceMod' => $baseChance + ); + + // if ($entry['lootmode'] > 1) + // { + $buff = []; + for ($i = 0; $i < 8; $i++) + if ($entry['lootmode'] & (1 << $i)) + $buff[] = $i + 1; + + $set['mode'] = implode(', ', $buff); + // } + // else + // $set['mode'] = 0; + + /* + modes:{"mode":8,"4":{"count":7173,"outof":17619},"8":{"count":7173,"outof":10684}} + ignore lootmodes from sharedDefines.h use different creatures/GOs from each template + modes.mode = b6543210 + ||||||'dungeon heroic + |||||'dungeon normal + ||||' + |||'10man normal + ||'25man normal + |'10man heroic + '25man heroic + */ + + if ($entry['mincountOrRef'] < 0) + { + // bandaid.. remove when propperly handling lootmodes + if (!in_array($entry['mincountOrRef'], $handledRefs)) + { // todo (high): find out, why i used this in the first place. (don't do drugs, kids) + list($data, $raw) = self::getByItemIdRecursive(LOOT_REFERENCE, $entry['mincountOrRef'], $handledRefs, /*$entry['groupid'],*/ 0, abs($entry['ChanceOrQuestChance'] / 100)); + + $handledRefs[] = $entry['mincountOrRef']; + + $loot = array_merge($loot, $data); + $rawItems = array_merge($rawItems, $raw); + } + + $set['content'] = $entry['mincountOrRef']; + $set['multiplier'] = $entry['maxcount']; + } + else + { + $rawItems[] = $entry['item']; + $set['content'] = $entry['item']; + $set['min'] = $entry['mincountOrRef']; + $set['max'] = $entry['maxcount']; + } + + if (!isset($groupChances[$entry['groupid']])) + { + $groupChances[$entry['groupid']] = 0; + $nGroupEquals[$entry['groupid']] = 0; + } + + if ($set['quest'] || !$set['group']) + $set['groupChance'] = abs($entry['ChanceOrQuestChance']); + else if ($entry['groupid'] && !$entry['ChanceOrQuestChance']) + { + $nGroupEquals[$entry['groupid']]++; + $set['groupChance'] = &$groupChances[$entry['groupid']]; + } + else if ($entry['groupid'] && $entry['ChanceOrQuestChance']) + { + @$groupChances[$entry['groupid']] += $entry['ChanceOrQuestChance']; + $set['groupChance'] = abs($entry['ChanceOrQuestChance']); + } + else // shouldn't have happened + { + self::addNote(U_GROUP_EMPLOYEE, 'Loot::getByItemIdRecursive: unhandled case in calculating chance for item '.$entry['item'].'!'); + continue; + } + + $loot[] = $set; + } + + foreach (array_keys($nGroupEquals) as $k) + { + $sum = $groupChances[$k]; + if (!$sum) + $sum = 0; + else if ($sum > 100) + { + self::addNote(U_GROUP_EMPLOYEE, 'Loot::getByItemIdRecursive: entry '.$lootId.' / group '.$k.' has a total chance of '.number_format($sum, 2).'%. Some items cannot drop!'); + $sum = 100; + } + + $cnt = empty($nGroupEquals[$k]) ? 1 : $nGroupEquals[$k]; + + $groupChances[$k] = (100 - $sum) / $cnt; // is applied as backReference to items with 0-chance + } + + return [$loot, array_unique($rawItems)]; + } + + public static function getByContainer($table, $entry) + { + $loot = null; + + if (!$table || !$entry) + return null; + + /* + todo (high): implement conditions on loot (and conditions in general) + + also + + // if (is_array($entry) && in_array($table, [LOOT_CREATURE, LOOT_GAMEOBJECT]) + // iterate over the 4 available difficulties and assign modes + + + modes:{"mode":1,"1":{"count":4408,"outof":16013},"4":{"count":4408,"outof":22531}} + */ + $handledRefs = []; + $struct = self::getByItemIdRecursive($table, $entry, $handledRefs); + if (!$struct) + return false; + + $items = new ItemList(array(['i.id', $struct[1]], CFG_SQL_LIMIT_NONE)); + self::$jsGlobals = $items->getJSGlobals(GLOBALINFO_SELF | GLOBALINFO_RELATED); + $foo = $items->getListviewData(); + + // assign listview LV rows to loot rows, not the other way round! The same item may be contained multiple times + foreach ($struct[0] as $loot) + { + $base = array( + 'percent' => round($loot['groupChance'] * $loot['realChanceMod'], 3), + 'group' => $loot['group'], + 'quest' => $loot['quest'], + 'count' => 1 // satisfies the sort-script + ); + + if ($_ = $loot['mode']) + $base['mode'] = $_; + + if ($_ = $loot['reference']) + $base['reference'] = $_; + + if ($_ = self::createStack($loot)) + $base['pctstack'] = $_; + + if ($loot['content'] > 0) // regular drop + { + if (!User::isInGroup(U_GROUP_STAFF)) + { + if (!isset(self::$results[$loot['content']])) + self::$results[$loot['content']] = array_merge($foo[$loot['content']], $base, ['stack' => [$loot['min'], $loot['max']]]); + else + self::$results[$loot['content']]['percent'] += $base['percent']; + } + else // in case of limited trash loot, check if $foo[] exists + self::$results[] = array_merge($foo[$loot['content']], $base, ['stack' => [$loot['min'], $loot['max']]]); + } + else if (User::isInGroup(U_GROUP_STAFF)) // create dummy for ref-drop + { + $data = array( + 'id' => $loot['content'], + 'name' => '@REFERENCE: '.abs($loot['content']), + 'icon' => 'trade_engineering', + 'stack' => [$loot['multiplier'], $loot['multiplier']] + ); + self::$results[] = array_merge($base, $data); + + self::$jsGlobals[TYPE_ITEM][$loot['content']] = $data; + } + } + + // move excessive % to extra loot + if (!User::isInGroup(U_GROUP_STAFF)) + { + foreach (self::$results as &$_) + { + if ($_['percent'] <= 100) + continue; + + while ($_['percent'] > 200) + { + $_['stack'][0]++; + $_['stack'][1]++; + $_['percent'] -= 100; + } + + $_['stack'][1]++; + $_['percent'] = 100; + } + } + else + { + $fields = ['mode', 'reference']; + $base = []; + $set = 0; + foreach (self::$results as $foo) + { + foreach ($fields as $idx => $field) + { + if (!isset($base[$idx])) + $base[$idx] = @$foo[$field]; + else if ($base[$idx] != @$foo[$field]) + $set |= 1 << $idx; + } + + if ($set == (pow(2, count($fields)) - 1)) + break; + } + + self::$extraCols[] = "Listview.funcBox.createSimpleCol('group', 'Group', '7%', 'group')"; + foreach ($fields as $idx => $field) + if ($set & (1 << $idx)) + self::$extraCols[] = "Listview.funcBox.createSimpleCol('".$field."', '".Util::ucFirst($field)."', '7%', '".$field."')"; + } + + return true; + } + + public static function getByItem($itemId, $maxResults = CFG_SQL_LIMIT_DEFAULT) + { + if (!$itemId) + return []; + + // [fileName, tabData, tabName, tabId, extraCols, hiddenCols, visibleCols] + $tabsFinal = array( + ['item', [], '$LANG.tab_containedin', 'contained-in-item', [], [], []], + ['item', [], '$LANG.tab_disenchantedfrom', 'disenchanted-from', [], [], []], + ['item', [], '$LANG.tab_prospectedfrom', 'prospected-from', [], [], []], + ['item', [], '$LANG.tab_milledfrom', 'milled-from', [], [], []], + ['creature', [], '$LANG.tab_droppedby', 'dropped-by', [], [], []], + ['creature', [], '$LANG.tab_pickpocketedfrom', 'pickpocketed-from', [], [], []], + ['creature', [], '$LANG.tab_skinnedfrom', 'skinned-from', [], [], []], + ['creature', [], '$LANG.tab_minedfromnpc', 'mined-from-npc', [], [], []], + ['creature', [], '$LANG.tab_salvagedfrom', 'salvaged-from', [], [], []], + ['creature', [], '$LANG.tab_gatheredfromnpc', 'gathered-from-npc', [], [], []], + ['quest', [], '$LANG.tab_rewardfrom', 'reward-from-quest', [], [], []], + ['zone', [], '$LANG.tab_fishedin', 'fished-in-zone', [], [], []], + ['object', [], '$LANG.tab_containedin', 'contained-in-object', [], [], []], + ['object', [], '$LANG.tab_minedfrom', 'mined-from-object', [], [], []], + ['object', [], '$LANG.tab_gatheredfrom', 'gathered-from-object', [], [], []], + ['object', [], '$LANG.tab_fishedin', 'fished-in-object', [], [], []], + ['spell', [], '$LANG.tab_createdby', 'created-by', [], [], []], + ['achievement', [], '$LANG.tab_rewardfrom', 'reward-from-achievemnt', [], [], []] + ); + $refResults = []; + $chanceMods = []; + $query = 'SELECT + -lt1.entry AS ARRAY_KEY, + IF(lt1.mincountOrRef > 0, lt1.item, lt1.mincountOrRef) AS item, + lt1.ChanceOrQuestChance AS chance, + SUM(IF(lt2.ChanceOrQuestChance = 0, 1, 0)) AS nZeroItems, + SUM(IF(lt2.ChanceOrQuestChance > 0, lt2.ChanceOrQuestChance, 0)) AS sumChance, + IF(lt1.groupid > 0, 1, 0) AS isGrouped, + IF(lt1.mincountOrRef > 0, lt1.mincountOrRef, 1) AS min, + IF(lt1.mincountOrRef > 0, lt1.maxcount, 1) AS max, + IF(lt1.mincountOrRef < 0, lt1.maxcount, 1) AS multiplier + FROM + ?# lt1 + LEFT JOIN + ?# lt2 ON lt1.entry = lt2.entry AND lt1.groupid = lt2.groupid + WHERE + %s + GROUP BY lt2.entry'; + + $calcChance = function ($refs, $parents = []) use (&$chanceMods) + { + $retData = []; + $retKeys = []; + + foreach ($refs as $rId => $ref) + { + // check for possible database inconsistencies + if (!$ref['chance'] && !$ref['isGrouped']) + self::addNote(U_GROUP_EMPLOYEE, 'Loot by Item: ungrouped Item/Ref '.$ref['item'].' has 0% chance assigned!'); + + if ($ref['isGrouped'] && $ref['sumChance'] > 100) + self::addNote(U_GROUP_EMPLOYEE, 'Loot by Item: group with Item/Ref '.$ref['item'].' has '.number_format($ref['sumChance'], 2).'% total chance! Some items cannot drop!'); + + if ($ref['isGrouped'] && $ref['sumChance'] == 100 && !$ref['chance']) + self::addNote(U_GROUP_EMPLOYEE, 'Loot by Item: Item/Ref '.$ref['item'].' with adaptive chance cannot drop. Group already at 100%!'); + + $chance = abs($ref['chance'] ? $ref['chance'] : (100 - $ref['sumChance']) / $ref['nZeroItems']) / 100; + + // apply inherited chanceMods + if (isset($chanceMods[$ref['item']])) + { + $chance *= $chanceMods[$ref['item']][0]; + $chance = 1 - pow(1 - $chance, $chanceMods[$ref['item']][1]); + } + + // save chance for parent-ref + $chanceMods[$rId] = [$chance, $ref['multiplier']]; + + // refTemplate doesn't point to a new ref -> we are done + if (!in_array($rId, $parents)) + { + $data = array( + 'percent' => $chance, + 'stack' => [$ref['min'], $ref['max']], + 'count' => 1 // ..and one for the sort script + ); + + if ($_ = self::createStack($ref)) + $data['pctstack'] = $_; + + // sort highest chances first + $i = 0; + for (; $i < count($retData); $i++) + if ($retData[$i]['percent'] < $data['percent']) + break; + + array_splice($retData, $i, 0, [$data]); + array_splice($retKeys, $i, 0, [$rId]); + } + } + + return array_combine($retKeys, $retData); + }; + + /* + get references containing the item + */ + $newRefs = DB::Aowow()->select( + sprintf($query, 'lt1.item = ?d AND lt1.mincountOrRef > 0'), + LOOT_REFERENCE, LOOT_REFERENCE, + $itemId + ); + + while ($newRefs) + { + $curRefs = $newRefs; + $newRefs = DB::Aowow()->select( + sprintf($query, 'lt1.mincountOrRef IN (?a)'), + LOOT_REFERENCE, LOOT_REFERENCE, + array_keys($curRefs) + ); + + $refResults += $calcChance($curRefs, array_column($newRefs, 'item')); + } + + /* + search the real loot-templates for the itemId and gathered refds + */ + for ($i = 1; $i < count(self::$lootTemplates); $i++) + { + $result = $calcChance(DB::Aowow()->select( + sprintf($query, '{lt1.mincountOrRef IN (?a) OR }(lt1.mincountOrRef > 0 AND lt1.item = ?d)'), + self::$lootTemplates[$i], self::$lootTemplates[$i], + $refResults ? array_keys($refResults) : DBSIMPLE_SKIP, + $itemId + )); + + // do not skip here if $result is empty. Additional loot for spells and quest is added separately + + // format for actual use + foreach ($result as $k => $v) + { + unset($result[$k]); + $v['percent'] = round($v['percent'] * 100, 3); + $result[abs($k)] = $v; + } + + // cap fetched entries to the sql-limit to guarantee, that the highest chance items get selected first + // screws with GO-loot and skinnig-loot as these templates are shared for several tabs (fish, herb, ore) (herb, ore, leather) + $ids = array_slice(array_keys($result), 0, $maxResults); + + switch (self::$lootTemplates[$i]) + { + case LOOT_CREATURE: $field = 'lootId'; $tabId = 4; break; + case LOOT_PICKPOCKET: $field = 'pickpocketLootId'; $tabId = 5; break; + case LOOT_SKINNING: $field = 'skinLootId'; $tabId = -6; break; // assigned later + case LOOT_PROSPECTING: $field = 'id'; $tabId = 2; break; + case LOOT_MILLING: $field = 'id'; $tabId = 3; break; + case LOOT_ITEM: $field = 'id'; $tabId = 0; break; + case LOOT_DISENCHANT: $field = 'disenchantId'; $tabId = 1; break; + case LOOT_FISHING: $field = 'id'; $tabId = 11; break; // subAreas are currently ignored + case LOOT_GAMEOBJECT: + if (!$ids) + break; + + $srcObj = new GameObjectList(array(['lootId', $ids])); + if ($srcObj->error) + break; + + $srcData = $srcObj->getListviewData(); + + foreach ($srcObj->iterate() as $curTpl) + { + switch ($curTpl['type']) + { + case 25: $tabId = 15; break; // fishing node + case -3: $tabId = 14; break; // herb + case -4: $tabId = 13; break; // vein + default: $tabId = 12; break; // general chest loot + } + + $tabsFinal[$tabId][1][] = array_merge($srcData[$srcObj->id], $result[$srcObj->getField('lootId')]); + $tabsFinal[$tabId][4][] = 'Listview.extraCols.percent'; + if ($tabId != 15) + $tabsFinal[$tabId][6][] = 'skill'; + } + break; + case LOOT_MAIL: + $conditions = array(['rewardChoiceItemId1', $itemId], ['rewardChoiceItemId2', $itemId], ['rewardChoiceItemId3', $itemId], ['rewardChoiceItemId4', $itemId], ['rewardChoiceItemId5', $itemId], + ['rewardChoiceItemId6', $itemId], ['rewardItemId1', $itemId], ['rewardItemId2', $itemId], ['rewardItemId3', $itemId], ['rewardItemId4', $itemId], + 'OR'); + if ($ids) + $conditions[] = ['rewardMailTemplateId', $ids]; + + $srcObj = new QuestList($conditions); + if (!$srcObj->error) + { + $srcObj->getJSGlobals(GLOBALINFO_SELF | GLOBALINFO_REWARDS); + $srcData = $srcObj->getListviewData(); + + foreach ($srcObj->iterate() as $_) + $tabsFinal[10][1][] = array_merge($srcData[$srcObj->id], empty($result[$srcObj->id]) ? ['percent' => -1] : $result[$srcObj->id]); + } + + /* + todo: search for achievements here + $tabsFinal[17] + */ + + break; + case LOOT_SPELL: + $conditions = ['OR', ['effect1CreateItemId', $itemId], ['effect2CreateItemId', $itemId], ['effect3CreateItemId', $itemId]]; + if ($ids) + $conditions[] = ['id', $ids]; + + $srcObj = new SpellList($conditions); + if (!$srcObj->error) + { + $srcObj->getJSGlobals(GLOBALINFO_SELF | GLOBALINFO_RELATED); + $srcData = $srcObj->getListviewData(); + + if (!empty($result)) + $tabsFinal[16][4][] = 'Listview.extraCols.percent'; + + if ($srcObj->hasSetFields(['reagent1'])) + $tabsFinal[16][6][] = 'reagents'; + + foreach ($srcObj->iterate() as $_) + $tabsFinal[16][1][] = array_merge($srcData[$srcObj->id], empty($result[$srcObj->id]) ? ['percent' => -1] : $result[$srcObj->id]); + } + break; + } + + if (!$ids) + continue; + + switch ($tabsFinal[abs($tabId)][0]) + { + case 'creature': // new CreatureList + case 'item': // new ItemList + case 'zone': // new ZoneList + $oName = ucFirst($tabsFinal[abs($tabId)][0]).'List'; + $srcObj = new $oName(array([$field, $ids])); + if (!$srcObj->error) + { + $srcObj->getJSGlobals(GLOBALINFO_SELF | GLOBALINFO_RELATED); + $srcData = $srcObj->getListviewData(); + + foreach ($srcObj->iterate() as $curTpl) + { + if ($tabId < 0 && $curTpl['typeFlags'] & NPC_TYPEFLAG_HERBLOOT) + $tabId = 9; + else if ($tabId < 0 && $curTpl['typeFlags'] & NPC_TYPEFLAG_ENGINEERLOOT) + $tabId = 8; + else if ($tabId < 0 && $curTpl['typeFlags'] & NPC_TYPEFLAG_MININGLOOT) + $tabId = 7; + else if ($tabId < 0) + $tabId = abs($tabId); // general case (skinning) + + $tabsFinal[$tabId][1][] = array_merge($srcData[$srcObj->id], $result[$srcObj->getField($field)]); + $tabsFinal[$tabId][4][] = 'Listview.extraCols.percent'; + } + } + break; + } + } + + return $tabsFinal; + } +} + +?> \ No newline at end of file diff --git a/includes/user.class.php b/includes/user.class.php index b9f93052..c9454f24 100644 --- a/includes/user.class.php +++ b/includes/user.class.php @@ -149,8 +149,6 @@ class User { self::$localeId = isset(Util::$localeStrings[$use]) ? $use : 0; self::$localeString = self::localeString(self::$localeId); - - Lang::load(self::$localeString); } public static function isInGroup($group) @@ -269,16 +267,16 @@ class User return $set; if ($_ = self::getCharacters()) - $subSet['characters'] = json_encode($_, JSON_NUMERIC_CHECK); + $subSet['characters'] = $_; if ($_ = self::getProfiles()) - $subSet['profiles'] = json_encode($_, JSON_NUMERIC_CHECK); + $subSet['profiles'] = $_; if ($_ = self::getWeightScales()) - $subSet['weightscales'] = json_encode($_, JSON_NUMERIC_CHECK); + $subSet['weightscales'] = $_; if ($_ = self::getCookies()) - $subSet['cookies'] = json_encode($_, JSON_NUMERIC_CHECK); + $subSet['cookies'] = $_; return array_merge($set, $subSet); } @@ -335,7 +333,7 @@ class User ); } - return $asJSON ? json_encode(self::$characters, JSON_NUMERIC_CHECK) : self::$characters; + return self::$characters; } public static function getProfiles($asJSON = true) @@ -350,7 +348,7 @@ class User ); } - return $asJSON ? json_encode(self::$profiles, JSON_NUMERIC_CHECK) : self::$profiles; + return self::$profiles; } public static function getCookies() @@ -360,7 +358,7 @@ class User if (self::$id) $data = DB::Aowow()->selectCol('SELECT name AS ARRAY_KEY, data FROM ?_account_cookies WHERE userId = ?d', self::$id); - return json_encode($data); + return $data; } public static function writeCookie() diff --git a/includes/utilities.php b/includes/utilities.php index 9bf007d0..6ebeeee0 100644 --- a/includes/utilities.php +++ b/includes/utilities.php @@ -198,21 +198,6 @@ class Util null, 1.0, 0.6, 1.0, 0.8, 1.0, 1.0, 1.2, 1.25, 1.44, 2.5, 1.728, 3.0, 0.0, 0.0, 1.2, 1.25 ); - public static $lootTemplates = array( - LOOT_REFERENCE, // internal - LOOT_ITEM, // item - LOOT_DISENCHANT, // item - LOOT_PROSPECTING, // item - LOOT_MILLING, // item - LOOT_CREATURE, // npc - LOOT_PICKPOCKET, // npc - LOOT_SKINNING, // npc (see its flags for mining, herbing or actual skinning) - LOOT_FISHING, // zone - LOOT_GAMEOBJECT, // object - LOOT_MAIL, // quest || achievement - LOOT_SPELL // spell - ); - // todo: translate and move to Lang public static $spellEffectStrings = array( 0 => 'None', @@ -979,6 +964,19 @@ class Util return 'b'.strToUpper($_); } + public static function htmlEscape($data) + { + if (is_array($data)) + { + foreach ($data as &$v) + $v = self::htmlEscape($v); + + return $data; + } + else + return htmlspecialchars(trim($data), ENT_QUOTES, 'utf-8'); + } + public static function jsEscape($data) { if (is_array($data)) @@ -1203,30 +1201,6 @@ class Util return $return; } - public static function isValidPage($struct, $keys) - { - switch (count($keys)) - { - case 0: // no params works always - return true; - case 1: // null is avalid || value in a 1-dim-array || key in a n-dim-array - return $keys[0] === null || in_array($keys[0], $struct) || (isset($struct[$keys[0]])); - case 2: // first param has to be a key. otherwise invalid - if (!isset($struct[$keys[0]])) - return false; - - // check if the sub-array is n-imensional - if (count($struct[$keys[0]]) == count($struct[$keys[0]], COUNT_RECURSIVE)) - return in_array($keys[1], $struct[$keys[0]]); // second param is value in second level array - else - return isset($struct[$keys[0]][$keys[1]]); // check if params is key of another array - case 3: // 3 params MUST point to a specific value - return isset($struct[$keys[0]][$keys[1]]) && in_array($keys[2], $struct[$keys[0]][$keys[1]]); - } - - return false; - } - // default ucFirst doesn't convert UTF-8 chars public static function ucFirst($str) { @@ -1336,538 +1310,6 @@ class Util return $data; } - /* todo (xxx): loot is extremly uncreative, messy, incomplete and uncreative - the type-lookups in particular should be separated in a way the template can access the required jsGloblas - */ - - /* from TC wiki - fishing_loot_template no relation entry is linked with ID of the fishing zone or area - creature_loot_template entry many <- many creature_template lootid - gameobject_loot_template entry many <- many gameobject_template data1 Only GO type 3 (CHEST) or 25 (FISHINGHOLE) - item_loot_template entry many <- one item_template entry - disenchant_loot_template entry many <- many item_template DisenchantID - prospecting_loot_template entry many <- one item_template entry - milling_loot_template entry many <- one item_template entry - pickpocketing_loot_template entry many <- many creature_template pickpocketloot - skinning_loot_template entry many <- many creature_template skinloot Can also store minable/herbable items gathered from creatures - quest_mail_loot_template entry quest_template RewMailTemplateId - reference_loot_template entry many <- many _loot_template -mincountOrRef In case of negative mincountOrRef - */ - private static function getLootByEntry($tableName, $lootId, &$handledRefs, $groupId = 0, $baseChance = 1.0) - { - $loot = []; - $rawItems = []; - - if (!$tableName || !$lootId) - return null; - - $rows = DB::Aowow()->select('SELECT * FROM ?# WHERE entry = ?d{ AND groupid = ?d}', $tableName, abs($lootId), $groupId ? $groupId : DBSIMPLE_SKIP); - if (!$rows) - return null; - - $groupChances = []; - $nGroupEquals = []; - foreach ($rows as $entry) - { - $set = array( - 'quest' => $entry['ChanceOrQuestChance'] < 0, - 'group' => $entry['groupid'], - 'reference' => $lootId < 0 ? abs($lootId) : 0, - 'realChanceMod' => $baseChance - ); - - // if ($entry['lootmode'] > 1) - // { - $buff = []; - for ($i = 0; $i < 8; $i++) - if ($entry['lootmode'] & (1 << $i)) - $buff[] = $i + 1; - - $set['mode'] = implode(', ', $buff); - // } - // else - // $set['mode'] = 0; - - /* - modes:{"mode":8,"4":{"count":7173,"outof":17619},"8":{"count":7173,"outof":10684}} - ignore lootmodes from sharedDefines.h use different creatures/GOs from each template - modes.mode = b6543210 - ||||||'dungeon heroic - |||||'dungeon normal - ||||' - |||'10man normal - ||'25man normal - |'10man heroic - '25man heroic - */ - - if ($entry['mincountOrRef'] < 0) - { - // bandaid.. remove when propperly handling lootmodes - if (!in_array($entry['mincountOrRef'], $handledRefs)) - { // todo (high): find out, why i used this in the first place. (don't do drugs, kids) - list($data, $raw) = self::getLootByEntry(LOOT_REFERENCE, $entry['mincountOrRef'], $handledRefs, /*$entry['groupid'],*/ 0, abs($entry['ChanceOrQuestChance'] / 100)); - - $handledRefs[] = $entry['mincountOrRef']; - - $loot = array_merge($loot, $data); - $rawItems = array_merge($rawItems, $raw); - } - - $set['content'] = $entry['mincountOrRef']; - $set['multiplier'] = $entry['maxcount']; - } - else - { - $rawItems[] = $entry['item']; - $set['content'] = $entry['item']; - $set['min'] = $entry['mincountOrRef']; - $set['max'] = $entry['maxcount']; - } - - if (!isset($groupChances[$entry['groupid']])) - { - $groupChances[$entry['groupid']] = 0; - $nGroupEquals[$entry['groupid']] = 0; - } - - if ($set['quest'] || !$set['group']) - $set['groupChance'] = abs($entry['ChanceOrQuestChance']); - else if ($entry['groupid'] && !$entry['ChanceOrQuestChance']) - { - $nGroupEquals[$entry['groupid']]++; - $set['groupChance'] = &$groupChances[$entry['groupid']]; - } - else if ($entry['groupid'] && $entry['ChanceOrQuestChance']) - { - @$groupChances[$entry['groupid']] += $entry['ChanceOrQuestChance']; - $set['groupChance'] = abs($entry['ChanceOrQuestChance']); - } - else // shouldn't have happened - { - self::addNote(U_GROUP_EMPLOYEE, 'Loot by LootId: unhandled case in calculating chance for item '.$entry['item'].'!'); - continue; - } - - $loot[] = $set; - } - - foreach (array_keys($nGroupEquals) as $k) - { - $sum = $groupChances[$k]; - if (!$sum) - $sum = 0; - else if ($sum > 100) - { - self::addNote(U_GROUP_EMPLOYEE, 'Loot by LootId: entry '.$lootId.' / group '.$k.' has a total chance of '.number_format($sum, 2).'%. Some items cannot drop!'); - $sum = 100; - } - - $cnt = empty($nGroupEquals[$k]) ? 1 : $nGroupEquals[$k]; - - $groupChances[$k] = (100 - $sum) / $cnt; // is applied as backReference to items with 0-chance - } - - return [$loot, array_unique($rawItems)]; - } - - public static function handleLoot($table, $entry, $debug = false, &$debugCols = []) - { - $lv = []; - $loot = null; - - if (!$table || !$entry) - return null; - - /* - todo (high): implement conditions on loot (and conditions in general) - - also - - // if (is_array($entry) && in_array($table, [LOOT_CREATURE, LOOT_GAMEOBJECT]) - // iterate over the 4 available difficulties and assign modes - - - modes:{"mode":1,"1":{"count":4408,"outof":16013},"4":{"count":4408,"outof":22531}} - */ - $handledRefs = []; - $struct = self::getLootByEntry($table, $entry, $handledRefs); - if (!$struct) - return $lv; - - $items = new ItemList(array(['i.id', $struct[1]], CFG_SQL_LIMIT_NONE)); - $items->getJSGlobals(GLOBALINFO_SELF | GLOBALINFO_RELATED); - $foo = $items->getListviewData(); - - // assign listview LV rows to loot rows, not the other way round! The same item may be contained multiple times - foreach ($struct[0] as $loot) - { - $base = array( - 'percent' => round($loot['groupChance'] * $loot['realChanceMod'], 3), - 'group' => $loot['group'], - 'quest' => $loot['quest'], - 'count' => 1 // satisfies the sort-script - ); - - if ($_ = $loot['mode']) - $base['mode'] = $_; - - if ($_ = $loot['reference']) - $base['reference'] = $_; - - $stack = []; // equal distribution between min/max .. not blizzlike, but hey, TC-issue - if (isset($loot['max']) && isset($loot['min']) && ($loot['max'] > $loot['min'])) - for ($i = $loot['min']; $i <= $loot['max']; $i++) - $stack[$i] = round(100 / (1 + $loot['max'] - $loot['min']), 3); - - if ($stack) // yes, it wants a string .. how weired is that.. - $base['pctstack'] = json_encode($stack, JSON_NUMERIC_CHECK); - - if ($loot['content'] > 0) // regular drop - { - if (!$debug) - { - if (!isset($lv[$loot['content']])) - $lv[$loot['content']] = array_merge($foo[$loot['content']], $base, ['stack' => [$loot['min'], $loot['max']]]); - else - $lv[$loot['content']]['percent'] += $base['percent']; - } - else // in case of limited trash loot, check if $foo[] exists - $lv[] = array_merge($foo[$loot['content']], $base, ['stack' => [$loot['min'], $loot['max']]]); - } - else if ($debug) // create dummy for ref-drop - { - $data = array( - 'id' => $loot['content'], - 'name' => '@REFERENCE: '.abs($loot['content']), - 'icon' => 'trade_engineering', - 'stack' => [$loot['multiplier'], $loot['multiplier']] - ); - $lv[] = array_merge($base, $data); - - Util::$pageTemplate->extendGlobalData(TYPE_ITEM, [$loot['content'] => $data]); - } - } - - // move excessive % to extra loot - if (!$debug) - { - foreach ($lv as &$_) - { - if ($_['percent'] <= 100) - continue; - - while ($_['percent'] > 200) - { - $_['stack'][0]++; - $_['stack'][1]++; - $_['percent'] -= 100; - } - - $_['stack'][1]++; - $_['percent'] = 100; - } - } - else - { - $fields = ['mode', 'reference']; - $base = []; - $set = 0; - foreach ($lv as $foo) - { - foreach ($fields as $idx => $field) - { - if (!isset($base[$idx])) - $base[$idx] = @$foo[$field]; - else if ($base[$idx] != @$foo[$field]) - $set |= 1 << $idx; - } - - if ($set == (pow(2, count($fields)) - 1)) - break; - } - - $debugCols[] = "Listview.funcBox.createSimpleCol('group', 'Group', '7%', 'group')"; - foreach ($fields as $idx => $field) - if ($set & (1 << $idx)) - $debugCols[] = "Listview.funcBox.createSimpleCol('".$field."', '".Util::ucFirst($field)."', '7%', '".$field."')"; - } - - return $lv; - } - - public static function getLootSource($itemId, $maxResults = CFG_SQL_LIMIT_DEFAULT) - { - if (!$itemId) - return []; - - // [fileName, tabData, tabName, tabId, extraCols, hiddenCols, visibleCols] - $tabsFinal = array( - ['item', [], '$LANG.tab_containedin', 'contained-in-item', [], [], []], - ['item', [], '$LANG.tab_disenchantedfrom', 'disenchanted-from', [], [], []], - ['item', [], '$LANG.tab_prospectedfrom', 'prospected-from', [], [], []], - ['item', [], '$LANG.tab_milledfrom', 'milled-from', [], [], []], - ['creature', [], '$LANG.tab_droppedby', 'dropped-by', [], [], []], - ['creature', [], '$LANG.tab_pickpocketedfrom', 'pickpocketed-from', [], [], []], - ['creature', [], '$LANG.tab_skinnedfrom', 'skinned-from', [], [], []], - ['creature', [], '$LANG.tab_minedfromnpc', 'mined-from-npc', [], [], []], - ['creature', [], '$LANG.tab_salvagedfrom', 'salvaged-from', [], [], []], - ['creature', [], '$LANG.tab_gatheredfromnpc', 'gathered-from-npc', [], [], []], - ['quest', [], '$LANG.tab_rewardfrom', 'reward-from-quest', [], [], []], - ['zone', [], '$LANG.tab_fishedin', 'fished-in-zone', [], [], []], - ['object', [], '$LANG.tab_containedin', 'contained-in-object', [], [], []], - ['object', [], '$LANG.tab_minedfrom', 'mined-from-object', [], [], []], - ['object', [], '$LANG.tab_gatheredfrom', 'gathered-from-object', [], [], []], - ['object', [], '$LANG.tab_fishedin', 'fished-in-object', [], [], []], - ['spell', [], '$LANG.tab_createdby', 'created-by', [], [], []], - ['achievement', [], '$LANG.tab_rewardfrom', 'reward-from-achievemnt', [], [], []] - ); - $refResults = []; - $chanceMods = []; - $query = 'SELECT - -lt1.entry AS ARRAY_KEY, - IF(lt1.mincountOrRef > 0, lt1.item, lt1.mincountOrRef) AS item, - lt1.ChanceOrQuestChance AS chance, - SUM(IF(lt2.ChanceOrQuestChance = 0, 1, 0)) AS nZeroItems, - SUM(IF(lt2.ChanceOrQuestChance > 0, lt2.ChanceOrQuestChance, 0)) AS sumChance, - IF(lt1.groupid > 0, 1, 0) AS isGrouped, - IF(lt1.mincountOrRef > 0, lt1.mincountOrRef, 1) AS min, - IF(lt1.mincountOrRef > 0, lt1.maxcount, 1) AS max, - IF(lt1.mincountOrRef < 0, lt1.maxcount, 1) AS multiplier - FROM - ?# lt1 - LEFT JOIN - ?# lt2 ON lt1.entry = lt2.entry AND lt1.groupid = lt2.groupid - WHERE - %s - GROUP BY lt2.entry'; - - $calcChance = function ($refs, $parents = []) use (&$chanceMods) - { - $retData = []; - $retKeys = []; - - foreach ($refs as $rId => $ref) - { - // check for possible database inconsistencies - if (!$ref['chance'] && !$ref['isGrouped']) - self::addNote(U_GROUP_EMPLOYEE, 'Loot by Item: ungrouped Item/Ref '.$ref['item'].' has 0% chance assigned!'); - - if ($ref['isGrouped'] && $ref['sumChance'] > 100) - self::addNote(U_GROUP_EMPLOYEE, 'Loot by Item: group with Item/Ref '.$ref['item'].' has '.number_format($ref['sumChance'], 2).'% total chance! Some items cannot drop!'); - - if ($ref['isGrouped'] && $ref['sumChance'] == 100 && !$ref['chance']) - self::addNote(U_GROUP_EMPLOYEE, 'Loot by Item: Item/Ref '.$ref['item'].' with adaptive chance cannot drop. Group already at 100%!'); - - $chance = abs($ref['chance'] ? $ref['chance'] : (100 - $ref['sumChance']) / $ref['nZeroItems']) / 100; - - // apply inherited chanceMods - if (isset($chanceMods[$ref['item']])) - { - $chance *= $chanceMods[$ref['item']][0]; - $chance = 1 - pow(1 - $chance, $chanceMods[$ref['item']][1]); - } - - // save chance for parent-ref - $chanceMods[$rId] = [$chance, $ref['multiplier']]; - - // refTemplate doesn't point to a new ref -> we are done - if (!in_array($rId, $parents)) - { - $data = array( - 'percent' => $chance, - 'stack' => [$ref['min'], $ref['max']], - 'count' => 1 // ..and one for the sort script - ); - - $stack = []; // equal distribution between min/max .. not blizzlike, but hey, TC-issue - if ($ref['max'] > 1) - for ($i = $ref['min']; $i <= $ref['max']; $i++) - $stack[$i] = round(100 / (1 + $ref['max'] - $ref['min']), 3); - - if ($stack) // yes, it wants a string .. how weired is that.. - $data['pctstack'] = json_encode($stack, JSON_NUMERIC_CHECK); - - // sort highest chances first - $i = 0; - for (; $i < count($retData); $i++) - if ($retData[$i]['percent'] < $data['percent']) - break; - - array_splice($retData, $i, 0, [$data]); - array_splice($retKeys, $i, 0, [$rId]); - } - } - - return array_combine($retKeys, $retData); - }; - - /* - get references containing the item - */ - $newRefs = DB::Aowow()->select( - sprintf($query, 'lt1.item = ?d AND lt1.mincountOrRef > 0'), - LOOT_REFERENCE, LOOT_REFERENCE, - $itemId - ); - - while ($newRefs) - { - $curRefs = $newRefs; - $newRefs = DB::Aowow()->select( - sprintf($query, 'lt1.mincountOrRef IN (?a)'), - LOOT_REFERENCE, LOOT_REFERENCE, - array_keys($curRefs) - ); - - $refResults += $calcChance($curRefs, array_column($newRefs, 'item')); - } - - /* - search the real loot-templates for the itemId and gathered refds - */ - for ($i = 1; $i < count(self::$lootTemplates); $i++) - { - $result = $calcChance(DB::Aowow()->select( - sprintf($query, '{lt1.mincountOrRef IN (?a) OR }(lt1.mincountOrRef > 0 AND lt1.item = ?d)'), - self::$lootTemplates[$i], self::$lootTemplates[$i], - $refResults ? array_keys($refResults) : DBSIMPLE_SKIP, - $itemId - )); - - // do not skip here if $result is empty. Additional loot for spells and quest is added separately - - // format for actual use - foreach ($result as $k => $v) - { - unset($result[$k]); - $v['percent'] = round($v['percent'] * 100, 3); - $result[abs($k)] = $v; - } - - // cap fetched entries to the sql-limit to guarantee, that the highest chance items get selected first - // screws with GO-loot and skinnig-loot as these templates are shared for several tabs (fish, herb, ore) (herb, ore, leather) - $ids = array_slice(array_keys($result), 0, $maxResults); - - switch (self::$lootTemplates[$i]) - { - case LOOT_CREATURE: $field = 'lootId'; $tabId = 4; break; - case LOOT_PICKPOCKET: $field = 'pickpocketLootId'; $tabId = 5; break; - case LOOT_SKINNING: $field = 'skinLootId'; $tabId = -6; break; // assigned later - case LOOT_PROSPECTING: $field = 'id'; $tabId = 2; break; - case LOOT_MILLING: $field = 'id'; $tabId = 3; break; - case LOOT_ITEM: $field = 'id'; $tabId = 0; break; - case LOOT_DISENCHANT: $field = 'disenchantId'; $tabId = 1; break; - case LOOT_FISHING: $field = 'id'; $tabId = 11; break; // subAreas are currently ignored - case LOOT_GAMEOBJECT: - if (!$ids) - break; - - $srcObj = new GameObjectList(array(['lootId', $ids])); - if ($srcObj->error) - break; - - $srcData = $srcObj->getListviewData(); - - foreach ($srcObj->iterate() as $curTpl) - { - switch ($curTpl['type']) - { - case 25: $tabId = 15; break; // fishing node - case -3: $tabId = 14; break; // herb - case -4: $tabId = 13; break; // vein - default: $tabId = 12; break; // general chest loot - } - - $tabsFinal[$tabId][1][] = array_merge($srcData[$srcObj->id], $result[$srcObj->getField('lootId')]); - $tabsFinal[$tabId][4][] = 'Listview.extraCols.percent'; - if ($tabId != 15) - $tabsFinal[$tabId][6][] = 'skill'; - } - break; - case LOOT_MAIL: - $conditions = array(['rewardChoiceItemId1', $itemId], ['rewardChoiceItemId2', $itemId], ['rewardChoiceItemId3', $itemId], ['rewardChoiceItemId4', $itemId], ['rewardChoiceItemId5', $itemId], - ['rewardChoiceItemId6', $itemId], ['rewardItemId1', $itemId], ['rewardItemId2', $itemId], ['rewardItemId3', $itemId], ['rewardItemId4', $itemId], - 'OR'); - if ($ids) - $conditions[] = ['rewardMailTemplateId', $ids]; - - $srcObj = new QuestList($conditions); - if (!$srcObj->error) - { - $srcObj->getJSGlobals(GLOBALINFO_SELF | GLOBALINFO_REWARDS); - $srcData = $srcObj->getListviewData(); - - foreach ($srcObj->iterate() as $_) - $tabsFinal[10][1][] = array_merge($srcData[$srcObj->id], empty($result[$srcObj->id]) ? ['percent' => -1] : $result[$srcObj->id]); - } - - /* - todo: search for achievements here - $tabsFinal[17] - */ - - break; - case LOOT_SPELL: - $conditions = ['OR', ['effect1CreateItemId', $itemId], ['effect2CreateItemId', $itemId], ['effect3CreateItemId', $itemId]]; - if ($ids) - $conditions[] = ['id', $ids]; - - $srcObj = new SpellList($conditions); - if (!$srcObj->error) - { - $srcObj->getJSGlobals(GLOBALINFO_SELF | GLOBALINFO_RELATED); - $srcData = $srcObj->getListviewData(); - - if (!empty($result)) - $tabsFinal[16][4][] = 'Listview.extraCols.percent'; - - if ($srcObj->hasSetFields(['reagent1'])) - $tabsFinal[16][6][] = 'reagents'; - - foreach ($srcObj->iterate() as $_) - $tabsFinal[16][1][] = array_merge($srcData[$srcObj->id], empty($result[$srcObj->id]) ? ['percent' => -1] : $result[$srcObj->id]); - } - break; - } - - if (!$ids) - continue; - - switch ($tabsFinal[abs($tabId)][0]) - { - case 'creature': // new CreatureList - case 'item': // new ItemList - case 'zone': // new ZoneList - $oName = ucFirst($tabsFinal[abs($tabId)][0]).'List'; - $srcObj = new $oName(array([$field, $ids])); - if (!$srcObj->error) - { - $srcObj->getJSGlobals(GLOBALINFO_SELF | GLOBALINFO_RELATED); - $srcData = $srcObj->getListviewData(); - - foreach ($srcObj->iterate() as $curTpl) - { - if ($tabId < 0 && $curTpl['typeFlags'] & NPC_TYPEFLAG_HERBLOOT) - $tabId = 9; - else if ($tabId < 0 && $curTpl['typeFlags'] & NPC_TYPEFLAG_ENGINEERLOOT) - $tabId = 8; - else if ($tabId < 0 && $curTpl['typeFlags'] & NPC_TYPEFLAG_MININGLOOT) - $tabId = 7; - else if ($tabId < 0) - $tabId = abs($tabId); // general case (skinning) - - $tabsFinal[$tabId][1][] = array_merge($srcData[$srcObj->id], $result[$srcObj->getField($field)]); - $tabsFinal[$tabId][4][] = 'Listview.extraCols.percent'; - } - } - break; - } - } - - return $tabsFinal; - } - public static function urlize($str) { $search = ['<', '>', ' / ', "'", '(', ')']; diff --git a/pages/object.php b/pages/object.php index c143bf4f..c4db4038 100644 --- a/pages/object.php +++ b/pages/object.php @@ -4,50 +4,26 @@ if (!defined('AOWOW_REVISION')) die('illegal access'); -require 'includes/community.class.php'; - -$_id = intVal($pageParam); -$_path = [0, 5]; - -$cacheKeyPage = implode('_', [CACHETYPE_PAGE, TYPE_OBJECT, $_id, -1, User::$localeId]); -$cacheKeyTooltip = implode('_', [CACHETYPE_TOOLTIP, TYPE_OBJECT, $_id, -1, User::$localeId]); - -// AowowPower-request -if (isset($_GET['power'])) +// menuId 5: Object g_initPath() +// tabId 0: Database g_initHeader() +class ObjectPage extends GenericPage { - header('Content-type: application/x-javascript; charsetUTF-8'); - - Util::powerUseLocale(@$_GET['domain']); - - if (!$smarty->loadCache($cacheKeyTooltip, $x)) - { - $object = new GameObjectList(array(['id', $_id])); - if ($object->error) - die('$WowheadPower.registerObject('.$_id.', '.User::$localeId.', {});'); - - $s = $object->getSpawns(true); - - $x = '$WowheadPower.registerObject('.$_id.', '.User::$localeId.", {\n"; - $x .= "\tname_".User::$localeString.": '".Util::jsEscape($object->getField('name', true))."',\n"; - $x .= "\ttooltip_".User::$localeString.": '".Util::jsEscape($object->renderTooltip())."'\n"; - // $x .= "\tmap: ".($s ? '{zone: '.$s[0].', coords: {0:'.json_encode($s[1], JSON_NUMERIC_CHECK).'}' : '{}')."\n"; - $x .= "});"; - - $smarty->saveCache($cacheKeyTooltip, $x); - } - - die($x); -} - -// regular page -if (!$smarty->loadCache($cacheKeyPage, $pageData)) -{ - $object = new GameObjectList(array(['id', $_id])); - if ($object->error) - $smarty->notFound(Lang::$game['gameObject'], $_id); - - $_path[] = $object->getField('typeCat'); + use DetailPage; + protected $type = TYPE_OBJECT; + protected $typeId = 0; + protected $tpl = 'object'; + protected $path = [0, 5]; + protected $tabId = 0; + protected $mode = CACHETYPE_PAGE; + protected $js = array( + 'swfobject.js', + // 'Mapper.js' + ); + protected $css = array( + // ['path' => 'Mapper.css'], + // ['path' => 'Mapper_ie6.css', 'ieCond' => 'lte IE 6'] + ); /* NOTE @@ -56,443 +32,474 @@ if (!$smarty->loadCache($cacheKeyPage, $pageData)) all of this has to be done manually */ - - /***********/ - /* Infobox */ - /***********/ - - $infobox = []; - - // Event - if ($_ = DB::Aowow()->selectRow('SELECT e.id, holidayId FROM ?_events e, game_event_gameobject geg, gameobject g WHERE e.id = ABS(geg.eventEntry) AND g.guid = geg.guid AND g.id = ?d', $_id)) + public function __construct($__, $id) { - if ($h = $_['holidayId']) - { - Util::$pageTemplate->extendGlobalIds(TYPE_WORLDEVENT, $_['id']); - $infobox[] = Util::ucFirst(Lang::$game['eventShort']).Lang::$colon.'[event='.$h.']'; - } - } + parent::__construct(); - // Reaction - $_ = function ($r) - { - if ($r == 1) return 2; - if ($r == -1) return 10; - return; - }; - $infobox[] = Lang::$npc['react'].Lang::$colon.'[color=q'.$_($object->getField('A')).']A[/color] [color=q'.$_($object->getField('H')).']H[/color]'; + // temp locale + if ($this->mode == CACHETYPE_TOOLTIP && isset($_GET['domain'])) + Util::powerUseLocale($_GET['domain']); - // reqSkill - switch ($object->getField('typeCat')) - { - case -3: // Herbalism - $infobox[] = sprintf(Lang::$game['requires'], Lang::$spell['lockType'][2].' ('.$object->getField('reqSkill').')'); - break; - case -4: // Mining - $infobox[] = sprintf(Lang::$game['requires'], Lang::$spell['lockType'][3].' ('.$object->getField('reqSkill').')'); - break; - case -5: // Lockpicking - $infobox[] = sprintf(Lang::$game['requires'], Lang::$spell['lockType'][1].' ('.$object->getField('reqSkill').')'); - break; - default: // requires key .. maybe - { - $locks = Lang::getLocks($object->getField('lockId')); - $l = ''; - foreach ($locks as $idx => $_) - { - if ($idx < 0) - continue; + $this->typeId = intVal($id); - Util::$pageTemplate->extendGlobalIds(TYPE_ITEM, $idx); - $l = Lang::$gameObject['key'].Lang::$colon.'[item='.$idx.']'; - } + $this->subject = new GameObjectList(array(['id', $this->typeId])); + if ($this->subject->error) + $this->notFound(Lang::$game['gameObject']); - // if no propper item is found use a skill - if ($locks) - $infobox[] = $l ? $l : array_pop($locks); - } - } - - // linked trap - if ($_ = $object->getField('linkedTrap')) - { - Util::$pageTemplate->extendGlobalIds(TYPE_OBJECT, $_); - $infobox[] = Lang::$gameObject['trap'].Lang::$colon.'[object='.$_.']'; - } - - // trap for - $trigger = new GameObjectList(array(['linkedTrap', $_id])); - if (!$trigger->error) - { - $trigger->addGlobalsToJScript(); - $infobox[] = Lang::$gameObject['triggeredBy'].Lang::$colon.'[object='.$trigger->id.']'; - } - - // SpellFocus - if ($_ = $object->getField('spellFocusId')) - $infobox[] = '[tooltip name=focus]'.Lang::$gameObject['focusDesc'].'[/tooltip][span class=tip tooltip=focus]'.Lang::$gameObject['focus'].Lang::$colon.Util::localizedString(DB::Aowow()->selectRow('SELECT * FROM ?_spellFocusObject WHERE id = ?d', $_), 'name').'[/span]'; - - // lootinfo: [min, max, restock] - if (($_ = $object->getField('lootStack')) && $_[0]) - { - $buff = Lang::$item['charges'].Lang::$colon.$_[0]; - if ($_[0] < $_[1]) - $buff .= Lang::$game['valueDelim'].$_[1]; - - // since Veins don't have charges anymore, the timer is questionable - $infobox[] = $_[2] > 1 ? '[tooltip name=restock][Recharges every '.Util::formatTime($_[2] * 1000).'][/tooltip][span class=tip tooltip=restock]'.$buff.'[/span]' : $buff; - } - - // meeting stone [minLevel, maxLevel, zone] - if ($object->getField('type') == OBJECT_MEETINGSTONE) - { - if ($_ = $object->getField('mStone')) - { - Util::$pageTemplate->extendGlobalIds(TYPE_ZONE, $_[2]); - $m = Lang::$game['meetingStone'].Lang::$colon.'[zone='.$_[2].']'; - - $l = $_[0]; - if ($_[0] > 1 && $_[1] > $_[0]) - $l .= Lang::$game['valueDelim'].min($_[1], MAX_LEVEL); - - $infobox[] = $l ? '[tooltip name=meetingstone]'.sprintf(Lang::$game['reqLevel'], $l).'[/tooltip][span class=tip tooltip=meetingstone]'.$m.'[/span]' : $m; - } - } - - // capture area [minPlayer, maxPlayer, minTime, maxTime, radius] - if ($object->getField('type') == OBJECT_CAPTURE_POINT) - { - if ($_ = $object->getField('capture')) - { - $buff = Lang::$gameObject['capturePoint']; - - if ($_[2] > 1 || $_[0]) - $buff .= Lang::$colon.'[ul]'; - - if ($_[2] > 1) - $buff .= '[li]'.Lang::$game['duration'].Lang::$colon.($_[3] > $_[2] ? Util::FormatTime($_[3] * 1000, true).' - ' : null).Util::FormatTime($_[2] * 1000, true).'[/li]'; - - if ($_[1]) - $buff .= '[li]'.Lang::$main['players'].Lang::$colon.$_[0].($_[1] > $_[0] ? ' - '.$_[1] : null).'[/li]'; - - if ($_[4]) - $buff .= '[li]'.sprintf(Lang::$spell['range'], $_[4]).'[/li]'; - - if ($_[2] > 1 || $_[0]) - $buff .= '[/ul]'; - } - - $infobox[] = $buff; - } - - // AI - if (User::isInGroup(U_GROUP_STAFF)) - { - if ($_ = $object->getField('ScriptName')) - $infobox[] = 'Script'.Lang::$colon.$_; - else if ($_ = $object->getField('AIName')) - $infobox[] = 'AI'.Lang::$colon.$_; - } - - - /****************/ - /* Main Content */ - /****************/ - - // pageText - $pageText = []; - if ($next = $object->getField('pageTextId')) - { - while ($next) - { - $row = DB::Aowow()->selectRow('SELECT *, text as Text_loc0 FROM page_text pt LEFT JOIN locales_page_text lpt ON pt.entry = lpt.entry WHERE pt.entry = ?d', $next); - $next = $row['next_page']; - $pageText[] = Util::parseHtmlText(Util::localizedString($row, 'Text')); - } - } - - // positions - $positions = []; -/* - $positions = position($object['entry'], 'gameobject'); - // Исправить type, чтобы подсвечивались event-овые объекты - if ($object['position']) - foreach ($object['position'] as $z => $zone) - foreach ($zone['points'] as $p => $pos) - if ($pos['type'] == 0 && ($events = event_find(array('object_guid' => $pos['guid'])))) - { - $names = arraySelectKey(event_name($events), 'name'); - $object['position'][$z]['points'][$p]['type'] = 4; - $object['position'][$z]['points'][$p]['events'] = implode(", ", $names); - } -*/ - - - // consider phaseMasks - - // consider pooled spawns - - - // menuId 5: Object g_initPath() - // tabId 0: Database g_initHeader() - $pageData = array( - 'page' => array( - 'title' => $object->getField('name', true).' - '.Util::ucFirst(Lang::$game['gameObject']), - 'path' => json_encode($_path, JSON_NUMERIC_CHECK), - 'name' => $object->getField('name', true), - 'tab' => 0, - 'type' => TYPE_OBJECT, - 'typeId' => $_id, - 'infobox' => $infobox ? '[ul][li]'.implode('[/li][li]', $infobox).'[/li][/ul]' : null, - 'pageText' => $pageText, - 'positions' => $positions, - 'redButtons' => array( - BUTTON_WOWHEAD => true, - BUTTON_LINKS => true, - BUTTON_VIEW3D => ['displayId' => $object->getField('displayId'), 'type' => TYPE_OBJECT, 'typeId' => $_id] - ), - 'reqCSS' => array( - $pageText ? ['path' => STATIC_URL.'/css/Book.css'] : null, - // ['path' => STATIC_URL.'/css/Mapper.css'], - // ['path' => STATIC_URL.'/css/Mapper_ie6.css', 'ieCond' => 'lte IE 6'] - ), - 'reqJS' => array( - $pageText ? STATIC_URL.'/js/Book.js' : null, - // STATIC_URL.'/js/Mapper.js', - STATIC_URL.'/js/swfobject.js' - ) - ), - 'relTabs' => [] - ); - - - /**************/ - /* Extra Tabs */ - /**************/ - - // tab: summoned by - $conditions = array( - 'OR', - ['AND', ['effect1Id', [50, 76, 104, 105, 106, 107]], ['effect1MiscValue', $_id]], - ['AND', ['effect2Id', [50, 76, 104, 105, 106, 107]], ['effect2MiscValue', $_id]], - ['AND', ['effect3Id', [50, 76, 104, 105, 106, 107]], ['effect3MiscValue', $_id]] - ); - - $summons = new SpellList($conditions); - if (!$summons->error) - { - $summons->addGlobalsToJScript(GLOBALINFO_SELF | GLOBALINFO_RELATED); - - $pageData['relTabs'][] = array( - 'file' => 'spell', - 'data' => $summons->getListviewData(), - 'params' => [ - 'tabs' => '$tabsRelated', - 'id' => 'summoned-by', - 'name' => '$LANG.tab_summonedby' - ] + $this->name = $this->subject->getField('name', true); + $this->gPageInfo = array( + 'type' => $this->type, + 'typeId' => $this->typeId, + 'name' => $this->name ); } - // tab: related spells - if ($_ = $object->getField('spells')) + protected function generatePath() { - $relSpells = new SpellList(array(['id', $_])); - if (!$relSpells->error) - { - $relSpells->addGlobalsToJScript(GLOBALINFO_SELF | GLOBALINFO_RELATED); - $data = $relSpells->getListviewData(); - - foreach ($data as $relId => $d) - $data[$relId]['trigger'] = array_search($relId, $_); - - $pageData['relTabs'][] = array( - 'file' => 'spell', - 'data' => $data, - 'params' => [ - 'tabs' => '$tabsRelated', - 'id' => 'spells', - 'name' => '$LANG.tab_spells', - 'hiddenCols' => "$['skill']", - 'extraCols' => "$[Listview.funcBox.createSimpleCol('trigger', 'Condition', '10%', 'trigger')]" - ] - ); - } + $this->path[] = $this->subject->getField('typeCat'); } - // tab: criteria of - $acvs = new AchievementList(array(['ac.type', [ACHIEVEMENT_CRITERIA_TYPE_USE_GAMEOBJECT, ACHIEVEMENT_CRITERIA_TYPE_FISH_IN_GAMEOBJECT]], ['ac.value1', $_id])); - if (!$acvs->error) + protected function generateTitle() { - $acvs->addGlobalsToJScript(GLOBALINFO_SELF | GLOBALINFO_RELATED); - - $pageData['relTabs'][] = array( - 'file' => 'achievement', - 'data' => $acvs->getListviewData(), - 'params' => [ - 'tabs' => '$tabsRelated', - 'id' => 'criteria-of', - 'name' => '$LANG.tab_criteriaof' - ] - ); + array_unshift($this->title, $this->subject->getField('name', true), Util::ucFirst(Lang::$game['gameObject'])); } - // tab: starts quest - // tab: ends quest - $startEnd = new QuestList(array(['qse.type', TYPE_OBJECT], ['qse.typeId', $_id])); - if (!$startEnd->error) + protected function generateContent() { - $startEnd->addGlobalsToJScript(); - $lvData = $startEnd->getListviewData(); - $_ = [[], []]; + /***********/ + /* Infobox */ + /***********/ - foreach ($startEnd->iterate() as $id => $__) + $infobox = []; + + // Event + if ($_ = DB::Aowow()->selectRow('SELECT e.id, holidayId FROM ?_events e, game_event_gameobject geg, gameobject g WHERE e.id = ABS(geg.eventEntry) AND g.guid = geg.guid AND g.id = ?d', $this->typeId)) { - $m = $startEnd->getField('method'); - if ($m & 0x1) - $_[0][] = $lvData[$id]; - if ($m & 0x2) - $_[1][] = $lvData[$id]; - } - - if ($_[0]) - { - $pageData['relTabs'][] = array( - 'file' => 'quest', - 'data' => $_[0], - 'params' => [ - 'tabs' => '$tabsRelated', - 'name' => '$LANG.tab_starts', - 'id' => 'starts' - ] - ); - } - - if ($_[1]) - { - $pageData['relTabs'][] = array( - 'file' => 'quest', - 'data' => $_[1], - 'params' => [ - 'tabs' => '$tabsRelated', - 'name' => '$LANG.tab_ends', - 'id' => 'ends' - ] - ); - } - } - - // tab: related quests - if ($_ = $object->getField('reqQuest')) - { - $relQuest = new QuestList(array(['id', $_])); - if (!$relQuest->error) - { - $relQuest->addGlobalsToJScript(); - - $pageData['relTabs'][] = array( - 'file' => 'quest', - 'data' => $relQuest->getListviewData(), - 'params' => [ - 'tabs' => '$tabsRelated', - 'name' => '$LANG.tab_quests', - 'id' => 'quests' - ] - ); - } - } - - // tab: contains - $reqQuest = []; - if ($_ = $object->getField('lootId')) - { - if ($itemLoot = Util::handleLoot(LOOT_GAMEOBJECT, $_, User::isInGroup(U_GROUP_STAFF), $extraCols)) - { - $hiddenCols = ['source', 'side', 'slot', 'reqlevel']; - foreach ($itemLoot as $l => $lv) + if ($h = $_['holidayId']) { - if (!empty($hiddenCols)) - foreach ($hiddenCols as $k => $str) - if (!empty($lv[$str])) - unset($hiddenCols[$k]); + Util::$pageTemplate->extendGlobalIds(TYPE_WORLDEVENT, $_['id']); + $infobox[] = Util::ucFirst(Lang::$game['eventShort']).Lang::$main['colon'].'[event='.$h.']'; + } + } - if (!$lv['quest']) - continue; + // Reaction + $_ = function ($r) + { + if ($r == 1) return 2; + if ($r == -1) return 10; + return; + }; + $infobox[] = Lang::$npc['react'].Lang::$main['colon'].'[color=q'.$_($this->subject->getField('A')).']A[/color] [color=q'.$_($this->subject->getField('H')).']H[/color]'; - $extraCols[] = 'Listview.extraCols.condition'; + // reqSkill + switch ($this->subject->getField('typeCat')) + { + case -3: // Herbalism + $infobox[] = sprintf(Lang::$game['requires'], Lang::$spell['lockType'][2].' ('.$this->subject->getField('reqSkill').')'); + break; + case -4: // Mining + $infobox[] = sprintf(Lang::$game['requires'], Lang::$spell['lockType'][3].' ('.$this->subject->getField('reqSkill').')'); + break; + case -5: // Lockpicking + $infobox[] = sprintf(Lang::$game['requires'], Lang::$spell['lockType'][1].' ('.$this->subject->getField('reqSkill').')'); + break; + default: // requires key .. maybe + { + $locks = Lang::getLocks($this->subject->getField('lockId')); + $l = ''; + foreach ($locks as $idx => $_) + { + if ($idx < 0) + continue; - $reqQuest[$lv['id']] = 0; + Util::$pageTemplate->extendGlobalIds(TYPE_ITEM, $idx); + $l = Lang::$gameObject['key'].Lang::$main['colon'].'[item='.$idx.']'; + } - $itemLoot[$l]['condition'][] = ['type' => TYPE_QUEST, 'typeId' => &$reqQuest[$lv['id']], 'status' => 1]; + // if no propper item is found use a skill + if ($locks) + $infobox[] = $l ? $l : array_pop($locks); + } + } + + // linked trap + if ($_ = $this->subject->getField('linkedTrap')) + { + Util::$pageTemplate->extendGlobalIds(TYPE_OBJECT, $_); + $infobox[] = Lang::$gameObject['trap'].Lang::$main['colon'].'[object='.$_.']'; + } + + // trap for + $trigger = new GameObjectList(array(['linkedTrap', $this->typeId])); + if (!$trigger->error) + { + $this->extendGlobalData($trigger->getJSGlobals()); + $infobox[] = Lang::$gameObject['triggeredBy'].Lang::$main['colon'].'[object='.$trigger->id.']'; + } + + // SpellFocus + if ($_ = $this->subject->getField('spellFocusId')) + $infobox[] = '[tooltip name=focus]'.Lang::$gameObject['focusDesc'].'[/tooltip][span class=tip tooltip=focus]'.Lang::$gameObject['focus'].Lang::$main['colon'].Util::localizedString(DB::Aowow()->selectRow('SELECT * FROM ?_spellFocusObject WHERE id = ?d', $_), 'name').'[/span]'; + + // lootinfo: [min, max, restock] + if (($_ = $this->subject->getField('lootStack')) && $_[0]) + { + $buff = Lang::$item['charges'].Lang::$main['colon'].$_[0]; + if ($_[0] < $_[1]) + $buff .= Lang::$game['valueDelim'].$_[1]; + + // since Veins don't have charges anymore, the timer is questionable + $infobox[] = $_[2] > 1 ? '[tooltip name=restock][Recharges every '.Util::formatTime($_[2] * 1000).'][/tooltip][span class=tip tooltip=restock]'.$buff.'[/span]' : $buff; + } + + // meeting stone [minLevel, maxLevel, zone] + if ($this->subject->getField('type') == OBJECT_MEETINGSTONE) + { + if ($_ = $this->subject->getField('mStone')) + { + Util::$pageTemplate->extendGlobalIds(TYPE_ZONE, $_[2]); + $m = Lang::$game['meetingStone'].Lang::$main['colon'].'[zone='.$_[2].']'; + + $l = $_[0]; + if ($_[0] > 1 && $_[1] > $_[0]) + $l .= Lang::$game['valueDelim'].min($_[1], MAX_LEVEL); + + $infobox[] = $l ? '[tooltip name=meetingstone]'.sprintf(Lang::$game['reqLevel'], $l).'[/tooltip][span class=tip tooltip=meetingstone]'.$m.'[/span]' : $m; + } + } + + // capture area [minPlayer, maxPlayer, minTime, maxTime, radius] + if ($this->subject->getField('type') == OBJECT_CAPTURE_POINT) + { + if ($_ = $this->subject->getField('capture')) + { + $buff = Lang::$gameObject['capturePoint']; + + if ($_[2] > 1 || $_[0]) + $buff .= Lang::$main['colon'].'[ul]'; + + if ($_[2] > 1) + $buff .= '[li]'.Lang::$game['duration'].Lang::$main['colon'].($_[3] > $_[2] ? Util::FormatTime($_[3] * 1000, true).' - ' : null).Util::FormatTime($_[2] * 1000, true).'[/li]'; + + if ($_[1]) + $buff .= '[li]'.Lang::$main['players'].Lang::$main['colon'].$_[0].($_[1] > $_[0] ? ' - '.$_[1] : null).'[/li]'; + + if ($_[4]) + $buff .= '[li]'.sprintf(Lang::$spell['range'], $_[4]).'[/li]'; + + if ($_[2] > 1 || $_[0]) + $buff .= '[/ul]'; } - $extraCols[] = 'Listview.extraCols.percent'; - - $pageData['relTabs'][] = array( - 'file' => 'item', - 'data' => $itemLoot, - 'params' => [ - 'tabs' => '$tabsRelated', - 'name' => '$LANG.tab_contains', - 'id' => 'contains', - 'extraCols' => "$[".implode(', ', array_unique($extraCols))."]", - 'hiddenCols' => $hiddenCols ? '$'.json_encode(array_values($hiddenCols)) : null - ] - ); + $infobox[] = $buff; } - } - if ($reqIds = array_keys($reqQuest)) // apply quest-conditions as back-reference - { + // AI + if (User::isInGroup(U_GROUP_STAFF)) + { + if ($_ = $this->subject->getField('ScriptName')) + $infobox[] = 'Script'.Lang::$main['colon'].$_; + else if ($_ = $this->subject->getField('AIName')) + $infobox[] = 'AI'.Lang::$main['colon'].$_; + } + + + /****************/ + /* Main Content */ + /****************/ + + // pageText + $pageText = []; + if ($next = $this->subject->getField('pageTextId')) + { + while ($next) + { + $row = DB::Aowow()->selectRow('SELECT *, text as Text_loc0 FROM page_text pt LEFT JOIN locales_page_text lpt ON pt.entry = lpt.entry WHERE pt.entry = ?d', $next); + $next = $row['next_page']; + $pageText[] = Util::parseHtmlText(Util::localizedString($row, 'Text')); + } + } + + // add conditional js & css + if ($pageText) + { + $this->addCSS(['path' => 'Book.css']); + $this->addJS('Book.js'); + } + + // positions + $positions = []; + /* + $positions = position($object['entry'], 'gameobject'); + // Исправить type, чтобы подсвечивались event-овые объекты + if ($object['position']) + foreach ($object['position'] as $z => $zone) + foreach ($zone['points'] as $p => $pos) + if ($pos['type'] == 0 && ($events = event_find(array('object_guid' => $pos['guid'])))) + { + $names = arraySelectKey(event_name($events), 'name'); + $object['position'][$z]['points'][$p]['type'] = 4; + $object['position'][$z]['points'][$p]['events'] = implode(", ", $names); + } + */ + + + // consider phaseMasks + + // consider pooled spawns + + $this->infobox = $infobox ? '[ul][li]'.implode('[/li][li]', $infobox).'[/li][/ul]' : null; + $this->pageText = $pageText; + $this->positions = $positions; + $this->redButtons = array( + BUTTON_WOWHEAD => true, + BUTTON_LINKS => true, + BUTTON_VIEW3D => ['displayId' => $this->subject->getField('displayId'), 'type' => TYPE_OBJECT, 'typeId' => $this->typeId] + ); + + + /**************/ + /* Extra Tabs */ + /**************/ + + // tab: summoned by $conditions = array( 'OR', - ['reqSourceItemId1', $reqIds], ['reqSourceItemId2', $reqIds], - ['reqSourceItemId3', $reqIds], ['reqSourceItemId4', $reqIds], - ['reqItemId1', $reqIds], ['reqItemId2', $reqIds], ['reqItemId3', $reqIds], - ['reqItemId4', $reqIds], ['reqItemId5', $reqIds], ['reqItemId6', $reqIds] + ['AND', ['effect1Id', [50, 76, 104, 105, 106, 107]], ['effect1MiscValue', $this->typeId]], + ['AND', ['effect2Id', [50, 76, 104, 105, 106, 107]], ['effect2MiscValue', $this->typeId]], + ['AND', ['effect3Id', [50, 76, 104, 105, 106, 107]], ['effect3MiscValue', $this->typeId]] ); - $reqQuests = new QuestList($conditions); - $reqQuests->addGlobalsToJscript(); - - foreach ($reqQuests->iterate() as $qId => $__) + $summons = new SpellList($conditions); + if (!$summons->error) { - if (empty($reqQuests->requires[$qId][TYPE_ITEM])) - continue; + $this->extendGlobalData($summons->getJSGlobals(GLOBALINFO_SELF | GLOBALINFO_RELATED)); - foreach ($reqIds as $rId) - if (in_array($rId, $reqQuests->requires[$qId][TYPE_ITEM])) - $reqQuest[$rId] = $reqQuests->id; + $this->lvData[] = array( + 'file' => 'spell', + 'data' => $summons->getListviewData(), + 'params' => [ + 'tabs' => '$tabsRelated', + 'id' => 'summoned-by', + 'name' => '$LANG.tab_summonedby' + ] + ); + } + + // tab: related spells + if ($_ = $this->subject->getField('spells')) + { + $relSpells = new SpellList(array(['id', $_])); + if (!$relSpells->error) + { + $this->extendGlobalData($relSpells->getJSGlobals(GLOBALINFO_SELF | GLOBALINFO_RELATED)); + $data = $relSpells->getListviewData(); + + foreach ($data as $relId => $d) + $data[$relId]['trigger'] = array_search($relId, $_); + + $this->lvData[] = array( + 'file' => 'spell', + 'data' => $data, + 'params' => [ + 'tabs' => '$tabsRelated', + 'id' => 'spells', + 'name' => '$LANG.tab_spells', + 'hiddenCols' => "$['skill']", + 'extraCols' => "$[Listview.funcBox.createSimpleCol('trigger', 'Condition', '10%', 'trigger')]" + ] + ); + } + } + + // tab: criteria of + $acvs = new AchievementList(array(['ac.type', [ACHIEVEMENT_CRITERIA_TYPE_USE_GAMEOBJECT, ACHIEVEMENT_CRITERIA_TYPE_FISH_IN_GAMEOBJECT]], ['ac.value1', $this->typeId])); + if (!$acvs->error) + { + $this->extendGlobalData($acvs->getJSGlobals(GLOBALINFO_SELF | GLOBALINFO_RELATED)); + + $this->lvData[] = array( + 'file' => 'achievement', + 'data' => $acvs->getListviewData(), + 'params' => [ + 'tabs' => '$tabsRelated', + 'id' => 'criteria-of', + 'name' => '$LANG.tab_criteriaof' + ] + ); + } + + // tab: starts quest + // tab: ends quest + $startEnd = new QuestList(array(['qse.type', TYPE_OBJECT], ['qse.typeId', $this->typeId])); + if (!$startEnd->error) + { + $this->extendGlobalData($startEnd->getJSGlobals()); + $lvData = $startEnd->getListviewData(); + $_ = [[], []]; + + foreach ($startEnd->iterate() as $id => $__) + { + $m = $startEnd->getField('method'); + if ($m & 0x1) + $_[0][] = $lvData[$id]; + if ($m & 0x2) + $_[1][] = $lvData[$id]; + } + + if ($_[0]) + { + $this->lvData[] = array( + 'file' => 'quest', + 'data' => $_[0], + 'params' => [ + 'tabs' => '$tabsRelated', + 'name' => '$LANG.tab_starts', + 'id' => 'starts' + ] + ); + } + + if ($_[1]) + { + $this->lvData[] = array( + 'file' => 'quest', + 'data' => $_[1], + 'params' => [ + 'tabs' => '$tabsRelated', + 'name' => '$LANG.tab_ends', + 'id' => 'ends' + ] + ); + } + } + + // tab: related quests + if ($_ = $this->subject->getField('reqQuest')) + { + $relQuest = new QuestList(array(['id', $_])); + if (!$relQuest->error) + { + $this->extendGlobalData($relQuest->getJSGlobals()); + + $this->lvData[] = array( + 'file' => 'quest', + 'data' => $relQuest->getListviewData(), + 'params' => [ + 'tabs' => '$tabsRelated', + 'name' => '$LANG.tab_quests', + 'id' => 'quests' + ] + ); + } + } + + // tab: contains + $reqQuest = []; + if ($_ = $this->subject->getField('lootId')) + { + include 'includes/loot.class.php'; + + if (Loot::getByContainer(LOOT_GAMEOBJECT, $_)) + { + $extraCols = Loot::$extraCols; + $hiddenCols = ['source', 'side', 'slot', 'reqlevel']; + $itemLoot = Loot::getResult(); + + $this->extendGlobalData(Loot::$jsGlobals); + + foreach ($itemLoot as $l => $lv) + { + if (!empty($hiddenCols)) + foreach ($hiddenCols as $k => $str) + if (!empty($lv[$str])) + unset($hiddenCols[$k]); + + if (!$lv['quest']) + continue; + + $extraCols[] = 'Listview.extraCols.condition'; + + $reqQuest[$lv['id']] = 0; + + $itemLoot[$l]['condition'][] = ['type' => TYPE_QUEST, 'typeId' => &$reqQuest[$lv['id']], 'status' => 1]; + } + + $extraCols[] = 'Listview.extraCols.percent'; + + $this->lvData[] = array( + 'file' => 'item', + 'data' => $itemLoot, + 'params' => [ + 'tabs' => '$tabsRelated', + 'name' => '$LANG.tab_contains', + 'id' => 'contains', + 'extraCols' => "$[".implode(', ', array_unique($extraCols))."]", + 'hiddenCols' => $hiddenCols ? '$'.json_encode(array_values($hiddenCols)) : null + ] + ); + } + } + + if ($reqIds = array_keys($reqQuest)) // apply quest-conditions as back-reference + { + $conditions = array( + 'OR', + ['reqSourceItemId1', $reqIds], ['reqSourceItemId2', $reqIds], + ['reqSourceItemId3', $reqIds], ['reqSourceItemId4', $reqIds], + ['reqItemId1', $reqIds], ['reqItemId2', $reqIds], ['reqItemId3', $reqIds], + ['reqItemId4', $reqIds], ['reqItemId5', $reqIds], ['reqItemId6', $reqIds] + ); + + $reqQuests = new QuestList($conditions); + $this->extendGlobalData($reqQuests->getJSGlobals()); + + foreach ($reqQuests->iterate() as $qId => $__) + { + if (empty($reqQuests->requires[$qId][TYPE_ITEM])) + continue; + + foreach ($reqIds as $rId) + if (in_array($rId, $reqQuests->requires[$qId][TYPE_ITEM])) + $reqQuest[$rId] = $reqQuests->id; + } + } + + // tab: Same model as .. whats the fucking point..? + $sameModel = new GameObjectList(array(['displayId', $this->subject->getField('displayId')], ['id', $this->typeId, '!'])); + if (!$sameModel->error) + { + $this->extendGlobalData($sameModel->getJSGlobals()); + + $this->lvData[] = array( + 'file' => 'object', + 'data' => $sameModel->getListviewData(), + 'params' => [ + 'tabs' => '$tabsRelated', + 'name' => '$LANG.tab_samemodelas', + 'id' => 'same-model-as' + ] + ); } } - // tab: Same model as .. whats the fucking point..? - $sameModel = new GameObjectList(array(['displayId', $object->getField('displayId')], ['id', $_id, '!'])); - if (!$sameModel->error) - { - $sameModel->addGlobalsToJScript(); - $pageData['relTabs'][] = array( - 'file' => 'object', - 'data' => $sameModel->getListviewData(), - 'params' => [ - 'tabs' => '$tabsRelated', - 'name' => '$LANG.tab_samemodelas', - 'id' => 'same-model-as' - ] - ); + protected function generateTooltip($asError = false) + { + if ($asError) + die('$WowheadPower.registerObject('.$this->typeId.', '.User::$localeId.', {});'); + + $s = $this->subject->getSpawns(true); + + $x = '$WowheadPower.registerObject('.$this->typeId.', '.User::$localeId.", {\n"; + $x .= "\tname_".User::$localeString.": '".Util::jsEscape($this->subject->getField('name', true))."',\n"; + $x .= "\ttooltip_".User::$localeString.": '".Util::jsEscape($this->subject->renderTooltip())."'\n"; + // $x .= "\tmap: ".($s ? '{zone: '.$s[0].', coords: {0:'.json_encode($s[1], JSON_NUMERIC_CHECK).'}' : '{}')."\n"; + $x .= "});"; + + return $x; } - $smarty->saveCache($cacheKeyPage, $pageData); } - -$smarty->updatePageVars($pageData['page']); -$smarty->assign('community', CommunityContent::getAll(TYPE_OBJECT, $_id)); // comments, screenshots, videos -$smarty->assign('lang', array_merge(Lang::$main, Lang::$game, Lang::$item, Lang::$gameObject, ['colon' => Lang::$colon])); -$smarty->assign('lvData', $pageData['relTabs']); - -// load the page -$smarty->display('object.tpl'); - ?> diff --git a/pages/objects.php b/pages/objects.php index a9bcc294..1ac0f0d4 100644 --- a/pages/objects.php +++ b/pages/objects.php @@ -4,82 +4,87 @@ if (!defined('AOWOW_REVISION')) die('illegal access'); -$filter = []; -$conditions = []; -$cat = Util::extractURLParams($pageParam); -$path = [0, 5]; -$validCats = [-2, -3, -4, -5, -6, 0, 3, 9, 25]; -$title = [Util::ucFirst(Lang::$game['gameObjects'])]; -$cacheKey = implode('_', [CACHETYPE_PAGE, TYPE_OBJECT, -1, $cat ? $cat[0] : -1, User::$localeId]); - -if (!Util::isValidPage($validCats, $cat)) - $smarty->error(); - -if ($cat) +// menuId 5: Object g_initPath() +// tabId 0: Database g_initHeader() +class ObjectsPage extends GenericPage { - $path[] = $cat[0]; - array_unshift($title, Lang::$gameObject['cat'][$cat[0]]); - $conditions[] = ['typeCat', (int)$cat[0]]; -} + use ListPage; -if (!$smarty->loadCache($cacheKey, $pageData, $filter)) -{ + protected $type = TYPE_OBJECT; + protected $tpl = 'objects'; + protected $path = [0, 5]; + protected $tabId = 0; + protected $mode = CACHETYPE_PAGE; + protected $validCats = [-2, -3, -4, -5, -6, 0, 3, 9, 25]; + protected $js = ['filters.js']; - $objectFilter = new GameObjectListFilter(); - if ($_ = $objectFilter->getConditions()) - $conditions[] = $_; - - $objects = new GameObjectList($conditions, ['extraOpts' => $objectFilter->extraOpts]); - - // menuId 5: Object g_initPath() - // tabId 0: Database g_initHeader() - $pageData = array( - 'page' => array( - 'tab' => 0, - 'title' => implode(" - ", $title), - 'path' => json_encode($path, JSON_NUMERIC_CHECK), - 'subCat' => $pageParam ? '='.$pageParam : '', - 'reqJS' => [STATIC_URL.'/js/filters.js'] - ), - 'lv' => [] - ); - - // recreate form selection - $filter = array_merge($objectFilter->getForm('form'), $filter); - $filter['query'] = isset($_GET['filter']) ? $_GET['filter'] : NULL; - $filter['fi'] = $objectFilter->getForm(); - - $params = []; - if ($objects->hasSetFields(['reqSkill'])) - $params['visibleCols'] = "$['skill']"; - - $lv = array( - 'file' => 'object', - 'data' => $objects->getListviewData(), - 'params' => $params - ); - - // create note if search limit was exceeded - if ($objects->getMatches() > CFG_SQL_LIMIT_DEFAULT) + public function __construct($pageCall, $pageParam) { - $lv['params']['note'] = sprintf(Util::$tryFilteringString, 'LANG.lvnote_objectsfound', $objects->getMatches(), CFG_SQL_LIMIT_DEFAULT); - $lv['params']['_truncated'] = 1; + $this->category = Util::extractURLParams($pageParam); + + parent::__construct(); + + $this->name = Util::ucFirst(Lang::$game['gameObjects']); + $this->subCat = $pageParam ? '='.$pageParam : ''; } - if ($objectFilter->error) - $lv['params']['_errors'] = '$1'; + protected function generateContent() + { + $conditions = []; - $pageData['lv'] = $lv; + if ($this->category) + $conditions[] = ['typeCat', (int)$this->category[0]]; - $smarty->saveCache($cacheKey, $pageData, $filter); + $objectFilter = new GameObjectListFilter(); + + // recreate form selection + $this->filter = $objectFilter->getForm('form'); + $this->filter['query'] = isset($_GET['filter']) ? $_GET['filter'] : null; + $this->filter['fi'] = $objectFilter->getForm(); + + if ($_ = $objectFilter->getConditions()) + $conditions[] = $_; + + $params = $data = []; + $objects = new GameObjectList($conditions, ['extraOpts' => $objectFilter->extraOpts]); + if (!$objects->error) + { + $data = $objects->getListviewData(); + if ($objects->hasSetFields(['reqSkill'])) + $params['visibleCols'] = "$['skill']"; + + + // create note if search limit was exceeded + if ($objects->getMatches() > CFG_SQL_LIMIT_DEFAULT) + { + $params['note'] = sprintf(Util::$tryFilteringString, 'LANG.lvnote_objectsfound', $objects->getMatches(), CFG_SQL_LIMIT_DEFAULT); + $params['_truncated'] = 1; + } + + if ($objectFilter->error) + $params['_errors'] = '$1'; + + } + + $this->lvData = array( + 'file' => 'object', + 'data' => $data, + 'params' => $params + ); + } + + protected function generateTitle() + { + array_unshift($this->title, $this->name); + if ($this->category) + array_unshift($this->title, Lang::$gameObject['cat'][$this->category[0]]); + } + + protected function generatePath() + { + if ($this->category) + $this->path[] = $this->category[0]; + } } -$smarty->updatePageVars($pageData['page']); -$smarty->assign('filter', $filter); -$smarty->assign('lang', array_merge(Lang::$main, ['colon' => Lang::$colon])); -$smarty->assign('lvData', $pageData['lv']); - -// load the page -$smarty->display('objects.tpl'); - ?> diff --git a/static/js/global.js b/static/js/global.js index fdd6517d..12753260 100644 --- a/static/js/global.js +++ b/static/js/global.js @@ -18386,16 +18386,16 @@ var Links = new function() { fields: [ { - id: 'wowheadurl', - type: 'text', + id: 'wowheadurl', + type: 'text', label: 'Aowow URL', - size: 40 + size: 40 }, { - id: 'markuptag', - type: 'text', + id: 'markuptag', + type: 'text', label: 'Markup Tag', - size: 40 + size: 40 } ], @@ -18405,7 +18405,7 @@ var Links = new function() { onShow: function(form) { setTimeout(function() { - document.getElementsByName('ingamelink')[0].select(); + $(form.ingamelink).select(); }, 50); setTimeout(Lightbox.reveal, 100); } diff --git a/template/bricks/book.tpl.php b/template/bricks/book.tpl.php index fb7b23bc..5529ceb4 100644 --- a/template/bricks/book.tpl.php +++ b/template/bricks/book.tpl.php @@ -2,7 +2,7 @@ if (!empty($this->pageText)): ?>
-

+

\n"; + echo ' \n"; endif; endforeach; ?> diff --git a/template/bricks/redButtons.tpl.php b/template/bricks/redButtons.tpl.php index 6e4996bd..c4167ecc 100644 --- a/template/bricks/redButtons.tpl.php +++ b/template/bricks/redButtons.tpl.php @@ -22,7 +22,14 @@ endif; // view in 3D if (isset($this->redButtons[BUTTON_VIEW3D])): if ($b = $this->redButtons[BUTTON_VIEW3D]): - echo ''.Lang::$main['view3D'].''.Lang::$main['view3D'].''; + { + // json_encode puts property names in brackets wich is not cool with inline javascript + $data = []; + foreach ($b as $k => $v) + $data[] = $k.': '.json_encode($v, JSON_NUMERIC_CHECK); + + echo ''.Lang::$main['view3D'].''.Lang::$main['view3D'].''; + } else: echo ''.Lang::$main['view3D'].''.Lang::$main['view3D'].''; endif; diff --git a/template/pages/object.tpl b/template/pages/object.tpl.php similarity index 71% rename from template/pages/object.tpl rename to template/pages/object.tpl.php index d8df761b..42e29dff 100644 --- a/template/pages/object.tpl +++ b/template/pages/object.tpl.php @@ -1,38 +1,37 @@ -{include file='header.tpl'} +brick('header'); ?>
-{if !empty($announcements)} - {foreach from=$announcements item=item} - {include file='bricks/announcement.tpl' an=$item} - {/foreach} -{/if} +brick('announcement'); ?> -{include file='bricks/infobox.tpl'} +brick('infobox'); ?>
-{include file='bricks/redButtons.tpl'} +brick('redButtons'); ?> -

{$name}

+

name; ?>

-{include file='bricks/article.tpl'} +brick('article'); -{if $positions} +if ($this->positions): +?>
{#This_Object_can_be_found_in#} -{strip} {foreach from=$object.position item=zone name=zone}
',type:'{$point.type}' - {rdelim}] + }] {if !$smarty.foreach.point.last},{/if} {/foreach} ] {/if} - {rdelim}); + }); g_setSelectedLink(this, 'mapper'); return false" onmousedown="return false"> {$zone.name}{if $zone.population > 1} ({$zone.population}){/if}{if $smarty.foreach.zone.last}.{else}, {/if} {/foreach}
-{/strip} +
-{else} - {$lang.unkPosition} -{/if} -{include file='bricks/book.tpl'} +{$lang.related} +$this->brick('book'); +?> + +

-{include file='bricks/tabsRelated.tpl' tabs=$lvData} +brick('tabsRelated'); ?> -{include file='bricks/contribute.tpl'} +brick('contribute'); ?>
-{include file='footer.tpl'} +brick('footer'); ?> diff --git a/template/pages/objects.tpl b/template/pages/objects.tpl deleted file mode 100644 index 898fcf51..00000000 --- a/template/pages/objects.tpl +++ /dev/null @@ -1,61 +0,0 @@ -{include file='header.tpl'} - -
-
-
- -{if !empty($announcements)} - {foreach from=$announcements item=item} - {include file='bricks/announcement.tpl' an=$item} - {/foreach} -{/if} - - - -
-
- - -
{$lang.name|ucFirst}:  
- -
- - -
-
{$lang.refineSearch}
- {$lang.match}{$lang.colon} -
- -
- -
- - -
- -
-
-
- - - -
- - -
-
-
- -{include file='footer.tpl'} diff --git a/template/pages/objects.tpl.php b/template/pages/objects.tpl.php new file mode 100644 index 00000000..709e7604 --- /dev/null +++ b/template/pages/objects.tpl.php @@ -0,0 +1,61 @@ +brick('header'); ?> + +
+
+
+ +brick('announcement'); ?> + + + +
+
+ + +
 filter['na']) ? 'value="'.Util::htmlEscape($this->filter['na']).'" ' : null; ?>/>
+ +
+
+ +
+
+ filter['ma']) ? 'checked="checked" ' : null ?>/>filter['ma']) ? 'checked="checked" ' : null ?> /> +
+ +
+ +
+ + +
+ +
+
+
+ + + +
+ + +
+
+
+ +brick('footer'); ?>