diff --git a/includes/types/itemset.class.php b/includes/types/itemset.class.php index 7fdc4d3c..ece86b0e 100644 --- a/includes/types/itemset.class.php +++ b/includes/types/itemset.class.php @@ -136,7 +136,8 @@ class ItemsetListFilter extends Filter // name [str] if (isset($_v['na'])) - $parts[] = ['name_loc'.User::$localeId, $_v['na']]; + if ($_ = $this->modularizeString(['name_loc'.User::$localeId])) + $parts[] = $_; // quality [enum] if (isset($_v['qu'])) diff --git a/pages/class.php b/pages/class.php index c3856b71..9e535f99 100644 --- a/pages/class.php +++ b/pages/class.php @@ -38,7 +38,7 @@ class ClassPage extends GenericPage protected function generateTitle() { - array_unshift($this->title, $this->subject->getField('name', true), Util::ucFirst(Lang::$game['class'])); + array_unshift($this->title, $this->name, Util::ucFirst(Lang::$game['class'])); } protected function generateContent() diff --git a/pages/itemset.php b/pages/itemset.php index 499cf9c4..e1596f46 100644 --- a/pages/itemset.php +++ b/pages/itemset.php @@ -4,278 +4,272 @@ if (!defined('AOWOW_REVISION')) die('illegal access'); -require 'includes/community.class.php'; - -$_id = intVal($pageParam); -$path = [0, 2]; - -$cacheKeyPage = implode('_', [CACHETYPE_PAGE, TYPE_ITEMSET, $_id, -1, User::$localeId]); - -if (!$smarty->loadCache($cacheKeyPage, $pageData)) +// menuId 2: Itemset g_initPath() +// tabId 0: Database g_initHeader() +class ItemsetPage extends GenericPage { - $iSet = new ItemsetList(array(['id', $_id])); - if ($iSet->error) - $smarty->notFound(Lang::$game['itemset'], $_id); + use DetailPage; - $_ta = $iSet->getField('contentGroup'); - $_ty = $iSet->getField('type'); - $_ev = $iSet->getField('holidayId'); - $_sk = $iSet->getField('skillId'); - $_cl = $iSet->getField('classMask'); - $_lvl = $iSet->getField('reqLevel'); - $_na = $iSet->getField('name', true); - $_cnt = count($iSet->getField('pieces')); + protected $type = TYPE_ITEMSET; + protected $typeId = 0; + protected $tpl = 'itemset'; + protected $path = [0, 2]; + protected $tabId = 0; + protected $mode = CACHETYPE_PAGE; + protected $js = array( + 'swfobject.js', + 'Summary.js' + ); - /***********/ - /* Infobox */ - /***********/ - - $infobox = []; - // unavailable (todo (low): set data) - if ($iSet->getField('cuFlags') & CUSTOM_UNAVAILABLE) - $infobox[] = Lang::$main['unavailable']; - - // holiday - if ($_ev) - $infobox[] = Lang::$game['eventShort'].Lang::$main['colon'].'[url=?event='.$_ev.']'.WorldEventList::getName($_ev).'[/url]'; - - // itemLevel - if ($min = $iSet->getField('minLevel')) + public function __construct($__, $id) { - $foo = Lang::$game['level'].Lang::$main['colon'].$min; - $max = $iSet->getField('maxLevel'); + parent::__construct(); - if ($min < $max) - $foo .= ' - '.$max; + $this->typeId = intVal($id); - $infobox[] = $foo; + $this->subject = new ItemsetList(array(['id', $this->typeId])); + if ($this->subject->error) + $this->notFound(Lang::$game['itemset']); + + $this->name = $this->subject->getField('name', true); + $this->extendGlobalData($this->subject->getJSGlobals()); } - // class - if ($_cl) + protected function generatePath() { - $foo = []; - for ($i = 0; $i < 11; $i++) - if ($_cl & (1 << $i)) - $foo[] = (!fMod(count($foo) + 1, 3) ? '\n' : null) . '[class='.($i + 1).']'; - - $t = count($foo) == 1 ? Lang::$game['class'] : Lang::$game['classes']; - $infobox[] = Util::ucFirst($t).Lang::$main['colon'].implode(', ', $foo); - } - - // required level - if ($_lvl) - $infobox[] = sprintf(Lang::$game['reqLevel'], $_lvl); - - // type - if ($_ty) - $infobox[] = Lang::$game['type'].Lang::$main['colon'].Lang::$itemset['types'][$_ty]; - - // tag - if ($_ta) - $infobox[] = Lang::$itemset['_tag'].Lang::$main['colon'].'[url=?itemsets&filter=ta='.$_ta.']'.Lang::$itemset['notes'][$_ta].'[/url]'; - - /****************/ - /* Main Content */ - /****************/ - - // pieces + Summary - $pieces = []; - $eqList = []; - $compare = []; - - if (!$iSet->pieceToSet) - $cnd = [0]; - else - $cnd = ['i.id', array_keys($iSet->pieceToSet)]; - - $iList = new ItemList(array($cnd)); - $data = $iList->getListviewData(ITEMINFO_SUBITEMS | ITEMINFO_JSON); - foreach ($iList->iterate() as $itemId => $__) - { - if (empty($data[$itemId])) - continue; - - $slot = $iList->getField('slot'); - $disp = $iList->getField('displayId'); - if ($slot && $disp) - $eqList[] = [$slot, $disp]; - - $compare[] = $itemId; - - $pieces[] = array( - 'id' => $itemId, - 'name' => $iList->getField('name', true), - 'quality' => $iList->getField('quality'), - 'icon' => $iList->getField('iconString'), - 'json' => json_encode($data[$itemId], JSON_NUMERIC_CHECK) - ); - } - - // spells - $foo = []; - $spells = []; - for ($i = 1; $i < 9; $i++) - { - $spl = $iSet->getField('spell'.$i); - $qty = $iSet->getField('bonus'.$i); - - if ($spl && $qty) + if ($_ = $this->subject->getField('classMask')) { - $foo[] = $spl; - $spells[] = array( // cant use spell as index, would change order - 'id' => $spl, - 'bonus' => $qty, - 'desc' => '' - ); + $bit = log($_, 2); + if (intVal($bit) != $bit) // bit is float => multiple classes were set => skip out + return; + + $this->path[] = $bit + 1; } } - // sort by required pieces ASC - usort($spells, function($a, $b) { - if ($a['bonus'] == $b['bonus']) - return 0; - - return ($a['bonus'] > $b['bonus']) ? 1 : -1; - }); - - $setSpells = new SpellList(array(['s.id', $foo])); - foreach ($setSpells->iterate() as $spellId => $__) + protected function generateTitle() { - foreach ($spells as &$s) + array_unshift($this->title, $this->name, Util::ucFirst(Lang::$game['itemset'])); + } + + protected function generateContent() + { + $_ta = $this->subject->getField('contentGroup'); + $_ty = $this->subject->getField('type'); + $_cnt = count($this->subject->getField('pieces')); + + /***********/ + /* Infobox */ + /***********/ + + $infobox = Lang::getInfoBoxForFlags($this->subject->getField('cuFlags')); + + // unavailable (todo (low): set data) + if ($this->subject->getField('cuFlags') & CUSTOM_UNAVAILABLE) + $infobox[] = Lang::$main['unavailable']; + + // holiday + if ($h = $this->subject->getField('holidayId')) { - if ($spellId != $s['id']) + $infobox[] = Lang::$game['eventShort'].Lang::$main['colon'].'[event='.$h.']'; + $this->extendGlobalIds(TYPE_GAMEVENT, $h); + } + + // itemLevel + if ($min = $this->subject->getField('minLevel')) + { + $foo = Lang::$game['level'].Lang::$main['colon'].$min; + $max = $this->subject->getField('maxLevel'); + + if ($min < $max) + $foo .= ' - '.$max; + + $infobox[] = $foo; + } + + // class + if ($cl = Lang::getClassString($this->subject->getField('classMask'), $jsg, $qty, false)) + { + $this->extendGlobalIds(TYPE_CLASS, $jsg); + $t = $qty == 1 ? Lang::$game['class'] : Lang::$game['classes']; + $infobox[] = Util::ucFirst($t).Lang::$main['colon'].$cl; + } + + // required level + if ($lvl = $this->subject->getField('reqLevel')) + $infobox[] = sprintf(Lang::$game['reqLevel'], $lvl); + + // type + if ($_ty) + $infobox[] = Lang::$game['type'].Lang::$main['colon'].Lang::$itemset['types'][$_ty]; + + // tag + if ($_ta) + $infobox[] = Lang::$itemset['_tag'].Lang::$main['colon'].'[url=?itemsets&filter=ta='.$_ta.']'.Lang::$itemset['notes'][$_ta].'[/url]'; + + /****************/ + /* Main Content */ + /****************/ + + // pieces + Summary + $pieces = []; + $eqList = []; + $compare = []; + + if (!$this->subject->pieceToSet) + $cnd = [0]; + else + $cnd = ['i.id', array_keys($this->subject->pieceToSet)]; + + $iList = new ItemList(array($cnd)); + $data = $iList->getListviewData(ITEMINFO_SUBITEMS | ITEMINFO_JSON); + foreach ($iList->iterate() as $itemId => $__) + { + if (empty($data[$itemId])) continue; - $s['desc'] = $setSpells->parseText('description')[0]; + $slot = $iList->getField('slot'); + $disp = $iList->getField('displayId'); + if ($slot && $disp) + $eqList[] = [$slot, $disp]; + + $compare[] = $itemId; + + $pieces[] = array( + 'id' => $itemId, + 'name' => $iList->getField('name', true), + 'quality' => $iList->getField('quality'), + 'icon' => $iList->getField('iconString'), + 'json' => $data[$itemId] + ); } - } - // path - if ($_cl) - { - for ($i = 0; $i < 11; $i++) + // spells + $foo = []; + $spells = []; + for ($i = 1; $i < 9; $i++) { - if ($_cl & (1 << $i)) - { - if ($_cl == (1 << $i)) // only bit set, add path - $path[] = $i + 1; + $spl = $this->subject->getField('spell'.$i); + $qty = $this->subject->getField('bonus'.$i); - break; // break anyway (cant set multiple classes) + if ($spl && $qty) + { + $foo[] = $spl; + $spells[] = array( // cant use spell as index, would change order + 'id' => $spl, + 'bonus' => $qty, + 'desc' => '' + ); + } + } + + // sort by required pieces ASC + usort($spells, function($a, $b) { + if ($a['bonus'] == $b['bonus']) + return 0; + + return ($a['bonus'] > $b['bonus']) ? 1 : -1; + }); + + $setSpells = new SpellList(array(['s.id', $foo])); + foreach ($setSpells->iterate() as $spellId => $__) + { + foreach ($spells as &$s) + { + if ($spellId != $s['id']) + continue; + + $s['desc'] = $setSpells->parseText('description')[0]; + } + } + + $skill = ''; + if ($_sk = $this->subject->getField('skillId')) + { + $spellLink = sprintf('%s (%s)', $_sk, Lang::$spell['cat'][11][$_sk][0], $this->subject->getField('skillLevel')); + $skill = ' – '.sprintf(Lang::$game['requires'], $spellLink).''; + } + + $this->bonusExt = $skill; + $this->description = $_ta ? sprintf(Lang::$itemset['_desc'], $this->name, Lang::$itemset['notes'][$_ta], $_cnt) : sprintf(Lang::$itemset['_descTagless'], $this->name, $_cnt); + $this->unavailable = $this->subject->getField('cuFlags') & CUSTOM_UNAVAILABLE; + $this->infobox = $infobox ? '[ul][li]'.implode('[/li][li]', $infobox).'[/li][/ul]' : null; + $this->pieces = $pieces; + $this->spells = $spells; + $this->expansion = 0; + $this->redButtons = array( + BUTTON_WOWHEAD => $this->typeId > 0, // bool only + BUTTON_LINKS => ['color' => '', 'linkId' => ''], + BUTTON_VIEW3D => ['type' => TYPE_ITEMSET, 'typeId' => $this->typeId, 'equipList' => $eqList], + BUTTON_COMPARE => ['eqList' => implode(':', $compare), 'qty' => $_cnt] + ); + $this->compare = array( + 'level' => $this->subject->getField('reqLevel'), + 'items' => array_map(function ($v) { + return [[$v]]; + }, $compare) + ); + + /**************/ + /* Extra Tabs */ + /**************/ + + // related sets (priority: 1: similar tag + class; 2: has event; 3: no tag + similar type, 4: similar type + profession) + $rel = []; + + if ($_ta && count($this->path) == 3) + { + $rel[] = ['id', $this->typeId, '!']; + $rel[] = ['classMask', 1 << (end($this->path) - 1), '&']; + $rel[] = ['contentGroup', (int)$_ta]; + } + else if ($this->subject->getField('holidayId')) + { + $rel[] = ['id', $this->typeId, '!']; + $rel[] = ['holidayId', 0, '!']; + } + else if ($this->subject->getField('skillId')) + { + $rel[] = ['id', $this->typeId, '!']; + $rel[] = ['contentGroup', 0]; + $rel[] = ['skillId', 0, '!']; + $rel[] = ['type', $_ty]; + } + else if (!$_ta && $_ty) + { + $rel[] = ['id', $this->typeId, '!']; + $rel[] = ['contentGroup', 0]; + $rel[] = ['type', $_ty]; + $rel[] = ['skillId', 0]; + } + + if ($rel) + { + $relSets = new ItemsetList($rel); + if (!$relSets->error) + { + $lv = array( + 'file' => 'itemset', + 'data' => $relSets->getListviewData(), + 'params' => array( + 'id' => 'see-also', + 'name' => '$LANG.tab_seealso', + 'tabs' => '$tabsRelated' + ) + ); + + if (!$relSets->hasDiffFields(['classMask'])) + $lv['params']['hiddenCols'] = "$['classes']"; + + $this->lvData[] = $lv; + + $this->extendGlobalData($relSets->getJSGlobals()); } } } - - $skill = ''; - if ($_sk) - { - $spellLink = sprintf('%s (%s)', $_sk, Lang::$spell['cat'][11][$_sk][0], $iSet->getField('skillLevel')); - $skill = ' – '.sprintf(Lang::$game['requires'], $spellLink).''; - } - - // menuId 2: Itemset g_initPath() - // tabId 0: Database g_initHeader() - $pageData = array( - 'page' => array( - 'name' => $_na, // for page content - 'bonusExt' => $skill, - 'description' => $_ta ? sprintf(Lang::$itemset['_desc'], $_na, Lang::$itemset['notes'][$_ta], $_cnt) : sprintf(Lang::$itemset['_descTagless'], $_na, $_cnt), - 'unavailable' => (bool)($iSet->getField('cuFlags') & CUSTOM_UNAVAILABLE), - 'infobox' => $infobox ? '[ul][li]'.implode('[/li][li]', $infobox).'[/li][/ul]' : null, - 'title' => $_na." - ".Util::ucfirst(Lang::$game['itemset']), - 'path' => json_encode($path, JSON_NUMERIC_CHECK), - 'tab' => 0, - 'type' => TYPE_ITEMSET, - 'typeId' => $_id, - 'reqJS' => array( - STATIC_URL.'/js/Summary.js', - STATIC_URL.'/js/swfobject.js' - ), - 'pieces' => $pieces, - 'spells' => $spells, - 'redButtons' => array( - BUTTON_WOWHEAD => $_id > 0, // bool only - BUTTON_LINKS => ['color' => '', 'linkId' => ''], - BUTTON_VIEW3D => ['type' => TYPE_ITEMSET, 'typeId' => $_id, 'equipList' => $eqList], - BUTTON_COMPARE => ['eqList' => implode(':', $compare), 'qty' => $_cnt] - ), - 'compare' => array( - 'qty' => $_cnt, - 'items' => $compare, - 'level' => $_lvl - ), - ), - 'relTabs' => [] - ); - - $iSet->getJSGlobals(); - - /**************/ - /* Extra Tabs */ - /**************/ - - // related sets (priority: 1: similar tag + class; 2: has event; 3: no tag + similar type, 4: similar type + profession) - $rel = []; - - if ($_ta && count($path) == 3) - { - $rel[] = ['id', $_id, '!']; - $rel[] = ['classMask', 1 << (end($path) - 1), '&']; - $rel[] = ['contentGroup', (int)$_ta]; - } - else if ($_ev) - { - $rel[] = ['id', $_id, '!']; - $rel[] = ['holidayId', 0, '!']; - } - else if ($_sk) - { - $rel[] = ['id', $_id, '!']; - $rel[] = ['contentGroup', 0]; - $rel[] = ['skillId', 0, '!']; - $rel[] = ['type', $_ty]; - } - else if (!$_ta && $_ty) - { - $rel[] = ['id', $_id, '!']; - $rel[] = ['contentGroup', 0]; - $rel[] = ['type', $_ty]; - $rel[] = ['skillId', 0]; - } - - if ($rel) - { - $relSets = new ItemsetList($rel); - if (!$relSets->error) - { - $pageData['relTabs'][] = array( - 'file' => 'itemset', - 'data' => $relSets->getListviewData(), - 'params' => array( - 'id' => 'see-also', - 'name' => '$LANG.tab_seealso', - 'tabs' => '$tabsRelated' - ) - ); - - $mask = $relSets->hasDiffFields(['classMask']); - if (!$mask) - $pageData['related']['params']['hiddenCols'] = "$['classes']"; - - $relSets->getJSGlobals(); - } - } - - $smarty->saveCache($cacheKeyPage, $pageData); } -$smarty->updatePageVars($pageData['page']); -$smarty->assign('community', CommunityContent::getAll(TYPE_ITEMSET, $_id)); // comments, screenshots, videos -$smarty->assign('lang', array_merge(Lang::$main, Lang::$itemset, ['colon' => Lang::$main['colon']])); -$smarty->assign('lvData', $pageData['relTabs']); -// load the page -$smarty->display('itemset.tpl'); ?> diff --git a/pages/itemsets.php b/pages/itemsets.php index b364bce4..3a528bca 100644 --- a/pages/itemsets.php +++ b/pages/itemsets.php @@ -4,76 +4,83 @@ if (!defined('AOWOW_REVISION')) die('illegal access'); -$filter = []; -$path = [0, 2]; -$filterHash = !empty($_GET['filter']) ? sha1(serialize($_GET['filter'])) : -1; -$cacheKey = implode('_', [CACHETYPE_PAGE, TYPE_ITEMSET, -1, $filterHash, User::$localeId]); - -if (!$smarty->loadCache($cacheKey, $pageData, $filter)) +// menuId 2: Itemset g_initPath() +// tabId 0: Database g_initHeader() +class ItemsetsPage extends GenericPage { - $itemsetFilter = new ItemsetListFilter(); + use ListPage; - $itemsets = new ItemsetList([$itemsetFilter->getConditions()]); - $itemsets->getJSGlobals(); + protected $type = TYPE_ITEMSET; + protected $tpl = 'itemsets'; + protected $path = [0, 2]; + protected $tabId = 0; + protected $mode = CACHETYPE_PAGE; + protected $js = ['filters.js']; - // recreate form selection - $filter = array_merge($itemsetFilter->getForm('form'), $filter); - $filter['query'] = isset($_GET['filter']) ? $_GET['filter'] : NULL; - $filter['fi'] = $itemsetFilter->getForm(); - - if (isset($filter['cl'])) - $path[] = $filter['cl']; - - // menuId 2: Itemset g_initPath() - // tabId 0: Database g_initHeader() - $pageData = array( - 'page' => array( - 'title' => Util::ucFirst(Lang::$game['itemsets']), - 'path' => json_encode($path, JSON_NUMERIC_CHECK), - 'tab' => 0, - 'subCat' => $pageParam ? '='.$pageParam : '', - 'reqJS' => array( - STATIC_URL.'/js/filters.js', - '?data=weight-presets&locale='.User::$localeId.'&t='.$_SESSION['dataKey'] - ) - ), - 'lv' => [] - ); - - $lv = array( - 'data' => $itemsets->getListviewData(), // listview content - 'params' => [] - ); - - if (!empty($filter['fi']['extraCols'])) - $lv['params']['extraCols'] = '$fi_getExtraCols(fi_extraCols, 0, 0)'; - - // create note if search limit was exceeded - if ($itemsets->getMatches() > CFG_SQL_LIMIT_DEFAULT) + public function __construct($pageCall, $pageParam) { - $lv['params']['note'] = sprintf(Util::$tryFilteringString, 'LANG.lvnote_itemsetsfound', $itemsets->getMatches(), CFG_SQL_LIMIT_DEFAULT); - $lv['params']['_truncated'] = 1; + $this->getCategoryFromUrl($pageParam);; + + parent::__construct(); + + $this->name = Util::ucFirst(Lang::$game['itemsets']); } - if ($itemsetFilter->error) - $lv['params']['_errors'] = '$1'; + protected function generateContent() + { + $itemsetFilter = new ItemsetListFilter(); - $pageData['lv'] = $lv; + $itemsets = new ItemsetList($itemsetFilter->getConditions()); + $this->extendGlobalData($itemsets->getJSGlobals()); - $smarty->saveCache($cacheKey, $pageData, $filter); + // recreate form selection + $this->filter = array_merge($itemsetFilter->getForm('form'), $this->filter); + $this->filter['query'] = isset($_GET['filter']) ? $_GET['filter'] : NULL; + $this->filter['fi'] = $itemsetFilter->getForm(); + + $this->addJS('?data=weight-presets&locale='.User::$localeId.'&t='.$_SESSION['dataKey']); + + $lv = array( + 'file' => 'itemset', + 'data' => $itemsets->getListviewData(), // listview content + 'params' => [] + ); + + if (!empty($this->filter['fi']['extraCols'])) + $lv['params']['extraCols'] = '$fi_getExtraCols(fi_extraCols, 0, 0)'; + + // create note if search limit was exceeded + if ($itemsets->getMatches() > CFG_SQL_LIMIT_DEFAULT) + { + $lv['params']['note'] = sprintf(Util::$tryFilteringString, 'LANG.lvnote_itemsetsfound', $itemsets->getMatches(), CFG_SQL_LIMIT_DEFAULT); + $lv['params']['_truncated'] = 1; + } + + if ($itemsetFilter->error) + $lv['params']['_errors'] = '$1'; + + $this->lvData = $lv; + + // sort for dropdown-menus + asort(Lang::$itemset['notes'], SORT_NATURAL); + asort(Lang::$game['cl']); + } + + protected function generateTitle() + { + array_unshift($this->title, $this->name); + + $form = (new ItemsetListFilter())->getForm('form'); + if (isset($form['cl'])) + array_unshift($this->title, Lang::$game['cl'][$form['cl']]); + } + + protected function generatePath() + { + $form = (new ItemsetListFilter())->getForm('form'); + if (isset($form['cl'])) + $this->path[] = $form['cl']; + } } - -// sort for dropdown-menus -asort(Lang::$itemset['notes'], SORT_NATURAL); -asort(Lang::$game['cl']); - -$smarty->updatePageVars($pageData['page']); -$smarty->assign('filter', $filter); -$smarty->assign('lang', array_merge(Lang::$main, Lang::$game, Lang::$itemset, Lang::$item, ['colon' => Lang::$main['colon']])); -$smarty->assign('lvData', $pageData['lv']); - -// load the page -$smarty->display('itemsets.tpl'); - ?> diff --git a/pages/npc.php b/pages/npc.php index d83f3b89..f18299ce 100644 --- a/pages/npc.php +++ b/pages/npc.php @@ -4,248 +4,674 @@ if (!defined('AOWOW_REVISION')) die('illegal access'); -require 'includes/community.class.php'; - -$_id = intVal($pageParam); -$_path = [0, 4]; -$_altIds = []; -$_altNPCs = null; - -$cacheKeyPage = implode('_', [CACHETYPE_PAGE, TYPE_NPC, $_id, -1, User::$localeId]); -$cacheKeyTooltip = implode('_', [CACHETYPE_TOOLTIP, TYPE_NPC, $_id, -1, User::$localeId]); - -// AowowPower-request -if (isset($_GET['power'])) +// menuId 4: NPC g_initPath() +// tabId 0: Database g_initHeader() +class NpcPage extends GenericPage { - header('Content-type: application/x-javascript; charsetUTF-8'); + use DetailPage; - Util::powerUseLocale(@$_GET['domain']); + protected $type = TYPE_NPC; + protected $typeId = 0; + protected $tpl = 'npc'; + protected $path = [0, 4]; + 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'] + ); - if (!$smarty->loadCache($cacheKeyTooltip, $x)) + public function __construct($__, $id) { - $npc = new CreatureList(array(['ct.id', $_id])); - if ($npc->error) - die('$WowheadPower.registerNpc('.$_id.', '.User::$localeId.', {})'); + parent::__construct(); - $s = $npc->getSpawns(true); + // temp locale + if ($this->mode == CACHETYPE_TOOLTIP && isset($_GET['domain'])) + Util::powerUseLocale($_GET['domain']); - $x = '$WowheadPower.registerNpc('.$_id.', '.User::$localeId.", {\n"; - $x .= "\tname_".User::$localeString.": '".Util::jsEscape($npc->getField('name', true))."',\n"; - $x .= "\ttooltip_".User::$localeString.": '".Util::jsEscape($npc->renderTooltip())."',\n"; + $this->typeId = intVal($id); + + $this->subject = new CreatureList(array(['id', $this->typeId])); + if ($this->subject->error) + $this->notFound(Lang::$game['npc']); + + $this->name = $this->subject->getField('name', true); + $this->subname = $this->subject->getField('subname', true); + } + + protected function generatePath() + { + $this->path[] = $this->subject->getField('type'); + + if ($_ = $this->subject->getField('family')) + $this->path[] = $_; + } + + protected function generateTitle() + { + array_unshift($this->title, $this->name, Util::ucFirst(Lang::$game['npc'])); + } + + protected function generateContent() + { + $_typeFlags = $this->subject->getField('typeFlags'); + $_altIds = []; + $_altNPCs = null; + $position = null; + + // difficulty entries of self + if ($this->subject->getField('cuFlags') & NPC_CU_DIFFICULTY_DUMMY) + { + // find and create link to regular creature + $regNPC = new CreatureList(array(['OR', ['difficultyEntry1', $this->typeId], ['difficultyEntry2', $this->typeId], ['difficultyEntry3', $this->typeId]])); + $position = [$regNPC->id, $regNPC->getField('name', true)]; + } + else + { + for ($i = 1; $i < 4; $i++) + if ($_ = $this->subject->getField('difficultyEntry'.$i)) + $_altIds[$_] = $i; + + if ($_altIds) + $_altNPCs = new CreatureList(array(['id', array_keys($_altIds)])); + } + + // hmm, this won't do if the creature is spawned by event/script + $mapType = 2; // should be 0, tmp-override until Zones + // $maps = DB::Aowow()->selectCol('SELECT DISTINCT map from creature WHERE id = ?d', $this->typeId); + // if (count($maps) == 1) // should only exist in one instance + // { + // $map = new ZoneList(array(1, ['mapId', $maps[0]], ['parentArea', 0])); + // $mapType = $map->getField('areaType'); + // } + + /***********/ + /* Infobox */ + /***********/ + + $infobox = Lang::getInfoBoxForFlags($this->subject->getField('cuFlags')); + + // Event + if ($_ = DB::Aowow()->selectRow('SELECT e.id, holidayId FROM ?_events e, game_event_creature gec, creature c WHERE e.id = ABS(gec.eventEntry) AND c.guid = gec.guid AND c.id = ?d', $this->typeId)) + { + if ($h = $_['holidayId']) + { + Util::$pageTemplate->extendGlobalIds(TYPE_WORLDEVENT, $_['id']); + $infobox[] = Util::ucFirst(Lang::$game['eventShort']).Lang::$main['colon'].'[event='.$h.']'; + } + } + + // Level + if ($this->subject->getField('rank') != NPC_RANK_BOSS) + { + $level = $this->subject->getField('minLevel'); + $maxLvl = $this->subject->getField('maxLevel'); + if ($level < $maxLvl) + $level .= ' - '.$maxLvl; + } + else // Boss Level + $level = '??'; + + $infobox[] = Lang::$game['level'].Lang::$main['colon'].$level; + + // Classification + if ($_ = $this->subject->getField('rank')) // != NPC_RANK_NORMAL + { + $str = $_typeFlags & 0x4 ? '[span class=icon-boss]'.Lang::$npc['rank'][$_].'[/span]' : Lang::$npc['rank'][$_]; + $infobox[] = Lang::$npc['classification'].Lang::$main['colon'].$str; + } + + // 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]'; + + // Faction + $this->extendGlobalIds(TYPE_FACTION, $this->subject->getField('factionId')); + $infobox[] = Util::ucFirst(Lang::$game['faction']).Lang::$main['colon'].'[faction='.$this->subject->getField('factionId').']'; + + // Wealth + if ($_ = intVal(($this->subject->getField('minGold') + $this->subject->getField('maxGold')) / 2)) + $infobox[] = Lang::$npc['worth'].Lang::$main['colon'].'[tooltip=tooltip_avgmoneydropped][money='.$_.'][/tooltip]'; + + // is Vehicle + if ($this->subject->getField('vehicleId')) + $infobox[] = Lang::$npc['vehicle']; + + // AI + if (User::isInGroup(U_GROUP_EMPLOYEE)) + { + if ($_ = $this->subject->getField('scriptName')) + $infobox[] = 'Script'.Lang::$main['colon'].$_; + else if ($_ = $this->subject->getField('aiName')) + $infobox[] = 'AI'.Lang::$main['colon'].$_; + } + + // > Stats + $_nf = function ($num) { return number_format($num, 0, '', '.'); }; + $stats = []; + $modes = []; // get difficulty versions if set + $hint = '[tooltip name=%3$s][table cellspacing=10][tr]%1s[/tr][/table][/tooltip][span class=tip tooltip=%3$s]%2s[/span]'; + $modeRow = '[tr][td]%s  [/td][td]%s[/td][/tr]'; + // Health + $health = $this->subject->getBaseStats('health'); + $stats['health'] = Util::ucFirst(Lang::$spell['powerTypes'][-2]).Lang::$main['colon'].($health[0] < $health[1] ? $_nf($health[0]).' - '.$_nf($health[1]) : $_nf($health[0])); + + // Mana (may be 0) + $mana = $this->subject->getBaseStats('power'); + $stats['mana'] = $mana[0] ? Lang::$spell['powerTypes'][0].Lang::$main['colon'].($mana[0] < $mana[1] ? $_nf($mana[0]).' - '.$_nf($mana[1]) : $_nf($mana[0])) : null; + + // Armor + $armor = $this->subject->getBaseStats('armor'); + $stats['armor'] = Lang::$npc['armor'].Lang::$main['colon'].($armor[0] < $armor[1] ? $_nf($armor[0]).' - '.$_nf($armor[1]) : $_nf($armor[0])); + + // Melee Damage + $melee = $this->subject->getBaseStats('melee'); + if ($_ = $this->subject->getField('dmgSchool')) // magic damage + $stats['melee'] = Lang::$npc['melee'].Lang::$main['colon'].$_nf($melee[0]).' - '.$_nf($melee[1]).' ('.Lang::$game['sc'][$_].')'; + else // phys. damage + $stats['melee'] = Lang::$npc['melee'].Lang::$main['colon'].$_nf($melee[0]).' - '.$_nf($melee[1]); + + // Ranged Damage + $ranged = $this->subject->getBaseStats('ranged'); + $stats['ranged'] = Lang::$npc['ranged'].Lang::$main['colon'].$_nf($ranged[0]).' - '.$_nf($ranged[1]); + + if (in_array($mapType, [1, 2])) // Dungeon or Raid + { + foreach ($_altIds as $id => $mode) + { + foreach ($_altNPCs->iterate() as $dId => $__) + { + if ($dId != $id) + continue; + + $m = Lang::$npc['modes'][$mapType][$mode]; + + // Health + $health = $_altNPCs->getBaseStats('health'); + $modes['health'][] = sprintf($modeRow, $m, $health[0] < $health[1] ? $_nf($health[0]).' - '.$_nf($health[1]) : $_nf($health[0])); + + // Mana (may be 0) + $mana = $_altNPCs->getBaseStats('power'); + $modes['mana'][] = $mana[0] ? sprintf($modeRow, $m, $mana[0] < $mana[1] ? $_nf($mana[0]).' - '.$_nf($mana[1]) : $_nf($mana[0])) : null; + + // Armor + $armor = $_altNPCs->getBaseStats('armor'); + $modes['armor'][] = sprintf($modeRow, $m, $armor[0] < $armor[1] ? $_nf($armor[0]).' - '.$_nf($armor[1]) : $_nf($armor[0])); + + // Melee Damage + $melee = $_altNPCs->getBaseStats('melee'); + if ($_ = $_altNPCs->getField('dmgSchool')) // magic damage + $modes['melee'][] = sprintf($modeRow, $m, $_nf($melee[0]).' - '.$_nf($melee[1]).' ('.Lang::$game['sc'][$_].')'); + else // phys. damage + $modes['melee'][] = sprintf($modeRow, $m, $_nf($melee[0]).' - '.$_nf($melee[1])); + + // Ranged Damage + $ranged = $_altNPCs->getBaseStats('ranged'); + $modes['ranged'][] = sprintf($modeRow, $m, $_nf($ranged[0]).' - '.$_nf($ranged[1])); + } + } + } + + if ($modes) + foreach ($stats as $k => $v) + if ($v) + $stats[$k] = sprintf($hint, implode('[/tr][tr]', $modes[$k]), $v, $k); + + // < Stats + if ($stats) + $infobox[] = Lang::$npc['stats'].($modes ? ' ('.Lang::$npc['modes'][$mapType][0].')' : null).Lang::$main['colon'].'[ul][li]'.implode('[/li][li]', $stats).'[/li][/ul]'; + + /****************/ + /* Main Content */ + /****************/ + + // get spawns and such + + // consider phaseMasks + + // consider pooled spawns + + // $this->mapper = true, + $this->infobox = '[ul][li]'.implode('[/li][li]', $infobox).'[/li][/ul]'; + $this->position = $position; + $this->quotes = $this->getQuotes(); + $this->reputation = $this->getOnKillRep($_altIds, $mapType); + $this->redButtons = array( + BUTTON_WOWHEAD => true, + BUTTON_LINKS => true, + BUTTON_VIEW3D => ['type' => TYPE_NPC, 'typeId' => $this->typeId, 'displayId' => $this->subject->getRandomModelId()] + ); + + /**************/ + /* Extra Tabs */ + /**************/ + + // tab: SAI + // hmm, how should this loot like + + // tab: abilities / tab_controlledabilities (dep: VehicleId) + // SMART_SCRIPT_TYPE_CREATURE = 0; SMART_ACTION_CAST = 11; SMART_ACTION_ADD_AURA = 75; SMART_ACTION_INVOKER_CAST = 85; SMART_ACTION_CROSS_CAST = 86 + $smartSpells = DB::Aowow()->selectCol('SELECT action_param1 FROM smart_scripts WHERE source_type = 0 AND action_type IN (11, 75, 85, 86) AND entryOrGUID = ?d', $this->typeId); + $tplSpells = []; + $conditions = ['OR']; + + for ($i = 1; $i < 9; $i++) + if ($_ = $this->subject->getField('spell'.$i)) + $tplSpells[] = $_; + + if ($tplSpells) + $conditions[] = ['id', $tplSpells]; + + if ($smartSpells) + $conditions[] = ['id', $smartSpells]; + + if ($tplSpells || $smartSpells) + { + $abilities = new SpellList($conditions); + if (!$abilities->error) + { + $this->extendGlobalData($abilities->getJSGlobals(GLOBALINFO_SELF | GLOBALINFO_RELATED)); + $normal = $abilities->getListviewData(); + $controled = []; + + if ($this->subject->getField('vehicleId')) // not quite right. All seats should be checked for allowed-to-cast-flag-something + { + foreach ($normal as $id => $values) + { + if (in_array($id, $smartSpells)) + continue; + + $controled[$id] = $values; + unset($normal[$id]); + } + } + + if ($normal) + $this->lvData[] = array( + 'file' => 'spell', + 'data' => $normal, + 'params' => [ + 'tabs' => '$tabsRelated', + 'name' => '$LANG.tab_abilities', + 'id' => 'abilities' + ] + ); + + if ($controled) + $this->lvData[] = array( + 'file' => 'spell', + 'data' => $controled, + 'params' => [ + 'tabs' => '$tabsRelated', + 'name' => '$LANG.tab_controlledabilities', + 'id' => 'controlled-abilities' + ] + ); + } + } + + // tab: summoned by + $conditions = array( + 'OR', + ['AND', ['effect1Id', 28], ['effect1MiscValue', $this->typeId]], + ['AND', ['effect2Id', 28], ['effect2MiscValue', $this->typeId]], + ['AND', ['effect3Id', 28], ['effect3MiscValue', $this->typeId]] + ); + + $summoned = new SpellList($conditions); + if (!$summoned->error) + { + $this->extendGlobalData($summoned->getJSGlobals()); + + $this->lvData[] = array( + 'file' => 'spell', + 'data' => $summoned->getListviewData(), + 'params' => [ + 'tabs' => '$tabsRelated', + 'name' => '$LANG.tab_summonedby', + 'id' => 'summoned-by' + ] + ); + } + + // tab: teaches + if ($this->subject->getField('npcflag') & NPC_FLAG_TRAINER) + { + $teachQuery = 'SELECT IFNULL(t2.spell, t1.spell) AS ARRAY_KEY, IFNULL(t2.spellcost, t1.spellcost) AS cost, IFNULL(t2.reqskill, t1.reqskill) AS reqSkillId, + IFNULL(t2.reqskillvalue, t1.reqskillvalue) AS reqSkillValue, IFNULL(t2.reqlevel, t1.reqlevel) AS reqLevel + FROM npc_trainer t1 LEFT JOIN npc_trainer t2 ON t2.entry = IF(t1.spell < 0, -t1.spell, null) WHERE t1.entry = ?d'; + + if ($tSpells = DB::Aowow()->select($teachQuery, $this->typeId)) + { + $teaches = new SpellList(array(['id', array_keys($tSpells)])); + if (!$teaches->error) + { + $this->extendGlobalData($teaches->getJSGlobals(GLOBALINFO_SELF | GLOBALINFO_RELATED)); + $data = $teaches->getListviewData(); + + $extra = []; + foreach ($tSpells as $sId => $train) + { + if (empty($data[$sId])) + continue; + + if ($_ = $train['reqSkillId']) + { + $this->extendGlobalIds(TYPE_SKILL, $_); + if (!isset($extra[0])) + $extra[0] = 'Listview.extraCols.condition'; + + $data[$sId]['condition'][] = ['type' => TYPE_SKILL, 'typeId' => $_, 'status' => 1, 'reqSkillLvl' => $train['reqSkillValue']]; + } + + if ($_ = $train['reqLevel']) + { + if (!isset($extra[1])) + $extra[1] = "Listview.funcBox.createSimpleCol('reqLevel', LANG.tooltip_reqlevel, '7%', 'reqLevel')"; + + $data[$sId]['reqLevel'] = $_; + } + + if ($_ = $train['cost']) + $data[$sId]['trainingcost'] = $_; + } + + $this->lvData[] = array( + 'file' => 'spell', + 'data' => $data, + 'params' => [ + 'tabs' => '$tabsRelated', + 'name' => '$LANG.tab_teaches', + 'id' => 'teaches', + 'visibleCols' => "$['trainingcost']", + 'extraCols' => $extra ? '$['.implode(', ', $extra).']' : null + ] + ); + } + } + else + Util::addNote(U_GROUP_EMPLOYEE, 'NPC '.$this->typeId.' is flagged as trainer, but doesn\'t have any spells set'); + } + + // tab: sells + if ($sells = DB::Aowow()->selectCol('SELECT item FROM npc_vendor nv WHERE entry = ?d UNION SELECT item FROM game_event_npc_vendor genv JOIN creature c ON genv.guid = c.guid WHERE c.id = ?d', $this->typeId, $this->typeId)) + { + $soldItems = new ItemList(array(['id', $sells])); + if (!$soldItems->error) + { + $this->extendGlobalData($soldItems->getJSGlobals()); + + $this->lvData[] = array( + 'file' => 'item', + 'data' => $soldItems->getListviewData(ITEMINFO_VENDOR, [TYPE_NPC => $this->typeId]), + 'params' => [ + 'tabs' => '$tabsRelated', + 'name' => '$LANG.tab_sells', + 'id' => 'currency-for', + 'extraCols' => "$[Listview.extraCols.condition, Listview.funcBox.createSimpleCol('stack', 'stack', '10%', 'stack'), Listview.extraCols.cost]" + ] + ); + } + } + + // tabs: this creature contains.. + $skinTab = ['tab_skinning', 'skinned-from']; + if ($_typeFlags & NPC_TYPEFLAG_HERBLOOT) + $skinTab = ['tab_gatheredfromnpc', 'gathered-from-npc']; + else if ($_typeFlags & NPC_TYPEFLAG_MININGLOOT) + $skinTab = ['tab_minedfromnpc', 'mined-from-npc']; + else if ($_typeFlags & NPC_TYPEFLAG_ENGINEERLOOT) + $skinTab = ['tab_salvagedfrom', 'salvaged-from-npc']; + + /* + extraCols: [Listview.extraCols.count, Listview.extraCols.percent, Listview.extraCols.mode], + _totalCount: 22531, + computeDataFunc: Listview.funcBox.initLootTable, + onAfterCreate: Listview.funcBox.addModeIndicator, + + modes:{"mode":1,"1":{"count":4408,"outof":16013},"4":{"count":4408,"outof":22531}} + */ + + $sourceFor = array( + [LOOT_CREATURE, $this->subject->getField('lootId'), '$LANG.tab_drops', 'drops', ['Listview.extraCols.percent'], [] , []], + [LOOT_PICKPOCKET, $this->subject->getField('pickpocketLootId'), '$LANG.tab_pickpocketing', 'pickpocketing', ['Listview.extraCols.percent'], ['side', 'slot', 'reqlevel'], []], + [LOOT_SKINNING, $this->subject->getField('skinLootId'), '$LANG.'.$skinTab[0], $skinTab[1], ['Listview.extraCols.percent'], ['side', 'slot', 'reqlevel'], []] + ); + + // temp: manually add loot for difficulty-versions + $langref = array( + "-2" => '$LANG.tab_heroic', + "-1" => '$LANG.tab_normal', + 1 => '$$WH.sprintf(LANG.tab_normalX, 10)', + 2 => '$$WH.sprintf(LANG.tab_normalX, 25)', + 3 => '$$WH.sprintf(LANG.tab_heroicX, 10)', + 4 => '$$WH.sprintf(LANG.tab_heroicX, 25)' + ); + + if ($_altIds) + { + $sourceFor[0][2] = $langref[1]; + foreach ($_altNPCs->iterate() as $id => $__) + { + $mode = $_altIds[$id]; + array_splice($sourceFor, 1, 0, [[LOOT_CREATURE, $_altNPCs->getField('lootId'), $langref[$mode + 1], 'drops-'.$mode, ['Listview.extraCols.percent'], [], []]]); + } + } + + $reqQuest = []; + foreach ($sourceFor as $sf) + { + $creatureLoot = new Loot(); + if ($creatureLoot->getByContainer($sf[0], $sf[1])) + { + if ($_ = $creatureLoot->extraCols) + $sf[4] = array_merge($sf[4], $_); + + $this->extendGlobalData($creatureLoot->jsGlobals); + + foreach ($creatureLoot->iterate() as &$lv) + { + if (!$lv['quest']) + continue; + + $sf[4][] = 'Listview.extraCols.condition'; + $reqQuest[$lv['id']] = 0; + $lv['condition'][] = ['type' => TYPE_QUEST, 'typeId' => &$reqQuest[$lv['id']], 'status' => 1]; + } + + $this->lvData[] = array( + 'file' => 'item', + 'data' => $creatureLoot->getResult(), + 'params' => [ + 'tabs' => '$tabsRelated', + 'name' => $sf[2], + 'id' => $sf[3], + 'extraCols' => $sf[4] ? "$[".implode(', ', array_unique($sf[4]))."]" : null, + 'hiddenCols' => $sf[5] ? "$".json_encode($sf[5]) : null, + 'visibleCols' => $sf[6] ? '$'.json_encode($sf[6]) : null, + 'sort' => "$['-percent', 'name']", + ] + ); + } + } + + 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: starts quest + // tab: ends quest + $startEnd = new QuestList(array(['qse.type', TYPE_NPC], ['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: objective of quest + $conditions = array( + 'OR', + ['AND', ['reqNpcOrGo1', $this->typeId], ['reqNpcOrGoCount1', 0, '>']], + ['AND', ['reqNpcOrGo2', $this->typeId], ['reqNpcOrGoCount2', 0, '>']], + ['AND', ['reqNpcOrGo3', $this->typeId], ['reqNpcOrGoCount3', 0, '>']], + ['AND', ['reqNpcOrGo4', $this->typeId], ['reqNpcOrGoCount4', 0, '>']], + ); + + $objectiveOf = new QuestList($conditions); + if (!$objectiveOf->error) + { + $this->extendGlobalData($objectiveOf->getJSGlobals()); + + $this->lvData[] = array( + 'file' => 'quest', + 'data' => $objectiveOf->getListviewData(), + 'params' => [ + 'tabs' => '$tabsRelated', + 'name' => '$LANG.tab_objectiveof', + 'id' => 'objective-of' + ] + ); + } + + // tab: criteria of [ACHIEVEMENT_CRITERIA_TYPE_KILL_CREATURE_TYPE have no data set to check for] + $conditions = array( + ['ac.type', [ACHIEVEMENT_CRITERIA_TYPE_KILL_CREATURE, ACHIEVEMENT_CRITERIA_TYPE_KILLED_BY_CREATURE]], + ['ac.value1', $this->typeId] + ); + + $crtOf = new AchievementList($conditions); + if (!$crtOf->error) + { + $this->extendGlobalData($crtOf->getJSGlobals()); + + $this->lvData[] = array( + 'file' => 'achievement', + 'data' => $crtOf->getListviewData(), + 'params' => [ + 'tabs' => '$tabsRelated', + 'name' => '$LANG.tab_criteriaof', + 'id' => 'criteria-of' + ] + ); + } + } + + protected function generateTooltip($asError = false) + { + if ($asError) + return '$WowheadPower.registerNpc('.$this->typeId.', '.User::$localeId.', {})'; + + $s = $this->subject->getSpawns(true); + + $x = '$WowheadPower.registerNpc('.$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 .= "});"; - $smarty->saveCache($cacheKeyTooltip, $x); + return $x; } - die($x); -} - -// regular page -if (!$smarty->loadCache($cacheKeyPage, $pageData)) -{ - $npc = new CreatureList(array(['ct.id', $_id])); - if ($npc->error) - $smarty->notFound(Lang::$game['npc'], $_id); - - // reconstruct path - $_path[] = $npc->getField('type'); - - $_typeFlags = $npc->getField('typeFlags'); - $_name = $npc->getField('name', true); - - if ($_ = $npc->getField('family')) - $_path[] = $_; - - $position = null; - - // difficulty entrys of self - if ($npc->getField('cuFlags') & NPC_CU_DIFFICULTY_DUMMY) + public function display($override = '') { - // find and create link to regular creature - $regNPC = new CreatureList(array(['OR', ['difficultyEntry1', $_id], ['difficultyEntry2', $_id], ['difficultyEntry3', $_id]])); - $position = [$regNPC->id, $regNPC->getField('name', true)]; - } - else - { - for ($i = 1; $i < 4; $i++) - if ($_ = $npc->getField('difficultyEntry'.$i)) - $_altIds[$_] = $i; + if ($this->mode != CACHETYPE_TOOLTIP) + return parent::display($override); - if ($_altIds) - $_altNPCs = new CreatureList(array(['id', array_keys($_altIds)])); - } - - // hmm, this won't do, if th creature is spawned by event/script - $mapType = 0; - $maps = DB::Aowow()->selectCol('SELECT DISTINCT map from creature WHERE id = ?d', $_id); - if (count($maps) == 1) // should only exist in one instance - { - $map = new ZoneList(array(1, ['mapId', $maps[0]])); - $mapType = $map->getField('areaType'); //NYI - } - - /***********/ - /* Infobox */ - /***********/ - - $infobox = []; - - // Event - if ($_ = DB::Aowow()->selectRow('SELECT e.id, holidayId FROM ?_events e, game_event_creature gec, creature c WHERE e.id = ABS(gec.eventEntry) AND c.guid = gec.guid AND c.id = ?d', $_id)) - { - if ($h = $_['holidayId']) + if (!$this->loadCache($tt)) { - Util::$pageTemplate->extendGlobalIds(TYPE_WORLDEVENT, $_['id']); - $infobox[] = Util::ucFirst(Lang::$game['eventShort']).Lang::$colon.'[event='.$h.']'; + $tt = $this->generateTooltip(); + $this->saveCache($tt); } + + header('Content-type: application/x-javascript; charset=utf-8'); + die($tt); } - // Level - if ($npc->getField('rank') != NPC_RANK_BOSS) + public function notFound($typeStr) { - $level = $npc->getField('minLevel'); - $maxLvl = $npc->getField('maxLevel'); - if ($level < $maxLvl) - $level .= ' - '.$maxLvl; - } - else // Boss Level - $level = '??'; + if ($this->mode != CACHETYPE_TOOLTIP) + return parent::notFound($typeStr); - $infobox[] = Lang::$game['level'].Lang::$colon.$level; - - // Classification - if ($_ = $npc->getField('rank')) // != NPC_RANK_NORMAL - { - $str = $_typeFlags & 0x4 ? '[span class=icon-boss]'.Lang::$npc['rank'][$_].'[/span]' : Lang::$npc['rank'][$_]; - $infobox[] = Lang::$npc['classification'].Lang::$colon.$str; + header('Content-type: application/x-javascript; charset=utf-8'); + echo $this->generateTooltip(true); + exit(); } - // Reaction - $_ = function ($r) - { - if ($r == 1) return 2; - if ($r == -1) return 10; - return; - }; - $infobox[] = Lang::$npc['react'].Lang::$colon.'[color=q'.$_($npc->getField('A')).']A[/color] [color=q'.$_($npc->getField('H')).']H[/color]'; - - // Faction - Util::$pageTemplate->extendGlobalIds(TYPE_FACTION, $npc->getField('factionId')); - $infobox[] = Util::ucFirst(Lang::$game['faction']).Lang::$colon.'[faction='.$npc->getField('factionId').']'; - - // Wealth - if ($_ = intVal(($npc->getField('minGold') + $npc->getField('maxGold')) / 2)) - $infobox[] = Lang::$npc['worth'].Lang::$colon.'[tooltip=tooltip_avgmoneydropped][money='.$_.'][/tooltip]'; - - // is Vehicle - if ($npc->getField('vehicleId')) - $infobox[] = Lang::$npc['vehicle']; - - // AI - if (User::isInGroup(U_GROUP_STAFF)) - { - if ($_ = $npc->getField('scriptName')) - $infobox[] = 'Script'.Lang::$colon.$_; - else if ($_ = $npc->getField('aiName')) - $infobox[] = 'AI'.Lang::$colon.$_; - } - - // > Stats - $_nf = function ($num) { return number_format($num, 0, '', '.'); }; - $stats = []; - $modes = []; // get difficulty versions if set - $hint = '[tooltip name=%3$s][table cellspacing=10][tr]%1s[/tr][/table][/tooltip][span class=tip tooltip=%3$s]%2s[/span]'; - $modeRow = '[tr][td]%s  [/td][td]%s[/td][/tr]'; - // Health - $health = $npc->getBaseStats('health'); - $stats['health'] = Util::ucFirst(Lang::$spell['powerTypes'][-2]).Lang::$colon.($health[0] < $health[1] ? $_nf($health[0]).' - '.$_nf($health[1]) : $_nf($health[0])); - - // Mana (may be 0) - $mana = $npc->getBaseStats('power'); - $stats['mana'] = $mana[0] ? Lang::$spell['powerTypes'][0].Lang::$colon.($mana[0] < $mana[1] ? $_nf($mana[0]).' - '.$_nf($mana[1]) : $_nf($mana[0])) : null; - - // Armor - $armor = $npc->getBaseStats('armor'); - $stats['armor'] = Lang::$npc['armor'].Lang::$colon.($armor[0] < $armor[1] ? $_nf($armor[0]).' - '.$_nf($armor[1]) : $_nf($armor[0])); - - // Melee Damage - $melee = $npc->getBaseStats('melee'); - if ($_ = $npc->getField('dmgSchool')) // magic damage - $stats['melee'] = Lang::$npc['melee'].Lang::$colon.$_nf($melee[0]).' - '.$_nf($melee[1]).' ('.Lang::$game['sc'][$_].')'; - else // phys. damage - $stats['melee'] = Lang::$npc['melee'].Lang::$colon.$_nf($melee[0]).' - '.$_nf($melee[1]); - - // Ranged Damage - $ranged = $npc->getBaseStats('ranged'); - $stats['ranged'] = Lang::$npc['ranged'].Lang::$colon.$_nf($ranged[0]).' - '.$_nf($ranged[1]); - - if ($mapType == 1 || $mapType == 2) // Dungeon or Raid - { - foreach ($_altIds as $id => $mode) - { - foreach ($_altNPCs->iterate() as $dId => $__) - { - if ($dId != $id) - continue; - - $m = Lang::$npc['modes'][$mapType][$mode]; - - // Health - $health = $_altNPCs->getBaseStats('health'); - $modes['health'][] = sprintf($modeRow, $m, $health[0] < $health[1] ? $_nf($health[0]).' - '.$_nf($health[1]) : $_nf($health[0])); - - // Mana (may be 0) - $mana = $_altNPCs->getBaseStats('power'); - $modes['mana'][] = $mana[0] ? sprintf($modeRow, $m, $mana[0] < $mana[1] ? $_nf($mana[0]).' - '.$_nf($mana[1]) : $_nf($mana[0])) : null; - - // Armor - $armor = $_altNPCs->getBaseStats('armor'); - $modes['armor'][] = sprintf($modeRow, $m, $armor[0] < $armor[1] ? $_nf($armor[0]).' - '.$_nf($armor[1]) : $_nf($armor[0])); - - // Melee Damage - $melee = $_altNPCs->getBaseStats('melee'); - if ($_ = $_altNPCs->getField('dmgSchool')) // magic damage - $modes['melee'][] = sprintf($modeRow, $m, $_nf($melee[0]).' - '.$_nf($melee[1]).' ('.Lang::$game['sc'][$_].')'); - else // phys. damage - $modes['melee'][] = sprintf($modeRow, $m, $_nf($melee[0]).' - '.$_nf($melee[1])); - - // Ranged Damage - $ranged = $_altNPCs->getBaseStats('ranged'); - $modes['ranged'][] = sprintf($modeRow, $m, $_nf($ranged[0]).' - '.$_nf($ranged[1])); - } - } - } - - if ($modes) - foreach ($stats as $k => $v) - if ($v) - $stats[$k] = sprintf($hint, implode('[/tr][tr]', $modes[$k]), $v, $k); - - // < Stats - if ($stats) - $infobox[] = Lang::$npc['stats'].($modes ? ' ('.Lang::$npc['modes'][$mapType][0].')' : null).Lang::$colon.'[ul][li]'.implode('[/li][li]', $stats).'[/li][/ul]'; - - - /****************/ - /* Main Content */ - /****************/ - - // reputations (by mode) - $spilledParents = []; - $reputation = []; - $_repFunc = function ($entries, &$spillover) + private function getRepForId($entries, &$spillover) { + $result = []; $q = 'SELECT f.id, f.parentFactionId, cor.creature_id AS npc, IF(f.id = RewOnKillRepFaction1, RewOnKillRepValue1, RewOnKillRepValue2) AS qty, IF(f.id = RewOnKillRepFaction1, MaxStanding1, MaxStanding2) AS maxRank, IF(f.id = RewOnKillRepFaction1, isTeamAward1, isTeamAward2) AS spillover FROM aowow_factions f JOIN creature_onkill_reputation cor ON f.Id = cor.RewOnKillRepFaction1 OR f.Id = cor.RewOnKillRepFaction2 WHERE cor.creature_id IN (?a)'; - $result = []; - $repData = DB::Aowow()->select($q, (array)$entries); - - foreach ($repData as $_) + foreach (DB::Aowow()->select($q, (array)$entries) as $_) { $set = array( 'id' => $_['id'], @@ -265,88 +691,95 @@ if (!$smarty->loadCache($cacheKeyPage, $pageData)) } return $result; - }; - - // base NPC - if ($base = $_repFunc($_id, $spilledParents)) - $reputation[] = [Lang::$npc['modes'][1][0], $base]; - - // difficulty dummys - if ($_altIds) - { - $alt = []; - $rep = $_repFunc(array_keys($_altIds), $spilledParents); - - // order by difficulty - foreach ($rep as $r) - $alt[$_altIds[$r['npc']]][] = $r; - - // apply by difficulty - foreach ($alt as $mode => $dat) - $reputation[] = [Lang::$npc['modes'][$mapType][$mode], $dat]; } - // get spillover factions and apply - if ($spilledParents) + private function getOnKillRep($dummyIds, $mapType) { - $spilled = new FactionList(array(['parentFactionId', array_keys($spilledParents)])); + $spilledParents = []; + $reputation = []; - foreach($reputation as &$sets) + // base NPC + if ($base = $this->getRepForId($this->typeId, $spilledParents)) + $reputation[] = [Lang::$npc['modes'][1][0], $base]; + + // difficulty dummys + if ($dummyIds) { - foreach ($sets[1] as &$row) + $alt = []; + $rep = $this->getRepForId(array_keys($dummyIds), $spilledParents); + + // order by difficulty + foreach ($rep as $r) + $alt[$dummyIds[$r['npc']]][] = $r; + + // apply by difficulty + foreach ($alt as $mode => $dat) + $reputation[] = [Lang::$npc['modes'][$mapType][$mode], $dat]; + } + + // get spillover factions and apply + if ($spilledParents) + { + $spilled = new FactionList(array(['parentFactionId', array_keys($spilledParents)])); + + foreach($reputation as &$sets) { - if (empty($row['spillover'])) - continue; - - foreach ($spilled->iterate() as $spId => $__) + foreach ($sets[1] as &$row) { - // find parent - if ($spilled->getField('parentFactionId') != $row['spillover']) + if (empty($row['spillover'])) continue; - // don't readd parent - if ($row['id'] == $spId) - continue; + foreach ($spilled->iterate() as $spId => $__) + { + // find parent + if ($spilled->getField('parentFactionId') != $row['spillover']) + continue; - $spMax = $spilledParents[$row['spillover']][1]; + // don't readd parent + if ($row['id'] == $spId) + continue; - $sets[1][] = array( - 'id' => $spId, - 'qty' => $spilledParents[$row['spillover']][0], - 'name' => $spilled->getField('name', true), - 'cap' => $spMax && $spMax < REP_EXALTED ? Lang::$game['rep'][$spMax] : null - ); + $spMax = $spilledParents[$row['spillover']][1]; + + $sets[1][] = array( + 'id' => $spId, + 'qty' => $spilledParents[$row['spillover']][0], + 'name' => $spilled->getField('name', true), + 'cap' => $spMax && $spMax < REP_EXALTED ? Lang::$game['rep'][$spMax] : null + ); + } } } } + + return $reputation; } - // Quotes - $quotes = []; - $quoteQuery = ' - SELECT - ct.groupid AS ARRAY_KEY, ct.id as ARRAY_KEY2, ct.`type`, - IFNULL(bct.`Language`, ct.`language`) AS lang, - IFNULL(bct.MaleText, IFNULL(bct.FemaleText, ct.`text`)) AS text_loc0, - IFNULL(lbct.MaleText_loc2, IFNULL(lbct.FemaleText_loc2, lct.text_loc2)) AS text_loc2, - IFNULL(lbct.MaleText_loc3, IFNULL(lbct.FemaleText_loc3, lct.text_loc3)) AS text_loc3, - IFNULL(lbct.MaleText_loc6, IFNULL(lbct.FemaleText_loc6, lct.text_loc6)) AS text_loc6, - IFNULL(lbct.MaleText_loc8, IFNULL(lbct.FemaleText_loc8, lct.text_loc8)) AS text_loc8 - FROM - creature_text ct - LEFT JOIN - locales_creature_text lct ON ct.entry = lct.entry AND ct.groupid = lct.groupid AND ct.id = lct.id - LEFT JOIN - broadcast_text bct ON ct.BroadcastTextId = bct.ID - LEFT JOIN - locales_broadcast_text lbct ON ct.BroadcastTextId = lbct.ID - WHERE - ct.entry = ?d'; - - if ($texts = DB::Aowow()->select($quoteQuery, $_id)) + private function getQuotes() { - $nQuotes = 0; - foreach ($texts as $text) + $nQuotes = 0; + $quotes = []; + $quoteQuery = ' + SELECT + ct.groupid AS ARRAY_KEY, ct.id as ARRAY_KEY2, ct.`type`, + IFNULL(bct.`Language`, ct.`language`) AS lang, + IFNULL(bct.MaleText, IFNULL(bct.FemaleText, ct.`text`)) AS text_loc0, + IFNULL(lbct.MaleText_loc2, IFNULL(lbct.FemaleText_loc2, lct.text_loc2)) AS text_loc2, + IFNULL(lbct.MaleText_loc3, IFNULL(lbct.FemaleText_loc3, lct.text_loc3)) AS text_loc3, + IFNULL(lbct.MaleText_loc6, IFNULL(lbct.FemaleText_loc6, lct.text_loc6)) AS text_loc6, + IFNULL(lbct.MaleText_loc8, IFNULL(lbct.FemaleText_loc8, lct.text_loc8)) AS text_loc8 + FROM + creature_text ct + LEFT JOIN + locales_creature_text lct ON ct.entry = lct.entry AND ct.groupid = lct.groupid AND ct.id = lct.id + LEFT JOIN + broadcast_text bct ON ct.BroadcastTextId = bct.ID + LEFT JOIN + locales_broadcast_text lbct ON ct.BroadcastTextId = lbct.ID + WHERE + ct.entry = ?d'; + + foreach (DB::Aowow()->select($quoteQuery, $this->typeId) as $text) { $group = []; foreach ($text as $t) @@ -357,23 +790,23 @@ if (!$smarty->loadCache($cacheKeyPage, $pageData)) $text = '%s '.$text; $line = array( - 'type' => 2, // [type: 0, 12] say: yellow-ish + 'type' => 2, // [type: 0, 12] say: yellow-ish 'lang' => !empty($t['language']) ? Lang::$game['languages'][$t['language']] : null, - 'text' => sprintf(Util::parseHtmlText(htmlentities($text)), $_name), + 'text' => sprintf(Util::parseHtmlText(htmlentities($text)), $this->name), ); switch ($t['type']) { - case 1: // yell: - case 14: $line['type'] = 1; break; // - dark red - case 2: // emote: - case 16: // " - case 3: // boss emote: - case 41: $line['type'] = 4; break; // - orange - case 4: // whisper: - case 15: // " - case 5: // boss whisper: - case 42: $line['type'] = 3; break; // - pink-ish + case 1: // yell: + case 14: $line['type'] = 1; break; // - dark red + case 2: // emote: + case 16: // " + case 3: // boss emote: + case 41: $line['type'] = 4; break; // - orange + case 4: // whisper: + case 15: // " + case 5: // boss whisper: + case 42: $line['type'] = 3; break; // - pink-ish } $nQuotes++; @@ -381,420 +814,10 @@ if (!$smarty->loadCache($cacheKeyPage, $pageData)) } $quotes[] = $group; } - $quotes = [$quotes, $nQuotes]; + + return [$quotes, $nQuotes]; } - - - // get spawns and such - - - // consider phaseMasks - - // consider pooled spawns - - - // menuId 4: NPC g_initPath() - // tabId 0: Database g_initHeader() - $pageData = array( - 'page' => array( - 'name' => $_name, - 'subname' => $npc->getField('subname', true), - 'infobox' => '[ul][li]'.implode('[/li][li]', $infobox).'[/li][/ul]', - 'difficultyPH' => isset($difficultyPH) ? $difficultyPH : null, - // 'mapper' => true, - 'position' => $position, - 'quotes' => $quotes, - 'reputation' => $reputation, - 'title' => $_name.' - '.Util::ucFirst(Lang::$game['npc']), - 'path' => json_encode($_path, JSON_NUMERIC_CHECK), - 'tab' => 0, - 'type' => TYPE_NPC, - 'typeId' => $_id, - 'reqJS' => [STATIC_URL.'/js/swfobject.js'], - 'redButtons' => array( - BUTTON_WOWHEAD => true, - BUTTON_LINKS => true, - BUTTON_VIEW3D => ['type' => TYPE_NPC, 'typeId' => $_id, 'displayId' => $npc->getRandomModelId()] - ) - ), - 'relTabs' => [] - ); - - /**************/ - /* Extra Tabs */ - /**************/ - - // tab: SAI - // hmm, how should this loot like - - // tab: abilities / tab_controlledabilities (dep: VehicleId) - // SMART_SCRIPT_TYPE_CREATURE = 0; SMART_ACTION_CAST = 11; SMART_ACTION_ADD_AURA = 75; SMART_ACTION_INVOKER_CAST = 85; SMART_ACTION_CROSS_CAST = 86 - $smartSpells = DB::Aowow()->selectCol('SELECT action_param1 FROM smart_scripts WHERE source_type = 0 AND action_type IN (11, 75, 85, 86) AND entryOrGUID = ?d', $_id); - $tplSpells = []; - $conditions = ['OR']; - - for ($i = 1; $i < 9; $i++) - if ($_ = $npc->getField('spell'.$i)) - $tplSpells[] = $_; - - if ($tplSpells) - $conditions[] = ['id', $tplSpells]; - - if ($smartSpells) - $conditions[] = ['id', $smartSpells]; - - if ($tplSpells || $smartSpells) - { - $abilities = new SpellList($conditions); - if (!$abilities->error) - { - $abilities->addGlobalsToJScript(GLOBALINFO_SELF | GLOBALINFO_RELATED); - $normal = $abilities->getListviewData(); - $controled = []; - - if ($npc->getField('vehicleId')) // not quite right. All seats should be checked for allowed-to-cast-flag-something - { - foreach ($normal as $id => $values) - { - if (in_array($id, $smartSpells)) - continue; - - $controled[$id] = $values; - unset($normal[$id]); - } - } - - if ($normal) - $pageData['relTabs'][] = array( - 'file' => 'spell', - 'data' => $normal, - 'params' => [ - 'tabs' => '$tabsRelated', - 'name' => '$LANG.tab_abilities', - 'id' => 'abilities' - ] - ); - - if ($controled) - $pageData['relTabs'][] = array( - 'file' => 'spell', - 'data' => $controled, - 'params' => [ - 'tabs' => '$tabsRelated', - 'name' => '$LANG.tab_controlledabilities', - 'id' => 'controlled-abilities' - ] - ); - } - } - - // tab: summoned by - $conditions = array( - 'OR', - ['AND', ['effect1Id', 28], ['effect1MiscValue', $_id]], - ['AND', ['effect2Id', 28], ['effect2MiscValue', $_id]], - ['AND', ['effect3Id', 28], ['effect3MiscValue', $_id]] - ); - - $summoned = new SpellList($conditions); - if (!$summoned->error) - { - $summoned->addGlobalsToJscript(); - - $pageData['relTabs'][] = array( - 'file' => 'spell', - 'data' => $summoned->getListviewData(), - 'params' => [ - 'tabs' => '$tabsRelated', - 'name' => '$LANG.tab_summonedby', - 'id' => 'summoned-by' - ] - ); - } - - - // tab: teaches - if ($npc->getField('npcflag') & NPC_FLAG_TRAINER) - { - $teachQuery = 'SELECT IFNULL(t2.spell, t1.spell) AS ARRAY_KEY, IFNULL(t2.spellcost, t1.spellcost) AS cost, IFNULL(t2.reqskill, t1.reqskill) AS reqSkillId, - IFNULL(t2.reqskillvalue, t1.reqskillvalue) AS reqSkillValue, IFNULL(t2.reqlevel, t1.reqlevel) AS reqLevel - FROM npc_trainer t1 LEFT JOIN npc_trainer t2 ON t2.entry = IF(t1.spell < 0, -t1.spell, null) WHERE t1.entry = ?d'; - - if ($tSpells = DB::Aowow()->select($teachQuery, $_id)) - { - $teaches = new SpellList(array(['id', array_keys($tSpells)])); - if (!$teaches->error) - { - $teaches->addGlobalsToJScript(GLOBALINFO_SELF | GLOBALINFO_RELATED); - $data = $teaches->getListviewData(); - - $extra = []; - foreach ($tSpells as $sId => $train) - { - if (empty($data[$sId])) - continue; - - if ($_ = $train['reqSkillId']) - { - Util::$pageTemplate->extendGlobalIds(TYPE_SKILL, $_); - if (!isset($extra[0])) - $extra[0] = 'Listview.extraCols.condition'; - - $data[$sId]['condition'][] = ['type' => TYPE_SKILL, 'typeId' => $_, 'status' => 1, 'reqSkillLvl' => $train['reqSkillValue']]; - } - - if ($_ = $train['reqLevel']) - { - if (!isset($extra[1])) - $extra[1] = "Listview.funcBox.createSimpleCol('reqLevel', LANG.tooltip_reqlevel, '7%', 'reqLevel')"; - - $data[$sId]['reqLevel'] = $_; - } - - if ($_ = $train['cost']) - $data[$sId]['trainingcost'] = $_; - } - - $pageData['relTabs'][] = array( - 'file' => 'spell', - 'data' => $data, - 'params' => [ - 'tabs' => '$tabsRelated', - 'name' => '$LANG.tab_teaches', - 'id' => 'teaches', - 'visibleCols' => "$['trainingcost']", - 'extraCols' => $extra ? '$['.implode(', ', $extra).']' : null - ] - ); - } - } - else - Util::$pageTemplate->internalNotice(U_GROUP_EMPLOYEE, 'NPC '.$_id.' is flagged as trainer, but doesn\'t have any spells set'); - } - - // tab: sells - if ($sells = DB::Aowow()->selectCol('SELECT item FROM npc_vendor nv WHERE entry = ?d UNION SELECT item FROM game_event_npc_vendor genv JOIN creature c ON genv.guid = c.guid WHERE c.id = ?d', $_id, $_id)) - { - $soldItems = new ItemList(array(['id', $sells])); - if (!$soldItems->error) - { - $soldItems->addGlobalsToJscript(); - - $pageData['relTabs'][] = array( - 'file' => 'item', - 'data' => $soldItems->getListviewData(ITEMINFO_VENDOR, [TYPE_NPC => $_id]), - 'params' => [ - 'tabs' => '$tabsRelated', - 'name' => '$LANG.tab_sells', - 'id' => 'currency-for', - 'extraCols' => "$[Listview.extraCols.condition, Listview.funcBox.createSimpleCol('stack', 'stack', '10%', 'stack'), Listview.extraCols.cost]" - ] - ); - } - } - - // tabs: this creature contains.. - $skinTab = ['tab_skinning', 'skinned-from']; - if ($_typeFlags & NPC_TYPEFLAG_HERBLOOT) - $skinTab = ['tab_gatheredfromnpc', 'gathered-from-npc']; - else if ($_typeFlags & NPC_TYPEFLAG_MININGLOOT) - $skinTab = ['tab_minedfromnpc', 'mined-from-npc']; - else if ($_typeFlags & NPC_TYPEFLAG_ENGINEERLOOT) - $skinTab = ['tab_salvagedfrom', 'salvaged-from-npc']; - -/* - extraCols: [Listview.extraCols.count, Listview.extraCols.percent, Listview.extraCols.mode], - _totalCount: 22531, - computeDataFunc: Listview.funcBox.initLootTable, - onAfterCreate: Listview.funcBox.addModeIndicator, - - modes:{"mode":1,"1":{"count":4408,"outof":16013},"4":{"count":4408,"outof":22531}} -*/ - - $sourceFor = array( - [LOOT_CREATURE, $npc->getField('lootId'), '$LANG.tab_drops', 'drops', ['Listview.extraCols.percent'], [] , []], - [LOOT_PICKPOCKET, $npc->getField('pickpocketLootId'), '$LANG.tab_pickpocketing', 'pickpocketing', ['Listview.extraCols.percent'], ['side', 'slot', 'reqlevel'], []], - [LOOT_SKINNING, $npc->getField('skinLootId'), '$LANG.'.$skinTab[0], $skinTab[1], ['Listview.extraCols.percent'], ['side', 'slot', 'reqlevel'], []] - ); - - // temp: manually add loot for difficulty-versions - $langref = array( - "-2" => '$LANG.tab_heroic', - "-1" => '$LANG.tab_normal', - 1 => '$$WH.sprintf(LANG.tab_normalX, 10)', - 2 => '$$WH.sprintf(LANG.tab_normalX, 25)', - 3 => '$$WH.sprintf(LANG.tab_heroicX, 10)', - 4 => '$$WH.sprintf(LANG.tab_heroicX, 25)' - ); - - if ($_altIds) - { - $sourceFor[0][2] = $langref[1]; - foreach ($_altNPCs->iterate() as $id => $__) - { - $mode = $_altIds[$id]; - array_splice($sourceFor, 1, 0, [[LOOT_CREATURE, $_altNPCs->getField('lootId'), $langref[$mode + 1], 'drops-'.$mode, ['Listview.extraCols.percent'], [], []]]); - } - } - - $reqQuest = []; - foreach ($sourceFor as $sf) - { - if ($itemLoot = Util::handleLoot($sf[0], $sf[1], User::isInGroup(U_GROUP_STAFF), $sf[4])) - { - foreach ($itemLoot as $l => $lv) - { - if (!$lv['quest']) - continue; - - $sf[4][] = 'Listview.extraCols.condition'; - - $reqQuest[$lv['id']] = 0; - - $itemLoot[$l]['condition'][] = ['type' => TYPE_QUEST, 'typeId' => &$reqQuest[$lv['id']], 'status' => 1]; - } - - $pageData['relTabs'][] = array( - 'file' => 'item', - 'data' => $itemLoot, - 'params' => [ - 'tabs' => '$tabsRelated', - 'name' => $sf[2], - 'id' => $sf[3], - 'extraCols' => $sf[4] ? "$[".implode(', ', array_unique($sf[4]))."]" : null, - 'hiddenCols' => $sf[5] ? "$".json_encode($sf[5]) : null, - 'visibleCols' => $sf[6] ? '$'.json_encode($sf[6]) : null, - 'sort' => "$['-percent', 'name']", - ] - ); - } - } - - 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); - $reqQuests->addGlobalsToJscript(); - - 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: starts quest - // tab: ends quest - $startEnd = new QuestList(array(['qse.type', TYPE_NPC], ['qse.typeId', $_id])); - if (!$startEnd->error) - { - $startEnd->addGlobalsToJScript(); - $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]) - { - $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: objective of quest - $conditions = array( - 'OR', - ['AND', ['reqNpcOrGo1', $_id], ['reqNpcOrGoCount1', 0, '>']], - ['AND', ['reqNpcOrGo2', $_id], ['reqNpcOrGoCount2', 0, '>']], - ['AND', ['reqNpcOrGo3', $_id], ['reqNpcOrGoCount3', 0, '>']], - ['AND', ['reqNpcOrGo4', $_id], ['reqNpcOrGoCount4', 0, '>']], - ); - - $objectiveOf = new QuestList($conditions); - if (!$objectiveOf->error) - { - $objectiveOf->addGlobalsToJScript(); - - $pageData['relTabs'][] = array( - 'file' => 'quest', - 'data' => $objectiveOf->getListviewData(), - 'params' => [ - 'tabs' => '$tabsRelated', - 'name' => '$LANG.tab_objectiveof', - 'id' => 'objective-of' - ] - ); - } - - // tab: criteria of [ACHIEVEMENT_CRITERIA_TYPE_KILL_CREATURE_TYPE have no data set to check for] - $conditions = array( - ['ac.type', [ACHIEVEMENT_CRITERIA_TYPE_KILL_CREATURE, ACHIEVEMENT_CRITERIA_TYPE_KILLED_BY_CREATURE]], - ['ac.value1', $_id] - ); - - $crtOf = new AchievementList($conditions); - if (!$crtOf->error) - { - $crtOf->addGlobalsToJScript(); - - $pageData['relTabs'][] = array( - 'file' => 'achievement', - 'data' => $crtOf->getListviewData(), - 'params' => [ - 'tabs' => '$tabsRelated', - 'name' => '$LANG.tab_criteriaof', - 'id' => 'criteria-of' - ] - ); - } - - $smarty->saveCache($cacheKeyPage, $pageData); } -$smarty->updatePageVars($pageData['page']); -$smarty->assign('community', CommunityContent::getAll(TYPE_NPC, $_id)); // comments, screenshots, videos -$smarty->assign('lang', array_merge(Lang::$main, Lang::$game, Lang::$npc, ['colon' => Lang::$colon])); -$smarty->assign('lvData', $pageData['relTabs']); - -// load the page -$smarty->display('npc.tpl'); ?> diff --git a/pages/npcs.php b/pages/npcs.php index d96d0efe..980cfff4 100644 --- a/pages/npcs.php +++ b/pages/npcs.php @@ -4,90 +4,95 @@ if (!defined('AOWOW_REVISION')) die('illegal access'); -$filter = []; -$cats = Util::extractURLParams($pageParam); -$path = [0, 4]; -$title = [Util::ucFirst(Lang::$game['npcs'])]; -$filterHash = !empty($_GET['filter']) ? sha1(serialize($_GET['filter'])) : -1; -$cacheKey = implode('_', [CACHETYPE_PAGE, TYPE_NPC, -1, ($cats ? $cats[0] : -1).$filterHash, User::$localeId]); -$validCats = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]; - -if (!Util::isValidPage($validCats, $cats)) - $smarty->error(); - -if (!$smarty->loadCache($cacheKey, $pageData, $filter)) +// menuId 4: NPC g_initPath() +// tabId 0: Database g_initHeader() +class NpcsPage extends GenericPage { - $conditions = []; - if ($cats) + use ListPage; + + protected $type = TYPE_NPC; + protected $tpl = 'npcs'; + protected $path = [0, 4]; + protected $tabId = 0; + protected $mode = CACHETYPE_PAGE; + protected $validCats = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]; + protected $js = ['filters.js']; + + public function __construct($pageCall, $pageParam) { - $conditions[] = ['type', $cats[0]]; - $path[] = $cats[0]; - array_unshift($title, Lang::$npc['cat'][$cats[0]]); + $this->getCategoryFromUrl($pageParam);; + + parent::__construct(); + + $this->name = Util::ucFirst(Lang::$game['npcs']); + $this->subCat = $pageParam ? '='.$pageParam : ''; } - $npcFilter = new CreatureListFilter(); - if ($_ = $npcFilter->getConditions()) - $conditions[] = $_; - - // beast subtypes are selected via filter - $npcs = new CreatureList($conditions, ['extraOpts' => $npcFilter->extraOpts]); - - // recreate form selection - $filter = array_merge($npcFilter->getForm('form'), $filter); - $filter['query'] = isset($_GET['filter']) ? $_GET['filter'] : NULL; - $filter['fi'] = $npcFilter->getForm(); - - if (isset($filter['fa'])) - $path[] = $filter['fa']; - - // menuId 4: NPC g_initPath() - // tabId 0: Database g_initHeader() - $pageData = array( - 'page' => array( - 'petFamPanel' => ($cats && $cats[0] == 1), - 'title' => implode(' - ', $title), - 'path' => json_encode($path, JSON_NUMERIC_CHECK), - 'tab' => 0, - 'subCat' => $pageParam ? '='.$pageParam : '', - 'reqJS' => array( - STATIC_URL.'/js/filters.js' - ) - ), - 'lv' => [] - ); - - $lv = array( - 'data' => $npcs->getListviewData(), // listview content - 'params' => [] - ); - - if (!empty($filter['fi']['extraCols'])) - $lv['params']['extraCols'] = '$fi_getExtraCols(fi_extraCols, 0, 0)'; - - // create note if search limit was exceeded - if ($npcs->getMatches() > CFG_SQL_LIMIT_DEFAULT) + protected function generateContent() { - $lv['params']['note'] = sprintf(Util::$tryFilteringString, 'LANG.lvnote_npcsfound', $npcs->getMatches(), CFG_SQL_LIMIT_DEFAULT); - $lv['params']['_truncated'] = 1; + $conditions = []; + + if ($this->category) + { + $conditions[] = ['type', $this->category[0]]; + $this->petFamPanel = $this->category[0] == 1; + } + else + $this->petFamPanel = false; + + $npcFilter = new CreatureListFilter(); + if ($_ = $npcFilter->getConditions()) + $conditions[] = $_; + + // beast subtypes are selected via filter + $npcs = new CreatureList($conditions, ['extraOpts' => $npcFilter->extraOpts]); + + // recreate form selection + $this->filter = array_merge($npcFilter->getForm('form'), $this->filter); + $this->filter['query'] = isset($_GET['filter']) ? $_GET['filter'] : NULL; + $this->filter['fi'] = $npcFilter->getForm(); + + $lv = array( + 'file' => 'creature', + 'data' => $npcs->getListviewData(), // listview content + 'params' => [] + ); + + if (!empty($this->filter['fi']['extraCols'])) + $lv['params']['extraCols'] = '$fi_getExtraCols(fi_extraCols, 0, 0)'; + + // create note if search limit was exceeded + if ($npcs->getMatches() > CFG_SQL_LIMIT_DEFAULT) + { + $lv['params']['note'] = sprintf(Util::$tryFilteringString, 'LANG.lvnote_npcsfound', $npcs->getMatches(), CFG_SQL_LIMIT_DEFAULT); + $lv['params']['_truncated'] = 1; + } + + if ($npcFilter->error) + $lv['params']['_errors'] = '$1'; + + $this->lvData = $lv; + + // sort for dropdown-menus + asort(Lang::$game['fa']); } - if ($npcFilter->error) - $lv['params']['_errors'] = '$1'; + protected function generateTitle() + { + array_unshift($this->title, $this->name); + if ($this->category) + array_unshift($this->title, Lang::$npc['cat'][$this->category[0]]); + } - $pageData['lv'] = $lv; + protected function generatePath() + { + if ($this->category) + $this->path[] = $this->category[0]; - $smarty->saveCache($cacheKey, $pageData); + $form = (new CreatureListFilter())->getForm('form'); + if (isset($form['fa']) && !is_array($form['fa'])) + $this->path[] = $form['fa']; + } } -// sort for dropdown-menus -asort(Lang::$game['fa']); - -$smarty->updatePageVars($pageData['page']); -$smarty->assign('filter', $filter); -$smarty->assign('lang', array_merge(Lang::$main, Lang::$npc, Lang::$game, ['colon' => lang::$colon])); -$smarty->assign('lvData', $pageData['lv']); - -// load the page -$smarty->display('npcs.tpl'); - ?> diff --git a/pages/skill.php b/pages/skill.php index 183c5738..a3f1522b 100644 --- a/pages/skill.php +++ b/pages/skill.php @@ -4,313 +4,368 @@ if (!defined('AOWOW_REVISION')) die('illegal access'); -require 'includes/community.class.php'; - -$_id = intVal($pageParam); -$_path = [0, 14]; - -$cacheKeyPage = implode('_', [CACHETYPE_PAGE, TYPE_SKILL, $_id, -1, User::$localeId]); - -if (!$smarty->loadCache($cacheKeyPage, $pageData)) +// menuId 14: Skill g_initPath() +// tabId 0: Database g_initHeader() +class SkillPage extends GenericPage { - $skill = new SkillList(array(['id', $_id])); - if ($skill->error) - $smarty->notFound(Lang::$game['skill'], $_id); + use DetailPage; - $_cat = $skill->getField('typeCat'); + protected $type = TYPE_SKILL; + protected $typeId = 0; + protected $tpl = 'detail-page-generic'; + protected $path = [0, 14]; + protected $tabId = 0; + protected $mode = CACHETYPE_PAGE; - $_path[] = (in_array($_cat, [9, 11]) || $_id == 762) ? $_id : $_cat; + private $cat = 0; - /****************/ - /* Main Content */ - /****************/ - - // menuId 14: Skill g_initPath() - // tabId 0: Database g_initHeader() - $pageData = array( - 'page' => array( - 'title' => $skill->getField('name', true)." - ".Util::ucfirst(Lang::$game['skill']), - 'name' => $skill->getField('name', true), - 'path' => json_encode($_path, JSON_NUMERIC_CHECK), - 'tab' => 0, - 'type' => TYPE_SKILL, - 'typeId' => $_id, - 'headIcons' => [$skill->getField('iconString')], - 'redButtons' => array( - BUTTON_WOWHEAD => true, - BUTTON_LINKS => true - ) - ), - 'relTabs' => [] - ); - - /**************/ - /* Extra Tabs */ - /**************/ - - if (in_array($_cat, [-5, 9, 11])) + public function __construct($__, $id) { - // tab: recipes [spells] (crafted) - $condition = array( - ['OR', ['s.reagent1', 0, '>'], ['s.reagent2', 0, '>'], ['s.reagent3', 0, '>'], ['s.reagent4', 0, '>'], ['s.reagent5', 0, '>'], ['s.reagent6', 0, '>'], ['s.reagent7', 0, '>'], ['s.reagent8', 0, '>']], - ['OR', ['s.skillLine1', $_id], ['AND', ['s.skillLine1', 0, '>'], ['s.skillLine2OrMask', $_id]]], - 0 + parent::__construct(); + + $this->typeId = intVal($id); + + $this->subject = new SkillList(array(['id', $this->typeId])); + if ($this->subject->error) + $this->notFound(Lang::$game['skill']); + + $this->name = $this->subject->getField('name', true); + $this->cat = $this->subject->getField('typeCat'); + } + + protected function generatePath() + { + $this->path[] = (in_array($this->cat, [9, 11]) || $this->typeId == 762) ? $this->typeId : $this->cat; + } + + protected function generateTitle() + { + array_unshift($this->title, $this->name, Util::ucFirst(Lang::$game['class'])); + } + + protected function generateContent() + { + /****************/ + /* Main Content */ + /****************/ + + $this->headIcons = [$this->subject->getField('iconString')]; + $this->redButtons = array( + BUTTON_WOWHEAD => true, + BUTTON_LINKS => true ); - $recipes = new SpellList($condition); // also relevant for 3 - if (!$recipes->error) - { - $recipes->addGlobalsToJScript(GLOBALINFO_SELF | GLOBALINFO_RELATED); + /**************/ + /* Extra Tabs */ + /**************/ - $pageData['relTabs'][] = array( - 'file' => 'spell', - 'data' => $recipes->getListviewData(), - 'params' => array( - 'tabs' => '$tabsRelated', - 'id' => 'recipes', - 'name' => '$LANG.tab_recipes', - 'visibleCols' => "$['reagents', 'source']", - 'note' => sprintf(Util::$filterResultString, '?spells='.$_cat.'.'.$_id.'&filter=cr=20;crs=1;crv=0') - ) + if (in_array($this->cat, [-5, 9, 11])) + { + // tab: recipes [spells] (crafted) + $condition = array( + ['OR', ['s.reagent1', 0, '>'], ['s.reagent2', 0, '>'], ['s.reagent3', 0, '>'], ['s.reagent4', 0, '>'], ['s.reagent5', 0, '>'], ['s.reagent6', 0, '>'], ['s.reagent7', 0, '>'], ['s.reagent8', 0, '>']], + ['OR', ['s.skillLine1', $this->typeId], ['AND', ['s.skillLine1', 0, '>'], ['s.skillLine2OrMask', $this->typeId]]], + CFG_SQL_LIMIT_NONE ); - } - // tab: recipe Items [items] (Books) - $filterRecipe = [null, 165, 197, 202, 164, 185, 171, 129, 333, 356, 755, 773, 186, 182]; - $conditions = array( - ['requiredSkill', $_id], - ['class', ITEM_CLASS_RECIPE], - 0 - ); - - $recipeItems = new ItemList($conditions); - if (!$recipeItems->error) - { - $recipeItems->addGlobalsToJScript(GLOBALINFO_SELF); - - if ($_ = array_search($_id, $filterRecipe)) - $_ = sprintf(Util::$filterResultString, "?items=9.".$_); - - $pageData['relTabs'][] = array( - 'file' => 'item', - 'data' => $recipeItems->getListviewData(), - 'params' => array( - 'id' => 'recipe-items', - 'name' => '$LANG.tab_recipeitems', - 'tabs' => '$tabsRelated', - 'note' => $_ - ) - ); - } - - // tab: crafted items [items] - $filterItem = [null, 171, 164, 185, 333, 202, 129, 755, 165, 186, 197, null, null, 356, 182, 773]; - $created = []; - foreach ($recipes->iterate() as $__) - if ($idx = $recipes->canCreateItem()) - foreach ($idx as $i) - $created[] = $recipes->getField('effect'.$i.'CreateItemId'); - - if ($created) - { - $created = new ItemList(array(['i.id', $created], 0)); - if (!$created->error) + $recipes = new SpellList($condition); // also relevant for 3 + if (!$recipes->error) { - $created->addGlobalsToJScript(GLOBALINFO_SELF); + $this->extendGlobalData($recipes->getJSGlobals(GLOBALINFO_SELF | GLOBALINFO_RELATED)); - if ($_ = array_search($_id, $filterItem)) - $_ = sprintf(Util::$filterResultString, "?items&filter=cr=86;crs=".$_.";crv=0"); - - $pageData['relTabs'][] = array( - 'file' => 'item', - 'data' => $created->getListviewData(), + $this->lvData[] = array( + 'file' => 'spell', + 'data' => $recipes->getListviewData(), 'params' => array( - 'id' => 'crafted-items', - 'name' => '$LANG.tab_crafteditems', + 'tabs' => '$tabsRelated', + 'id' => 'recipes', + 'name' => '$LANG.tab_recipes', + 'visibleCols' => "$['reagents', 'source']", + 'note' => sprintf(Util::$filterResultString, '?spells='.$this->cat.'.'.$this->typeId.'&filter=cr=20;crs=1;crv=0') + ) + ); + } + + // tab: recipe Items [items] (Books) + $filterRecipe = [null, 165, 197, 202, 164, 185, 171, 129, 333, 356, 755, 773, 186, 182]; + $conditions = array( + ['requiredSkill', $this->typeId], + ['class', ITEM_CLASS_RECIPE], + CFG_SQL_LIMIT_NONE + ); + + $recipeItems = new ItemList($conditions); + if (!$recipeItems->error) + { + $this->extendGlobalData($recipeItems->getJSGlobals(GLOBALINFO_SELF)); + + if ($_ = array_search($this->typeId, $filterRecipe)) + $_ = sprintf(Util::$filterResultString, "?items=9.".$_); + + $this->lvData[] = array( + 'file' => 'item', + 'data' => $recipeItems->getListviewData(), + 'params' => array( + 'id' => 'recipe-items', + 'name' => '$LANG.tab_recipeitems', 'tabs' => '$tabsRelated', 'note' => $_ ) ); } - } - // tab: required by [item] - $conditions = array( - ['requiredSkill', $_id], - ['class', ITEM_CLASS_RECIPE, '!'], - 0 - ); + // tab: crafted items [items] + $filterItem = [null, 171, 164, 185, 333, 202, 129, 755, 165, 186, 197, null, null, 356, 182, 773]; + $created = []; + foreach ($recipes->iterate() as $__) + if ($idx = $recipes->canCreateItem()) + foreach ($idx as $i) + $created[] = $recipes->getField('effect'.$i.'CreateItemId'); - $reqBy = new ItemList($conditions); - if (!$reqBy->error) - { - $reqBy->addGlobalsToJScript(GLOBALINFO_SELF); - - if ($_ = array_search($_id, $filterItem)) - $_ = sprintf(Util::$filterResultString, "?items&filter=cr=99:168;crs=".$_.":2;crv=0:0"); - - $pageData['relTabs'][] = array( - 'file' => 'item', - 'data' => $reqBy->getListviewData(), - 'params' => array( - 'id' => 'required-by', - 'name' => '$LANG.tab_requiredby', - 'tabs' => '$tabsRelated', - 'note' => $_ - ) - ); - } - - // tab: required by [itemset] - $conditions = array( - ['skillId', $_id], - 0 - ); - - $reqBy = new ItemsetList($conditions); - if (!$reqBy->error) - { - $reqBy->addGlobalsToJScript(GLOBALINFO_SELF); - - $pageData['relTabs'][] = array( - 'file' => 'itemset', - 'data' => $reqBy->getListviewData(), - 'params' => array( - 'id' => 'required-by-set', - 'name' => '$LANG.tab_requiredby', - 'tabs' => '$tabsRelated' - ) - ); - } - } - - // tab: spells [spells] (exclude first tab) - $reqClass = 0x0; - $reqRace = 0x0; - $condition = array( - ['AND', ['s.reagent1', 0], ['s.reagent2', 0], ['s.reagent3', 0], ['s.reagent4', 0], ['s.reagent5', 0], ['s.reagent6', 0], ['s.reagent7', 0], ['s.reagent8', 0]], - ['OR', ['s.skillLine1', $_id], ['AND', ['s.skillLine1', 0, '>'], ['s.skillLine2OrMask', $_id]]], - 0 - ); - - foreach (Util::$skillLineMask as $line1 => $sets) - foreach ($sets as $idx => $set) - if ($set[1] == $_id) + if ($created) { - $condition[1][] = array('AND', ['s.skillLine1', $line1], ['s.skillLine2OrMask', 1 << $idx, '&']); - break 2; + $created = new ItemList(array(['i.id', $created], CFG_SQL_LIMIT_NONE)); + if (!$created->error) + { + $this->extendGlobalData($created->getJSGlobals(GLOBALINFO_SELF)); + + if ($_ = array_search($this->typeId, $filterItem)) + $_ = sprintf(Util::$filterResultString, "?items&filter=cr=86;crs=".$_.";crv=0"); + + $this->lvData[] = array( + 'file' => 'item', + 'data' => $created->getListviewData(), + 'params' => array( + 'id' => 'crafted-items', + 'name' => '$LANG.tab_crafteditems', + 'tabs' => '$tabsRelated', + 'note' => $_ + ) + ); + } } - $spells = new SpellList($condition); - if (!$spells->error) - { - foreach ($spells->iterate() as $__) - { - $reqClass |= $spells->getField('reqClassMask'); - $reqRace |= $spells->getField('reqRaceMask'); - } - - $spells->addGlobalsToJScript(GLOBALINFO_SELF); - - $pageData['relTabs'][] = array( - 'file' => 'spell', - 'data' => $spells->getListviewData(), - 'params' => array( - 'tabs' => '$tabsRelated', - 'visibleCols' => "$['source']" - ) - ); - - switch ($_cat) - { - case -4: - $pageData['spells']['params']['note'] = sprintf(Util::$filterResultString, '?spells=-4'); - break; - case 7: - if ($_id != 769) // Internal - $pageData['spells']['params']['note'] = sprintf(Util::$filterResultString, '?spells='.$_cat.'.'.(log($reqClass, 2) + 1).'.'.$_id); // doesn't matter what spell; reqClass should be identical for all Class Spells - break; - case 9: - case 11: - $pageData['spells']['params']['note'] = sprintf(Util::$filterResultString, '?spells='.$_cat.'.'.$_id); - break; - - } - } - - // tab: trainers [npcs] - if (in_array($_cat, [-5, 6, 7, 8, 9, 11])) - { - $list = []; - if (@$tt = Util::$trainerTemplates[TYPE_SKILL][$_id]) - $list = DB::Aowow()->selectCol('SELECT DISTINCT entry FROM npc_trainer WHERE spell IN (?a) AND entry < 200000', $tt); - else - { - $mask = 0; - foreach (Util::$skillLineMask[-3] as $idx => $pair) - if ($pair[1] == $_id) - $mask |= 1 << $idx; - - $list = DB::Aowow()->selectCol(' - SELECT IF(t1.entry > 200000, t2.entry, t1.entry) - FROM npc_trainer t1 - JOIN aowow_spell s ON s.id = t1.spell - LEFT JOIN npc_trainer t2 ON t2.spell = -t1.entry - WHERE s.typeCat IN (-11, 9) AND (s.skillLine1 = ?d OR (s.skillLine1 > 0 AND s.skillLine2OrMask = ?d) '.($mask ? ' OR (s.skilllIne1 = -3 AND s.skillLine2OrMask = '.$mask.')' : null).')', - $_id, - $_id + // tab: required by [item] + $conditions = array( + ['requiredSkill', $this->typeId], + ['class', ITEM_CLASS_RECIPE, '!'], + CFG_SQL_LIMIT_NONE ); - } - if ($list) - { - $trainer = new CreatureList(array(0, ['ct.id', $list], ['ct.spawns', 0, '>'], ['ct.npcflag', 0x10, '&'])); - - if (!$trainer->error) + $reqBy = new ItemList($conditions); + if (!$reqBy->error) { - $trainer->addGlobalsToJscript(); + $this->extendGlobalData($reqBy->getJSGlobals(GLOBALINFO_SELF)); - $pageData['relTabs'][] = array( - 'file' => 'creature', - 'data' => $trainer->getListviewData(), + if ($_ = array_search($this->typeId, $filterItem)) + $_ = sprintf(Util::$filterResultString, "?items&filter=cr=99:168;crs=".$_.":2;crv=0:0"); + + $this->lvData[] = array( + 'file' => 'item', + 'data' => $reqBy->getListviewData(), 'params' => array( + 'id' => 'required-by', + 'name' => '$LANG.tab_requiredby', 'tabs' => '$tabsRelated', - 'id' => 'trainer', - 'name' => '$LANG.tab_trainers', + 'note' => $_ + ) + ); + } + + // tab: required by [itemset] + $conditions = array( + ['skillId', $this->typeId], + CFG_SQL_LIMIT_NONE + ); + + $reqBy = new ItemsetList($conditions); + if (!$reqBy->error) + { + $this->extendGlobalData($reqBy->getJSGlobals(GLOBALINFO_SELF)); + + $this->lvData[] = array( + 'file' => 'itemset', + 'data' => $reqBy->getListviewData(), + 'params' => array( + 'id' => 'required-by-set', + 'name' => '$LANG.tab_requiredby', + 'tabs' => '$tabsRelated' ) ); } } - } - // tab: quests [quests] - if (in_array($_cat, [9, 11])) // only for professions - { - $sort = 0; - switch ($_id) + // tab: spells [spells] (exclude first tab) + $reqClass = 0x0; + $reqRace = 0x0; + $condition = array( + ['AND', ['s.reagent1', 0], ['s.reagent2', 0], ['s.reagent3', 0], ['s.reagent4', 0], ['s.reagent5', 0], ['s.reagent6', 0], ['s.reagent7', 0], ['s.reagent8', 0]], + ['OR', ['s.skillLine1', $this->typeId], ['AND', ['s.skillLine1', 0, '>'], ['s.skillLine2OrMask', $this->typeId]]], + CFG_SQL_LIMIT_NONE + ); + + foreach (Util::$skillLineMask as $line1 => $sets) + foreach ($sets as $idx => $set) + if ($set[1] == $this->typeId) + { + $condition[1][] = array('AND', ['s.skillLine1', $line1], ['s.skillLine2OrMask', 1 << $idx, '&']); + break 2; + } + + $spells = new SpellList($condition); + if (!$spells->error) { - case 182: $sort = 24; break; // Herbalism - case 356: $sort = 101; break; // Fishing - case 164: $sort = 121; break; // Blacksmithing - case 171: $sort = 181; break; // Alchemy - case 165: $sort = 182; break; // Leatherworking - case 202: $sort = 201; break; // Engineering - case 197: $sort = 264; break; // Tailoring - case 185: $sort = 304; break; // Cooking - case 129: $sort = 324; break; // First Aid - case 773: $sort = 371; break; // Inscription - case 755: $sort = 373; break; // Jewelcrafting + foreach ($spells->iterate() as $__) + { + $reqClass |= $spells->getField('reqClassMask'); + $reqRace |= $spells->getField('reqRaceMask'); + } + + $this->extendGlobalData($spells->getJSGlobals(GLOBALINFO_SELF)); + + $lv = array( + 'file' => 'spell', + 'data' => $spells->getListviewData(), + 'params' => array( + 'tabs' => '$tabsRelated', + 'visibleCols' => "$['source']" + ) + ); + + switch ($this->cat) + { + case -4: + $lv['params']['note'] = sprintf(Util::$filterResultString, '?spells=-4'); + break; + case 7: + if ($this->typeId != 769) // Internal + $lv['params']['note'] = sprintf(Util::$filterResultString, '?spells='.$this->cat.'.'.(log($reqClass, 2) + 1).'.'.$this->typeId); // doesn't matter what spell; reqClass should be identical for all Class Spells + break; + case 9: + case 11: + $lv['params']['note'] = sprintf(Util::$filterResultString, '?spells='.$this->cat.'.'.$this->typeId); + break; + } + + $this->lvData[] = $lv; } - if ($sort) + // tab: trainers [npcs] + if (in_array($this->cat, [-5, 6, 7, 8, 9, 11])) { - $quests = new QuestList(array(['zoneOrSort', -$sort], 0)); - if (!$quests->error) + $list = []; + if (@$tt = Util::$trainerTemplates[TYPE_SKILL][$this->typeId]) + $list = DB::Aowow()->selectCol('SELECT DISTINCT entry FROM npc_trainer WHERE spell IN (?a) AND entry < 200000', $tt); + else { - $quests->addGlobalsToJScript(); - $pageData['relTabs'][] = array( - 'file' => 'quest', - 'data' => $quests->getListviewData(), + $mask = 0; + foreach (Util::$skillLineMask[-3] as $idx => $pair) + if ($pair[1] == $this->typeId) + $mask |= 1 << $idx; + + $list = DB::Aowow()->selectCol(' + SELECT IF(t1.entry > 200000, t2.entry, t1.entry) + FROM npc_trainer t1 + JOIN aowow_spell s ON s.id = t1.spell + LEFT JOIN npc_trainer t2 ON t2.spell = -t1.entry + WHERE s.typeCat IN (-11, 9) AND (s.skillLine1 = ?d OR (s.skillLine1 > 0 AND s.skillLine2OrMask = ?d) '.($mask ? ' OR (s.skilllIne1 = -3 AND s.skillLine2OrMask = '.$mask.')' : null).')', + $this->typeId, + $this->typeId + ); + } + + if ($list) + { + $trainer = new CreatureList(array(CFG_SQL_LIMIT_NONE, ['ct.id', $list], ['ct.spawns', 0, '>'], ['ct.npcflag', 0x10, '&'])); + + if (!$trainer->error) + { + $this->extendGlobalData($trainer->getJSGlobals()); + + $this->lvData[] = array( + 'file' => 'creature', + 'data' => $trainer->getListviewData(), + 'params' => array( + 'tabs' => '$tabsRelated', + 'id' => 'trainer', + 'name' => '$LANG.tab_trainers', + ) + ); + } + } + } + + // tab: quests [quests] + if (in_array($this->cat, [9, 11])) // only for professions + { + $sort = 0; + switch ($this->typeId) + { + case 182: $sort = 24; break; // Herbalism + case 356: $sort = 101; break; // Fishing + case 164: $sort = 121; break; // Blacksmithing + case 171: $sort = 181; break; // Alchemy + case 165: $sort = 182; break; // Leatherworking + case 202: $sort = 201; break; // Engineering + case 197: $sort = 264; break; // Tailoring + case 185: $sort = 304; break; // Cooking + case 129: $sort = 324; break; // First Aid + case 773: $sort = 371; break; // Inscription + case 755: $sort = 373; break; // Jewelcrafting + } + + if ($sort) + { + $quests = new QuestList(array(['zoneOrSort', -$sort], CFG_SQL_LIMIT_NONE)); + if (!$quests->error) + { + $this->extendGlobalData($quests->getJSGlobals()); + $this->lvData[] = array( + 'file' => 'quest', + 'data' => $quests->getListviewData(), + 'params' => array( + 'tabs' => '$tabsRelated', + ) + ); + } + } + } + + // tab: related classes (apply classes from [spells]) + $class = []; + for ($i = 0; $i < 11; $i++) + if ($reqClass & (1 << $i)) + $class[] = $i + 1; + + if ($class) + { + $classes = new CharClassList(array(['id', $class])); + if (!$classes->error) + { + $this->lvData[] = array( + 'file' => 'class', + 'data' => $classes->getListviewData(), + 'params' => array( + 'tabs' => '$tabsRelated', + ) + ); + } + } + + // tab: related races (apply races from [spells]) + $race = []; + for ($i = 0; $i < 12; $i++) + if ($reqRace & (1 << $i)) + $race[] = $i + 1; + + if ($race) + { + $races = new CharRaceList(array(['id', $race])); + if (!$races->error) + { + $this->lvData[] = array( + 'file' => 'race', + 'data' => $races->getListviewData(), 'params' => array( 'tabs' => '$tabsRelated', ) @@ -318,58 +373,7 @@ if (!$smarty->loadCache($cacheKeyPage, $pageData)) } } } - - // tab: related classes (apply classes from [itemset]) - $class = []; - for ($i = 0; $i < 11; $i++) - if ($reqClass & (1 << $i)) - $class[] = $i + 1; - - if ($class) - { - $classes = new CharClassList(array(['id', $class])); - if (!$classes->error) - { - $pageData['relTabs'][] = array( - 'file' => 'class', - 'data' => $classes->getListviewData(), - 'params' => array( - 'tabs' => '$tabsRelated', - ) - ); - } - } - - // tab: related races (apply races from [itemset]) - $race = []; - for ($i = 0; $i < 12; $i++) - if ($reqRace & (1 << $i)) - $race[] = $i + 1; - - if ($race) - { - $races = new CharRaceList(array(['id', $race])); - if (!$races->error) - { - $pageData['relTabs'][] = array( - 'file' => 'race', - 'data' => $races->getListviewData(), - 'params' => array( - 'tabs' => '$tabsRelated', - ) - ); - } - } - - $smarty->saveCache($cacheKeyPage, $pageData); } -$smarty->updatePageVars($pageData['page']); -$smarty->assign('community', CommunityContent::getAll(TYPE_SKILL, $_id)); // comments, screenshots, videos -$smarty->assign('lang', array_merge(Lang::$main)); -$smarty->assign('lvData', $pageData['relTabs']); - -// load the page -$smarty->display('detail-page-generic.tpl'); ?> diff --git a/pages/skills.php b/pages/skills.php index 7c54a090..88672a6b 100644 --- a/pages/skills.php +++ b/pages/skills.php @@ -4,53 +4,57 @@ if (!defined('AOWOW_REVISION')) die('illegal access'); -$cat = Util::extractURLParams($pageParam); -$path = [0, 14]; -$title = [Util::ucFirst(Lang::$game['skills'])]; -$cacheKey = implode('_', [CACHETYPE_PAGE, TYPE_SKILL, -1, $cat ? $cat[0] : -1, User::$localeId]); -$validCats = [-6, -5, -4, 6, 7, 8, 9, 10, 11]; - -if (!Util::isValidPage($validCats, $cat)) - $smarty->error(); - -if (!$smarty->loadCache($cacheKey, $pageData)) +// menuId 14: Skill g_initPath() +// tabId 0: Database g_initHeader() +class SkillsPage extends GenericPage { - $conditions = [['categoryId', 12, '!']]; // DND - if ($cat) + use ListPage; + + protected $type = TYPE_SKILL; + protected $tpl = 'list-page-generic'; + protected $path = [0, 14]; + protected $tabId = 0; + protected $mode = CACHETYPE_PAGE; + protected $validCats = [-6, -5, -4, 6, 7, 8, 9, 10, 11]; + + public function __construct($pageCall, $pageParam) { - $conditions[] = ['typeCat', $cat[0]]; - $path[] = $cat[0]; - array_unshift($title, Lang::$skill['cat'][$cat[0]]); + $this->getCategoryFromUrl($pageParam);; + + parent::__construct(); + + $this->name = Util::ucFirst(Lang::$game['skills']); } - $skills = new SkillList($conditions); + protected function generateContent() + { + $conditions = []; + if (User::isInGroup(U_GROUP_EMPLOYEE)) + $conditions[] = ['categoryId', 12, '!']; // GENERIC (DND) - // menuId 14: Skill g_initPath() - // tabId 0: Database g_initHeader() - $pageData = array( - 'page' => array( - 'title' => implode(' - ', $title), - 'path' => json_encode($path, JSON_NUMERIC_CHECK), - 'tab' => 0 - ), - 'lv' => array( - array( - 'file' => 'skill', - 'data' => $skills->getListviewData(), - 'params' => [] - ) - ) - ); + if ($this->category) + $conditions[] = ['typeCat', $this->category[0]]; - $smarty->saveCache($cacheKey, $pageData); + $skills = new SkillList($conditions); + + $this->lvData[] = array( + 'file' => 'skill', + 'data' => $skills->getListviewData(), // listview content + 'params' => [] + ); + } + + protected function generateTitle() + { + if ($this->category) + array_unshift($this->title, Lang::$skill['cat'][$this->category[0]]); + } + + protected function generatePath() + { + if ($this->category) + $this->path[] = $this->category[0]; + } } - -$smarty->updatePageVars($pageData['page']); -$smarty->assign('lang', Lang::$main); -$smarty->assign('lvData', $pageData['lv']); - -// load the page -$smarty->display('list-page-generic.tpl'); - ?> diff --git a/static/js/global.js b/static/js/global.js index 8bbef298..f9e22272 100644 --- a/static/js/global.js +++ b/static/js/global.js @@ -6235,7 +6235,7 @@ Listview.extraCols = { id: 'condition', name: LANG.requires, - width: '30%', + width: '25%', compute: function(row, td) { if (!row.condition || !$WH.is_array(row.condition)) { return; diff --git a/template/pages/itemset.tpl b/template/pages/itemset.tpl deleted file mode 100644 index cbad0248..00000000 --- a/template/pages/itemset.tpl +++ /dev/null @@ -1,81 +0,0 @@ -{include file='header.tpl'} - -
-
-
- -{if !empty($announcements)} - {foreach from=$announcements item=item} - {include file='bricks/announcement.tpl' an=$item} - {/foreach} -{/if} - - - -{include file='bricks/infobox.tpl'} - -
-{include file='bricks/headIcons.tpl'} - -{include file='bricks/redButtons.tpl'} - - {$name}{else}>{$name}{/if} - -{include file='bricks/article.tpl'} - -{$description} - - - - -{section name=i loop=$pieces} - -{/section} -
{$pieces[i].name}
- - - -{if $unavailable} -
- {$lang._unavailable} -{/if} - -

{$lang._setBonuses}{$bonusExt}

- - {$lang._conveyBonus} -
    -{section name=i loop=$spells} -
  • {$spells[i].bonus} {$lang._pieces}{$lang.colon}{$spells[i].desc}
  • -{/section} -
- -

{$lang.summary}

- -
- - -

{$lang.related}

-
- -{include file='bricks/tabsRelated.tpl' tabs=$lvData} - -{include file='bricks/contribute.tpl'} - -
-
- -{include file='footer.tpl'} diff --git a/template/pages/itemset.tpl.php b/template/pages/itemset.tpl.php new file mode 100644 index 00000000..dfb27d65 --- /dev/null +++ b/template/pages/itemset.tpl.php @@ -0,0 +1,94 @@ +brick('header'); ?> + +
+
+
+ +brick('announcement'); ?> + + + +brick('infobox'); ?> + +
+brick('redButtons'); + +if ($this->expansion): + echo '

'.$this->name."

\n"; +else: + echo '

'.$this->name."

\n"; +endif; + +$this->brick('article'); + +echo $this->description; +?> + + + +pieces as $i => $p): + echo ' \n"; +endforeach; +?> +
'.$p['name']."
+ + + +unavailable): +?> +
+ + + +

bonusExt; ?>

+ + +
    +spells as $i => $s): + echo '
  • '.$s['bonus'].' '.Lang::$itemset['_pieces'].Lang::$main['colon'].''.$s['desc']."
  • \n"; +endforeach; +?> +
+ +

+ +
+ + +

+
+ +brick('tabsRelated'); + +$this->brick('contribute'); +?> + +
+
+ +brick('footer'); ?> diff --git a/template/pages/itemsets.tpl b/template/pages/itemsets.tpl deleted file mode 100644 index a50d391b..00000000 --- a/template/pages/itemsets.tpl +++ /dev/null @@ -1,114 +0,0 @@ -{include file='header.tpl'} - -
-
-
- -{if !empty($announcements)} - {foreach from=$announcements item=item} - {include file='bricks/announcement.tpl' an=$item} - {/foreach} -{/if} - - - -
-
-
-
{$lang._quality}{$lang.colon}
- {$lang.clear} -
- -
- -
-
{$lang.type}{$lang.colon}
- {$lang.clear} -
- -
- - - - - - - - - - - - - - -
{$lang.name|ucFirst}{$lang.colon} 
{$lang.level}{$lang.colon}  - - - - -
   {$lang._reqLevel}{$lang.colon}  -
-
{$lang.class|ucfirst}{$lang.colon}  - - - -
   {$lang._tag}{$lang.colon} 
-
- -
-
{$lang.addFilter}
- -
-
{$lang.refineSearch}
- {$lang.match}{$lang.colon} -
- -
- -
- - -
- -
-
-
- - - -
- - -
-
-
- -{include file='footer.tpl'} diff --git a/template/pages/itemsets.tpl.php b/template/pages/itemsets.tpl.php new file mode 100644 index 00000000..6497bda8 --- /dev/null +++ b/template/pages/itemsets.tpl.php @@ -0,0 +1,133 @@ +brick('header'); +$f = $this->filter +?> + +
+
+
+ +brick('announcement'); ?> + + + +
+
+
+
+ +
+ +
+ +
+
+ +
+ +
+ + + + + + + + + + + + + + +
 />
 /> - /> + + + +
    /> - />
+
  + + + +
    
+
+ +
+
+ +
+
+ /> /> +
+ +
+ +
+ + +
+ +
+
+
+ + + +
+ + +
+
+
+ +brick('footer'); ?> diff --git a/template/pages/npc.tpl b/template/pages/npc.tpl.php similarity index 51% rename from template/pages/npc.tpl rename to template/pages/npc.tpl.php index 02681177..e3d1fa90 100644 --- a/template/pages/npc.tpl +++ b/template/pages/npc.tpl.php @@ -1,40 +1,42 @@ -{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}{if $subname} <{$subname}>{/if}

+

name.($this->subname ? ' <'.$this->subname.'>' : null); ?>

-{include file='bricks/article.tpl'} +brick('article'); -{if is_array($position)} -
{$lang.difficultyPH} {$position[1]}.
+if (is_array($this->position)): + echo '
'.Lang::$npc['difficultyPH'].' '.$this->position[1].".
\n"; +?>
-{elseif $position} -
{#This_NPC_can_be_found_in#} {strip} +position)): +?> +
{#This_NPC_can_be_found_in#} {foreach from=$position item=zone name=zone}
',type:'{$point.type}' - {rdelim}] + }] {if !$smarty.foreach.point.last},{/if} {/foreach} ] {/if} - {rdelim}); + }); $WH.ge('mapper-generic').style.display='block'; {else} $WH.ge('mapper-generic').style.display='none'; @@ -72,57 +74,84 @@ {$zone.name}{if $zone.population > 1} ({$zone.population}){/if}{if $smarty.foreach.zone.last}.{else}, {/if} {/foreach}
-{/strip} +
-{else} - {$lang.unkPosition} -{/if} +{$lang.quotes} ({$quotes[1]}) - -{/if} +if ($this->quotes[0]): +?> +

quotes[1]; ?>)

+ - {/foreach} - - {if $reputation|@count > 1}{/if} -{/foreach} + $last = end($group); + foreach ($group as $itr): + echo '
'.($itr['type'] != 4 ? $this->name.' '.Lang::$npc['textTypes'][$itr['type']].Lang::$main['colon'].($itr['lang'] ? '['.$itr['lang'].']' : null) : null).$itr['text'].'
'; + echo ($itr == $last) ? null : "\n
  • "; + endforeach; -{/if} -

    {$lang.related}

    + echo "
  • \n"; + + if (count($group) > 1 && count($this->quotes[0]) > 1): + echo "\n"; + endif; + + endforeach; +?> +
    +reputation): +?> +

    +reputation as $set): + if (count($this->reputation) > 1): + echo '
    '; + endforeach; + + echo ''; + + if (count($this->reputation) > 1): + echo ''; + endif; + endforeach; +endif; +?> +

    -{include file='bricks/tabsRelated.tpl' tabs=$lvData} - -{include file='bricks/contribute.tpl'} +brick('tabsRelated'); +$this->brick('contribute'); +?> -{include file='footer.tpl'} +brick('footer'); ?> diff --git a/template/pages/npcs.tpl b/template/pages/npcs.tpl deleted file mode 100644 index 8df93b93..00000000 --- a/template/pages/npcs.tpl +++ /dev/null @@ -1,112 +0,0 @@ -{include file='header.tpl'} - -
    -
    -
    - -{if !empty($announcements)} - {foreach from=$announcements item=item} - {include file='bricks/announcement.tpl' an=$item} - {/foreach} -{/if} - - - -
    -
    -
    -
    {$lang.classification}{$lang.colon}
    - {$lang.clear} -
    - -
    -{if $petFamPanel} -
    -
    {$lang.petFamily}{$lang.colon}
    {$lang.clear} -
    - -
    -{/if} - - - - - - - - - -
    {$lang.name|ucFirst}{$lang.colon} - - - - -
      
    -
    {$lang.level}{$lang.colon}  - - - - -
       {$lang.react}{$lang.colon}  - -
    -
    - -
    -
    {$lang.addFilter}
    - -
    -
    {$lang.refineSearch}
    - {$lang.match}{$lang.colon} -
    - -
    - -
    - - -
    - -
    -
    -
    - - - -
    - - -
    -
    -
    - -{include file='footer.tpl'} diff --git a/template/pages/npcs.tpl.php b/template/pages/npcs.tpl.php new file mode 100644 index 00000000..944e8e57 --- /dev/null +++ b/template/pages/npcs.tpl.php @@ -0,0 +1,123 @@ +brick('header'); +$f = $this->filter; // shorthand +?> + +
    +
    +
    + +brick('announcement'); ?> + + + +
    +
    +
    +
    + +
    + +
    +petFamPanel): ?> +
    +
    +
    + +
    + + + + + + + + + + +
    + + + + +
     />  />
    +
     /> - /> + + + +
         + +
    +
    + +
    +
    + +
    +
    + /> /> +
    + +
    + +
    + + +
    + +
    +
    +
    + + + +
    + + +
    +
    +
    + +brick('footer'); ?>