Map/Spawns

* fix spawns for multifloor dungeonmaps that use their worldmaparea entry for coordinates
 * fix mapper feature: move visible spawn point
 * resolve confused X/Y coordinate remapping from a time before i knew world coordinated are rotated by 90°
 * PS: Ulduar floor indizes were funky. No idea why.
 * restore old define order for g_zone_areas
This commit is contained in:
Sarjuuk
2024-06-25 21:34:03 +02:00
parent a08a713dcc
commit 88c5127ab5
15 changed files with 55 additions and 71 deletions

View File

@@ -404,9 +404,9 @@ class AjaxAdmin extends AjaxHandler
if ($type == Type::NPC)
{
$jobs = array(
'SELECT -w.id AS `entry`, w.point AS `pointId`, w.position_y AS `posX`, w.position_x AS `posY` FROM creature_addon ca JOIN waypoint_data w ON w.id = ca.path_id WHERE ca.guid = ?d AND ca.path_id <> 0',
'SELECT `entry`, `pointId`, `location_y` AS `posX`, `location_x` AS `posY` FROM `script_waypoint` WHERE `entry` = ?d',
'SELECT `entry`, `pointId`, `position_y` AS `posX`, `position_x` AS `posY` FROM `waypoints` WHERE `entry` = ?d'
'SELECT -w.id AS `entry`, w.point AS `pointId`, w.position_x AS `posX`, w.position_y AS `posY` FROM creature_addon ca JOIN waypoint_data w ON w.id = ca.path_id WHERE ca.guid = ?d AND ca.path_id <> 0',
'SELECT `entry`, `pointId`, `location_x` AS `posX`, `location_y` AS `posY` FROM `script_waypoint` WHERE `entry` = ?d',
'SELECT `entry`, `pointId`, `position_x` AS `posX`, `position_y` AS `posY` FROM `waypoints` WHERE `entry` = ?d'
);
foreach ($jobs as $idx => $job)

View File

@@ -591,7 +591,8 @@ trait spawnHelper
return;
if (User::isInGroup(U_GROUP_MODERATOR))
$worldPos = Game::getWorldPosForGUID(self::$type, ...array_column($spawns, 'guid'));
if ($guids = array_filter(array_column($spawns, 'guid'), function ($x) { return $x > 0; }))
$worldPos = Game::getWorldPosForGUID(self::$type, ...$guids);
foreach ($spawns as $s)
{
@@ -671,7 +672,7 @@ trait spawnHelper
$floors = [];
foreach ($points as $p)
{
if ($p['floor'])
if ($p['multifloor'])
$floors[$p['areaId']][] = $p['floor'];
if (isset($menu[$p['areaId']]))
@@ -679,7 +680,7 @@ trait spawnHelper
else if ($p['areaId'] == $s['areaId'])
$menu[$p['areaId']] = [$p['areaId'], '$g_zones['.$p['areaId'].']', '', null, ['class' => 'checked q0']];
else
$menu[$p['areaId']] = [$p['areaId'], '$g_zones['.$p['areaId'].']', '$spawnposfix.bind(null, '.self::$type.', '.$s['guid'].', '.$p['areaId'].', -1)', null, null];
$menu[$p['areaId']] = [$p['areaId'], '$g_zones['.$p['areaId'].']', '$spawnposfix.bind(null, '.self::$type.', '.$s['guid'].', '.$p['areaId'].', 0)', null, null];
}
foreach ($floors as $area => $f)
@@ -691,16 +692,10 @@ trait spawnHelper
foreach ($f as $n)
{
$jsRef = $n;
if ($area != 4273) // Ulduar is weird maaaan.....
$jsRef--;
// todo: 3959 (BT) and 4075 (Sunwell) start at level 0 or something
if ($n == $s['floor'])
$menu[$area][3][] = [$jsRef, '$g_zone_areas['.$area.']['.$jsRef.']', '', null, ['class' => 'checked q0']];
$menu[$area][3][] = [$n, '$g_zone_areas['.$area.']['.($n - 1).']', '', null, ['class' => 'checked q0']];
else
$menu[$area][3][] = [$jsRef, '$g_zone_areas['.$area.']['.$jsRef.']', '$spawnposfix.bind(null, '.self::$type.', '.$s['guid'].', '.$area.', '.$n.')'];
$menu[$area][3][] = [$n, '$g_zone_areas['.$area.']['.($n - 1).']', '$spawnposfix.bind(null, '.self::$type.', '.$s['guid'].', '.$area.', '.$n.')'];
}
}

View File

@@ -311,10 +311,10 @@ class Game
switch ($type)
{
case Type::NPC:
$result = DB::World()->select('SELECT `guid` AS ARRAY_KEY, `id`, `map` AS `mapId`, `position_y` AS `posX`, `position_x` AS `posY` FROM creature WHERE `guid` IN (?a)', $guids);
$result = DB::World()->select('SELECT `guid` AS ARRAY_KEY, `id`, `map` AS `mapId`, `position_x` AS `posX`, `position_y` AS `posY` FROM creature WHERE `guid` IN (?a)', $guids);
break;
case Type::OBJECT:
$result = DB::World()->select('SELECT `guid` AS ARRAY_KEY, `id`, `map` AS `mapId`, `position_y` AS `posX`, `position_x` AS `posY` FROM gameobject WHERE `guid` IN (?a)', $guids);
$result = DB::World()->select('SELECT `guid` AS ARRAY_KEY, `id`, `map` AS `mapId`, `position_x` AS `posX`, `position_y` AS `posY` FROM gameobject WHERE `guid` IN (?a)', $guids);
break;
case Type::SOUND:
$result = DB::AoWoW()->select('SELECT `id` AS ARRAY_KEY, `soundId` AS `id`, `mapId`, `posX`, `posY` FROM ?_soundemitters WHERE `id` IN (?a)', $guids);
@@ -337,40 +337,41 @@ class Game
if (!$mapId < 0)
return [];
$query = 'SELECT
dm.`id`,
wma.`areaId`,
IFNULL(dm.`floor`, 0) AS `floor`,
100 - ROUND(IF(dm.`id` IS NOT NULL, (?f - dm.`minY`) * 100 / (dm.`maxY` - dm.`minY`), (?f - wma.`right`) * 100 / (wma.`left` - wma.`right`)), 1) AS `posX`,
100 - ROUND(IF(dm.`id` IS NOT NULL, (?f - dm.`minX`) * 100 / (dm.`maxX` - dm.`minX`), (?f - wma.`bottom`) * 100 / (wma.`top` - wma.`bottom`)), 1) AS `posY`,
SQRT(POWER(ABS(IF(dm.`id` IS NOT NULL, (?f - dm.`minY`) * 100 / (dm.`maxY` - dm.`minY`), (?f - wma.`right`) * 100 / (wma.`left` - wma.`right`)) - 50), 2) +
POWER(ABS(IF(dm.`id` IS NOT NULL, (?f - dm.`minX`) * 100 / (dm.`maxX` - dm.`minX`), (?f - wma.`bottom`) * 100 / (wma.`top` - wma.`bottom`)) - 50), 2)) AS `dist`
FROM
?_worldmaparea wma
LEFT JOIN
?_dungeonmap dm ON dm.`mapId` = IF(?d AND (wma.`mapId` NOT IN (0, 1, 530, 571) OR wma.`areaId` = 4395), wma.`mapId`, -1)
WHERE
wma.`mapId` = ?d AND IF(?d, wma.`areaId` = ?d, wma.`areaId` <> 0){ AND IF(dm.`floor` IS NULL, 1, dm.`floor` = ?d)}
HAVING
(`posX` BETWEEN 0.1 AND 99.9 AND `posY` BETWEEN 0.1 AND 99.9)
ORDER BY
`dist` ASC';
$query =
'SELECT
x.`id`,
x.`areaId`,
IF(x.`defaultDungeonMapId` < 0, x.`floor` + 1, x.`floor`) AS `floor`,
IF(dm.`id` IS NOT NULL OR x.`defaultDungeonMapId` < 0, 1, 0) AS `multifloor`,
ROUND((x.`maxY` - ?d) * 100 / (x.`maxY` - x.`minY`), 1) AS `posX`,
ROUND((x.`maxX` - ?d) * 100 / (x.`maxX` - x.`minX`), 1) AS `posY`,
SQRT(POWER(ABS((x.`maxY` - ?d) * 100 / (x.`maxY` - x.`minY`) - 50), 2) +
POWER(ABS((x.`maxX` - ?d) * 100 / (x.`maxX` - x.`minX`) - 50), 2)) AS `dist`
FROM
(SELECT 0 AS `id`, `areaId`, `mapId`, `right` AS `minY`, `left` AS `maxY`, `top` AS `maxX`, `bottom` AS `minX`, 0 AS `floor`, 0 AS `worldMapAreaId`, `defaultDungeonMapId` FROM ?_worldmaparea wma UNION
SELECT dm.`id`, `areaId`, wma.`mapId`, `minY`, `maxY`, `maxX`, `minX`, `floor`, `worldMapAreaId`, `defaultDungeonMapId` FROM ?_worldmaparea wma
JOIN ?_dungeonmap dm ON dm.`mapId` = wma.`mapId` WHERE wma.`mapId` NOT IN (0, 1, 530, 571) OR wma.`areaId` = 4395) x
LEFT JOIN
?_dungeonmap dm ON dm.`mapId` = x.`mapId` AND dm.`worldMapAreaId` = x.`worldMapAreaId` AND dm.`floor` <> x.`floor` AND dm.`worldMapAreaId` > 0
WHERE
x.`mapId` = ?d AND IF(?d, x.`areaId` = ?d, x.`areaId` <> 0){ AND x.`floor` = ?d - IF(x.`defaultDungeonMapId` < 0, 1, 0)}
GROUP BY
x.`id`, x.`areaId`
HAVING
(`posX` BETWEEN 0.1 AND 99.9 AND `posY` BETWEEN 0.1 AND 99.9)
ORDER BY
`multifloor` DESC, `dist` ASC';
// dist BETWEEN 0 (center) AND 70.7 (corner)
$points = DB::Aowow()->select($query, $posX, $posX, $posY, $posY, $posX, $posX, $posY, $posY, 1, $mapId, $areaId, $areaId, $floor < 0 ? DBSIMPLE_SKIP : $floor);
if (!$points) // retry: TC counts pre-instance subareas as instance-maps .. which have no map file
$points = DB::Aowow()->select($query, $posX, $posX, $posY, $posY, $posX, $posX, $posY, $posY, 0, $mapId, 0, 0, DBSIMPLE_SKIP);
$points = DB::Aowow()->select($query, $posY, $posX, $posY, $posX, $mapId, $areaId, $areaId, $floor < 0 ? DBSIMPLE_SKIP : $floor);
if (!$points) // retry: pre-instance subareas belong to the instance-maps but are displayed on the outside. There also cases where the zone reaches outside it's own map.
$points = DB::Aowow()->select($query, $posY, $posX, $posY, $posX, $mapId, 0, 0, DBSIMPLE_SKIP);
if (!is_array($points))
{
trigger_error('Game::worldPosToZonePos - dbc query failed', E_USER_ERROR);
return [];
}
// Black Temple and Sunwell floor offset bullshit
if ($points && in_array($mapId, [564, 580]))
$points[0]['floor']++;
return $points;
}

View File

@@ -174,7 +174,7 @@ class ZonePage extends GenericPage
// we cannot fetch spawns via lists. lists are grouped by entry
$oSpawns = DB::Aowow()->select('SELECT * FROM ?_spawns WHERE `areaId` = ?d AND `type` = ?d AND `posX` > 0 AND `posY` > 0', $this->typeId, Type::OBJECT);
$cSpawns = DB::Aowow()->select('SELECT * FROM ?_spawns WHERE areaId = ?d AND `type` = ?d AND `posX` > 0 AND `posY` > 0', $this->typeId, Type::NPC);
$cSpawns = DB::Aowow()->select('SELECT * FROM ?_spawns WHERE `areaId` = ?d AND `type` = ?d AND `posX` > 0 AND `posY` > 0', $this->typeId, Type::NPC);
$aSpawns = User::isInGroup(U_GROUP_STAFF) ? DB::Aowow()->select('SELECT * FROM ?_spawns WHERE `areaId` = ?d AND `type` = ?d AND `posX` > 0 AND `posY` > 0', $this->typeId, Type::AREATRIGGER) : [];
$conditions = [Cfg::get('SQL_LIMIT_NONE'), ['s.areaId', $this->typeId]];

View File

@@ -78,8 +78,8 @@ lightId = x
[areatrigger]
id = n
mapId = i
posY = f
posX = f
posY = f
UNUSED4 = x
UNUSED5 = x
UNUSED6 = x
@@ -941,8 +941,8 @@ soundIdNight = i
[soundemitters]
id = n
posY = f
posX = f
posY = f
UNUSED3 = x
UNUSED4 = x
UNUSED5 = x

View File

@@ -38,10 +38,10 @@ CLISetup::registerSetup('sql', new class extends SetupScript
CLI::write('[areatrigger] - calculation teleporter coordinates');
$addData = DB::World()->select(
'SELECT ID AS ARRAY_KEY, Name AS `name`, target_map AS `map`, target_position_x AS `posY`, target_position_y AS `posX`, target_orientation AS `orientation`
'SELECT ID AS ARRAY_KEY, Name AS `name`, target_map AS `map`, target_position_x AS `posX`, target_position_y AS `posY`, target_orientation AS `orientation`
FROM areatrigger_teleport
UNION
SELECT entryorguid AS ARRAY_KEY, "TBD" AS `name`, action_param1 AS `map`, target_x AS `posY`, target_y AS `posX`, target_o AS `orientation`
SELECT entryorguid AS ARRAY_KEY, "TBD" AS `name`, action_param1 AS `map`, target_x AS `posX`, target_y AS `posY`, target_o AS `orientation`
FROM smart_scripts
WHERE source_type = 2 AND action_type = 62'
);

View File

@@ -20,11 +20,11 @@ CLISetup::registerSetup("sql", new class extends SetupScript
protected $setupAfter = [['dungeonmap', 'worldmaparea', 'zones'], ['img-maps']];
private $querys = array(
1 => ['SELECT c.`guid`, 1 AS `type`, c.`id` AS `typeId`, c.`spawntimesecs` AS `respawn`, c.`phaseMask`, c.`zoneId` AS `areaId`, c.`map`, IFNULL(ca.`path_id`, 0) AS `pathId`, c.`position_y` AS `posX`, c.`position_x` AS `posY` ' .
1 => ['SELECT c.`guid`, 1 AS `type`, c.`id` AS `typeId`, c.`spawntimesecs` AS `respawn`, c.`phaseMask`, c.`zoneId` AS `areaId`, c.`map`, IFNULL(ca.`path_id`, 0) AS `pathId`, c.`position_x` AS `posX`, c.`position_y` AS `posY` ' .
'FROM creature c LEFT JOIN creature_addon ca ON ca.guid = c.guid',
'`creature` spawns', Type::NPC],
2 => ['SELECT c.`guid`, 2 AS `type`, c.`id` AS `typeId`, c.`spawntimesecs` AS `respawn`, c.`phaseMask`, c.`zoneId` AS `areaId`, c.`map`, 0 AS `pathId`, c.`position_y` AS `posX`, c.`position_x` AS `posY` ' .
2 => ['SELECT c.`guid`, 2 AS `type`, c.`id` AS `typeId`, c.`spawntimesecs` AS `respawn`, c.`phaseMask`, c.`zoneId` AS `areaId`, c.`map`, 0 AS `pathId`, c.`position_x` AS `posX`, c.`position_y` AS `posY` ' .
'FROM gameobject c',
'`gameobject` spawns', Type::OBJECT],
@@ -36,15 +36,15 @@ CLISetup::registerSetup("sql", new class extends SetupScript
'FROM dbc_areatrigger',
'AreaTrigger.dbc spawns', Type::AREATRIGGER],
5 => ['SELECT c.`guid`, w.`entry` AS `npcOrPath`, w.`pointId` AS `point`, c.`zoneId` AS `areaId`, c.`map`, w.`waittime` AS `wait`, w.`location_y` AS `posX`, w.`location_x` AS `posY` ' .
5 => ['SELECT c.`guid`, w.`entry` AS `npcOrPath`, w.`pointId` AS `point`, c.`zoneId` AS `areaId`, c.`map`, w.`waittime` AS `wait`, w.`location_x` AS `posX`, w.`location_y` AS `posY` ' .
'FROM creature c JOIN script_waypoint w ON c.`id` = w.`entry`',
'`script_waypoint`', Type::NPC],
6 => ['SELECT c.`guid`, w.`entry` AS `npcOrPath`, w.`pointId` AS `point`, c.`zoneId` AS `areaId`, c.`map`, 0 AS `wait`, w.`position_y` AS `posX`, w.`position_x` AS `posY` ' .
6 => ['SELECT c.`guid`, w.`entry` AS `npcOrPath`, w.`pointId` AS `point`, c.`zoneId` AS `areaId`, c.`map`, 0 AS `wait`, w.`position_x` AS `posX`, w.`position_y` AS `posY` ' .
'FROM creature c JOIN waypoints w ON c.`id` = w.`entry`',
'`waypoints`', Type::NPC],
7 => ['SELECT c.`guid`, -w.`id` AS `npcOrPath`, w.`point`, c.`zoneId` AS `areaId`, c.`map`, w.`delay` AS `wait`, w.`position_y` AS `posX`, w.`position_x` AS `posY` ' .
7 => ['SELECT c.`guid`, -w.`id` AS `npcOrPath`, w.`point`, c.`zoneId` AS `areaId`, c.`map`, w.`delay` AS `wait`, w.`position_x` AS `posX`, w.`position_y` AS `posY` ' .
'FROM creature c JOIN creature_addon ca ON ca.`guid` = c.`guid` JOIN waypoint_data w ON w.`id` = ca.`path_id` WHERE ca.`path_id` <> 0',
'`waypoint_data`', Type::NPC]
);
@@ -99,12 +99,11 @@ CLISetup::registerSetup("sql", new class extends SetupScript
CLI::write(' * '.$idx.'/'.count($this->querys).': '. CLI::bold($q[1]).' - '.sprintf('%'.$qtLen.'d / %d (%4.1f%%)', $sum, $qryTotal, round(100 * $sum / $qryTotal, 1)), CLI::LOG_BLANK, true, true);
// npc/object is on a transport -> apply offsets to path of transport
// note, that the coordinates are mixed up .. again
// also note, that transport DO spawn outside of displayable area maps .. another todo i guess..
// note, that transport DO spawn outside of displayable area maps .. another todo i guess..
if (isset($transports[$spawn['map']]))
{
$spawn['posX'] += $transports[$spawn['map']]['posY'];
$spawn['posY'] += $transports[$spawn['map']]['posX'];
$spawn['posX'] += $transports[$spawn['map']]['posX'];
$spawn['posY'] += $transports[$spawn['map']]['posY'];
$spawn['map'] = $transports[$spawn['map']]['mapId'];
}

View File

@@ -0,0 +1,3 @@
DROP TABLE IF EXISTS dbc_areatrigger, dbc_soundemitters;
UPDATE `aowow_dbversion` SET `sql` = CONCAT(IFNULL(`sql`, ''), ' areatrigger soundemitters spawns');

View File

@@ -19470,7 +19470,7 @@ Mapper.onlyOneFloor = {
};
Mapper.zoneLevelOffset = {
4273: 0 // Ulduar
// 4273: 0 // Ulduar // aowow - removed. why did this exist. what did i miss..?
};
Mapper.zoneDefaultLevel = {
@@ -20401,9 +20401,7 @@ Mapper.prototype = {
}
};
/* aowow: already defined in locale_xx instead of being fetched later
var g_zone_areas = {};
*/
var MapViewer = new function()
{

View File

@@ -2133,8 +2133,6 @@ var g_zones = {
4987: 'Das Rubinsanktum'
};
var g_zone_areas = {};
var g_zone_categories = {
0: 'Östliche Königreiche',
1: 'Kalimdor',

View File

@@ -2181,8 +2181,6 @@ var g_zones = {
4987: 'The Ruby Sanctum'
};
var g_zone_areas = {};
var g_zone_categories = {
0: 'Eastern Kingdoms',
1: 'Kalimdor',

View File

@@ -2133,8 +2133,6 @@ var g_zones = {
4987: 'El Sagrario Rubí'
};
var g_zone_areas = {};
var g_zone_categories = {
0: 'Reinos del Este',
1: 'Kalimdor',

View File

@@ -2133,8 +2133,6 @@ var g_zones = {
4987: 'Le sanctum Rubis'
};
var g_zone_areas = {};
var g_zone_categories = {
0: 'Royaumes de l\'est',
1: 'Kalimdor',

View File

@@ -2133,8 +2133,6 @@ var g_zones = {
4987: 'Рубиновое святилище'
};
var g_zone_areas = {};
var g_zone_categories = {
0: 'Восточные королевства',
1: 'Калимдор',

View File

@@ -2180,8 +2180,6 @@ var g_zones = {
4987: '红玉圣殿'
};
var g_zone_areas = {};
var g_zone_categories = {
0: "东部王国",
1: "卡利姆多",