Files
aowow/includes/dbtypes/enchantment.class.php
Sarjuuk 65d490a8ae Enchantments/Stats
* entirely forgo ?_item_stats table when calculating enchantment stats
2025-10-12 22:24:09 +02:00

264 lines
13 KiB
PHP

<?php
namespace Aowow;
if (!defined('AOWOW_REVISION'))
die('illegal access');
class EnchantmentList extends DBTypeList
{
use listviewHelper;
public static int $type = Type::ENCHANTMENT;
public static string $brickFile = 'enchantment';
public static string $dataTable = '?_itemenchantment';
private array $jsonStats = [];
private ?SpellList $relSpells = null;
private array $triggerIds = [];
protected string $queryBase = 'SELECT ie.*, ie.id AS ARRAY_KEY FROM ?_itemenchantment ie';
protected array $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;
}
}
}
if ($relSpells)
$this->relSpells = new SpellList(array(['id', $relSpells]));
// 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 we can't rely on ?_item_stats and always have to calc stats
foreach ($this->iterate() as $ench)
{
$relSpells = [];
foreach ($ench['spells'] as $s)
if ($_ = $this->relSpells->getEntry($s[0]))
$relSpells[$s[0]] = $_;
$this->jsonStats[$this->id] = (new StatsContainer($relSpells))->fromEnchantment($ench);
}
}
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(includeEmpty: false));
}
return $data;
}
public function getStatGainForCurrent() : array
{
return $this->jsonStats[$this->id]->toJson(includeEmpty: true);
}
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;
}
}
?>