mirror of
https://github.com/Sarjuuk/aowow.git
synced 2025-11-29 15:58:16 +08:00
* implement SpawnedByDefault from TC
* maybe fix a longstanding issue where multiple parent areas per instanced map lead to wrong spawn points
map should be can also be
36 The Deadmines The Great Sea, Unused Ironcladcove
109 The Temple of Atal'Hakkar Sunken Temple
540 The Shattered Halls Hellfire Citadel
560 Old Hillsbrad Foothills Hyjal Past
631 Icecrown Citadel The Frost Queen's Lair, Putricide's Laboratory of Alchemical Horrors and Fun, The Crimson Hall, The Frozen Throne, The Sanctum of Blood
574 lines
30 KiB
PHP
574 lines
30 KiB
PHP
<?php
|
|
|
|
if (!defined('AOWOW_REVISION'))
|
|
die('illegal access');
|
|
|
|
|
|
class Game
|
|
{
|
|
public static $resistanceFields = array(
|
|
null, 'resHoly', 'resFire', 'resNature', 'resFrost', 'resShadow', 'resArcane'
|
|
);
|
|
|
|
public static $rarityColorStings = array( // zero-indexed
|
|
'9d9d9d', 'ffffff', '1eff00', '0070dd', 'a335ee', 'ff8000', 'e5cc80', 'e6cc80'
|
|
);
|
|
|
|
public static $specIconStrings = array(
|
|
-1 => 'inv_misc_questionmark',
|
|
0 => 'spell_nature_elementalabsorption',
|
|
6 => ['spell_deathknight_bloodpresence', 'spell_deathknight_frostpresence', 'spell_deathknight_unholypresence' ],
|
|
11 => ['spell_nature_starfall', 'ability_racial_bearform', 'spell_nature_healingtouch' ],
|
|
3 => ['ability_hunter_beasttaming', 'ability_marksmanship', 'ability_hunter_swiftstrike' ],
|
|
8 => ['spell_holy_magicalsentry', 'spell_fire_firebolt02', 'spell_frost_frostbolt02' ],
|
|
2 => ['spell_holy_holybolt', 'spell_holy_devotionaura', 'spell_holy_auraoflight' ],
|
|
5 => ['spell_holy_wordfortitude', 'spell_holy_holybolt', 'spell_shadow_shadowwordpain' ],
|
|
4 => ['ability_rogue_eviscerate', 'ability_backstab', 'ability_stealth' ],
|
|
7 => ['spell_nature_lightning', 'spell_nature_lightningshield', 'spell_nature_magicimmunity' ],
|
|
9 => ['spell_shadow_deathcoil', 'spell_shadow_metamorphosis', 'spell_shadow_rainoffire' ],
|
|
1 => ['ability_rogue_eviscerate', 'ability_warrior_innerrage', 'ability_warrior_defensivestance' ]
|
|
);
|
|
|
|
public static $classFileStrings = array(
|
|
null, 'warrior', 'paladin', 'hunter', 'rogue', 'priest', 'deathknight', 'shaman', 'mage', 'warlock', null, 'druid'
|
|
);
|
|
|
|
private static $combatRatingToItemMod = array( // zero-indexed idx:CR; val:Mod
|
|
null, 12, 13, 14, 15, 16, 17, 18, 19,
|
|
20, 21, 22, 23, 24, 25, 26, 27, 28,
|
|
29, 30, null, null, null, 37, 44
|
|
);
|
|
|
|
public static $lvlIndepRating = array( // rating doesn't scale with level
|
|
ITEM_MOD_MANA, ITEM_MOD_HEALTH, ITEM_MOD_ATTACK_POWER, ITEM_MOD_MANA_REGENERATION, ITEM_MOD_SPELL_POWER,
|
|
ITEM_MOD_HEALTH_REGEN, ITEM_MOD_SPELL_PENETRATION, ITEM_MOD_BLOCK_VALUE
|
|
);
|
|
|
|
public static $questClasses = array(
|
|
-2 => [ 0],
|
|
0 => [ 1, 3, 4, 8, 9, 10, 11, 12, 25, 28, 33, 36, 38, 40, 41, 44, 45, 46, 47, 51, 85, 130, 132, 139, 154, 267, 1497, 1519, 1537, 2257, 3430, 3431, 3433, 3487, 4080, 4298],
|
|
1 => [ 14, 15, 16, 17, 141, 148, 188, 215, 220, 331, 357, 361, 363, 400, 405, 406, 440, 490, 493, 618, 1377, 1637, 1638, 1657, 1769, 3524, 3525, 3526, 3557],
|
|
2 => [ 206, 209, 491, 717, 718, 719, 721, 722, 796, 1176, 1196, 1337, 1477, 1581, 1583, 1584, 1941, 2017, 2057, 2100, 2366, 2367, 2437, 2557, 3535, 3562, 3688, 3713, 3714, 3715, 3716, 3717, 3789, 3790, 3791, 3792, 3842, 3847, 3848, 3849, 3905, 4100, 4131, 4196, 4228, 4264, 4265, 4272, 4277, 4415, 4416, 4494, 4522, 4723, 4809, 4813, 4820],
|
|
3 => [ 1977, 2159, 2677, 2717, 3428, 3429, 3456, 3457, 3606, 3607, 3805, 3836, 3845, 3923, 3959, 4075, 4273, 4493, 4500, 4603, 4722, 4812, 4987],
|
|
4 => [ -372, -263, -262, -261, -162, -161, -141, -82, -81, -61],
|
|
5 => [ -373, -371, -324, -304, -264, -201, -182, -181, -121, -101, -24],
|
|
6 => [ -25, 2597, 3277, 3358, 3820, 4384, 4710],
|
|
7 => [-1010, -368, -367, -365, -344, -241, -1],
|
|
8 => [ 3483, 3518, 3519, 3520, 3521, 3522, 3523, 3679, 3703],
|
|
9 => [-1005, -1003, -1002, -1001, -376, -375, -374, -370, -369, -366, -364, -41, -22], // 22: seasonal
|
|
10 => [ 65, 66, 67, 210, 394, 495, 2817, 3537, 3711, 4024, 4197, 4395, 4742]
|
|
);
|
|
|
|
// zoneorsort for quests need updating
|
|
// partially points non-instanced area with identical name for instance quests
|
|
public static $questSortFix = array(
|
|
-221 => 440, // Treasure Map => Tanaris
|
|
-284 => 0, // Special => Misc (some quests get shuffled into seasonal)
|
|
151 => 0, // Designer Island => Misc
|
|
22 => 0, // Programmer Isle
|
|
35 => 33, // Booty Bay => Stranglethorn Vale
|
|
131 => 132, // Kharanos => Coldridge Valley
|
|
24 => 9, // Northshire Abbey => Northshire Valley
|
|
279 => 36, // Dalaran Crater => Alterac Mountains
|
|
4342 => 4298, // Acherus: The Ebon Hold => The Scarlet Enclave
|
|
2079 => 15, // Alcaz Island => Dustwallow Marsh
|
|
1939 => 440, // Abyssal Sands => Tanaris
|
|
393 => 363, // Darkspeer Strand => Valley of Trials
|
|
702 => 141, // Rut'theran Village => Teldrassil
|
|
221 => 220, // Camp Narache => Red Cloud Mesa
|
|
1116 => 357, // Feathermoon Stronghold => Feralas
|
|
236 => 209, // Shadowfang Keep
|
|
4769 => 4742, // Hrothgar's Landing => Hrothgar's Landing
|
|
4613 => 4395, // Dalaran City => Dalaran
|
|
4522 => 210, // Icecrown Citadell => Icecrown
|
|
3896 => 3703, // Aldor Rise => Shattrath City
|
|
3696 => 3522, // The Barrier Hills => Blade's Edge Mountains
|
|
2839 => 2597, // Alterac Valley
|
|
19 => 1977, // Zul'Gurub
|
|
4445 => 4273, // Ulduar
|
|
2300 => 1941, // Caverns of Time
|
|
3545 => 3535, // Hellfire Citadel
|
|
2562 => 3457, // Karazhan
|
|
3840 => 3959, // Black Temple
|
|
1717 => 491, // Razorfen Kraul
|
|
978 => 1176, // Zul'Farrak
|
|
133 => 721, // Gnomeregan
|
|
3607 => 3905, // Serpentshrine Cavern
|
|
3845 => 3842, // Tempest Keep
|
|
1517 => 1337, // Uldaman
|
|
1417 => 1477 // Sunken Temple
|
|
);
|
|
|
|
public static $questSubCats = array(
|
|
1 => [132], // Dun Morogh: Coldridge Valley
|
|
12 => [9], // Elwynn Forest: Northshire Valley
|
|
141 => [188], // Teldrassil: Shadowglen
|
|
3524 => [3526], // Azuremyst Isle: Ammen Vale
|
|
|
|
14 => [363], // Durotar: Valley of Trials
|
|
85 => [154], // Tirisfal Glades: Deathknell
|
|
215 => [220], // Mulgore: Red Cloud Mesa
|
|
3430 => [3431], // Eversong Woods: Sunstrider Isle
|
|
|
|
46 => [25], // Burning Steppes: Blackrock Mountain
|
|
361 => [1769], // Felwood: Timbermaw Hold
|
|
3519 => [3679], // Terokkar: Skettis
|
|
3535 => [3562, 3713, 3714], // Hellfire Citadel
|
|
3905 => [3715, 3716, 3717], // Coilfang Reservoir
|
|
3688 => [3789, 3790, 3792], // Auchindoun
|
|
1941 => [2366, 2367, 4100], // Caverns of Time
|
|
3842 => [3847, 3848, 3849], // Tempest Keep
|
|
4522 => [4809, 4813, 4820] // Icecrown Citadel
|
|
);
|
|
|
|
/* why:
|
|
Because petSkills (and ranged weapon skills) are the only ones with more than two skillLines attached. Because Left Joining ?_spell with ?_skillLineability causes more trouble than it has uses.
|
|
Because this is more or less the only reaonable way to fit all that information into one database field, so..
|
|
.. the indizes of this array are bits of skillLine2OrMask in ?_spell if skillLineId1 is negative
|
|
*/
|
|
public static $skillLineMask = array( // idx => [familyId, skillLineId]
|
|
-1 => array( // Pets (Hunter)
|
|
[ 1, 208], [ 2, 209], [ 3, 203], [ 4, 210], [ 5, 211], [ 6, 212], [ 7, 213], // Wolf, Cat, Spider, Bear, Boar, Crocolisk, Carrion Bird
|
|
[ 8, 214], [ 9, 215], [11, 217], [12, 218], [20, 236], [21, 251], [24, 653], // Crab, Gorilla, Raptor, Tallstrider, Scorpid, Turtle, Bat
|
|
[25, 654], [26, 655], [27, 656], [30, 763], [31, 767], [32, 766], [33, 765], // Hyena, Bird of Prey, Wind Serpent, Dragonhawk, Ravager, Warp Stalker, Sporebat
|
|
[34, 764], [35, 768], [37, 775], [38, 780], [39, 781], [41, 783], [42, 784], // Nether Ray, Serpent, Moth, Chimaera, Devilsaur, Silithid, Worm
|
|
[43, 786], [44, 785], [45, 787], [46, 788] // Rhino, Wasp, Core Hound, Spirit Beast
|
|
),
|
|
-2 => array( // Pets (Warlock)
|
|
[15, 189], [16, 204], [17, 205], [19, 207], [23, 188], [29, 761] // Felhunter, Voidwalker, Succubus, Doomguard, Imp, Felguard
|
|
),
|
|
-3 => array( // Ranged Weapons
|
|
[null, 45], [null, 46], [null, 226] // Bow, Gun, Crossbow
|
|
)
|
|
);
|
|
|
|
public static $sockets = array( // jsStyle Strings
|
|
'meta', 'red', 'yellow', 'blue'
|
|
);
|
|
|
|
// 'replicates' $WH.g_statToJson
|
|
public static $itemMods = array( // zero-indexed; "mastrtng": unused mastery; _[a-z] => taken mods..
|
|
'dmg', 'mana', 'health', 'agi', 'str', 'int', 'spi',
|
|
'sta', 'energy', 'rage', 'focus', 'runicpwr', 'defrtng', 'dodgertng',
|
|
'parryrtng', 'blockrtng', 'mlehitrtng', 'rgdhitrtng', 'splhitrtng', 'mlecritstrkrtng', 'rgdcritstrkrtng',
|
|
'splcritstrkrtng', '_mlehitrtng', '_rgdhitrtng', '_splhitrtng', '_mlecritstrkrtng', '_rgdcritstrkrtng', '_splcritstrkrtng',
|
|
'mlehastertng', 'rgdhastertng', 'splhastertng', 'hitrtng', 'critstrkrtng', '_hitrtng', '_critstrkrtng',
|
|
'resirtng', 'hastertng', 'exprtng', 'atkpwr', 'rgdatkpwr', 'feratkpwr', 'splheal',
|
|
'spldmg', 'manargn', 'armorpenrtng', 'splpwr', 'healthrgn', 'splpen', 'block', // ITEM_MOD_BLOCK_VALUE
|
|
'mastrtng', 'armor', 'firres', 'frores', 'holres', 'shares', 'natres',
|
|
'arcres', 'firsplpwr', 'frosplpwr', 'holsplpwr', 'shasplpwr', 'natsplpwr', 'arcsplpwr'
|
|
);
|
|
|
|
public static $class2SpellFamily = array(
|
|
// null Warrior Paladin Hunter Rogue Priest DK Shaman Mage Warlock null Druid
|
|
null, 4, 10, 9, 8, 6, 15, 11, 3, 5, null, 7
|
|
);
|
|
|
|
public static $areaFloors = array(
|
|
206 => 3, 209 => 7, 719 => 3, 721 => 4, 796 => 4, 1196 => 2, 1337 => 2, 1581 => 2, 1583 => 7, 1584 => 2,
|
|
2017 => 2, 2057 => 4, 2100 => 2, 2557 => 6, 2677 => 4, 3428 => 3, 3457 => 17, 3790 => 2, 3791 => 2, 3959 => 8,
|
|
3456 => 6, 3715 => 2, 3848 => 3, 3849 => 2, 4075 => 2, 4100 => 2, 4131 => 2, 4196 => 2, 4228 => 4, 4272 => 2,
|
|
4273 => 6, 4277 => 3, 4395 => 2, 4494 => 2, 4722 => 2, 4812 => 8
|
|
);
|
|
|
|
public static function itemModByRatingMask($mask)
|
|
{
|
|
if (($mask & 0x1C000) == 0x1C000) // special case resilience
|
|
return ITEM_MOD_RESILIENCE_RATING;
|
|
|
|
if (($mask & 0x00E0) == 0x00E0) // hit rating - all subcats (mle, rgd, spl)
|
|
return ITEM_MOD_HIT_RATING;
|
|
|
|
if (($mask & 0x0700) == 0x0700) // crit rating - all subcats (mle, rgd, spl)
|
|
return ITEM_MOD_CRIT_RATING;
|
|
|
|
for ($j = 0; $j < count(self::$combatRatingToItemMod); $j++)
|
|
{
|
|
if (!self::$combatRatingToItemMod[$j])
|
|
continue;
|
|
|
|
if (!($mask & (1 << $j)))
|
|
continue;
|
|
|
|
return self::$combatRatingToItemMod[$j];
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
public static function sideByRaceMask($race)
|
|
{
|
|
// Any
|
|
if (!$race || ($race & RACE_MASK_ALL) == RACE_MASK_ALL)
|
|
return SIDE_BOTH;
|
|
|
|
// Horde
|
|
if ($race & RACE_MASK_HORDE && !($race & RACE_MASK_ALLIANCE))
|
|
return SIDE_HORDE;
|
|
|
|
// Alliance
|
|
if ($race & RACE_MASK_ALLIANCE && !($race & RACE_MASK_HORDE))
|
|
return SIDE_ALLIANCE;
|
|
|
|
return SIDE_BOTH;
|
|
}
|
|
|
|
public static function getReputationLevelForPoints($pts)
|
|
{
|
|
if ($pts >= 41999)
|
|
return REP_EXALTED;
|
|
else if ($pts >= 20999)
|
|
return REP_REVERED;
|
|
else if ($pts >= 8999)
|
|
return REP_HONORED;
|
|
else if ($pts >= 2999)
|
|
return REP_FRIENDLY;
|
|
else if ($pts >= 0)
|
|
return REP_NEUTRAL;
|
|
else if ($pts >= -3000)
|
|
return REP_UNFRIENDLY;
|
|
else if ($pts >= -6000)
|
|
return REP_HOSTILE;
|
|
else
|
|
return REP_HATED;
|
|
}
|
|
|
|
public static function getTaughtSpells(&$spell)
|
|
{
|
|
$extraIds = [-1]; // init with -1 to prevent empty-array errors
|
|
$lookup = [-1];
|
|
switch (gettype($spell))
|
|
{
|
|
case 'object':
|
|
if (get_class($spell) != 'SpellList')
|
|
return [];
|
|
|
|
$lookup[] = $spell->id;
|
|
foreach ($spell->canTeachSpell() as $idx)
|
|
$extraIds[] = $spell->getField('effect'.$idx.'TriggerSpell');
|
|
|
|
break;
|
|
case 'integer':
|
|
$lookup[] = $spell;
|
|
break;
|
|
case 'array':
|
|
$lookup = $spell;
|
|
break;
|
|
default:
|
|
return [];
|
|
}
|
|
|
|
// note: omits required spell and chance in skill_discovery_template
|
|
$data = array_merge(
|
|
DB::World()->selectCol('SELECT spellId FROM spell_learn_spell WHERE entry IN (?a)', $lookup),
|
|
DB::World()->selectCol('SELECT spellId FROM skill_discovery_template WHERE reqSpell IN (?a)', $lookup),
|
|
$extraIds
|
|
);
|
|
|
|
// return list of integers, not strings
|
|
$data = array_map('intVal', $data);
|
|
|
|
return $data;
|
|
}
|
|
|
|
public static function getPageText($ptId)
|
|
{
|
|
$pages = [];
|
|
while ($ptId)
|
|
{
|
|
if ($row = DB::World()->selectRow('SELECT ptl.Text AS Text_loc?d, pt.* FROM page_text pt LEFT JOIN page_text_locale ptl ON pt.ID = ptl.ID AND locale = ? WHERE pt.ID = ?d', User::$localeId, User::$localeString, $ptId))
|
|
{
|
|
$ptId = $row['NextPageID'];
|
|
$pages[] = Util::parseHtmlText(Util::localizedString($row, 'Text'));
|
|
}
|
|
else
|
|
{
|
|
trigger_error('Referenced PageTextId #'.$ptId.' is not in DB', E_USER_WARNING);
|
|
break;
|
|
}
|
|
}
|
|
|
|
return $pages;
|
|
}
|
|
|
|
|
|
/*********************/
|
|
/* World Pos. Checks */
|
|
/*********************/
|
|
|
|
private static $alphaMapCache = [];
|
|
|
|
private static function alphaMapCheck(int $areaId, array &$set) : bool
|
|
{
|
|
$file = 'setup/generated/alphaMaps/'.$areaId.'.png';
|
|
if (!file_exists($file)) // file does not exist (probably instanced area)
|
|
return false;
|
|
|
|
// invalid and corner cases (literally)
|
|
if (!is_array($set) || empty($set['posX']) || empty($set['posY']) || $set['posX'] >= 100 || $set['posY'] >= 100)
|
|
{
|
|
$set = null;
|
|
return true;
|
|
}
|
|
|
|
if (empty(self::$alphaMapCache[$areaId]))
|
|
self::$alphaMapCache[$areaId] = imagecreatefrompng($file);
|
|
|
|
// alphaMaps are 1000 x 1000, adapt points [black => valid point]
|
|
if (!imagecolorat(self::$alphaMapCache[$areaId], $set['posX'] * 10, $set['posY'] * 10))
|
|
$set = null;
|
|
|
|
return true;
|
|
}
|
|
|
|
public static function checkCoords(array $points) : array
|
|
{
|
|
$result = [];
|
|
$capitals = array( // capitals take precedence over their surroundings
|
|
1497, 1637, 1638, 3487, // Undercity, Ogrimmar, Thunder Bluff, Silvermoon City
|
|
1519, 1537, 1657, 3557, // Stormwind City, Ironforge, Darnassus, The Exodar
|
|
3703, 4395 // Shattrath City, Dalaran
|
|
);
|
|
|
|
foreach ($points as $res)
|
|
{
|
|
if (self::alphaMapCheck($res['areaId'], $res))
|
|
{
|
|
if (!$res)
|
|
continue;
|
|
|
|
// some rough measure how central the spawn is on the map (the lower the number, the better)
|
|
// 0: perfect center; 1: touches a border
|
|
$q = abs( (($res['posX'] - 50) / 50) * (($res['posY'] - 50) / 50) );
|
|
|
|
if (empty($result) || $result[0] > $q)
|
|
$result = [$q, $res];
|
|
}
|
|
else if (in_array($res['areaId'], $capitals)) // capitals (auto-discovered) and no hand-made alphaMap available
|
|
return $res;
|
|
else if (empty($result)) // add with lowest quality if alpha map is missing
|
|
$result = [1.0, $res];
|
|
}
|
|
|
|
// spawn does not really match on a map, but we need at least one result
|
|
if (!$result)
|
|
{
|
|
usort($points, function ($a, $b) { return ($a['dist'] < $b['dist']) ? -1 : 1; });
|
|
$result = [1.0, $points[0]];
|
|
}
|
|
|
|
return $result[1];
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
if ($diff = array_diff($guids, array_keys($result)))
|
|
trigger_error('Game::getWorldPosForGUID - no spawn points for TYPE #'.$type.' GUIDS: '.implode(', ', $diff), 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);
|
|
|
|
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;
|
|
}
|
|
|
|
public static function getQuotesForCreature(int $creatureId, bool $asHTML = false, string $talkSource = '') : array
|
|
{
|
|
$nQuotes = 0;
|
|
$quotes = [];
|
|
$soundIds = [];
|
|
|
|
$quoteSrc = DB::World()->select('
|
|
SELECT
|
|
ct.GroupID AS ARRAY_KEY, ct.ID as ARRAY_KEY2,
|
|
ct.`Type` AS `talkType`,
|
|
ct.TextRange AS `range`,
|
|
IFNULL(bct.`LanguageID`, ct.`Language`) AS lang,
|
|
IFNULL(NULLIF(bct.Text, ""), IFNULL(NULLIF(bct.Text1, ""), IFNULL(ct.`Text`, ""))) AS text_loc0,
|
|
{IFNULL(NULLIF(bctl.Text, ""), IFNULL(NULLIF(bctl.Text1, ""), IFNULL(ctl.Text, ""))) AS text_loc?d,}
|
|
IF(bct.SoundEntriesID > 0, bct.SoundEntriesID, ct.Sound) AS soundId
|
|
FROM
|
|
creature_text ct
|
|
{LEFT JOIN
|
|
creature_text_locale ctl ON ct.CreatureID = ctl.CreatureID AND ct.GroupID = ctl.GroupID AND ct.ID = ctl.ID AND ctl.Locale = ?}
|
|
LEFT JOIN
|
|
broadcast_text bct ON ct.BroadcastTextId = bct.ID
|
|
{LEFT JOIN
|
|
broadcast_text_locale bctl ON ct.BroadcastTextId = bctl.ID AND bctl.locale = ?}
|
|
WHERE
|
|
ct.CreatureID = ?d',
|
|
User::$localeId ?: DBSIMPLE_SKIP,
|
|
User::$localeId ? Util::$localeStrings[User::$localeId] : DBSIMPLE_SKIP,
|
|
User::$localeId ? Util::$localeStrings[User::$localeId] : DBSIMPLE_SKIP,
|
|
$creatureId
|
|
);
|
|
|
|
foreach ($quoteSrc as $grp => $text)
|
|
{
|
|
$group = [];
|
|
foreach ($text as $t)
|
|
{
|
|
if ($t['soundId'])
|
|
$soundIds[] = $t['soundId'];
|
|
|
|
$msg = Util::localizedString($t, 'text');
|
|
if (!$msg)
|
|
continue;
|
|
|
|
// fixup .. either set %s for emotes or dont >.<
|
|
if (in_array($t['talkType'], [2, 16]) && strpos($msg, '%s') === false)
|
|
$msg = '%s '.$msg;
|
|
|
|
// fixup: bad case-insensivity
|
|
$msg = Util::parseHtmlText(str_replace('%S', '%s', htmlentities($msg)), !$asHTML);
|
|
|
|
if ($talkSource)
|
|
$msg = sprintf($msg, $talkSource);
|
|
|
|
// make type css compatible
|
|
switch ($t['talkType'])
|
|
{
|
|
case 1: // yell:
|
|
case 14: $t['talkType'] = 1; break; // - dark red
|
|
case 2: // emote:
|
|
case 16: // "
|
|
case 3: // boss emote:
|
|
case 41: $t['talkType'] = 4; break; // - orange
|
|
case 4: // whisper:
|
|
case 15: // "
|
|
case 5: // boss whisper:
|
|
case 42: $t['talkType'] = 3; break; // - pink-ish
|
|
default: $t['talkType'] = 2; // [type: 0, 12] say: yellow-ish
|
|
|
|
}
|
|
|
|
// prefix
|
|
$pre = '';
|
|
if ($t['talkType'] != 4)
|
|
$pre = ($talkSource ?: '%s').' '.Lang::npc('textTypes', $t['talkType']).Lang::main('colon').($t['lang'] ? '['.Lang::game('languages', $t['lang']).'] ' : null);
|
|
|
|
if ($asHTML)
|
|
$msg = '<div><span class="s'.$t['talkType'].'">%s'.($t['range'] ? sprintf(Util::$dfnString, Lang::npc('textRanges', $t['range']), $msg) : $msg).'</span></div>';
|
|
else
|
|
$msg = '[div][span class=s'.$t['talkType'].']%s'.html_entity_decode($msg).'[/span][/div]';
|
|
|
|
$line = array(
|
|
'range' => $t['range'],
|
|
'text' => $msg,
|
|
'prefix' => $pre
|
|
);
|
|
|
|
|
|
$nQuotes++;
|
|
$group[] = $line;
|
|
}
|
|
|
|
if ($group)
|
|
$quotes[$grp] = $group;
|
|
}
|
|
|
|
return [$quotes, $nQuotes, $soundIds];
|
|
}
|
|
|
|
public static function getBreakpointsForSkill(int $skillId, int $reqLevel) : array
|
|
{
|
|
if ($skillId == SKILL_FISHING)
|
|
return array(
|
|
round(sqrt(.25) * $reqLevel), // 25% valid catches
|
|
round(sqrt(.50) * $reqLevel), // 50% valid catches
|
|
round(sqrt(.75) * $reqLevel), // 75% valid catches
|
|
$reqLevel // 100% valid catches
|
|
);
|
|
|
|
switch ($skillId)
|
|
{
|
|
case SKILL_SKINNING:
|
|
if ($reqLevel < 10)
|
|
$reqLevel = 0;
|
|
else if ($reqLevel < 20)
|
|
$reqLevel = ($reqLevel - 10) * 10;
|
|
else
|
|
$reqLevel *= 5;
|
|
case SKILL_HERBALISM:
|
|
case SKILL_LOCKPICKING:
|
|
case SKILL_JEWELCRAFTING:
|
|
case SKILL_INSCRIPTION:
|
|
case SKILL_MINING:
|
|
$points = [$reqLevel]; // red/orange
|
|
|
|
if ($reqLevel + 25 <= MAX_SKILL) // orange/yellow
|
|
$points[] = $reqLevel + 25;
|
|
|
|
if ($reqLevel + 50 <= MAX_SKILL) // yellow/green
|
|
$points[] = $reqLevel + 50;
|
|
|
|
if ($reqLevel + 100 <= MAX_SKILL) // green/grey
|
|
$points[] = $reqLevel + 100;
|
|
|
|
return $points;
|
|
default:
|
|
return [$reqLevel];
|
|
}
|
|
}
|
|
}
|
|
|
|
?>
|