InventoryTransaction: Added a more robust action flattening mechanism

This now handles the case where there are multiple options which could be taken, and opts for the first result which successfully ties all the actions together. Previously it would be entirely down to chance (ordering) whether the actions would get ordered successfully.
This commit is contained in:
Dylan K. Taylor
2018-03-29 11:21:48 +01:00
parent 30a83544a0
commit 1420cd1fa5

View File

@ -165,65 +165,73 @@ class InventoryTransaction{
protected function squashDuplicateSlotChanges() : bool{ protected function squashDuplicateSlotChanges() : bool{
/** @var SlotChangeAction[][] $slotChanges */ /** @var SlotChangeAction[][] $slotChanges */
$slotChanges = []; $slotChanges = [];
/** @var Inventory[] $inventories */
$inventories = [];
/** @var int[] $slots */
$slots = [];
foreach($this->actions as $key => $action){ foreach($this->actions as $key => $action){
if($action instanceof SlotChangeAction){ if($action instanceof SlotChangeAction){
$slotChanges[spl_object_hash($action->getInventory()) . "@" . $action->getSlot()][] = $action; $slotChanges[$h = (spl_object_hash($action->getInventory()) . "@" . $action->getSlot())][] = $action;
$inventories[$h] = $action->getInventory();
$slots[$h] = $action->getSlot();
} }
} }
foreach($slotChanges as $hash => $list){ foreach($slotChanges as $hash => $list){
if(count($list) === 1){ //No need to compact slot changes if there is only one on this slot if(count($list) === 1){ //No need to compact slot changes if there is only one on this slot
unset($slotChanges[$hash]);
continue; continue;
} }
$originalList = $list; $inventory = $inventories[$hash];
$slot = $slots[$hash];
$sourceItem = $inventory->getItem($slot);
/** @var SlotChangeAction|null $originalAction */ $targetItem = $this->findResultItem($sourceItem, $list);
$originalAction = null; if($targetItem === null){
/** @var Item|null $lastTargetItem */ MainLogger::getLogger()->debug("Failed to compact " . count($list) . " actions for " . $this->source->getName());
$lastTargetItem = null;
foreach($list as $i => $action){
if($action->isValid($this->source)){
$originalAction = $action;
$lastTargetItem = $action->getTargetItem();
unset($list[$i]);
break;
}
}
if($originalAction === null){
return false; //Couldn't find any actions that had a source-item matching the current inventory slot
}
do{
$sortedThisLoop = 0;
foreach($list as $i => $action){
$actionSource = $action->getSourceItem();
if($actionSource->equalsExact($lastTargetItem)){
$lastTargetItem = $action->getTargetItem();
unset($list[$i]);
$sortedThisLoop++;
}
}
}while($sortedThisLoop > 0);
if(count($list) > 0){ //couldn't chain all the actions together
MainLogger::getLogger()->debug("Failed to compact " . count($originalList) . " actions for " . $this->source->getName());
return false; return false;
} }
foreach($originalList as $action){ foreach($list as $action){
unset($this->actions[spl_object_hash($action)]); unset($this->actions[spl_object_hash($action)]);
} }
$this->addAction(new SlotChangeAction($originalAction->getInventory(), $originalAction->getSlot(), $originalAction->getSourceItem(), $lastTargetItem)); if(!$targetItem->equalsExact($sourceItem)){
//sometimes we get actions on the crafting grid whose source and target items are the same, so dump them
$this->addAction(new SlotChangeAction($inventory, $slot, $sourceItem, $targetItem));
}
} }
return true; return true;
} }
/**
* @param Item $needOrigin
* @param SlotChangeAction[] $possibleActions
*
* @return null|Item
*/
protected function findResultItem(Item $needOrigin, array $possibleActions) : ?Item{
assert(!empty($possibleActions));
foreach($possibleActions as $i => $action){
if($action->getSourceItem()->equalsExact($needOrigin)){
$newList = $possibleActions;
unset($newList[$i]);
if(empty($newList)){
return $action->getTargetItem();
}
$result = $this->findResultItem($action->getTargetItem(), $newList);
if($result !== null){
return $result;
}
}
}
return null;
}
/** /**
* @return bool * @return bool
*/ */