mirror of
https://github.com/Sarjuuk/aowow.git
synced 2025-11-29 15:58:16 +08:00
Maps/Spawns
* Entities (Objects, NPCs, ect) can now easily be assigned to a different map to be displayed on by clicking their pip on the map * Entities with already assigned area (by TrinityCore) that were unable to be matched onto the map are no longer discarded. They'll now show up in appropriate listviews. * Entities without already assigned area that are also unable to be matched onto the map now get an area assigned as long as the relationship areaId <=> mapId is unique (read instanced areas)
This commit is contained in:
@@ -55,6 +55,24 @@ class AjaxHandler
|
||||
return $this->contentType;
|
||||
}
|
||||
|
||||
protected function reqPOST(string ...$keys) : bool
|
||||
{
|
||||
foreach ($keys as $k)
|
||||
if (!isset($this->_post[$k]) || $this->_post[$k] === null || $this->_post[$k] === '')
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected function reqGET(string ...$keys) : bool
|
||||
{
|
||||
foreach ($keys as $k)
|
||||
if (!isset($this->_get[$k]) || $this->_get[$k] === null || $this->_get[$k] === '')
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected function checkEmptySet(string $val) : bool
|
||||
{
|
||||
return $val === ''; // parameter is expected to be empty
|
||||
|
||||
@@ -5,7 +5,7 @@ if (!defined('AOWOW_REVISION'))
|
||||
|
||||
class AjaxAdmin extends AjaxHandler
|
||||
{
|
||||
protected $validParams = ['screenshots', 'siteconfig', 'weight-presets'];
|
||||
protected $validParams = ['screenshots', 'siteconfig', 'weight-presets', 'spawn-override'];
|
||||
protected $_get = array(
|
||||
'action' => [FILTER_SANITIZE_STRING, FILTER_FLAG_STRIP_LOW | FILTER_FLAG_STRIP_HIGH ],
|
||||
'id' => [FILTER_CALLBACK, ['options' => 'AjaxHandler::checkIdListUnsigned']],
|
||||
@@ -14,11 +14,14 @@ class AjaxAdmin extends AjaxHandler
|
||||
'type' => [FILTER_CALLBACK, ['options' => 'AjaxHandler::checkInt'] ],
|
||||
'typeid' => [FILTER_CALLBACK, ['options' => 'AjaxHandler::checkInt'] ],
|
||||
'user' => [FILTER_CALLBACK, ['options' => 'AjaxAdmin::checkUser'] ],
|
||||
'val' => [FILTER_CALLBACK, ['options' => 'AjaxHandler::checkFulltext'] ]
|
||||
'val' => [FILTER_CALLBACK, ['options' => 'AjaxHandler::checkFulltext'] ],
|
||||
'guid' => [FILTER_CALLBACK, ['options' => 'AjaxHandler::checkInt'] ],
|
||||
'area' => [FILTER_CALLBACK, ['options' => 'AjaxHandler::checkInt'] ],
|
||||
'floor' => [FILTER_CALLBACK, ['options' => 'AjaxHandler::checkInt'] ]
|
||||
);
|
||||
protected $_post = array(
|
||||
'alt' => [FILTER_SANITIZE_STRING, FILTER_FLAG_STRIP_LOW ],
|
||||
'id' => [FILTER_SANITIZE_NUMBER_INT, null ],
|
||||
'id' => [FILTER_CALLBACK, ['options' => 'AjaxHandler::checkInt']],
|
||||
'scale' => [FILTER_CALLBACK, ['options' => 'AjaxAdmin::checkScale']],
|
||||
'__icon' => [FILTER_CALLBACK, ['options' => 'AjaxAdmin::checkKey'] ]
|
||||
);
|
||||
@@ -71,6 +74,13 @@ class AjaxAdmin extends AjaxHandler
|
||||
if ($this->_get['action'] == 'save')
|
||||
$this->handler = 'wtSave';
|
||||
}
|
||||
else if ($this->params[0] == 'spawn-override')
|
||||
{
|
||||
if (!User::isInGroup(U_GROUP_MODERATOR))
|
||||
return;
|
||||
|
||||
$this->handler = 'spawnPosFix';
|
||||
}
|
||||
}
|
||||
|
||||
// get all => null (optional)
|
||||
@@ -115,7 +125,7 @@ class AjaxAdmin extends AjaxHandler
|
||||
// resp: ''
|
||||
protected function ssApprove() : void
|
||||
{
|
||||
if (!$this->_get['id'])
|
||||
if (!$this->reqGET('id'))
|
||||
{
|
||||
trigger_error('AjaxAdmin::ssApprove - screenshotId empty', E_USER_ERROR);
|
||||
return;
|
||||
@@ -190,7 +200,7 @@ class AjaxAdmin extends AjaxHandler
|
||||
// resp: ''
|
||||
protected function ssSticky() : void
|
||||
{
|
||||
if (!$this->_get['id'])
|
||||
if (!$this->reqGET('id'))
|
||||
{
|
||||
trigger_error('AjaxAdmin::ssSticky - screenshotId empty', E_USER_ERROR);
|
||||
return;
|
||||
@@ -217,7 +227,7 @@ class AjaxAdmin extends AjaxHandler
|
||||
// 2 steps: 1) remove from sight, 2) remove from disk
|
||||
protected function ssDelete() : void
|
||||
{
|
||||
if (!$this->_get['id'])
|
||||
if (!$this->reqGET('id'))
|
||||
{
|
||||
trigger_error('AjaxAdmin::ssDelete - screenshotId empty', E_USER_ERROR);
|
||||
return;
|
||||
@@ -266,7 +276,7 @@ class AjaxAdmin extends AjaxHandler
|
||||
// resp: ''
|
||||
protected function ssRelocate() : void
|
||||
{
|
||||
if (!$this->_get['id'] || !$this->_get['typeid'])
|
||||
if (!$this->reqGET('id', 'typeid'))
|
||||
{
|
||||
trigger_error('AjaxAdmin::ssRelocate - screenshotId or typeId empty', E_USER_ERROR);
|
||||
return;
|
||||
@@ -317,7 +327,7 @@ class AjaxAdmin extends AjaxHandler
|
||||
|
||||
protected function confRemove() : string
|
||||
{
|
||||
if (!$this->_get['key'])
|
||||
if (!$this->reqGET('key'))
|
||||
return 'invalid configuration option given';
|
||||
|
||||
if (DB::Aowow()->query('DELETE FROM ?_config WHERE `key` = ? AND (`flags` & ?d) = 0', $this->_get['key'], CON_FLAG_PERSISTENT))
|
||||
@@ -357,7 +367,7 @@ class AjaxAdmin extends AjaxHandler
|
||||
|
||||
protected function wtSave() : string
|
||||
{
|
||||
if (!$this->_post['id'] || !$this->_post['__icon'])
|
||||
if (!$this->reqPOST('id', '__icon'))
|
||||
return '3';
|
||||
|
||||
// save to db
|
||||
@@ -385,6 +395,73 @@ class AjaxAdmin extends AjaxHandler
|
||||
return '0';
|
||||
}
|
||||
|
||||
protected function spawnPosFix() : string
|
||||
{
|
||||
if (!$this->reqGET('type', 'guid', 'area', 'floor'))
|
||||
return '-4';
|
||||
|
||||
$guid = $this->_get['guid'];
|
||||
$type = $this->_get['type'];
|
||||
$area = $this->_get['area'];
|
||||
$floor = $this->_get['floor'];
|
||||
|
||||
if (!in_array($type, [TYPE_NPC, TYPE_OBJECT, TYPE_SOUND, TYPE_AREATRIGGER]))
|
||||
return '-3';
|
||||
|
||||
DB::Aowow()->query('REPLACE INTO ?_spawns_override VALUES (?d, ?d, ?d, ?d, ?d)', $type, $guid, $area, $floor, AOWOW_REVISION);
|
||||
|
||||
if ($wPos = Game::getWorldPosForGUID($type, $guid))
|
||||
{
|
||||
if ($point = Game::worldPosToZonePos($wPos[$guid]['mapId'], $wPos[$guid]['posX'], $wPos[$guid]['posY'], $area, $floor))
|
||||
{
|
||||
$p = array(
|
||||
'posX' => $point[0]['posX'],
|
||||
'posY' => $point[0]['posY'],
|
||||
'areaId' => $point[0]['areaId'],
|
||||
'floor' => $point[0]['floor']
|
||||
);
|
||||
|
||||
DB::Aowow()->query('UPDATE ?_spawns SET ?a WHERE `type` = ?d AND `guid` = ?d', $p, $type, $guid);
|
||||
|
||||
// if creature try for waypoints
|
||||
if ($type != TYPE_NPC)
|
||||
return '1';
|
||||
|
||||
$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'
|
||||
);
|
||||
|
||||
foreach ($jobs as $idx => $job)
|
||||
{
|
||||
if ($swp = DB::World()->select($job, $idx ? $wPos['id'] : $guid))
|
||||
{
|
||||
foreach ($swp as $w)
|
||||
{
|
||||
if ($point = Game::worldPosToZonePos($wPos[$guid]['mapId'], $w['posX'], $w['posY'], $area, $floor))
|
||||
{
|
||||
$p = array(
|
||||
'posX' => $point[0]['posX'],
|
||||
'posY' => $point[0]['posY'],
|
||||
'areaId' => $point[0]['areaId'],
|
||||
'floor' => $point[0]['floor']
|
||||
);
|
||||
}
|
||||
DB::Aowow()->query('UPDATE ?_creature_waypoints SET ?a WHERE `creatureOrPath` = ?d AND `point` = ?d', $p, $w['entry'], $w['pointId']);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return '1';
|
||||
}
|
||||
|
||||
return '-2';
|
||||
}
|
||||
|
||||
return '-1';
|
||||
}
|
||||
|
||||
protected function checkKey(string $val) : string
|
||||
{
|
||||
// expecting string
|
||||
|
||||
@@ -560,10 +560,10 @@ trait spawnHelper
|
||||
$this->spawnResult[SPAWNINFO_SHORT] = new StdClass;
|
||||
|
||||
// first get zone/floor with the most spawns
|
||||
if ($res = DB::Aowow()->selectRow('SELECT areaId, floor FROM ?_spawns WHERE type = ?d && typeId = ?d GROUP BY areaId, floor ORDER BY count(1) DESC LIMIT 1', self::$type, $this->id))
|
||||
if ($res = DB::Aowow()->selectRow('SELECT areaId, floor FROM ?_spawns WHERE type = ?d AND typeId = ?d AND posX > 0 AND posY > 0 GROUP BY areaId, floor ORDER BY count(1) DESC LIMIT 1', self::$type, $this->id))
|
||||
{
|
||||
// get relevant spawn points
|
||||
$points = DB::Aowow()->select('SELECT posX, posY FROM ?_spawns WHERE type = ?d && typeId = ?d && areaId = ?d && floor = ?d', self::$type, $this->id, $res['areaId'], $res['floor']);
|
||||
$points = DB::Aowow()->select('SELECT posX, posY FROM ?_spawns WHERE type = ?d AND typeId = ?d AND areaId = ?d AND floor = ?d AND posX > 0 AND posY > 0', self::$type, $this->id, $res['areaId'], $res['floor']);
|
||||
$spawns = [];
|
||||
foreach ($points as $p)
|
||||
$spawns[] = [$p['posX'], $p['posY']];
|
||||
@@ -575,14 +575,18 @@ trait spawnHelper
|
||||
|
||||
private function createFullSpawns() // for display on map (objsct/npc detail page)
|
||||
{
|
||||
$data = [];
|
||||
$wpSum = [];
|
||||
$wpIdx = 0;
|
||||
$spawns = DB::Aowow()->select("SELECT * FROM ?_spawns WHERE type = ?d AND typeId = ?d", self::$type, $this->id);
|
||||
$data = [];
|
||||
$wpSum = [];
|
||||
$wpIdx = 0;
|
||||
$worldPos = [];
|
||||
$spawns = DB::Aowow()->select("SELECT * FROM ?_spawns WHERE type = ?d AND typeId = ?d AND posX > 0 AND posY > 0", self::$type, $this->id);
|
||||
|
||||
if (!$spawns)
|
||||
return;
|
||||
|
||||
if (User::isInGroup(U_GROUP_MODERATOR))
|
||||
$worldPos = Game::getWorldPosForGUID(self::$type, ...array_column($spawns, 'guid'));
|
||||
|
||||
foreach ($spawns as $s)
|
||||
{
|
||||
// check, if we can attach waypoints to creature
|
||||
@@ -648,7 +652,49 @@ trait spawnHelper
|
||||
$info[5] = 'Orientation'.Lang::main('colon').$o[0].'° ('.$o[1].')';
|
||||
}
|
||||
|
||||
// $footer = '<span class="q2">Click to move to different floor</span>';
|
||||
if (User::isInGroup(U_GROUP_MODERATOR))
|
||||
{
|
||||
if ($points = Game::worldPosToZonePos($worldPos[$s['guid']]['mapId'], $worldPos[$s['guid']]['posX'], $worldPos[$s['guid']]['posY']))
|
||||
{
|
||||
$floors = [];
|
||||
foreach ($points as $p)
|
||||
{
|
||||
if ($p['floor'])
|
||||
$floors[$p['areaId']][] = $p['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'].', -1)', 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 ($info)
|
||||
@@ -698,12 +744,12 @@ trait spawnHelper
|
||||
$this->spawnResult[SPAWNINFO_ZONES] = $res;
|
||||
}
|
||||
|
||||
private function createQuestSpawns() // [zoneId => [floor => [[x1, y1], [x2, y2], ..]]]
|
||||
private function createQuestSpawns() // [zoneId => [floor => [[x1, y1], [x2, y2], ..]]] mapper on quest detail page
|
||||
{
|
||||
if (self::$type == TYPE_SOUND)
|
||||
return;
|
||||
|
||||
$res = DB::Aowow()->select('SELECT areaId, floor, typeId, posX, posY FROM ?_spawns WHERE type = ?d && typeId IN (?a)', self::$type, $this->getFoundIDs());
|
||||
$res = DB::Aowow()->select('SELECT areaId, floor, typeId, posX, posY FROM ?_spawns WHERE type = ?d AND typeId IN (?a) AND posX > 0 AND posY > 0', self::$type, $this->getFoundIDs());
|
||||
$spawns = [];
|
||||
foreach ($res as $data)
|
||||
{
|
||||
|
||||
@@ -203,6 +203,62 @@ class Game
|
||||
return $pages;
|
||||
}
|
||||
|
||||
public static function getWorldPosForGUID(int $type, int ...$guids) : array
|
||||
{
|
||||
$result = [];
|
||||
|
||||
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);
|
||||
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);
|
||||
break;
|
||||
case TYPE_SOUND:
|
||||
$result = DB::AoWoW()->select('SELECT `soundId` AS ARRAY_KEY, `soundId` AS `id`, `mapId`, `posX`, `posY` FROM dbc_soundemitters WHERE `soundId` IN (?a)', $guids);
|
||||
break;
|
||||
case TYPE_AREATRIGGER:
|
||||
$result = DB::AoWoW()->select('SELECT `id` AS ARRAY_KEY, `id`, `mapId`, `posX`, `posY` FROM dbc_areatrigger WHERE `id` IN (?a)', $guids);
|
||||
break;
|
||||
default:
|
||||
trigger_error('Game::getWorldPosForGUID - instanced with unsupported TYPE '.$type, E_USER_WARNING);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
public static function worldPosToZonePos(int $mapId, float $posX, float $posY, int $areaId = 0, int $floor = -1) : array
|
||||
{
|
||||
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
|
||||
dbc_worldmaparea wma
|
||||
LEFT JOIN
|
||||
dbc_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';
|
||||
|
||||
// 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);
|
||||
|
||||
return $points;
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?php
|
||||
|
||||
define('AOWOW_REVISION', 30);
|
||||
define('AOWOW_REVISION', 31);
|
||||
define('CLI', PHP_SAPI === 'cli');
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user