mirror of
https://github.com/Sarjuuk/aowow.git
synced 2025-11-29 15:58:16 +08:00
Template/Endpoints (Prep)
* modernize DB-Types - long term: should be split in class that describes the DB-Type and container class that handles multiples * make unchanging filter props static, allow lookup of criteria indizes through filter * move username/mail/password checks to util and make them usable as input filter
This commit is contained in:
390
includes/dbtypes/achievement.class.php
Normal file
390
includes/dbtypes/achievement.class.php
Normal file
@@ -0,0 +1,390 @@
|
||||
<?php
|
||||
|
||||
namespace Aowow;
|
||||
|
||||
if (!defined('AOWOW_REVISION'))
|
||||
die('illegal access');
|
||||
|
||||
|
||||
class AchievementList extends DBTypeList
|
||||
{
|
||||
use listviewHelper;
|
||||
|
||||
public static int $type = Type::ACHIEVEMENT;
|
||||
public static string $brickFile = 'achievement';
|
||||
public static string $dataTable = '?_achievement';
|
||||
public array $criteria = [];
|
||||
|
||||
protected string $queryBase = 'SELECT `a`.*, `a`.`id` AS ARRAY_KEY FROM ?_achievement a';
|
||||
protected array $queryOpts = array(
|
||||
'a' => [['ic'], 'o' => 'orderInGroup ASC'],
|
||||
'ic' => ['j' => ['?_icons ic ON ic.id = a.iconId', true], 's' => ', ic.name AS iconString'],
|
||||
'ac' => ['j' => ['?_achievementcriteria AS `ac` ON `ac`.`refAchievementId` = `a`.`id`', true], 'g' => '`a`.`id`']
|
||||
);
|
||||
|
||||
public function __construct(array $conditions = [], array $miscData = [])
|
||||
{
|
||||
parent::__construct($conditions, $miscData);
|
||||
|
||||
if ($this->error)
|
||||
return;
|
||||
|
||||
// post processing
|
||||
$rewards = DB::World()->select(
|
||||
'SELECT ar.`ID` AS ARRAY_KEY, ar.`TitleA`, ar.`TitleH`, ar.`ItemID`, ar.`Sender` AS "sender", ar.`MailTemplateID`,
|
||||
ar.`Subject` AS "subject_loc0", IFNULL(arl2.`Subject`, "") AS "subject_loc2", IFNULL(arl3.`Subject`, "") AS "subject_loc3", IFNULL(arl4.`Subject`, "") AS "subject_loc4", IFNULL(arl6.`Subject`, "") AS "subject_loc6", IFNULL(arl8.`Subject`, "") AS "subject_loc8",
|
||||
ar.`Body` AS "text_loc0", IFNULL(arl2.`Body`, "") AS "text_loc2", IFNULL(arl3.`Body`, "") AS "text_loc3", IFNULL(arl4.`Body`, "") AS "text_loc4", IFNULL(arl6.`Body`, "") AS "text_loc6", IFNULL(arl8.`Body`, "") AS "text_loc8"
|
||||
FROM achievement_reward ar
|
||||
LEFT JOIN achievement_reward_locale arl2 ON arl2.`ID` = ar.`ID` AND arl2.`Locale` = "frFR"
|
||||
LEFT JOIN achievement_reward_locale arl3 ON arl3.`ID` = ar.`ID` AND arl3.`Locale` = "deDE"
|
||||
LEFT JOIN achievement_reward_locale arl4 ON arl4.`ID` = ar.`ID` AND arl4.`Locale` = "zhCN"
|
||||
LEFT JOIN achievement_reward_locale arl6 ON arl6.`ID` = ar.`ID` AND arl6.`Locale` = "esES"
|
||||
LEFT JOIN achievement_reward_locale arl8 ON arl8.`ID` = ar.`ID` AND arl8.`Locale` = "ruRU"
|
||||
WHERE ar.`ID` IN (?a)',
|
||||
$this->getFoundIDs()
|
||||
);
|
||||
|
||||
foreach ($this->iterate() as $_id => &$_curTpl)
|
||||
{
|
||||
$_curTpl['rewards'] = [];
|
||||
|
||||
if (!empty($rewards[$_id]))
|
||||
{
|
||||
$_curTpl = array_merge($rewards[$_id], $_curTpl);
|
||||
|
||||
$_curTpl['mailTemplate'] = $rewards[$_id]['MailTemplateID'];
|
||||
|
||||
if ($rewards[$_id]['MailTemplateID'])
|
||||
{
|
||||
// using class Loot creates an inifinite loop cirling between Loot, ItemList and SpellList or something
|
||||
// $mailSrc = new Loot();
|
||||
// $mailSrc->getByContainer(LOOT_MAIL, $rewards[$_id]['MailTemplateID']);
|
||||
// foreach ($mailSrc->iterate() as $loot)
|
||||
// $_curTpl['rewards'][] = [Type::ITEM, $loot['id']];
|
||||
|
||||
// lets just assume for now, that mailRewards for achievements do not contain references
|
||||
$mailRew = DB::World()->selectCol('SELECT `Item` FROM mail_loot_template WHERE `Reference` <= 0 AND `entry` = ?d', $rewards[$_id]['MailTemplateID']);
|
||||
foreach ($mailRew AS $mr)
|
||||
$_curTpl['rewards'][] = [Type::ITEM, $mr];
|
||||
}
|
||||
}
|
||||
|
||||
//"rewards":[[11,137],[3,138]] [type, typeId]
|
||||
if (!empty($_curTpl['ItemID']))
|
||||
$_curTpl['rewards'][] = [Type::ITEM, $_curTpl['ItemID']];
|
||||
if (!empty($_curTpl['itemExtra']))
|
||||
$_curTpl['rewards'][] = [Type::ITEM, $_curTpl['itemExtra']];
|
||||
if (!empty($_curTpl['TitleA']))
|
||||
$_curTpl['rewards'][] = [Type::TITLE, $_curTpl['TitleA']];
|
||||
if (!empty($_curTpl['TitleH']))
|
||||
if (empty($_curTpl['TitleA']) || $_curTpl['TitleA'] != $_curTpl['TitleH'])
|
||||
$_curTpl['rewards'][] = [Type::TITLE, $_curTpl['TitleH']];
|
||||
|
||||
// icon
|
||||
$_curTpl['iconString'] = $_curTpl['iconString'] ?: 'trade_engineering';
|
||||
}
|
||||
}
|
||||
|
||||
public function getJSGlobals(int $addMask = GLOBALINFO_ANY) : array
|
||||
{
|
||||
$data = [];
|
||||
|
||||
foreach ($this->iterate() as $__)
|
||||
{
|
||||
if ($addMask & GLOBALINFO_SELF)
|
||||
$data[Type::ACHIEVEMENT][$this->id] = ['icon' => $this->curTpl['iconString'], 'name' => $this->getField('name', true)];
|
||||
|
||||
if ($addMask & GLOBALINFO_REWARDS)
|
||||
foreach ($this->curTpl['rewards'] as $_)
|
||||
$data[$_[0]][$_[1]] = $_[1];
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
public function getListviewData(int $addInfoMask = 0x0) : array
|
||||
{
|
||||
$data = [];
|
||||
|
||||
foreach ($this->iterate() as $__)
|
||||
{
|
||||
$data[$this->id] = array(
|
||||
'id' => $this->id,
|
||||
'name' => $this->getField('name', true),
|
||||
'description' => $this->getField('description', true),
|
||||
'points' => $this->curTpl['points'],
|
||||
'side' => $this->curTpl['faction'],
|
||||
'category' => $this->curTpl['category'],
|
||||
'parentcat' => $this->curTpl['parentCat'],
|
||||
);
|
||||
|
||||
if ($addInfoMask & ACHIEVEMENTINFO_PROFILE)
|
||||
$data[$this->id]['icon'] = $this->curTpl['iconString'];
|
||||
|
||||
// going out on a limb here: type = 1 if in level 3 of statistics tree, so, IF (statistic AND parentCat NOT statistic (1)) i guess
|
||||
if ($this->curTpl['flags'] & ACHIEVEMENT_FLAG_COUNTER && $this->curTpl['parentCat'] != 1)
|
||||
$data[$this->id]['type'] = 1;
|
||||
|
||||
if ($_ = $this->curTpl['rewards'])
|
||||
$data[$this->id]['rewards'] = $_;
|
||||
else if ($_ = $this->getField('reward', true))
|
||||
$data[$this->id]['reward'] = $_;
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
// only for current template
|
||||
public function getCriteria() : array
|
||||
{
|
||||
if (isset($this->criteria[$this->id]))
|
||||
return $this->criteria[$this->id];
|
||||
|
||||
$result = DB::Aowow()->Select('SELECT * FROM ?_achievementcriteria WHERE `refAchievementId` = ?d ORDER BY `order` ASC', $this->curTpl['refAchievement'] ?: $this->id);
|
||||
if (!$result)
|
||||
return [];
|
||||
|
||||
$this->criteria[$this->id] = $result;
|
||||
|
||||
return $this->criteria[$this->id];
|
||||
}
|
||||
|
||||
public function renderTooltip() : ?string
|
||||
{
|
||||
$criteria = $this->getCriteria();
|
||||
$tmp = [];
|
||||
$rows = [];
|
||||
$i = 0;
|
||||
foreach ($criteria as $_row)
|
||||
{
|
||||
if ($i++ % 2)
|
||||
$tmp[] = $_row;
|
||||
else
|
||||
$rows[] = $_row;
|
||||
}
|
||||
if ($tmp)
|
||||
$rows = array_merge($rows, $tmp);
|
||||
|
||||
$description = $this->getField('description', true);
|
||||
$name = $this->getField('name', true);
|
||||
$criteria = '';
|
||||
|
||||
$i = 0;
|
||||
foreach ($rows as $crt)
|
||||
{
|
||||
$obj = (int)$crt['value1'];
|
||||
$qty = (int)$crt['value2'];
|
||||
|
||||
// we could show them, but the tooltips are cluttered
|
||||
if (($crt['completionFlags'] & ACHIEVEMENT_CRITERIA_FLAG_HIDDEN) && User::isInGroup(U_GROUP_STAFF))
|
||||
continue;
|
||||
|
||||
$crtName = Util::localizedString($crt, 'name');
|
||||
switch ($crt['type'])
|
||||
{
|
||||
// link to quest
|
||||
case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUEST:
|
||||
if (!$crtName)
|
||||
$crtName = QuestList::getName($obj);
|
||||
break;
|
||||
// link to spell (/w icon)
|
||||
case ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET:
|
||||
case ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET2:
|
||||
case ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL:
|
||||
case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SPELL:
|
||||
case ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL2:
|
||||
if (!$crtName)
|
||||
$crtName = SpellList::getName($obj);
|
||||
break;
|
||||
// link to item (/w icon)
|
||||
case ACHIEVEMENT_CRITERIA_TYPE_OWN_ITEM:
|
||||
case ACHIEVEMENT_CRITERIA_TYPE_USE_ITEM:
|
||||
case ACHIEVEMENT_CRITERIA_TYPE_LOOT_ITEM:
|
||||
case ACHIEVEMENT_CRITERIA_TYPE_EQUIP_ITEM:
|
||||
if (!$crtName)
|
||||
$crtName = ItemList::getName($obj);
|
||||
break;
|
||||
// link to faction (/w target reputation)
|
||||
case ACHIEVEMENT_CRITERIA_TYPE_GAIN_REPUTATION:
|
||||
if (!$crtName)
|
||||
$crtName = FactionList::getName($obj);
|
||||
break;
|
||||
}
|
||||
|
||||
$criteria .= '<!--cr'.$crt['id'].':'.$crt['type'].':'.$crt['value1'].'-->- '.$crtName;
|
||||
|
||||
if ($crt['completionFlags'] & ACHIEVEMENT_CRITERIA_FLAG_MONEY_COUNTER)
|
||||
$criteria .= ' <span class="moneygold">'.Lang::nf($crt['value2' ] / 10000).'</span>';
|
||||
|
||||
$criteria .= '<br />';
|
||||
|
||||
if (++$i == round(count($rows)/2))
|
||||
$criteria .= '</small></td><th class="q0" style="white-space: nowrap; text-align: left"><small>';
|
||||
}
|
||||
|
||||
$x = '<table><tr><td><b class="q">';
|
||||
$x .= $name;
|
||||
$x .= '</b></td></tr></table>';
|
||||
if ($description || $criteria)
|
||||
$x .= '<table><tr><td>';
|
||||
|
||||
if ($description)
|
||||
$x .= '<br />'.$description.'<br />';
|
||||
|
||||
if ($criteria)
|
||||
{
|
||||
$x .= '<br /><span class="q">'.Lang::achievement('criteria').':</span>';
|
||||
$x .= '<table width="100%"><tr><td class="q0" style="white-space: nowrap"><small>'.$criteria.'</small></th></tr></table>';
|
||||
}
|
||||
if ($description || $criteria)
|
||||
$x .= '</td></tr></table>';
|
||||
|
||||
return $x;
|
||||
}
|
||||
|
||||
public function getSourceData(int $id = 0) : array
|
||||
{
|
||||
$data = [];
|
||||
|
||||
foreach ($this->iterate() as $__)
|
||||
{
|
||||
if ($id && $id != $this->id)
|
||||
continue;
|
||||
|
||||
$data[$this->id] = array(
|
||||
"n" => $this->getField('name', true),
|
||||
"s" => $this->curTpl['faction'],
|
||||
"t" => Type::ACHIEVEMENT,
|
||||
"ti" => $this->id
|
||||
);
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class AchievementListFilter extends Filter
|
||||
{
|
||||
protected string $type = 'achievements';
|
||||
protected static array $enums = array(
|
||||
4 => parent::ENUM_ZONE, // location
|
||||
11 => array(
|
||||
327 => 160, // Lunar Festival
|
||||
423 => 187, // Love is in the Air
|
||||
181 => 159, // Noblegarden
|
||||
201 => 163, // Children's Week
|
||||
341 => 161, // Midsummer Fire Festival
|
||||
372 => 162, // Brewfest
|
||||
324 => 158, // Hallow's End
|
||||
404 => 14981, // Pilgrim's Bounty
|
||||
141 => 156, // Feast of Winter Veil
|
||||
409 => -3456, // Day of the Dead
|
||||
398 => -3457, // Pirates' Day
|
||||
parent::ENUM_ANY => true,
|
||||
parent::ENUM_NONE => false,
|
||||
283 => -1, // valid events without achievements
|
||||
285 => -1, 353 => -1, 420 => -1,
|
||||
400 => -1, 284 => -1, 374 => -1,
|
||||
321 => -1, 424 => -1, 301 => -1
|
||||
)
|
||||
);
|
||||
|
||||
protected static array $genericFilter = array(
|
||||
2 => [parent::CR_BOOLEAN, 'reward_loc0', true ], // givesreward
|
||||
3 => [parent::CR_STRING, 'reward', STR_LOCALIZED ], // rewardtext
|
||||
4 => [parent::CR_NYI_PH, null, 1, ], // location [enum]
|
||||
5 => [parent::CR_CALLBACK, 'cbSeries', ACHIEVEMENT_CU_FIRST_SERIES, null], // first in series [yn]
|
||||
6 => [parent::CR_CALLBACK, 'cbSeries', ACHIEVEMENT_CU_LAST_SERIES, null], // last in series [yn]
|
||||
7 => [parent::CR_BOOLEAN, 'chainId', ], // partseries
|
||||
9 => [parent::CR_NUMERIC, 'id', NUM_CAST_INT, true], // id
|
||||
10 => [parent::CR_STRING, 'ic.name', ], // icon
|
||||
11 => [parent::CR_CALLBACK, 'cbRelEvent', null, null], // related event [enum]
|
||||
14 => [parent::CR_FLAG, 'cuFlags', CUSTOM_HAS_COMMENT ], // hascomments
|
||||
15 => [parent::CR_FLAG, 'cuFlags', CUSTOM_HAS_SCREENSHOT ], // hasscreenshots
|
||||
16 => [parent::CR_FLAG, 'cuFlags', CUSTOM_HAS_VIDEO ], // hasvideos
|
||||
18 => [parent::CR_STAFFFLAG, 'flags', ] // flags
|
||||
);
|
||||
|
||||
protected static array $inputFields = array(
|
||||
'cr' => [parent::V_RANGE, [2, 18], true ], // criteria ids
|
||||
'crs' => [parent::V_LIST, [parent::ENUM_NONE, parent::ENUM_ANY, [0, 99999]], true ], // criteria operators
|
||||
'crv' => [parent::V_REGEX, parent::PATTERN_CRV, true ], // criteria values - only printable chars, no delimiters
|
||||
'na' => [parent::V_REGEX, parent::PATTERN_NAME, false], // name / description - only printable chars, no delimiter
|
||||
'ex' => [parent::V_EQUAL, 'on', false], // extended name search
|
||||
'ma' => [parent::V_EQUAL, 1, false], // match any / all filter
|
||||
'si' => [parent::V_LIST, [SIDE_ALLIANCE, SIDE_HORDE, SIDE_BOTH, -SIDE_ALLIANCE, -SIDE_HORDE], false], // side
|
||||
'minpt' => [parent::V_RANGE, [1, 99], false], // required level min
|
||||
'maxpt' => [parent::V_RANGE, [1, 99], false] // required level max
|
||||
);
|
||||
|
||||
protected function createSQLForValues() : array
|
||||
{
|
||||
$parts = [];
|
||||
$_v = &$this->values;
|
||||
|
||||
// name ex: +description, +rewards
|
||||
if ($_v['na'])
|
||||
{
|
||||
$_ = [];
|
||||
if ($_v['ex'] == 'on')
|
||||
$_ = $this->tokenizeString(['name_loc'.Lang::getLocale()->value, 'reward_loc'.Lang::getLocale()->value, 'description_loc'.Lang::getLocale()->value]);
|
||||
else
|
||||
$_ = $this->tokenizeString(['name_loc'.Lang::getLocale()->value]);
|
||||
|
||||
if ($_)
|
||||
$parts[] = $_;
|
||||
}
|
||||
|
||||
// points min
|
||||
if ($_v['minpt'])
|
||||
$parts[] = ['points', $_v['minpt'], '>='];
|
||||
|
||||
// points max
|
||||
if ($_v['maxpt'])
|
||||
$parts[] = ['points', $_v['maxpt'], '<='];
|
||||
|
||||
// faction (side)
|
||||
if ($_v['si'])
|
||||
{
|
||||
$parts[] = match ($_v['si'])
|
||||
{
|
||||
-SIDE_ALLIANCE, // equals faction
|
||||
-SIDE_HORDE => ['faction', -$_v['si']],
|
||||
SIDE_ALLIANCE, // includes faction
|
||||
SIDE_HORDE,
|
||||
SIDE_BOTH => ['faction', $_v['si'], '&']
|
||||
};
|
||||
}
|
||||
|
||||
return $parts;
|
||||
}
|
||||
|
||||
protected function cbRelEvent(int $cr, int $crs, string $crv) : ?array
|
||||
{
|
||||
if (!isset(self::$enums[$cr][$crs]))
|
||||
return null;
|
||||
|
||||
$_ = self::$enums[$cr][$crs];
|
||||
if (is_int($_))
|
||||
return ($_ > 0) ? ['category', $_] : ['id', abs($_)];
|
||||
else
|
||||
{
|
||||
$ids = array_filter(self::$enums[$cr], fn($x) => is_int($x) && $x > 0);
|
||||
|
||||
return ['category', $ids, $_ ? null : '!'];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
protected function cbSeries(int $cr, int $crs, string $crv, int $seriesFlag) : ?array
|
||||
{
|
||||
if ($this->int2Bool($crs))
|
||||
return $crs ? ['AND', ['chainId', 0, '!'], ['cuFlags', $seriesFlag, '&']] : ['AND', ['chainId', 0, '!'], [['cuFlags', $seriesFlag, '&'], 0]];
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
99
includes/dbtypes/areatrigger.class.php
Normal file
99
includes/dbtypes/areatrigger.class.php
Normal file
@@ -0,0 +1,99 @@
|
||||
<?php
|
||||
|
||||
namespace Aowow;
|
||||
|
||||
if (!defined('AOWOW_REVISION'))
|
||||
die('illegal access');
|
||||
|
||||
|
||||
class AreaTriggerList extends DBTypeList
|
||||
{
|
||||
use spawnHelper;
|
||||
|
||||
public static int $type = Type::AREATRIGGER;
|
||||
public static string $brickFile = 'areatrigger';
|
||||
public static string $dataTable = '?_areatrigger';
|
||||
public static int $contribute = CONTRIBUTE_CO;
|
||||
|
||||
protected string $queryBase = 'SELECT a.*, a.id AS ARRAY_KEY FROM ?_areatrigger a';
|
||||
protected array $queryOpts = array(
|
||||
'a' => [['s']], // guid < 0 are teleporter targets, so exclude them here
|
||||
's' => ['j' => ['?_spawns s ON s.`type` = 503 AND s.`typeId` = a.`id` AND s.`guid` > 0', true], 's' => ', GROUP_CONCAT(s.`areaId`) AS "areaId"', 'g' => 'a.`id`']
|
||||
);
|
||||
|
||||
public function __construct(array $conditions = [], array $miscData = [])
|
||||
{
|
||||
parent::__construct($conditions, $miscData);
|
||||
|
||||
foreach ($this->iterate() as $id => &$_curTpl)
|
||||
if (!$_curTpl['name'])
|
||||
$_curTpl['name'] = 'Unnamed Areatrigger #' . $id;
|
||||
}
|
||||
|
||||
public static function getName(int $id) : ?LocString
|
||||
{
|
||||
if ($n = DB::Aowow()->SelectRow('SELECT IF(`name`, `name`, CONCAT("Unnamed Areatrigger #", `id`) AS "name_loc0" FROM ?# WHERE `id` = ?d', self::$dataTable, $id))
|
||||
return new LocString($n);
|
||||
return null;
|
||||
}
|
||||
|
||||
public function getListviewData() : array
|
||||
{
|
||||
$data = [];
|
||||
|
||||
foreach ($this->iterate() as $__)
|
||||
{
|
||||
$data[$this->id] = array(
|
||||
'id' => $this->curTpl['id'],
|
||||
'type' => $this->curTpl['type'],
|
||||
'name' => $this->curTpl['name'],
|
||||
);
|
||||
|
||||
if ($_ = $this->curTpl['areaId'])
|
||||
$data[$this->id]['location'] = explode(',', $_);
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
public function getJSGlobals(int $addMask = GLOBALINFO_ANY) : array { return []; }
|
||||
|
||||
public function renderTooltip() : ?string { return null; }
|
||||
}
|
||||
|
||||
class AreaTriggerListFilter extends Filter
|
||||
{
|
||||
protected string $type = 'areatrigger';
|
||||
protected static array $genericFilter = array(
|
||||
2 => [parent::CR_NUMERIC, 'id', NUM_CAST_INT] // id
|
||||
);
|
||||
|
||||
// fieldId => [checkType, checkValue[, fieldIsArray]]
|
||||
protected static array $inputFields = array(
|
||||
'cr' => [parent::V_LIST, [2], true ], // criteria ids
|
||||
'crs' => [parent::V_RANGE, [1, 6], true ], // criteria operators
|
||||
'crv' => [parent::V_REGEX, parent::PATTERN_INT, true ], // criteria values - all criteria are numeric here
|
||||
'na' => [parent::V_REGEX, parent::PATTERN_NAME, false], // name - only printable chars, no delimiter
|
||||
'ma' => [parent::V_EQUAL, 1, false], // match any / all filter
|
||||
'ty' => [parent::V_RANGE, [0, 5], true ] // types
|
||||
);
|
||||
|
||||
protected function createSQLForValues() : array
|
||||
{
|
||||
$parts = [];
|
||||
$_v = &$this->values;
|
||||
|
||||
// name [str]
|
||||
if ($_v['na'])
|
||||
if ($_ = $this->tokenizeString(['name']))
|
||||
$parts[] = $_;
|
||||
|
||||
// type [list]
|
||||
if ($_v['ty'])
|
||||
$parts[] = ['type', $_v['ty']];
|
||||
|
||||
return $parts;
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
374
includes/dbtypes/arenateam.class.php
Normal file
374
includes/dbtypes/arenateam.class.php
Normal file
@@ -0,0 +1,374 @@
|
||||
<?php
|
||||
|
||||
namespace Aowow;
|
||||
|
||||
if (!defined('AOWOW_REVISION'))
|
||||
die('illegal access');
|
||||
|
||||
|
||||
class ArenaTeamList extends DBTypeList
|
||||
{
|
||||
use profilerHelper, listviewHelper;
|
||||
|
||||
public static int $contribute = CONTRIBUTE_NONE;
|
||||
|
||||
public function getListviewData() : array
|
||||
{
|
||||
$data = [];
|
||||
foreach ($this->iterate() as $__)
|
||||
{
|
||||
$data[$this->id] = array(
|
||||
'name' => $this->curTpl['name'],
|
||||
'realm' => Profiler::urlize($this->curTpl['realmName'], true),
|
||||
'realmname' => $this->curTpl['realmName'],
|
||||
// 'battlegroup' => Profiler::urlize($this->curTpl['battlegroup']), // was renamed to subregion somewhere around cata release
|
||||
// 'battlegroupname' => $this->curTpl['battlegroup'],
|
||||
'region' => Profiler::urlize($this->curTpl['region']),
|
||||
'faction' => $this->curTpl['faction'],
|
||||
'size' => $this->curTpl['type'],
|
||||
'rank' => $this->curTpl['rank'],
|
||||
'wins' => $this->curTpl['seasonWins'],
|
||||
'games' => $this->curTpl['seasonGames'],
|
||||
'rating' => $this->curTpl['rating'],
|
||||
'members' => $this->curTpl['members']
|
||||
);
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
// plz dont..
|
||||
public static function getName(int|string $id) : ?LocString { return null; }
|
||||
|
||||
public function renderTooltip() : ?string { return null; }
|
||||
public function getJSGlobals(int $addMask = 0) : array { return []; }
|
||||
}
|
||||
|
||||
|
||||
class ArenaTeamListFilter extends Filter
|
||||
{
|
||||
use TrProfilerFilter;
|
||||
|
||||
protected string $type = 'arenateams';
|
||||
protected static array $genericFilter = [];
|
||||
protected static array $inputFields = array(
|
||||
'na' => [parent::V_REGEX, parent::PATTERN_NAME, false], // name - only printable chars, no delimiter
|
||||
'ma' => [parent::V_EQUAL, 1, false], // match any / all filter
|
||||
'ex' => [parent::V_EQUAL, 'on', false], // only match exact
|
||||
'si' => [parent::V_LIST, [1, 2], false], // side
|
||||
'sz' => [parent::V_LIST, [2, 3, 5], false], // tema size
|
||||
'rg' => [parent::V_CALLBACK, 'cbRegionCheck', false], // region
|
||||
'sv' => [parent::V_CALLBACK, 'cbServerCheck', false], // server
|
||||
);
|
||||
|
||||
public array $extraOpts = [];
|
||||
|
||||
protected function createSQLForValues() : array
|
||||
{
|
||||
$parts = [];
|
||||
$_v = $this->values;
|
||||
|
||||
// region (rg), battlegroup (bg) and server (sv) are passed to ArenaTeamList as miscData and handled there
|
||||
|
||||
// name [str]
|
||||
if ($_v['na'])
|
||||
if ($_ = $this->tokenizeString(['at.name'], $_v['na'], $_v['ex'] == 'on'))
|
||||
$parts[] = $_;
|
||||
|
||||
// side [list]
|
||||
if ($_v['si'] == SIDE_ALLIANCE)
|
||||
$parts[] = ['c.race', ChrRace::fromMask(ChrRace::MASK_ALLIANCE)];
|
||||
else if ($_v['si'] == SIDE_HORDE)
|
||||
$parts[] = ['c.race', ChrRace::fromMask(ChrRace::MASK_HORDE)];
|
||||
|
||||
// size [int]
|
||||
if ($_v['sz'])
|
||||
$parts[] = ['at.type', $_v['sz']];
|
||||
|
||||
return $parts;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class RemoteArenaTeamList extends ArenaTeamList
|
||||
{
|
||||
protected string $queryBase = 'SELECT `at`.*, `at`.`arenaTeamId` AS ARRAY_KEY FROM arena_team at';
|
||||
protected array $queryOpts = array(
|
||||
'at' => [['atm', 'c'], 'g' => 'ARRAY_KEY', 'o' => 'rating DESC'],
|
||||
'atm' => ['j' => 'arena_team_member atm ON atm.`arenaTeamId` = at.`arenaTeamId`'],
|
||||
'c' => ['j' => 'characters c ON c.`guid` = atm.`guid` AND c.`deleteInfos_Account` IS NULL AND c.`level` <= 80 AND (c.`extra_flags` & '.Profiler::CHAR_GMFLAGS.') = 0', 's' => ', BIT_OR(IF(c.`race` IN (1, 3, 4, 7, 11), 1, 2)) - 1 AS "faction"']
|
||||
);
|
||||
|
||||
private array $members = [];
|
||||
private array $rankOrder = [];
|
||||
|
||||
public function __construct(array $conditions = [], array $miscData = [])
|
||||
{
|
||||
// select DB by realm
|
||||
if (!$this->selectRealms($miscData))
|
||||
{
|
||||
trigger_error('RemoteArenaTeamList::__construct - cannot access any realm.', E_USER_WARNING);
|
||||
return;
|
||||
}
|
||||
|
||||
parent::__construct($conditions, $miscData);
|
||||
|
||||
if ($this->error)
|
||||
return;
|
||||
|
||||
// ranks in DB are inaccurate. recalculate from rating (fetched as DESC from DB)
|
||||
foreach ($this->dbNames as $rId => $__)
|
||||
foreach ([2, 3, 5] as $type)
|
||||
$this->rankOrder[$rId][$type] = DB::Characters($rId)->selectCol('SELECT `arenaTeamId` FROM arena_team WHERE `type` = ?d ORDER BY `rating` DESC', $type);
|
||||
|
||||
reset($this->dbNames); // only use when querying single realm
|
||||
$realms = Profiler::getRealms();
|
||||
$distrib = [];
|
||||
|
||||
// post processing
|
||||
foreach ($this->iterate() as $guid => &$curTpl)
|
||||
{
|
||||
// battlegroup
|
||||
$curTpl['battlegroup'] = Cfg::get('BATTLEGROUP');
|
||||
|
||||
// realm, rank
|
||||
$r = explode(':', $guid);
|
||||
if (!empty($realms[$r[0]]))
|
||||
{
|
||||
$curTpl['realm'] = $r[0];
|
||||
$curTpl['realmName'] = $realms[$r[0]]['name'];
|
||||
$curTpl['region'] = $realms[$r[0]]['region'];
|
||||
$curTpl['rank'] = array_search($curTpl['arenaTeamId'], $this->rankOrder[$r[0]][$curTpl['type']]) + 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
trigger_error('arena team #'.$guid.' belongs to nonexistent realm #'.$r, E_USER_WARNING);
|
||||
unset($this->templates[$guid]);
|
||||
continue;
|
||||
}
|
||||
|
||||
// empty name
|
||||
if (!$curTpl['name'])
|
||||
{
|
||||
trigger_error('arena team #'.$guid.' on realm #'.$r.' has empty name.', E_USER_WARNING);
|
||||
unset($this->templates[$guid]);
|
||||
continue;
|
||||
}
|
||||
|
||||
// team members
|
||||
$this->members[$r[0]][$r[1]] = $r[1];
|
||||
|
||||
// equalize distribution
|
||||
if (empty($distrib[$curTpl['realm']]))
|
||||
$distrib[$curTpl['realm']] = 1;
|
||||
else
|
||||
$distrib[$curTpl['realm']]++;
|
||||
}
|
||||
|
||||
// get team members
|
||||
foreach ($this->members as $realmId => &$teams)
|
||||
$teams = DB::Characters($realmId)->select(
|
||||
'SELECT at.`arenaTeamId` AS ARRAY_KEY, c.`guid` AS ARRAY_KEY2, c.`name` AS "0", c.`class` AS "1", IF(at.`captainguid` = c.`guid`, 1, 0) AS "2"
|
||||
FROM arena_team at
|
||||
JOIN arena_team_member atm ON atm.`arenaTeamId` = at.`arenaTeamId` JOIN characters c ON c.`guid` = atm.`guid`
|
||||
WHERE at.`arenaTeamId` IN (?a) AND c.`deleteInfos_Account` IS NULL AND c.`level` <= ?d AND (c.`extra_flags` & ?d) = 0',
|
||||
$teams, MAX_LEVEL, Profiler::CHAR_GMFLAGS
|
||||
);
|
||||
|
||||
// equalize subject distribution across realms
|
||||
foreach ($conditions as $c)
|
||||
if (is_int($c))
|
||||
$limit = $c;
|
||||
|
||||
$limit ??= Cfg::get('SQL_LIMIT_DEFAULT');
|
||||
if (!$limit) // int:0 means unlimited, so skip early
|
||||
return;
|
||||
|
||||
$total = array_sum($distrib);
|
||||
foreach ($distrib as &$d)
|
||||
$d = ceil($limit * $d / $total);
|
||||
|
||||
foreach ($this->iterate() as $guid => &$curTpl)
|
||||
{
|
||||
if ($limit <= 0 || $distrib[$curTpl['realm']] <= 0)
|
||||
{
|
||||
unset($this->templates[$guid]);
|
||||
continue;
|
||||
}
|
||||
|
||||
$r = explode(':', $guid);
|
||||
if (isset($this->members[$r[0]][$r[1]]))
|
||||
$curTpl['members'] = array_values($this->members[$r[0]][$r[1]]); // [name, classId, isCaptain]
|
||||
|
||||
$distrib[$curTpl['realm']]--;
|
||||
$limit--;
|
||||
}
|
||||
}
|
||||
|
||||
public function initializeLocalEntries() : void
|
||||
{
|
||||
if (!$this->templates)
|
||||
return;
|
||||
|
||||
$profiles = [];
|
||||
// init members for tooltips
|
||||
foreach ($this->members as $realmId => $teams)
|
||||
{
|
||||
$gladiators = [];
|
||||
foreach ($teams as $team)
|
||||
$gladiators = array_merge($gladiators, array_keys($team));
|
||||
|
||||
$profiles[$realmId] = new RemoteProfileList(array(['c.guid', $gladiators], Cfg::get('SQL_LIMIT_NONE')), ['sv' => $realmId]);
|
||||
|
||||
if (!$profiles[$realmId]->error)
|
||||
$profiles[$realmId]->initializeLocalEntries();
|
||||
}
|
||||
|
||||
$data = [];
|
||||
foreach ($this->iterate() as $guid => $__)
|
||||
{
|
||||
$data[$guid] = array(
|
||||
'realm' => $this->getField('realm'),
|
||||
'realmGUID' => $this->getField('arenaTeamId'),
|
||||
'name' => $this->getField('name'),
|
||||
'nameUrl' => Profiler::urlize($this->getField('name')),
|
||||
'type' => $this->getField('type'),
|
||||
'rating' => $this->getField('rating'),
|
||||
'cuFlags' => PROFILER_CU_NEEDS_RESYNC
|
||||
);
|
||||
}
|
||||
|
||||
// basic arena team data
|
||||
foreach (Util::createSqlBatchInsert($data) as $ins)
|
||||
DB::Aowow()->query('INSERT INTO ?_profiler_arena_team (?#) VALUES '.$ins.' ON DUPLICATE KEY UPDATE `id` = `id`', array_keys(reset($data)));
|
||||
|
||||
// merge back local ids
|
||||
$localIds = DB::Aowow()->selectCol(
|
||||
'SELECT CONCAT(`realm`, ":", `realmGUID`) AS ARRAY_KEY, `id` FROM ?_profiler_arena_team WHERE `realm` IN (?a) AND `realmGUID` IN (?a)',
|
||||
array_column($data, 'realm'),
|
||||
array_column($data, 'realmGUID')
|
||||
);
|
||||
|
||||
foreach ($this->iterate() as $guid => &$_curTpl)
|
||||
if (isset($localIds[$guid]))
|
||||
$_curTpl['id'] = $localIds[$guid];
|
||||
|
||||
|
||||
// profiler_arena_team_member requires profiles and arena teams to be filled
|
||||
foreach ($this->members as $realmId => $teams)
|
||||
{
|
||||
if (empty($profiles[$realmId]))
|
||||
continue;
|
||||
|
||||
$memberData = [];
|
||||
foreach ($teams as $teamId => $team)
|
||||
{
|
||||
$clearMembers = [];
|
||||
foreach ($team as $memberId => $member)
|
||||
{
|
||||
$clearMembers[] = $profiles[$realmId]->getEntry($realmId.':'.$memberId)['id'];
|
||||
$memberData[] = array(
|
||||
'arenaTeamId' => $localIds[$realmId.':'.$teamId],
|
||||
'profileId' => $profiles[$realmId]->getEntry($realmId.':'.$memberId)['id'],
|
||||
'captain' => $member[2]
|
||||
);
|
||||
}
|
||||
|
||||
// Delete members from other teams of the same type
|
||||
DB::Aowow()->query(
|
||||
'DELETE atm
|
||||
FROM ?_profiler_arena_team_member atm
|
||||
JOIN ?_profiler_arena_team at ON atm.`arenaTeamId` = at.`id` AND at.`type` = ?d
|
||||
WHERE atm.`profileId` IN (?a)',
|
||||
$data[$realmId.':'.$teamId]['type'] ?? 0,
|
||||
$clearMembers
|
||||
);
|
||||
}
|
||||
|
||||
foreach (Util::createSqlBatchInsert($memberData) as $ins)
|
||||
DB::Aowow()->query('INSERT INTO ?_profiler_arena_team_member (?#) VALUES '.$ins.' ON DUPLICATE KEY UPDATE `profileId` = `profileId`', array_keys(reset($memberData)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class LocalArenaTeamList extends ArenaTeamList
|
||||
{
|
||||
protected string $queryBase = 'SELECT at.*, at.id AS ARRAY_KEY FROM ?_profiler_arena_team at';
|
||||
protected array $queryOpts = array(
|
||||
'at' => [['atm', 'c'], 'g' => 'ARRAY_KEY', 'o' => 'rating DESC'],
|
||||
'atm' => ['j' => '?_profiler_arena_team_member atm ON atm.`arenaTeamId` = at.`id`'],
|
||||
'c' => ['j' => '?_profiler_profiles c ON c.`id` = atm.`profileId`', 's' => ', BIT_OR(IF(c.`race` IN (1, 3, 4, 7, 11), 1, 2)) - 1 AS "faction"']
|
||||
);
|
||||
|
||||
public function __construct(array $conditions = [], array $miscData = [])
|
||||
{
|
||||
$realms = Profiler::getRealms();
|
||||
|
||||
// graft realm selection from miscData onto conditions
|
||||
if (isset($miscData['sv']))
|
||||
$realms = array_filter($realms, fn($x) => Profiler::urlize($x['name']) == Profiler::urlize($miscData['sv']));
|
||||
|
||||
if (isset($miscData['rg']))
|
||||
$realms = array_filter($realms, fn($x) => $x['region'] == $miscData['rg']);
|
||||
|
||||
if (!$realms)
|
||||
{
|
||||
trigger_error('LocalArenaTeamList::__construct - cannot access any realm.', E_USER_WARNING);
|
||||
return;
|
||||
}
|
||||
|
||||
if ($conditions)
|
||||
{
|
||||
array_unshift($conditions, 'AND');
|
||||
$conditions = ['AND', ['realm', array_keys($realms)], $conditions];
|
||||
}
|
||||
else
|
||||
$conditions = [['realm', array_keys($realms)]];
|
||||
|
||||
parent::__construct($conditions, $miscData);
|
||||
|
||||
if ($this->error)
|
||||
return;
|
||||
|
||||
// post processing
|
||||
$members = DB::Aowow()->select(
|
||||
'SELECT `arenaTeamId` AS ARRAY_KEY, p.`id` AS ARRAY_KEY2, p.`name` AS "0", p.`class` AS "1", atm.`captain` AS "2"
|
||||
FROM ?_profiler_arena_team_member atm
|
||||
JOIN ?_profiler_profiles p ON p.`id` = atm.`profileId`
|
||||
WHERE `arenaTeamId` IN (?a)',
|
||||
$this->getFoundIDs()
|
||||
);
|
||||
|
||||
foreach ($this->iterate() as $id => &$curTpl)
|
||||
{
|
||||
if ($curTpl['realm'] && !isset($realms[$curTpl['realm']]))
|
||||
continue;
|
||||
|
||||
if (isset($realms[$curTpl['realm']]))
|
||||
{
|
||||
$curTpl['realmName'] = $realms[$curTpl['realm']]['name'];
|
||||
$curTpl['region'] = $realms[$curTpl['realm']]['region'];
|
||||
}
|
||||
|
||||
// battlegroup
|
||||
$curTpl['battlegroup'] = Cfg::get('BATTLEGROUP');
|
||||
|
||||
$curTpl['members'] = array_values($members[$id]);
|
||||
}
|
||||
}
|
||||
|
||||
public function getProfileUrl() : string
|
||||
{
|
||||
$url = '?arena-team=';
|
||||
|
||||
return $url.implode('.', array(
|
||||
$this->getField('region'),
|
||||
Profiler::urlize($this->getField('realmName'), true),
|
||||
Profiler::urlize($this->getField('name'))
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
?>
|
||||
64
includes/dbtypes/charclass.class.php
Normal file
64
includes/dbtypes/charclass.class.php
Normal file
@@ -0,0 +1,64 @@
|
||||
<?php
|
||||
|
||||
namespace Aowow;
|
||||
|
||||
if (!defined('AOWOW_REVISION'))
|
||||
die('illegal access');
|
||||
|
||||
|
||||
class CharClassList extends DBTypeList
|
||||
{
|
||||
public static int $type = Type::CHR_CLASS;
|
||||
public static string $brickFile = 'class';
|
||||
public static string $dataTable = '?_classes';
|
||||
|
||||
protected string $queryBase = 'SELECT c.*, id AS ARRAY_KEY FROM ?_classes c';
|
||||
|
||||
public function __construct($conditions = [], array $miscData = [])
|
||||
{
|
||||
parent::__construct($conditions, $miscData);
|
||||
|
||||
foreach ($this->iterate() as $k => &$_curTpl)
|
||||
$_curTpl['skills'] = explode(' ', $_curTpl['skills']);
|
||||
}
|
||||
|
||||
public function getListviewData() : array
|
||||
{
|
||||
$data = [];
|
||||
|
||||
foreach ($this->iterate() as $__)
|
||||
{
|
||||
$data[$this->id] = array(
|
||||
'id' => $this->id,
|
||||
'name' => $this->getField('name', true),
|
||||
'races' => $this->curTpl['raceMask'],
|
||||
'roles' => $this->curTpl['roles'],
|
||||
'weapon' => $this->curTpl['weaponTypeMask'],
|
||||
'armor' => $this->curTpl['armorTypeMask'],
|
||||
'power' => $this->curTpl['powerType'],
|
||||
);
|
||||
|
||||
if ($this->curTpl['flags'] & 0x40)
|
||||
$data[$this->id]['hero'] = 1;
|
||||
|
||||
if ($this->curTpl['expansion'])
|
||||
$data[$this->id]['expansion'] = $this->curTpl['expansion'];
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
public function getJSGlobals(int $addMask = 0) : array
|
||||
{
|
||||
$data = [];
|
||||
|
||||
foreach ($this->iterate() as $__)
|
||||
$data[self::$type][$this->id] = ['name' => $this->getField('name', true)];
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
public function renderTooltip() : ?string { return null; }
|
||||
}
|
||||
|
||||
?>
|
||||
53
includes/dbtypes/charrace.class.php
Normal file
53
includes/dbtypes/charrace.class.php
Normal file
@@ -0,0 +1,53 @@
|
||||
<?php
|
||||
|
||||
namespace Aowow;
|
||||
|
||||
if (!defined('AOWOW_REVISION'))
|
||||
die('illegal access');
|
||||
|
||||
|
||||
class CharRaceList extends DBTypeList
|
||||
{
|
||||
public static int $type = Type::CHR_RACE;
|
||||
public static string $brickFile = 'race';
|
||||
public static string $dataTable = '?_races';
|
||||
|
||||
protected string $queryBase = 'SELECT r.*, id AS ARRAY_KEY FROM ?_races r';
|
||||
|
||||
public function getListviewData() : array
|
||||
{
|
||||
$data = [];
|
||||
|
||||
foreach ($this->iterate() as $__)
|
||||
{
|
||||
$data[$this->id] = array(
|
||||
'id' => $this->id,
|
||||
'name' => $this->getField('name', true),
|
||||
'classes' => $this->curTpl['classMask'],
|
||||
'faction' => $this->curTpl['factionId'],
|
||||
'leader' => $this->curTpl['leader'],
|
||||
'zone' => $this->curTpl['startAreaId'],
|
||||
'side' => $this->curTpl['side']
|
||||
);
|
||||
|
||||
if ($this->curTpl['expansion'])
|
||||
$data[$this->id]['expansion'] = $this->curTpl['expansion'];
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
public function getJSGlobals(int $addMask = 0) : array
|
||||
{
|
||||
$data = [];
|
||||
|
||||
foreach ($this->iterate() as $__)
|
||||
$data[Type::CHR_RACE][$this->id] = ['name' => $this->getField('name', true)];
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
public function renderTooltip() : ?string { return null; }
|
||||
}
|
||||
|
||||
?>
|
||||
538
includes/dbtypes/creature.class.php
Normal file
538
includes/dbtypes/creature.class.php
Normal file
@@ -0,0 +1,538 @@
|
||||
<?php
|
||||
|
||||
namespace Aowow;
|
||||
|
||||
if (!defined('AOWOW_REVISION'))
|
||||
die('illegal access');
|
||||
|
||||
|
||||
class CreatureList extends DBTypeList
|
||||
{
|
||||
use spawnHelper;
|
||||
|
||||
public static int $type = Type::NPC;
|
||||
public static string $brickFile = 'npc';
|
||||
public static string $dataTable = '?_creature';
|
||||
|
||||
protected string $queryBase = 'SELECT ct.*, ct.`id` AS ARRAY_KEY FROM ?_creature ct';
|
||||
public array $queryOpts = array(
|
||||
'ct' => [['ft', 'qse', 'dct1', 'dct2', 'dct3'], 's' => ', IFNULL(dct1.`id`, IFNULL(dct2.`id`, IFNULL(dct3.`id`, 0))) AS "parentId", IFNULL(dct1.`name_loc0`, IFNULL(dct2.`name_loc0`, IFNULL(dct3.`name_loc0`, ""))) AS "parent_loc0", IFNULL(dct1.`name_loc2`, IFNULL(dct2.`name_loc2`, IFNULL(dct3.`name_loc2`, ""))) AS "parent_loc2", IFNULL(dct1.`name_loc3`, IFNULL(dct2.`name_loc3`, IFNULL(dct3.`name_loc3`, ""))) AS "parent_loc3", IFNULL(dct1.`name_loc4`, IFNULL(dct2.`name_loc4`, IFNULL(dct3.`name_loc4`, ""))) AS "`parent_loc4`", IFNULL(dct1.`name_loc6`, IFNULL(dct2.`name_loc6`, IFNULL(dct3.`name_loc6`, ""))) AS "`parent_loc6`", IFNULL(dct1.name_loc8, IFNULL(dct2.`name_loc8`, IFNULL(dct3.`name_loc8`, ""))) AS "parent_loc8", IF(dct1.`difficultyEntry1` = ct.`id`, 1, IF(dct2.`difficultyEntry2` = ct.`id`, 2, IF(dct3.`difficultyEntry3` = ct.`id`, 3, 0))) AS "difficultyMode"'],
|
||||
'dct1' => ['j' => ['?_creature dct1 ON ct.`cuFlags` & 0x02 AND dct1.`difficultyEntry1` = ct.`id`', true]],
|
||||
'dct2' => ['j' => ['?_creature dct2 ON ct.`cuFlags` & 0x02 AND dct2.`difficultyEntry2` = ct.`id`', true]],
|
||||
'dct3' => ['j' => ['?_creature dct3 ON ct.`cuFlags` & 0x02 AND dct3.`difficultyEntry3` = ct.`id`', true]],
|
||||
'ft' => ['j' => '?_factiontemplate ft ON ft.`id` = ct.`faction`', 's' => ', ft.`factionId`, IFNULL(ft.`A`, 0) AS "A", IFNULL(ft.`H`, 0) AS "H"'],
|
||||
'qse' => ['j' => ['?_quests_startend qse ON qse.`type` = 1 AND qse.`typeId` = ct.id', true], 's' => ', IF(MIN(qse.`method`) = 1 OR MAX(qse.`method`) = 3, 1, 0) AS "startsQuests", IF(MIN(qse.`method`) = 2 OR MAX(qse.`method`) = 3, 1, 0) AS "endsQuests"', 'g' => 'ct.`id`'],
|
||||
'qt' => ['j' => '?_quests qt ON qse.`questId` = qt.`id`'],
|
||||
's' => ['j' => ['?_spawns s ON s.`type` = 1 AND s.`typeId` = ct.`id`', true]]
|
||||
);
|
||||
|
||||
public function __construct(array $conditions = [], array $miscData = [])
|
||||
{
|
||||
parent::__construct($conditions, $miscData);
|
||||
|
||||
if ($this->error)
|
||||
return;
|
||||
|
||||
// post processing
|
||||
foreach ($this->iterate() as $_id => &$curTpl)
|
||||
{
|
||||
// check for attackspeeds
|
||||
if (!$curTpl['atkSpeed'])
|
||||
$curTpl['atkSpeed'] = 2.0;
|
||||
else
|
||||
$curTpl['atkSpeed'] /= 1000;
|
||||
|
||||
if (!$curTpl['rngAtkSpeed'])
|
||||
$curTpl['rngAtkSpeed'] = 2.0;
|
||||
else
|
||||
$curTpl['rngAtkSpeed'] /= 1000;
|
||||
}
|
||||
}
|
||||
|
||||
public function renderTooltip() : ?string
|
||||
{
|
||||
if (!$this->curTpl)
|
||||
return null;
|
||||
|
||||
$level = '??';
|
||||
$type = $this->curTpl['type'];
|
||||
$row3 = [Lang::game('level')];
|
||||
$fam = $this->curTpl['family'];
|
||||
|
||||
if (!($this->curTpl['typeFlags'] & 0x4))
|
||||
{
|
||||
$level = $this->curTpl['minLevel'];
|
||||
if ($level != $this->curTpl['maxLevel'])
|
||||
$level .= ' - '.$this->curTpl['maxLevel'];
|
||||
}
|
||||
else
|
||||
$level = '??';
|
||||
|
||||
$row3[] = $level;
|
||||
|
||||
if ($type)
|
||||
$row3[] = Lang::game('ct', $type);
|
||||
|
||||
if ($_ = Lang::npc('rank', $this->curTpl['rank']))
|
||||
$row3[] = '('.$_.')';
|
||||
|
||||
$x = '<table>';
|
||||
$x .= '<tr><td><b class="q">'.Util::htmlEscape($this->getField('name', true)).'</b></td></tr>';
|
||||
|
||||
if ($sn = $this->getField('subname', true))
|
||||
$x .= '<tr><td>'.Util::htmlEscape($sn).'</td></tr>';
|
||||
|
||||
$x .= '<tr><td>'.implode(' ', $row3).'</td></tr>';
|
||||
|
||||
if ($type == 1 && $fam) // 1: Beast
|
||||
$x .= '<tr><td>'.Lang::game('fa', $fam).'</td></tr>';
|
||||
|
||||
$fac = new FactionList(array([['cuFlags', CUSTOM_EXCLUDE_FOR_LISTVIEW, '&'], 0], ['id', (int)$this->getField('factionId')]));
|
||||
if (!$fac->error)
|
||||
$x .= '<tr><td>'.$fac->getField('name', true).'</td></tr>';
|
||||
|
||||
$x .= '</table>';
|
||||
|
||||
return $x;
|
||||
}
|
||||
|
||||
public function getRandomModelId() : int
|
||||
{
|
||||
// dwarf?? [null, 30754, 30753, 30755, 30736]
|
||||
// totems use hardcoded models, tauren model is base
|
||||
$totems = [null, 4589, 4588, 4587, 4590]; // slot => modelId
|
||||
$data = [];
|
||||
|
||||
for ($i = 1; $i < 5; $i++)
|
||||
if ($_ = $this->curTpl['displayId'.$i])
|
||||
$data[] = $_;
|
||||
|
||||
if (count($data) == 1 && ($slotId = array_search($data[0], $totems)))
|
||||
$data = DB::World()->selectCol('SELECT `DisplayId` FROM player_totem_model WHERE `TotemSlot` = ?d', $slotId);
|
||||
|
||||
return !$data ? 0 : $data[array_rand($data)];
|
||||
}
|
||||
|
||||
public function getBaseStats(string $type) : array
|
||||
{
|
||||
// i'm aware of the BaseVariance/RangedVariance fields ... i'm just totaly unsure about the whole damage calculation
|
||||
switch ($type)
|
||||
{
|
||||
case 'health':
|
||||
$hMin = $this->getField('healthMin');
|
||||
$hMax = $this->getField('healthMax');
|
||||
return [$hMin, $hMax];
|
||||
case 'power':
|
||||
$mMin = $this->getField('manaMin');
|
||||
$mMax = $this->getField('manaMax');
|
||||
return [$mMin, $mMax];
|
||||
case 'armor':
|
||||
$aMin = $this->getField('armorMin');
|
||||
$aMax = $this->getField('armorMax');
|
||||
return [$aMin, $aMax];
|
||||
case 'melee':
|
||||
$mleMin = ($this->getField('dmgMin') + ($this->getField('mleAtkPwrMin') / 14)) * $this->getField('dmgMultiplier') * $this->getField('atkSpeed');
|
||||
$mleMax = ($this->getField('dmgMax') * 1.5 + ($this->getField('mleAtkPwrMax') / 14)) * $this->getField('dmgMultiplier') * $this->getField('atkSpeed');
|
||||
return [$mleMin, $mleMax];
|
||||
case 'ranged':
|
||||
$rngMin = ($this->getField('dmgMin') + ($this->getField('rngAtkPwrMin') / 14)) * $this->getField('dmgMultiplier') * $this->getField('rngAtkSpeed');
|
||||
$rngMax = ($this->getField('dmgMax') * 1.5 + ($this->getField('rngAtkPwrMax') / 14)) * $this->getField('dmgMultiplier') * $this->getField('rngAtkSpeed');
|
||||
return [$rngMin, $rngMax];
|
||||
case 'resistance':
|
||||
$r = [];
|
||||
for ($i = SPELL_SCHOOL_HOLY; $i < SPELL_SCHOOL_ARCANE+1; $i++)
|
||||
$r[$i] = $this->getField('resistance'.$i);
|
||||
|
||||
return $r;
|
||||
default:
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
public function isBoss() : bool
|
||||
{
|
||||
return ($this->curTpl['cuFlags'] & NPC_CU_INSTANCE_BOSS) || ($this->curTpl['typeFlags'] & NPC_TYPEFLAG_BOSS_MOB && $this->curTpl['rank']);
|
||||
}
|
||||
|
||||
public function getListviewData(int $addInfoMask = 0x0) : array
|
||||
{
|
||||
/* looks like this data differs per occasion
|
||||
*
|
||||
* NPCINFO_TAMEABLE (0x1): include texture & react
|
||||
* NPCINFO_MODEL (0x2):
|
||||
* NPCINFO_REP (0x4): include repreward
|
||||
*/
|
||||
|
||||
$data = [];
|
||||
$rewRep = [];
|
||||
|
||||
if ($addInfoMask & NPCINFO_REP && $this->getFoundIDs())
|
||||
{
|
||||
$rewRep = DB::World()->selectCol(
|
||||
'SELECT `creature_id` AS ARRAY_KEY, `RewOnKillRepFaction1` AS ARRAY_KEY2, `RewOnKillRepValue1` FROM creature_onkill_reputation WHERE `creature_id` IN (?a) AND `RewOnKillRepFaction1` > 0 UNION
|
||||
SELECT `creature_id` AS ARRAY_KEY, `RewOnKillRepFaction2` AS ARRAY_KEY2, `RewOnKillRepValue2` FROM creature_onkill_reputation WHERE `creature_id` IN (?a) AND `RewOnKillRepFaction2` > 0',
|
||||
$this->getFoundIDs(),
|
||||
$this->getFoundIDs()
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
foreach ($this->iterate() as $__)
|
||||
{
|
||||
if ($addInfoMask & NPCINFO_MODEL)
|
||||
{
|
||||
$texStr = strtolower($this->curTpl['textureString']);
|
||||
|
||||
if (isset($data[$texStr]))
|
||||
{
|
||||
if ($data[$texStr]['minLevel'] > $this->curTpl['minLevel'])
|
||||
$data[$texStr]['minLevel'] = $this->curTpl['minLevel'];
|
||||
|
||||
if ($data[$texStr]['maxLevel'] < $this->curTpl['maxLevel'])
|
||||
$data[$texStr]['maxLevel'] = $this->curTpl['maxLevel'];
|
||||
|
||||
$data[$texStr]['count']++;
|
||||
}
|
||||
else
|
||||
$data[$texStr] = array(
|
||||
'family' => $this->curTpl['family'],
|
||||
'minLevel' => $this->curTpl['minLevel'],
|
||||
'maxLevel' => $this->curTpl['maxLevel'],
|
||||
'modelId' => $this->curTpl['modelId'],
|
||||
'displayId' => $this->curTpl['displayId1'],
|
||||
'skin' => $texStr,
|
||||
'count' => 1
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
$data[$this->id] = array(
|
||||
'family' => $this->curTpl['family'],
|
||||
'minlevel' => $this->curTpl['minLevel'],
|
||||
'maxlevel' => $this->curTpl['maxLevel'],
|
||||
'id' => $this->id,
|
||||
'boss' => $this->isBoss() ? 1 : 0,
|
||||
'classification' => $this->curTpl['rank'],
|
||||
'location' => $this->getSpawns(SPAWNINFO_ZONES),
|
||||
'name' => $this->getField('name', true),
|
||||
'type' => $this->curTpl['type'],
|
||||
'react' => [$this->curTpl['A'], $this->curTpl['H']],
|
||||
);
|
||||
|
||||
|
||||
if ($this->getField('startsQuests'))
|
||||
$data[$this->id]['hasQuests'] = 1;
|
||||
|
||||
if ($_ = $this->getField('subname', true))
|
||||
$data[$this->id]['tag'] = $_;
|
||||
|
||||
if ($addInfoMask & NPCINFO_TAMEABLE) // only first skin of first model ... we're omitting potentially 11 skins here .. but the lv accepts only one .. w/e
|
||||
$data[$this->id]['skin'] = $this->curTpl['textureString'];
|
||||
|
||||
if ($addInfoMask & NPCINFO_REP)
|
||||
{
|
||||
$data[$this->id]['reprewards'] = [];
|
||||
if ($rewRep[$this->id])
|
||||
foreach ($rewRep[$this->id] as $fac => $val)
|
||||
$data[$this->id]['reprewards'][] = [$fac, $val];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ksort($data);
|
||||
return $data;
|
||||
}
|
||||
|
||||
public function getJSGlobals(int $addMask = 0) : array
|
||||
{
|
||||
$data = [];
|
||||
|
||||
foreach ($this->iterate() as $__)
|
||||
$data[Type::NPC][$this->id] = ['name' => $this->getField('name', true)];
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
public function getSourceData(int $id = 0) : array
|
||||
{
|
||||
$data = [];
|
||||
|
||||
foreach ($this->iterate() as $__)
|
||||
{
|
||||
if ($id && $id != $this->id)
|
||||
continue;
|
||||
|
||||
$data[$this->id] = array(
|
||||
'n' => $this->getField('parentId') ? $this->getField('parent', true) : $this->getField('name', true),
|
||||
't' => Type::NPC,
|
||||
'ti' => $this->getField('parentId') ?: $this->id
|
||||
);
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class CreatureListFilter extends Filter
|
||||
{
|
||||
protected string $type = 'npcs';
|
||||
protected static array $enums = array(
|
||||
3 => parent::ENUM_FACTION, // faction
|
||||
6 => parent::ENUM_ZONE, // foundin
|
||||
42 => parent::ENUM_FACTION, // increasesrepwith
|
||||
43 => parent::ENUM_FACTION, // decreasesrepwith
|
||||
38 => parent::ENUM_EVENT // relatedevent
|
||||
);
|
||||
|
||||
protected static array $genericFilter = array(
|
||||
1 => [parent::CR_CALLBACK, 'cbHealthMana', 'healthMax', 'healthMin'], // health [num]
|
||||
2 => [parent::CR_CALLBACK, 'cbHealthMana', 'manaMin', 'manaMax' ], // mana [num]
|
||||
3 => [parent::CR_CALLBACK, 'cbFaction', null, null ], // faction [enum]
|
||||
5 => [parent::CR_FLAG, 'npcflag', NPC_FLAG_REPAIRER ], // canrepair
|
||||
6 => [parent::CR_ENUM, 's.areaId', false, true ], // foundin
|
||||
7 => [parent::CR_CALLBACK, 'cbQuestRelation', 'startsQuests', 0x1 ], // startsquest [enum]
|
||||
8 => [parent::CR_CALLBACK, 'cbQuestRelation', 'endsQuests', 0x2 ], // endsquest [enum]
|
||||
9 => [parent::CR_BOOLEAN, 'lootId', ], // lootable
|
||||
10 => [parent::CR_CALLBACK, 'cbRegularSkinLoot', NPC_TYPEFLAG_SPECIALLOOT ], // skinnable [yn]
|
||||
11 => [parent::CR_BOOLEAN, 'pickpocketLootId', ], // pickpocketable
|
||||
12 => [parent::CR_CALLBACK, 'cbMoneyDrop', null, null ], // averagemoneydropped [op] [int]
|
||||
15 => [parent::CR_CALLBACK, 'cbSpecialSkinLoot', NPC_TYPEFLAG_SKIN_WITH_HERBALISM, null ], // gatherable [yn]
|
||||
16 => [parent::CR_CALLBACK, 'cbSpecialSkinLoot', NPC_TYPEFLAG_SKIN_WITH_MINING, null ], // minable [yn]
|
||||
18 => [parent::CR_FLAG, 'npcflag', NPC_FLAG_AUCTIONEER ], // auctioneer
|
||||
19 => [parent::CR_FLAG, 'npcflag', NPC_FLAG_BANKER ], // banker
|
||||
20 => [parent::CR_FLAG, 'npcflag', NPC_FLAG_BATTLEMASTER ], // battlemaster
|
||||
21 => [parent::CR_FLAG, 'npcflag', NPC_FLAG_FLIGHT_MASTER ], // flightmaster
|
||||
22 => [parent::CR_FLAG, 'npcflag', NPC_FLAG_GUILD_MASTER ], // guildmaster
|
||||
23 => [parent::CR_FLAG, 'npcflag', NPC_FLAG_INNKEEPER ], // innkeeper
|
||||
24 => [parent::CR_FLAG, 'npcflag', NPC_FLAG_CLASS_TRAINER ], // talentunlearner
|
||||
25 => [parent::CR_FLAG, 'npcflag', NPC_FLAG_GUILD_MASTER ], // tabardvendor
|
||||
27 => [parent::CR_FLAG, 'npcflag', NPC_FLAG_STABLE_MASTER ], // stablemaster
|
||||
28 => [parent::CR_FLAG, 'npcflag', NPC_FLAG_TRAINER ], // trainer
|
||||
29 => [parent::CR_FLAG, 'npcflag', NPC_FLAG_VENDOR ], // vendor
|
||||
31 => [parent::CR_FLAG, 'cuFlags', CUSTOM_HAS_SCREENSHOT ], // hasscreenshots
|
||||
32 => [parent::CR_FLAG, 'cuFlags', NPC_CU_INSTANCE_BOSS ], // instanceboss
|
||||
33 => [parent::CR_FLAG, 'cuFlags', CUSTOM_HAS_COMMENT ], // hascomments
|
||||
34 => [parent::CR_STRING, 'modelId', STR_MATCH_EXACT | STR_ALLOW_SHORT ], // usemodel [str] (wants int in string fmt <_<)
|
||||
35 => [parent::CR_STRING, 'textureString' ], // useskin [str]
|
||||
37 => [parent::CR_NUMERIC, 'id', NUM_CAST_INT, true ], // id
|
||||
38 => [parent::CR_CALLBACK, 'cbRelEvent', null, null ], // relatedevent [enum]
|
||||
40 => [parent::CR_FLAG, 'cuFlags', CUSTOM_HAS_VIDEO ], // hasvideos
|
||||
41 => [parent::CR_NYI_PH, 1, null ], // haslocation [yn] [staff]
|
||||
42 => [parent::CR_CALLBACK, 'cbReputation', '>', null ], // increasesrepwith [enum]
|
||||
43 => [parent::CR_CALLBACK, 'cbReputation', '<', null ], // decreasesrepwith [enum]
|
||||
44 => [parent::CR_CALLBACK, 'cbSpecialSkinLoot', NPC_TYPEFLAG_SKIN_WITH_ENGINEERING, null ] // salvageable [yn]
|
||||
);
|
||||
|
||||
protected static array $inputFields = array(
|
||||
'cr' => [parent::V_LIST, [[1, 3],[5, 12], 15, 16, [18, 25], [27, 29], [31, 35], 37, 38, [40, 44]], true ], // criteria ids
|
||||
'crs' => [parent::V_LIST, [parent::ENUM_NONE, parent::ENUM_ANY, [0, 9999]], true ], // criteria operators
|
||||
'crv' => [parent::V_REGEX, parent::PATTERN_CRV, true ], // criteria values - only printable chars, no delimiter
|
||||
'na' => [parent::V_REGEX, parent::PATTERN_NAME, false], // name / subname - only printable chars, no delimiter
|
||||
'ex' => [parent::V_EQUAL, 'on', false], // also match subname
|
||||
'ma' => [parent::V_EQUAL, 1, false], // match any / all filter
|
||||
'fa' => [parent::V_CALLBACK, 'cbPetFamily', true ], // pet family [list] - cat[0] == 1
|
||||
'minle' => [parent::V_RANGE, [1, 99], false], // min level [int]
|
||||
'maxle' => [parent::V_RANGE, [1, 99], false], // max level [int]
|
||||
'cl' => [parent::V_RANGE, [0, 4], true ], // classification [list]
|
||||
'ra' => [parent::V_LIST, [-1, 0, 1], false], // react alliance [int]
|
||||
'rh' => [parent::V_LIST, [-1, 0, 1], false] // react horde [int]
|
||||
);
|
||||
|
||||
public array $extraOpts = [];
|
||||
|
||||
protected function createSQLForValues() : array
|
||||
{
|
||||
$parts = [];
|
||||
$_v = &$this->values;
|
||||
|
||||
// name [str]
|
||||
if ($_v['na'])
|
||||
{
|
||||
$_ = [];
|
||||
if ($_v['ex'] == 'on')
|
||||
$_ = $this->tokenizeString(['name_loc'.Lang::getLocale()->value, 'subname_loc'.Lang::getLocale()->value]);
|
||||
else
|
||||
$_ = $this->tokenizeString(['name_loc'.Lang::getLocale()->value]);
|
||||
|
||||
if ($_)
|
||||
$parts[] = $_;
|
||||
}
|
||||
|
||||
// pet family [list]
|
||||
if ($_v['fa'])
|
||||
$parts[] = ['family', $_v['fa']];
|
||||
|
||||
// creatureLevel min [int]
|
||||
if ($_v['minle'])
|
||||
$parts[] = ['minLevel', $_v['minle'], '>='];
|
||||
|
||||
// creatureLevel max [int]
|
||||
if ($_v['maxle'])
|
||||
$parts[] = ['maxLevel', $_v['maxle'], '<='];
|
||||
|
||||
// classification [list]
|
||||
if ($_v['cl'])
|
||||
$parts[] = ['rank', $_v['cl']];
|
||||
|
||||
// react Alliance [int]
|
||||
if ($_v['ra'])
|
||||
$parts[] = ['ft.A', $_v['ra']];
|
||||
|
||||
// react Horde [int]
|
||||
if ($_v['rh'])
|
||||
$parts[] = ['ft.H', $_v['rh']];
|
||||
|
||||
return $parts;
|
||||
}
|
||||
|
||||
protected function cbPetFamily(string &$val) : bool
|
||||
{
|
||||
if (!$this->parentCats || $this->parentCats[0] != 1)
|
||||
return false;
|
||||
|
||||
if (!Util::checkNumeric($val, NUM_CAST_INT))
|
||||
return false;
|
||||
|
||||
$type = parent::V_LIST;
|
||||
$valid = [[1, 9], 11, 12, 20, 21, [24, 27], [30, 35], [37, 39], [41, 46]];
|
||||
|
||||
return $this->checkInput($type, $valid, $val);
|
||||
}
|
||||
|
||||
protected function cbRelEvent(int $cr, int $crs, string $crv) : ?array
|
||||
{
|
||||
if ($crs == parent::ENUM_ANY)
|
||||
{
|
||||
if ($eventIds = DB::Aowow()->selectCol('SELECT `id` FROM ?_events WHERE `holidayId` <> 0'))
|
||||
if ($cGuids = DB::World()->selectCol('SELECT DISTINCT `guid` FROM game_event_creature WHERE `eventEntry` IN (?a)', $eventIds))
|
||||
return ['s.guid', $cGuids];
|
||||
|
||||
return [0];
|
||||
}
|
||||
else if ($crs == parent::ENUM_NONE)
|
||||
{
|
||||
if ($eventIds = DB::Aowow()->selectCol('SELECT `id` FROM ?_events WHERE `holidayId` <> 0'))
|
||||
if ($cGuids = DB::World()->selectCol('SELECT DISTINCT `guid` FROM game_event_creature WHERE `eventEntry` IN (?a)', $eventIds))
|
||||
return ['s.guid', $cGuids, '!'];
|
||||
|
||||
return [0];
|
||||
}
|
||||
else if (in_array($crs, self::$enums[$cr]))
|
||||
{
|
||||
if ($eventIds = DB::Aowow()->selectCol('SELECT `id` FROM ?_events WHERE `holidayId` = ?d', $crs))
|
||||
if ($cGuids = DB::World()->selectCol('SELECT DISTINCT `guid` FROM `game_event_creature` WHERE `eventEntry` IN (?a)', $eventIds))
|
||||
return ['s.guid', $cGuids];
|
||||
|
||||
return [0];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
protected function cbMoneyDrop(int $cr, int $crs, string $crv) : ?array
|
||||
{
|
||||
if (!Util::checkNumeric($crv, NUM_CAST_INT) || !$this->int2Op($crs))
|
||||
return null;
|
||||
|
||||
return ['AND', ['((minGold + maxGold) / 2)', $crv, $crs]];
|
||||
}
|
||||
|
||||
protected function cbQuestRelation(int $cr, int $crs, string $crv, $field, $val) : ?array
|
||||
{
|
||||
switch ($crs)
|
||||
{
|
||||
case 1: // any
|
||||
return ['AND', ['qse.method', $val, '&'], ['qse.questId', null, '!']];
|
||||
case 2: // alliance
|
||||
return ['AND', ['qse.method', $val, '&'], ['qse.questId', null, '!'], [['qt.reqRaceMask', ChrRace::MASK_HORDE, '&'], 0], ['qt.reqRaceMask', ChrRace::MASK_ALLIANCE, '&']];
|
||||
case 3: // horde
|
||||
return ['AND', ['qse.method', $val, '&'], ['qse.questId', null, '!'], [['qt.reqRaceMask', ChrRace::MASK_ALLIANCE, '&'], 0], ['qt.reqRaceMask', ChrRace::MASK_HORDE, '&']];
|
||||
case 4: // both
|
||||
return ['AND', ['qse.method', $val, '&'], ['qse.questId', null, '!'], ['OR', ['AND', ['qt.reqRaceMask', ChrRace::MASK_ALLIANCE, '&'], ['qt.reqRaceMask', ChrRace::MASK_HORDE, '&']], ['qt.reqRaceMask', 0]]];
|
||||
case 5: // none
|
||||
$this->extraOpts['ct']['h'][] = $field.' = 0';
|
||||
return [1];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
protected function cbHealthMana(int $cr, int $crs, string $crv, $minField, $maxField) : ?array
|
||||
{
|
||||
if (!Util::checkNumeric($crv, NUM_CAST_INT) || !$this->int2Op($crs))
|
||||
return null;
|
||||
|
||||
// remap OP for this special case
|
||||
switch ($crs)
|
||||
{
|
||||
case '=': // min > max is totally possible
|
||||
$this->extraOpts['ct']['h'][] = $minField.' = '.$maxField.' AND '.$minField.' = '.$crv;
|
||||
break;
|
||||
case '>':
|
||||
case '>=':
|
||||
case '<':
|
||||
case '<=':
|
||||
$this->extraOpts['ct']['h'][] = 'IF('.$minField.' > '.$maxField.', '.$maxField.', '.$minField.') '.$crs.' '.$crv;
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
return [1]; // always true, use post-filter
|
||||
}
|
||||
|
||||
protected function cbSpecialSkinLoot(int $cr, int $crs, string $crv, $typeFlag) : ?array
|
||||
{
|
||||
if (!$this->int2Bool($crs))
|
||||
return null;
|
||||
|
||||
|
||||
if ($crs)
|
||||
return ['AND', ['skinLootId', 0, '>'], ['typeFlags', $typeFlag, '&']];
|
||||
else
|
||||
return ['OR', ['skinLootId', 0], [['typeFlags', $typeFlag, '&'], 0]];
|
||||
}
|
||||
|
||||
protected function cbRegularSkinLoot(int $cr, int $crs, string $crv, $typeFlag) : ?array
|
||||
{
|
||||
if (!$this->int2Bool($crs))
|
||||
return null;
|
||||
|
||||
if ($crs)
|
||||
return ['AND', ['skinLootId', 0, '>'], [['typeFlags', $typeFlag, '&'], 0]];
|
||||
else
|
||||
return ['OR', ['skinLootId', 0], ['typeFlags', $typeFlag, '&']];
|
||||
}
|
||||
|
||||
protected function cbReputation(int $cr, int $crs, string $crv, $op) : ?array
|
||||
{
|
||||
if (!in_array($crs, self::$enums[$cr]))
|
||||
return null;
|
||||
|
||||
if ($_ = DB::Aowow()->selectRow('SELECT * FROM ?_factions WHERE `id` = ?d', $crs))
|
||||
$this->fiReputationCols[] = [$crs, Util::localizedString($_, 'name')];
|
||||
|
||||
if ($cIds = DB::World()->selectCol('SELECT `creature_id` FROM creature_onkill_reputation WHERE (`RewOnKillRepFaction1` = ?d AND `RewOnKillRepValue1` '.$op.' 0) OR (`RewOnKillRepFaction2` = ?d AND `RewOnKillRepValue2` '.$op.' 0)', $crs, $crs))
|
||||
return ['id', $cIds];
|
||||
else
|
||||
return [0];
|
||||
}
|
||||
|
||||
protected function cbFaction(int $cr, int $crs, string $crv) : ?array
|
||||
{
|
||||
if (!Util::checkNumeric($crs, NUM_CAST_INT))
|
||||
return null;
|
||||
|
||||
if (!in_array($crs, self::$enums[$cr]))
|
||||
return null;
|
||||
|
||||
$facTpls = [];
|
||||
$facs = new FactionList(array('OR', ['parentFactionId', $crs], ['id', $crs]));
|
||||
foreach ($facs->iterate() as $__)
|
||||
$facTpls = array_merge($facTpls, $facs->getField('templateIds'));
|
||||
|
||||
return $facTpls ? ['faction', $facTpls] : [0];
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
88
includes/dbtypes/currency.class.php
Normal file
88
includes/dbtypes/currency.class.php
Normal file
@@ -0,0 +1,88 @@
|
||||
<?php
|
||||
|
||||
namespace Aowow;
|
||||
|
||||
if (!defined('AOWOW_REVISION'))
|
||||
die('illegal access');
|
||||
|
||||
|
||||
class CurrencyList extends DBTypeList
|
||||
{
|
||||
public static int $type = Type::CURRENCY;
|
||||
public static string $brickFile = 'currency';
|
||||
public static string $dataTable = '?_currencies';
|
||||
|
||||
protected string $queryBase = 'SELECT c.*, c.`id` AS ARRAY_KEY FROM ?_currencies c';
|
||||
protected array $queryOpts = array(
|
||||
'c' => [['ic']],
|
||||
'ic' => ['j' => ['?_icons ic ON ic.`id` = c.`iconId`', true], 's' => ', ic.`name` AS "iconString"']
|
||||
);
|
||||
|
||||
public function __construct(array $conditions = [], array $miscData = [])
|
||||
{
|
||||
parent::__construct($conditions, $miscData);
|
||||
|
||||
foreach ($this->iterate() as &$_curTpl)
|
||||
$_curTpl['iconString'] = $_curTpl['iconString'] ?: DEFAULT_ICON;
|
||||
}
|
||||
|
||||
|
||||
public function getListviewData() : array
|
||||
{
|
||||
$data = [];
|
||||
|
||||
foreach ($this->iterate() as $__)
|
||||
{
|
||||
$data[$this->id] = array(
|
||||
'id' => $this->id,
|
||||
'category' => $this->curTpl['category'],
|
||||
'name' => $this->getField('name', true),
|
||||
'icon' => $this->curTpl['iconString']
|
||||
);
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
public function getJSGlobals(int $addMask = 0) : array
|
||||
{
|
||||
$data = [];
|
||||
|
||||
foreach ($this->iterate() as $__)
|
||||
{
|
||||
// todo (low): un-hardcode icon strings
|
||||
$icon = match ($this->id)
|
||||
{
|
||||
CURRENCY_HONOR_POINTS => ['pvp-currency-alliance', 'pvp-currency-horde' ],
|
||||
CURRENCY_ARENA_POINTS => ['pvp-arenapoints-icon', 'pvp-arenapoints-icon' ],
|
||||
default => [$this->curTpl['iconString'], $this->curTpl['iconString']]
|
||||
};
|
||||
|
||||
$data[Type::CURRENCY][$this->id] = ['name' => $this->getField('name', true), 'icon' => $icon];
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
public function renderTooltip() : ?string
|
||||
{
|
||||
if (!$this->curTpl)
|
||||
return null;
|
||||
|
||||
$x = '<table><tr><td>';
|
||||
$x .= '<b>'.$this->getField('name', true).'</b><br />';
|
||||
|
||||
// cata+ (or go fill it by hand)
|
||||
if ($_ = $this->getField('description', true))
|
||||
$x .= '<div style="max-width: 300px" class="q">'.$_.'</div>';
|
||||
|
||||
if ($_ = $this->getField('cap'))
|
||||
$x .= '<br /><span class="q">'.Lang::currency('cap').Lang::main('colon').'</span>'.Lang::nf($_).'<br />';
|
||||
|
||||
$x .= '</td></tr></table>';
|
||||
|
||||
return $x;
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
65
includes/dbtypes/emote.class.php
Normal file
65
includes/dbtypes/emote.class.php
Normal file
@@ -0,0 +1,65 @@
|
||||
<?php
|
||||
|
||||
namespace Aowow;
|
||||
|
||||
if (!defined('AOWOW_REVISION'))
|
||||
die('illegal access');
|
||||
|
||||
|
||||
class EmoteList extends DBTypeList
|
||||
{
|
||||
public static int $type = Type::EMOTE;
|
||||
public static string $brickFile = 'emote';
|
||||
public static string $dataTable = '?_emotes';
|
||||
|
||||
protected string $queryBase = 'SELECT e.*, e.`id` AS ARRAY_KEY FROM ?_emotes e';
|
||||
|
||||
public function __construct(array $conditions = [], array $miscData = [])
|
||||
{
|
||||
parent::__construct($conditions, $miscData);
|
||||
|
||||
// post processing
|
||||
foreach ($this->iterate() as &$curTpl)
|
||||
{
|
||||
// remap for generic access
|
||||
$curTpl['name'] = $curTpl['cmd'];
|
||||
}
|
||||
}
|
||||
|
||||
public static function getName(int $id) : ?LocString
|
||||
{
|
||||
if ($n = DB::Aowow()->SelectRow('SELECT `cmd` AS "name_loc0" FROM ?# WHERE `id` = ?d', self::$dataTable, $id))
|
||||
return new LocString($n);
|
||||
return null;
|
||||
}
|
||||
|
||||
public function getListviewData() : array
|
||||
{
|
||||
$data = [];
|
||||
|
||||
foreach ($this->iterate() as $__)
|
||||
{
|
||||
$data[$this->id] = array(
|
||||
'id' => $this->curTpl['id'],
|
||||
'name' => $this->curTpl['cmd'],
|
||||
'preview' => Util::parseHtmlText($this->getField('meToExt', true) ?: $this->getField('meToNone', true) ?: $this->getField('extToMe', true) ?: $this->getField('extToExt', true) ?: $this->getField('extToNone', true), true)
|
||||
);
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
public function getJSGlobals(int $addMask = GLOBALINFO_ANY) : array
|
||||
{
|
||||
$data = [];
|
||||
|
||||
foreach ($this->iterate() as $__)
|
||||
$data[Type::EMOTE][$this->id] = ['name' => $this->getField('cmd')];
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
public function renderTooltip() : ?string { return null; }
|
||||
}
|
||||
|
||||
?>
|
||||
257
includes/dbtypes/enchantment.class.php
Normal file
257
includes/dbtypes/enchantment.class.php
Normal file
@@ -0,0 +1,257 @@
|
||||
<?php
|
||||
|
||||
namespace Aowow;
|
||||
|
||||
if (!defined('AOWOW_REVISION'))
|
||||
die('illegal access');
|
||||
|
||||
|
||||
class EnchantmentList extends DBTypeList
|
||||
{
|
||||
use listviewHelper;
|
||||
|
||||
public static $type = Type::ENCHANTMENT;
|
||||
public static $brickFile = 'enchantment';
|
||||
public static $dataTable = '?_itemenchantment';
|
||||
|
||||
private array $jsonStats = [];
|
||||
private ?SpellList $relSpells = null;
|
||||
private array $triggerIds = [];
|
||||
|
||||
protected $queryBase = 'SELECT ie.*, ie.id AS ARRAY_KEY FROM ?_itemenchantment ie';
|
||||
protected $queryOpts = array( // 502 => Type::ENCHANTMENT
|
||||
'ie' => [['is']],
|
||||
'is' => ['j' => ['?_item_stats `is` ON `is`.`type` = 502 AND `is`.`typeId` = `ie`.`id`', true], 's' => ', `is`.*'],
|
||||
);
|
||||
|
||||
public function __construct(array $conditions = [], array $miscData = [])
|
||||
{
|
||||
parent::__construct($conditions, $miscData);
|
||||
|
||||
$relSpells = [];
|
||||
|
||||
// post processing
|
||||
foreach ($this->iterate() as &$curTpl)
|
||||
{
|
||||
$curTpl['spells'] = []; // [spellId, triggerType, charges, chanceOrPpm]
|
||||
for ($i = 1; $i <=3; $i++)
|
||||
{
|
||||
if ($curTpl['object'.$i] <= 0)
|
||||
continue;
|
||||
|
||||
switch ($curTpl['type'.$i]) // SPELL_TRIGGER_* just reused for wording
|
||||
{
|
||||
case ENCHANTMENT_TYPE_COMBAT_SPELL:
|
||||
$proc = -$this->getField('ppmRate') ?: ($this->getField('procChance') ?: $this->getField('amount'.$i));
|
||||
$curTpl['spells'][$i] = [$curTpl['object'.$i], SPELL_TRIGGER_HIT, $curTpl['charges'], $proc];
|
||||
$relSpells[] = $curTpl['object'.$i];
|
||||
break;
|
||||
case ENCHANTMENT_TYPE_EQUIP_SPELL:
|
||||
$curTpl['spells'][$i] = [$curTpl['object'.$i], SPELL_TRIGGER_EQUIP, $curTpl['charges'], 0];
|
||||
$relSpells[] = $curTpl['object'.$i];
|
||||
break;
|
||||
case ENCHANTMENT_TYPE_USE_SPELL:
|
||||
$curTpl['spells'][$i] = [$curTpl['object'.$i], SPELL_TRIGGER_USE, $curTpl['charges'], 0];
|
||||
$relSpells[] = $curTpl['object'.$i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// issue with scaling stats enchantments
|
||||
// stats are stored as NOT NULL to be usable by the search filters and such become indistinguishable from scaling enchantments that _actually_ use the value 0
|
||||
// so filter the stats container and if it is empty, rebuild from self. .. there are no mixed scaling/static enchantments, right!?
|
||||
$this->jsonStats[$this->id] = (new StatsContainer())->fromJson($curTpl, true)->filter();
|
||||
if (!count($this->jsonStats[$this->id]))
|
||||
$this->jsonStats[$this->id]->fromEnchantment($curTpl);
|
||||
}
|
||||
|
||||
if ($relSpells)
|
||||
$this->relSpells = new SpellList(array(['id', $relSpells]));
|
||||
}
|
||||
|
||||
public function getListviewData(int $addInfoMask = 0x0) : array
|
||||
{
|
||||
$data = [];
|
||||
|
||||
foreach ($this->iterate() as $__)
|
||||
{
|
||||
$data[$this->id] = array(
|
||||
'id' => $this->id,
|
||||
'name' => $this->getField('name', true),
|
||||
'spells' => []
|
||||
);
|
||||
|
||||
if ($this->curTpl['skillLine'] > 0)
|
||||
$data[$this->id]['reqskill'] = $this->curTpl['skillLine'];
|
||||
|
||||
if ($this->curTpl['skillLevel'] > 0)
|
||||
$data[$this->id]['reqskillrank'] = $this->curTpl['skillLevel'];
|
||||
|
||||
if ($this->curTpl['requiredLevel'] > 0)
|
||||
$data[$this->id]['reqlevel'] = $this->curTpl['requiredLevel'];
|
||||
|
||||
foreach ($this->curTpl['spells'] as [$spellId, $trigger, $charges, $procChance])
|
||||
{
|
||||
// spell is procing
|
||||
$trgSpell = 0;
|
||||
if ($this->relSpells && $this->relSpells->getEntry($spellId) && ($_ = $this->relSpells->canTriggerSpell()))
|
||||
{
|
||||
foreach ($_ as $idx)
|
||||
{
|
||||
if ($trgSpell = $this->relSpells->getField('effect'.$idx.'TriggerSpell'))
|
||||
{
|
||||
$this->triggerIds[] = $trgSpell;
|
||||
$data[$this->id]['spells'][$trgSpell] = $charges;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// spell was not proccing
|
||||
if (!$trgSpell)
|
||||
$data[$this->id]['spells'][$spellId] = $charges;
|
||||
}
|
||||
|
||||
if (!$data[$this->id]['spells'])
|
||||
unset($data[$this->id]['spells']);
|
||||
|
||||
Util::arraySumByKey($data[$this->id], $this->jsonStats[$this->id]->toJson());
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
public function getStatGainForCurrent() : array
|
||||
{
|
||||
return $this->jsonStats[$this->id]->toJson();
|
||||
}
|
||||
|
||||
public function getRelSpell(int $id) : ?array
|
||||
{
|
||||
if ($this->relSpells)
|
||||
return $this->relSpells->getEntry($id);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public function getJSGlobals(int $addMask = GLOBALINFO_ANY) : array
|
||||
{
|
||||
$data = [];
|
||||
|
||||
if ($addMask & GLOBALINFO_SELF)
|
||||
foreach ($this->iterate() as $__)
|
||||
$data[Type::ENCHANTMENT][$this->id] = ['name' => $this->getField('name', true)];
|
||||
|
||||
if ($addMask & GLOBALINFO_RELATED)
|
||||
{
|
||||
if ($this->relSpells)
|
||||
$data = $this->relSpells->getJSGlobals(GLOBALINFO_SELF);
|
||||
|
||||
foreach ($this->triggerIds as $tId)
|
||||
if (empty($data[Type::SPELL][$tId]))
|
||||
$data[Type::SPELL][$tId] = $tId;
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
public function renderTooltip() : ?string { return null; }
|
||||
}
|
||||
|
||||
|
||||
class EnchantmentListFilter extends Filter
|
||||
{
|
||||
protected string $type = 'enchantments';
|
||||
protected static array $enums = array(
|
||||
3 => parent::ENUM_PROFESSION // requiresprof
|
||||
);
|
||||
|
||||
protected static array $genericFilter = array(
|
||||
2 => [parent::CR_NUMERIC, 'id', NUM_CAST_INT, true], // id
|
||||
3 => [parent::CR_ENUM, 'skillLine' ], // requiresprof
|
||||
4 => [parent::CR_NUMERIC, 'skillLevel', NUM_CAST_INT ], // reqskillrank
|
||||
5 => [parent::CR_BOOLEAN, 'conditionId' ], // hascondition
|
||||
10 => [parent::CR_FLAG, 'cuFlags', CUSTOM_HAS_COMMENT ], // hascomments
|
||||
11 => [parent::CR_FLAG, 'cuFlags', CUSTOM_HAS_SCREENSHOT ], // hasscreenshots
|
||||
12 => [parent::CR_FLAG, 'cuFlags', CUSTOM_HAS_VIDEO ], // hasvideos
|
||||
20 => [parent::CR_NUMERIC, 'is.str', NUM_CAST_INT, true], // str
|
||||
21 => [parent::CR_NUMERIC, 'is.agi', NUM_CAST_INT, true], // agi
|
||||
22 => [parent::CR_NUMERIC, 'is.sta', NUM_CAST_INT, true], // sta
|
||||
23 => [parent::CR_NUMERIC, 'is.int', NUM_CAST_INT, true], // int
|
||||
24 => [parent::CR_NUMERIC, 'is.spi', NUM_CAST_INT, true], // spi
|
||||
25 => [parent::CR_NUMERIC, 'is.arcres', NUM_CAST_INT, true], // arcres
|
||||
26 => [parent::CR_NUMERIC, 'is.firres', NUM_CAST_INT, true], // firres
|
||||
27 => [parent::CR_NUMERIC, 'is.natres', NUM_CAST_INT, true], // natres
|
||||
28 => [parent::CR_NUMERIC, 'is.frores', NUM_CAST_INT, true], // frores
|
||||
29 => [parent::CR_NUMERIC, 'is.shares', NUM_CAST_INT, true], // shares
|
||||
30 => [parent::CR_NUMERIC, 'is.holres', NUM_CAST_INT, true], // holres
|
||||
32 => [parent::CR_NUMERIC, 'is.dps', NUM_CAST_FLOAT, true], // dps
|
||||
34 => [parent::CR_NUMERIC, 'is.dmg', NUM_CAST_FLOAT, true], // dmg
|
||||
37 => [parent::CR_NUMERIC, 'is.mleatkpwr', NUM_CAST_INT, true], // mleatkpwr
|
||||
38 => [parent::CR_NUMERIC, 'is.rgdatkpwr', NUM_CAST_INT, true], // rgdatkpwr
|
||||
39 => [parent::CR_NUMERIC, 'is.rgdhitrtng', NUM_CAST_INT, true], // rgdhitrtng
|
||||
40 => [parent::CR_NUMERIC, 'is.rgdcritstrkrtng', NUM_CAST_INT, true], // rgdcritstrkrtng
|
||||
41 => [parent::CR_NUMERIC, 'is.armor', NUM_CAST_INT, true], // armor
|
||||
42 => [parent::CR_NUMERIC, 'is.defrtng', NUM_CAST_INT, true], // defrtng
|
||||
43 => [parent::CR_NUMERIC, 'is.block', NUM_CAST_INT, true], // block
|
||||
44 => [parent::CR_NUMERIC, 'is.blockrtng', NUM_CAST_INT, true], // blockrtng
|
||||
45 => [parent::CR_NUMERIC, 'is.dodgertng', NUM_CAST_INT, true], // dodgertng
|
||||
46 => [parent::CR_NUMERIC, 'is.parryrtng', NUM_CAST_INT, true], // parryrtng
|
||||
48 => [parent::CR_NUMERIC, 'is.splhitrtng', NUM_CAST_INT, true], // splhitrtng
|
||||
49 => [parent::CR_NUMERIC, 'is.splcritstrkrtng', NUM_CAST_INT, true], // splcritstrkrtng
|
||||
50 => [parent::CR_NUMERIC, 'is.splheal', NUM_CAST_INT, true], // splheal
|
||||
51 => [parent::CR_NUMERIC, 'is.spldmg', NUM_CAST_INT, true], // spldmg
|
||||
52 => [parent::CR_NUMERIC, 'is.arcsplpwr', NUM_CAST_INT, true], // arcsplpwr
|
||||
53 => [parent::CR_NUMERIC, 'is.firsplpwr', NUM_CAST_INT, true], // firsplpwr
|
||||
54 => [parent::CR_NUMERIC, 'is.frosplpwr', NUM_CAST_INT, true], // frosplpwr
|
||||
55 => [parent::CR_NUMERIC, 'is.holsplpwr', NUM_CAST_INT, true], // holsplpwr
|
||||
56 => [parent::CR_NUMERIC, 'is.natsplpwr', NUM_CAST_INT, true], // natsplpwr
|
||||
57 => [parent::CR_NUMERIC, 'is.shasplpwr', NUM_CAST_INT, true], // shasplpwr
|
||||
60 => [parent::CR_NUMERIC, 'is.healthrgn', NUM_CAST_INT, true], // healthrgn
|
||||
61 => [parent::CR_NUMERIC, 'is.manargn', NUM_CAST_INT, true], // manargn
|
||||
77 => [parent::CR_NUMERIC, 'is.atkpwr', NUM_CAST_INT, true], // atkpwr
|
||||
78 => [parent::CR_NUMERIC, 'is.mlehastertng', NUM_CAST_INT, true], // mlehastertng
|
||||
79 => [parent::CR_NUMERIC, 'is.resirtng', NUM_CAST_INT, true], // resirtng
|
||||
84 => [parent::CR_NUMERIC, 'is.mlecritstrkrtng', NUM_CAST_INT, true], // mlecritstrkrtng
|
||||
94 => [parent::CR_NUMERIC, 'is.splpen', NUM_CAST_INT, true], // splpen
|
||||
95 => [parent::CR_NUMERIC, 'is.mlehitrtng', NUM_CAST_INT, true], // mlehitrtng
|
||||
96 => [parent::CR_NUMERIC, 'is.critstrkrtng', NUM_CAST_INT, true], // critstrkrtng
|
||||
97 => [parent::CR_NUMERIC, 'is.feratkpwr', NUM_CAST_INT, true], // feratkpwr
|
||||
101 => [parent::CR_NUMERIC, 'is.rgdhastertng', NUM_CAST_INT, true], // rgdhastertng
|
||||
102 => [parent::CR_NUMERIC, 'is.splhastertng', NUM_CAST_INT, true], // splhastertng
|
||||
103 => [parent::CR_NUMERIC, 'is.hastertng', NUM_CAST_INT, true], // hastertng
|
||||
114 => [parent::CR_NUMERIC, 'is.armorpenrtng', NUM_CAST_INT, true], // armorpenrtng
|
||||
115 => [parent::CR_NUMERIC, 'is.health', NUM_CAST_INT, true], // health
|
||||
116 => [parent::CR_NUMERIC, 'is.mana', NUM_CAST_INT, true], // mana
|
||||
117 => [parent::CR_NUMERIC, 'is.exprtng', NUM_CAST_INT, true], // exprtng
|
||||
119 => [parent::CR_NUMERIC, 'is.hitrtng', NUM_CAST_INT, true], // hitrtng
|
||||
123 => [parent::CR_NUMERIC, 'is.splpwr', NUM_CAST_INT, true] // splpwr
|
||||
);
|
||||
|
||||
protected static array $inputFields = array(
|
||||
'cr' => [parent::V_RANGE, [2, 123], true ], // criteria ids
|
||||
'crs' => [parent::V_RANGE, [1, 15], true ], // criteria operators
|
||||
'crv' => [parent::V_REGEX, parent::PATTERN_INT, true ], // criteria values - only numerals
|
||||
'na' => [parent::V_REGEX, parent::PATTERN_NAME, false], // name - only printable chars, no delimiter
|
||||
'ma' => [parent::V_EQUAL, 1, false], // match any / all filter
|
||||
'ty' => [parent::V_RANGE, [1, 8], true ] // types
|
||||
);
|
||||
|
||||
protected function createSQLForValues() : array
|
||||
{
|
||||
$parts = [];
|
||||
$_v = &$this->values;
|
||||
|
||||
//string
|
||||
if ($_v['na'])
|
||||
if ($_ = $this->tokenizeString(['name_loc'.Lang::getLocale()->value]))
|
||||
$parts[] = $_;
|
||||
|
||||
// type
|
||||
if ($_v['ty'])
|
||||
$parts[] = ['OR', ['type1', $_v['ty']], ['type2', $_v['ty']], ['type3', $_v['ty']]];
|
||||
|
||||
return $parts;
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
83
includes/dbtypes/faction.class.php
Normal file
83
includes/dbtypes/faction.class.php
Normal file
@@ -0,0 +1,83 @@
|
||||
<?php
|
||||
|
||||
namespace Aowow;
|
||||
|
||||
if (!defined('AOWOW_REVISION'))
|
||||
die('illegal access');
|
||||
|
||||
|
||||
class FactionList extends DBTypeList
|
||||
{
|
||||
public static int $type = Type::FACTION;
|
||||
public static string $brickFile = 'faction';
|
||||
public static string $dataTable = '?_factions';
|
||||
|
||||
protected string $queryBase = 'SELECT f.*, f.`parentFactionId` AS "cat", f.`id` AS ARRAY_KEY FROM ?_factions f';
|
||||
protected array $queryOpts = array(
|
||||
'f' => [['f2']],
|
||||
'f2' => ['j' => ['?_factions f2 ON f.`parentFactionId` = f2.`id`', true], 's' => ', IFNULL(f2.`parentFactionId`, 0) AS "cat2"'],
|
||||
'ft' => ['j' => '?_factiontemplate ft ON ft.`factionId` = f.`id`']
|
||||
);
|
||||
|
||||
public function __construct(array $conditions = [], array $miscData = [])
|
||||
{
|
||||
parent::__construct($conditions, $miscData);
|
||||
|
||||
if ($this->error)
|
||||
return;
|
||||
|
||||
// post processing
|
||||
foreach ($this->iterate() as &$_curTpl)
|
||||
{
|
||||
// prepare factionTemplates
|
||||
$_curTpl['templateIds'] = $_curTpl['templateIds'] ? explode(' ', $_curTpl['templateIds']) : [];
|
||||
|
||||
// prepare quartermaster
|
||||
$_curTpl['qmNpcIds'] = $_curTpl['qmNpcIds'] ? explode(' ', $_curTpl['qmNpcIds']) : [];
|
||||
}
|
||||
}
|
||||
|
||||
public function getListviewData() : array
|
||||
{
|
||||
$data = [];
|
||||
|
||||
foreach ($this->iterate() as $__)
|
||||
{
|
||||
$data[$this->id] = array(
|
||||
'expansion' => $this->curTpl['expansion'],
|
||||
'id' => $this->id,
|
||||
'side' => $this->curTpl['side'],
|
||||
'name' => $this->getField('name', true)
|
||||
);
|
||||
|
||||
if ($this->curTpl['cat2'])
|
||||
{
|
||||
$data[$this->id]['category'] = $this->curTpl['cat'];
|
||||
$data[$this->id]['category2'] = $this->curTpl['cat2'];
|
||||
}
|
||||
else
|
||||
{
|
||||
$data[$this->id]['category'] = $this->curTpl['cat2'];
|
||||
$data[$this->id]['category2'] = $this->curTpl['cat'];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
public function getJSGlobals(int $addMask = 0) : array
|
||||
{
|
||||
$data = [];
|
||||
|
||||
foreach ($this->iterate() as $__)
|
||||
$data[Type::FACTION][$this->id] = ['name' => $this->getField('name', true)];
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
public function renderTooltip() : ?string { return null; }
|
||||
|
||||
}
|
||||
|
||||
?>
|
||||
246
includes/dbtypes/gameobject.class.php
Normal file
246
includes/dbtypes/gameobject.class.php
Normal file
@@ -0,0 +1,246 @@
|
||||
<?php
|
||||
|
||||
namespace Aowow;
|
||||
|
||||
if (!defined('AOWOW_REVISION'))
|
||||
die('illegal access');
|
||||
|
||||
|
||||
class GameObjectList extends DBTypeList
|
||||
{
|
||||
use listviewHelper, spawnHelper;
|
||||
|
||||
public static int $type = Type::OBJECT;
|
||||
public static string $brickFile = 'object';
|
||||
public static string $dataTable = '?_objects';
|
||||
|
||||
protected string $queryBase = 'SELECT o.*, o.`id` AS ARRAY_KEY FROM ?_objects o';
|
||||
protected array $queryOpts = array(
|
||||
'o' => [['ft', 'qse']],
|
||||
'ft' => ['j' => ['?_factiontemplate ft ON ft.`id` = o.`faction`', true], 's' => ', ft.`factionId`, IFNULL(ft.`A`, 0) AS "A", IFNULL(ft.`H`, 0) AS "H"'],
|
||||
'qse' => ['j' => ['?_quests_startend qse ON qse.`type` = 2 AND qse.`typeId` = o.id', true], 's' => ', IF(MIN(qse.`method`) = 1 OR MAX(qse.`method`) = 3, 1, 0) AS "startsQuests", IF(MIN(qse.`method`) = 2 OR MAX(qse.`method`) = 3, 1, 0) AS "endsQuests"', 'g' => 'o.`id`'],
|
||||
'qt' => ['j' => '?_quests qt ON qse.`questId` = qt.`id`'],
|
||||
's' => ['j' => '?_spawns s ON s.`type` = 2 AND s.`typeId` = o.`id`']
|
||||
);
|
||||
|
||||
public function __construct(array $conditions = [], array $miscData = [])
|
||||
{
|
||||
parent::__construct($conditions, $miscData);
|
||||
|
||||
if ($this->error)
|
||||
return;
|
||||
|
||||
// post processing
|
||||
foreach ($this->iterate() as $_id => &$curTpl)
|
||||
{
|
||||
if (!$curTpl['name_loc0'])
|
||||
$curTpl['name_loc0'] = 'Unnamed Object #' . $_id;
|
||||
|
||||
// unpack miscInfo
|
||||
$curTpl['lootStack'] = [];
|
||||
$curTpl['spells'] = [];
|
||||
|
||||
if (in_array($curTpl['type'], [OBJECT_GOOBER, OBJECT_RITUAL, OBJECT_SPELLCASTER, OBJECT_FLAGSTAND, OBJECT_FLAGDROP, OBJECT_AURA_GENERATOR, OBJECT_TRAP]))
|
||||
$curTpl['spells'] = array_combine(['onUse', 'onSuccess', 'aura', 'triggered'], [$curTpl['onUseSpell'], $curTpl['onSuccessSpell'], $curTpl['auraSpell'], $curTpl['triggeredSpell']]);
|
||||
|
||||
if (!$curTpl['miscInfo'])
|
||||
continue;
|
||||
|
||||
switch ($curTpl['type'])
|
||||
{
|
||||
case OBJECT_CHEST:
|
||||
case OBJECT_FISHINGHOLE:
|
||||
$curTpl['lootStack'] = explode(' ', $curTpl['miscInfo']);
|
||||
break;
|
||||
case OBJECT_CAPTURE_POINT:
|
||||
$curTpl['capture'] = explode(' ', $curTpl['miscInfo']);
|
||||
break;
|
||||
case OBJECT_MEETINGSTONE:
|
||||
$curTpl['mStone'] = explode(' ', $curTpl['miscInfo']);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function getListviewData() : array
|
||||
{
|
||||
$data = [];
|
||||
foreach ($this->iterate() as $__)
|
||||
{
|
||||
$data[$this->id] = array(
|
||||
'id' => $this->id,
|
||||
'name' => Lang::unescapeUISequences($this->getField('name', true), Lang::FMT_RAW),
|
||||
'type' => $this->curTpl['typeCat'],
|
||||
'location' => $this->getSpawns(SPAWNINFO_ZONES)
|
||||
);
|
||||
|
||||
if (!empty($this->curTpl['reqSkill']))
|
||||
$data[$this->id]['skill'] = $this->curTpl['reqSkill'];
|
||||
|
||||
if ($this->curTpl['startsQuests'])
|
||||
$data[$this->id]['hasQuests'] = 1;
|
||||
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
public function renderTooltip($interactive = false) : ?string
|
||||
{
|
||||
if (!$this->curTpl)
|
||||
return null;
|
||||
|
||||
$x = '<table>';
|
||||
$x .= '<tr><td><b class="q">'.Lang::unescapeUISequences($this->getField('name', true), Lang::FMT_HTML).'</b></td></tr>';
|
||||
if ($this->curTpl['typeCat'])
|
||||
if ($_ = Lang::gameObject('type', $this->curTpl['typeCat']))
|
||||
$x .= '<tr><td>'.$_.'</td></tr>';
|
||||
|
||||
if (isset($this->curTpl['lockId']))
|
||||
if ($locks = Lang::getLocks($this->curTpl['lockId']))
|
||||
foreach ($locks as $l)
|
||||
$x .= '<tr><td>'.sprintf(Lang::game('requires'), $l).'</td></tr>';
|
||||
|
||||
$x .= '</table>';
|
||||
|
||||
return $x;
|
||||
}
|
||||
|
||||
public function getJSGlobals(int $addMask = 0) : array
|
||||
{
|
||||
$data = [];
|
||||
|
||||
foreach ($this->iterate() as $__)
|
||||
$data[Type::OBJECT][$this->id] = ['name' => Lang::unescapeUISequences($this->getField('name', true), Lang::FMT_RAW)];
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
public function getSourceData(int $id = 0) : array
|
||||
{
|
||||
$data = [];
|
||||
|
||||
foreach ($this->iterate() as $__)
|
||||
{
|
||||
if ($id && $id != $this->id)
|
||||
continue;
|
||||
|
||||
$data[$this->id] = array(
|
||||
'n' => $this->getField('name', true),
|
||||
't' => Type::OBJECT,
|
||||
'ti' => $this->id
|
||||
);
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class GameObjectListFilter extends Filter
|
||||
{
|
||||
protected string $type = 'objects';
|
||||
protected static array $enums = array(
|
||||
1 => parent::ENUM_ZONE,
|
||||
16 => parent::ENUM_EVENT,
|
||||
50 => [1, 2, 3, 4, 663, 883]
|
||||
);
|
||||
|
||||
protected static array $genericFilter = array(
|
||||
1 => [parent::CR_ENUM, 's.areaId', false, true], // foundin
|
||||
2 => [parent::CR_CALLBACK, 'cbQuestRelation', 'startsQuests', 0x1 ], // startsquest [side]
|
||||
3 => [parent::CR_CALLBACK, 'cbQuestRelation', 'endsQuests', 0x2 ], // endsquest [side]
|
||||
4 => [parent::CR_CALLBACK, 'cbOpenable', null, null], // openable [yn]
|
||||
5 => [parent::CR_NYI_PH, null, 0 ], // averagemoneycontained [op] [int] - GOs don't contain money, match against 0
|
||||
7 => [parent::CR_NUMERIC, 'reqSkill', NUM_CAST_INT ], // requiredskilllevel
|
||||
11 => [parent::CR_FLAG, 'cuFlags', CUSTOM_HAS_SCREENSHOT ], // hasscreenshots
|
||||
13 => [parent::CR_FLAG, 'cuFlags', CUSTOM_HAS_COMMENT ], // hascomments
|
||||
15 => [parent::CR_NUMERIC, 'id', NUM_CAST_INT ], // id
|
||||
16 => [parent::CR_CALLBACK, 'cbRelEvent', null, null], // relatedevent (ignore removed by event)
|
||||
18 => [parent::CR_FLAG, 'cuFlags', CUSTOM_HAS_VIDEO ], // hasvideos
|
||||
50 => [parent::CR_ENUM, 'spellFocusId', true, true], // spellfocus
|
||||
);
|
||||
|
||||
protected static array $inputFields = array(
|
||||
'cr' => [parent::V_LIST, [[1, 5], 7, 11, 13, 15, 16, 18, 50], true ], // criteria ids
|
||||
'crs' => [parent::V_LIST, [parent::ENUM_NONE, parent::ENUM_ANY, [0, 5000]], true ], // criteria operators
|
||||
'crv' => [parent::V_REGEX, parent::PATTERN_INT, true ], // criteria values - only numeric input values expected
|
||||
'na' => [parent::V_REGEX, parent::PATTERN_NAME, false], // name - only printable chars, no delimiter
|
||||
'ma' => [parent::V_EQUAL, 1, false] // match any / all filter
|
||||
);
|
||||
|
||||
public array $extraOpts = [];
|
||||
|
||||
protected function createSQLForValues() : array
|
||||
{
|
||||
$parts = [];
|
||||
$_v = $this->values;
|
||||
|
||||
// name
|
||||
if ($_v['na'])
|
||||
if ($_ = $this->tokenizeString(['name_loc'.Lang::getLocale()->value]))
|
||||
$parts[] = $_;
|
||||
|
||||
return $parts;
|
||||
}
|
||||
|
||||
protected function cbOpenable(int $cr, int $crs, string $crv) : ?array
|
||||
{
|
||||
if ($this->int2Bool($crs))
|
||||
return $crs ? ['OR', ['flags', 0x2, '&'], ['type', 3]] : ['AND', [['flags', 0x2, '&'], 0], ['type', 3, '!']];
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
protected function cbQuestRelation(int $cr, int $crs, string $crv, $field, $value) : ?array
|
||||
{
|
||||
switch ($crs)
|
||||
{
|
||||
case 1: // any
|
||||
return ['AND', ['qse.method', $value, '&'], ['qse.questId', null, '!']];
|
||||
case 2: // alliance only
|
||||
return ['AND', ['qse.method', $value, '&'], ['qse.questId', null, '!'], [['qt.reqRaceMask', ChrRace::MASK_HORDE, '&'], 0], ['qt.reqRaceMask', ChrRace::MASK_ALLIANCE, '&']];
|
||||
case 3: // horde only
|
||||
return ['AND', ['qse.method', $value, '&'], ['qse.questId', null, '!'], [['qt.reqRaceMask', ChrRace::MASK_ALLIANCE, '&'], 0], ['qt.reqRaceMask', ChrRace::MASK_HORDE, '&']];
|
||||
case 4: // both
|
||||
return ['AND', ['qse.method', $value, '&'], ['qse.questId', null, '!'], ['OR', ['AND', ['qt.reqRaceMask', ChrRace::MASK_ALLIANCE, '&'], ['qt.reqRaceMask', ChrRace::MASK_HORDE, '&']], ['qt.reqRaceMask', 0]]];
|
||||
case 5: // none todo (low): broken, if entry starts and ends quests...
|
||||
$this->extraOpts['o']['h'][] = $field.' = 0';
|
||||
return [1];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
protected function cbRelEvent(int $cr, int $crs, string $crv) : ?array
|
||||
{
|
||||
if ($crs == parent::ENUM_ANY)
|
||||
{
|
||||
if ($eventIds = DB::Aowow()->selectCol('SELECT `id` FROM ?_events WHERE `holidayId` <> 0'))
|
||||
if ($goGuids = DB::World()->selectCol('SELECT DISTINCT `guid` FROM game_event_gameobject WHERE `eventEntry` IN (?a)', $eventIds))
|
||||
return ['s.guid', $goGuids];
|
||||
|
||||
return [0];
|
||||
}
|
||||
else if ($crs == parent::ENUM_NONE)
|
||||
{
|
||||
if ($eventIds = DB::Aowow()->selectCol('SELECT `id` FROM ?_events WHERE `holidayId` <> 0'))
|
||||
if ($goGuids = DB::World()->selectCol('SELECT DISTINCT `guid` FROM game_event_gameobject WHERE `eventEntry` IN (?a)', $eventIds))
|
||||
return ['s.guid', $goGuids, '!'];
|
||||
|
||||
return [0];
|
||||
}
|
||||
else if (in_array($crs, self::$enums[$cr]))
|
||||
{
|
||||
if ($eventIds = DB::Aowow()->selectCol('SELECT `id` FROM ?_events WHERE `holidayId` = ?d', $crs))
|
||||
if ($goGuids = DB::World()->selectCol('SELECT DISTINCT `guid` FROM game_event_gameobject WHERE `eventEntry` IN (?a)', $eventIds))
|
||||
return ['s.guid', $goGuids];
|
||||
|
||||
return [0];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
182
includes/dbtypes/guide.class.php
Normal file
182
includes/dbtypes/guide.class.php
Normal file
@@ -0,0 +1,182 @@
|
||||
<?php
|
||||
|
||||
namespace Aowow;
|
||||
|
||||
if (!defined('AOWOW_REVISION'))
|
||||
die('illegal access');
|
||||
|
||||
|
||||
class GuideList extends DBTypeList
|
||||
{
|
||||
use ListviewHelper;
|
||||
|
||||
public const /* array */ STATUS_COLORS = array(
|
||||
GUIDE_STATUS_DRAFT => '#71D5FF',
|
||||
GUIDE_STATUS_REVIEW => '#FFFF00',
|
||||
GUIDE_STATUS_APPROVED => '#1EFF00',
|
||||
GUIDE_STATUS_REJECTED => '#FF4040',
|
||||
GUIDE_STATUS_ARCHIVED => '#FFD100'
|
||||
);
|
||||
|
||||
public static int $type = Type::GUIDE;
|
||||
public static string $brickFile = 'guide';
|
||||
public static string $dataTable = '?_guides';
|
||||
public static int $contribute = CONTRIBUTE_CO;
|
||||
|
||||
private array $article = [];
|
||||
private array $jsGlobals = [];
|
||||
|
||||
protected string $queryBase = 'SELECT g.*, g.`id` AS ARRAY_KEY FROM ?_guides g';
|
||||
protected array $queryOpts = array(
|
||||
'g' => [['a', 'c'], 'g' => 'g.`id`'],
|
||||
'a' => ['j' => ['?_account a ON a.`id` = g.`userId`', true], 's' => ', IFNULL(a.`username`, "") AS "author"'],
|
||||
'c' => ['j' => ['?_comments c ON c.`type` = '.Type::GUIDE.' AND c.`typeId` = g.`id` AND (c.`flags` & '.CC_FLAG_DELETED.') = 0', true], 's' => ', COUNT(c.`id`) AS "comments"']
|
||||
);
|
||||
|
||||
public function __construct(array $conditions = [], array $miscData = [])
|
||||
{
|
||||
parent::__construct($conditions, $miscData);
|
||||
|
||||
if ($this->error)
|
||||
return;
|
||||
|
||||
$ratings = DB::Aowow()->select('SELECT `entry` AS ARRAY_KEY, IFNULL(SUM(`value`), 0) AS `t`, IFNULL(COUNT(*), 0) AS `n`, IFNULL(MAX(IF(`userId` = ?d, `value`, 0)), 0) AS `s` FROM ?_user_ratings WHERE `type` = ?d AND `entry` IN (?a)', User::$id, RATING_GUIDE, $this->getFoundIDs());
|
||||
|
||||
// post processing
|
||||
foreach ($this->iterate() as $id => &$_curTpl)
|
||||
{
|
||||
if (isset($ratings[$id]))
|
||||
{
|
||||
$_curTpl['nvotes'] = $ratings[$id]['n'];
|
||||
$_curTpl['rating'] = $ratings[$id]['n'] < 5 ? -1 : $ratings[$id]['t'] / $ratings[$id]['n'];
|
||||
$_curTpl['_self'] = $ratings[$id]['s'];
|
||||
}
|
||||
else
|
||||
{
|
||||
$_curTpl['nvotes'] = 0;
|
||||
$_curTpl['rating'] = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static function getName(int $id) : ?LocString
|
||||
{
|
||||
if ($n = DB::Aowow()->SelectRow('SELECT `title` AS "name_loc0" FROM ?# WHERE `id` = ?d', self::$dataTable, $id))
|
||||
return new LocString($n);
|
||||
return null;
|
||||
}
|
||||
|
||||
public function getArticle(int $rev = -1) : string
|
||||
{
|
||||
if ($rev < -1)
|
||||
$rev = -1;
|
||||
|
||||
if (empty($this->article[$rev]))
|
||||
{
|
||||
$a = DB::Aowow()->selectRow('SELECT `article`, `rev` FROM ?_articles WHERE ((`type` = ?d AND `typeId` = ?d){ OR `url` = ?}){ AND `rev`= ?d} ORDER BY `rev` DESC LIMIT 1',
|
||||
Type::GUIDE, $this->id, $this->getField('url') ?: DBSIMPLE_SKIP, $rev < 0 ? DBSIMPLE_SKIP : $rev);
|
||||
|
||||
$this->article[$a['rev']] = $a['article'];
|
||||
if ($this->article[$a['rev']])
|
||||
{
|
||||
Markup::parseTags($this->article[$a['rev']], $this->jsGlobals);
|
||||
return $this->article[$a['rev']];
|
||||
}
|
||||
else
|
||||
trigger_error('GuideList::getArticle - linked article is missing');
|
||||
}
|
||||
|
||||
return $this->article[$rev] ?? '';
|
||||
}
|
||||
|
||||
public function getListviewData(bool $addDescription = false) : array
|
||||
{
|
||||
$data = [];
|
||||
|
||||
foreach ($this->iterate() as $__)
|
||||
{
|
||||
$data[$this->id] = array(
|
||||
'id' => $this->id,
|
||||
'category' => $this->getField('category'),
|
||||
'title' => $this->getField('title'),
|
||||
'description' => $this->getField('description'),
|
||||
'sticky' => !!($this->getField('cuFlags') & CC_FLAG_STICKY),
|
||||
'nvotes' => $this->getField('nvotes'),
|
||||
'url' => '?guide=' . ($this->getField('url') ?: $this->id),
|
||||
'status' => $this->getField('status'),
|
||||
'author' => $this->getField('author'),
|
||||
'authorroles' => $this->getField('roles'),
|
||||
'rating' => $this->getField('rating'),
|
||||
'views' => $this->getField('views'),
|
||||
'comments' => $this->getField('comments'),
|
||||
// 'patch' => $this->getField(''), // 30305 - patch is pointless, use date instead
|
||||
'date' => $this->getField('date'), // ok
|
||||
'when' => date(Util::$dateFormatInternal, $this->getField('date'))
|
||||
);
|
||||
|
||||
if ($this->getField('category') == 1)
|
||||
{
|
||||
$data[$this->id]['classs'] = $this->getField('classId');
|
||||
$data[$this->id]['spec'] = $this->getField('specId');
|
||||
}
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
public function userCanView() : bool
|
||||
{
|
||||
// is owner || is staff
|
||||
return $this->getField('userId') == User::$id || User::isInGroup(U_GROUP_STAFF);
|
||||
}
|
||||
|
||||
public function canBeViewed() : bool
|
||||
{
|
||||
// currently approved || has prev. approved version
|
||||
return $this->getField('status') == GUIDE_STATUS_APPROVED || $this->getField('rev') > 0;
|
||||
}
|
||||
|
||||
public function canBeReported() : bool
|
||||
{
|
||||
// not own guide && is not archived
|
||||
return $this->getField('userId') != User::$id && $this->getField('status') != GUIDE_STATUS_ARCHIVED;
|
||||
}
|
||||
|
||||
public function getJSGlobals(int $addMask = GLOBALINFO_ANY) : array
|
||||
{
|
||||
return $this->jsGlobals;
|
||||
}
|
||||
|
||||
public function renderTooltip() : ?string
|
||||
{
|
||||
$specStr = '';
|
||||
|
||||
if ($this->getField('classId') && $this->getField('category') == 1)
|
||||
{
|
||||
if ($c = $this->getField('classId'))
|
||||
{
|
||||
$n = Lang::game('cl', $c);
|
||||
$specStr .= ' – <span class="icontiny c'.$c.'" style="background-image: url('.Cfg::get('STATIC_URL').'/images/wow/icons/tiny/class_'.ChrClass::tryFrom($c)->json().'.gif)">%s</span>';
|
||||
|
||||
if (($s = $this->getField('specId')) > -1)
|
||||
{
|
||||
$i = Game::$specIconStrings[$c][$s];
|
||||
$n = '';
|
||||
$specStr .= '<span class="icontiny c'.$c.'" style="background-image: url('.Cfg::get('STATIC_URL').'/images/wow/icons/tiny/'.$i.'.gif)">'.Lang::game('classSpecs', $c, $s).'</span>';
|
||||
}
|
||||
|
||||
$specStr = sprintf($specStr, $n);
|
||||
}
|
||||
}
|
||||
|
||||
$tt = '<table><tr><td><div style="max-width: 320px"><b class="q">'.$this->getField('title').'</b><br />';
|
||||
$tt .= '<table width="100%"><tr><td>'.Lang::game('guide').'</td><th>'.Lang::guide('byAuthor', [$this->getField('author')]).'</th></tr></table>';
|
||||
$tt .= '<table width="100%"><tr><td>'.Lang::guide('category', $this->getField('category')).$specStr.'</td><th>'.Lang::guide('patch').' 3.3.5</th></tr></table>';
|
||||
$tt .= '<div class="q" style="margin: 0.25em 0">'.$this->getField('description').'</div>';
|
||||
$tt .= '</div></td></tr></table>';
|
||||
|
||||
return $tt;
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
314
includes/dbtypes/guild.class.php
Normal file
314
includes/dbtypes/guild.class.php
Normal file
@@ -0,0 +1,314 @@
|
||||
<?php
|
||||
|
||||
namespace Aowow;
|
||||
|
||||
if (!defined('AOWOW_REVISION'))
|
||||
die('illegal access');
|
||||
|
||||
|
||||
class GuildList extends DBTypeList
|
||||
{
|
||||
use profilerHelper, listviewHelper;
|
||||
|
||||
public static int $contribute = CONTRIBUTE_NONE;
|
||||
|
||||
public function getListviewData() : array
|
||||
{
|
||||
$this->getGuildScores();
|
||||
|
||||
$data = [];
|
||||
foreach ($this->iterate() as $__)
|
||||
{
|
||||
$data[$this->id] = array(
|
||||
'name' => '$"'.str_replace ('"', '', $this->curTpl['name']).'"', // MUST be a string, omit any quotes in name
|
||||
'members' => $this->curTpl['members'],
|
||||
'faction' => $this->curTpl['faction'],
|
||||
'achievementpoints' => $this->getField('achievementpoints'),
|
||||
'gearscore' => $this->getField('gearscore'),
|
||||
'realm' => Profiler::urlize($this->curTpl['realmName'], true),
|
||||
'realmname' => $this->curTpl['realmName'],
|
||||
// 'battlegroup' => Profiler::urlize($this->curTpl['battlegroup']), // was renamed to subregion somewhere around cata release
|
||||
// 'battlegroupname' => $this->curTpl['battlegroup'],
|
||||
'region' => Profiler::urlize($this->curTpl['region'])
|
||||
);
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
private function getGuildScores() : void
|
||||
{
|
||||
/*
|
||||
Guild gear scores and achievement points are derived using a weighted average of all of the known characters in that guild.
|
||||
Guilds with at least 25 level 80 players receive full benefit of the top 25 characters' gear scores, while guilds with at least 10 level 80 characters receive a slight penalty,
|
||||
at least 1 level 80 a moderate penalty, and no level 80 characters a severe penalty. [...]
|
||||
Instead of being based on level, achievement point averages are based around 1,500 points, but the same penalties apply.
|
||||
*/
|
||||
$guilds = array_column($this->templates, 'id');
|
||||
if (!$guilds)
|
||||
return;
|
||||
|
||||
$stats = DB::Aowow()->select('SELECT `guild` AS ARRAY_KEY, `id` AS ARRAY_KEY2, `level`, `gearscore`, `achievementpoints`, IF(`cuFlags` & ?d, 0, 1) AS "synced" FROM ?_profiler_profiles WHERE `guild` IN (?a) ORDER BY `gearscore` DESC', PROFILER_CU_NEEDS_RESYNC, $guilds);
|
||||
foreach ($this->iterate() as &$_curTpl)
|
||||
{
|
||||
$id = $_curTpl['id'];
|
||||
if (empty($stats[$id]))
|
||||
continue;
|
||||
|
||||
$guildStats = array_filter($stats[$id], function ($x) { return $x['synced']; } );
|
||||
if (!$guildStats)
|
||||
continue;
|
||||
|
||||
$nMaxLevel = count(array_filter($stats[$id], function ($x) { return $x['level'] >= MAX_LEVEL; } ));
|
||||
$levelMod = 1.0;
|
||||
|
||||
if ($nMaxLevel < 25)
|
||||
$levelMod = 0.85;
|
||||
if ($nMaxLevel < 10)
|
||||
$levelMod = 0.66;
|
||||
if ($nMaxLevel < 1)
|
||||
$levelMod = 0.20;
|
||||
|
||||
$totalGS = $totalAP = $nMembers = 0;
|
||||
foreach ($guildStats as $gs)
|
||||
{
|
||||
$totalGS += $gs['gearscore'] * $levelMod * min($gs['level'], MAX_LEVEL) / MAX_LEVEL;
|
||||
$totalAP += $gs['achievementpoints'] * $levelMod * min($gs['achievementpoints'], 1500) / 1500;
|
||||
$nMembers += min($gs['level'], MAX_LEVEL) / MAX_LEVEL;
|
||||
}
|
||||
|
||||
$_curTpl['gearscore'] = intval($totalGS / $nMembers);
|
||||
$_curTpl['achievementpoints'] = intval($totalAP / $nMembers);
|
||||
}
|
||||
}
|
||||
|
||||
public static function getName(int $id) : ?LocString { return null; }
|
||||
|
||||
public function renderTooltip() : ?string { return null; }
|
||||
public function getJSGlobals(int $addMask = 0) : array { return []; }
|
||||
}
|
||||
|
||||
|
||||
class GuildListFilter extends Filter
|
||||
{
|
||||
use TrProfilerFilter;
|
||||
|
||||
protected string $type = 'guilds';
|
||||
protected static array $genericFilter = [];
|
||||
protected static array $inputFields = array(
|
||||
'na' => [parent::V_REGEX, parent::PATTERN_NAME, false], // name - only printable chars, no delimiter
|
||||
'ma' => [parent::V_EQUAL, 1, false], // match any / all filter
|
||||
'ex' => [parent::V_EQUAL, 'on', false], // only match exact
|
||||
'si' => [parent::V_LIST, [SIDE_ALLIANCE, SIDE_HORDE], false], // side
|
||||
'rg' => [parent::V_CALLBACK, 'cbRegionCheck', false], // region
|
||||
'sv' => [parent::V_CALLBACK, 'cbServerCheck', false], // server
|
||||
);
|
||||
|
||||
public array $extraOpts = [];
|
||||
|
||||
protected function createSQLForValues() : array
|
||||
{
|
||||
$parts = [];
|
||||
$_v = $this->values;
|
||||
|
||||
// region (rg), battlegroup (bg) and server (sv) are passed to GuildList as miscData and handled there
|
||||
|
||||
// name [str]
|
||||
if ($_v['na'])
|
||||
if ($_ = $this->tokenizeString(['g.name'], $_v['na'], $_v['ex'] == 'on'))
|
||||
$parts[] = $_;
|
||||
|
||||
// side [list]
|
||||
if ($_v['si'] == SIDE_ALLIANCE)
|
||||
$parts[] = ['c.race', ChrRace::fromMask(ChrRace::MASK_ALLIANCE)];
|
||||
else if ($_v['si'] == SIDE_HORDE)
|
||||
$parts[] = ['c.race', ChrRace::fromMask(ChrRace::MASK_HORDE)];
|
||||
|
||||
return $parts;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class RemoteGuildList extends GuildList
|
||||
{
|
||||
protected string $queryBase = 'SELECT `g`.*, `g`.`guildid` AS ARRAY_KEY FROM guild g';
|
||||
protected array $queryOpts = array(
|
||||
'g' => [['gm', 'c'], 'g' => 'ARRAY_KEY'],
|
||||
'gm' => ['j' => 'guild_member gm ON gm.`guildid` = g.`guildid`', 's' => ', COUNT(1) AS "members"'],
|
||||
'c' => ['j' => 'characters c ON c.`guid` = gm.`guid`', 's' => ', BIT_OR(IF(c.`race` IN (1, 3, 4, 7, 11), 1, 2)) - 1 AS "faction"']
|
||||
);
|
||||
|
||||
public function __construct(array $conditions = [], array $miscData = [])
|
||||
{
|
||||
// select DB by realm
|
||||
if (!$this->selectRealms($miscData))
|
||||
{
|
||||
trigger_error('RemoteGuildList::__construct - cannot access any realm.', E_USER_WARNING);
|
||||
return;
|
||||
}
|
||||
|
||||
parent::__construct($conditions, $miscData);
|
||||
|
||||
if ($this->error)
|
||||
return;
|
||||
|
||||
reset($this->dbNames); // only use when querying single realm
|
||||
$realms = Profiler::getRealms();
|
||||
$distrib = [];
|
||||
|
||||
// post processing
|
||||
foreach ($this->iterate() as $guid => &$curTpl)
|
||||
{
|
||||
// battlegroup
|
||||
$curTpl['battlegroup'] = Cfg::get('BATTLEGROUP');
|
||||
|
||||
$r = explode(':', $guid)[0];
|
||||
if (!empty($realms[$r]))
|
||||
{
|
||||
$curTpl['realm'] = $r;
|
||||
$curTpl['realmName'] = $realms[$r]['name'];
|
||||
$curTpl['region'] = $realms[$r]['region'];
|
||||
}
|
||||
else
|
||||
{
|
||||
trigger_error('guild #'.$guid.' belongs to nonexistent realm #'.$r, E_USER_WARNING);
|
||||
unset($this->templates[$guid]);
|
||||
continue;
|
||||
}
|
||||
|
||||
// empty name
|
||||
if (!$curTpl['name'])
|
||||
{
|
||||
trigger_error('guild #'.$guid.' on realm #'.$r.' has empty name.', E_USER_WARNING);
|
||||
unset($this->templates[$guid]);
|
||||
continue;
|
||||
}
|
||||
|
||||
// equalize distribution
|
||||
if (empty($distrib[$curTpl['realm']]))
|
||||
$distrib[$curTpl['realm']] = 1;
|
||||
else
|
||||
$distrib[$curTpl['realm']]++;
|
||||
}
|
||||
|
||||
foreach ($conditions as $c)
|
||||
if (is_int($c))
|
||||
$limit = $c;
|
||||
|
||||
$limit ??= Cfg::get('SQL_LIMIT_DEFAULT');
|
||||
if (!$limit) // int:0 means unlimited, so skip early
|
||||
return;
|
||||
|
||||
$total = array_sum($distrib);
|
||||
foreach ($distrib as &$d)
|
||||
$d = ceil($limit * $d / $total);
|
||||
|
||||
foreach ($this->iterate() as $guid => &$curTpl)
|
||||
{
|
||||
if ($limit <= 0 || $distrib[$curTpl['realm']] <= 0)
|
||||
{
|
||||
unset($this->templates[$guid]);
|
||||
continue;
|
||||
}
|
||||
|
||||
$distrib[$curTpl['realm']]--;
|
||||
$limit--;
|
||||
}
|
||||
}
|
||||
|
||||
public function initializeLocalEntries() : void
|
||||
{
|
||||
$data = [];
|
||||
foreach ($this->iterate() as $guid => $__)
|
||||
{
|
||||
$data[$guid] = array(
|
||||
'realm' => $this->getField('realm'),
|
||||
'realmGUID' => $this->getField('guildid'),
|
||||
'name' => $this->getField('name'),
|
||||
'nameUrl' => Profiler::urlize($this->getField('name')),
|
||||
'cuFlags' => PROFILER_CU_NEEDS_RESYNC
|
||||
);
|
||||
}
|
||||
|
||||
// basic guild data
|
||||
foreach (Util::createSqlBatchInsert($data) as $ins)
|
||||
DB::Aowow()->query('INSERT INTO ?_profiler_guild (?#) VALUES '.$ins.' ON DUPLICATE KEY UPDATE `id` = `id`', array_keys(reset($data)));
|
||||
|
||||
// merge back local ids
|
||||
$localIds = DB::Aowow()->selectCol(
|
||||
'SELECT CONCAT(`realm`, ":", `realmGUID`) AS ARRAY_KEY, `id` FROM ?_profiler_guild WHERE `realm` IN (?a) AND `realmGUID` IN (?a)',
|
||||
array_column($data, 'realm'),
|
||||
array_column($data, 'realmGUID')
|
||||
);
|
||||
|
||||
foreach ($this->iterate() as $guid => &$_curTpl)
|
||||
if (isset($localIds[$guid]))
|
||||
$_curTpl['id'] = $localIds[$guid];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class LocalGuildList extends GuildList
|
||||
{
|
||||
protected string $queryBase = 'SELECT g.*, g.`id` AS ARRAY_KEY FROM ?_profiler_guild g';
|
||||
|
||||
public function __construct(array $conditions = [], array $miscData = [])
|
||||
{
|
||||
$realms = Profiler::getRealms();
|
||||
|
||||
// graft realm selection from miscData onto conditions
|
||||
if (isset($miscData['sv']))
|
||||
$realms = array_filter($realms, fn($x) => Profiler::urlize($x['name']) == Profiler::urlize($miscData['sv']));
|
||||
|
||||
if (isset($miscData['rg']))
|
||||
$realms = array_filter($realms, fn($x) => $x['region'] == $miscData['rg']);
|
||||
|
||||
if (!$realms)
|
||||
{
|
||||
trigger_error('LocalGuildList::__construct - cannot access any realm.', E_USER_WARNING);
|
||||
return;
|
||||
}
|
||||
|
||||
if ($conditions)
|
||||
{
|
||||
array_unshift($conditions, 'AND');
|
||||
$conditions = ['AND', ['realm', array_keys($realms)], $conditions];
|
||||
}
|
||||
else
|
||||
$conditions = [['realm', array_keys($realms)]];
|
||||
|
||||
parent::__construct($conditions, $miscData);
|
||||
|
||||
if ($this->error)
|
||||
return;
|
||||
|
||||
foreach ($this->iterate() as $id => &$curTpl)
|
||||
{
|
||||
if ($curTpl['realm'] && !isset($realms[$curTpl['realm']]))
|
||||
continue;
|
||||
|
||||
if (isset($realms[$curTpl['realm']]))
|
||||
{
|
||||
$curTpl['realmName'] = $realms[$curTpl['realm']]['name'];
|
||||
$curTpl['region'] = $realms[$curTpl['realm']]['region'];
|
||||
}
|
||||
|
||||
// battlegroup
|
||||
$curTpl['battlegroup'] = Cfg::get('BATTLEGROUP');
|
||||
}
|
||||
}
|
||||
|
||||
public function getProfileUrl() : string
|
||||
{
|
||||
$url = '?guild=';
|
||||
|
||||
return $url.implode('.', array(
|
||||
$this->getField('region'),
|
||||
Profiler::urlize($this->getField('realmName'), true),
|
||||
Profiler::urlize($this->getField('name'))
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
?>
|
||||
227
includes/dbtypes/icon.class.php
Normal file
227
includes/dbtypes/icon.class.php
Normal file
@@ -0,0 +1,227 @@
|
||||
<?php
|
||||
|
||||
namespace Aowow;
|
||||
|
||||
if (!defined('AOWOW_REVISION'))
|
||||
die('illegal access');
|
||||
|
||||
|
||||
class IconList extends DBTypeList
|
||||
{
|
||||
use listviewHelper;
|
||||
|
||||
public static int $type = Type::ICON;
|
||||
public static string $brickFile = 'icongallery';
|
||||
public static string $dataTable = '?_icons';
|
||||
public static int $contribute = CONTRIBUTE_CO;
|
||||
|
||||
private string $pseudoQry = 'SELECT `iconId` AS ARRAY_KEY, COUNT(*) FROM ?# WHERE `iconId` IN (?a) GROUP BY `iconId`';
|
||||
private array $pseudoJoin = array(
|
||||
'nItems' => '?_items',
|
||||
'nSpells' => '?_spell',
|
||||
'nAchievements' => '?_achievement',
|
||||
'nCurrencies' => '?_currencies',
|
||||
'nPets' => '?_pet'
|
||||
);
|
||||
|
||||
protected string $queryBase = 'SELECT ic.*, ic.`id` AS ARRAY_KEY FROM ?_icons ic';
|
||||
/* this works, but takes ~100x more time than i'm comfortable with .. kept as reference
|
||||
protected array $queryOpts = array( // 29 => Type::ICON
|
||||
'ic' => [['s', 'i', 'a', 'c', 'p'], 'g' => 'ic.id'],
|
||||
'i' => ['j' => ['?_items `i` ON `i`.`iconId` = `ic`.`id`', true], 's' => ', COUNT(DISTINCT `i`.`id`) AS "nItems"'],
|
||||
's' => ['j' => ['?_spell `s` ON `s`.`iconId` = `ic`.`id`', true], 's' => ', COUNT(DISTINCT `s`.`id`) AS "nSpells"'],
|
||||
'a' => ['j' => ['?_achievement `a` ON `a`.`iconId` = `ic`.`id`', true], 's' => ', COUNT(DISTINCT `a`.`id`) AS "nAchievements"'],
|
||||
'c' => ['j' => ['?_currencies `c` ON `c`.`iconId` = `ic`.`id`', true], 's' => ', COUNT(DISTINCT `c`.`id`) AS "nCurrencies"'],
|
||||
'p' => ['j' => ['?_pet `p` ON `p`.`iconId` = `ic`.`id`', true], 's' => ', COUNT(DISTINCT `p`.`id`) AS "nPets"']
|
||||
);
|
||||
*/
|
||||
|
||||
public function __construct(array $conditions = [], array $miscData = [])
|
||||
{
|
||||
parent::__construct($conditions, $miscData);
|
||||
|
||||
if (!$this->getFoundIDs())
|
||||
return;
|
||||
|
||||
foreach ($this->pseudoJoin as $var => $tbl)
|
||||
{
|
||||
$res = DB::Aowow()->selectCol($this->pseudoQry, $tbl, $this->getFoundIDs());
|
||||
foreach ($res as $icon => $qty)
|
||||
$this->templates[$icon][$var] = $qty;
|
||||
}
|
||||
}
|
||||
|
||||
public static function getName(int $id) : ?LocString
|
||||
{
|
||||
if ($n = DB::Aowow()->selectRow('SELECT `name` AS "name_loc0" FROM ?# WHERE `id` = ?d', self::$dataTable, $id))
|
||||
return new LocString($n);
|
||||
return null;
|
||||
}
|
||||
|
||||
public function getListviewData(int $addInfoMask = 0x0) : array
|
||||
{
|
||||
$data = [];
|
||||
|
||||
foreach ($this->iterate() as $__)
|
||||
{
|
||||
$data[$this->id] = array(
|
||||
'id' => $this->id,
|
||||
'name' => $this->getField('name', true, true),
|
||||
'icon' => $this->getField('name', true, true),
|
||||
'itemcount' => (int)$this->getField('nItems'),
|
||||
'spellcount' => (int)$this->getField('nSpells'),
|
||||
'achievementcount' => (int)$this->getField('nAchievements'),
|
||||
'npccount' => 0, // UNUSED
|
||||
'petabilitycount' => 0, // UNUSED
|
||||
'currencycount' => (int)$this->getField('nCurrencies'),
|
||||
'missionabilitycount' => 0, // UNUSED
|
||||
'buildingcount' => 0, // UNUSED
|
||||
'petcount' => (int)$this->getField('nPets'),
|
||||
'threatcount' => 0, // UNUSED
|
||||
'classcount' => 0 // class icons are hardcoded and not referenced in dbc
|
||||
);
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
public function getJSGlobals(int $addMask = GLOBALINFO_ANY) : array
|
||||
{
|
||||
$data = [];
|
||||
|
||||
foreach ($this->iterate() as $__)
|
||||
$data[Type::ICON][$this->id] = ['name' => $this->getField('name', true, true), 'icon' => $this->getField('name', true, true)];
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
public function renderTooltip() : ?string { return null; }
|
||||
}
|
||||
|
||||
|
||||
class IconListFilter extends Filter
|
||||
{
|
||||
private array $totalUses = [];
|
||||
private array $criterion2field = array(
|
||||
1 => '?_items', // items [num]
|
||||
2 => '?_spell', // spells [num]
|
||||
3 => '?_achievement', // achievements [num]
|
||||
// 4 => '', // battlepets [num]
|
||||
// 5 => '', // battlepetabilities [num]
|
||||
6 => '?_currencies', // currencies [num]
|
||||
// 7 => '', // garrisonabilities [num]
|
||||
// 8 => '', // garrisonbuildings [num]
|
||||
9 => '?_pet', // hunterpets [num]
|
||||
// 10 => '', // garrisonmissionthreats [num]
|
||||
11 => '', // classes [num]
|
||||
13 => '' // used [num]
|
||||
);
|
||||
|
||||
protected string $type = 'icons';
|
||||
protected static array $genericFilter = array(
|
||||
1 => [parent::CR_CALLBACK, 'cbUseAny' ], // items [num]
|
||||
2 => [parent::CR_CALLBACK, 'cbUseAny' ], // spells [num]
|
||||
3 => [parent::CR_CALLBACK, 'cbUseAny' ], // achievements [num]
|
||||
6 => [parent::CR_CALLBACK, 'cbUseAny' ], // currencies [num]
|
||||
9 => [parent::CR_CALLBACK, 'cbUseAny' ], // hunterpets [num]
|
||||
11 => [parent::CR_NYI_PH, null, 0], // classes [num]
|
||||
13 => [parent::CR_CALLBACK, 'cbUseAll' ] // used [num]
|
||||
);
|
||||
|
||||
protected static array $inputFields = array(
|
||||
'cr' => [parent::V_LIST, [1, 2, 3, 6, 9, 11, 13], true ], // criteria ids
|
||||
'crs' => [parent::V_RANGE, [1, 6], true ], // criteria operators
|
||||
'crv' => [parent::V_REGEX, parent::PATTERN_INT, true ], // criteria values - all criteria are numeric here
|
||||
'na' => [parent::V_REGEX, parent::PATTERN_NAME, false], // name - only printable chars, no delimiter
|
||||
'ma' => [parent::V_EQUAL, 1, false] // match any / all filter
|
||||
);
|
||||
|
||||
public array $extraOpts = [];
|
||||
|
||||
private function _getCnd(string $op, int $val, string $tbl) : ?array
|
||||
{
|
||||
switch ($op)
|
||||
{
|
||||
case '>':
|
||||
case '>=':
|
||||
case '=':
|
||||
$ids = DB::Aowow()->selectCol('SELECT `iconId` AS ARRAY_KEY, COUNT(*) AS "n" FROM ?# GROUP BY `iconId` HAVING n '.$op.' '.$val, $tbl);
|
||||
return $ids ? ['id', array_keys($ids)] : [1];
|
||||
case '<=':
|
||||
if ($val)
|
||||
$op = '>';
|
||||
break;
|
||||
case '<':
|
||||
if ($val)
|
||||
$op = '>=';
|
||||
break;
|
||||
case '!=':
|
||||
if ($val)
|
||||
$op = '=';
|
||||
break;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
|
||||
$ids = DB::Aowow()->selectCol('SELECT `iconId` AS ARRAY_KEY, COUNT(*) AS "n" FROM ?# GROUP BY `iconId` HAVING n '.$op.' '.$val, $tbl);
|
||||
return $ids ? ['id', array_keys($ids), '!'] : [1];
|
||||
}
|
||||
|
||||
protected function createSQLForValues() : array
|
||||
{
|
||||
$parts = [];
|
||||
$_v = &$this->values;
|
||||
|
||||
//string
|
||||
if ($_v['na'])
|
||||
if ($_ = $this->tokenizeString(['name']))
|
||||
$parts[] = $_;
|
||||
|
||||
return $parts;
|
||||
}
|
||||
|
||||
protected function cbUseAny(int $cr, int $crs, string $crv) : ?array
|
||||
{
|
||||
if (Util::checkNumeric($crv, NUM_CAST_INT) && $this->int2Op($crs))
|
||||
return $this->_getCnd($crs, $crv, $this->criterion2field[$cr]);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
protected function cbUseAll(int $cr, int $crs, string $crv) : ?array
|
||||
{
|
||||
if (!Util::checkNumeric($crv, NUM_CAST_INT) || !$this->int2Op($crs))
|
||||
return null;
|
||||
|
||||
if (!$this->totalUses)
|
||||
{
|
||||
foreach ($this->criterion2field as $tbl)
|
||||
{
|
||||
if (!$tbl)
|
||||
continue;
|
||||
|
||||
$res = DB::Aowow()->selectCol('SELECT `iconId` AS ARRAY_KEY, COUNT(*) AS "n" FROM ?# GROUP BY `iconId`', $tbl);
|
||||
Util::arraySumByKey($this->totalUses, $res);
|
||||
}
|
||||
}
|
||||
|
||||
if ($crs == '=')
|
||||
$crs = '==';
|
||||
|
||||
$op = $crs;
|
||||
if ($crs == '<=' && $crv)
|
||||
$op = '>';
|
||||
else if ($crs == '<' && $crv)
|
||||
$op = '>=';
|
||||
else if ($crs == '!=' && $crv)
|
||||
$op = '==';
|
||||
$ids = array_filter($this->totalUses, fn($x) => eval('return '.$x.' '.$op.' '.$crv.';'));
|
||||
|
||||
if ($crs != $op)
|
||||
return $ids ? ['id', array_keys($ids), '!'] : [1];
|
||||
else
|
||||
return $ids ? ['id', array_keys($ids)] : ['id', array_keys($this->totalUses), '!'];
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
2655
includes/dbtypes/item.class.php
Normal file
2655
includes/dbtypes/item.class.php
Normal file
File diff suppressed because it is too large
Load Diff
254
includes/dbtypes/itemset.class.php
Normal file
254
includes/dbtypes/itemset.class.php
Normal file
@@ -0,0 +1,254 @@
|
||||
<?php
|
||||
|
||||
namespace Aowow;
|
||||
|
||||
if (!defined('AOWOW_REVISION'))
|
||||
die('illegal access');
|
||||
|
||||
|
||||
class ItemsetList extends DBTypeList
|
||||
{
|
||||
use ListviewHelper;
|
||||
|
||||
public static int $type = Type::ITEMSET;
|
||||
public static string $brickFile = 'itemset';
|
||||
public static string $dataTable = '?_itemset';
|
||||
public array $pieceToSet = []; // used to build g_items and search
|
||||
|
||||
private array $classes = []; // used to build g_classes
|
||||
|
||||
protected string $queryBase = 'SELECT `set`.*, `set`.`id` AS ARRAY_KEY FROM ?_itemset `set`';
|
||||
protected array $queryOpts = array(
|
||||
'set' => ['o' => 'maxlevel DESC'],
|
||||
'e' => ['j' => ['?_events e ON `e`.`id` = `set`.`eventId`', true], 's' => ', e.`holidayId`'],
|
||||
'src' => ['j' => ['?_source src ON `src`.`typeId` = `set`.`id` AND `src`.`type` = 4', true], 's' => ', `src1`, `src2`, `src3`, `src4`, `src5`, `src6`, `src7`, `src8`, `src9`, `src10`, `src11`, `src12`, `src13`, `src14`, `src15`, `src16`, `src17`, `src18`, `src19`, `src20`, `src21`, `src22`, `src23`, `src24`']
|
||||
);
|
||||
|
||||
public function __construct(array $conditions = [], array $miscData = [])
|
||||
{
|
||||
parent::__construct($conditions, $miscData);
|
||||
|
||||
// post processing
|
||||
foreach ($this->iterate() as &$_curTpl)
|
||||
{
|
||||
$_curTpl['classes'] = ChrClass::fromMask($_curTpl['classMask']);
|
||||
$this->classes = array_merge($this->classes, $_curTpl['classes']);
|
||||
|
||||
$_curTpl['pieces'] = [];
|
||||
for ($i = 1; $i < 10; $i++)
|
||||
{
|
||||
if ($piece = $_curTpl['item'.$i])
|
||||
{
|
||||
$_curTpl['pieces'][] = $piece;
|
||||
$this->pieceToSet[$piece] = $this->id;
|
||||
}
|
||||
}
|
||||
}
|
||||
$this->classes = array_unique($this->classes);
|
||||
}
|
||||
|
||||
public function getListviewData() : array
|
||||
{
|
||||
$data = [];
|
||||
|
||||
foreach ($this->iterate() as $__)
|
||||
{
|
||||
$data[$this->id] = array(
|
||||
'id' => $this->id,
|
||||
'idbak' => $this->curTpl['refSetId'],
|
||||
'name' => (7 - $this->curTpl['quality']).$this->getField('name', true),
|
||||
'minlevel' => $this->curTpl['minLevel'],
|
||||
'maxlevel' => $this->curTpl['maxLevel'],
|
||||
'note' => $this->curTpl['contentGroup'],
|
||||
'type' => $this->curTpl['type'],
|
||||
'reqclass' => $this->curTpl['classMask'],
|
||||
'classes' => $this->curTpl['classes'],
|
||||
'pieces' => $this->curTpl['pieces'],
|
||||
'heroic' => $this->curTpl['heroic']
|
||||
);
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
public function getJSGlobals(int $addMask = GLOBALINFO_ANY) : array
|
||||
{
|
||||
$data = [];
|
||||
|
||||
if ($this->classes && ($addMask & GLOBALINFO_RELATED))
|
||||
$data[Type::CHR_CLASS] = array_combine($this->classes, $this->classes);
|
||||
|
||||
if ($this->pieceToSet && ($addMask & GLOBALINFO_SELF))
|
||||
$data[Type::ITEM] = array_combine(array_keys($this->pieceToSet), array_keys($this->pieceToSet));
|
||||
|
||||
if ($addMask & GLOBALINFO_SELF)
|
||||
foreach ($this->iterate() as $id => $__)
|
||||
$data[Type::ITEMSET][$id] = ['name' => $this->getField('name', true)];
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
public function renderTooltip() : ?string
|
||||
{
|
||||
if (!$this->curTpl)
|
||||
return null;
|
||||
|
||||
$x = '<table><tr><td>';
|
||||
$x .= '<span class="q'.$this->getField('quality').'">'.$this->getField('name', true).'</span><br />';
|
||||
|
||||
$nCl = 0;
|
||||
if ($_ = $this->getField('classMask'))
|
||||
{
|
||||
$jsg = [];
|
||||
$cl = Lang::getClassString($_, $jsg);
|
||||
$nCl = count($jsg);
|
||||
$x .= Util::ucFirst($nCl > 1 ? Lang::game('classes') : Lang::game('class')).Lang::main('colon').$cl.'<br />';
|
||||
}
|
||||
|
||||
if ($_ = $this->getField('contentGroup'))
|
||||
$x .= Lang::itemset('notes', $_).($this->getField('heroic') ? ' <i class="q2">('.Lang::item('heroic').')</i>' : '').'<br />';
|
||||
|
||||
if (!$nCl || !$this->getField('type'))
|
||||
$x.= Lang::itemset('types', $this->getField('type')).'<br />';
|
||||
|
||||
if ($bonuses = $this->getBonuses())
|
||||
{
|
||||
$x .= '<span>';
|
||||
|
||||
foreach ($bonuses as $b)
|
||||
$x .= '<br /><span class="q13">'.$b['bonus'].' '.Lang::itemset('_pieces').Lang::main('colon').'</span>'.$b['desc'];
|
||||
|
||||
$x .= '</span>';
|
||||
}
|
||||
|
||||
$x .= '</td></tr></table>';
|
||||
|
||||
return $x;
|
||||
}
|
||||
|
||||
public function getBonuses() : array
|
||||
{
|
||||
$spells = [];
|
||||
for ($i = 1; $i < 9; $i++)
|
||||
{
|
||||
$spl = $this->getField('spell'.$i);
|
||||
$qty = $this->getField('bonus'.$i);
|
||||
|
||||
// cant use spell as index, would change order
|
||||
if ($spl && $qty)
|
||||
$spells[] = ['id' => $spl, 'bonus' => $qty];
|
||||
}
|
||||
|
||||
// sort by required pieces ASC
|
||||
usort($spells, fn(array $a, array $b) => $a['bonus'] <=> $b['bonus']);
|
||||
|
||||
$setSpells = new SpellList(array(['s.id', array_column($spells, 'id')]));
|
||||
foreach ($setSpells->iterate() as $spellId => $__)
|
||||
{
|
||||
foreach ($spells as &$s)
|
||||
{
|
||||
if ($spellId != $s['id'])
|
||||
continue;
|
||||
|
||||
$s['desc'] = $setSpells->parseText('description', $this->getField('reqLevel') ?: MAX_LEVEL)[0];
|
||||
}
|
||||
}
|
||||
|
||||
return $spells;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// missing filter: "Available to Players"
|
||||
class ItemsetListFilter extends Filter
|
||||
{
|
||||
protected string $type = 'itemsets';
|
||||
protected static array $enums = array(
|
||||
6 => parent::ENUM_EVENT
|
||||
);
|
||||
|
||||
protected static array $genericFilter = array(
|
||||
2 => [parent::CR_NUMERIC, 'id', NUM_CAST_INT, true], // id
|
||||
3 => [parent::CR_NUMERIC, 'npieces', NUM_CAST_INT ], // pieces
|
||||
4 => [parent::CR_STRING, 'bonusText', STR_LOCALIZED ], // bonustext
|
||||
5 => [parent::CR_BOOLEAN, 'heroic' ], // heroic
|
||||
6 => [parent::CR_ENUM, 'e.holidayId', true, true], // relatedevent
|
||||
8 => [parent::CR_FLAG, 'cuFlags', CUSTOM_HAS_COMMENT ], // hascomments
|
||||
9 => [parent::CR_FLAG, 'cuFlags', CUSTOM_HAS_SCREENSHOT ], // hasscreenshots
|
||||
10 => [parent::CR_FLAG, 'cuFlags', CUSTOM_HAS_VIDEO ], // hasvideos
|
||||
12 => [parent::CR_CALLBACK, 'cbAvaliable', ] // available to players [yn]
|
||||
);
|
||||
|
||||
protected static array $inputFields = array(
|
||||
'cr' => [parent::V_RANGE, [2, 12], true ], // criteria ids
|
||||
'crs' => [parent::V_LIST, [parent::ENUM_NONE, parent::ENUM_ANY, [0, 424]], true ], // criteria operators
|
||||
'crv' => [parent::V_REGEX, parent::PATTERN_CRV, true ], // criteria values - only printable chars, no delimiters
|
||||
'na' => [parent::V_REGEX, parent::PATTERN_NAME, false], // name / description - only printable chars, no delimiter
|
||||
'ma' => [parent::V_EQUAL, 1, false], // match any / all filter
|
||||
'qu' => [parent::V_RANGE, [0, 7], true ], // quality
|
||||
'ty' => [parent::V_RANGE, [1, 12], true ], // set type
|
||||
'minle' => [parent::V_RANGE, [1, 999], false], // min item level
|
||||
'maxle' => [parent::V_RANGE, [1, 999], false], // max itemlevel
|
||||
'minrl' => [parent::V_RANGE, [1, MAX_LEVEL], false], // min required level
|
||||
'maxrl' => [parent::V_RANGE, [1, MAX_LEVEL], false], // max required level
|
||||
'cl' => [parent::V_LIST, [[1, 9], 11], false], // class
|
||||
'ta' => [parent::V_RANGE, [1, 30], false] // tag / content group
|
||||
);
|
||||
|
||||
protected function createSQLForValues() : array
|
||||
{
|
||||
$parts = [];
|
||||
$_v = &$this->values;
|
||||
|
||||
// name [str]
|
||||
if ($_v['na'])
|
||||
if ($_ = $this->tokenizeString(['name_loc'.Lang::getLocale()->value]))
|
||||
$parts[] = $_;
|
||||
|
||||
// quality [enum]
|
||||
if ($_v['qu'])
|
||||
$parts[] = ['quality', $_v['qu']];
|
||||
|
||||
// type [enum]
|
||||
if ($_v['ty'])
|
||||
$parts[] = ['type', $_v['ty']];
|
||||
|
||||
// itemLevel min [int]
|
||||
if ($_v['minle'])
|
||||
$parts[] = ['minLevel', $_v['minle'], '>='];
|
||||
|
||||
// itemLevel max [int]
|
||||
if ($_v['maxle'])
|
||||
$parts[] = ['maxLevel', $_v['maxle'], '<='];
|
||||
|
||||
// reqLevel min [int]
|
||||
if ($_v['minrl'])
|
||||
$parts[] = ['reqLevel', $_v['minrl'], '>='];
|
||||
|
||||
// reqLevel max [int]
|
||||
if ($_v['maxrl'])
|
||||
$parts[] = ['reqLevel', $_v['maxrl'], '<='];
|
||||
|
||||
// class [enum]
|
||||
if ($_v['cl'])
|
||||
$parts[] = ['classMask', $this->list2Mask([$_v['cl']]), '&'];
|
||||
|
||||
// tag [enum]
|
||||
if ($_v['ta'])
|
||||
$parts[] = ['contentGroup', intVal($_v['ta'])];
|
||||
|
||||
return $parts;
|
||||
}
|
||||
|
||||
protected function cbAvaliable(int $cr, int $crs, string $crv) : ?array
|
||||
{
|
||||
return match ($crs)
|
||||
{
|
||||
1 => ['src.typeId', null, '!'], // Yes
|
||||
2 => ['src.typeId', null], // No
|
||||
default => null
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
77
includes/dbtypes/mail.class.php
Normal file
77
includes/dbtypes/mail.class.php
Normal file
@@ -0,0 +1,77 @@
|
||||
<?php
|
||||
|
||||
namespace Aowow;
|
||||
|
||||
if (!defined('AOWOW_REVISION'))
|
||||
die('illegal access');
|
||||
|
||||
|
||||
class MailList extends DBTypeList
|
||||
{
|
||||
public static int $type = Type::MAIL;
|
||||
public static string $brickFile = 'mail';
|
||||
public static string $dataTable = '?_mails';
|
||||
|
||||
protected string $queryBase = 'SELECT m.*, m.`id` AS ARRAY_KEY FROM ?_mails m';
|
||||
protected array $queryOpts = [];
|
||||
|
||||
public function __construct(array $conditions = [], array $miscData = [])
|
||||
{
|
||||
parent::__construct($conditions, $miscData);
|
||||
|
||||
if ($this->error)
|
||||
return;
|
||||
|
||||
// post processing
|
||||
foreach ($this->iterate() as $_id => &$_curTpl)
|
||||
{
|
||||
$_curTpl['name'] = Util::localizedString($_curTpl, 'subject', true);
|
||||
if (!$_curTpl['name'])
|
||||
{
|
||||
$_curTpl['name'] = sprintf(Lang::mail('untitled'), $_id);
|
||||
$_curTpl['subject_loc0'] = $_curTpl['name'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static function getName(int $id) : ?LocString
|
||||
{
|
||||
if ($n = DB::Aowow()->SelectRow('SELECT `subject_loc0`, `subject_loc2`, `subject_loc3`, `subject_loc4`, `subject_loc6`, `subject_loc8` FROM ?# WHERE `id` = ?d', self::$dataTable, $id))
|
||||
return new LocString($n, 'subject');
|
||||
return null;
|
||||
}
|
||||
|
||||
public function getListviewData() : array
|
||||
{
|
||||
$data = [];
|
||||
|
||||
foreach ($this->iterate() as $__)
|
||||
{
|
||||
$body = str_replace('[br]', ' ', Util::parseHtmlText($this->getField('text', true), true));
|
||||
|
||||
$data[$this->id] = array(
|
||||
'id' => $this->id,
|
||||
'subject' => $this->getField('subject', true),
|
||||
'body' => Lang::trimTextClean($body),
|
||||
'attachments' => [$this->getField('attachment')]
|
||||
);
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
public function getJSGlobals(int $addMask = 0) : array
|
||||
{
|
||||
$data = [];
|
||||
|
||||
foreach ($this->iterate() as $__)
|
||||
if ($a = $this->curTpl['attachment'])
|
||||
$data[Type::ITEM][$a] = $a;
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
public function renderTooltip() : ?string { return null; }
|
||||
}
|
||||
|
||||
?>
|
||||
76
includes/dbtypes/pet.class.php
Normal file
76
includes/dbtypes/pet.class.php
Normal file
@@ -0,0 +1,76 @@
|
||||
<?php
|
||||
|
||||
namespace Aowow;
|
||||
|
||||
if (!defined('AOWOW_REVISION'))
|
||||
die('illegal access');
|
||||
|
||||
|
||||
class PetList extends DBTypeList
|
||||
{
|
||||
use ListviewHelper;
|
||||
|
||||
public static int $type = Type::PET;
|
||||
public static string $brickFile = 'pet';
|
||||
public static string $dataTable = '?_pet';
|
||||
|
||||
protected string $queryBase = 'SELECT p.*, p.`id` AS ARRAY_KEY FROM ?_pet p';
|
||||
protected array $queryOpts = array(
|
||||
'p' => [['ic']],
|
||||
'ic' => ['j' => ['?_icons ic ON p.`iconId` = ic.`id`', true], 's' => ', ic.`name` AS "iconString"'],
|
||||
);
|
||||
|
||||
public function getListviewData() : array
|
||||
{
|
||||
$data = [];
|
||||
|
||||
foreach ($this->iterate() as $__)
|
||||
{
|
||||
$data[$this->id] = array(
|
||||
'armor' => $this->curTpl['armor'],
|
||||
'damage' => $this->curTpl['damage'],
|
||||
'health' => $this->curTpl['health'],
|
||||
'diet' => $this->curTpl['foodMask'],
|
||||
'icon' => $this->curTpl['iconString'],
|
||||
'id' => $this->id,
|
||||
'maxlevel' => $this->curTpl['maxLevel'],
|
||||
'minlevel' => $this->curTpl['minLevel'],
|
||||
'name' => $this->getField('name', true),
|
||||
'type' => $this->curTpl['type'],
|
||||
'exotic' => $this->curTpl['exotic'],
|
||||
'spells' => []
|
||||
);
|
||||
|
||||
if ($this->curTpl['expansion'] > 0)
|
||||
$data[$this->id]['expansion'] = $this->curTpl['expansion'];
|
||||
|
||||
for ($i = 1; $i <= 4; $i++)
|
||||
if ($this->curTpl['spellId'.$i] > 0)
|
||||
$data[$this->id]['spells'][] = $this->curTpl['spellId'.$i];
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
public function getJSGlobals(int $addMask = GLOBALINFO_ANY) : array
|
||||
{
|
||||
$data = [];
|
||||
|
||||
foreach ($this->iterate() as $__)
|
||||
{
|
||||
if ($addMask & GLOBALINFO_RELATED)
|
||||
for ($i = 1; $i <= 4; $i++)
|
||||
if ($this->curTpl['spellId'.$i] > 0)
|
||||
$data[Type::SPELL][$this->curTpl['spellId'.$i]] = $this->curTpl['spellId'.$i];
|
||||
|
||||
if ($addMask & GLOBALINFO_SELF)
|
||||
$data[Type::PET][$this->id] = ['icon' => $this->curTpl['iconString']];
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
public function renderTooltip() : ?string { return null; }
|
||||
}
|
||||
|
||||
?>
|
||||
728
includes/dbtypes/profile.class.php
Normal file
728
includes/dbtypes/profile.class.php
Normal file
@@ -0,0 +1,728 @@
|
||||
<?php
|
||||
|
||||
namespace Aowow;
|
||||
|
||||
if (!defined('AOWOW_REVISION'))
|
||||
die('illegal access');
|
||||
|
||||
|
||||
class ProfileList extends DBTypeList
|
||||
{
|
||||
use profilerHelper, listviewHelper;
|
||||
|
||||
public static int $contribute = CONTRIBUTE_NONE;
|
||||
|
||||
public function getListviewData(int $addInfoMask = 0, array $reqCols = []) : array
|
||||
{
|
||||
$data = [];
|
||||
foreach ($this->iterate() as $__)
|
||||
{
|
||||
if (!$this->isVisibleToUser())
|
||||
continue;
|
||||
|
||||
if (($addInfoMask & PROFILEINFO_PROFILE) && !$this->isCustom())
|
||||
continue;
|
||||
|
||||
if (($addInfoMask & PROFILEINFO_CHARACTER) && $this->isCustom())
|
||||
continue;
|
||||
|
||||
$data[$this->id] = array(
|
||||
'id' => $this->getField('id'),
|
||||
'name' => $this->getField('name'),
|
||||
'race' => $this->getField('race'),
|
||||
'classs' => $this->getField('class'),
|
||||
'gender' => $this->getField('gender'),
|
||||
'level' => $this->getField('level'),
|
||||
'faction' => ChrRace::tryFrom($this->getField('race'))?->isAlliance() ? 0 : 1,
|
||||
'talenttree1' => $this->getField('talenttree1'),
|
||||
'talenttree2' => $this->getField('talenttree2'),
|
||||
'talenttree3' => $this->getField('talenttree3'),
|
||||
'talentspec' => $this->getField('activespec') + 1, // 0 => 1; 1 => 2
|
||||
'achievementpoints' => $this->getField('achievementpoints'),
|
||||
'guild' => $this->curTpl['guildname'] ? '$"'.str_replace ('"', '', $this->curTpl['guildname']).'"' : '', // force this to be a string
|
||||
'guildrank' => $this->getField('guildrank'),
|
||||
'realm' => Profiler::urlize($this->getField('realmName'), true),
|
||||
'realmname' => $this->getField('realmName'),
|
||||
// 'battlegroup' => Profiler::urlize($this->getField('battlegroup')), // was renamed to subregion somewhere around cata release
|
||||
// 'battlegroupname' => $this->getField('battlegroup'),
|
||||
'gearscore' => $this->getField('gearscore')
|
||||
);
|
||||
|
||||
if ($addInfoMask & PROFILEINFO_USER)
|
||||
$data[$this->id]['published'] = (int)!!($this->getField('cuFlags') & PROFILER_CU_PUBLISHED);
|
||||
|
||||
// for the lv this determins if the link is profile=<id> or profile=<region>.<realm>.<name>
|
||||
if (!$this->isCustom())
|
||||
$data[$this->id]['region'] = Profiler::urlize($this->getField('region'));
|
||||
|
||||
if ($addInfoMask & PROFILEINFO_ARENA)
|
||||
{
|
||||
$data[$this->id]['rating'] = $this->getField('rating');
|
||||
$data[$this->id]['captain'] = $this->getField('captain');
|
||||
$data[$this->id]['games'] = $this->getField('seasonGames');
|
||||
$data[$this->id]['wins'] = $this->getField('seasonWins');
|
||||
}
|
||||
|
||||
// Filter asked for skills - add them
|
||||
foreach ($reqCols as $col)
|
||||
$data[$this->id][$col] = $this->getField($col);
|
||||
|
||||
if ($addInfoMask & PROFILEINFO_PROFILE)
|
||||
{
|
||||
if ($_ = $this->getField('description'))
|
||||
$data[$this->id]['description'] = $_;
|
||||
|
||||
if ($_ = $this->getField('icon'))
|
||||
$data[$this->id]['icon'] = $_;
|
||||
}
|
||||
|
||||
if ($addInfoMask & PROFILEINFO_CHARACTER)
|
||||
if ($_ = $this->getField('renameItr'))
|
||||
$data[$this->id]['renameItr'] = $_;
|
||||
|
||||
if ($this->getField('cuFlags') & PROFILER_CU_PINNED)
|
||||
$data[$this->id]['pinned'] = 1;
|
||||
|
||||
if ($this->getField('cuFlags') & PROFILER_CU_DELETED)
|
||||
$data[$this->id]['deleted'] = 1;
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
public function renderTooltip() : ?string
|
||||
{
|
||||
if (!$this->curTpl)
|
||||
return null;
|
||||
|
||||
$title = '';
|
||||
$name = $this->getField('name');
|
||||
if ($_ = $this->getField('title'))
|
||||
$title = (new TitleList(array(['id', $_])))->getField($this->getField('gender') ? 'female' : 'male', true);
|
||||
|
||||
if ($this->isCustom())
|
||||
$name .= Lang::profiler('customProfile');
|
||||
else if ($title)
|
||||
$name = sprintf($title, $name);
|
||||
|
||||
$x = '<table>';
|
||||
$x .= '<tr><td><b class="q">'.$name.'</b></td></tr>';
|
||||
if ($g = $this->getField('guildname'))
|
||||
$x .= '<tr><td><'.$g.'></td></tr>';
|
||||
else if ($d = $this->getField('description'))
|
||||
$x .= '<tr><td>'.$d.'</td></tr>';
|
||||
$x .= '<tr><td>'.Lang::game('level').' '.$this->getField('level').' '.Lang::game('ra', $this->getField('race')).' '.Lang::game('cl', $this->getField('class')).'</td></tr>';
|
||||
$x .= '</table>';
|
||||
|
||||
return $x;
|
||||
}
|
||||
|
||||
public function getJSGlobals(int $addMask = 0) : array
|
||||
{
|
||||
$data = [];
|
||||
$realms = Profiler::getRealms();
|
||||
|
||||
foreach ($this->iterate() as $id => $__)
|
||||
{
|
||||
if (($addMask & PROFILEINFO_PROFILE) && $this->isCustom())
|
||||
{
|
||||
$profile = array(
|
||||
'id' => $this->getField('id'),
|
||||
'name' => $this->getField('name'),
|
||||
'race' => $this->getField('race'),
|
||||
'classs' => $this->getField('class'),
|
||||
'level' => $this->getField('level'),
|
||||
'gender' => $this->getField('gender')
|
||||
);
|
||||
|
||||
if ($_ = $this->getField('icon'))
|
||||
$profile['icon'] = $_;
|
||||
|
||||
$data[] = $profile;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($addMask & PROFILEINFO_CHARACTER && !$this->isCustom())
|
||||
{
|
||||
if (!isset($realms[$this->getField('realm')]))
|
||||
continue;
|
||||
|
||||
$data[] = array(
|
||||
'id' => $this->getField('id'),
|
||||
'name' => $this->getField('name'),
|
||||
'realmname' => $realms[$this->getField('realm')]['name'],
|
||||
'region' => $realms[$this->getField('realm')]['region'],
|
||||
'realm' => Profiler::urlize($realms[$this->getField('realm')]['name']),
|
||||
'race' => $this->getField('race'),
|
||||
'classs' => $this->getField('class'),
|
||||
'level' => $this->getField('level'),
|
||||
'gender' => $this->getField('gender'),
|
||||
'pinned' => $this->getField('cuFlags') & PROFILER_CU_PINNED ? 1 : 0
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
public function isCustom() : bool
|
||||
{
|
||||
return $this->getField('cuFlags') & PROFILER_CU_PROFILE;
|
||||
}
|
||||
|
||||
public function isVisibleToUser() : bool
|
||||
{
|
||||
if (!$this->isCustom() || User::isInGroup(U_GROUP_ADMIN | U_GROUP_BUREAU))
|
||||
return true;
|
||||
|
||||
if ($this->getField('cuFlags') & PROFILER_CU_DELETED)
|
||||
return false;
|
||||
|
||||
if (User::$id == $this->getField('user'))
|
||||
return true;
|
||||
|
||||
return (bool)($this->getField('cuFlags') & PROFILER_CU_PUBLISHED);
|
||||
}
|
||||
|
||||
public function getIcon() : string
|
||||
{
|
||||
if ($_ = $this->getField('icon'))
|
||||
return $_;
|
||||
|
||||
return sprintf('chr_%s_%s_%s%02d',
|
||||
ChrRace::from($this->getField('race'))->json(),
|
||||
$this->getField('gender') ? 'female' : 'male',
|
||||
ChrClass::from($this->getField('class'))->json(),
|
||||
max(1, floor(($this->getField('level') - 60) / 10) + 2)
|
||||
);
|
||||
}
|
||||
|
||||
public static function getName(int $id) : ?LocString { return null; }
|
||||
}
|
||||
|
||||
|
||||
class ProfileListFilter extends Filter
|
||||
{
|
||||
use TrProfilerFilter;
|
||||
|
||||
protected string $type = 'profiles';
|
||||
protected static array $genericFilter = array(
|
||||
2 => [parent::CR_NUMERIC, 'gearscore', NUM_CAST_INT ], // gearscore [num]
|
||||
3 => [parent::CR_CALLBACK, 'cbAchievs', null, null], // achievementpoints [num]
|
||||
5 => [parent::CR_NUMERIC, 'talenttree1', NUM_CAST_INT ], // talenttree1 [num]
|
||||
6 => [parent::CR_NUMERIC, 'talenttree2', NUM_CAST_INT ], // talenttree2 [num]
|
||||
7 => [parent::CR_NUMERIC, 'talenttree3', NUM_CAST_INT ], // talenttree3 [num]
|
||||
9 => [parent::CR_STRING, 'g.name' ], // guildname
|
||||
10 => [parent::CR_CALLBACK, 'cbHasGuildRank', null, null], // guildrank
|
||||
12 => [parent::CR_CALLBACK, 'cbTeamName', 2, null], // teamname2v2
|
||||
15 => [parent::CR_CALLBACK, 'cbTeamName', 3, null], // teamname3v3
|
||||
18 => [parent::CR_CALLBACK, 'cbTeamName', 5, null], // teamname5v5
|
||||
13 => [parent::CR_CALLBACK, 'cbTeamRating', 2, null], // teamrtng2v2
|
||||
16 => [parent::CR_CALLBACK, 'cbTeamRating', 3, null], // teamrtng3v3
|
||||
19 => [parent::CR_CALLBACK, 'cbTeamRating', 5, null], // teamrtng5v5
|
||||
14 => [parent::CR_NYI_PH, null, 0 /* 2 */ ], // teamcontrib2v2 [num]
|
||||
17 => [parent::CR_NYI_PH, null, 0 /* 3 */ ], // teamcontrib3v3 [num]
|
||||
20 => [parent::CR_NYI_PH, null, 0 /* 5 */ ], // teamcontrib5v5 [num]
|
||||
21 => [parent::CR_CALLBACK, 'cbWearsItems', null, null], // wearingitem [str]
|
||||
23 => [parent::CR_CALLBACK, 'cbCompletedAcv', null, null], // completedachievement
|
||||
25 => [parent::CR_CALLBACK, 'cbProfession', SKILL_ALCHEMY, null], // alchemy [num]
|
||||
26 => [parent::CR_CALLBACK, 'cbProfession', SKILL_BLACKSMITHING, null], // blacksmithing [num]
|
||||
27 => [parent::CR_CALLBACK, 'cbProfession', SKILL_ENCHANTING, null], // enchanting [num]
|
||||
28 => [parent::CR_CALLBACK, 'cbProfession', SKILL_ENGINEERING, null], // engineering [num]
|
||||
29 => [parent::CR_CALLBACK, 'cbProfession', SKILL_HERBALISM, null], // herbalism [num]
|
||||
30 => [parent::CR_CALLBACK, 'cbProfession', SKILL_INSCRIPTION, null], // inscription [num]
|
||||
31 => [parent::CR_CALLBACK, 'cbProfession', SKILL_JEWELCRAFTING, null], // jewelcrafting [num]
|
||||
32 => [parent::CR_CALLBACK, 'cbProfession', SKILL_LEATHERWORKING, null], // leatherworking [num]
|
||||
33 => [parent::CR_CALLBACK, 'cbProfession', SKILL_MINING, null], // mining [num]
|
||||
34 => [parent::CR_CALLBACK, 'cbProfession', SKILL_SKINNING, null], // skinning [num]
|
||||
35 => [parent::CR_CALLBACK, 'cbProfession', SKILL_TAILORING, null], // tailoring [num]
|
||||
36 => [parent::CR_CALLBACK, 'cbHasGuild', null, null] // hasguild [yn]
|
||||
);
|
||||
|
||||
protected static array $inputFields = array(
|
||||
'cr' => [parent::V_RANGE, [1, 36], true ], // criteria ids
|
||||
'crs' => [parent::V_LIST, [parent::ENUM_NONE, parent::ENUM_ANY, [0, 5000]], true ], // criteria operators
|
||||
'crv' => [parent::V_REGEX, parent::PATTERN_CRV, true ], // criteria values
|
||||
'na' => [parent::V_REGEX, parent::PATTERN_NAME, false], // name - only printable chars, no delimiter
|
||||
'ma' => [parent::V_EQUAL, 1, false], // match any / all filter
|
||||
'ex' => [parent::V_EQUAL, 'on', false], // only match exact
|
||||
'si' => [parent::V_LIST, [SIDE_ALLIANCE, SIDE_HORDE], false], // side
|
||||
'ra' => [parent::V_LIST, [[1, 8], 10, 11], true ], // race
|
||||
'cl' => [parent::V_LIST, [[1, 9], 11], true ], // class
|
||||
'minle' => [parent::V_RANGE, [1, MAX_LEVEL], false], // min level
|
||||
'maxle' => [parent::V_RANGE, [1, MAX_LEVEL], false], // max level
|
||||
'rg' => [parent::V_CALLBACK, 'cbRegionCheck', false], // region
|
||||
'sv' => [parent::V_CALLBACK, 'cbServerCheck', false], // server
|
||||
);
|
||||
|
||||
public bool $useLocalList = false;
|
||||
public array $extraOpts = [];
|
||||
|
||||
/* heads up!
|
||||
a couple of filters are too complex to be run against the characters database
|
||||
if they are selected, force useage of LocalProfileList
|
||||
*/
|
||||
|
||||
public function __construct(string|array $data, array $opts = [])
|
||||
{
|
||||
parent::__construct($data, $opts);
|
||||
|
||||
if (!empty($this->criteria['cr']))
|
||||
if (array_intersect($this->criteria['cr'], [2, 5, 6, 7, 21]))
|
||||
$this->useLocalList = true;
|
||||
}
|
||||
|
||||
protected function createSQLForValues() : array
|
||||
{
|
||||
$parts = [];
|
||||
$_v = $this->values;
|
||||
|
||||
// region (rg), battlegroup (bg) and server (sv) are passed to ProflieList as miscData and handled there
|
||||
|
||||
// table key differs between remote and local :<
|
||||
$k = $this->useLocalList ? 'p' : 'c';
|
||||
|
||||
// name [str] - the table is case sensitive. Since i don't want to destroy indizes, lets alter the search terms
|
||||
if ($_v['na'])
|
||||
{
|
||||
$lower = $this->tokenizeString([$k.'.name'], Util::lower($_v['na']), $_v['ex'] == 'on', true);
|
||||
$proper = $this->tokenizeString([$k.'.name'], Util::ucWords($_v['na']), $_v['ex'] == 'on', true);
|
||||
|
||||
$parts[] = ['OR', $lower, $proper];
|
||||
}
|
||||
|
||||
// side [list]
|
||||
if ($_v['si'] == SIDE_ALLIANCE)
|
||||
$parts[] = [$k.'.race', ChrRace::fromMask(ChrRace::MASK_ALLIANCE)];
|
||||
else if ($_v['si'] == SIDE_HORDE)
|
||||
$parts[] = [$k.'.race', ChrRace::fromMask(ChrRace::MASK_HORDE)];
|
||||
|
||||
// race [list]
|
||||
if ($_v['ra'])
|
||||
$parts[] = [$k.'.race', $_v['ra']];
|
||||
|
||||
// class [list]
|
||||
if ($_v['cl'])
|
||||
$parts[] = [$k.'.class', $_v['cl']];
|
||||
|
||||
// min level [int]
|
||||
if ($_v['minle'])
|
||||
$parts[] = [$k.'.level', $_v['minle'], '>='];
|
||||
|
||||
// max level [int]
|
||||
if ($_v['maxle'])
|
||||
$parts[] = [$k.'.level', $_v['maxle'], '<='];
|
||||
|
||||
return $parts;
|
||||
}
|
||||
|
||||
protected function cbProfession(int $cr, int $crs, string $crv, $skillId) : ?array
|
||||
{
|
||||
if (!Util::checkNumeric($crv, NUM_CAST_INT) || !$this->int2Op($crs))
|
||||
return null;
|
||||
|
||||
$k = 'sk_'.Util::createHash(12);
|
||||
$col = 'skill-'.$skillId;
|
||||
|
||||
$this->fiExtraCols[$skillId] = $col;
|
||||
|
||||
if ($this->useLocalList)
|
||||
{
|
||||
$this->extraOpts[$k] = array(
|
||||
'j' => [sprintf('?_profiler_completion_skills %1$s ON `%1$s`.`id` = p.`id` AND `%1$s`.`skillId` = %2$d AND `%1$s`.`value` %3$s %4$d', $k, $skillId, $crs, $crv), true],
|
||||
's' => [', '.$k.'.`value` AS "'.$col.'"']
|
||||
);
|
||||
return [$k.'.skillId', null, '!'];
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->extraOpts[$k] = array(
|
||||
'j' => [sprintf('character_skills %1$s ON `%1$s`.`guid` = c.`guid` AND `%1$s`.`skill` = %2$d AND `%1$s`.`value` %3$s %4$d', $k, $skillId, $crs, $crv), true],
|
||||
's' => [', '.$k.'.`value` AS "'.$col.'"']
|
||||
);
|
||||
return [$k.'.skill', null, '!'];
|
||||
}
|
||||
}
|
||||
|
||||
protected function cbCompletedAcv(int $cr, int $crs, string $crv) : ?array
|
||||
{
|
||||
if (!Util::checkNumeric($crv, NUM_CAST_INT))
|
||||
return null;
|
||||
|
||||
if (!Type::validateIds(Type::ACHIEVEMENT, $crv))
|
||||
return null;
|
||||
|
||||
$k = 'acv_'.Util::createHash(12);
|
||||
|
||||
if ($this->useLocalList)
|
||||
{
|
||||
$this->extraOpts[$k] = ['j' => [sprintf('?_profiler_completion_achievements %1$s ON `%1$s`.`id` = p.`id` AND `%1$s`.`achievementId` = %2$d', $k, $crv), true]];
|
||||
return [$k.'.achievementId', null, '!'];
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->extraOpts[$k] = ['j' => [sprintf('character_achievement %1$s ON `%1$s`.`guid` = c.`guid` AND `%1$s`.`achievement` = %2$d', $k, $crv), true]];
|
||||
return [$k.'.achievement', null, '!'];
|
||||
}
|
||||
}
|
||||
|
||||
protected function cbWearsItems(int $cr, int $crs, string $crv) : ?array
|
||||
{
|
||||
if (!Util::checkNumeric($crv, NUM_CAST_INT))
|
||||
return null;
|
||||
|
||||
if (!Type::validateIds(Type::ITEM, $crv))
|
||||
return null;
|
||||
|
||||
$k = 'i_'.Util::createHash(12);
|
||||
|
||||
$this->extraOpts[$k] = ['j' => [sprintf('?_profiler_items %1$s ON `%1$s`.`id` = p.`id` AND `%1$s`.`item` = %2$d', $k, $crv), true]];
|
||||
return [$k.'.item', null, '!'];
|
||||
}
|
||||
|
||||
protected function cbHasGuild(int $cr, int $crs, string $crv) : ?array
|
||||
{
|
||||
if (!$this->int2Bool($crs))
|
||||
return null;
|
||||
|
||||
if ($this->useLocalList)
|
||||
return ['p.guild', null, $crs ? '!' : null];
|
||||
else
|
||||
return ['gm.guildId', null, $crs ? '!' : null];
|
||||
}
|
||||
|
||||
protected function cbHasGuildRank(int $cr, int $crs, string $crv) : ?array
|
||||
{
|
||||
if (!Util::checkNumeric($crv, NUM_CAST_INT) || !$this->int2Op($crs))
|
||||
return null;
|
||||
|
||||
if ($this->useLocalList)
|
||||
return ['p.guildrank', $crv, $crs];
|
||||
else
|
||||
return ['gm.rank', $crv, $crs];
|
||||
}
|
||||
|
||||
protected function cbTeamName(int $cr, int $crs, string $crv, $size) : ?array
|
||||
{
|
||||
if ($_ = $this->tokenizeString(['at.name'], $crv))
|
||||
return ['AND', ['at.type', $size], $_];
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
protected function cbTeamRating(int $cr, int $crs, string $crv, $size) : ?array
|
||||
{
|
||||
if (!Util::checkNumeric($crv, NUM_CAST_INT) || !$this->int2Op($crs))
|
||||
return null;
|
||||
|
||||
return ['AND', ['at.type', $size], ['at.rating', $crv, $crs]];
|
||||
}
|
||||
|
||||
protected function cbAchievs(int $cr, int $crs, string $crv) : ?array
|
||||
{
|
||||
if (!Util::checkNumeric($crv, NUM_CAST_INT) || !$this->int2Op($crs))
|
||||
return null;
|
||||
|
||||
if ($this->useLocalList)
|
||||
return ['p.achievementpoints', $crv, $crs];
|
||||
else
|
||||
return ['cap.counter', $crv, $crs];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class RemoteProfileList extends ProfileList
|
||||
{
|
||||
protected string $queryBase = 'SELECT `c`.*, `c`.`guid` AS ARRAY_KEY FROM characters c';
|
||||
protected array $queryOpts = array(
|
||||
'c' => [['gm', 'g', 'cap']], // 12698: use criteria of Achievement 4496 as shortcut to get total achievement points
|
||||
'cap' => ['j' => ['character_achievement_progress cap ON cap.`guid` = c.`guid` AND cap.`criteria` = 12698', true], 's' => ', IFNULL(cap.`counter`, 0) AS "achievementpoints"'],
|
||||
'gm' => ['j' => ['guild_member gm ON gm.`guid` = c.`guid`', true], 's' => ', gm.`rank` AS "guildrank"'],
|
||||
'g' => ['j' => ['guild g ON g.`guildid` = gm.`guildid`', true], 's' => ', g.`guildid` AS "guild", g.`name` AS "guildname"'],
|
||||
'atm' => ['j' => ['arena_team_member atm ON atm.`guid` = c.`guid`', true], 's' => ', atm.`personalRating` AS "rating"'],
|
||||
'at' => [['atm'], 'j' => 'arena_team at ON atm.`arenaTeamId` = at.`arenaTeamId`', 's' => ', at.`name` AS "arenateam", IF(at.`captainGuid` = c.`guid`, 1, 0) AS "captain"']
|
||||
);
|
||||
|
||||
private array $rnItr = []; // rename iterator [name => nCharsWithThisName]
|
||||
|
||||
public function __construct(array $conditions = [], array $miscData = [])
|
||||
{
|
||||
// select DB by realm
|
||||
if (!$this->selectRealms($miscData))
|
||||
{
|
||||
trigger_error('RemoteProfileList::__construct - cannot access any realm.', E_USER_WARNING);
|
||||
return;
|
||||
}
|
||||
|
||||
parent::__construct($conditions, $miscData);
|
||||
|
||||
if ($this->error)
|
||||
return;
|
||||
|
||||
reset($this->dbNames); // only use when querying single realm
|
||||
$realmId = key($this->dbNames);
|
||||
$realms = Profiler::getRealms();
|
||||
$talentSpells = [];
|
||||
$talentLookup = [];
|
||||
$distrib = [];
|
||||
|
||||
// post processing
|
||||
foreach ($this->iterate() as $guid => &$curTpl)
|
||||
{
|
||||
// battlegroup
|
||||
$curTpl['battlegroup'] = Cfg::get('BATTLEGROUP');
|
||||
|
||||
// realm
|
||||
[$r, $g] = explode(':', $guid);
|
||||
if (!empty($realms[$r]))
|
||||
{
|
||||
$curTpl['realm'] = $r;
|
||||
$curTpl['realmName'] = $realms[$r]['name'];
|
||||
$curTpl['region'] = $realms[$r]['region'];
|
||||
}
|
||||
else
|
||||
{
|
||||
trigger_error('char #'.$guid.' belongs to nonexistent realm #'.$r, E_USER_WARNING);
|
||||
unset($this->templates[$guid]);
|
||||
continue;
|
||||
}
|
||||
|
||||
// empty name
|
||||
if (!$curTpl['name'])
|
||||
{
|
||||
trigger_error('char #'.$guid.' on realm #'.$r.' has empty name.', E_USER_WARNING);
|
||||
unset($this->templates[$guid]);
|
||||
continue;
|
||||
}
|
||||
|
||||
// temp id
|
||||
$curTpl['id'] = 0;
|
||||
|
||||
// talent points pre
|
||||
$talentLookup[$r][$g] = [];
|
||||
$talentSpells[] = $curTpl['class'];
|
||||
$curTpl['activespec'] = $curTpl['activeTalentGroup'];
|
||||
|
||||
// equalize distribution
|
||||
if (empty($distrib[$curTpl['realm']]))
|
||||
$distrib[$curTpl['realm']] = 1;
|
||||
else
|
||||
$distrib[$curTpl['realm']]++;
|
||||
|
||||
// char is pending rename
|
||||
if ($curTpl['at_login'] & 0x1)
|
||||
{
|
||||
if (!isset($this->rnItr[$curTpl['name']]))
|
||||
$this->rnItr[$curTpl['name']] = DB::Aowow()->selectCell('SELECT MAX(`renameItr`) FROM ?_profiler_profiles WHERE `realm` = ?d AND `realmGUID` IS NOT NULL AND `name` = ?', $r, $curTpl['name']) ?: 0;
|
||||
|
||||
// already saved as "pending rename"
|
||||
if ($rnItr = DB::Aowow()->selectCell('SELECT `renameItr` FROM ?_profiler_profiles WHERE `realm` = ?d AND `realmGUID` = ?d', $r, $g))
|
||||
$curTpl['renameItr'] = $rnItr;
|
||||
// not yet recognized: get max itr
|
||||
else
|
||||
$curTpl['renameItr'] = ++$this->rnItr[$curTpl['name']];
|
||||
}
|
||||
else
|
||||
$curTpl['renameItr'] = 0;
|
||||
|
||||
$curTpl['cuFlags'] = 0;
|
||||
}
|
||||
|
||||
foreach ($talentLookup as $realm => $chars)
|
||||
$talentLookup[$realm] = DB::Characters($realm)->selectCol('SELECT `guid` AS ARRAY_KEY, `spell` AS ARRAY_KEY2, `talentGroup` FROM character_talent ct WHERE `guid` IN (?a)', array_keys($chars));
|
||||
|
||||
$talentSpells = DB::Aowow()->select('SELECT `spell` AS ARRAY_KEY, `tab`, `rank` FROM ?_talents WHERE `class` IN (?a)', array_unique($talentSpells));
|
||||
|
||||
foreach ($conditions as $c)
|
||||
if (is_int($c))
|
||||
$limit = $c;
|
||||
|
||||
$limit ??= Cfg::get('SQL_LIMIT_DEFAULT');
|
||||
if (!$limit) // int:0 means unlimited, so skip process
|
||||
$distrib = [];
|
||||
|
||||
$total = array_sum($distrib);
|
||||
foreach ($distrib as &$d)
|
||||
$d = ceil($limit * $d / $total);
|
||||
|
||||
foreach ($this->iterate() as $guid => &$curTpl)
|
||||
{
|
||||
if ($distrib)
|
||||
{
|
||||
if ($limit <= 0 || $distrib[$curTpl['realm']] <= 0)
|
||||
{
|
||||
unset($this->templates[$guid]);
|
||||
continue;
|
||||
}
|
||||
|
||||
$distrib[$curTpl['realm']]--;
|
||||
$limit--;
|
||||
}
|
||||
|
||||
[$r, $g] = explode(':', $guid);
|
||||
|
||||
// talent points post
|
||||
$curTpl['talenttree1'] = 0;
|
||||
$curTpl['talenttree2'] = 0;
|
||||
$curTpl['talenttree3'] = 0;
|
||||
if (!empty($talentLookup[$r][$g]))
|
||||
{
|
||||
$talents = array_filter($talentLookup[$r][$g], function($v) use ($curTpl) { return $curTpl['activespec'] == $v; } );
|
||||
foreach (array_intersect_key($talentSpells, $talents) as $spell => $data)
|
||||
$curTpl['talenttree'.($data['tab'] + 1)] += $data['rank'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function getListviewData(int $addInfoMask = 0, array $reqCols = []) : array
|
||||
{
|
||||
$data = parent::getListviewData($addInfoMask, $reqCols);
|
||||
|
||||
// not wanted on server list
|
||||
foreach ($data as &$d)
|
||||
unset($d['published']);
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
public function initializeLocalEntries() : void
|
||||
{
|
||||
$baseData = $guildData = [];
|
||||
foreach ($this->iterate() as $guid => $__)
|
||||
{
|
||||
$realmId = $this->getField('realm');
|
||||
$guildGUID = $this->getField('guild');
|
||||
|
||||
$baseData[$guid] = array(
|
||||
'realm' => $realmId,
|
||||
'realmGUID' => $this->getField('guid'),
|
||||
'name' => $this->getField('name'),
|
||||
'renameItr' => $this->getField('renameItr'),
|
||||
'race' => $this->getField('race'),
|
||||
'class' => $this->getField('class'),
|
||||
'level' => $this->getField('level'),
|
||||
'gender' => $this->getField('gender'),
|
||||
'guild' => $guildGUID ?: null,
|
||||
'guildrank' => $guildGUID ? $this->getField('guildrank') : null,
|
||||
'cuFlags' => PROFILER_CU_NEEDS_RESYNC
|
||||
);
|
||||
|
||||
if ($guildGUID && empty($guildData[$realmId.'-'.$guildGUID]))
|
||||
$guildData[$realmId.'-'.$guildGUID] = array(
|
||||
'realm' => $realmId,
|
||||
'realmGUID' => $guildGUID,
|
||||
'name' => $this->getField('guildname'),
|
||||
'nameUrl' => Profiler::urlize($this->getField('guildname')),
|
||||
'cuFlags' => PROFILER_CU_NEEDS_RESYNC
|
||||
);
|
||||
}
|
||||
|
||||
// basic guild data (satisfying table constraints)
|
||||
if ($guildData)
|
||||
{
|
||||
foreach (Util::createSqlBatchInsert($guildData) as $ins)
|
||||
DB::Aowow()->query('INSERT INTO ?_profiler_guild (?#) VALUES '.$ins.' ON DUPLICATE KEY UPDATE `id` = `id`', array_keys(reset($guildData)));
|
||||
|
||||
// merge back local ids
|
||||
$localGuilds = DB::Aowow()->selectCol('SELECT `realm` AS ARRAY_KEY, `realmGUID` AS ARRAY_KEY2, `id` FROM ?_profiler_guild WHERE `realm` IN (?a) AND `realmGUID` IN (?a)',
|
||||
array_column($guildData, 'realm'), array_column($guildData, 'realmGUID')
|
||||
);
|
||||
|
||||
foreach ($baseData as &$bd)
|
||||
if ($bd['guild'])
|
||||
$bd['guild'] = $localGuilds[$bd['realm']][$bd['guild']];
|
||||
}
|
||||
|
||||
// basic char data (enough for tooltips)
|
||||
if ($baseData)
|
||||
{
|
||||
foreach ($baseData as $ins)
|
||||
DB::Aowow()->query('INSERT INTO ?_profiler_profiles (?#) VALUES (?a) ON DUPLICATE KEY UPDATE `name` = ?, `renameItr` = ?d', array_keys($ins), array_values($ins), $ins['name'], $ins['renameItr']);
|
||||
|
||||
// merge back local ids
|
||||
$localIds = DB::Aowow()->select(
|
||||
'SELECT CONCAT(`realm`, ":", `realmGUID`) AS ARRAY_KEY, `id`, `gearscore` FROM ?_profiler_profiles WHERE (`cuFlags` & ?d) = 0 AND `realm` IN (?a) AND `realmGUID` IN (?a)',
|
||||
PROFILER_CU_PROFILE,
|
||||
array_column($baseData, 'realm'),
|
||||
array_column($baseData, 'realmGUID')
|
||||
);
|
||||
|
||||
foreach ($this->iterate() as $guid => &$_curTpl)
|
||||
if (isset($localIds[$guid]))
|
||||
$_curTpl = array_merge($_curTpl, $localIds[$guid]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class LocalProfileList extends ProfileList
|
||||
{
|
||||
protected string $queryBase = 'SELECT p.*, p.`id` AS ARRAY_KEY FROM ?_profiler_profiles p';
|
||||
protected array $queryOpts = array(
|
||||
'p' => [['g'], 'g' => 'p.`id`'],
|
||||
'ap' => ['j' => ['?_account_profiles ap ON ap.`profileId` = p.`id`', true], 's' => ', (IFNULL(ap.`ExtraFlags`, 0) | p.`cuFlags`) AS "cuFlags"'],
|
||||
'atm' => ['j' => ['?_profiler_arena_team_member atm ON atm.`profileId` = p.`id`', true], 's' => ', atm.`captain`, atm.`personalRating` AS "rating", atm.`seasonGames`, atm.`seasonWins`'],
|
||||
'at' => [['atm'], 'j' => ['?_profiler_arena_team at ON at.`id` = atm.`arenaTeamId`', true], 's' => ', at.`type`'],
|
||||
'g' => ['j' => ['?_profiler_guild g ON g.`id` = p.`guild`', true], 's' => ', g.`name` AS "guildname"']
|
||||
);
|
||||
|
||||
public function __construct(array $conditions = [], array $miscData = [])
|
||||
{
|
||||
$realms = Profiler::getRealms();
|
||||
|
||||
// graft realm selection from miscData onto conditions
|
||||
$realmIds = [];
|
||||
if (isset($miscData['sv']))
|
||||
$realmIds = array_keys(array_filter($realms, fn($x) => Profiler::urlize($x['name']) == Profiler::urlize($miscData['sv'])));
|
||||
|
||||
if (isset($miscData['rg']))
|
||||
$realmIds = array_merge($realmIds, array_keys(array_filter($realms, fn($x) => $x['region'] == $miscData['rg'])));
|
||||
|
||||
if ($conditions && $realmIds)
|
||||
{
|
||||
array_unshift($conditions, 'AND');
|
||||
$conditions = ['AND', ['realm', $realmIds], $conditions];
|
||||
}
|
||||
else if ($realmIds)
|
||||
$conditions = [['realm', $realmIds]];
|
||||
|
||||
parent::__construct($conditions, $miscData);
|
||||
|
||||
if ($this->error)
|
||||
return;
|
||||
|
||||
foreach ($this->iterate() as $id => &$curTpl)
|
||||
{
|
||||
if (!$curTpl['realm']) // custom profile w/o realminfo
|
||||
continue;
|
||||
|
||||
if (!isset($realms[$curTpl['realm']]))
|
||||
{
|
||||
unset($this->templates[$id]);
|
||||
continue;
|
||||
}
|
||||
|
||||
$curTpl['realmName'] = $realms[$curTpl['realm']]['name'];
|
||||
$curTpl['region'] = $realms[$curTpl['realm']]['region'];
|
||||
$curTpl['battlegroup'] = Cfg::get('BATTLEGROUP');
|
||||
}
|
||||
}
|
||||
|
||||
public function getProfileUrl() : string
|
||||
{
|
||||
$url = '?profile=';
|
||||
|
||||
if ($this->isCustom())
|
||||
return $url.$this->getField('id');
|
||||
|
||||
return $url.implode('.', array(
|
||||
$this->getField('region'),
|
||||
Profiler::urlize($this->getField('realmName'), true),
|
||||
urlencode($this->getField('name'))
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
?>
|
||||
720
includes/dbtypes/quest.class.php
Normal file
720
includes/dbtypes/quest.class.php
Normal file
@@ -0,0 +1,720 @@
|
||||
<?php
|
||||
|
||||
namespace Aowow;
|
||||
|
||||
if (!defined('AOWOW_REVISION'))
|
||||
die('illegal access');
|
||||
|
||||
|
||||
class QuestList extends DBTypeList
|
||||
{
|
||||
public static int $type = Type::QUEST;
|
||||
public static string $brickFile = 'quest';
|
||||
public static string $dataTable = '?_quests';
|
||||
public array $requires = [];
|
||||
public array $rewards = [];
|
||||
public array $choices = [];
|
||||
|
||||
protected string $queryBase = 'SELECT q.*, q.`id` AS ARRAY_KEY FROM ?_quests q';
|
||||
protected array $queryOpts = array(
|
||||
'q' => [],
|
||||
'rsc' => ['j' => '?_spell rsc ON q.`rewardSpellCast` = rsc.`id`'], // limit rewardSpellCasts
|
||||
'qse' => ['j' => '?_quests_startend qse ON q.`id` = qse.`questId`', 's' => ', qse.`method`'], // groupConcat..?
|
||||
'e' => ['j' => ['?_events e ON e.`id` = q.`eventId`', true], 's' => ', e.`holidayId`']
|
||||
);
|
||||
|
||||
public function __construct(array $conditions = [], array $miscData = [])
|
||||
{
|
||||
parent::__construct($conditions, $miscData);
|
||||
|
||||
// i don't like this very much
|
||||
$currencies = DB::Aowow()->selectCol('SELECT `id` AS ARRAY_KEY, `itemId` FROM ?_currencies');
|
||||
|
||||
// post processing
|
||||
foreach ($this->iterate() as $id => &$_curTpl)
|
||||
{
|
||||
$_curTpl['cat1'] = $_curTpl['zoneOrSort']; // should probably be in a method...
|
||||
$_curTpl['cat2'] = 0;
|
||||
|
||||
foreach (Game::$questClasses as $k => $arr)
|
||||
{
|
||||
if (in_array($_curTpl['cat1'], $arr))
|
||||
{
|
||||
$_curTpl['cat2'] = $k;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// store requirements
|
||||
$requires = [];
|
||||
for ($i = 1; $i < 7; $i++)
|
||||
{
|
||||
if ($_ = $_curTpl['reqItemId'.$i])
|
||||
$requires[Type::ITEM][] = $_;
|
||||
|
||||
if ($i > 4)
|
||||
continue;
|
||||
|
||||
if ($_curTpl['reqNpcOrGo'.$i] > 0)
|
||||
$requires[Type::NPC][] = $_curTpl['reqNpcOrGo'.$i];
|
||||
else if ($_curTpl['reqNpcOrGo'.$i] < 0)
|
||||
$requires[Type::OBJECT][] = -$_curTpl['reqNpcOrGo'.$i];
|
||||
|
||||
if ($_ = $_curTpl['reqSourceItemId'.$i])
|
||||
$requires[Type::ITEM][] = $_;
|
||||
}
|
||||
if ($requires)
|
||||
$this->requires[$id] = $requires;
|
||||
|
||||
// store rewards
|
||||
$rewards = [];
|
||||
$choices = [];
|
||||
|
||||
if ($_ = $_curTpl['rewardTitleId'])
|
||||
$rewards[Type::TITLE][] = $_;
|
||||
|
||||
if ($_ = $_curTpl['rewardHonorPoints'])
|
||||
$rewards[Type::CURRENCY][CURRENCY_HONOR_POINTS] = $_;
|
||||
|
||||
if ($_ = $_curTpl['rewardArenaPoints'])
|
||||
$rewards[Type::CURRENCY][CURRENCY_ARENA_POINTS] = $_;
|
||||
|
||||
for ($i = 1; $i < 7; $i++)
|
||||
{
|
||||
if ($_ = $_curTpl['rewardChoiceItemId'.$i])
|
||||
$choices[Type::ITEM][$_] = $_curTpl['rewardChoiceItemCount'.$i];
|
||||
|
||||
if ($i > 5)
|
||||
continue;
|
||||
|
||||
if ($_ = $_curTpl['rewardFactionId'.$i])
|
||||
$rewards[Type::FACTION][$_] = $_curTpl['rewardFactionValue'.$i];
|
||||
|
||||
if ($i > 4)
|
||||
continue;
|
||||
|
||||
if ($_ = $_curTpl['rewardItemId'.$i])
|
||||
{
|
||||
$qty = $_curTpl['rewardItemCount'.$i];
|
||||
if (in_array($_, $currencies))
|
||||
$rewards[Type::CURRENCY][array_search($_, $currencies)] = $qty;
|
||||
else
|
||||
$rewards[Type::ITEM][$_] = $qty;
|
||||
}
|
||||
}
|
||||
if ($rewards)
|
||||
$this->rewards[$id] = $rewards;
|
||||
|
||||
if ($choices)
|
||||
$this->choices[$id] = $choices;
|
||||
}
|
||||
}
|
||||
|
||||
public function isRepeatable() : bool
|
||||
{
|
||||
return $this->curTpl['flags'] & QUEST_FLAG_REPEATABLE || $this->curTpl['specialFlags'] & QUEST_FLAG_SPECIAL_REPEATABLE;
|
||||
}
|
||||
|
||||
public function isDaily() : int
|
||||
{
|
||||
if ($this->curTpl['flags'] & QUEST_FLAG_DAILY)
|
||||
return 1;
|
||||
|
||||
if ($this->curTpl['flags'] & QUEST_FLAG_WEEKLY)
|
||||
return 2;
|
||||
|
||||
if ($this->curTpl['specialFlags'] & QUEST_FLAG_SPECIAL_MONTHLY)
|
||||
return 3;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// using reqPlayerKills and rewardHonor as a crutch .. has TC this even implemented..?
|
||||
public function isPvPEnabled() : bool
|
||||
{
|
||||
return $this->curTpl['reqPlayerKills'] || $this->curTpl['rewardHonorPoints'] || $this->curTpl['rewardArenaPoints'];
|
||||
}
|
||||
|
||||
// by TC definition
|
||||
public function isSeasonal() : bool
|
||||
{
|
||||
return in_array($this->getField('zoneOrSortBak'), [-22, -284, -366, -369, -370, -376, -374]) && !$this->isRepeatable();
|
||||
}
|
||||
|
||||
public function getSourceData(int $id = 0) : array
|
||||
{
|
||||
$data = [];
|
||||
|
||||
foreach ($this->iterate() as $__)
|
||||
{
|
||||
if ($id && $id != $this->id)
|
||||
continue;
|
||||
|
||||
$data[$this->id] = array(
|
||||
"n" => $this->getField('name', true),
|
||||
"t" => Type::QUEST,
|
||||
"ti" => $this->id,
|
||||
"c" => $this->curTpl['cat1'],
|
||||
"c2" => $this->curTpl['cat2']
|
||||
);
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
public function getSOMData(int $side = SIDE_BOTH) : array
|
||||
{
|
||||
$data = [];
|
||||
|
||||
foreach ($this->iterate() as $__)
|
||||
{
|
||||
if (!(ChrRace::sideFromMask($this->curTpl['reqRaceMask']) & $side))
|
||||
continue;
|
||||
|
||||
[$series, $first] = DB::Aowow()->SelectRow(
|
||||
'SELECT IF(prev.`id` OR cur.`nextQuestIdChain`, 1, 0) AS "0", IF(prev.`id` IS NULL AND cur.`nextQuestIdChain`, 1, 0) AS "1" FROM ?_quests cur LEFT JOIN ?_quests prev ON prev.`nextQuestIdChain` = cur.`id` WHERE cur.`id` = ?d',
|
||||
$this->id
|
||||
);
|
||||
|
||||
$data[$this->id] = array(
|
||||
'level' => $this->curTpl['level'] < 0 ? MAX_LEVEL : $this->curTpl['level'],
|
||||
'name' => $this->getField('name', true),
|
||||
'category' => $this->curTpl['cat1'],
|
||||
'category2' => $this->curTpl['cat2'],
|
||||
'series' => $series,
|
||||
'first' => $first
|
||||
);
|
||||
|
||||
if ($this->isDaily())
|
||||
$data[$this->id]['daily'] = 1;
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
public function getListviewData(int $extraFactionId = 0) : array
|
||||
{
|
||||
$data = [];
|
||||
|
||||
foreach ($this->iterate() as $__)
|
||||
{
|
||||
$data[$this->id] = array(
|
||||
'category' => $this->curTpl['cat1'],
|
||||
'category2' => $this->curTpl['cat2'],
|
||||
'id' => $this->id,
|
||||
'level' => $this->curTpl['level'],
|
||||
'reqlevel' => $this->curTpl['minLevel'],
|
||||
'name' => Lang::unescapeUISequences($this->getField('name', true), Lang::FMT_RAW),
|
||||
'side' => ChrRace::sideFromMask($this->curTpl['reqRaceMask']),
|
||||
'wflags' => 0x0,
|
||||
'xp' => $this->curTpl['rewardXP']
|
||||
);
|
||||
|
||||
if (!empty($this->rewards[$this->id][Type::CURRENCY]))
|
||||
foreach ($this->rewards[$this->id][Type::CURRENCY] as $iId => $qty)
|
||||
$data[$this->id]['currencyrewards'][] = [$iId, $qty];
|
||||
|
||||
if (!empty($this->rewards[$this->id][Type::ITEM]))
|
||||
foreach ($this->rewards[$this->id][Type::ITEM] as $iId => $qty)
|
||||
$data[$this->id]['itemrewards'][] = [$iId, $qty];
|
||||
|
||||
if (!empty($this->choices[$this->id][Type::ITEM]))
|
||||
foreach ($this->choices[$this->id][Type::ITEM] as $iId => $qty)
|
||||
$data[$this->id]['itemchoices'][] = [$iId, $qty];
|
||||
|
||||
if ($_ = $this->curTpl['rewardTitleId'])
|
||||
$data[$this->id]['titlereward'] = $_;
|
||||
|
||||
if ($_ = $this->curTpl['type'])
|
||||
$data[$this->id]['type'] = $_;
|
||||
|
||||
if ($_ = $this->curTpl['reqClassMask'])
|
||||
$data[$this->id]['reqclass'] = $_;
|
||||
|
||||
if ($_ = ($this->curTpl['reqRaceMask'] & ChrRace::MASK_ALL))
|
||||
if ((($_ & ChrRace::MASK_ALLIANCE) != ChrRace::MASK_ALLIANCE) && (($_ & ChrRace::MASK_HORDE) != ChrRace::MASK_HORDE))
|
||||
$data[$this->id]['reqrace'] = $_;
|
||||
|
||||
if ($_ = $this->curTpl['rewardOrReqMoney'])
|
||||
if ($_ > 0)
|
||||
$data[$this->id]['money'] = $_;
|
||||
|
||||
// todo (med): also get disables
|
||||
if ($this->curTpl['flags'] & QUEST_FLAG_UNAVAILABLE)
|
||||
$data[$this->id]['historical'] = true;
|
||||
|
||||
// if ($this->isRepeatable()) // dafuque..? says repeatable and is used as 'disabled'..?
|
||||
// $data[$this->id]['wflags'] |= QUEST_CU_REPEATABLE;
|
||||
if ($this->curTpl['cuFlags'] & (CUSTOM_UNAVAILABLE | CUSTOM_DISABLED))
|
||||
$data[$this->id]['wflags'] |= QUEST_CU_REPEATABLE;
|
||||
|
||||
if ($this->curTpl['flags'] & QUEST_FLAG_DAILY)
|
||||
{
|
||||
$data[$this->id]['wflags'] |= QUEST_CU_DAILY;
|
||||
$data[$this->id]['daily'] = true;
|
||||
}
|
||||
|
||||
if ($this->curTpl['flags'] & QUEST_FLAG_WEEKLY)
|
||||
{
|
||||
$data[$this->id]['wflags'] |= QUEST_CU_WEEKLY;
|
||||
$data[$this->id]['weekly'] = true;
|
||||
}
|
||||
|
||||
if ($this->isSeasonal())
|
||||
$data[$this->id]['wflags'] |= QUEST_CU_SEASONAL;
|
||||
|
||||
if ($this->curTpl['flags'] & QUEST_FLAG_AUTO_REWARDED) // not shown in log
|
||||
$data[$this->id]['wflags'] |= QUEST_CU_SKIP_LOG;
|
||||
|
||||
if ($this->curTpl['flags'] & QUEST_FLAG_AUTO_ACCEPT) // self-explanatory
|
||||
$data[$this->id]['wflags'] |= QUEST_CU_AUTO_ACCEPT;
|
||||
|
||||
if ($this->isPvPEnabled()) // not sure why this flag also requires auto-accept to be set
|
||||
$data[$this->id]['wflags'] |= (QUEST_CU_AUTO_ACCEPT | QUEST_CU_PVP_ENABLED);
|
||||
|
||||
$data[$this->id]['reprewards'] = [];
|
||||
for ($i = 1; $i < 6; $i++)
|
||||
{
|
||||
$foo = $this->curTpl['rewardFactionId'.$i];
|
||||
$bar = $this->curTpl['rewardFactionValue'.$i];
|
||||
if ($foo && $bar)
|
||||
{
|
||||
$data[$this->id]['reprewards'][] = [$foo, $bar];
|
||||
|
||||
if ($extraFactionId == $foo)
|
||||
$data[$this->id]['reputation'] = $bar;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
public function parseText(string $type = 'objectives', bool $jsEscaped = true) : string
|
||||
{
|
||||
$text = $this->getField($type, true);
|
||||
if (!$text)
|
||||
return '';
|
||||
|
||||
$text = Util::parseHtmlText($text);
|
||||
|
||||
if ($jsEscaped)
|
||||
$text = Util::jsEscape($text);
|
||||
|
||||
return $text;
|
||||
}
|
||||
|
||||
public function renderTooltip() : ?string
|
||||
{
|
||||
if (!$this->curTpl)
|
||||
return null;
|
||||
|
||||
$title = Lang::unescapeUISequences(Util::htmlEscape($this->getField('name', true)), Lang::FMT_HTML);
|
||||
$level = $this->curTpl['level'];
|
||||
if ($level < 0)
|
||||
$level = 0;
|
||||
|
||||
$x = '';
|
||||
if ($level)
|
||||
{
|
||||
$level = sprintf(Lang::quest('questLevel'), $level);
|
||||
|
||||
if ($this->curTpl['flags'] & QUEST_FLAG_DAILY) // daily
|
||||
$level .= ' '.Lang::quest('daily');
|
||||
|
||||
$x .= '<table><tr><td><table width="100%"><tr><td><b class="q">'.$title.'</b></td><th><b class="q0">'.$level.'</b></th></tr></table></td></tr></table>';
|
||||
}
|
||||
else
|
||||
$x .= '<table><tr><td><b class="q">'.$title.'</b></td></tr></table>';
|
||||
|
||||
|
||||
$x .= '<table><tr><td><br />'.$this->parseText('objectives', false);
|
||||
|
||||
|
||||
$xReq = '';
|
||||
for ($i = 1; $i < 5; $i++)
|
||||
{
|
||||
$ot = $this->getField('objectiveText'.$i, true);
|
||||
$rng = $this->curTpl['reqNpcOrGo'.$i];
|
||||
$rngQty = $this->curTpl['reqNpcOrGoCount'.$i];
|
||||
|
||||
if ($rngQty < 1 && (!$rng || $ot))
|
||||
continue;
|
||||
|
||||
if ($ot)
|
||||
$name = $ot;
|
||||
else
|
||||
$name = $rng > 0 ? CreatureList::getName($rng) : Lang::unescapeUISequences(GameObjectList::getName(-$rng), Lang::FMT_HTML);
|
||||
|
||||
if (!$name)
|
||||
$name = Util::ucFirst(Lang::game($rng > 0 ? 'npc' : 'object')).' #'.abs($rng);
|
||||
|
||||
$xReq .= '<br /> - '.$name.($rngQty > 1 ? ' x '.$rngQty : '');
|
||||
}
|
||||
|
||||
for ($i = 1; $i < 7; $i++)
|
||||
{
|
||||
$ri = $this->curTpl['reqItemId'.$i];
|
||||
$riQty = $this->curTpl['reqItemCount'.$i];
|
||||
|
||||
if (!$ri || $riQty < 1)
|
||||
continue;
|
||||
|
||||
$name = Lang::unescapeUISequences(ItemList::getName($ri), Lang::FMT_HTML) ?: Util::ucFirst(Lang::game('item')).' #'.$ri;
|
||||
|
||||
$xReq .= '<br /> - '.$name.($riQty > 1 ? ' x '.$riQty : '');
|
||||
}
|
||||
|
||||
if ($et = $this->getField('end', true))
|
||||
$xReq .= '<br /> - '.$et;
|
||||
|
||||
if ($_ = $this->getField('rewardOrReqMoney'))
|
||||
if ($_ < 0)
|
||||
$xReq .= '<br /> - '.Lang::quest('money').Lang::main('colon').Util::formatMoney(abs($_));
|
||||
|
||||
if ($xReq)
|
||||
$x .= '<br /><br /><span class="q">'.Lang::quest('requirements').Lang::main('colon').'</span>'.$xReq;
|
||||
|
||||
$x .= '</td></tr></table>';
|
||||
|
||||
return $x;
|
||||
}
|
||||
|
||||
public function getJSGlobals(int $addMask = GLOBALINFO_ANY) : array
|
||||
{
|
||||
$data = [];
|
||||
|
||||
foreach ($this->iterate() as $__)
|
||||
{
|
||||
if ($addMask & GLOBALINFO_REWARDS)
|
||||
{
|
||||
// items
|
||||
for ($i = 1; $i < 5; $i++)
|
||||
if ($this->curTpl['rewardItemId'.$i] > 0)
|
||||
$data[Type::ITEM][$this->curTpl['rewardItemId'.$i]] = $this->curTpl['rewardItemId'.$i];
|
||||
|
||||
for ($i = 1; $i < 7; $i++)
|
||||
if ($this->curTpl['rewardChoiceItemId'.$i] > 0)
|
||||
$data[Type::ITEM][$this->curTpl['rewardChoiceItemId'.$i]] = $this->curTpl['rewardChoiceItemId'.$i];
|
||||
|
||||
// spells
|
||||
if ($this->curTpl['rewardSpell'] > 0)
|
||||
$data[Type::SPELL][$this->curTpl['rewardSpell']] = $this->curTpl['rewardSpell'];
|
||||
|
||||
if ($this->curTpl['rewardSpellCast'] > 0)
|
||||
$data[Type::SPELL][$this->curTpl['rewardSpellCast']] = $this->curTpl['rewardSpellCast'];
|
||||
|
||||
// titles
|
||||
if ($this->curTpl['rewardTitleId'] > 0)
|
||||
$data[Type::TITLE][$this->curTpl['rewardTitleId']] = $this->curTpl['rewardTitleId'];
|
||||
|
||||
// currencies
|
||||
if (!empty($this->rewards[$this->id][Type::CURRENCY]))
|
||||
foreach ($this->rewards[$this->id][Type::CURRENCY] as $id => $__)
|
||||
$data[Type::CURRENCY][$id] = $id;
|
||||
}
|
||||
|
||||
if ($addMask & GLOBALINFO_SELF)
|
||||
$data[Type::QUEST][$this->id] = ['name' => $this->getField('name', true)];
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class QuestListFilter extends Filter
|
||||
{
|
||||
protected string $type = 'quests';
|
||||
protected static array $enums = array(
|
||||
37 => parent::ENUM_CLASSS, // classspecific
|
||||
38 => parent::ENUM_RACE, // racespecific
|
||||
9 => parent::ENUM_FACTION, // objectiveearnrepwith
|
||||
33 => parent::ENUM_EVENT, // relatedevent
|
||||
43 => parent::ENUM_CURRENCY, // currencyrewarded
|
||||
1 => parent::ENUM_FACTION, // increasesrepwith
|
||||
10 => parent::ENUM_FACTION // decreasesrepwith
|
||||
);
|
||||
|
||||
protected static array $genericFilter = array(
|
||||
1 => [parent::CR_CALLBACK, 'cbReputation', '>', null], // increasesrepwith
|
||||
2 => [parent::CR_NUMERIC, 'rewardXP', NUM_CAST_INT ], // experiencegained
|
||||
3 => [parent::CR_NUMERIC, 'rewardOrReqMoney', NUM_CAST_INT ], // moneyrewarded
|
||||
4 => [parent::CR_CALLBACK, 'cbSpellRewards', null, null], // spellrewarded [yn]
|
||||
5 => [parent::CR_FLAG, 'flags', QUEST_FLAG_SHARABLE ], // sharable
|
||||
6 => [parent::CR_NUMERIC, 'timeLimit', NUM_CAST_INT ], // timer
|
||||
7 => [parent::CR_FLAG, 'cuFlags', QUEST_CU_FIRST_SERIES ], // firstquestseries
|
||||
9 => [parent::CR_CALLBACK, 'cbEarnReputation', null, null], // objectiveearnrepwith [enum]
|
||||
10 => [parent::CR_CALLBACK, 'cbReputation', '<', null], // decreasesrepwith
|
||||
11 => [parent::CR_NUMERIC, 'suggestedPlayers', NUM_CAST_INT ], // suggestedplayers
|
||||
15 => [parent::CR_FLAG, 'cuFlags', QUEST_CU_LAST_SERIES ], // lastquestseries
|
||||
16 => [parent::CR_FLAG, 'cuFlags', QUEST_CU_PART_OF_SERIES ], // partseries
|
||||
18 => [parent::CR_FLAG, 'cuFlags', CUSTOM_HAS_SCREENSHOT ], // hasscreenshots
|
||||
19 => [parent::CR_CALLBACK, 'cbQuestRelation', 0x1, null], // startsfrom [enum]
|
||||
21 => [parent::CR_CALLBACK, 'cbQuestRelation', 0x2, null], // endsat [enum]
|
||||
22 => [parent::CR_CALLBACK, 'cbItemRewards', null, null], // itemrewards [op] [int]
|
||||
23 => [parent::CR_CALLBACK, 'cbItemChoices', null, null], // itemchoices [op] [int]
|
||||
24 => [parent::CR_CALLBACK, 'cbLacksStartEnd', null, null], // lacksstartend [yn]
|
||||
25 => [parent::CR_FLAG, 'cuFlags', CUSTOM_HAS_COMMENT ], // hascomments
|
||||
27 => [parent::CR_FLAG, 'flags', QUEST_FLAG_DAILY ], // daily
|
||||
28 => [parent::CR_FLAG, 'flags', QUEST_FLAG_WEEKLY ], // weekly
|
||||
29 => [parent::CR_CALLBACK, 'cbRepeatable', null ], // repeatable
|
||||
30 => [parent::CR_NUMERIC, 'id', NUM_CAST_INT, true], // id
|
||||
33 => [parent::CR_ENUM, 'e.holidayId', true, true], // relatedevent
|
||||
34 => [parent::CR_CALLBACK, 'cbAvailable', null, null], // availabletoplayers [yn]
|
||||
36 => [parent::CR_FLAG, 'cuFlags', CUSTOM_HAS_VIDEO ], // hasvideos
|
||||
37 => [parent::CR_CALLBACK, 'cbClassSpec', null, null], // classspecific [enum]
|
||||
38 => [parent::CR_CALLBACK, 'cbRaceSpec', null, null], // racespecific [enum]
|
||||
42 => [parent::CR_STAFFFLAG, 'flags' ], // flags
|
||||
43 => [parent::CR_CALLBACK, 'cbCurrencyReward', null, null], // currencyrewarded [enum]
|
||||
44 => [parent::CR_CALLBACK, 'cbLoremaster', null, null], // countsforloremaster_stc [yn]
|
||||
45 => [parent::CR_BOOLEAN, 'rewardTitleId' ] // titlerewarded
|
||||
);
|
||||
|
||||
protected static array $inputFields = array(
|
||||
'cr' => [parent::V_RANGE, [1, 45], true ], // criteria ids
|
||||
'crs' => [parent::V_LIST, [parent::ENUM_NONE, parent::ENUM_ANY, [0, 99999]], true ], // criteria operators
|
||||
'crv' => [parent::V_REGEX, parent::PATTERN_INT, true ], // criteria values - only numerals
|
||||
'na' => [parent::V_REGEX, parent::PATTERN_NAME, false], // name / text - only printable chars, no delimiter
|
||||
'ex' => [parent::V_EQUAL, 'on', false], // also match subname
|
||||
'ma' => [parent::V_EQUAL, 1, false], // match any / all filter
|
||||
'minle' => [parent::V_RANGE, [1, 99], false], // min quest level
|
||||
'maxle' => [parent::V_RANGE, [1, 99], false], // max quest level
|
||||
'minrl' => [parent::V_RANGE, [1, 99], false], // min required level
|
||||
'maxrl' => [parent::V_RANGE, [1, 99], false], // max required level
|
||||
'si' => [parent::V_LIST, [-SIDE_HORDE, -SIDE_ALLIANCE, SIDE_ALLIANCE, SIDE_HORDE, SIDE_BOTH], false], // side
|
||||
'ty' => [parent::V_LIST, [0, 1, 21, 41, 62, [81, 85], 88, 89], true ] // type
|
||||
);
|
||||
|
||||
public array $extraOpts = [];
|
||||
|
||||
protected function createSQLForValues() : array
|
||||
{
|
||||
$parts = [];
|
||||
$_v = $this->values;
|
||||
|
||||
// name
|
||||
if ($_v['na'])
|
||||
{
|
||||
$_ = [];
|
||||
if ($_v['ex'] == 'on')
|
||||
$_ = $this->tokenizeString(['name_loc'.Lang::getLocale()->value, 'objectives_loc'.Lang::getLocale()->value, 'details_loc'.Lang::getLocale()->value]);
|
||||
else
|
||||
$_ = $this->tokenizeString(['name_loc'.Lang::getLocale()->value]);
|
||||
|
||||
if ($_)
|
||||
$parts[] = $_;
|
||||
}
|
||||
|
||||
// level min
|
||||
if ($_v['minle'])
|
||||
$parts[] = ['level', $_v['minle'], '>=']; // not considering quests that are always at player level (-1)
|
||||
|
||||
// level max
|
||||
if ($_v['maxle'])
|
||||
$parts[] = ['level', $_v['maxle'], '<='];
|
||||
|
||||
// reqLevel min
|
||||
if ($_v['minrl'])
|
||||
$parts[] = ['minLevel', $_v['minrl'], '>=']; // ignoring maxLevel
|
||||
|
||||
// reqLevel max
|
||||
if ($_v['maxrl'])
|
||||
$parts[] = ['minLevel', $_v['maxrl'], '<=']; // ignoring maxLevel
|
||||
|
||||
// side
|
||||
if ($_v['si'])
|
||||
{
|
||||
$excl = [['reqRaceMask', ChrRace::MASK_ALL, '&'], ChrRace::MASK_ALL, '!'];
|
||||
$incl = ['OR', ['reqRaceMask', 0], [['reqRaceMask', ChrRace::MASK_ALL, '&'], ChrRace::MASK_ALL]];
|
||||
|
||||
$parts[] = match ($_v['si'])
|
||||
{
|
||||
SIDE_BOTH => $incl,
|
||||
SIDE_HORDE => ['OR', $incl, ['reqRaceMask', ChrRace::MASK_HORDE, '&']],
|
||||
-SIDE_HORDE => ['AND', $excl, ['reqRaceMask', ChrRace::MASK_HORDE, '&']],
|
||||
SIDE_ALLIANCE => ['OR', $incl, ['reqRaceMask', ChrRace::MASK_ALLIANCE, '&']],
|
||||
-SIDE_ALLIANCE => ['AND', $excl, ['reqRaceMask', ChrRace::MASK_ALLIANCE, '&']]
|
||||
};
|
||||
}
|
||||
|
||||
// type [list]
|
||||
if ($_v['ty'] !== null)
|
||||
$parts[] = ['type', $_v['ty']];
|
||||
|
||||
return $parts;
|
||||
}
|
||||
|
||||
protected function cbReputation(int $cr, int $crs, string $crv, string $sign) : ?array
|
||||
{
|
||||
if (!Util::checkNumeric($crs, NUM_CAST_INT))
|
||||
return null;
|
||||
|
||||
if (!in_array($crs, self::$enums[$cr]))
|
||||
return null;
|
||||
|
||||
if ($_ = DB::Aowow()->selectRow('SELECT * FROM ?_factions WHERE `id` = ?d', $crs))
|
||||
$this->fiReputationCols[] = [$crs, Util::localizedString($_, 'name')];
|
||||
|
||||
return [
|
||||
'OR',
|
||||
['AND', ['rewardFactionId1', $crs], ['rewardFactionValue1', 0, $sign]],
|
||||
['AND', ['rewardFactionId2', $crs], ['rewardFactionValue2', 0, $sign]],
|
||||
['AND', ['rewardFactionId3', $crs], ['rewardFactionValue3', 0, $sign]],
|
||||
['AND', ['rewardFactionId4', $crs], ['rewardFactionValue4', 0, $sign]],
|
||||
['AND', ['rewardFactionId5', $crs], ['rewardFactionValue5', 0, $sign]]
|
||||
];
|
||||
}
|
||||
|
||||
protected function cbQuestRelation(int $cr, int $crs, string $crv, $flags) : ?array
|
||||
{
|
||||
return match ($crs)
|
||||
{
|
||||
Type::NPC,
|
||||
Type::OBJECT,
|
||||
Type::ITEM => ['AND', ['qse.type', $crs], ['qse.method', $flags, '&']],
|
||||
default => null
|
||||
};
|
||||
}
|
||||
|
||||
protected function cbCurrencyReward(int $cr, int $crs, string $crv) : ?array
|
||||
{
|
||||
if (!Util::checkNumeric($crs, NUM_CAST_INT))
|
||||
return null;
|
||||
|
||||
if (!in_array($crs, self::$enums[$cr]))
|
||||
return null;
|
||||
|
||||
return [
|
||||
'OR',
|
||||
['rewardItemId1', $crs], ['rewardItemId2', $crs], ['rewardItemId3', $crs], ['rewardItemId4', $crs],
|
||||
['rewardChoiceItemId1', $crs], ['rewardChoiceItemId2', $crs], ['rewardChoiceItemId3', $crs], ['rewardChoiceItemId4', $crs], ['rewardChoiceItemId5', $crs], ['rewardChoiceItemId6', $crs]
|
||||
];
|
||||
}
|
||||
|
||||
protected function cbAvailable(int $cr, int $crs, string $crv) : ?array
|
||||
{
|
||||
if (!$this->int2Bool($crs))
|
||||
return null;
|
||||
|
||||
if ($crs)
|
||||
return [['cuFlags', CUSTOM_UNAVAILABLE | CUSTOM_DISABLED, '&'], 0];
|
||||
else
|
||||
return ['cuFlags', CUSTOM_UNAVAILABLE | CUSTOM_DISABLED, '&'];
|
||||
}
|
||||
|
||||
protected function cbRepeatable(int $cr, int $crs, string $crv) : ?array
|
||||
{
|
||||
if (!$this->int2Bool($crs))
|
||||
return null;
|
||||
|
||||
if ($crs)
|
||||
return ['OR', ['flags', QUEST_FLAG_REPEATABLE, '&'], ['specialFlags', QUEST_FLAG_SPECIAL_REPEATABLE, '&']];
|
||||
else
|
||||
return ['AND', [['flags', QUEST_FLAG_REPEATABLE, '&'], 0], [['specialFlags', QUEST_FLAG_SPECIAL_REPEATABLE, '&'], 0]];
|
||||
}
|
||||
|
||||
protected function cbItemChoices(int $cr, int $crs, string $crv) : ?array
|
||||
{
|
||||
if (!Util::checkNumeric($crv, NUM_CAST_INT) || !$this->int2Op($crs))
|
||||
return null;
|
||||
|
||||
$this->extraOpts['q']['s'][] = ', (IF(`rewardChoiceItemId1`, 1, 0) + IF(`rewardChoiceItemId2`, 1, 0) + IF(`rewardChoiceItemId3`, 1, 0) + IF(`rewardChoiceItemId4`, 1, 0) + IF(`rewardChoiceItemId5`, 1, 0) + IF(`rewardChoiceItemId6`, 1, 0)) AS "numChoices"';
|
||||
$this->extraOpts['q']['h'][] = '`numChoices` '.$crs.' '.$crv;
|
||||
return [1];
|
||||
}
|
||||
|
||||
protected function cbItemRewards(int $cr, int $crs, string $crv) : ?array
|
||||
{
|
||||
if (!Util::checkNumeric($crv, NUM_CAST_INT) || !$this->int2Op($crs))
|
||||
return null;
|
||||
|
||||
$this->extraOpts['q']['s'][] = ', (IF(`rewardItemId1`, 1, 0) + IF(`rewardItemId2`, 1, 0) + IF(`rewardItemId3`, 1, 0) + IF(`rewardItemId4`, 1, 0)) AS "numRewards"';
|
||||
$this->extraOpts['q']['h'][] = '`numRewards` '.$crs.' '.$crv;
|
||||
return [1];
|
||||
}
|
||||
|
||||
protected function cbLoremaster(int $cr, int $crs, string $crv) : ?array
|
||||
{
|
||||
if (!$this->int2Bool($crs))
|
||||
return null;
|
||||
|
||||
if ($crs)
|
||||
return ['AND', ['zoneOrSort', 0, '>'], [['flags', QUEST_FLAG_DAILY | QUEST_FLAG_WEEKLY | QUEST_FLAG_REPEATABLE, '&'], 0], [['specialFlags', QUEST_FLAG_SPECIAL_REPEATABLE | QUEST_FLAG_SPECIAL_MONTHLY, '&'], 0]];
|
||||
else
|
||||
return ['OR', ['zoneOrSort', 0, '<'], ['flags', QUEST_FLAG_DAILY | QUEST_FLAG_WEEKLY | QUEST_FLAG_REPEATABLE, '&'], ['specialFlags', QUEST_FLAG_SPECIAL_REPEATABLE | QUEST_FLAG_SPECIAL_MONTHLY, '&']];
|
||||
}
|
||||
|
||||
protected function cbSpellRewards(int $cr, int $crs, string $crv) : ?array
|
||||
{
|
||||
if (!$this->int2Bool($crs))
|
||||
return null;
|
||||
|
||||
if ($crs)
|
||||
return ['OR', ['sourceSpellId', 0, '>'], ['rewardSpell', 0, '>'], ['rsc.effect1Id', SpellList::EFFECTS_TEACH], ['rsc.effect2Id', SpellList::EFFECTS_TEACH], ['rsc.effect3Id', SpellList::EFFECTS_TEACH]];
|
||||
else
|
||||
return ['AND', ['sourceSpellId', 0], ['rewardSpell', 0], ['rewardSpellCast', 0]];
|
||||
}
|
||||
|
||||
protected function cbEarnReputation(int $cr, int $crs, string $crv) : ?array
|
||||
{
|
||||
if (!Util::checkNumeric($crs, NUM_CAST_INT))
|
||||
return null;
|
||||
|
||||
if ($crs == parent::ENUM_ANY)
|
||||
return ['OR', ['reqFactionId1', 0, '>'], ['reqFactionId2', 0, '>']];
|
||||
else if ($crs == parent::ENUM_NONE)
|
||||
return ['AND', ['reqFactionId1', 0], ['reqFactionId2', 0]];
|
||||
else if (in_array($crs, self::$enums[$cr]))
|
||||
return ['OR', ['reqFactionId1', $crs], ['reqFactionId2', $crs]];
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
protected function cbClassSpec(int $cr, int $crs, string $crv) : ?array
|
||||
{
|
||||
if (!isset(self::$enums[$cr][$crs]))
|
||||
return null;
|
||||
|
||||
$_ = self::$enums[$cr][$crs];
|
||||
if ($_ === true)
|
||||
return ['AND', ['reqClassMask', 0, '!'], [['reqClassMask', ChrClass::MASK_ALL, '&'], ChrClass::MASK_ALL, '!']];
|
||||
else if ($_ === false)
|
||||
return ['OR', ['reqClassMask', 0], [['reqClassMask', ChrClass::MASK_ALL, '&'], ChrClass::MASK_ALL]];
|
||||
else if (is_int($_))
|
||||
return ['AND', ['reqClassMask', ChrClass::from($_)->toMask(), '&'], [['reqClassMask', ChrClass::MASK_ALL, '&'], ChrClass::MASK_ALL, '!']];
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
protected function cbRaceSpec(int $cr, int $crs, string $crv) : ?array
|
||||
{
|
||||
if (!isset(self::$enums[$cr][$crs]))
|
||||
return null;
|
||||
|
||||
$_ = self::$enums[$cr][$crs];
|
||||
if ($_ === true)
|
||||
return ['AND', ['reqRaceMask', 0, '!'], [['reqRaceMask', ChrRace::MASK_ALL, '&'], ChrRace::MASK_ALL, '!'], [['reqRaceMask', ChrRace::MASK_ALLIANCE, '&'], ChrRace::MASK_ALLIANCE, '!'], [['reqRaceMask', ChrRace::MASK_HORDE, '&'], ChrRace::MASK_HORDE, '!']];
|
||||
else if ($_ === false)
|
||||
return ['OR', ['reqRaceMask', 0], ['reqRaceMask', ChrRace::MASK_ALL], ['reqRaceMask', ChrRace::MASK_ALLIANCE], ['reqRaceMask', ChrRace::MASK_HORDE]];
|
||||
else if (is_int($_))
|
||||
return ['AND', ['reqRaceMask', ChrRace::from($_)->toMask(), '&'], [['reqRaceMask', ChrRace::MASK_ALLIANCE, '&'], ChrRace::MASK_ALLIANCE, '!'], [['reqRaceMask', ChrRace::MASK_HORDE, '&'], ChrRace::MASK_HORDE, '!']];
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
protected function cbLacksStartEnd(int $cr, int $crs, string $crv) : ?array
|
||||
{
|
||||
if (!$this->int2Bool($crs))
|
||||
return null;
|
||||
|
||||
$missing = DB::Aowow()->selectCol('SELECT `questId`, BIT_OR(`method`) AS "se" FROM ?_quests_startend GROUP BY `questId` HAVING "se" <> 3');
|
||||
if ($crs)
|
||||
return ['id', $missing];
|
||||
else
|
||||
return ['id', $missing, '!'];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
?>
|
||||
77
includes/dbtypes/skill.class.php
Normal file
77
includes/dbtypes/skill.class.php
Normal file
@@ -0,0 +1,77 @@
|
||||
<?php
|
||||
|
||||
namespace Aowow;
|
||||
|
||||
if (!defined('AOWOW_REVISION'))
|
||||
die('illegal access');
|
||||
|
||||
|
||||
class SkillList extends DBTypeList
|
||||
{
|
||||
public static int $type = Type::SKILL;
|
||||
public static string $brickFile = 'skill';
|
||||
public static string $dataTable = '?_skillline';
|
||||
|
||||
protected string $queryBase = 'SELECT sl.*, sl.`id` AS ARRAY_KEY FROM ?_skillline sl';
|
||||
protected array $queryOpts = array(
|
||||
'sl' => [['ic']],
|
||||
'ic' => ['j' => ['?_icons ic ON ic.`id` = sl.`iconId`', true], 's' => ', ic.`name` AS "iconString"'],
|
||||
);
|
||||
|
||||
public function __construct(array $conditions = [], array $miscData = [])
|
||||
{
|
||||
parent::__construct($conditions, $miscData);
|
||||
|
||||
// post processing
|
||||
foreach ($this->iterate() as &$_curTpl)
|
||||
{
|
||||
$_ = &$_curTpl['specializations']; // shorthand
|
||||
if (!$_)
|
||||
$_ = [0, 0, 0, 0, 0];
|
||||
else
|
||||
{
|
||||
$_ = explode(' ', $_);
|
||||
while (count($_) < 5)
|
||||
$_[] = 0;
|
||||
}
|
||||
|
||||
if (!$_curTpl['iconId'])
|
||||
$_curTpl['iconString'] = DEFAULT_ICON;
|
||||
}
|
||||
}
|
||||
|
||||
public function getListviewData() : array
|
||||
{
|
||||
$data = [];
|
||||
|
||||
foreach ($this->iterate() as $__)
|
||||
{
|
||||
$data[$this->id] = array(
|
||||
'category' => $this->curTpl['typeCat'],
|
||||
'categorybak' => $this->curTpl['categoryId'],
|
||||
'id' => $this->id,
|
||||
'name' => $this->getField('name', true),
|
||||
'profession' => $this->curTpl['professionMask'],
|
||||
'recipeSubclass' => $this->curTpl['recipeSubClass'],
|
||||
'specializations' => Util::toJSON($this->curTpl['specializations'], JSON_NUMERIC_CHECK),
|
||||
'icon' => $this->curTpl['iconString']
|
||||
);
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
public function getJSGlobals(int $addMask = 0) : array
|
||||
{
|
||||
$data = [];
|
||||
|
||||
foreach ($this->iterate() as $__)
|
||||
$data[self::$type][$this->id] = ['name' => $this->getField('name', true), 'icon' => $this->curTpl['iconString']];
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
public function renderTooltip() : ?string { return null; }
|
||||
}
|
||||
|
||||
?>
|
||||
129
includes/dbtypes/sound.class.php
Normal file
129
includes/dbtypes/sound.class.php
Normal file
@@ -0,0 +1,129 @@
|
||||
<?php
|
||||
|
||||
namespace Aowow;
|
||||
|
||||
if (!defined('AOWOW_REVISION'))
|
||||
die('illegal access');
|
||||
|
||||
|
||||
class SoundList extends DBTypeList
|
||||
{
|
||||
use spawnHelper;
|
||||
|
||||
public static int $type = Type::SOUND;
|
||||
public static string $brickFile = 'sound';
|
||||
public static string $dataTable = '?_sounds';
|
||||
public static int $contribute = CONTRIBUTE_CO;
|
||||
|
||||
protected string $queryBase = 'SELECT s.*, s.`id` AS ARRAY_KEY FROM ?_sounds s';
|
||||
|
||||
private array $fileBuffer = [];
|
||||
private static array $fileTypes = [SOUND_TYPE_OGG => MIME_TYPE_OGG, SOUND_TYPE_MP3 => MIME_TYPE_MP3];
|
||||
|
||||
public function __construct(array $conditions = [], array $miscData = [])
|
||||
{
|
||||
parent::__construct($conditions, $miscData);
|
||||
|
||||
// post processing
|
||||
foreach ($this->iterate() as $id => &$_curTpl)
|
||||
{
|
||||
$_curTpl['files'] = [];
|
||||
for ($i = 1; $i < 11; $i++)
|
||||
{
|
||||
if ($_curTpl['soundFile'.$i])
|
||||
{
|
||||
$this->fileBuffer[$_curTpl['soundFile'.$i]] = null;
|
||||
$_curTpl['files'][] = &$this->fileBuffer[$_curTpl['soundFile'.$i]];
|
||||
}
|
||||
|
||||
unset($_curTpl['soundFile'.$i]);
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->fileBuffer)
|
||||
{
|
||||
$files = DB::Aowow()->select('SELECT `id` AS ARRAY_KEY, `id`, `file` AS "title", CAST(`type` AS UNSIGNED) AS "type", `path` FROM ?_sounds_files sf WHERE `id` IN (?a)', array_keys($this->fileBuffer));
|
||||
foreach ($files as $id => $data)
|
||||
{
|
||||
// 3.3.5 bandaid - need fullpath to play via wow API, remove for cata and later
|
||||
$data['path'] = str_replace('\\', '\\\\', $data['path'] ? $data['path'] . '\\' . $data['title'] : $data['title']);
|
||||
// skip file extension
|
||||
$data['title'] = substr($data['title'], 0, -4);
|
||||
// enum to string
|
||||
$data['type'] = self::$fileTypes[$data['type']];
|
||||
// get real url
|
||||
$data['url'] = Cfg::get('STATIC_URL') . '/wowsounds/' . $data['id'];
|
||||
// v push v
|
||||
$this->fileBuffer[$id] = $data;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static function getName(int $id) : ?LocString
|
||||
{
|
||||
if ($n = DB::Aowow()->SelectRow('SELECT `name` AS "name_loc0" FROM ?# WHERE `id` = ?d', self::$dataTable, $id))
|
||||
return new LocString($n);
|
||||
return null;
|
||||
}
|
||||
|
||||
public function getListviewData() : array
|
||||
{
|
||||
$data = [];
|
||||
|
||||
foreach ($this->iterate() as $__)
|
||||
{
|
||||
$data[$this->id] = array(
|
||||
'id' => $this->id,
|
||||
'type' => $this->getField('cat'),
|
||||
'name' => $this->getField('name'),
|
||||
'files' => array_values(array_filter($this->getField('files')))
|
||||
);
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
public function getJSGlobals(int $addMask = 0) : array
|
||||
{
|
||||
$data = [];
|
||||
|
||||
foreach ($this->iterate() as $__)
|
||||
$data[self::$type][$this->id] = array(
|
||||
'name' => $this->getField('name', true),
|
||||
'type' => $this->getField('cat'),
|
||||
'files' => array_values(array_filter($this->getField('files')))
|
||||
);
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
public function renderTooltip() : ?string { return null; }
|
||||
}
|
||||
|
||||
class SoundListFilter extends Filter
|
||||
{
|
||||
protected string $type = 'sounds';
|
||||
protected static array $inputFields = array(
|
||||
'na' => [parent::V_REGEX, parent::PATTERN_NAME, false], // name - only printable chars, no delimiter
|
||||
'ty' => [parent::V_LIST, [[1, 4], 6, 9, 10, 12, 13, 14, 16, 17, [19, 31], 50, 52, 53], true ] // type
|
||||
);
|
||||
|
||||
protected function createSQLForValues() : array
|
||||
{
|
||||
$parts = [];
|
||||
$_v = &$this->values;
|
||||
|
||||
// name [str]
|
||||
if ($_v['na'])
|
||||
if ($_ = $this->tokenizeString(['name']))
|
||||
$parts[] = $_;
|
||||
|
||||
// type [list]
|
||||
if ($_v['ty'])
|
||||
$parts[] = ['cat', $_v['ty']];
|
||||
|
||||
return $parts;
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
2805
includes/dbtypes/spell.class.php
Normal file
2805
includes/dbtypes/spell.class.php
Normal file
File diff suppressed because it is too large
Load Diff
180
includes/dbtypes/title.class.php
Normal file
180
includes/dbtypes/title.class.php
Normal file
@@ -0,0 +1,180 @@
|
||||
<?php
|
||||
|
||||
namespace Aowow;
|
||||
|
||||
if (!defined('AOWOW_REVISION'))
|
||||
die('illegal access');
|
||||
|
||||
|
||||
class TitleList extends DBTypeList
|
||||
{
|
||||
use listviewHelper;
|
||||
|
||||
public static int $type = Type::TITLE;
|
||||
public static string $brickFile = 'title';
|
||||
public static string $dataTable = '?_titles';
|
||||
public array $sources = [];
|
||||
|
||||
protected string $queryBase = 'SELECT t.*, t.`id` AS ARRAY_KEY FROM ?_titles t';
|
||||
protected array $queryOpts = array(
|
||||
't' => [['src']], // 11: Type::TITLE
|
||||
'src' => ['j' => ['?_source src ON `type` = 11 AND `typeId` = t.`id`', true], 's' => ', `src13`, `moreType`, `moreTypeId`']
|
||||
);
|
||||
|
||||
public function __construct(array $conditions = [], array $miscData = [])
|
||||
{
|
||||
parent::__construct($conditions, $miscData);
|
||||
|
||||
// post processing
|
||||
foreach ($this->iterate() as &$_curTpl)
|
||||
{
|
||||
// preparse sources - notice: under this system titles can't have more than one source (or two for achivements), which is enough for standard TC cases but may break custom cases
|
||||
if ($_curTpl['moreType'] == Type::ACHIEVEMENT)
|
||||
$this->sources[$this->id][SRC_ACHIEVEMENT][] = $_curTpl['moreTypeId'];
|
||||
else if ($_curTpl['moreType'] == Type::QUEST)
|
||||
$this->sources[$this->id][SRC_QUEST][] = $_curTpl['moreTypeId'];
|
||||
else if ($_curTpl['src13'])
|
||||
$this->sources[$this->id][SRC_CUSTOM_STRING][] = $_curTpl['src13'];
|
||||
|
||||
// titles display up to two achievements at once
|
||||
if ($_curTpl['src12Ext'])
|
||||
$this->sources[$this->id][SRC_ACHIEVEMENT][] = $_curTpl['src12Ext'];
|
||||
|
||||
unset($_curTpl['src12Ext']);
|
||||
unset($_curTpl['moreType']);
|
||||
unset($_curTpl['moreTypeId']);
|
||||
unset($_curTpl['src3']);
|
||||
|
||||
// shorthand for more generic access; required by CommunityContent to determine subject
|
||||
foreach (Locale::cases() as $loc)
|
||||
if ($loc->validate())
|
||||
$_curTpl['name'] = new LocString($_curTpl, 'male', fn($x) => trim(str_replace('%s', '', $x)));
|
||||
// $_curTpl['name_loc'.$loc->value] = trim(str_replace('%s', '', $_curTpl['male_loc'.$loc->value]));
|
||||
}
|
||||
}
|
||||
|
||||
public static function getName(int $id) : ?LocString
|
||||
{
|
||||
if ($n = DB::Aowow()->SelectRow('SELECT `male_loc0`, `male_loc2`, `male_loc3`, `male_loc4`, `male_loc6`, `male_loc8` FROM ?# WHERE `id` = ?d', self::$dataTable, $id))
|
||||
return new LocString($n, 'male', fn($x) => trim(str_replace('%s', '', $x)));
|
||||
return null;
|
||||
}
|
||||
|
||||
public function getListviewData() : array
|
||||
{
|
||||
$data = [];
|
||||
$this->createSource();
|
||||
|
||||
foreach ($this->iterate() as $__)
|
||||
{
|
||||
$data[$this->id] = array(
|
||||
'id' => $this->id,
|
||||
'name' => $this->getField('male', true),
|
||||
'namefemale' => $this->getField('female', true),
|
||||
'side' => $this->curTpl['side'],
|
||||
'gender' => $this->curTpl['gender'],
|
||||
'expansion' => $this->curTpl['expansion'],
|
||||
'category' => $this->curTpl['category']
|
||||
);
|
||||
|
||||
if (!empty($this->curTpl['source']))
|
||||
$data[$this->id]['source'] = $this->curTpl['source'];
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
public function getJSGlobals(int $addMask = 0) : array
|
||||
{
|
||||
$data = [];
|
||||
|
||||
foreach ($this->iterate() as $__)
|
||||
{
|
||||
$data[Type::TITLE][$this->id]['name'] = $this->getField('male', true);
|
||||
|
||||
if ($_ = $this->getField('female', true))
|
||||
$data[Type::TITLE][$this->id]['namefemale'] = $_;
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
private function createSource() : void
|
||||
{
|
||||
$sources = array(
|
||||
SRC_QUEST => [],
|
||||
SRC_ACHIEVEMENT => [],
|
||||
SRC_CUSTOM_STRING => []
|
||||
);
|
||||
|
||||
foreach ($this->iterate() as $__)
|
||||
{
|
||||
if (empty($this->sources[$this->id]))
|
||||
continue;
|
||||
|
||||
foreach (array_keys($sources) as $srcKey)
|
||||
if (isset($this->sources[$this->id][$srcKey]))
|
||||
$sources[$srcKey] = array_merge($sources[$srcKey], $this->sources[$this->id][$srcKey]);
|
||||
}
|
||||
|
||||
// fill in the details
|
||||
if (!empty($sources[SRC_QUEST]))
|
||||
$sources[SRC_QUEST] = (new QuestList(array(['id', $sources[SRC_QUEST]])))->getSourceData();
|
||||
|
||||
if (!empty($sources[SRC_ACHIEVEMENT]))
|
||||
$sources[SRC_ACHIEVEMENT] = (new AchievementList(array(['id', $sources[SRC_ACHIEVEMENT]])))->getSourceData();
|
||||
|
||||
foreach ($this->sources as $Id => $src)
|
||||
{
|
||||
$tmp = [];
|
||||
|
||||
// Quest-source
|
||||
if (isset($src[SRC_QUEST]))
|
||||
{
|
||||
foreach ($src[SRC_QUEST] as $s)
|
||||
{
|
||||
if (isset($sources[SRC_QUEST][$s]['s']))
|
||||
$this->faction2Side($sources[SRC_QUEST][$s]['s']);
|
||||
|
||||
$tmp[SRC_QUEST][] = $sources[SRC_QUEST][$s];
|
||||
}
|
||||
}
|
||||
|
||||
// Achievement-source
|
||||
if (isset($src[SRC_ACHIEVEMENT]))
|
||||
{
|
||||
foreach ($src[SRC_ACHIEVEMENT] as $s)
|
||||
{
|
||||
if (isset($sources[SRC_ACHIEVEMENT][$s]['s']))
|
||||
$this->faction2Side($sources[SRC_ACHIEVEMENT][$s]['s']);
|
||||
|
||||
$tmp[SRC_ACHIEVEMENT][] = $sources[SRC_ACHIEVEMENT][$s];
|
||||
}
|
||||
}
|
||||
|
||||
// other source (only one item possible, so no iteration needed)
|
||||
if (isset($src[SRC_CUSTOM_STRING]))
|
||||
$tmp[SRC_CUSTOM_STRING] = [Lang::game('pvpSources', $Id)];
|
||||
|
||||
$this->templates[$Id]['source'] = $tmp;
|
||||
}
|
||||
}
|
||||
|
||||
public function getHtmlizedName(int $gender = GENDER_MALE) : string
|
||||
{
|
||||
$field = $gender == GENDER_FEMALE ? 'female' : 'male';
|
||||
return str_replace('%s', '<span class="q0"><'.Util::ucFirst(Lang::main('name')).'></span>', $this->getField($field, true));
|
||||
}
|
||||
|
||||
public function renderTooltip() : ?string { return null; }
|
||||
|
||||
private function faction2Side(int &$faction) : void // thats weird.. and hopefully unique to titles
|
||||
{
|
||||
if ($faction == 2) // Horde
|
||||
$faction = 0;
|
||||
else if ($faction != 1) // Alliance
|
||||
$faction = -1; // Both
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
61
includes/dbtypes/user.class.php
Normal file
61
includes/dbtypes/user.class.php
Normal file
@@ -0,0 +1,61 @@
|
||||
<?php
|
||||
|
||||
namespace Aowow;
|
||||
|
||||
if (!defined('AOWOW_REVISION'))
|
||||
die('illegal access');
|
||||
|
||||
|
||||
class UserList extends DBTypeList
|
||||
{
|
||||
public static int $type = Type::USER;
|
||||
public static string $brickFile = 'user';
|
||||
public static string $dataTable = '';
|
||||
public static int $contribute = CONTRIBUTE_NONE;
|
||||
|
||||
protected string $queryBase = 'SELECT *, a.`id` AS ARRAY_KEY FROM ?_account a';
|
||||
protected array $queryOpts = array(
|
||||
'a' => [['r']],
|
||||
'r' => ['j' => ['?_account_reputation r ON r.`userId` = a.`id`', true], 's' => ', IFNULL(SUM(r.`amount`), 0) AS "reputation"', 'g' => 'a.`id`']
|
||||
);
|
||||
|
||||
public function getJSGlobals(int $addMask = 0) : array
|
||||
{
|
||||
$data = [];
|
||||
|
||||
foreach ($this->iterate() as $__)
|
||||
{
|
||||
$data[$this->curTpl['username']] = array(
|
||||
'border' => 0, // border around avatar (rarityColors)
|
||||
'roles' => $this->curTpl['userGroups'],
|
||||
'joined' => date(Util::$dateFormatInternal, $this->curTpl['joinDate']),
|
||||
'posts' => 0, // forum posts
|
||||
// 'gold' => 0, // achievement system
|
||||
// 'silver' => 0, // achievement system
|
||||
// 'copper' => 0, // achievement system
|
||||
'reputation' => $this->curTpl['reputation']
|
||||
);
|
||||
|
||||
// custom titles (only seen on user page..?)
|
||||
if ($_ = $this->curTpl['title'])
|
||||
$data[$this->curTpl['username']]['title'] = $_;
|
||||
|
||||
if ($_ = $this->curTpl['avatar'])
|
||||
{
|
||||
$data[$this->curTpl['username']]['avatar'] = is_numeric($_) ? 2 : 1;
|
||||
$data[$this->curTpl['username']]['avatarmore'] = $_;
|
||||
}
|
||||
|
||||
// more optional data
|
||||
// sig: markdown formated string (only used in forum?)
|
||||
// border: seen as null|1|3 .. changes the border around the avatar (i suspect its meaning changed and got decoupled from premium-status with the introduction of patreon-status)
|
||||
}
|
||||
|
||||
return [Type::USER => $data];
|
||||
}
|
||||
|
||||
public function getListviewData() : array { return []; }
|
||||
public function renderTooltip() : ?string { return null; }
|
||||
}
|
||||
|
||||
?>
|
||||
190
includes/dbtypes/worldevent.class.php
Normal file
190
includes/dbtypes/worldevent.class.php
Normal file
@@ -0,0 +1,190 @@
|
||||
<?php
|
||||
|
||||
namespace Aowow;
|
||||
|
||||
if (!defined('AOWOW_REVISION'))
|
||||
die('illegal access');
|
||||
|
||||
|
||||
class WorldEventList extends DBTypeList
|
||||
{
|
||||
public static int $type = Type::WORLDEVENT;
|
||||
public static string $brickFile = 'event';
|
||||
public static string $dataTable = '?_events';
|
||||
|
||||
protected string $queryBase = 'SELECT e.`holidayId`, e.`cuFlags`, e.`startTime`, e.`endTime`, e.`occurence`, e.`length`, e.`requires`, e.`description` AS "nameINT", e.`id` AS "eventId", e.`id` AS "ARRAY_KEY", h.* FROM ?_events e';
|
||||
protected array $queryOpts = array(
|
||||
'e' => [['h']],
|
||||
'h' => ['j' => ['?_holidays h ON e.`holidayId` = h.`id`', true], 'o' => '-e.`id` ASC']
|
||||
);
|
||||
|
||||
public function __construct(array $conditions = [], array $miscData = [])
|
||||
{
|
||||
parent::__construct($conditions, $miscData);
|
||||
|
||||
// unseting elements while we iterate over the array will cause the pointer to reset
|
||||
$replace = [];
|
||||
|
||||
// post processing
|
||||
foreach ($this->iterate() as $__)
|
||||
{
|
||||
// emulate category
|
||||
$sT = $this->curTpl['scheduleType'];
|
||||
if (!$this->curTpl['holidayId'])
|
||||
$this->curTpl['category'] = 0;
|
||||
else if ($sT == 2)
|
||||
$this->curTpl['category'] = 3;
|
||||
else if (in_array($sT, [0, 1]))
|
||||
$this->curTpl['category'] = 2;
|
||||
else if ($sT == -1)
|
||||
$this->curTpl['category'] = 1;
|
||||
|
||||
// preparse requisites
|
||||
if ($this->curTpl['requires'])
|
||||
$this->curTpl['requires'] = explode(' ', $this->curTpl['requires']);
|
||||
|
||||
// change Ids if holiday is set
|
||||
if ($this->curTpl['holidayId'] > 0)
|
||||
{
|
||||
$this->curTpl['name'] = $this->getField('name', true);
|
||||
$replace[$this->id] = $this->curTpl;
|
||||
}
|
||||
else // set a name if holiday is missing
|
||||
{
|
||||
// template
|
||||
$this->curTpl['name_loc0'] = $this->curTpl['nameINT'];
|
||||
$this->curTpl['iconString'] = 'trade_engineering';
|
||||
$this->curTpl['name'] = '(SERVERSIDE) '.$this->getField('nameINT', true);
|
||||
$replace[$this->id] = $this->curTpl;
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($replace as $old => $data)
|
||||
{
|
||||
unset($this->templates[$old]);
|
||||
$this->templates[$data['eventId']] = $data;
|
||||
}
|
||||
}
|
||||
|
||||
public static function getName(int $id) : ?LocString
|
||||
{
|
||||
$row = DB::Aowow()->SelectRow(
|
||||
'SELECT IFNULL(h.`name_loc0`, e.`description`) AS "name_loc0", h.`name_loc2`, h.`name_loc3`, h.`name_loc4`, h.`name_loc6`, h.`name_loc8`
|
||||
FROM ?_events e
|
||||
LEFT JOIN ?_holidays h ON e.`holidayId` = h.`id`
|
||||
WHERE e.`id` = ?d',
|
||||
$id
|
||||
);
|
||||
|
||||
return $row ? new LocString($row) : null;
|
||||
}
|
||||
|
||||
public static function updateDates($date = null)
|
||||
{
|
||||
if (!$date || empty($date['firstDate']) || empty($date['length']))
|
||||
{
|
||||
return array(
|
||||
'start' => 0,
|
||||
'end' => 0,
|
||||
'rec' => 0
|
||||
);
|
||||
}
|
||||
|
||||
// Convert everything to seconds
|
||||
$firstDate = intVal($date['firstDate']);
|
||||
$lastDate = !empty($date['lastDate']) ? intVal($date['lastDate']) : 5000000000; // in the far far FAR future..;
|
||||
$interval = !empty($date['rec']) ? intVal($date['rec']) : -1;
|
||||
$length = intVal($date['length']);
|
||||
|
||||
$curStart = $firstDate;
|
||||
$curEnd = $firstDate + $length;
|
||||
$nextStart = $curStart + $interval;
|
||||
$nextEnd = $curEnd + $interval;
|
||||
|
||||
while ($interval > 0 && $nextEnd <= $lastDate && $curEnd < time())
|
||||
{
|
||||
$curStart = $nextStart;
|
||||
$curEnd = $nextEnd;
|
||||
$nextStart = $curStart + $interval;
|
||||
$nextEnd = $curEnd + $interval;
|
||||
}
|
||||
|
||||
return array(
|
||||
'start' => $curStart,
|
||||
'end' => $curEnd,
|
||||
'rec' => $interval
|
||||
);
|
||||
}
|
||||
|
||||
public function getListviewData(bool $forNow = false) : array
|
||||
{
|
||||
$data = [];
|
||||
|
||||
foreach ($this->iterate() as $__)
|
||||
{
|
||||
$data[$this->id] = array(
|
||||
'category' => $this->curTpl['category'],
|
||||
'id' => $this->id,
|
||||
'name' => $this->getField('name', true),
|
||||
'_date' => array(
|
||||
'rec' => $this->curTpl['occurence'],
|
||||
'length' => $this->curTpl['length'],
|
||||
'firstDate' => $this->curTpl['startTime'],
|
||||
'lastDate' => $this->curTpl['endTime']
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
if ($forNow)
|
||||
{
|
||||
foreach ($data as &$d)
|
||||
{
|
||||
$u = self::updateDates($d['_date']);
|
||||
unset($d['_date']);
|
||||
$d['startDate'] = $u['start'];
|
||||
$d['endDate'] = $u['end'];
|
||||
$d['rec'] = $u['rec'];
|
||||
}
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
public function getJSGlobals(int $addMask = 0) : array
|
||||
{
|
||||
$data = [];
|
||||
|
||||
foreach ($this->iterate() as $__)
|
||||
$data[Type::WORLDEVENT][$this->id] = ['name' => $this->getField('name', true), 'icon' => $this->curTpl['iconString']];
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
public function renderTooltip() : ?string
|
||||
{
|
||||
if (!$this->curTpl)
|
||||
return null;
|
||||
|
||||
$x = '<table><tr><td>';
|
||||
|
||||
// head v that extra % is nesecary because we are using sprintf later on
|
||||
$x .= '<table width="100%%"><tr><td><b>'.$this->getField('name', true).'</b></td><th><b class="q0">'.Lang::event('category', $this->getField('category')).'</b></th></tr></table>';
|
||||
|
||||
// use string-placeholder for dates
|
||||
// start
|
||||
$x .= Lang::event('start').Lang::main('colon').'%s<br />';
|
||||
// end
|
||||
$x .= Lang::event('end').Lang::main('colon').'%s';
|
||||
|
||||
$x .= '</td></tr></table>';
|
||||
|
||||
// desc
|
||||
if ($this->getField('holidayId'))
|
||||
if ($_ = $this->getField('description', true))
|
||||
$x .= '<table><tr><td><span class="q">'.$_.'</span></td></tr></table>';
|
||||
|
||||
return $x;
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
106
includes/dbtypes/zone.class.php
Normal file
106
includes/dbtypes/zone.class.php
Normal file
@@ -0,0 +1,106 @@
|
||||
<?php
|
||||
|
||||
namespace Aowow;
|
||||
|
||||
if (!defined('AOWOW_REVISION'))
|
||||
die('illegal access');
|
||||
|
||||
|
||||
class ZoneList extends DBTypeList
|
||||
{
|
||||
use listviewHelper;
|
||||
|
||||
public static int $type = Type::ZONE;
|
||||
public static string $brickFile = 'zone';
|
||||
public static string $dataTable = '?_zones';
|
||||
|
||||
protected string $queryBase = 'SELECT z.*, z.`id` AS ARRAY_KEY FROM ?_zones z';
|
||||
|
||||
public function __construct(array $conditions = [], array $miscData = [])
|
||||
{
|
||||
parent::__construct($conditions, $miscData);
|
||||
|
||||
foreach ($this->iterate() as &$_curTpl)
|
||||
{
|
||||
// unpack attunements
|
||||
$_curTpl['attunes'] = [];
|
||||
|
||||
if ($_curTpl['attunementsN'])
|
||||
{
|
||||
foreach (explode(' ', $_curTpl['attunementsN']) as $req)
|
||||
{
|
||||
$req = explode(':', $req);
|
||||
if (!isset($_curTpl['attunes'][$req[0]]))
|
||||
$_curTpl['attunes'][$req[0]] = [$req[1]];
|
||||
else
|
||||
$_curTpl['attunes'][$req[0]][] = $req[1];
|
||||
}
|
||||
}
|
||||
if ($_curTpl['attunementsH'])
|
||||
{
|
||||
foreach (explode(' ', $_curTpl['attunementsH']) as $req)
|
||||
{
|
||||
$req = explode(':', $req);
|
||||
if (!isset($_curTpl['attunes'][$req[0]]))
|
||||
$_curTpl['attunes'][$req[0]] = [-$req[1]];
|
||||
else
|
||||
$_curTpl['attunes'][$req[0]][] = -$req[1];
|
||||
}
|
||||
}
|
||||
|
||||
unset($_curTpl['attunementsN']);
|
||||
unset($_curTpl['attunementsH']);
|
||||
}
|
||||
}
|
||||
|
||||
public function getListviewData() : array
|
||||
{
|
||||
$data = [];
|
||||
|
||||
foreach ($this->iterate() as $__)
|
||||
{
|
||||
$data[$this->id] = array(
|
||||
'id' => $this->id,
|
||||
'category' => $this->curTpl['category'],
|
||||
'territory' => $this->curTpl['faction'],
|
||||
'minlevel' => $this->curTpl['levelMin'],
|
||||
'maxlevel' => $this->curTpl['levelMax'],
|
||||
'name' => $this->getField('name', true)
|
||||
);
|
||||
|
||||
if ($_ = $this->curTpl['expansion'])
|
||||
$data[$this->id]['expansion'] = $_;
|
||||
|
||||
if ($_ = $this->curTpl['type'])
|
||||
$data[$this->id]['instance'] = $_;
|
||||
|
||||
if ($_ = $this->curTpl['maxPlayer'])
|
||||
$data[$this->id]['nplayers'] = $_;
|
||||
|
||||
if ($_ = $this->curTpl['levelReq'])
|
||||
$data[$this->id]['reqlevel'] = $_;
|
||||
|
||||
if ($_ = $this->curTpl['levelReqLFG'])
|
||||
$data[$this->id]['lfgReqLevel'] = $_;
|
||||
|
||||
if ($_ = $this->curTpl['levelHeroic'])
|
||||
$data[$this->id]['heroicLevel'] = $_;
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
public function getJSGlobals(int $addMask = 0) : array
|
||||
{
|
||||
$data = [];
|
||||
|
||||
foreach ($this->iterate() as $__)
|
||||
$data[Type::ZONE][$this->id] = ['name' => $this->getField('name', true)];
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
public function renderTooltip() : ?string { return null; }
|
||||
}
|
||||
|
||||
?>
|
||||
Reference in New Issue
Block a user