Map/Spawns

* move areatrigger teleport endpoints and instance entrance points to spawns. This makes them editable like spawn points.
 * since instance entrances aren't shown on maps they are moved through the admin menu on the instances page.
 * rewrote spawns SetupScript. Individual groups can now be recalculated separately.
This commit is contained in:
Sarjuuk
2024-06-26 21:38:12 +02:00
parent 040cac41a1
commit 69de457108
12 changed files with 417 additions and 302 deletions

View File

@@ -383,7 +383,7 @@ class AjaxAdmin extends AjaxHandler
$area = $this->_get['area'];
$floor = $this->_get['floor'];
if (!in_array($type, [Type::NPC, Type::OBJECT, Type::SOUND, Type::AREATRIGGER]))
if (!in_array($type, [Type::NPC, Type::OBJECT, Type::SOUND, Type::AREATRIGGER, Type::ZONE]))
return '-3';
DB::Aowow()->query('REPLACE INTO ?_spawns_override VALUES (?d, ?d, ?d, ?d, ?d)', $type, $guid, $area, $floor, AOWOW_REVISION);

View File

@@ -591,14 +591,16 @@ trait spawnHelper
return;
if (User::isInGroup(U_GROUP_MODERATOR))
if ($guids = array_filter(array_column($spawns, 'guid'), function ($x) { return $x > 0; }))
if ($guids = array_column(array_filter($spawns, function ($x) { return $x['guid'] > 0 || $x['type'] != Type::NPC; }), 'guid'))
$worldPos = Game::getWorldPosForGUID(self::$type, ...$guids);
foreach ($spawns as $s)
{
$isAccessory = $s['guid'] < 0 && $s['type'] == Type::NPC;
// 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
if (count($spawns) < 6 && self::$type == Type::NPC)
if (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']))
{
@@ -641,7 +643,10 @@ trait spawnHelper
if (User::isInGroup(U_GROUP_STAFF))
{
$info[0] = $s['guid'] < 0 ? 'Vehicle Accessory' : 'GUID'.Lang::main('colon').$s['guid'];
if ($isAccessory)
$info[0] = 'Vehicle Accessory';
else if ($s['guid'] > 0 && ($s['type'] == Type::NPC || $s['type'] == Type::OBJECT))
$info[0] = 'GUID'.Lang::main('colon').$s['guid'];
if ($s['phaseMask'] > 1 && ($s['phaseMask'] & 0xFFFF) != 0xFFFF)
$info[2] = Lang::game('phases').Lang::main('colon').Util::asHex($s['phaseMask']);
@@ -658,56 +663,27 @@ trait spawnHelper
$info[4] = Lang::game('mode').Lang::main('colon').implode(', ', $_);
}
if (self::$type == Type::AREATRIGGER)
if ($s['type'] == Type::AREATRIGGER)
{
$o = Util::O2Deg($this->getField('orientation'));
$info[5] = 'Orientation'.Lang::main('colon').$o[0].'° ('.$o[1].')';
// teleporter endpoint
if ($s['guid'] < 0)
{
$opts['type'] = 4;
$info[5] = 'Teleport Destination';
}
else
{
$o = Util::O2Deg($this->getField('orientation'));
$info[5] = 'Orientation'.Lang::main('colon').$o[0].'° ('.$o[1].')';
}
}
// guid < 0 are vehicle accessories. those are moved by moving the vehicle
if (User::isInGroup(U_GROUP_MODERATOR) && $worldPos && $s['guid'] > 0 && isset($worldPos[$s['guid']]))
{
if ($points = Game::worldPosToZonePos($worldPos[$s['guid']]['mapId'], $worldPos[$s['guid']]['posX'], $worldPos[$s['guid']]['posY']))
{
$floors = [];
foreach ($points as $p)
{
if ($p['multifloor'])
$floors[$p['areaId']][] = $p['floor'];
if (User::isInGroup(U_GROUP_MODERATOR) && $worldPos && !$isAccessory && isset($worldPos[$s['guid']]))
$menu = Util::buildPosFixMenu($worldPos[$s['guid']]['mapId'], $worldPos[$s['guid']]['posX'], $worldPos[$s['guid']]['posY'], $s['type'], $s['guid'], $s['areaId'], $s['floor']);
if (isset($menu[$p['areaId']]))
continue;
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'].', 0)', null, null];
}
foreach ($floors as $area => $f)
{
$menu[$area][2] = '';
$menu[$area][3] = [];
if ($menu[$area][4])
$menu[$area][4]['class'] = 'checked';
foreach ($f as $n)
{
if ($n == $s['floor'])
$menu[$area][3][] = [$n, '$g_zone_areas['.$area.']['.($n - 1).']', '', null, ['class' => 'checked q0']];
else
$menu[$area][3][] = [$n, '$g_zone_areas['.$area.']['.($n - 1).']', '$spawnposfix.bind(null, '.self::$type.', '.$s['guid'].', '.$area.', '.$n.')'];
}
}
$menu = array_values($menu);
}
if ($menu)
{
$footer = '<br /><span class="q2">Click to move displayed spawn point</span>';
array_unshift($menu, [null, "Move to..."]);
}
}
if ($menu)
$footer = '<br /><span class="q2">Click to move pin</span>';
}
if ($info)

View File

@@ -257,6 +257,13 @@ define('GUIDE_STATUS_ARCHIVED', 5);
define('DEFAULT_ICON', 'inv_misc_questionmark');
define('MENU_IDX_ID', 0); // ID: A number or string; null makes the menu item a separator
define('MENU_IDX_NAME', 1); // Name: A string
define('MENU_IDX_URL', 2); // URL: A string for the URL, or a function to call when the menu item is clicked
define('MENU_IDX_SUB', 3); // Submenu: Child menu
define('MENU_IDX_OPT', 4); // Options: JSON array with additional options
/*
* Game
*/

View File

@@ -311,16 +311,27 @@ class Game
switch ($type)
{
case Type::NPC:
$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);
$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_x` AS `posX`, `position_y` 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);
$result = DB::AoWoW()->select('SELECT `id` AS ARRAY_KEY, `soundId` AS `id`, `mapId`, `posX`, `posY` FROM ?_soundemitters WHERE `id` IN (?a)', $guids);
break;
case Type::ZONE:
$result = DB::Aowow()->select('SELECT -`id` AS ARRAY_KEY, `id`, `parentMapId` AS `mapId`, `parentX` AS `posX`, `parentY` AS `posY` FROM ?_zones WHERE -`id` IN (?a)', $guids);
break;
case Type::AREATRIGGER:
$result = DB::AoWoW()->select('SELECT `id` AS ARRAY_KEY, `id`, `mapId`, `posX`, `posY` FROM ?_areatrigger WHERE `id` IN (?a)', $guids);
$result = [];
if ($base = array_filter($guids, function ($x) { return $x > 0; }))
$result = array_replace($result, DB::AoWoW()->select('SELECT `id` AS ARRAY_KEY, `id`, `mapId`, `posX`, `posY` FROM ?_areatrigger WHERE `id` IN (?a)', $base));
if ($endpoints = array_filter($guids, function ($x) { return $x < 0; }))
$result = array_replace($result, DB::World()->select(
'SELECT -`ID` AS ARRAY_KEY, ID AS `id`, `target_map` AS `mapId`, `target_position_x` AS `posX`, `target_position_y` AS `posY` FROM areatrigger_teleport WHERE -`id` IN (?a) UNION
SELECT -`entryorguid` AS ARRAY_KEY, entryorguid AS `id`, `action_param1` AS `mapId`, `target_x` AS `posX`, `target_y` AS `posY` FROM smart_scripts WHERE -`entryorguid` IN (?a) AND `source_type` = ?d AND `action_type` = ?d',
$endpoints, $endpoints, SAI_SRC_TYPE_AREATRIGGER, SAI_ACTION_TELEPORT
));
break;
default:
trigger_error('Game::getWorldPosForGUID - instanced with unsupported TYPE #'.$type, E_USER_WARNING);

View File

@@ -140,6 +140,7 @@ abstract class CLI
private const CHR_ESC = 27;
private const CHR_BACKSPACE = 127;
public const LOG_NONE = -1;
public const LOG_BLANK = 0;
public const LOG_ERROR = 1;
public const LOG_WARN = 2;
@@ -190,13 +191,13 @@ abstract class CLI
}
if ($i || $headless)
self::write(' '.implode(' ' . self::tblDelim(' ') . ' ', $row), -1, $timestamp);
self::write(' '.implode(' ' . self::tblDelim(' ') . ' ', $row), CLI::LOG_NONE, $timestamp);
else
self::write(self::tblHead(' '.implode(' ', $row)), -1, $timestamp);
self::write(self::tblHead(' '.implode(' ', $row)), CLI::LOG_NONE, $timestamp);
}
if (!$headless)
self::write(self::tblHead(str_pad('', array_sum($pads) + count($pads) * 3 - 2)), -1, $timestamp);
self::write(self::tblHead(str_pad('', array_sum($pads) + count($pads) * 3 - 2)), CLI::LOG_NONE, $timestamp);
self::write();
}
@@ -1576,7 +1577,7 @@ abstract class Util
}
// orientation is 2*M_PI for a full circle, increasing counterclockwise
static function O2Deg($o)
public static function O2Deg($o)
{
// orientation values can exceed boundaries (for whatever reason)
while ($o < 0)
@@ -1609,7 +1610,7 @@ abstract class Util
return [(int)$deg, $desc];
}
static function mask2bits(int $bitmask, int $offset = 0) : array
public static function mask2bits(int $bitmask, int $offset = 0) : array
{
$bits = [];
$i = 0;
@@ -1625,6 +1626,46 @@ abstract class Util
return $bits;
}
public static function buildPosFixMenu(int $mapId, float $posX, float $posY, int $type, int $guid, int $parentArea = 0, int $parentFloor = 0) : array
{
$points = Game::worldPosToZonePos($mapId, $posX, $posY);
if (!$points || count($points) < 2)
return [];
$floors = [];
$menu = [[null, "Move Location to..."]];
foreach ($points as $p)
{
if ($p['multifloor'])
$floors[$p['areaId']][] = $p['floor'];
if (isset($menu[$p['areaId']]))
continue;
else if ($p['areaId'] == $parentArea)
$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, '.$type.', '.$guid.', '.$p['areaId'].', 0)', null, null];
}
foreach ($floors as $area => $f)
{
$menu[$area][MENU_IDX_URL] = null;
$menu[$area][MENU_IDX_SUB] = [];
if ($menu[$area][MENU_IDX_OPT])
$menu[$area][MENU_IDX_OPT]['class'] = 'checked';
foreach ($f as $n)
{
if ($n == $parentFloor)
$menu[$area][MENU_IDX_SUB][] = [$n, '$g_zone_areas['.$area.']['.($n - 1).']', '', null, ['class' => 'checked q0']];
else
$menu[$area][MENU_IDX_SUB][] = [$n, '$g_zone_areas['.$area.']['.($n - 1).']', '$spawnposfix.bind(null, '.$type.', '.$guid.', '.$area.', '.$n.')'];
}
}
return array_values($menu);
}
}