ItemDetailPage/Vendors

* show mapper for vendor locations
This commit is contained in:
Sarjuuk
2025-05-20 17:05:57 +02:00
parent 6475a9d181
commit 9d704c5ff9
14 changed files with 59 additions and 34 deletions

View File

@@ -599,18 +599,19 @@ trait spawnHelper
} }
} }
private function createFullSpawns() // for display on map (object/npc detail page) // for display on map (object/npc detail page)
private function createFullSpawns(bool $skipWPs = false, bool $skipAdmin = false, bool $hasLabel = false, bool $hasLink = false)
{ {
$data = []; $data = [];
$wpSum = []; $wpSum = [];
$wpIdx = 0; $wpIdx = 0;
$worldPos = []; $worldPos = [];
$spawns = DB::Aowow()->select("SELECT * FROM ?_spawns WHERE `type` = ?d AND `typeId` = ?d AND `posX` > 0 AND `posY` > 0", self::$type, $this->id); $spawns = DB::Aowow()->select("SELECT * FROM ?_spawns WHERE `type` = ?d AND `typeId` IN (?a) AND `posX` > 0 AND `posY` > 0", self::$type, $this->getFoundIDs());
if (!$spawns) if (!$spawns)
return; return;
if (User::isInGroup(U_GROUP_MODERATOR)) if (!$skipAdmin && User::isInGroup(U_GROUP_MODERATOR))
if ($guids = array_column(array_filter($spawns, fn($x) => $x['guid'] > 0 || $x['type'] != Type::NPC), 'guid')) if ($guids = array_column(array_filter($spawns, fn($x) => $x['guid'] > 0 || $x['type'] != Type::NPC), 'guid'))
$worldPos = WorldPosition::getForGUID(self::$type, ...$guids); $worldPos = WorldPosition::getForGUID(self::$type, ...$guids);
@@ -620,7 +621,7 @@ trait spawnHelper
// check, if we can attach waypoints to creature // check, if we can attach waypoints to creature
// we will get a nice clusterfuck of dots if we do this for more GUIDs, than we have colors though // we will get a nice clusterfuck of dots if we do this for more GUIDs, than we have colors though
if (count($spawns) < 6 && $s['type'] == Type::NPC) if (!$skipWPs && count($spawns) < 6 && $s['type'] == Type::NPC)
{ {
if ($wPoints = DB::Aowow()->select('SELECT * FROM ?_creature_waypoints WHERE creatureOrPath = ?d AND floor = ?d', $s['pathId'] ? -$s['pathId'] : $this->id, $s['floor'])) if ($wPoints = DB::Aowow()->select('SELECT * FROM ?_creature_waypoints WHERE creatureOrPath = ?d AND floor = ?d', $s['pathId'] ? -$s['pathId'] : $this->id, $s['floor']))
{ {
@@ -661,7 +662,7 @@ trait spawnHelper
$opts['type'] = 4; // make pip purple $opts['type'] = 4; // make pip purple
} }
if (User::isInGroup(U_GROUP_STAFF)) if (!$skipAdmin && User::isInGroup(U_GROUP_STAFF))
{ {
if ($isAccessory) if ($isAccessory)
$info[0] = 'Vehicle Accessory'; $info[0] = 'Vehicle Accessory';
@@ -706,14 +707,28 @@ trait spawnHelper
$footer = '<br /><span class="q2">Click to move pin</span>'; $footer = '<br /><span class="q2">Click to move pin</span>';
} }
/* recognized opts
* url: string - makes pin clickable
* tooltip: array - title => [info: <arr>lines, footer: <string>line]
* label: string - single line tooltip (skipped if 'tooltip' is set)
* menu: array - menu definiton (conflicts with url)
* type: int - colors the pip [default, green, red, blue, purple]
* lines: array - [[destX, destY]] - draws line from point to dest
*/
if ($info) if ($info)
$tt['info'] = $info; $tt['info'] = $info;
if ($footer) if ($footer)
$tt['footer'] = $footer; $tt['footer'] = $footer;
if ($tt) if ($tt && $this->getEntry($s['typeId']))
$opts['tooltip'] = [$this->getField('name', true) => $tt]; $opts['tooltip'] = [$this->getField('name', true) => $tt];
else if ($hasLabel && $this->getEntry($s['typeId']))
$opts['label'] = $this->getField('name', true);
if ($hasLink)
$opts['url'] = '?' . Type::getFileString(self::$type) . '=' . $s['typeId'];
if ($menu) if ($menu)
$opts['menu'] = $menu; $opts['menu'] = $menu;
@@ -722,9 +737,9 @@ trait spawnHelper
} }
foreach ($data as $a => &$areas) foreach ($data as $a => &$areas)
foreach ($areas as $f => &$floor) foreach ($areas as $f => &$floor)
$floor['count'] = count($floor['coords']) - (!empty($wpSum[$a][$f]) ? $wpSum[$a][$f] : 0); $floor['count'] = count($floor['coords']) - ($wpSum[$a][$f] ?? 0);
uasort($data, array($this, 'sortBySpawnCount')); uasort($data, [$this, 'sortBySpawnCount']);
$this->spawnResult[SPAWNINFO_FULL] = $data; $this->spawnResult[SPAWNINFO_FULL] = $data;
} }
@@ -733,11 +748,7 @@ trait spawnHelper
$aCount = current($a)['count']; $aCount = current($a)['count'];
$bCount = current($b)['count']; $bCount = current($b)['count'];
if ($aCount == $bCount) { return $bCount <=> $aCount; // sort descending
return 0;
}
return ($aCount < $bCount) ? 1 : -1;
} }
private function createZoneSpawns() // [zoneId1, zoneId2, ..] for locations-column in listview private function createZoneSpawns() // [zoneId1, zoneId2, ..] for locations-column in listview
@@ -785,7 +796,7 @@ trait spawnHelper
$this->spawnResult[SPAWNINFO_QUEST] = $spawns; $this->spawnResult[SPAWNINFO_QUEST] = $spawns;
} }
public function getSpawns($mode) public function getSpawns(int $mode, bool ...$info)
{ {
// only Creatures, GOs and SoundEmitters can be spawned // only Creatures, GOs and SoundEmitters can be spawned
if (!self::$type || !$this->getfoundIDs() || (self::$type != Type::NPC && self::$type != Type::OBJECT && self::$type != Type::SOUND && self::$type != Type::AREATRIGGER)) if (!self::$type || !$this->getfoundIDs() || (self::$type != Type::NPC && self::$type != Type::OBJECT && self::$type != Type::SOUND && self::$type != Type::AREATRIGGER))
@@ -800,7 +811,7 @@ trait spawnHelper
return $this->spawnResult[SPAWNINFO_SHORT]; return $this->spawnResult[SPAWNINFO_SHORT];
case SPAWNINFO_FULL: case SPAWNINFO_FULL:
if (empty($this->spawnResult[SPAWNINFO_FULL])) if (empty($this->spawnResult[SPAWNINFO_FULL]))
$this->createFullSpawns(); $this->createFullSpawns(...$info);
return $this->spawnResult[SPAWNINFO_FULL]; return $this->spawnResult[SPAWNINFO_FULL];
case SPAWNINFO_ZONES: case SPAWNINFO_ZONES:

View File

@@ -2081,6 +2081,8 @@ $lang = array(
'uniqueEquipped'=> ["Einzigartig anlegbar", null, "Einzigartig angelegt: %s (%d)"], 'uniqueEquipped'=> ["Einzigartig anlegbar", null, "Einzigartig angelegt: %s (%d)"],
'speed' => "Tempo", 'speed' => "Tempo",
'dps' => "(%.1f Schaden pro Sekunde)", 'dps' => "(%.1f Schaden pro Sekunde)",
'vendorIn' => "Händlerstandpunkte",
'purchasedIn' => "Dieser Gegenstand kann gekauft werden in",
'duration' => array( // ITEM_DURATION_* 'duration' => array( // ITEM_DURATION_*
'', '',
"Dauer: %d Sek.", "Dauer: %d Sek.",

View File

@@ -2081,6 +2081,8 @@ $lang = array(
'uniqueEquipped'=> ["Unique-Equipped", null, "Unique-Equipped: %s (%d)"], // ITEM_UNIQUE_EQUIPPABLE, null, ITEM_LIMIT_CATEGORY_MULTIPLE 'uniqueEquipped'=> ["Unique-Equipped", null, "Unique-Equipped: %s (%d)"], // ITEM_UNIQUE_EQUIPPABLE, null, ITEM_LIMIT_CATEGORY_MULTIPLE
'speed' => "Speed", // SPEED 'speed' => "Speed", // SPEED
'dps' => "(%.1f damage per second)", // DPS_TEMPLATE 'dps' => "(%.1f damage per second)", // DPS_TEMPLATE
'vendorIn' => "Vendor Locations",
'purchasedIn' => "This item can be purchased in",
'duration' => array( // ITEM_DURATION_* 'duration' => array( // ITEM_DURATION_*
'', '',
"Duration: %d sec", "Duration: %d sec",

View File

@@ -2081,6 +2081,8 @@ $lang = array(
'uniqueEquipped'=> ["Único-Equipado", null, "Único-Equipado: %s (%d)"], 'uniqueEquipped'=> ["Único-Equipado", null, "Único-Equipado: %s (%d)"],
'speed' => "Veloc.", 'speed' => "Veloc.",
'dps' => "(%.1f daño por segundo)", 'dps' => "(%.1f daño por segundo)",
'vendorIn' => "Ubicación de Vendedores",
'purchasedIn' => "[This item can be purchased in]",
'duration' => array( 'duration' => array(
'', '',
"Duración: %d s", "Duración: %d s",

View File

@@ -2081,6 +2081,8 @@ $lang = array(
'uniqueEquipped'=> ["Unique - Equipé", null, "Unique - Equipé: %s (%d)"], // ITEM_UNIQUE_EQUIPPABLE, null, ITEM_LIMIT_CATEGORY_MULTIPLE 'uniqueEquipped'=> ["Unique - Equipé", null, "Unique - Equipé: %s (%d)"], // ITEM_UNIQUE_EQUIPPABLE, null, ITEM_LIMIT_CATEGORY_MULTIPLE
'speed' => "Vitesse", 'speed' => "Vitesse",
'dps' => "(%.1f dégâts par seconde)", 'dps' => "(%.1f dégâts par seconde)",
'vendorIn' => "Lieux de vente",
'purchasedIn' => "Cet objet peut être acheté dans",
'duration' => array( 'duration' => array(
'', '',
"Durée : %d sec", "Durée : %d sec",

View File

@@ -2081,6 +2081,8 @@ $lang = array(
'uniqueEquipped'=> ["Уникальный использующийся", null, "Уникальный использующийся предмет: %s (%d)"], 'uniqueEquipped'=> ["Уникальный использующийся", null, "Уникальный использующийся предмет: %s (%d)"],
'speed' => "Скорость", 'speed' => "Скорость",
'dps' => "(%.1f ед. урона в секунду)", 'dps' => "(%.1f ед. урона в секунду)",
'vendorIn' => "Расположение торговца",
'purchasedIn' => "Этот предмет приобретается в",
'duration' => array( 'duration' => array(
'', '',
"Срок действия: %d |4секунда:секунды:секунд;", "Срок действия: %d |4секунда:секунды:секунд;",

View File

@@ -2081,6 +2081,8 @@ $lang = array(
'uniqueEquipped'=> ["装备唯一", null, "装备唯一:%s %d"], 'uniqueEquipped'=> ["装备唯一", null, "装备唯一:%s %d"],
'speed' => "速度", 'speed' => "速度",
'dps' => "(每秒伤害%.1f", 'dps' => "(每秒伤害%.1f",
'vendorIn' => "[Vendor Locations]",
'purchasedIn' => "[This item can be purchased in]",
'duration' => array( 'duration' => array(
'', '',
"持续时间:%d秒", "持续时间:%d秒",

View File

@@ -58,7 +58,7 @@ class AreaTriggerPage extends GenericPage
$map = null; $map = null;
if ($spawns = $this->subject->getSpawns(SPAWNINFO_FULL)) if ($spawns = $this->subject->getSpawns(SPAWNINFO_FULL))
{ {
$map = ['data' => ['parent' => 'mapper-generic'], 'mapperData' => &$spawns]; $map = ['data' => ['parent' => 'mapper-generic'], 'mapperData' => &$spawns, 'foundIn' => Lang::areatrigger('foundIn')];
foreach ($spawns as $areaId => &$areaData) foreach ($spawns as $areaId => &$areaData)
$map['extra'][$areaId] = ZoneList::getName($areaId); $map['extra'][$areaId] = ZoneList::getName($areaId);
} }

View File

@@ -783,6 +783,14 @@ class ItemPage extends genericPage
$soldBy = new CreatureList(array(['id', array_keys($vendors)])); $soldBy = new CreatureList(array(['id', array_keys($vendors)]));
if (!$soldBy->error) if (!$soldBy->error)
{ {
// show mapper for vendors
if ($vendorSpawns = $soldBy->getSpawns(SPAWNINFO_FULL, true, true, true, true))
{
$this->map = ['data' => ['parent' => 'mapper-generic'], 'mapperData' => &$vendorSpawns, 'foundIn' => Lang::item('purchasedIn')];
foreach ($vendorSpawns as $areaId => $_)
$this->map['extra'][$areaId] = ZoneList::getName($areaId);
}
$sbData = $soldBy->getListviewData(); $sbData = $soldBy->getListviewData();
$this->extendGlobalData($soldBy->getJSGlobals(GLOBALINFO_SELF)); $this->extendGlobalData($soldBy->getJSGlobals(GLOBALINFO_SELF));

View File

@@ -371,7 +371,7 @@ class NpcPage extends GenericPage
$map = null; $map = null;
if ($spawns = $this->subject->getSpawns(SPAWNINFO_FULL)) if ($spawns = $this->subject->getSpawns(SPAWNINFO_FULL))
{ {
$map = ['data' => ['parent' => 'mapper-generic'], 'mapperData' => &$spawns]; $map = ['data' => ['parent' => 'mapper-generic'], 'mapperData' => &$spawns, 'foundIn' => Lang::npc('foundIn')];
foreach ($spawns as $areaId => &$areaData) foreach ($spawns as $areaId => &$areaData)
$map['extra'][$areaId] = ZoneList::getName($areaId); $map['extra'][$areaId] = ZoneList::getName($areaId);
} }

View File

@@ -222,7 +222,7 @@ class ObjectPage extends GenericPage
$map = null; $map = null;
if ($spawns = $this->subject->getSpawns(SPAWNINFO_FULL)) if ($spawns = $this->subject->getSpawns(SPAWNINFO_FULL))
{ {
$map = ['data' => ['parent' => 'mapper-generic'], 'mapperData' => &$spawns]; $map = ['data' => ['parent' => 'mapper-generic'], 'mapperData' => &$spawns, 'foundIn' => Lang::gameObject('foundIn')];
foreach ($spawns as $areaId => &$areaData) foreach ($spawns as $areaId => &$areaData)
$map['extra'][$areaId] = ZoneList::getName($areaId); $map['extra'][$areaId] = ZoneList::getName($areaId);
} }

View File

@@ -87,7 +87,7 @@ class SoundPage extends GenericPage
$map = null; $map = null;
if ($spawns = $this->subject->getSpawns(SPAWNINFO_FULL)) if ($spawns = $this->subject->getSpawns(SPAWNINFO_FULL))
{ {
$map = ['data' => ['parent' => 'mapper-generic'], 'mapperData' => &$spawns]; $map = ['data' => ['parent' => 'mapper-generic'], 'mapperData' => &$spawns, 'foundIn' => Lang::sound('foundIn')];
foreach ($spawns as $areaId => &$areaData) foreach ($spawns as $areaId => &$areaData)
$map['extra'][$areaId] = ZoneList::getName($areaId); $map['extra'][$areaId] = ZoneList::getName($areaId);
} }

View File

@@ -6,26 +6,15 @@ if (isset($this->map) && empty($this->map)):
elseif (!empty($this->map['data'])): elseif (!empty($this->map['data'])):
if ($this->type == Type::QUEST) : if ($this->type == Type::QUEST) :
echo " <div id=\"mapper-zone-generic\"></div>\n"; echo " <div id=\"mapper-zone-generic\"></div>\n";
elseif ($this->type != Type::ZONE): elseif ($this->map['mapperData']):
echo ' <div>'; echo ' <div>';
if ($this->type == Type::OBJECT): echo $this->map['foundIn'];
echo Lang::gameObject('foundIn');
elseif ($this->type == Type::SOUND):
echo Lang::sound('foundIn');
elseif ($this->type == Type::NPC):
echo Lang::npc('foundIn');
elseif ($this->type == Type::AREATRIGGER):
echo Lang::areatrigger('foundIn');
else:
echo "UNKNOWN TYPE";
endif;
echo ' <span id="mapper-zone-generic">'; echo ' <span id="mapper-zone-generic">';
$extra = $this->map['extra']; echo Lang::concat($this->map['mapperData'], true, function ($areaData, $areaId) {
echo Lang::concat($this->map['mapperData'], true, function ($areaData, $areaId) use ($extra) { return '<a href="javascript:;" onclick="myMapper.update({zone: '.$areaId.'}); g_setSelectedLink(this, \'mapper\'); return false" onmousedown="return false">'.$this->map['extra'][$areaId].'</a>&nbsp;('.array_sum(array_column($areaData, 'count')).')';
return '<a href="javascript:;" onclick="myMapper.update({zone: '.$areaId.'}); g_setSelectedLink(this, \'mapper\'); return false" onmousedown="return false">'.$extra[$areaId].'</a>&nbsp;('.array_sum(array_column($areaData, 'count')).')';
}); });
echo ".</span></div>\n"; echo ".</span></div>\n";

View File

@@ -31,6 +31,11 @@ endif;
$this->brick('article'); $this->brick('article');
if (isset($this->map)):
echo " <h3>".Lang::item('vendorIn')."</h3>\n";
$this->brick('mapper');
endif;
if (!empty($this->transfer)): if (!empty($this->transfer)):
echo " <div class=\"pad\"></div>\n ".$this->transfer."\n"; echo " <div class=\"pad\"></div>\n ".$this->transfer."\n";
endif; endif;