Files
aowow/includes/types/achievement.class.php
Sarjuuk 979a21afae Localization/Caching
* store source lists as object so different locales can be fetched
 * store parsed spell texts locale dependent so the locale isn't fixed
2024-02-25 21:19:04 +01:00

410 lines
17 KiB
PHP

<?php
if (!defined('AOWOW_REVISION'))
die('illegal access');
class AchievementList extends BaseType
{
use listviewHelper;
public static $type = Type::ACHIEVEMENT;
public static $brickFile = 'achievement';
public static $dataTable = '?_achievement';
public $criteria = [];
protected $queryBase = 'SELECT `a`.*, `a`.`id` AS ARRAY_KEY FROM ?_achievement a';
protected $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`']
);
/*
todo: evaluate TC custom-data-tables: a*_criteria_data should be merged on installation
*/
public function __construct($conditions = [], $miscData = null)
{
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($addMask = GLOBALINFO_ANY)
{
$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($addInfoMask = 0x0)
{
$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()
{
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()
{
$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::$perms <= 0)
continue;
$crtName = Util::localizedString($crt, 'name');
switch ($crt['type'])
{
// link to title - todo (low): crosslink
case ACHIEVEMENT_CRITERIA_TYPE_EARNED_PVP_TITLE:
$crtName = Util::ucFirst(Lang::game('title')).Lang::main('colon').$crtName;
break;
// 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 .= '&nbsp;<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 $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
FILTER_ENUM_ANY => true,
FILTER_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 $genericFilter = array(
2 => [FILTER_CR_BOOLEAN, 'reward_loc0', true ], // givesreward
3 => [FILTER_CR_STRING, 'reward', STR_LOCALIZED ], // rewardtext
4 => [FILTER_CR_NYI_PH, null, 1, ], // location [enum]
5 => [FILTER_CR_CALLBACK, 'cbSeries', ACHIEVEMENT_CU_FIRST_SERIES, null], // first in series [yn]
6 => [FILTER_CR_CALLBACK, 'cbSeries', ACHIEVEMENT_CU_LAST_SERIES, null], // last in series [yn]
7 => [FILTER_CR_BOOLEAN, 'chainId', ], // partseries
9 => [FILTER_CR_NUMERIC, 'id', NUM_CAST_INT, true], // id
10 => [FILTER_CR_STRING, 'ic.name', ], // icon
11 => [FILTER_CR_CALLBACK, 'cbRelEvent', null, null], // related event [enum]
14 => [FILTER_CR_FLAG, 'cuFlags', CUSTOM_HAS_COMMENT ], // hascomments
15 => [FILTER_CR_FLAG, 'cuFlags', CUSTOM_HAS_SCREENSHOT ], // hasscreenshots
16 => [FILTER_CR_FLAG, 'cuFlags', CUSTOM_HAS_VIDEO ], // hasvideos
18 => [FILTER_CR_STAFFFLAG, 'flags', ] // flags
);
protected $inputFields = array(
'cr' => [FILTER_V_RANGE, [2, 18], true ], // criteria ids
'crs' => [FILTER_V_LIST, [FILTER_ENUM_NONE, FILTER_ENUM_ANY, [0, 99999]], true ], // criteria operators
'crv' => [FILTER_V_REGEX, parent::PATTERN_CRV, true ], // criteria values - only printable chars, no delimiters
'na' => [FILTER_V_REGEX, parent::PATTERN_NAME, false], // name / description - only printable chars, no delimiter
'ex' => [FILTER_V_EQUAL, 'on', false], // extended name search
'ma' => [FILTER_V_EQUAL, 1, false], // match any / all filter
'si' => [FILTER_V_LIST, [1, 2, 3, -1, -2], false], // side
'minpt' => [FILTER_V_RANGE, [1, 99], false], // required level min
'maxpt' => [FILTER_V_RANGE, [1, 99], false] // required level max
);
protected function createSQLForValues()
{
$parts = [];
$_v = &$this->fiData['v'];
// name ex: +description, +rewards
if (isset($_v['na']))
{
$_ = [];
if (isset($_v['ex']) && $_v['ex'] == 'on')
$_ = $this->modularizeString(['name_loc'.User::$localeId, 'reward_loc'.User::$localeId, 'description_loc'.User::$localeId]);
else
$_ = $this->modularizeString(['name_loc'.User::$localeId]);
if ($_)
$parts[] = $_;
}
// points min
if (isset($_v['minpt']))
$parts[] = ['points', $_v['minpt'], '>='];
// points max
if (isset($_v['maxpt']))
$parts[] = ['points', $_v['maxpt'], '<='];
// faction (side)
if (isset($_v['si']))
{
switch ($_v['si'])
{
case -1: // faction, exclusive both
case -2:
$parts[] = ['faction', -$_v['si']];
break;
case 1: // faction, inclusive both
case 2:
case 3: // both
$parts[] = ['faction', $_v['si'], '&'];
break;
}
}
return $parts;
}
protected function cbRelEvent($cr, $value)
{
if (!isset($this->enums[$cr[0]][$cr[1]]))
return false;
$_ = $this->enums[$cr[0]][$cr[1]];
if (is_int($_))
return ($_ > 0) ? ['category', $_] : ['id', abs($_)];
else
{
$ids = array_filter($this->enums[$cr[0]], function($x) { return is_int($x) && $x > 0; });
return ['category', $ids, $_ ? null : '!'];
}
return false;
}
protected function cbSeries($cr, $value)
{
if ($this->int2Bool($cr[1]))
return $cr[1] ? ['AND', ['chainId', 0, '!'], ['cuFlags', $value, '&']] : ['AND', ['chainId', 0, '!'], [['cuFlags', $value, '&'], 0]];
return false;
}
}
?>