mirror of
https://github.com/Sarjuuk/aowow.git
synced 2025-11-29 15:58:16 +08:00
Loot/Refloot
* fixed Reference Loot's drop chance counting towards the adaptive drop chance of loot within the same loot group.
This commit is contained in:
@@ -159,6 +159,7 @@ class DbSimple_Mysqli extends DbSimple_Database
|
|||||||
{
|
{
|
||||||
$this->_lastQuery = $queryMain;
|
$this->_lastQuery = $queryMain;
|
||||||
$this->_expandPlaceholders($queryMain, false);
|
$this->_expandPlaceholders($queryMain, false);
|
||||||
|
mysqli_ping($this->link);
|
||||||
$result = mysqli_query($this->link, $queryMain[0]);
|
$result = mysqli_query($this->link, $queryMain[0]);
|
||||||
if ($result === false)
|
if ($result === false)
|
||||||
return $this->_setDbError($queryMain[0]);
|
return $this->_setDbError($queryMain[0]);
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ class Loot
|
|||||||
|
|
||||||
private $entry = 0; // depending on the lookup itemId oder templateId
|
private $entry = 0; // depending on the lookup itemId oder templateId
|
||||||
private $results = [];
|
private $results = [];
|
||||||
|
private $chanceMods = [];
|
||||||
private $lootTemplates = array(
|
private $lootTemplates = array(
|
||||||
LOOT_REFERENCE, // internal
|
LOOT_REFERENCE, // internal
|
||||||
LOOT_ITEM, // item
|
LOOT_ITEM, // item
|
||||||
@@ -40,7 +41,7 @@ class Loot
|
|||||||
LOOT_SPELL // spell
|
LOOT_SPELL // spell
|
||||||
);
|
);
|
||||||
|
|
||||||
public function &iterate()
|
public function &iterate() : iterable
|
||||||
{
|
{
|
||||||
reset($this->results);
|
reset($this->results);
|
||||||
|
|
||||||
@@ -48,15 +49,15 @@ class Loot
|
|||||||
yield $k => $this->results[$k];
|
yield $k => $this->results[$k];
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getResult()
|
public function getResult() : array
|
||||||
{
|
{
|
||||||
return $this->results;
|
return $this->results;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function createStack($l) // issue: TC always has an equal distribution between min/max
|
private function createStack(array $l) : string // issue: TC always has an equal distribution between min/max
|
||||||
{
|
{
|
||||||
if (empty($l['min']) || empty($l['max']) || $l['max'] <= $l['min'])
|
if (empty($l['min']) || empty($l['max']) || $l['max'] <= $l['min'])
|
||||||
return null;
|
return '';
|
||||||
|
|
||||||
$stack = [];
|
$stack = [];
|
||||||
for ($i = $l['min']; $i <= $l['max']; $i++)
|
for ($i = $l['min']; $i <= $l['max']; $i++)
|
||||||
@@ -66,7 +67,7 @@ class Loot
|
|||||||
return json_encode($stack, JSON_NUMERIC_CHECK); // do not replace with Util::toJSON !
|
return json_encode($stack, JSON_NUMERIC_CHECK); // do not replace with Util::toJSON !
|
||||||
}
|
}
|
||||||
|
|
||||||
private function storeJSGlobals($data)
|
private function storeJSGlobals(array $data) : void
|
||||||
{
|
{
|
||||||
foreach ($data as $type => $jsData)
|
foreach ($data as $type => $jsData)
|
||||||
{
|
{
|
||||||
@@ -81,7 +82,62 @@ class Loot
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private function getByContainerRecursive($tableName, $lootId, &$handledRefs, $groupId = 0, $baseChance = 1.0)
|
private function calcChance(array $refs, array $parents = []) : array
|
||||||
|
{
|
||||||
|
$retData = [];
|
||||||
|
$retKeys = [];
|
||||||
|
|
||||||
|
foreach ($refs as $rId => $ref)
|
||||||
|
{
|
||||||
|
// check for possible database inconsistencies
|
||||||
|
if (!$ref['chance'] && !$ref['isGrouped'])
|
||||||
|
trigger_error('Loot by Item: Ungrouped Item/Ref '.$ref['item'].' has 0% chance assigned!', E_USER_WARNING);
|
||||||
|
|
||||||
|
if ($ref['isGrouped'] && $ref['sumChance'] > 100)
|
||||||
|
trigger_error('Loot by Item: Group with Item/Ref '.$ref['item'].' has '.number_format($ref['sumChance'], 2).'% total chance! Some items cannot drop!', E_USER_WARNING);
|
||||||
|
|
||||||
|
if ($ref['isGrouped'] && $ref['sumChance'] >= 100 && !$ref['chance'])
|
||||||
|
trigger_error('Loot by Item: Item/Ref '.$ref['item'].' with adaptive chance cannot drop. Group already at 100%!', E_USER_WARNING);
|
||||||
|
|
||||||
|
$chance = abs($ref['chance'] ?: (100 - $ref['sumChance']) / $ref['nZeroItems']) / 100;
|
||||||
|
|
||||||
|
// apply inherited chanceMods
|
||||||
|
if (isset($this->chanceMods[$ref['item']]))
|
||||||
|
{
|
||||||
|
$chance *= $this->chanceMods[$ref['item']][0];
|
||||||
|
$chance = 1 - pow(1 - $chance, $this->chanceMods[$ref['item']][1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// save chance for parent-ref
|
||||||
|
$this->chanceMods[$rId] = [$chance, $ref['multiplier']];
|
||||||
|
|
||||||
|
// refTemplate doesn't point to a new ref -> we are done
|
||||||
|
if (!in_array($rId, $parents))
|
||||||
|
{
|
||||||
|
$data = array(
|
||||||
|
'percent' => $chance,
|
||||||
|
'stack' => [$ref['min'], $ref['max']],
|
||||||
|
'count' => 1 // ..and one for the sort script
|
||||||
|
);
|
||||||
|
|
||||||
|
if ($_ = self::createStack($ref))
|
||||||
|
$data['pctstack'] = $_;
|
||||||
|
|
||||||
|
// sort highest chances first
|
||||||
|
$i = 0;
|
||||||
|
for (; $i < count($retData); $i++)
|
||||||
|
if ($retData[$i]['percent'] < $data['percent'])
|
||||||
|
break;
|
||||||
|
|
||||||
|
array_splice($retData, $i, 0, [$data]);
|
||||||
|
array_splice($retKeys, $i, 0, [$rId]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return array_combine($retKeys, $retData);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getByContainerRecursive(string $tableName, int $lootId, array &$handledRefs, int $groupId = 0, float $baseChance = 1.0) : ?array
|
||||||
{
|
{
|
||||||
$loot = [];
|
$loot = [];
|
||||||
$rawItems = [];
|
$rawItems = [];
|
||||||
@@ -101,7 +157,8 @@ class Loot
|
|||||||
'quest' => $entry['QuestRequired'],
|
'quest' => $entry['QuestRequired'],
|
||||||
'group' => $entry['GroupId'],
|
'group' => $entry['GroupId'],
|
||||||
'parentRef' => $tableName == LOOT_REFERENCE ? $lootId : 0,
|
'parentRef' => $tableName == LOOT_REFERENCE ? $lootId : 0,
|
||||||
'realChanceMod' => $baseChance
|
'realChanceMod' => $baseChance,
|
||||||
|
'groupChance' => 0
|
||||||
);
|
);
|
||||||
|
|
||||||
// if ($entry['LootMode'] > 1)
|
// if ($entry['LootMode'] > 1)
|
||||||
@@ -166,12 +223,16 @@ class Loot
|
|||||||
$set['groupChance'] = &$groupChances[$entry['GroupId']];
|
$set['groupChance'] = &$groupChances[$entry['GroupId']];
|
||||||
}
|
}
|
||||||
else if ($entry['GroupId'] && $entry['Chance'])
|
else if ($entry['GroupId'] && $entry['Chance'])
|
||||||
|
{
|
||||||
|
$set['groupChance'] = $entry['Chance'];
|
||||||
|
|
||||||
|
if (!$entry['Reference'])
|
||||||
{
|
{
|
||||||
if (empty($groupChances[$entry['GroupId']]))
|
if (empty($groupChances[$entry['GroupId']]))
|
||||||
$groupChances[$entry['GroupId']] = 0;
|
$groupChances[$entry['GroupId']] = 0;
|
||||||
|
|
||||||
$groupChances[$entry['GroupId']] += $entry['Chance'];
|
$groupChances[$entry['GroupId']] += $entry['Chance'];
|
||||||
$set['groupChance'] = $entry['Chance'];
|
}
|
||||||
}
|
}
|
||||||
else // shouldn't have happened
|
else // shouldn't have happened
|
||||||
{
|
{
|
||||||
@@ -192,21 +253,19 @@ class Loot
|
|||||||
trigger_error('Loot entry '.$lootId.' / group '.$k.' has a total chance of '.number_format($sum, 2).'%. Some items cannot drop!', E_USER_WARNING);
|
trigger_error('Loot entry '.$lootId.' / group '.$k.' has a total chance of '.number_format($sum, 2).'%. Some items cannot drop!', E_USER_WARNING);
|
||||||
$sum = 100;
|
$sum = 100;
|
||||||
}
|
}
|
||||||
|
// is applied as backReference to items with 0-chance
|
||||||
$cnt = empty($nGroupEquals[$k]) ? 1 : $nGroupEquals[$k];
|
$groupChances[$k] = (100 - $sum) / ($nGroupEquals[$k] ?: 1);
|
||||||
|
|
||||||
$groupChances[$k] = (100 - $sum) / $cnt; // is applied as backReference to items with 0-chance
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return [$loot, array_unique($rawItems)];
|
return [$loot, array_unique($rawItems)];
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getByContainer($table, $entry)
|
public function getByContainer(string $table, int $entry): bool
|
||||||
{
|
{
|
||||||
$this->entry = intVal($entry);
|
$this->entry = intVal($entry);
|
||||||
|
|
||||||
if (!in_array($table, $this->lootTemplates) || !$this->entry)
|
if (!in_array($table, $this->lootTemplates) || !$this->entry)
|
||||||
return null;
|
return false;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
todo (high): implement conditions on loot (and conditions in general)
|
todo (high): implement conditions on loot (and conditions in general)
|
||||||
@@ -321,7 +380,7 @@ class Loot
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getByItem($entry, $maxResults = CFG_SQL_LIMIT_DEFAULT, $lootTableList = [])
|
public function getByItem(int $entry, int $maxResults = CFG_SQL_LIMIT_DEFAULT, array $lootTableList = []) : bool
|
||||||
{
|
{
|
||||||
$this->entry = intVal($entry);
|
$this->entry = intVal($entry);
|
||||||
|
|
||||||
@@ -350,13 +409,12 @@ class Loot
|
|||||||
['achievement', [], '$LANG.tab_rewardfrom', 'reward-from-achievement', [], [], []]
|
['achievement', [], '$LANG.tab_rewardfrom', 'reward-from-achievement', [], [], []]
|
||||||
);
|
);
|
||||||
$refResults = [];
|
$refResults = [];
|
||||||
$chanceMods = [];
|
|
||||||
$query = 'SELECT
|
$query = 'SELECT
|
||||||
lt1.entry AS ARRAY_KEY,
|
lt1.entry AS ARRAY_KEY,
|
||||||
IF(lt1.reference = 0, lt1.item, lt1.reference) AS item,
|
IF(lt1.reference = 0, lt1.item, lt1.reference) AS item,
|
||||||
lt1.chance,
|
lt1.chance,
|
||||||
SUM(IF(lt2.chance = 0, 1, 0)) AS nZeroItems,
|
SUM(IF(lt2.chance = 0, 1, 0)) AS nZeroItems,
|
||||||
SUM(lt2.chance) AS sumChance,
|
SUM(IF(lt2.reference = 0, lt2.chance, 0)) AS sumChance,
|
||||||
IF(lt1.groupid > 0, 1, 0) AS isGrouped,
|
IF(lt1.groupid > 0, 1, 0) AS isGrouped,
|
||||||
IF(lt1.reference = 0, lt1.mincount, 1) AS min,
|
IF(lt1.reference = 0, lt1.mincount, 1) AS min,
|
||||||
IF(lt1.reference = 0, lt1.maxcount, 1) AS max,
|
IF(lt1.reference = 0, lt1.maxcount, 1) AS max,
|
||||||
@@ -369,61 +427,6 @@ class Loot
|
|||||||
%s
|
%s
|
||||||
GROUP BY lt2.entry, lt2.groupid';
|
GROUP BY lt2.entry, lt2.groupid';
|
||||||
|
|
||||||
$calcChance = function ($refs, $parents = []) use (&$chanceMods)
|
|
||||||
{
|
|
||||||
$retData = [];
|
|
||||||
$retKeys = [];
|
|
||||||
|
|
||||||
foreach ($refs as $rId => $ref)
|
|
||||||
{
|
|
||||||
// check for possible database inconsistencies
|
|
||||||
if (!$ref['chance'] && !$ref['isGrouped'])
|
|
||||||
trigger_error('Loot by Item: Ungrouped Item/Ref '.$ref['item'].' has 0% chance assigned!', E_USER_WARNING);
|
|
||||||
|
|
||||||
if ($ref['isGrouped'] && $ref['sumChance'] > 100)
|
|
||||||
trigger_error('Loot by Item: Group with Item/Ref '.$ref['item'].' has '.number_format($ref['sumChance'], 2).'% total chance! Some items cannot drop!', E_USER_WARNING);
|
|
||||||
|
|
||||||
if ($ref['isGrouped'] && $ref['sumChance'] >= 100 && !$ref['chance'])
|
|
||||||
trigger_error('Loot by Item: Item/Ref '.$ref['item'].' with adaptive chance cannot drop. Group already at 100%!', E_USER_WARNING);
|
|
||||||
|
|
||||||
$chance = abs($ref['chance'] ?: (100 - $ref['sumChance']) / $ref['nZeroItems']) / 100;
|
|
||||||
|
|
||||||
// apply inherited chanceMods
|
|
||||||
if (isset($chanceMods[$ref['item']]))
|
|
||||||
{
|
|
||||||
$chance *= $chanceMods[$ref['item']][0];
|
|
||||||
$chance = 1 - pow(1 - $chance, $chanceMods[$ref['item']][1]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// save chance for parent-ref
|
|
||||||
$chanceMods[$rId] = [$chance, $ref['multiplier']];
|
|
||||||
|
|
||||||
// refTemplate doesn't point to a new ref -> we are done
|
|
||||||
if (!in_array($rId, $parents))
|
|
||||||
{
|
|
||||||
$data = array(
|
|
||||||
'percent' => $chance,
|
|
||||||
'stack' => [$ref['min'], $ref['max']],
|
|
||||||
'count' => 1 // ..and one for the sort script
|
|
||||||
);
|
|
||||||
|
|
||||||
if ($_ = self::createStack($ref))
|
|
||||||
$data['pctstack'] = $_;
|
|
||||||
|
|
||||||
// sort highest chances first
|
|
||||||
$i = 0;
|
|
||||||
for (; $i < count($retData); $i++)
|
|
||||||
if ($retData[$i]['percent'] < $data['percent'])
|
|
||||||
break;
|
|
||||||
|
|
||||||
array_splice($retData, $i, 0, [$data]);
|
|
||||||
array_splice($retKeys, $i, 0, [$rId]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return array_combine($retKeys, $retData);
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
get references containing the item
|
get references containing the item
|
||||||
*/
|
*/
|
||||||
@@ -442,7 +445,7 @@ class Loot
|
|||||||
array_keys($curRefs)
|
array_keys($curRefs)
|
||||||
);
|
);
|
||||||
|
|
||||||
$refResults += $calcChance($curRefs, array_column($newRefs, 'item'));
|
$refResults += $this->calcChance($curRefs, array_column($newRefs, 'item'));
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -453,7 +456,7 @@ class Loot
|
|||||||
if ($lootTableList && !in_array($this->lootTemplates[$i], $lootTableList))
|
if ($lootTableList && !in_array($this->lootTemplates[$i], $lootTableList))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
$result = $calcChance(DB::World()->select(
|
$result = $this->calcChance(DB::World()->select(
|
||||||
sprintf($query, '{lt1.reference IN (?a) OR }(lt1.reference = 0 AND lt1.item = ?d)'),
|
sprintf($query, '{lt1.reference IN (?a) OR }(lt1.reference = 0 AND lt1.item = ?d)'),
|
||||||
$this->lootTemplates[$i], $this->lootTemplates[$i],
|
$this->lootTemplates[$i], $this->lootTemplates[$i],
|
||||||
$refResults ? array_keys($refResults) : DBSIMPLE_SKIP,
|
$refResults ? array_keys($refResults) : DBSIMPLE_SKIP,
|
||||||
@@ -494,7 +497,7 @@ class Loot
|
|||||||
|
|
||||||
$srcData = $srcObj->getListviewData();
|
$srcData = $srcObj->getListviewData();
|
||||||
|
|
||||||
foreach ($srcObj->iterate() as $__id => $curTpl)
|
foreach ($srcObj->iterate() as $curTpl)
|
||||||
{
|
{
|
||||||
switch ($curTpl['typeCat'])
|
switch ($curTpl['typeCat'])
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user