diff --git a/pages/item.php b/endpoints/item/item.php similarity index 63% rename from pages/item.php rename to endpoints/item/item.php index ad21cc58..5d81b9b2 100644 --- a/pages/item.php +++ b/endpoints/item/item.php @@ -6,142 +6,80 @@ if (!defined('AOWOW_REVISION')) die('illegal access'); -// menuId 0: Item g_initPath() -// tabId 0: Database g_initHeader() -class ItemPage extends genericPage +class ItemBaseResponse extends TemplateResponse implements ICache { - use TrDetailPage; + use TrDetailPage, TrCache; - protected $pageText = []; - protected $tooltip = null; - protected $unavailable = false; - protected $subItems = []; + protected int $cacheType = CACHE_TYPE_PAGE; - protected $type = Type::ITEM; - protected $typeId = 0; - protected $tpl = 'item'; - protected $path = [0, 0]; - protected $tabId = 0; - protected $mode = CACHE_TYPE_PAGE; - protected $enhancedTT = []; - protected $scripts = array( + protected string $template = 'item'; + protected string $pageName = 'item'; + protected ?int $activeTab = parent::TAB_DATABASE; + protected array $breadcrumb = [0, 0]; + + protected array $scripts = array( [SC_JS_FILE, 'js/swfobject.js'], [SC_JS_FILE, 'js/profile.js'], [SC_JS_FILE, 'js/filters.js'] ); - protected $_get = array( - 'domain' => ['filter' => FILTER_CALLBACK, 'options' => 'Aowow\Locale::tryFromDomain'], - 'rand' => ['filter' => FILTER_CALLBACK, 'options' => 'Aowow\GenericPage::checkInt'], - 'ench' => ['filter' => FILTER_CALLBACK, 'options' => 'Aowow\GenericPage::checkInt'], - 'gems' => ['filter' => FILTER_CALLBACK, 'options' => 'Aowow\GenericPage::checkIntArray'], - 'sock' => ['filter' => FILTER_CALLBACK, 'options' => 'Aowow\GenericPage::checkEmptySet'] - ); + public int $type = Type::ITEM; + public int $typeId = 0; + public bool $unavailable = false; + public ?Book $book = null; + public ?array $subItems = null; + public array $tooltip = []; - private $powerTpl = '$WowheadPower.registerItem(%s, %d, %s);'; + private ItemList $subject; - public function __construct($pageCall, $param) + public function __construct(string $id) { - parent::__construct($pageCall, $param); + parent::__construct($id); - $conditions = [['i.id', intVal($param)]]; + $this->typeId = intVal($id); + $this->contribute = Type::getClassAttrib($this->type, 'contribute') ?? CONTRIBUTE_NONE; + } - $this->typeId = intVal($param); - - if ($this->mode == CACHE_TYPE_TOOLTIP) - { - // temp locale - if ($this->_get['domain']) - Lang::load($this->_get['domain']); - - if ($this->_get['rand']) - $this->enhancedTT['r'] = $this->_get['rand']; - if ($this->_get['ench']) - $this->enhancedTT['e'] = $this->_get['ench']; - if ($this->_get['gems']) - $this->enhancedTT['g'] = $this->_get['gems']; - if ($this->_get['sock']) - $this->enhancedTT['s'] = ''; - } - else if ($this->mode == CACHE_TYPE_XML) - { - // temp locale - if ($this->_get['domain']) - Lang::load($this->_get['domain']); - - // allow lookup by name for xml - if (!is_numeric($param)) - $conditions = [['name_loc'.Lang::getLocale()->value, urldecode($param)]]; - } - - $this->subject = new ItemList($conditions); + protected function generate() : void + { + $this->subject = new ItemList(array(['i.id', $this->typeId])); if ($this->subject->error) - $this->notFound(Lang::game('item'), Lang::item('notFound')); + $this->generateNotFound(Lang::game('item'), Lang::item('notFound')); - if (!is_numeric($param)) - $this->typeId = $this->subject->id; + $jsg = $this->subject->getJSGlobals(GLOBALINFO_EXTRA | GLOBALINFO_SELF, $extra); + $this->extendGlobalData($jsg, $extra); - $this->name = Lang::unescapeUISequences($this->subject->getField('name', true), Lang::FMT_HTML); + $this->h1 = Lang::unescapeUISequences($this->subject->getField('name', true), Lang::FMT_HTML); - if ($this->mode == CACHE_TYPE_PAGE) - { - $jsg = $this->subject->getJSGlobals(GLOBALINFO_EXTRA | GLOBALINFO_SELF, $extra); - $this->extendGlobalData($jsg, $extra); - } - } - - protected function generatePath() - { - $_class = $this->subject->getField('class'); - $_subClass = $this->subject->getField('subClass'); - - if (in_array($_class, [ITEM_CLASS_REAGENT, ITEM_CLASS_GENERIC, ITEM_CLASS_PERMANENT])) - { - $this->path[] = ITEM_CLASS_MISC; // misc. - - if ($_class == ITEM_CLASS_REAGENT) // reagent - $this->path[] = 1; - else // other - $this->path[] = 4; - } - else - { - $this->path[] = $_class; - - if (!in_array($_class, [ITEM_CLASS_MONEY, ITEM_CLASS_QUEST, ITEM_CLASS_KEY])) - $this->path[] = $_subClass; - - if ($_class == ITEM_CLASS_ARMOR && in_array($_subClass, [1, 2, 3, 4])) - { - if ($_ = $this->subject->getField('slot')); - $this->path[] = $_; - } - else if (($_class == ITEM_CLASS_CONSUMABLE && $_subClass == 2) || $_class == ITEM_CLASS_GLYPH) - $this->path[] = $this->subject->getField('subSubClass'); - } - } - - protected function generateTitle() - { - array_unshift($this->title, Lang::unescapeUISequences($this->subject->getField('name', true), Lang::FMT_RAW), Util::ucFirst(Lang::game('item'))); - } - - protected function generateContent() - { - $this->addScript([SC_JS_FILE, '?data=weight-presets.zones']); + $this->gPageInfo += array( + 'type' => $this->type, + 'typeId' => $this->typeId, + 'name' => $this->h1 + ); $_flags = $this->subject->getField('flags'); $_slot = $this->subject->getField('slot'); $_class = $this->subject->getField('class'); $_subClass = $this->subject->getField('subClass'); $_bagFamily = $this->subject->getField('bagFamily'); - $_model = $this->subject->getField('displayId'); + $_displayId = $this->subject->getField('displayId'); $_ilvl = $this->subject->getField('itemLevel'); - $_visSlots = array( - INVTYPE_HEAD, INVTYPE_SHOULDERS, INVTYPE_BODY, INVTYPE_CHEST, INVTYPE_WAIST, INVTYPE_LEGS, INVTYPE_FEET, INVTYPE_WRISTS, - INVTYPE_HANDS, INVTYPE_WEAPON, INVTYPE_SHIELD, INVTYPE_RANGED, INVTYPE_CLOAK, INVTYPE_2HWEAPON, INVTYPE_TABARD, INVTYPE_ROBE, - INVTYPE_WEAPONMAINHAND, INVTYPE_WEAPONOFFHAND, INVTYPE_HOLDABLE, INVTYPE_THROWN, INVTYPE_RANGEDRIGHT - ); + + + /*************/ + /* Menu Path */ + /*************/ + + if ($path = $this->followBreadcrumbPath()) + array_push($this->breadcrumb, ...$path); + + + /**************/ + /* Page Title */ + /**************/ + + array_unshift($this->title, Lang::unescapeUISequences($this->subject->getField('name', true), Lang::FMT_RAW), Util::ucFirst(Lang::game('item'))); + /***********/ /* Infobox */ @@ -159,8 +97,12 @@ class ItemPage extends genericPage // side if ($si = $this->subject->json[$this->typeId]['side']) - if ($si != 3) - $infobox[] = Lang::main('side').Lang::main('colon').'[span class=icon-'.($si == 1 ? 'alliance' : 'horde').']'.Lang::game('si', $si).'[/span]'; + $infobox[] = Lang::main('side') . match ($si) + { + SIDE_ALLIANCE => '[span class=icon-alliance]'.Lang::game('si', SIDE_ALLIANCE).'[/span]', + SIDE_HORDE => '[span class=icon-horde]'.Lang::game('si', SIDE_HORDE).'[/span]', + SIDE_BOTH => Lang::game('si', SIDE_BOTH) + }; // icon if ($_ = $this->subject->getField('iconId')) @@ -188,20 +130,20 @@ class ItemPage extends genericPage } if ($hasUse) - $infobox[] = isset($tt) ? $tt : '[tooltip=tooltip_notconsumedonuse]'.Lang::item('nonConsumable').'[/tooltip]'; + $infobox[] = $tt ?? '[tooltip=tooltip_notconsumedonuse]'.Lang::item('nonConsumable').'[/tooltip]'; } // related holiday if ($eId = $this->subject->getField('eventId')) { $this->extendGlobalIds(Type::WORLDEVENT, $eId); - $infobox[] = Lang::game('eventShort').Lang::main('colon').'[event='.$eId.']'; + $infobox[] = Lang::game('eventShort', ['[event='.$eId.']']); } // tool if ($tId = $this->subject->getField('totemCategory')) - if ($tName = DB::Aowow()->selectRow('SELECT * FROM ?_totemcategory WHERE id = ?d', $tId)) - $infobox[] = Lang::item('tool').Lang::main('colon').'[url=?items&filter=cr=91;crs='.$tId.';crv=0]'.Util::localizedString($tName, 'name').'[/url]'; + if ($tName = DB::Aowow()->selectRow('SELECT * FROM ?_totemcategory WHERE `id` = ?d', $tId)) + $infobox[] = Lang::item('tool').'[url=?items&filter=cr=91;crs='.$tId.';crv=0]'.Util::localizedString($tName, 'name').'[/url]'; // extendedCost if (!empty($this->subject->getExtendedCost([], $_reqRating)[$this->subject->id])) @@ -265,17 +207,17 @@ class ItemPage extends genericPage else $cost = '[money'; - $stringify = function(&$v) use ($divisor) { return $v = $v[0] . ',' . ($v[1] / $divisor); }; + $stringify = fn(&$x) => $x = $x[0] . ',' . ($x[1] / $divisor); if ($tokens) { - array_walk($tokens, $stringify, $divisor); + array_walk($tokens, $stringify); $cost .= ' items='.implode(',', $tokens); } if ($currency) { - array_walk($currency, $stringify, $divisor); + array_walk($currency, $stringify); $cost .= ' currency='.implode(',', $currency); } @@ -304,7 +246,7 @@ class ItemPage extends genericPage // repair cost if ($_ = $this->subject->getField('repairPrice')) - $infobox[] = Lang::item('repairCost').Lang::main('colon').'[money='.$_.']'; + $infobox[] = Lang::item('repairCost').'[money='.$_.']'; // avg auction buyout if (in_array($this->subject->getField('bonding'), [0, 2, 3])) @@ -314,7 +256,7 @@ class ItemPage extends genericPage // avg money contained if ($_flags & ITEM_FLAG_OPENABLE) if ($_ = intVal(($this->subject->getField('minMoneyLoot') + $this->subject->getField('maxMoneyLoot')) / 2)) - $infobox[] = Lang::item('worth').Lang::main('colon').'[tooltip=tooltip_avgmoneycontained][money='.$_.'][/tooltip]'; + $infobox[] = Lang::item('worth').'[tooltip=tooltip_avgmoneycontained][money='.$_.'][/tooltip]'; // if it goes into a slot it may be disenchanted if ($_slot && $_class != ITEM_CLASS_CONTAINER) @@ -375,28 +317,31 @@ class ItemPage extends genericPage if ($_bagFamily & 0x0100) $infobox[] = Lang::item('atKeyring'); + if ($infobox) + $this->infobox = new InfoboxMarkup($infobox, ['allow' => Markup::CLASS_STAFF, 'dbpage' => true], 'infobox-contents0'); + + /****************/ /* Main Content */ /****************/ - $_cu = in_array($_class, [ITEM_CLASS_WEAPON, ITEM_CLASS_ARMOR]) || $this->subject->getField('gemEnchantmentId'); + if ($canBeWeighted = in_array($_class, [ITEM_CLASS_WEAPON, ITEM_CLASS_ARMOR, ITEM_CLASS_GEM])) + $this->addDataLoader('weight-presets'); // pageText - if ($this->pageText = Game::getBook($this->subject->getField('pageTextId'))) + if ($this->book = Game::getBook($this->subject->getField('pageTextId'))) $this->addScript( [SC_JS_FILE, 'js/Book.js'], [SC_CSS_FILE, 'css/Book.css'] ); - $this->headIcons = [$this->subject->getField('iconString', true, true), $this->subject->getField('stackable')]; - $this->infobox = $infobox ? '[ul][li]'.implode('[/li][li]', $infobox).'[/li][/ul]' : null; - $this->tooltip = $this->subject->renderTooltip(true); + $this->tooltip = [$this->subject->getField('iconString'), $this->subject->getField('stackable'), false]; $this->redButtons = array( BUTTON_WOWHEAD => true, - BUTTON_VIEW3D => in_array($_slot, $_visSlots) && $_model ? ['displayId' => $this->subject->getField('displayId'), 'slot' => $_slot, 'type' => Type::ITEM, 'typeId' => $this->typeId] : false, - BUTTON_COMPARE => $_cu, + BUTTON_VIEW3D => $this->subject->isDisplayable() ? ['displayId' => $_displayId, 'slot' => $_slot, 'type' => Type::ITEM, 'typeId' => $this->typeId] : false, + BUTTON_COMPARE => $canBeWeighted, BUTTON_EQUIP => in_array($_class, [ITEM_CLASS_WEAPON, ITEM_CLASS_ARMOR]), - BUTTON_UPGRADE => ($_cu ? ['class' => $_class, 'slot' => $_slot] : false), + BUTTON_UPGRADE => $canBeWeighted ? ['class' => $_class, 'slot' => $_slot] : false, BUTTON_LINKS => array( 'linkColor' => 'ff'.Game::$rarityColorStings[$this->subject->getField('quality')], 'linkId' => 'item:'.$this->typeId.':0:0:0:0:0:0:0:0', @@ -413,7 +358,7 @@ class ItemPage extends genericPage $this->subject->initSubItems(); if (!empty($this->subject->subItems[$this->typeId])) { - uaSort($this->subject->subItems[$this->typeId], function($a, $b) { return strcmp($a['name'], $b['name']); }); + uaSort($this->subject->subItems[$this->typeId], fn($a, $b) => $a['name'] <=> $b['name']); $this->subItems = array( 'data' => array_values($this->subject->subItems[$this->typeId]), 'randIds' => array_keys($this->subject->subItems[$this->typeId]), @@ -440,29 +385,31 @@ class ItemPage extends genericPage } // factionchange-equivalent - if ($pendant = DB::World()->selectCell('SELECT IF(horde_id = ?d, alliance_id, -horde_id) FROM player_factionchange_items WHERE alliance_id = ?d OR horde_id = ?d', $this->typeId, $this->typeId, $this->typeId)) + if ($pendant = DB::World()->selectCell('SELECT IF(`horde_id` = ?d, `alliance_id`, -`horde_id`) FROM player_factionchange_items WHERE `alliance_id` = ?d OR `horde_id` = ?d', $this->typeId, $this->typeId, $this->typeId)) { $altItem = new ItemList(array(['id', abs($pendant)])); if (!$altItem->error) { - $this->transfer = sprintf( - Lang::item('_transfer'), + $this->transfer = Lang::item('_transfer', [ $altItem->id, $altItem->getField('quality'), - $altItem->getField('iconString', true, true), + $altItem->getField('iconString'), $altItem->getField('name', true), $pendant > 0 ? 'alliance' : 'horde', - $pendant > 0 ? Lang::game('si', 1) : Lang::game('si', 2) - ); + $pendant > 0 ? Lang::game('si', SIDE_ALLIANCE) : Lang::game('si', SIDE_HORDE) + ]); } } + /**************/ /* Extra Tabs */ /**************/ + $this->lvTabs = new Tabs(['parent' => "\$\$WH.ge('tabs-generic')"], 'tabsRelated', true); + // tab: createdBy (perfect item specific) - if ($perfItem = DB::World()->select('SELECT *, spellId AS ARRAY_KEY FROM skill_perfect_item_template WHERE perfectItemType = ?d', $this->typeId)) + if ($perfItem = DB::World()->select('SELECT *, `spellId` AS ARRAY_KEY FROM skill_perfect_item_template WHERE `perfectItemType` = ?d', $this->typeId)) { $perfSpells = new SpellList(array(['id', array_column($perfItem, 'spellId')])); if (!$perfSpells->error) @@ -477,12 +424,12 @@ class ItemPage extends genericPage $this->extendGlobalIDs(Type::SPELL, $perfItem[$sId]['requiredSpecialization']); } - $this->lvTabs[] = [SpellList::$brickFile, array( - 'data' => array_values($lvData), + $this->lvTabs->addListviewTab(new Listview(array( + 'data' => $lvData, 'name' => '$LANG.tab_createdby', 'id' => 'created-by', // should by exclusive with created-by from spell_loot 'extraCols' => ['$Listview.extraCols.percent', '$Listview.extraCols.condition'] - )]; + ), SpellList::$brickFile)); } } @@ -493,7 +440,7 @@ class ItemPage extends genericPage { $this->extendGlobalData($lootTabs->jsGlobals); - foreach ($lootTabs->iterate() as $idx => [$file, $tabData]) + foreach ($lootTabs->iterate() as $idx => [$template, $tabData]) { if (!$tabData['data']) continue; @@ -518,7 +465,10 @@ class ItemPage extends genericPage } } - $this->lvTabs[] = [$file, $tabData]; + if ($template == 'npc' || $template == 'object') + $this->addDataLoader('zones'); + + $this->lvTabs->addListviewTab(new Listview($tabData, $template)); } } @@ -539,7 +489,7 @@ class ItemPage extends genericPage $extraCols = array_merge($extraCols, $lootTab->extraCols); $tabData = array( - 'data' => array_values($lootTab->getResult()), + 'data' => $lootTab->getResult(), 'name' => $tabName, 'id' => $tabId, ); @@ -550,7 +500,7 @@ class ItemPage extends genericPage if ($hiddenCols) $tabData['hiddenCols'] = array_unique($hiddenCols); - $this->lvTabs[] = [ItemList::$brickFile, $tabData]; + $this->lvTabs->addListviewTab(new Listview($tabData, ItemList::$brickFile)); } } @@ -566,12 +516,12 @@ class ItemPage extends genericPage if (!$contains->hasSetFields('slot')) $hCols[] = 'slot'; - $this->lvTabs[] = [ItemList::$brickFile, array( - 'data' => array_values($contains->getListviewData()), + $this->lvTabs->addListviewTab(new Listview(array( + 'data' => $contains->getListviewData(), 'name' => '$LANG.tab_cancontain', 'id' => 'can-contain', 'hiddenCols' => $hCols - )]; + ), ItemList::$brickFile)); } } @@ -583,12 +533,12 @@ class ItemPage extends genericPage { $this->extendGlobalData($contains->getJSGlobals(GLOBALINFO_SELF)); - $this->lvTabs[] = [ItemList::$brickFile, array( - 'data' => array_values($contains->getListviewData()), + $this->lvTabs->addListviewTab(new Listview(array( + 'data' => $contains->getListviewData(), 'name' => '$LANG.tab_canbeplacedin', 'id' => 'can-be-placed-in', 'hiddenCols' => ['side'] - )]; + ), ItemList::$brickFile)); } } @@ -604,7 +554,7 @@ class ItemPage extends genericPage $this->extendGlobalData($criteriaOf->getJSGlobals(GLOBALINFO_SELF | GLOBALINFO_REWARDS)); $tabData = array( - 'data' => array_values($criteriaOf->getListviewData()), + 'data' => $criteriaOf->getListviewData(), 'name' => '$LANG.tab_criteriaof', 'id' => 'criteria-of', 'visibleCols' => ['category'] @@ -613,7 +563,7 @@ class ItemPage extends genericPage if (!$criteriaOf->hasSetFields('reward_loc0')) $tabData['hiddenCols'] = ['rewards']; - $this->lvTabs[] = [AchievementList::$brickFile, $tabData]; + $this->lvTabs->addListviewTab(new Listview($tabData, AchievementList::$brickFile)); } // tab: reagent for @@ -628,19 +578,19 @@ class ItemPage extends genericPage { $this->extendGlobalData($reagent->getJSGlobals(GLOBALINFO_SELF | GLOBALINFO_RELATED)); - $this->lvTabs[] = [SpellList::$brickFile, array( - 'data' => array_values($reagent->getListviewData()), + $this->lvTabs->addListviewTab(new Listview(array( + 'data' => $reagent->getListviewData(), 'name' => '$LANG.tab_reagentfor', 'id' => 'reagent-for', 'visibleCols' => ['reagents'] - )]; + ), SpellList::$brickFile)); } - // tab: unlocks (object or item) + // tab: unlocks (object or item) - LOCK_TYPE_ITEM: 1 $lockIds = DB::Aowow()->selectCol( - 'SELECT id FROM ?_lock WHERE (type1 = 1 AND properties1 = ?d) OR - (type2 = 1 AND properties2 = ?d) OR (type3 = 1 AND properties3 = ?d) OR - (type4 = 1 AND properties4 = ?d) OR (type5 = 1 AND properties5 = ?d)', + 'SELECT `id` FROM ?_lock WHERE (`type1` = 1 AND `properties1` = ?d) OR + (`type2` = 1 AND `properties2` = ?d) OR (`type3` = 1 AND `properties3` = ?d) OR + (`type4` = 1 AND `properties4` = ?d) OR (`type5` = 1 AND `properties5` = ?d)', $this->typeId, $this->typeId, $this->typeId, $this->typeId, $this->typeId ); @@ -650,11 +600,12 @@ class ItemPage extends genericPage $lockedObj = new GameObjectList(array(['lockId', $lockIds])); if (!$lockedObj->error) { - $this->lvTabs[] = [GameObjectList::$brickFile, array( - 'data' => array_values($lockedObj->getListviewData()), + $this->addDataLoader('zones'); + $this->lvTabs->addListviewTab(new Listview(array( + 'data' => $lockedObj->getListviewData(), 'name' => '$LANG.tab_unlocks', - 'id' => 'unlocks-object' - )]; + 'id' => 'unlocks-object', + ), GameObjectList::$brickFile)); } // items (generally unused. It's the spell on the item, that unlocks stuff) @@ -663,11 +614,11 @@ class ItemPage extends genericPage { $this->extendGlobalData($lockedItm->getJSGlobals(GLOBALINFO_SELF)); - $this->lvTabs[] = [ItemList::$brickFile, array( - 'data' => array_values($lockedItm->getListviewData()), + $this->lvTabs->addListviewTab(new Listview(array( + 'data' => $lockedItm->getListviewData(), 'name' => '$LANG.tab_unlocks', 'id' => 'unlocks-item' - )]; + ), ItemList::$brickFile)); } } @@ -698,11 +649,11 @@ class ItemPage extends genericPage { $this->extendGlobalData($saItems->getJSGlobals(GLOBALINFO_SELF)); - $this->lvTabs[] = [ItemList::$brickFile, array( - 'data' => array_values($saItems->getListviewData()), + $this->lvTabs->addListviewTab(new Listview(array( + 'data' => $saItems->getListviewData(), 'name' => '$LANG.tab_seealso', 'id' => 'see-also' - )]; + ), ItemList::$brickFile)); } // tab: starts (quest) @@ -713,11 +664,11 @@ class ItemPage extends genericPage { $this->extendGlobalData($starts->getJSGlobals(GLOBALINFO_SELF | GLOBALINFO_REWARDS)); - $this->lvTabs[] = [QuestList::$brickFile, array( - 'data' => array_values($starts->getListviewData()), + $this->lvTabs->addListviewTab(new Listview(array( + 'data' => $starts->getListviewData(), 'name' => '$LANG.tab_starts', 'id' => 'starts-quest' - )]; + ), QuestList::$brickFile)); } } @@ -732,11 +683,11 @@ class ItemPage extends genericPage { $this->extendGlobalData($objective->getJSGlobals(GLOBALINFO_SELF | GLOBALINFO_REWARDS)); - $this->lvTabs[] = [QuestList::$brickFile, array( - 'data' => array_values($objective->getListviewData()), + $this->lvTabs->addListviewTab(new Listview(array( + 'data' => $objective->getListviewData(), 'name' => '$LANG.tab_objectiveof', 'id' => 'objective-of-quest' - )]; + ), QuestList::$brickFile)); } // tab: provided for (quest) @@ -750,11 +701,11 @@ class ItemPage extends genericPage { $this->extendGlobalData($provided->getJSGlobals(GLOBALINFO_SELF | GLOBALINFO_REWARDS)); - $this->lvTabs[] = [QuestList::$brickFile, array( - 'data' => array_values($provided->getListviewData()), + $this->lvTabs->addListviewTab(new Listview(array( + 'data' => $provided->getListviewData(), 'name' => '$LANG.tab_providedfor', 'id' => 'provided-for-quest' - )]; + ), QuestList::$brickFile)); } // tab: same model as @@ -766,12 +717,12 @@ class ItemPage extends genericPage { $this->extendGlobalData($sameModel->getJSGlobals(GLOBALINFO_SELF)); - $this->lvTabs[] = ['genericmodel', array( - 'data' => array_values($sameModel->getListviewData(ITEMINFO_MODEL)), + $this->lvTabs->addListviewTab(new Listview(array( + 'data' => $sameModel->getListviewData(ITEMINFO_MODEL), 'name' => '$LANG.tab_samemodelas', 'id' => 'same-model-as', 'genericlinktype' => 'item' - )]; + ), 'genericmodel')); } } @@ -785,7 +736,12 @@ class ItemPage extends genericPage // show mapper for vendors if ($vendorSpawns = $soldBy->getSpawns(SPAWNINFO_FULL, true, true, true, true)) { - $this->map = ['data' => ['parent' => 'mapper-generic'], 'mapperData' => &$vendorSpawns, 'foundIn' => Lang::item('purchasedIn')]; + $this->map = array( + ['parent' => 'mapper-generic'], // Mapper + $vendorSpawns, // mapperData + null, // ShowOnMap + [Lang::item('purchasedIn')] // foundIn + ); foreach ($vendorSpawns as $areaId => $_) $this->map['extra'][$areaId] = ZoneList::getName($areaId); } @@ -839,13 +795,14 @@ class ItemPage extends genericPage if ($cnd->toListviewColumn($sbData, $extraCols, 'id', $this->typeId)) $this->extendGlobalData($cnd->getJsGlobals()); - $this->lvTabs[] = [CreatureList::$brickFile, array( - 'data' => array_values($sbData), + $this->addDataLoader('zones'); + $this->lvTabs->addListviewTab(new Listview(array( + 'data' => $sbData, 'name' => '$LANG.tab_soldby', 'id' => 'sold-by-npc', 'extraCols' => $extraCols, 'hiddenCols' => ['level', 'type'] - )]; + ), CreatureList::$brickFile)); } } @@ -865,7 +822,7 @@ class ItemPage extends genericPage else $w = '`reqItemId1` = '.$this->typeId.' OR `reqItemId2` = '.$this->typeId.' OR `reqItemId3` = '.$this->typeId.' OR `reqItemId4` = '.$this->typeId.' OR `reqItemId5` = '.$this->typeId; - if (!$n && ItemListFilter::isCurrencyFor($this->typeId)) + if (!$n && !is_null(ItemListFilter::getCriteriaIndex(158, $this->typeId))) $n = '?items&filter=cr=158;crs='.$this->typeId.';crv=0'; $xCosts = DB::Aowow()->selectCol('SELECT `id` FROM ?_itemextendedcost WHERE '.$w); @@ -879,7 +836,7 @@ class ItemPage extends genericPage $filter = $iCur->error ? [Type::ITEM => $this->typeId] : [Type::CURRENCY => $iCur->id]; $tabData = array( - 'data' => array_values($boughtBy->getListviewData(ITEMINFO_VENDOR, $filter)), + 'data' => $boughtBy->getListviewData(ITEMINFO_VENDOR, $filter), 'name' => '$LANG.tab_currencyfor', 'id' => 'currency-for', 'extraCols' => ["\$Listview.funcBox.createSimpleCol('stack', 'stack', '10%', 'stack')", '$Listview.extraCols.cost'] @@ -888,7 +845,7 @@ class ItemPage extends genericPage if ($n) $tabData['note'] = sprintf(Util::$filterResultString, $n); - $this->lvTabs[] = [ItemList::$brickFile, $tabData]; + $this->lvTabs->addListviewTab(new Listview($tabData, ItemList::$brickFile)); $this->extendGlobalData($boughtBy->getJSGlobals(GLOBALINFO_SELF | GLOBALINFO_RELATED)); } @@ -927,12 +884,12 @@ class ItemPage extends genericPage if ($taughtSpells->hasSetFields('reagent1', 'reagent2', 'reagent3', 'reagent4', 'reagent5', 'reagent6', 'reagent7', 'reagent8')) $visCols[] = 'reagents'; - $this->lvTabs[] = [SpellList::$brickFile, array( - 'data' => array_values($taughtSpells->getListviewData()), + $this->lvTabs->addListviewTab(new Listview(array( + 'data' => $taughtSpells->getListviewData(), 'name' => '$LANG.tab_teaches', 'id' => 'teaches', 'visibleCols' => $visCols - )]; + ), SpellList::$brickFile)); } } @@ -950,7 +907,7 @@ class ItemPage extends genericPage $useSpells[] = $this->subject->getField('spellId'.$i); } if ($useSpells) - if ($_ = DB::Aowow()->selectCol('SELECT category FROM ?_spell WHERE id IN (?a) AND recoveryCategory > 0', $useSpells)) + if ($_ = DB::Aowow()->selectCol('SELECT `category` FROM ?_spell WHERE `id` IN (?a) AND `recoveryCategory` > 0', $useSpells)) $cdCats += $_; if ($cdCats) @@ -967,18 +924,18 @@ class ItemPage extends genericPage ] ); - if ($spellsByCat = DB::Aowow()->selectCol('SELECT id FROM ?_spell WHERE category IN (?a)', $cdCats)) + if ($spellsByCat = DB::Aowow()->selectCol('SELECT `id` FROM ?_spell WHERE `category` IN (?a)', $cdCats)) for ($i = 1; $i < 6; $i++) $conditions[1][] = ['spellId'.$i, $spellsByCat]; $cdItems = new ItemList($conditions); if (!$cdItems->error) { - $this->lvTabs[] = [ItemList::$brickFile, array( - 'data' => array_values($cdItems->getListviewData()), + $this->lvTabs->addListviewTab(new Listview(array( + 'data' => $cdItems->getListviewData(), 'name' => '$LANG.tab_sharedcooldown', 'id' => 'shared-cooldown' - )]; + ), ItemList::$brickFile)); $this->extendGlobalData($cdItems->getJSGlobals(GLOBALINFO_SELF)); } @@ -992,7 +949,7 @@ class ItemPage extends genericPage if ($this->subject->getField('soundOverrideSubclass') > 0) $scm = (1 << $this->subject->getField('soundOverrideSubclass')); - $soundIds = DB::Aowow()->selectCol('SELECT soundId FROM ?_items_sounds WHERE subClassMask & ?d', $scm); + $soundIds = DB::Aowow()->selectCol('SELECT `soundId` FROM ?_items_sounds WHERE `subClassMask` & ?d', $scm); } $fields = ['pickUpSoundId', 'dropDownSoundId', 'sheatheSoundId', 'unsheatheSoundId']; @@ -1002,7 +959,7 @@ class ItemPage extends genericPage if ($x = $this->subject->getField('spellVisualId')) { - if ($spellSounds = DB::Aowow()->selectRow('SELECT * FROM ?_spell_sounds WHERE id = ?d', $x)) + if ($spellSounds = DB::Aowow()->selectRow('SELECT * FROM ?_spell_sounds WHERE `id` = ?d', $x)) { array_shift($spellSounds); // bye 'id'-field foreach ($spellSounds as $ss) @@ -1017,7 +974,7 @@ class ItemPage extends genericPage if (!$sounds->error) { $this->extendGlobalData($sounds->getJSGlobals(GLOBALINFO_SELF)); - $this->lvTabs[] = [SoundList::$brickFile, ['data' => array_values($sounds->getListviewData())]]; + $this->lvTabs->addListviewTab(new Listview(['data' => $sounds->getListviewData()], SoundList::$brickFile)); } } @@ -1027,7 +984,7 @@ class ItemPage extends genericPage if ($tab = $cnd->toListviewTab('condition-for', '$LANG.tab_condition_for')) { $this->extendGlobalData($cnd->getJsGlobals()); - $this->lvTabs[] = $tab; + $this->lvTabs->addDataTab(...$tab); } @@ -1035,174 +992,39 @@ class ItemPage extends genericPage // use var $createdBy to find source of this spell // id: 'taught-by-X', // name: LANG.tab_taughtby + + parent::generate(); } - protected function generateTooltip() + private function followBreadcrumbPath() : array { - $power = new \StdClass(); - if (!$this->subject->error) - { - $power->{'name_'.Lang::getLocale()->json()} = Lang::unescapeUISequences($this->subject->getField('name', true, false, $this->enhancedTT), Lang::FMT_RAW); - $power->quality = $this->subject->getField('quality'); - $power->icon = rawurlencode($this->subject->getField('iconString', true, true)); - $power->{'tooltip_'.Lang::getLocale()->json()} = $this->subject->renderTooltip(false, 0, $this->enhancedTT); - } + $c = $this->subject->getField('class'); + $sc = $this->subject->getField('subClass'); + $ssc = $this->subject->getField('subSubClass'); + $slot = $this->subject->getField('slot'); - $itemString = $this->typeId; - if ($this->enhancedTT) - { - foreach ($this->enhancedTT as $k => $val) - $itemString .= $k.(is_array($val) ? implode(',', $val) : $val); - $itemString = "'".$itemString."'"; - } + if ($c == ITEM_CLASS_REAGENT) + return [ITEM_CLASS_MISC, 1]; // misc > reagents - return sprintf($this->powerTpl, $itemString,Lang::getLocale()->value, Util::toJSON($power, JSON_AOWOW_POWER)); - } + if ($c == ITEM_CLASS_GENERIC || $c == ITEM_CLASS_PERMANENT) + return [ITEM_CLASS_MISC, 4]; // misc > other - protected function generateXML() - { - $root = new SimpleXML(''); + // depths: 1 + $path = [$c]; - if ($this->subject->error) - $root->addChild('error', 'Item not found!'); - else - { - // item root - $xml = $root->addChild('item'); - $xml->addAttribute('id', $this->typeId); + if (in_array($c, [ITEM_CLASS_MONEY, ITEM_CLASS_QUEST, ITEM_CLASS_KEY])) + return $path; - // name - $xml->addChild('name')->addCData($this->subject->getField('name', true)); - // itemlevel - $xml->addChild('level', $this->subject->getField('itemLevel')); - // quality - $xml->addChild('quality', Lang::item('quality', $this->subject->getField('quality')))->addAttribute('id', $this->subject->getField('quality')); - // class - $x = Lang::item('cat', $this->subject->getField('class')); - $xml->addChild('class')->addCData(is_array($x) ? $x[0] : $x)->addAttribute('id', $this->subject->getField('class')); - // subclass - $x = $this->subject->getField('class') == 2 ? Lang::spell('weaponSubClass') : Lang::item('cat', $this->subject->getField('class'), 1); - $xml->addChild('subclass')->addCData(is_array($x) ? (is_array($x[$this->subject->getField('subClass')]) ? $x[$this->subject->getField('subClass')][0] : $x[$this->subject->getField('subClass')]) : Lang::item('cat', $this->subject->getField('class')))->addAttribute('id', $this->subject->getField('subClass')); - // icon + displayId - $xml->addChild('icon', $this->subject->getField('iconString', true, true))->addAttribute('displayId', $this->subject->getField('displayId')); - // inventorySlot - $xml->addChild('inventorySlot', Lang::item('inventoryType', $this->subject->getField('slot')))->addAttribute('id', $this->subject->getField('slot')); - // tooltip - $xml->addChild('htmlTooltip')->addCData($this->subject->renderTooltip()); + // depths: 2 + $path[] = $sc; - $this->subject->extendJsonStats(); + // maybe depths: 3 + if ($this->subject->isBodyArmor() && $slot) + $path[] = $slot; + else if (($c == ITEM_CLASS_CONSUMABLE && $sc == ITEM_SUBCLASS_ELIXIR) || $c == ITEM_CLASS_GLYPH) + $path[] = $ssc; - // json - $fields = ['classs', 'displayid', 'dps', 'id', 'level', 'name', 'reqlevel', 'slot', 'slotbak', 'speed', 'subclass']; - $json = []; - foreach ($fields as $f) - { - if (isset($this->subject->json[$this->typeId][$f])) - { - $_ = $this->subject->json[$this->typeId][$f]; - if ($f == 'name') - $_ = (7 - $this->subject->getField('quality')).$_; - - $json[$f] = $_; - } - } - - // itemsource - if ($this->subject->getSources($s, $sm)) - { - $json['source'] = $s; - if ($sm) - $json['sourcemore'] = $sm; - } - - $xml->addChild('json')->addCData(substr(json_encode($json), 1, -1)); - - // jsonEquip missing: avgbuyout - $json = []; - if ($_ = $this->subject->getField('sellPrice')) // sellprice - $json['sellprice'] = $_; - - if ($_ = $this->subject->getField('requiredLevel')) // reqlevel - $json['reqlevel'] = $_; - - if ($_ = $this->subject->getField('requiredSkill')) // reqskill - $json['reqskill'] = $_; - - if ($_ = $this->subject->getField('requiredSkillRank')) // reqskillrank - $json['reqskillrank'] = $_; - - if ($_ = $this->subject->getField('cooldown')) // cooldown - $json['cooldown'] = $_ / 1000; - - Util::arraySumByKey($json, $this->subject->jsonStats[$this->typeId] ?? []); - - foreach ($this->subject->json[$this->typeId] as $name => $qty) - if ($idx = Stat::getIndexFrom(Stat::IDX_JSON_STR, $name)) - if (Stat::getFilterCriteriumId($idx)) - $json[$name] = $qty; - - $xml->addChild('jsonEquip')->addCData(substr(json_encode($json), 1, -1)); - - // jsonUse - if ($onUse = $this->subject->getOnUseStats()) - { - $j = ''; - foreach ($onUse->toJson() as $key => $amt) - $j .= ',"'.$key.'":'.$amt; - - $xml->addChild('jsonUse')->addCData(substr($j, 1)); - } - - // reagents - $cnd = array( - 'OR', - ['AND', ['effect1CreateItemId', $this->typeId], ['OR', ['effect1Id', SpellList::EFFECTS_ITEM_CREATE], ['effect1AuraId', SpellList::AURAS_ITEM_CREATE]]], - ['AND', ['effect2CreateItemId', $this->typeId], ['OR', ['effect2Id', SpellList::EFFECTS_ITEM_CREATE], ['effect2AuraId', SpellList::AURAS_ITEM_CREATE]]], - ['AND', ['effect3CreateItemId', $this->typeId], ['OR', ['effect3Id', SpellList::EFFECTS_ITEM_CREATE], ['effect3AuraId', SpellList::AURAS_ITEM_CREATE]]], - ); - - $spellSource = new SpellList($cnd); - if (!$spellSource->error) - { - $cbNode = $xml->addChild('createdBy'); - - foreach ($spellSource->iterate() as $sId => $__) - { - foreach ($spellSource->canCreateItem() as $idx) - { - if ($spellSource->getField('effect'.$idx.'CreateItemId') != $this->typeId) - continue; - - $splNode = $cbNode->addChild('spell'); - $splNode->addAttribute('id', $sId); - $splNode->addAttribute('name', $spellSource->getField('name', true)); - $splNode->addAttribute('icon', $this->subject->getField('iconString', true, true)); - $splNode->addAttribute('minCount', $spellSource->getField('effect'.$idx.'BasePoints') + 1); - $splNode->addAttribute('maxCount', $spellSource->getField('effect'.$idx.'BasePoints') + $spellSource->getField('effect'.$idx.'DieSides')); - - foreach ($spellSource->getReagentsForCurrent() as $rId => $qty) - { - if ($reagent = $spellSource->relItems->getEntry($rId)) - { - $rgtNode = $splNode->addChild('reagent'); - $rgtNode->addAttribute('id', $rId); - $rgtNode->addAttribute('name', Util::localizedString($reagent, 'name')); - $rgtNode->addAttribute('quality', $reagent['quality']); - $rgtNode->addAttribute('icon', $reagent['iconString']); - $rgtNode->addAttribute('count', $qty[1]); - } - } - - break; - } - } - } - - // link - $xml->addChild('link', Cfg::get('HOST_URL').'?item='.$this->subject->id); - } - - return $root->asXML(); + return $path; } } diff --git a/endpoints/item/item_power.php b/endpoints/item/item_power.php new file mode 100644 index 00000000..25617b47 --- /dev/null +++ b/endpoints/item/item_power.php @@ -0,0 +1,70 @@ + ['filter' => FILTER_CALLBACK, 'options' => [Locale::class, 'tryFromDomain']], + 'rand' => ['filter' => FILTER_VALIDATE_INT ], + 'ench' => ['filter' => FILTER_VALIDATE_INT ], + 'gems' => ['filter' => FILTER_CALLBACK, 'options' => [self::class, 'checkIntArray'] ], + 'sock' => ['filter' => FILTER_CALLBACK, 'options' => [self::class, 'checkEmptySet'] ] + ); + + public function __construct($param) + { + parent::__construct($param); + + // temp locale + if ($this->_get['domain']) + Lang::load($this->_get['domain']); + + $this->typeId = intVal($param); + + if ($this->_get['rand']) + $this->enhancedTT['r'] = $this->_get['rand']; + if ($this->_get['ench']) + $this->enhancedTT['e'] = $this->_get['ench']; + if ($this->_get['gems']) + $this->enhancedTT['g'] = $this->_get['gems']; + if ($this->_get['sock']) + $this->enhancedTT['s'] = ''; + } + + protected function generate() : void + { + $item = new ItemList(array(['i.id', $this->typeId])); + if ($item->error) + $this->cacheType = CACHE_TYPE_NONE; + else + { + $itemString = $this->typeId; + foreach ($this->enhancedTT as $k => $val) + $itemString .= $k.(is_array($val) ? implode(',', $val) : $val); + + $opts = array( + 'name' => Lang::unescapeUISequences($item->getField('name', true, false, $this->enhancedTT), Lang::FMT_RAW), + 'tooltip' => $item->renderTooltip(enhance: $this->enhancedTT), + 'icon' => $item->getField('iconString'), + 'quality' => $item->getField('quality') + ); + } + + $this->result = new Tooltip(self::POWER_TEMPLATE, $itemString ?? $this->typeId, $opts ?? []); + } +} + +?> diff --git a/endpoints/item/item_xml.php b/endpoints/item/item_xml.php new file mode 100644 index 00000000..5599be5c --- /dev/null +++ b/endpoints/item/item_xml.php @@ -0,0 +1,231 @@ + ['filter' => FILTER_CALLBACK, 'options' => [Locale::class, 'tryFromDomain']] + ); + + private ItemList $subject; + private string $search = ''; + + public function __construct(string $param) + { + parent::__construct($param); + + // temp locale + if ($this->_get['domain']) + Lang::load($this->_get['domain']); + + // allow lookup by name + if (is_numeric($param)) + $this->typeId = intVal($param); + else + $this->search = urldecode($param); + } + + protected function generate() : void + { + if ($this->search) + $conditions = [['name_loc'.Lang::getLocale()->value, $this->search]]; + else + $conditions = [['i.id', $this->typeId]]; + + $this->subject = new ItemList($conditions); + if ($this->subject->error) + { + $this->cacheType = CACHE_TYPE_NONE; + header('HTTP/1.0 404 Not Found', true, 404); + + $root = new SimpleXML(''); + $root->addChild('error', 'Item not found!'); + $this->result = $root->asXML(); + + return; + } + else + $this->typeId = $this->subject->id; + + $root = new SimpleXML(''); + + // item root + $xml = $root->addChild('item'); + $xml->addAttribute('id', $this->typeId); + + // name + $xml->addChild('name')->addCData($this->subject->getField('name', true)); + // itemlevel + $xml->addChild('level', $this->subject->getField('itemLevel')); + // quality + $xml->addChild('quality', Lang::item('quality', $this->subject->getField('quality')))->addAttribute('id', $this->subject->getField('quality')); + // class + $x = Lang::item('cat', $this->subject->getField('class')); + $xml->addChild('class')->addCData(is_array($x) ? $x[0] : $x)->addAttribute('id', $this->subject->getField('class')); + // subclass + $xml->addChild('subclass')->addCData($this->getSubclass())->addAttribute('id', $this->subject->getField('subClass')); + // icon + displayId + $xml->addChild('icon', $this->subject->getField('iconString'))->addAttribute('displayId', $this->subject->getField('displayId')); + // inventorySlot + $xml->addChild('inventorySlot', Lang::item('inventoryType', $this->subject->getField('slot')))->addAttribute('id', $this->subject->getField('slot')); + // tooltip + $xml->addChild('htmlTooltip')->addCData($this->subject->renderTooltip()); + + $this->subject->extendJsonStats(); + + // json + $fields = ['classs', 'displayid', 'dps', 'id', 'level', 'name', 'reqlevel', 'slot', 'slotbak', 'speed', 'subclass']; + $json = []; + foreach ($fields as $f) + { + if (isset($this->subject->json[$this->typeId][$f])) + { + $_ = $this->subject->json[$this->typeId][$f]; + if ($f == 'name') + $_ = (7 - $this->subject->getField('quality')).$_; + + $json[$f] = $_; + } + } + + // itemsource + if ($this->subject->getSources($s, $sm)) + { + $json['source'] = $s; + if ($sm) + $json['sourcemore'] = $sm; + } + + $xml->addChild('json')->addCData(substr(json_encode($json), 1, -1)); + + // jsonEquip missing: avgbuyout + $json = []; + if ($_ = $this->subject->getField('sellPrice')) // sellprice + $json['sellprice'] = $_; + + if ($_ = $this->subject->getField('requiredLevel')) // reqlevel + $json['reqlevel'] = $_; + + if ($_ = $this->subject->getField('requiredSkill')) // reqskill + $json['reqskill'] = $_; + + if ($_ = $this->subject->getField('requiredSkillRank')) // reqskillrank + $json['reqskillrank'] = $_; + + if ($_ = $this->subject->getField('cooldown')) // cooldown + $json['cooldown'] = $_ / 1000; + + Util::arraySumByKey($json, $this->subject->jsonStats[$this->typeId] ?? []); + + foreach ($this->subject->json[$this->typeId] as $name => $qty) + if ($idx = Stat::getIndexFrom(Stat::IDX_JSON_STR, $name)) + if (Stat::getFilterCriteriumId($idx)) + $json[$name] = $qty; + + $xml->addChild('jsonEquip')->addCData(substr(json_encode($json), 1, -1)); + + // jsonUse + if ($onUse = $this->subject->getOnUseStats()) + { + $j = ''; + foreach ($onUse->toJson(includeEmpty: false) as $key => $amt) + $j .= ',"'.$key.'":'.$amt; + + $xml->addChild('jsonUse')->addCData(substr($j, 1)); + } + + // reagents + $cnd = array( + 'OR', + ['AND', ['effect1CreateItemId', $this->typeId], ['OR', ['effect1Id', SpellList::EFFECTS_ITEM_CREATE], ['effect1AuraId', SpellList::AURAS_ITEM_CREATE]]], + ['AND', ['effect2CreateItemId', $this->typeId], ['OR', ['effect2Id', SpellList::EFFECTS_ITEM_CREATE], ['effect2AuraId', SpellList::AURAS_ITEM_CREATE]]], + ['AND', ['effect3CreateItemId', $this->typeId], ['OR', ['effect3Id', SpellList::EFFECTS_ITEM_CREATE], ['effect3AuraId', SpellList::AURAS_ITEM_CREATE]]], + ); + + $spellSource = new SpellList($cnd); + if (!$spellSource->error) + { + $cbNode = $xml->addChild('createdBy'); + + foreach ($spellSource->iterate() as $sId => $__) + { + foreach ($spellSource->canCreateItem() as $idx) + { + if ($spellSource->getField('effect'.$idx.'CreateItemId') != $this->typeId) + continue; + + $splNode = $cbNode->addChild('spell'); + $splNode->addAttribute('id', $sId); + $splNode->addAttribute('name', $spellSource->getField('name', true)); + $splNode->addAttribute('icon', $this->subject->getField('iconString')); + $splNode->addAttribute('minCount', $spellSource->getField('effect'.$idx.'BasePoints') + 1); + $splNode->addAttribute('maxCount', $spellSource->getField('effect'.$idx.'BasePoints') + $spellSource->getField('effect'.$idx.'DieSides')); + + foreach ($spellSource->getReagentsForCurrent() as $rId => $qty) + { + if ($reagent = $spellSource->relItems->getEntry($rId)) + { + $rgtNode = $splNode->addChild('reagent'); + $rgtNode->addAttribute('id', $rId); + $rgtNode->addAttribute('name', Util::localizedString($reagent, 'name')); + $rgtNode->addAttribute('quality', $reagent['quality']); + $rgtNode->addAttribute('icon', $reagent['iconString']); + $rgtNode->addAttribute('count', $qty[1]); + } + } + + break; + } + } + } + + // link + $xml->addChild('link', Cfg::get('HOST_URL').'?item='.$this->subject->id); + + $this->result = $root->asXML(); + } + + private function getSubclass() : string + { + $c = $this->subject->getField('class'); + $sc = $this->subject->getField('subClass'); + + if ($c == ITEM_CLASS_WEAPON) + $langRef = Lang::spell('weaponSubClass'); + else + $langRef = Lang::item('cat', $c, 1); + + if (!is_array($langRef)) + return Lang::item('cat', $c); + + if (is_array($langRef[$sc])) + return $langRef[$sc][0]; + + return $langRef[$sc]; + } + + public function getCacheKeyComponents() : array + { + return array( + $this->type, // DBType + $this->typeId, // DBTypeId + -1, // category + -1, // staff mask (content does not diff) + '' // misc (unused) + ); + } +} + +?> diff --git a/pages/items.php b/endpoints/items/items.php similarity index 70% rename from pages/items.php rename to endpoints/items/items.php index f334b52b..97d4a9d1 100644 --- a/pages/items.php +++ b/endpoints/items/items.php @@ -6,28 +6,24 @@ if (!defined('AOWOW_REVISION')) die('illegal access'); -// menuId 0: Item g_initPath() -// tabId 0: Database g_initHeader() -class ItemsPage extends GenericPage +class ItemsBaseResponse extends TemplateResponse implements ICache { - use TrListPage; + use TrListPage, TrCache; - protected $forceTabs = false; - protected $gemScores = []; + protected int $type = Type::ITEM; + protected int $cacheType = CACHE_TYPE_PAGE; - protected $typeList = []; // rightPanel - content by context - protected $slotList = []; // rightPanel - INV_TYPE_Xs + protected string $template = 'items'; + protected string $pageName = 'items'; + protected ?int $activeTab = parent::TAB_DATABASE; + protected array $breadcrumb = [0, 0]; - protected $type = Type::ITEM; - protected $tpl = 'items'; - protected $path = [0, 0]; - protected $tabId = 0; - protected $mode = CACHE_TYPE_PAGE; - protected $scripts = [[SC_JS_FILE, 'js/filters.js'], [SC_JS_FILE, 'js/swfobject.js']]; - - protected $_get = ['filter' => ['filter' => FILTER_UNSAFE_RAW]]; - - protected $validCats = array( // if > 0 class => subclass + protected array $dataLoader = ['weight-presets']; + protected array $scripts = [[SC_JS_FILE, 'js/filters.js'], [SC_JS_FILE, 'js/swfobject.js']]; + protected array $expectedGET = array( + 'filter' => ['filter' => FILTER_VALIDATE_REGEXP, 'options' => ['regexp' => Filter::PATTERN_PARAM]] + ); + protected array $validCats = array( // if > 0 class => subclass 2 => [15, 13, 0, 4, 7, 6, 10, 1, 5, 8, 2, 18, 3, 16, 19, 20, 14], 4 => array( 0 => true, @@ -84,51 +80,52 @@ class ItemsPage extends GenericPage 13 => true ); - private $filterOpts = []; - private $sharedLV = array( // common listview components across all tabs + public array $gemScores = []; + public array $typeList = []; // rightPanel - content by context + public array $slotList = []; // rightPanel - INV_TYPE_Xs + + private array $filterOpts = []; + private array $sharedLV = array( // common listview components across all tabs 'hiddenCols' => [], 'visibleCols' => [], 'extraCols' => [] ); - public function __construct($pageCall, $pageParam) + public function __construct(string $pageParam) { $this->getCategoryFromUrl($pageParam); - parent::__construct($pageCall, $pageParam); + parent::__construct($pageParam); - $this->filterObj = new ItemListFilter($this->_get['filter'] ?? '', ['parentCats' => $this->category]); - - $this->name = Util::ucFirst(Lang::game('items')); $this->subCat = $pageParam !== '' ? '='.$pageParam : ''; + $this->filter = new ItemListFilter($this->_get['filter'] ?? '', ['parentCats' => $this->category]); + $this->filterError = $this->filter->error; } - protected function generateContent() + protected function generate() : void { - $this->addScript([SC_JS_FILE, '?data=weight-presets']); + $this->h1 = Util::ucFirst(Lang::game('items')); $conditions = []; - if (!User::isInGroup(U_GROUP_EMPLOYEE)) $conditions[] = [['cuFlags', CUSTOM_EXCLUDE_FOR_LISTVIEW, '&'], 0]; + $this->filter->evalCriteria(); + $this->filter->evalWeights(); + + if ($_ = $this->filter->getConditions()) + $conditions[] = $_; + + $this->filterError = $this->filter->error; // maybe the evalX() caused something + /*******************/ /* evaluate filter */ /*******************/ - $this->filterObj->evalCriteria(); - $this->filterObj->evalWeights(); + $fiForm = $this->filter->values; - if ($_ = $this->filterObj->getConditions()) - $conditions[] = $_; - - // recreate form selection (must be evaluated first via getConditions()) - $fiForm = $this->filterObj->values; - - $xCols = $this->filterObj->fiExtraCols; - - $this->createExtraMenus(); + $xCols = $this->filter->fiExtraCols; $infoMask = ITEMINFO_JSON; if (array_intersect([63, 64, 125], $xCols)) // 63:buyPrice; 64:sellPrice; 125:reqarenartng @@ -137,8 +134,42 @@ class ItemsPage extends GenericPage if ($xCols) $this->sharedLV['extraCols'] = '$fi_getExtraCols(fi_extraCols, '.($fiForm['gm'] ?? 0).', '.(array_intersect([63], $xCols) ? 1 : 0).')'; - if ($this->filterObj->error) - $this->sharedLV['_errors'] = '$1'; + $this->createExtraMenus(); // right side panels in search form + + + /*************/ + /* Menu Path */ + /*************/ + + foreach ($this->category as $c) + $this->breadcrumb[] = $c; + + // if slot-dropdown is available && Armor && $path points to Armor-Class + if (count($this->breadcrumb) == 4 && $this->category[0] == 4 && count($fiForm['sl']) == 1) + $this->breadcrumb[] = $fiForm['sl'][0]; + else if (isset($this->category[0]) && $this->category[0] == 0 && count($fiForm['ty']) == 1) + $this->breadcrumb[] = $fiForm['ty'][0]; + + + /**************/ + /* Page Title */ + /**************/ + + array_unshift($this->title, $this->h1); + + if ($this->category) + { + if (isset($this->category[2]) && is_array(Lang::item('cat', $this->category[0], 1, $this->category[1]))) + $tPart = Lang::item('cat', $this->category[0], 1, $this->category[1], 1, $this->category[2]); + else if (isset($this->category[1]) && is_array(Lang::item('cat', $this->category[0]))) + $tPart = Lang::item('cat', $this->category[0], 1, $this->category[1]); + else if ($this->category[0] == 0 && count($fiForm['ty']) == 1) + $tPart = Lang::item('cat', 0, 1, $fiForm['ty'][0]); + else + $tPart = Lang::item('cat', $this->category[0]); + + array_unshift($this->title, is_array($tPart) ? $tPart[0] : $tPart); + } /******************/ @@ -157,7 +188,7 @@ class ItemsPage extends GenericPage /* handle auto-gemming */ /***********************/ - $this->gemScores = $this->createGemScores($fiForm['gm'] ?? 0); + $this->createGemScores($fiForm['gm'] ?? 0); /*************************/ @@ -165,9 +196,9 @@ class ItemsPage extends GenericPage /*************************/ $upgItemData = []; - if ($this->filterObj->upgrades && $this->filterObj->fiSetWeights) + if ($this->filter->upgrades && $this->filter->fiSetWeights) { - $upgItems = new ItemList(array(['id', array_keys($this->filterObj->upgrades)]), ['extraOpts' => $this->filterObj->extraOpts]); + $upgItems = new ItemList(array(['id', array_keys($this->filter->upgrades)]), ['extraOpts' => $this->filter->extraOpts]); if (!$upgItems->error) { $upgItemData = $upgItems->getListviewData($infoMask); @@ -175,11 +206,11 @@ class ItemsPage extends GenericPage } } - if ($upgItemData) // check if upItems cover multiple slots + if ($upgItemData) // check if upgItems cover multiple slots { $singleSlot = true; - $ref = reset($this->filterObj->upgrades); - foreach ($this->filterObj->upgrades as $slot) + $ref = reset($this->filter->upgrades); + foreach ($this->filter->upgrades as $slot) { if ($slot == $ref) continue; @@ -189,10 +220,10 @@ class ItemsPage extends GenericPage } if ($singleSlot && empty($fiForm['gb'])) // enforce group by slot - $fiForm['gb'] = 1; + $fiForm['gb'] = ItemListFilter::GROUP_BY_SLOT; else if (!$singleSlot) // multiples can only be grouped by slot { - $fiForm['gb'] = 1; + $fiForm['gb'] = ItemListFilter::GROUP_BY_SLOT; $maxResults = 25; $this->sharedLV['customFilter'] = '$fi_filterUpgradeListview'; } @@ -221,15 +252,17 @@ class ItemsPage extends GenericPage ); $groups = []; $nameSource = []; - $grouping = $fiForm['gb'] ?? 0; + $grouping = $fiForm['gb'] ?? ItemListFilter::GROUP_BY_NONE; $extraOpts = []; $maxResults = Cfg::get('SQL_LIMIT_DEFAULT'); + $forceTabs = false; + $tabs = []; switch ($grouping) { // slot: (try to limit the lookups by class grouping and intersecting with preselected slots) // if intersect yields an empty array no lookups will occur - case 1: + case ItemListFilter::GROUP_BY_SLOT: if (isset($this->category[0]) && $this->category[0] == ITEM_CLASS_ARMOR) $groups = $availableSlots[ITEM_CLASS_ARMOR]; else if (isset($this->category[0]) && $this->category[0] == ITEM_CLASS_WEAPON) @@ -240,20 +273,20 @@ class ItemsPage extends GenericPage if ($fiForm['sl']) // skip lookups for unselected slots $groups = array_intersect($groups, $fiForm['sl']); - if (!empty($this->filterObj->upgrades)) // skip lookups for slots we dont have items to upgrade for - $groups = array_intersect($groups, $this->filterObj->upgrades); + if ($this->filter->upgrades) // skip lookups for slots we dont have items to upgrade for + $groups = array_intersect($groups, $this->filter->upgrades); if ($groups) { $nameSource = Lang::item('inventoryType'); - $this->forceTabs = true; + $forceTabs = true; } break; - case 2: // itemlevel: first, try to find 10 level steps within range (if given) as tabs + case ItemListFilter::GROUP_BY_LEVEL: // itemlevel: first, try to find 10 level steps within range (if given) as tabs // ohkayy, maybe i need to rethink $this - $this->filterOpts = $this->filterObj->extraOpts; - $this->filterOpts['is']['o'] = [null]; // remove 'order by' from itemStats + $this->filterOpts = $this->filter->extraOpts; + $this->filterOpts['is']['o'] = [null]; // remove 'order by' from ?_item_stats $extraOpts = array_merge($this->filterOpts, ['i' => ['g' => ['itemlevel'], 'o' => ['itemlevel DESC']]]); $levelRef = new ItemList(array_merge($conditions, [10]), ['extraOpts' => $extraOpts]); @@ -270,25 +303,25 @@ class ItemsPage extends GenericPage $groups[] = $l; // push last value as negativ to signal misc group after $this level $extraOpts = ['i' => ['o' => ['itemlevel DESC']]]; $nameSource[$l] = Lang::item('tabOther'); - $this->forceTabs = true; + $forceTabs = true; } break; - case 3: // source + case ItemListFilter::GROUP_BY_SOURCE: // source $groups = [1, 2, 3, 4, 5, 10, 11, 12, 0]; $nameSource = Lang::game('sources'); - $this->forceTabs = true; + $forceTabs = true; break; // none default: - $grouping = 0; + $grouping = ItemListFilter::GROUP_BY_NONE; $groups[0] = null; } // write back 'gb' to filter if ($grouping) - $this->filterObj->values['gb'] = $grouping; + $this->filter->values['gb'] = $grouping; /*****************************/ @@ -297,22 +330,15 @@ class ItemsPage extends GenericPage foreach ($groups as $group) { - switch ($grouping) + $finalCnd = match ($grouping) { - case 1: - $finalCnd = array_merge($conditions, [['slot', $group], $maxResults]); - break; - case 2: - $finalCnd = array_merge($conditions, [['itemlevel', abs($group), $group > 0 ? null : '<'], $maxResults]); - break; - case 3: - $finalCnd = array_merge($conditions, [$group ? ['src.src'.$group, null, '!'] : ['src.typeId', null], $maxResults]); - break; - default: - $finalCnd = $conditions; - } + ItemListFilter::GROUP_BY_SLOT => array_merge($conditions, [['slot', $group], $maxResults]), + ItemListFilter::GROUP_BY_LEVEL => array_merge($conditions, [['itemlevel', abs($group), $group > 0 ? null : '<'], $maxResults]), + ItemListFilter::GROUP_BY_SOURCE => array_merge($conditions, [$group ? ['src.src'.$group, null, '!'] : ['src.typeId', null], $maxResults]), + default => $conditions + }; - $items = new ItemList($finalCnd, ['extraOpts' => array_merge($extraOpts, $this->filterObj->extraOpts), 'calcTotal' => true]); + $items = new ItemList($finalCnd, ['extraOpts' => array_merge($extraOpts, $this->filter->extraOpts), 'calcTotal' => true]); if ($items->error) continue; @@ -326,11 +352,10 @@ class ItemsPage extends GenericPage $upg = []; if ($upgItemData) { - if ($grouping == 1) // slot: match upgradeItem to slot + // slot: match upgradeItem to slot + if ($grouping == ItemListFilter::GROUP_BY_SLOT) { - $upg = array_keys(array_filter($this->filterObj->upgrades, function ($v) use ($group) { - return $v == $group; - })); + $upg = array_keys(array_filter($this->filter->upgrades, fn($x) => $x == $group)); foreach ($upg as $uId) $tabData['data'][$uId] = $upgItemData[$uId]; @@ -340,7 +365,7 @@ class ItemsPage extends GenericPage } else if ($grouping) { - $upg = array_keys($this->filterObj->upgrades); + $upg = array_keys($this->filter->upgrades); $tabData['_upgradeIds'] = $upg; foreach ($upgItemData as $uId => $data) // using numeric keys => cant use array_merge $tabData['data'][$uId] = $data; @@ -349,24 +374,18 @@ class ItemsPage extends GenericPage if ($grouping) { - switch ($grouping) + $tabData['id'] = match ($grouping) { - case 1: - $tabData['id'] = 'slot-'.$group; - break; - case 2: - $tabData['id'] = $group > 0 ? 'level-'.$group : 'other'; - break; - case 3: - $tabData['id'] = $group ? 'source-'.$group : 'unknown'; - break; - } + ItemListFilter::GROUP_BY_SLOT => 'slot-'.$group, + ItemListFilter::GROUP_BY_LEVEL => $group > 0 ? 'level-'.$group : 'other', + ItemListFilter::GROUP_BY_SOURCE => $group ? 'source-'.$group : 'unknown' + }; $tabData['name'] = $nameSource[$group]; $tabData['tabs'] = '$tabsGroups'; } - if ($this->filterObj->fiSetWeights) + if ($this->filter->fiSetWeights) if ($items->hasSetFields('tplArmor')) $tabData['visibleCols'][] = 'armor'; @@ -375,18 +394,18 @@ class ItemsPage extends GenericPage { $tabData['_truncated'] = 1; - $cls = isset($this->category[0]) ? '='.$this->category[0] : ''; + $catg = isset($this->category[0]) ? '='.$this->category[0] : ''; $override = ['gb' => '']; if ($upg) - $override['upg'] = implode(':', $upg); + $override['upg'] = $upg; switch ($grouping) { - case 1: + case ItemListFilter::GROUP_BY_SLOT: $override['sl'] = $group; - $tabData['note'] = '$$WH.sprintf(LANG.lvnote_viewmoreslot, \''.$cls.'\', \''.$this->filterObj->buildGETParam($override).'\')'; + $tabData['note'] = '$$WH.sprintf(LANG.lvnote_viewmoreslot, \''.$catg.'\', \''.$this->filter->buildGETParam($override).'\')'; break; - case 2: + case ItemListFilter::GROUP_BY_LEVEL: if ($group > 0) { $override['minle'] = $group; @@ -395,11 +414,11 @@ class ItemsPage extends GenericPage else $override['maxle'] = abs($group) - 1; - $tabData['note'] = '$$WH.sprintf(LANG.lvnote_viewmorelevel, \''.$cls.'\', \''.$this->filterObj->buildGETParam($override).'\')'; + $tabData['note'] = '$$WH.sprintf(LANG.lvnote_viewmorelevel, \''.$catg.'\', \''.$this->filter->buildGETParam($override).'\')'; break; - case 3: + case ItemListFilter::GROUP_BY_SOURCE: if ($_ = [null, 3, 4, 5, 6, 7, 9, 10, 11][$group]) - $tabData['note'] = '$$WH.sprintf(LANG.lvnote_viewmoresource, \''.$cls.'\', \''.$this->filterObj->buildGETParam($override, ['cr' => 128, 'crs' => $_, 'crv' => 0]).'\')'; + $tabData['note'] = '$$WH.sprintf(LANG.lvnote_viewmoresource, \''.$catg.'\', \''.$this->filter->buildGETParam($override, ['cr' => 128, 'crs' => $_, 'crv' => 0]).'\')'; break; } @@ -410,105 +429,43 @@ class ItemsPage extends GenericPage $tabData['_truncated'] = 1; } - foreach ($tabData as $k => $p) - if (!$p && $k != 'data') - unset($tabData[$k]); + // inherited from >sharedLV, may be empty + if (!$tabData['hiddenCols']) + unset($tabData['hiddenCols']); + if (!$tabData['visibleCols']) + unset($tabData['visibleCols']); + if (!$tabData['extraCols']) + unset($tabData['extraCols']); if ($grouping) $tabData['hideCount'] = 1; - $tabData['data'] = array_values($tabData['data']); - - $this->lvTabs[] = [ItemList::$brickFile, $tabData]; + $tabs[] = $tabData; } + $this->lvTabs = new Tabs(['parent' => "\$\$WH.ge('tabs-generic')"], 'tabsGroups', $forceTabs && $tabs); + // whoops, we have no data? create emergency content - if (empty($this->lvTabs)) - { - $this->forceTabs = false; - $this->lvTabs[] = [ItemList::$brickFile, ['data' => []]]; - } - } + if (!count($tabs)) + $tabs[] = ['data' => []]; - protected function postCache() - { - // sort for dropdown-menus - Lang::sort('game', 'ra'); - Lang::sort('game', 'cl'); - } + foreach ($tabs as $t) + $this->lvTabs->addListviewTab(new Listview($t, ItemList::$brickFile)); - protected function generateTitle() - { - array_unshift($this->title, $this->name); + $this->redButtons[BUTTON_WOWHEAD] = true; + if ($fiQuery = $this->filter->buildGETParam()) + $this->wowheadLink .= '&filter='.$fiQuery; - if (!$this->category) - return; + parent::generate(); - if (isset($this->category[2]) && is_array(Lang::item('cat', $this->category[0], 1, $this->category[1]))) - $tPart = Lang::item('cat', $this->category[0], 1, $this->category[1], 1, $this->category[2]); - else if (isset($this->category[1]) && is_array(Lang::item('cat', $this->category[0]))) - $tPart = Lang::item('cat', $this->category[0], 1, $this->category[1]); - else if ($this->category[0] == 0 && count($this->filterObj->values['ty']) == 1) - $tPart = Lang::item('cat', 0, 1, $this->filterObj->values['ty'][0]); - else - $tPart = Lang::item('cat', $this->category[0]); - - array_unshift($this->title, is_array($tPart) ? $tPart[0] : $tPart); - } - - protected function generatePath() - { - foreach ($this->category as $c) - $this->path[] = $c; - - // if slot-dropdown is available && Armor && $path points to Armor-Class - $form = $this->filterObj->values; - if (count($this->path) == 4 && $this->category[0] == 4 && count($form['sl']) == 1) - $this->path[] = $form['sl'][0]; - else if (isset($this->category[0]) && $this->category[0] == 0 && count($form['ty']) == 1) - $this->path[] = $form['ty'][0]; + $this->setOnCacheLoaded([self::class, 'onBeforeDisplay']); } // fetch best possible gems for chosen weights - private function createGemScores(int $gemQuality) : array + private function createGemScores(int $gemQuality) : void { - $gemScores = []; - - if (!$this->filterObj->fiSetWeights) - return []; - - if (!$gemQuality) - return []; - - $this->sharedLV['computeDataFunc'] = '$fi_scoreSockets'; - - $q = intVal($this->filterObj->values['gm']); - $mask = 0xE; - $cnd = [10, ['class', ITEM_CLASS_GEM], ['gemColorMask', &$mask, '&'], ['quality', &$q]]; - if (!$this->filterObj->values['jc']) - $cnd[] = ['itemLimitCategory', 0]; // Jeweler's Gems - - If ($this->filterObj->wtCnd) - $cnd[] = $this->filterObj->wtCnd; - - $anyColor = new ItemList($cnd, ['extraOpts' => $this->filterObj->extraOpts]); - if (!$anyColor->error) - { - $this->extendGlobalData($anyColor->getJSGlobals()); - $gemScores[0] = array_values($anyColor->getListviewData(ITEMINFO_GEM)); - } - - for ($i = 0; $i < 4; $i++) - { - $mask = 1 << $i; - $q = !$i ? ITEM_QUALITY_RARE : intVal($gemQuality); // meta gems are always included.. ($q is backReferenced) - $byColor = new ItemList($cnd, ['extraOpts' => $this->filterObj->extraOpts]); - if (!$byColor->error) - { - $this->extendGlobalData($byColor->getJSGlobals()); - $gemScores[$mask] = array_values($byColor->getListviewData(ITEMINFO_GEM)); - } - } + if (!$this->filter->fiSetWeights) + return; $this->sharedLV['onBeforeCreate'] = '$fi_initWeightedListview'; $this->sharedLV['onAfterCreate'] = '$fi_addUpgradeIndicator'; @@ -516,7 +473,38 @@ class ItemsPage extends GenericPage array_push($this->sharedLV['hiddenCols'], 'type', 'source'); - return $gemScores; + if (!$gemQuality) + return; + + $this->sharedLV['computeDataFunc'] = '$fi_scoreSockets'; + + $q = intVal($gemQuality); + $mask = 0xE; + $cnd = [10, ['class', ITEM_CLASS_GEM], ['gemColorMask', &$mask, '&'], ['quality', &$q]]; + if (!isset($fiForm['jc'])) + $cnd[] = ['itemLimitCategory', 0]; // Jeweler's Gems + + if ($this->filter->wtCnd) + $cnd[] = $this->filter->wtCnd; + + $anyColor = new ItemList($cnd, ['extraOpts' => $this->filter->extraOpts]); + if (!$anyColor->error) + { + $this->extendGlobalData($anyColor->getJSGlobals()); + $this->gemScores[0] = array_values($anyColor->getListviewData(ITEMINFO_GEM)); + } + + for ($i = 0; $i < 4; $i++) + { + $mask = 1 << $i; + $q = !$i ? ITEM_QUALITY_RARE : intVal($gemQuality); // meta gems are always included.. ($q is backReferenced) + $byColor = new ItemList($cnd, ['extraOpts' => $this->filter->extraOpts]); + if (!$byColor->error) + { + $this->extendGlobalData($byColor->getJSGlobals()); + $this->gemScores[$mask] = array_values($byColor->getListviewData(ITEMINFO_GEM)); + } + } } // display available submenus 'type' and 'slot', if applicable @@ -571,7 +559,6 @@ class ItemsPage extends GenericPage $slotData = Lang::item('inventoryType'); $slotMask = 0x7EA; } - asort($slotData); break; case 16: @@ -596,12 +583,19 @@ class ItemsPage extends GenericPage foreach ($typeData as $k => $str) if ($str && (!$typeMask || ($typeMask & (1 << $k)))) - $this->typeList[$k] = $str; + $this->typeList[$k] = is_array($str) ? $str[0] : $str; foreach ($slotData as $k => $str) // "Off Hand" => "Shield" if ($str && (!$slotMask || ($slotMask & (1 << $k)))) $this->slotList[$k] = $k == INVTYPE_SHIELD ? Lang::item('armorSubClass', 6) : $str; } + + protected static function onBeforeDisplay() : void + { + // sort for dropdown-menus + Lang::sort('game', 'ra'); + Lang::sort('game', 'cl'); + } } ?> diff --git a/includes/dbtypes/enchantment.class.php b/includes/dbtypes/enchantment.class.php index 4fbcbb64..fbe71b3a 100644 --- a/includes/dbtypes/enchantment.class.php +++ b/includes/dbtypes/enchantment.class.php @@ -114,7 +114,7 @@ class EnchantmentList extends DBTypeList if (!$data[$this->id]['spells']) unset($data[$this->id]['spells']); - Util::arraySumByKey($data[$this->id], $this->jsonStats[$this->id]->toJson()); + Util::arraySumByKey($data[$this->id], $this->jsonStats[$this->id]->toJson(includeEmpty: false)); } return $data; @@ -122,7 +122,7 @@ class EnchantmentList extends DBTypeList public function getStatGainForCurrent() : array { - return $this->jsonStats[$this->id]->toJson(); + return $this->jsonStats[$this->id]->toJson(includeEmpty: false); } public function getRelSpell(int $id) : ?array diff --git a/includes/dbtypes/item.class.php b/includes/dbtypes/item.class.php index 36333893..d8e11659 100644 --- a/includes/dbtypes/item.class.php +++ b/includes/dbtypes/item.class.php @@ -49,7 +49,7 @@ class ItemList extends DBTypeList // from json to json .. the gentle fuckups of legacy code integration $this->initJsonStats(); - $this->jsonStats[$this->id] = (new StatsContainer())->fromJson($_curTpl, true)->toJson(Stat::FLAG_ITEM /* | Stat::FLAG_SERVERSIDE */); + $this->jsonStats[$this->id] = (new StatsContainer())->fromJson($_curTpl, true)->toJson(Stat::FLAG_ITEM /* | Stat::FLAG_SERVERSIDE */, false); if ($miscData) { @@ -723,32 +723,7 @@ class ItemList extends DBTypeList // activation conditions for meta gems if (!empty($gemEnch['conditionId'])) - { - if ($gemCnd = DB::Aowow()->selectRow('SELECT * FROM ?_itemenchantmentcondition WHERE `id` = ?d', $gemEnch['conditionId'])) - { - for ($i = 1; $i < 6; $i++) - { - if (!$gemCnd['color'.$i]) - continue; - - $vspfArgs = []; - switch ($gemCnd['comparator'.$i]) - { - case 2: // requires less than ( || ) gems - case 5: // requires at least than ( || ) gems - $vspfArgs = [$gemCnd['value'.$i], Lang::item('gemColors', $gemCnd['color'.$i] - 1)]; - break; - case 3: // requires more than ( || ) gems - $vspfArgs = [Lang::item('gemColors', $gemCnd['color'.$i] - 1), Lang::item('gemColors', $gemCnd['cmpColor'.$i] - 1)]; - break; - default: - continue 2; - } - - $x .= ''.Lang::achievement('reqNumCrt').' '.Lang::item('gemConditions', $gemCnd['comparator'.$i], $vspfArgs).'
'; - } - } - } + $x .= Game::getEnchantmentCondition($gemEnch['conditionId'], $interactive); } // Random Enchantment - if random enchantment is set, prepend stats from it @@ -1461,6 +1436,25 @@ class ItemList extends DBTypeList return in_array($this->curTpl['subClassBak'], [ITEM_SUBCLASS_BOW, ITEM_SUBCLASS_GUN, ITEM_SUBCLASS_THROWN, ITEM_SUBCLASS_CROSSBOW, ITEM_SUBCLASS_WAND]); } + public function isBodyArmor() : bool + { + if ($this->curTpl['class'] != ITEM_CLASS_ARMOR) + return false; + + return in_array($this->curTpl['subClassBak'], [ITEM_SUBCLASS_CLOTH_ARMOR, ITEM_SUBCLASS_LEATHER_ARMOR, ITEM_SUBCLASS_MAIL_ARMOR, ITEM_SUBCLASS_PLATE_ARMOR]); + } + + public function isDisplayable() : bool + { + if (!$this->curTpl['displayId']) + return false; + + return in_array($this->curTpl['slot'], array( + INVTYPE_HEAD, INVTYPE_SHOULDERS, INVTYPE_BODY, INVTYPE_CHEST, INVTYPE_WAIST, INVTYPE_LEGS, INVTYPE_FEET, INVTYPE_WRISTS, + INVTYPE_HANDS, INVTYPE_WEAPON, INVTYPE_SHIELD, INVTYPE_RANGED, INVTYPE_CLOAK, INVTYPE_2HWEAPON, INVTYPE_TABARD, INVTYPE_ROBE, + INVTYPE_WEAPONMAINHAND, INVTYPE_WEAPONOFFHAND, INVTYPE_HOLDABLE, INVTYPE_THROWN, INVTYPE_RANGEDRIGHT)); + } + private function formatRating(int $statId, int $itemMod, int $qty, bool $interactive = false, bool &$scaling = false) : string { // clamp level range @@ -1773,6 +1767,11 @@ class ItemList extends DBTypeList class ItemListFilter extends Filter { + public const /* int */ GROUP_BY_NONE = 0; + public const /* int */ GROUP_BY_SLOT = 1; + public const /* int */ GROUP_BY_LEVEL = 2; + public const /* int */ GROUP_BY_SOURCE = 3; + private array $ubFilter = []; // usable-by - limit weapon/armor selection per CharClass - itemClass => available itemsubclasses private string $extCostQuery = 'SELECT `item` FROM npc_vendor WHERE `extendedCost` IN (?a) UNION SELECT `item` FROM game_event_npc_vendor WHERE `extendedCost` IN (?a)'; @@ -2372,7 +2371,7 @@ class ItemListFilter extends Filter return null; $costs = DB::Aowow()->selectCol( - 'SELECT `id` FROM ?_itemextendedcost WHERE `reqItemId1` IN (?a) OR `reqItemId2` IN (?a) OR `reqItemId3` IN (?a) OR `reqItemId4` IN (?a) OR `reqItemId5` IN (?a)', + 'SELECT `id` FROM ?_itemextendedcost WHERE `reqItemId1` IN (?a) OR `reqItemId2` IN (?a) OR `reqItemId3` IN (?a) OR `reqItemId4` IN (?a) OR `reqItemId5` IN (?a)', $_, $_, $_, $_, $_ ); if ($items = DB::World()->selectCol($this->extCostQuery, $costs, $costs)) diff --git a/includes/dbtypes/spell.class.php b/includes/dbtypes/spell.class.php index aaff8865..aef037c7 100644 --- a/includes/dbtypes/spell.class.php +++ b/includes/dbtypes/spell.class.php @@ -210,7 +210,7 @@ class SpellList extends DBTypeList $data = []; // flat gains foreach ($this->getStatGain() as $id => $spellData) { - $data[$id] = $spellData->toJson(STAT::FLAG_ITEM | STAT::FLAG_PROFILER); + $data[$id] = $spellData->toJson(STAT::FLAG_ITEM | STAT::FLAG_PROFILER, false); // apply weapon restrictions $this->getEntry($id); diff --git a/includes/game/chrstatistics.php b/includes/game/chrstatistics.php index 77dbe581..c1819501 100644 --- a/includes/game/chrstatistics.php +++ b/includes/game/chrstatistics.php @@ -487,11 +487,11 @@ class StatsContainer implements \Countable /* Output */ /**********/ - public function toJson(int $outFlags = Stat::FLAG_NONE) : array + public function toJson(int $outFlags = Stat::FLAG_NONE, bool $includeEmpty = true) : array { $out = []; foreach ($this->store as $stat => $amt) - if (!$outFlags || (Stat::getFlags($stat) & $outFlags)) + if ((!$outFlags || (Stat::getFlags($stat) & $outFlags)) && ($amt || $includeEmpty)) $out[Stat::getJsonString($stat)] = $amt; return $out; diff --git a/localization/locale_dede.php b/localization/locale_dede.php index df489fdd..4244baf7 100644 --- a/localization/locale_dede.php +++ b/localization/locale_dede.php @@ -37,7 +37,7 @@ $lang = array( 'quickFacts' => "Kurzübersicht", 'screenshots' => "Screenshots", 'videos' => "Videos", - 'side' => "Seite", + 'side' => "Seite: ", 'related' => "Weiterführende Informationen", 'contribute' => "Beitragen", // 'replyingTo' => "Antwort zu einem Kommentar von", @@ -73,7 +73,7 @@ $lang = array( 'refineSearch' => 'Tipp: Präzisiere deine Suche mit Durchsuchen einer Unterkategorie.', 'clear' => "leeren", 'exactMatch' => "Exakt passend", - '_reqLevel' => "Mindeststufe", + '_reqLevel' => "Mindeststufe: ", // infobox 'unavailable' => "Nicht für Spieler verfügbar", @@ -106,12 +106,12 @@ $lang = array( 'langOnly' => "Diese Seite ist nur in %s verfügbar.", // calculators - 'preset' => "Vorlage", + 'preset' => "Vorlage: ", 'addWeight' => "Weitere Gewichtung hinzufügen", 'createWS' => "Gewichtungsverteilung erstellen", 'jcGemsOnly' => "JS-exklusive Edelsteine einschließen", 'cappedHint' => 'Tipp: Entfernt Gewichtungen für gedeckte Werte wie Trefferwertung.', - 'groupBy' => "Ordnen nach", + 'groupBy' => "Ordnen nach: ", 'gb' => array( ["Nichts", "none"], ["Platz", "slot"], ["Stufe", "level"], ["Quelle", "source"] ), @@ -345,7 +345,7 @@ $lang = array( 'reqLevel' => "Benötigt Stufe %s", 'reqSkillLevel' => "Benötigte Fertigkeitsstufe", 'school' => "Magieart", - 'type' => "Art", + 'type' => "Art: ", 'valueDelim' => " - ", // " bis " 'pvp' => "PvP", @@ -2059,7 +2059,7 @@ $lang = array( 'refundable' => "Rückzahlbar", 'noNeedRoll' => "Kann nicht für Bedarf werfen", 'atKeyring' => "Passt in den Schlüsselbund", - 'worth' => "Wert", + 'worth' => "Wert: ", 'consumable' => "Verbrauchbar", 'nonConsumable' => "Nicht verbrauchbar", 'accountWide' => "Accountweit", @@ -2068,17 +2068,17 @@ $lang = array( 'prospectable' => "Sondierbar", 'disenchantable'=> "Kann entzaubert werden", 'cantDisenchant'=> "Kann nicht entzaubert werden", - 'repairCost' => "Reparaturkosten", - 'tool' => "Werkzeug", + 'repairCost' => "Reparaturkosten: ", + 'tool' => "Werkzeug: ", 'cost' => "Preis", 'content' => "Inhalt", '_transfer' => 'Dieser Gegenstand wird mit %s vertauscht, wenn Ihr zur %s wechselt.', '_unavailable' => "Dieser Gegenstand ist nicht für Spieler verfügbar.", '_rndEnchants' => "Zufällige Verzauberungen", '_chance' => "(Chance von %s%%)", - 'slot' => "Platz", - '_quality' => "Qualität", - 'usableBy' => "Benutzbar von", + 'slot' => "Platz: ", + '_quality' => "Qualität: ", + 'usableBy' => "Benutzbar von: ", 'buyout' => "Sofortkaufpreis", 'each' => "Stück", 'tabOther' => "Anderes", @@ -2110,7 +2110,7 @@ $lang = array( 'range' => ['%1$d - %2$d Schaden', '%1$d - %2$d %3$sschaden', '+ %1$d - %2$d Schaden', '+ %1$d - %2$d %3$sschaden' ], 'ammo' => ["Verursacht %g zusätzlichen Schaden pro Sekunde.", "Verursacht %g zusätzlichen %sschaden pro Sekunde", "+ %g Schaden pro Sekunde", "+ %g %sschaden pro Sekunde"] ), - 'gems' => "Edelsteine", + 'gems' => "Edelsteine: ", 'socketBonus' => "Sockelbonus: %s", 'socket' => array( "Metasockel", "Roter Sockel", "Gelber Sockel", "Blauer Sockel", -1 => "Prismatischer Sockel" diff --git a/localization/locale_enus.php b/localization/locale_enus.php index 0dbf5cfb..ad214f03 100644 --- a/localization/locale_enus.php +++ b/localization/locale_enus.php @@ -37,7 +37,7 @@ $lang = array( 'quickFacts' => "Quick Facts", 'screenshots' => "Screenshots", 'videos' => "Videos", - 'side' => "Side", + 'side' => "Side: ", 'related' => "Related", 'contribute' => "Contribute", // 'replyingTo' => "The answer to a comment from", @@ -73,7 +73,7 @@ $lang = array( 'refineSearch' => 'Tip: Refine your search by browsing a subcategory.', 'clear' => "clear", 'exactMatch' => "Exact match", - '_reqLevel' => "Required level", + '_reqLevel' => "Required level: ", // infobox 'unavailable' => "Not available to players", // alternative wording found: "No longer available to players" ... aw screw it <_< @@ -106,12 +106,12 @@ $lang = array( 'langOnly' => "This page is only available in %s.", // calculators - 'preset' => "Preset", + 'preset' => "Preset: ", 'addWeight' => "Add another weight", 'createWS' => "Create a weight scale", 'jcGemsOnly' => "Include JC-only gems", 'cappedHint' => 'Tip: Remove weights for capped statistics such as Hit rating.', - 'groupBy' => "Group By", + 'groupBy' => "Group By: ", 'gb' => array( ["None", "none"], ["Slot", "slot"], ["Level", "level"], ["Source", "source"] ), @@ -345,7 +345,7 @@ $lang = array( 'reqLevel' => "Requires Level %s", 'reqSkillLevel' => "Required skill level", 'school' => "School", - 'type' => "Type", + 'type' => "Type: ", 'valueDelim' => " to ", 'pvp' => "PvP", // PVP @@ -2059,7 +2059,7 @@ $lang = array( 'refundable' => "Refundable", 'noNeedRoll' => "Cannot roll Need", 'atKeyring' => "Can be placed in the keyring", - 'worth' => "Worth", + 'worth' => "Worth: ", 'consumable' => "Consumable", 'nonConsumable' => "Non-consumable", 'accountWide' => "Account-wide", @@ -2068,17 +2068,17 @@ $lang = array( 'prospectable' => "Prospectable", // ITEM_PROSPECTABLE 'disenchantable'=> "Disenchantable", // ITEM_DISENCHANT_ANY_SKILL 'cantDisenchant'=> "Cannot be disenchanted", // ITEM_DISENCHANT_NOT_DISENCHANTABLE - 'repairCost' => "Repair cost", // REPAIR_COST - 'tool' => "Tool", + 'repairCost' => "Repair cost: ", // REPAIR_COST + 'tool' => "Tool: ", 'cost' => "Cost", // COSTS_LABEL 'content' => "Content", '_transfer' => 'This item will be converted to %s if you transfer to %s.', '_unavailable' => "This item is not available to players.", '_rndEnchants' => "Random Enchantments", '_chance' => "(%s%% chance)", - 'slot' => "Slot", - '_quality' => "Quality", // QUALITY - 'usableBy' => "Usable by", + 'slot' => "Slot: ", + '_quality' => "Quality: ", // QUALITY + 'usableBy' => "Usable by: ", 'buyout' => "Buyout price", // BUYOUT_PRICE 'each' => "each", 'tabOther' => "Other", @@ -2110,7 +2110,7 @@ $lang = array( 'range' => ["%d - %d Damage", "%d - %d %s Damage", "+ %d - %d Damage", "+%d - %d %s Damage" ], 'ammo' => ["Adds %g damage per second", "Adds %g %s damage per second", "+ %g damage per second", "+ %g %s damage per second" ] ), - 'gems' => "Gems", + 'gems' => "Gems: ", 'socketBonus' => "Socket Bonus: %s", // ITEM_SOCKET_BONUS 'socket' => array( // EMPTY_SOCKET_* "Meta Socket", "Red Socket", "Yellow Socket", "Blue Socket", -1 => "Prismatic Socket" diff --git a/localization/locale_eses.php b/localization/locale_eses.php index 80540f31..660fc84c 100644 --- a/localization/locale_eses.php +++ b/localization/locale_eses.php @@ -37,7 +37,7 @@ $lang = array( 'quickFacts' => "Notas rápidas", 'screenshots' => "Capturas de pantalla", 'videos' => "Videos", - 'side' => "Lado", + 'side' => "Lado: ", 'related' => "Información relacionada", 'contribute' => "Contribuir", // 'replyingTo' => "The answer to a comment from", @@ -73,7 +73,7 @@ $lang = array( 'refineSearch' => 'Sugerencia: Refina tu búsqueda llendo a una subcategoría.', 'clear' => "borrar", 'exactMatch' => "Coincidencia exacta", - '_reqLevel' => "Nivel requerido", + '_reqLevel' => "Nivel requerido: ", // infobox 'unavailable' => "No está disponible a los jugadores", @@ -106,12 +106,12 @@ $lang = array( 'langOnly' => "Esta página sólo está disponible en %s.", // calculators - 'preset' => "Predet.", + 'preset' => "Predet.: ", 'addWeight' => "Añadir otro factor", 'createWS' => "Crear escala de valores", 'jcGemsOnly' => "Incluir solo gemas de joyería", 'cappedHint' => 'Consejo: Elimina escalas para atributos al máximo como el Índice de Golpe.', - 'groupBy' => "Agrupar por", + 'groupBy' => "Agrupar por: ", 'gb' => array( ["Ninguno", "none"], ["Casilla", "slot"], ["Nivel", "level"], ["Fuente", "source"] ), @@ -345,7 +345,7 @@ $lang = array( 'reqLevel' => "Necesitas ser de nivel %s", 'reqSkillLevel' => "Requiere nivel de habilidad", 'school' => "Escuela", - 'type' => "Tipo", + 'type' => "Tipo: ", 'valueDelim' => " - ", 'pvp' => "JcJ", @@ -2059,7 +2059,7 @@ $lang = array( 'refundable' => "Se puede devolver", 'noNeedRoll' => "No se puede hacer una tirada por Necesidad", 'atKeyring' => "Se puede poner en el llavero", - 'worth' => "Valor", + 'worth' => "Valor: ", 'consumable' => "Consumible", 'nonConsumable' => "No consumible", 'accountWide' => "Ligado a la cuenta", @@ -2068,17 +2068,17 @@ $lang = array( 'prospectable' => "Prospectable", 'disenchantable'=> "Desencantable", 'cantDisenchant'=> "No se puede desencantar", - 'repairCost' => "Coste de reparación", - 'tool' => "Herramienta", + 'repairCost' => "Coste de reparación: ", + 'tool' => "Herramienta: ", 'cost' => "Coste", 'content' => "Contenido", '_transfer' => 'Este objeto será convertido a %s si lo transfieres a la %s.', '_unavailable' => "Este objeto no está disponible para los jugadores.", '_rndEnchants' => "Encantamientos aleatorios", '_chance' => "(probabilidad %s%%)", - 'slot' => "Casilla", - '_quality' => "Calidad", - 'usableBy' => "Usable por", + 'slot' => "Casilla: ", + '_quality' => "Calidad: ", + 'usableBy' => "Usable por: ", 'buyout' => "Precio de venta en subasta", 'each' => "cada uno", 'tabOther' => "Otros", @@ -2110,7 +2110,7 @@ $lang = array( "range" => ["%d - %d Daño", "%d - %d daño de %s", "+ %d: %d daño", "+%d - %d daño de %s" ], 'ammo' => ["Añade %g daño por segundo", "Añade %g %s daño por segundo", "+ %g daño por segundo", "+ %g %s daño por segundo"] ), - 'gems' => "Gemas", + 'gems' => "Gemas: ", 'socketBonus' => "Bono de ranura: %s", 'socket' => array( "Ranura meta", "Ranura roja", "Ranura amarilla", "Ranura azul", -1 => "Ranura prismática" diff --git a/localization/locale_frfr.php b/localization/locale_frfr.php index 96eb30a4..6cc43e08 100644 --- a/localization/locale_frfr.php +++ b/localization/locale_frfr.php @@ -37,7 +37,7 @@ $lang = array( 'quickFacts' => "En bref", 'screenshots' => "Captures d'écran", 'videos' => "Vidéos", - 'side' => "Coté", + 'side' => "Coté : ", 'related' => "Informations connexes", 'contribute' => "Contribuer", // 'replyingTo' => "En réponse au commentaire de", @@ -73,7 +73,7 @@ $lang = array( 'refineSearch' => "Astuce : Affinez votre recherche en utilisant une sous-catégorie.", 'clear' => "effacer", 'exactMatch' => "Concordance exacte", - '_reqLevel' => "Niveau requis", + '_reqLevel' => "Niveau requis : ", // infobox 'unavailable' => "Non disponible aux joueurs", @@ -106,12 +106,12 @@ $lang = array( 'langOnly' => "Cette page n'est disponible qu'en %s pour le moment.", // calculators - 'preset' => "Prédéterminée", + 'preset' => "Prédéterminée : ", 'addWeight' => "Ajouter un autre facteur", 'createWS' => "Créer une échelle de valeurs", 'jcGemsOnly' => "Inclure les gemmes de joaillier", 'cappedHint' => 'Conseil: Enlever un facteur pour les statistiques au maximum tel que le score de touche.', - 'groupBy' => "Groupé par", + 'groupBy' => "Groupé par : ", 'gb' => array( ["Aucun", "none"], ["Emplacement", "slot"], ["Niveau", "level"], ["Source", "source"] ), @@ -345,7 +345,7 @@ $lang = array( 'reqLevel' => "Niveau %s requis", 'reqSkillLevel' => "Niveau de compétence requis", 'school' => "École", - 'type' => "Type", + 'type' => "Type : ", 'valueDelim' => " - ", 'pvp' => "JcJ", @@ -2059,7 +2059,7 @@ $lang = array( 'refundable' => "Remboursable", 'noNeedRoll' => "Ne peut pas faire un jet de Besoin", 'atKeyring' => "Va dans le trousseau de clés", - 'worth' => "Vaut", + 'worth' => "Vaut : ", 'consumable' => "Consommable", 'nonConsumable' => "Non-consommable", 'accountWide' => "Portant sur le compte", @@ -2068,17 +2068,17 @@ $lang = array( 'prospectable' => "Prospectable", 'disenchantable'=> "Desencantable", 'cantDisenchant'=> "Ne peut pas être désenchanté", - 'repairCost' => "Cout de réparation", - 'tool' => "Outil", + 'repairCost' => "Cout de réparation : ", + 'tool' => "Outil : ", 'cost' => "Coût", 'content' => "Contenu", '_transfer' => 'Cet objet sera converti en %s si vous transférez en %s.', '_unavailable' => "Cet objet n'est pas disponible pour les joueurs.", '_rndEnchants' => "Enchantements aléatoires", '_chance' => "(%s%% de chance)", - 'slot' => "Emplacement", - '_quality' => "Qualité", - 'usableBy' => "Utilisable par", + 'slot' => "Emplacement : ", + '_quality' => "Qualité : ", + 'usableBy' => "Utilisable par : ", 'buyout' => "Vente immédiate", 'each' => "chacun", 'tabOther' => "Autre", @@ -2110,7 +2110,7 @@ $lang = array( 'range' => ["Dégâts : %d - %d", "%d - %d points de dégâts (%s)", "+ %d - %d points de dégâts", "+%d - %d points de dégâts (%s)" ], 'ammo' => ["Ajoute %g dégâts par seconde", "Ajoute %g points de dégâts (%s) par seconde", "+ %g points de dégâts par seconde", "+ %g points de dégâts (%s) par seconde" ] ), - 'gems' => "Gemmes", + 'gems' => "Gemmes : ", 'socketBonus' => "Bonus de châsse: %s", 'socket' => array( "Méta-châsse", "Châsse rouge", "Châsse jaune", "Châsse bleue", -1 => "Châsse prismatique" diff --git a/localization/locale_ruru.php b/localization/locale_ruru.php index 24c97f08..79f6c974 100644 --- a/localization/locale_ruru.php +++ b/localization/locale_ruru.php @@ -37,7 +37,7 @@ $lang = array( 'quickFacts' => "Краткая информация", 'screenshots' => "Изображения", 'videos' => "Видео", - 'side' => "Сторона", + 'side' => "Сторона: ", 'related' => "Дополнительная информация", 'contribute' => "Добавить", // 'replyingTo' => "Ответ на комментарий от", @@ -73,7 +73,7 @@ $lang = array( 'refineSearch' => 'Совет: Уточните поиск, добавив подкатегорию.', 'clear' => "Очистить", 'exactMatch' => "Полное совпадение", - '_reqLevel' => "Требуется уровень", + '_reqLevel' => "Требуется уровень: ", // infobox 'unavailable' => "Недоступно игрокам", @@ -106,12 +106,12 @@ $lang = array( 'langOnly' => "Эта страница доступна только на %s языке.", // calculators - 'preset' => "Готовая таблица", + 'preset' => "Готовая таблица: ", 'addWeight' => "Добавить фильтр значимости", 'createWS' => "Отсортировать по значимости", 'jcGemsOnly' => "Использовать ювелирские", 'cappedHint' => 'Подсказка: Удалите характеристики с капом (например, меткость).', - 'groupBy' => "Группировать", + 'groupBy' => "Группировать: ", 'gb' => array( ['Нет', 'none'], ['Слот', 'slot'], ['Уровень', 'level'], ['Источник', 'source'] ), @@ -345,7 +345,7 @@ $lang = array( 'reqLevel' => "Требуется уровень: %s", 'reqSkillLevel' => "Требуется уровень навыка", 'school' => "Школа", - 'type' => "Тип", + 'type' => "Тип: ", 'valueDelim' => " - ", 'pvp' => "PvP", @@ -2059,7 +2059,7 @@ $lang = array( 'refundable' => "Подлежит возврату", 'noNeedRoll' => 'Нельзя говорить "Мне это нужно"', 'atKeyring' => "Может быть помещён в связку для ключей", - 'worth' => "Деньги", + 'worth' => "Деньги: ", 'consumable' => "Расходуется", 'nonConsumable' => "Не расходуется", 'accountWide' => "Привязано к учетной записи", @@ -2068,17 +2068,17 @@ $lang = array( 'prospectable' => "Просеиваемое", 'disenchantable'=> "Распыляемый", 'cantDisenchant'=> "Нельзя распылить", - 'repairCost' => "Цена починки", - 'tool' => "Инструмент", + 'repairCost' => "Цена починки: ", + 'tool' => "Инструмент: ", 'cost' => "Цена", 'content' => "Материал", '_transfer' => 'Этот предмет превратится в %s, если вы перейдете за %s.', '_unavailable' => "Этот предмет не доступен игрокам.", '_rndEnchants' => "Случайные улучшения", '_chance' => "(шанс %s%%)", - 'slot' => "Слот", - '_quality' => "Качество", - 'usableBy' => "Используется (кем)", + 'slot' => "Слот: ", + '_quality' => "Качество: ", + 'usableBy' => "Используется (кем): ", 'buyout' => "Цена выкупа", 'each' => "каждый", 'tabOther' => "Другое", @@ -2111,7 +2111,7 @@ $lang = array( 'range' => ["Урон: %d - %d", "%d - %d ед. |3-6(%s)", "+ %d - %d ед. урона", "+%d - %d ед. урона (%s)" ], 'ammo' => ["Добавляет %g ед. урона в секунду", "Добавляет %g ед. урона (%s) в секунду", "+ ед. урона в секунду от боеприпасов (%g)", "+ %g %s ед. урона в секунду" ] ), - 'gems' => "Самоцветы", + 'gems' => "Самоцветы: ", 'socketBonus' => "При соответствии цвета: %s", 'socket' => array( "Особое гнездо", "Красное гнездо", "Желтое гнездо", "Синее гнездо", -1 => "Бесцветное гнездо" diff --git a/localization/locale_zhcn.php b/localization/locale_zhcn.php index aec6c216..666689dd 100644 --- a/localization/locale_zhcn.php +++ b/localization/locale_zhcn.php @@ -37,7 +37,7 @@ $lang = array( 'quickFacts' => "相关信息", 'screenshots' => "屏幕截图", 'videos' => "视频", - 'side' => "阵营", + 'side' => "阵营:", 'related' => "相关", 'contribute' => "贡献", // 'replyingTo' => "The answer to a comment from", @@ -73,7 +73,7 @@ $lang = array( 'refineSearch' => '提示:通过浏览 子类别搜索。', 'clear' => "清除", 'exactMatch' => "精确匹配", - '_reqLevel' => "要求等级", + '_reqLevel' => "要求等级:", // infobox 'unavailable' => "对玩家不可用", // alternative wording found: "No longer available to players" ... aw screw it <_< @@ -106,12 +106,12 @@ $lang = array( 'langOnly' => "该页面仅以%s提供。", // calculators - 'preset' => "预设", + 'preset' => "预设:", 'addWeight' => "添加另一个权重", 'createWS' => "创建一个权重比例", 'jcGemsOnly' => "包含JC-only宝石", 'cappedHint' => '提示:移除 命中等级等上限属性的权重。', - 'groupBy' => "按组", + 'groupBy' => "按组:", 'gb' => array( ["无", "none"], ["插槽", "slot"], ["等级", "level"], ["来源", "source"] ), @@ -344,7 +344,7 @@ $lang = array( 'reqLevel' => "需要等级%s", 'reqSkillLevel' => "需要技能等级", 'school' => "类型", - 'type' => "类型", + 'type' => "类型:", 'valueDelim' => "到", 'pvp' => "PvP", @@ -2058,7 +2058,7 @@ $lang = array( 'refundable' => "可退还的", 'noNeedRoll' => "无法需求掷骰", 'atKeyring' => "可以放在钥匙链", - 'worth' => "价值", + 'worth' => "价值:", 'consumable' => "消耗品", 'nonConsumable' => "非消耗品", 'accountWide' => "账号共享", @@ -2067,17 +2067,17 @@ $lang = array( 'prospectable' => "可选矿", 'disenchantable'=> "可分解", 'cantDisenchant'=> "无法分解", - 'repairCost' => "修理费用", - 'tool' => "工具", + 'repairCost' => "修理费用:", + 'tool' => "工具:", 'cost' => "花费", 'content' => "内容", '_transfer' => '这个物品将被转换到%s,如果你转移到%s。', '_unavailable' => "这个物品对玩家不可用。", '_rndEnchants' => "随机附魔", '_chance' => "(%s%%几率)", - 'slot' => "装备部位", - '_quality' => "品质", - 'usableBy' => "可用职业", + 'slot' => "装备部位:", + '_quality' => "品质:", + 'usableBy' => "可用职业:", 'buyout' => "一口价", 'each' => "每个", 'tabOther' => "其他", @@ -2109,7 +2109,7 @@ $lang = array( 'range' => ["%d - %d伤害", "%d - %d 点%s伤害", "+ %d - %d伤害", "+%d - %d 点%s伤害" ], 'ammo' => ["每秒伤害提高%g", "每秒增加%g点%s系伤害", "每秒伤害+%g", "每秒+%g点%s伤害" ] ), - 'gems' => "宝石", + 'gems' => "宝石:", 'socketBonus' => "镶孔奖励:%s", 'socket' => array( "多彩插槽", "红色插槽", "黄色插槽", "蓝色插槽", -1 => "棱彩插槽" diff --git a/setup/tools/filegen/itemsets.ss.php b/setup/tools/filegen/itemsets.ss.php index 514b5f7f..0280f2ec 100644 --- a/setup/tools/filegen/itemsets.ss.php +++ b/setup/tools/filegen/itemsets.ss.php @@ -101,7 +101,7 @@ CLISetup::registerSetup("build", new class extends SetupScript if (!$itemQty || !$itemSpl || !isset($jsonBonus[$itemSpl])) continue; - if ($x = $jsonBonus[$itemSpl]->toJson(Stat::FLAG_ITEM)) + if ($x = $jsonBonus[$itemSpl]->toJson(Stat::FLAG_ITEM, false)) { if (!isset($setbonus[$itemQty])) $setbonus[$itemQty] = []; diff --git a/static/js/locale_dede.js b/static/js/locale_dede.js index 68a054cb..7ee34a44 100644 --- a/static/js/locale_dede.js +++ b/static/js/locale_dede.js @@ -1283,7 +1283,8 @@ var g_item_subclasses = { 8: 'Verzauberkunstformel', 9: 'Angelbuch', 10: 'Juwelenschleifer-Vorlage', - 11: 'Inschriften-Technik' + 11: 'Inschriften-Technik', + 12: 'Bergbauleitfäden' }, 11: { 2: 'Köcher', diff --git a/static/js/locale_enus.js b/static/js/locale_enus.js index c60dbdbf..a06c0c6f 100644 --- a/static/js/locale_enus.js +++ b/static/js/locale_enus.js @@ -1330,7 +1330,8 @@ var g_item_subclasses = { 8: 'Enchanting Formula', 9: 'Fishing Book', 10: 'Jewelcrafting Design', - 11: 'Inscription Technique' + 11: 'Inscription Technique', + 12: 'Mining Guides' }, 11: { 2: 'Quiver', diff --git a/static/js/locale_eses.js b/static/js/locale_eses.js index 8cabdf1f..e4f82e5a 100644 --- a/static/js/locale_eses.js +++ b/static/js/locale_eses.js @@ -1283,7 +1283,8 @@ var g_item_subclasses = { 8: 'Fórmula de encantamiento', 9: 'Libro de pesca', 10: 'Boceto de joyería', - 11: 'Técnica de Inscripción' + 11: 'Técnica de Inscripción', + 12: 'Guías de minería' }, 11: { 2: 'Carcaj', diff --git a/static/js/locale_frfr.js b/static/js/locale_frfr.js index b2f16454..42128086 100644 --- a/static/js/locale_frfr.js +++ b/static/js/locale_frfr.js @@ -1283,7 +1283,8 @@ var g_item_subclasses = { 8: 'Formule d\'enchantement', 9: 'Livre de pêche', 10: 'Dessin de joaillerie', - 11: 'Technique de calligraphie' + 11: 'Technique de calligraphie', + 12: 'Guides de Minage' }, 11: { 2: 'Carquois', diff --git a/static/js/locale_ruru.js b/static/js/locale_ruru.js index bb57d724..8851c21b 100644 --- a/static/js/locale_ruru.js +++ b/static/js/locale_ruru.js @@ -1283,7 +1283,8 @@ var g_item_subclasses = { 8: 'Зачаровывание', 9: 'Рыбная ловля', 10: 'Ювелирное дело', - 11: 'Технология Начертания' + 11: 'Технология Начертания', + 12: 'Руководства по Шахтерскому делу' }, 11: { 2: 'Колчан', diff --git a/static/js/locale_zhcn.js b/static/js/locale_zhcn.js index c21fdcbd..53d7cfda 100644 --- a/static/js/locale_zhcn.js +++ b/static/js/locale_zhcn.js @@ -1330,6 +1330,7 @@ var g_item_subclasses = { 9: "钓鱼书籍", 10: "宝石加工设计", 11: "铭文技能", + 12: "采矿指南" }, 11: { 2: '箭袋', diff --git a/template/bricks/tooltip.tpl.php b/template/bricks/tooltip.tpl.php index d4d3e29c..358ecfcf 100644 --- a/template/bricks/tooltip.tpl.php +++ b/template/bricks/tooltip.tpl.php @@ -1,5 +1,10 @@ - +tooltip; +?>
@@ -7,8 +12,6 @@
jsGlobals[Type::SPELL][2][$this->typeId]['buff']); // not set with items - if ($hasBuff): ?>

@@ -25,7 +28,7 @@ endif; ?>