From 61f59acf8127536d02fde307baa45830769c2d13 Mon Sep 17 00:00:00 2001 From: Unholychick Date: Tue, 10 Jun 2014 17:24:54 +0200 Subject: [PATCH 01/80] Scripts/Ulduar: Implement Mimiron TODO: Make ACU hover properly (not fly, but hover). Fix laser barrage visual not always being infront of VX-001. Problem is caused by bad orientation update on spellcast. --- .../world/2014_06_12_02_world_misc.sql | 197 ++ src/server/game/Spells/SpellMgr.cpp | 10 + .../Northrend/Ulduar/Ulduar/boss_mimiron.cpp | 2935 ++++++++++++++++- .../Ulduar/Ulduar/instance_ulduar.cpp | 79 +- .../scripts/Northrend/Ulduar/Ulduar/ulduar.h | 34 + 5 files changed, 3209 insertions(+), 46 deletions(-) create mode 100644 sql/updates/world/2014_06_12_02_world_misc.sql diff --git a/sql/updates/world/2014_06_12_02_world_misc.sql b/sql/updates/world/2014_06_12_02_world_misc.sql new file mode 100644 index 0000000000..da40f46d69 --- /dev/null +++ b/sql/updates/world/2014_06_12_02_world_misc.sql @@ -0,0 +1,197 @@ +-- +DELETE FROM `vehicle_template_accessory` WHERE `entry` IN (33651, 33432); +INSERT INTO `vehicle_template_accessory` (`entry`, `accessory_entry`, `seat_id`, `minion`, `description`, `summontype`) VALUES +(33651, 34050, 5, 1, 'VX-001 Rocket Seat 6', 8), +(33651, 34050, 6, 1, 'VX-001 Rocket Seat 7', 8), +(33432, 34071, 3, 1, 'MKII MKII turret Seat 1', 8); + +DELETE FROM `npc_spellclick_spells` WHERE `npc_entry` IN (33651, 33432); +INSERT INTO `npc_spellclick_spells` (`npc_entry`, `spell_id`, `cast_flags`, `user_type`) VALUES +(33651, 46598, 1, 1), +(33432, 46598, 1, 1); + +DELETE FROM `spell_script_names` WHERE `spell_id` IN (63274, 63414, 63667, 63382, 64402, 65034, 63681, 64542, 65192, 64570, 63027, 65346, 66351, 65224, 63009, 64620, 63820, 64425, 64426, 64621, 64398, 64567, 64619, 64623, 64627, 65354, 64626, 64618, 65333, 64436, 63340, 64383, 63339, 64562, 63041, 63801, 64463); +INSERT INTO `spell_script_names` (`spell_id`, `ScriptName`) VALUES +(63801, 'spell_mimiron_bomb_bot'), +(65192, 'spell_mimiron_clear_fires'), +(65224, 'spell_mimiron_clear_fires'), +(64619, 'spell_mimiron_clear_fires'), +(65354, 'spell_mimiron_clear_fires'), +(64463, 'spell_mimiron_despawn_assault_bots'), +(64618, 'spell_mimiron_fire_search'), +(64570, 'spell_mimiron_flame_suppressant'), +(64626, 'spell_mimiron_frost_bomb_explosion'), +(65333, 'spell_mimiron_frost_bomb_explosion'), +(64436, 'spell_mimiron_magnetic_core'), +(63667, 'spell_mimiron_napalm_shell'), +(63274, 'spell_mimiron_p3wx2_laser_barrage'), +(64542, 'spell_mimiron_plasma_blast'), +(63027, 'spell_mimiron_proximity_mines'), +(66351, 'spell_mimiron_proximity_explosion'), +(63009, 'spell_mimiron_proximity_explosion'), +(65346, 'spell_mimiron_proximity_trigger'), +(63382, 'spell_mimiron_rapid_burst'), +(64402, 'spell_mimiron_rocket_strike'), +(65034, 'spell_mimiron_rocket_strike'), +(63041, 'spell_mimiron_rocket_strike_damage'), +(63681, 'spell_mimiron_rocket_strike_target_select'), +(64383, 'spell_mimiron_self_repair'), +(63414, 'spell_mimiron_spinning_up'), +(64426, 'spell_mimiron_summon_assault_bot'), +(64425, 'spell_mimiron_summon_assault_bot_target'), +(64621, 'spell_mimiron_summon_fire_bot'), +(64620, 'spell_mimiron_summon_fire_bot_target'), +(64567, 'spell_mimiron_summon_flames_initial'), +(64562, 'spell_mimiron_summon_flames_spread'), +(64627, 'spell_mimiron_summon_frost_bomb'), +(64623, 'spell_mimiron_summon_frost_bomb_target'), +(64398, 'spell_mimiron_summon_junk_bot'), +(63820, 'spell_mimiron_summon_junk_bot_target'), +(63339, 'spell_mimiron_weld'); + +DELETE FROM `spelldifficulty_dbc` WHERE `id` IN (66351, 64019, 63387, 62997, 64348, 64352, 63689, 65647, 63677, 63679, 64626); +INSERT INTO `spelldifficulty_dbc` (`id`, `spellid0`, `spellid1`) VALUES +(64348, 64348, 64536), +(64352, 64352, 64537), +(66351, 66351, 63009), +(63387, 63387, 64531), +(64019, 64019, 64532), +(62997, 62997, 64529), +(63689, 63689, 64535), +(65647, 65647, 65648), +(63677, 63677, 64533), +(63679, 63679, 64534), +(64626, 64626, 65333); + +DELETE FROM `conditions` WHERE `SourceEntry` IN (65192, 64570, 63274, 66490, 63300, 63414, 64539, 64402, 65034, 63041, 65224, 64620, 63820, 64425, 64619, 64626, 65333, 65354, 64618, 64623, 64436, 64444, 65101, 64463); +INSERT INTO `conditions` (`SourceTypeOrReferenceId`, `SourceGroup`, `SourceEntry`, `SourceId`, `ElseGroup`, `ConditionTypeOrReference`, `ConditionTarget`, `ConditionValue1`, `ConditionValue2`, `ConditionValue3`, `NegativeCondition`, `ErrorType`, `ErrorTextId`, `ScriptName`, `Comment`) VALUES +(13, 1, 65192, 0, 0, 31, 0, 3, 34363, 0, 0, 0, 0, '', 'Flame Suppressant VX EFFECT_0 can only hit NPC_FLAME'), +(13, 1, 65192, 0, 1, 31, 0, 3, 34121, 0, 0, 0, 0, '', 'Flame Suppressant VX EFFECT_0 can only hit NPC_FLAME_SPREAD'), +(13, 2, 64570, 0, 0, 31, 0, 3, 34363, 0, 0, 0, 0, '', 'Flame Suppressant MK EFFECT_1 can only hit NPC_FLAME'), +(13, 2, 64570, 0, 1, 31, 0, 3, 34121, 0, 0, 0, 0, '', 'Flame Suppressant MK EFFECT_1 can only hit NPC_FLAME_SPREAD'), +(13, 1, 64623, 0, 0, 31, 0, 3, 34363, 0, 0, 0, 0, '', 'Frost Bomb EFFECT_0 can only hit NPC_FLAME'), +(13, 1, 64623, 0, 1, 31, 0, 3, 34121, 0, 0, 0, 0, '', 'Frost Bomb EFFECT_0 can only hit NPC_FLAME_SPREAD'), +(13, 4, 64626, 0, 0, 31, 0, 3, 34363, 0, 0, 0, 0, '', 'Explosion EFFECT_2 can only hit NPC_FLAME'), +(13, 4, 64626, 0, 1, 31, 0, 3, 34121, 0, 0, 0, 0, '', 'Explosion EFFECT_2 can only hit NPC_FLAME_SPREAD'), +(13, 4, 65333, 0, 0, 31, 0, 3, 34363, 0, 0, 0, 0, '', 'Explosion EFFECT_2 can only hit NPC_FLAME'), +(13, 4, 65333, 0, 1, 31, 0, 3, 34121, 0, 0, 0, 0, '', 'Explosion EFFECT_2 can only hit NPC_FLAME_SPREAD'), +(13, 1, 65224, 0, 0, 31, 0, 3, 34363, 0, 0, 0, 0, '', 'Clear Fires EFFECT_0 can only hit NPC_FLAME'), +(13, 1, 65224, 0, 1, 31, 0, 3, 34121, 0, 0, 0, 0, '', 'Clear Fires EFFECT_0 can only hit NPC_FLAME_SPREAD'), +(13, 1, 65354, 0, 0, 31, 0, 3, 34363, 0, 0, 0, 0, '', 'Clear Fires EFFECT_0 can only hit NPC_FLAME'), +(13, 1, 65354, 0, 1, 31, 0, 3, 34121, 0, 0, 0, 0, '', 'Clear Fires EFFECT_0 can only hit NPC_FLAME_SPREAD'), +(13, 1, 64619, 0, 0, 31, 0, 3, 34363, 0, 0, 0, 0, '', 'Water Spray EFFECT_0 can only hit NPC_FLAME'), +(13, 1, 64619, 0, 1, 31, 0, 3, 34121, 0, 0, 0, 0, '', 'Water Spray EFFECT_0 can only hit NPC_FLAME_SPREAD'), +(13, 1, 64618, 0, 0, 31, 0, 3, 34363, 0, 0, 0, 0, '', 'Fire Search EFFECT_0 can only hit NPC_FLAME'), +(13, 1, 64618, 0, 1, 31, 0, 3, 34121, 0, 0, 0, 0, '', 'Fire Search EFFECT_0 can only hit NPC_FLAME_SPREAD'), +(13, 1, 64539, 0, 0, 31, 0, 3, 34071, 0, 0, 0, 0, '', 'Napalm Shell EFFECT_0 can only hit NPC_LEVIATHAN_MKII_TURRET'), +(13, 1, 63274, 0, 0, 31, 0, 3, 33576, 0, 0, 0, 0, '', 'P3Wx2 Laser Barrage EFFECT_0 can only hit NPC_DB_TARGET'), +(17, 0, 66490, 0, 0, 31, 0, 3, 33432, 0, 0, 0, 0, '', 'P3Wx2 Laser Barrage can only hit NPC_LEVIATHAN_MKII'), +(17, 0, 63300, 0, 0, 31, 0, 3, 33651, 0, 0, 0, 0, '', 'P3Wx2 Laser Barrage can only hit NPC_VX_001'), +(13, 1, 63414, 0, 0, 31, 0, 3, 33576, 0, 0, 0, 0, '', 'Spinning Up EFFECT_0 can only hit NPC_DB_TARGET'), +(13, 2, 63414, 0, 1, 31, 0, 3, 33432, 0, 0, 0, 0, '', 'Spinning Up EFFECT_1 can only hit NPC_LEVIATHAN_MKII'), +(13, 1, 64620, 0, 0, 31, 0, 3, 33856, 0, 0, 0, 0, '', 'Summon Fire Bot Trigger EFFECT_0 can only hit NPC_BOT_SUMMON_TRIGGER'), +(13, 1, 63820, 0, 0, 31, 0, 3, 33856, 0, 0, 0, 0, '', 'Summon Junk Bot Trigger EFFECT_0 can only hit NPC_BOT_SUMMON_TRIGGER'), +(13, 1, 64425, 0, 0, 31, 0, 3, 33856, 0, 0, 0, 0, '', 'Summon Assault Bot Trigger EFFECT_0 can only hit NPC_BOT_SUMMON_TRIGGER'), +(13, 1, 64402, 0, 0, 31, 0, 3, 34050, 0, 0, 0, 0, '', 'Rocket Strike EFFECT_0 can only hit NPC_ROCKET_MIMIRON_VISUAL'), +(13, 1, 65034, 0, 0, 31, 0, 3, 34050, 0, 0, 0, 0, '', 'Rocket Strike EFFECT_0 can only hit NPC_ROCKET_MIMIRON_VISUAL'), +(13, 2, 64436, 0, 0, 31, 0, 3, 33670, 0, 0, 0, 0, '', 'Magnetic Core EFFECT_1 can only hit NPC_AERIAL_COMMAND_UNIT'), +(13, 1, 64444, 0, 0, 31, 0, 3, 33670, 0, 0, 0, 0, '', 'Magnetic Core EFFECT_0 can only hit NPC_AERIAL_COMMAND_UNIT'), +(13, 1, 65101, 0, 0, 31, 0, 3, 34071, 0, 0, 0, 0, '', 'Emergency Mode EFFECT_0 can only hit NPC_LEVIATHAN_MKII_TURRET'), +(13, 1, 64463, 0, 0, 31, 0, 3, 34057, 0, 0, 0, 0, '', 'Despawn Assault Bots EFFECT_1 can only hit NPC_ASSAULT_BOT'), +(13, 2, 63041, 0, 0, 31, 0, 3, 34057, 0, 0, 0, 0, '', 'Rocket Strike EFFECT_1 can only hit NPC_ASSAULT_BOT'), +(13, 4, 63041, 0, 1, 31, 0, 3, 34137, 0, 0, 0, 0, '', 'Rocket Strike EFFECT_2 can only hit NPC_EMERGENCY_FIRE_BOT'), +(13, 4, 63041, 0, 2, 31, 0, 3, 33855, 0, 0, 0, 0, '', 'Rocket Strike EFFECT_2 can only hit NPC_JUNK_BOT'), +(13, 4, 63041, 0, 3, 31, 0, 3, 33836, 0, 0, 0, 0, '', 'Rocket Strike EFFECT_2 can only hit NPC_BOMB_BOT'); + +UPDATE `creature_template` SET `HoverHeight` = 15, `ScriptName` = 'boss_aerial_command_unit', `InhabitType` = 3, `mechanic_immune_mask` = 646135679 WHERE `entry` = 33670; +UPDATE `creature_template` SET `HoverHeight` = 15, `InhabitType` = 3, `mechanic_immune_mask` = 646135679 WHERE `entry` = 34109; +UPDATE `creature_template` SET `unit_flags` = 33554438, `ScriptName` = 'boss_vx_001', `mechanic_immune_mask` = 646135679 WHERE `entry` = 33651; +UPDATE `creature_template` SET `unit_flags` = 33554438, `mechanic_immune_mask` = 646135679 WHERE `entry` = 34108; +UPDATE `creature_template` SET `ScriptName` = 'boss_leviathan_mk_ii', `mechanic_immune_mask` = 646135679 WHERE `entry` = 33432; +UPDATE `creature_template` SET `mechanic_immune_mask` = 646135679 WHERE `entry` = 34106; +UPDATE `creature_template` SET `ScriptName` = 'boss_mimiron' WHERE `entry` = 33350; +UPDATE `creature_template` SET `unit_flags` = 131078, `ScriptName` = 'npc_mimiron_proximity_mine' WHERE `entry` = 34362; +UPDATE `creature_template` SET `unit_flags` = 33685510, `faction` = 14, `ScriptName` = 'npc_mimiron_frost_bomb' WHERE `entry` = 34149; +UPDATE `creature_template` SET `unit_flags` = 33685510 WHERE `entry` IN( 34047, 34110, 34050, 34071); +UPDATE `creature_template` SET `unit_flags` = 33685510, `ScriptName` = 'npc_magnetic_core' WHERE `entry` = 34068; +UPDATE `creature_template` SET `unit_flags` = 33554436, `ScriptName` = 'npc_mimiron_flames' WHERE `entry` IN (34363, 34121); +UPDATE `creature_template` SET `faction` = 14 , `ScriptName` = 'npc_mimiron_emergency_fire_bot' WHERE `entry` = 34147; +UPDATE `creature_template` SET `faction` = 14 WHERE `entry` IN (34148, 34114, 33855); +UPDATE `creature_template` SET `dmg_multiplier` = 60, `ScriptName` = 'npc_mimiron_assault_bot' WHERE `entry` = 34057; +UPDATE `creature_template` SET `dmg_multiplier` = 75 WHERE `entry` = 34115; +UPDATE `creature_template` SET `ScriptName` = 'npc_mimiron_computer' WHERE `entry` = 34143; +UPDATE `creature_template` SET `speed_walk` = 2.5 , `speed_run` = 4.5 , `MovementType` = 2 , `InhabitType` = 7, `flags_extra` = 2 WHERE `entry` = 33576; + +UPDATE `creature_model_info` SET `bounding_radius` = 0.31 , `combat_reach` = 5 WHERE `modelid` = 28979; +UPDATE `creature_model_info` SET `bounding_radius` = 0.306 , `combat_reach` = 7 WHERE `modelid` = 28841; +UPDATE `creature_model_info` SET `bounding_radius` = 0.31 , `combat_reach` = 7.7 WHERE `modelid` = 28831; + +UPDATE `gameobject_template` SET `flags` = 32, `ScriptName` = 'go_mimiron_hardmode_button' WHERE `entry` = 194739; + +UPDATE `creature_addon` SET `path_id` = 1376200 WHERE `guid` = 137620; + +UPDATE `creature` SET `position_x` = 2785.423 , `position_y` = 2673.119, `MovementType` = 2 WHERE `guid` = 137620; + +DELETE FROM `creature_template_addon` WHERE `entry` IN (34047, 34148, 33836, 34149, 34147, 33670, 34363, 34121); +INSERT INTO `creature_template_addon` (`entry`, `bytes1`, `bytes2`, `auras`) VALUES +(33670, 50331648, 1, ''), +(34363, 0, 1, '64561'), +(34121, 0, 1, '64561'), +(34047, 0, 1, '64064'), +(34148, 0, 1, '64616 64617'), +(33836, 0, 1, '63767'), +(34149, 0, 1, '64624'), +(34147, 0, 1, '64617'); + +DELETE FROM `creature_text` WHERE `entry` = 34143; +INSERT INTO `creature_text` (`entry`, `groupid`, `text`, `type`, `probability`, `sound`, `BroadcastTextID`) VALUES +(34143, 0, 'Self-destruct sequence initiated.', 14, 100, 15413, 34268), +(34143, 1, 'Self-destruct sequence terminated. Override code A905..', 14, 100, 15414, 34284), +(34143, 2, 'This area will self-destruct in ten minutes.', 14, 100, 15415, 34283), +(34143, 3, 'This area will self-destruct in nine minutes.', 14, 100, 15416, 34282), +(34143, 4, 'This area will self-destruct in eight minutes.', 14, 100, 15417, 34281), +(34143, 5, 'This area will self-destruct in seven minutes.', 14, 100, 15418, 34280), +(34143, 6, 'This area will self-destruct in six minutes.', 14, 100, 15419, 34273), +(34143, 7, 'This area will self-destruct in five minutes.', 14, 100, 15420, 34274), +(34143, 8, 'This area will self-destruct in four minutes.', 14, 100, 15421, 34275), +(34143, 9, 'This area will self-destruct in three minutes.', 14, 100, 15422, 34276), +(34143, 10, 'This area will self-destruct in two minutes.', 14, 100, 15423, 34277), +(34143, 11, 'This area will self-destruct in one minute.', 14, 100, 15424, 34278), +(34143, 12, 'Self-destruct sequence finalized. Have a nice day.', 14, 100, 15425, 34279); + +DELETE FROM `waypoint_data` WHERE `id` = 1376200; +INSERT INTO `waypoint_data` (`id`, `point`, `position_x`, `position_y`, `position_z`, `orientation`, `delay`, `move_flag`, `action`, `action_chance`, `wpguid`) VALUES +(1376200, 0, 2785.423, 2673.119, 372.3605, 0, 0, 1, 0, 100, 0), +(1376200, 1, 2823.024, 2649.059, 371.9791, 0, 0, 1, 0, 100, 0), +(1376200, 2, 2854.097, 2590.826, 371.9791, 0, 0, 1, 0, 100, 0), +(1376200, 3, 2852.951, 2547.112, 372.3605, 0, 0, 1, 0, 100, 0), +(1376200, 4, 2822.796, 2489.515, 372.3605, 0, 0, 1, 0, 100, 0), +(1376200, 5, 2784.964, 2465.247, 372.3605, 0, 0, 1, 0, 100, 0), +(1376200, 6, 2741.24, 2456.771, 372.3605, 0, 0, 1, 0, 100, 0), +(1376200, 7, 2701.036, 2464.319, 372.3605, 0, 0, 1, 0, 100, 0), +(1376200, 8, 2660.476, 2489.578, 372.3605, 0, 0, 1, 0, 100, 0), +(1376200, 9, 2636.893, 2525.687, 372.3605, 0, 0, 1, 0, 100, 0), +(1376200, 10, 2631.294, 2547.831, 372.3605, 0, 0, 1, 0, 100, 0), +(1376200, 11, 2631.437, 2591.752, 372.3605, 0, 0, 1, 0, 100, 0), +(1376200, 12, 2637.362, 2613.7, 372.3605, 0, 0, 1, 0, 100, 0), +(1376200, 13, 2650.221, 2636.163, 372.3605, 0, 0, 1, 0, 100, 0), +(1376200, 14, 2661.572, 2649.715, 372.3605, 0, 0, 1, 0, 100, 0), +(1376200, 15, 2696.595, 2672.664, 372.3605, 0, 0, 1, 0, 100, 0), +(1376200, 16, 2711.099, 2677.791, 372.3605, 0, 0, 1, 0, 100, 0), +(1376200, 17, 2740.676, 2683.12, 372.3605, 0, 0, 1, 0, 100, 0), +(1376200, 18, 2771.834, 2677.767, 372.3605, 0, 0, 1, 0, 100, 0); + +DELETE FROM `disables` WHERE `sourceType` = 4 AND `entry` IN (10544, 10547, 10543, 10546, 10548, 10545, 10450, 10463, 10406, 10405); + +DELETE FROM `achievement_criteria_data` WHERE `criteria_id` IN (10406, 10405) AND `type` = 18; +DELETE FROM `achievement_criteria_data` WHERE `criteria_id` IN (10545,10548,10546,10543,10547,10544, 10450, 10463) AND `type` = 11; +INSERT INTO `achievement_criteria_data` (`criteria_id`, `type`, `value1`, `value2`, `ScriptName`) VALUES +(10544, 11, 0, 0, 'achievement_setup_rocket'), +(10547, 11, 0, 0, 'achievement_setup_rocket'), +(10543, 11, 0, 0, 'achievement_setup_mine'), +(10546, 11, 0, 0, 'achievement_setup_mine'), +(10548, 11, 0, 0, 'achievement_setup_boom'), +(10545, 11, 0, 0, 'achievement_setup_boom'), +(10450, 11, 0, 0, 'achievement_firefighter'), +(10463, 11, 0, 0, 'achievement_firefighter'); +-- diff --git a/src/server/game/Spells/SpellMgr.cpp b/src/server/game/Spells/SpellMgr.cpp index b94d0def3d..75243139fe 100644 --- a/src/server/game/Spells/SpellMgr.cpp +++ b/src/server/game/Spells/SpellMgr.cpp @@ -3338,6 +3338,16 @@ void SpellMgr::LoadSpellInfoCorrections() // that will be clear if we get more spells with problem like this spellInfo->AttributesEx |= SPELL_ATTR1_DISPEL_AURAS_ON_IMMUNITY; break; + case 63414: // Spinning Up (Mimiron) + spellInfo->Effects[EFFECT_0].TargetB = SpellImplicitTargetInfo(TARGET_UNIT_CASTER); + spellInfo->ChannelInterruptFlags = 0; + break; + case 63036: // Rocket Strike (Mimiron) + spellInfo->Speed = 0; + break; + case 64668: // Magnetic Field (Mimiron) + spellInfo->Mechanic = MECHANIC_NONE; + break; case 64468: // Empowering Shadows (Yogg-Saron) case 64486: // Empowering Shadows (Yogg-Saron) spellInfo->MaxAffectedTargets = 3; // same for both modes? diff --git a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_mimiron.cpp b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_mimiron.cpp index f284aacf99..a3c3077fd7 100644 --- a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_mimiron.cpp +++ b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_mimiron.cpp @@ -15,10 +15,16 @@ * with this program. If not, see . */ +#include "Cell.h" +#include "CellImpl.h" +#include "GridNotifiers.h" +#include "GridNotifiersImpl.h" #include "ScriptMgr.h" #include "ScriptedCreature.h" #include "SpellScript.h" +#include "SpellAuraEffects.h" #include "ulduar.h" +#include "Vehicle.h" enum Yells { @@ -39,78 +45,2927 @@ enum Yells SAY_BERSERK = 14 }; +enum ComputerYells +{ + SAY_SELF_DESTRUCT_INITIATED = 0, + SAY_SELF_DESTRUCT_TERMINATED = 1, + SAY_SELF_DESTRUCT_10 = 2, + SAY_SELF_DESTRUCT_9 = 3, + SAY_SELF_DESTRUCT_8 = 4, + SAY_SELF_DESTRUCT_7 = 5, + SAY_SELF_DESTRUCT_6 = 6, + SAY_SELF_DESTRUCT_5 = 7, + SAY_SELF_DESTRUCT_4 = 8, + SAY_SELF_DESTRUCT_3 = 9, + SAY_SELF_DESTRUCT_2 = 10, + SAY_SELF_DESTRUCT_1 = 11, + SAY_SELF_DESTRUCT_FINALIZED = 12 +}; + enum Spells { + // Mimiron + SPELL_WELD = 63339, // Idle aura. + SPELL_SEAT_1 = 52391, // Cast on all vehicles, Cycled on MKII + SPELL_SEAT_2 = 63313, // Cast on MKII and VX-001, Cycled on MKII + SPELL_SEAT_3 = 63314, // Cast on MKII, Cycled on MKII + SPELL_SEAT_5 = 63316, // Cast on MKII and VX-001, Cycled on MKII + SPELL_SEAT_6 = 63344, // Cast on MKII + SPELL_SEAT_7 = 63345, // Cast on MKII SPELL_JETPACK = 63341, - SPELL_EMERGENCY_MODE = 64582, - SPELL_SELF_REPAIR = 64383, - SPELL_MAGNETIC_CORE = 64444, + SPELL_DESPAWN_ASSAULT_BOTS = 64463, // only despawns assault bots... no equivalent spell for the other adds... + SPELL_TELEPORT_VISUAL = 41232, + SPELL_SLEEP_VISUAL_1 = 64393, + SPELL_SLEEP_VISUAL_2 = 64394, + // Leviathan MK II SPELL_FLAME_SUPPRESSANT_MK = 64570, SPELL_NAPALM_SHELL = 63666, - SPELL_PLASMA_BLAST = 62977, - SPELL_PROXIMITY_MINES = 63027, + SPELL_FORCE_CAST_NAPALM_SHELL = 64539, + SPELL_PLASMA_BLAST = 62997, + SPELL_SCRIPT_EFFECT_PLASMA_BLAST = 64542, SPELL_SHOCK_BLAST = 63631, - // VX 001 + SPELL_SHOCK_BLAST_AURA = 63632, // Deprecated? It is never cast. + + // VX-001 SPELL_FLAME_SUPPRESSANT_VX = 65192, - SPELL_FROSTBOMB = 64623, - SPELL_HAND_PULSE = 64348, SPELL_SPINNING_UP = 63414, - SPELL_RAPID_BURST = 63387, - SPELL_P3WX2_LASER_BARRAGE = 63293, - SPELL_ROCKET_STRIKE = 63041, - SPELL_HEAT_WAVE = 63677, + SPELL_HEAT_WAVE_AURA = 63679, + SPELL_HAND_PULSE_LEFT = 64348, + SPELL_HAND_PULSE_RIGHT = 64352, + SPELL_MOUNT_MKII = 64387, + SPELL_TORSO_DISABLED = 64120, + // Aerial Command Unit - SPELL_PLASMA_BALL = 63689, - // Additonal spells - SPELL_MAGNETIC_FIELD = 64668, - SPELL_DEAFENING_SIREN = 64616, + SPELL_PLASMA_BALL_P1 = 63689, + SPELL_PLASMA_BALL_P2 = 65647, + SPELL_MOUNT_VX_001 = 64388, + + // Proximity Mines + SPELL_PROXIMITY_MINES = 63027, // Cast by Leviathan MK II + SPELL_PROXIMITY_MINE_EXPLOSION = 66351, + SPELL_PROXIMITY_MINE_TRIGGER = 65346, + SPELL_PROXIMITY_MINE_PERIODIC_TRIGGER = 65345, + SPELL_PERIODIC_PROXIMITY_AURA = 65345, + SPELL_SUMMON_PROXIMITY_MINE = 65347, + + // Rapid Burst + SPELL_RAPID_BURST_LEFT = 63387, + SPELL_RAPID_BURST_RIGHT = 64019, + SPELL_RAPID_BURST = 63382, // Cast by VX-001 + SPELL_RAPID_BURST_TARGET_ME = 64841, // Cast by Burst Target + SPELL_SUMMON_BURST_TARGET = 64840, // Cast by VX-001 + + // Rocket Strike + SPELL_SUMMON_ROCKET_STRIKE = 63036, + SPELL_SCRIPT_EFFECT_ROCKET_STRIKE = 63681, // Cast by Rocket (Mimiron Visual) + SPELL_ROCKET_STRIKE = 64064, // Added in creature_template_addon + SPELL_ROCKET_STRIKE_LEFT = 64402, // Cast by VX-001 + SPELL_ROCKET_STRIKE_BOTH = 65034, // Cast by VX-001 + + // Flames + SPELL_FLAMES_PERIODIC_TRIGGER = 64561, // Added in creature_template_addon + SPELL_SUMMON_FLAMES_SPREAD_TRIGGER = 64562, + SPELL_SUMMON_FLAMES_INITIAL = 64563, + SPELL_SUMMON_FLAMES_SPREAD = 64564, + SPELL_FLAMES = 64566, + SPELL_SCRIPT_EFFECT_SUMMON_FLAMES_INITIAL = 64567, + + // Frost Bomb + SPELL_SCRIPT_EFFECT_FROST_BOMB = 64623, // Cast by VX-001 + SPELL_FROST_BOMB_LINKED = 64624, // Added in creature_template_addon + SPELL_FROST_BOMB_DUMMY = 64625, + SPELL_SUMMON_FROST_BOMB = 64627, // Cast by VX-001 + SPELL_FROST_BOMB_EXPLOSION = 64626, + SPELL_CLEAR_FIRES = 65354, + + // Bots + SPELL_SUMMON_FIRE_BOT = 64622, + SPELL_SUMMON_FIRE_BOT_DUMMY = 64621, + SPELL_SUMMON_FIRE_BOT_TRIGGER = 64620, // Cast by Areal Command Unit + SPELL_DEAFENING_SIREN = 64616, // Added in creature_template_addon + SPELL_FIRE_SEARCH_AURA = 64617, // Added in creature_template_addon + SPELL_FIRE_SEARCH = 64618, SPELL_WATER_SPRAY = 64619, - SPELL_FROST_BOMB_HARD_MODE = 64627, - SPELL_EXPLOSION = 66351, - SPELL_DISARM = 1842, - SPELL_RIDE_VEHICLE = 46598, - SPELL_TRIGGER_MISSILE = 65347, + + SPELL_SUMMON_JUNK_BOT = 63819, + SPELL_SUMMON_JUNK_BOT_TRIGGER = 63820, // Cast by Areal Command Unit + SPELL_SUMMON_JUNK_BOT_DUMMY = 64398, + + SPELL_SUMMON_ASSAULT_BOT_TRIGGER = 64425, // Cast by Areal Command Unit + SPELL_SUMMON_ASSAULT_BOT_DUMMY = 64426, + SPELL_SUMMON_ASSAULT_BOT = 64427, + SPELL_MAGNETIC_FIELD = 64668, + + SPELL_SUMMON_BOMB_BOT = 63811, // Cast by Areal Command Unit + SPELL_BOMB_BOT_AURA = 63767, // Added in creature_template_addon + + // Miscellaneous + SPELL_SELF_DESTRUCTION_AURA = 64610, + SPELL_SELF_DESTRUCTION_VISUAL = 64613, + SPELL_NOT_SO_FRIENDLY_FIRE = 65040, + SPELL_ELEVATOR_KNOCKBACK = 65096, // Cast by worldtrigger. + SPELL_VEHICLE_DAMAGED = 63415, + SPELL_EMERGENCY_MODE = 64582, // mkii, vx001, aerial, assault, junk + SPELL_EMERGENCY_MODE_TURRET = 65101, // Cast by Leviathan MK II, only hits Leviathan MK II turret + SPELL_SELF_REPAIR = 64383, + SPELL_MAGNETIC_CORE = 64436, + SPELL_MAGNETIC_CORE_VISUAL = 64438, + SPELL_HALF_HEAL = 64188, + SPELL_CLEAR_ALL_DEBUFFS = 34098, // TODO: make use of this spell... + SPELL_FREEZE_ANIM_STUN = 63354, // used to prevent mkii from doing stuff?.. + SPELL_FREEZE_ANIM = 16245 // Idle aura. Freezes animation. }; -enum Npc +enum Data { - NPC_ASSAULT_BOT = 34057, - NPC_BOMB_BOT = 33836, - NPC_JUNK_BOT = 33855, - NPC_EMERGENCE_FIRE_BOT = 34147, - NPC_FROST_BOMB = 34149, + DATA_SETUP_MINE, + DATA_SETUP_BOMB, + DATA_SETUP_ROCKET, + DATA_NOT_SO_FRIENDLY_FIRE, + DATA_FIREFIGHTER, + DATA_WATERSPRAY, + DATA_MOVE_NEW }; -class spell_ulduar_proximity_mines : public SpellScriptLoader +enum Events +{ + EVENT_NONE, + + // Leviathan MK II + EVENT_PROXIMITY_MINE, + EVENT_NAPALM_SHELL, + EVENT_PLASMA_BLAST, + EVENT_SHOCK_BLAST, + EVENT_FLAME_SUPPRESSANT_MK, + + // VX-001 + EVENT_RAPID_BURST, + EVENT_SPINNING_UP, + EVENT_ROCKET_STRIKE, + EVENT_HAND_PULSE, + EVENT_FROST_BOMB, + EVENT_FLAME_SUPPRESSANT_VX, + EVENT_RELOAD, + + // Aerial Command Unit + EVENT_SUMMON_FIRE_BOTS, + EVENT_SUMMON_JUNK_BOT, + EVENT_SUMMON_ASSAULT_BOT, + EVENT_SUMMON_BOMB_BOT, + + // Mimiron + EVENT_SUMMON_FLAMES, + EVENT_INTRO_1, + EVENT_INTRO_2, + EVENT_INTRO_3, + + EVENT_VX001_ACTIVATION_1, + EVENT_VX001_ACTIVATION_2, + EVENT_VX001_ACTIVATION_3, + EVENT_VX001_ACTIVATION_4, + EVENT_VX001_ACTIVATION_5, + EVENT_VX001_ACTIVATION_6, + EVENT_VX001_ACTIVATION_7, + EVENT_VX001_ACTIVATION_8, + EVENT_VX001_ACTIVATION_9, + + EVENT_AERIAL_ACTIVATION_1, + EVENT_AERIAL_ACTIVATION_2, + EVENT_AERIAL_ACTIVATION_3, + EVENT_AERIAL_ACTIVATION_4, + EVENT_AERIAL_ACTIVATION_5, + EVENT_AERIAL_ACTIVATION_6, + + EVENT_VOL7RON_ACTIVATION_1, + EVENT_VOL7RON_ACTIVATION_2, + EVENT_VOL7RON_ACTIVATION_3, + EVENT_VOL7RON_ACTIVATION_4, + EVENT_VOL7RON_ACTIVATION_5, + EVENT_VOL7RON_ACTIVATION_6, + EVENT_VOL7RON_ACTIVATION_7, + + EVENT_OUTTRO_1, + EVENT_OUTTRO_2, + EVENT_OUTTRO_3, + + // Computer + EVENT_SELF_DESTRUCT_10, + EVENT_SELF_DESTRUCT_9, + EVENT_SELF_DESTRUCT_8, + EVENT_SELF_DESTRUCT_7, + EVENT_SELF_DESTRUCT_6, + EVENT_SELF_DESTRUCT_5, + EVENT_SELF_DESTRUCT_4, + EVENT_SELF_DESTRUCT_3, + EVENT_SELF_DESTRUCT_2, + EVENT_SELF_DESTRUCT_1, + EVENT_SELF_DESTRUCT_FINALIZED +}; + +enum Actions +{ + DO_START_MKII, + DO_HARDMODE_MKII, + + DO_ACTIVATE_VX001, + DO_START_VX001, + DO_HARDMODE_VX001, + + DO_ACTIVATE_AERIAL, + DO_START_AERIAL, + DO_HARDMODE_AERIAL, + DO_DISABLE_AERIAL, + DO_ENABLE_AERIAL, + + DO_ACTIVATE_V0L7R0N_1, + DO_ACTIVATE_V0L7R0N_2, + DO_ASSEMBLED_COMBAT, // All 3 parts use this action, its done on purpose. + + DO_ACTIVATE_HARD_MODE, + DO_ACTIVATE_COMPUTER, + DO_DEACTIVATE_COMPUTER, + DO_ACTIVATE_SELF_DESTRUCT, + + DO_ENCOUNTER_DONE +}; + +enum Waypoints +{ + WP_NONE, + WP_MKII_P1_IDLE, + WP_MKII_P4_POS_1, + WP_MKII_P4_POS_2, + WP_MKII_P4_POS_3, + WP_MKII_P4_POS_4, + WP_MKII_P4_POS_5, + WP_AERIAL_P4_POS +}; + +enum Phases +{ + PHASE_NULL, + + // Leviathan MK II + PHASE_LEVIATHAN_SOLO, + PHASE_LEVIATHAN_ASSEMBLED, + + // VX-001 + PHASE_VX001_SOLO, + PHASE_VX001_ASSEMBLED, + + // Aerial Command Unit + PHASE_AERIAL_SOLO, + PHASE_AERIAL_ASSEMBLED +}; + +uint32 const repairSpells[4] = +{ + SPELL_SEAT_1, + SPELL_SEAT_2, + SPELL_SEAT_3, + SPELL_SEAT_5 +}; + +Position const pos[] = +{ + {}, + { 2792.070f, 2596.320f, 364.3136f }, // WP_MKII_P1_IDLE + { 2765.945f, 2571.095f, 364.0636f }, // WP_MKII_P4_POS_1 + { 2768.195f, 2573.095f, 364.0636f }, // WP_MKII_P4_POS_2 + { 2763.820f, 2568.870f, 364.3136f }, // WP_MKII_P4_POS_3 + { 2761.215f, 2568.875f, 364.0636f }, // WP_MKII_P4_POS_4 + { 2744.610f, 2569.380f, 364.3136f }, // WP_MKII_P4_POS_5 + { 2748.513f, 2569.051f, 364.3136f } // WP_AERIAL_P4_POS +}; + +class AerialTargetSelector { public: - spell_ulduar_proximity_mines() : SpellScriptLoader("spell_ulduar_proximity_mines") { } - - class spell_ulduar_proximity_minesSpellScript : public SpellScript + AerialTargetSelector() {} + + bool operator() (WorldObject* target) const { - PrepareSpellScript(spell_ulduar_proximity_minesSpellScript); - - void HandleScript(SpellEffIndex effIndex) + if (Unit* unit = target->ToUnit()) { - PreventHitDefaultEffect(effIndex); - for (uint8 i = 0; i < 10; ++i) - GetCaster()->CastSpell(GetCaster(), SPELL_TRIGGER_MISSILE, true); + if (unit->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE)) + return true; + + if (unit->GetVehicleBase()) + return true; + } + return false; + } +}; + +class EncounterDone +{ + Unit* _who; + Creature* _vehiclePart; + public: + EncounterDone(Unit* who, Creature* vehiclePart) : _who(who), _vehiclePart(vehiclePart) {} + + bool IsEncounterFinished() + { + if (InstanceScript* instance = _vehiclePart->GetInstanceScript()) + if (Creature* mkii = ObjectAccessor::GetCreature(*_vehiclePart, instance->GetData64(DATA_LEVIATHAN_MK_II))) + if (Creature* vx001 = ObjectAccessor::GetCreature(*_vehiclePart, instance->GetData64(DATA_VX_001))) + if (Creature* aerial = ObjectAccessor::GetCreature(*_vehiclePart, instance->GetData64(DATA_AERIAL_COMMAND_UNIT))) + if (mkii->getStandState() == UNIT_STAND_STATE_DEAD && vx001->getStandState() == UNIT_STAND_STATE_DEAD && aerial->getStandState() == UNIT_STAND_STATE_DEAD) + { + _who->Kill(mkii); + _who->Kill(vx001); + _who->Kill(aerial); + mkii->DespawnOrUnsummon(120000); + vx001->DespawnOrUnsummon(120000); + aerial->DespawnOrUnsummon(120000); + if (Creature* mimiron = ObjectAccessor::GetCreature(*_vehiclePart, instance->GetData64(BOSS_MIMIRON))) + mimiron->AI()->JustDied(_who); + return true; + } + return false; + } +}; + +class boss_mimiron : public CreatureScript +{ + public: + boss_mimiron() : CreatureScript("boss_mimiron") { } + + struct boss_mimironAI : public BossAI + { + boss_mimironAI(Creature* creature) : BossAI(creature, BOSS_MIMIRON) + { + me->SetReactState(REACT_PASSIVE); + me->SetDisableGravity(true); + } + + void DoAction(int32 action) override + { + switch (action) + { + case DO_ACTIVATE_VX001: + events.ScheduleEvent(EVENT_VX001_ACTIVATION_1, 1000); + break; + case DO_ACTIVATE_AERIAL: + events.ScheduleEvent(EVENT_AERIAL_ACTIVATION_1, 5000); + break; + case DO_ACTIVATE_V0L7R0N_1: + Talk(SAY_AERIAL_DEATH); + if (Creature* mkii = ObjectAccessor::GetCreature(*me, instance->GetData64(DATA_LEVIATHAN_MK_II))) + mkii->GetMotionMaster()->MovePoint(WP_MKII_P4_POS_3, pos[WP_MKII_P4_POS_3]); // MovePoint(WP_MKII_P4_POS_1, pos[WP_MKII_P4_POS_1]); + break; + case DO_ACTIVATE_V0L7R0N_2: + events.ScheduleEvent(EVENT_VOL7RON_ACTIVATION_1, 1000); + break; + case DO_ACTIVATE_HARD_MODE: + _fireFighter = true; + DoZoneInCombat(me); + break; + default: + break; + } + } + + void EnterCombat(Unit* /*who*/) override + { + if (!me->GetVehicleBase()) + return; + + _EnterCombat(); + me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); + me->RemoveAurasDueToSpell(SPELL_WELD); + DoCast(me->GetVehicleBase(), SPELL_SEAT_6); + + if (GameObject* button = ObjectAccessor::GetGameObject(*me, instance->GetData64(DATA_MIMIRON_BUTTON))) + button->SetFlag(GAMEOBJECT_FLAGS, GO_FLAG_NOT_SELECTABLE); + + if (_fireFighter) + events.ScheduleEvent(EVENT_SUMMON_FLAMES, 3000); + events.ScheduleEvent(EVENT_INTRO_1, 1500); + } + + void JustDied(Unit* /*who*/) override + { + instance->SetBossState(BOSS_MIMIRON, DONE); + events.Reset(); + me->CombatStop(true); + me->SetDisableGravity(false); + DoCast(me, SPELL_SLEEP_VISUAL_1); + DoCastAOE(SPELL_DESPAWN_ASSAULT_BOTS); + me->ExitVehicle(); + // ExitVehicle() offset position is not implemented, so we make up for that with MoveJump()... + me->GetMotionMaster()->MoveJump(me->GetPositionX() + (float)(10 * cos(me->GetOrientation())), me->GetPositionY() + (float)(10 * sin(me->GetOrientation())), me->GetPositionZ(), 10.0f, 5.0f); + events.ScheduleEvent(EVENT_OUTTRO_1, 7000); + } + + void Reset() override + { + if (instance->GetBossState(BOSS_MIMIRON) == DONE) // Mimiron will attempt to reset because he is not dead and will be set to friendly before despawning. + return; + + _Reset(); + me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); + instance->SetData(DATA_MIMIRON_ELEVATOR, GO_STATE_ACTIVE); + + if (_fireFighter) + if (Creature* computer = ObjectAccessor::GetCreature(*me, instance->GetData64(DATA_COMPUTER))) + computer->AI()->DoAction(DO_DEACTIVATE_COMPUTER); + + if (GameObject* button = ObjectAccessor::GetGameObject(*me, instance->GetData64(DATA_MIMIRON_BUTTON))) + { + button->SetGoState(GO_STATE_READY); + button->RemoveFlag(GAMEOBJECT_FLAGS,GO_FLAG_NOT_SELECTABLE); + } + + _fireFighter = false; + DoCast(me, SPELL_WELD); + + if (Unit* mkii = ObjectAccessor::GetUnit(*me, instance->GetData64(DATA_LEVIATHAN_MK_II))) + DoCast(mkii, SPELL_SEAT_3); + } + + void UpdateAI(uint32 diff) override + { + if ((!UpdateVictim() || !CheckInRoom()) && instance->GetBossState(BOSS_MIMIRON) != DONE) + return; + + events.Update(diff); + + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; + + while (uint32 eventId = events.ExecuteEvent()) + { + switch (eventId) + { + case EVENT_SUMMON_FLAMES: + if (Unit* worldtrigger = ObjectAccessor::GetUnit(*me, instance->GetData64(DATA_MIMIRON_WORLD_TRIGGER))) + worldtrigger->CastSpell((Unit*)NULL, SPELL_SCRIPT_EFFECT_SUMMON_FLAMES_INITIAL, true, NULL, NULL, me->GetGUID()); + events.RescheduleEvent(EVENT_SUMMON_FLAMES, 28000); + break; + case EVENT_INTRO_1: + Talk(_fireFighter ? SAY_HARDMODE_ON : SAY_MKII_ACTIVATE); + events.ScheduleEvent(EVENT_INTRO_2, 5000); + break; + case EVENT_INTRO_2: + if (Unit* mkii = me->GetVehicleBase()) + { + DoCast(mkii, SPELL_SEAT_7); + mkii->RemoveAurasDueToSpell(SPELL_FREEZE_ANIM); + mkii->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE); + } + events.ScheduleEvent(EVENT_INTRO_3, 2000); + break; + case EVENT_INTRO_3: + me->GetVehicleBase()->GetAI()->DoAction(_fireFighter? DO_HARDMODE_MKII : DO_START_MKII); + break; + case EVENT_VX001_ACTIVATION_1: + me->GetVehicleBase()->SetFacingTo(3.686f); + events.ScheduleEvent(EVENT_VX001_ACTIVATION_2, 1000); + break; + case EVENT_VX001_ACTIVATION_2: + DoCast(me->GetVehicleBase(), SPELL_SEAT_6); + events.ScheduleEvent(EVENT_VX001_ACTIVATION_3, 1000); + break; + case EVENT_VX001_ACTIVATION_3: + Talk(SAY_MKII_DEATH); + events.ScheduleEvent(EVENT_VX001_ACTIVATION_4, 5000); + break; + case EVENT_VX001_ACTIVATION_4: + instance->SetData(DATA_MIMIRON_ELEVATOR, GO_STATE_READY); + if (Unit* worldtrigger = ObjectAccessor::GetUnit(*me, instance->GetData64(DATA_MIMIRON_WORLD_TRIGGER))) + worldtrigger->CastSpell(worldtrigger, SPELL_ELEVATOR_KNOCKBACK); + events.ScheduleEvent(EVENT_VX001_ACTIVATION_5, 6000); + break; + case EVENT_VX001_ACTIVATION_5: + me->SummonCreature(NPC_VX_001, 2744.431f, 2569.385f, 364.3968f, 3.141593f, TEMPSUMMON_MANUAL_DESPAWN); + instance->SetData(DATA_MIMIRON_ELEVATOR, GO_STATE_ACTIVE_ALTERNATIVE); + events.ScheduleEvent(EVENT_VX001_ACTIVATION_6, 19000); + break; + case EVENT_VX001_ACTIVATION_6: + if (Unit* vx001 = ObjectAccessor::GetUnit(*me, instance->GetData64(DATA_VX_001))) + DoCast(vx001, SPELL_SEAT_1); + events.ScheduleEvent(EVENT_VX001_ACTIVATION_7, 3500); + break; + case EVENT_VX001_ACTIVATION_7: + Talk(SAY_VX001_ACTIVATE); + events.ScheduleEvent(EVENT_VX001_ACTIVATION_8, 4000); + break; + case EVENT_VX001_ACTIVATION_8: + DoCast(me->GetVehicleBase(), SPELL_SEAT_2); + events.ScheduleEvent(EVENT_VX001_ACTIVATION_9, 3000); + break; + case EVENT_VX001_ACTIVATION_9: + me->GetVehicleBase()->GetAI()->DoAction(_fireFighter ? DO_HARDMODE_VX001 : DO_START_VX001); + break; + case EVENT_AERIAL_ACTIVATION_1: + DoCast(me->GetVehicleBase(), SPELL_SEAT_5); + events.ScheduleEvent(EVENT_AERIAL_ACTIVATION_2, 2500); + break; + case EVENT_AERIAL_ACTIVATION_2: + Talk(SAY_VX001_DEATH); + events.ScheduleEvent(EVENT_AERIAL_ACTIVATION_3, 5000); + break; + case EVENT_AERIAL_ACTIVATION_3: + me->SummonCreature(NPC_AERIAL_COMMAND_UNIT, 2744.65f, 2569.46f, 380, 0, TEMPSUMMON_MANUAL_DESPAWN); + events.ScheduleEvent(EVENT_AERIAL_ACTIVATION_4, 5000); + break; + case EVENT_AERIAL_ACTIVATION_4: + if (Unit* aerial = ObjectAccessor::GetUnit(*me, instance->GetData64(DATA_AERIAL_COMMAND_UNIT))) + me->CastSpell(aerial, SPELL_SEAT_1); + events.ScheduleEvent(EVENT_AERIAL_ACTIVATION_5, 2000); + break; + case EVENT_AERIAL_ACTIVATION_5: + Talk(SAY_AERIAL_ACTIVATE); + events.ScheduleEvent(EVENT_AERIAL_ACTIVATION_6, 8000); + break; + case EVENT_AERIAL_ACTIVATION_6: + me->GetVehicleBase()->GetAI()->DoAction(_fireFighter? DO_HARDMODE_AERIAL : DO_START_AERIAL); + break; + case EVENT_VOL7RON_ACTIVATION_1: + if (Creature* mkii = ObjectAccessor::GetCreature(*me, instance->GetData64(DATA_LEVIATHAN_MK_II))) + mkii->SetFacingTo(M_PI); + events.ScheduleEvent(EVENT_VOL7RON_ACTIVATION_2, 1000); + break; + case EVENT_VOL7RON_ACTIVATION_2: + if (Creature* mkii = ObjectAccessor::GetCreature(*me, instance->GetData64(DATA_LEVIATHAN_MK_II))) + if (Creature* vx001 = ObjectAccessor::GetCreature(*me, instance->GetData64(DATA_VX_001))) + { + vx001->RemoveAurasDueToSpell(SPELL_TORSO_DISABLED); + vx001->CastSpell(mkii, SPELL_MOUNT_MKII); + } + events.ScheduleEvent(EVENT_VOL7RON_ACTIVATION_3, 4500); + break; + case EVENT_VOL7RON_ACTIVATION_3: + if (Creature* mkii = ObjectAccessor::GetCreature(*me, instance->GetData64(DATA_LEVIATHAN_MK_II))) + mkii->GetMotionMaster()->MovePoint(WP_MKII_P4_POS_5, pos[WP_MKII_P4_POS_5]); // MovePoint(WP_MKII_P4_POS_4, pos[WP_MKII_P4_POS_4]); + events.ScheduleEvent(EVENT_VOL7RON_ACTIVATION_4, 5000); + break; + case EVENT_VOL7RON_ACTIVATION_4: + if (Creature* vx001 = ObjectAccessor::GetCreature(*me, instance->GetData64(DATA_VX_001))) + if (Creature* aerial = ObjectAccessor::GetCreature(*me, instance->GetData64(DATA_AERIAL_COMMAND_UNIT))) + { + aerial->GetMotionMaster()->MoveLand(0, (aerial->GetPositionX(), aerial->GetPositionY(), aerial->GetPositionZMinusOffset())); + //aerial->SetDisableGravity(false); + //aerial->SetHover(false); + aerial->SetUInt32Value(UNIT_FIELD_BYTES_1, 0); + aerial->CastSpell(vx001, SPELL_MOUNT_VX_001); + aerial->CastSpell(aerial, SPELL_HALF_HEAL); + } + events.ScheduleEvent(EVENT_VOL7RON_ACTIVATION_5, 4000); + break; + case EVENT_VOL7RON_ACTIVATION_5: + Talk(SAY_V07TRON_ACTIVATE); + events.ScheduleEvent(EVENT_VOL7RON_ACTIVATION_6, 3000); + break; + case EVENT_VOL7RON_ACTIVATION_6: + if (Creature* vx001 = ObjectAccessor::GetCreature(*me, instance->GetData64(DATA_VX_001))) + DoCast(vx001, SPELL_SEAT_2); + events.ScheduleEvent(EVENT_VOL7RON_ACTIVATION_7, 5000); + break; + case EVENT_VOL7RON_ACTIVATION_7: + for (uint8 data = DATA_LEVIATHAN_MK_II; data <= DATA_AERIAL_COMMAND_UNIT; data++) + if (Creature* mimironVehicle = ObjectAccessor::GetCreature(*me, instance->GetData64(data))) + mimironVehicle->AI()->DoAction(DO_ASSEMBLED_COMBAT); + break; + case EVENT_OUTTRO_1: + me->RemoveAurasDueToSpell(SPELL_SLEEP_VISUAL_1); + DoCast(me, SPELL_SLEEP_VISUAL_2); + me->setFaction(35); + events.ScheduleEvent(EVENT_OUTTRO_2, 3000); + break; + case EVENT_OUTTRO_2: + Talk(SAY_V07TRON_DEATH); + if (_fireFighter) + { + if (Creature* computer = ObjectAccessor::GetCreature(*me, instance->GetData64(DATA_COMPUTER))) + computer->AI()->DoAction(DO_DEACTIVATE_COMPUTER); + me->SummonGameObject(RAID_MODE(GO_CACHE_OF_INNOVATION_FIREFIGHTER, GO_CACHE_OF_INNOVATION_FIREFIGHTER_HERO), 2744.65f, 2569.46f, 364.314f, 3.14159f, 0, 0, 0.7f, 0.7f, 604800); + } + else + me->SummonGameObject(RAID_MODE(GO_CACHE_OF_INNOVATION, GO_CACHE_OF_INNOVATION_HERO), 2744.65f, 2569.46f, 364.314f, 3.14159f, 0, 0, 0.7f, 0.7f, 604800); + events.ScheduleEvent(EVENT_OUTTRO_3, 11000); + break; + case EVENT_OUTTRO_3: + DoCast(me, SPELL_TELEPORT_VISUAL); + me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + me->DespawnOrUnsummon(1000); // sniffs say 6 sec after, but it doesnt matter. + break; + default: + break; + } + } + } + + private: + bool _fireFighter; + }; + + CreatureAI* GetAI(Creature* creature) const override + { + return GetUlduarAI(creature); + } +}; + +class boss_leviathan_mk_ii : public CreatureScript +{ + public: + boss_leviathan_mk_ii() : CreatureScript("boss_leviathan_mk_ii") { } + + struct boss_leviathan_mk_iiAI : public BossAI + { + boss_leviathan_mk_iiAI(Creature* creature) : BossAI(creature, BOSS_MIMIRON) + { + } + + void DamageTaken(Unit* who, uint32 &damage) override + { + if (damage >= me->GetHealth()) + { + damage = me->GetHealth() - 1; // Let creature fall to 1 hp, but do not let it die or damage itself with SetHealth(). + me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); + DoCast(me, SPELL_VEHICLE_DAMAGED, true); + me->AttackStop(); + me->SetReactState(REACT_PASSIVE); + me->RemoveAllAurasExceptType(SPELL_AURA_CONTROL_VEHICLE, SPELL_AURA_MOD_INCREASE_HEALTH_PERCENT); + + if (events.IsInPhase(PHASE_LEVIATHAN_SOLO)) + { + me->CastStop(); + if (Unit* turret = me->GetVehicleKit()->GetPassenger(3)) + turret->Kill(turret); + + me->SetSpeed(MOVE_RUN, 1.5f, true); + me->GetMotionMaster()->MovePoint(WP_MKII_P1_IDLE, pos[WP_MKII_P1_IDLE]); + } + else if (events.IsInPhase(PHASE_LEVIATHAN_ASSEMBLED)) + { + me->SetStandState(UNIT_STAND_STATE_DEAD); + + if (EncounterDone(who, me).IsEncounterFinished()) + return; + + me->CastStop(); + DoCast(me, SPELL_SELF_REPAIR); + } + events.Reset(); + } + } + + void DoAction(int32 action) override + { + switch (action) + { + case DO_HARDMODE_MKII: + _fireFighter = true; + DoCast(me, SPELL_EMERGENCY_MODE); + DoCastAOE(SPELL_EMERGENCY_MODE_TURRET); + events.ScheduleEvent(EVENT_FLAME_SUPPRESSANT_MK, 60000, 0, PHASE_LEVIATHAN_SOLO); + // Missing break intended. + case DO_START_MKII: + _setupMine = true; + _setupBomb = true; + _setupRocket = true; + + me->SetReactState(REACT_AGGRESSIVE); + events.SetPhase(PHASE_LEVIATHAN_SOLO); + + events.ScheduleEvent(EVENT_NAPALM_SHELL, 3000, 0, PHASE_LEVIATHAN_SOLO); + events.ScheduleEvent(EVENT_PLASMA_BLAST, 15000, 0, PHASE_LEVIATHAN_SOLO); + events.ScheduleEvent(EVENT_PROXIMITY_MINE, 5000); + events.ScheduleEvent(EVENT_SHOCK_BLAST, 18000); + break; + case DO_ASSEMBLED_COMBAT: + me->SetStandState(UNIT_STAND_STATE_STAND); + me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE); + me->SetReactState(REACT_AGGRESSIVE); + + events.SetPhase(PHASE_LEVIATHAN_ASSEMBLED); + events.ScheduleEvent(EVENT_PROXIMITY_MINE, 15000); + events.ScheduleEvent(EVENT_SHOCK_BLAST, 45000); + break; + default: + break; + } + } + + uint32 GetData(uint32 type) const override + { + switch (type) + { + case DATA_SETUP_MINE: + return _setupMine; + case DATA_SETUP_BOMB: + return _setupBomb; + case DATA_SETUP_ROCKET: + return _setupRocket; + case DATA_FIREFIGHTER: + return _fireFighter; + default: + return 0; + } + } + + void JustDied(Unit* /*who*/) override + { + _JustDied(); + me->SetStandState(UNIT_STAND_STATE_DEAD); + } + + void JustSummoned(Creature* summon) override + { + summons.Summon(summon); + } + + void KilledUnit(Unit* victim) override + { + if (victim->GetTypeId() == TYPEID_PLAYER) + if (Creature* mimiron = ObjectAccessor::GetCreature(*me, instance->GetData64(BOSS_MIMIRON))) + if (mimiron->AI()) + mimiron->AI()->Talk(events.IsInPhase(PHASE_LEVIATHAN_SOLO) ? SAY_MKII_SLAY : SAY_V07TRON_SLAY); + } + + void MovementInform(uint32 type, uint32 point) override + { + if (type != POINT_MOTION_TYPE) + return; + + switch (point) + { + case WP_MKII_P1_IDLE: + me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + DoCast(me, SPELL_HALF_HEAL); + + if (Creature* mimiron = ObjectAccessor::GetCreature(*me, instance->GetData64(BOSS_MIMIRON))) + mimiron->AI()->DoAction(DO_ACTIVATE_VX001); + break; + case WP_MKII_P4_POS_1: + me->GetMotionMaster()->MovePoint(WP_MKII_P4_POS_2, pos[WP_MKII_P4_POS_2]); + break; + case WP_MKII_P4_POS_2: + me->GetMotionMaster()->MovePoint(WP_MKII_P4_POS_3, pos[WP_MKII_P4_POS_3]); + break; + case WP_MKII_P4_POS_3: + if (Creature* mimiron = ObjectAccessor::GetCreature(*me, instance->GetData64(BOSS_MIMIRON))) + mimiron->AI()->DoAction(DO_ACTIVATE_V0L7R0N_2); + break; + case WP_MKII_P4_POS_4: + me->GetMotionMaster()->Clear(); + me->GetMotionMaster()->MovePoint(WP_MKII_P4_POS_5, pos[WP_MKII_P4_POS_5]); + break; + default: + break; + } + } + + void Reset() override + { + _Reset(); + me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE); + me->SetReactState(REACT_PASSIVE); + _fireFighter = false; + _setupMine = false; + _setupBomb = false; + _setupRocket = false; + DoCast(me, SPELL_FREEZE_ANIM); + } + + void SetData(uint32 id, uint32 data) override + { + switch (id) + { + case DATA_SETUP_MINE: + _setupMine = data; + break; + case DATA_SETUP_BOMB: + _setupBomb = data; + break; + case DATA_SETUP_ROCKET: + _setupRocket = data; + break; + case DATA_FIREFIGHTER: + _fireFighter = data; + break; + default: + break; + } + } + + void UpdateAI(uint32 diff) override + { + if (!UpdateVictim() || !CheckInRoom()) + return; + + events.Update(diff); + + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; + + while (uint32 eventId = events.ExecuteEvent()) + { + switch (eventId) + { + case EVENT_PROXIMITY_MINE: + DoCastAOE(SPELL_PROXIMITY_MINES); + events.RescheduleEvent(EVENT_PROXIMITY_MINE, 35000); + break; + case EVENT_PLASMA_BLAST: + DoCastVictim(SPELL_SCRIPT_EFFECT_PLASMA_BLAST); + events.RescheduleEvent(EVENT_PLASMA_BLAST, urand(30000, 45000), 0, PHASE_LEVIATHAN_SOLO); + + if (events.GetTimeUntilEvent(EVENT_NAPALM_SHELL) < 9000) + events.RescheduleEvent(EVENT_NAPALM_SHELL, 9000, 0, PHASE_LEVIATHAN_SOLO); // The actual spell is cast by the turret, we should not let it interrupt itself. + break; + case EVENT_SHOCK_BLAST: + DoCastAOE(SPELL_SHOCK_BLAST); + events.RescheduleEvent(EVENT_SHOCK_BLAST, urand(34000, 36000)); + break; + case EVENT_FLAME_SUPPRESSANT_MK: + DoCastAOE(SPELL_FLAME_SUPPRESSANT_MK); + events.RescheduleEvent(EVENT_FLAME_SUPPRESSANT_MK, 60000, 0, PHASE_LEVIATHAN_SOLO); + break; + case EVENT_NAPALM_SHELL: + DoCastAOE(SPELL_FORCE_CAST_NAPALM_SHELL); + events.RescheduleEvent(EVENT_NAPALM_SHELL, urand(6000, 15000), 0, PHASE_LEVIATHAN_SOLO); + + if (events.GetTimeUntilEvent(EVENT_PLASMA_BLAST) < 2000) + events.RescheduleEvent(EVENT_PLASMA_BLAST, 2000, 0, PHASE_LEVIATHAN_SOLO); // The actual spell is cast by the turret, we should not let it interrupt itself. + break; + default: + break; + } + } + DoMeleeAttackIfReady(); + } + + private: + bool _fireFighter; + bool _setupMine; + bool _setupBomb; + bool _setupRocket; + }; + + CreatureAI* GetAI(Creature* creature) const override + { + return GetUlduarAI(creature); + } +}; + +class boss_vx_001 : public CreatureScript +{ + public: + boss_vx_001() : CreatureScript("boss_vx_001") { } + + struct boss_vx_001AI : public BossAI + { + boss_vx_001AI(Creature* creature) : BossAI(creature, BOSS_MIMIRON) + { + me->SetDisableGravity(true); // This is the unfold visual state of VX-001, it has to be set on create as it requires an objectupdate if set later. + me->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_STATE_SPECIAL_UNARMED); // This is a hack to force the yet to be unfolded visual state. + me->SetReactState(REACT_PASSIVE); + DoCast(me, SPELL_FREEZE_ANIM); + _fireFighter = false; + } + + void DamageTaken(Unit* who, uint32 &damage) override + { + if (damage >= me->GetHealth()) + { + damage = me->GetHealth() - 1; // Let creature fall to 1 hp, but do not let it die or damage itself with SetHealth(). + me->AttackStop(); + me->SetUInt64Value(UNIT_FIELD_TARGET, NULL); // This should be removed when channel track target implementation is properly functioning. + DoCast(me, SPELL_VEHICLE_DAMAGED, true); + me->RemoveAllAurasExceptType(SPELL_AURA_CONTROL_VEHICLE, SPELL_AURA_MOD_INCREASE_HEALTH_PERCENT); + + if (events.IsInPhase(PHASE_VX001_SOLO)) + { + me->CastStop(); + me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE); + DoCast(me, SPELL_HALF_HEAL); + DoCast(me, SPELL_TORSO_DISABLED); + if (Creature* mimiron = ObjectAccessor::GetCreature(*me, instance->GetData64(BOSS_MIMIRON))) + mimiron->AI()->DoAction(DO_ACTIVATE_AERIAL); + } + else if (events.IsInPhase(PHASE_VX001_ASSEMBLED)) + { + me->SetStandState(UNIT_STAND_STATE_DEAD); + me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); + + if (EncounterDone(who, me).IsEncounterFinished()) + return; + + me->CastStop(); + DoCast(me, SPELL_SELF_REPAIR); + } + events.Reset(); + } + } + + void DoAction(int32 action) override + { + switch (action) + { + case DO_HARDMODE_VX001: + _fireFighter = true; + DoCast(me, SPELL_EMERGENCY_MODE); + events.ScheduleEvent(EVENT_FROST_BOMB, 1000); + events.ScheduleEvent(EVENT_FLAME_SUPPRESSANT_VX, 6000); + // Missing break intended. + case DO_START_VX001: + me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE); + me->RemoveAurasDueToSpell(SPELL_FREEZE_ANIM); + me->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_ONESHOT_NONE); // Remove emotestate. + //me->SetUInt32Value(UNIT_FIELD_BYTES_1, 33554432); Blizzard handles hover animation like this it seems. + DoCast(me, SPELL_HEAT_WAVE_AURA); + + events.SetPhase(PHASE_VX001_SOLO); + events.ScheduleEvent(EVENT_ROCKET_STRIKE, 20000); + events.ScheduleEvent(EVENT_SPINNING_UP, urand(30000, 35000)); + events.ScheduleEvent(EVENT_RAPID_BURST, 500, 0, PHASE_VX001_SOLO); + break; + case DO_ASSEMBLED_COMBAT: + me->SetStandState(UNIT_STAND_STATE_STAND); + me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE); + + events.SetPhase(PHASE_VX001_ASSEMBLED); + events.ScheduleEvent(EVENT_ROCKET_STRIKE, 20000); + events.ScheduleEvent(EVENT_SPINNING_UP, urand(30000, 35000)); + events.ScheduleEvent(EVENT_HAND_PULSE, 500, 0, PHASE_VX001_ASSEMBLED); + if (_fireFighter) + events.ScheduleEvent(EVENT_FROST_BOMB, 1000); + break; + default: + break; + } + } + + void EnterEvadeMode() override + { + summons.DespawnAll(); + } + + void JustSummoned(Creature* summon) override + { + summons.Summon(summon); + if (summon->GetEntry() == NPC_BURST_TARGET) + summon->CastSpell(me, SPELL_RAPID_BURST_TARGET_ME); + } + + void KilledUnit(Unit* victim) override + { + if (victim->GetTypeId() == TYPEID_PLAYER) + if (Creature* mimiron = ObjectAccessor::GetCreature(*me, instance->GetData64(BOSS_MIMIRON))) + if (mimiron->AI()) + mimiron->AI()->Talk(events.IsInPhase(PHASE_VX001_SOLO) ? SAY_VX001_SLAY : SAY_V07TRON_SLAY); + } + + void SpellHit(Unit* caster, SpellInfo const* /*spellProto*/) override + { + if (caster->GetEntry() == NPC_BURST_TARGET && !me->HasUnitState(UNIT_STATE_CASTING)) + DoCast(caster, SPELL_RAPID_BURST); + } + + void UpdateAI(uint32 diff) override + { + if (!UpdateVictim()) + return; + + events.Update(diff); + + // Handle rotation during SPELL_SPINNING_UP, SPELL_P3WX2_LASER_BARRAGE, SPELL_RAPID_BURST, and SPELL_HAND_PULSE_LEFT/RIGHT + if (me->HasUnitState(UNIT_STATE_CASTING)) + { + if (Creature* channelTarget = ObjectAccessor::GetCreature(*me, me->GetUInt64Value(UNIT_FIELD_CHANNEL_OBJECT))) + me->SetFacingToObject(channelTarget); + return; + } + + while (uint32 eventId = events.ExecuteEvent()) + { + switch (eventId) + { + case EVENT_RAPID_BURST: + if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 120, true)) + DoCast(target, SPELL_SUMMON_BURST_TARGET); + events.RescheduleEvent(EVENT_RAPID_BURST, 3000, 0, PHASE_VX001_SOLO); + break; + case EVENT_ROCKET_STRIKE: + DoCastAOE(events.IsInPhase(PHASE_VX001_SOLO) ? SPELL_ROCKET_STRIKE_LEFT : SPELL_ROCKET_STRIKE_BOTH); + events.ScheduleEvent(EVENT_RELOAD, 10000); + events.RescheduleEvent(EVENT_ROCKET_STRIKE, urand(20000, 25000)); + break; + case EVENT_RELOAD: + for (uint8 seat = 6; seat <= 7; seat++) + if (Unit* rocket = me->GetVehicleKit()->GetPassenger(seat)) + rocket->SetDisplayId(rocket->GetNativeDisplayId()); + break; + case EVENT_HAND_PULSE: + if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 120, true)) + DoCast(target, urand(0,1) == 0 ? SPELL_HAND_PULSE_LEFT : SPELL_HAND_PULSE_RIGHT); + events.RescheduleEvent(EVENT_HAND_PULSE, urand(1500, 3000), 0, PHASE_VX001_ASSEMBLED); + break; + case EVENT_FROST_BOMB: + DoCastAOE(SPELL_SCRIPT_EFFECT_FROST_BOMB); + events.RescheduleEvent(EVENT_FROST_BOMB, 45000); + break; + case EVENT_SPINNING_UP: + DoCastAOE(SPELL_SPINNING_UP); + events.DelayEvents(14000); + events.RescheduleEvent(EVENT_SPINNING_UP, urand(55000, 65000)); + break; + case EVENT_FLAME_SUPPRESSANT_VX: + DoCastAOE(SPELL_FLAME_SUPPRESSANT_VX); + events.RescheduleEvent(EVENT_FLAME_SUPPRESSANT_VX, urand(10000, 12000), 0, PHASE_VX001_SOLO); + break; + default: + break; + } + } + } + + private: + bool _fireFighter; + }; + + CreatureAI* GetAI(Creature* creature) const override + { + return GetUlduarAI(creature); + } +}; + +class boss_aerial_command_unit : public CreatureScript +{ + public: + boss_aerial_command_unit() : CreatureScript("boss_aerial_command_unit") { } + + struct boss_aerial_command_unitAI : public BossAI + { + boss_aerial_command_unitAI(Creature* creature) : BossAI(creature, BOSS_MIMIRON) + { + //me->SetDisableGravity(true); + me->SetReactState(REACT_PASSIVE); + fireFigther = false; + } + + void DamageTaken(Unit* who, uint32 &damage) override + { + if (damage >= me->GetHealth()) + { + damage = me->GetHealth() - 1; // Let creature fall to 1 hp, but do not let it die or damage itself with SetHealth(). + me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); + me->AttackStop(); + me->SetReactState(REACT_PASSIVE); + DoCast(me, SPELL_VEHICLE_DAMAGED, true); + me->RemoveAllAurasExceptType(SPELL_AURA_CONTROL_VEHICLE, SPELL_AURA_MOD_INCREASE_HEALTH_PERCENT); + + if (events.IsInPhase(PHASE_AERIAL_SOLO)) + { + me->GetMotionMaster()->Clear(true); + me->GetMotionMaster()->MovePoint(WP_AERIAL_P4_POS, pos[WP_AERIAL_P4_POS]); + } + else if (events.IsInPhase(PHASE_AERIAL_ASSEMBLED)) + { + me->SetStandState(UNIT_STAND_STATE_DEAD); + + if (EncounterDone(who, me).IsEncounterFinished()) + return; + + me->CastStop(); + DoCast(me, SPELL_SELF_REPAIR); + } + events.Reset(); + } + } + + void DoAction(int32 action) override + { + switch (action) + { + case DO_HARDMODE_AERIAL: + fireFigther = true; + DoCast(me, SPELL_EMERGENCY_MODE); + events.ScheduleEvent(EVENT_SUMMON_FIRE_BOTS, 1000, 0, PHASE_AERIAL_SOLO); + // Missing break intended. + case DO_START_AERIAL: + //me->SetDisableGravity(false); + //me->SetHover(true); + me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE); + me->SetReactState(REACT_AGGRESSIVE); + + events.SetPhase(PHASE_AERIAL_SOLO); + events.ScheduleEvent(EVENT_SUMMON_JUNK_BOT, 5000, 0, PHASE_AERIAL_SOLO); + events.ScheduleEvent(EVENT_SUMMON_ASSAULT_BOT, 9000, 0, PHASE_AERIAL_SOLO); + events.ScheduleEvent(EVENT_SUMMON_BOMB_BOT, 9000, 0, PHASE_AERIAL_SOLO); + break; + case DO_DISABLE_AERIAL: + //me->SetHover(false); + me->CastStop(); + me->AttackStop(); + me->SetReactState(REACT_PASSIVE); + me->GetMotionMaster()->MoveFall(); + events.DelayEvents(23000); + break; + case DO_ENABLE_AERIAL: + //me->SetHover(true); + me->SetReactState(REACT_AGGRESSIVE); + break; + case DO_ASSEMBLED_COMBAT: + me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE); + me->SetReactState(REACT_AGGRESSIVE); + me->SetStandState(UNIT_STAND_STATE_STAND); + events.SetPhase(PHASE_AERIAL_ASSEMBLED); + break; + default: + break; + } + } + + void EnterEvadeMode() override + { + summons.DespawnAll(); + } + + void JustSummoned(Creature* summon) override + { + if (fireFigther && (summon->GetEntry() == NPC_ASSAULT_BOT || summon->GetEntry() == NPC_JUNK_BOT)) + summon->AddAura(SPELL_EMERGENCY_MODE, summon); + BossAI::JustSummoned(summon); + } + + void KilledUnit(Unit* victim) override + { + if (victim->GetTypeId() == TYPEID_PLAYER) + if (Creature* mimiron = ObjectAccessor::GetCreature(*me, instance->GetData64(BOSS_MIMIRON))) + if (mimiron->AI()) + mimiron->AI()->Talk(events.IsInPhase(PHASE_AERIAL_SOLO) ? SAY_AERIAL_SLAY : SAY_V07TRON_SLAY); + } + + void MovementInform(uint32 type, uint32 point) override + { + if (type == POINT_MOTION_TYPE && point == WP_AERIAL_P4_POS) + { + me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + + if (Creature* mimiron = ObjectAccessor::GetCreature(*me, instance->GetData64(BOSS_MIMIRON))) + mimiron->AI()->DoAction(DO_ACTIVATE_V0L7R0N_1); + } + } + + void UpdateAI(uint32 diff) override + { + if (!UpdateVictim()) + return; + + events.Update(diff); + + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; + + while (uint32 eventId = events.ExecuteEvent()) + { + switch (eventId) + { + case EVENT_SUMMON_FIRE_BOTS: + DoCastAOE(SPELL_SUMMON_FIRE_BOT_TRIGGER); + events.RescheduleEvent(EVENT_SUMMON_FIRE_BOTS, 45000, 0, PHASE_AERIAL_SOLO); + break; + case EVENT_SUMMON_JUNK_BOT: + DoCastAOE(SPELL_SUMMON_JUNK_BOT_TRIGGER); + events.RescheduleEvent(EVENT_SUMMON_JUNK_BOT, urand(11000, 12000), 0, PHASE_AERIAL_SOLO); + break; + case EVENT_SUMMON_ASSAULT_BOT: + DoCastAOE(SPELL_SUMMON_ASSAULT_BOT_TRIGGER); + events.RescheduleEvent(EVENT_SUMMON_ASSAULT_BOT, 30000, 0, PHASE_AERIAL_SOLO); + break; + case EVENT_SUMMON_BOMB_BOT: + DoCast(me, SPELL_SUMMON_BOMB_BOT); + events.RescheduleEvent(EVENT_SUMMON_BOMB_BOT, urand(15000, 20000), 0, PHASE_AERIAL_SOLO); + break; + default: + break; + } + } + DoSpellAttackIfReady(events.IsInPhase(PHASE_AERIAL_SOLO) ? SPELL_PLASMA_BALL_P1 : SPELL_PLASMA_BALL_P2); + } + + private: + bool fireFigther; + }; + + CreatureAI* GetAI(Creature* creature) const override + { + return GetUlduarAI(creature); + } +}; + +class npc_magnetic_core : public CreatureScript +{ + public: + npc_magnetic_core() : CreatureScript("npc_magnetic_core") { } + + struct npc_magnetic_coreAI : public ScriptedAI + { + npc_magnetic_coreAI(Creature* creature) : ScriptedAI(creature) + { + DoCast(me, SPELL_MAGNETIC_CORE); + me->DespawnOrUnsummon(25000); // Just incase handling in aurascript fails to despawn. + } + }; + + CreatureAI* GetAI(Creature* creature) const override + { + return GetUlduarAI(creature); + } +}; + +class npc_mimiron_assault_bot : public CreatureScript +{ + public: + npc_mimiron_assault_bot() : CreatureScript("npc_mimiron_assault_bot") { } + + struct npc_mimiron_assault_botAI : public ScriptedAI + { + npc_mimiron_assault_botAI(Creature* creature) : ScriptedAI(creature) + { + magneticTimer = 14000; + } + + uint32 magneticTimer; + + void UpdateAI(uint32 diff) override + { + if (me->HasUnitState(UNIT_STATE_ROOT)) + if (Unit* newTarget = SelectTarget(SELECT_TARGET_NEAREST, 0, 30.0f, true)) + { + me->DeleteThreatList(); + AttackStart(newTarget); + } + + if (magneticTimer <= diff) + { + DoCastVictim(SPELL_MAGNETIC_FIELD); + me->ClearUnitState(UNIT_STATE_CASTING); + magneticTimer = 30000; + } + else magneticTimer -= diff; + + DoMeleeAttackIfReady(); + } + }; + + CreatureAI* GetAI(Creature* creature) const override + { + return GetUlduarAI(creature); + } +}; + +class npc_mimiron_emergency_fire_bot : public CreatureScript +{ + public: + npc_mimiron_emergency_fire_bot() : CreatureScript("npc_mimiron_emergency_fire_bot") { } + + struct npc_mimiron_emergency_fire_botAI : public ScriptedAI + { + npc_mimiron_emergency_fire_botAI(Creature* creature) : ScriptedAI(creature) + { + me->SetReactState(REACT_PASSIVE); + watersprayTimer = 7000; + isWaterSprayReady = true; + moveNew = true; + } + + uint32 watersprayTimer; + bool isWaterSprayReady; + bool moveNew; + + uint32 GetData(uint32 id) const override + { + if (id == DATA_WATERSPRAY) + return isWaterSprayReady; + if (id == DATA_MOVE_NEW) + return moveNew; + return 0; + } + + void SetData(uint32 id, uint32 data) override + { + if (id == DATA_WATERSPRAY) + isWaterSprayReady = false; + else if (id == DATA_MOVE_NEW) + moveNew = data; + } + + void UpdateAI(uint32 diff) override + { + if (!isWaterSprayReady) + { + if (watersprayTimer <= diff) + { + isWaterSprayReady = true; + watersprayTimer = urand(7000, 9000); + } + else watersprayTimer -= diff; + } + } + }; + + CreatureAI* GetAI(Creature* creature) const override + { + return GetUlduarAI(creature); + } +}; + +class npc_mimiron_computer : public CreatureScript +{ + public: + npc_mimiron_computer() : CreatureScript("npc_mimiron_computer") { } + + struct npc_mimiron_computerAI : public ScriptedAI + { + npc_mimiron_computerAI(Creature* creature) : ScriptedAI(creature) + { + instance = me->GetInstanceScript(); + } + + InstanceScript* instance; + EventMap events; + + void DoAction(int32 action) override + { + switch (action) + { + case DO_ACTIVATE_COMPUTER: + Talk(SAY_SELF_DESTRUCT_INITIATED); + events.ScheduleEvent(EVENT_SELF_DESTRUCT_10, 3000); + break; + case DO_DEACTIVATE_COMPUTER: + Talk(SAY_SELF_DESTRUCT_TERMINATED); + me->RemoveAurasDueToSpell(SPELL_SELF_DESTRUCTION_AURA); + me->RemoveAurasDueToSpell(SPELL_SELF_DESTRUCTION_VISUAL); + events.Reset(); + break; + default: + break; + } + } + + void UpdateAI(uint32 diff) override + { + events.Update(diff); + + while (uint32 eventId = events.ExecuteEvent()) + { + switch (eventId) + { + case EVENT_SELF_DESTRUCT_10: + Talk(SAY_SELF_DESTRUCT_10); + if (Creature* mimiron = ObjectAccessor::GetCreature(*me, instance->GetData64(BOSS_MIMIRON))) + mimiron->AI()->DoAction(DO_ACTIVATE_HARD_MODE); + events.ScheduleEvent(EVENT_SELF_DESTRUCT_9, 60000); + break; + case EVENT_SELF_DESTRUCT_9: + Talk(SAY_SELF_DESTRUCT_9); + events.ScheduleEvent(EVENT_SELF_DESTRUCT_8, 60000); + break; + case EVENT_SELF_DESTRUCT_8: + Talk(SAY_SELF_DESTRUCT_8); + events.ScheduleEvent(EVENT_SELF_DESTRUCT_7, 60000); + break; + case EVENT_SELF_DESTRUCT_7: + Talk(SAY_SELF_DESTRUCT_7); + events.ScheduleEvent(EVENT_SELF_DESTRUCT_6, 60000); + break; + case EVENT_SELF_DESTRUCT_6: + Talk(SAY_SELF_DESTRUCT_6); + events.ScheduleEvent(EVENT_SELF_DESTRUCT_5, 60000); + break; + case EVENT_SELF_DESTRUCT_5: + Talk(SAY_SELF_DESTRUCT_5); + events.ScheduleEvent(EVENT_SELF_DESTRUCT_4, 60000); + break; + case EVENT_SELF_DESTRUCT_4: + Talk(SAY_SELF_DESTRUCT_4); + events.ScheduleEvent(EVENT_SELF_DESTRUCT_3, 60000); + break; + case EVENT_SELF_DESTRUCT_3: + Talk(SAY_SELF_DESTRUCT_3); + events.ScheduleEvent(EVENT_SELF_DESTRUCT_2, 60000); + break; + case EVENT_SELF_DESTRUCT_2: + Talk(SAY_SELF_DESTRUCT_2); + events.ScheduleEvent(EVENT_SELF_DESTRUCT_1, 60000); + break; + case EVENT_SELF_DESTRUCT_1: + Talk(SAY_SELF_DESTRUCT_1); + events.ScheduleEvent(EVENT_SELF_DESTRUCT_FINALIZED, 60000); + break; + case EVENT_SELF_DESTRUCT_FINALIZED: + Talk(SAY_SELF_DESTRUCT_FINALIZED); + if (Creature* mimiron = ObjectAccessor::GetCreature(*me, instance->GetData64(BOSS_MIMIRON))) + mimiron->AI()->DoAction(DO_ACTIVATE_SELF_DESTRUCT); + DoCast(me, SPELL_SELF_DESTRUCTION_AURA); + DoCast(me, SPELL_SELF_DESTRUCTION_VISUAL); + break; + default: + break; + } + } + } + }; + + CreatureAI* GetAI(Creature* creature) const override + { + return GetUlduarAI(creature); + } +}; + +class npc_mimiron_flames : public CreatureScript +{ + public: + npc_mimiron_flames() : CreatureScript("npc_mimiron_flames") { } + + struct npc_mimiron_flamesAI : public ScriptedAI + { + npc_mimiron_flamesAI(Creature* creature) : ScriptedAI(creature) + { + instance = me->GetInstanceScript(); + timer = 4000; + spawnedFlames = false; + } + + uint32 timer; + bool spawnedFlames; + InstanceScript* instance; + + void UpdateAI(uint32 diff) override + { + if (instance->GetBossState(BOSS_MIMIRON) != IN_PROGRESS) + me->DespawnOrUnsummon(); + if (!spawnedFlames) + { + if (timer <= diff) + { + DoCastAOE(SPELL_SUMMON_FLAMES_SPREAD_TRIGGER); + spawnedFlames = true; + } + else timer -= diff; + } + } + }; + + CreatureAI* GetAI(Creature* creature) const override + { + return GetUlduarAI(creature); + } +}; + +class npc_mimiron_frost_bomb : public CreatureScript +{ + public: + npc_mimiron_frost_bomb() : CreatureScript("npc_mimiron_frost_bomb") { } + + struct npc_mimiron_frost_bombAI : public ScriptedAI + { + npc_mimiron_frost_bombAI(Creature* creature) : ScriptedAI(creature) + { + timer = 10000; + exploded = false; + } + + uint32 timer; + bool exploded; + + void UpdateAI(uint32 diff) override + { + if (timer <= diff) + { + if (!exploded) + { + DoCastAOE(SPELL_FROST_BOMB_EXPLOSION); + timer = 3000; + exploded = true; + } + else + { + DoCastAOE(SPELL_CLEAR_FIRES); + me->DespawnOrUnsummon(3000); + timer = 10000; + } + } + else timer -= diff; + } + }; + + CreatureAI* GetAI(Creature* creature) const override + { + return GetUlduarAI(creature); + } +}; + +class npc_mimiron_proximity_mine : public CreatureScript +{ + public: + npc_mimiron_proximity_mine() : CreatureScript("npc_mimiron_proximity_mine") { } + + struct npc_mimiron_proximity_mineAI : public ScriptedAI + { + npc_mimiron_proximity_mineAI(Creature* creature) : ScriptedAI(creature) + { + explosionTimer = 35000; + boom = false; + armed = false; + } + + uint32 explosionTimer; + bool boom; + bool armed; + + void UpdateAI(uint32 diff) override + { + if (!boom) + { + if (explosionTimer <= 33500 && !armed) + { + DoCast(me, SPELL_PROXIMITY_MINE_PERIODIC_TRIGGER); + armed = true; + } + if (explosionTimer <= diff) + { + if (me->HasAura(SPELL_PROXIMITY_MINE_PERIODIC_TRIGGER)) + DoCastAOE(SPELL_PROXIMITY_MINE_EXPLOSION); + boom = true; + me->DespawnOrUnsummon(1000); + } + else explosionTimer -= diff; + } + } + }; + + CreatureAI* GetAI(Creature* creature) const override + { + return GetUlduarAI(creature); + } +}; + +class go_mimiron_hardmode_button : public GameObjectScript +{ +public: + go_mimiron_hardmode_button() : GameObjectScript("go_mimiron_hardmode_button") { } + + bool OnGossipHello(Player* /*player*/, GameObject* go) + { + InstanceScript* instance = go->GetInstanceScript(); + + if (!instance) + return false; + + if (Creature* computer = ObjectAccessor::GetCreature(*go, instance->GetData64(DATA_COMPUTER))) + computer->AI()->DoAction(DO_ACTIVATE_COMPUTER); + go->SetGoState(GO_STATE_ACTIVE); + go->SetFlag(GAMEOBJECT_FLAGS,GO_FLAG_NOT_SELECTABLE); + return true; + } +}; + +// 63801 Bomb Bot +class spell_mimiron_bomb_bot : public SpellScriptLoader +{ + public: + spell_mimiron_bomb_bot() : SpellScriptLoader("spell_mimiron_bomb_bot") { } + + class spell_mimiron_bomb_bot_SpellScript : public SpellScript + { + PrepareSpellScript(spell_mimiron_bomb_bot_SpellScript); + + void HandleScript(SpellEffIndex /*effIndex*/) + { + if (GetHitPlayer()) + if (InstanceScript* instance = GetCaster()->GetInstanceScript()) + if (Creature* mkii = ObjectAccessor::GetCreature(*GetCaster(), instance->GetData64(DATA_LEVIATHAN_MK_II))) + mkii->AI()->SetData(DATA_SETUP_BOMB, 0); + } + + void HandleDespawn(SpellEffIndex /*effIndex*/) + { + if (GetHitUnit()->IsSummon()) + { + GetHitUnit()->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_PACIFIED); + GetHitUnit()->ToTempSummon()->DespawnOrUnsummon(1000); + } } void Register() override { - OnEffectHitTarget += SpellEffectFn(spell_ulduar_proximity_minesSpellScript::HandleScript, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT); + OnEffectHitTarget += SpellEffectFn(spell_mimiron_bomb_bot_SpellScript::HandleScript, EFFECT_0, SPELL_EFFECT_SCHOOL_DAMAGE); + OnEffectHitTarget += SpellEffectFn(spell_mimiron_bomb_bot_SpellScript::HandleDespawn, EFFECT_1, SPELL_EFFECT_APPLY_AURA); } }; SpellScript* GetSpellScript() const override { - return new spell_ulduar_proximity_minesSpellScript(); + return new spell_mimiron_bomb_bot_SpellScript(); + } +}; + +// 65192 - Flame Suppressant, 65224 - Clear Fires, 65354 - Clear Fires, 64619 - Water Spray +class spell_mimiron_clear_fires : public SpellScriptLoader +{ + public: + spell_mimiron_clear_fires() : SpellScriptLoader("spell_mimiron_clear_fires") { } + + class spell_mimiron_clear_fires_SpellScript : public SpellScript + { + PrepareSpellScript(spell_mimiron_clear_fires_SpellScript); + + void HandleDummy(SpellEffIndex /*effIndex*/) + { + if (GetHitUnit()->IsSummon()) + GetHitUnit()->ToTempSummon()->DespawnOrUnsummon(); + } + + void Register() override + { + OnEffectHitTarget += SpellEffectFn(spell_mimiron_clear_fires_SpellScript::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY); + } + }; + + SpellScript* GetSpellScript() const override + { + return new spell_mimiron_clear_fires_SpellScript(); + } +}; + +// 64463 - Despawn Assault Bots +class spell_mimiron_despawn_assault_bots : public SpellScriptLoader +{ + public: + spell_mimiron_despawn_assault_bots() : SpellScriptLoader("spell_mimiron_despawn_assault_bots") { } + + class spell_mimiron_despawn_assault_bots_SpellScript : public SpellScript + { + PrepareSpellScript(spell_mimiron_despawn_assault_bots_SpellScript); + + void HandleScript(SpellEffIndex /*effIndex*/) + { + if (GetHitUnit()->IsSummon()) + GetHitUnit()->ToTempSummon()->DespawnOrUnsummon(); + } + + void Register() override + { + OnEffectHitTarget += SpellEffectFn(spell_mimiron_despawn_assault_bots_SpellScript::HandleScript, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT); + } + }; + + SpellScript* GetSpellScript() const override + { + return new spell_mimiron_despawn_assault_bots_SpellScript(); + } +}; + +// 64626 - Explosion +class spell_mimiron_frost_bomb_explosion : public SpellScriptLoader +{ + public: + spell_mimiron_frost_bomb_explosion() : SpellScriptLoader("spell_mimiron_frost_bomb_explosion") { } + + class spell_mimiron_frost_bomb_explosion_SpellScript : public SpellScript + { + PrepareSpellScript(spell_mimiron_frost_bomb_explosion_SpellScript); + + void HandleDummy(SpellEffIndex /*effIndex*/) + { + GetHitUnit()->RemoveAurasDueToSpell(SPELL_FLAMES_PERIODIC_TRIGGER); + } + + void HandleUnaura(SpellEffIndex /*effIndex*/) + { + GetCaster()->RemoveAurasDueToSpell(SPELL_FROST_BOMB_LINKED); + } + + void Register() override + { + OnEffectHit += SpellEffectFn(spell_mimiron_frost_bomb_explosion_SpellScript::HandleUnaura, EFFECT_2, SPELL_EFFECT_DUMMY); + OnEffectHitTarget += SpellEffectFn(spell_mimiron_frost_bomb_explosion_SpellScript::HandleDummy, EFFECT_2, SPELL_EFFECT_DUMMY); + } + }; + + SpellScript* GetSpellScript() const override + { + return new spell_mimiron_frost_bomb_explosion_SpellScript(); + } +}; + +// 64618 - Fire Search +class spell_mimiron_fire_search : public SpellScriptLoader +{ + public: + spell_mimiron_fire_search() : SpellScriptLoader("spell_mimiron_fire_search") { } + + class spell_mimiron_fire_search_SpellScript : public SpellScript + { + PrepareSpellScript(spell_mimiron_fire_search_SpellScript); + + bool Validate(SpellInfo const* /*spell*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_WATER_SPRAY)) + return false; + return true; + } + + void FilterTargets(std::list& targets) + { + if (targets.empty()) + { + GetCaster()->GetMotionMaster()->MoveRandom(15.0f); + return; + } + + WorldObject* target = Trinity::Containers::SelectRandomContainerElement(targets); + + targets.clear(); + targets.push_back(target); + } + + void HandleScript(SpellEffIndex /*effIndex*/) + { + Unit* caster = GetCaster(); + + if (caster->GetAI()) + { + if (caster->GetDistance2d(GetHitUnit()) <= 15.0f && caster->GetAI()->GetData(DATA_WATERSPRAY)) + { + caster->CastSpell(GetHitUnit(), SPELL_WATER_SPRAY, true); + caster->GetAI()->SetData(DATA_WATERSPRAY, 0); + caster->GetAI()->SetData(DATA_MOVE_NEW, 1); + } + else if (caster->GetAI()->GetData(DATA_MOVE_NEW)) + { + caster->GetMotionMaster()->MoveChase(GetHitUnit()); + caster->GetAI()->SetData(DATA_MOVE_NEW, 0); + } + } + } + + void Register() override + { + OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_mimiron_fire_search_SpellScript::FilterTargets, EFFECT_0, TARGET_UNIT_SRC_AREA_ENTRY); + OnEffectHitTarget += SpellEffectFn(spell_mimiron_fire_search_SpellScript::HandleScript, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT); + } + }; + + SpellScript* GetSpellScript() const override + { + return new spell_mimiron_fire_search_SpellScript(); + } +}; + +// 64570 - Flame Suppressant +class spell_mimiron_flame_suppressant : public SpellScriptLoader +{ + public: + spell_mimiron_flame_suppressant() : SpellScriptLoader("spell_mimiron_flame_suppressant") { } + + class spell_mimiron_flame_suppressant_SpellScript : public SpellScript + { + PrepareSpellScript(spell_mimiron_flame_suppressant_SpellScript); + + void HandleDummy(SpellEffIndex /*effIndex*/) + { + GetHitUnit()->RemoveAurasDueToSpell(SPELL_FLAMES_PERIODIC_TRIGGER); + } + + void Register() override + { + OnEffectHitTarget += SpellEffectFn(spell_mimiron_flame_suppressant_SpellScript::HandleDummy, EFFECT_1, SPELL_EFFECT_DUMMY); + } + }; + + SpellScript* GetSpellScript() const override + { + return new spell_mimiron_flame_suppressant_SpellScript(); + } +}; + +// 64436 - Magnetic Core +class spell_mimiron_magnetic_core : public SpellScriptLoader +{ + public: + spell_mimiron_magnetic_core() : SpellScriptLoader("spell_mimiron_magnetic_core") { } + + class spell_mimiron_magnetic_core_SpellScript : public SpellScript + { + PrepareSpellScript(spell_mimiron_magnetic_core_SpellScript); + + void FilterTargets(std::list& targets) + { + targets.remove_if(AerialTargetSelector()); + } + + void Register() override + { + OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_mimiron_magnetic_core_SpellScript::FilterTargets, EFFECT_1, TARGET_UNIT_SRC_AREA_ENTRY); + } + }; + + SpellScript* GetSpellScript() const override + { + return new spell_mimiron_magnetic_core_SpellScript(); + } + + class spell_mimiron_magnetic_core_AuraScript : public AuraScript + { + PrepareAuraScript(spell_mimiron_magnetic_core_AuraScript); + + bool Validate(SpellInfo const* /*spell*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_MAGNETIC_CORE_VISUAL)) + return false; + return true; + } + + void OnApply(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) + { + Unit* target = GetTarget(); + if (target->GetAI()) + target->GetAI()->DoAction(DO_DISABLE_AERIAL); + + target->CastSpell(target, SPELL_MAGNETIC_CORE_VISUAL, true); + } + + void OnRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) + { + Unit* target = GetTarget(); + if (target->GetAI()) + target->GetAI()->DoAction(DO_ENABLE_AERIAL); + + target->RemoveAurasDueToSpell(SPELL_MAGNETIC_CORE_VISUAL); + } + + void OnRemoveSelf(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) + { + if (GetTarget()->ToCreature()) + GetTarget()->ToCreature()->DespawnOrUnsummon(); + } + + void Register() override + { + AfterEffectApply += AuraEffectApplyFn(spell_mimiron_magnetic_core_AuraScript::OnApply, EFFECT_1, SPELL_AURA_MOD_DAMAGE_PERCENT_TAKEN, AURA_EFFECT_HANDLE_REAL); + AfterEffectRemove += AuraEffectRemoveFn(spell_mimiron_magnetic_core_AuraScript::OnRemove, EFFECT_1, SPELL_AURA_MOD_DAMAGE_PERCENT_TAKEN, AURA_EFFECT_HANDLE_REAL); + AfterEffectRemove += AuraEffectRemoveFn(spell_mimiron_magnetic_core_AuraScript::OnRemoveSelf, EFFECT_0, SPELL_AURA_DUMMY, AURA_EFFECT_HANDLE_REAL); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_mimiron_magnetic_core_AuraScript(); + } +}; + +// 63667 - Napalm Shell +class spell_mimiron_napalm_shell : public SpellScriptLoader +{ + public: + spell_mimiron_napalm_shell() : SpellScriptLoader("spell_mimiron_napalm_shell") { } + + class spell_mimiron_napalm_shell_SpellScript : public SpellScript + { + PrepareSpellScript(spell_mimiron_napalm_shell_SpellScript); + + bool Validate(SpellInfo const* /*spell*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_NAPALM_SHELL)) + return false; + return true; + } + + void FilterTargets(std::list& targets) + { + if (targets.empty()) + return; + + WorldObject* target = Trinity::Containers::SelectRandomContainerElement(targets); + + targets.remove_if(Trinity::AllWorldObjectsInRange(GetCaster(), 15.0f)); + + if (!targets.empty()) + target = Trinity::Containers::SelectRandomContainerElement(targets); + + targets.clear(); + targets.push_back(target); + } + + void HandleScript(SpellEffIndex /*effIndex*/) + { + GetCaster()->CastSpell(GetHitUnit(), SPELL_NAPALM_SHELL); + } + + void Register() override + { + OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_mimiron_napalm_shell_SpellScript::FilterTargets, EFFECT_0, TARGET_UNIT_SRC_AREA_ENEMY); + OnEffectHitTarget += SpellEffectFn(spell_mimiron_napalm_shell_SpellScript::HandleScript, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT); + } + }; + + SpellScript* GetSpellScript() const override + { + return new spell_mimiron_napalm_shell_SpellScript(); + } +}; + +// 63274 - P3Wx2 Laser Barrage -- This should be removed when channel track target implementation is properly functioning. +class spell_mimiron_p3wx2_laser_barrage : public SpellScriptLoader +{ + public: + spell_mimiron_p3wx2_laser_barrage() : SpellScriptLoader("spell_mimiron_p3wx2_laser_barrage") { } + + class spell_mimiron_p3wx2_laser_barrage_SpellScript : public SpellScript + { + PrepareSpellScript(spell_mimiron_p3wx2_laser_barrage_SpellScript); + + void OnHit(SpellEffIndex /*effIndex*/) + { + GetCaster()->SetUInt64Value(UNIT_FIELD_CHANNEL_OBJECT, GetHitUnit()->GetGUID()); + } + + void Register() override + { + OnEffectHitTarget += SpellEffectFn(spell_mimiron_p3wx2_laser_barrage_SpellScript::OnHit, EFFECT_0, SPELL_EFFECT_APPLY_AURA); + } + }; + + SpellScript* GetSpellScript() const override + { + return new spell_mimiron_p3wx2_laser_barrage_SpellScript(); + } +}; + +// 64542 - Plasma Blast +class spell_mimiron_plasma_blast : public SpellScriptLoader +{ + public: + spell_mimiron_plasma_blast() : SpellScriptLoader("spell_mimiron_plasma_blast") { } + + class spell_mimiron_plasma_blast_SpellScript : public SpellScript + { + PrepareSpellScript(spell_mimiron_plasma_blast_SpellScript); + + bool Validate(SpellInfo const* /*spell*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_PLASMA_BLAST)) + return false; + return true; + } + + bool Load() override + { + return GetCaster()->GetVehicleKit(); + } + + void HandleScript(SpellEffIndex /*effIndex*/) + { + if (Unit* caster = GetCaster()->GetVehicleKit()->GetPassenger(3)) + caster->CastSpell(GetHitUnit(), SPELL_PLASMA_BLAST); + } + + void Register() override + { + OnEffectHitTarget += SpellEffectFn(spell_mimiron_plasma_blast_SpellScript::HandleScript, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT); + } + }; + + SpellScript* GetSpellScript() const override + { + return new spell_mimiron_plasma_blast_SpellScript(); + } +}; + +// 66351 - Explosion +class spell_mimiron_proximity_explosion : public SpellScriptLoader +{ + public: + spell_mimiron_proximity_explosion() : SpellScriptLoader("spell_mimiron_proximity_explosion") { } + + class spell_mimiron_proximity_explosion_SpellScript : public SpellScript + { + PrepareSpellScript(spell_mimiron_proximity_explosion_SpellScript); + + void OnHit(SpellEffIndex /*effIndex*/) + { + if (GetHitPlayer()) + if (InstanceScript* instance = GetCaster()->GetInstanceScript()) + if (Creature* mkII = ObjectAccessor::GetCreature(*GetCaster(), instance->GetData64(DATA_LEVIATHAN_MK_II))) + mkII->AI()->SetData(DATA_SETUP_MINE, 0); + } + + void HandleAura(SpellEffIndex /*effIndex*/) + { + GetCaster()->RemoveAurasDueToSpell(SPELL_PROXIMITY_MINE_PERIODIC_TRIGGER); + } + + void Register() override + { + OnEffectHitTarget += SpellEffectFn(spell_mimiron_proximity_explosion_SpellScript::OnHit, EFFECT_0, SPELL_EFFECT_SCHOOL_DAMAGE); + OnEffectHitTarget += SpellEffectFn(spell_mimiron_proximity_explosion_SpellScript::HandleAura, EFFECT_1, SPELL_EFFECT_APPLY_AURA); + } + }; + + SpellScript* GetSpellScript() const override + { + return new spell_mimiron_proximity_explosion_SpellScript(); + } +}; + +// 63027 - Proximity Mines +class spell_mimiron_proximity_mines : public SpellScriptLoader +{ + public: + spell_mimiron_proximity_mines() : SpellScriptLoader("spell_mimiron_proximity_mines") { } + + class spell_mimiron_proximity_mines_SpellScript : public SpellScript + { + PrepareSpellScript(spell_mimiron_proximity_mines_SpellScript); + + bool Validate(SpellInfo const* /*spell*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_SUMMON_PROXIMITY_MINE)) + return false; + return true; + } + + void HandleScript(SpellEffIndex /*effIndex*/) + { + for (uint8 i = 0; i < 10; ++i) + GetCaster()->CastSpell(GetCaster(), SPELL_SUMMON_PROXIMITY_MINE, true); + } + + void Register() override + { + OnEffectHitTarget += SpellEffectFn(spell_mimiron_proximity_mines_SpellScript::HandleScript, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT); + } + }; + + SpellScript* GetSpellScript() const override + { + return new spell_mimiron_proximity_mines_SpellScript(); + } +}; + +// 65346 - Proximity Mine +class spell_mimiron_proximity_trigger : public SpellScriptLoader +{ + public: + spell_mimiron_proximity_trigger() : SpellScriptLoader("spell_mimiron_proximity_trigger") { } + + class spell_mimiron_proximity_trigger_SpellScript : public SpellScript + { + PrepareSpellScript(spell_mimiron_proximity_trigger_SpellScript); + + bool Validate(SpellInfo const* /*spell*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_PROXIMITY_MINE_EXPLOSION)) + return false; + return true; + } + + void FilterTargets(std::list& targets) + { + targets.remove(GetExplTargetWorldObject()); + + if (targets.empty()) + FinishCast(SPELL_FAILED_NO_VALID_TARGETS); + } + + void HandleDummy(SpellEffIndex /*effIndex*/) + { + GetCaster()->CastSpell((Unit*)NULL, SPELL_PROXIMITY_MINE_EXPLOSION, true); + } + + void Register() override + { + OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_mimiron_proximity_trigger_SpellScript::FilterTargets, EFFECT_0, TARGET_UNIT_SRC_AREA_ENTRY); + OnEffectHit += SpellEffectFn(spell_mimiron_proximity_trigger_SpellScript::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY); + } + }; + + SpellScript* GetSpellScript() const override + { + return new spell_mimiron_proximity_trigger_SpellScript(); + } +}; + +// 63382 - Rapid Burst +class spell_mimiron_rapid_burst : public SpellScriptLoader +{ + public: + spell_mimiron_rapid_burst() : SpellScriptLoader("spell_mimiron_rapid_burst") { } + + class spell_mimiron_rapid_burst_AuraScript : public AuraScript + { + PrepareAuraScript(spell_mimiron_rapid_burst_AuraScript); + + bool Validate(SpellInfo const* /*spell*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_RAPID_BURST_LEFT) || !sSpellMgr->GetSpellInfo(SPELL_RAPID_BURST_RIGHT)) + return false; + return true; + } + + void AfterRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) + { + if (GetTarget()->IsSummon()) + GetTarget()->ToTempSummon()->DespawnOrUnsummon(); + } + + void HandleDummyTick(AuraEffect const* aurEff) + { + if (GetCaster()) + GetCaster()->CastSpell(GetTarget(), aurEff->GetTickNumber() % 2 == 0 ? SPELL_RAPID_BURST_RIGHT : SPELL_RAPID_BURST_LEFT, true, NULL, aurEff); + } + + void Register() override + { + AfterEffectRemove += AuraEffectApplyFn(spell_mimiron_rapid_burst_AuraScript::AfterRemove, EFFECT_0, SPELL_AURA_PERIODIC_DUMMY, AURA_EFFECT_HANDLE_REAL); + OnEffectPeriodic += AuraEffectPeriodicFn(spell_mimiron_rapid_burst_AuraScript::HandleDummyTick, EFFECT_1, SPELL_AURA_PERIODIC_DUMMY); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_mimiron_rapid_burst_AuraScript(); + } +}; + +// 64402 - Rocket Strike, 65034 - Rocket Strike +class spell_mimiron_rocket_strike : public SpellScriptLoader +{ + public: + spell_mimiron_rocket_strike() : SpellScriptLoader("spell_mimiron_rocket_strike") { } + + class spell_mimiron_rocket_strike_SpellScript : public SpellScript + { + PrepareSpellScript(spell_mimiron_rocket_strike_SpellScript); + + bool Validate(SpellInfo const* /*spell*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_SCRIPT_EFFECT_ROCKET_STRIKE)) + return false; + return true; + } + + void FilterTargets(std::list& targets) + { + if (targets.empty()) + return; + + if (m_scriptSpellId == SPELL_ROCKET_STRIKE_LEFT && GetCaster()->IsVehicle()) + if (WorldObject* target = GetCaster()->GetVehicleKit()->GetPassenger(6)) + { + targets.clear(); + targets.push_back(target); + } + } + + void HandleDummy(SpellEffIndex /*effIndex*/) + { + GetHitUnit()->CastSpell((Unit*)NULL, SPELL_SCRIPT_EFFECT_ROCKET_STRIKE, true, NULL, NULL, GetCaster()->GetGUID()); + } + + void Register() override + { + OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_mimiron_rocket_strike_SpellScript::FilterTargets, EFFECT_0, TARGET_UNIT_SRC_AREA_ENTRY); + OnEffectHitTarget += SpellEffectFn(spell_mimiron_rocket_strike_SpellScript::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY); + } + }; + + SpellScript* GetSpellScript() const override + { + return new spell_mimiron_rocket_strike_SpellScript(); + } +}; + +// 63041 - Rocket Strike +class spell_mimiron_rocket_strike_damage : public SpellScriptLoader +{ + public: + spell_mimiron_rocket_strike_damage() : SpellScriptLoader("spell_mimiron_rocket_strike_damage") { } + + class spell_mimiron_rocket_strike_damage_SpellScript : public SpellScript + { + PrepareSpellScript(spell_mimiron_rocket_strike_damage_SpellScript); + + bool Validate(SpellInfo const* /*spell*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_NOT_SO_FRIENDLY_FIRE)) + return false; + return true; + } + + void HandleDespawn(SpellEffIndex /*effIndex*/) + { + if (GetCaster()->IsSummon()) + GetCaster()->ToTempSummon()->DespawnOrUnsummon(1000); + } + + void HandleScript(SpellEffIndex /*effIndex*/) + { + if (GetHitPlayer()) + if (InstanceScript* instance = GetCaster()->GetInstanceScript()) + if (Creature* mkii = ObjectAccessor::GetCreature(*GetCaster(), instance->GetData64(DATA_LEVIATHAN_MK_II))) + mkii->AI()->SetData(DATA_SETUP_ROCKET, 0); + } + + void HandleFriendlyFire(SpellEffIndex /*effIndex*/) + { + GetHitUnit()->CastSpell((Unit*)NULL, SPELL_NOT_SO_FRIENDLY_FIRE, true); + } + + void Register() override + { + OnEffectHit += SpellEffectFn(spell_mimiron_rocket_strike_damage_SpellScript::HandleDespawn, EFFECT_0, SPELL_EFFECT_SCHOOL_DAMAGE); + OnEffectHitTarget += SpellEffectFn(spell_mimiron_rocket_strike_damage_SpellScript::HandleScript, EFFECT_0, SPELL_EFFECT_SCHOOL_DAMAGE); + OnEffectHitTarget += SpellEffectFn(spell_mimiron_rocket_strike_damage_SpellScript::HandleFriendlyFire, EFFECT_1, SPELL_EFFECT_SCHOOL_DAMAGE); + } + }; + + SpellScript* GetSpellScript() const override + { + return new spell_mimiron_rocket_strike_damage_SpellScript(); + } +}; + +// 63681 - Rocket Strike +class spell_mimiron_rocket_strike_target_select : public SpellScriptLoader +{ + public: + spell_mimiron_rocket_strike_target_select() : SpellScriptLoader("spell_mimiron_rocket_strike_target_select") { } + + class spell_mimiron_rocket_strike_target_select_SpellScript : public SpellScript + { + PrepareSpellScript(spell_mimiron_rocket_strike_target_select_SpellScript); + + bool Validate(SpellInfo const* /*spell*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_SUMMON_ROCKET_STRIKE)) + return false; + return true; + } + + void FilterTargets(std::list& targets) + { + if (targets.empty()) + return; + + WorldObject* target = Trinity::Containers::SelectRandomContainerElement(targets); + + targets.remove_if(Trinity::AllWorldObjectsInRange(GetCaster(), 15.0f)); + + if (!targets.empty()) + target = Trinity::Containers::SelectRandomContainerElement(targets); + + targets.clear(); + targets.push_back(target); + } + + void HandleScript(SpellEffIndex /*effIndex*/) + { + InstanceScript* instance = GetCaster()->GetInstanceScript(); + GetCaster()->CastSpell(GetHitUnit(), SPELL_SUMMON_ROCKET_STRIKE, true, NULL, NULL, instance ? instance->GetData64(DATA_VX_001) : GetCaster()->GetGUID()); + GetCaster()->SetDisplayId(11686); + } + + void Register() override + { + OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_mimiron_rocket_strike_target_select_SpellScript::FilterTargets, EFFECT_0, TARGET_UNIT_SRC_AREA_ENEMY); + OnEffectHitTarget += SpellEffectFn(spell_mimiron_rocket_strike_target_select_SpellScript::HandleScript, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT); + } + }; + + SpellScript* GetSpellScript() const override + { + return new spell_mimiron_rocket_strike_target_select_SpellScript(); + } +}; + +// 64383 - Self Repair +class spell_mimiron_self_repair : public SpellScriptLoader +{ + public: + spell_mimiron_self_repair() : SpellScriptLoader("spell_mimiron_self_repair") { } + + class spell_mimiron_self_repair_SpellScript : public SpellScript + { + PrepareSpellScript(spell_mimiron_self_repair_SpellScript); + + void HandleScript() + { + if (GetCaster()->GetAI()) + GetCaster()->GetAI()->DoAction(DO_ASSEMBLED_COMBAT); + } + + void Register() override + { + AfterHit += SpellHitFn(spell_mimiron_self_repair_SpellScript::HandleScript); + } + }; + + SpellScript* GetSpellScript() const override + { + return new spell_mimiron_self_repair_SpellScript(); + } +}; + +// 63414 - Spinning Up -- This should be removed when channel track target implementation is properly functioning. +class spell_mimiron_spinning_up : public SpellScriptLoader +{ + public: + spell_mimiron_spinning_up() : SpellScriptLoader("spell_mimiron_spinning_up") { } + + class spell_mimiron_spinning_up_SpellScript : public SpellScript + { + PrepareSpellScript(spell_mimiron_spinning_up_SpellScript); + + void OnHit(SpellEffIndex /*effIndex*/) + { + if (GetHitUnit() != GetCaster()) + GetCaster()->SetUInt64Value(UNIT_FIELD_CHANNEL_OBJECT, GetHitUnit()->GetGUID()); + } + + void Register() override + { + OnEffectHitTarget += SpellEffectFn(spell_mimiron_spinning_up_SpellScript::OnHit, EFFECT_0, SPELL_EFFECT_APPLY_AURA); + } + }; + + SpellScript* GetSpellScript() const override + { + return new spell_mimiron_spinning_up_SpellScript(); + } +}; + +// 64426 - Summon Scrap Bot +class spell_mimiron_summon_assault_bot : public SpellScriptLoader +{ + public: + spell_mimiron_summon_assault_bot() : SpellScriptLoader("spell_mimiron_summon_assault_bot") { } + + class spell_mimiron_summon_assault_bot_AuraScript : public AuraScript + { + PrepareAuraScript(spell_mimiron_summon_assault_bot_AuraScript); + + bool Validate(SpellInfo const* /*spell*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_SUMMON_ASSAULT_BOT)) + return false; + return true; + } + + void OnRemove(AuraEffect const* aurEff, AuraEffectHandleModes /*mode*/) + { + if (Unit* caster = GetCaster()) + if (InstanceScript* instance = caster->GetInstanceScript()) + if (instance->GetBossState(BOSS_MIMIRON) == IN_PROGRESS) + caster->CastSpell(caster, SPELL_SUMMON_ASSAULT_BOT, false, NULL, aurEff, instance->GetData64(DATA_AERIAL_COMMAND_UNIT)); + } + + void Register() override + { + OnEffectRemove += AuraEffectRemoveFn(spell_mimiron_summon_assault_bot_AuraScript::OnRemove, EFFECT_0, SPELL_AURA_DUMMY, AURA_EFFECT_HANDLE_REAL); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_mimiron_summon_assault_bot_AuraScript(); + } +}; + +// 64425 - Summon Scrap Bot Trigger +class spell_mimiron_summon_assault_bot_target : public SpellScriptLoader +{ + public: + spell_mimiron_summon_assault_bot_target() : SpellScriptLoader("spell_mimiron_summon_assault_bot_target") { } + + class spell_mimiron_summon_assault_bot_target_SpellScript : public SpellScript + { + PrepareSpellScript(spell_mimiron_summon_assault_bot_target_SpellScript); + + bool Validate(SpellInfo const* /*spell*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_SUMMON_ASSAULT_BOT_DUMMY)) + return false; + return true; + } + + void FilterTargets(std::list& targets) + { + if (targets.empty()) + return; + + WorldObject* target = Trinity::Containers::SelectRandomContainerElement(targets); + + targets.clear(); + targets.push_back(target); + } + + void HandleDummy(SpellEffIndex /*effIndex*/) + { + GetHitUnit()->CastSpell(GetHitUnit(), SPELL_SUMMON_ASSAULT_BOT_DUMMY, true); + } + + void Register() override + { + OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_mimiron_summon_assault_bot_target_SpellScript::FilterTargets, EFFECT_0, TARGET_UNIT_SRC_AREA_ENTRY); + OnEffectHitTarget += SpellEffectFn(spell_mimiron_summon_assault_bot_target_SpellScript::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY); + } + }; + + SpellScript* GetSpellScript() const override + { + return new spell_mimiron_summon_assault_bot_target_SpellScript(); + } +}; + +// 64621 - Summon Fire Bot +class spell_mimiron_summon_fire_bot : public SpellScriptLoader +{ + public: + spell_mimiron_summon_fire_bot() : SpellScriptLoader("spell_mimiron_summon_fire_bot") { } + + class spell_mimiron_summon_fire_bot_AuraScript : public AuraScript + { + PrepareAuraScript(spell_mimiron_summon_fire_bot_AuraScript); + + bool Validate(SpellInfo const* /*spell*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_SUMMON_FIRE_BOT)) + return false; + + return true; + } + + void OnRemove(AuraEffect const* aurEff, AuraEffectHandleModes /*mode*/) + { + if (Unit* caster = GetCaster()) + if (InstanceScript* instance = caster->GetInstanceScript()) + if (instance->GetBossState(BOSS_MIMIRON) == IN_PROGRESS) + caster->CastSpell(caster, SPELL_SUMMON_FIRE_BOT, false, NULL, aurEff, instance->GetData64(DATA_AERIAL_COMMAND_UNIT)); + } + + void Register() override + { + OnEffectRemove += AuraEffectRemoveFn(spell_mimiron_summon_fire_bot_AuraScript::OnRemove, EFFECT_0, SPELL_AURA_DUMMY, AURA_EFFECT_HANDLE_REAL); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_mimiron_summon_fire_bot_AuraScript(); + } +}; + +// 64620 - Summon Fire Bot Trigger +class spell_mimiron_summon_fire_bot_target : public SpellScriptLoader +{ + public: + spell_mimiron_summon_fire_bot_target() : SpellScriptLoader("spell_mimiron_summon_fire_bot_target") { } + + class spell_mimiron_summon_fire_bot_target_SpellScript : public SpellScript + { + PrepareSpellScript(spell_mimiron_summon_fire_bot_target_SpellScript); + + bool Validate(SpellInfo const* /*spell*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_SUMMON_FIRE_BOT_DUMMY)) + return false; + return true; + } + + void FilterTargets(std::list& targets) + { + Trinity::Containers::RandomResizeList(targets, 3); + } + + void HandleDummy(SpellEffIndex /*effIndex*/) + { + GetHitUnit()->CastSpell(GetHitUnit(), SPELL_SUMMON_FIRE_BOT_DUMMY, true); + } + + void Register() override + { + OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_mimiron_summon_fire_bot_target_SpellScript::FilterTargets, EFFECT_0, TARGET_UNIT_SRC_AREA_ENTRY); + OnEffectHitTarget += SpellEffectFn(spell_mimiron_summon_fire_bot_target_SpellScript::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY); + } + }; + + SpellScript* GetSpellScript() const override + { + return new spell_mimiron_summon_fire_bot_target_SpellScript(); + } +}; + +// 64567 - Summon Flames Initial +class spell_mimiron_summon_flames_initial : public SpellScriptLoader +{ + public: + spell_mimiron_summon_flames_initial() : SpellScriptLoader("spell_mimiron_summon_flames_initial") { } + + class spell_mimiron_summon_flames_initial_SpellScript : public SpellScript + { + PrepareSpellScript(spell_mimiron_summon_flames_initial_SpellScript); + + bool Validate(SpellInfo const* /*spell*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_SUMMON_FLAMES_INITIAL)) + return false; + return true; + } + + void FilterTargets(std::list& targets) + { + Trinity::Containers::RandomResizeList(targets, 3); + } + + void HandleScript(SpellEffIndex /*effIndex*/) + { + GetCaster()->CastSpell(GetHitUnit(), SPELL_SUMMON_FLAMES_INITIAL, true); + } + + void Register() override + { + OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_mimiron_summon_flames_initial_SpellScript::FilterTargets, EFFECT_0, TARGET_UNIT_SRC_AREA_ENEMY); + OnEffectHitTarget += SpellEffectFn(spell_mimiron_summon_flames_initial_SpellScript::HandleScript, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT); + } + }; + + SpellScript* GetSpellScript() const override + { + return new spell_mimiron_summon_flames_initial_SpellScript(); + } +}; + +// 64562 - Summon Flames Spread Trigger +class spell_mimiron_summon_flames_spread : public SpellScriptLoader +{ + public: + spell_mimiron_summon_flames_spread() : SpellScriptLoader("spell_mimiron_summon_flames_spread") { } + + class spell_mimiron_summon_flames_spread_SpellScript : public SpellScript + { + PrepareSpellScript(spell_mimiron_summon_flames_spread_SpellScript); + + void FilterTargets(std::list& targets) + { + if (targets.empty()) + return; + + WorldObject* target = targets.front(); + + for (std::list::const_iterator iter = targets.begin(); iter != targets.end(); iter++) + if (GetCaster()->GetDistance2d(*iter) < GetCaster()->GetDistance2d(target)) + target = *iter; + + targets.clear(); + targets.push_back(target); + } + + void OnHit(SpellEffIndex /*effIndex*/) + { + GetCaster()->SetInFront(GetHitUnit()); + } + + void Register() override + { + OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_mimiron_summon_flames_spread_SpellScript::FilterTargets, EFFECT_0, TARGET_UNIT_SRC_AREA_ENEMY); + OnEffectHitTarget += SpellEffectFn(spell_mimiron_summon_flames_spread_SpellScript::OnHit, EFFECT_0, SPELL_EFFECT_APPLY_AURA); + } + }; + + SpellScript* GetSpellScript() const override + { + return new spell_mimiron_summon_flames_spread_SpellScript(); + } + + class spell_mimiron_summon_flames_spread_AuraScript : public AuraScript + { + PrepareAuraScript(spell_mimiron_summon_flames_spread_AuraScript); + + bool Validate(SpellInfo const* /*spell*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_SUMMON_FLAMES_SPREAD)) + return false; + return true; + } + + void HandleTick(AuraEffect const* /*aurEff*/) + { + PreventDefaultAction(); + if (Unit* caster = GetCaster()) + if (caster->HasAura(SPELL_FLAMES_PERIODIC_TRIGGER)) + caster->CastSpell(GetTarget(), SPELL_SUMMON_FLAMES_SPREAD, true); + } + + void Register() override + { + OnEffectPeriodic += AuraEffectPeriodicFn(spell_mimiron_summon_flames_spread_AuraScript::HandleTick, EFFECT_0, SPELL_AURA_PERIODIC_TRIGGER_SPELL); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_mimiron_summon_flames_spread_AuraScript(); + } +}; + +// 64627 - Frost Bomb +class spell_mimiron_summon_frost_bomb : public SpellScriptLoader +{ + public: + spell_mimiron_summon_frost_bomb() : SpellScriptLoader("spell_mimiron_summon_frost_bomb") { } + + class spell_mimiron_summon_frost_bomb_SpellScript : public SpellScript + { + PrepareSpellScript(spell_mimiron_summon_frost_bomb_SpellScript); + + void HandleDummy(SpellEffIndex /*effIndex*/) + { + GetHitUnit()->RemoveAurasDueToSpell(SPELL_FLAMES_PERIODIC_TRIGGER); + } + + void Register() override + { + OnEffectHitTarget += SpellEffectFn(spell_mimiron_summon_frost_bomb_SpellScript::HandleDummy, EFFECT_1, SPELL_EFFECT_DUMMY); + } + }; + + SpellScript* GetSpellScript() const override + { + return new spell_mimiron_summon_frost_bomb_SpellScript(); + } +}; + +// 64623 - Frost Bomb +class spell_mimiron_summon_frost_bomb_target : public SpellScriptLoader +{ + public: + spell_mimiron_summon_frost_bomb_target() : SpellScriptLoader("spell_mimiron_summon_frost_bomb_target") { } + + class spell_mimiron_summon_frost_bomb_target_SpellScript : public SpellScript + { + PrepareSpellScript(spell_mimiron_summon_frost_bomb_target_SpellScript); + + bool Validate(SpellInfo const* /*spell*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_SUMMON_FROST_BOMB)) + return false; + return true; + } + + void FilterTargets(std::list& targets) + { + if (targets.empty()) + return; + + targets.remove_if(Trinity::AllWorldObjectsInRange(GetCaster(), 15.0f)); + + if (targets.empty()) + return; + + WorldObject* target = Trinity::Containers::SelectRandomContainerElement(targets); + + targets.clear(); + targets.push_back(target); + } + + void HandleScript(SpellEffIndex /*effIndex*/) + { + GetCaster()->CastSpell(GetHitUnit(), SPELL_SUMMON_FROST_BOMB, true); + } + + void Register() override + { + OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_mimiron_summon_frost_bomb_target_SpellScript::FilterTargets, EFFECT_0, TARGET_UNIT_SRC_AREA_ENTRY); + OnEffectHitTarget += SpellEffectFn(spell_mimiron_summon_frost_bomb_target_SpellScript::HandleScript, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT); + } + }; + + SpellScript* GetSpellScript() const override + { + return new spell_mimiron_summon_frost_bomb_target_SpellScript(); + } +}; + +// 64398 - Summon Scrap Bot +class spell_mimiron_summon_junk_bot : public SpellScriptLoader +{ + public: + spell_mimiron_summon_junk_bot() : SpellScriptLoader("spell_mimiron_summon_junk_bot") { } + + class spell_mimiron_summon_junk_bot_AuraScript : public AuraScript + { + PrepareAuraScript(spell_mimiron_summon_junk_bot_AuraScript); + + bool Validate(SpellInfo const* /*spell*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_SUMMON_JUNK_BOT)) + return false; + return true; + } + + void OnRemove(AuraEffect const* aurEff, AuraEffectHandleModes /*mode*/) + { + if (Unit* caster = GetCaster()) + if (InstanceScript* instance = caster->GetInstanceScript()) + if (instance->GetBossState(BOSS_MIMIRON) == IN_PROGRESS) + caster->CastSpell(caster, SPELL_SUMMON_JUNK_BOT, false, NULL, aurEff, instance->GetData64(DATA_AERIAL_COMMAND_UNIT)); + } + + void Register() override + { + OnEffectRemove += AuraEffectRemoveFn(spell_mimiron_summon_junk_bot_AuraScript::OnRemove, EFFECT_0, SPELL_AURA_DUMMY, AURA_EFFECT_HANDLE_REAL); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_mimiron_summon_junk_bot_AuraScript(); + } +}; + +// 63820 - Summon Scrap Bot Trigger +class spell_mimiron_summon_junk_bot_target : public SpellScriptLoader +{ + public: + spell_mimiron_summon_junk_bot_target() : SpellScriptLoader("spell_mimiron_summon_junk_bot_target") { } + + class spell_mimiron_summon_junk_bot_target_SpellScript : public SpellScript + { + PrepareSpellScript(spell_mimiron_summon_junk_bot_target_SpellScript); + + bool Validate(SpellInfo const* /*spell*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_SUMMON_JUNK_BOT_DUMMY)) + return false; + return true; + } + + void FilterTargets(std::list& targets) + { + if (targets.empty()) + return; + + WorldObject* target = Trinity::Containers::SelectRandomContainerElement(targets); + + targets.clear(); + targets.push_back(target); + } + + void HandleDummy(SpellEffIndex /*effIndex*/) + { + GetHitUnit()->CastSpell(GetHitUnit(), SPELL_SUMMON_JUNK_BOT_DUMMY, true); + } + + void Register() override + { + OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_mimiron_summon_junk_bot_target_SpellScript::FilterTargets, EFFECT_0, TARGET_UNIT_SRC_AREA_ENTRY); + OnEffectHitTarget += SpellEffectFn(spell_mimiron_summon_junk_bot_target_SpellScript::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY); + } + }; + + SpellScript* GetSpellScript() const override + { + return new spell_mimiron_summon_junk_bot_target_SpellScript(); + } +}; + +// 63339 - Weld +class spell_mimiron_weld : public SpellScriptLoader +{ + public: + spell_mimiron_weld() : SpellScriptLoader("spell_mimiron_weld") { } + + class spell_mimiron_weld_AuraScript : public AuraScript + { + PrepareAuraScript(spell_mimiron_weld_AuraScript); + + void HandleTick(AuraEffect const* aurEff) + { + Unit* caster = GetTarget(); + if (Unit* vehicle = caster->GetVehicleBase()) + { + if (aurEff->GetTickNumber() % 5 == 0) + caster->CastSpell(vehicle, repairSpells[urand(0, 3)]); + caster->SetFacingToObject(vehicle); + } + } + + void Register() override + { + OnEffectPeriodic += AuraEffectPeriodicFn(spell_mimiron_weld_AuraScript::HandleTick, EFFECT_0, SPELL_AURA_PERIODIC_TRIGGER_SPELL); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_mimiron_weld_AuraScript(); + } +}; + +class achievement_setup_boom : public AchievementCriteriaScript +{ + public: + achievement_setup_boom() : AchievementCriteriaScript("achievement_setup_boom") { } + + bool OnCheck(Player* /*source*/, Unit* target) + { + return target && target->GetAI()->GetData(DATA_SETUP_BOMB); + } +}; + +class achievement_setup_mine : public AchievementCriteriaScript +{ + public: + achievement_setup_mine() : AchievementCriteriaScript("achievement_setup_mine") { } + + bool OnCheck(Player* /*source*/, Unit* target) + { + return target && target->GetAI()->GetData(DATA_SETUP_MINE); + } +}; + +class achievement_setup_rocket : public AchievementCriteriaScript +{ + public: + achievement_setup_rocket() : AchievementCriteriaScript("achievement_setup_rocket") { } + + bool OnCheck(Player* /*source*/, Unit* target) + { + return target && target->GetAI()->GetData(DATA_SETUP_ROCKET); + } +}; + +class achievement_firefighter : public AchievementCriteriaScript +{ + public: + achievement_firefighter() : AchievementCriteriaScript("achievement_firefighter") { } + + bool OnCheck(Player* /*source*/, Unit* target) + { + return target && target->GetAI()->GetData(DATA_FIREFIGHTER); } }; void AddSC_boss_mimiron() { - new spell_ulduar_proximity_mines(); + new boss_aerial_command_unit(); + new boss_leviathan_mk_ii(); + new boss_mimiron(); + new boss_vx_001(); + + new npc_magnetic_core(); + new npc_mimiron_assault_bot(); + new npc_mimiron_emergency_fire_bot(); + new npc_mimiron_computer(); + new npc_mimiron_flames(); + new npc_mimiron_frost_bomb(); + new npc_mimiron_proximity_mine(); + + new go_mimiron_hardmode_button(); + + new spell_mimiron_bomb_bot(); + new spell_mimiron_clear_fires(); + new spell_mimiron_despawn_assault_bots(); + new spell_mimiron_fire_search(); + new spell_mimiron_flame_suppressant(); + new spell_mimiron_frost_bomb_explosion(); + new spell_mimiron_magnetic_core(); + new spell_mimiron_napalm_shell(); + new spell_mimiron_p3wx2_laser_barrage(); + new spell_mimiron_plasma_blast(); + new spell_mimiron_proximity_explosion(); + new spell_mimiron_proximity_mines(); + new spell_mimiron_proximity_trigger(); + new spell_mimiron_rapid_burst(); + new spell_mimiron_rocket_strike(); + new spell_mimiron_rocket_strike_damage(); + new spell_mimiron_rocket_strike_target_select(); + new spell_mimiron_self_repair(); + new spell_mimiron_spinning_up(); + new spell_mimiron_summon_assault_bot(); + new spell_mimiron_summon_assault_bot_target(); + new spell_mimiron_summon_fire_bot(); + new spell_mimiron_summon_fire_bot_target(); + new spell_mimiron_summon_flames_initial(); + new spell_mimiron_summon_flames_spread(); + new spell_mimiron_summon_frost_bomb(); + new spell_mimiron_summon_frost_bomb_target(); + new spell_mimiron_summon_junk_bot(); + new spell_mimiron_summon_junk_bot_target(); + new spell_mimiron_weld(); + + new achievement_setup_boom(); + new achievement_setup_mine(); + new achievement_setup_rocket(); + new achievement_firefighter(); } diff --git a/src/server/scripts/Northrend/Ulduar/Ulduar/instance_ulduar.cpp b/src/server/scripts/Northrend/Ulduar/Ulduar/instance_ulduar.cpp index d728bc2a89..4dfa38abae 100644 --- a/src/server/scripts/Northrend/Ulduar/Ulduar/instance_ulduar.cpp +++ b/src/server/scripts/Northrend/Ulduar/Ulduar/instance_ulduar.cpp @@ -31,6 +31,9 @@ static DoorData const doorData[] = { GO_HODIR_ENTRANCE, BOSS_HODIR, DOOR_TYPE_ROOM, BOUNDARY_E }, { GO_HODIR_DOOR, BOSS_HODIR, DOOR_TYPE_PASSAGE, BOUNDARY_NONE }, { GO_HODIR_ICE_DOOR, BOSS_HODIR, DOOR_TYPE_PASSAGE, BOUNDARY_W }, + { GO_MIMIRON_DOOR_1, BOSS_MIMIRON, DOOR_TYPE_ROOM, BOUNDARY_W }, + { GO_MIMIRON_DOOR_2, BOSS_MIMIRON, DOOR_TYPE_ROOM, BOUNDARY_E }, + { GO_MIMIRON_DOOR_3, BOSS_MIMIRON, DOOR_TYPE_ROOM, BOUNDARY_S }, { GO_VEZAX_DOOR, BOSS_VEZAX, DOOR_TYPE_PASSAGE, BOUNDARY_E }, { GO_YOGG_SARON_DOOR, BOSS_YOGG_SARON, DOOR_TYPE_ROOM, BOUNDARY_S }, { GO_DOODAD_UL_SIGILDOOR_03, BOSS_ALGALON, DOOR_TYPE_ROOM, BOUNDARY_W }, @@ -69,11 +72,14 @@ class instance_ulduar : public InstanceMapScript uint64 AssemblyGUIDs[3]; uint64 KologarnGUID; uint64 AuriayaGUID; - uint64 MimironGUID; uint64 HodirGUID; uint64 ThorimGUID; uint64 FreyaGUID; uint64 ElderGUIDs[3]; + uint64 MimironGUID; + uint64 MimironVehicleGUIDs[3]; + uint64 MimironComputerGUID; + uint64 MimironWorldTriggerGUID; uint64 VezaxGUID; uint64 YoggSaronGUID; uint64 VoiceOfYoggSaronGUID; @@ -91,6 +97,9 @@ class instance_ulduar : public InstanceMapScript uint64 ThorimChestGUID; uint64 HodirRareCacheGUID; uint64 HodirChestGUID; + uint64 MimironTramGUID; + uint64 MimironElevatorGUID; + uint64 MimironButtonGUID; uint64 BrainRoomDoorGUIDs[3]; uint64 AlgalonSigilDoorGUID[3]; uint64 AlgalonFloorGUID[2]; @@ -125,6 +134,8 @@ class instance_ulduar : public InstanceMapScript KologarnGUID = 0; AuriayaGUID = 0; MimironGUID = 0; + MimironComputerGUID = 0; + MimironWorldTriggerGUID = 0; HodirGUID = 0; ThorimGUID = 0; FreyaGUID = 0; @@ -139,6 +150,9 @@ class instance_ulduar : public InstanceMapScript ThorimChestGUID = 0; HodirRareCacheGUID = 0; HodirChestGUID = 0; + MimironTramGUID = 0; + MimironElevatorGUID = 0; + MimironButtonGUID = 0; LeviathanGateGUID = 0; AlgalonUniverseGUID = 0; AlgalonTrapdoorGUID = 0; @@ -165,6 +179,7 @@ class instance_ulduar : public InstanceMapScript memset(AssemblyGUIDs, 0, sizeof(AssemblyGUIDs)); memset(RazorHarpoonGUIDs, 0, sizeof(RazorHarpoonGUIDs)); memset(ElderGUIDs, 0, sizeof(ElderGUIDs)); + memset(MimironVehicleGUIDs, 0, sizeof(MimironVehicleGUIDs)); memset(BrainRoomDoorGUIDs, 0, sizeof(BrainRoomDoorGUIDs)); memset(KeeperGUIDs, 0, sizeof(KeeperGUIDs)); memset(_summonObservationRingKeeper, false, sizeof(_summonObservationRingKeeper)); @@ -288,9 +303,6 @@ class instance_ulduar : public InstanceMapScript case NPC_AURIAYA: AuriayaGUID = creature->GetGUID(); break; - case NPC_MIMIRON: - MimironGUID = creature->GetGUID(); - break; // Hodir case NPC_HODIR: @@ -353,6 +365,28 @@ class instance_ulduar : public InstanceMapScript creature->DespawnOrUnsummon(); break; + // Mimiron + case NPC_MIMIRON: + MimironGUID = creature->GetGUID(); + break; + case NPC_LEVIATHAN_MKII: + MimironVehicleGUIDs[0] = creature->GetGUID(); + if (GetBossState(BOSS_MIMIRON) == DONE) + creature->DespawnOrUnsummon(); + break; + case NPC_VX_001: + MimironVehicleGUIDs[1] = creature->GetGUID(); + break; + case NPC_AERIAL_COMMAND_UNIT: + MimironVehicleGUIDs[2] = creature->GetGUID(); + break; + case NPC_COMPUTER: + MimironComputerGUID = creature->GetGUID(); + break; + case NPC_WORLD_TRIGGER_MIMIRON: + MimironWorldTriggerGUID = creature->GetGUID(); + break; + case NPC_VEZAX: VezaxGUID = creature->GetGUID(); break; @@ -469,6 +503,15 @@ class instance_ulduar : public InstanceMapScript case GO_HODIR_CHEST: HodirChestGUID = gameObject->GetGUID(); break; + case GO_MIMIRON_TRAM: + MimironTramGUID = gameObject->GetGUID(); + break; + case GO_MIMIRON_ELEVATOR: + MimironElevatorGUID = gameObject->GetGUID(); + break; + case GO_MIMIRON_BUTTON: + MimironButtonGUID = gameObject->GetGUID(); + break; case GO_LEVIATHAN_GATE: LeviathanGateGUID = gameObject->GetGUID(); if (GetBossState(BOSS_LEVIATHAN) == DONE) @@ -481,6 +524,9 @@ class instance_ulduar : public InstanceMapScript case GO_HODIR_ENTRANCE: case GO_HODIR_DOOR: case GO_HODIR_ICE_DOOR: + case GO_MIMIRON_DOOR_1: + case GO_MIMIRON_DOOR_2: + case GO_MIMIRON_DOOR_3: case GO_VEZAX_DOOR: case GO_YOGG_SARON_DOOR: AddDoor(gameObject, true); @@ -565,6 +611,9 @@ class instance_ulduar : public InstanceMapScript case GO_HODIR_ENTRANCE: case GO_HODIR_DOOR: case GO_HODIR_ICE_DOOR: + case GO_MIMIRON_DOOR_1: + case GO_MIMIRON_DOOR_2: + case GO_MIMIRON_DOOR_3: case GO_VEZAX_DOOR: case GO_YOGG_SARON_DOOR: case GO_DOODAD_UL_SIGILDOOR_03: @@ -773,6 +822,10 @@ class instance_ulduar : public InstanceMapScript break; case DATA_UNBROKEN: Unbroken = bool(data); + break; + case DATA_MIMIRON_ELEVATOR: + if (GameObject* gameObject = instance->GetGameObject(MimironElevatorGUID)) + gameObject->SetGoState((GOState)data); break; case DATA_ILLUSION: illusion = data; @@ -845,8 +898,6 @@ class instance_ulduar : public InstanceMapScript return KologarnGUID; case BOSS_AURIAYA: return AuriayaGUID; - case BOSS_MIMIRON: - return MimironGUID; case BOSS_HODIR: return HodirGUID; case BOSS_THORIM: @@ -861,6 +912,22 @@ class instance_ulduar : public InstanceMapScript return ElderGUIDs[1]; case BOSS_STONEBARK: return ElderGUIDs[2]; + + // Mimiron + case BOSS_MIMIRON: + return MimironGUID; + case DATA_LEVIATHAN_MK_II: + return MimironVehicleGUIDs[0]; + case DATA_VX_001: + return MimironVehicleGUIDs[1]; + case DATA_AERIAL_COMMAND_UNIT: + return MimironVehicleGUIDs[2]; + case DATA_COMPUTER: + return MimironComputerGUID; + case DATA_MIMIRON_WORLD_TRIGGER: + return MimironWorldTriggerGUID; + case DATA_MIMIRON_BUTTON: + return MimironButtonGUID; case BOSS_VEZAX: return VezaxGUID; diff --git a/src/server/scripts/Northrend/Ulduar/Ulduar/ulduar.h b/src/server/scripts/Northrend/Ulduar/Ulduar/ulduar.h index 3544ff3c07..1c9fa7575d 100644 --- a/src/server/scripts/Northrend/Ulduar/Ulduar/ulduar.h +++ b/src/server/scripts/Northrend/Ulduar/Ulduar/ulduar.h @@ -83,6 +83,18 @@ enum UlduarNPCs NPC_LEVIATHAN_MKII = 33432, NPC_VX_001 = 33651, NPC_AERIAL_COMMAND_UNIT = 33670, + NPC_ASSAULT_BOT = 34057, + NPC_BOMB_BOT = 33836, + NPC_JUNK_BOT = 33855, + NPC_EMERGENCY_FIRE_BOT = 34147, + NPC_FROST_BOMB = 34149, + NPC_BURST_TARGET = 34211, + NPC_FLAME = 34363, + NPC_FLAME_SPREAD = 34121, + NPC_DB_TARGET = 33576, + NPC_ROCKET_MIMIRON_VISUAL = 34050, + NPC_WORLD_TRIGGER_MIMIRON = 21252, + NPC_COMPUTER = 34143, // Freya's Keepers NPC_IRONBRANCH = 32913, @@ -204,6 +216,18 @@ enum UlduarGameObjects GO_THORIM_CHEST_HERO = 194315, GO_THORIM_CHEST = 194314, + // Mimiron + GO_MIMIRON_TRAM = 194675, + GO_MIMIRON_ELEVATOR = 194749, + GO_MIMIRON_BUTTON = 194739, + GO_MIMIRON_DOOR_1 = 194774, + GO_MIMIRON_DOOR_2 = 194775, + GO_MIMIRON_DOOR_3 = 194776, + GO_CACHE_OF_INNOVATION = 194789, + GO_CACHE_OF_INNOVATION_FIREFIGHTER = 194957, + GO_CACHE_OF_INNOVATION_HERO = 194956, + GO_CACHE_OF_INNOVATION_FIREFIGHTER_HERO = 194958, + // Vezax GO_VEZAX_DOOR = 194750, @@ -292,6 +316,16 @@ enum UlduarData // Hodir DATA_HODIR_RARE_CACHE, + // Mimiron + DATA_LEVIATHAN_MK_II, + DATA_VX_001, + DATA_AERIAL_COMMAND_UNIT, + DATA_COMPUTER, + DATA_MIMIRON_WORLD_TRIGGER, + DATA_MIMIRON_ELEVATOR, + DATA_MIMIRON_TRAM, + DATA_MIMIRON_BUTTON, + // Yogg-Saron DATA_VOICE_OF_YOGG_SARON, DATA_SARA, From 80e0c443fff2baf812d03c18970f591e4abc15f1 Mon Sep 17 00:00:00 2001 From: Kittnz Date: Tue, 24 Jun 2014 18:23:37 +0200 Subject: [PATCH 02/80] Quest: Powering our Defenses fixed Fix by @Discover-, @untaught, @Kittnz --- .../world/2014_06_24_05_world_quest.sql | 2 ++ .../EasternKingdoms/zone_eversong_woods.cpp | 18 +++++++----------- 2 files changed, 9 insertions(+), 11 deletions(-) create mode 100644 sql/updates/world/2014_06_24_05_world_quest.sql diff --git a/sql/updates/world/2014_06_24_05_world_quest.sql b/sql/updates/world/2014_06_24_05_world_quest.sql new file mode 100644 index 0000000000..49431fdf46 --- /dev/null +++ b/sql/updates/world/2014_06_24_05_world_quest.sql @@ -0,0 +1,2 @@ +-- +UPDATE `quest_template` SET `SpecialFlags`=0 WHERE `entry`=8490; diff --git a/src/server/scripts/EasternKingdoms/zone_eversong_woods.cpp b/src/server/scripts/EasternKingdoms/zone_eversong_woods.cpp index 8c612a1162..bc4fff4da7 100644 --- a/src/server/scripts/EasternKingdoms/zone_eversong_woods.cpp +++ b/src/server/scripts/EasternKingdoms/zone_eversong_woods.cpp @@ -190,6 +190,9 @@ enum InfusedCrystal // Quest QUEST_POWERING_OUR_DEFENSES = 8490, + // Quest Credit + QUEST_POD_CREDIT = 16364, + // Says EMOTE = 0, @@ -266,24 +269,17 @@ public: summoned->AI()->AttackStart(me); } - void JustDied(Unit* /*killer*/) override - { - if (PlayerGUID && !Completed) - if (Player* player = ObjectAccessor::GetPlayer(*me, PlayerGUID)) - player->FailQuest(QUEST_POWERING_OUR_DEFENSES); - } - void UpdateAI(uint32 diff) override { if (EndTimer < diff && Progress) { - Talk(EMOTE); Completed = true; if (PlayerGUID) if (Player* player = ObjectAccessor::GetPlayer(*me, PlayerGUID)) - player->CompleteQuest(QUEST_POWERING_OUR_DEFENSES); - - me->DealDamage(me, me->GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false); + { + Talk(EMOTE, player); + player->KilledMonsterCredit(QUEST_POD_CREDIT); + } me->RemoveCorpse(); } else EndTimer -= diff; From ec59bf569930a6b2f5c5922214b150dde9314842 Mon Sep 17 00:00:00 2001 From: Vincent-Michael Date: Tue, 24 Jun 2014 18:30:51 +0200 Subject: [PATCH 03/80] SQL: Rename sql after merge pr --- ...00_action_ip_logger.sql => 2014_06_23_00_action_ip_logger.sql} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename sql/updates/auth/{2014_05_02_00_action_ip_logger.sql => 2014_06_23_00_action_ip_logger.sql} (100%) diff --git a/sql/updates/auth/2014_05_02_00_action_ip_logger.sql b/sql/updates/auth/2014_06_23_00_action_ip_logger.sql similarity index 100% rename from sql/updates/auth/2014_05_02_00_action_ip_logger.sql rename to sql/updates/auth/2014_06_23_00_action_ip_logger.sql From 028614b95ba413f92afe87f02a413b700e026cf9 Mon Sep 17 00:00:00 2001 From: Aokromes Date: Tue, 24 Jun 2014 20:03:05 +0200 Subject: [PATCH 04/80] DB/Quest: Changing the Wind's Course By Trista, updated by dr-j, closes #1084 --- .../world/2014_06_24_05_world_misc.sql | 68 +++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100644 sql/updates/world/2014_06_24_05_world_misc.sql diff --git a/sql/updates/world/2014_06_24_05_world_misc.sql b/sql/updates/world/2014_06_24_05_world_misc.sql new file mode 100644 index 0000000000..71898ee156 --- /dev/null +++ b/sql/updates/world/2014_06_24_05_world_misc.sql @@ -0,0 +1,68 @@ +-- Add support for quest Changing the Wind's Course - ID: 13058 /Horde/ +SET @Stormhoof := 30388; +SET @VehicleSpell := 56863; +SET @Ride := 46598; +SET @Wind := 30474; +SET @DropHorn := 56892; +SET @Stun := 62794; +SET @Guid := 40509; -- Set by TDB + +-- Add spawn in phase 256 for The Wind of North +DELETE FROM `creature` WHERE `guid`=@Guid; +INSERT INTO `creature` (`guid`,`id`,`map`,`spawnMask`,`phaseMask`,`modelid`,`equipment_id`,`position_x`,`position_y`,`position_z`,`orientation`,`spawntimesecs`,`spawndist`,`currentwaypoint`,`curhealth`,`curmana`,`MovementType`,`npcflag`,`unit_flags`,`dynamicflags`) VALUES +(@Guid,@Wind,571,1,256,0,0,7942.61,-2723.29,1138,6.09394,60,0,0,63000,19970,0,0,0,0); + +-- SAI and spell support for StormHoof +UPDATE `creature_template` SET `AIName`='SmartAI',`spell1`=56897,`spell2`=61668,`spell3`=56896 WHERE `entry`=30388; +DELETE FROM `smart_scripts` WHERE `entryorguid`=@Stormhoof; +INSERT INTO `smart_scripts` (`entryorguid`,`source_type`,`id`,`link`,`event_type`,`event_phase_mask`,`event_chance`,`event_flags`,`event_param1`,`event_param2`,`event_param3`,`event_param4`,`action_type`,`action_param1`,`action_param2`,`action_param3`,`action_param4`,`action_param5`,`action_param6`,`target_type`,`target_param1`,`target_param2`,`target_param3`,`target_x`,`target_y`,`target_z`,`target_o`,`comment`) VALUES +(@Stormhoof,0,0,1,54,0,100,0,0,0,0,0,85,56900,0,0,0,0,0,1,0,0,0,0,0,0,0,'Stormhoof - On summoned - Cast power of Lorehammer on invoker /used to store invoker/'), +(@Stormhoof,0,1,0,61,0,100,0,0,0,0,0,85,@Ride,0,0,0,0,0,1,0,0,0,0,0,0,0,'Stormhoof - Linked with previous event - Ivoker cast Ride hardcoded on Stormhoof '), +(@Stormhoof,0,2,0,4,0,100,1,0,0,0,0,20,0,0,0,0,0,0,1,0,0,0,0,0,0,0,'Stormhoof - On aggro - Disable auto attacks'), +(@Stormhoof,0,3,0,8,0,100,0,@Ride,0,0,0,2,2141,0,0,0,0,0,1,0,0,0,0,0,0,0,'Stormhoof - On hit by spell Ride - Change faction to hostile towards Wind'), +(@Stormhoof,0,4,5,38,0,100,0,0,1,0,0,18,8196,0,0,0,0,0,1,0,0,0,0,0,0,0,'Stormhoof - On data set 0 1 - Set unit flags for No move, Silence and Pacified'), +(@Stormhoof,0,5,0,61,0,100,0,0,0,0,0,1,0,1000,0,0,0,0,1,0,0,0,0,0,0,0,'Stormhoof - Linked with previous event - Say text 1'), +(@Stormhoof,0,6,7,38,0,100,0,0,2,0,0,75,@Stun,0,0,0,0,0,19,@Wind,30,0,0,0,0,0,'Stormhoof - On Data set 0 2 - Set stun on wind'), +(@Stormhoof,0,7,8,61,0,100,0,0,0,0,0,37,0,0,0,0,0,0,1,0,0,0,0,0,0,0,'Stormhoof - Linked with previous event - Die'), +(@Stormhoof,0,8,0,61,0,100,0,0,0,0,0,47,0,0,0,0,0,0,1,0,0,0,0,0,0,0,'Stormhoof - Linked with previous event - Set unseen'), +(@Stormhoof,0,9,10,6,0,100,0,0,0,0,0,19,8196,0,0,0,0,0,1,0,0,0,0,0,0,0,'Stormhoof - On death - Remove unit_flags'), +(@Stormhoof,0,10,0,61,0,100,0,0,0,0,0,2,35,0,0,0,0,0,1,0,0,0,0,0,0,0,'Stormhoof - Linkedw with previous event - Change faction to 35'); + +-- Add SAI support for North Wind +UPDATE `creature_template` SET `mindmg`=327,`maxdmg`=362,`faction`=16,`AIName`='SmartAI' WHERE `entry`=@Wind; +DELETE FROM `smart_scripts` WHERE `entryorguid`=@Wind; +INSERT INTO `smart_scripts` (`entryorguid`,`source_type`,`id`,`link`,`event_type`,`event_phase_mask`,`event_chance`,`event_flags`,`event_param1`,`event_param2`,`event_param3`,`event_param4`,`action_type`,`action_param1`,`action_param2`,`action_param3`,`action_param4`,`action_param5`,`action_param6`,`target_type`,`target_param1`,`target_param2`,`target_param3`,`target_x`,`target_y`,`target_z`,`target_o`,`comment`) VALUES +(@Wind,0,0,0,0,0,100,0,1000,3000,8000,11000,11,61662,0,0,0,0,0,2,0,0,0,0,0,0,0,'North Wind - IC - Cast Cyclone'), +(@Wind,0,1,0,0,0,100,0,1000,8000,12000,16000,11,61663,0,0,0,0,0,2,0,0,0,0,0,0,0,'North Wind - IC - Gust of Wind'), +(@Wind,0,2,3,2,0,100,1,10,20,20000,20000,11,@DropHorn,0,0,0,0,0,1,0,0,0,0,0,0,0,'North Wind - Between 10 and 20% HP - Drop Horn'), +(@Wind,0,3,4,61,0,100,0,0,0,0,0,18,139270,0,0,0,0,0,1,0,0,0,0,0,0,0,'North Wind - Linked with previous event - Set unit flags for No move, Silence and Pacified'), +(@Wind,0,4,0,61,0,100,0,0,0,0,0,1,0,1200,0,0,0,0,1,0,0,0,0,0,0,0,'North Wind - Linked with previous event - Say text 0'), +(@Wind,0,5,6,52,0,100,0,0,@Wind,0,0,45,0,1,0,0,0,0,19,@Stormhoof,30,0,0,0,0,0,'North Wind - On text 0 over - Set data 0 1 on Stormhoof'), +(@Wind,0,6,0,61,0,100,0,0,0,0,0,1,1,4000,0,0,0,0,1,0,0,0,0,0,0,0,'North Wind - Linked with previous event - Say text 1'), +(@Wind,0,7,0,52,0,100,0,1,@Wind,0,0,45,0,2,0,0,0,0,19,@Stormhoof,30,0,0,0,0,0,'North Wind - On text over 1 - Data set 0 2 on Stormhoof'), +(@Wind,0,8,9,38,0,100,1,0,2,0,0,1,2,1000,0,0,0,0,1,0,0,0,0,0,0,0,'North Wind - On data set 0 2 - Say text 2'), +(@Wind,0,9,0,61,0,100,0,0,0,0,0,37,0,0,0,0,0,0,1,0,0,0,0,0,0,0,'North Wind - Linked with previous event - Die'), +(@Wind,0,10,0,6,0,100,0,0,0,0,0,19,139270,0,0,0,0,0,1,0,0,0,0,0,0,0,'North Wind - On death - Remove unit_flags'), +(@Wind,0,11,0,4,0,100,0,0,0,0,0,20,0,0,0,0,0,0,1,0,0,0,0,0,0,0,'North Wind - On Agro - Disable Auto Attack'); + +-- Texts +DELETE FROM `creature_text` WHERE `entry` IN (@Wind,@Stormhoof); + +INSERT INTO `creature_text` (`entry`, `groupid`, `id`, `text`, `type`, `language`, `probability`, `emote`, `duration`, `sound`, `comment`, `BroadcastTextID`) VALUES +(@Stormhoof, 0, 0, 'The horn! Use the horn on it while it''s weak!', 14, 0, 100, 1, 1000, 0, 'Stormhoof', 31183), +(@Wind, 2, 0, 'That horn is... MINE!', 14, 0, 100, 1, 2000, 0, 'North Wind', 31195), +(@Wind, 1, 0, 'I will never allow you to use the Horn of Elemental Fury against us! Die, weakling!', 14, 0, 100, 1, 3000, 0, 'North Wind', 31181), +(@Wind, 0, 0, 'Fool! You will never defeat me!', 14, 0, 100, 1, 1200, 0, 'North Wind', 31180); + +-- Add SAI for Horn of Elemental Fury +SET @GO := 194123; +UPDATE `gameobject_template` SET `AIName`= 'SmartGameObjectAI' WHERE `entry`=@GO; +DELETE FROM `smart_scripts` WHERE `source_type`=1 AND `entryorguid`=@GO; +INSERT INTO `smart_scripts` (`entryorguid`,`source_type`,`id`,`link`,`event_type`,`event_phase_mask`,`event_chance`,`event_flags`,`event_param1`,`event_param2`,`event_param3`,`event_param4`,`action_type`,`action_param1`,`action_param2`,`action_param3`,`action_param4`,`action_param5`,`action_param6`,`target_type`,`target_param1`,`target_param2`,`target_param3`,`target_x`,`target_y`,`target_z`,`target_o`,`comment`) VALUES +(@GO,1,0,0,70,0,100,0,2,0,0,0,45,0,2,0,0,0,0,19,@Wind,30,0,0,0,0,0,'Horn of Elemental Fury - On state changed - Data set 0 2 on The Winf of North'); + +-- Add Npc_spell_click data +UPDATE `creature_template` SET `npcflag`=16777216 WHERE `entry`=@Stormhoof; +DELETE FROM `npc_spellclick_spells` WHERE `npc_entry`=@Stormhoof; +INSERT INTO `npc_spellclick_spells` (`npc_entry`,`spell_id`,`cast_flags`,`user_type`) VALUES +(@Stormhoof,@Ride,1,0); From 6bd0ef737eef7ce5824ef13aed7103ae4d49a0fa Mon Sep 17 00:00:00 2001 From: Aokromes Date: Tue, 24 Jun 2014 20:06:34 +0200 Subject: [PATCH 05/80] DB/Quest: Brains! Brains! Brains! By dr-j, closes #10244 --- sql/updates/world/2014_06_24_06_world_misc.sql | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 sql/updates/world/2014_06_24_06_world_misc.sql diff --git a/sql/updates/world/2014_06_24_06_world_misc.sql b/sql/updates/world/2014_06_24_06_world_misc.sql new file mode 100644 index 0000000000..0c01fade35 --- /dev/null +++ b/sql/updates/world/2014_06_24_06_world_misc.sql @@ -0,0 +1,12 @@ +-- +SET @GRICKSBONESAW :=43286; +SET @DERANGEDEXPLORER :=23967; + +DELETE FROM `conditions` WHERE `SourceEntry`=@GRICKSBONESAW AND `SourceTypeOrReferenceId`=17; +INSERT INTO `conditions` (`SourceTypeOrReferenceId`, `SourceGroup`, `SourceEntry`, `SourceId`, `ElseGroup`, `ConditionTypeOrReference`, `ConditionTarget`, `ConditionValue1`, `ConditionValue2`, `ConditionValue3`, `NegativeCondition`, `ErrorTextId`, `ScriptName`, `Comment`) VALUES +(17,0,@GRICKSBONESAW ,0,0,36,1,0,0,0,1,0,'', 'Gricks Bonesaw can target only a corpse'), +(17,0,@GRICKSBONESAW ,0,0,31,1,3,@DERANGEDEXPLORER,0,0,0,'', 'Gricks Bonesaw can target only Deranged Explorer'); +UPDATE `creature_template` SET `AIName`='SmartAI' WHERE `entry` = @DERANGEDEXPLORER; +DELETE FROM `smart_scripts` WHERE `entryorguid` = @DERANGEDEXPLORER AND `source_type`=0; +INSERT INTO `smart_scripts` (`entryorguid`, `source_type`, `id`, `link`, `event_type`, `event_phase_mask`, `event_chance`, `event_flags`, `event_param1`, `event_param2`, `event_param3`, `event_param4`, `action_type`, `action_param1`, `action_param2`, `action_param3`, `action_param4`, `action_param5`, `action_param6`, `target_type`, `target_param1`, `target_param2`, `target_param3`, `target_x`, `target_y`, `target_z`, `target_o`, `comment`) VALUES +(@DERANGEDEXPLORER,0,0,1,8,0,100,1,@GRICKSBONESAW,0,0,0,41,1000,0,0,0,0,0,1,0,0,0,0,0,0,0, 'Deranged Explorer - On Spellhit Despawn after 1 second'); From d1bcaf36eef263f8dea7f432f61a24cdff6ffdd5 Mon Sep 17 00:00:00 2001 From: Aokromes Date: Tue, 24 Jun 2014 20:15:59 +0200 Subject: [PATCH 06/80] Update 2014_06_23_00_action_ip_logger.sql --- sql/updates/auth/2014_06_23_00_action_ip_logger.sql | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/sql/updates/auth/2014_06_23_00_action_ip_logger.sql b/sql/updates/auth/2014_06_23_00_action_ip_logger.sql index f5603738f5..375b97d35f 100644 --- a/sql/updates/auth/2014_06_23_00_action_ip_logger.sql +++ b/sql/updates/auth/2014_06_23_00_action_ip_logger.sql @@ -1,5 +1,4 @@ -ALTER TABLE `account` - ADD COLUMN `last_attempt_ip` VARCHAR(15) NOT NULL DEFAULT '127.0.0.1' AFTER `last_ip`; +ALTER TABLE `account` ADD COLUMN `last_attempt_ip` VARCHAR(15) NOT NULL DEFAULT '127.0.0.1' AFTER `last_ip`; CREATE TABLE `logs_ip_actions` ( `id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 'Unique Identifier', From b3ddccb389db9374922683f600a9687a237ea712 Mon Sep 17 00:00:00 2001 From: Aokromes Date: Tue, 24 Jun 2014 20:43:34 +0200 Subject: [PATCH 07/80] DB/Item: Tier 7.5 Mage set bonus By bazgrim, closes #10151 --- sql/updates/world/2014_06_24_07_world_spell_proc_event.sql | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 sql/updates/world/2014_06_24_07_world_spell_proc_event.sql diff --git a/sql/updates/world/2014_06_24_07_world_spell_proc_event.sql b/sql/updates/world/2014_06_24_07_world_spell_proc_event.sql new file mode 100644 index 0000000000..5eafb052ac --- /dev/null +++ b/sql/updates/world/2014_06_24_07_world_spell_proc_event.sql @@ -0,0 +1,5 @@ +-- Improved Mana Gems +DELETE FROM `spell_proc_event` WHERE `entry` IN (37447, 61062); +INSERT INTO `spell_proc_event` (`entry`,`SchoolMask`,`SpellFamilyName`,`SpellFamilyMask0`,`SpellFamilyMask1`,`SpellFamilyMask2`,`procFlags`,`procEx`,`ppmRate`,`CustomChance`,`Cooldown`) VALUES +(37447, 0, 3, 0, 0x00000100, 0, 0x04000, 0, 0, 0, 0), -- Serpent-Coil Braid +(61062, 0, 3, 0, 0x00000100, 0, 0x04000, 0, 0, 0, 0); -- 2/5 Frostfire Garb From 3f21b14bc0cb8425ee2d60e6d4864250d22b1762 Mon Sep 17 00:00:00 2001 From: Sebastian Valle Herrera Date: Tue, 24 Jun 2014 15:21:34 -0500 Subject: [PATCH 08/80] Core/Config: Fixed Rate.Corpse.Decay.Looted Looted creatures should now correctly obey the Rate.Corpse.Decay.Looted value. Closes #5358 --- src/server/game/Entities/Creature/Creature.cpp | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/server/game/Entities/Creature/Creature.cpp b/src/server/game/Entities/Creature/Creature.cpp index c81ba40949..1823c7c96c 100644 --- a/src/server/game/Entities/Creature/Creature.cpp +++ b/src/server/game/Entities/Creature/Creature.cpp @@ -2274,19 +2274,16 @@ void Creature::AllLootRemovedFromCorpse() if (m_corpseRemoveTime <= now) return; - float decayRate; + float decayRate = sWorld->getRate(RATE_CORPSE_DECAY_LOOTED); CreatureTemplate const* cinfo = GetCreatureTemplate(); - decayRate = sWorld->getRate(RATE_CORPSE_DECAY_LOOTED); - uint32 diff = uint32((m_corpseRemoveTime - now) * decayRate); - - m_respawnTime -= diff; - // corpse skinnable, but without skinning flag, and then skinned, corpse will despawn next update if (cinfo && cinfo->SkinLootId) m_corpseRemoveTime = time(NULL); else - m_corpseRemoveTime -= diff; + m_corpseRemoveTime = now + m_corpseDelay * decayRate; + + m_respawnTime = m_corpseRemoveTime + m_respawnTime; } } From 9892dfa662704c47e7260f1df2a4691b7cbb9cb8 Mon Sep 17 00:00:00 2001 From: Aokromes Date: Tue, 24 Jun 2014 23:40:02 +0200 Subject: [PATCH 09/80] DB/Quest: Remove Obsolete Midsummer Fire Festival quests By Exodius, closes #10265 --- sql/updates/world/2014_06_24_08_world_misc.sql | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 sql/updates/world/2014_06_24_08_world_misc.sql diff --git a/sql/updates/world/2014_06_24_08_world_misc.sql b/sql/updates/world/2014_06_24_08_world_misc.sql new file mode 100644 index 0000000000..9dd8f8f469 --- /dev/null +++ b/sql/updates/world/2014_06_24_08_world_misc.sql @@ -0,0 +1,15 @@ +-- Disable Midsummer Fire Festival quests that were removed in patch 2.4.0 +DELETE FROM `disables` WHERE `sourceType`=1 AND `entry` IN (9367,9368,9319,9386,9322,9323,9388,9389); +INSERT INTO `disables` (`sourceType`,`entry`,`flags`,`params_0`,`params_1`,`comment`) VALUES +(1,9367,0,0,0,'Quest: The Festival of Fire {A}, removed in patch 2.4.0'), +(1,9368,0,0,0,'Quest: The Festival of Fire {H}, removed in patch 2.4.0'), +(1,9319,0,0,0,'Quest: A Light in Dark Places, removed in patch 2.4.0'), +(1,9386,0,0,0,'Quest: A Light in Dark Places {Daily}, removed in patch 2.4.0'), +(1,9322,0,0,0,'Quest: Wild Fires in Kalimdor, removed in patch 2.4.0'), +(1,9323,0,0,0,'Quest: Wild Fires in the Eastern Kingdoms, removed in patch 2.4.0'), +(1,9388,0,0,0,'Quest: Flickering Flames in Kalimdor, removed in patch 2.4.0'), +(1,9389,0,0,0,'Quest: Flickering Flames in the Eastern Kingdoms, removed in patch 2.4.0'); + +-- Remove above mentioned quests from QuestStarters and QuestEnders +DELETE FROM `creature_queststarter` WHERE `quest` IN (9367,9368,9319,9386,9322,9323,9388,9389); +DELETE FROM `creature_questender` WHERE `quest` IN (9367,9368,9319,9386,9322,9323,9388,9389); From c2d1a36966d11ddb9da30117bc2a947a95167c06 Mon Sep 17 00:00:00 2001 From: Aokromes Date: Wed, 25 Jun 2014 00:09:59 +0200 Subject: [PATCH 10/80] DB/Quest: Lost Thunderbrew Recipe By dr-j, closes #5054 --- .../world/2014_06_24_09_world_misc.sql | 99 +++++++++++++++++++ 1 file changed, 99 insertions(+) create mode 100644 sql/updates/world/2014_06_24_09_world_misc.sql diff --git a/sql/updates/world/2014_06_24_09_world_misc.sql b/sql/updates/world/2014_06_24_09_world_misc.sql new file mode 100644 index 0000000000..fc092e0810 --- /dev/null +++ b/sql/updates/world/2014_06_24_09_world_misc.sql @@ -0,0 +1,99 @@ +-- +SET @CGuid := 71997; + +UPDATE `gameobject_template` SET `AIName`='SmartGameObjectAI', `ScriptName`='' WHERE `entry`=164911; +UPDATE `creature_template` SET `AIName`= 'SmartAI',`unit_flags`=0 WHERE `entry` IN(9537,9541); + +DELETE FROM `smart_scripts` WHERE `source_type`=1 AND `entryorguid`=164911; +DELETE FROM `smart_scripts` WHERE `source_type`=0 AND `entryorguid`IN(9537,-@CGuid,-@CGuid-1,-@CGuid-2); +INSERT INTO `smart_scripts` (`entryorguid`, `source_type`, `id`, `link`, `event_type`, `event_phase_mask`, `event_chance`, `event_flags`, `event_param1`, `event_param2`, `event_param3`, `event_param4`, `action_type`, `action_param1`, `action_param2`, `action_param3`, `action_param4`, `action_param5`, `action_param6`, `target_type`, `target_param1`, `target_param2`, `target_param3`, `target_x`, `target_y`, `target_z`, `target_o`, `comment`) VALUES +(164911, 1, 0 ,0, 70, 0, 100, 0, 2, 0, 0,0,45,1,1,0,0,0,0,13,164911,0,200,0,0,0,0, 'Thunderbrew Lager Keg - On State Changed - Set Data'), +(164911, 1, 1 ,0, 70, 2, 100, 0, 2, 0, 0,0,45,1,1,0,0,0,0,9,9537,0,200,0,0,0,0, 'Thunderbrew Lager Keg - On State Changed - Set Data'), +(164911, 1, 2 ,0, 38, 0, 100, 0, 1, 1, 0,0,23,1,0,0,0,0,0,9,9537,0,200,0,0,0,0, 'Thunderbrew Lager Keg - On Data Set - Increment Phase'), +-- +(9537, 0, 0 ,1, 11, 0, 100, 0, 0, 0, 0,0,18,320,0,0,0,0,0,1,0,0,0,0,0,0,0, 'Hurley Blackbreath - On Spawn - Set Unit Flags'), +(9537, 0, 1 ,0, 61, 0, 100, 0, 0, 0, 0,0,47,0,0,0,0,0,0,1,0,0,0,0,0,0,0, 'Hurley Blackbreath - Linked with Previous Event - Set Invisible'), +(9537, 0, 3 ,4, 38, 0, 100, 1, 1, 1, 0,0,47,1,0,0,0,0,0,1,0,0,0,0,0,0,0, 'Hurley Blackbreath - On Data Set - Set Visible'), +(9537, 0, 4 ,6, 61, 0, 100, 0, 0, 0, 0,0,45,1,1,0,0,0,0,9,9541,0,200,0,0,0,0, 'Hurley Blackbreath - Linked with Previous Event - Set Data'), +(9537, 0, 6 ,7, 61, 0, 100, 0, 0, 0, 0,0,1,0,0,0,0,0,0,1,0,0,0,0,0,0,0, 'Hurley Blackbreath - Linked with Previous Event - Say'), +(9537, 0, 7 ,0, 61, 0, 100, 0, 0, 0, 0,0,53,0,9537,0,0,0,0,1,0,0,0,0,0,0,0, 'Hurley Blackbreath - Linked with Previous Event - Start WP'), +(9537, 0, 8 ,9, 40, 0, 100, 0, 3, 0, 0,0,101,0,0,0,0,0,0,1,0,0,0,0,0,0,0, 'Hurley Blackbreath - On Reached WP 3 - Set Home Position'), +(9537, 0, 9 ,10, 61, 0, 100, 0, 0, 0, 0,0,19,320,0,0,0,0,0,1,0,0,0,0,0,0,0, 'Hurley Blackbreath - Linked with Previous Event - Set Unit Flags'), +(9537, 0, 10 ,15, 61, 0, 100, 0, 0, 0, 0,0,1,1,0,0,0,0,0,1,0,0,0,0,0,0,0, 'Hurley Blackbreath - Linked with Previous Event - Say'), +(9537, 0, 11 ,0, 2, 0, 100, 0, 0, 30, 30000,45000,11,14872,0,0,0,0,0,1,0,0,0,0,0,0,0, 'Hurley Blackbreath - On Less than 30% HP - Cast Drunken Rage'), +(9537, 0, 12 ,0, 9, 0, 100, 0, 0, 5, 10000,15000,11,9573,0,0,0,0,0,7,0,0,0,0,0,0,0, 'Hurley Blackbreath - On Range - Cast Flame Breath'), +(9537, 0, 13 ,0, 0, 0, 100, 0, 5000, 6000, 10000,14000,11,26211,0,0,0,0,0,2,0,0,0,0,0,0,0, 'Hurley Blackbreath - IC - Cast Hamstring'), +(9537, 0, 14 ,0, 0, 0, 100, 0, 5000, 8000, 8000,13000,11,16856,0,0,0,0,0,2,0,0,0,0,0,0,0, 'Hurley Blackbreath - IC - Cast Mortal Strike'), +(9537, 0, 15 ,0, 61, 0, 100, 0, 0, 0, 0,0,89,3,0,0,0,0,0,1,0,0,0,0,0,0,0, 'Hurley Blackbreath - Linked with Previous Event - Set Random Movement'), +(9537, 0, 16 ,17, 4, 0, 100, 0, 0, 0, 0,0,45,1,2,0,0,0,0,9,9541,0,200,0,0,0,0, 'Hurley Blackbreath - On Agro - Set Set Data'), +(9537, 0, 17 ,0, 61, 0, 100, 0, 0, 0, 0,0,49,0,0,0,0,0,0,7,0,0,0,0,0,0,0, 'Hurley Blackbreath - Linked with Previous Event - Attack'), +(9537, 0, 18 ,0, 38, 0, 100, 0, 1, 2, 0,0,49,0,0,0,0,0,0,21,200,0,0,0,0,0,0, 'Hurley Blackbreath - On Data Set - Attack'), +-- +(-@CGuid, 0, 0 ,1, 11, 0, 100, 0, 0, 0, 0,0,18,320,0,0,0,0,0,1,0,0,0,0,0,0,0, 'Blackbreath Crony - On Spawn - Set Unit Flags'), +(-@CGuid, 0, 1 ,0, 61, 0, 100, 0, 0, 0, 0,0,47,0,0,0,0,0,0,1,0,0,0,0,0,0,0, 'Blackbreath Crony - Linked with Previous Event - Set Invisible'), +(-@CGuid, 0, 2 ,4, 38, 0, 100, 0, 1, 1, 0,0,47,1,0,0,0,0,0,1,0,0,0,0,0,0,0, 'Blackbreath Crony - On Data Set - Set visible'), +(-@CGuid, 0, 4 ,0, 61, 0, 100, 0, 0, 0, 0,0,53,0,9541,0,0,0,0,1,0,0,0,0,0,0,0, 'Blackbreath Crony - Linked with Previous Event - Start WP'), +(-@CGuid, 0, 5 ,6, 40, 0, 100, 0, 3, 0, 0,0,101,0,0,0,0,0,0,1,0,0,0,0,0,0,0, 'Blackbreath Crony - On Reached WP 5 - Set Home Position'), +(-@CGuid, 0, 6 ,7, 61, 0, 100, 0, 0, 0, 0,0,19,320,0,0,0,0,0,1,0,0,0,0,0,0,0, 'Blackbreath Crony - Linked with Previous Event - Set Unit Flags'), +(-@CGuid, 0, 7 ,0, 61, 0, 100, 0, 0, 0, 0,0,89,3,0,0,0,0,0,1,0,0,0,0,0,0,0, 'Blackbreath Crony - Linked with Previous Event - Set Random Movement'), +(-@CGuid, 0, 8, 0, 0, 0, 100, 0, 1000, 3000, 4000, 7000, 11, 15581, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 'Blackbreath Crony - IC - Cast Sinister Strike'), +(-@CGuid, 0, 9, 0, 0, 0, 100, 0, 2000, 2000, 11000, 12000, 11, 15583, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 'Blackbreath Crony - IC - Cast Rupture'), +(-@CGuid, 0, 10 ,11, 4, 0, 100, 0, 0, 0, 0,0,45,1,2,0,0,0,0,9,9541,0,200,0,0,0,0, 'Hurley Blackbreath - On Agro - Set Set Data'), +(-@CGuid, 0, 11 ,12,61, 0, 100, 0, 0, 0, 0,0,45,1,2,0,0,0,0,9,9537,0,200,0,0,0,0, 'Hurley Blackbreath - On Agro - Set Set Data'), +(-@CGuid, 0, 12 ,0, 61, 0, 100, 0, 0, 0, 0,0,49,0,0,0,0,0,0,7,0,0,0,0,0,0,0, 'Hurley Blackbreath - Linked with Previous Event - Attack'), +(-@CGuid, 0, 13 ,0, 38, 0, 100, 0, 1, 2, 0,0,49,0,0,0,0,0,0,21,200,0,0,0,0,0,0, 'Hurley Blackbreath - On Data Set - Attack'), +-- +(-@CGuid-1, 0, 0 ,1, 11, 0, 100, 0, 0, 0, 0,0,18,320,0,0,0,0,0,1,0,0,0,0,0,0,0, 'Blackbreath Crony - On Spawn - Set Unit Flags'), +(-@CGuid-1, 0, 1 ,0, 61, 0, 100, 0, 0, 0, 0,0,47,0,0,0,0,0,0,1,0,0,0,0,0,0,0, 'Blackbreath Crony - Linked with Previous Event - Set Invisible'), +(-@CGuid-1, 0, 2 ,4, 38, 0, 100, 0, 1, 1, 0,0,47,1,0,0,0,0,0,1,0,0,0,0,0,0,0, 'Blackbreath Crony - On Data Set - Set visible'), +(-@CGuid-1, 0, 4 ,0, 61, 0, 100, 0, 0, 0, 0,0,53,0,9541000,0,0,0,0,1,0,0,0,0,0,0,0, 'Blackbreath Crony - Linked with Previous Event - Start WP'), +(-@CGuid-1, 0, 5 ,6, 40, 0, 100, 0, 3, 0, 0,0,101,0,0,0,0,0,0,1,0,0,0,0,0,0,0, 'Blackbreath Crony - On Reached WP 5 - Set Home Position'), +(-@CGuid-1, 0, 6 ,7, 61, 0, 100, 0, 0, 0, 0,0,19,320,0,0,0,0,0,1,0,0,0,0,0,0,0, 'Blackbreath Crony - Linked with Previous Event - Set Unit Flags'), +(-@CGuid-1, 0, 7 ,0, 61, 0, 100, 0, 0, 0, 0,0,89,3,0,0,0,0,0,1,0,0,0,0,0,0,0, 'Blackbreath Crony - Linked with Previous Event - Set Random Movement'), +(-@CGuid-1, 0, 8, 0, 0, 0, 100, 0, 1000, 3000, 4000, 7000, 11, 15581, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 'Blackbreath Crony - IC - Cast Sinister Strike'), +(-@CGuid-1, 0, 9, 0, 0, 0, 100, 0, 2000, 2000, 11000, 12000, 11, 15583, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 'Blackbreath Crony - IC - Cast Rupture'), +(-@CGuid-1, 0, 10 ,11, 4, 0, 100, 0, 0, 0, 0,0,45,1,2,0,0,0,0,9,9541,0,200,0,0,0,0, 'Blackbreath Crony - On Agro - Set Set Data'), +(-@CGuid-1, 0, 11 ,12,61, 0, 100, 0, 0, 0, 0,0,45,1,2,0,0,0,0,9,9537,0,200,0,0,0,0, 'Blackbreath Crony - On Agro - Set Set Data'), +(-@CGuid-1, 0, 12 ,0, 61, 0, 100, 0, 0, 0, 0,0,49,0,0,0,0,0,0,7,0,0,0,0,0,0,0, 'Blackbreath Crony - Linked with Previous Event - Attack'), +(-@CGuid-1, 0, 13 ,0, 38, 0, 100, 0, 1, 2, 0,0,49,0,0,0,0,0,0,21,200,0,0,0,0,0,0, 'Blackbreath Crony - On Data Set - Attack'), +-- +(-@CGuid-2, 0, 0 ,1, 11, 0, 100, 0, 0, 0, 0,0,18,320,0,0,0,0,0,1,0,0,0,0,0,0,0, 'Blackbreath Crony - On Spawn - Set Unit Flags'), +(-@CGuid-2, 0, 1 ,0, 61, 0, 100, 0, 0, 0, 0,0,47,0,0,0,0,0,0,1,0,0,0,0,0,0,0, 'Blackbreath Crony - Linked with Previous Event - Set Invisible'), +(-@CGuid-2, 0, 2 ,4, 38, 0, 100, 0, 1, 1, 0,0,47,1,0,0,0,0,0,1,0,0,0,0,0,0,0, 'Blackbreath Crony - On Data Set - Set visible'), +(-@CGuid-2, 0, 4 ,0, 61, 0, 100, 0, 0, 0, 0,0,53,0,9541001,0,0,0,0,1,0,0,0,0,0,0,0, 'Blackbreath Crony - Linked with Previous Event - Start WP'), +(-@CGuid-2, 0, 5 ,6, 40, 0, 100, 0, 3, 0, 0,0,101,0,0,0,0,0,0,1,0,0,0,0,0,0,0, 'Blackbreath Crony - On Reached WP 5 - Set Home Position'), +(-@CGuid-2, 0, 6 ,7, 61, 0, 100, 0, 0, 0, 0,0,19,320,0,0,0,0,0,1,0,0,0,0,0,0,0, 'Blackbreath Crony - Linked with Previous Event - Set Unit Flags'), +(-@CGuid-2, 0, 7 ,0, 61, 0, 100, 0, 0, 0, 0,0,89,3,0,0,0,0,0,1,0,0,0,0,0,0,0, 'Blackbreath Crony - Linked with Previous Event - Set Random Movement'), +(-@CGuid-2, 0, 8, 0, 0, 0, 100, 0, 2000, 2000, 11000, 12000, 11, 15581, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 'Blackbreath Crony - IC - Cast Sinister Strike'), +(-@CGuid-2, 0, 9, 0, 0, 0, 100, 0, 3000, 5000, 15000, 20000, 11, 15583, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 'Blackbreath Crony - IC - Cast Rupture'), +(-@CGuid-2, 0, 10 ,11, 4, 0, 100, 0, 0, 0, 0,0,45,1,2,0,0,0,0,9,9541,0,200,0,0,0,0, 'Blackbreath Crony - On Agro - Set Set Data'), +(-@CGuid-2, 0, 11 ,12,61, 0, 100, 0, 0, 0, 0,0,45,1,2,0,0,0,0,9,9537,0,200,0,0,0,0, 'Blackbreath Crony - On Agro - Set Set Data'), +(-@CGuid-2, 0, 12 ,0, 61, 0, 100, 0, 0, 0, 0,0,49,0,0,0,0,0,0,7,0,0,0,0,0,0,0, 'Blackbreath Crony - Linked with Previous Event - Attack'), +(-@CGuid-2, 0, 13 ,0, 38, 0, 100, 0, 1, 2, 0,0,49,0,0,0,0,0,0,21,200,0,0,0,0,0,0, 'Blackbreath Crony - On Data Set - Attack'); + +DELETE FROM `creature_text` WHERE `entry` =9537; +INSERT INTO `creature_text` (`entry`, `groupid`, `id`, `text`, `type`, `language`, `probability`, `emote`, `duration`, `sound`, `comment`, `BroadcastTextID`) VALUES +(9537, 1, 0, 'You''ll pay for that!', 12, 0, 100, 0, 0, 0, 'Hurley Blackbreath', 4936), +(9537, 0, 0, 'Get away from those kegs!', 14, 0, 100, 0, 0, 0, 'Hurley Blackbreath', 4934); + +DELETE FROM `waypoints` WHERE `entry`IN (9537,9541,9541000,9541001); +INSERT INTO `waypoints` (`entry`, `pointid`, `position_x`, `position_y`, `position_z`, `point_comment`) VALUES +(9537, 1, 886.916870,-149.176147,-49.760010, 'Hurley Blackbreath'), +(9537, 2, 897.698730,-137.324112,-49.752193, 'Hurley Blackbreath'), +(9537, 3, 898.569946,-141.830872,-49.755016, 'Hurley Blackbreath'), +(9541, 1, 885.926453,-145.907608,-49.760010, 'Blackbreath Crony'), +(9541, 2, 891.983093,-138.613907,-49.754261, 'Blackbreath Crony'), +(9541, 3, 896.412170,-144.160172,-49.757446, 'Blackbreath Crony'), +(9541000, 1, 888.984070,-152.598846,-49.760010, 'Blackbreath Crony'), +(9541000, 2, 895.560303,-134.140274,-49.748451, 'Blackbreath Crony'), +(9541000, 3, 897.199036,-139.512802,-49.755379, 'Blackbreath Crony'), +(9541001, 1, 886.916870,-149.176147,-49.760010, 'Blackbreath Crony'), +(9541001, 2, 899.913330,-133.534821,-49.747944, 'Blackbreath Crony'), +(9541001, 3, 901.009399,-139.603699,-49.755054, 'Blackbreath Crony'); + +DELETE FROM `creature` WHERE `id` IN(9537,9541); +INSERT INTO `creature` (`guid`, `id`, `map`, `spawnMask`, `phaseMask`, `modelid`, `equipment_id`, `position_x`, `position_y`, `position_z`, `orientation`, `spawntimesecs`, `spawndist`, `currentwaypoint`, `curhealth`, `curmana`, `MovementType`, `npcflag`, `unit_flags`, `dynamicflags`) VALUES +(@CGuid, 9541, 230, 1, 1, 0, 1, 879.018, -155.273, -49.7591, 0.57177, 300, 0, 0, 7359, 0, 0, 0, 0, 0), +(@CGuid+1, 9541, 230, 1, 1, 0, 1, 876.961, -151.796, -49.7591, 0.499513, 300, 0, 0, 7359, 0, 0, 0, 0, 0), +(@CGuid+2, 9541, 230, 1, 1, 0, 1, 876.535, -154.334, -49.7586, 0.339292, 300, 0, 0, 7359, 0, 0, 0, 0, 0), +(@CGuid+3, 9537, 230, 1, 1, 0, 1, 878.121, -153.074, -49.76, 0.527783, 300, 0, 0, 13070, 0, 0, 0, 0, 0); From cac491bef8c60be0a8b20e6d49480224e50ba0a1 Mon Sep 17 00:00:00 2001 From: Trisjdc Date: Wed, 25 Jun 2014 01:03:24 +0100 Subject: [PATCH 11/80] Fix a warning picked by static code analysis (assignment of a temporary variable before destruction) --- src/server/game/Entities/Player/Player.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index 9733c0f2b5..6548b2dffd 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -2920,11 +2920,10 @@ void Player::UninviteFromGroup() void Player::RemoveFromGroup(Group* group, uint64 guid, RemoveMethod method /* = GROUP_REMOVEMETHOD_DEFAULT*/, uint64 kicker /* = 0 */, const char* reason /* = NULL */) { - if (group) - { - group->RemoveMember(guid, method, kicker, reason); - group = NULL; - } + if (!group) + return; + + group->RemoveMember(guid, method, kicker, reason); } void Player::SendLogXPGain(uint32 GivenXP, Unit* victim, uint32 BonusXP, bool recruitAFriend, float /*group_rate*/) From a0fedd1d781bd6a4c6f2790e012a0f3ee12ccbf0 Mon Sep 17 00:00:00 2001 From: Subv Date: Tue, 24 Jun 2014 20:04:07 -0500 Subject: [PATCH 12/80] Core/Skinning: Fixed the creatures becoming non-skinnable even before looting them completely. Closes #5318 --- src/server/game/Entities/Creature/Creature.cpp | 2 +- src/server/game/Entities/Creature/Creature.h | 4 ++++ src/server/game/Entities/Player/Player.cpp | 8 ++++++-- src/server/game/Handlers/LootHandler.cpp | 6 +++++- src/server/game/Spells/Spell.cpp | 6 ++++-- src/server/game/Spells/SpellEffects.cpp | 17 +++++++++++++++-- 6 files changed, 35 insertions(+), 8 deletions(-) diff --git a/src/server/game/Entities/Creature/Creature.cpp b/src/server/game/Entities/Creature/Creature.cpp index 1823c7c96c..71143c3c7e 100644 --- a/src/server/game/Entities/Creature/Creature.cpp +++ b/src/server/game/Entities/Creature/Creature.cpp @@ -142,7 +142,7 @@ bool ForcedDespawnDelayEvent::Execute(uint64 /*e_time*/, uint32 /*p_time*/) } Creature::Creature(bool isWorldObject): Unit(isWorldObject), MapObject(), -lootForPickPocketed(false), lootForBody(false), m_groupLootTimer(0), lootingGroupLowGUID(0), +lootForPickPocketed(false), lootForBody(false), lootForSkinned(false), _skinner(0), m_groupLootTimer(0), lootingGroupLowGUID(0), m_PlayerDamageReq(0), m_lootRecipient(0), m_lootRecipientGroup(0), m_corpseRemoveTime(0), m_respawnTime(0), m_respawnDelay(300), m_corpseDelay(60), m_respawnradius(0.0f), m_reactState(REACT_AGGRESSIVE), m_defaultMovementType(IDLE_MOTION_TYPE), m_DBTableGuid(0), m_equipmentId(0), m_originalEquipmentId(0), m_AlreadyCallAssistance(false), diff --git a/src/server/game/Entities/Creature/Creature.h b/src/server/game/Entities/Creature/Creature.h index 9cc08e3b71..069308c9e8 100644 --- a/src/server/game/Entities/Creature/Creature.h +++ b/src/server/game/Entities/Creature/Creature.h @@ -553,6 +553,9 @@ class Creature : public Unit, public GridObject, public MapObject Loot loot; bool lootForPickPocketed; bool lootForBody; + bool lootForSkinned; + void SetSkinner(uint64 guid) { _skinner = guid; } + uint64 GetSkinner() const { return _skinner; } // Returns the player who skinned this creature Player* GetLootRecipient() const; Group* GetLootRecipientGroup() const; bool hasLootRecipient() const { return m_lootRecipient || m_lootRecipientGroup; } @@ -688,6 +691,7 @@ class Creature : public Unit, public GridObject, public MapObject uint64 m_lootRecipient; uint32 m_lootRecipientGroup; + uint64 _skinner; /// Timers time_t m_corpseRemoveTime; // (msecs)timer for death or corpse disappearance diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index 6548b2dffd..b28d20c937 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -9020,8 +9020,12 @@ void Player::SendLoot(uint64 guid, LootType loot_type) // possible only if creature->lootForBody && loot->empty() at spell cast check if (loot_type == LOOT_SKINNING) { - loot->clear(); - loot->FillLoot(creature->GetCreatureTemplate()->SkinLootId, LootTemplates_Skinning, this, true); + if (!creature->lootForSkinned) + { + creature->lootForSkinned = true; + loot->clear(); + loot->FillLoot(creature->GetCreatureTemplate()->SkinLootId, LootTemplates_Skinning, this, true); + } permission = OWNER_PERMISSION; } // set group rights only for loot_type != LOOT_SKINNING diff --git a/src/server/game/Handlers/LootHandler.cpp b/src/server/game/Handlers/LootHandler.cpp index 61f0b9afce..2464a5385a 100644 --- a/src/server/game/Handlers/LootHandler.cpp +++ b/src/server/game/Handlers/LootHandler.cpp @@ -355,11 +355,15 @@ void WorldSession::DoLootRelease(uint64 lguid) loot = &creature->loot; if (loot->isLooted()) { + creature->RemoveFlag(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_LOOTABLE); + + if (loot->loot_type == LOOT_SKINNING) + creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_SKINNABLE); + // skip pickpocketing loot for speed, skinning timer reduction is no-op in fact if (!creature->IsAlive()) creature->AllLootRemovedFromCorpse(); - creature->RemoveFlag(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_LOOTABLE); loot->clear(); } else diff --git a/src/server/game/Spells/Spell.cpp b/src/server/game/Spells/Spell.cpp index 48c2a76578..f9a5e742f9 100644 --- a/src/server/game/Spells/Spell.cpp +++ b/src/server/game/Spells/Spell.cpp @@ -5058,11 +5058,13 @@ SpellCastResult Spell::CheckCast(bool strict) return SPELL_FAILED_TARGET_UNSKINNABLE; Creature* creature = m_targets.GetUnitTarget()->ToCreature(); - if (creature->GetCreatureType() != CREATURE_TYPE_CRITTER && !creature->loot.isLooted()) + if (creature->GetCreatureType() != CREATURE_TYPE_CRITTER && creature->loot.loot_type != LOOT_SKINNING && !creature->loot.isLooted()) return SPELL_FAILED_TARGET_NOT_LOOTED; uint32 skill = creature->GetCreatureTemplate()->GetRequiredLootSkill(); + bool alreadySkinned = creature->loot.loot_type == LOOT_SKINNING && creature->GetSkinner() == m_caster->GetGUID(); + int32 skillValue = m_caster->ToPlayer()->GetSkillValue(skill); int32 TargetLevel = m_targets.GetUnitTarget()->getLevel(); int32 ReqValue = (skillValue < 100 ? (TargetLevel-10) * 10 : TargetLevel * 5); @@ -5072,7 +5074,7 @@ SpellCastResult Spell::CheckCast(bool strict) // chance for fail at orange skinning attempt if ((m_selfContainer && (*m_selfContainer) == this) && skillValue < sWorld->GetConfigMaxSkillValue() && - (ReqValue < 0 ? 0 : ReqValue) > irand(skillValue - 25, skillValue + 37)) + (ReqValue < 0 ? 0 : ReqValue) > irand(skillValue - 25, skillValue + 37) && !alreadySkinned) return SPELL_FAILED_TRY_AGAIN; break; diff --git a/src/server/game/Spells/SpellEffects.cpp b/src/server/game/Spells/SpellEffects.cpp index 9cf0e1ae45..0a43c5a2c4 100644 --- a/src/server/game/Spells/SpellEffects.cpp +++ b/src/server/game/Spells/SpellEffects.cpp @@ -4702,15 +4702,28 @@ void Spell::EffectSkinning(SpellEffIndex /*effIndex*/) uint32 skill = creature->GetCreatureTemplate()->GetRequiredLootSkill(); + bool awardPoints = true; + + // Check if a skinning loot table was already generated for this creature + if (creature->loot.loot_type == LOOT_SKINNING) + { + if (creature->GetSkinner() != m_caster->GetGUID()) + return; + + awardPoints = false; // Do not grant skill points for this loot, they were already granted the first time. + } + else + creature->SetSkinner(m_caster->GetGUID()); + m_caster->ToPlayer()->SendLoot(creature->GetGUID(), LOOT_SKINNING); - creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_SKINNABLE); int32 reqValue = targetLevel < 10 ? 0 : targetLevel < 20 ? (targetLevel-10)*10 : targetLevel*5; int32 skillValue = m_caster->ToPlayer()->GetPureSkillValue(skill); // Double chances for elites - m_caster->ToPlayer()->UpdateGatherSkill(skill, skillValue, reqValue, creature->isElite() ? 2 : 1); + if (awardPoints) + m_caster->ToPlayer()->UpdateGatherSkill(skill, skillValue, reqValue, creature->isElite() ? 2 : 1); } void Spell::EffectCharge(SpellEffIndex /*effIndex*/) From 8c944f1456af624ab98672390be63d16cc244a7f Mon Sep 17 00:00:00 2001 From: Subv Date: Tue, 24 Jun 2014 22:13:59 -0500 Subject: [PATCH 13/80] Core/Loot: Make the pickpocket loot refill every 10 minutes by default on the NPCs after they have been pickpocketed. Configurable by Creature.PickPocketRefillDelay Closes #935 --- .../game/Entities/Creature/Creature.cpp | 21 ++++++++++++++++--- src/server/game/Entities/Creature/Creature.h | 3 +++ src/server/game/Entities/Player/Player.cpp | 1 + src/server/game/Entities/Unit/Unit.cpp | 3 +++ src/server/game/World/World.cpp | 2 ++ src/server/game/World/World.h | 1 + src/server/worldserver/worldserver.conf.dist | 8 +++++++ 7 files changed, 36 insertions(+), 3 deletions(-) diff --git a/src/server/game/Entities/Creature/Creature.cpp b/src/server/game/Entities/Creature/Creature.cpp index 71143c3c7e..84393070c7 100644 --- a/src/server/game/Entities/Creature/Creature.cpp +++ b/src/server/game/Entities/Creature/Creature.cpp @@ -142,7 +142,7 @@ bool ForcedDespawnDelayEvent::Execute(uint64 /*e_time*/, uint32 /*p_time*/) } Creature::Creature(bool isWorldObject): Unit(isWorldObject), MapObject(), -lootForPickPocketed(false), lootForBody(false), lootForSkinned(false), _skinner(0), m_groupLootTimer(0), lootingGroupLowGUID(0), +lootForPickPocketed(false), _pickpocketLootRestore(0), lootForBody(false), lootForSkinned(false), _skinner(0), m_groupLootTimer(0), lootingGroupLowGUID(0), m_PlayerDamageReq(0), m_lootRecipient(0), m_lootRecipientGroup(0), m_corpseRemoveTime(0), m_respawnTime(0), m_respawnDelay(300), m_corpseDelay(60), m_respawnradius(0.0f), m_reactState(REACT_AGGRESSIVE), m_defaultMovementType(IDLE_MOTION_TYPE), m_DBTableGuid(0), m_equipmentId(0), m_originalEquipmentId(0), m_AlreadyCallAssistance(false), @@ -547,6 +547,15 @@ void Creature::Update(uint32 diff) if (!IsAlive()) break; + time_t now = time(NULL); + + // Check if we should refill the pickpocketing loot + if (lootForPickPocketed && _pickpocketLootRestore && _pickpocketLootRestore <= now) + { + lootForPickPocketed = false; + _pickpocketLootRestore = 0; + } + if (m_regenTimer > 0) { if (diff >= m_regenTimer) @@ -1527,9 +1536,10 @@ void Creature::Respawn(bool force) TC_LOG_DEBUG("entities.unit", "Respawning creature %s (GuidLow: %u, Full GUID: " UI64FMTD " Entry: %u)", GetName().c_str(), GetGUIDLow(), GetGUID(), GetEntry()); m_respawnTime = 0; + _pickpocketLootRestore = 0; lootForPickPocketed = false; - lootForBody = false; - + lootForBody = false; + lootForSkinned = false; if (m_originalEntry != GetEntry()) UpdateEntry(m_originalEntry); @@ -2709,3 +2719,8 @@ void Creature::ReleaseFocus(Spell const* focusSpell) ClearUnitState(UNIT_STATE_ROTATING); } +void Creature::StartPickPocketRefillTimer() +{ + _pickpocketLootRestore = time(NULL) + sWorld->getIntConfig(CONFIG_CREATURE_PICKPOCKET_REFILL); +} + diff --git a/src/server/game/Entities/Creature/Creature.h b/src/server/game/Entities/Creature/Creature.h index 069308c9e8..2360d04c45 100644 --- a/src/server/game/Entities/Creature/Creature.h +++ b/src/server/game/Entities/Creature/Creature.h @@ -554,6 +554,8 @@ class Creature : public Unit, public GridObject, public MapObject bool lootForPickPocketed; bool lootForBody; bool lootForSkinned; + void StartPickPocketRefillTimer(); + void ResetPickPocketRefillTimer() { _pickpocketLootRestore = 0; } void SetSkinner(uint64 guid) { _skinner = guid; } uint64 GetSkinner() const { return _skinner; } // Returns the player who skinned this creature Player* GetLootRecipient() const; @@ -694,6 +696,7 @@ class Creature : public Unit, public GridObject, public MapObject uint64 _skinner; /// Timers + time_t _pickpocketLootRestore; time_t m_corpseRemoveTime; // (msecs)timer for death or corpse disappearance time_t m_respawnTime; // (secs) time of next respawn uint32 m_respawnDelay; // (secs) delay between corpse disappearance and respawning diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index b28d20c937..fb4d4c3dda 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -8972,6 +8972,7 @@ void Player::SendLoot(uint64 guid, LootType loot_type) if (!creature->lootForPickPocketed) { creature->lootForPickPocketed = true; + creature->StartPickPocketRefillTimer(); loot->clear(); if (uint32 lootid = creature->GetCreatureTemplate()->pickpocketLootId) diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp index 5e12022312..d0cc8d771f 100644 --- a/src/server/game/Entities/Unit/Unit.cpp +++ b/src/server/game/Entities/Unit/Unit.cpp @@ -15272,7 +15272,10 @@ void Unit::Kill(Unit* victim, bool durabilityLoss) { Loot* loot = &creature->loot; if (creature->lootForPickPocketed) + { + creature->ResetPickPocketRefillTimer(); creature->lootForPickPocketed = false; + } loot->clear(); if (uint32 lootid = creature->GetCreatureTemplate()->lootid) diff --git a/src/server/game/World/World.cpp b/src/server/game/World/World.cpp index b643d127c0..62603cce95 100644 --- a/src/server/game/World/World.cpp +++ b/src/server/game/World/World.cpp @@ -1044,6 +1044,8 @@ void World::LoadConfigSettings(bool reload) m_bool_configs[CONFIG_OFFHAND_CHECK_AT_SPELL_UNLEARN] = sConfigMgr->GetBoolDefault("OffhandCheckAtSpellUnlearn", true); + m_int_configs[CONFIG_CREATURE_PICKPOCKET_REFILL] = sConfigMgr->GetIntDefault("Creature.PickPocketRefillDelay", 10 * MINUTE); + if (int32 clientCacheId = sConfigMgr->GetIntDefault("ClientCacheVersion", 0)) { // overwrite DB/old value diff --git a/src/server/game/World/World.h b/src/server/game/World/World.h index efd7570992..61acdc37b0 100644 --- a/src/server/game/World/World.h +++ b/src/server/game/World/World.h @@ -333,6 +333,7 @@ enum WorldIntConfigs CONFIG_BG_REWARD_LOSER_HONOR_FIRST, CONFIG_BG_REWARD_LOSER_HONOR_LAST, CONFIG_BIRTHDAY_TIME, + CONFIG_CREATURE_PICKPOCKET_REFILL, INT_CONFIG_VALUE_COUNT }; diff --git a/src/server/worldserver/worldserver.conf.dist b/src/server/worldserver/worldserver.conf.dist index f71b7bb815..5a3ea2bb4e 100644 --- a/src/server/worldserver/worldserver.conf.dist +++ b/src/server/worldserver/worldserver.conf.dist @@ -1352,6 +1352,14 @@ Rate.Creature.Elite.RARE.HP = 1 Rate.Creature.Elite.RAREELITE.HP = 1 Rate.Creature.Elite.WORLDBOSS.HP = 1 +# +# Creature.PickPocketRefillDelay +# Description: Time in seconds that the server will wait before refilling the pickpocket loot +# for a creature +# Default: 600 + +Creature.PickPocketRefillDelay = 600 + # # ListenRange.Say # Description: Distance in which players can read say messages from creatures or From db0fe628a2618e02bf355d0b55960331f349ff7f Mon Sep 17 00:00:00 2001 From: Aokromes Date: Wed, 25 Jun 2014 05:32:30 +0200 Subject: [PATCH 14/80] Update and rename 2014_06_24_05_world_quest.sql to 2014_06_25_00_world_quest.sql --- sql/updates/world/2014_06_24_05_world_quest.sql | 2 -- sql/updates/world/2014_06_25_00_world_quest.sql | 2 ++ 2 files changed, 2 insertions(+), 2 deletions(-) delete mode 100644 sql/updates/world/2014_06_24_05_world_quest.sql create mode 100644 sql/updates/world/2014_06_25_00_world_quest.sql diff --git a/sql/updates/world/2014_06_24_05_world_quest.sql b/sql/updates/world/2014_06_24_05_world_quest.sql deleted file mode 100644 index 49431fdf46..0000000000 --- a/sql/updates/world/2014_06_24_05_world_quest.sql +++ /dev/null @@ -1,2 +0,0 @@ --- -UPDATE `quest_template` SET `SpecialFlags`=0 WHERE `entry`=8490; diff --git a/sql/updates/world/2014_06_25_00_world_quest.sql b/sql/updates/world/2014_06_25_00_world_quest.sql new file mode 100644 index 0000000000..77f5bf0547 --- /dev/null +++ b/sql/updates/world/2014_06_25_00_world_quest.sql @@ -0,0 +1,2 @@ +-- +UPDATE `quest_template` SET `SpecialFlags`=0 WHERE `Id`=8490; From 1320c9f8d60d36624c1aa920533622eb84494812 Mon Sep 17 00:00:00 2001 From: Subv Date: Tue, 24 Jun 2014 23:00:54 -0500 Subject: [PATCH 15/80] Core/SmartAI: Allow the use of SMART_ACTION_SET_EVENT_PHASE with SMART_EVENT_RESPAWN. Closes #9289 --- src/server/game/AI/SmartScripts/SmartAI.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/server/game/AI/SmartScripts/SmartAI.cpp b/src/server/game/AI/SmartScripts/SmartAI.cpp index e36433dd8c..af47b52f50 100644 --- a/src/server/game/AI/SmartScripts/SmartAI.cpp +++ b/src/server/game/AI/SmartScripts/SmartAI.cpp @@ -537,9 +537,9 @@ void SmartAI::JustRespawned() me->SetVisible(true); if (me->getFaction() != me->GetCreatureTemplate()->faction) me->RestoreFaction(); - GetScript()->ProcessEventsFor(SMART_EVENT_RESPAWN); mJustReset = true; JustReachedHome(); + GetScript()->ProcessEventsFor(SMART_EVENT_RESPAWN); mFollowGuid = 0;//do not reset follower on Reset(), we need it after combat evade mFollowDist = 0; mFollowAngle = 0; From 04d72fa73f1c3f6ac1475ff5908bb53f3b54af3d Mon Sep 17 00:00:00 2001 From: Aokromes Date: Wed, 25 Jun 2014 06:23:04 +0200 Subject: [PATCH 16/80] DB/Creature: Midsummer Fire Festival Spawns Closes #1515 --- .../world/2014_06_25_01_world_misc.sql | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 sql/updates/world/2014_06_25_01_world_misc.sql diff --git a/sql/updates/world/2014_06_25_01_world_misc.sql b/sql/updates/world/2014_06_25_01_world_misc.sql new file mode 100644 index 0000000000..88e47f6510 --- /dev/null +++ b/sql/updates/world/2014_06_25_01_world_misc.sql @@ -0,0 +1,33 @@ +-- +SET @CGUID := 127340; +INSERT INTO `creature` (`guid`, `id`, `map`, `spawnMask`, `phaseMask`, `position_x`, `position_y`, `position_z`, `orientation`, `spawntimesecs`, `spawndist`, `MovementType`) VALUES +(@CGUID+0, 32801, 571, 1, 1, 4131.279, 5394.604, 26.09047, 3.595378, 120, 0, 0), -- 32801 +(@CGUID+1, 32809, 571, 1, 1, 4454.667, 5623.537, 56.91562, 4.13643, 120, 0, 0), -- 32809 +(@CGUID+2, 32810, 571, 1, 1, 5499.812, 4878.976, -197.8652, 3.176499, 120, 0, 0), -- 32810 +(@CGUID+3, 32802, 571, 1, 1, 5360.002, 4834.393, -196.3981, 5.550147, 120, 0, 0), -- 32802 +(@CGUID+4, 32811, 571, 1, 1, 3762.387, 1481.287, 92.8882, 3.438299, 120, 0, 0), -- 32811 +(@CGUID+5, 32803, 571, 1, 1, 3936.924, -595.4437, 241.1534, 5.951573, 120, 0, 0), -- 32803 +(@CGUID+6, 32812, 571, 1, 1, 2586.682, -4337.068, 276.0698, 4.13643, 120, 0, 0), -- 32812 +(@CGUID+7, 32804, 571, 1, 1, 2466.965, -4892.675, 262.5474, 2.303835, 120, 0, 0), -- 32804 +(@CGUID+8, 32813, 571, 1, 1, 3376.009, -2124.778, 124.664, 0.1396263, 120, 0, 0), -- 32813 +(@CGUID+9, 32805, 571, 1, 1, 3400.944, -2890.375, 201.4968, 2.303835, 120, 0, 0), -- 32805 +(@CGUID+10, 32814, 571, 1, 1, 6150.945, -1023.05, 408.3642, 1.43117, 120, 0, 0), -- 32814 +(@CGUID+11, 32806, 571, 1, 1, 6087.442, -1105.523, 418.2672, 1.099557, 120, 0, 0), -- 32806 +(@CGUID+12, 32815, 571, 1, 1, 5536.516, -733.7185, 149.6217, 3.124139, 120, 0, 0), -- 32815 +(@CGUID+13, 32807, 571, 1, 1, 5141.776, -685.0027, 170.2738, 5.951573, 120, 0, 0), -- 32807 +(@CGUID+14, 32816, 571, 1, 1, 5280.307, -2766.146, 292.5019, 2.303835, 120, 0, 0), -- 32816 +(@CGUID+15, 32808, 571, 1, 1, 5627.932, -2616.493, 292.5019, 1.518436, 120, 0, 0), -- 32808 +(@CGUID+16, 25892, 0, 1, 1, -8258.798, -2618.704, 133.3752, 1.343904, 120, 0, 0), -- 25892 +(@CGUID+17, 25899, 1, 1, 1, -4391.495, 2187.865, 12.22644, 5.864306, 120, 0, 0), -- 25899 +(@CGUID+18, 25936, 1, 1, 1, -2321.744, -619.7491, -9.089149, 5.67232, 120, 0, 0), -- 25936 +(@CGUID+19, 25888, 530, 1, 1, -4219.219, -12310.55, 2.439188, 5.934119, 120, 0, 0), -- 25888 +(@CGUID+20, 25891, 530, 1, 1, -2234.83, -11895.94, 26.62421, 4.694936, 120, 0, 0), -- 25891 +(@CGUID+21, 25907, 530, 1, 1, -2998.854, 4164.042, 5.169014, 0.5934119, 120, 0, 0), -- 25907 +(@CGUID+22, 25938, 530, 1, 1, -3067.278, 2387.903, 62.19349, 3.368485, 120, 0, 0), -- 25938 +(@CGUID+23, 25905, 530, 1, 1, -3951.305, 2045.65, 95.14804, 4.520403, 120, 0, 0), -- 25905 +(@CGUID+24, 25912, 530, 1, 1, 193.7309, 6016.713, 22.73373, 6.161012, 120, 0, 0), -- 25912 +(@CGUID+25, 25903, 530, 1, 1, -2517.064, 7555.085, -1.793157, 5.375614, 120, 0, 0), -- 25903 +(@CGUID+26, 25889, 530, 1, 1, 2024.906, 6590.532, 135.3214, 5.934119, 120, 0, 0), -- 25889 +(@CGUID+27, 25926, 530, 1, 1, 2286.707, 6137.618, 136.3056, 1.570796, 120, 0, 0), -- 25926 +(@CGUID+28, 25913, 530, 1, 1, 3127.551, 3743.148, 141.7706, 6.161012, 120, 0, 0), -- 25913 +(@CGUID+29, 25918, 530, 1, 1, 2917.222, 3693.411, 143.7703, 2.932153, 120, 0, 0); -- 25918 From 689f7c2fc6a69475151f1b0f15e636833ee38b86 Mon Sep 17 00:00:00 2001 From: Aokromes Date: Wed, 25 Jun 2014 06:38:17 +0200 Subject: [PATCH 17/80] DB/Creature: Challe's Home For Little Tykes Closes #5921 --- sql/updates/world/2014_06_25_02_world_creature.sql | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 sql/updates/world/2014_06_25_02_world_creature.sql diff --git a/sql/updates/world/2014_06_25_02_world_creature.sql b/sql/updates/world/2014_06_25_02_world_creature.sql new file mode 100644 index 0000000000..8c43786c8e --- /dev/null +++ b/sql/updates/world/2014_06_25_02_world_creature.sql @@ -0,0 +1,6 @@ +-- +SET @CGUID := 72776; +INSERT INTO `creature` (`guid`, `id`, `map`, `spawnMask`, `phaseMask`, `position_x`, `position_y`, `position_z`, `orientation`, `spawntimesecs`, `spawndist`, `MovementType`) VALUES +(@CGUID+0, 23108, 530, 1, 1, -493.0895, 7448.848, 178.6083, 4.83718, 120, 5, 1), -- 23108 (Area: 0) +(@CGUID+1, 23107, 530, 1, 1, -475.5646, 7463.805, 181.9694, 1.496497, 120, 5, 1), -- 23107 (Area: 0) +(@CGUID+2, 23106, 530, 1, 1, -511.7721, 7500.889, 183.1165, 5.096361, 120, 0, 0); -- 23106 (Area: 0) From d9281bc45bcb71bb9000f4d7cf6c4d55b25464f4 Mon Sep 17 00:00:00 2001 From: Aokromes Date: Wed, 25 Jun 2014 10:59:59 +0200 Subject: [PATCH 18/80] DB/Quest: A Tangled Skein By Trista, closes #6584 --- .../world/2014_06_25_03_world_misc.sql | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 sql/updates/world/2014_06_25_03_world_misc.sql diff --git a/sql/updates/world/2014_06_25_03_world_misc.sql b/sql/updates/world/2014_06_25_03_world_misc.sql new file mode 100644 index 0000000000..3ddd98aa8d --- /dev/null +++ b/sql/updates/world/2014_06_25_03_world_misc.sql @@ -0,0 +1,40 @@ +-- Add support for {Q} A Tangled Skein ID: 12555 +-- rewritten from sniff: thx Pitcrawler +SET @Sprayer := 28274; +SET @Thrower := 51165; -- Throws a web +SET @Summon := 51314; -- Summons Broken Sprayer GO +SET @Explosion := 53236; -- Explosion on fall +SET @Credit := 28289; -- Kill Credit +SET @Trigger := 29457; -- Trigger to permit explosion only when Sprayer reach him +SET @SpellTrigger := 51173; -- A Tangled Skein: Encasing Webs - Effect that procs from @Thrower +SET @EncasingWebs := 51168; -- The visual that A Tangled Skein: Encasing Webs - Effect should apply +SET @SummonTrigger := 54496; +SET @Script := 2827400; +-- Add SAI support for Plague Sprayer +UPDATE `creature_template` SET `AIName`='SmartAI' WHERE `entry`=@Sprayer; -- they shouldn't be attackable, but unit_flag 2 cause evade +UPDATE `creature_template` SET `AIName`='SmartAI',`InhabitType`=1 WHERE `entry`=@Trigger; -- need this to make trigger (while guardian) stay at ground +DELETE FROM `smart_scripts` WHERE `entryorguid` IN (@Sprayer,@Trigger) AND `source_type`=0; +DELETE FROM `smart_scripts` WHERE `entryorguid`=@Script AND `source_type`=9; +INSERT INTO `smart_scripts` (`entryorguid`,`source_type`,`id`,`link`,`event_type`,`event_phase_mask`,`event_chance`,`event_flags`,`event_param1`,`event_param2`,`event_param3`,`event_param4`,`action_type`,`action_param1`,`action_param2`,`action_param3`,`action_param4`,`action_param5`,`action_param6`,`target_type`,`target_param1`,`target_param2`,`target_param3`,`target_x`,`target_y`,`target_z`,`target_o`,`comment`) VALUES +(@Trigger,0,0,0,54,0,100,0,0,0,0,0,75,@EncasingWebs,0,0,0,0,0,7,0,0,0,0,0,0,0, 'Plagued Sprayer - On just summoned - Cast Encasing Webs'), +(@Sprayer,0,0,0,25,0,100,0,0,0,0,0,8,0,0,0,0,0,0,1,0,0,0,0,0,0,0, 'Plagued Sprayer - On spawn/reset - Set react state passive'), +(@Sprayer,0,1,0,8,0,100,1,@Thrower,0,0,0,64,1,0,0,0,0,0,7,0,0,0,0,0,0,0, 'Plagued Sprayer - On spell hit by player with thrower - Store invoker as target'), +(@Sprayer,0,2,3,8,0,100,1,@SpellTrigger,0,0,0,11,@SummonTrigger,0,0,0,0,0,1,0,0,0,0,0,0,0, 'Plagued Sprayer - On spell hit by item spell - Cast summon trigger'), +(@Sprayer,0,3,0,61,0,100,1,0,0,0,0,59,1,0,0,0,0,0,1,0,0,0,0,0,0,0, 'Plagued Sprayer - Linked with previous event - Set run on'), +(@Sprayer,0,4,0,23,0,100,1,@EncasingWebs,1,0,0,69,1,0,0,0,0,0,19,@Trigger,20,0,0,0,0,0, 'Plagued Sprayer - On creature has aura Encasing Webs - Move to closest trigger in 20 yards'), +(@Sprayer,0,5,0,34,0,100,1,8,1,0,0,80,@Script,2,0,0,0,0,1,0,0,0,0,0,0,0, 'Plagued Sprayer - On point 1 reached - Start action list'), +-- Script +(@Script,9,0,0,0,0,100,0,0,0,0,0,11,@Explosion,0,0,0,0,0,1,0,0,0,0,0,0,0,'Plagued Sprayer - Action 0 - Cast huge explosion on self'), +(@Script,9,1,0,0,0,100,0,300,300,0,0,33,@Credit,0,0,0,0,0,12,1,0,0,0,0,0,0,'Plagued Sprayer - Action 1 - Give credit to stored invoker'), +(@Script,9,2,0,0,0,100,0,0,0,0,0,11,@Summon,0,0,0,0,0,1,0,0,0,0,0,0,0,'Plagued Sprayer - Action 2 - Cast summon broken GO sprayer on self'), -- need small delay for the explosion to be displayed +(@Script,9,3,0,0,0,100,0,0,0,0,0,37,0,0,0,0,0,0,1,0,0,0,0,0,0,0,'Plagued Sprayer - Action 3 - Die'), +(@Script,9,4,0,0,0,100,0,0,0,0,0,47,0,0,0,0,0,0,1,0,0,0,0,0,0,0,'Plagued Sprayer - Action 4 - Set unseen'); +-- Conditions +DELETE FROM `conditions` WHERE `SourceEntry`=@Thrower AND `SourceTypeOrReferenceId`=17; +INSERT INTO `conditions` (`SourceTypeOrReferenceId`, `SourceGroup`, `SourceEntry`, `SourceId`, `ElseGroup`, `ConditionTypeOrReference`, `ConditionTarget`, `ConditionValue1`, `ConditionValue2`, `ConditionValue3`, `NegativeCondition`, `ErrorTextId`, `ScriptName`, `Comment`) VALUES +(17,0,@Thrower,0,0,31,1,3,@Sprayer,0,0,0,'', 'Thrower can target only Plague Sprayer'); +-- Delete wrong, nonexistant Trigger spawn +DELETE FROM `creature` WHERE `id`=@Trigger; +-- Update creature data for plague sprayers they should have movement type 0 +UPDATE `creature` SET `spawndist`=0,`MovementType`=0 WHERE `id`=@Sprayer; +UPDATE `creature_template` SET `InhabitType`=4 WHERE `entry`=28274; From bc27b342d639a345111e842896cca3a54a47d0e4 Mon Sep 17 00:00:00 2001 From: Aokromes Date: Wed, 25 Jun 2014 11:23:45 +0200 Subject: [PATCH 19/80] Create 2014_06_25_04_world_misc.sql By dr-j, closes #4399 --- sql/updates/world/2014_06_25_04_world_misc.sql | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 sql/updates/world/2014_06_25_04_world_misc.sql diff --git a/sql/updates/world/2014_06_25_04_world_misc.sql b/sql/updates/world/2014_06_25_04_world_misc.sql new file mode 100644 index 0000000000..3bd4bbaafe --- /dev/null +++ b/sql/updates/world/2014_06_25_04_world_misc.sql @@ -0,0 +1,10 @@ +-- Fix for ... and a Batch of Ooze (4294) and ... and a Batch of Ooze (4293) +UPDATE `quest_template` SET `PrevQuestId`=0 WHERE `Id`=4661; -- Testing for Impurities - Un'Goro Crater +UPDATE `quest_template` SET `PrevQuestId`=0 WHERE `Id`=4561; -- Testing for Corruption - Felwood + +DELETE FROM `conditions` WHERE `SourceTypeOrReferenceId` IN(19,20) AND `SourceEntry` IN (4661,4561); +INSERT INTO `conditions` (`SourceTypeOrReferenceId`, `SourceGroup`, `SourceEntry`, `SourceId`, `ElseGroup`, `ConditionTypeOrReference`, `ConditionTarget`, `ConditionValue1`, `ConditionValue2`, `ConditionValue3`, `NegativeCondition`, `ErrorType`, `ErrorTextId`, `ScriptName`, `Comment`) VALUES +(19, 0, 4561, 0, 0, 9, 0, 4294, 0, 0, 0, 0, 0, '', 'player needs to be on quest ... and a Batch of Ooze to see Testing for Impurities - UnGoro Crater'), +(19, 0, 4661, 0, 0, 9, 0, 4293, 0, 0, 0, 0, 0, '', 'player needs to be on quest and a batch of slime to see Testing for Corruption - Felwood'), +(20, 0, 4561, 0, 0, 9, 0, 4294, 0, 0, 0, 0, 0, '', 'player needs to be on quest ... and a Batch of Ooze to see Testing for Impurities - UnGoro Crater'), +(20, 0, 4661, 0, 0, 9, 0, 4293, 0, 0, 0, 0, 0, '', 'player needs to be on quest and a batch of slime to see Testing for Corruption - Felwood'); From 0d8ba1233e89c3d7e87dc98ca89be38e9688576b Mon Sep 17 00:00:00 2001 From: Aokromes Date: Wed, 25 Jun 2014 11:38:26 +0200 Subject: [PATCH 20/80] DB/Quest: Redemption By dr-j, closes #11233 --- sql/updates/world/2014_06_25_05_world_misc.sql | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 sql/updates/world/2014_06_25_05_world_misc.sql diff --git a/sql/updates/world/2014_06_25_05_world_misc.sql b/sql/updates/world/2014_06_25_05_world_misc.sql new file mode 100644 index 0000000000..8845d2a1e2 --- /dev/null +++ b/sql/updates/world/2014_06_25_05_world_misc.sql @@ -0,0 +1,17 @@ +UPDATE `creature_template` SET `AIName`= 'SmartAI',`ScriptName`='' WHERE `entry` =17542; +DELETE FROM `smart_scripts` WHERE `entryorguid` =17542; + +INSERT INTO `smart_scripts` (`entryorguid`, `source_type`, `id`, `link`, `event_type`, `event_phase_mask`, `event_chance`, `event_flags`, `event_param1`, `event_param2`, `event_param3`, `event_param4`, `action_type`, `action_param1`, `action_param2`, `action_param3`, `action_param4`, `action_param5`, `action_param6`, `target_type`, `target_param1`, `target_param2`, `target_param3`, `target_x`, `target_y`, `target_z`, `target_o`, `comment`) VALUES +(17542,0,0,1,8,0,100,0,8593,0,0,0,33,17542,0,0,0,0,0,7,0,0,0,0,0,0,0,'Young Furbolg Shaman - On Spell Hit - Give Kill Credit'), +(17542,0,1,2,61,0,100,0,0,0,0,0,28,29266,0,0,0,0,0,1,0,0,0,0,0,0,0,'Young Furbolg Shaman - Linked with Previous Event - Remove permament Feign Death'), +(17542,0,2,0,61,0,100,0,0,0,0,0,23,2,0,0,0,0,0,1,0,0,0,0,0,0,0,'Young Furbolg Shaman - Linked with Previous Event - Set Phase 2'), + +(17542,0,4,5, 1,2,100,0,60000,60000,60000,60000,11,29266,2,0,0,0,0,1,0,0,0,0,0,0,0,'Young Furbolg Shaman - OOC - Cast Permament Feign Death'), +(17542,0,5,0,61,2,100,0,0,0,0,0,23,1,0,0,0,0,0,1,0,0,0,0,0,0,0,'Young Furbolg Shaman - Linked with Previous Event - Set Phase 1'), +(17542,0,6,0,25,0,100,0,0,0,0,0,11,29266,2,0,0,0,0,1,0,0,0,0,0,0,0,'Young Furbolg Shaman - On Spawn - Cast Permament Feign Death'); + +DELETE FROM `conditions` WHERE `SourceTypeOrReferenceId`=13 AND `SourceEntry`=8593; +INSERT INTO `conditions` (`SourceTypeOrReferenceId`, `SourceGroup`, `SourceEntry`, `SourceId`, `ElseGroup`, `ConditionTypeOrReference`, `ConditionTarget`, `ConditionValue1`, `ConditionValue2`, `ConditionValue3`, `NegativeCondition`, `ErrorType`, `ErrorTextId`, `ScriptName`, `Comment`) VALUES +(17, 0, 8593, 0, 0, 29, 0, 17542, 40, 0, 0, 0, 0, '', 'Symbol of Life can be casted within 40 yards of Young Furbolg Shaman'), +(17, 0, 8593, 0, 1, 29, 0, 6172, 40, 0, 0, 0, 0, '', 'Symbol of Life can be casted within 40 yards Henze Faulk'), +(17, 0, 8593, 0, 2, 29, 0, 6177, 40, 0, 0, 0, 0, '', 'Symbol of Life can be casted within 40 yards Henze Faulk'); From 77c4ff292389ee450dd2fc371a698a6a0ef863df Mon Sep 17 00:00:00 2001 From: Aokromes Date: Wed, 25 Jun 2014 14:47:07 +0200 Subject: [PATCH 21/80] DB/Conditions: Healing the Lake/Neutralizing Agent By MitchesD, closes #3337 --- sql/updates/world/2014_06_25_06_world_conditions.sql | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 sql/updates/world/2014_06_25_06_world_conditions.sql diff --git a/sql/updates/world/2014_06_25_06_world_conditions.sql b/sql/updates/world/2014_06_25_06_world_conditions.sql new file mode 100644 index 0000000000..8bd179e360 --- /dev/null +++ b/sql/updates/world/2014_06_25_06_world_conditions.sql @@ -0,0 +1,4 @@ +-- +DELETE FROM `conditions` WHERE `SourceEntry`=28700 AND `SourceTypeOrReferenceId`=17; +INSERT INTO `conditions` (`SourceTypeOrReferenceId`, `SourceGroup`, `SourceEntry`, `SourceId`, `ElseGroup`, `ConditionTypeOrReference`, `ConditionTarget`, `ConditionValue1`, `ConditionValue2`, `ConditionValue3`, `NegativeCondition`, `ErrorType`, `ErrorTextId`, `Comment`) VALUES +(17,0,28700,0,0,30,0,181433,15,0,0,0,0,'Disperse Neutralizing Agent (28700) can be used only near the Irradiated Power Crystal (181433)'); From f825d6b7382ff083ee8769258575a058e7f5a9c0 Mon Sep 17 00:00:00 2001 From: Vincent-Michael Date: Wed, 25 Jun 2014 18:40:03 +0200 Subject: [PATCH 22/80] SQL: Sync auth_database --- sql/base/auth_database.sql | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/sql/base/auth_database.sql b/sql/base/auth_database.sql index be64a56079..76cddd7e45 100644 --- a/sql/base/auth_database.sql +++ b/sql/base/auth_database.sql @@ -1,8 +1,8 @@ --- MySQL dump 10.15 Distrib 10.0.10-MariaDB, for Win64 (x86) +-- MySQL dump 10.15 Distrib 10.0.12-MariaDB, for Win64 (x86) -- -- Host: localhost Database: auth -- ------------------------------------------------------ --- Server version 10.0.10-MariaDB +-- Server version 10.0.12-MariaDB /*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; /*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; @@ -248,17 +248,17 @@ DROP TABLE IF EXISTS `logs_ip_actions`; /*!40101 SET @saved_cs_client = @@character_set_client */; /*!40101 SET character_set_client = utf8 */; CREATE TABLE `logs_ip_actions` ( -`id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 'Unique Identifier', -`account_id` INT(10) UNSIGNED NOT NULL COMMENT 'Account ID', -`character_guid` INT(10) UNSIGNED NOT NULL COMMENT 'Character Guid', -`type` TINYINT(3) UNSIGNED NOT NULL, -`ip` VARCHAR(15) NOT NULL DEFAULT '127.0.0.1', -`systemnote` TEXT NULL COMMENT 'Notes inserted by system', -`unixtime` INT(10) UNSIGNED NOT NULL COMMENT 'Unixtime', -`time` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 'Timestamp', -`comment` TEXT NULL COMMENT 'Allows users to add a comment', -PRIMARY KEY (`id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8 + `id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT 'Unique Identifier', + `account_id` int(10) unsigned NOT NULL COMMENT 'Account ID', + `character_guid` int(10) unsigned NOT NULL COMMENT 'Character Guid', + `type` tinyint(3) unsigned NOT NULL, + `ip` varchar(15) NOT NULL DEFAULT '127.0.0.1', + `systemnote` text COMMENT 'Notes inserted by system', + `unixtime` int(10) unsigned NOT NULL COMMENT 'Unixtime', + `time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 'Timestamp', + `comment` text COMMENT 'Allows users to add a comment', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='Used to log ips of individual actions'; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -471,4 +471,4 @@ UNLOCK TABLES; /*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; /*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; --- Dump completed on 2014-04-28 13:43:54 +-- Dump completed on 2014-06-25 18:37:31 From a7a9f44cf535cedabd5df369c3cb063626ed0b64 Mon Sep 17 00:00:00 2001 From: Dr-J Date: Wed, 25 Jun 2014 19:07:24 +0200 Subject: [PATCH 23/80] DB/Conditions: Fix conditions for spell "Symbol of Life" --- sql/updates/world/2014_06_25_05_world_misc.sql | 6 +++--- sql/updates/world/2014_06_25_07_world_conditions.sql | 5 +++++ 2 files changed, 8 insertions(+), 3 deletions(-) create mode 100644 sql/updates/world/2014_06_25_07_world_conditions.sql diff --git a/sql/updates/world/2014_06_25_05_world_misc.sql b/sql/updates/world/2014_06_25_05_world_misc.sql index 8845d2a1e2..e253e445e0 100644 --- a/sql/updates/world/2014_06_25_05_world_misc.sql +++ b/sql/updates/world/2014_06_25_05_world_misc.sql @@ -12,6 +12,6 @@ INSERT INTO `smart_scripts` (`entryorguid`, `source_type`, `id`, `link`, `event_ DELETE FROM `conditions` WHERE `SourceTypeOrReferenceId`=13 AND `SourceEntry`=8593; INSERT INTO `conditions` (`SourceTypeOrReferenceId`, `SourceGroup`, `SourceEntry`, `SourceId`, `ElseGroup`, `ConditionTypeOrReference`, `ConditionTarget`, `ConditionValue1`, `ConditionValue2`, `ConditionValue3`, `NegativeCondition`, `ErrorType`, `ErrorTextId`, `ScriptName`, `Comment`) VALUES -(17, 0, 8593, 0, 0, 29, 0, 17542, 40, 0, 0, 0, 0, '', 'Symbol of Life can be casted within 40 yards of Young Furbolg Shaman'), -(17, 0, 8593, 0, 1, 29, 0, 6172, 40, 0, 0, 0, 0, '', 'Symbol of Life can be casted within 40 yards Henze Faulk'), -(17, 0, 8593, 0, 2, 29, 0, 6177, 40, 0, 0, 0, 0, '', 'Symbol of Life can be casted within 40 yards Henze Faulk'); +(13, 1, 8593, 0, 0, 31, 0, 3, 17542, 0, 0, 0, 0, '', 'Symbol of Life can be casted on Young Furbolg Shaman'), +(13, 1, 8593, 0, 1, 31, 0, 3, 6172, 0, 0, 0, 0, '', 'Symbol of Life can be casted on Henze Faulk'), +(13, 1, 8593, 0, 2, 31, 0, 3, 6177, 0, 0, 0, 0, '', 'Symbol of Life can be casted on Narm Faulk'); diff --git a/sql/updates/world/2014_06_25_07_world_conditions.sql b/sql/updates/world/2014_06_25_07_world_conditions.sql new file mode 100644 index 0000000000..fd1c5a2b68 --- /dev/null +++ b/sql/updates/world/2014_06_25_07_world_conditions.sql @@ -0,0 +1,5 @@ +DELETE FROM `conditions` WHERE `SourceTypeOrReferenceId`=13 AND `SourceEntry`=8593; +INSERT INTO `conditions` (`SourceTypeOrReferenceId`, `SourceGroup`, `SourceEntry`, `SourceId`, `ElseGroup`, `ConditionTypeOrReference`, `ConditionTarget`, `ConditionValue1`, `ConditionValue2`, `ConditionValue3`, `NegativeCondition`, `ErrorType`, `ErrorTextId`, `ScriptName`, `Comment`) VALUES +(13, 1, 8593, 0, 0, 31, 0, 3, 17542, 0, 0, 0, 0, '', 'Symbol of Life can be casted on Young Furbolg Shaman'), +(13, 1, 8593, 0, 1, 31, 0, 3, 6172, 0, 0, 0, 0, '', 'Symbol of Life can be casted on Henze Faulk'), +(13, 1, 8593, 0, 2, 31, 0, 3, 6177, 0, 0, 0, 0, '', 'Symbol of Life can be casted on Narm Faulk'); From 2ae5036ad98f6e7efed993e5ebebbf266da304bd Mon Sep 17 00:00:00 2001 From: Shauren Date: Wed, 25 Jun 2014 19:54:30 +0200 Subject: [PATCH 24/80] SQL: Renamed updates to follow naming convention --- ...tion_ip_logger.sql => 2014_06_23_00_auth_action_ip_logger.sql} | 0 ...ales_creature.sql => 2014_06_22_00_world_locales_creature.sql} | 0 ...06_22_01_gameobject.sql => 2014_06_22_01_world_gameobject.sql} | 0 3 files changed, 0 insertions(+), 0 deletions(-) rename sql/updates/auth/{2014_06_23_00_action_ip_logger.sql => 2014_06_23_00_auth_action_ip_logger.sql} (100%) rename sql/updates/world/{2014_06_22_00_locales_creature.sql => 2014_06_22_00_world_locales_creature.sql} (100%) rename sql/updates/world/{2014_06_22_01_gameobject.sql => 2014_06_22_01_world_gameobject.sql} (100%) diff --git a/sql/updates/auth/2014_06_23_00_action_ip_logger.sql b/sql/updates/auth/2014_06_23_00_auth_action_ip_logger.sql similarity index 100% rename from sql/updates/auth/2014_06_23_00_action_ip_logger.sql rename to sql/updates/auth/2014_06_23_00_auth_action_ip_logger.sql diff --git a/sql/updates/world/2014_06_22_00_locales_creature.sql b/sql/updates/world/2014_06_22_00_world_locales_creature.sql similarity index 100% rename from sql/updates/world/2014_06_22_00_locales_creature.sql rename to sql/updates/world/2014_06_22_00_world_locales_creature.sql diff --git a/sql/updates/world/2014_06_22_01_gameobject.sql b/sql/updates/world/2014_06_22_01_world_gameobject.sql similarity index 100% rename from sql/updates/world/2014_06_22_01_gameobject.sql rename to sql/updates/world/2014_06_22_01_world_gameobject.sql From afd931550b942007d57eb1a8cf3f6b60c8b2903e Mon Sep 17 00:00:00 2001 From: Shauren Date: Wed, 25 Jun 2014 20:34:10 +0200 Subject: [PATCH 25/80] Core/Skills: Check if a skill can be unlearned with SkillRaceClassInfo.dbc data and use max skill level from SkillTiers.dbc --- src/server/game/DataStores/DBCEnums.h | 32 +++++++++++++++-------- src/server/game/DataStores/DBCStores.cpp | 26 ++++++++++++++++++ src/server/game/DataStores/DBCStores.h | 5 ++++ src/server/game/DataStores/DBCStructure.h | 21 +++++++++++++++ src/server/game/DataStores/DBCfmt.h | 2 ++ src/server/game/Handlers/SkillHandler.cpp | 3 ++- src/server/game/Server/WorldSession.h | 1 - src/server/game/Spells/SpellEffects.cpp | 10 ++++++- 8 files changed, 86 insertions(+), 14 deletions(-) diff --git a/src/server/game/DataStores/DBCEnums.h b/src/server/game/DataStores/DBCEnums.h index 8e186e9d09..10b7c25bb1 100644 --- a/src/server/game/DataStores/DBCEnums.h +++ b/src/server/game/DataStores/DBCEnums.h @@ -349,23 +349,22 @@ enum ItemLimitCategoryMode ITEM_LIMIT_CATEGORY_MODE_EQUIP = 1 // limit applied to amount equipped items (including used gems) }; +enum SkillRaceClassInfoFlags +{ + SKILL_FLAG_NO_SKILLUP_MESSAGE = 0x2, + SKILL_FLAG_ALWAYS_MAX_VALUE = 0x10, + SKILL_FLAG_UNLEARNABLE = 0x20, // Skill can be unlearned + SKILL_FLAG_INCLUDE_IN_SORT = 0x80, // Spells belonging to a skill with this flag will additionally compare skill ids when sorting spellbook in client + SKILL_FLAG_NOT_TRAINABLE = 0x100, + SKILL_FLAG_MONO_VALUE = 0x400 // Skill always has value 1 +}; + enum SpellCategoryFlags { SPELL_CATEGORY_FLAG_COOLDOWN_SCALES_WITH_WEAPON_SPEED = 0x01, // unused SPELL_CATEGORY_FLAG_COOLDOWN_STARTS_ON_EVENT = 0x04 }; -enum TotemCategoryType -{ - TOTEM_CATEGORY_TYPE_KNIFE = 1, - TOTEM_CATEGORY_TYPE_TOTEM = 2, - TOTEM_CATEGORY_TYPE_ROD = 3, - TOTEM_CATEGORY_TYPE_PICK = 21, - TOTEM_CATEGORY_TYPE_STONE = 22, - TOTEM_CATEGORY_TYPE_HAMMER = 23, - TOTEM_CATEGORY_TYPE_SPANNER = 24 -}; - // SummonProperties.dbc, col 1 enum SummonPropGroup { @@ -398,6 +397,17 @@ enum SummonPropFlags SUMMON_PROP_FLAG_UNK16 = 0x00008000 // Light/Dark Bullet, Soul/Fiery Consumption, Twisted Visage, Twilight Whelp. Phase related? }; +enum TotemCategoryType +{ + TOTEM_CATEGORY_TYPE_KNIFE = 1, + TOTEM_CATEGORY_TYPE_TOTEM = 2, + TOTEM_CATEGORY_TYPE_ROD = 3, + TOTEM_CATEGORY_TYPE_PICK = 21, + TOTEM_CATEGORY_TYPE_STONE = 22, + TOTEM_CATEGORY_TYPE_HAMMER = 23, + TOTEM_CATEGORY_TYPE_SPANNER = 24 +}; + enum VehicleSeatFlags { VEHICLE_SEAT_FLAG_HAS_LOWER_ANIM_FOR_ENTER = 0x00000001, diff --git a/src/server/game/DataStores/DBCStores.cpp b/src/server/game/DataStores/DBCStores.cpp index 44f03b6978..92d00b2064 100644 --- a/src/server/game/DataStores/DBCStores.cpp +++ b/src/server/game/DataStores/DBCStores.cpp @@ -151,6 +151,9 @@ DBCStorage sScalingStatValuesStore(ScalingStatValuesfmt DBCStorage sSkillLineStore(SkillLinefmt); DBCStorage sSkillLineAbilityStore(SkillLineAbilityfmt); +DBCStorage sSkillRaceClassInfoStore(SkillRaceClassInfofmt); +SkillRaceClassInfoMap SkillRaceClassInfoBySkill; +DBCStorage sSkillTiersStore(SkillTiersfmt); DBCStorage sSoundEntriesStore(SoundEntriesfmt); @@ -409,6 +412,13 @@ void LoadDBCStores(const std::string& dataPath) LoadDBC(availableDbcLocales, bad_dbc_files, sScalingStatValuesStore, dbcPath, "ScalingStatValues.dbc"); LoadDBC(availableDbcLocales, bad_dbc_files, sSkillLineStore, dbcPath, "SkillLine.dbc"); LoadDBC(availableDbcLocales, bad_dbc_files, sSkillLineAbilityStore, dbcPath, "SkillLineAbility.dbc"); + LoadDBC(availableDbcLocales, bad_dbc_files, sSkillRaceClassInfoStore, dbcPath, "SkillRaceClassInfo.dbc"); + for (uint32 i = 0; i < sSkillRaceClassInfoStore.GetNumRows(); ++i) + if (SkillRaceClassInfoEntry const* entry = sSkillRaceClassInfoStore.LookupEntry(i)) + if (sSkillLineStore.LookupEntry(entry->SkillId)) + SkillRaceClassInfoBySkill.emplace(entry->SkillId, entry); + + LoadDBC(availableDbcLocales, bad_dbc_files, sSkillTiersStore, dbcPath, "SkillTiers.dbc"); LoadDBC(availableDbcLocales, bad_dbc_files, sSoundEntriesStore, dbcPath, "SoundEntries.dbc"); LoadDBC(availableDbcLocales, bad_dbc_files, sSpellStore, dbcPath, "Spell.dbc", &CustomSpellEntryfmt, &CustomSpellEntryIndex); for (uint32 i = 1; i < sSpellStore.GetNumRows(); ++i) @@ -965,3 +975,19 @@ uint32 GetDefaultMapLight(uint32 mapId) return 0; } + +SkillRaceClassInfoEntry const* GetSkillRaceClassInfo(uint32 skill, uint8 race, uint8 class_) +{ + SkillRaceClassInfoBounds bounds = SkillRaceClassInfoBySkill.equal_range(skill); + for (SkillRaceClassInfoMap::iterator itr = bounds.first; itr != bounds.second; ++itr) + { + if (itr->second->RaceMask && !(itr->second->RaceMask & (1 << (race - 1)))) + continue; + if (itr->second->ClassMask && !(itr->second->ClassMask & (1 << (class_ - 1)))) + continue; + + return itr->second; + } + + return NULL; +} diff --git a/src/server/game/DataStores/DBCStores.h b/src/server/game/DataStores/DBCStores.h index fe775dfda1..8b89a86faf 100644 --- a/src/server/game/DataStores/DBCStores.h +++ b/src/server/game/DataStores/DBCStores.h @@ -74,6 +74,10 @@ LFGDungeonEntry const* GetLFGDungeon(uint32 mapId, Difficulty difficulty); uint32 GetDefaultMapLight(uint32 mapId); +typedef std::unordered_multimap SkillRaceClassInfoMap; +typedef std::pair SkillRaceClassInfoBounds; +SkillRaceClassInfoEntry const* GetSkillRaceClassInfo(uint32 skill, uint8 race, uint8 class_); + extern DBCStorage sAchievementStore; extern DBCStorage sAchievementCriteriaStore; extern DBCStorage sAreaStore;// recommend access using functions @@ -150,6 +154,7 @@ extern DBCStorage sScalingStatDistributionStore; extern DBCStorage sScalingStatValuesStore; extern DBCStorage sSkillLineStore; extern DBCStorage sSkillLineAbilityStore; +extern DBCStorage sSkillTiersStore; extern DBCStorage sSoundEntriesStore; extern DBCStorage sSpellCastTimesStore; extern DBCStorage sSpellCategoryStore; diff --git a/src/server/game/DataStores/DBCStructure.h b/src/server/game/DataStores/DBCStructure.h index 5d6c8c7aa8..2da166fb04 100644 --- a/src/server/game/DataStores/DBCStructure.h +++ b/src/server/game/DataStores/DBCStructure.h @@ -1584,6 +1584,27 @@ struct SkillLineAbilityEntry //uint32 characterPoints[2]; // 12-13 m_characterPoints[2] }; +struct SkillRaceClassInfoEntry +{ + //uint32 Id; // 0 + uint32 SkillId; // 1 + uint32 RaceMask; // 2 + uint32 ClassMask; // 3 + uint32 Flags; // 4 + //uint32 MinLevel; // 5 + uint32 SkillTier; // 6 + //uint32 SkillCostType; // 7 +}; + +#define MAX_SKILL_STEP 16 + +struct SkillTiersEntry +{ + uint32 Id; // 0 + //uint32 StepCost[MAX_SKILL_STEP]; // 1-16 + uint32 MaxSkill[MAX_SKILL_STEP]; // 17-32 +}; + struct SoundEntriesEntry { uint32 Id; // 0 m_ID diff --git a/src/server/game/DataStores/DBCfmt.h b/src/server/game/DataStores/DBCfmt.h index 222353467f..a90cc48c5a 100644 --- a/src/server/game/DataStores/DBCfmt.h +++ b/src/server/game/DataStores/DBCfmt.h @@ -98,6 +98,8 @@ char const ScalingStatDistributionfmt[] = "niiiiiiiiiiiiiiiiiiiii"; char const ScalingStatValuesfmt[] = "iniiiiiiiiiiiiiiiiiiiiii"; char const SkillLinefmt[] = "nixssssssssssssssssxxxxxxxxxxxxxxxxxxixxxxxxxxxxxxxxxxxi"; char const SkillLineAbilityfmt[] = "niiiixxiiiiixx"; +char const SkillRaceClassInfofmt[] = "diiiixix"; +char const SkillTiersfmt[] = "nxxxxxxxxxxxxxxxxiiiiiiiiiiiiiiii"; char const SoundEntriesfmt[] = "nxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"; char const SpellCastTimefmt[] = "nixx"; char const SpellCategoryfmt[] = "ni"; diff --git a/src/server/game/Handlers/SkillHandler.cpp b/src/server/game/Handlers/SkillHandler.cpp index f90dfef268..8a94753b69 100644 --- a/src/server/game/Handlers/SkillHandler.cpp +++ b/src/server/game/Handlers/SkillHandler.cpp @@ -98,7 +98,8 @@ void WorldSession::HandleUnlearnSkillOpcode(WorldPacket& recvData) uint32 skillId; recvData >> skillId; - if (!IsPrimaryProfessionSkill(skillId)) + SkillRaceClassInfoEntry const* rcEntry = GetSkillRaceClassInfo(skillId, GetPlayer()->getRace(), GetPlayer()->getClass()); + if (!rcEntry || !(rcEntry->Flags & SKILL_FLAG_UNLEARNABLE)) return; GetPlayer()->SetSkill(skillId, 0, 0, 0); diff --git a/src/server/game/Server/WorldSession.h b/src/server/game/Server/WorldSession.h index 3422934fcd..c8c81a415e 100644 --- a/src/server/game/Server/WorldSession.h +++ b/src/server/game/Server/WorldSession.h @@ -444,7 +444,6 @@ class WorldSession void HandleForceSpeedChangeAck(WorldPacket& recvData); void HandlePingOpcode(WorldPacket& recvPacket); - void HandleAuthSessionOpcode(WorldPacket& recvPacket); void HandleRepopRequestOpcode(WorldPacket& recvPacket); void HandleAutostoreLootItemOpcode(WorldPacket& recvPacket); void HandleLootMoneyOpcode(WorldPacket& recvPacket); diff --git a/src/server/game/Spells/SpellEffects.cpp b/src/server/game/Spells/SpellEffects.cpp index 0a43c5a2c4..1240d8af7a 100644 --- a/src/server/game/Spells/SpellEffects.cpp +++ b/src/server/game/Spells/SpellEffects.cpp @@ -2608,8 +2608,16 @@ void Spell::EffectLearnSkill(SpellEffIndex effIndex) return; uint32 skillid = m_spellInfo->Effects[effIndex].MiscValue; + SkillRaceClassInfoEntry const* rcEntry = GetSkillRaceClassInfo(skillid, unitTarget->getRace(), unitTarget->getClass()); + if (!rcEntry) + return; + + SkillTiersEntry const* tier = sSkillTiersStore.LookupEntry(rcEntry->SkillTier); + if (!tier) + return; + uint16 skillval = unitTarget->ToPlayer()->GetPureSkillValue(skillid); - unitTarget->ToPlayer()->SetSkill(skillid, m_spellInfo->Effects[effIndex].CalcValue(), skillval?skillval:1, damage*75); + unitTarget->ToPlayer()->SetSkill(skillid, m_spellInfo->Effects[effIndex].CalcValue(), std::min(skillval, 1), tier->MaxSkill[damage - 1]); } void Spell::EffectAddHonor(SpellEffIndex /*effIndex*/) From ff257363383a4411c013879804512bbef272ba4c Mon Sep 17 00:00:00 2001 From: jackpoz Date: Wed, 25 Jun 2014 22:01:44 +0200 Subject: [PATCH 26/80] Tools/MMapsGenerator: Improve mmaps generation time on multi-core CPUs Queue biggest maps first for mmaps generation to avoid waiting for last map, usually map id 571. Fix wrong maps count being printed on startup. --- src/tools/mmaps_generator/MapBuilder.cpp | 32 +++++++++++++++--------- src/tools/mmaps_generator/MapBuilder.h | 20 ++++++++++++++- 2 files changed, 39 insertions(+), 13 deletions(-) diff --git a/src/tools/mmaps_generator/MapBuilder.cpp b/src/tools/mmaps_generator/MapBuilder.cpp index cc24035b05..131041e0cd 100644 --- a/src/tools/mmaps_generator/MapBuilder.cpp +++ b/src/tools/mmaps_generator/MapBuilder.cpp @@ -77,8 +77,8 @@ namespace MMAP { for (TileList::iterator it = m_tiles.begin(); it != m_tiles.end(); ++it) { - (*it).second->clear(); - delete (*it).second; + (*it).m_tiles->clear(); + delete (*it).m_tiles; } delete m_terrainBuilder; @@ -97,9 +97,9 @@ namespace MMAP for (uint32 i = 0; i < files.size(); ++i) { mapID = uint32(atoi(files[i].substr(0,3).c_str())); - if (m_tiles.find(mapID) == m_tiles.end()) + if (std::find(m_tiles.begin(), m_tiles.end(), mapID) == m_tiles.end()) { - m_tiles.insert(std::pair*>(mapID, new std::set)); + m_tiles.emplace_back(MapTiles(mapID, new std::set)); count++; } } @@ -109,8 +109,11 @@ namespace MMAP for (uint32 i = 0; i < files.size(); ++i) { mapID = uint32(atoi(files[i].substr(0,3).c_str())); - m_tiles.insert(std::pair*>(mapID, new std::set)); - count++; + if (std::find(m_tiles.begin(), m_tiles.end(), mapID) == m_tiles.end()) + { + m_tiles.emplace_back(MapTiles(mapID, new std::set)); + count++; + } } printf("found %u.\n", count); @@ -118,8 +121,8 @@ namespace MMAP printf("Discovering tiles... "); for (TileList::iterator itr = m_tiles.begin(); itr != m_tiles.end(); ++itr) { - std::set* tiles = (*itr).second; - mapID = (*itr).first; + std::set* tiles = (*itr).m_tiles; + mapID = (*itr).m_mapId; sprintf(filter, "%03u*.vmtile", mapID); files.clear(); @@ -153,12 +156,12 @@ namespace MMAP /**************************************************************************/ std::set* MapBuilder::getTileList(uint32 mapID) { - TileList::iterator itr = m_tiles.find(mapID); + TileList::iterator itr = std::find(m_tiles.begin(), m_tiles.end(), mapID); if (itr != m_tiles.end()) - return (*itr).second; + return (*itr).m_tiles; std::set* tiles = new std::set(); - m_tiles.insert(std::pair*>(mapID, tiles)); + m_tiles.emplace_back(MapTiles(mapID, tiles)); return tiles; } @@ -169,9 +172,14 @@ namespace MMAP BuilderThreadPool* pool = threads > 0 ? new BuilderThreadPool() : NULL; + m_tiles.sort([](MapTiles a, MapTiles b) + { + return a.m_tiles->size() > b.m_tiles->size(); + }); + for (TileList::iterator it = m_tiles.begin(); it != m_tiles.end(); ++it) { - uint32 mapID = it->first; + uint32 mapID = it->m_mapId; if (!shouldSkipMap(mapID)) { if (threads > 0) diff --git a/src/tools/mmaps_generator/MapBuilder.h b/src/tools/mmaps_generator/MapBuilder.h index 86a6db2077..08b87324d0 100644 --- a/src/tools/mmaps_generator/MapBuilder.h +++ b/src/tools/mmaps_generator/MapBuilder.h @@ -22,6 +22,7 @@ #include #include #include +#include #include "TerrainBuilder.h" #include "IntermediateValues.h" @@ -39,7 +40,24 @@ using namespace VMAP; namespace MMAP { - typedef std::map*> TileList; + struct MapTiles + { + MapTiles() : m_mapId(uint32(-1)), m_tiles(NULL) {} + + MapTiles(uint32 id, std::set* tiles) : m_mapId(id), m_tiles(tiles) {} + ~MapTiles() {} + + uint32 m_mapId; + std::set* m_tiles; + + bool operator==(uint32 id) + { + return m_mapId == id; + } + }; + + typedef std::list TileList; + struct Tile { Tile() : chf(NULL), solid(NULL), cset(NULL), pmesh(NULL), dmesh(NULL) {} From ed5e3fceed5721e88fcd22018c41df65626f6557 Mon Sep 17 00:00:00 2001 From: jackpoz Date: Sat, 21 Jun 2014 00:00:54 +0200 Subject: [PATCH 27/80] Core/MMAPs: Update recast Update recast to https://github.com/memononen/recastnavigation/commit/42b96b7306d39bb7680ddb0f89d480a8296c83ff Previous MMAPs might still work but it's recommended to re-extract them. --- dep/PackageList.txt | 2 +- .../Detour/Include/DetourNavMesh.h | 19 + .../Detour/Include/DetourNavMeshQuery.h | 54 +- .../Detour/Include/DetourNode.h | 19 +- .../Detour/Source/DetourNavMeshQuery.cpp | 354 +- .../Detour/Source/DetourNode.cpp | 26 +- dep/recastnavigation/Recast/Include/Recast.h | 17 +- .../Recast/Source/RecastContour.cpp | 539 ++- .../Recast/Source/RecastLayers.cpp | 2 +- .../Recast/Source/RecastMesh.cpp | 86 +- .../Recast/Source/RecastMeshDetail.cpp | 279 +- .../Recast/Source/RecastRegion.cpp | 427 +- dep/recastnavigation/recastnavigation.diff | 3676 +---------------- 13 files changed, 1514 insertions(+), 3986 deletions(-) diff --git a/dep/PackageList.txt b/dep/PackageList.txt index 55dac51cad..3fa0143a58 100644 --- a/dep/PackageList.txt +++ b/dep/PackageList.txt @@ -42,4 +42,4 @@ gSOAP (a portable development toolkit for C and C++ XML Web services and XML dat recastnavigation (Recast is state of the art navigation mesh construction toolset for games) https://github.com/memononen/recastnavigation - Version: 740a7ba51600a3c87ce5667ae276a38284a1ce75 + Version: 42b96b7306d39bb7680ddb0f89d480a8296c83ff diff --git a/dep/recastnavigation/Detour/Include/DetourNavMesh.h b/dep/recastnavigation/Detour/Include/DetourNavMesh.h index cdd473f1af..782ddbc2ed 100644 --- a/dep/recastnavigation/Detour/Include/DetourNavMesh.h +++ b/dep/recastnavigation/Detour/Include/DetourNavMesh.h @@ -119,6 +119,25 @@ enum dtStraightPathOptions DT_STRAIGHTPATH_ALL_CROSSINGS = 0x02, ///< Add a vertex at every polygon edge crossing. }; + +/// Options for dtNavMeshQuery::findPath +enum dtFindPathOptions +{ + DT_FINDPATH_LOW_QUALITY_FAR = 0x01, ///< [provisional] trade quality for performance far from the origin. The idea is that by then a new query will be issued + DT_FINDPATH_ANY_ANGLE = 0x02, ///< use raycasts during pathfind to "shortcut" (raycast still consider costs) +}; + +/// Options for dtNavMeshQuery::raycast +enum dtRaycastOptions +{ + DT_RAYCAST_USE_COSTS = 0x01, ///< Raycast should calculate movement cost along the ray and fill RaycastHit::cost +}; + + +/// Limit raycasting during any angle pahfinding +/// The limit is given as a multiple of the character radius +static const float DT_RAY_CAST_LIMIT_PROPORTIONS = 50.0f; + /// Flags representing the type of a navigation mesh polygon. enum dtPolyTypes { diff --git a/dep/recastnavigation/Detour/Include/DetourNavMeshQuery.h b/dep/recastnavigation/Detour/Include/DetourNavMeshQuery.h index 4a5112c9eb..c7b360dcdc 100644 --- a/dep/recastnavigation/Detour/Include/DetourNavMeshQuery.h +++ b/dep/recastnavigation/Detour/Include/DetourNavMeshQuery.h @@ -41,6 +41,10 @@ class dtQueryFilter public: dtQueryFilter(); +#ifdef DT_VIRTUAL_QUERYFILTER + virtual ~dtQueryFilter() { } +#endif + /// Returns true if the polygon can be visited. (I.e. Is traversable.) /// @param[in] ref The reference id of the polygon test. /// @param[in] tile The tile containing the polygon. @@ -115,6 +119,34 @@ public: }; + + +/// Provides information about raycast hit +/// filled by dtNavMeshQuery::raycast +/// @ingroup detour +struct dtRaycastHit +{ + /// The hit parameter. (FLT_MAX if no wall hit.) + float t; + + /// hitNormal The normal of the nearest wall hit. [(x, y, z)] + float hitNormal[3]; + + /// Pointer to an array of reference ids of the visited polygons. [opt] + dtPolyRef* path; + + /// The number of visited polygons. [opt] + int pathCount; + + /// The maximum number of polygons the @p path array can hold. + int maxPath; + + /// The cost of the path until hit. + float pathCost; +}; + + + /// Provides the ability to perform pathfinding related queries against /// a navigation mesh. /// @ingroup detour @@ -179,10 +211,11 @@ public: /// @param[in] startPos A position within the start polygon. [(x, y, z)] /// @param[in] endPos A position within the end polygon. [(x, y, z)] /// @param[in] filter The polygon filter to apply to the query. + /// @param[in] options query options (see: #dtFindPathOptions) /// @returns The status flags for the query. dtStatus initSlicedFindPath(dtPolyRef startRef, dtPolyRef endRef, const float* startPos, const float* endPos, - const dtQueryFilter* filter); + const dtQueryFilter* filter, const unsigned int options = 0); /// Updates an in-progress sliced path query. /// @param[in] maxIter The maximum number of iterations to perform. @@ -308,6 +341,7 @@ public: /// Casts a 'walkability' ray along the surface of the navigation mesh from /// the start position toward the end position. + /// @note A wrapper around raycast(..., RaycastHit*). Retained for backward compatibility. /// @param[in] startRef The reference id of the start polygon. /// @param[in] startPos A position within the start polygon representing /// the start of the ray. [(x, y, z)] @@ -323,6 +357,22 @@ public: const dtQueryFilter* filter, float* t, float* hitNormal, dtPolyRef* path, int* pathCount, const int maxPath) const; + /// Casts a 'walkability' ray along the surface of the navigation mesh from + /// the start position toward the end position. + /// @param[in] startRef The reference id of the start polygon. + /// @param[in] startPos A position within the start polygon representing + /// the start of the ray. [(x, y, z)] + /// @param[in] endPos The position to cast the ray toward. [(x, y, z)] + /// @param[in] filter The polygon filter to apply to the query. + /// @param[in] flags govern how the raycast behaves. See dtRaycastOptions + /// @param[out] hit Pointer to a raycast hit structure which will be filled by the results. + /// @param[in] prevRef parent of start ref. Used during for cost calculation [opt] + /// @returns The status flags for the query. + dtStatus raycast(dtPolyRef startRef, const float* startPos, const float* endPos, + const dtQueryFilter* filter, const unsigned int options, + dtRaycastHit* hit, dtPolyRef prevRef = 0) const; + + /// Finds the distance from the specified position to the nearest polygon wall. /// @param[in] startRef The reference id of the polygon containing @p centerPos. /// @param[in] centerPos The center of the search circle. [(x, y, z)] @@ -463,6 +513,8 @@ private: dtPolyRef startRef, endRef; float startPos[3], endPos[3]; const dtQueryFilter* filter; + unsigned int options; + float raycastLimitSqr; }; dtQueryData m_query; ///< Sliced query state. diff --git a/dep/recastnavigation/Detour/Include/DetourNode.h b/dep/recastnavigation/Detour/Include/DetourNode.h index b68c922d03..6fefdc8e0f 100644 --- a/dep/recastnavigation/Detour/Include/DetourNode.h +++ b/dep/recastnavigation/Detour/Include/DetourNode.h @@ -25,6 +25,7 @@ enum dtNodeFlags { DT_NODE_OPEN = 0x01, DT_NODE_CLOSED = 0x02, + DT_NODE_PARENT_DETACHED = 0x04, // parent of the node is not adjacent. Found using raycast. }; typedef unsigned short dtNodeIndex; @@ -35,12 +36,17 @@ struct dtNode float pos[3]; ///< Position of the node. float cost; ///< Cost from previous node to current node. float total; ///< Cost up to the node. - unsigned int pidx : 30; ///< Index to parent node. - unsigned int flags : 2; ///< Node flags 0/open/closed. + unsigned int pidx : 24; ///< Index to parent node. + unsigned int state : 2; ///< extra state information. A polyRef can have multiple nodes with different extra info. see DT_MAX_STATES_PER_NODE + unsigned int flags : 3; ///< Node flags. A combination of dtNodeFlags. dtPolyRef id; ///< Polygon ref the node corresponds to. }; +static const int DT_MAX_STATES_PER_NODE = 4; // number of extra states per node. See dtNode::state + + + class dtNodePool { public: @@ -48,8 +54,12 @@ public: ~dtNodePool(); inline void operator=(const dtNodePool&) {} void clear(); - dtNode* getNode(dtPolyRef id); - dtNode* findNode(dtPolyRef id); + + // Get a dtNode by ref and extra state information. If there is none then - allocate + // There can be more than one node for the same polyRef but with different extra state information + dtNode* getNode(dtPolyRef id, unsigned char state=0); + dtNode* findNode(dtPolyRef id, unsigned char state); + unsigned int findNodes(dtPolyRef id, dtNode** nodes, const int maxNodes); inline unsigned int getNodeIdx(const dtNode* node) const { @@ -82,6 +92,7 @@ public: inline int getHashSize() const { return m_hashSize; } inline dtNodeIndex getFirst(int bucket) const { return m_first[bucket]; } inline dtNodeIndex getNext(int i) const { return m_next[i]; } + inline int getNodeCount() const { return m_nodeCount; } private: diff --git a/dep/recastnavigation/Detour/Source/DetourNavMeshQuery.cpp b/dep/recastnavigation/Detour/Source/DetourNavMeshQuery.cpp index f1709dfd4c..ec3a2946ea 100644 --- a/dep/recastnavigation/Detour/Source/DetourNavMeshQuery.cpp +++ b/dep/recastnavigation/Detour/Source/DetourNavMeshQuery.cpp @@ -1011,7 +1011,13 @@ dtStatus dtNavMeshQuery::findPath(dtPolyRef startRef, dtPolyRef endRef, if (!filter->passFilter(neighbourRef, neighbourTile, neighbourPoly)) continue; - dtNode* neighbourNode = m_nodePool->getNode(neighbourRef); + // deal explicitly with crossing tile boundaries + unsigned char crossSide = 0; + if (bestTile->links[i].side != 0xff) + crossSide = bestTile->links[i].side >> 1; + + // get the node + dtNode* neighbourNode = m_nodePool->getNode(neighbourRef, crossSide); if (!neighbourNode) { status |= DT_OUT_OF_NODES; @@ -1139,7 +1145,7 @@ dtStatus dtNavMeshQuery::findPath(dtPolyRef startRef, dtPolyRef endRef, /// dtStatus dtNavMeshQuery::initSlicedFindPath(dtPolyRef startRef, dtPolyRef endRef, const float* startPos, const float* endPos, - const dtQueryFilter* filter) + const dtQueryFilter* filter, const unsigned int options) { dtAssert(m_nav); dtAssert(m_nodePool); @@ -1153,6 +1159,8 @@ dtStatus dtNavMeshQuery::initSlicedFindPath(dtPolyRef startRef, dtPolyRef endRef dtVcopy(m_query.startPos, startPos); dtVcopy(m_query.endPos, endPos); m_query.filter = filter; + m_query.options = options; + m_query.raycastLimitSqr = FLT_MAX; if (!startRef || !endRef) return DT_FAILURE | DT_INVALID_PARAM; @@ -1161,6 +1169,16 @@ dtStatus dtNavMeshQuery::initSlicedFindPath(dtPolyRef startRef, dtPolyRef endRef if (!m_nav->isValidPolyRef(startRef) || !m_nav->isValidPolyRef(endRef)) return DT_FAILURE | DT_INVALID_PARAM; + // trade quality with performance? + if (options & DT_FINDPATH_ANY_ANGLE) + { + // limiting to several times the character radius yields nice results. It is not sensitive + // so it is enough to compute it from the first tile. + const dtMeshTile* tile = m_nav->getTileByRef(startRef); + float agentRadius = tile->header->walkableRadius; + m_query.raycastLimitSqr = dtSqr(agentRadius * DT_RAY_CAST_LIMIT_PROPORTIONS); + } + if (startRef == endRef) { m_query.status = DT_SUCCESS; @@ -1197,6 +1215,9 @@ dtStatus dtNavMeshQuery::updateSlicedFindPath(const int maxIter, int* doneIters) m_query.status = DT_FAILURE; return DT_FAILURE; } + + dtRaycastHit rayHit; + rayHit.maxPath = 0; int iter = 0; while (iter < maxIter && !m_openList->empty()) @@ -1233,15 +1254,22 @@ dtStatus dtNavMeshQuery::updateSlicedFindPath(const int maxIter, int* doneIters) return m_query.status; } - // Get parent poly and tile. - dtPolyRef parentRef = 0; + // Get parent and grand parent poly and tile. + dtPolyRef parentRef = 0, grandpaRef = 0; const dtMeshTile* parentTile = 0; const dtPoly* parentPoly = 0; + dtNode* parentNode = 0; if (bestNode->pidx) - parentRef = m_nodePool->getNodeAtIdx(bestNode->pidx)->id; + { + parentNode = m_nodePool->getNodeAtIdx(bestNode->pidx); + parentRef = parentNode->id; + if (parentNode->pidx) + grandpaRef = m_nodePool->getNodeAtIdx(parentNode->pidx)->id; + } if (parentRef) { - if (dtStatusFailed(m_nav->getTileAndPolyByRef(parentRef, &parentTile, &parentPoly))) + bool invalidParent = dtStatusFailed(m_nav->getTileAndPolyByRef(parentRef, &parentTile, &parentPoly)); + if (invalidParent || (grandpaRef && !m_nav->isValidPolyRef(grandpaRef)) ) { // The polygon has disappeared during the sliced query, fail. m_query.status = DT_FAILURE; @@ -1250,6 +1278,14 @@ dtStatus dtNavMeshQuery::updateSlicedFindPath(const int maxIter, int* doneIters) return m_query.status; } } + + // decide whether to test raycast to previous nodes + bool tryLOS = false; + if (m_query.options & DT_FINDPATH_ANY_ANGLE) + { + if ((parentRef != 0) && (dtVdistSqr(parentNode->pos, bestNode->pos) < m_query.raycastLimitSqr)) + tryLOS = true; + } for (unsigned int i = bestPoly->firstLink; i != DT_NULL_LINK; i = bestTile->links[i].next) { @@ -1268,13 +1304,22 @@ dtStatus dtNavMeshQuery::updateSlicedFindPath(const int maxIter, int* doneIters) if (!m_query.filter->passFilter(neighbourRef, neighbourTile, neighbourPoly)) continue; - dtNode* neighbourNode = m_nodePool->getNode(neighbourRef); + // deal explicitly with crossing tile boundaries + unsigned char crossSide = 0; + if (bestTile->links[i].side != 0xff) + crossSide = bestTile->links[i].side >> 1; + + dtNode* neighbourNode = m_nodePool->getNode(neighbourRef, crossSide); if (!neighbourNode) { m_query.status |= DT_OUT_OF_NODES; continue; } + // do not expand to nodes that were already visited from the same parent + if (neighbourNode->pidx != 0 && neighbourNode->pidx == bestNode->pidx) + continue; + // If the node is visited the first time, calculate node position. if (neighbourNode->flags == 0) { @@ -1287,30 +1332,44 @@ dtStatus dtNavMeshQuery::updateSlicedFindPath(const int maxIter, int* doneIters) float cost = 0; float heuristic = 0; + // raycast parent + bool foundShortCut = false; + rayHit.pathCost = rayHit.t = 0; + if (tryLOS) + { + raycast(parentRef, parentNode->pos, neighbourNode->pos, m_query.filter, DT_RAYCAST_USE_COSTS, &rayHit, grandpaRef); + foundShortCut = rayHit.t >= 1.0f; + } + + // update move cost + if (foundShortCut) + { + // shortcut found using raycast. Using shorter cost instead + cost = parentNode->cost + rayHit.pathCost; + } + else + { + // No shortcut found. + const float curCost = m_query.filter->getCost(bestNode->pos, neighbourNode->pos, + parentRef, parentTile, parentPoly, + bestRef, bestTile, bestPoly, + neighbourRef, neighbourTile, neighbourPoly); + cost = bestNode->cost + curCost; + } + // Special case for last node. if (neighbourRef == m_query.endRef) { - // Cost - const float curCost = m_query.filter->getCost(bestNode->pos, neighbourNode->pos, - parentRef, parentTile, parentPoly, - bestRef, bestTile, bestPoly, - neighbourRef, neighbourTile, neighbourPoly); const float endCost = m_query.filter->getCost(neighbourNode->pos, m_query.endPos, bestRef, bestTile, bestPoly, neighbourRef, neighbourTile, neighbourPoly, 0, 0, 0); - cost = bestNode->cost + curCost + endCost; + cost = cost + endCost; heuristic = 0; } else { - // Cost - const float curCost = m_query.filter->getCost(bestNode->pos, neighbourNode->pos, - parentRef, parentTile, parentPoly, - bestRef, bestTile, bestPoly, - neighbourRef, neighbourTile, neighbourPoly); - cost = bestNode->cost + curCost; heuristic = dtVdist(neighbourNode->pos, m_query.endPos)*H_SCALE; } @@ -1324,11 +1383,13 @@ dtStatus dtNavMeshQuery::updateSlicedFindPath(const int maxIter, int* doneIters) continue; // Add or update the node. - neighbourNode->pidx = m_nodePool->getNodeIdx(bestNode); + neighbourNode->pidx = foundShortCut ? bestNode->pidx : m_nodePool->getNodeIdx(bestNode); neighbourNode->id = neighbourRef; - neighbourNode->flags = (neighbourNode->flags & ~DT_NODE_CLOSED); + neighbourNode->flags = (neighbourNode->flags & ~(DT_NODE_CLOSED | DT_NODE_PARENT_DETACHED)); neighbourNode->cost = cost; neighbourNode->total = total; + if (foundShortCut) + neighbourNode->flags = (neighbourNode->flags | DT_NODE_PARENT_DETACHED); if (neighbourNode->flags & DT_NODE_OPEN) { @@ -1392,11 +1453,15 @@ dtStatus dtNavMeshQuery::finalizeSlicedFindPath(dtPolyRef* path, int* pathCount, dtNode* prev = 0; dtNode* node = m_query.lastBestNode; + int prevRay = 0; do { dtNode* next = m_nodePool->getNodeAtIdx(node->pidx); node->pidx = m_nodePool->getNodeIdx(prev); prev = node; + int nextRay = node->flags & DT_NODE_PARENT_DETACHED; // keep track of whether parent is not adjacent (i.e. due to raycast shortcut) + node->flags = (node->flags & ~DT_NODE_PARENT_DETACHED) | prevRay; // and store it in the reversed path's node + prevRay = nextRay; node = next; } while (node); @@ -1405,13 +1470,31 @@ dtStatus dtNavMeshQuery::finalizeSlicedFindPath(dtPolyRef* path, int* pathCount, node = prev; do { - path[n++] = node->id; - if (n >= maxPath) + dtNode* next = m_nodePool->getNodeAtIdx(node->pidx); + dtStatus status = 0; + if (node->flags & DT_NODE_PARENT_DETACHED) { - m_query.status |= DT_BUFFER_TOO_SMALL; + float t, normal[3]; + int m; + status = raycast(node->id, node->pos, next->pos, m_query.filter, &t, normal, path+n, &m, maxPath-n); + n += m; + // raycast ends on poly boundary and the path might include the next poly boundary. + if (path[n-1] == next->id) + n--; // remove to avoid duplicates + } + else + { + path[n++] = node->id; + if (n >= maxPath) + status = DT_BUFFER_TOO_SMALL; + } + + if (status & DT_STATUS_DETAIL_MASK) + { + m_query.status |= status & DT_STATUS_DETAIL_MASK; break; } - node = m_nodePool->getNodeAtIdx(node->pidx); + node = next; } while (node); } @@ -1457,7 +1540,7 @@ dtStatus dtNavMeshQuery::finalizeSlicedFindPathPartial(const dtPolyRef* existing dtNode* node = 0; for (int i = existingSize-1; i >= 0; --i) { - node = m_nodePool->findNode(existing[i]); + m_nodePool->findNodes(existing[i], &node, 1); if (node) break; } @@ -1470,11 +1553,15 @@ dtStatus dtNavMeshQuery::finalizeSlicedFindPathPartial(const dtPolyRef* existing } // Reverse the path. + int prevRay = 0; do { dtNode* next = m_nodePool->getNodeAtIdx(node->pidx); node->pidx = m_nodePool->getNodeIdx(prev); prev = node; + int nextRay = node->flags & DT_NODE_PARENT_DETACHED; // keep track of whether parent is not adjacent (i.e. due to raycast shortcut) + node->flags = (node->flags & ~DT_NODE_PARENT_DETACHED) | prevRay; // and store it in the reversed path's node + prevRay = nextRay; node = next; } while (node); @@ -1483,13 +1570,31 @@ dtStatus dtNavMeshQuery::finalizeSlicedFindPathPartial(const dtPolyRef* existing node = prev; do { - path[n++] = node->id; - if (n >= maxPath) + dtNode* next = m_nodePool->getNodeAtIdx(node->pidx); + dtStatus status = 0; + if (node->flags & DT_NODE_PARENT_DETACHED) { - m_query.status |= DT_BUFFER_TOO_SMALL; + float t, normal[3]; + int m; + status = raycast(node->id, node->pos, next->pos, m_query.filter, &t, normal, path+n, &m, maxPath-n); + n += m; + // raycast ends on poly boundary and the path might include the next poly boundary. + if (path[n-1] == next->id) + n--; // remove to avoid duplicates + } + else + { + path[n++] = node->id; + if (n >= maxPath) + status = DT_BUFFER_TOO_SMALL; + } + + if (status & DT_STATUS_DETAIL_MASK) + { + m_query.status |= status & DT_STATUS_DETAIL_MASK; break; } - node = m_nodePool->getNodeAtIdx(node->pidx); + node = next; } while (node); } @@ -2161,6 +2266,8 @@ dtStatus dtNavMeshQuery::getEdgeMidPoint(dtPolyRef from, const dtPoly* fromPoly, return DT_SUCCESS; } + + /// @par /// /// This method is meant to be used for quick, short distance checks. @@ -2202,74 +2309,145 @@ dtStatus dtNavMeshQuery::getEdgeMidPoint(dtPolyRef from, const dtPoly* fromPoly, dtStatus dtNavMeshQuery::raycast(dtPolyRef startRef, const float* startPos, const float* endPos, const dtQueryFilter* filter, float* t, float* hitNormal, dtPolyRef* path, int* pathCount, const int maxPath) const +{ + dtRaycastHit hit; + hit.path = path; + hit.maxPath = maxPath; + + dtStatus status = raycast(startRef, startPos, endPos, filter, 0, &hit); + + *t = hit.t; + if (hitNormal) + dtVcopy(hitNormal, hit.hitNormal); + if (pathCount) + *pathCount = hit.pathCount; + + return status; +} + + +/// @par +/// +/// This method is meant to be used for quick, short distance checks. +/// +/// If the path array is too small to hold the result, it will be filled as +/// far as possible from the start postion toward the end position. +/// +/// Using the Hit Parameter t of RaycastHit +/// +/// If the hit parameter is a very high value (FLT_MAX), then the ray has hit +/// the end position. In this case the path represents a valid corridor to the +/// end position and the value of @p hitNormal is undefined. +/// +/// If the hit parameter is zero, then the start position is on the wall that +/// was hit and the value of @p hitNormal is undefined. +/// +/// If 0 < t < 1.0 then the following applies: +/// +/// @code +/// distanceToHitBorder = distanceToEndPosition * t +/// hitPoint = startPos + (endPos - startPos) * t +/// @endcode +/// +/// Use Case Restriction +/// +/// The raycast ignores the y-value of the end position. (2D check.) This +/// places significant limits on how it can be used. For example: +/// +/// Consider a scene where there is a main floor with a second floor balcony +/// that hangs over the main floor. So the first floor mesh extends below the +/// balcony mesh. The start position is somewhere on the first floor. The end +/// position is on the balcony. +/// +/// The raycast will search toward the end position along the first floor mesh. +/// If it reaches the end position's xz-coordinates it will indicate FLT_MAX +/// (no wall hit), meaning it reached the end position. This is one example of why +/// this method is meant for short distance checks. +/// +dtStatus dtNavMeshQuery::raycast(dtPolyRef startRef, const float* startPos, const float* endPos, + const dtQueryFilter* filter, const unsigned int options, + dtRaycastHit* hit, dtPolyRef prevRef) const { dtAssert(m_nav); - *t = 0; - if (pathCount) - *pathCount = 0; - + hit->t = 0; + hit->pathCount = 0; + hit->pathCost = 0; + // Validate input if (!startRef || !m_nav->isValidPolyRef(startRef)) return DT_FAILURE | DT_INVALID_PARAM; + if (prevRef && !m_nav->isValidPolyRef(prevRef)) + return DT_FAILURE | DT_INVALID_PARAM; - dtPolyRef curRef = startRef; - float verts[DT_VERTS_PER_POLYGON*3]; + float dir[3], curPos[3], lastPos[3]; + float verts[DT_VERTS_PER_POLYGON*3+3]; int n = 0; - - hitNormal[0] = 0; - hitNormal[1] = 0; - hitNormal[2] = 0; - + + dtVcopy(curPos, startPos); + dtVsub(dir, endPos, startPos); + dtVset(hit->hitNormal, 0, 0, 0); + dtStatus status = DT_SUCCESS; - + + const dtMeshTile* prevTile, *tile, *nextTile; + const dtPoly* prevPoly, *poly, *nextPoly; + dtPolyRef curRef, nextRef; + + // The API input has been checked already, skip checking internal data. + nextRef = curRef = startRef; + tile = 0; + poly = 0; + m_nav->getTileAndPolyByRefUnsafe(curRef, &tile, &poly); + nextTile = prevTile = tile; + nextPoly = prevPoly = poly; + if (prevRef) + m_nav->getTileAndPolyByRefUnsafe(prevRef, &prevTile, &prevPoly); + while (curRef) { // Cast ray against current polygon. - // The API input has been cheked already, skip checking internal data. - const dtMeshTile* tile = 0; - const dtPoly* poly = 0; - m_nav->getTileAndPolyByRefUnsafe(curRef, &tile, &poly); - // Collect vertices. int nv = 0; for (int i = 0; i < (int)poly->vertCount; ++i) { dtVcopy(&verts[nv*3], &tile->verts[poly->verts[i]*3]); nv++; - } + } float tmin, tmax; int segMin, segMax; if (!dtIntersectSegmentPoly2D(startPos, endPos, verts, nv, tmin, tmax, segMin, segMax)) { // Could not hit the polygon, keep the old t and report hit. - if (pathCount) - *pathCount = n; + hit->pathCount = n; return status; } // Keep track of furthest t so far. - if (tmax > *t) - *t = tmax; + if (tmax > hit->t) + hit->t = tmax; // Store visited polygons. - if (n < maxPath) - path[n++] = curRef; + if (n < hit->maxPath) + hit->path[n++] = curRef; else status |= DT_BUFFER_TOO_SMALL; - + // Ray end is completely inside the polygon. if (segMax == -1) { - *t = FLT_MAX; - if (pathCount) - *pathCount = n; + hit->t = FLT_MAX; + hit->pathCount = n; + + // add the cost + if (options & DT_RAYCAST_USE_COSTS) + hit->pathCost += filter->getCost(curPos, endPos, prevRef, prevTile, prevPoly, curRef, tile, poly, curRef, tile, poly); return status; } - + // Follow neighbours. - dtPolyRef nextRef = 0; + nextRef = 0; for (unsigned int i = poly->firstLink; i != DT_NULL_LINK; i = tile->links[i].next) { @@ -2280,8 +2458,8 @@ dtStatus dtNavMeshQuery::raycast(dtPolyRef startRef, const float* startPos, cons continue; // Get pointer to the next polygon. - const dtMeshTile* nextTile = 0; - const dtPoly* nextPoly = 0; + nextTile = 0; + nextPoly = 0; m_nav->getTileAndPolyByRefUnsafe(link->ref, &nextTile, &nextPoly); // Skip off-mesh connections. @@ -2349,6 +2527,24 @@ dtStatus dtNavMeshQuery::raycast(dtPolyRef startRef, const float* startPos, cons } } + // add the cost + if (options & DT_RAYCAST_USE_COSTS) + { + // compute the intersection point at the furthest end of the polygon + // and correct the height (since the raycast moves in 2d) + dtVcopy(lastPos, curPos); + dtVmad(curPos, startPos, dir, hit->t); + float* e1 = &verts[segMax*3]; + float* e2 = &verts[((segMax+1)%nv)*3]; + float eDir[3], diff[3]; + dtVsub(eDir, e2, e1); + dtVsub(diff, curPos, e1); + float s = dtSqr(eDir[0]) > dtSqr(eDir[2]) ? diff[0] / eDir[0] : diff[2] / eDir[2]; + curPos[1] = e1[1] + eDir[1] * s; + + hit->pathCost += filter->getCost(lastPos, curPos, prevRef, prevTile, prevPoly, curRef, tile, poly, nextRef, nextTile, nextPoly); + } + if (!nextRef) { // No neighbour, we hit a wall. @@ -2360,22 +2556,25 @@ dtStatus dtNavMeshQuery::raycast(dtPolyRef startRef, const float* startPos, cons const float* vb = &verts[b*3]; const float dx = vb[0] - va[0]; const float dz = vb[2] - va[2]; - hitNormal[0] = dz; - hitNormal[1] = 0; - hitNormal[2] = -dx; - dtVnormalize(hitNormal); + hit->hitNormal[0] = dz; + hit->hitNormal[1] = 0; + hit->hitNormal[2] = -dx; + dtVnormalize(hit->hitNormal); - if (pathCount) - *pathCount = n; + hit->pathCount = n; return status; } - + // No hit, advance to neighbour polygon. + prevRef = curRef; curRef = nextRef; + prevTile = tile; + tile = nextTile; + prevPoly = poly; + poly = nextPoly; } - if (pathCount) - *pathCount = n; + hit->pathCount = n; return status; } @@ -3333,6 +3532,15 @@ bool dtNavMeshQuery::isValidPolyRef(dtPolyRef ref, const dtQueryFilter* filter) bool dtNavMeshQuery::isInClosedList(dtPolyRef ref) const { if (!m_nodePool) return false; - const dtNode* node = m_nodePool->findNode(ref); - return node && node->flags & DT_NODE_CLOSED; + + dtNode* nodes[DT_MAX_STATES_PER_NODE]; + int n= m_nodePool->findNodes(ref, nodes, DT_MAX_STATES_PER_NODE); + + for (int i=0; iflags & DT_NODE_CLOSED) + return true; + } + + return false; } diff --git a/dep/recastnavigation/Detour/Source/DetourNode.cpp b/dep/recastnavigation/Detour/Source/DetourNode.cpp index 4c8215e20d..1d1897708f 100644 --- a/dep/recastnavigation/Detour/Source/DetourNode.cpp +++ b/dep/recastnavigation/Detour/Source/DetourNode.cpp @@ -71,27 +71,46 @@ void dtNodePool::clear() m_nodeCount = 0; } -dtNode* dtNodePool::findNode(dtPolyRef id) +unsigned int dtNodePool::findNodes(dtPolyRef id, dtNode** nodes, const int maxNodes) { + int n = 0; unsigned int bucket = dtHashRef(id) & (m_hashSize-1); dtNodeIndex i = m_first[bucket]; while (i != DT_NULL_IDX) { if (m_nodes[i].id == id) + { + if (n >= maxNodes) + return n; + nodes[n++] = &m_nodes[i]; + } + i = m_next[i]; + } + + return n; +} + +dtNode* dtNodePool::findNode(dtPolyRef id, unsigned char state) +{ + unsigned int bucket = dtHashRef(id) & (m_hashSize-1); + dtNodeIndex i = m_first[bucket]; + while (i != DT_NULL_IDX) + { + if (m_nodes[i].id == id && m_nodes[i].state == state) return &m_nodes[i]; i = m_next[i]; } return 0; } -dtNode* dtNodePool::getNode(dtPolyRef id) +dtNode* dtNodePool::getNode(dtPolyRef id, unsigned char state) { unsigned int bucket = dtHashRef(id) & (m_hashSize-1); dtNodeIndex i = m_first[bucket]; dtNode* node = 0; while (i != DT_NULL_IDX) { - if (m_nodes[i].id == id) + if (m_nodes[i].id == id && m_nodes[i].state == state) return &m_nodes[i]; i = m_next[i]; } @@ -108,6 +127,7 @@ dtNode* dtNodePool::getNode(dtPolyRef id) node->cost = 0; node->total = 0; node->id = id; + node->state = state; node->flags = 0; m_next[i] = m_first[bucket]; diff --git a/dep/recastnavigation/Recast/Include/Recast.h b/dep/recastnavigation/Recast/Include/Recast.h index 3f4ae96d1c..66974cdbcc 100644 --- a/dep/recastnavigation/Recast/Include/Recast.h +++ b/dep/recastnavigation/Recast/Include/Recast.h @@ -338,7 +338,7 @@ struct rcHeightfieldLayer int maxy; ///< The maximum y-bounds of usable data. (Along the z-axis.) int hmin; ///< The minimum height bounds of usable data. (Along the y-axis.) int hmax; ///< The maximum height bounds of usable data. (Along the y-axis.) - unsigned char* heights; ///< The heightfield. [Size: (width - borderSize*2) * (h - borderSize*2)] + unsigned char* heights; ///< The heightfield. [Size: width * height] unsigned char* areas; ///< Area ids. [Size: Same as #heights] unsigned char* cons; ///< Packed neighbor connection information. [Size: Same as #heights] }; @@ -969,7 +969,7 @@ void rcMarkCylinderArea(rcContext* ctx, const float* pos, /// @returns True if the operation completed successfully. bool rcBuildDistanceField(rcContext* ctx, rcCompactHeightfield& chf); -/// Builds region data for the heightfield using watershed partitioning. +/// Builds region data for the heightfield using watershed partitioning. /// @ingroup recast /// @param[in,out] ctx The build context to use during the operation. /// @param[in,out] chf A populated compact heightfield. @@ -983,6 +983,18 @@ bool rcBuildDistanceField(rcContext* ctx, rcCompactHeightfield& chf); bool rcBuildRegions(rcContext* ctx, rcCompactHeightfield& chf, const int borderSize, const int minRegionArea, const int mergeRegionArea); +/// Builds region data for the heightfield by partitioning the heightfield in non-overlapping layers. +/// @ingroup recast +/// @param[in,out] ctx The build context to use during the operation. +/// @param[in,out] chf A populated compact heightfield. +/// @param[in] borderSize The size of the non-navigable border around the heightfield. +/// [Limit: >=0] [Units: vx] +/// @param[in] minRegionArea The minimum number of cells allowed to form isolated island areas. +/// [Limit: >=0] [Units: vx]. +/// @returns True if the operation completed successfully. +bool rcBuildLayerRegions(rcContext* ctx, rcCompactHeightfield& chf, + const int borderSize, const int minRegionArea); + /// Builds region data for the heightfield using simple monotone partitioning. /// @ingroup recast /// @param[in,out] ctx The build context to use during the operation. @@ -997,7 +1009,6 @@ bool rcBuildRegions(rcContext* ctx, rcCompactHeightfield& chf, bool rcBuildRegionsMonotone(rcContext* ctx, rcCompactHeightfield& chf, const int borderSize, const int minRegionArea, const int mergeRegionArea); - /// Sets the neighbor connection data for the specified direction. /// @param[in] s The span to update. /// @param[in] dir The direction to set. [Limits: 0 <= value < 4] diff --git a/dep/recastnavigation/Recast/Source/RecastContour.cpp b/dep/recastnavigation/Recast/Source/RecastContour.cpp index 5c324bcedf..8aa9d1d92a 100644 --- a/dep/recastnavigation/Recast/Source/RecastContour.cpp +++ b/dep/recastnavigation/Recast/Source/RecastContour.cpp @@ -20,6 +20,7 @@ #include #include #include +#include #include "Recast.h" #include "RecastAlloc.h" #include "RecastAssert.h" @@ -36,7 +37,7 @@ static int getCornerHeight(int x, int y, int i, int dir, unsigned int regs[4] = {0,0,0,0}; // Combine region and area codes in order to prevent - // border vertices which are in between two areas to be removed. + // border vertices which are in between two areas to be removed. regs[0] = chf.spans[i].reg | (chf.areas[i] << 16); if (rcGetCon(s, dir) != RC_NOT_CONNECTED) @@ -187,27 +188,6 @@ static float distancePtSeg(const int x, const int z, const int px, const int pz, const int qx, const int qz) { -/* float pqx = (float)(qx - px); - float pqy = (float)(qy - py); - float pqz = (float)(qz - pz); - float dx = (float)(x - px); - float dy = (float)(y - py); - float dz = (float)(z - pz); - float d = pqx*pqx + pqy*pqy + pqz*pqz; - float t = pqx*dx + pqy*dy + pqz*dz; - if (d > 0) - t /= d; - if (t < 0) - t = 0; - else if (t > 1) - t = 1; - - dx = px + t*pqx - x; - dy = py + t*pqy - y; - dz = pz + t*pqz - z; - - return dx*dx + dy*dy + dz*dz;*/ - float pqx = (float)(qx - px); float pqz = (float)(qz - pz); float dx = (float)(x - px); @@ -257,13 +237,13 @@ static void simplifyContour(rcIntArray& points, rcIntArray& simplified, simplified.push(points[i*4+2]); simplified.push(i); } - } + } } if (simplified.size() == 0) { // If there is no connections at all, - // create some initial points for the simplification process. + // create some initial points for the simplification process. // Find lower-left and upper-right vertices of the contour. int llx = points[0]; int lly = points[1]; @@ -311,19 +291,19 @@ static void simplifyContour(rcIntArray& points, rcIntArray& simplified, { int ii = (i+1) % (simplified.size()/4); - const int ax = simplified[i*4+0]; - const int az = simplified[i*4+2]; - const int ai = simplified[i*4+3]; - - const int bx = simplified[ii*4+0]; - const int bz = simplified[ii*4+2]; - const int bi = simplified[ii*4+3]; + int ax = simplified[i*4+0]; + int az = simplified[i*4+2]; + int ai = simplified[i*4+3]; + + int bx = simplified[ii*4+0]; + int bz = simplified[ii*4+2]; + int bi = simplified[ii*4+3]; // Find maximum deviation from the segment. float maxd = 0; int maxi = -1; int ci, cinc, endi; - + // Traverse the segment in lexilogical order so that the // max deviation is calculated similarly when traversing // opposite segments. @@ -338,6 +318,8 @@ static void simplifyContour(rcIntArray& points, rcIntArray& simplified, cinc = pn-1; ci = (bi+cinc) % pn; endi = ai; + rcSwap(ax, bx); + rcSwap(az, bz); } // Tessellate only outer edges or edges between areas. @@ -397,11 +379,11 @@ static void simplifyContour(rcIntArray& points, rcIntArray& simplified, const int bx = simplified[ii*4+0]; const int bz = simplified[ii*4+2]; const int bi = simplified[ii*4+3]; - + // Find maximum deviation from the segment. int maxi = -1; int ci = (ai+1) % pn; - + // Tessellate only outer edges or edges between areas. bool tess = false; // Wall edges. @@ -469,32 +451,6 @@ static void simplifyContour(rcIntArray& points, rcIntArray& simplified, } -static void removeDegenerateSegments(rcIntArray& simplified) -{ - // Remove adjacent vertices which are equal on xz-plane, - // or else the triangulator will get confused. - for (int i = 0; i < simplified.size()/4; ++i) - { - int ni = i+1; - if (ni >= (simplified.size()/4)) - ni = 0; - - if (simplified[i*4+0] == simplified[ni*4+0] && - simplified[i*4+2] == simplified[ni*4+2]) - { - // Degenerate segment, remove. - for (int j = i; j < simplified.size()/4-1; ++j) - { - simplified[j*4+0] = simplified[(j+1)*4+0]; - simplified[j*4+1] = simplified[(j+1)*4+1]; - simplified[j*4+2] = simplified[(j+1)*4+2]; - simplified[j*4+3] = simplified[(j+1)*4+3]; - } - simplified.resize(simplified.size()-4); - } - } -} - static int calcAreaOfPolygon2D(const int* verts, const int nverts) { int area = 0; @@ -507,54 +463,155 @@ static int calcAreaOfPolygon2D(const int* verts, const int nverts) return (area+1) / 2; } -inline bool ileft(const int* a, const int* b, const int* c) +// TODO: these are the same as in RecastMesh.cpp, consider using the same. + +inline int prev(int i, int n) { return i-1 >= 0 ? i-1 : n-1; } +inline int next(int i, int n) { return i+1 < n ? i+1 : 0; } + +inline int area2(const int* a, const int* b, const int* c) { - return (b[0] - a[0]) * (c[2] - a[2]) - (c[0] - a[0]) * (b[2] - a[2]) <= 0; + return (b[0] - a[0]) * (c[2] - a[2]) - (c[0] - a[0]) * (b[2] - a[2]); } -static void getClosestIndices(const int* vertsa, const int nvertsa, - const int* vertsb, const int nvertsb, - int& ia, int& ib) +// Exclusive or: true iff exactly one argument is true. +// The arguments are negated to ensure that they are 0/1 +// values. Then the bitwise Xor operator may apply. +// (This idea is due to Michael Baldwin.) +inline bool xorb(bool x, bool y) { - int closestDist = 0xfffffff; - ia = -1, ib = -1; - for (int i = 0; i < nvertsa; ++i) + return !x ^ !y; +} + +// Returns true iff c is strictly to the left of the directed +// line through a to b. +inline bool left(const int* a, const int* b, const int* c) +{ + return area2(a, b, c) < 0; +} + +inline bool leftOn(const int* a, const int* b, const int* c) +{ + return area2(a, b, c) <= 0; +} + +inline bool collinear(const int* a, const int* b, const int* c) +{ + return area2(a, b, c) == 0; +} + +// Returns true iff ab properly intersects cd: they share +// a point interior to both segments. The properness of the +// intersection is ensured by using strict leftness. +static bool intersectProp(const int* a, const int* b, const int* c, const int* d) +{ + // Eliminate improper cases. + if (collinear(a,b,c) || collinear(a,b,d) || + collinear(c,d,a) || collinear(c,d,b)) + return false; + + return xorb(left(a,b,c), left(a,b,d)) && xorb(left(c,d,a), left(c,d,b)); +} + +// Returns T iff (a,b,c) are collinear and point c lies +// on the closed segement ab. +static bool between(const int* a, const int* b, const int* c) +{ + if (!collinear(a, b, c)) + return false; + // If ab not vertical, check betweenness on x; else on y. + if (a[0] != b[0]) + return ((a[0] <= c[0]) && (c[0] <= b[0])) || ((a[0] >= c[0]) && (c[0] >= b[0])); + else + return ((a[2] <= c[2]) && (c[2] <= b[2])) || ((a[2] >= c[2]) && (c[2] >= b[2])); +} + +// Returns true iff segments ab and cd intersect, properly or improperly. +static bool intersect(const int* a, const int* b, const int* c, const int* d) +{ + if (intersectProp(a, b, c, d)) + return true; + else if (between(a, b, c) || between(a, b, d) || + between(c, d, a) || between(c, d, b)) + return true; + else + return false; +} + +static bool vequal(const int* a, const int* b) +{ + return a[0] == b[0] && a[2] == b[2]; +} + +static bool intersectSegCountour(const int* d0, const int* d1, int i, int n, const int* verts) +{ + // For each edge (k,k+1) of P + for (int k = 0; k < n; k++) { - const int in = (i+1) % nvertsa; - const int ip = (i+nvertsa-1) % nvertsa; - const int* va = &vertsa[i*4]; - const int* van = &vertsa[in*4]; - const int* vap = &vertsa[ip*4]; + int k1 = next(k, n); + // Skip edges incident to i. + if (i == k || i == k1) + continue; + const int* p0 = &verts[k * 4]; + const int* p1 = &verts[k1 * 4]; + if (vequal(d0, p0) || vequal(d1, p0) || vequal(d0, p1) || vequal(d1, p1)) + continue; - for (int j = 0; j < nvertsb; ++j) + if (intersect(d0, d1, p0, p1)) + return true; + } + return false; +} + +static bool inCone(int i, int n, const int* verts, const int* pj) +{ + const int* pi = &verts[i * 4]; + const int* pi1 = &verts[next(i, n) * 4]; + const int* pin1 = &verts[prev(i, n) * 4]; + + // If P[i] is a convex vertex [ i+1 left or on (i-1,i) ]. + if (leftOn(pin1, pi, pi1)) + return left(pi, pj, pin1) && left(pj, pi, pi1); + // Assume (i-1,i,i+1) not collinear. + // else P[i] is reflex. + return !(leftOn(pi, pj, pi1) && leftOn(pj, pi, pin1)); +} + + +static void removeDegenerateSegments(rcIntArray& simplified) +{ + // Remove adjacent vertices which are equal on xz-plane, + // or else the triangulator will get confused. + int npts = simplified.size()/4; + for (int i = 0; i < npts; ++i) + { + int ni = next(i, npts); + + if (vequal(&simplified[i*4], &simplified[ni*4])) { - const int* vb = &vertsb[j*4]; - // vb must be "infront" of va. - if (ileft(vap,va,vb) && ileft(va,van,vb)) + // Degenerate segment, remove. + for (int j = i; j < simplified.size()/4-1; ++j) { - const int dx = vb[0] - va[0]; - const int dz = vb[2] - va[2]; - const int d = dx*dx + dz*dz; - if (d < closestDist) - { - ia = i; - ib = j; - closestDist = d; - } + simplified[j*4+0] = simplified[(j+1)*4+0]; + simplified[j*4+1] = simplified[(j+1)*4+1]; + simplified[j*4+2] = simplified[(j+1)*4+2]; + simplified[j*4+3] = simplified[(j+1)*4+3]; } + simplified.resize(simplified.size()-4); + npts--; } } } + static bool mergeContours(rcContour& ca, rcContour& cb, int ia, int ib) { const int maxVerts = ca.nverts + cb.nverts + 2; int* verts = (int*)rcAlloc(sizeof(int)*maxVerts*4, RC_ALLOC_PERM); if (!verts) return false; - + int nv = 0; - + // Copy contour A. for (int i = 0; i <= ca.nverts; ++i) { @@ -582,7 +639,7 @@ static bool mergeContours(rcContour& ca, rcContour& cb, int ia, int ib) rcFree(ca.verts); ca.verts = verts; ca.nverts = nv; - + rcFree(cb.verts); cb.verts = 0; cb.nverts = 0; @@ -590,18 +647,179 @@ static bool mergeContours(rcContour& ca, rcContour& cb, int ia, int ib) return true; } +struct rcContourHole +{ + rcContour* contour; + int minx, minz, leftmost; +}; + +struct rcContourRegion +{ + rcContour* outline; + rcContourHole* holes; + int nholes; +}; + +struct rcPotentialDiagonal +{ + int vert; + int dist; +}; + +// Finds the lowest leftmost vertex of a contour. +static void findLeftMostVertex(rcContour* contour, int* minx, int* minz, int* leftmost) +{ + *minx = contour->verts[0]; + *minz = contour->verts[2]; + *leftmost = 0; + for (int i = 1; i < contour->nverts; i++) + { + const int x = contour->verts[i*4+0]; + const int z = contour->verts[i*4+2]; + if (x < *minx || (x == *minx && z < *minz)) + { + *minx = x; + *minz = z; + *leftmost = i; + } + } +} + +static int compareHoles(const void* va, const void* vb) +{ + const rcContourHole* a = (const rcContourHole*)va; + const rcContourHole* b = (const rcContourHole*)vb; + if (a->minx == b->minx) + { + if (a->minz < b->minz) + return -1; + if (a->minz > b->minz) + return 1; + } + else + { + if (a->minx < b->minx) + return -1; + if (a->minx > b->minx) + return 1; + } + return 0; +} + + +static int compareDiagDist(const void* va, const void* vb) +{ + const rcPotentialDiagonal* a = (const rcPotentialDiagonal*)va; + const rcPotentialDiagonal* b = (const rcPotentialDiagonal*)vb; + if (a->dist < b->dist) + return -1; + if (a->dist > b->dist) + return 1; + return 0; +} + + +static void mergeRegionHoles(rcContext* ctx, rcContourRegion& region) +{ + // Sort holes from left to right. + for (int i = 0; i < region.nholes; i++) + findLeftMostVertex(region.holes[i].contour, ®ion.holes[i].minx, ®ion.holes[i].minz, ®ion.holes[i].leftmost); + + qsort(region.holes, region.nholes, sizeof(rcContourHole), compareHoles); + + int maxVerts = region.outline->nverts; + for (int i = 0; i < region.nholes; i++) + maxVerts += region.holes[i].contour->nverts; + + rcScopedDelete diags = (rcPotentialDiagonal*)rcAlloc(sizeof(rcPotentialDiagonal)*maxVerts, RC_ALLOC_TEMP); + if (!diags) + { + ctx->log(RC_LOG_WARNING, "mergeRegionHoles: Failed to allocated diags %d.", maxVerts); + return; + } + + rcContour* outline = region.outline; + + // Merge holes into the outline one by one. + for (int i = 0; i < region.nholes; i++) + { + rcContour* hole = region.holes[i].contour; + + int index = -1; + int bestVertex = region.holes[i].leftmost; + for (int iter = 0; iter < hole->nverts; iter++) + { + // Find potential diagonals. + // The 'best' vertex must be in the cone described by 3 cosequtive vertices of the outline. + // ..o j-1 + // | + // | * best + // | + // j o-----o j+1 + // : + int ndiags = 0; + const int* corner = &hole->verts[bestVertex*4]; + for (int j = 0; j < outline->nverts; j++) + { + if (inCone(j, outline->nverts, outline->verts, corner)) + { + int dx = outline->verts[j*4+0] - corner[0]; + int dz = outline->verts[j*4+2] - corner[2]; + diags[ndiags].vert = j; + diags[ndiags].dist = dx*dx + dz*dz; + ndiags++; + } + } + // Sort potential diagonals by distance, we want to make the connection as short as possible. + qsort(diags, ndiags, sizeof(rcPotentialDiagonal), compareDiagDist); + + // Find a diagonal that is not intersecting the outline not the remaining holes. + index = -1; + for (int j = 0; j < ndiags; j++) + { + const int* pt = &outline->verts[diags[j].vert*4]; + bool intersect = intersectSegCountour(pt, corner, diags[i].vert, outline->nverts, outline->verts); + for (int k = i; k < region.nholes && !intersect; k++) + intersect |= intersectSegCountour(pt, corner, -1, region.holes[k].contour->nverts, region.holes[k].contour->verts); + if (!intersect) + { + index = diags[j].vert; + break; + } + } + // If found non-intersecting diagonal, stop looking. + if (index != -1) + break; + // All the potential diagonals for the current vertex were intersecting, try next vertex. + bestVertex = (bestVertex + 1) % hole->nverts; + } + + if (index == -1) + { + ctx->log(RC_LOG_WARNING, "mergeHoles: Failed to find merge points for %p and %p.", region.outline, hole); + continue; + } + if (!mergeContours(*region.outline, *hole, index, bestVertex)) + { + ctx->log(RC_LOG_WARNING, "mergeHoles: Failed to merge contours %p and %p.", region.outline, hole); + continue; + } + } +} + + /// @par /// /// The raw contours will match the region outlines exactly. The @p maxError and @p maxEdgeLen /// parameters control how closely the simplified contours will match the raw contours. /// -/// Simplified contours are generated such that the vertices for portals between areas match up. +/// Simplified contours are generated such that the vertices for portals between areas match up. /// (They are considered mandatory vertices.) /// /// Setting @p maxEdgeLength to zero will disabled the edge length feature. -/// +/// /// See the #rcConfig documentation for more information on the configuration parameters. -/// +/// /// @see rcAllocContourSet, rcCompactHeightfield, rcContourSet, rcConfig bool rcBuildContours(rcContext* ctx, rcCompactHeightfield& chf, const float maxError, const int maxEdgeLen, @@ -704,17 +922,17 @@ bool rcBuildContours(rcContext* ctx, rcCompactHeightfield& chf, verts.resize(0); simplified.resize(0); - + ctx->startTimer(RC_TIMER_BUILD_CONTOURS_TRACE); walkContour(x, y, i, chf, flags, verts); ctx->stopTimer(RC_TIMER_BUILD_CONTOURS_TRACE); - + ctx->startTimer(RC_TIMER_BUILD_CONTOURS_SIMPLIFY); simplifyContour(verts, simplified, maxError, maxEdgeLen, buildFlags); removeDegenerateSegments(simplified); ctx->stopTimer(RC_TIMER_BUILD_CONTOURS_SIMPLIFY); - + // Store region->contour remap info. // Create contour. if (simplified.size()/4 >= 3) @@ -722,7 +940,7 @@ bool rcBuildContours(rcContext* ctx, rcCompactHeightfield& chf, if (cset.nconts >= maxContours) { // Allocate more contours. - // This can happen when there are tiny holes in the heightfield. + // This happens when a region has holes. const int oldMax = maxContours; maxContours *= 2; rcContour* newConts = (rcContour*)rcAlloc(sizeof(rcContour)*maxContours, RC_ALLOC_PERM); @@ -735,10 +953,10 @@ bool rcBuildContours(rcContext* ctx, rcCompactHeightfield& chf, } rcFree(cset.conts); cset.conts = newConts; - + ctx->log(RC_LOG_WARNING, "rcBuildContours: Expanding max contours from %d to %d.", oldMax, maxContours); } - + rcContour* cont = &cset.conts[cset.nconts++]; cont->nverts = simplified.size()/4; @@ -779,17 +997,6 @@ bool rcBuildContours(rcContext* ctx, rcCompactHeightfield& chf, } } -/* cont->cx = cont->cy = cont->cz = 0; - for (int i = 0; i < cont->nverts; ++i) - { - cont->cx += cont->verts[i*4+0]; - cont->cy += cont->verts[i*4+1]; - cont->cz += cont->verts[i*4+2]; - } - cont->cx /= cont->nverts; - cont->cy /= cont->nverts; - cont->cz /= cont->nverts;*/ - cont->reg = reg; cont->area = area; } @@ -797,52 +1004,100 @@ bool rcBuildContours(rcContext* ctx, rcCompactHeightfield& chf, } } - // Check and merge droppings. - // Sometimes the previous algorithms can fail and create several contours - // per area. This pass will try to merge the holes into the main region. - for (int i = 0; i < cset.nconts; ++i) + // Merge holes if needed. + if (cset.nconts > 0) { - rcContour& cont = cset.conts[i]; - // Check if the contour is would backwards. - if (calcAreaOfPolygon2D(cont.verts, cont.nverts) < 0) + // Calculate winding of all polygons. + rcScopedDelete winding = (char*)rcAlloc(sizeof(char)*cset.nconts, RC_ALLOC_TEMP); + if (!winding) { - // Find another contour which has the same region ID. - int mergeIdx = -1; - for (int j = 0; j < cset.nconts; ++j) + ctx->log(RC_LOG_ERROR, "rcBuildContours: Out of memory 'hole' (%d).", cset.nconts); + return false; + } + int nholes = 0; + for (int i = 0; i < cset.nconts; ++i) + { + rcContour& cont = cset.conts[i]; + // If the contour is wound backwards, it is a hole. + winding[i] = calcAreaOfPolygon2D(cont.verts, cont.nverts) < 0 ? -1 : 1; + if (winding[i] < 0) + nholes++; + } + + if (nholes > 0) + { + // Collect outline contour and holes contours per region. + // We assume that there is one outline and multiple holes. + const int nregions = chf.maxRegions+1; + rcScopedDelete regions = (rcContourRegion*)rcAlloc(sizeof(rcContourRegion)*nregions, RC_ALLOC_TEMP); + if (!regions) { - if (i == j) continue; - if (cset.conts[j].nverts && cset.conts[j].reg == cont.reg) + ctx->log(RC_LOG_ERROR, "rcBuildContours: Out of memory 'regions' (%d).", nregions); + return false; + } + memset(regions, 0, sizeof(rcContourRegion)*nregions); + + rcScopedDelete holes = (rcContourHole*)rcAlloc(sizeof(rcContourHole)*cset.nconts, RC_ALLOC_TEMP); + if (!holes) + { + ctx->log(RC_LOG_ERROR, "rcBuildContours: Out of memory 'holes' (%d).", cset.nconts); + return false; + } + memset(holes, 0, sizeof(rcContourHole)*cset.nconts); + + for (int i = 0; i < cset.nconts; ++i) + { + rcContour& cont = cset.conts[i]; + // Positively would contours are outlines, negative holes. + if (winding[i] > 0) { - // Make sure the polygon is correctly oriented. - if (calcAreaOfPolygon2D(cset.conts[j].verts, cset.conts[j].nverts)) - { - mergeIdx = j; - break; - } + if (regions[cont.reg].outline) + ctx->log(RC_LOG_ERROR, "rcBuildContours: Multiple outlines for region %d.", cont.reg); + regions[cont.reg].outline = &cont; + } + else + { + regions[cont.reg].nholes++; } } - if (mergeIdx == -1) + int index = 0; + for (int i = 0; i < nregions; i++) { - ctx->log(RC_LOG_WARNING, "rcBuildContours: Could not find merge target for bad contour %d.", i); - } - else - { - rcContour& mcont = cset.conts[mergeIdx]; - // Merge by closest points. - int ia = 0, ib = 0; - getClosestIndices(mcont.verts, mcont.nverts, cont.verts, cont.nverts, ia, ib); - if (ia == -1 || ib == -1) + if (regions[i].nholes > 0) { - ctx->log(RC_LOG_WARNING, "rcBuildContours: Failed to find merge points for %d and %d.", i, mergeIdx); - continue; + regions[i].holes = &holes[index]; + index += regions[i].nholes; + regions[i].nholes = 0; } - if (!mergeContours(mcont, cont, ia, ib)) + } + for (int i = 0; i < cset.nconts; ++i) + { + rcContour& cont = cset.conts[i]; + rcContourRegion& reg = regions[cont.reg]; + if (winding[i] < 0) + reg.holes[reg.nholes++].contour = &cont; + } + + // Finally merge each regions holes into the outline. + for (int i = 0; i < nregions; i++) + { + rcContourRegion& reg = regions[i]; + if (!reg.nholes) continue; + + if (reg.outline) { - ctx->log(RC_LOG_WARNING, "rcBuildContours: Failed to merge contours %d and %d.", i, mergeIdx); - continue; + mergeRegionHoles(ctx, reg); + } + else + { + // The region does not have an outline. + // This can happen if the contour becaomes selfoverlapping because of + // too aggressive simplification settings. + ctx->log(RC_LOG_ERROR, "rcBuildContours: Bad outline for region %d, contour simplification is likely too aggressive.", i); } } } + } ctx->stopTimer(RC_TIMER_BUILD_CONTOURS); diff --git a/dep/recastnavigation/Recast/Source/RecastLayers.cpp b/dep/recastnavigation/Recast/Source/RecastLayers.cpp index 204f72e8cb..cb1a39f4bd 100644 --- a/dep/recastnavigation/Recast/Source/RecastLayers.cpp +++ b/dep/recastnavigation/Recast/Source/RecastLayers.cpp @@ -38,7 +38,7 @@ struct rcLayerRegion unsigned char layerId; // Layer ID unsigned char nlayers; // Layer count unsigned char nneis; // Neighbour count - unsigned char base; // Flag indicating if the region is hte base of merged regions. + unsigned char base; // Flag indicating if the region is the base of merged regions. }; diff --git a/dep/recastnavigation/Recast/Source/RecastMesh.cpp b/dep/recastnavigation/Recast/Source/RecastMesh.cpp index 8af609b79f..e4f9c4b362 100644 --- a/dep/recastnavigation/Recast/Source/RecastMesh.cpp +++ b/dep/recastnavigation/Recast/Source/RecastMesh.cpp @@ -288,6 +288,53 @@ static bool diagonal(int i, int j, int n, const int* verts, int* indices) return inCone(i, j, n, verts, indices) && diagonalie(i, j, n, verts, indices); } + +static bool diagonalieLoose(int i, int j, int n, const int* verts, int* indices) +{ + const int* d0 = &verts[(indices[i] & 0x0fffffff) * 4]; + const int* d1 = &verts[(indices[j] & 0x0fffffff) * 4]; + + // For each edge (k,k+1) of P + for (int k = 0; k < n; k++) + { + int k1 = next(k, n); + // Skip edges incident to i or j + if (!((k == i) || (k1 == i) || (k == j) || (k1 == j))) + { + const int* p0 = &verts[(indices[k] & 0x0fffffff) * 4]; + const int* p1 = &verts[(indices[k1] & 0x0fffffff) * 4]; + + if (vequal(d0, p0) || vequal(d1, p0) || vequal(d0, p1) || vequal(d1, p1)) + continue; + + if (intersectProp(d0, d1, p0, p1)) + return false; + } + } + return true; +} + +static bool inConeLoose(int i, int j, int n, const int* verts, int* indices) +{ + const int* pi = &verts[(indices[i] & 0x0fffffff) * 4]; + const int* pj = &verts[(indices[j] & 0x0fffffff) * 4]; + const int* pi1 = &verts[(indices[next(i, n)] & 0x0fffffff) * 4]; + const int* pin1 = &verts[(indices[prev(i, n)] & 0x0fffffff) * 4]; + + // If P[i] is a convex vertex [ i+1 left or on (i-1,i) ]. + if (leftOn(pin1, pi, pi1)) + return leftOn(pi, pj, pin1) && leftOn(pj, pi, pi1); + // Assume (i-1,i,i+1) not collinear. + // else P[i] is reflex. + return !(leftOn(pi, pj, pi1) && leftOn(pj, pi, pin1)); +} + +static bool diagonalLoose(int i, int j, int n, const int* verts, int* indices) +{ + return inConeLoose(i, j, n, verts, indices) && diagonalieLoose(i, j, n, verts, indices); +} + + static int triangulate(int n, const int* verts, int* indices, int* tris) { int ntris = 0; @@ -328,14 +375,41 @@ static int triangulate(int n, const int* verts, int* indices, int* tris) if (mini == -1) { - // Should not happen. -/* printf("mini == -1 ntris=%d n=%d\n", ntris, n); + // We might get here because the contour has overlapping segments, like this: + // + // A o-o=====o---o B + // / |C D| \ + // o o o o + // : : : : + // We'll try to recover by loosing up the inCone test a bit so that a diagonal + // like A-B or C-D can be found and we can continue. + minLen = -1; + mini = -1; for (int i = 0; i < n; i++) { - printf("%d ", indices[i] & 0x0fffffff); + int i1 = next(i, n); + int i2 = next(i1, n); + if (diagonalLoose(i, i2, n, verts, indices)) + { + const int* p0 = &verts[(indices[i] & 0x0fffffff) * 4]; + const int* p2 = &verts[(indices[next(i2, n)] & 0x0fffffff) * 4]; + int dx = p2[0] - p0[0]; + int dy = p2[2] - p0[2]; + int len = dx*dx + dy*dy; + + if (minLen < 0 || len < minLen) + { + minLen = len; + mini = i; + } + } + } + if (mini == -1) + { + // The contour is messed up. This sometimes happens + // if the contour simplification is too aggressive. + return -ntris; } - printf("\n");*/ - return -ntris; } int i = mini; @@ -1463,7 +1537,7 @@ bool rcCopyPolyMesh(rcContext* ctx, const rcPolyMesh& src, rcPolyMesh& dst) ctx->log(RC_LOG_ERROR, "rcCopyPolyMesh: Out of memory 'dst.flags' (%d).", src.npolys); return false; } - memcpy(dst.flags, src.flags, sizeof(unsigned char)*src.npolys); + memcpy(dst.flags, src.flags, sizeof(unsigned short)*src.npolys); return true; } diff --git a/dep/recastnavigation/Recast/Source/RecastMeshDetail.cpp b/dep/recastnavigation/Recast/Source/RecastMeshDetail.cpp index 8325b88370..5cc2adf032 100644 --- a/dep/recastnavigation/Recast/Source/RecastMeshDetail.cpp +++ b/dep/recastnavigation/Recast/Source/RecastMeshDetail.cpp @@ -56,7 +56,7 @@ inline float vdist2(const float* p, const float* q) } inline float vcross2(const float* p1, const float* p2, const float* p3) -{ +{ const float u1 = p2[0] - p1[0]; const float v1 = p2[2] - p1[2]; const float u2 = p3[0] - p1[0]; @@ -68,21 +68,27 @@ static bool circumCircle(const float* p1, const float* p2, const float* p3, float* c, float& r) { static const float EPS = 1e-6f; + // Calculate the circle relative to p1, to avoid some precision issues. + const float v1[3] = {0,0,0}; + float v2[3], v3[3]; + rcVsub(v2, p2,p1); + rcVsub(v3, p3,p1); - const float cp = vcross2(p1, p2, p3); + const float cp = vcross2(v1, v2, v3); if (fabsf(cp) > EPS) { - const float p1Sq = vdot2(p1,p1); - const float p2Sq = vdot2(p2,p2); - const float p3Sq = vdot2(p3,p3); - c[0] = (p1Sq*(p2[2]-p3[2]) + p2Sq*(p3[2]-p1[2]) + p3Sq*(p1[2]-p2[2])) / (2*cp); - c[2] = (p1Sq*(p3[0]-p2[0]) + p2Sq*(p1[0]-p3[0]) + p3Sq*(p2[0]-p1[0])) / (2*cp); - r = vdist2(c, p1); + const float v1Sq = vdot2(v1,v1); + const float v2Sq = vdot2(v2,v2); + const float v3Sq = vdot2(v3,v3); + c[0] = (v1Sq*(v2[2]-v3[2]) + v2Sq*(v3[2]-v1[2]) + v3Sq*(v1[2]-v2[2])) / (2*cp); + c[1] = 0; + c[2] = (v1Sq*(v3[0]-v2[0]) + v2Sq*(v1[0]-v3[0]) + v3Sq*(v2[0]-v1[0])) / (2*cp); + r = vdist2(c, v1); + rcVadd(c, c, p1); return true; } - - c[0] = p1[0]; - c[2] = p1[2]; + + rcVcopy(c, p1); r = 0; return false; } @@ -93,7 +99,7 @@ static float distPtTri(const float* p, const float* a, const float* b, const flo rcVsub(v0, c,a); rcVsub(v1, b,a); rcVsub(v2, p,a); - + const float dot00 = vdot2(v0, v0); const float dot01 = vdot2(v0, v1); const float dot02 = vdot2(v0, v2); @@ -178,7 +184,7 @@ static float distToTriMesh(const float* p, const float* verts, const int /*nvert static float distToPoly(int nvert, const float* verts, const float* p) { - + float dmin = FLT_MAX; int i, j, c = 0; for (i = 0, j = nvert-1; i < nvert; j = i++) @@ -216,22 +222,13 @@ static unsigned short getHeight(const float fx, const float fy, const float fz, if (nx < 0 || nz < 0 || nx >= hp.width || nz >= hp.height) continue; const unsigned short nh = hp.data[nx+nz*hp.width]; if (nh == RC_UNSET_HEIGHT) continue; - + const float d = fabsf(nh*ch - fy); if (d < dmin) { h = nh; dmin = d; } - -/* const float dx = (nx+0.5f)*cs - fx; - const float dz = (nz+0.5f)*cs - fz; - const float d = dx*dx+dz*dz; - if (d < dmin) - { - h = nh; - dmin = d; - } */ } } return h; @@ -263,7 +260,7 @@ static int addEdge(rcContext* ctx, int* edges, int& nedges, const int maxEdges, return UNDEF; } - // Add edge if not already in the triangulation. + // Add edge if not already in the triangulation. int e = findEdge(edges, nedges, s, t); if (e == UNDEF) { @@ -286,7 +283,7 @@ static void updateLeftFace(int* e, int s, int t, int f) e[2] = f; else if (e[1] == s && e[0] == t && e[3] == UNDEF) e[3] = f; -} +} static int overlapSegSeg2d(const float* a, const float* b, const float* c, const float* d) { @@ -298,7 +295,7 @@ static int overlapSegSeg2d(const float* a, const float* b, const float* c, const float a4 = a3 + a2 - a1; if (a3 * a4 < 0.0f) return 1; - } + } return 0; } @@ -320,7 +317,7 @@ static bool overlapEdges(const float* pts, const int* edges, int nedges, int s1, static void completeFacet(rcContext* ctx, const float* pts, int npts, int* edges, int& nedges, const int maxEdges, int& nfaces, int e) { static const float EPS = 1e-5f; - + int* edge = &edges[e*4]; // Cache s and t. @@ -337,11 +334,11 @@ static void completeFacet(rcContext* ctx, const float* pts, int npts, int* edges } else { - // Edge already completed. + // Edge already completed. return; } - // Find best point on left of edge. + // Find best point on left of edge. int pt = npts; float c[3] = {0,0,0}; float r = -1; @@ -385,20 +382,20 @@ static void completeFacet(rcContext* ctx, const float* pts, int npts, int* edges } } - // Add new triangle or update edge info if s-t is on hull. + // Add new triangle or update edge info if s-t is on hull. if (pt < npts) { - // Update face information of edge being completed. + // Update face information of edge being completed. updateLeftFace(&edges[e*4], s, t, nfaces); - // Add new edge or update face info of old edge. + // Add new edge or update face info of old edge. e = findEdge(edges, nedges, pt, s); if (e == UNDEF) addEdge(ctx, edges, nedges, maxEdges, pt, s, nfaces, UNDEF); else updateLeftFace(&edges[e*4], pt, s, nfaces); - // Add new edge or update face info of old edge. + // Add new edge or update face info of old edge. e = findEdge(edges, nedges, t, pt); if (e == UNDEF) addEdge(ctx, edges, nedges, maxEdges, t, pt, nfaces, UNDEF); @@ -434,7 +431,7 @@ static void delaunayHull(rcContext* ctx, const int npts, const float* pts, completeFacet(ctx, pts, npts, &edges[0], nedges, maxEdges, nfaces, currentEdge); currentEdge++; } - + // Create tris tris.resize(nfaces*4); for (int i = 0; i < nfaces*4; ++i) @@ -489,6 +486,103 @@ static void delaunayHull(rcContext* ctx, const int npts, const float* pts, } } +// Calculate minimum extend of the polygon. +static float polyMinExtent(const float* verts, const int nverts) +{ + float minDist = FLT_MAX; + for (int i = 0; i < nverts; i++) + { + const int ni = (i+1) % nverts; + const float* p1 = &verts[i*3]; + const float* p2 = &verts[ni*3]; + float maxEdgeDist = 0; + for (int j = 0; j < nverts; j++) + { + if (j == i || j == ni) continue; + float d = distancePtSeg2d(&verts[j*3], p1,p2); + maxEdgeDist = rcMax(maxEdgeDist, d); + } + minDist = rcMin(minDist, maxEdgeDist); + } + return rcSqrt(minDist); +} + +inline int next(int i, int n) +{ + return (i+1) % n; +} + +inline int prev(int i, int n) +{ + return (i + n-1) % n; +} + +static void triangulateHull(const int nverts, const float* verts, const int nhull, const int* hull, rcIntArray& tris) +{ + int start = 0, left = 1, right = nhull-1; + + // Start from an ear with shortest perimeter. + // This tends to favor well formed triangles as starting point. + float dmin = 0; + for (int i = 0; i < nhull; i++) + { + int pi = prev(i, nhull); + int ni = next(i, nhull); + const float* pv = &verts[hull[pi]*3]; + const float* cv = &verts[hull[i]*3]; + const float* nv = &verts[hull[ni]*3]; + const float d = vdist2(pv,cv) + vdist2(cv,nv) + vdist2(nv,pv); + if (d < dmin) + { + start = i; + left = ni; + right = pi; + dmin = d; + } + } + + // Add first triangle + tris.push(hull[start]); + tris.push(hull[left]); + tris.push(hull[right]); + tris.push(0); + + // Triangulate the polygon by moving left or right, + // depending on which triangle has shorter perimeter. + // This heuristic was chose emprically, since it seems + // handle tesselated straight edges well. + while (next(left, nhull) != right) + { + // Check to see if se should advance left or right. + int nleft = next(left, nhull); + int nright = prev(right, nhull); + + const float* cvleft = &verts[hull[left]*3]; + const float* nvleft = &verts[hull[nleft]*3]; + const float* cvright = &verts[hull[right]*3]; + const float* nvright = &verts[hull[nright]*3]; + const float dleft = vdist2(cvleft, nvleft) + vdist2(nvleft, cvright); + const float dright = vdist2(cvright, nvright) + vdist2(cvleft, nvright); + + if (dleft < dright) + { + tris.push(hull[left]); + tris.push(hull[nleft]); + tris.push(hull[right]); + tris.push(0); + left = nleft; + } + else + { + tris.push(hull[left]); + tris.push(hull[nright]); + tris.push(hull[right]); + tris.push(0); + right = nright; + } + } +} + inline float getJitterX(const int i) { @@ -512,16 +606,22 @@ static bool buildPolyDetail(rcContext* ctx, const float* in, const int nin, float edge[(MAX_VERTS_PER_EDGE+1)*3]; int hull[MAX_VERTS]; int nhull = 0; - + nverts = 0; - + for (int i = 0; i < nin; ++i) rcVcopy(&verts[i*3], &in[i*3]); nverts = nin; + edges.resize(0); + tris.resize(0); + const float cs = chf.cs; const float ics = 1.0f/cs; + // Calculate minimum extents of the polygon based on input data. + float minExtent = polyMinExtent(verts, nverts); + // Tessellate outlines. // This is done in separate pass in order to ensure // seamless height values across the ply boundaries. @@ -628,27 +728,26 @@ static bool buildPolyDetail(rcContext* ctx, const float* in, const int nin, } } - + // If the polygon minimum extent is small (sliver or small triangle), do not try to add internal points. + if (minExtent < sampleDist*2) + { + triangulateHull(nverts, verts, nhull, hull, tris); + return true; + } + // Tessellate the base mesh. - edges.resize(0); - tris.resize(0); - - delaunayHull(ctx, nverts, verts, nhull, hull, tris, edges); + // We're using the triangulateHull instead of delaunayHull as it tends to + // create a bit better triangulation for long thing triangles when there + // are no internal points. + triangulateHull(nverts, verts, nhull, hull, tris); if (tris.size() == 0) { // Could not triangulate the poly, make sure there is some valid data there. - ctx->log(RC_LOG_WARNING, "buildPolyDetail: Could not triangulate polygon, adding default data."); - for (int i = 2; i < nverts; ++i) - { - tris.push(0); - tris.push(i-1); - tris.push(i); - tris.push(0); - } + ctx->log(RC_LOG_WARNING, "buildPolyDetail: Could not triangulate polygon (%d verts).", nverts); return true; } - + if (sampleDist > 0) { // Create sample locations in a grid. @@ -681,7 +780,7 @@ static bool buildPolyDetail(rcContext* ctx, const float* in, const int nin, samples.push(0); // Not added } } - + // Add the samples starting from the one that has the most // error. The procedure stops when all samples are added // or when the max error is within treshold. @@ -690,7 +789,7 @@ static bool buildPolyDetail(rcContext* ctx, const float* in, const int nin, { if (nverts >= MAX_VERTS) break; - + // Find sample with most error. float bestpt[3] = {0,0,0}; float bestd = 0; @@ -728,24 +827,24 @@ static bool buildPolyDetail(rcContext* ctx, const float* in, const int nin, edges.resize(0); tris.resize(0); delaunayHull(ctx, nverts, verts, nhull, hull, tris, edges); - } + } } - + const int ntris = tris.size()/4; if (ntris > MAX_TRIS) { tris.resize(MAX_TRIS*4); ctx->log(RC_LOG_ERROR, "rcBuildPolyMeshDetail: Shrinking triangle count from %d to max %d.", ntris, MAX_TRIS); } - + return true; } static void getHeightDataSeedsFromVertices(const rcCompactHeightfield& chf, - const unsigned short* poly, const int npoly, - const unsigned short* verts, const int bs, - rcHeightPatch& hp, rcIntArray& stack) + const unsigned short* poly, const int npoly, + const unsigned short* verts, const int bs, + rcHeightPatch& hp, rcIntArray& stack) { // Floodfill the heightfield to get 2D height data, // starting at vertex locations as seeds. @@ -849,7 +948,7 @@ static void getHeightDataSeedsFromVertices(const rcCompactHeightfield& chf, continue; const int ai = (int)chf.cells[(ax+bs)+(ay+bs)*chf.width].index + rcGetCon(cs, dir); - + int idx = ax-hp.xmin+(ay-hp.ymin)*hp.width; hp.data[idx] = 1; @@ -858,9 +957,9 @@ static void getHeightDataSeedsFromVertices(const rcCompactHeightfield& chf, stack.push(ai); } } - + memset(hp.data, 0xff, sizeof(unsigned short)*hp.width*hp.height); - + // Mark start locations. for (int i = 0; i < stack.size(); i += 3) { @@ -870,8 +969,8 @@ static void getHeightDataSeedsFromVertices(const rcCompactHeightfield& chf, int idx = cx-hp.xmin+(cy-hp.ymin)*hp.width; const rcCompactSpan& cs = chf.spans[ci]; hp.data[idx] = cs.y; - - // getHeightData seeds are given in coordinates with borders + + // getHeightData seeds are given in coordinates with borders stack[i+0] += bs; stack[i+1] += bs; } @@ -888,12 +987,12 @@ static void getHeightData(const rcCompactHeightfield& chf, { // Note: Reads to the compact heightfield are offset by border size (bs) // since border size offset is already removed from the polymesh vertices. - + stack.resize(0); memset(hp.data, 0xff, sizeof(unsigned short)*hp.width*hp.height); bool empty = true; - + // Copy the height from the same region, and mark region borders // as seed points to fill the rest. for (int hy = 0; hy < hp.height; hy++) @@ -911,7 +1010,7 @@ static void getHeightData(const rcCompactHeightfield& chf, // Store height hp.data[hx + hy*hp.width] = s.y; empty = false; - + // If any of the neighbours is not in same region, // add the current location as flood fill start bool border = false; @@ -940,8 +1039,8 @@ static void getHeightData(const rcCompactHeightfield& chf, } } } - } - + } + // if the polygon does not contian any points from the current region (rare, but happens) // then use the cells closest to the polygon vertices as seeds to fill the height field if (empty) @@ -963,7 +1062,7 @@ static void getHeightData(const rcCompactHeightfield& chf, memmove(&stack[0], &stack[RETRACT_SIZE*3], sizeof(int)*(stack.size()-RETRACT_SIZE*3)); stack.resize(stack.size()-RETRACT_SIZE*3); } - + const rcCompactSpan& cs = chf.spans[ci]; for (int dir = 0; dir < 4; ++dir) { @@ -982,9 +1081,9 @@ static void getHeightData(const rcCompactHeightfield& chf, const int ai = (int)chf.cells[ax + ay*chf.width].index + rcGetCon(cs, dir); const rcCompactSpan& as = chf.spans[ai]; - + hp.data[hx + hy*hp.width] = as.y; - + stack.push(ax); stack.push(ay); stack.push(ai); @@ -999,7 +1098,7 @@ static unsigned char getEdgeFlags(const float* va, const float* vb, static const float thrSqr = rcSqr(0.001f); for (int i = 0, j = npoly-1; i < npoly; j=i++) { - if (distancePtSeg2d(va, &vpoly[j*3], &vpoly[i*3]) < thrSqr && + if (distancePtSeg2d(va, &vpoly[j*3], &vpoly[i*3]) < thrSqr && distancePtSeg2d(vb, &vpoly[j*3], &vpoly[i*3]) < thrSqr) return 1; } @@ -1028,7 +1127,7 @@ bool rcBuildPolyMeshDetail(rcContext* ctx, const rcPolyMesh& mesh, const rcCompa rcAssert(ctx); ctx->startTimer(RC_TIMER_BUILD_POLYMESHDETAIL); - + if (mesh.nverts == 0 || mesh.npolys == 0) return true; @@ -1107,10 +1206,10 @@ bool rcBuildPolyMeshDetail(rcContext* ctx, const rcPolyMesh& mesh, const rcCompa ctx->log(RC_LOG_ERROR, "rcBuildPolyMeshDetail: Out of memory 'dmesh.meshes' (%d).", dmesh.nmeshes*4); return false; } - + int vcap = nPolyVerts+nPolyVerts/2; int tcap = vcap*2; - + dmesh.nverts = 0; dmesh.verts = (float*)rcAlloc(sizeof(float)*vcap*3, RC_ALLOC_PERM); if (!dmesh.verts) @@ -1119,7 +1218,7 @@ bool rcBuildPolyMeshDetail(rcContext* ctx, const rcPolyMesh& mesh, const rcCompa return false; } dmesh.ntris = 0; - dmesh.tris = (unsigned char*)rcAlloc(sizeof(unsigned char*)*tcap*4, RC_ALLOC_PERM); + dmesh.tris = (unsigned char*)rcAlloc(sizeof(unsigned char)*tcap*4, RC_ALLOC_PERM); if (!dmesh.tris) { ctx->log(RC_LOG_ERROR, "rcBuildPolyMeshDetail: Out of memory 'dmesh.tris' (%d).", tcap*4); @@ -1158,7 +1257,7 @@ bool rcBuildPolyMeshDetail(rcContext* ctx, const rcPolyMesh& mesh, const rcCompa { return false; } - + // Move detail verts to world space. for (int j = 0; j < nverts; ++j) { @@ -1173,21 +1272,21 @@ bool rcBuildPolyMeshDetail(rcContext* ctx, const rcPolyMesh& mesh, const rcCompa poly[j*3+1] += orig[1]; poly[j*3+2] += orig[2]; } - + // Store detail submesh. const int ntris = tris.size()/4; - + dmesh.meshes[i*4+0] = (unsigned int)dmesh.nverts; dmesh.meshes[i*4+1] = (unsigned int)nverts; dmesh.meshes[i*4+2] = (unsigned int)dmesh.ntris; - dmesh.meshes[i*4+3] = (unsigned int)ntris; + dmesh.meshes[i*4+3] = (unsigned int)ntris; // Store vertices, allocate more memory if necessary. if (dmesh.nverts+nverts > vcap) { while (dmesh.nverts+nverts > vcap) vcap += 256; - + float* newv = (float*)rcAlloc(sizeof(float)*vcap*3, RC_ALLOC_PERM); if (!newv) { @@ -1233,9 +1332,9 @@ bool rcBuildPolyMeshDetail(rcContext* ctx, const rcPolyMesh& mesh, const rcCompa dmesh.ntris++; } } - + ctx->stopTimer(RC_TIMER_BUILD_POLYMESHDETAIL); - + return true; } @@ -1245,11 +1344,11 @@ bool rcMergePolyMeshDetails(rcContext* ctx, rcPolyMeshDetail** meshes, const int rcAssert(ctx); ctx->startTimer(RC_TIMER_MERGE_POLYMESHDETAIL); - + int maxVerts = 0; int maxTris = 0; int maxMeshes = 0; - + for (int i = 0; i < nmeshes; ++i) { if (!meshes[i]) continue; @@ -1257,7 +1356,7 @@ bool rcMergePolyMeshDetails(rcContext* ctx, rcPolyMeshDetail** meshes, const int maxTris += meshes[i]->ntris; maxMeshes += meshes[i]->nmeshes; } - + mesh.nmeshes = 0; mesh.meshes = (unsigned int*)rcAlloc(sizeof(unsigned int)*maxMeshes*4, RC_ALLOC_PERM); if (!mesh.meshes) @@ -1265,7 +1364,7 @@ bool rcMergePolyMeshDetails(rcContext* ctx, rcPolyMeshDetail** meshes, const int ctx->log(RC_LOG_ERROR, "rcBuildPolyMeshDetail: Out of memory 'pmdtl.meshes' (%d).", maxMeshes*4); return false; } - + mesh.ntris = 0; mesh.tris = (unsigned char*)rcAlloc(sizeof(unsigned char)*maxTris*4, RC_ALLOC_PERM); if (!mesh.tris) @@ -1273,7 +1372,7 @@ bool rcMergePolyMeshDetails(rcContext* ctx, rcPolyMeshDetail** meshes, const int ctx->log(RC_LOG_ERROR, "rcBuildPolyMeshDetail: Out of memory 'dmesh.tris' (%d).", maxTris*4); return false; } - + mesh.nverts = 0; mesh.verts = (float*)rcAlloc(sizeof(float)*maxVerts*3, RC_ALLOC_PERM); if (!mesh.verts) @@ -1297,7 +1396,7 @@ bool rcMergePolyMeshDetails(rcContext* ctx, rcPolyMeshDetail** meshes, const int dst[3] = src[3]; mesh.nmeshes++; } - + for (int k = 0; k < dm->nverts; ++k) { rcVcopy(&mesh.verts[mesh.nverts*3], &dm->verts[k*3]); @@ -1312,7 +1411,7 @@ bool rcMergePolyMeshDetails(rcContext* ctx, rcPolyMeshDetail** meshes, const int mesh.ntris++; } } - + ctx->stopTimer(RC_TIMER_MERGE_POLYMESHDETAIL); return true; diff --git a/dep/recastnavigation/Recast/Source/RecastRegion.cpp b/dep/recastnavigation/Recast/Source/RecastRegion.cpp index 589fac2920..38bc4ff476 100644 --- a/dep/recastnavigation/Recast/Source/RecastRegion.cpp +++ b/dep/recastnavigation/Recast/Source/RecastRegion.cpp @@ -315,6 +315,7 @@ static bool floodRegion(int x, int y, int i, srcReg[ci] = 0; continue; } + count++; // Expand neighbours. @@ -516,7 +517,11 @@ struct rcRegion id(i), areaType(0), remap(false), - visited(false) + visited(false), + overlap(false), + connectsToBorder(false), + ymin(0xffff), + ymax(0) {} int spanCount; // Number of spans belonging to this region @@ -524,6 +529,9 @@ struct rcRegion unsigned char areaType; // Are type. bool remap; bool visited; + bool overlap; + bool connectsToBorder; + unsigned short ymin, ymax; rcIntArray connections; rcIntArray floors; }; @@ -768,10 +776,11 @@ static void walkContour(int x, int y, int i, int dir, } } -static bool filterSmallRegions(rcContext* ctx, int minRegionArea, int mergeRegionSize, - unsigned short& maxRegionId, - rcCompactHeightfield& chf, - unsigned short* srcReg) + +static bool mergeAndFilterRegions(rcContext* ctx, int minRegionArea, int mergeRegionSize, + unsigned short& maxRegionId, + rcCompactHeightfield& chf, + unsigned short* srcReg, rcIntArray& overlaps) { const int w = chf.width; const int h = chf.height; @@ -780,7 +789,7 @@ static bool filterSmallRegions(rcContext* ctx, int minRegionArea, int mergeRegio rcRegion* regions = (rcRegion*)rcAlloc(sizeof(rcRegion)*nreg, RC_ALLOC_TEMP); if (!regions) { - ctx->log(RC_LOG_ERROR, "filterSmallRegions: Out of memory 'regions' (%d).", nreg); + ctx->log(RC_LOG_ERROR, "mergeAndFilterRegions: Out of memory 'regions' (%d).", nreg); return false; } @@ -803,7 +812,6 @@ static bool filterSmallRegions(rcContext* ctx, int minRegionArea, int mergeRegio rcRegion& reg = regions[r]; reg.spanCount++; - // Update floors. for (int j = (int)c.index; j < ni; ++j) { @@ -811,6 +819,8 @@ static bool filterSmallRegions(rcContext* ctx, int minRegionArea, int mergeRegio unsigned short floorId = srcReg[j]; if (floorId == 0 || floorId >= nreg) continue; + if (floorId == r) + reg.overlap = true; addUniqueFloorRegion(reg, floorId); } @@ -906,7 +916,7 @@ static bool filterSmallRegions(rcContext* ctx, int minRegionArea, int mergeRegio } } } - + // Merge too small regions to neighbour regions. int mergeCount = 0 ; do @@ -916,7 +926,9 @@ static bool filterSmallRegions(rcContext* ctx, int minRegionArea, int mergeRegio { rcRegion& reg = regions[i]; if (reg.id == 0 || (reg.id & RC_BORDER_REG)) - continue; + continue; + if (reg.overlap) + continue; if (reg.spanCount == 0) continue; @@ -933,7 +945,7 @@ static bool filterSmallRegions(rcContext* ctx, int minRegionArea, int mergeRegio { if (reg.connections[j] & RC_BORDER_REG) continue; rcRegion& mreg = regions[reg.connections[j]]; - if (mreg.id == 0 || (mreg.id & RC_BORDER_REG)) continue; + if (mreg.id == 0 || (mreg.id & RC_BORDER_REG) || mreg.overlap) continue; if (mreg.spanCount < smallest && canMergeWithRegion(reg, mreg) && canMergeWithRegion(mreg, reg)) @@ -997,6 +1009,224 @@ static bool filterSmallRegions(rcContext* ctx, int minRegionArea, int mergeRegio } maxRegionId = regIdGen; + // Remap regions. + for (int i = 0; i < chf.spanCount; ++i) + { + if ((srcReg[i] & RC_BORDER_REG) == 0) + srcReg[i] = regions[srcReg[i]].id; + } + + // Return regions that we found to be overlapping. + for (int i = 0; i < nreg; ++i) + if (regions[i].overlap) + overlaps.push(regions[i].id); + + for (int i = 0; i < nreg; ++i) + regions[i].~rcRegion(); + rcFree(regions); + + + return true; +} + + +static void addUniqueConnection(rcRegion& reg, int n) +{ + for (int i = 0; i < reg.connections.size(); ++i) + if (reg.connections[i] == n) + return; + reg.connections.push(n); +} + +static bool mergeAndFilterLayerRegions(rcContext* ctx, int minRegionArea, + unsigned short& maxRegionId, + rcCompactHeightfield& chf, + unsigned short* srcReg, rcIntArray& overlaps) +{ + const int w = chf.width; + const int h = chf.height; + + const int nreg = maxRegionId+1; + rcRegion* regions = (rcRegion*)rcAlloc(sizeof(rcRegion)*nreg, RC_ALLOC_TEMP); + if (!regions) + { + ctx->log(RC_LOG_ERROR, "mergeAndFilterLayerRegions: Out of memory 'regions' (%d).", nreg); + return false; + } + + // Construct regions + for (int i = 0; i < nreg; ++i) + new(®ions[i]) rcRegion((unsigned short)i); + + // Find region neighbours and overlapping regions. + rcIntArray lregs(32); + for (int y = 0; y < h; ++y) + { + for (int x = 0; x < w; ++x) + { + const rcCompactCell& c = chf.cells[x+y*w]; + + lregs.resize(0); + + for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i) + { + const rcCompactSpan& s = chf.spans[i]; + const unsigned short ri = srcReg[i]; + if (ri == 0 || ri >= nreg) continue; + rcRegion& reg = regions[ri]; + + reg.spanCount++; + + reg.ymin = rcMin(reg.ymin, s.y); + reg.ymax = rcMax(reg.ymax, s.y); + + // Collect all region layers. + lregs.push(ri); + + // Update neighbours + for (int dir = 0; dir < 4; ++dir) + { + if (rcGetCon(s, dir) != RC_NOT_CONNECTED) + { + const int ax = x + rcGetDirOffsetX(dir); + const int ay = y + rcGetDirOffsetY(dir); + const int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(s, dir); + const unsigned short rai = srcReg[ai]; + if (rai > 0 && rai < nreg && rai != ri) + addUniqueConnection(reg, rai); + if (rai & RC_BORDER_REG) + reg.connectsToBorder = true; + } + } + + } + + // Update overlapping regions. + for (int i = 0; i < lregs.size()-1; ++i) + { + for (int j = i+1; j < lregs.size(); ++j) + { + if (lregs[i] != lregs[j]) + { + rcRegion& ri = regions[lregs[i]]; + rcRegion& rj = regions[lregs[j]]; + addUniqueFloorRegion(ri, lregs[j]); + addUniqueFloorRegion(rj, lregs[i]); + } + } + } + + } + } + + // Create 2D layers from regions. + unsigned short layerId = 1; + + for (int i = 0; i < nreg; ++i) + regions[i].id = 0; + + // Merge montone regions to create non-overlapping areas. + rcIntArray stack(32); + for (int i = 1; i < nreg; ++i) + { + rcRegion& root = regions[i]; + // Skip already visited. + if (root.id != 0) + continue; + + // Start search. + root.id = layerId; + + stack.resize(0); + stack.push(i); + + while (stack.size() > 0) + { + // Pop front + rcRegion& reg = regions[stack[0]]; + for (int j = 0; j < stack.size()-1; ++j) + stack[j] = stack[j+1]; + stack.resize(stack.size()-1); + + const int ncons = (int)reg.connections.size(); + for (int j = 0; j < ncons; ++j) + { + const int nei = reg.connections[j]; + rcRegion& regn = regions[nei]; + // Skip already visited. + if (regn.id != 0) + continue; + // Skip if the neighbour is overlapping root region. + bool overlap = false; + for (int k = 0; k < root.floors.size(); k++) + { + if (root.floors[k] == nei) + { + overlap = true; + break; + } + } + if (overlap) + continue; + + // Deepen + stack.push(nei); + + // Mark layer id + regn.id = layerId; + // Merge current layers to root. + for (int k = 0; k < regn.floors.size(); ++k) + addUniqueFloorRegion(root, regn.floors[k]); + root.ymin = rcMin(root.ymin, regn.ymin); + root.ymax = rcMax(root.ymax, regn.ymax); + root.spanCount += regn.spanCount; + regn.spanCount = 0; + root.connectsToBorder = root.connectsToBorder || regn.connectsToBorder; + } + } + + layerId++; + } + + // Remove small regions + for (int i = 0; i < nreg; ++i) + { + if (regions[i].spanCount > 0 && regions[i].spanCount < minRegionArea && !regions[i].connectsToBorder) + { + unsigned short reg = regions[i].id; + for (int j = 0; j < nreg; ++j) + if (regions[j].id == reg) + regions[j].id = 0; + } + } + + // Compress region Ids. + for (int i = 0; i < nreg; ++i) + { + regions[i].remap = false; + if (regions[i].id == 0) continue; // Skip nil regions. + if (regions[i].id & RC_BORDER_REG) continue; // Skip external regions. + regions[i].remap = true; + } + + unsigned short regIdGen = 0; + for (int i = 0; i < nreg; ++i) + { + if (!regions[i].remap) + continue; + unsigned short oldId = regions[i].id; + unsigned short newId = ++regIdGen; + for (int j = i; j < nreg; ++j) + { + if (regions[j].id == oldId) + { + regions[j].id = newId; + regions[j].remap = false; + } + } + } + maxRegionId = regIdGen; + // Remap regions. for (int i = 0; i < chf.spanCount; ++i) { @@ -1011,6 +1241,8 @@ static bool filterSmallRegions(rcContext* ctx, int minRegionArea, int mergeRegio return true; } + + /// @par /// /// This is usually the second to the last step in creating a fully built @@ -1256,13 +1488,17 @@ bool rcBuildRegionsMonotone(rcContext* ctx, rcCompactHeightfield& chf, } } + ctx->startTimer(RC_TIMER_BUILD_REGIONS_FILTER); - // Filter out small regions. + // Merge regions and filter out small regions. + rcIntArray overlaps; chf.maxRegions = id; - if (!filterSmallRegions(ctx, minRegionArea, mergeRegionArea, chf.maxRegions, chf, srcReg)) + if (!mergeAndFilterRegions(ctx, minRegionArea, mergeRegionArea, chf.maxRegions, chf, srcReg, overlaps)) return false; + // Monotone partitioning does not generate overlapping regions. + ctx->stopTimer(RC_TIMER_BUILD_REGIONS_FILTER); // Store the result out. @@ -1407,11 +1643,18 @@ bool rcBuildRegions(rcContext* ctx, rcCompactHeightfield& chf, ctx->startTimer(RC_TIMER_BUILD_REGIONS_FILTER); - // Filter out small regions. + // Merge regions and filter out smalle regions. + rcIntArray overlaps; chf.maxRegions = regionId; - if (!filterSmallRegions(ctx, minRegionArea, mergeRegionArea, chf.maxRegions, chf, srcReg)) + if (!mergeAndFilterRegions(ctx, minRegionArea, mergeRegionArea, chf.maxRegions, chf, srcReg, overlaps)) return false; - + + // If overlapping regions were found during merging, split those regions. + if (overlaps.size() > 0) + { + ctx->log(RC_LOG_ERROR, "rcBuildRegions: %d overlapping regions.", overlaps.size()); + } + ctx->stopTimer(RC_TIMER_BUILD_REGIONS_FILTER); // Write the result out. @@ -1424,3 +1667,157 @@ bool rcBuildRegions(rcContext* ctx, rcCompactHeightfield& chf, } +bool rcBuildLayerRegions(rcContext* ctx, rcCompactHeightfield& chf, + const int borderSize, const int minRegionArea) +{ + rcAssert(ctx); + + ctx->startTimer(RC_TIMER_BUILD_REGIONS); + + const int w = chf.width; + const int h = chf.height; + unsigned short id = 1; + + rcScopedDelete srcReg = (unsigned short*)rcAlloc(sizeof(unsigned short)*chf.spanCount, RC_ALLOC_TEMP); + if (!srcReg) + { + ctx->log(RC_LOG_ERROR, "rcBuildRegionsMonotone: Out of memory 'src' (%d).", chf.spanCount); + return false; + } + memset(srcReg,0,sizeof(unsigned short)*chf.spanCount); + + const int nsweeps = rcMax(chf.width,chf.height); + rcScopedDelete sweeps = (rcSweepSpan*)rcAlloc(sizeof(rcSweepSpan)*nsweeps, RC_ALLOC_TEMP); + if (!sweeps) + { + ctx->log(RC_LOG_ERROR, "rcBuildRegionsMonotone: Out of memory 'sweeps' (%d).", nsweeps); + return false; + } + + + // Mark border regions. + if (borderSize > 0) + { + // Make sure border will not overflow. + const int bw = rcMin(w, borderSize); + const int bh = rcMin(h, borderSize); + // Paint regions + paintRectRegion(0, bw, 0, h, id|RC_BORDER_REG, chf, srcReg); id++; + paintRectRegion(w-bw, w, 0, h, id|RC_BORDER_REG, chf, srcReg); id++; + paintRectRegion(0, w, 0, bh, id|RC_BORDER_REG, chf, srcReg); id++; + paintRectRegion(0, w, h-bh, h, id|RC_BORDER_REG, chf, srcReg); id++; + + chf.borderSize = borderSize; + } + + rcIntArray prev(256); + + // Sweep one line at a time. + for (int y = borderSize; y < h-borderSize; ++y) + { + // Collect spans from this row. + prev.resize(id+1); + memset(&prev[0],0,sizeof(int)*id); + unsigned short rid = 1; + + for (int x = borderSize; x < w-borderSize; ++x) + { + const rcCompactCell& c = chf.cells[x+y*w]; + + for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i) + { + const rcCompactSpan& s = chf.spans[i]; + if (chf.areas[i] == RC_NULL_AREA) continue; + + // -x + unsigned short previd = 0; + if (rcGetCon(s, 0) != RC_NOT_CONNECTED) + { + const int ax = x + rcGetDirOffsetX(0); + const int ay = y + rcGetDirOffsetY(0); + const int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(s, 0); + if ((srcReg[ai] & RC_BORDER_REG) == 0 && chf.areas[i] == chf.areas[ai]) + previd = srcReg[ai]; + } + + if (!previd) + { + previd = rid++; + sweeps[previd].rid = previd; + sweeps[previd].ns = 0; + sweeps[previd].nei = 0; + } + + // -y + if (rcGetCon(s,3) != RC_NOT_CONNECTED) + { + const int ax = x + rcGetDirOffsetX(3); + const int ay = y + rcGetDirOffsetY(3); + const int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(s, 3); + if (srcReg[ai] && (srcReg[ai] & RC_BORDER_REG) == 0 && chf.areas[i] == chf.areas[ai]) + { + unsigned short nr = srcReg[ai]; + if (!sweeps[previd].nei || sweeps[previd].nei == nr) + { + sweeps[previd].nei = nr; + sweeps[previd].ns++; + prev[nr]++; + } + else + { + sweeps[previd].nei = RC_NULL_NEI; + } + } + } + + srcReg[i] = previd; + } + } + + // Create unique ID. + for (int i = 1; i < rid; ++i) + { + if (sweeps[i].nei != RC_NULL_NEI && sweeps[i].nei != 0 && + prev[sweeps[i].nei] == (int)sweeps[i].ns) + { + sweeps[i].id = sweeps[i].nei; + } + else + { + sweeps[i].id = id++; + } + } + + // Remap IDs + for (int x = borderSize; x < w-borderSize; ++x) + { + const rcCompactCell& c = chf.cells[x+y*w]; + + for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i) + { + if (srcReg[i] > 0 && srcReg[i] < rid) + srcReg[i] = sweeps[srcReg[i]].id; + } + } + } + + + ctx->startTimer(RC_TIMER_BUILD_REGIONS_FILTER); + + // Merge monotone regions to layers and remove small regions. + rcIntArray overlaps; + chf.maxRegions = id; + if (!mergeAndFilterLayerRegions(ctx, minRegionArea, chf.maxRegions, chf, srcReg, overlaps)) + return false; + + ctx->stopTimer(RC_TIMER_BUILD_REGIONS_FILTER); + + + // Store the result out. + for (int i = 0; i < chf.spanCount; ++i) + chf.spans[i].reg = srcReg[i]; + + ctx->stopTimer(RC_TIMER_BUILD_REGIONS); + + return true; +} diff --git a/dep/recastnavigation/recastnavigation.diff b/dep/recastnavigation/recastnavigation.diff index 68e976c955..42bab6474e 100644 --- a/dep/recastnavigation/recastnavigation.diff +++ b/dep/recastnavigation/recastnavigation.diff @@ -1,25 +1,21 @@ - Detour/Include/DetourNavMesh.h | 76 +- - Detour/Include/DetourNavMeshQuery.h | 0 - Detour/Include/DetourNode.h | 0 - Detour/Source/DetourCommon.cpp | 4 +- - Detour/Source/DetourNavMesh.cpp | 32 +- - Detour/Source/DetourNavMeshBuilder.cpp | 6 +- - Detour/Source/DetourNavMeshQuery.cpp | 9 +- - Detour/Source/DetourNode.cpp | 29 +- - DetourCrowd/Include/DetourObstacleAvoidance.h | 0 - DetourCrowd/Source/DetourObstacleAvoidance.cpp | 6 +- - Recast/Include/Recast.h | 8 +- - Recast/Source/RecastContour.cpp | 0 - Recast/Source/RecastLayers.cpp | 0 - Recast/Source/RecastMesh.cpp | 0 - Recast/Source/RecastMeshDetail.cpp | 0 - Recast/Source/RecastRegion.cpp | 0 - RecastDemo/Contrib/stb_truetype.h | 3612 ++++++++++++------------ - RecastDemo/Include/NavMeshPruneTool.h | 0 - 18 files changed, 1865 insertions(+), 1917 deletions(-) +From b84e9ffbd1d1e1fb2f5d78cc53d2bb7b56c3fce3 Mon Sep 17 00:00:00 2001 +From: jackpoz +Date: Fri, 20 Jun 2014 23:15:04 +0200 +Subject: [PATCH] Add custom trinitycore changes + +--- + Detour/Include/DetourNavMesh.h | 76 ++++++++------------------ + Detour/Source/DetourCommon.cpp | 4 +- + Detour/Source/DetourNavMesh.cpp | 32 ++++------- + Detour/Source/DetourNavMeshBuilder.cpp | 6 +- + Detour/Source/DetourNavMeshQuery.cpp | 9 +-- + Detour/Source/DetourNode.cpp | 29 +++------- + DetourCrowd/Source/DetourObstacleAvoidance.cpp | 6 +- + Recast/Include/Recast.h | 8 +-- + 8 files changed, 59 insertions(+), 111 deletions(-) diff --git a/Detour/Include/DetourNavMesh.h b/Detour/Include/DetourNavMesh.h -index 95a63e4..cdd473f 100644 +index 1060845..782ddbc 100644 --- a/Detour/Include/DetourNavMesh.h +++ b/Detour/Include/DetourNavMesh.h @@ -22,39 +22,35 @@ @@ -93,7 +89,7 @@ index 95a63e4..cdd473f 100644 /// Tile flags used for various functions and fields. /// For an example, see dtNavMesh::addTile(). enum dtTileFlags -@@ -492,11 +494,7 @@ public: +@@ -511,11 +513,7 @@ public: /// @param[in] ip The index of the polygon within the tile. inline dtPolyRef encodePolyId(unsigned int salt, unsigned int it, unsigned int ip) const { @@ -105,7 +101,7 @@ index 95a63e4..cdd473f 100644 } /// Decodes a standard polygon reference. -@@ -508,21 +506,12 @@ public: +@@ -527,21 +525,12 @@ public: /// @see #encodePolyId inline void decodePolyId(dtPolyRef ref, unsigned int& salt, unsigned int& it, unsigned int& ip) const { @@ -127,7 +123,7 @@ index 95a63e4..cdd473f 100644 } /// Extracts a tile's salt value from the specified polygon reference. -@@ -531,13 +520,8 @@ public: +@@ -550,13 +539,8 @@ public: /// @see #encodePolyId inline unsigned int decodePolyIdSalt(dtPolyRef ref) const { @@ -141,7 +137,7 @@ index 95a63e4..cdd473f 100644 } /// Extracts the tile's index from the specified polygon reference. -@@ -546,13 +530,8 @@ public: +@@ -565,13 +549,8 @@ public: /// @see #encodePolyId inline unsigned int decodePolyIdTile(dtPolyRef ref) const { @@ -155,7 +151,7 @@ index 95a63e4..cdd473f 100644 } /// Extracts the polygon's index (within its tile) from the specified polygon reference. -@@ -561,13 +540,8 @@ public: +@@ -580,13 +559,8 @@ public: /// @see #encodePolyId inline unsigned int decodePolyIdPoly(dtPolyRef ref) const { @@ -169,7 +165,7 @@ index 95a63e4..cdd473f 100644 } /// @} -@@ -626,11 +600,9 @@ private: +@@ -645,11 +619,9 @@ private: dtMeshTile* m_nextFree; ///< Freelist of tiles. dtMeshTile* m_tiles; ///< List of tiles. @@ -305,7 +301,7 @@ index 1bf271b..9d8471b 100644 int curNode = 0; diff --git a/Detour/Source/DetourNavMeshQuery.cpp b/Detour/Source/DetourNavMeshQuery.cpp -index 2e30464..f1709df 100644 +index 9debb4d..ec3a294 100644 --- a/Detour/Source/DetourNavMeshQuery.cpp +++ b/Detour/Source/DetourNavMeshQuery.cpp @@ -16,13 +16,13 @@ @@ -335,7 +331,7 @@ index 2e30464..f1709df 100644 dtNavMeshQuery* dtAllocNavMeshQuery() -@@ -3305,7 +3306,7 @@ dtStatus dtNavMeshQuery::findDistanceToWall(dtPolyRef startRef, const float* cen +@@ -3504,7 +3505,7 @@ dtStatus dtNavMeshQuery::findDistanceToWall(dtPolyRef startRef, const float* cen dtVsub(hitNormal, centerPos, hitPos); dtVnormalize(hitNormal); @@ -345,7 +341,7 @@ index 2e30464..f1709df 100644 return status; } diff --git a/Detour/Source/DetourNode.cpp b/Detour/Source/DetourNode.cpp -index 57cb206..4c8215e 100644 +index 5cf6548..1d18977 100644 --- a/Detour/Source/DetourNode.cpp +++ b/Detour/Source/DetourNode.cpp @@ -22,30 +22,17 @@ @@ -422,7 +418,7 @@ index 0fad9ef..d3f90b7 100644 // Always add sample at zero pat[npat*2+0] = 0; diff --git a/Recast/Include/Recast.h b/Recast/Include/Recast.h -index 336837e..3f4ae96 100644 +index 83ca606..66974cd 100644 --- a/Recast/Include/Recast.h +++ b/Recast/Include/Recast.h @@ -243,7 +243,7 @@ struct rcConfig @@ -447,3620 +443,6 @@ index 336837e..3f4ae96 100644 rcSpan* next; ///< The next span higher up in column. }; -diff --git a/RecastDemo/Contrib/stb_truetype.h b/RecastDemo/Contrib/stb_truetype.h -index fd72578..92dc8c2 100644 ---- a/RecastDemo/Contrib/stb_truetype.h -+++ b/RecastDemo/Contrib/stb_truetype.h -@@ -1,1806 +1,1806 @@ --// stb_truetype.h - v0.3 - public domain - 2009 Sean Barrett / RAD Game Tools --// --// This library processes TrueType files: --// parse files --// extract glyph metrics --// extract glyph shapes --// render glyphs to one-channel bitmaps with antialiasing (box filter) --// --// Todo: --// non-MS cmaps --// crashproof on bad data --// hinting --// subpixel positioning when rendering bitmap --// cleartype-style AA --// --// ADDITIONAL CONTRIBUTORS --// --// Mikko Mononen: compound shape support, more cmap formats --// --// VERSIONS --// --// 0.3 (2009-06-24) cmap fmt=12, compound shapes (MM) --// userdata, malloc-from-userdata, non-zero fill (STB) --// 0.2 (2009-03-11) Fix unsigned/signed char warnings --// 0.1 (2009-03-09) First public release --// --// USAGE --// --// Include this file in whatever places neeed to refer to it. In ONE C/C++ --// file, write: --// #define STB_TRUETYPE_IMPLEMENTATION --// before the #include of this file. This expands out the actual --// implementation into that C/C++ file. --// --// Look at the header-file sections below for the API, but here's a quick skim: --// --// Simple 3D API (don't ship this, but it's fine for tools and quick start, --// and you can cut and paste from it to move to more advanced) --// stbtt_BakeFontBitmap() -- bake a font to a bitmap for use as texture --// stbtt_GetBakedQuad() -- compute quad to draw for a given char --// --// "Load" a font file from a memory buffer (you have to keep the buffer loaded) --// stbtt_InitFont() --// stbtt_GetFontOffsetForIndex() -- use for TTC font collections --// --// Render a unicode codepoint to a bitmap --// stbtt_GetCodepointBitmap() -- allocates and returns a bitmap --// stbtt_MakeCodepointBitmap() -- renders into bitmap you provide --// stbtt_GetCodepointBitmapBox() -- how big the bitmap must be --// --// Character advance/positioning --// stbtt_GetCodepointHMetrics() --// stbtt_GetFontVMetrics() --// --// NOTES --// --// The system uses the raw data found in the .ttf file without changing it --// and without building auxiliary data structures. This is a bit inefficient --// on little-endian systems (the data is big-endian), but assuming you're --// caching the bitmaps or glyph shapes this shouldn't be a big deal. --// --// It appears to be very hard to programmatically determine what font a --// given file is in a general way. I provide an API for this, but I don't --// recommend it. --// --// --// SOURCE STATISTICS (based on v0.3, 1800 LOC) --// --// Documentation & header file 350 LOC \___ 500 LOC documentation --// Sample code 140 LOC / --// Truetype parsing 580 LOC ---- 600 LOC TrueType --// Software rasterization 240 LOC \ . --// Curve tesselation 120 LOC \__ 500 LOC Bitmap creation --// Bitmap management 70 LOC / --// Baked bitmap interface 70 LOC / --// Font name matching & access 150 LOC ---- 150 --// C runtime library abstraction 60 LOC ---- 60 -- -- --////////////////////////////////////////////////////////////////////////////// --////////////////////////////////////////////////////////////////////////////// --//// --//// SAMPLE PROGRAMS --//// --// --// Incomplete text-in-3d-api example, which draws quads properly aligned to be lossless --// --#if 0 --#define STB_TRUETYPE_IMPLEMENTATION // force following include to generate implementation --#include "stb_truetype.h" -- --char ttf_buffer[1<<20]; --unsigned char temp_bitmap[512*512]; -- --stbtt_chardata cdata[96]; // ASCII 32..126 is 95 glyphs --GLstbtt_uint ftex; -- --void my_stbtt_initfont(void) --{ -- fread(ttf_buffer, 1, 1<<20, fopen("c:/windows/fonts/times.ttf", "rb")); -- stbtt_BakeFontBitmap(data,0, 32.0, temp_bitmap,512,512, 32,96, cdata); // no guarantee this fits! -- // can free ttf_buffer at this point -- glGenTextures(1, &ftex); -- glBindTexture(GL_TEXTURE_2D, ftex); -- glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, 512,512, 0, GL_ALPHA, GL_UNSIGNED_BYTE, temp_bitmap); -- // can free temp_bitmap at this point -- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); --} -- --void my_stbtt_print(float x, float y, char *text) --{ -- // assume orthographic projection with units = screen pixels, origin at top left -- glBindTexture(GL_TEXTURE_2D, ftex); -- glBegin(GL_QUADS); -- while (*text) { -- if (*text >= 32 && *text < 128) { -- stbtt_aligned_quad q; -- stbtt_GetBakedQuad(cdata, 512,512, *text-32, &x,&y,&q,1);//1=opengl,0=old d3d -- glTexCoord2f(q.s0,q.t1); glVertex2f(q.x0,q.y0); -- glTexCoord2f(q.s1,q.t1); glVertex2f(q.x1,q.y0); -- glTexCoord2f(q.s1,q.t0); glVertex2f(q.x1,q.y1); -- glTexCoord2f(q.s0,q.t0); glVertex2f(q.x0,q.y1); -- } -- ++text; -- } -- glEnd(); --} --#endif --// --// --////////////////////////////////////////////////////////////////////////////// --// --// Complete program (this compiles): get a single bitmap, print as ASCII art --// --#if 0 --#include --#define STB_TRUETYPE_IMPLEMENTATION // force following include to generate implementation --#include "stb_truetype.h" -- --char ttf_buffer[1<<25]; -- --int main(int argc, char **argv) --{ -- stbtt_fontinfo font; -- unsigned char *bitmap; -- int w,h,i,j,c = (argc > 1 ? atoi(argv[1]) : 'a'), s = (argc > 2 ? atoi(argv[2]) : 20); -- -- fread(ttf_buffer, 1, 1<<25, fopen(argc > 3 ? argv[3] : "c:/windows/fonts/arialbd.ttf", "rb")); -- -- stbtt_InitFont(&font, ttf_buffer, stbtt_GetFontOffsetForIndex(ttf_buffer,0)); -- bitmap = stbtt_GetCodepointBitmap(&font, 0,stbtt_ScaleForPixelHeight(&font, s), c, &w, &h, 0,0); -- -- for (j=0; j < h; ++j) { -- for (i=0; i < w; ++i) -- putchar(" .:ioVM@"[bitmap[j*w+i]>>5]); -- putchar('\n'); -- } -- return 0; --} --#endif --// --// Output: --// --// .ii. --// @@@@@@. --// V@Mio@@o --// :i. V@V --// :oM@@M --// :@@@MM@M --// @@o o@M --// :@@. M@M --// @@@o@@@@ --// :M@@V:@@. --// --////////////////////////////////////////////////////////////////////////////// --// --// Complete program: print "Hello World!" banner, with bugs --// --#if 0 --int main(int arg, char **argv) --{ -- unsigned char screen[20][79]; -- int i,j, pos=0; -- float scale; -- char *text = "Heljo World!"; -- -- fread(buffer, 1, 1000000, fopen("c:/windows/fonts/arialbd.ttf", "rb")); -- stbtt_InitFont(&font, buffer, 0); -- -- scale = stbtt_ScaleForPixelHeight(&font, 16); -- memset(screen, 0, sizeof(screen)); -- -- while (*text) { -- int advance,lsb,x0,y0,x1,y1, newpos, baseline=13; -- stbtt_GetCodepointHMetrics(&font, *text, &advance, &lsb); -- stbtt_GetCodepointBitmapBox(&font, *text, scale,scale, &x0,&y0,&x1,&y1); -- newpos = pos + (int) (lsb * scale) + x0; -- stbtt_MakeCodepointBitmap(&font, &screen[baseline + y0][newpos], x1-x0,y1-y0, 79, scale,scale, *text); -- // note that this stomps the old data, so where character boxes overlap (e.g. 'lj') it's wrong -- // because this API is really for baking character bitmaps into textures -- pos += (int) (advance * scale); -- ++text; -- } -- -- for (j=0; j < 20; ++j) { -- for (i=0; i < 79; ++i) -- putchar(" .:ioVM@"[screen[j][i]>>5]); -- putchar('\n'); -- } -- -- return 0; --} --#endif -- -- --////////////////////////////////////////////////////////////////////////////// --////////////////////////////////////////////////////////////////////////////// --//// --//// INTEGRATION WITH RUNTIME LIBRARIES --//// -- --#ifdef STB_TRUETYPE_IMPLEMENTATION -- // #define your own (u)stbtt_int8/16/32 before including to override this -- #ifndef stbtt_uint8 -- typedef unsigned char stbtt_uint8; -- typedef signed char stbtt_int8; -- typedef unsigned short stbtt_uint16; -- typedef signed short stbtt_int16; -- typedef unsigned int stbtt_uint32; -- typedef signed int stbtt_int32; -- #endif -- -- typedef char stbtt__check_size32[sizeof(stbtt_int32)==4 ? 1 : -1]; -- typedef char stbtt__check_size16[sizeof(stbtt_int16)==2 ? 1 : -1]; -- -- // #define your own STBTT_sort() to override this to avoid qsort -- #ifndef STBTT_sort -- #include -- #define STBTT_sort(data,num_items,item_size,compare_func) qsort(data,num_items,item_size,compare_func) -- #endif -- -- // #define your own STBTT_ifloor/STBTT_iceil() to avoid math.h -- #ifndef STBTT_ifloor -- #include -- #define STBTT_ifloor(x) ((int) floor(x)) -- #define STBTT_iceil(x) ((int) ceil(x)) -- #endif -- -- // #define your own functions "STBTT_malloc" / "STBTT_free" to avoid malloc.h -- #ifndef STBTT_malloc -- #include -- #define STBTT_malloc(x,u) malloc(x) -- #define STBTT_free(x,u) free(x) -- #endif -- -- #ifndef STBTT_assert -- #include -- #define STBTT_assert(x) assert(x) -- #endif -- -- #ifndef STBTT_strlen -- #include -- #define STBTT_strlen(x) strlen(x) -- #endif -- -- #ifndef STBTT_memcpy -- #include -- #define STBTT_memcpy memcpy -- #define STBTT_memset memset -- #endif --#endif -- --/////////////////////////////////////////////////////////////////////////////// --/////////////////////////////////////////////////////////////////////////////// --//// --//// INTERFACE --//// --//// -- --#ifndef __STB_INCLUDE_STB_TRUETYPE_H__ --#define __STB_INCLUDE_STB_TRUETYPE_H__ -- --#ifdef __cplusplus --extern "C" { --#endif -- --////////////////////////////////////////////////////////////////////////////// --// --// TEXTURE BAKING API --// --// If you use this API, you only have to call two functions ever. --// -- --typedef struct --{ -- unsigned short x0,y0,x1,y1; // coordinates of bbox in bitmap -- float xoff,yoff,xadvance; --} stbtt_bakedchar; -- --extern int stbtt_BakeFontBitmap(const unsigned char *data, int offset, // font location (use offset=0 for plain .ttf) -- float pixel_height, // height of font in pixels -- unsigned char *pixels, int pw, int ph, // bitmap to be filled in -- int first_char, int num_chars, // characters to bake -- stbtt_bakedchar *chardata); // you allocate this, it's num_chars long --// if return is positive, the first unused row of the bitmap --// if return is negative, returns the negative of the number of characters that fit --// if return is 0, no characters fit and no rows were used --// This uses a very crappy packing. -- --typedef struct --{ -- float x0,y0,s0,t0; // top-left -- float x1,y1,s1,t1; // bottom-right --} stbtt_aligned_quad; -- --extern void stbtt_GetBakedQuad(stbtt_bakedchar *chardata, int pw, int ph, // same data as above -- int char_index, // character to display -- float *xpos, float *ypos, // pointers to current position in screen pixel space -- stbtt_aligned_quad *q, // output: quad to draw -- int opengl_fillrule); // true if opengl fill rule; false if DX9 or earlier --// Call GetBakedQuad with char_index = 'character - first_char', and it --// creates the quad you need to draw and advances the current position. --// It's inefficient; you might want to c&p it and optimize it. -- -- --////////////////////////////////////////////////////////////////////////////// --// --// FONT LOADING --// --// -- --extern int stbtt_GetFontOffsetForIndex(const unsigned char *data, int index); --// Each .ttf file may have more than one font. Each has a sequential index --// number starting from 0. Call this function to get the font offset for a --// given index; it returns -1 if the index is out of range. A regular .ttf --// file will only define one font and it always be at offset 0, so it will --// return '0' for index 0, and -1 for all other indices. You can just skip --// this step if you know it's that kind of font. -- -- --// The following structure is defined publically so you can declare one on --// the stack or as a global or etc. --typedef struct --{ -- void *userdata; -- unsigned char *data; // pointer to .ttf file -- int fontstart; // offset of start of font -- -- int numGlyphs; // number of glyphs, needed for range checking -- -- int loca,head,glyf,hhea,hmtx; // table locations as offset from start of .ttf -- int index_map; // a cmap mapping for our chosen character encoding -- int indexToLocFormat; // format needed to map from glyph index to glyph --} stbtt_fontinfo; -- --extern int stbtt_InitFont(stbtt_fontinfo *info, const unsigned char *data, int offset); --// Given an offset into the file that defines a font, this function builds --// the necessary cached info for the rest of the system. You must allocate --// the stbtt_fontinfo yourself, and stbtt_InitFont will fill it out. You don't --// need to do anything special to free it, because the contents are a pure --// cache with no additional data structures. Returns 0 on failure. -- -- --////////////////////////////////////////////////////////////////////////////// --// --// CHARACTER TO GLYPH-INDEX CONVERSIOn -- --int stbtt_FindGlyphIndex(const stbtt_fontinfo *info, int unicode_codepoint); --// If you're going to perform multiple operations on the same character --// and you want a speed-up, call this function with the character you're --// going to process, then use glyph-based functions instead of the --// codepoint-based functions. -- -- --////////////////////////////////////////////////////////////////////////////// --// --// CHARACTER PROPERTIES --// -- --extern float stbtt_ScaleForPixelHeight(const stbtt_fontinfo *info, float pixels); --// computes a scale factor to produce a font whose "height" is 'pixels' tall. --// Height is measured as the distance from the highest ascender to the lowest --// descender; in other words, it's equivalent to calling stbtt_GetFontVMetrics --// and computing: --// scale = pixels / (ascent - descent) --// so if you prefer to measure height by the ascent only, use a similar calculation. -- --extern void stbtt_GetFontVMetrics(const stbtt_fontinfo *info, int *ascent, int *descent, int *lineGap); --// ascent is the coordinate above the baseline the font extends; descent --// is the coordinate below the baseline the font extends (i.e. it is typically negative) --// lineGap is the spacing between one row's descent and the next row's ascent... --// so you should advance the vertical position by "*ascent - *descent + *lineGap" --// these are expressed in unscaled coordinates -- --extern void stbtt_GetCodepointHMetrics(const stbtt_fontinfo *info, int codepoint, int *advanceWidth, int *leftSideBearing); --// leftSideBearing is the offset from the current horizontal position to the left edge of the character --// advanceWidth is the offset from the current horizontal position to the next horizontal position --// these are expressed in unscaled coordinates -- --extern int stbtt_GetCodepointKernAdvance(const stbtt_fontinfo *info, int ch1, int ch2); --// an additional amount to add to the 'advance' value between ch1 and ch2 --// @TODO; for now always returns 0! -- --extern int stbtt_GetCodepointBox(const stbtt_fontinfo *info, int codepoint, int *x0, int *y0, int *x1, int *y1); --// Gets the bounding box of the visible part of the glyph, in unscaled coordinates -- --extern void stbtt_GetGlyphHMetrics(const stbtt_fontinfo *info, int glyph_index, int *advanceWidth, int *leftSideBearing); --extern int stbtt_GetGlyphKernAdvance(const stbtt_fontinfo *info, int glyph1, int glyph2); --extern int stbtt_GetGlyphBox(const stbtt_fontinfo *info, int glyph_index, int *x0, int *y0, int *x1, int *y1); --// as above, but takes one or more glyph indices for greater efficiency -- -- --////////////////////////////////////////////////////////////////////////////// --// --// GLYPH SHAPES (you probably don't need these, but they have to go before --// the bitmaps for C declaration-order reasons) --// -- --#ifndef STBTT_vmove // you can predefine these to use different values (but why?) -- enum { -- STBTT_vmove=1, -- STBTT_vline, -- STBTT_vcurve -- }; --#endif -- --#ifndef stbtt_vertex // you can predefine this to use different values -- // (we share this with other code at RAD) -- #define stbtt_vertex_type short // can't use stbtt_int16 because that's not visible in the header file -- typedef struct -- { -- stbtt_vertex_type x,y,cx,cy; -- unsigned char type,padding; -- } stbtt_vertex; --#endif -- --extern int stbtt_GetCodepointShape(const stbtt_fontinfo *info, int unicode_codepoint, stbtt_vertex **vertices); --extern int stbtt_GetGlyphShape(const stbtt_fontinfo *info, int glyph_index, stbtt_vertex **vertices); --// returns # of vertices and fills *vertices with the pointer to them --// these are expressed in "unscaled" coordinates -- --extern void stbtt_FreeShape(const stbtt_fontinfo *info, stbtt_vertex *vertices); --// frees the data allocated above -- --////////////////////////////////////////////////////////////////////////////// --// --// BITMAP RENDERING --// -- --extern void stbtt_FreeBitmap(unsigned char *bitmap, void *userdata); --// frees the bitmap allocated below -- --extern unsigned char *stbtt_GetCodepointBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int codepoint, int *width, int *height, int *xoff, int *yoff); --// allocates a large-enough single-channel 8bpp bitmap and renders the --// specified character/glyph at the specified scale into it, with --// antialiasing. 0 is no coverage (transparent), 255 is fully covered (opaque). --// *width & *height are filled out with the width & height of the bitmap, --// which is stored left-to-right, top-to-bottom. --// --// xoff/yoff are the offset it pixel space from the glyph origin to the top-left of the bitmap -- --extern void stbtt_MakeCodepointBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int codepoint); --// the same as above, but you pass in storage for the bitmap in the form --// of 'output', with row spacing of 'out_stride' bytes. the bitmap is --// clipped to out_w/out_h bytes. call the next function to get the --// height and width and positioning info -- --extern void stbtt_GetCodepointBitmapBox(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1); --// get the bbox of the bitmap centered around the glyph origin; so the --// bitmap width is ix1-ix0, height is iy1-iy0, and location to place --// the bitmap top left is (leftSideBearing*scale,iy0). --// (Note that the bitmap uses y-increases-down, but the shape uses --// y-increases-up, so CodepointBitmapBox and CodepointBox are inverted.) -- --extern unsigned char *stbtt_GetGlyphBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int glyph, int *width, int *height, int *xoff, int *yoff); --extern void stbtt_GetGlyphBitmapBox(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1); --extern void stbtt_MakeGlyphBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int glyph); -- --//extern void stbtt_get_true_bbox(stbtt_vertex *vertices, int num_verts, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1); -- --// @TODO: don't expose this structure --typedef struct --{ -- int w,h,stride; -- unsigned char *pixels; --} stbtt__bitmap; -- --extern void stbtt_Rasterize(stbtt__bitmap *result, float flatness_in_pixels, stbtt_vertex *vertices, int num_verts, float scale_x, float scale_y, int x_off, int y_off, int invert, void *userdata); -- --////////////////////////////////////////////////////////////////////////////// --// --// Finding the right font... --// --// You should really just solve this offline, keep your own tables --// of what font is what, and don't try to get it out of the .ttf file. --// That's because getting it out of the .ttf file is really hard, because --// the names in the file can appear in many possible encodings, in many --// possible languages, and e.g. if you need a case-insensitive comparison, --// the details of that depend on the encoding & language in a complex way --// (actually underspecified in truetype, but also gigantic). --// --// But you can use the provided functions in two possible ways: --// stbtt_FindMatchingFont() will use *case-sensitive* comparisons on --// unicode-encoded names to try to find the font you want; --// you can run this before calling stbtt_InitFont() --// --// stbtt_GetFontNameString() lets you get any of the various strings --// from the file yourself and do your own comparisons on them. --// You have to have called stbtt_InitFont() first. -- -- --extern int stbtt_FindMatchingFont(const unsigned char *fontdata, const char *name, int flags); --// returns the offset (not index) of the font that matches, or -1 if none --// if you use STBTT_MACSTYLE_DONTCARE, use a font name like "Arial Bold". --// if you use any other flag, use a font name like "Arial"; this checks --// the 'macStyle' header field; i don't know if fonts set this consistently --#define STBTT_MACSTYLE_DONTCARE 0 --#define STBTT_MACSTYLE_BOLD 1 --#define STBTT_MACSTYLE_ITALIC 2 --#define STBTT_MACSTYLE_UNDERSCORE 4 --#define STBTT_MACSTYLE_NONE 8 // <= not same as 0, this makes us check the bitfield is 0 -- --extern int stbtt_CompareUTF8toUTF16_bigendian(const char *s1, int len1, const char *s2, int len2); --// returns 1/0 whether the first string interpreted as utf8 is identical to --// the second string interpreted as big-endian utf16... useful for strings from next func -- --extern char *stbtt_GetFontNameString(const stbtt_fontinfo *font, int *length, int platformID, int encodingID, int languageID, int nameID); --// returns the string (which may be big-endian double byte, e.g. for unicode) --// and puts the length in bytes in *length. --// --// some of the values for the IDs are below; for more see the truetype spec: --// http://developer.apple.com/textfonts/TTRefMan/RM06/Chap6name.html --// http://www.microsoft.com/typography/otspec/name.htm -- --enum { // platformID -- STBTT_PLATFORM_ID_UNICODE =0, -- STBTT_PLATFORM_ID_MAC =1, -- STBTT_PLATFORM_ID_ISO =2, -- STBTT_PLATFORM_ID_MICROSOFT =3 --}; -- --enum { // encodingID for STBTT_PLATFORM_ID_UNICODE -- STBTT_UNICODE_EID_UNICODE_1_0 =0, -- STBTT_UNICODE_EID_UNICODE_1_1 =1, -- STBTT_UNICODE_EID_ISO_10646 =2, -- STBTT_UNICODE_EID_UNICODE_2_0_BMP=3, -- STBTT_UNICODE_EID_UNICODE_2_0_FULL=4, --}; -- --enum { // encodingID for STBTT_PLATFORM_ID_MICROSOFT -- STBTT_MS_EID_SYMBOL =0, -- STBTT_MS_EID_UNICODE_BMP =1, -- STBTT_MS_EID_SHIFTJIS =2, -- STBTT_MS_EID_UNICODE_FULL =10, --}; -- --enum { // encodingID for STBTT_PLATFORM_ID_MAC; same as Script Manager codes -- STBTT_MAC_EID_ROMAN =0, STBTT_MAC_EID_ARABIC =4, -- STBTT_MAC_EID_JAPANESE =1, STBTT_MAC_EID_HEBREW =5, -- STBTT_MAC_EID_CHINESE_TRAD =2, STBTT_MAC_EID_GREEK =6, -- STBTT_MAC_EID_KOREAN =3, STBTT_MAC_EID_RUSSIAN =7, --}; -- --enum { // languageID for STBTT_PLATFORM_ID_MICROSOFT; same as LCID... -- // problematic because there are e.g. 16 english LCIDs and 16 arabic LCIDs -- STBTT_MS_LANG_ENGLISH =0x0409, STBTT_MS_LANG_ITALIAN =0x0410, -- STBTT_MS_LANG_CHINESE =0x0804, STBTT_MS_LANG_JAPANESE =0x0411, -- STBTT_MS_LANG_DUTCH =0x0413, STBTT_MS_LANG_KOREAN =0x0412, -- STBTT_MS_LANG_FRENCH =0x040c, STBTT_MS_LANG_RUSSIAN =0x0419, -- STBTT_MS_LANG_GERMAN =0x0407, STBTT_MS_LANG_SPANISH =0x0409, -- STBTT_MS_LANG_HEBREW =0x040d, STBTT_MS_LANG_SWEDISH =0x041D, --}; -- --enum { // languageID for STBTT_PLATFORM_ID_MAC -- STBTT_MAC_LANG_ENGLISH =0 , STBTT_MAC_LANG_JAPANESE =11, -- STBTT_MAC_LANG_ARABIC =12, STBTT_MAC_LANG_KOREAN =23, -- STBTT_MAC_LANG_DUTCH =4 , STBTT_MAC_LANG_RUSSIAN =32, -- STBTT_MAC_LANG_FRENCH =1 , STBTT_MAC_LANG_SPANISH =6 , -- STBTT_MAC_LANG_GERMAN =2 , STBTT_MAC_LANG_SWEDISH =5 , -- STBTT_MAC_LANG_HEBREW =10, STBTT_MAC_LANG_CHINESE_SIMPLIFIED =33, -- STBTT_MAC_LANG_ITALIAN =3 , STBTT_MAC_LANG_CHINESE_TRAD =19, --}; -- --#ifdef __cplusplus --} --#endif -- --#endif // __STB_INCLUDE_STB_TRUETYPE_H__ -- --/////////////////////////////////////////////////////////////////////////////// --/////////////////////////////////////////////////////////////////////////////// --//// --//// IMPLEMENTATION --//// --//// -- --#ifdef STB_TRUETYPE_IMPLEMENTATION -- --////////////////////////////////////////////////////////////////////////// --// --// accessors to parse data from file --// -- --// on platforms that don't allow misaligned reads, if we want to allow --// truetype fonts that aren't padded to alignment, define ALLOW_UNALIGNED_TRUETYPE -- --#define ttBYTE(p) (* (stbtt_uint8 *) (p)) --#define ttCHAR(p) (* (stbtt_int8 *) (p)) --#define ttFixed(p) ttLONG(p) -- --#if defined(STB_TRUETYPE_BIGENDIAN) && !defined(ALLOW_UNALIGNED_TRUETYPE) -- -- #define ttUSHORT(p) (* (stbtt_uint16 *) (p)) -- #define ttSHORT(p) (* (stbtt_int16 *) (p)) -- #define ttULONG(p) (* (stbtt_uint32 *) (p)) -- #define ttLONG(p) (* (stbtt_int32 *) (p)) -- --#else -- -- stbtt_uint16 ttUSHORT(const stbtt_uint8 *p) { return p[0]*256 + p[1]; } -- stbtt_int16 ttSHORT(const stbtt_uint8 *p) { return p[0]*256 + p[1]; } -- stbtt_uint32 ttULONG(const stbtt_uint8 *p) { return (p[0]<<24) + (p[1]<<16) + (p[2]<<8) + p[3]; } -- stbtt_int32 ttLONG(const stbtt_uint8 *p) { return (p[0]<<24) + (p[1]<<16) + (p[2]<<8) + p[3]; } -- --#endif -- --#define stbtt_tag4(p,c0,c1,c2,c3) ((p)[0] == (c0) && (p)[1] == (c1) && (p)[2] == (c2) && (p)[3] == (c3)) --#define stbtt_tag(p,str) stbtt_tag4(p,str[0],str[1],str[2],str[3]) -- --static int stbtt__isfont(const stbtt_uint8 *font) --{ -- // check the version number -- if (stbtt_tag(font, "1")) return 1; // TrueType 1 -- if (stbtt_tag(font, "typ1")) return 1; // TrueType with type 1 font -- we don't support this! -- if (stbtt_tag(font, "OTTO")) return 1; // OpenType with CFF -- if (stbtt_tag4(font, 0,1,0,0)) return 1; // OpenType 1.0 -- return 0; --} -- --// @OPTIMIZE: binary search --static stbtt_uint32 stbtt__find_table(stbtt_uint8 *data, stbtt_uint32 fontstart, const char *tag) --{ -- stbtt_int32 num_tables = ttUSHORT(data+fontstart+4); -- stbtt_uint32 tabledir = fontstart + 12; -- stbtt_int32 i; -- for (i=0; i < num_tables; ++i) { -- stbtt_uint32 loc = tabledir + 16*i; -- if (stbtt_tag(data+loc+0, tag)) -- return ttULONG(data+loc+8); -- } -- return 0; --} -- --int stbtt_GetFontOffsetForIndex(const unsigned char *font_collection, int index) --{ -- // if it's just a font, there's only one valid index -- if (stbtt__isfont(font_collection)) -- return index == 0 ? 0 : -1; -- -- // check if it's a TTC -- if (stbtt_tag(font_collection, "ttcf")) { -- // version 1? -- if (ttULONG(font_collection+4) == 0x00010000 || ttULONG(font_collection+4) == 0x00020000) { -- stbtt_int32 n = ttLONG(font_collection+8); -- if (index >= n) -- return -1; -- return ttULONG(font_collection+12+index*14); -- } -- } -- return -1; --} -- --int stbtt_InitFont(stbtt_fontinfo *info, const unsigned char *data2, int fontstart) --{ -- stbtt_uint8 *data = (stbtt_uint8 *) data2; -- stbtt_uint32 cmap, t; -- stbtt_int32 i,numTables; -- -- info->data = data; -- info->fontstart = fontstart; -- -- cmap = stbtt__find_table(data, fontstart, "cmap"); -- info->loca = stbtt__find_table(data, fontstart, "loca"); -- info->head = stbtt__find_table(data, fontstart, "head"); -- info->glyf = stbtt__find_table(data, fontstart, "glyf"); -- info->hhea = stbtt__find_table(data, fontstart, "hhea"); -- info->hmtx = stbtt__find_table(data, fontstart, "hmtx"); -- if (!cmap || !info->loca || !info->head || !info->glyf || !info->hhea || !info->hmtx) -- return 0; -- -- t = stbtt__find_table(data, fontstart, "maxp"); -- if (t) -- info->numGlyphs = ttUSHORT(data+t+4); -- else -- info->numGlyphs = 0xffff; -- -- // find a cmap encoding table we understand *now* to avoid searching -- // later. (todo: could make this installable) -- // the same regardless of glyph. -- numTables = ttUSHORT(data + cmap + 2); -- info->index_map = 0; -- for (i=0; i < numTables; ++i) { -- stbtt_uint32 encoding_record = cmap + 4 + 8 * i; -- // find an encoding we understand: -- switch(ttUSHORT(data+encoding_record)) { -- case STBTT_PLATFORM_ID_MICROSOFT: -- switch (ttUSHORT(data+encoding_record+2)) { -- case STBTT_MS_EID_UNICODE_BMP: -- case STBTT_MS_EID_UNICODE_FULL: -- // MS/Unicode -- info->index_map = cmap + ttULONG(data+encoding_record+4); -- break; -- } -- break; -- } -- } -- if (info->index_map == 0) -- return 0; -- -- info->indexToLocFormat = ttUSHORT(data+info->head + 50); -- return 1; --} -- --int stbtt_FindGlyphIndex(const stbtt_fontinfo *info, int unicode_codepoint) --{ -- stbtt_uint8 *data = info->data; -- stbtt_uint32 index_map = info->index_map; -- -- stbtt_uint16 format = ttUSHORT(data + index_map + 0); -- if (format == 0) { // apple byte encoding -- stbtt_int32 bytes = ttUSHORT(data + index_map + 2); -- if (unicode_codepoint < bytes-6) -- return ttBYTE(data + index_map + 6 + unicode_codepoint); -- return 0; -- } else if (format == 6) { -- stbtt_uint32 first = ttUSHORT(data + index_map + 6); -- stbtt_uint32 count = ttUSHORT(data + index_map + 8); -- if ((stbtt_uint32) unicode_codepoint >= first && (stbtt_uint32) unicode_codepoint < first+count) -- return ttUSHORT(data + index_map + 10 + (unicode_codepoint - first)*2); -- return 0; -- } else if (format == 2) { -- STBTT_assert(0); // @TODO: high-byte mapping for japanese/chinese/korean -- return 0; -- } else if (format == 4) { // standard mapping for windows fonts: binary search collection of ranges -- stbtt_uint16 segcount = ttUSHORT(data+index_map+6) >> 1; -- stbtt_uint16 searchRange = ttUSHORT(data+index_map+8) >> 1; -- stbtt_uint16 entrySelector = ttUSHORT(data+index_map+10); -- stbtt_uint16 rangeShift = ttUSHORT(data+index_map+12) >> 1; -- stbtt_uint16 item, offset, start, end; -- -- // do a binary search of the segments -- stbtt_uint32 endCount = index_map + 14; -- stbtt_uint32 search = endCount; -- -- if (unicode_codepoint > 0xffff) -- return 0; -- -- // they lie from endCount .. endCount + segCount -- // but searchRange is the nearest power of two, so... -- if (unicode_codepoint >= ttUSHORT(data + search + rangeShift*2)) -- search += rangeShift*2; -- -- // now decrement to bias correctly to find smallest -- search -= 2; -- while (entrySelector) { -- stbtt_uint16 start, end; -- searchRange >>= 1; -- start = ttUSHORT(data + search + 2 + segcount*2 + 2); -- end = ttUSHORT(data + search + 2); -- start = ttUSHORT(data + search + searchRange*2 + segcount*2 + 2); -- end = ttUSHORT(data + search + searchRange*2); -- if (unicode_codepoint > end) -- search += searchRange*2; -- --entrySelector; -- } -- search += 2; -- -- item = (stbtt_uint16) ((search - endCount) >> 1); -- -- STBTT_assert(unicode_codepoint <= ttUSHORT(data + endCount + 2*item)); -- start = ttUSHORT(data + index_map + 14 + segcount*2 + 2 + 2*item); -- end = ttUSHORT(data + index_map + 14 + 2 + 2*item); -- if (unicode_codepoint < start) -- return 0; -- -- offset = ttUSHORT(data + index_map + 14 + segcount*6 + 2 + 2*item); -- if (offset == 0) -- return unicode_codepoint + ttSHORT(data + index_map + 14 + segcount*4 + 2 + 2*item); -- -- return ttUSHORT(data + offset + (unicode_codepoint-start)*2 + index_map + 14 + segcount*6 + 2 + 2*item); -- } else if (format == 12) { -- stbtt_uint16 ngroups = ttUSHORT(data+index_map+6); -- stbtt_int32 low,high; -- low = 0; high = (stbtt_int32)ngroups; -- // Binary search the right group. -- while (low <= high) { -- stbtt_int32 mid = low + ((high-low) >> 1); // rounds down, so low <= mid < high -- stbtt_uint32 start_char = ttULONG(data+index_map+16+mid*12); -- stbtt_uint32 end_char = ttULONG(data+index_map+16+mid*12+4); -- if ((stbtt_uint32) unicode_codepoint < start_char) -- high = mid-1; -- else if ((stbtt_uint32) unicode_codepoint > end_char) -- low = mid+1; -- else { -- stbtt_uint32 start_glyph = ttULONG(data+index_map+16+mid*12+8); -- return start_glyph + unicode_codepoint-start_char; -- } -- } -- return 0; // not found -- } -- // @TODO -- STBTT_assert(0); -- return 0; --} -- --int stbtt_GetCodepointShape(const stbtt_fontinfo *info, int unicode_codepoint, stbtt_vertex **vertices) --{ -- return stbtt_GetGlyphShape(info, stbtt_FindGlyphIndex(info, unicode_codepoint), vertices); --} -- --static void stbtt_setvertex(stbtt_vertex *v, stbtt_uint8 type, stbtt_int16 x, stbtt_int16 y, stbtt_int16 cx, stbtt_int16 cy) --{ -- v->type = type; -- v->x = x; -- v->y = y; -- v->cx = cx; -- v->cy = cy; --} -- --static int stbtt__GetGlyfOffset(const stbtt_fontinfo *info, int glyph_index) --{ -- int g1,g2; -- -- if (glyph_index >= info->numGlyphs) return -1; // glyph index out of range -- if (info->indexToLocFormat >= 2) return -1; // unknown index->glyph map format -- -- if (info->indexToLocFormat == 0) { -- g1 = info->glyf + ttUSHORT(info->data + info->loca + glyph_index * 2) * 2; -- g2 = info->glyf + ttUSHORT(info->data + info->loca + glyph_index * 2 + 2) * 2; -- } else { -- g1 = info->glyf + ttULONG (info->data + info->loca + glyph_index * 4); -- g2 = info->glyf + ttULONG (info->data + info->loca + glyph_index * 4 + 4); -- } -- -- return g1==g2 ? -1 : g1; // if length is 0, return -1 --} -- --int stbtt_GetGlyphBox(const stbtt_fontinfo *info, int glyph_index, int *x0, int *y0, int *x1, int *y1) --{ -- int g = stbtt__GetGlyfOffset(info, glyph_index); -- if (g < 0) return 0; -- -- if (x0) *x0 = ttSHORT(info->data + g + 2); -- if (y0) *y0 = ttSHORT(info->data + g + 4); -- if (x1) *x1 = ttSHORT(info->data + g + 6); -- if (y1) *y1 = ttSHORT(info->data + g + 8); -- return 1; --} -- --int stbtt_GetCodepointBox(const stbtt_fontinfo *info, int codepoint, int *x0, int *y0, int *x1, int *y1) --{ -- return stbtt_GetGlyphBox(info, stbtt_FindGlyphIndex(info,codepoint), x0,y0,x1,y1); --} -- --int stbtt_GetGlyphShape(const stbtt_fontinfo *info, int glyph_index, stbtt_vertex **pvertices) --{ -- stbtt_int16 numberOfContours; -- stbtt_uint8 *endPtsOfContours; -- stbtt_uint8 *data = info->data; -- stbtt_vertex *vertices=0; -- int num_vertices=0; -- int g = stbtt__GetGlyfOffset(info, glyph_index); -- -- *pvertices = NULL; -- -- if (g < 0) return 0; -- -- numberOfContours = ttSHORT(data + g); -- -- if (numberOfContours > 0) { -- stbtt_uint8 flags=0,flagcount; -- stbtt_int32 ins, i,j=0,m,n, next_move, was_off=0, off; -- stbtt_int16 x,y,cx,cy,sx,sy; -- stbtt_uint8 *points; -- endPtsOfContours = (data + g + 10); -- ins = ttUSHORT(data + g + 10 + numberOfContours * 2); -- points = data + g + 10 + numberOfContours * 2 + 2 + ins; -- -- n = 1+ttUSHORT(endPtsOfContours + numberOfContours*2-2); -- -- m = n + numberOfContours; // a loose bound on how many vertices we might need -- vertices = (stbtt_vertex *) STBTT_malloc(m * sizeof(vertices[0]), info->userdata); -- if (vertices == 0) -- return 0; -- -- next_move = 0; -- flagcount=0; -- -- // in first pass, we load uninterpreted data into the allocated array -- // above, shifted to the end of the array so we won't overwrite it when -- // we create our final data starting from the front -- -- off = m - n; // starting offset for uninterpreted data, regardless of how m ends up being calculated -- -- // first load flags -- -- for (i=0; i < n; ++i) { -- if (flagcount == 0) { -- flags = *points++; -- if (flags & 8) -- flagcount = *points++; -- } else -- --flagcount; -- vertices[off+i].type = flags; -- } -- -- // now load x coordinates -- x=0; -- for (i=0; i < n; ++i) { -- flags = vertices[off+i].type; -- if (flags & 2) { -- stbtt_int16 dx = *points++; -- x += (flags & 16) ? dx : -dx; // ??? -- } else { -- if (!(flags & 16)) { -- x = x + (stbtt_int16) (points[0]*256 + points[1]); -- points += 2; -- } -- } -- vertices[off+i].x = x; -- } -- -- // now load y coordinates -- y=0; -- for (i=0; i < n; ++i) { -- flags = vertices[off+i].type; -- if (flags & 4) { -- stbtt_int16 dy = *points++; -- y += (flags & 32) ? dy : -dy; // ??? -- } else { -- if (!(flags & 32)) { -- y = y + (stbtt_int16) (points[0]*256 + points[1]); -- points += 2; -- } -- } -- vertices[off+i].y = y; -- } -- -- // now convert them to our format -- num_vertices=0; -- sx = sy = cx = cy = 0; -- for (i=0; i < n; ++i) { -- flags = vertices[off+i].type; -- x = (stbtt_int16) vertices[off+i].x; -- y = (stbtt_int16) vertices[off+i].y; -- if (next_move == i) { -- // when we get to the end, we have to close the shape explicitly -- if (i != 0) { -- if (was_off) -- stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve,sx,sy,cx,cy); -- else -- stbtt_setvertex(&vertices[num_vertices++], STBTT_vline,sx,sy,0,0); -- } -- -- // now start the new one -- stbtt_setvertex(&vertices[num_vertices++], STBTT_vmove,x,y,0,0); -- next_move = 1 + ttUSHORT(endPtsOfContours+j*2); -- ++j; -- was_off = 0; -- sx = x; -- sy = y; -- } else { -- if (!(flags & 1)) { // if it's a curve -- if (was_off) // two off-curve control points in a row means interpolate an on-curve midpoint -- stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, (cx+x)>>1, (cy+y)>>1, cx, cy); -- cx = x; -- cy = y; -- was_off = 1; -- } else { -- if (was_off) -- stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, x,y, cx, cy); -- else -- stbtt_setvertex(&vertices[num_vertices++], STBTT_vline, x,y,0,0); -- was_off = 0; -- } -- } -- } -- if (i != 0) { -- if (was_off) -- stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve,sx,sy,cx,cy); -- else -- stbtt_setvertex(&vertices[num_vertices++], STBTT_vline,sx,sy,0,0); -- } -- } else if (numberOfContours == -1) { -- // Compound shapes. -- int more = 1; -- stbtt_uint8 *comp = data + g + 10; -- num_vertices = 0; -- vertices = 0; -- while (more) { -- stbtt_uint16 flags, gidx; -- int comp_num_verts = 0, i; -- stbtt_vertex *comp_verts = 0, *tmp = 0; -- float mtx[6] = {1,0,0,1,0,0}, m, n; -- -- flags = ttSHORT(comp); comp+=2; -- gidx = ttSHORT(comp); comp+=2; -- -- if (flags & 2) { // XY values -- if (flags & 1) { // shorts -- mtx[4] = ttSHORT(comp); comp+=2; -- mtx[5] = ttSHORT(comp); comp+=2; -- } else { -- mtx[4] = ttCHAR(comp); comp+=1; -- mtx[5] = ttCHAR(comp); comp+=1; -- } -- } -- else { -- // @TODO handle matching point -- STBTT_assert(0); -- } -- if (flags & (1<<3)) { // WE_HAVE_A_SCALE -- mtx[0] = mtx[3] = ttSHORT(comp)/16384.0f; comp+=2; -- mtx[1] = mtx[2] = 0; -- } else if (flags & (1<<6)) { // WE_HAVE_AN_X_AND_YSCALE -- mtx[0] = ttSHORT(comp)/16384.0f; comp+=2; -- mtx[1] = mtx[2] = 0; -- mtx[3] = ttSHORT(comp)/16384.0f; comp+=2; -- } else if (flags & (1<<7)) { // WE_HAVE_A_TWO_BY_TWO -- mtx[0] = ttSHORT(comp)/16384.0f; comp+=2; -- mtx[1] = ttSHORT(comp)/16384.0f; comp+=2; -- mtx[2] = ttSHORT(comp)/16384.0f; comp+=2; -- mtx[3] = ttSHORT(comp)/16384.0f; comp+=2; -- } -- -- // Find transformation scales. -- m = (float) sqrt(mtx[0]*mtx[0] + mtx[1]*mtx[1]); -- n = (float) sqrt(mtx[2]*mtx[2] + mtx[3]*mtx[3]); -- -- // Get indexed glyph. -- comp_num_verts = stbtt_GetGlyphShape(info, gidx, &comp_verts); -- if (comp_num_verts > 0) { -- // Transform vertices. -- for (i = 0; i < comp_num_verts; ++i) { -- stbtt_vertex* v = &comp_verts[i]; -- stbtt_vertex_type x,y; -- x=v->x; y=v->y; -- v->x = (stbtt_vertex_type)(m * (mtx[0]*x + mtx[2]*y + mtx[4])); -- v->y = (stbtt_vertex_type)(n * (mtx[1]*x + mtx[3]*y + mtx[5])); -- x=v->cx; y=v->cy; -- v->cx = (stbtt_vertex_type)(m * (mtx[0]*x + mtx[2]*y + mtx[4])); -- v->cy = (stbtt_vertex_type)(n * (mtx[1]*x + mtx[3]*y + mtx[5])); -- } -- // Append vertices. -- tmp = (stbtt_vertex*)STBTT_malloc((num_vertices+comp_num_verts)*sizeof(stbtt_vertex), info->userdata); -- if (!tmp) { -- if (vertices) STBTT_free(vertices, info->userdata); -- if (comp_verts) STBTT_free(comp_verts, info->userdata); -- return 0; -- } -- if (num_vertices > 0) memcpy(tmp, vertices, num_vertices*sizeof(stbtt_vertex)); -- memcpy(tmp+num_vertices, comp_verts, comp_num_verts*sizeof(stbtt_vertex)); -- if (vertices) STBTT_free(vertices, info->userdata); -- vertices = tmp; -- STBTT_free(comp_verts, info->userdata); -- num_vertices += comp_num_verts; -- } -- // More components ? -- more = flags & (1<<5); -- } -- } else if (numberOfContours < 0) { -- // @TODO other compound variations? -- STBTT_assert(0); -- } else { -- // numberOfCounters == 0, do nothing -- } -- -- *pvertices = vertices; -- return num_vertices; --} -- --void stbtt_GetGlyphHMetrics(const stbtt_fontinfo *info, int glyph_index, int *advanceWidth, int *leftSideBearing) --{ -- stbtt_uint16 numOfLongHorMetrics = ttUSHORT(info->data+info->hhea + 34); -- if (glyph_index < numOfLongHorMetrics) { -- if (advanceWidth) *advanceWidth = ttSHORT(info->data + info->hmtx + 4*glyph_index); -- if (leftSideBearing) *leftSideBearing = ttSHORT(info->data + info->hmtx + 4*glyph_index + 2); -- } else { -- if (advanceWidth) *advanceWidth = ttSHORT(info->data + info->hmtx + 4*(numOfLongHorMetrics-1)); -- if (leftSideBearing) *leftSideBearing = ttSHORT(info->data + info->hmtx + 4*numOfLongHorMetrics + 2*(glyph_index - numOfLongHorMetrics)); -- } --} -- --int stbtt_GetGlyphKernAdvance(const stbtt_fontinfo * /*info*/, int /*glyph1*/, int /*glyph2*/) --{ -- return 0; --} -- --int stbtt_GetCodepointKernAdvance(const stbtt_fontinfo * /*info*/, int /*ch1*/, int /*ch2*/) --{ -- return 0; --} -- --void stbtt_GetCodepointHMetrics(const stbtt_fontinfo *info, int codepoint, int *advanceWidth, int *leftSideBearing) --{ -- stbtt_GetGlyphHMetrics(info, stbtt_FindGlyphIndex(info,codepoint), advanceWidth, leftSideBearing); --} -- --void stbtt_GetFontVMetrics(const stbtt_fontinfo *info, int *ascent, int *descent, int *lineGap) --{ -- if (ascent ) *ascent = ttSHORT(info->data+info->hhea + 4); -- if (descent) *descent = ttSHORT(info->data+info->hhea + 6); -- if (lineGap) *lineGap = ttSHORT(info->data+info->hhea + 8); --} -- --float stbtt_ScaleForPixelHeight(const stbtt_fontinfo *info, float height) --{ -- int fheight = ttSHORT(info->data + info->hhea + 4) - ttSHORT(info->data + info->hhea + 6); -- return (float) height / fheight; --} -- --void stbtt_FreeShape(const stbtt_fontinfo *info, stbtt_vertex *v) --{ -- STBTT_free(v, info->userdata); --} -- --////////////////////////////////////////////////////////////////////////////// --// --// antialiasing software rasterizer --// -- --void stbtt_GetGlyphBitmapBox(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1) --{ -- int x0,y0,x1,y1; -- if (!stbtt_GetGlyphBox(font, glyph, &x0,&y0,&x1,&y1)) -- x0=y0=x1=y1=0; // e.g. space character -- // now move to integral bboxes (treating pixels as little squares, what pixels get touched)? -- if (ix0) *ix0 = STBTT_ifloor(x0 * scale_x); -- if (iy0) *iy0 = -STBTT_iceil (y1 * scale_y); -- if (ix1) *ix1 = STBTT_iceil (x1 * scale_x); -- if (iy1) *iy1 = -STBTT_ifloor(y0 * scale_y); --} -- --void stbtt_GetCodepointBitmapBox(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1) --{ -- stbtt_GetGlyphBitmapBox(font, stbtt_FindGlyphIndex(font,codepoint), scale_x, scale_y, ix0,iy0,ix1,iy1); --} -- --typedef struct stbtt__edge { -- float x0,y0, x1,y1; -- int invert; --} stbtt__edge; -- --typedef struct stbtt__active_edge --{ -- int x,dx; -- float ey; -- struct stbtt__active_edge *next; -- int valid; --} stbtt__active_edge; -- --#define FIXSHIFT 10 --#define FIX (1 << FIXSHIFT) --#define FIXMASK (FIX-1) -- --static stbtt__active_edge *new_active(stbtt__edge *e, int off_x, float start_point, void *userdata) --{ -- stbtt__active_edge *z = (stbtt__active_edge *) STBTT_malloc(sizeof(*z), userdata); // @TODO: make a pool of these!!! -- float dxdy = (e->x1 - e->x0) / (e->y1 - e->y0); -- STBTT_assert(e->y0 <= start_point); -- if (!z) return z; -- // round dx down to avoid going too far -- if (dxdy < 0) -- z->dx = -STBTT_ifloor(FIX * -dxdy); -- else -- z->dx = STBTT_ifloor(FIX * dxdy); -- z->x = STBTT_ifloor(FIX * (e->x0 + dxdy * (start_point - e->y0))); -- z->x -= off_x * FIX; -- z->ey = e->y1; -- z->next = 0; -- z->valid = e->invert ? 1 : -1; -- return z; --} -- --// note: this routine clips fills that extend off the edges... ideally this --// wouldn't happen, but it could happen if the truetype glyph bounding boxes --// are wrong, or if the user supplies a too-small bitmap --static void stbtt__fill_active_edges(unsigned char *scanline, int len, stbtt__active_edge *e, int max_weight) --{ -- // non-zero winding fill -- int x0=0, w=0; -- -- while (e) { -- if (w == 0) { -- // if we're currently at zero, we need to record the edge start point -- x0 = e->x; w += e->valid; -- } else { -- int x1 = e->x; w += e->valid; -- // if we went to zero, we need to draw -- if (w == 0) { -- int i = x0 >> FIXSHIFT; -- int j = x1 >> FIXSHIFT; -- -- if (i < len && j >= 0) { -- if (i == j) { -- // x0,x1 are the same pixel, so compute combined coverage -- scanline[i] = scanline[i] + (stbtt_uint8) ((x1 - x0) * max_weight >> FIXSHIFT); -- } else { -- if (i >= 0) // add antialiasing for x0 -- scanline[i] = scanline[i] + (stbtt_uint8) (((FIX - (x0 & FIXMASK)) * max_weight) >> FIXSHIFT); -- else -- i = -1; // clip -- -- if (j < len) // add antialiasing for x1 -- scanline[j] = scanline[j] + (stbtt_uint8) (((x1 & FIXMASK) * max_weight) >> FIXSHIFT); -- else -- j = len; // clip -- -- for (++i; i < j; ++i) // fill pixels between x0 and x1 -- scanline[i] = scanline[i] + (stbtt_uint8) max_weight; -- } -- } -- } -- } -- -- e = e->next; -- } --} -- --static void stbtt__rasterize_sorted_edges(stbtt__bitmap *result, stbtt__edge *e, int n, int vsubsample, int off_x, int off_y, void *userdata) --{ -- stbtt__active_edge *active = NULL; -- int y,j=0; -- int max_weight = (255 / vsubsample); // weight per vertical scanline -- int s; // vertical subsample index -- unsigned char scanline_data[512], *scanline; -- -- if (result->w > 512) -- scanline = (unsigned char *) STBTT_malloc(result->w, userdata); -- else -- scanline = scanline_data; -- -- y = off_y * vsubsample; -- e[n].y0 = (off_y + result->h) * (float) vsubsample + 1; -- -- while (j < result->h) { -- STBTT_memset(scanline, 0, result->w); -- for (s=0; s < vsubsample; ++s) { -- // find center of pixel for this scanline -- float scan_y = y + 0.5f; -- stbtt__active_edge **step = &active; -- -- // update all active edges; -- // remove all active edges that terminate before the center of this scanline -- while (*step) { -- stbtt__active_edge * z = *step; -- if (z->ey <= scan_y) { -- *step = z->next; // delete from list -- STBTT_assert(z->valid); -- z->valid = 0; -- STBTT_free(z, userdata); -- } else { -- z->x += z->dx; // advance to position for current scanline -- step = &((*step)->next); // advance through list -- } -- } -- -- // resort the list if needed -- for(;;) { -- int changed=0; -- step = &active; -- while (*step && (*step)->next) { -- if ((*step)->x > (*step)->next->x) { -- stbtt__active_edge *t = *step; -- stbtt__active_edge *q = t->next; -- -- t->next = q->next; -- q->next = t; -- *step = q; -- changed = 1; -- } -- step = &(*step)->next; -- } -- if (!changed) break; -- } -- -- // insert all edges that start before the center of this scanline -- omit ones that also end on this scanline -- while (e->y0 <= scan_y) { -- if (e->y1 > scan_y) { -- stbtt__active_edge *z = new_active(e, off_x, scan_y, userdata); -- // find insertion point -- if (active == NULL) -- active = z; -- else if (z->x < active->x) { -- // insert at front -- z->next = active; -- active = z; -- } else { -- // find thing to insert AFTER -- stbtt__active_edge *p = active; -- while (p->next && p->next->x < z->x) -- p = p->next; -- // at this point, p->next->x is NOT < z->x -- z->next = p->next; -- p->next = z; -- } -- } -- ++e; -- } -- -- // now process all active edges in XOR fashion -- if (active) -- stbtt__fill_active_edges(scanline, result->w, active, max_weight); -- -- ++y; -- } -- STBTT_memcpy(result->pixels + j * result->stride, scanline, result->w); -- ++j; -- } -- -- while (active) { -- stbtt__active_edge *z = active; -- active = active->next; -- STBTT_free(z, userdata); -- } -- -- if (scanline != scanline_data) -- STBTT_free(scanline, userdata); --} -- --static int stbtt__edge_compare(const void *p, const void *q) --{ -- stbtt__edge *a = (stbtt__edge *) p; -- stbtt__edge *b = (stbtt__edge *) q; -- -- if (a->y0 < b->y0) return -1; -- if (a->y0 > b->y0) return 1; -- return 0; --} -- --typedef struct --{ -- float x,y; --} stbtt__point; -- --static void stbtt__rasterize(stbtt__bitmap *result, stbtt__point *pts, int *wcount, int windings, float scale_x, float scale_y, int off_x, int off_y, int invert, void *userdata) --{ -- float y_scale_inv = invert ? -scale_y : scale_y; -- stbtt__edge *e; -- int n,i,j,k,m; -- int vsubsample = result->h < 8 ? 15 : 5; -- // vsubsample should divide 255 evenly; otherwise we won't reach full opacity -- -- // now we have to blow out the windings into explicit edge lists -- n = 0; -- for (i=0; i < windings; ++i) -- n += wcount[i]; -- -- e = (stbtt__edge *) STBTT_malloc(sizeof(*e) * (n+1), userdata); // add an extra one as a sentinel -- if (e == 0) return; -- n = 0; -- -- m=0; -- for (i=0; i < windings; ++i) { -- stbtt__point *p = pts + m; -- m += wcount[i]; -- j = wcount[i]-1; -- for (k=0; k < wcount[i]; j=k++) { -- int a=k,b=j; -- // skip the edge if horizontal -- if (p[j].y == p[k].y) -- continue; -- // add edge from j to k to the list -- e[n].invert = 0; -- if (invert ? p[j].y > p[k].y : p[j].y < p[k].y) { -- e[n].invert = 1; -- a=j,b=k; -- } -- e[n].x0 = p[a].x * scale_x; -- e[n].y0 = p[a].y * y_scale_inv * vsubsample; -- e[n].x1 = p[b].x * scale_x; -- e[n].y1 = p[b].y * y_scale_inv * vsubsample; -- ++n; -- } -- } -- -- // now sort the edges by their highest point (should snap to integer, and then by x) -- STBTT_sort(e, n, sizeof(e[0]), stbtt__edge_compare); -- -- // now, traverse the scanlines and find the intersections on each scanline, use xor winding rule -- stbtt__rasterize_sorted_edges(result, e, n, vsubsample, off_x, off_y, userdata); -- -- STBTT_free(e, userdata); --} -- --static void stbtt__add_point(stbtt__point *points, int n, float x, float y) --{ -- if (!points) return; // during first pass, it's unallocated -- points[n].x = x; -- points[n].y = y; --} -- --// tesselate until threshhold p is happy... @TODO warped to compensate for non-linear stretching --static int stbtt__tesselate_curve(stbtt__point *points, int *num_points, float x0, float y0, float x1, float y1, float x2, float y2, float objspace_flatness_squared, int n) --{ -- // midpoint -- float mx = (x0 + 2*x1 + x2)/4; -- float my = (y0 + 2*y1 + y2)/4; -- // versus directly drawn line -- float dx = (x0+x2)/2 - mx; -- float dy = (y0+y2)/2 - my; -- if (n > 16) // 65536 segments on one curve better be enough! -- return 1; -- if (dx*dx+dy*dy > objspace_flatness_squared) { // half-pixel error allowed... need to be smaller if AA -- stbtt__tesselate_curve(points, num_points, x0,y0, (x0+x1)/2.0f,(y0+y1)/2.0f, mx,my, objspace_flatness_squared,n+1); -- stbtt__tesselate_curve(points, num_points, mx,my, (x1+x2)/2.0f,(y1+y2)/2.0f, x2,y2, objspace_flatness_squared,n+1); -- } else { -- stbtt__add_point(points, *num_points,x2,y2); -- *num_points = *num_points+1; -- } -- return 1; --} -- --// returns number of contours --stbtt__point *stbtt_FlattenCurves(stbtt_vertex *vertices, int num_verts, float objspace_flatness, int **contour_lengths, int *num_contours, void *userdata) --{ -- stbtt__point *points=0; -- int num_points=0; -- -- float objspace_flatness_squared = objspace_flatness * objspace_flatness; -- int i,n=0,start=0, pass; -- -- // count how many "moves" there are to get the contour count -- for (i=0; i < num_verts; ++i) -- if (vertices[i].type == STBTT_vmove) -- ++n; -- -- *num_contours = n; -- if (n == 0) return 0; -- -- *contour_lengths = (int *) STBTT_malloc(sizeof(**contour_lengths) * n, userdata); -- -- if (*contour_lengths == 0) { -- *num_contours = 0; -- return 0; -- } -- -- // make two passes through the points so we don't need to realloc -- for (pass=0; pass < 2; ++pass) { -- float x=0,y=0; -- if (pass == 1) { -- points = (stbtt__point *) STBTT_malloc(num_points * sizeof(points[0]), userdata); -- if (points == NULL) goto error; -- } -- num_points = 0; -- n= -1; -- for (i=0; i < num_verts; ++i) { -- switch (vertices[i].type) { -- case STBTT_vmove: -- // start the next contour -- if (n >= 0) -- (*contour_lengths)[n] = num_points - start; -- ++n; -- start = num_points; -- -- x = vertices[i].x, y = vertices[i].y; -- stbtt__add_point(points, num_points++, x,y); -- break; -- case STBTT_vline: -- x = vertices[i].x, y = vertices[i].y; -- stbtt__add_point(points, num_points++, x, y); -- break; -- case STBTT_vcurve: -- stbtt__tesselate_curve(points, &num_points, x,y, -- vertices[i].cx, vertices[i].cy, -- vertices[i].x, vertices[i].y, -- objspace_flatness_squared, 0); -- x = vertices[i].x, y = vertices[i].y; -- break; -- } -- } -- (*contour_lengths)[n] = num_points - start; -- } -- -- return points; --error: -- STBTT_free(points, userdata); -- STBTT_free(*contour_lengths, userdata); -- *contour_lengths = 0; -- *num_contours = 0; -- return NULL; --} -- --void stbtt_Rasterize(stbtt__bitmap *result, float flatness_in_pixels, stbtt_vertex *vertices, int num_verts, float scale_x, float scale_y, int x_off, int y_off, int invert, void *userdata) --{ -- float scale = scale_x > scale_y ? scale_y : scale_x; -- int winding_count, *winding_lengths; -- stbtt__point *windings = stbtt_FlattenCurves(vertices, num_verts, flatness_in_pixels / scale, &winding_lengths, &winding_count, userdata); -- if (windings) { -- stbtt__rasterize(result, windings, winding_lengths, winding_count, scale_x, scale_y, x_off, y_off, invert, userdata); -- STBTT_free(winding_lengths, userdata); -- STBTT_free(windings, userdata); -- } --} -- --void stbtt_FreeBitmap(unsigned char *bitmap, void *userdata) --{ -- STBTT_free(bitmap, userdata); --} -- --unsigned char *stbtt_GetGlyphBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int glyph, int *width, int *height, int *xoff, int *yoff) --{ -- int ix0,iy0,ix1,iy1; -- stbtt__bitmap gbm; -- stbtt_vertex *vertices; -- int num_verts = stbtt_GetGlyphShape(info, glyph, &vertices); -- -- if (scale_x == 0) scale_x = scale_y; -- if (scale_y == 0) { -- if (scale_x == 0) return NULL; -- scale_y = scale_x; -- } -- -- stbtt_GetGlyphBitmapBox(info, glyph, scale_x, scale_y, &ix0,&iy0,&ix1,&iy1); -- -- // now we get the size -- gbm.w = (ix1 - ix0); -- gbm.h = (iy1 - iy0); -- gbm.pixels = NULL; // in case we error -- -- if (width ) *width = gbm.w; -- if (height) *height = gbm.h; -- if (xoff ) *xoff = ix0; -- if (yoff ) *yoff = iy0; -- -- if (gbm.w && gbm.h) { -- gbm.pixels = (unsigned char *) STBTT_malloc(gbm.w * gbm.h, info->userdata); -- if (gbm.pixels) { -- gbm.stride = gbm.w; -- -- stbtt_Rasterize(&gbm, 0.35f, vertices, num_verts, scale_x, scale_y, ix0, iy0, 1, info->userdata); -- } -- } -- STBTT_free(vertices, info->userdata); -- return gbm.pixels; --} -- --void stbtt_MakeGlyphBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int glyph) --{ -- int ix0,iy0; -- stbtt_vertex *vertices; -- int num_verts = stbtt_GetGlyphShape(info, glyph, &vertices); -- stbtt__bitmap gbm; -- -- stbtt_GetGlyphBitmapBox(info, glyph, scale_x, scale_y, &ix0,&iy0,0,0); -- gbm.pixels = output; -- gbm.w = out_w; -- gbm.h = out_h; -- gbm.stride = out_stride; -- -- if (gbm.w && gbm.h) -- stbtt_Rasterize(&gbm, 0.35f, vertices, num_verts, scale_x, scale_y, ix0,iy0, 1, info->userdata); -- -- STBTT_free(vertices, info->userdata); --} -- --unsigned char *stbtt_GetCodepointBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int codepoint, int *width, int *height, int *xoff, int *yoff) --{ -- return stbtt_GetGlyphBitmap(info, scale_x, scale_y, stbtt_FindGlyphIndex(info,codepoint), width,height,xoff,yoff); --} -- --void stbtt_MakeCodepointBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int codepoint) --{ -- stbtt_MakeGlyphBitmap(info, output, out_w, out_h, out_stride, scale_x, scale_y, stbtt_FindGlyphIndex(info,codepoint)); --} -- --////////////////////////////////////////////////////////////////////////////// --// --// bitmap baking --// --// This is SUPER-SHITTY packing to keep source code small -- --extern int stbtt_BakeFontBitmap(const unsigned char *data, int offset, // font location (use offset=0 for plain .ttf) -- float pixel_height, // height of font in pixels -- unsigned char *pixels, int pw, int ph, // bitmap to be filled in -- int first_char, int num_chars, // characters to bake -- stbtt_bakedchar *chardata) --{ -- float scale; -- int x,y,bottom_y, i; -- stbtt_fontinfo f; -- stbtt_InitFont(&f, data, offset); -- STBTT_memset(pixels, 0, pw*ph); // background of 0 around pixels -- x=y=1; -- bottom_y = 1; -- -- scale = stbtt_ScaleForPixelHeight(&f, pixel_height); -- -- for (i=0; i < num_chars; ++i) { -- int advance, lsb, x0,y0,x1,y1,gw,gh; -- int g = stbtt_FindGlyphIndex(&f, first_char + i); -- stbtt_GetGlyphHMetrics(&f, g, &advance, &lsb); -- stbtt_GetGlyphBitmapBox(&f, g, scale,scale, &x0,&y0,&x1,&y1); -- gw = x1-x0; -- gh = y1-y0; -- if (x + gw + 1 >= pw) -- y = bottom_y, x = 1; // advance to next row -- if (y + gh + 1 >= ph) // check if it fits vertically AFTER potentially moving to next row -- return -i; -- STBTT_assert(x+gw < pw); -- STBTT_assert(y+gh < ph); -- stbtt_MakeGlyphBitmap(&f, pixels+x+y*pw, gw,gh,pw, scale,scale, g); -- chardata[i].x0 = (stbtt_int16) x; -- chardata[i].y0 = (stbtt_int16) y; -- chardata[i].x1 = (stbtt_int16) (x + gw); -- chardata[i].y1 = (stbtt_int16) (y + gh); -- chardata[i].xadvance = scale * advance; -- chardata[i].xoff = (float) x0; -- chardata[i].yoff = (float) y0; -- x = x + gw + 2; -- if (y+gh+2 > bottom_y) -- bottom_y = y+gh+2; -- } -- return bottom_y; --} -- --void stbtt_GetBakedQuad(stbtt_bakedchar *chardata, int pw, int ph, int char_index, float *xpos, float *ypos, stbtt_aligned_quad *q, int opengl_fillrule) --{ -- float d3d_bias = opengl_fillrule ? 0 : -0.5f; -- float ipw = 1.0f / pw, iph = 1.0f / ph; -- stbtt_bakedchar *b = chardata + char_index; -- int round_x = STBTT_ifloor((*xpos + b->xoff) + 0.5); -- int round_y = STBTT_ifloor((*ypos + b->yoff) + 0.5); -- -- q->x0 = round_x + d3d_bias; -- q->y0 = round_y + d3d_bias; -- q->x1 = round_x + b->x1 - b->x0 + d3d_bias; -- q->y1 = round_y + b->y1 - b->y0 + d3d_bias; -- -- q->s0 = b->x0 * ipw; -- q->t0 = b->y0 * ipw; -- q->s1 = b->x1 * iph; -- q->t1 = b->y1 * iph; -- -- *xpos += b->xadvance; --} -- --////////////////////////////////////////////////////////////////////////////// --// --// font name matching -- recommended not to use this --// -- --// check if a utf8 string contains a prefix which is the utf16 string; if so return length of matching utf8 string --static stbtt_int32 stbtt__CompareUTF8toUTF16_bigendian_prefix(stbtt_uint8 *s1, stbtt_int32 len1, stbtt_uint8 *s2, stbtt_int32 len2) --{ -- stbtt_int32 i=0; -- -- // convert utf16 to utf8 and compare the results while converting -- while (len2) { -- stbtt_uint16 ch = s2[0]*256 + s2[1]; -- if (ch < 0x80) { -- if (i >= len1) return -1; -- if (s1[i++] != ch) return -1; -- } else if (ch < 0x800) { -- if (i+1 >= len1) return -1; -- if (s1[i++] != 0xc0 + (ch >> 6)) return -1; -- if (s1[i++] != 0x80 + (ch & 0x3f)) return -1; -- } else if (ch >= 0xd800 && ch < 0xdc00) { -- stbtt_uint32 c; -- stbtt_uint16 ch2 = s2[2]*256 + s2[3]; -- if (i+3 >= len1) return -1; -- c = ((ch - 0xd800) << 10) + (ch2 - 0xdc00) + 0x10000; -- if (s1[i++] != 0xf0 + (c >> 18)) return -1; -- if (s1[i++] != 0x80 + ((c >> 12) & 0x3f)) return -1; -- if (s1[i++] != 0x80 + ((c >> 6) & 0x3f)) return -1; -- if (s1[i++] != 0x80 + ((c ) & 0x3f)) return -1; -- s2 += 2; // plus another 2 below -- len2 -= 2; -- } else if (ch >= 0xdc00 && ch < 0xe000) { -- return -1; -- } else { -- if (i+2 >= len1) return -1; -- if (s1[i++] != 0xe0 + (ch >> 12)) return -1; -- if (s1[i++] != 0x80 + ((ch >> 6) & 0x3f)) return -1; -- if (s1[i++] != 0x80 + ((ch ) & 0x3f)) return -1; -- } -- s2 += 2; -- len2 -= 2; -- } -- return i; --} -- --int stbtt_CompareUTF8toUTF16_bigendian(const char *s1, int len1, const char *s2, int len2) --{ -- return len1 == stbtt__CompareUTF8toUTF16_bigendian_prefix((stbtt_uint8*) s1, len1, (stbtt_uint8*) s2, len2); --} -- --// returns results in whatever encoding you request... but note that 2-byte encodings --// will be BIG-ENDIAN... use stbtt_CompareUTF8toUTF16_bigendian() to compare --char *stbtt_GetFontNameString(const stbtt_fontinfo *font, int *length, int platformID, int encodingID, int languageID, int nameID) --{ -- stbtt_int32 i,count,stringOffset; -- stbtt_uint8 *fc = font->data; -- stbtt_uint32 offset = font->fontstart; -- stbtt_uint32 nm = stbtt__find_table(fc, offset, "name"); -- if (!nm) return NULL; -- -- count = ttUSHORT(fc+nm+2); -- stringOffset = nm + ttUSHORT(fc+nm+4); -- for (i=0; i < count; ++i) { -- stbtt_uint32 loc = nm + 6 + 12 * i; -- if (platformID == ttUSHORT(fc+loc+0) && encodingID == ttUSHORT(fc+loc+2) -- && languageID == ttUSHORT(fc+loc+4) && nameID == ttUSHORT(fc+loc+6)) { -- *length = ttUSHORT(fc+loc+8); -- return (char *) (fc+stringOffset+ttUSHORT(fc+loc+10)); -- } -- } -- return NULL; --} -- --static int stbtt__matchpair(stbtt_uint8 *fc, stbtt_uint32 nm, stbtt_uint8 *name, stbtt_int32 nlen, stbtt_int32 target_id, stbtt_int32 next_id) --{ -- stbtt_int32 i; -- stbtt_int32 count = ttUSHORT(fc+nm+2); -- stbtt_int32 stringOffset = nm + ttUSHORT(fc+nm+4); -- -- for (i=0; i < count; ++i) { -- stbtt_uint32 loc = nm + 6 + 12 * i; -- stbtt_int32 id = ttUSHORT(fc+loc+6); -- if (id == target_id) { -- // find the encoding -- stbtt_int32 platform = ttUSHORT(fc+loc+0), encoding = ttUSHORT(fc+loc+2), language = ttUSHORT(fc+loc+4); -- -- // is this a Unicode encoding? -- if (platform == 0 || (platform == 3 && encoding == 1) || (platform == 3 && encoding == 10)) { -- stbtt_int32 slen = ttUSHORT(fc+loc+8), off = ttUSHORT(fc+loc+10); -- -- // check if there's a prefix match -- stbtt_int32 matchlen = stbtt__CompareUTF8toUTF16_bigendian_prefix(name, nlen, fc+stringOffset+off,slen); -- if (matchlen >= 0) { -- // check for target_id+1 immediately following, with same encoding & language -- if (i+1 < count && ttUSHORT(fc+loc+12+6) == next_id && ttUSHORT(fc+loc+12) == platform && ttUSHORT(fc+loc+12+2) == encoding && ttUSHORT(fc+loc+12+4) == language) { -- stbtt_int32 slen = ttUSHORT(fc+loc+12+8), off = ttUSHORT(fc+loc+12+10); -- if (slen == 0) { -- if (matchlen == nlen) -- return 1; -- } else if (matchlen < nlen && name[matchlen] == ' ') { -- ++matchlen; -- if (stbtt_CompareUTF8toUTF16_bigendian((char*) (name+matchlen), nlen-matchlen, (char*)(fc+stringOffset+off),slen)) -- return 1; -- } -- } else { -- // if nothing immediately following -- if (matchlen == nlen) -- return 1; -- } -- } -- } -- -- // @TODO handle other encodings -- } -- } -- return 0; --} -- --static int stbtt__matches(stbtt_uint8 *fc, stbtt_uint32 offset, stbtt_uint8 *name, stbtt_int32 flags) --{ -- stbtt_int32 nlen = STBTT_strlen((char *) name); -- stbtt_uint32 nm,hd; -- if (!stbtt__isfont(fc+offset)) return 0; -- -- // check italics/bold/underline flags in macStyle... -- if (flags) { -- hd = stbtt__find_table(fc, offset, "head"); -- if ((ttUSHORT(fc+hd+44) & 7) != (flags & 7)) return 0; -- } -- -- nm = stbtt__find_table(fc, offset, "name"); -- if (!nm) return 0; -- -- if (flags) { -- // if we checked the macStyle flags, then just check the family and ignore the subfamily -- if (stbtt__matchpair(fc, nm, name, nlen, 16, -1)) return 1; -- if (stbtt__matchpair(fc, nm, name, nlen, 1, -1)) return 1; -- if (stbtt__matchpair(fc, nm, name, nlen, 3, -1)) return 1; -- } else { -- if (stbtt__matchpair(fc, nm, name, nlen, 16, 17)) return 1; -- if (stbtt__matchpair(fc, nm, name, nlen, 1, 2)) return 1; -- if (stbtt__matchpair(fc, nm, name, nlen, 3, -1)) return 1; -- } -- -- return 0; --} -- --int stbtt_FindMatchingFont(const unsigned char *font_collection, const char *name_utf8, stbtt_int32 flags) --{ -- stbtt_int32 i; -- for (i=0;;++i) { -- stbtt_int32 off = stbtt_GetFontOffsetForIndex(font_collection, i); -- if (off < 0) return off; -- if (stbtt__matches((stbtt_uint8 *) font_collection, off, (stbtt_uint8*) name_utf8, flags)) -- return off; -- } --} -- --#endif // STB_TRUETYPE_IMPLEMENTATION -+// stb_truetype.h - v0.3 - public domain - 2009 Sean Barrett / RAD Game Tools -+// -+// This library processes TrueType files: -+// parse files -+// extract glyph metrics -+// extract glyph shapes -+// render glyphs to one-channel bitmaps with antialiasing (box filter) -+// -+// Todo: -+// non-MS cmaps -+// crashproof on bad data -+// hinting -+// subpixel positioning when rendering bitmap -+// cleartype-style AA -+// -+// ADDITIONAL CONTRIBUTORS -+// -+// Mikko Mononen: compound shape support, more cmap formats -+// -+// VERSIONS -+// -+// 0.3 (2009-06-24) cmap fmt=12, compound shapes (MM) -+// userdata, malloc-from-userdata, non-zero fill (STB) -+// 0.2 (2009-03-11) Fix unsigned/signed char warnings -+// 0.1 (2009-03-09) First public release -+// -+// USAGE -+// -+// Include this file in whatever places neeed to refer to it. In ONE C/C++ -+// file, write: -+// #define STB_TRUETYPE_IMPLEMENTATION -+// before the #include of this file. This expands out the actual -+// implementation into that C/C++ file. -+// -+// Look at the header-file sections below for the API, but here's a quick skim: -+// -+// Simple 3D API (don't ship this, but it's fine for tools and quick start, -+// and you can cut and paste from it to move to more advanced) -+// stbtt_BakeFontBitmap() -- bake a font to a bitmap for use as texture -+// stbtt_GetBakedQuad() -- compute quad to draw for a given char -+// -+// "Load" a font file from a memory buffer (you have to keep the buffer loaded) -+// stbtt_InitFont() -+// stbtt_GetFontOffsetForIndex() -- use for TTC font collections -+// -+// Render a unicode codepoint to a bitmap -+// stbtt_GetCodepointBitmap() -- allocates and returns a bitmap -+// stbtt_MakeCodepointBitmap() -- renders into bitmap you provide -+// stbtt_GetCodepointBitmapBox() -- how big the bitmap must be -+// -+// Character advance/positioning -+// stbtt_GetCodepointHMetrics() -+// stbtt_GetFontVMetrics() -+// -+// NOTES -+// -+// The system uses the raw data found in the .ttf file without changing it -+// and without building auxiliary data structures. This is a bit inefficient -+// on little-endian systems (the data is big-endian), but assuming you're -+// caching the bitmaps or glyph shapes this shouldn't be a big deal. -+// -+// It appears to be very hard to programmatically determine what font a -+// given file is in a general way. I provide an API for this, but I don't -+// recommend it. -+// -+// -+// SOURCE STATISTICS (based on v0.3, 1800 LOC) -+// -+// Documentation & header file 350 LOC \___ 500 LOC documentation -+// Sample code 140 LOC / -+// Truetype parsing 580 LOC ---- 600 LOC TrueType -+// Software rasterization 240 LOC \ . -+// Curve tesselation 120 LOC \__ 500 LOC Bitmap creation -+// Bitmap management 70 LOC / -+// Baked bitmap interface 70 LOC / -+// Font name matching & access 150 LOC ---- 150 -+// C runtime library abstraction 60 LOC ---- 60 -+ -+ -+////////////////////////////////////////////////////////////////////////////// -+////////////////////////////////////////////////////////////////////////////// -+//// -+//// SAMPLE PROGRAMS -+//// -+// -+// Incomplete text-in-3d-api example, which draws quads properly aligned to be lossless -+// -+#if 0 -+#define STB_TRUETYPE_IMPLEMENTATION // force following include to generate implementation -+#include "stb_truetype.h" -+ -+char ttf_buffer[1<<20]; -+unsigned char temp_bitmap[512*512]; -+ -+stbtt_chardata cdata[96]; // ASCII 32..126 is 95 glyphs -+GLstbtt_uint ftex; -+ -+void my_stbtt_initfont(void) -+{ -+ fread(ttf_buffer, 1, 1<<20, fopen("c:/windows/fonts/times.ttf", "rb")); -+ stbtt_BakeFontBitmap(data,0, 32.0, temp_bitmap,512,512, 32,96, cdata); // no guarantee this fits! -+ // can free ttf_buffer at this point -+ glGenTextures(1, &ftex); -+ glBindTexture(GL_TEXTURE_2D, ftex); -+ glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, 512,512, 0, GL_ALPHA, GL_UNSIGNED_BYTE, temp_bitmap); -+ // can free temp_bitmap at this point -+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); -+} -+ -+void my_stbtt_print(float x, float y, char *text) -+{ -+ // assume orthographic projection with units = screen pixels, origin at top left -+ glBindTexture(GL_TEXTURE_2D, ftex); -+ glBegin(GL_QUADS); -+ while (*text) { -+ if (*text >= 32 && *text < 128) { -+ stbtt_aligned_quad q; -+ stbtt_GetBakedQuad(cdata, 512,512, *text-32, &x,&y,&q,1);//1=opengl,0=old d3d -+ glTexCoord2f(q.s0,q.t1); glVertex2f(q.x0,q.y0); -+ glTexCoord2f(q.s1,q.t1); glVertex2f(q.x1,q.y0); -+ glTexCoord2f(q.s1,q.t0); glVertex2f(q.x1,q.y1); -+ glTexCoord2f(q.s0,q.t0); glVertex2f(q.x0,q.y1); -+ } -+ ++text; -+ } -+ glEnd(); -+} -+#endif -+// -+// -+////////////////////////////////////////////////////////////////////////////// -+// -+// Complete program (this compiles): get a single bitmap, print as ASCII art -+// -+#if 0 -+#include -+#define STB_TRUETYPE_IMPLEMENTATION // force following include to generate implementation -+#include "stb_truetype.h" -+ -+char ttf_buffer[1<<25]; -+ -+int main(int argc, char **argv) -+{ -+ stbtt_fontinfo font; -+ unsigned char *bitmap; -+ int w,h,i,j,c = (argc > 1 ? atoi(argv[1]) : 'a'), s = (argc > 2 ? atoi(argv[2]) : 20); -+ -+ fread(ttf_buffer, 1, 1<<25, fopen(argc > 3 ? argv[3] : "c:/windows/fonts/arialbd.ttf", "rb")); -+ -+ stbtt_InitFont(&font, ttf_buffer, stbtt_GetFontOffsetForIndex(ttf_buffer,0)); -+ bitmap = stbtt_GetCodepointBitmap(&font, 0,stbtt_ScaleForPixelHeight(&font, s), c, &w, &h, 0,0); -+ -+ for (j=0; j < h; ++j) { -+ for (i=0; i < w; ++i) -+ putchar(" .:ioVM@"[bitmap[j*w+i]>>5]); -+ putchar('\n'); -+ } -+ return 0; -+} -+#endif -+// -+// Output: -+// -+// .ii. -+// @@@@@@. -+// V@Mio@@o -+// :i. V@V -+// :oM@@M -+// :@@@MM@M -+// @@o o@M -+// :@@. M@M -+// @@@o@@@@ -+// :M@@V:@@. -+// -+////////////////////////////////////////////////////////////////////////////// -+// -+// Complete program: print "Hello World!" banner, with bugs -+// -+#if 0 -+int main(int arg, char **argv) -+{ -+ unsigned char screen[20][79]; -+ int i,j, pos=0; -+ float scale; -+ char *text = "Heljo World!"; -+ -+ fread(buffer, 1, 1000000, fopen("c:/windows/fonts/arialbd.ttf", "rb")); -+ stbtt_InitFont(&font, buffer, 0); -+ -+ scale = stbtt_ScaleForPixelHeight(&font, 16); -+ memset(screen, 0, sizeof(screen)); -+ -+ while (*text) { -+ int advance,lsb,x0,y0,x1,y1, newpos, baseline=13; -+ stbtt_GetCodepointHMetrics(&font, *text, &advance, &lsb); -+ stbtt_GetCodepointBitmapBox(&font, *text, scale,scale, &x0,&y0,&x1,&y1); -+ newpos = pos + (int) (lsb * scale) + x0; -+ stbtt_MakeCodepointBitmap(&font, &screen[baseline + y0][newpos], x1-x0,y1-y0, 79, scale,scale, *text); -+ // note that this stomps the old data, so where character boxes overlap (e.g. 'lj') it's wrong -+ // because this API is really for baking character bitmaps into textures -+ pos += (int) (advance * scale); -+ ++text; -+ } -+ -+ for (j=0; j < 20; ++j) { -+ for (i=0; i < 79; ++i) -+ putchar(" .:ioVM@"[screen[j][i]>>5]); -+ putchar('\n'); -+ } -+ -+ return 0; -+} -+#endif -+ -+ -+////////////////////////////////////////////////////////////////////////////// -+////////////////////////////////////////////////////////////////////////////// -+//// -+//// INTEGRATION WITH RUNTIME LIBRARIES -+//// -+ -+#ifdef STB_TRUETYPE_IMPLEMENTATION -+ // #define your own (u)stbtt_int8/16/32 before including to override this -+ #ifndef stbtt_uint8 -+ typedef unsigned char stbtt_uint8; -+ typedef signed char stbtt_int8; -+ typedef unsigned short stbtt_uint16; -+ typedef signed short stbtt_int16; -+ typedef unsigned int stbtt_uint32; -+ typedef signed int stbtt_int32; -+ #endif -+ -+ typedef char stbtt__check_size32[sizeof(stbtt_int32)==4 ? 1 : -1]; -+ typedef char stbtt__check_size16[sizeof(stbtt_int16)==2 ? 1 : -1]; -+ -+ // #define your own STBTT_sort() to override this to avoid qsort -+ #ifndef STBTT_sort -+ #include -+ #define STBTT_sort(data,num_items,item_size,compare_func) qsort(data,num_items,item_size,compare_func) -+ #endif -+ -+ // #define your own STBTT_ifloor/STBTT_iceil() to avoid math.h -+ #ifndef STBTT_ifloor -+ #include -+ #define STBTT_ifloor(x) ((int) floor(x)) -+ #define STBTT_iceil(x) ((int) ceil(x)) -+ #endif -+ -+ // #define your own functions "STBTT_malloc" / "STBTT_free" to avoid malloc.h -+ #ifndef STBTT_malloc -+ #include -+ #define STBTT_malloc(x,u) malloc(x) -+ #define STBTT_free(x,u) free(x) -+ #endif -+ -+ #ifndef STBTT_assert -+ #include -+ #define STBTT_assert(x) assert(x) -+ #endif -+ -+ #ifndef STBTT_strlen -+ #include -+ #define STBTT_strlen(x) strlen(x) -+ #endif -+ -+ #ifndef STBTT_memcpy -+ #include -+ #define STBTT_memcpy memcpy -+ #define STBTT_memset memset -+ #endif -+#endif -+ -+/////////////////////////////////////////////////////////////////////////////// -+/////////////////////////////////////////////////////////////////////////////// -+//// -+//// INTERFACE -+//// -+//// -+ -+#ifndef __STB_INCLUDE_STB_TRUETYPE_H__ -+#define __STB_INCLUDE_STB_TRUETYPE_H__ -+ -+#ifdef __cplusplus -+extern "C" { -+#endif -+ -+////////////////////////////////////////////////////////////////////////////// -+// -+// TEXTURE BAKING API -+// -+// If you use this API, you only have to call two functions ever. -+// -+ -+typedef struct -+{ -+ unsigned short x0,y0,x1,y1; // coordinates of bbox in bitmap -+ float xoff,yoff,xadvance; -+} stbtt_bakedchar; -+ -+extern int stbtt_BakeFontBitmap(const unsigned char *data, int offset, // font location (use offset=0 for plain .ttf) -+ float pixel_height, // height of font in pixels -+ unsigned char *pixels, int pw, int ph, // bitmap to be filled in -+ int first_char, int num_chars, // characters to bake -+ stbtt_bakedchar *chardata); // you allocate this, it's num_chars long -+// if return is positive, the first unused row of the bitmap -+// if return is negative, returns the negative of the number of characters that fit -+// if return is 0, no characters fit and no rows were used -+// This uses a very crappy packing. -+ -+typedef struct -+{ -+ float x0,y0,s0,t0; // top-left -+ float x1,y1,s1,t1; // bottom-right -+} stbtt_aligned_quad; -+ -+extern void stbtt_GetBakedQuad(stbtt_bakedchar *chardata, int pw, int ph, // same data as above -+ int char_index, // character to display -+ float *xpos, float *ypos, // pointers to current position in screen pixel space -+ stbtt_aligned_quad *q, // output: quad to draw -+ int opengl_fillrule); // true if opengl fill rule; false if DX9 or earlier -+// Call GetBakedQuad with char_index = 'character - first_char', and it -+// creates the quad you need to draw and advances the current position. -+// It's inefficient; you might want to c&p it and optimize it. -+ -+ -+////////////////////////////////////////////////////////////////////////////// -+// -+// FONT LOADING -+// -+// -+ -+extern int stbtt_GetFontOffsetForIndex(const unsigned char *data, int index); -+// Each .ttf file may have more than one font. Each has a sequential index -+// number starting from 0. Call this function to get the font offset for a -+// given index; it returns -1 if the index is out of range. A regular .ttf -+// file will only define one font and it always be at offset 0, so it will -+// return '0' for index 0, and -1 for all other indices. You can just skip -+// this step if you know it's that kind of font. -+ -+ -+// The following structure is defined publically so you can declare one on -+// the stack or as a global or etc. -+typedef struct -+{ -+ void *userdata; -+ unsigned char *data; // pointer to .ttf file -+ int fontstart; // offset of start of font -+ -+ int numGlyphs; // number of glyphs, needed for range checking -+ -+ int loca,head,glyf,hhea,hmtx; // table locations as offset from start of .ttf -+ int index_map; // a cmap mapping for our chosen character encoding -+ int indexToLocFormat; // format needed to map from glyph index to glyph -+} stbtt_fontinfo; -+ -+extern int stbtt_InitFont(stbtt_fontinfo *info, const unsigned char *data, int offset); -+// Given an offset into the file that defines a font, this function builds -+// the necessary cached info for the rest of the system. You must allocate -+// the stbtt_fontinfo yourself, and stbtt_InitFont will fill it out. You don't -+// need to do anything special to free it, because the contents are a pure -+// cache with no additional data structures. Returns 0 on failure. -+ -+ -+////////////////////////////////////////////////////////////////////////////// -+// -+// CHARACTER TO GLYPH-INDEX CONVERSIOn -+ -+int stbtt_FindGlyphIndex(const stbtt_fontinfo *info, int unicode_codepoint); -+// If you're going to perform multiple operations on the same character -+// and you want a speed-up, call this function with the character you're -+// going to process, then use glyph-based functions instead of the -+// codepoint-based functions. -+ -+ -+////////////////////////////////////////////////////////////////////////////// -+// -+// CHARACTER PROPERTIES -+// -+ -+extern float stbtt_ScaleForPixelHeight(const stbtt_fontinfo *info, float pixels); -+// computes a scale factor to produce a font whose "height" is 'pixels' tall. -+// Height is measured as the distance from the highest ascender to the lowest -+// descender; in other words, it's equivalent to calling stbtt_GetFontVMetrics -+// and computing: -+// scale = pixels / (ascent - descent) -+// so if you prefer to measure height by the ascent only, use a similar calculation. -+ -+extern void stbtt_GetFontVMetrics(const stbtt_fontinfo *info, int *ascent, int *descent, int *lineGap); -+// ascent is the coordinate above the baseline the font extends; descent -+// is the coordinate below the baseline the font extends (i.e. it is typically negative) -+// lineGap is the spacing between one row's descent and the next row's ascent... -+// so you should advance the vertical position by "*ascent - *descent + *lineGap" -+// these are expressed in unscaled coordinates -+ -+extern void stbtt_GetCodepointHMetrics(const stbtt_fontinfo *info, int codepoint, int *advanceWidth, int *leftSideBearing); -+// leftSideBearing is the offset from the current horizontal position to the left edge of the character -+// advanceWidth is the offset from the current horizontal position to the next horizontal position -+// these are expressed in unscaled coordinates -+ -+extern int stbtt_GetCodepointKernAdvance(const stbtt_fontinfo *info, int ch1, int ch2); -+// an additional amount to add to the 'advance' value between ch1 and ch2 -+// @TODO; for now always returns 0! -+ -+extern int stbtt_GetCodepointBox(const stbtt_fontinfo *info, int codepoint, int *x0, int *y0, int *x1, int *y1); -+// Gets the bounding box of the visible part of the glyph, in unscaled coordinates -+ -+extern void stbtt_GetGlyphHMetrics(const stbtt_fontinfo *info, int glyph_index, int *advanceWidth, int *leftSideBearing); -+extern int stbtt_GetGlyphKernAdvance(const stbtt_fontinfo *info, int glyph1, int glyph2); -+extern int stbtt_GetGlyphBox(const stbtt_fontinfo *info, int glyph_index, int *x0, int *y0, int *x1, int *y1); -+// as above, but takes one or more glyph indices for greater efficiency -+ -+ -+////////////////////////////////////////////////////////////////////////////// -+// -+// GLYPH SHAPES (you probably don't need these, but they have to go before -+// the bitmaps for C declaration-order reasons) -+// -+ -+#ifndef STBTT_vmove // you can predefine these to use different values (but why?) -+ enum { -+ STBTT_vmove=1, -+ STBTT_vline, -+ STBTT_vcurve -+ }; -+#endif -+ -+#ifndef stbtt_vertex // you can predefine this to use different values -+ // (we share this with other code at RAD) -+ #define stbtt_vertex_type short // can't use stbtt_int16 because that's not visible in the header file -+ typedef struct -+ { -+ stbtt_vertex_type x,y,cx,cy; -+ unsigned char type,padding; -+ } stbtt_vertex; -+#endif -+ -+extern int stbtt_GetCodepointShape(const stbtt_fontinfo *info, int unicode_codepoint, stbtt_vertex **vertices); -+extern int stbtt_GetGlyphShape(const stbtt_fontinfo *info, int glyph_index, stbtt_vertex **vertices); -+// returns # of vertices and fills *vertices with the pointer to them -+// these are expressed in "unscaled" coordinates -+ -+extern void stbtt_FreeShape(const stbtt_fontinfo *info, stbtt_vertex *vertices); -+// frees the data allocated above -+ -+////////////////////////////////////////////////////////////////////////////// -+// -+// BITMAP RENDERING -+// -+ -+extern void stbtt_FreeBitmap(unsigned char *bitmap, void *userdata); -+// frees the bitmap allocated below -+ -+extern unsigned char *stbtt_GetCodepointBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int codepoint, int *width, int *height, int *xoff, int *yoff); -+// allocates a large-enough single-channel 8bpp bitmap and renders the -+// specified character/glyph at the specified scale into it, with -+// antialiasing. 0 is no coverage (transparent), 255 is fully covered (opaque). -+// *width & *height are filled out with the width & height of the bitmap, -+// which is stored left-to-right, top-to-bottom. -+// -+// xoff/yoff are the offset it pixel space from the glyph origin to the top-left of the bitmap -+ -+extern void stbtt_MakeCodepointBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int codepoint); -+// the same as above, but you pass in storage for the bitmap in the form -+// of 'output', with row spacing of 'out_stride' bytes. the bitmap is -+// clipped to out_w/out_h bytes. call the next function to get the -+// height and width and positioning info -+ -+extern void stbtt_GetCodepointBitmapBox(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1); -+// get the bbox of the bitmap centered around the glyph origin; so the -+// bitmap width is ix1-ix0, height is iy1-iy0, and location to place -+// the bitmap top left is (leftSideBearing*scale,iy0). -+// (Note that the bitmap uses y-increases-down, but the shape uses -+// y-increases-up, so CodepointBitmapBox and CodepointBox are inverted.) -+ -+extern unsigned char *stbtt_GetGlyphBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int glyph, int *width, int *height, int *xoff, int *yoff); -+extern void stbtt_GetGlyphBitmapBox(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1); -+extern void stbtt_MakeGlyphBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int glyph); -+ -+//extern void stbtt_get_true_bbox(stbtt_vertex *vertices, int num_verts, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1); -+ -+// @TODO: don't expose this structure -+typedef struct -+{ -+ int w,h,stride; -+ unsigned char *pixels; -+} stbtt__bitmap; -+ -+extern void stbtt_Rasterize(stbtt__bitmap *result, float flatness_in_pixels, stbtt_vertex *vertices, int num_verts, float scale_x, float scale_y, int x_off, int y_off, int invert, void *userdata); -+ -+////////////////////////////////////////////////////////////////////////////// -+// -+// Finding the right font... -+// -+// You should really just solve this offline, keep your own tables -+// of what font is what, and don't try to get it out of the .ttf file. -+// That's because getting it out of the .ttf file is really hard, because -+// the names in the file can appear in many possible encodings, in many -+// possible languages, and e.g. if you need a case-insensitive comparison, -+// the details of that depend on the encoding & language in a complex way -+// (actually underspecified in truetype, but also gigantic). -+// -+// But you can use the provided functions in two possible ways: -+// stbtt_FindMatchingFont() will use *case-sensitive* comparisons on -+// unicode-encoded names to try to find the font you want; -+// you can run this before calling stbtt_InitFont() -+// -+// stbtt_GetFontNameString() lets you get any of the various strings -+// from the file yourself and do your own comparisons on them. -+// You have to have called stbtt_InitFont() first. -+ -+ -+extern int stbtt_FindMatchingFont(const unsigned char *fontdata, const char *name, int flags); -+// returns the offset (not index) of the font that matches, or -1 if none -+// if you use STBTT_MACSTYLE_DONTCARE, use a font name like "Arial Bold". -+// if you use any other flag, use a font name like "Arial"; this checks -+// the 'macStyle' header field; i don't know if fonts set this consistently -+#define STBTT_MACSTYLE_DONTCARE 0 -+#define STBTT_MACSTYLE_BOLD 1 -+#define STBTT_MACSTYLE_ITALIC 2 -+#define STBTT_MACSTYLE_UNDERSCORE 4 -+#define STBTT_MACSTYLE_NONE 8 // <= not same as 0, this makes us check the bitfield is 0 -+ -+extern int stbtt_CompareUTF8toUTF16_bigendian(const char *s1, int len1, const char *s2, int len2); -+// returns 1/0 whether the first string interpreted as utf8 is identical to -+// the second string interpreted as big-endian utf16... useful for strings from next func -+ -+extern char *stbtt_GetFontNameString(const stbtt_fontinfo *font, int *length, int platformID, int encodingID, int languageID, int nameID); -+// returns the string (which may be big-endian double byte, e.g. for unicode) -+// and puts the length in bytes in *length. -+// -+// some of the values for the IDs are below; for more see the truetype spec: -+// http://developer.apple.com/textfonts/TTRefMan/RM06/Chap6name.html -+// http://www.microsoft.com/typography/otspec/name.htm -+ -+enum { // platformID -+ STBTT_PLATFORM_ID_UNICODE =0, -+ STBTT_PLATFORM_ID_MAC =1, -+ STBTT_PLATFORM_ID_ISO =2, -+ STBTT_PLATFORM_ID_MICROSOFT =3 -+}; -+ -+enum { // encodingID for STBTT_PLATFORM_ID_UNICODE -+ STBTT_UNICODE_EID_UNICODE_1_0 =0, -+ STBTT_UNICODE_EID_UNICODE_1_1 =1, -+ STBTT_UNICODE_EID_ISO_10646 =2, -+ STBTT_UNICODE_EID_UNICODE_2_0_BMP=3, -+ STBTT_UNICODE_EID_UNICODE_2_0_FULL=4, -+}; -+ -+enum { // encodingID for STBTT_PLATFORM_ID_MICROSOFT -+ STBTT_MS_EID_SYMBOL =0, -+ STBTT_MS_EID_UNICODE_BMP =1, -+ STBTT_MS_EID_SHIFTJIS =2, -+ STBTT_MS_EID_UNICODE_FULL =10, -+}; -+ -+enum { // encodingID for STBTT_PLATFORM_ID_MAC; same as Script Manager codes -+ STBTT_MAC_EID_ROMAN =0, STBTT_MAC_EID_ARABIC =4, -+ STBTT_MAC_EID_JAPANESE =1, STBTT_MAC_EID_HEBREW =5, -+ STBTT_MAC_EID_CHINESE_TRAD =2, STBTT_MAC_EID_GREEK =6, -+ STBTT_MAC_EID_KOREAN =3, STBTT_MAC_EID_RUSSIAN =7, -+}; -+ -+enum { // languageID for STBTT_PLATFORM_ID_MICROSOFT; same as LCID... -+ // problematic because there are e.g. 16 english LCIDs and 16 arabic LCIDs -+ STBTT_MS_LANG_ENGLISH =0x0409, STBTT_MS_LANG_ITALIAN =0x0410, -+ STBTT_MS_LANG_CHINESE =0x0804, STBTT_MS_LANG_JAPANESE =0x0411, -+ STBTT_MS_LANG_DUTCH =0x0413, STBTT_MS_LANG_KOREAN =0x0412, -+ STBTT_MS_LANG_FRENCH =0x040c, STBTT_MS_LANG_RUSSIAN =0x0419, -+ STBTT_MS_LANG_GERMAN =0x0407, STBTT_MS_LANG_SPANISH =0x0409, -+ STBTT_MS_LANG_HEBREW =0x040d, STBTT_MS_LANG_SWEDISH =0x041D, -+}; -+ -+enum { // languageID for STBTT_PLATFORM_ID_MAC -+ STBTT_MAC_LANG_ENGLISH =0 , STBTT_MAC_LANG_JAPANESE =11, -+ STBTT_MAC_LANG_ARABIC =12, STBTT_MAC_LANG_KOREAN =23, -+ STBTT_MAC_LANG_DUTCH =4 , STBTT_MAC_LANG_RUSSIAN =32, -+ STBTT_MAC_LANG_FRENCH =1 , STBTT_MAC_LANG_SPANISH =6 , -+ STBTT_MAC_LANG_GERMAN =2 , STBTT_MAC_LANG_SWEDISH =5 , -+ STBTT_MAC_LANG_HEBREW =10, STBTT_MAC_LANG_CHINESE_SIMPLIFIED =33, -+ STBTT_MAC_LANG_ITALIAN =3 , STBTT_MAC_LANG_CHINESE_TRAD =19, -+}; -+ -+#ifdef __cplusplus -+} -+#endif -+ -+#endif // __STB_INCLUDE_STB_TRUETYPE_H__ -+ -+/////////////////////////////////////////////////////////////////////////////// -+/////////////////////////////////////////////////////////////////////////////// -+//// -+//// IMPLEMENTATION -+//// -+//// -+ -+#ifdef STB_TRUETYPE_IMPLEMENTATION -+ -+////////////////////////////////////////////////////////////////////////// -+// -+// accessors to parse data from file -+// -+ -+// on platforms that don't allow misaligned reads, if we want to allow -+// truetype fonts that aren't padded to alignment, define ALLOW_UNALIGNED_TRUETYPE -+ -+#define ttBYTE(p) (* (stbtt_uint8 *) (p)) -+#define ttCHAR(p) (* (stbtt_int8 *) (p)) -+#define ttFixed(p) ttLONG(p) -+ -+#if defined(STB_TRUETYPE_BIGENDIAN) && !defined(ALLOW_UNALIGNED_TRUETYPE) -+ -+ #define ttUSHORT(p) (* (stbtt_uint16 *) (p)) -+ #define ttSHORT(p) (* (stbtt_int16 *) (p)) -+ #define ttULONG(p) (* (stbtt_uint32 *) (p)) -+ #define ttLONG(p) (* (stbtt_int32 *) (p)) -+ -+#else -+ -+ stbtt_uint16 ttUSHORT(const stbtt_uint8 *p) { return p[0]*256 + p[1]; } -+ stbtt_int16 ttSHORT(const stbtt_uint8 *p) { return p[0]*256 + p[1]; } -+ stbtt_uint32 ttULONG(const stbtt_uint8 *p) { return (p[0]<<24) + (p[1]<<16) + (p[2]<<8) + p[3]; } -+ stbtt_int32 ttLONG(const stbtt_uint8 *p) { return (p[0]<<24) + (p[1]<<16) + (p[2]<<8) + p[3]; } -+ -+#endif -+ -+#define stbtt_tag4(p,c0,c1,c2,c3) ((p)[0] == (c0) && (p)[1] == (c1) && (p)[2] == (c2) && (p)[3] == (c3)) -+#define stbtt_tag(p,str) stbtt_tag4(p,str[0],str[1],str[2],str[3]) -+ -+static int stbtt__isfont(const stbtt_uint8 *font) -+{ -+ // check the version number -+ if (stbtt_tag(font, "1")) return 1; // TrueType 1 -+ if (stbtt_tag(font, "typ1")) return 1; // TrueType with type 1 font -- we don't support this! -+ if (stbtt_tag(font, "OTTO")) return 1; // OpenType with CFF -+ if (stbtt_tag4(font, 0,1,0,0)) return 1; // OpenType 1.0 -+ return 0; -+} -+ -+// @OPTIMIZE: binary search -+static stbtt_uint32 stbtt__find_table(stbtt_uint8 *data, stbtt_uint32 fontstart, const char *tag) -+{ -+ stbtt_int32 num_tables = ttUSHORT(data+fontstart+4); -+ stbtt_uint32 tabledir = fontstart + 12; -+ stbtt_int32 i; -+ for (i=0; i < num_tables; ++i) { -+ stbtt_uint32 loc = tabledir + 16*i; -+ if (stbtt_tag(data+loc+0, tag)) -+ return ttULONG(data+loc+8); -+ } -+ return 0; -+} -+ -+int stbtt_GetFontOffsetForIndex(const unsigned char *font_collection, int index) -+{ -+ // if it's just a font, there's only one valid index -+ if (stbtt__isfont(font_collection)) -+ return index == 0 ? 0 : -1; -+ -+ // check if it's a TTC -+ if (stbtt_tag(font_collection, "ttcf")) { -+ // version 1? -+ if (ttULONG(font_collection+4) == 0x00010000 || ttULONG(font_collection+4) == 0x00020000) { -+ stbtt_int32 n = ttLONG(font_collection+8); -+ if (index >= n) -+ return -1; -+ return ttULONG(font_collection+12+index*14); -+ } -+ } -+ return -1; -+} -+ -+int stbtt_InitFont(stbtt_fontinfo *info, const unsigned char *data2, int fontstart) -+{ -+ stbtt_uint8 *data = (stbtt_uint8 *) data2; -+ stbtt_uint32 cmap, t; -+ stbtt_int32 i,numTables; -+ -+ info->data = data; -+ info->fontstart = fontstart; -+ -+ cmap = stbtt__find_table(data, fontstart, "cmap"); -+ info->loca = stbtt__find_table(data, fontstart, "loca"); -+ info->head = stbtt__find_table(data, fontstart, "head"); -+ info->glyf = stbtt__find_table(data, fontstart, "glyf"); -+ info->hhea = stbtt__find_table(data, fontstart, "hhea"); -+ info->hmtx = stbtt__find_table(data, fontstart, "hmtx"); -+ if (!cmap || !info->loca || !info->head || !info->glyf || !info->hhea || !info->hmtx) -+ return 0; -+ -+ t = stbtt__find_table(data, fontstart, "maxp"); -+ if (t) -+ info->numGlyphs = ttUSHORT(data+t+4); -+ else -+ info->numGlyphs = 0xffff; -+ -+ // find a cmap encoding table we understand *now* to avoid searching -+ // later. (todo: could make this installable) -+ // the same regardless of glyph. -+ numTables = ttUSHORT(data + cmap + 2); -+ info->index_map = 0; -+ for (i=0; i < numTables; ++i) { -+ stbtt_uint32 encoding_record = cmap + 4 + 8 * i; -+ // find an encoding we understand: -+ switch(ttUSHORT(data+encoding_record)) { -+ case STBTT_PLATFORM_ID_MICROSOFT: -+ switch (ttUSHORT(data+encoding_record+2)) { -+ case STBTT_MS_EID_UNICODE_BMP: -+ case STBTT_MS_EID_UNICODE_FULL: -+ // MS/Unicode -+ info->index_map = cmap + ttULONG(data+encoding_record+4); -+ break; -+ } -+ break; -+ } -+ } -+ if (info->index_map == 0) -+ return 0; -+ -+ info->indexToLocFormat = ttUSHORT(data+info->head + 50); -+ return 1; -+} -+ -+int stbtt_FindGlyphIndex(const stbtt_fontinfo *info, int unicode_codepoint) -+{ -+ stbtt_uint8 *data = info->data; -+ stbtt_uint32 index_map = info->index_map; -+ -+ stbtt_uint16 format = ttUSHORT(data + index_map + 0); -+ if (format == 0) { // apple byte encoding -+ stbtt_int32 bytes = ttUSHORT(data + index_map + 2); -+ if (unicode_codepoint < bytes-6) -+ return ttBYTE(data + index_map + 6 + unicode_codepoint); -+ return 0; -+ } else if (format == 6) { -+ stbtt_uint32 first = ttUSHORT(data + index_map + 6); -+ stbtt_uint32 count = ttUSHORT(data + index_map + 8); -+ if ((stbtt_uint32) unicode_codepoint >= first && (stbtt_uint32) unicode_codepoint < first+count) -+ return ttUSHORT(data + index_map + 10 + (unicode_codepoint - first)*2); -+ return 0; -+ } else if (format == 2) { -+ STBTT_assert(0); // @TODO: high-byte mapping for japanese/chinese/korean -+ return 0; -+ } else if (format == 4) { // standard mapping for windows fonts: binary search collection of ranges -+ stbtt_uint16 segcount = ttUSHORT(data+index_map+6) >> 1; -+ stbtt_uint16 searchRange = ttUSHORT(data+index_map+8) >> 1; -+ stbtt_uint16 entrySelector = ttUSHORT(data+index_map+10); -+ stbtt_uint16 rangeShift = ttUSHORT(data+index_map+12) >> 1; -+ stbtt_uint16 item, offset, start, end; -+ -+ // do a binary search of the segments -+ stbtt_uint32 endCount = index_map + 14; -+ stbtt_uint32 search = endCount; -+ -+ if (unicode_codepoint > 0xffff) -+ return 0; -+ -+ // they lie from endCount .. endCount + segCount -+ // but searchRange is the nearest power of two, so... -+ if (unicode_codepoint >= ttUSHORT(data + search + rangeShift*2)) -+ search += rangeShift*2; -+ -+ // now decrement to bias correctly to find smallest -+ search -= 2; -+ while (entrySelector) { -+ stbtt_uint16 start, end; -+ searchRange >>= 1; -+ start = ttUSHORT(data + search + 2 + segcount*2 + 2); -+ end = ttUSHORT(data + search + 2); -+ start = ttUSHORT(data + search + searchRange*2 + segcount*2 + 2); -+ end = ttUSHORT(data + search + searchRange*2); -+ if (unicode_codepoint > end) -+ search += searchRange*2; -+ --entrySelector; -+ } -+ search += 2; -+ -+ item = (stbtt_uint16) ((search - endCount) >> 1); -+ -+ STBTT_assert(unicode_codepoint <= ttUSHORT(data + endCount + 2*item)); -+ start = ttUSHORT(data + index_map + 14 + segcount*2 + 2 + 2*item); -+ end = ttUSHORT(data + index_map + 14 + 2 + 2*item); -+ if (unicode_codepoint < start) -+ return 0; -+ -+ offset = ttUSHORT(data + index_map + 14 + segcount*6 + 2 + 2*item); -+ if (offset == 0) -+ return unicode_codepoint + ttSHORT(data + index_map + 14 + segcount*4 + 2 + 2*item); -+ -+ return ttUSHORT(data + offset + (unicode_codepoint-start)*2 + index_map + 14 + segcount*6 + 2 + 2*item); -+ } else if (format == 12) { -+ stbtt_uint16 ngroups = ttUSHORT(data+index_map+6); -+ stbtt_int32 low,high; -+ low = 0; high = (stbtt_int32)ngroups; -+ // Binary search the right group. -+ while (low <= high) { -+ stbtt_int32 mid = low + ((high-low) >> 1); // rounds down, so low <= mid < high -+ stbtt_uint32 start_char = ttULONG(data+index_map+16+mid*12); -+ stbtt_uint32 end_char = ttULONG(data+index_map+16+mid*12+4); -+ if ((stbtt_uint32) unicode_codepoint < start_char) -+ high = mid-1; -+ else if ((stbtt_uint32) unicode_codepoint > end_char) -+ low = mid+1; -+ else { -+ stbtt_uint32 start_glyph = ttULONG(data+index_map+16+mid*12+8); -+ return start_glyph + unicode_codepoint-start_char; -+ } -+ } -+ return 0; // not found -+ } -+ // @TODO -+ STBTT_assert(0); -+ return 0; -+} -+ -+int stbtt_GetCodepointShape(const stbtt_fontinfo *info, int unicode_codepoint, stbtt_vertex **vertices) -+{ -+ return stbtt_GetGlyphShape(info, stbtt_FindGlyphIndex(info, unicode_codepoint), vertices); -+} -+ -+static void stbtt_setvertex(stbtt_vertex *v, stbtt_uint8 type, stbtt_int16 x, stbtt_int16 y, stbtt_int16 cx, stbtt_int16 cy) -+{ -+ v->type = type; -+ v->x = x; -+ v->y = y; -+ v->cx = cx; -+ v->cy = cy; -+} -+ -+static int stbtt__GetGlyfOffset(const stbtt_fontinfo *info, int glyph_index) -+{ -+ int g1,g2; -+ -+ if (glyph_index >= info->numGlyphs) return -1; // glyph index out of range -+ if (info->indexToLocFormat >= 2) return -1; // unknown index->glyph map format -+ -+ if (info->indexToLocFormat == 0) { -+ g1 = info->glyf + ttUSHORT(info->data + info->loca + glyph_index * 2) * 2; -+ g2 = info->glyf + ttUSHORT(info->data + info->loca + glyph_index * 2 + 2) * 2; -+ } else { -+ g1 = info->glyf + ttULONG (info->data + info->loca + glyph_index * 4); -+ g2 = info->glyf + ttULONG (info->data + info->loca + glyph_index * 4 + 4); -+ } -+ -+ return g1==g2 ? -1 : g1; // if length is 0, return -1 -+} -+ -+int stbtt_GetGlyphBox(const stbtt_fontinfo *info, int glyph_index, int *x0, int *y0, int *x1, int *y1) -+{ -+ int g = stbtt__GetGlyfOffset(info, glyph_index); -+ if (g < 0) return 0; -+ -+ if (x0) *x0 = ttSHORT(info->data + g + 2); -+ if (y0) *y0 = ttSHORT(info->data + g + 4); -+ if (x1) *x1 = ttSHORT(info->data + g + 6); -+ if (y1) *y1 = ttSHORT(info->data + g + 8); -+ return 1; -+} -+ -+int stbtt_GetCodepointBox(const stbtt_fontinfo *info, int codepoint, int *x0, int *y0, int *x1, int *y1) -+{ -+ return stbtt_GetGlyphBox(info, stbtt_FindGlyphIndex(info,codepoint), x0,y0,x1,y1); -+} -+ -+int stbtt_GetGlyphShape(const stbtt_fontinfo *info, int glyph_index, stbtt_vertex **pvertices) -+{ -+ stbtt_int16 numberOfContours; -+ stbtt_uint8 *endPtsOfContours; -+ stbtt_uint8 *data = info->data; -+ stbtt_vertex *vertices=0; -+ int num_vertices=0; -+ int g = stbtt__GetGlyfOffset(info, glyph_index); -+ -+ *pvertices = NULL; -+ -+ if (g < 0) return 0; -+ -+ numberOfContours = ttSHORT(data + g); -+ -+ if (numberOfContours > 0) { -+ stbtt_uint8 flags=0,flagcount; -+ stbtt_int32 ins, i,j=0,m,n, next_move, was_off=0, off; -+ stbtt_int16 x,y,cx,cy,sx,sy; -+ stbtt_uint8 *points; -+ endPtsOfContours = (data + g + 10); -+ ins = ttUSHORT(data + g + 10 + numberOfContours * 2); -+ points = data + g + 10 + numberOfContours * 2 + 2 + ins; -+ -+ n = 1+ttUSHORT(endPtsOfContours + numberOfContours*2-2); -+ -+ m = n + numberOfContours; // a loose bound on how many vertices we might need -+ vertices = (stbtt_vertex *) STBTT_malloc(m * sizeof(vertices[0]), info->userdata); -+ if (vertices == 0) -+ return 0; -+ -+ next_move = 0; -+ flagcount=0; -+ -+ // in first pass, we load uninterpreted data into the allocated array -+ // above, shifted to the end of the array so we won't overwrite it when -+ // we create our final data starting from the front -+ -+ off = m - n; // starting offset for uninterpreted data, regardless of how m ends up being calculated -+ -+ // first load flags -+ -+ for (i=0; i < n; ++i) { -+ if (flagcount == 0) { -+ flags = *points++; -+ if (flags & 8) -+ flagcount = *points++; -+ } else -+ --flagcount; -+ vertices[off+i].type = flags; -+ } -+ -+ // now load x coordinates -+ x=0; -+ for (i=0; i < n; ++i) { -+ flags = vertices[off+i].type; -+ if (flags & 2) { -+ stbtt_int16 dx = *points++; -+ x += (flags & 16) ? dx : -dx; // ??? -+ } else { -+ if (!(flags & 16)) { -+ x = x + (stbtt_int16) (points[0]*256 + points[1]); -+ points += 2; -+ } -+ } -+ vertices[off+i].x = x; -+ } -+ -+ // now load y coordinates -+ y=0; -+ for (i=0; i < n; ++i) { -+ flags = vertices[off+i].type; -+ if (flags & 4) { -+ stbtt_int16 dy = *points++; -+ y += (flags & 32) ? dy : -dy; // ??? -+ } else { -+ if (!(flags & 32)) { -+ y = y + (stbtt_int16) (points[0]*256 + points[1]); -+ points += 2; -+ } -+ } -+ vertices[off+i].y = y; -+ } -+ -+ // now convert them to our format -+ num_vertices=0; -+ sx = sy = cx = cy = 0; -+ for (i=0; i < n; ++i) { -+ flags = vertices[off+i].type; -+ x = (stbtt_int16) vertices[off+i].x; -+ y = (stbtt_int16) vertices[off+i].y; -+ if (next_move == i) { -+ // when we get to the end, we have to close the shape explicitly -+ if (i != 0) { -+ if (was_off) -+ stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve,sx,sy,cx,cy); -+ else -+ stbtt_setvertex(&vertices[num_vertices++], STBTT_vline,sx,sy,0,0); -+ } -+ -+ // now start the new one -+ stbtt_setvertex(&vertices[num_vertices++], STBTT_vmove,x,y,0,0); -+ next_move = 1 + ttUSHORT(endPtsOfContours+j*2); -+ ++j; -+ was_off = 0; -+ sx = x; -+ sy = y; -+ } else { -+ if (!(flags & 1)) { // if it's a curve -+ if (was_off) // two off-curve control points in a row means interpolate an on-curve midpoint -+ stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, (cx+x)>>1, (cy+y)>>1, cx, cy); -+ cx = x; -+ cy = y; -+ was_off = 1; -+ } else { -+ if (was_off) -+ stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, x,y, cx, cy); -+ else -+ stbtt_setvertex(&vertices[num_vertices++], STBTT_vline, x,y,0,0); -+ was_off = 0; -+ } -+ } -+ } -+ if (i != 0) { -+ if (was_off) -+ stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve,sx,sy,cx,cy); -+ else -+ stbtt_setvertex(&vertices[num_vertices++], STBTT_vline,sx,sy,0,0); -+ } -+ } else if (numberOfContours == -1) { -+ // Compound shapes. -+ int more = 1; -+ stbtt_uint8 *comp = data + g + 10; -+ num_vertices = 0; -+ vertices = 0; -+ while (more) { -+ stbtt_uint16 flags, gidx; -+ int comp_num_verts = 0, i; -+ stbtt_vertex *comp_verts = 0, *tmp = 0; -+ float mtx[6] = {1,0,0,1,0,0}, m, n; -+ -+ flags = ttSHORT(comp); comp+=2; -+ gidx = ttSHORT(comp); comp+=2; -+ -+ if (flags & 2) { // XY values -+ if (flags & 1) { // shorts -+ mtx[4] = ttSHORT(comp); comp+=2; -+ mtx[5] = ttSHORT(comp); comp+=2; -+ } else { -+ mtx[4] = ttCHAR(comp); comp+=1; -+ mtx[5] = ttCHAR(comp); comp+=1; -+ } -+ } -+ else { -+ // @TODO handle matching point -+ STBTT_assert(0); -+ } -+ if (flags & (1<<3)) { // WE_HAVE_A_SCALE -+ mtx[0] = mtx[3] = ttSHORT(comp)/16384.0f; comp+=2; -+ mtx[1] = mtx[2] = 0; -+ } else if (flags & (1<<6)) { // WE_HAVE_AN_X_AND_YSCALE -+ mtx[0] = ttSHORT(comp)/16384.0f; comp+=2; -+ mtx[1] = mtx[2] = 0; -+ mtx[3] = ttSHORT(comp)/16384.0f; comp+=2; -+ } else if (flags & (1<<7)) { // WE_HAVE_A_TWO_BY_TWO -+ mtx[0] = ttSHORT(comp)/16384.0f; comp+=2; -+ mtx[1] = ttSHORT(comp)/16384.0f; comp+=2; -+ mtx[2] = ttSHORT(comp)/16384.0f; comp+=2; -+ mtx[3] = ttSHORT(comp)/16384.0f; comp+=2; -+ } -+ -+ // Find transformation scales. -+ m = (float) sqrt(mtx[0]*mtx[0] + mtx[1]*mtx[1]); -+ n = (float) sqrt(mtx[2]*mtx[2] + mtx[3]*mtx[3]); -+ -+ // Get indexed glyph. -+ comp_num_verts = stbtt_GetGlyphShape(info, gidx, &comp_verts); -+ if (comp_num_verts > 0) { -+ // Transform vertices. -+ for (i = 0; i < comp_num_verts; ++i) { -+ stbtt_vertex* v = &comp_verts[i]; -+ stbtt_vertex_type x,y; -+ x=v->x; y=v->y; -+ v->x = (stbtt_vertex_type)(m * (mtx[0]*x + mtx[2]*y + mtx[4])); -+ v->y = (stbtt_vertex_type)(n * (mtx[1]*x + mtx[3]*y + mtx[5])); -+ x=v->cx; y=v->cy; -+ v->cx = (stbtt_vertex_type)(m * (mtx[0]*x + mtx[2]*y + mtx[4])); -+ v->cy = (stbtt_vertex_type)(n * (mtx[1]*x + mtx[3]*y + mtx[5])); -+ } -+ // Append vertices. -+ tmp = (stbtt_vertex*)STBTT_malloc((num_vertices+comp_num_verts)*sizeof(stbtt_vertex), info->userdata); -+ if (!tmp) { -+ if (vertices) STBTT_free(vertices, info->userdata); -+ if (comp_verts) STBTT_free(comp_verts, info->userdata); -+ return 0; -+ } -+ if (num_vertices > 0) memcpy(tmp, vertices, num_vertices*sizeof(stbtt_vertex)); -+ memcpy(tmp+num_vertices, comp_verts, comp_num_verts*sizeof(stbtt_vertex)); -+ if (vertices) STBTT_free(vertices, info->userdata); -+ vertices = tmp; -+ STBTT_free(comp_verts, info->userdata); -+ num_vertices += comp_num_verts; -+ } -+ // More components ? -+ more = flags & (1<<5); -+ } -+ } else if (numberOfContours < 0) { -+ // @TODO other compound variations? -+ STBTT_assert(0); -+ } else { -+ // numberOfCounters == 0, do nothing -+ } -+ -+ *pvertices = vertices; -+ return num_vertices; -+} -+ -+void stbtt_GetGlyphHMetrics(const stbtt_fontinfo *info, int glyph_index, int *advanceWidth, int *leftSideBearing) -+{ -+ stbtt_uint16 numOfLongHorMetrics = ttUSHORT(info->data+info->hhea + 34); -+ if (glyph_index < numOfLongHorMetrics) { -+ if (advanceWidth) *advanceWidth = ttSHORT(info->data + info->hmtx + 4*glyph_index); -+ if (leftSideBearing) *leftSideBearing = ttSHORT(info->data + info->hmtx + 4*glyph_index + 2); -+ } else { -+ if (advanceWidth) *advanceWidth = ttSHORT(info->data + info->hmtx + 4*(numOfLongHorMetrics-1)); -+ if (leftSideBearing) *leftSideBearing = ttSHORT(info->data + info->hmtx + 4*numOfLongHorMetrics + 2*(glyph_index - numOfLongHorMetrics)); -+ } -+} -+ -+int stbtt_GetGlyphKernAdvance(const stbtt_fontinfo * /*info*/, int /*glyph1*/, int /*glyph2*/) -+{ -+ return 0; -+} -+ -+int stbtt_GetCodepointKernAdvance(const stbtt_fontinfo * /*info*/, int /*ch1*/, int /*ch2*/) -+{ -+ return 0; -+} -+ -+void stbtt_GetCodepointHMetrics(const stbtt_fontinfo *info, int codepoint, int *advanceWidth, int *leftSideBearing) -+{ -+ stbtt_GetGlyphHMetrics(info, stbtt_FindGlyphIndex(info,codepoint), advanceWidth, leftSideBearing); -+} -+ -+void stbtt_GetFontVMetrics(const stbtt_fontinfo *info, int *ascent, int *descent, int *lineGap) -+{ -+ if (ascent ) *ascent = ttSHORT(info->data+info->hhea + 4); -+ if (descent) *descent = ttSHORT(info->data+info->hhea + 6); -+ if (lineGap) *lineGap = ttSHORT(info->data+info->hhea + 8); -+} -+ -+float stbtt_ScaleForPixelHeight(const stbtt_fontinfo *info, float height) -+{ -+ int fheight = ttSHORT(info->data + info->hhea + 4) - ttSHORT(info->data + info->hhea + 6); -+ return (float) height / fheight; -+} -+ -+void stbtt_FreeShape(const stbtt_fontinfo *info, stbtt_vertex *v) -+{ -+ STBTT_free(v, info->userdata); -+} -+ -+////////////////////////////////////////////////////////////////////////////// -+// -+// antialiasing software rasterizer -+// -+ -+void stbtt_GetGlyphBitmapBox(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1) -+{ -+ int x0,y0,x1,y1; -+ if (!stbtt_GetGlyphBox(font, glyph, &x0,&y0,&x1,&y1)) -+ x0=y0=x1=y1=0; // e.g. space character -+ // now move to integral bboxes (treating pixels as little squares, what pixels get touched)? -+ if (ix0) *ix0 = STBTT_ifloor(x0 * scale_x); -+ if (iy0) *iy0 = -STBTT_iceil (y1 * scale_y); -+ if (ix1) *ix1 = STBTT_iceil (x1 * scale_x); -+ if (iy1) *iy1 = -STBTT_ifloor(y0 * scale_y); -+} -+ -+void stbtt_GetCodepointBitmapBox(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1) -+{ -+ stbtt_GetGlyphBitmapBox(font, stbtt_FindGlyphIndex(font,codepoint), scale_x, scale_y, ix0,iy0,ix1,iy1); -+} -+ -+typedef struct stbtt__edge { -+ float x0,y0, x1,y1; -+ int invert; -+} stbtt__edge; -+ -+typedef struct stbtt__active_edge -+{ -+ int x,dx; -+ float ey; -+ struct stbtt__active_edge *next; -+ int valid; -+} stbtt__active_edge; -+ -+#define FIXSHIFT 10 -+#define FIX (1 << FIXSHIFT) -+#define FIXMASK (FIX-1) -+ -+static stbtt__active_edge *new_active(stbtt__edge *e, int off_x, float start_point, void *userdata) -+{ -+ stbtt__active_edge *z = (stbtt__active_edge *) STBTT_malloc(sizeof(*z), userdata); // @TODO: make a pool of these!!! -+ float dxdy = (e->x1 - e->x0) / (e->y1 - e->y0); -+ STBTT_assert(e->y0 <= start_point); -+ if (!z) return z; -+ // round dx down to avoid going too far -+ if (dxdy < 0) -+ z->dx = -STBTT_ifloor(FIX * -dxdy); -+ else -+ z->dx = STBTT_ifloor(FIX * dxdy); -+ z->x = STBTT_ifloor(FIX * (e->x0 + dxdy * (start_point - e->y0))); -+ z->x -= off_x * FIX; -+ z->ey = e->y1; -+ z->next = 0; -+ z->valid = e->invert ? 1 : -1; -+ return z; -+} -+ -+// note: this routine clips fills that extend off the edges... ideally this -+// wouldn't happen, but it could happen if the truetype glyph bounding boxes -+// are wrong, or if the user supplies a too-small bitmap -+static void stbtt__fill_active_edges(unsigned char *scanline, int len, stbtt__active_edge *e, int max_weight) -+{ -+ // non-zero winding fill -+ int x0=0, w=0; -+ -+ while (e) { -+ if (w == 0) { -+ // if we're currently at zero, we need to record the edge start point -+ x0 = e->x; w += e->valid; -+ } else { -+ int x1 = e->x; w += e->valid; -+ // if we went to zero, we need to draw -+ if (w == 0) { -+ int i = x0 >> FIXSHIFT; -+ int j = x1 >> FIXSHIFT; -+ -+ if (i < len && j >= 0) { -+ if (i == j) { -+ // x0,x1 are the same pixel, so compute combined coverage -+ scanline[i] = scanline[i] + (stbtt_uint8) ((x1 - x0) * max_weight >> FIXSHIFT); -+ } else { -+ if (i >= 0) // add antialiasing for x0 -+ scanline[i] = scanline[i] + (stbtt_uint8) (((FIX - (x0 & FIXMASK)) * max_weight) >> FIXSHIFT); -+ else -+ i = -1; // clip -+ -+ if (j < len) // add antialiasing for x1 -+ scanline[j] = scanline[j] + (stbtt_uint8) (((x1 & FIXMASK) * max_weight) >> FIXSHIFT); -+ else -+ j = len; // clip -+ -+ for (++i; i < j; ++i) // fill pixels between x0 and x1 -+ scanline[i] = scanline[i] + (stbtt_uint8) max_weight; -+ } -+ } -+ } -+ } -+ -+ e = e->next; -+ } -+} -+ -+static void stbtt__rasterize_sorted_edges(stbtt__bitmap *result, stbtt__edge *e, int n, int vsubsample, int off_x, int off_y, void *userdata) -+{ -+ stbtt__active_edge *active = NULL; -+ int y,j=0; -+ int max_weight = (255 / vsubsample); // weight per vertical scanline -+ int s; // vertical subsample index -+ unsigned char scanline_data[512], *scanline; -+ -+ if (result->w > 512) -+ scanline = (unsigned char *) STBTT_malloc(result->w, userdata); -+ else -+ scanline = scanline_data; -+ -+ y = off_y * vsubsample; -+ e[n].y0 = (off_y + result->h) * (float) vsubsample + 1; -+ -+ while (j < result->h) { -+ STBTT_memset(scanline, 0, result->w); -+ for (s=0; s < vsubsample; ++s) { -+ // find center of pixel for this scanline -+ float scan_y = y + 0.5f; -+ stbtt__active_edge **step = &active; -+ -+ // update all active edges; -+ // remove all active edges that terminate before the center of this scanline -+ while (*step) { -+ stbtt__active_edge * z = *step; -+ if (z->ey <= scan_y) { -+ *step = z->next; // delete from list -+ STBTT_assert(z->valid); -+ z->valid = 0; -+ STBTT_free(z, userdata); -+ } else { -+ z->x += z->dx; // advance to position for current scanline -+ step = &((*step)->next); // advance through list -+ } -+ } -+ -+ // resort the list if needed -+ for(;;) { -+ int changed=0; -+ step = &active; -+ while (*step && (*step)->next) { -+ if ((*step)->x > (*step)->next->x) { -+ stbtt__active_edge *t = *step; -+ stbtt__active_edge *q = t->next; -+ -+ t->next = q->next; -+ q->next = t; -+ *step = q; -+ changed = 1; -+ } -+ step = &(*step)->next; -+ } -+ if (!changed) break; -+ } -+ -+ // insert all edges that start before the center of this scanline -- omit ones that also end on this scanline -+ while (e->y0 <= scan_y) { -+ if (e->y1 > scan_y) { -+ stbtt__active_edge *z = new_active(e, off_x, scan_y, userdata); -+ // find insertion point -+ if (active == NULL) -+ active = z; -+ else if (z->x < active->x) { -+ // insert at front -+ z->next = active; -+ active = z; -+ } else { -+ // find thing to insert AFTER -+ stbtt__active_edge *p = active; -+ while (p->next && p->next->x < z->x) -+ p = p->next; -+ // at this point, p->next->x is NOT < z->x -+ z->next = p->next; -+ p->next = z; -+ } -+ } -+ ++e; -+ } -+ -+ // now process all active edges in XOR fashion -+ if (active) -+ stbtt__fill_active_edges(scanline, result->w, active, max_weight); -+ -+ ++y; -+ } -+ STBTT_memcpy(result->pixels + j * result->stride, scanline, result->w); -+ ++j; -+ } -+ -+ while (active) { -+ stbtt__active_edge *z = active; -+ active = active->next; -+ STBTT_free(z, userdata); -+ } -+ -+ if (scanline != scanline_data) -+ STBTT_free(scanline, userdata); -+} -+ -+static int stbtt__edge_compare(const void *p, const void *q) -+{ -+ stbtt__edge *a = (stbtt__edge *) p; -+ stbtt__edge *b = (stbtt__edge *) q; -+ -+ if (a->y0 < b->y0) return -1; -+ if (a->y0 > b->y0) return 1; -+ return 0; -+} -+ -+typedef struct -+{ -+ float x,y; -+} stbtt__point; -+ -+static void stbtt__rasterize(stbtt__bitmap *result, stbtt__point *pts, int *wcount, int windings, float scale_x, float scale_y, int off_x, int off_y, int invert, void *userdata) -+{ -+ float y_scale_inv = invert ? -scale_y : scale_y; -+ stbtt__edge *e; -+ int n,i,j,k,m; -+ int vsubsample = result->h < 8 ? 15 : 5; -+ // vsubsample should divide 255 evenly; otherwise we won't reach full opacity -+ -+ // now we have to blow out the windings into explicit edge lists -+ n = 0; -+ for (i=0; i < windings; ++i) -+ n += wcount[i]; -+ -+ e = (stbtt__edge *) STBTT_malloc(sizeof(*e) * (n+1), userdata); // add an extra one as a sentinel -+ if (e == 0) return; -+ n = 0; -+ -+ m=0; -+ for (i=0; i < windings; ++i) { -+ stbtt__point *p = pts + m; -+ m += wcount[i]; -+ j = wcount[i]-1; -+ for (k=0; k < wcount[i]; j=k++) { -+ int a=k,b=j; -+ // skip the edge if horizontal -+ if (p[j].y == p[k].y) -+ continue; -+ // add edge from j to k to the list -+ e[n].invert = 0; -+ if (invert ? p[j].y > p[k].y : p[j].y < p[k].y) { -+ e[n].invert = 1; -+ a=j,b=k; -+ } -+ e[n].x0 = p[a].x * scale_x; -+ e[n].y0 = p[a].y * y_scale_inv * vsubsample; -+ e[n].x1 = p[b].x * scale_x; -+ e[n].y1 = p[b].y * y_scale_inv * vsubsample; -+ ++n; -+ } -+ } -+ -+ // now sort the edges by their highest point (should snap to integer, and then by x) -+ STBTT_sort(e, n, sizeof(e[0]), stbtt__edge_compare); -+ -+ // now, traverse the scanlines and find the intersections on each scanline, use xor winding rule -+ stbtt__rasterize_sorted_edges(result, e, n, vsubsample, off_x, off_y, userdata); -+ -+ STBTT_free(e, userdata); -+} -+ -+static void stbtt__add_point(stbtt__point *points, int n, float x, float y) -+{ -+ if (!points) return; // during first pass, it's unallocated -+ points[n].x = x; -+ points[n].y = y; -+} -+ -+// tesselate until threshhold p is happy... @TODO warped to compensate for non-linear stretching -+static int stbtt__tesselate_curve(stbtt__point *points, int *num_points, float x0, float y0, float x1, float y1, float x2, float y2, float objspace_flatness_squared, int n) -+{ -+ // midpoint -+ float mx = (x0 + 2*x1 + x2)/4; -+ float my = (y0 + 2*y1 + y2)/4; -+ // versus directly drawn line -+ float dx = (x0+x2)/2 - mx; -+ float dy = (y0+y2)/2 - my; -+ if (n > 16) // 65536 segments on one curve better be enough! -+ return 1; -+ if (dx*dx+dy*dy > objspace_flatness_squared) { // half-pixel error allowed... need to be smaller if AA -+ stbtt__tesselate_curve(points, num_points, x0,y0, (x0+x1)/2.0f,(y0+y1)/2.0f, mx,my, objspace_flatness_squared,n+1); -+ stbtt__tesselate_curve(points, num_points, mx,my, (x1+x2)/2.0f,(y1+y2)/2.0f, x2,y2, objspace_flatness_squared,n+1); -+ } else { -+ stbtt__add_point(points, *num_points,x2,y2); -+ *num_points = *num_points+1; -+ } -+ return 1; -+} -+ -+// returns number of contours -+stbtt__point *stbtt_FlattenCurves(stbtt_vertex *vertices, int num_verts, float objspace_flatness, int **contour_lengths, int *num_contours, void *userdata) -+{ -+ stbtt__point *points=0; -+ int num_points=0; -+ -+ float objspace_flatness_squared = objspace_flatness * objspace_flatness; -+ int i,n=0,start=0, pass; -+ -+ // count how many "moves" there are to get the contour count -+ for (i=0; i < num_verts; ++i) -+ if (vertices[i].type == STBTT_vmove) -+ ++n; -+ -+ *num_contours = n; -+ if (n == 0) return 0; -+ -+ *contour_lengths = (int *) STBTT_malloc(sizeof(**contour_lengths) * n, userdata); -+ -+ if (*contour_lengths == 0) { -+ *num_contours = 0; -+ return 0; -+ } -+ -+ // make two passes through the points so we don't need to realloc -+ for (pass=0; pass < 2; ++pass) { -+ float x=0,y=0; -+ if (pass == 1) { -+ points = (stbtt__point *) STBTT_malloc(num_points * sizeof(points[0]), userdata); -+ if (points == NULL) goto error; -+ } -+ num_points = 0; -+ n= -1; -+ for (i=0; i < num_verts; ++i) { -+ switch (vertices[i].type) { -+ case STBTT_vmove: -+ // start the next contour -+ if (n >= 0) -+ (*contour_lengths)[n] = num_points - start; -+ ++n; -+ start = num_points; -+ -+ x = vertices[i].x, y = vertices[i].y; -+ stbtt__add_point(points, num_points++, x,y); -+ break; -+ case STBTT_vline: -+ x = vertices[i].x, y = vertices[i].y; -+ stbtt__add_point(points, num_points++, x, y); -+ break; -+ case STBTT_vcurve: -+ stbtt__tesselate_curve(points, &num_points, x,y, -+ vertices[i].cx, vertices[i].cy, -+ vertices[i].x, vertices[i].y, -+ objspace_flatness_squared, 0); -+ x = vertices[i].x, y = vertices[i].y; -+ break; -+ } -+ } -+ (*contour_lengths)[n] = num_points - start; -+ } -+ -+ return points; -+error: -+ STBTT_free(points, userdata); -+ STBTT_free(*contour_lengths, userdata); -+ *contour_lengths = 0; -+ *num_contours = 0; -+ return NULL; -+} -+ -+void stbtt_Rasterize(stbtt__bitmap *result, float flatness_in_pixels, stbtt_vertex *vertices, int num_verts, float scale_x, float scale_y, int x_off, int y_off, int invert, void *userdata) -+{ -+ float scale = scale_x > scale_y ? scale_y : scale_x; -+ int winding_count, *winding_lengths; -+ stbtt__point *windings = stbtt_FlattenCurves(vertices, num_verts, flatness_in_pixels / scale, &winding_lengths, &winding_count, userdata); -+ if (windings) { -+ stbtt__rasterize(result, windings, winding_lengths, winding_count, scale_x, scale_y, x_off, y_off, invert, userdata); -+ STBTT_free(winding_lengths, userdata); -+ STBTT_free(windings, userdata); -+ } -+} -+ -+void stbtt_FreeBitmap(unsigned char *bitmap, void *userdata) -+{ -+ STBTT_free(bitmap, userdata); -+} -+ -+unsigned char *stbtt_GetGlyphBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int glyph, int *width, int *height, int *xoff, int *yoff) -+{ -+ int ix0,iy0,ix1,iy1; -+ stbtt__bitmap gbm; -+ stbtt_vertex *vertices; -+ int num_verts = stbtt_GetGlyphShape(info, glyph, &vertices); -+ -+ if (scale_x == 0) scale_x = scale_y; -+ if (scale_y == 0) { -+ if (scale_x == 0) return NULL; -+ scale_y = scale_x; -+ } -+ -+ stbtt_GetGlyphBitmapBox(info, glyph, scale_x, scale_y, &ix0,&iy0,&ix1,&iy1); -+ -+ // now we get the size -+ gbm.w = (ix1 - ix0); -+ gbm.h = (iy1 - iy0); -+ gbm.pixels = NULL; // in case we error -+ -+ if (width ) *width = gbm.w; -+ if (height) *height = gbm.h; -+ if (xoff ) *xoff = ix0; -+ if (yoff ) *yoff = iy0; -+ -+ if (gbm.w && gbm.h) { -+ gbm.pixels = (unsigned char *) STBTT_malloc(gbm.w * gbm.h, info->userdata); -+ if (gbm.pixels) { -+ gbm.stride = gbm.w; -+ -+ stbtt_Rasterize(&gbm, 0.35f, vertices, num_verts, scale_x, scale_y, ix0, iy0, 1, info->userdata); -+ } -+ } -+ STBTT_free(vertices, info->userdata); -+ return gbm.pixels; -+} -+ -+void stbtt_MakeGlyphBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int glyph) -+{ -+ int ix0,iy0; -+ stbtt_vertex *vertices; -+ int num_verts = stbtt_GetGlyphShape(info, glyph, &vertices); -+ stbtt__bitmap gbm; -+ -+ stbtt_GetGlyphBitmapBox(info, glyph, scale_x, scale_y, &ix0,&iy0,0,0); -+ gbm.pixels = output; -+ gbm.w = out_w; -+ gbm.h = out_h; -+ gbm.stride = out_stride; -+ -+ if (gbm.w && gbm.h) -+ stbtt_Rasterize(&gbm, 0.35f, vertices, num_verts, scale_x, scale_y, ix0,iy0, 1, info->userdata); -+ -+ STBTT_free(vertices, info->userdata); -+} -+ -+unsigned char *stbtt_GetCodepointBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int codepoint, int *width, int *height, int *xoff, int *yoff) -+{ -+ return stbtt_GetGlyphBitmap(info, scale_x, scale_y, stbtt_FindGlyphIndex(info,codepoint), width,height,xoff,yoff); -+} -+ -+void stbtt_MakeCodepointBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int codepoint) -+{ -+ stbtt_MakeGlyphBitmap(info, output, out_w, out_h, out_stride, scale_x, scale_y, stbtt_FindGlyphIndex(info,codepoint)); -+} -+ -+////////////////////////////////////////////////////////////////////////////// -+// -+// bitmap baking -+// -+// This is SUPER-SHITTY packing to keep source code small -+ -+extern int stbtt_BakeFontBitmap(const unsigned char *data, int offset, // font location (use offset=0 for plain .ttf) -+ float pixel_height, // height of font in pixels -+ unsigned char *pixels, int pw, int ph, // bitmap to be filled in -+ int first_char, int num_chars, // characters to bake -+ stbtt_bakedchar *chardata) -+{ -+ float scale; -+ int x,y,bottom_y, i; -+ stbtt_fontinfo f; -+ stbtt_InitFont(&f, data, offset); -+ STBTT_memset(pixels, 0, pw*ph); // background of 0 around pixels -+ x=y=1; -+ bottom_y = 1; -+ -+ scale = stbtt_ScaleForPixelHeight(&f, pixel_height); -+ -+ for (i=0; i < num_chars; ++i) { -+ int advance, lsb, x0,y0,x1,y1,gw,gh; -+ int g = stbtt_FindGlyphIndex(&f, first_char + i); -+ stbtt_GetGlyphHMetrics(&f, g, &advance, &lsb); -+ stbtt_GetGlyphBitmapBox(&f, g, scale,scale, &x0,&y0,&x1,&y1); -+ gw = x1-x0; -+ gh = y1-y0; -+ if (x + gw + 1 >= pw) -+ y = bottom_y, x = 1; // advance to next row -+ if (y + gh + 1 >= ph) // check if it fits vertically AFTER potentially moving to next row -+ return -i; -+ STBTT_assert(x+gw < pw); -+ STBTT_assert(y+gh < ph); -+ stbtt_MakeGlyphBitmap(&f, pixels+x+y*pw, gw,gh,pw, scale,scale, g); -+ chardata[i].x0 = (stbtt_int16) x; -+ chardata[i].y0 = (stbtt_int16) y; -+ chardata[i].x1 = (stbtt_int16) (x + gw); -+ chardata[i].y1 = (stbtt_int16) (y + gh); -+ chardata[i].xadvance = scale * advance; -+ chardata[i].xoff = (float) x0; -+ chardata[i].yoff = (float) y0; -+ x = x + gw + 2; -+ if (y+gh+2 > bottom_y) -+ bottom_y = y+gh+2; -+ } -+ return bottom_y; -+} -+ -+void stbtt_GetBakedQuad(stbtt_bakedchar *chardata, int pw, int ph, int char_index, float *xpos, float *ypos, stbtt_aligned_quad *q, int opengl_fillrule) -+{ -+ float d3d_bias = opengl_fillrule ? 0 : -0.5f; -+ float ipw = 1.0f / pw, iph = 1.0f / ph; -+ stbtt_bakedchar *b = chardata + char_index; -+ int round_x = STBTT_ifloor((*xpos + b->xoff) + 0.5); -+ int round_y = STBTT_ifloor((*ypos + b->yoff) + 0.5); -+ -+ q->x0 = round_x + d3d_bias; -+ q->y0 = round_y + d3d_bias; -+ q->x1 = round_x + b->x1 - b->x0 + d3d_bias; -+ q->y1 = round_y + b->y1 - b->y0 + d3d_bias; -+ -+ q->s0 = b->x0 * ipw; -+ q->t0 = b->y0 * ipw; -+ q->s1 = b->x1 * iph; -+ q->t1 = b->y1 * iph; -+ -+ *xpos += b->xadvance; -+} -+ -+////////////////////////////////////////////////////////////////////////////// -+// -+// font name matching -- recommended not to use this -+// -+ -+// check if a utf8 string contains a prefix which is the utf16 string; if so return length of matching utf8 string -+static stbtt_int32 stbtt__CompareUTF8toUTF16_bigendian_prefix(stbtt_uint8 *s1, stbtt_int32 len1, stbtt_uint8 *s2, stbtt_int32 len2) -+{ -+ stbtt_int32 i=0; -+ -+ // convert utf16 to utf8 and compare the results while converting -+ while (len2) { -+ stbtt_uint16 ch = s2[0]*256 + s2[1]; -+ if (ch < 0x80) { -+ if (i >= len1) return -1; -+ if (s1[i++] != ch) return -1; -+ } else if (ch < 0x800) { -+ if (i+1 >= len1) return -1; -+ if (s1[i++] != 0xc0 + (ch >> 6)) return -1; -+ if (s1[i++] != 0x80 + (ch & 0x3f)) return -1; -+ } else if (ch >= 0xd800 && ch < 0xdc00) { -+ stbtt_uint32 c; -+ stbtt_uint16 ch2 = s2[2]*256 + s2[3]; -+ if (i+3 >= len1) return -1; -+ c = ((ch - 0xd800) << 10) + (ch2 - 0xdc00) + 0x10000; -+ if (s1[i++] != 0xf0 + (c >> 18)) return -1; -+ if (s1[i++] != 0x80 + ((c >> 12) & 0x3f)) return -1; -+ if (s1[i++] != 0x80 + ((c >> 6) & 0x3f)) return -1; -+ if (s1[i++] != 0x80 + ((c ) & 0x3f)) return -1; -+ s2 += 2; // plus another 2 below -+ len2 -= 2; -+ } else if (ch >= 0xdc00 && ch < 0xe000) { -+ return -1; -+ } else { -+ if (i+2 >= len1) return -1; -+ if (s1[i++] != 0xe0 + (ch >> 12)) return -1; -+ if (s1[i++] != 0x80 + ((ch >> 6) & 0x3f)) return -1; -+ if (s1[i++] != 0x80 + ((ch ) & 0x3f)) return -1; -+ } -+ s2 += 2; -+ len2 -= 2; -+ } -+ return i; -+} -+ -+int stbtt_CompareUTF8toUTF16_bigendian(const char *s1, int len1, const char *s2, int len2) -+{ -+ return len1 == stbtt__CompareUTF8toUTF16_bigendian_prefix((stbtt_uint8*) s1, len1, (stbtt_uint8*) s2, len2); -+} -+ -+// returns results in whatever encoding you request... but note that 2-byte encodings -+// will be BIG-ENDIAN... use stbtt_CompareUTF8toUTF16_bigendian() to compare -+char *stbtt_GetFontNameString(const stbtt_fontinfo *font, int *length, int platformID, int encodingID, int languageID, int nameID) -+{ -+ stbtt_int32 i,count,stringOffset; -+ stbtt_uint8 *fc = font->data; -+ stbtt_uint32 offset = font->fontstart; -+ stbtt_uint32 nm = stbtt__find_table(fc, offset, "name"); -+ if (!nm) return NULL; -+ -+ count = ttUSHORT(fc+nm+2); -+ stringOffset = nm + ttUSHORT(fc+nm+4); -+ for (i=0; i < count; ++i) { -+ stbtt_uint32 loc = nm + 6 + 12 * i; -+ if (platformID == ttUSHORT(fc+loc+0) && encodingID == ttUSHORT(fc+loc+2) -+ && languageID == ttUSHORT(fc+loc+4) && nameID == ttUSHORT(fc+loc+6)) { -+ *length = ttUSHORT(fc+loc+8); -+ return (char *) (fc+stringOffset+ttUSHORT(fc+loc+10)); -+ } -+ } -+ return NULL; -+} -+ -+static int stbtt__matchpair(stbtt_uint8 *fc, stbtt_uint32 nm, stbtt_uint8 *name, stbtt_int32 nlen, stbtt_int32 target_id, stbtt_int32 next_id) -+{ -+ stbtt_int32 i; -+ stbtt_int32 count = ttUSHORT(fc+nm+2); -+ stbtt_int32 stringOffset = nm + ttUSHORT(fc+nm+4); -+ -+ for (i=0; i < count; ++i) { -+ stbtt_uint32 loc = nm + 6 + 12 * i; -+ stbtt_int32 id = ttUSHORT(fc+loc+6); -+ if (id == target_id) { -+ // find the encoding -+ stbtt_int32 platform = ttUSHORT(fc+loc+0), encoding = ttUSHORT(fc+loc+2), language = ttUSHORT(fc+loc+4); -+ -+ // is this a Unicode encoding? -+ if (platform == 0 || (platform == 3 && encoding == 1) || (platform == 3 && encoding == 10)) { -+ stbtt_int32 slen = ttUSHORT(fc+loc+8), off = ttUSHORT(fc+loc+10); -+ -+ // check if there's a prefix match -+ stbtt_int32 matchlen = stbtt__CompareUTF8toUTF16_bigendian_prefix(name, nlen, fc+stringOffset+off,slen); -+ if (matchlen >= 0) { -+ // check for target_id+1 immediately following, with same encoding & language -+ if (i+1 < count && ttUSHORT(fc+loc+12+6) == next_id && ttUSHORT(fc+loc+12) == platform && ttUSHORT(fc+loc+12+2) == encoding && ttUSHORT(fc+loc+12+4) == language) { -+ stbtt_int32 slen = ttUSHORT(fc+loc+12+8), off = ttUSHORT(fc+loc+12+10); -+ if (slen == 0) { -+ if (matchlen == nlen) -+ return 1; -+ } else if (matchlen < nlen && name[matchlen] == ' ') { -+ ++matchlen; -+ if (stbtt_CompareUTF8toUTF16_bigendian((char*) (name+matchlen), nlen-matchlen, (char*)(fc+stringOffset+off),slen)) -+ return 1; -+ } -+ } else { -+ // if nothing immediately following -+ if (matchlen == nlen) -+ return 1; -+ } -+ } -+ } -+ -+ // @TODO handle other encodings -+ } -+ } -+ return 0; -+} -+ -+static int stbtt__matches(stbtt_uint8 *fc, stbtt_uint32 offset, stbtt_uint8 *name, stbtt_int32 flags) -+{ -+ stbtt_int32 nlen = STBTT_strlen((char *) name); -+ stbtt_uint32 nm,hd; -+ if (!stbtt__isfont(fc+offset)) return 0; -+ -+ // check italics/bold/underline flags in macStyle... -+ if (flags) { -+ hd = stbtt__find_table(fc, offset, "head"); -+ if ((ttUSHORT(fc+hd+44) & 7) != (flags & 7)) return 0; -+ } -+ -+ nm = stbtt__find_table(fc, offset, "name"); -+ if (!nm) return 0; -+ -+ if (flags) { -+ // if we checked the macStyle flags, then just check the family and ignore the subfamily -+ if (stbtt__matchpair(fc, nm, name, nlen, 16, -1)) return 1; -+ if (stbtt__matchpair(fc, nm, name, nlen, 1, -1)) return 1; -+ if (stbtt__matchpair(fc, nm, name, nlen, 3, -1)) return 1; -+ } else { -+ if (stbtt__matchpair(fc, nm, name, nlen, 16, 17)) return 1; -+ if (stbtt__matchpair(fc, nm, name, nlen, 1, 2)) return 1; -+ if (stbtt__matchpair(fc, nm, name, nlen, 3, -1)) return 1; -+ } -+ -+ return 0; -+} -+ -+int stbtt_FindMatchingFont(const unsigned char *font_collection, const char *name_utf8, stbtt_int32 flags) -+{ -+ stbtt_int32 i; -+ for (i=0;;++i) { -+ stbtt_int32 off = stbtt_GetFontOffsetForIndex(font_collection, i); -+ if (off < 0) return off; -+ if (stbtt__matches((stbtt_uint8 *) font_collection, off, (stbtt_uint8*) name_utf8, flags)) -+ return off; -+ } -+} -+ -+#endif // STB_TRUETYPE_IMPLEMENTATION +-- +1.9.0.msysgit.0 + From 17fd20f50c09aaff6b6741353b8ab21bff8f12fd Mon Sep 17 00:00:00 2001 From: Aokromes Date: Wed, 25 Jun 2014 22:32:04 +0200 Subject: [PATCH 28/80] DB/Quest: Redemption Fix missing delete. --- sql/updates/world/2014_06_25_08_world_conditions.sql | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 sql/updates/world/2014_06_25_08_world_conditions.sql diff --git a/sql/updates/world/2014_06_25_08_world_conditions.sql b/sql/updates/world/2014_06_25_08_world_conditions.sql new file mode 100644 index 0000000000..e2e904ae06 --- /dev/null +++ b/sql/updates/world/2014_06_25_08_world_conditions.sql @@ -0,0 +1,8 @@ +DELETE FROM `conditions` WHERE `SourceTypeOrReferenceId`=13 AND `SourceEntry`=8593 OR `SourceTypeOrReferenceId`=17 AND `SourceEntry`=8593; +INSERT INTO `conditions` (`SourceTypeOrReferenceId`, `SourceGroup`, `SourceEntry`, `SourceId`, `ElseGroup`, `ConditionTypeOrReference`, `ConditionTarget`, `ConditionValue1`, `ConditionValue2`, `ConditionValue3`, `NegativeCondition`, `ErrorType`, `ErrorTextId`, `ScriptName`, `Comment`) VALUES +(17, 0, 8593, 0, 0, 29, 0, 17542, 40, 0, 0, 0, 0, '', 'Symbol of Life can be casted within 40 yards of Young Furbolg Shaman'), +(17, 0, 8593, 0, 1, 29, 0, 6172, 40, 0, 0, 0, 0, '', 'Symbol of Life can be casted within 40 yards Henze Faulk'), +(17, 0, 8593, 0, 2, 29, 0, 6177, 40, 0, 0, 0, 0, '', 'Symbol of Life can be casted within 40 yards Henze Faulk'), +(13, 1, 8593, 0, 0, 31, 0, 3, 17542, 0, 0, 0, 0, '', 'Symbol of Life can be casted on Young Furbolg Shaman'), +(13, 1, 8593, 0, 1, 31, 0, 3, 6172, 0, 0, 0, 0, '', 'Symbol of Life can be casted on Henze Faulk'), +(13, 1, 8593, 0, 2, 31, 0, 3, 6177, 0, 0, 0, 0, '', 'Symbol of Life can be casted on Narm Faulk'); From ec4b8da3b513f68139136f16af46f768df3b8259 Mon Sep 17 00:00:00 2001 From: Subv Date: Wed, 25 Jun 2014 18:39:57 -0500 Subject: [PATCH 29/80] Core/Items: Items shouldn't have a negative item level Closes #11631 --- src/server/game/Entities/Item/ItemPrototype.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/server/game/Entities/Item/ItemPrototype.h b/src/server/game/Entities/Item/ItemPrototype.h index cc477c5bd3..bdf956f892 100644 --- a/src/server/game/Entities/Item/ItemPrototype.h +++ b/src/server/game/Entities/Item/ItemPrototype.h @@ -734,7 +734,7 @@ struct ItemTemplate default: break; } - return itemLevel; + return std::max(0.f, itemLevel); } bool IsPotion() const { return Class == ITEM_CLASS_CONSUMABLE && SubClass == ITEM_SUBCLASS_POTION; } From 36414de688eec4211582574465a80f4f66b64aa7 Mon Sep 17 00:00:00 2001 From: Aokromes Date: Thu, 26 Jun 2014 06:38:28 +0200 Subject: [PATCH 30/80] DB/Quest: A Suitable Disguise Original Work by Discover-, fixes by Nastrand, zaphod77, foldy and, dr-j. Closes #3291 --- .../world/2014_06_26_00_world_misc.sql | 232 ++++++++++++++++++ 1 file changed, 232 insertions(+) create mode 100644 sql/updates/world/2014_06_26_00_world_misc.sql diff --git a/sql/updates/world/2014_06_26_00_world_misc.sql b/sql/updates/world/2014_06_26_00_world_misc.sql new file mode 100644 index 0000000000..b116a05979 --- /dev/null +++ b/sql/updates/world/2014_06_26_00_world_misc.sql @@ -0,0 +1,232 @@ +/* [Q] A Suitable Disguise + +SUMMARY: +Basically we are running a random script (4 in total) every 12 seconds after gossip select. Every script has a different subject. For example when we run script @ENTRY*100+1 it will require you to throw a bucket of water into the tub. If you don't do this on time, the event will fail and you'll have to start it over again. +The gossip flags are turned off on gossip select (@ENTRY*100+0) because we don't want the same event to be ran more than once at a time. +As you might have noticed we are using phases a LOT. This helps us to basically add conditions to some lines so they won't occur all the time, yet we can easily time them OOC. +We are also using Data Set event and action types a lot. As you can see when a bunny is hit by the spell which belongs to it, it will set data to Shandy which makes Shandy say something like 'Well done'. +The way the gameobjects are handled is maybe pretty much a work-around but it will need a LOT of core work to make this work like it works on Blizzlike. It should be different objects if you're either horde or alliance. Right now I've just spawned them on-top of each other, because in the end you can only use the object which belongs to your faction. + + +PROBLEMS: +Shandy Glossgleam should start a waypoint after completing script. (wtb sniffs) +Aquanos' evocation spell seems to have a bugged effect. + +RESOURCES: +http://www.youtube.com/watch?v=Fe4mRWO20Hw +http://www.youtube.com/watch?v=tqBbFr_BhC4 +http://www.youtube.com/watch?v=OFBRam2MgUw +http://www.wowwiki.com/Quest:A_Suitable_Disguise_(Alliance) + +PHASES: +Phase 0: when event did not start yet +Phase 1: when event is happening +Phase 2: when event failed +Phase 4: resetting everything */ + +-- Shandy Glossgleam SAI +SET @ENTRY := 36856; +SET @QUEST_A := 20438; +SET @QUEST_H := 24556; +SET @OGUID := 56249; +SET @GOSSIP := 10854; + +UPDATE `creature_template` SET `AIName`='SmartAI',`gossip_menu_id`=@GOSSIP WHERE `entry`=@ENTRY; + +DELETE FROM `smart_scripts` WHERE `entryorguid` IN (@ENTRY,@ENTRY*100+0,@ENTRY*100+1,@ENTRY*100+2,@ENTRY*100+3,@ENTRY*100+4,@ENTRY*100+5,@ENTRY*100+6,@ENTRY*100+7); +INSERT INTO `smart_scripts` (`entryorguid`,`source_type`,`id`,`link`,`event_type`,`event_phase_mask`,`event_chance`,`event_flags`,`event_param1`,`event_param2`,`event_param3`, `event_param4`,`action_type`,`action_param1`,`action_param2`,`action_param3`,`action_param4`,`action_param5`,`action_param6`, `target_type`,`target_param1`,`target_param2`,`target_param3`,`target_x`,`target_y`,`target_z`,`target_o`,`comment`) VALUES +(@ENTRY,0,0,0,62,0,100,0,@GOSSIP,0,0,0,80,@ENTRY*100+0,0,0,0,0,0,1,0,0,0,0,0,0,0,"Shandy Glossgleam - On Gossip Select - Run script"), +(@ENTRY,0,1,0,1,1,100,0,8000,8000,12000,12000,88,@ENTRY*100+1,@ENTRY*100+4,0,0,0,0,1,0,0,0,0,0,0,0,"Shandy Glossgleam - Timed - Run Random Script"), +(@ENTRY,0,2,0,38,1,100,0,1,1,0,0,80,@ENTRY*100+5,0,0,0,0,0,1,0,0,0,0,0,0,0,"Shandy Glossgleam - On Data Set 1 1 - Run Task Successful Script"), +(@ENTRY,0,3,0,1,1,100,0,120000,120000,120000,120000,80,@ENTRY*100+7,0,0,0,0,0,1,0,0,0,0,0,0,0,"Shandy Glossgleam - Timed - Run Complete Script"), +(@ENTRY,0,4,0,1,2,100,0,0,0,0,0,80,@ENTRY*100+6,0,0,0,0,0,1,0,0,0,0,0,0,0,"Shandy Glossgleam - Timed - Run Failure Script"), +(@ENTRY,0,5,6,1,4,100,0,30000,30000,30000,30000,81,3,0,0,0,0,0,1,0,0,0,0,0,0,0,"Shandy Glossgleam - Timed - Set Back Gossip & Quest Flags"), -- This can only occur in phase 4 so no need to time it (P4 is set in completion script) +(@ENTRY,0,6,7,61,4,100,0,0,0,0,0,22,0,0,0,0,0,0,1,0,0,0,0,0,0,0,"Shandy Glossgleam - Timed - Set Phase 0"), +(@ENTRY,0,7,0,61,4,100,0,0,0,0,0,78,0,0,0,0,0,0,1,0,0,0,0,0,0,0,"Shandy Glossgleam - Timed - Reset Scripts"), +(@ENTRY,0,8,0,40,0,100,0,16,@ENTRY,0,0,41,0,0,0,0,0,0,1,0,0,0,0,0,0,0,"Shandy Glossgleam - On Reached WP 16 - Despawn"), +(@ENTRY,0,9,0,62,0,100,0,@GOSSIP,1,0,0,80,@ENTRY*100+0,0,0,0,0,0,1,0,0,0,0,0,0,0,"Shandy Glossgleam - On Gossip Select - Run script"), + +-- Start script +(@ENTRY*100+0,9,0,0,0,0,100,0,0,0,0,0,72,0,0,0,0,0,0,7,0,0,0,0,0,0,0,"Shandy Glossgleam - Script 0 - Close Gossip"), +(@ENTRY*100+0,9,1,0,0,0,100,0,0,0,0,0,81,0,0,0,0,0,0,1,0,0,0,0,0,0,0,"Shandy Glossgleam - Script 0 - Turn of Gossip & Questgiver flags"), +(@ENTRY*100+0,9,2,0,0,0,100,0,1000,1000,0,0,1,0,0,0,0,0,0,1,0,0,0,0,0,0,0,"Shandy Glossgleam - Script 0 - Say Line 0"), +(@ENTRY*100+0,9,3,0,0,0,100,0,5000,5000,0,0,1,1,0,0,0,0,0,1,0,0,0,0,0,0,0,"Shandy Glossgleam - Script 0 - Say Line 1"), +(@ENTRY*100+0,9,4,0,0,0,100,0,0,0,0,0,22,1,0,0,0,0,0,1,0,0,0,0,0,0,0,"Shandy Glossgleam - Script 0 - Set Phase 1"), +-- Wants Water +(@ENTRY*100+1,9,0,0,0,0,100,0,0,0,0,0,1,4,0,0,0,0,0,1,0,0,0,0,0,0,0,"Shandy Glossgleam - Script 1 - Say Line 4"), +(@ENTRY*100+1,9,1,0,0,0,100,0,0,0,0,0,12,36947,1,10000,0,0,0,8,0,0,0,5796.970215,693.942993,658.351990,0,"Shandy Glossgleam - Script 1 - Summon Wants Water"), +(@ENTRY*100+1,9,2,0,0,0,100,0,10000,10000,0,0,22,2,0,0,0,0,0,1,0,0,0,0,0,0,0,"Shandy Glossgleam - Script 1 - Set Phase 2"), +-- Wants Pants +(@ENTRY*100+2,9,0,0,0,0,100,0,0,0,0,0,1,5,0,0,0,0,0,1,0,0,0,0,0,0,0,"Shandy Glossgleam - Script 2 - Say Line 5"), +(@ENTRY*100+2,9,1,0,0,0,100,0,0,0,0,0,12,36945,1,10000,0,0,0,8,0,0,0,5796.970215,693.942993,658.351990,0,"Shandy Glossgleam - Script 2 - Summon Wants Pants"), +(@ENTRY*100+2,9,2,0,0,0,100,0,10000,10000,0,0,22,2,0,0,0,0,0,1,0,0,0,0,0,0,0,"Shandy Glossgleam - Script 2 - Set Phase 2"), +-- Wants Unmentionables +(@ENTRY*100+3,9,0,0,0,0,100,0,0,0,0,0,1,3,0,0,0,0,0,1,0,0,0,0,0,0,0,"Shandy Glossgleam - Script 3 - Say Line 3"), +(@ENTRY*100+3,9,1,0,0,0,100,0,0,0,0,0,12,36946,1,10000,0,0,0,8,0,0,0,5796.970215,693.942993,658.351990,0,"Shandy Glossgleam - Script 3 - Summon Wants Unmentionables"), +(@ENTRY*100+3,9,2,0,0,0,100,0,10000,10000,0,0,22,2,0,0,0,0,0,1,0,0,0,0,0,0,0,"Shandy Glossgleam - Script 3 - Set Phase 2"), +-- Wants Shirts +(@ENTRY*100+4,9,0,0,0,0,100,0,0,0,0,0,1,2,0,0,0,0,0,1,0,0,0,0,0,0,0,"Shandy Glossgleam - Script 4 - Say Line 2"), +(@ENTRY*100+4,9,1,0,0,0,100,0,0,0,0,0,12,36944,1,10000,0,0,0,8,0,0,0,5796.970215,693.942993,658.351990,0,"Shandy Glossgleam - Script 4 - Summon Wants Shirts"), +(@ENTRY*100+4,9,2,0,0,0,100,0,10000,10000,0,0,22,2,0,0,0,0,0,1,0,0,0,0,0,0,0,"Shandy Glossgleam - Script 4 - Set Phase 2"), + +-- Task successful +(@ENTRY*100+5,9,0,0,0,0,100,0,0,0,0,0,1,6,0,0,0,0,0,1,0,0,0,0,0,0,0,"Shandy Glossgleam - Script 6 - Say Line 6 (random)"), +-- End failure +(@ENTRY*100+6,9,0,0,0,0,100,0,0,0,0,0,1,7,0,0,0,0,0,1,0,0,0,0,0,0,0,"Shandy Glossgleam - Script 7 - Say Line 7"), +(@ENTRY*100+6,9,1,0,0,0,100,0,0,0,0,0,22,0,0,0,0,0,0,1,0,0,0,0,0,0,0,"Shandy Glossgleam - Script 7 - Set Phase 0"), +(@ENTRY*100+6,9,2,0,0,0,100,0,0,0,0,0,81,3,0,0,0,0,0,1,0,0,0,0,0,0,0,"Shandy Glossgleam - Script 7 - Set Gossip Flag"), +-- Completion script +(@ENTRY*100+7,9,0,0,0,0,100,0,0,0,0,0,78,0,0,0,0,0,0,1,0,0,0,0,0,0,0,"Shandy Glossgleam - Script 8 - Stop Current Scripts"), +(@ENTRY*100+7,9,1,0,0,0,100,0,2000,2000,0,0,1,8,0,0,0,0,0,1,0,0,0,0,0,0,0,"Shandy Glossgleam - Script 8 - Say Line 8"), +(@ENTRY*100+7,9,2,0,0,0,100,0,0,0,0,0,66,0,0,0,0,0,0,19,36851,0,0,0,0,0,0,"Shandy Glossgleam - Script 8 - Face Aquanos"), +(@ENTRY*100+7,9,3,0,0,0,100,0,8000,8000,0,0,1,9,0,0,0,0,0,1,0,0,0,0,0,0,0,"Shandy Glossgleam - Script 8 - Say Line 9"), +(@ENTRY*100+7,9,4,0,0,0,100,0,0,0,0,0,50,201384,30000,0,0,0,0,8,0,0,0,5797.147461,696.602417,657.949463,6.090852,"Shandy Glossgleam - Script 8 - Summon Clean Laundry"), +(@ENTRY*100+7,9,5,0,0,0,100,0,3000,3000,0,0,53,0,@ENTRY,0,0,0,0,1,0,0,0,0,0,0,0,"Shandy Glossgleam - Script 8 - Set Phase 4"); + +-- Texts +-- They all emote ONESHOT_TALK apart from request texts (as seen in videos) +DELETE FROM `creature_text` WHERE `entry`=@ENTRY; +INSERT INTO `creature_text` (`entry`,`groupid`,`id`,`text`,`type`,`language`,`probability`,`emote`,`duration`,`sound`,`comment`,`BroadcastTextID`) VALUES +-- Start +(@ENTRY,0,0,"You're in luck. I've got just what you need in the load I'm about to wash.",12,0,100,0,0,0,"Shandy Glossgleam",36761), +(@ENTRY,1,0,"See the piles of laundry and the bucket of water? I'll run out what I need next, and you put it in the tub. Ready?",12,0,100,0,0,0,"Shandy Glossgleam",36762), +-- Requests +(@ENTRY,2,0,"Quick, add some shirts to the laundry!",12,0,100,0,0,0,"Shandy Glossgleam",36788), -- Emote ONESHOT_POINT +(@ENTRY,3,0,"Add the unmentionables... uh, I mean, the 'delicates'!",12,0,100,25,0,0,"Shandy Glossgleam",36789), -- Emote ONESHOT_POINT +(@ENTRY,4,0,"The tub needs more water!",12,0,100,25,0,0,"Shandy Glossgleam",36790), -- Emote ONESHOT_POINT +(@ENTRY,5,0,"Toss some pants in to the tub!",12,0,100,25,0,0,"Shandy Glossgleam",36787), -- Emote ONESHOT_POINT +-- Correct +(@ENTRY,6,0,"I should keep you around.",12,0,100,0,0,0,"Shandy Glossgleam",37989), +(@ENTRY,6,1,"Well done!",12,0,100,0,0,0,"Shandy Glossgleam",36764), +(@ENTRY,6,2,"That's how it's done!",12,0,100,0,0,0,"Shandy Glossgleam",37986), +(@ENTRY,6,3,"Clean and tidy!",12,0,100,0,0,0,"Shandy Glossgleam",37985), +(@ENTRY,6,4,"Nice. I don't want to know what that stain was.",12,0,100,0,0,0,"Shandy Glossgleam",37987), +(@ENTRY,6,5,"Aquanos can hardly keep up!",12,0,40,0,0,0,"Shandy Glossgleam",37988), +-- Incorrect +(@ENTRY,7,0,"Oh, no! That wasn't right. Now I'll have to go get more detergent so we can start over!",12,0,100,0,0,0,"Shandy Glossgleam",36763), +-- End +(@ENTRY,8,0,"Aquanos, stop sending the clothes so high! You didn't have to see the look on Aethas Sunreaver's face when he found his pants in the fountain!",12,0,100,0,0,0,"Shandy Glossgleam",36817), +(@ENTRY,9,0,"See how easy that was with everyone working together? Just take what you need from the clean laundry here, but don't forget to return it when you're finished.",12,0,100,0,0,0,"Shandy Glossgleam",36791); + +-- Give Clean Laundry quest item loot +DELETE FROM `gameobject_loot_template` WHERE `entry`=27725; +INSERT INTO `gameobject_loot_template` (`entry`,`item`,`ChanceOrQuestChance`,`lootmode`,`groupid`,`mincountOrRef`,`maxcount`) VALUES +(27725,49648,-100,1,0,1,1); + +-- Summon quest gameobjects +DELETE FROM `gameobject` WHERE `id` IN (201295,201931,201301,201296,201300,201932,201936,201933,201299,201855,201298,201297,201934,201935) AND `guid` BETWEEN @OGUID AND @OGUID+13; +INSERT INTO `gameobject` (`guid`,`id`,`map`,`spawnMask`,`phaseMask`,`position_x`,`position_y`,`position_z`,`orientation`,`rotation0`,`rotation1`,`rotation2`,`rotation3`,`spawntimesecs`,`animprogress`,`state`) VALUES +-- Game objects for Alliance quest 20438 +-- Water Bucket +(@OGUID, 201298, 571, 1, 1, 5807.094, 690.5868, 659.1125, 0, 0, 0, 0, 1, 120, 255, 1), -- 201298 (Area: 4613) +-- Pants +(@OGUID+1, 201295, 571, 1, 1, 5806.047, 694.5938, 657.949, 1.710422, 0, 0, 0, 1, 120, 255, 1), -- 201295 (Area: 4613) +(@OGUID+2, 201931, 571, 1, 1, 5806.068, 694.5799, 658.4567, 0, 0, 0, 0, 1, 120, 255, 1), -- 201931 (Area: 4613) +-- Shirts +(@OGUID+3, 201296, 571, 1, 1, 5805.592, 691.0816, 657.9492, 0, 0, 0, 0, 1, 120, 255, 1), -- 201296 (Area: 4613) +(@OGUID+4, 201932, 571, 1, 1, 5805.556, 691.1024, 658.3948, 0, 0, 0, 0, 1, 120, 255, 1), -- 201932 (Area: 4613) +-- Unmentionalables +(@OGUID+5, 201933, 571, 1, 1, 5805.316, 697.5625, 658.3061, 0, 0, 0, 0, 1, 120, 255, 1), -- 201933 (Area: 4613) +(@OGUID+6, 201297, 571, 1, 1, 5805.338, 697.5729, 657.9791, 1.640607, 0, 0, 0, 1, 120, 255, 1), -- 201297 (Area: 4613) +-- Game objects for Horde quest 24556 +-- Water Bucket +(@OGUID+7, 201855, 571, 1, 1, 5807.094, 690.5868, 659.1125, 0, 0, 0, 0, 1, 120, 255, 1), -- 201855 (Area: 4613) +-- Pants +(@OGUID+8, 201301, 571, 1, 1, 5806.068, 694.5799, 658.4575, 0, 0, 0, 0, 1, 120, 255, 1), -- 201301 (Area: 4613) +(@OGUID+9, 201934, 571, 1, 1, 5806.015, 694.6285, 657.949, 1.710422, 0, 0, 0, 1, 120, 255, 1), -- 201934 (Area: 4613) +-- Shirts +(@OGUID+10, 201300, 571, 1, 1, 5805.579, 691.0868, 658.3615, 0, 0, 0, 0, 1, 120, 255, 1), -- 201300 (Area: 4613) +(@OGUID+11, 201935, 571, 1, 1, 5805.587, 691.0608, 657.9492, 0, 0, 0, 0, 1, 120, 255, 1), -- 201935 (Area: 4613) +-- Unmentionalables +(@OGUID+12, 201936, 571, 1, 1, 5805.326, 697.5555, 657.949, 1.640607, 0, 0, 0, 1, 120, 255, 1), -- 201936 (Area: 4613) +(@OGUID+13, 201299, 571, 1, 1, 5805.316, 697.5625, 658.3046, 0, 0, 0, 0, 1, 120, 255, 1); -- 201299 (Area: 4613) + +-- Insert option menu +DELETE FROM `gossip_menu_option` WHERE `menu_id`=@GOSSIP; +INSERT INTO `gossip_menu_option` (`menu_id`,`id`,`option_icon`,`option_text`,`option_id`,`npc_option_npcflag`,`BoxBroadcastTextID`) VALUES +(@GOSSIP,0,0,'Arcanist Tybalin said you might be able to lend me a certain tabard.',1,1,36760), +(@GOSSIP,1,0,'Magister Hathorel said you might be able to lend me a certain tabard.',1,1,37552); + +-- Add conditions for gossip - as you see we are using ElseGroup (logical 'OR') +DELETE FROM `conditions` WHERE `SourceGroup` IN (@GOSSIP) AND `ConditionValue1` IN (@QUEST_A,@QUEST_H); +INSERT INTO `conditions` (`SourceTypeOrReferenceId`,`SourceGroup`,`SourceEntry`,`ElseGroup`,`ConditionTypeOrReference`,`ConditionValue1`,`ConditionValue2`,`ConditionValue3`,`ErrorTextId`,`ScriptName`,`Comment`) VALUES +(15,@GOSSIP,0,0,9,@QUEST_A,0,0,0,'',"Only show first gossip if player is on quest A Suitable Disguise (A)"), +(15,@GOSSIP,1,0,9,@QUEST_H,0,0,0,'',"Only show first gossip if player is on quest A Suitable Disguise (H)"); + +-- Aquanos SAI +SET @ENTRY := 36851; +SET @SPELL_EVOCATION := 69659; +UPDATE `creature_template` SET `AIName`='SmartAI' WHERE `entry`=@ENTRY; +DELETE FROM `smart_scripts` WHERE `entryorguid`=@ENTRY; +INSERT INTO `smart_scripts` (`entryorguid`,`source_type`,`id`,`link`,`event_type`,`event_phase_mask`,`event_chance`,`event_flags`,`event_param1`,`event_param2`,`event_param3`, `event_param4`,`action_type`,`action_param1`,`action_param2`,`action_param3`,`action_param4`,`action_param5`,`action_param6`, `target_type`,`target_param1`,`target_param2`,`target_param3`,`target_x`,`target_y`,`target_z`,`target_o`,`comment`) VALUES +(@ENTRY,0,0,0,38,0,100,0,2,2,0,0,11,@SPELL_EVOCATION,1,0,0,0,0,1,0,0,0,0,0,0,0,"Aquanos - On Data Set 2 2 - Cast Evocation (Visual Only)"); + +-- Wants Water SAI +SET @ENTRY := 36947; +SET @SPELL_WATER := 69614; +UPDATE `creature_template` SET `AIName`='SmartAI',`minlevel`=70,`maxlevel`=70,`exp`=2,`unit_class`=2,`unit_flags`=`unit_flags`|33554432,`flags_extra`=`flags_extra`|128 WHERE `entry`=@ENTRY; +DELETE FROM `smart_scripts` WHERE `entryorguid`=@ENTRY; +INSERT INTO `smart_scripts` (`entryorguid`,`source_type`,`id`,`link`,`event_type`,`event_phase_mask`,`event_chance`,`event_flags`,`event_param1`,`event_param2`,`event_param3`, `event_param4`,`action_type`,`action_param1`,`action_param2`,`action_param3`,`action_param4`,`action_param5`,`action_param6`, `target_type`,`target_param1`,`target_param2`,`target_param3`,`target_x`,`target_y`,`target_z`,`target_o`,`comment`) VALUES +(@ENTRY,0,0,1,8,0,100,1,@SPELL_WATER,0,0,0,45,1,1,0,0,0,0,19,36856,0,0,0,0,0,0,"Wants Water - On Spellhit - Set Data Shandy Glossgleam"), +(@ENTRY,0,1,0,61,0,100,0,0,0,0,0,45,2,2,0,0,0,0,19,36851,0,0,0,0,0,0,"Wants Water - On Spellhit - Set Data Aquanos"); + +-- Wants Pants SAI +SET @ENTRY := 36945; +SET @SPELL_PANTS := 69600; +UPDATE `creature_template` SET `AIName`='SmartAI',`minlevel`=70,`maxlevel`=70,`exp`=2,`unit_class`=2,`unit_flags`=`unit_flags`|33554432,`flags_extra`=`flags_extra`|128 WHERE `entry`=@ENTRY; +DELETE FROM `smart_scripts` WHERE `entryorguid`=@ENTRY; +INSERT INTO `smart_scripts` (`entryorguid`,`source_type`,`id`,`link`,`event_type`,`event_phase_mask`,`event_chance`,`event_flags`,`event_param1`,`event_param2`,`event_param3`, `event_param4`,`action_type`,`action_param1`,`action_param2`,`action_param3`,`action_param4`,`action_param5`,`action_param6`, `target_type`,`target_param1`,`target_param2`,`target_param3`,`target_x`,`target_y`,`target_z`,`target_o`,`comment`) VALUES +(@ENTRY,0,0,1,8,0,100,1,@SPELL_PANTS,0,0,0,45,1,1,0,0,0,0,19,36856,0,0,0,0,0,0,"Wants Pants - On Spellhit - Set Data Shandy Glossgleam"), +(@ENTRY,0,1,0,61,0,100,0,0,0,0,0,45,2,2,0,0,0,0,19,36851,0,0,0,0,0,0,"Wants Pants - On Spellhit - Set Data Aquanos"); + +-- Wants Unmentionables SAI +SET @ENTRY := 36946; +SET @SPELL_UNMENTIONABLES := 69601; +UPDATE `creature_template` SET `AIName`='SmartAI',`minlevel`=70,`maxlevel`=70,`exp`=2,`unit_class`=2,`unit_flags`=`unit_flags`|33554432,`flags_extra`=`flags_extra`|128 WHERE `entry`=@ENTRY; +DELETE FROM `smart_scripts` WHERE `entryorguid`=@ENTRY; +INSERT INTO `smart_scripts` (`entryorguid`,`source_type`,`id`,`link`,`event_type`,`event_phase_mask`,`event_chance`,`event_flags`,`event_param1`,`event_param2`,`event_param3`, `event_param4`,`action_type`,`action_param1`,`action_param2`,`action_param3`,`action_param4`,`action_param5`,`action_param6`, `target_type`,`target_param1`,`target_param2`,`target_param3`,`target_x`,`target_y`,`target_z`,`target_o`,`comment`) VALUES +(@ENTRY,0,0,1,8,0,100,1,@SPELL_UNMENTIONABLES,0,0,0,45,1,1,0,0,0,0,19,36856,0,0,0,0,0,0,"Wants Unmentionables - On Spellhit - Set Data Shandy Glossgleam"), +(@ENTRY,0,1,0,61,0,100,0,0,0,0,0,45,2,2,0,0,0,0,19,36851,0,0,0,0,0,0,"Wants Unmentionables - On Spellhit - Set Data Aquanos"); + +-- Wants Shirts SAI +SET @ENTRY := 36944; +SET @SPELL_SHIRT := 69593; +UPDATE `creature_template` SET `AIName`='SmartAI',`minlevel`=70,`maxlevel`=70,`exp`=2,`unit_class`=2,`unit_flags`=`unit_flags`|33554432,`flags_extra`=`flags_extra`|128 WHERE `entry`=@ENTRY; +DELETE FROM `smart_scripts` WHERE `entryorguid`=@ENTRY; +INSERT INTO `smart_scripts` (`entryorguid`,`source_type`,`id`,`link`,`event_type`,`event_phase_mask`,`event_chance`,`event_flags`,`event_param1`,`event_param2`,`event_param3`, `event_param4`,`action_type`,`action_param1`,`action_param2`,`action_param3`,`action_param4`,`action_param5`,`action_param6`, `target_type`,`target_param1`,`target_param2`,`target_param3`,`target_x`,`target_y`,`target_z`,`target_o`,`comment`) VALUES +(@ENTRY,0,0,1,8,0,100,1,@SPELL_SHIRT,0,0,0,45,1,1,0,0,0,0,19,36856,0,0,0,0,0,0,"Wants Shirts - On Spellhit - Set Data Shandy Glossgleam"), +(@ENTRY,0,1,0,61,0,100,0,0,0,0,0,45,2,2,0,0,0,0,19,36851,0,0,0,0,0,0,"Wants Shirts - On Spellhit - Set Data Aquanos"); + +-- The conditions are made this way because the PLAYER should throw the 'item'. (water, shirt, pant, etc.) Basically when you click the object it casts a trigger spell on you. This trigger spell can only target players and will make the player cast 'Toss XX', which has direct conditions to one of the Wants XX imps. +DELETE FROM `conditions` WHERE `SourceTypeOrReferenceId`=13 AND `SourceEntry` IN (69593,69600,69601,69614,69548,69542,69544,69543); +INSERT INTO `conditions` (`SourceTypeOrReferenceId`,`SourceGroup`,`SourceEntry`,`ConditionTypeOrReference`,`ConditionValue1`,`ConditionValue2`,`comment`) VALUES +(13,1,69593,31,3,36944, 'Toss Shirts requires target Wants Shirts'), +(13,1,69600,31,3,36945, 'Toss Pants requires target Wants Pants'), +(13,1,69601,31,3,36946, 'Toss Unmentionables requires target Wants Unmentionables'), +(13,1,69614,31,3,36947, 'Toss Water requires target Wants Water'), +-- other conditions are unneeded, and having them causes problems. +-- These are erroring still, but the quest works with this change: +(13,1,69548,32,128,0,'Trigger Throw Water requires target player'), +(13,1,69542,32,128,0,'Trigger Throw Pants requires target player'), +(13,1,69544,32,128,0,'Trigger Throw Unmentionables requires target player'), +(13,1,69543,32,128,0,'Trigger Throw Shirt requires target player'); + +-- Waypoints +DELETE FROM `waypoints` WHERE `entry`=36856; +INSERT INTO `waypoints` (`entry`, `pointid`, `position_x`, `position_y`, `position_z`, `point_comment`) VALUES +(36856, 1, 5800.057, 691.5624, 658.0007, 'Shandy Glossgleam WP 1'), +(36856, 2, 5802.057, 691.5624, 658.0007, 'Shandy Glossgleam WP 2'), +(36856, 3, 5802.307, 690.0624, 658.0007, 'Shandy Glossgleam WP 3'), +(36856, 4, 5802.557, 688.8124, 658.0007, 'Shandy Glossgleam WP 4'), +(36856, 5, 5802.824, 688.4631, 657.9935, 'Shandy Glossgleam WP 5'), +(36856, 6, 5803.074, 686.4631, 658.2435, 'Shandy Glossgleam WP 6'), +(36856, 7, 5803.574, 681.9631, 658.2435, 'Shandy Glossgleam WP 7'), +(36856, 8, 5804.324, 677.7131, 658.2435, 'Shandy Glossgleam WP 8'), +(36856, 9, 5804.842, 673.8136, 658.0798, 'Shandy Glossgleam WP 9'), +(36856, 10, 5802.092, 671.8136, 658.3298, 'Shandy Glossgleam WP 10'), +(36856, 11, 5801.806, 671.6563, 658.1652, 'Shandy Glossgleam WP 11'), +(36856, 12, 5801.306, 671.1563, 658.1652, 'Shandy Glossgleam WP 12'), +(36856, 13, 5800.306, 671.9063, 658.4152, 'Shandy Glossgleam WP 13'), +(36856, 14, 5799.306, 672.9063, 657.4152, 'Shandy Glossgleam WP 14'), +(36856, 15, 5798.056, 673.4063, 656.4152, 'Shandy Glossgleam WP 15'), +(36856, 16, 5795.953, 674.7413, 654.7663, 'Shandy Glossgleam WP 16 - Despawn'); From 4368747de62fc42ba8545cd2b92536936355a045 Mon Sep 17 00:00:00 2001 From: Aokromes Date: Thu, 26 Jun 2014 11:15:06 +0200 Subject: [PATCH 31/80] DB/Spawns: Add multiple Brewfest missing things By Discover-, updates #3619 --- .../world/2014_06_26_01_world_misc.sql | 102 ++++++++++++++++++ 1 file changed, 102 insertions(+) create mode 100644 sql/updates/world/2014_06_26_01_world_misc.sql diff --git a/sql/updates/world/2014_06_26_01_world_misc.sql b/sql/updates/world/2014_06_26_01_world_misc.sql new file mode 100644 index 0000000000..aea1adbf3c --- /dev/null +++ b/sql/updates/world/2014_06_26_01_world_misc.sql @@ -0,0 +1,102 @@ +-- A lot of missing spawns. Objects and creatures +SET @CGUID := 134649; -- 34 needed +SET @OGUID := 59171; -- 21 needed +SET @EVENT := 24; +-- Berfest Reveler +DELETE FROM `creature` WHERE `guid` BETWEEN @CGUID+0 AND @CGUID+33; +INSERT INTO `creature` (`guid`,`id`,`map`,`spawnMask`,`phaseMask`,`modelid`,`equipment_id`,`position_x`,`position_y`,`position_z`,`orientation`,`spawntimesecs`,`spawndist`,`currentwaypoint`,`curhealth`,`curmana`,`MovementType`,`npcflag`,`unit_flags`,`dynamicflags`) VALUES +-- Barleybrew Apprentice +(@CGUID+0,23482,530,1,1,22395,0,-4016.6,-11833,0.106771,4.04165,300,0,0,2215,0,0,0,0,0), +-- Brewfest Reveler - Exodar +(@CGUID+1,24484,530,1,1,21843,86,-4004.32,-11859.4,0.177081,0.860784,300,0,0,2215,0,0,0,0,0), +(@CGUID+2,24484,530,1,1,21844,86,-4001.38,-11855.2,0.211436,4.18773,300,0,0,2215,0,0,0,0,0), +(@CGUID+3,24484,530,1,1,21843,86,-4010.47,-11852.8,0.118782,4.25763,300,0,0,2215,0,0,0,0,0), +(@CGUID+4,24484,530,1,1,21842,86,-4009.89,-11854.7,0.121793,3.64502,300,0,0,2215,0,0,0,0,0), +(@CGUID+5,24484,530,1,1,21844,86,-4012.09,-11839.5,0.131949,0.507355,300,0,0,2215,0,0,0,0,0), +(@CGUID+6,24484,530,1,1,21845,86,-4005.04,-11840.8,0.203372,2.26665,300,0,0,2215,0,0,0,0,0), +(@CGUID+7,24484,530,1,1,21842,86,-4017.34,-11851.2,0.047381,1.00608,300,0,0,2215,0,0,0,0,0), +(@CGUID+8,24484,530,1,1,21845,86,-4013.17,-11842.1,0.114192,4.16731,300,0,0,2215,0,0,0,0,0), +(@CGUID+9,24484,530,1,1,21844,86,-4012.1,-11847.4,0.111304,2.65935,300,0,0,2215,0,0,0,0,0), +(@CGUID+10,24484,530,1,1,21843,86,-4017.5,-11845,0.0595054,5.9266,300,0,0,2215,0,0,0,0,0), +-- Brewfest Reveler - Eversong Woods +(@CGUID+11,24484,530,1,1,21841,86,9318.1,-7278.19,13.3383,0.709729,300,0,0,2215,0,0,0,0,0), +(@CGUID+12,24484,530,1,1,21838,86,9318.04,-7274.55,13.3571,5.13466,300,0,0,2215,0,0,0,0,0), +(@CGUID+13,24484,530,1,1,21840,86,9322.69,-7278.57,13.2085,3.03844,300,0,0,2215,0,0,0,0,0), +(@CGUID+14,24484,530,1,1,21839,86,9323.46,-7274.86,13.2653,3.31333,300,0,0,2215,0,0,0,0,0), +(@CGUID+15,24484,530,1,1,21840,86,9333.88,-7275.82,13.514,5.39384,300,0,0,2215,0,0,0,0,0), +(@CGUID+16,24484,530,1,1,21841,86,9335.93,-7281.15,13.695,2.03234,300,0,0,2215,0,0,0,0,0), +(@CGUID+17,24484,530,1,1,21838,86,9340.67,-7275.46,14.0555,4.97837,300,0,0,2215,0,0,0,0,0), +(@CGUID+18,24484,530,1,1,21839,86,9343.15,-7280.24,14.3778,2.35828,300,0,0,2215,0,0,0,0,0), +(@CGUID+19,24484,530,1,1,21840,86,9334.47,-7296.27,13.7759,3.58271,300,0,0,2215,0,0,0,0,0), +(@CGUID+20,24484,530,1,1,21839,86,9329.98,-7295.05,13.7486,5.6758,300,0,0,2215,0,0,0,0,0), +(@CGUID+21,24484,530,1,1,21841,86,9340.93,-7269.42,14.5058,0.837748,300,0,0,2215,0,0,0,0,0), +-- Troll Commoner +(@CGUID+22,19177,1,1,1,21876,0,1748.94,-4497.53,28.793,5.47104,300,0,0,42,0,0,0,0,0), +(@CGUID+23,19177,1,1,1,21877,0,1752.58,-4499.43,28.2911,2.89886,300,0,0,42,0,0,0,0,0), +-- Orc Commoner +(@CGUID+24,19175,1,1,1,21868,0,1609.47,-4323.21,2.33176,3.34811,300,0,0,42,0,0,0,0,0), +(@CGUID+25,19175,1,1,1,21869,0,1605.95,-4324.2,2.19915,0.0101724,300,0,0,42,0,0,0,0,0), +-- Gnome Commoner +(@CGUID+26,19172,0,1,1,21850,0,-4831.74,-1172.58,502.278,6.19592,2,0,0,42,0,0,3,0,0), +(@CGUID+27,19172,0,1,1,21853,0,-4829.88,-1173.25,502.277,2.49582,2,0,0,42,0,0,3,0,0), +-- Bok Dropcertain +(@CGUID+28,24527,1,1,1,0,0,795.14,-4522.59,6.31052,5.20636,300,0,0,1004,0,0,0,0,0), +-- Drohn's Distillery Apprentice +(@CGUID+29,24501,530,1,1,22491,0,9333.7,-7269.67,13.7715,4.52047,300,0,0,2215,0,0,0,0,0), +-- Gordok Brew Apprentice +(@CGUID+30,23511,530,1,1,0,0,-4020.13,-11855.6,0.0061873,1.0273,300,0,0,2215,0,0,0,0,0), +(@CGUID+31,23511,530,1,1,0,0,9313.83,-7281.3,13.8523,0.903714,300,0,0,2215,0,0,0,0,0), +-- T'chali's Voodoo Brewery Apprentice +(@CGUID+32,23533,530,1,1,22493,0,9330.11,-7285.87,13.7137,1.37103,300,0,0,2215,0,0,0,0,0), +-- Thunderbrew Apprentice +(@CGUID+33,23510,530,1,1,0,0,-4021.3,-11842.9,0.0212859,5.9439,300,0,0,2215,0,0,0,0,0); + +-- Make the above spawns only happen if Brewfest is active +DELETE FROM `game_event_creature` WHERE `guid` BETWEEN @CGUID+0 AND @CGUID+33; +INSERT INTO `game_event_creature` (`eventEntry`,`guid`) VALUES +(@EVENT,@CGUID+0),(@EVENT,@CGUID+1),(@EVENT,@CGUID+2),(@EVENT,@CGUID+3), +(@EVENT,@CGUID+4),(@EVENT,@CGUID+5),(@EVENT,@CGUID+6),(@EVENT,@CGUID+7), +(@EVENT,@CGUID+8),(@EVENT,@CGUID+9),(@EVENT,@CGUID+10),(@EVENT,@CGUID+11), +(@EVENT,@CGUID+12),(@EVENT,@CGUID+13),(@EVENT,@CGUID+14),(@EVENT,@CGUID+15), +(@EVENT,@CGUID+16),(@EVENT,@CGUID+17),(@EVENT,@CGUID+18),(@EVENT,@CGUID+19), +(@EVENT,@CGUID+20),(@EVENT,@CGUID+21),(@EVENT,@CGUID+22),(@EVENT,@CGUID+23), +(@EVENT,@CGUID+24),(@EVENT,@CGUID+25),(@EVENT,@CGUID+26),(@EVENT,@CGUID+27), +(@EVENT,@CGUID+28),(@EVENT,@CGUID+29),(@EVENT,@CGUID+30),(@EVENT,@CGUID+31), +(@EVENT,@CGUID+32),(@EVENT,@CGUID+33); + +DELETE FROM `gameobject` WHERE `guid` BETWEEN @OGUID+0 AND @OGUID+20; +INSERT INTO `gameobject` (`guid`,`id`,`map`,`spawnMask`,`phaseMask`,`position_x`,`position_y`,`position_z`,`orientation`,`rotation0`,`rotation1`,`rotation2`,`rotation3`,`spawntimesecs`,`animprogress`,`state`) VALUES +-- Exodar +(@OGUID+0,178666,530,1,1,-4015.97,-11829.2,0.146814,2.80549,0,0,0.985913,0.167262,300,0,1), +(@OGUID+1,178666,530,1,1,-4024.94,-11840.7,-0.0137537,4.17208,0,0,0.870173,-0.492747,300,0,1), +(@OGUID+2,186259,530,1,1,-4029.42,-11849.1,-0.0817791,3.05053,0,0,0.998964,0.0455147,300,0,1), +(@OGUID+3,186260,530,1,1,-4008.1,-11835.5,0.186614,1.27632,0,0,0.595718,0.803194,300,0,1), +(@OGUID+4,186257,530,1,1,-4016.1,-11859.2,0.0441619,4.26476,0,0,0.846413,-0.532527,300,0,1), +(@OGUID+5,186221,530,1,1,-4008.42,-11850.7,0.144533,2.04601,0,0,0.853677,0.520803,300,0,1), +(@OGUID+6,186717,530,1,1,-4008.29,-11857.1,0.135722,5.47893,0,0,0.391378,-0.92023,300,0,1), +(@OGUID+7,186717,530,1,1,-4002.56,-11851,0.205429,5.13728,0,0,0.542116,-0.840304,300,0,1), +-- Darnassus +(@OGUID+8,186717,1,1,1,9962.87,2218.32,1329.82,1.77101,0,0,0.774236,0.632897,300,0,1), +(@OGUID+9,186717,1,1,1,9963.64,2192.59,1328.52,4.74374,0,0,0.695937,-0.718103,300,0,1), +-- Eversong Woods +(@OGUID+10,178666,530,1,1,9333.24,-7265.71,13.7149,3.15074,0,0,0.99999,-0.00457361,300,0,1), +(@OGUID+11,178666,530,1,1,9330.88,-7289.79,13.7388,0.18586,0,0,0.0927961,0.995685,300,0,1), +(@OGUID+12,186717,530,1,1,9319.23,-7265.19,13.7578,2.02761,0,0,0.84885,0.528633,300,0,1), +(@OGUID+13,186717,530,1,1,9310.25,-7291.41,14.4908,2.53027,0,0,0.953648,0.300923,300,0,1), +(@OGUID+14,186221,530,1,1,9318.17,-7291.17,14.4236,0.649241,0,0,0.318949,0.947772,300,0,1), +(@OGUID+15,186257,530,1,1,9318.61,-7289.66,14.436,0.782759,0,0,0.381464,0.924384,300,0,1), +(@OGUID+16,186256,530,1,1,9341.19,-7288.21,14.3979,4.71368,0,0,0.70665,-0.707563,300,0,1), +(@OGUID+17,186255,530,1,1,9343.07,-7265.55,14.691,1.48569,0,0,0.676387,0.736546,300,0,1), +(@OGUID+18,180037,530,1,1,9316.77,-7283.36,13.7084,3.63768,0,0,0.969394,-0.24551,300,0,1), +(@OGUID+19,180037,530,1,1,9313.62,-7285.06,13.9541,3.38636,0,0,0.992521,-0.122077,300,0,1), +(@OGUID+20,180037,530,1,1,9308.97,-7280.39,14.447,5.40091,0,0,0.426971,-0.904265,300,0,1); + +-- Make the above game objects only spawn if Brewfest is active +DELETE FROM `game_event_gameobject` WHERE `guid` BETWEEN @OGUID+0 AND @OGUID+20; +INSERT INTO `game_event_gameobject` (`eventEntry`,`guid`) VALUES +(@EVENT,@OGUID+0),(@EVENT,@OGUID+1),(@EVENT,@OGUID+2),(@EVENT,@OGUID+3), +(@EVENT,@OGUID+4),(@EVENT,@OGUID+5),(@EVENT,@OGUID+6),(@EVENT,@OGUID+7), +(@EVENT,@OGUID+8),(@EVENT,@OGUID+9),(@EVENT,@OGUID+10),(@EVENT,@OGUID+11), +(@EVENT,@OGUID+12),(@EVENT,@OGUID+13),(@EVENT,@OGUID+14),(@EVENT,@OGUID+15), +(@EVENT,@OGUID+16),(@EVENT,@OGUID+17),(@EVENT,@OGUID+18),(@EVENT,@OGUID+19), +(@EVENT,@OGUID+20); From 224b5c08dfecd3cd9981fc89bb96ae0e38b68889 Mon Sep 17 00:00:00 2001 From: Shauren Date: Thu, 26 Jun 2014 17:39:09 +0200 Subject: [PATCH 32/80] Core/Spells: Fixed professions resetting back to 1 when learning new level (expert/artisan) --- src/server/game/Spells/SpellEffects.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/server/game/Spells/SpellEffects.cpp b/src/server/game/Spells/SpellEffects.cpp index 1240d8af7a..73d0fb7565 100644 --- a/src/server/game/Spells/SpellEffects.cpp +++ b/src/server/game/Spells/SpellEffects.cpp @@ -2617,7 +2617,7 @@ void Spell::EffectLearnSkill(SpellEffIndex effIndex) return; uint16 skillval = unitTarget->ToPlayer()->GetPureSkillValue(skillid); - unitTarget->ToPlayer()->SetSkill(skillid, m_spellInfo->Effects[effIndex].CalcValue(), std::min(skillval, 1), tier->MaxSkill[damage - 1]); + unitTarget->ToPlayer()->SetSkill(skillid, m_spellInfo->Effects[effIndex].CalcValue(), std::max(skillval, 1), tier->MaxSkill[damage - 1]); } void Spell::EffectAddHonor(SpellEffIndex /*effIndex*/) From 74850abcfc9e18a665e7d6fa5942e5b7ccc813ca Mon Sep 17 00:00:00 2001 From: Shauren Date: Thu, 26 Jun 2014 22:01:55 +0200 Subject: [PATCH 33/80] Core/Loot: Skinning improvements * Set skinnable flag only after all loot was taken * Creatures are skinnable only once - after skinning, the player only has to loot the creature again if he did not take all skinning loot --- .../game/Entities/Creature/Creature.cpp | 45 ++++++++----------- src/server/game/Entities/Creature/Creature.h | 3 -- src/server/game/Entities/Player/Player.cpp | 29 ++++++------ src/server/game/Entities/Unit/Unit.cpp | 12 ++--- src/server/game/Handlers/LootHandler.cpp | 9 ++-- src/server/game/Loot/LootMgr.h | 3 ++ src/server/game/Spells/Spell.cpp | 6 +-- src/server/game/Spells/SpellEffects.cpp | 18 ++------ 8 files changed, 51 insertions(+), 74 deletions(-) diff --git a/src/server/game/Entities/Creature/Creature.cpp b/src/server/game/Entities/Creature/Creature.cpp index 84393070c7..9b2cd01380 100644 --- a/src/server/game/Entities/Creature/Creature.cpp +++ b/src/server/game/Entities/Creature/Creature.cpp @@ -142,8 +142,8 @@ bool ForcedDespawnDelayEvent::Execute(uint64 /*e_time*/, uint32 /*p_time*/) } Creature::Creature(bool isWorldObject): Unit(isWorldObject), MapObject(), -lootForPickPocketed(false), _pickpocketLootRestore(0), lootForBody(false), lootForSkinned(false), _skinner(0), m_groupLootTimer(0), lootingGroupLowGUID(0), -m_PlayerDamageReq(0), m_lootRecipient(0), m_lootRecipientGroup(0), m_corpseRemoveTime(0), m_respawnTime(0), +_pickpocketLootRestore(0), m_groupLootTimer(0), lootingGroupLowGUID(0), m_PlayerDamageReq(0), +m_lootRecipient(0), m_lootRecipientGroup(0), _skinner(0), m_corpseRemoveTime(0), m_respawnTime(0), m_respawnDelay(300), m_corpseDelay(60), m_respawnradius(0.0f), m_reactState(REACT_AGGRESSIVE), m_defaultMovementType(IDLE_MOTION_TYPE), m_DBTableGuid(0), m_equipmentId(0), m_originalEquipmentId(0), m_AlreadyCallAssistance(false), m_AlreadySearchedAssistance(false), m_regenHealth(true), m_AI_locked(false), m_meleeDamageSchoolMask(SPELL_SCHOOL_MASK_NORMAL), @@ -550,9 +550,9 @@ void Creature::Update(uint32 diff) time_t now = time(NULL); // Check if we should refill the pickpocketing loot - if (lootForPickPocketed && _pickpocketLootRestore && _pickpocketLootRestore <= now) + if (loot.loot_type == LOOT_PICKPOCKETING && _pickpocketLootRestore && _pickpocketLootRestore <= now) { - lootForPickPocketed = false; + loot.clear(); _pickpocketLootRestore = 0; } @@ -1472,11 +1472,6 @@ void Creature::setDeathState(DeathState s) setActive(false); - if (!IsPet() && GetCreatureTemplate()->SkinLootId) - if (LootTemplates_Skinning.HaveLootFor(GetCreatureTemplate()->SkinLootId)) - if (hasLootRecipient()) - SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_SKINNABLE); - if (HasSearchedAssistance()) { SetNoSearchAssistance(false); @@ -1537,9 +1532,7 @@ void Creature::Respawn(bool force) GetName().c_str(), GetGUIDLow(), GetGUID(), GetEntry()); m_respawnTime = 0; _pickpocketLootRestore = 0; - lootForPickPocketed = false; - lootForBody = false; - lootForSkinned = false; + loot.clear(); if (m_originalEntry != GetEntry()) UpdateEntry(m_originalEntry); @@ -2278,23 +2271,23 @@ void Creature::GetRespawnPosition(float &x, float &y, float &z, float* ori, floa void Creature::AllLootRemovedFromCorpse() { - if (!HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_SKINNABLE)) - { - time_t now = time(NULL); - if (m_corpseRemoveTime <= now) - return; + if (loot.loot_type != LOOT_SKINNING && !IsPet() && GetCreatureTemplate()->SkinLootId && hasLootRecipient()) + if (LootTemplates_Skinning.HaveLootFor(GetCreatureTemplate()->SkinLootId)) + SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_SKINNABLE); - float decayRate = sWorld->getRate(RATE_CORPSE_DECAY_LOOTED); - CreatureTemplate const* cinfo = GetCreatureTemplate(); + time_t now = time(NULL); + if (m_corpseRemoveTime <= now) + return; - // corpse skinnable, but without skinning flag, and then skinned, corpse will despawn next update - if (cinfo && cinfo->SkinLootId) - m_corpseRemoveTime = time(NULL); - else - m_corpseRemoveTime = now + m_corpseDelay * decayRate; + float decayRate = sWorld->getRate(RATE_CORPSE_DECAY_LOOTED); - m_respawnTime = m_corpseRemoveTime + m_respawnTime; - } + // corpse skinnable, but without skinning flag, and then skinned, corpse will despawn next update + if (loot.loot_type == LOOT_SKINNING) + m_corpseRemoveTime = time(NULL); + else + m_corpseRemoveTime = now + m_corpseDelay * decayRate; + + m_respawnTime = m_corpseRemoveTime + m_respawnTime; } uint8 Creature::getLevelForTarget(WorldObject const* target) const diff --git a/src/server/game/Entities/Creature/Creature.h b/src/server/game/Entities/Creature/Creature.h index 2360d04c45..ca536e44e4 100644 --- a/src/server/game/Entities/Creature/Creature.h +++ b/src/server/game/Entities/Creature/Creature.h @@ -551,9 +551,6 @@ class Creature : public Unit, public GridObject, public MapObject virtual void DeleteFromDB(); // overriden in Pet Loot loot; - bool lootForPickPocketed; - bool lootForBody; - bool lootForSkinned; void StartPickPocketRefillTimer(); void ResetPickPocketRefillTimer() { _pickpocketLootRestore = 0; } void SetSkinner(uint64 guid) { _skinner = guid; } diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index fb4d4c3dda..8d0223d224 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -8969,9 +8969,8 @@ void Player::SendLoot(uint64 guid, LootType loot_type) if (loot_type == LOOT_PICKPOCKETING) { - if (!creature->lootForPickPocketed) + if (loot->loot_type != LOOT_PICKPOCKETING) { - creature->lootForPickPocketed = true; creature->StartPickPocketRefillTimer(); loot->clear(); @@ -8992,12 +8991,9 @@ void Player::SendLoot(uint64 guid, LootType loot_type) if (!recipient) return; - if (!creature->lootForBody) + if (loot->loot_type == LOOT_NONE) { - creature->lootForBody = true; - // for creature, loot is filled when creature is killed. - if (Group* group = recipient->GetGroup()) { switch (group->GetLootMethod()) @@ -9018,15 +9014,17 @@ void Player::SendLoot(uint64 guid, LootType loot_type) } } - // possible only if creature->lootForBody && loot->empty() at spell cast check - if (loot_type == LOOT_SKINNING) + // if loot is already skinning loot then don't do anything else + if (loot->loot_type == LOOT_SKINNING) { - if (!creature->lootForSkinned) - { - creature->lootForSkinned = true; - loot->clear(); - loot->FillLoot(creature->GetCreatureTemplate()->SkinLootId, LootTemplates_Skinning, this, true); - } + loot_type = LOOT_SKINNING; + permission = creature->GetSkinner() == GetGUID() ? OWNER_PERMISSION : NONE_PERMISSION; + } + else if (loot_type == LOOT_SKINNING) + { + loot->clear(); + loot->FillLoot(creature->GetCreatureTemplate()->SkinLootId, LootTemplates_Skinning, this, true); + creature->SetSkinner(GetGUID()); permission = OWNER_PERMISSION; } // set group rights only for loot_type != LOOT_SKINNING @@ -17835,6 +17833,9 @@ bool Player::isAllowedToLoot(const Creature* creature) if (loot->isLooted()) // nothing to loot or everything looted. return false; + if (loot->loot_type == LOOT_SKINNING) + return creature->GetSkinner() == GetGUID(); + Group* thisGroup = GetGroup(); if (!thisGroup) return this == creature->GetLootRecipient(); diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp index d0cc8d771f..363738f96e 100644 --- a/src/server/game/Entities/Unit/Unit.cpp +++ b/src/server/game/Entities/Unit/Unit.cpp @@ -15271,11 +15271,8 @@ void Unit::Kill(Unit* victim, bool durabilityLoss) if (creature) { Loot* loot = &creature->loot; - if (creature->lootForPickPocketed) - { + if (creature->loot.loot_type == LOOT_PICKPOCKETING) creature->ResetPickPocketRefillTimer(); - creature->lootForPickPocketed = false; - } loot->clear(); if (uint32 lootid = creature->GetCreatureTemplate()->lootid) @@ -15394,9 +15391,12 @@ void Unit::Kill(Unit* victim, bool durabilityLoss) if (!creature->IsPet()) { creature->DeleteThreatList(); - CreatureTemplate const* cInfo = creature->GetCreatureTemplate(); - if (cInfo && (cInfo->lootid || cInfo->maxgold > 0)) + + // must be after setDeathState which resets dynamic flags + if (!creature->loot.empty()) creature->SetFlag(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_LOOTABLE); + else + creature->AllLootRemovedFromCorpse(); } // Call KilledUnit for creatures, this needs to be called after the lootable flag is set diff --git a/src/server/game/Handlers/LootHandler.cpp b/src/server/game/Handlers/LootHandler.cpp index 2464a5385a..b9c6f349ac 100644 --- a/src/server/game/Handlers/LootHandler.cpp +++ b/src/server/game/Handlers/LootHandler.cpp @@ -81,7 +81,7 @@ void WorldSession::HandleAutostoreLootItemOpcode(WorldPacket& recvData) { Creature* creature = GetPlayer()->GetMap()->GetCreature(lguid); - bool lootAllowed = creature && creature->IsAlive() == (player->getClass() == CLASS_ROGUE && creature->lootForPickPocketed); + bool lootAllowed = creature && creature->IsAlive() == (player->getClass() == CLASS_ROGUE && creature->loot.loot_type == LOOT_PICKPOCKETING); if (!lootAllowed || !creature->IsWithinDistInMap(_player, INTERACTION_DISTANCE)) { @@ -148,7 +148,7 @@ void WorldSession::HandleLootMoneyOpcode(WorldPacket& /*recvData*/) case HIGHGUID_VEHICLE: { Creature* creature = player->GetMap()->GetCreature(guid); - bool lootAllowed = creature && creature->IsAlive() == (player->getClass() == CLASS_ROGUE && creature->lootForPickPocketed); + bool lootAllowed = creature && creature->IsAlive() == (player->getClass() == CLASS_ROGUE && creature->loot.loot_type == LOOT_PICKPOCKETING); if (lootAllowed && creature->IsWithinDistInMap(player, INTERACTION_DISTANCE)) { loot = &creature->loot; @@ -348,7 +348,7 @@ void WorldSession::DoLootRelease(uint64 lguid) { Creature* creature = GetPlayer()->GetMap()->GetCreature(lguid); - bool lootAllowed = creature && creature->IsAlive() == (player->getClass() == CLASS_ROGUE && creature->lootForPickPocketed); + bool lootAllowed = creature && creature->IsAlive() == (player->getClass() == CLASS_ROGUE && creature->loot.loot_type == LOOT_PICKPOCKETING); if (!lootAllowed || !creature->IsWithinDistInMap(_player, INTERACTION_DISTANCE)) return; @@ -356,9 +356,6 @@ void WorldSession::DoLootRelease(uint64 lguid) if (loot->isLooted()) { creature->RemoveFlag(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_LOOTABLE); - - if (loot->loot_type == LOOT_SKINNING) - creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_SKINNABLE); // skip pickpocketing loot for speed, skinning timer reduction is no-op in fact if (!creature->IsAlive()) diff --git a/src/server/game/Loot/LootMgr.h b/src/server/game/Loot/LootMgr.h index 9be745e622..cb3b9082c2 100644 --- a/src/server/game/Loot/LootMgr.h +++ b/src/server/game/Loot/LootMgr.h @@ -76,6 +76,8 @@ enum PermissionTypes enum LootType { + LOOT_NONE = 0, + LOOT_CORPSE = 1, LOOT_PICKPOCKETING = 2, LOOT_FISHING = 3, @@ -341,6 +343,7 @@ struct Loot gold = 0; unlootedCount = 0; roundRobinPlayer = 0; + loot_type = LOOT_NONE; i_LootValidatorRefManager.clearReferences(); } diff --git a/src/server/game/Spells/Spell.cpp b/src/server/game/Spells/Spell.cpp index f9a5e742f9..48c2a76578 100644 --- a/src/server/game/Spells/Spell.cpp +++ b/src/server/game/Spells/Spell.cpp @@ -5058,13 +5058,11 @@ SpellCastResult Spell::CheckCast(bool strict) return SPELL_FAILED_TARGET_UNSKINNABLE; Creature* creature = m_targets.GetUnitTarget()->ToCreature(); - if (creature->GetCreatureType() != CREATURE_TYPE_CRITTER && creature->loot.loot_type != LOOT_SKINNING && !creature->loot.isLooted()) + if (creature->GetCreatureType() != CREATURE_TYPE_CRITTER && !creature->loot.isLooted()) return SPELL_FAILED_TARGET_NOT_LOOTED; uint32 skill = creature->GetCreatureTemplate()->GetRequiredLootSkill(); - bool alreadySkinned = creature->loot.loot_type == LOOT_SKINNING && creature->GetSkinner() == m_caster->GetGUID(); - int32 skillValue = m_caster->ToPlayer()->GetSkillValue(skill); int32 TargetLevel = m_targets.GetUnitTarget()->getLevel(); int32 ReqValue = (skillValue < 100 ? (TargetLevel-10) * 10 : TargetLevel * 5); @@ -5074,7 +5072,7 @@ SpellCastResult Spell::CheckCast(bool strict) // chance for fail at orange skinning attempt if ((m_selfContainer && (*m_selfContainer) == this) && skillValue < sWorld->GetConfigMaxSkillValue() && - (ReqValue < 0 ? 0 : ReqValue) > irand(skillValue - 25, skillValue + 37) && !alreadySkinned) + (ReqValue < 0 ? 0 : ReqValue) > irand(skillValue - 25, skillValue + 37)) return SPELL_FAILED_TRY_AGAIN; break; diff --git a/src/server/game/Spells/SpellEffects.cpp b/src/server/game/Spells/SpellEffects.cpp index 73d0fb7565..606851bf8f 100644 --- a/src/server/game/Spells/SpellEffects.cpp +++ b/src/server/game/Spells/SpellEffects.cpp @@ -4710,28 +4710,16 @@ void Spell::EffectSkinning(SpellEffIndex /*effIndex*/) uint32 skill = creature->GetCreatureTemplate()->GetRequiredLootSkill(); - bool awardPoints = true; - - // Check if a skinning loot table was already generated for this creature - if (creature->loot.loot_type == LOOT_SKINNING) - { - if (creature->GetSkinner() != m_caster->GetGUID()) - return; - - awardPoints = false; // Do not grant skill points for this loot, they were already granted the first time. - } - else - creature->SetSkinner(m_caster->GetGUID()); - m_caster->ToPlayer()->SendLoot(creature->GetGUID(), LOOT_SKINNING); + creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_SKINNABLE); + creature->SetFlag(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_LOOTABLE); int32 reqValue = targetLevel < 10 ? 0 : targetLevel < 20 ? (targetLevel-10)*10 : targetLevel*5; int32 skillValue = m_caster->ToPlayer()->GetPureSkillValue(skill); // Double chances for elites - if (awardPoints) - m_caster->ToPlayer()->UpdateGatherSkill(skill, skillValue, reqValue, creature->isElite() ? 2 : 1); + m_caster->ToPlayer()->UpdateGatherSkill(skill, skillValue, reqValue, creature->isElite() ? 2 : 1); } void Spell::EffectCharge(SpellEffIndex /*effIndex*/) From 6ebb64892fa4cbfacd3f3de5dfa1fe36b02f848c Mon Sep 17 00:00:00 2001 From: MitchesD Date: Thu, 26 Jun 2014 21:33:40 +0200 Subject: [PATCH 34/80] Scripts/MagistersTerrace: reworked InstanceScript and fixed some issues on boss Vexallus * removed old hacks and added missing auras * Vexallus converted to EventMap --- .../world/2014_06_26_02_world_misc.sql | 7 + .../boss_felblood_kaelthas.cpp | 8 +- .../boss_priestess_delrissa.cpp | 8 +- .../MagistersTerrace/boss_selin_fireheart.cpp | 8 +- .../MagistersTerrace/boss_vexallus.cpp | 278 +++++------ .../instance_magisters_terrace.cpp | 452 ++++++++---------- .../MagistersTerrace/magisters_terrace.h | 38 +- 7 files changed, 364 insertions(+), 435 deletions(-) create mode 100644 sql/updates/world/2014_06_26_02_world_misc.sql diff --git a/sql/updates/world/2014_06_26_02_world_misc.sql b/sql/updates/world/2014_06_26_02_world_misc.sql new file mode 100644 index 0000000000..dade11c549 --- /dev/null +++ b/sql/updates/world/2014_06_26_02_world_misc.sql @@ -0,0 +1,7 @@ +-- Pure Energy +SET @ENTRY := 24745; +UPDATE `creature_template` SET `flags_extra`=0 WHERE `entry`=@ENTRY; + +DELETE FROM `creature_template_addon` WHERE `entry`=@ENTRY; +INSERT INTO `creature_template_addon` (`entry`, `mount`, `bytes1`, `bytes2`, `auras`) VALUES +(@ENTRY, 0, 0x0, 0x0, '44326 46156'); diff --git a/src/server/scripts/EasternKingdoms/MagistersTerrace/boss_felblood_kaelthas.cpp b/src/server/scripts/EasternKingdoms/MagistersTerrace/boss_felblood_kaelthas.cpp index 3c4b372808..7944011c7a 100644 --- a/src/server/scripts/EasternKingdoms/MagistersTerrace/boss_felblood_kaelthas.cpp +++ b/src/server/scripts/EasternKingdoms/MagistersTerrace/boss_felblood_kaelthas.cpp @@ -150,14 +150,14 @@ public: Phase = 0; - instance->SetData(DATA_KAELTHAS_EVENT, NOT_STARTED); + instance->SetBossState(DATA_KAELTHAS, NOT_STARTED); } void JustDied(Unit* /*killer*/) override { Talk(SAY_DEATH); - instance->SetData(DATA_KAELTHAS_EVENT, DONE); + instance->SetBossState(DATA_KAELTHAS, DONE); // Enable the Translocation Orb Exit if (GameObject* escapeOrb = ObjectAccessor::GetGameObject(*me, instance->GetData64(DATA_ESCAPE_ORB))) @@ -172,7 +172,7 @@ public: void EnterCombat(Unit* /*who*/) override { - instance->SetData(DATA_KAELTHAS_EVENT, IN_PROGRESS); + instance->SetBossState(DATA_KAELTHAS, IN_PROGRESS); } void MoveInLineOfSight(Unit* who) override @@ -515,7 +515,7 @@ public: return; } //Don't really die in all phases of Kael'Thas - if (instance->GetData(DATA_KAELTHAS_EVENT) == 0) + if (instance->GetBossState(DATA_KAELTHAS) == 0) { //prevent death damage = 0; diff --git a/src/server/scripts/EasternKingdoms/MagistersTerrace/boss_priestess_delrissa.cpp b/src/server/scripts/EasternKingdoms/MagistersTerrace/boss_priestess_delrissa.cpp index 8dc8ff799b..2af9d9b156 100644 --- a/src/server/scripts/EasternKingdoms/MagistersTerrace/boss_priestess_delrissa.cpp +++ b/src/server/scripts/EasternKingdoms/MagistersTerrace/boss_priestess_delrissa.cpp @@ -153,7 +153,7 @@ public: //this mean she at some point evaded void JustReachedHome() override { - instance->SetData(DATA_DELRISSA_EVENT, FAIL); + instance->SetBossState(DATA_DELRISSA, FAIL); } void EnterCombat(Unit* who) override @@ -172,7 +172,7 @@ public: } } - instance->SetData(DATA_DELRISSA_EVENT, IN_PROGRESS); + instance->SetBossState(DATA_DELRISSA, IN_PROGRESS); } void InitializeLackeys() @@ -240,7 +240,7 @@ public: Talk(SAY_DEATH); if (instance->GetData(DATA_DELRISSA_DEATH_COUNT) == MAX_ACTIVE_LACKEY) - instance->SetData(DATA_DELRISSA_EVENT, DONE); + instance->SetBossState(DATA_DELRISSA, DONE); else { if (me->HasFlag(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_LOOTABLE)) @@ -434,7 +434,7 @@ struct boss_priestess_lackey_commonAI : public ScriptedAI if (!pDelrissa->HasFlag(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_LOOTABLE)) pDelrissa->SetFlag(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_LOOTABLE); - instance->SetData(DATA_DELRISSA_EVENT, DONE); + instance->SetBossState(DATA_DELRISSA, DONE); } } } diff --git a/src/server/scripts/EasternKingdoms/MagistersTerrace/boss_selin_fireheart.cpp b/src/server/scripts/EasternKingdoms/MagistersTerrace/boss_selin_fireheart.cpp index d77f5db9ce..d6c0f95f96 100644 --- a/src/server/scripts/EasternKingdoms/MagistersTerrace/boss_selin_fireheart.cpp +++ b/src/server/scripts/EasternKingdoms/MagistersTerrace/boss_selin_fireheart.cpp @@ -119,7 +119,7 @@ public: } // Set Inst data for encounter - instance->SetData(DATA_SELIN_EVENT, NOT_STARTED); + instance->SetBossState(DATA_SELIN, NOT_STARTED); DrainLifeTimer = urand(3000, 7000); DrainManaTimer = DrainLifeTimer + 5000; @@ -194,7 +194,7 @@ public: void EnterCombat(Unit* /*who*/) override { Talk(SAY_AGGRO); - instance->SetData(DATA_SELIN_EVENT, IN_PROGRESS); + instance->SetBossState(DATA_SELIN, IN_PROGRESS); } void KilledUnit(Unit* /*victim*/) override @@ -228,7 +228,7 @@ public: { Talk(SAY_DEATH); - instance->SetData(DATA_SELIN_EVENT, DONE); // Encounter complete! + instance->SetBossState(DATA_SELIN, DONE); // Encounter complete! ShatterRemainingCrystals(); } @@ -351,7 +351,7 @@ public: } } } - } else TC_LOG_ERROR("scripts", ERROR_INST_DATA); + } } }; }; diff --git a/src/server/scripts/EasternKingdoms/MagistersTerrace/boss_vexallus.cpp b/src/server/scripts/EasternKingdoms/MagistersTerrace/boss_vexallus.cpp index 58b9ef1209..644f686024 100644 --- a/src/server/scripts/EasternKingdoms/MagistersTerrace/boss_vexallus.cpp +++ b/src/server/scripts/EasternKingdoms/MagistersTerrace/boss_vexallus.cpp @@ -1,6 +1,5 @@ /* * Copyright (C) 2008-2014 TrinityCore - * Copyright (C) 2006-2009 ScriptDev2 * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the @@ -16,13 +15,6 @@ * with this program. If not, see . */ -/* ScriptData -SDName: Boss_Vexallus -SD%Complete: 90 -SDComment: Heroic and Normal support. Needs further testing. -SDCategory: Magister's Terrace -EndScriptData */ - #include "ScriptMgr.h" #include "ScriptedCreature.h" #include "magisters_terrace.h" @@ -41,26 +33,22 @@ enum Yells enum Spells { - // Pure energy spell info - SPELL_ENERGY_BOLT = 46156, - SPELL_ENERGY_FEEDBACK = 44335, - - // Vexallus spell info SPELL_CHAIN_LIGHTNING = 44318, - SPELL_H_CHAIN_LIGHTNING = 46380, // heroic spell SPELL_OVERLOAD = 44353, SPELL_ARCANE_SHOCK = 44319, - SPELL_H_ARCANE_SHOCK = 46381, // heroic spell SPELL_SUMMON_PURE_ENERGY = 44322, // mod scale -10 H_SPELL_SUMMON_PURE_ENERGY1 = 46154, // mod scale -5 H_SPELL_SUMMON_PURE_ENERGY2 = 46159 // mod scale -5 - }; -enum Creatures +enum Events { - NPC_PURE_ENERGY = 24745, + EVENT_ENERGY_BOLT = 1, + EVENT_ENERGY_FEEDBACK, + EVENT_CHAIN_LIGHTNING, + EVENT_OVERLOAD, + EVENT_ARCANE_SHOCK }; enum Misc @@ -71,170 +59,160 @@ enum Misc class boss_vexallus : public CreatureScript { -public: - boss_vexallus() : CreatureScript("boss_vexallus") { } + public: + boss_vexallus() : CreatureScript("boss_vexallus") { } - CreatureAI* GetAI(Creature* creature) const override - { - return GetInstanceAI(creature); - }; - - struct boss_vexallusAI : public BossAI - { - boss_vexallusAI(Creature* creature) : BossAI(creature, DATA_VEXALLUS_EVENT) + struct boss_vexallusAI : public BossAI { - instance = creature->GetInstanceScript(); - } - - InstanceScript* instance; - - uint32 ChainLightningTimer; - uint32 ArcaneShockTimer; - uint32 OverloadTimer; - uint32 IntervalHealthAmount; - bool Enraged; - - void Reset() override - { - summons.DespawnAll(); - ChainLightningTimer = 8000; - ArcaneShockTimer = 5000; - OverloadTimer = 1200; - IntervalHealthAmount = 1; - Enraged = false; - - instance->SetData(DATA_VEXALLUS_EVENT, NOT_STARTED); - } - - void KilledUnit(Unit* /*victim*/) override - { - Talk(SAY_KILL); - } - - void JustDied(Unit* /*killer*/) override - { - summons.DespawnAll(); - instance->SetData(DATA_VEXALLUS_EVENT, DONE); - } - - void EnterCombat(Unit* /*who*/) override - { - Talk(SAY_AGGRO); - - instance->SetData(DATA_VEXALLUS_EVENT, IN_PROGRESS); - } - - void JustSummoned(Creature* summoned) override - { - if (Unit* temp = SelectTarget(SELECT_TARGET_RANDOM, 0)) - summoned->GetMotionMaster()->MoveFollow(temp, 0, 0); - - //spells are SUMMON_TYPE_GUARDIAN, so using setOwner should be ok - summoned->CastSpell(summoned, SPELL_ENERGY_BOLT, false, 0, 0, me->GetGUID()); - } - - void UpdateAI(uint32 diff) override - { - if (!UpdateVictim()) - return; - - if (!Enraged) + boss_vexallusAI(Creature* creature) : BossAI(creature, DATA_VEXALLUS) { - //used for check, when Vexallus cast adds 85%, 70%, 55%, 40%, 25% - if (!HealthAbovePct(100 - INTERVAL_MODIFIER * IntervalHealthAmount)) + _intervalHealthAmount = 1; + _enraged = false; + } + + void Reset() override + { + _Reset(); + _intervalHealthAmount = 1; + _enraged = false; + } + + void KilledUnit(Unit* /*victim*/) override + { + Talk(SAY_KILL); + } + + void JustDied(Unit* /*killer*/) override + { + _JustDied(); + } + + void EnterCombat(Unit* /*who*/) override + { + Talk(SAY_AGGRO); + _EnterCombat(); + + events.ScheduleEvent(EVENT_CHAIN_LIGHTNING, 8000); + events.ScheduleEvent(EVENT_ARCANE_SHOCK, 5000); + } + + void JustSummoned(Creature* summoned) override + { + if (Unit* temp = SelectTarget(SELECT_TARGET_RANDOM, 0)) + summoned->GetMotionMaster()->MoveFollow(temp, 0, 0); + + summons.Summon(summoned); + } + + void DamageTaken(Unit* /*who*/, uint32& damage) override + { + if (_enraged) + return; + + // 85%, 70%, 55%, 40%, 25% + if (!HealthAbovePct(100 - INTERVAL_MODIFIER * _intervalHealthAmount)) { - //increase amount, unless we're at 10%, then we switch and return - if (IntervalHealthAmount == INTERVAL_SWITCH) + // increase amount, unless we're at 10%, then we switch and return + if (_intervalHealthAmount == INTERVAL_SWITCH) { - Enraged = true; + _enraged = true; + events.Reset(); + events.ScheduleEvent(EVENT_OVERLOAD, 1200); return; } else - ++IntervalHealthAmount; + ++_intervalHealthAmount; Talk(SAY_ENERGY); Talk(EMOTE_DISCHARGE_ENERGY); if (IsHeroic()) { - DoCast(me, H_SPELL_SUMMON_PURE_ENERGY1, false); - DoCast(me, H_SPELL_SUMMON_PURE_ENERGY2, false); + DoCast(me, H_SPELL_SUMMON_PURE_ENERGY1); + DoCast(me, H_SPELL_SUMMON_PURE_ENERGY2); } else - DoCast(me, SPELL_SUMMON_PURE_ENERGY, false); + DoCast(me, SPELL_SUMMON_PURE_ENERGY); + } + } - //below are workaround summons, remove when summoning spells w/implicitTarget 73 implemented in the core - me->SummonCreature(NPC_PURE_ENERGY, 0.0f, 0.0f, 0.0f, 0.0f, TEMPSUMMON_CORPSE_DESPAWN, 0); + void UpdateAI(uint32 diff) override + { + if (!UpdateVictim()) + return; - if (IsHeroic()) - me->SummonCreature(NPC_PURE_ENERGY, 0.0f, 0.0f, 0.0f, 0.0f, TEMPSUMMON_CORPSE_DESPAWN, 0); + events.Update(diff); + + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; + + while (uint32 eventId = events.ExecuteEvent()) + { + switch (eventId) + { + case EVENT_CHAIN_LIGHTNING: + if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 0.0f, true)) + DoCast(target, SPELL_CHAIN_LIGHTNING); + events.ScheduleEvent(EVENT_CHAIN_LIGHTNING, 8000); + break; + case EVENT_ARCANE_SHOCK: + if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 20.0f, true)) + DoCast(target, SPELL_ARCANE_SHOCK); + events.ScheduleEvent(EVENT_ARCANE_SHOCK, 8000); + break; + case EVENT_OVERLOAD: + DoCastVictim(SPELL_OVERLOAD); + events.ScheduleEvent(EVENT_OVERLOAD, 2000); + break; + default: + break; + } } - if (ChainLightningTimer <= diff) - { - if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0)) - DoCast(target, SPELL_CHAIN_LIGHTNING); - - ChainLightningTimer = 8000; - } else ChainLightningTimer -= diff; - - if (ArcaneShockTimer <= diff) - { - if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0)) - if (target) - DoCast(target, SPELL_ARCANE_SHOCK); - - ArcaneShockTimer = 8000; - } else ArcaneShockTimer -= diff; - } - else - { - if (OverloadTimer <= diff) - { - DoCastVictim(SPELL_OVERLOAD); - - OverloadTimer = 2000; - } else OverloadTimer -= diff; + DoMeleeAttackIfReady(); } - DoMeleeAttackIfReady(); - } - }; + private: + uint32 _intervalHealthAmount; + bool _enraged; + }; + + CreatureAI* GetAI(Creature* creature) const override + { + return GetInstanceAI(creature); + }; +}; + +enum NpcPureEnergy +{ + SPELL_ENERGY_BOLT = 46156, + SPELL_ENERGY_FEEDBACK = 44335, + SPELL_PURE_ENERGY_PASSIVE = 44326 }; class npc_pure_energy : public CreatureScript { -public: - npc_pure_energy() : CreatureScript("npc_pure_energy") { } + public: + npc_pure_energy() : CreatureScript("npc_pure_energy") { } - CreatureAI* GetAI(Creature* creature) const override - { - return new npc_pure_energyAI(creature); - }; - - struct npc_pure_energyAI : public ScriptedAI - { - npc_pure_energyAI(Creature* creature) : ScriptedAI(creature) + struct npc_pure_energyAI : public ScriptedAI { - me->SetDisplayId(me->GetCreatureTemplate()->Modelid2); - } - - void Reset() override { } - - void JustDied(Unit* slayer) override - { - if (Unit* temp = me->GetOwner()) + npc_pure_energyAI(Creature* creature) : ScriptedAI(creature) { - if (temp && temp->IsAlive()) - slayer->CastSpell(slayer, SPELL_ENERGY_FEEDBACK, true, 0, 0, temp->GetGUID()); + me->SetDisplayId(me->GetCreatureTemplate()->Modelid2); } - } - void EnterCombat(Unit* /*who*/) override { } - void MoveInLineOfSight(Unit* /*who*/) override { } + void JustDied(Unit* killer) override + { + killer->CastSpell(killer, SPELL_ENERGY_FEEDBACK, true); + me->RemoveAurasDueToSpell(SPELL_PURE_ENERGY_PASSIVE); + } + }; - void AttackStart(Unit* /*who*/) override { } - }; + CreatureAI* GetAI(Creature* creature) const override + { + return new npc_pure_energyAI(creature); + }; }; void AddSC_boss_vexallus() diff --git a/src/server/scripts/EasternKingdoms/MagistersTerrace/instance_magisters_terrace.cpp b/src/server/scripts/EasternKingdoms/MagistersTerrace/instance_magisters_terrace.cpp index daea647609..cce83a8d7e 100644 --- a/src/server/scripts/EasternKingdoms/MagistersTerrace/instance_magisters_terrace.cpp +++ b/src/server/scripts/EasternKingdoms/MagistersTerrace/instance_magisters_terrace.cpp @@ -1,6 +1,5 @@ /* * Copyright (C) 2008-2014 TrinityCore - * Copyright (C) 2006-2009 ScriptDev2 * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the @@ -16,19 +15,10 @@ * with this program. If not, see . */ -/* ScriptData -SDName: Instance_Magisters_Terrace -SD%Complete: 60 -SDComment: Designed only for Selin Fireheart -SDCategory: Magister's Terrace -EndScriptData */ - #include "ScriptMgr.h" #include "InstanceScript.h" #include "magisters_terrace.h" -#define MAX_ENCOUNTER 4 - /* 0 - Selin Fireheart 1 - Vexallus @@ -36,281 +26,223 @@ EndScriptData */ 3 - Kael'thas Sunstrider */ -enum Creatures +DoorData const doorData[] = { - NPC_SELIN = 24723, - NPC_DELRISSA = 24560, - NPC_FELCRYSTALS = 24722 -}; - -enum GameObjects -{ - GO_VEXALLUS_DOOR = 187896, - GO_SELIN_DOOR = 187979, - GO_SELIN_ENCOUNTER_DOOR = 188065, - GO_DELRISSA_DOOR = 187770, - GO_KAEL_DOOR = 188064, - GO_KAEL_STATUE_1 = 188165, - GO_KAEL_STATUE_2 = 188166, - GO_ESCAPE_ORB = 188173 + { GO_SELIN_DOOR, DATA_SELIN, DOOR_TYPE_PASSAGE, BOUNDARY_NONE }, + { GO_SELIN_ENCOUNTER_DOOR, DATA_SELIN, DOOR_TYPE_ROOM, BOUNDARY_NONE }, + { GO_VEXALLUS_DOOR, DATA_VEXALLUS, DOOR_TYPE_PASSAGE, BOUNDARY_NONE }, + { GO_DELRISSA_DOOR, DATA_DELRISSA, DOOR_TYPE_PASSAGE, BOUNDARY_NONE }, + { GO_KAEL_DOOR, DATA_KAELTHAS, DOOR_TYPE_ROOM, BOUNDARY_NONE }, + { 0, 0, DOOR_TYPE_ROOM, BOUNDARY_NONE } // END }; class instance_magisters_terrace : public InstanceMapScript { -public: - instance_magisters_terrace() : InstanceMapScript("instance_magisters_terrace", 585) { } + public: + instance_magisters_terrace() : InstanceMapScript("instance_magisters_terrace", 585) { } - struct instance_magisters_terrace_InstanceMapScript : public InstanceScript - { - instance_magisters_terrace_InstanceMapScript(Map* map) : InstanceScript(map) { } - - uint32 Encounter[MAX_ENCOUNTER]; - uint32 DelrissaDeathCount; - - std::vector FelCrystals; - - uint64 SelinGUID; - uint64 DelrissaGUID; - uint64 VexallusDoorGUID; - uint64 SelinDoorGUID; - uint64 SelinEncounterDoorGUID; - uint64 DelrissaDoorGUID; - uint64 KaelDoorGUID; - uint64 KaelStatue[2]; - uint64 EscapeOrbGUID; - uint32 StatuesState; - uint8 felCristalIndex; - - void Initialize() override + struct instance_magisters_terrace_InstanceMapScript : public InstanceScript { - memset(&Encounter, 0, sizeof(Encounter)); - - FelCrystals.clear(); - - DelrissaDeathCount = 0; - - SelinGUID = 0; - DelrissaGUID = 0; - VexallusDoorGUID = 0; - SelinDoorGUID = 0; - SelinEncounterDoorGUID = 0; - DelrissaDoorGUID = 0; - KaelDoorGUID = 0; - KaelStatue[0] = 0; - KaelStatue[1] = 0; - EscapeOrbGUID = 0; - StatuesState = 0; - felCristalIndex = 0; - } - - bool IsEncounterInProgress() const override - { - for (uint8 i = 0; i < MAX_ENCOUNTER; ++i) - if (Encounter[i] == IN_PROGRESS) - return true; - return false; - } - - uint32 GetData(uint32 identifier) const - { - switch (identifier) + instance_magisters_terrace_InstanceMapScript(Map* map) : InstanceScript(map) { - case DATA_SELIN_EVENT: - return Encounter[0]; - case DATA_VEXALLUS_EVENT: - return Encounter[1]; - case DATA_DELRISSA_EVENT: - return Encounter[2]; - case DATA_KAELTHAS_EVENT: - return Encounter[3]; - case DATA_DELRISSA_DEATH_COUNT: - return DelrissaDeathCount; - case DATA_FEL_CRYSTAL_SIZE: - return FelCrystals.size(); + SetBossNumber(EncounterCount); + LoadDoorData(doorData); + + FelCrystals.clear(); + DelrissaDeathCount = 0; + + SelinGUID = 0; + DelrissaGUID = 0; + EscapeOrbGUID = 0; + FelCristalIndex = 0; + + memset(KaelStatue, 0, 2 * sizeof(uint64)); } - return 0; - } - void SetData(uint32 identifier, uint32 data) - { - switch (identifier) + uint32 GetData(uint32 type) const override { - case DATA_SELIN_EVENT: - if (data == DONE) + switch (type) + { + case DATA_DELRISSA_DEATH_COUNT: + return DelrissaDeathCount; + case DATA_FEL_CRYSTAL_SIZE: + return FelCrystals.size(); + default: + break; + } + return 0; + } + + void SetData(uint32 type, uint32 data) override + { + switch (type) + { + case DATA_DELRISSA_DEATH_COUNT: + if (data == SPECIAL) + ++DelrissaDeathCount; + else + DelrissaDeathCount = 0; + break; + case DATA_KAELTHAS_STATUES: + HandleGameObject(KaelStatue[0], data); + HandleGameObject(KaelStatue[1], data); + break; + default: + break; + } + } + + void OnCreatureCreate(Creature* creature) override + { + switch (creature->GetEntry()) + { + case NPC_SELIN: + SelinGUID = creature->GetGUID(); + break; + case NPC_DELRISSA: + DelrissaGUID = creature->GetGUID(); + break; + case NPC_FELCRYSTALS: + FelCrystals.push_back(creature->GetGUID()); + break; + default: + break; + } + } + + void OnGameObjectCreate(GameObject* go) override + { + switch (go->GetEntry()) + { + case GO_VEXALLUS_DOOR: + case GO_SELIN_DOOR: + case GO_SELIN_ENCOUNTER_DOOR: + case GO_DELRISSA_DOOR: + case GO_KAEL_DOOR: + AddDoor(go, true); + break; + case GO_KAEL_STATUE_1: + KaelStatue[0] = go->GetGUID(); + break; + case GO_KAEL_STATUE_2: + KaelStatue[1] = go->GetGUID(); + break; + case GO_ESCAPE_ORB: + EscapeOrbGUID = go->GetGUID(); + break; + default: + break; + } + } + + bool SetBossState(uint32 type, EncounterState state) override + { + if (!InstanceScript::SetBossState(type, state)) + return false; + + switch (type) + { + case DATA_DELRISSA: + if (type == IN_PROGRESS) + DelrissaDeathCount = 0; + break; + default: + break; + } + return true; + } + + std::string GetSaveData() override + { + OUT_SAVE_INST_DATA; + + std::ostringstream saveStream; + saveStream << "M T " << GetBossSaveData(); + + OUT_SAVE_INST_DATA_COMPLETE; + return saveStream.str(); + } + + void Load(const char* str) override + { + if (!str) + { + OUT_LOAD_INST_DATA_FAIL; + return; + } + + OUT_LOAD_INST_DATA(str); + + char dataHead1, dataHead2; + + std::istringstream loadStream(str); + loadStream >> dataHead1 >> dataHead2; + if (dataHead1 == 'M' && dataHead2 == 'T') + { + for (uint32 i = 0; i < EncounterCount; ++i) { - HandleGameObject(SelinEncounterDoorGUID, true); - HandleGameObject(SelinDoorGUID, true); + uint32 tmpState; + loadStream >> tmpState; + if (tmpState == IN_PROGRESS || tmpState > SPECIAL) + tmpState = NOT_STARTED; + SetBossState(i, EncounterState(tmpState)); } - else if (data == IN_PROGRESS) - HandleGameObject(SelinEncounterDoorGUID, false); - else if (data == NOT_STARTED) - HandleGameObject(SelinEncounterDoorGUID, true); + } + else + OUT_LOAD_INST_DATA_FAIL; - Encounter[0] = data; - break; - case DATA_VEXALLUS_EVENT: - if (data == DONE) - HandleGameObject(VexallusDoorGUID, true); - Encounter[1] = data; - break; - case DATA_DELRISSA_EVENT: - if (data == DONE) - HandleGameObject(DelrissaDoorGUID, true); - if (data == IN_PROGRESS) - DelrissaDeathCount = 0; - Encounter[2] = data; - break; - case DATA_KAELTHAS_EVENT: - if (data == NOT_STARTED || data == DONE) - HandleGameObject(KaelDoorGUID, true); - else if (data == IN_PROGRESS) - HandleGameObject(KaelDoorGUID, false); - Encounter[3] = data; - break; - case DATA_DELRISSA_DEATH_COUNT: - if (data == SPECIAL) - ++DelrissaDeathCount; - else - DelrissaDeathCount = 0; - break; - case DATA_KAELTHAS_STATUES: - HandleGameObject(KaelStatue[0], data); - HandleGameObject(KaelStatue[1], data); - StatuesState = data; - break; + OUT_LOAD_INST_DATA_COMPLETE; } - SaveToDB(); - } - - void OnCreatureCreate(Creature* creature) override - { - switch (creature->GetEntry()) + uint64 GetData64(uint32 type) const override { - case NPC_SELIN: - SelinGUID = creature->GetGUID(); - break; - case NPC_DELRISSA: - DelrissaGUID = creature->GetGUID(); - break; - case NPC_FELCRYSTALS: - FelCrystals.push_back(creature->GetGUID()); - break; - } - } + switch (type) + { + case DATA_SELIN: + return SelinGUID; + case DATA_DELRISSA: + return DelrissaGUID; + case DATA_KAEL_STATUE_LEFT: + return KaelStatue[0]; + case DATA_KAEL_STATUE_RIGHT: + return KaelStatue[1]; + case DATA_ESCAPE_ORB: + return EscapeOrbGUID; + case DATA_FEL_CRYSTAL: + if (FelCrystals.size() < FelCristalIndex) + { + TC_LOG_ERROR("scripts", "Magisters Terrace: No Fel Crystals loaded in Inst Data"); + return 0; + } - void OnGameObjectCreate(GameObject* go) override - { - switch (go->GetEntry()) + return FelCrystals.at(FelCristalIndex); + default: + break; + } + return 0; + } + + void SetData64(uint32 type, uint64 value) override { - case GO_VEXALLUS_DOOR: - VexallusDoorGUID = go->GetGUID(); - break; - case GO_SELIN_DOOR: - SelinDoorGUID = go->GetGUID(); - break; - case GO_SELIN_ENCOUNTER_DOOR: - SelinEncounterDoorGUID = go->GetGUID(); - break; - case GO_DELRISSA_DOOR: - DelrissaDoorGUID = go->GetGUID(); - break; - case GO_KAEL_DOOR: - KaelDoorGUID = go->GetGUID(); - break; - case GO_KAEL_STATUE_1: - KaelStatue[0] = go->GetGUID(); - break; - case GO_KAEL_STATUE_2: - KaelStatue[1] = go->GetGUID(); - break; - case GO_ESCAPE_ORB: - EscapeOrbGUID = go->GetGUID(); - break; - } - } - - std::string GetSaveData() override - { - OUT_SAVE_INST_DATA; - - std::ostringstream saveStream; - saveStream << Encounter[0] << ' ' << Encounter[1] << ' ' << Encounter[2] << ' ' << Encounter[3] << ' ' << StatuesState; - - OUT_SAVE_INST_DATA_COMPLETE; - return saveStream.str(); - } - - void Load(const char* str) override - { - if (!str) - { - OUT_LOAD_INST_DATA_FAIL; - return; + if (type == DATA_FEL_CRYSTAL) + FelCristalIndex = value; } - OUT_LOAD_INST_DATA(str); + protected: + std::vector FelCrystals; - std::istringstream loadStream(str); + uint64 SelinGUID; + uint64 DelrissaGUID; + uint64 KaelStatue[2]; + uint64 EscapeOrbGUID; + uint32 DelrissaDeathCount; + uint8 FelCristalIndex; + }; - for (uint32 i = 0; i < MAX_ENCOUNTER; ++i) - { - uint32 tmpState; - loadStream >> tmpState; - if (tmpState == IN_PROGRESS || tmpState > SPECIAL) - tmpState = NOT_STARTED; - SetData(i, tmpState); - } - - loadStream >> StatuesState; - SetData(DATA_KAELTHAS_STATUES, StatuesState); - - OUT_LOAD_INST_DATA_COMPLETE; - } - - uint64 GetData64(uint32 identifier) const + InstanceScript* GetInstanceScript(InstanceMap* map) const override { - switch (identifier) - { - case DATA_SELIN: - return SelinGUID; - case DATA_DELRISSA: - return DelrissaGUID; - case DATA_VEXALLUS_DOOR: - return VexallusDoorGUID; - case DATA_DELRISSA_DOOR: - return DelrissaDoorGUID; - case DATA_KAEL_DOOR: - return KaelDoorGUID; - case DATA_KAEL_STATUE_LEFT: - return KaelStatue[0]; - case DATA_KAEL_STATUE_RIGHT: - return KaelStatue[1]; - case DATA_ESCAPE_ORB: - return EscapeOrbGUID; - case DATA_FEL_CRYSTAL: - if (FelCrystals.size() < felCristalIndex) - { - TC_LOG_ERROR("scripts", "Magisters Terrace: No Fel Crystals loaded in Inst Data"); - return 0; - } - - return FelCrystals.at(felCristalIndex); - } - return 0; + return new instance_magisters_terrace_InstanceMapScript(map); } - - void SetData64(uint32 identifier, uint64 value) - { - if (identifier == DATA_FEL_CRYSTAL) - felCristalIndex = value; - } - }; - - InstanceScript* GetInstanceScript(InstanceMap* map) const override - { - return new instance_magisters_terrace_InstanceMapScript(map); - } }; void AddSC_instance_magisters_terrace() diff --git a/src/server/scripts/EasternKingdoms/MagistersTerrace/magisters_terrace.h b/src/server/scripts/EasternKingdoms/MagistersTerrace/magisters_terrace.h index ddfaa91bc9..d3517dfccf 100644 --- a/src/server/scripts/EasternKingdoms/MagistersTerrace/magisters_terrace.h +++ b/src/server/scripts/EasternKingdoms/MagistersTerrace/magisters_terrace.h @@ -19,24 +19,17 @@ #ifndef DEF_MAGISTERS_TERRACE_H #define DEF_MAGISTERS_TERRACE_H -#define ERROR_INST_DATA "TSCR Error: Instance Data not set properly for Magister's Terrace instance (map 585). Encounters will be buggy." +uint32 const EncounterCount = 4; -enum Data +enum DataTypes { - DATA_SELIN_EVENT, - DATA_VEXALLUS_EVENT, - DATA_DELRISSA_EVENT, - DATA_KAELTHAS_EVENT, - DATA_SELIN, + DATA_VEXALLUS, + DATA_DELRISSA, + DATA_KAELTHAS, + DATA_FEL_CRYSTAL, DATA_FEL_CRYSTAL_SIZE, - - DATA_VEXALLUS_DOOR, - DATA_DELRISSA, - DATA_DELRISSA_DOOR, - - DATA_KAEL_DOOR, DATA_KAEL_STATUE_LEFT, DATA_KAEL_STATUE_RIGHT, @@ -45,4 +38,23 @@ enum Data DATA_ESCAPE_ORB }; +enum CreatureIds +{ + NPC_SELIN = 24723, + NPC_DELRISSA = 24560, + NPC_FELCRYSTALS = 24722 +}; + +enum GameObjectIds +{ + GO_VEXALLUS_DOOR = 187896, + GO_SELIN_DOOR = 187979, + GO_SELIN_ENCOUNTER_DOOR = 188065, + GO_DELRISSA_DOOR = 187770, + GO_KAEL_DOOR = 188064, + GO_KAEL_STATUE_1 = 188165, + GO_KAEL_STATUE_2 = 188166, + GO_ESCAPE_ORB = 188173 +}; + #endif From 27f28552489bab153d5c4717ce48e31496b4c43f Mon Sep 17 00:00:00 2001 From: Vincent-Michael Date: Thu, 26 Jun 2014 22:50:06 +0200 Subject: [PATCH 35/80] Core: Fix warnings and kill again whitespace ... --- src/server/game/Handlers/CharacterHandler.cpp | 4 ++-- src/server/game/World/World.cpp | 2 +- .../EasternKingdoms/MagistersTerrace/boss_vexallus.cpp | 2 +- .../MagistersTerrace/instance_magisters_terrace.cpp | 2 +- src/server/worldserver/worldserver.conf.dist | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/server/game/Handlers/CharacterHandler.cpp b/src/server/game/Handlers/CharacterHandler.cpp index c2f93190a0..9ad382b468 100644 --- a/src/server/game/Handlers/CharacterHandler.cpp +++ b/src/server/game/Handlers/CharacterHandler.cpp @@ -750,9 +750,9 @@ void WorldSession::HandleCharDeleteOpcode(WorldPacket& recvData) std::string IP_str = GetRemoteAddress(); TC_LOG_INFO("entities.player.character", "Account: %d, IP: %s deleted character: %s, GUID: %u, Level: %u", accountId, IP_str.c_str(), name.c_str(), GUID_LOPART(guid), level); - + // To prevent hook failure, place hook before removing reference from DB - sScriptMgr->OnPlayerDelete(guid, initAccountId); // To prevent race conditioning, but as it also makes sense, we hand the accountId over for successful delete. + sScriptMgr->OnPlayerDelete(guid, initAccountId); // To prevent race conditioning, but as it also makes sense, we hand the accountId over for successful delete. // Shouldn't interfere with character deletion though if (sLog->ShouldLog("entities.player.dump", LOG_LEVEL_INFO)) // optimize GetPlayerDump call diff --git a/src/server/game/World/World.cpp b/src/server/game/World/World.cpp index 62603cce95..78470d4e79 100644 --- a/src/server/game/World/World.cpp +++ b/src/server/game/World/World.cpp @@ -1256,7 +1256,7 @@ void World::LoadConfigSettings(bool reload) m_int_configs[CONFIG_BIRTHDAY_TIME] = sConfigMgr->GetIntDefault("BirthdayTime", 1222964635); m_bool_configs[CONFIG_IP_BASED_ACTION_LOGGING] = sConfigMgr->GetBoolDefault("Allow.IP.Based.Action.Logging", false); - + m_bool_configs[CONFIG_IP_BASED_LOGIN_LOGGING] = sConfigMgr->GetBoolDefault("Wrong.Password.Login.Logging", false); // call ScriptMgr if we're reloading the configuration diff --git a/src/server/scripts/EasternKingdoms/MagistersTerrace/boss_vexallus.cpp b/src/server/scripts/EasternKingdoms/MagistersTerrace/boss_vexallus.cpp index 644f686024..b993082030 100644 --- a/src/server/scripts/EasternKingdoms/MagistersTerrace/boss_vexallus.cpp +++ b/src/server/scripts/EasternKingdoms/MagistersTerrace/boss_vexallus.cpp @@ -104,7 +104,7 @@ class boss_vexallus : public CreatureScript summons.Summon(summoned); } - void DamageTaken(Unit* /*who*/, uint32& damage) override + void DamageTaken(Unit* /*who*/, uint32& /*damage*/) override { if (_enraged) return; diff --git a/src/server/scripts/EasternKingdoms/MagistersTerrace/instance_magisters_terrace.cpp b/src/server/scripts/EasternKingdoms/MagistersTerrace/instance_magisters_terrace.cpp index cce83a8d7e..620249e70c 100644 --- a/src/server/scripts/EasternKingdoms/MagistersTerrace/instance_magisters_terrace.cpp +++ b/src/server/scripts/EasternKingdoms/MagistersTerrace/instance_magisters_terrace.cpp @@ -229,7 +229,7 @@ class instance_magisters_terrace : public InstanceMapScript } protected: - std::vector FelCrystals; + std::vector FelCrystals; uint64 SelinGUID; uint64 DelrissaGUID; diff --git a/src/server/worldserver/worldserver.conf.dist b/src/server/worldserver/worldserver.conf.dist index 5a3ea2bb4e..90f330bac4 100644 --- a/src/server/worldserver/worldserver.conf.dist +++ b/src/server/worldserver/worldserver.conf.dist @@ -1354,7 +1354,7 @@ Rate.Creature.Elite.WORLDBOSS.HP = 1 # # Creature.PickPocketRefillDelay -# Description: Time in seconds that the server will wait before refilling the pickpocket loot +# Description: Time in seconds that the server will wait before refilling the pickpocket loot # for a creature # Default: 600 From c9a129708fd8904a680671b36e8ad67e94f417e3 Mon Sep 17 00:00:00 2001 From: jackpoz Date: Thu, 26 Jun 2014 23:11:12 +0200 Subject: [PATCH 36/80] Core/Mail: Fix exploit that allowed to send free emails --- src/server/game/Handlers/MailHandler.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/server/game/Handlers/MailHandler.cpp b/src/server/game/Handlers/MailHandler.cpp index 10ef7810d1..326d198903 100644 --- a/src/server/game/Handlers/MailHandler.cpp +++ b/src/server/game/Handlers/MailHandler.cpp @@ -131,6 +131,13 @@ void WorldSession::HandleSendMail(WorldPacket& recvData) uint32 reqmoney = cost + money; + // Check for overflow + if (reqmoney < money) + { + player->SendMailResult(0, MAIL_SEND, MAIL_ERR_NOT_ENOUGH_MONEY); + return; + } + if (!player->HasEnoughMoney(reqmoney) && !player->IsGameMaster()) { player->SendMailResult(0, MAIL_SEND, MAIL_ERR_NOT_ENOUGH_MONEY); From f94fe58f78c430bbe3a3299aba0729df4b275670 Mon Sep 17 00:00:00 2001 From: Vincent-Michael Date: Thu, 26 Jun 2014 23:15:53 +0200 Subject: [PATCH 37/80] Script/MagistersTerrace: Fix fail in 27f28552489bab153d5c4717ce48e31496b4c43f --- .../MagistersTerrace/instance_magisters_terrace.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/server/scripts/EasternKingdoms/MagistersTerrace/instance_magisters_terrace.cpp b/src/server/scripts/EasternKingdoms/MagistersTerrace/instance_magisters_terrace.cpp index 620249e70c..2ab51dbc6b 100644 --- a/src/server/scripts/EasternKingdoms/MagistersTerrace/instance_magisters_terrace.cpp +++ b/src/server/scripts/EasternKingdoms/MagistersTerrace/instance_magisters_terrace.cpp @@ -66,7 +66,7 @@ class instance_magisters_terrace : public InstanceMapScript case DATA_DELRISSA_DEATH_COUNT: return DelrissaDeathCount; case DATA_FEL_CRYSTAL_SIZE: - return FelCrystals.size(); + return uint32(FelCrystals.size()); default: break; } @@ -229,7 +229,7 @@ class instance_magisters_terrace : public InstanceMapScript } protected: - std::vector FelCrystals; + std::vector FelCrystals; uint64 SelinGUID; uint64 DelrissaGUID; From 4ed4b165f1a1476abb82988e42f9bd52eb17aef4 Mon Sep 17 00:00:00 2001 From: Trisjdc Date: Fri, 27 Jun 2014 00:39:24 +0100 Subject: [PATCH 38/80] Core/Misc: Define CREATURE_TYPEFLAGS_PROJECTILE_COLLISION --- src/server/game/Miscellaneous/SharedDefines.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/server/game/Miscellaneous/SharedDefines.h b/src/server/game/Miscellaneous/SharedDefines.h index 9967b59ece..82126ffede 100644 --- a/src/server/game/Miscellaneous/SharedDefines.h +++ b/src/server/game/Miscellaneous/SharedDefines.h @@ -2596,7 +2596,7 @@ enum CreatureTypeFlags CREATURE_TYPEFLAGS_UNK17 = 0x00020000, // ? Related to vehicles/pvp? CREATURE_TYPEFLAGS_UNK18 = 0x00040000, // ? Related to vehicle/siege weapons? CREATURE_TYPEFLAGS_UNK19 = 0x00080000, - CREATURE_TYPEFLAGS_UNK20 = 0x00100000, + CREATURE_TYPEFLAGS_PROJECTILE_COLLISION = 0x00080000, // Projectiles can collide with this creature - interacts with TARGET_DEST_TRAJ CREATURE_TYPEFLAGS_UNK21 = 0x00200000, CREATURE_TYPEFLAGS_UNK22 = 0x00400000, CREATURE_TYPEFLAGS_UNK23 = 0x00800000, // ? First seen in 3.2.2. Related to banner/backpack of creature/companion? From 0a641bace9198f49c82ac1e0276a33d893e990ea Mon Sep 17 00:00:00 2001 From: Trisjdc Date: Fri, 27 Jun 2014 00:40:59 +0100 Subject: [PATCH 39/80] Core/Spells: Ensure Haunt's healing takes in consideration crit damage --- src/server/scripts/Spells/spell_warlock.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/server/scripts/Spells/spell_warlock.cpp b/src/server/scripts/Spells/spell_warlock.cpp index 3f935077b2..4571798506 100644 --- a/src/server/scripts/Spells/spell_warlock.cpp +++ b/src/server/scripts/Spells/spell_warlock.cpp @@ -518,7 +518,7 @@ class spell_warl_haunt : public SpellScriptLoader { PrepareSpellScript(spell_warl_haunt_SpellScript); - void HandleOnHit() + void HandleAfterHit() { if (Aura* aura = GetHitAura()) if (AuraEffect* aurEff = aura->GetEffect(EFFECT_1)) @@ -527,7 +527,7 @@ class spell_warl_haunt : public SpellScriptLoader void Register() override { - OnHit += SpellHitFn(spell_warl_haunt_SpellScript::HandleOnHit); + AfterHit += SpellHitFn(spell_warl_haunt_SpellScript::HandleAfterHit); } }; From b61dd90f1c4a05bf626690ebd36878fab028fc03 Mon Sep 17 00:00:00 2001 From: Aokromes Date: Fri, 27 Jun 2014 14:37:24 +0200 Subject: [PATCH 40/80] DB/Misc: Fix startup and runtime errors By dr-j --- sql/updates/world/2014_06_27_00_world_misc.sql | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 sql/updates/world/2014_06_27_00_world_misc.sql diff --git a/sql/updates/world/2014_06_27_00_world_misc.sql b/sql/updates/world/2014_06_27_00_world_misc.sql new file mode 100644 index 0000000000..275ecbbf4a --- /dev/null +++ b/sql/updates/world/2014_06_27_00_world_misc.sql @@ -0,0 +1,4 @@ +-- +UPDATE `creature` SET `equipment_id`=0 WHERE `equipment_id`=86 AND `id`=24484; +DELETE FROM `creature_addon` WHERE `guid`=97892; +UPDATE `smart_scripts` SET `link`=0 WHERE `entryorguid`=-126866 AND `source_type`=0 AND `id` IN (7,15); From 17f91aee76a5676c188d3bf951b79eaf5dcbbea2 Mon Sep 17 00:00:00 2001 From: Aokromes Date: Fri, 27 Jun 2014 15:57:49 +0200 Subject: [PATCH 41/80] DB/SAI: Fix more runtime errors By dr-j --- sql/updates/world/2014_06_27_00_world_smart_scripts.sql | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 sql/updates/world/2014_06_27_00_world_smart_scripts.sql diff --git a/sql/updates/world/2014_06_27_00_world_smart_scripts.sql b/sql/updates/world/2014_06_27_00_world_smart_scripts.sql new file mode 100644 index 0000000000..e1340a68a6 --- /dev/null +++ b/sql/updates/world/2014_06_27_00_world_smart_scripts.sql @@ -0,0 +1,8 @@ +-- +UPDATE `smart_scripts` SET `target_type`=1 WHERE `entryorguid`=27899 AND `source_type`=0 AND `id`=25 AND `link`=0; +UPDATE `smart_scripts` SET `target_type`=1 WHERE `entryorguid`=25773 AND `source_type`=0 AND `id`=2 AND `link`=0; +UPDATE `smart_scripts` SET `target_type`=1 WHERE `entryorguid`=30135 AND `source_type`=0 AND `id`=2 AND `link`=0; +UPDATE `smart_scripts` SET `target_type`=1 WHERE `entryorguid`=29974 AND `source_type`=0 AND `id`=2 AND `link`=0; +UPDATE `smart_scripts` SET `target_type`=1 WHERE `entryorguid`=17299 AND `source_type`=0 AND `id`=0 AND `link`=0; +UPDATE `smart_scripts` SET `target_type`=1 WHERE `entryorguid`=2719901 AND `source_type`=9 AND `id`=7 AND `link`=0; +UPDATE `smart_scripts` SET `target_type`=1 WHERE `entryorguid`=30144 AND `source_type`=0 AND `id`=2 AND `link`=0; From 78ffaf6ca6dc8887941b9e26b51b6d19aef46bc5 Mon Sep 17 00:00:00 2001 From: Trisjdc Date: Fri, 27 Jun 2014 16:58:46 +0100 Subject: [PATCH 42/80] Core/Spells: Implement CREATURE_TYPEFLAGS_PROJECTILE_COLLISION, and solve some targeting problems with TARGET_DEST_TRAJ --- src/server/game/Miscellaneous/SharedDefines.h | 2 +- src/server/game/Spells/Spell.cpp | 16 +++++++++++++++- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/src/server/game/Miscellaneous/SharedDefines.h b/src/server/game/Miscellaneous/SharedDefines.h index 82126ffede..77bc76c6df 100644 --- a/src/server/game/Miscellaneous/SharedDefines.h +++ b/src/server/game/Miscellaneous/SharedDefines.h @@ -2595,8 +2595,8 @@ enum CreatureTypeFlags CREATURE_TYPEFLAGS_EXOTIC = 0x00010000, // Can be tamed by hunter as exotic pet CREATURE_TYPEFLAGS_UNK17 = 0x00020000, // ? Related to vehicles/pvp? CREATURE_TYPEFLAGS_UNK18 = 0x00040000, // ? Related to vehicle/siege weapons? - CREATURE_TYPEFLAGS_UNK19 = 0x00080000, CREATURE_TYPEFLAGS_PROJECTILE_COLLISION = 0x00080000, // Projectiles can collide with this creature - interacts with TARGET_DEST_TRAJ + CREATURE_TYPEFLAGS_UNK20 = 0x00100000, CREATURE_TYPEFLAGS_UNK21 = 0x00200000, CREATURE_TYPEFLAGS_UNK22 = 0x00400000, CREATURE_TYPEFLAGS_UNK23 = 0x00800000, // ? First seen in 3.2.2. Related to banner/backpack of creature/companion? diff --git a/src/server/game/Spells/Spell.cpp b/src/server/game/Spells/Spell.cpp index 48c2a76578..9086a304e1 100644 --- a/src/server/game/Spells/Spell.cpp +++ b/src/server/game/Spells/Spell.cpp @@ -1514,10 +1514,24 @@ void Spell::SelectImplicitTrajTargets(SpellEffIndex effIndex) std::list::const_iterator itr = targets.begin(); for (; itr != targets.end(); ++itr) { + if (!m_caster->HasInLine(*itr, 5.0f)) + continue; + + if (m_spellInfo->CheckTarget(m_caster, *itr, true) != SPELL_CAST_OK) + continue; + if (Unit* unitTarget = (*itr)->ToUnit()) - if (m_caster == *itr || m_caster->IsOnVehicle(unitTarget) || (unitTarget)->GetVehicle())//(*itr)->IsOnVehicle(m_caster)) + { + if (m_caster == *itr || m_caster->IsOnVehicle(unitTarget) || unitTarget->GetVehicle()) continue; + if (Creature* creatureTarget = unitTarget->ToCreature()) + { + if (!(creatureTarget->GetCreatureTemplate()->type_flags & CREATURE_TYPEFLAGS_PROJECTILE_COLLISION)) + continue; + } + } + const float size = std::max((*itr)->GetObjectSize() * 0.7f, 1.0f); // 1/sqrt(3) /// @todo all calculation should be based on src instead of m_caster const float objDist2d = m_targets.GetSrcPos()->GetExactDist2d(*itr) * std::cos(m_targets.GetSrcPos()->GetRelativeAngle(*itr)); From 52023b1e6b1fefaf4be95fa201fc1dd2ecca55a7 Mon Sep 17 00:00:00 2001 From: joschiwald Date: Fri, 27 Jun 2014 18:45:49 +0200 Subject: [PATCH 43/80] Scripts/MagistersTerrace: fix possible crash --- .../instance_magisters_terrace.cpp | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/server/scripts/EasternKingdoms/MagistersTerrace/instance_magisters_terrace.cpp b/src/server/scripts/EasternKingdoms/MagistersTerrace/instance_magisters_terrace.cpp index 2ab51dbc6b..e0050420a0 100644 --- a/src/server/scripts/EasternKingdoms/MagistersTerrace/instance_magisters_terrace.cpp +++ b/src/server/scripts/EasternKingdoms/MagistersTerrace/instance_magisters_terrace.cpp @@ -135,6 +135,22 @@ class instance_magisters_terrace : public InstanceMapScript } } + void OnGameObjectRemove(GameObject* go) override + { + switch (go->GetEntry()) + { + case GO_VEXALLUS_DOOR: + case GO_SELIN_DOOR: + case GO_SELIN_ENCOUNTER_DOOR: + case GO_DELRISSA_DOOR: + case GO_KAEL_DOOR: + AddDoor(go, false); + break; + default: + break; + } + } + bool SetBossState(uint32 type, EncounterState state) override { if (!InstanceScript::SetBossState(type, state)) From df164bf2d7925037cd5edfca3947015637873197 Mon Sep 17 00:00:00 2001 From: jackpoz Date: Fri, 27 Jun 2014 20:55:37 +0200 Subject: [PATCH 44/80] Core/Misc: Fix gcc warnings --- src/server/game/Entities/Creature/Creature.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/server/game/Entities/Creature/Creature.cpp b/src/server/game/Entities/Creature/Creature.cpp index 9b2cd01380..19d32b41c9 100644 --- a/src/server/game/Entities/Creature/Creature.cpp +++ b/src/server/game/Entities/Creature/Creature.cpp @@ -142,8 +142,8 @@ bool ForcedDespawnDelayEvent::Execute(uint64 /*e_time*/, uint32 /*p_time*/) } Creature::Creature(bool isWorldObject): Unit(isWorldObject), MapObject(), -_pickpocketLootRestore(0), m_groupLootTimer(0), lootingGroupLowGUID(0), m_PlayerDamageReq(0), -m_lootRecipient(0), m_lootRecipientGroup(0), _skinner(0), m_corpseRemoveTime(0), m_respawnTime(0), +m_groupLootTimer(0), lootingGroupLowGUID(0), m_PlayerDamageReq(0), +m_lootRecipient(0), m_lootRecipientGroup(0), _skinner(0), _pickpocketLootRestore(0), m_corpseRemoveTime(0), m_respawnTime(0), m_respawnDelay(300), m_corpseDelay(60), m_respawnradius(0.0f), m_reactState(REACT_AGGRESSIVE), m_defaultMovementType(IDLE_MOTION_TYPE), m_DBTableGuid(0), m_equipmentId(0), m_originalEquipmentId(0), m_AlreadyCallAssistance(false), m_AlreadySearchedAssistance(false), m_regenHealth(true), m_AI_locked(false), m_meleeDamageSchoolMask(SPELL_SCHOOL_MASK_NORMAL), From c28345e279f31af2fa32df2411325ccfabe06fc8 Mon Sep 17 00:00:00 2001 From: joschiwald Date: Fri, 27 Jun 2014 23:14:15 +0200 Subject: [PATCH 45/80] Core/Battleground: refactored BattlegroundScore (original code by Machiavelli) --- src/server/game/Battlegrounds/ArenaScore.h | 93 ++++++++++ src/server/game/Battlegrounds/ArenaTeam.h | 2 +- .../game/Battlegrounds/Battleground.cpp | 133 +++++++------- src/server/game/Battlegrounds/Battleground.h | 59 +----- .../game/Battlegrounds/BattlegroundMgr.cpp | 170 ------------------ .../game/Battlegrounds/BattlegroundMgr.h | 1 - .../game/Battlegrounds/BattlegroundScore.h | 120 +++++++++++++ .../Battlegrounds/Zones/BattlegroundAB.cpp | 21 +-- .../game/Battlegrounds/Zones/BattlegroundAB.h | 39 +++- .../Battlegrounds/Zones/BattlegroundAV.cpp | 35 ++-- .../game/Battlegrounds/Zones/BattlegroundAV.h | 60 +++++-- .../Battlegrounds/Zones/BattlegroundBE.cpp | 13 +- .../game/Battlegrounds/Zones/BattlegroundBE.h | 3 - .../Battlegrounds/Zones/BattlegroundDS.cpp | 3 +- .../Battlegrounds/Zones/BattlegroundEY.cpp | 17 +- .../game/Battlegrounds/Zones/BattlegroundEY.h | 33 +++- .../Battlegrounds/Zones/BattlegroundIC.cpp | 23 +-- .../game/Battlegrounds/Zones/BattlegroundIC.h | 40 ++++- .../Battlegrounds/Zones/BattlegroundNA.cpp | 3 +- .../Battlegrounds/Zones/BattlegroundRL.cpp | 3 +- .../Battlegrounds/Zones/BattlegroundRV.cpp | 3 +- .../Battlegrounds/Zones/BattlegroundSA.cpp | 18 +- .../game/Battlegrounds/Zones/BattlegroundSA.h | 45 +++-- .../Battlegrounds/Zones/BattlegroundWS.cpp | 16 +- .../game/Battlegrounds/Zones/BattlegroundWS.h | 39 +++- src/server/game/Entities/Player/Player.cpp | 1 + src/server/game/Entities/Unit/Unit.cpp | 4 +- .../game/Handlers/BattleGroundHandler.cpp | 2 +- 28 files changed, 545 insertions(+), 454 deletions(-) create mode 100644 src/server/game/Battlegrounds/ArenaScore.h create mode 100644 src/server/game/Battlegrounds/BattlegroundScore.h diff --git a/src/server/game/Battlegrounds/ArenaScore.h b/src/server/game/Battlegrounds/ArenaScore.h new file mode 100644 index 0000000000..7b25427c07 --- /dev/null +++ b/src/server/game/Battlegrounds/ArenaScore.h @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2008-2014 TrinityCore + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +#ifndef TRINITY_ARENA_SCORE_H +#define TRINITY_ARENA_SCORE_H + +#include "BattlegroundScore.h" +#include "SharedDefines.h" + +struct ArenaScore : public BattlegroundScore +{ + friend class BattlegroundBE; + friend class BattlegroundDS; + friend class BattlegroundNA; + friend class BattlegroundRL; + friend class BattlegroundRV; + + protected: + ArenaScore(uint64 playerGuid, uint32 team) : BattlegroundScore(playerGuid), TeamId(team == ALLIANCE ? 1 : 0) { } + + void AppendToPacket(WorldPacket& data) final + { + data << uint64(PlayerGuid); + + data << uint32(KillingBlows); + data << uint8(TeamId); + data << uint32(DamageDone); + data << uint32(HealingDone); + data << uint32(0); // Objectives Count + } + + // For Logging purpose + std::string ToString() const override + { + std::ostringstream stream; + stream << "Damage done: " << DamageDone << ", Healing done: " << HealingDone << ", Killing blows: " << KillingBlows; + return stream.str(); + } + + uint8 TeamId; // TEAM_ALLIANCE or TEAM_HORDE +}; + +struct ArenaTeamScore +{ + friend class Battleground; + + protected: + ArenaTeamScore() : RatingChange(0), MatchmakerRating(0) { } + + virtual ~ArenaTeamScore() { } + + void Assign(int32 ratingChange, uint32 matchMakerRating, std::string const& teamName) + { + RatingChange = ratingChange; + MatchmakerRating = matchMakerRating; + TeamName = teamName; + } + + void BuildRatingInfoBlock(WorldPacket& data) + { + uint32 ratingLost = std::abs(std::min(RatingChange, 0)); + uint32 ratingWon = std::max(RatingChange, 0); + + data << uint32(ratingLost); + data << uint32(ratingWon); + data << uint32(MatchmakerRating); + } + + void BuildTeamInfoBlock(WorldPacket& data) + { + data << TeamName; + } + + int32 RatingChange; + uint32 MatchmakerRating; + std::string TeamName; +}; + +#endif // TRINITY_ARENA_SCORE_H diff --git a/src/server/game/Battlegrounds/ArenaTeam.h b/src/server/game/Battlegrounds/ArenaTeam.h index cd9e2be431..c977a137c2 100644 --- a/src/server/game/Battlegrounds/ArenaTeam.h +++ b/src/server/game/Battlegrounds/ArenaTeam.h @@ -130,7 +130,7 @@ class ArenaTeam uint8 GetSlot() const { return GetSlotByType(GetType()); } static uint8 GetSlotByType(uint32 type); uint64 GetCaptain() const { return CaptainGuid; } - std::string const& GetName() const { return TeamName; } + std::string const& GetName() const { return TeamName; } const ArenaTeamStats& GetStats() const { return Stats; } uint32 GetRating() const { return Stats.Rating; } diff --git a/src/server/game/Battlegrounds/Battleground.cpp b/src/server/game/Battlegrounds/Battleground.cpp index ca48ffb3a1..96911cfeda 100644 --- a/src/server/game/Battlegrounds/Battleground.cpp +++ b/src/server/game/Battlegrounds/Battleground.cpp @@ -16,10 +16,12 @@ * with this program. If not, see . */ +#include "ArenaScore.h" #include "ArenaTeam.h" #include "ArenaTeamMgr.h" #include "Battleground.h" #include "BattlegroundMgr.h" +#include "BattlegroundScore.h" #include "Creature.h" #include "CreatureTextMgr.h" #include "Chat.h" @@ -165,12 +167,13 @@ Battleground::Battleground() m_ArenaTeamIds[TEAM_ALLIANCE] = 0; m_ArenaTeamIds[TEAM_HORDE] = 0; - m_ArenaTeamRatingChanges[TEAM_ALLIANCE] = 0; - m_ArenaTeamRatingChanges[TEAM_HORDE] = 0; - m_ArenaTeamMMR[TEAM_ALLIANCE] = 0; m_ArenaTeamMMR[TEAM_HORDE] = 0; + // Iterate this way for consistency's sake - client expects it to be sent in this order + for (int8 i = WINNER_ALLIANCE; i >= WINNER_HORDE; --i) + _arenaTeamScores[i] = new ArenaTeamScore(); + m_BgRaids[TEAM_ALLIANCE] = NULL; m_BgRaids[TEAM_HORDE] = NULL; @@ -222,6 +225,10 @@ Battleground::~Battleground() for (BattlegroundScoreMap::const_iterator itr = PlayerScores.begin(); itr != PlayerScores.end(); ++itr) delete itr->second; + + // Iterate this way for consistency's sake - client expects it to be sent in this order + for (int8 i = WINNER_ALLIANCE; i >= WINNER_HORDE; --i) + delete _arenaTeamScores[i]; } void Battleground::Update(uint32 diff) @@ -771,49 +778,50 @@ void Battleground::EndBattleground(uint32 winner) if (winnerArenaTeam && loserArenaTeam && winnerArenaTeam != loserArenaTeam) { + loserTeamRating = loserArenaTeam->GetRating(); + loserMatchmakerRating = GetArenaMatchmakerRating(GetOtherTeam(winner)); + winnerTeamRating = winnerArenaTeam->GetRating(); + winnerMatchmakerRating = GetArenaMatchmakerRating(winner); + if (winner != WINNER_NONE) { - loserTeamRating = loserArenaTeam->GetRating(); - loserMatchmakerRating = GetArenaMatchmakerRating(GetOtherTeam(winner)); - winnerTeamRating = winnerArenaTeam->GetRating(); - winnerMatchmakerRating = GetArenaMatchmakerRating(winner); winnerMatchmakerChange = winnerArenaTeam->WonAgainst(winnerMatchmakerRating, loserMatchmakerRating, winnerChange); loserMatchmakerChange = loserArenaTeam->LostAgainst(loserMatchmakerRating, winnerMatchmakerRating, loserChange); TC_LOG_DEBUG("bg.arena", "match Type: %u --- Winner: old rating: %u, rating gain: %d, old MMR: %u, MMR gain: %d --- Loser: old rating: %u, rating loss: %d, old MMR: %u, MMR loss: %d ---", m_ArenaType, winnerTeamRating, winnerChange, winnerMatchmakerRating, winnerMatchmakerChange, loserTeamRating, loserChange, loserMatchmakerRating, loserMatchmakerChange); SetArenaMatchmakerRating(winner, winnerMatchmakerRating + winnerMatchmakerChange); SetArenaMatchmakerRating(GetOtherTeam(winner), loserMatchmakerRating + loserMatchmakerChange); - SetArenaTeamRatingChangeForTeam(winner, winnerChange); - SetArenaTeamRatingChangeForTeam(GetOtherTeam(winner), loserChange); + + uint8 winnerId = GetWinner(); + uint8 loserId = winnerId == WINNER_ALLIANCE ? WINNER_HORDE : winnerId; + + _arenaTeamScores[winnerId]->Assign(winnerChange, winnerMatchmakerRating + winnerMatchmakerChange, winnerArenaTeam->GetName()); + _arenaTeamScores[loserId]->Assign(loserChange, loserMatchmakerRating + loserMatchmakerChange, loserArenaTeam->GetName()); + TC_LOG_DEBUG("bg.arena", "Arena match Type: %u for Team1Id: %u - Team2Id: %u ended. WinnerTeamId: %u. Winner rating: +%d, Loser rating: %d", m_ArenaType, m_ArenaTeamIds[TEAM_ALLIANCE], m_ArenaTeamIds[TEAM_HORDE], winnerArenaTeam->GetId(), winnerChange, loserChange); if (sWorld->getBoolConfig(CONFIG_ARENA_LOG_EXTENDED_INFO)) - for (Battleground::BattlegroundScoreMap::const_iterator itr = GetPlayerScoresBegin(); itr != GetPlayerScoresEnd(); ++itr) - if (Player* player = ObjectAccessor::FindPlayer(itr->first)) + for (auto const& score : PlayerScores) + if (Player* player = ObjectAccessor::FindPlayer(MAKE_NEW_GUID(score.first, 0, HIGHGUID_PLAYER))) { - TC_LOG_DEBUG("bg.arena", "Statistics match Type: %u for %s (GUID: " UI64FMTD ", Team: %d, IP: %s): %u damage, %u healing, %u killing blows", - m_ArenaType, player->GetName().c_str(), itr->first, player->GetArenaTeamId(m_ArenaType == 5 ? 2 : m_ArenaType == 3), - player->GetSession()->GetRemoteAddress().c_str(), itr->second->DamageDone, itr->second->HealingDone, - itr->second->KillingBlows); + TC_LOG_DEBUG("bg.arena", "Statistics match Type: %u for %s (GUID: %u, Team: %d, IP: %s): %s", + m_ArenaType, player->GetName().c_str(), score.first, player->GetArenaTeamId(m_ArenaType == 5 ? 2 : m_ArenaType == 3), + player->GetSession()->GetRemoteAddress().c_str(), score.second->ToString().c_str()); } } // Deduct 16 points from each teams arena-rating if there are no winners after 45+2 minutes else { - SetArenaTeamRatingChangeForTeam(ALLIANCE, ARENA_TIMELIMIT_POINTS_LOSS); - SetArenaTeamRatingChangeForTeam(HORDE, ARENA_TIMELIMIT_POINTS_LOSS); + _arenaTeamScores[WINNER_ALLIANCE]->Assign(ARENA_TIMELIMIT_POINTS_LOSS, winnerMatchmakerRating + winnerMatchmakerChange, winnerArenaTeam->GetName()); + _arenaTeamScores[WINNER_HORDE]->Assign(ARENA_TIMELIMIT_POINTS_LOSS, loserMatchmakerRating + loserMatchmakerChange, loserArenaTeam->GetName()); + winnerArenaTeam->FinishGame(ARENA_TIMELIMIT_POINTS_LOSS); loserArenaTeam->FinishGame(ARENA_TIMELIMIT_POINTS_LOSS); } } - else - { - SetArenaTeamRatingChangeForTeam(ALLIANCE, 0); - SetArenaTeamRatingChangeForTeam(HORDE, 0); - } } WorldPacket pvpLogData; - sBattlegroundMgr->BuildPvpLogDataPacket(&pvpLogData, this); + BuildPvPLogDataPacket(pvpLogData); BattlegroundQueueTypeId bgQueueTypeId = BattlegroundMgr::BGQueueTypeId(GetTypeID(), GetArenaType()); @@ -958,7 +966,7 @@ void Battleground::RemovePlayerAtLeave(uint64 guid, bool Transport, bool SendPac participant = true; } - BattlegroundScoreMap::iterator itr2 = PlayerScores.find(guid); + BattlegroundScoreMap::iterator itr2 = PlayerScores.find(GUID_LOPART(guid)); if (itr2 != PlayerScores.end()) { delete itr2->second; // delete player's score @@ -1349,47 +1357,48 @@ bool Battleground::HasFreeSlots() const return GetPlayersSize() < GetMaxPlayers(); } -void Battleground::UpdatePlayerScore(Player* Source, uint32 type, uint32 value, bool doAddHonor) +void Battleground::BuildPvPLogDataPacket(WorldPacket& data) { - //this procedure is called from virtual function implemented in bg subclass - BattlegroundScoreMap::const_iterator itr = PlayerScores.find(Source->GetGUID()); - if (itr == PlayerScores.end()) // player not found... - return; + uint8 type = (isArena() ? 1 : 0); - switch (type) + data.Initialize(MSG_PVP_LOG_DATA, 1 + 1 + 4 + 40 * GetPlayerScoresSize()); + data << uint8(type); // type (battleground = 0 / arena = 1) + + if (type) // arena { - case SCORE_KILLING_BLOWS: // Killing blows - itr->second->KillingBlows += value; - break; - case SCORE_DEATHS: // Deaths - itr->second->Deaths += value; - break; - case SCORE_HONORABLE_KILLS: // Honorable kills - itr->second->HonorableKills += value; - break; - case SCORE_BONUS_HONOR: // Honor bonus - // do not add honor in arenas - if (isBattleground()) - { - // reward honor instantly - if (doAddHonor) - Source->RewardHonor(NULL, 1, value); // RewardHonor calls UpdatePlayerScore with doAddHonor = false - else - itr->second->BonusHonor += value; - } - break; - // used only in EY, but in MSG_PVP_LOG_DATA opcode - case SCORE_DAMAGE_DONE: // Damage Done - itr->second->DamageDone += value; - break; - case SCORE_HEALING_DONE: // Healing Done - itr->second->HealingDone += value; - break; - default: - TC_LOG_ERROR("bg.battleground", "Battleground::UpdatePlayerScore: unknown score type (%u) for BG (map: %u, instance id: %u)!", - type, m_MapId, m_InstanceID); - break; + // it seems this must be according to BG_WINNER_A/H and _NOT_ TEAM_A/H + for (int8 i = WINNER_ALLIANCE; i >= WINNER_HORDE; --i) + _arenaTeamScores[i]->BuildRatingInfoBlock(data); + + for (int8 i = WINNER_ALLIANCE; i >= WINNER_HORDE; --i) + _arenaTeamScores[i]->BuildTeamInfoBlock(data); } + + if (GetStatus() == STATUS_WAIT_LEAVE) + { + data << uint8(1); // bg ended + data << uint8(GetWinner()); // who win + } + else + data << uint8(0); // bg not ended + + data << uint32(GetPlayerScoresSize()); + for (auto const& score : PlayerScores) + score.second->AppendToPacket(data); +} + +bool Battleground::UpdatePlayerScore(Player* player, uint32 type, uint32 value, bool doAddHonor) +{ + BattlegroundScoreMap::const_iterator itr = PlayerScores.find(player->GetGUIDLow()); + if (itr == PlayerScores.end()) // player not found... + return false; + + itr->second->UpdateScore(type, value); + + if (type == SCORE_BONUS_HONOR && doAddHonor && isBattleground()) + player->RewardHonor(NULL, 1, value); // RewardHonor calls UpdatePlayerScore with doAddHonor = false + + return true; } void Battleground::AddPlayerToResurrectQueue(uint64 npc_guid, uint64 player_guid) @@ -1868,7 +1877,7 @@ void Battleground::PlayerAddedToBGCheckIfBGIsRunning(Player* player) BlockMovement(player); - sBattlegroundMgr->BuildPvpLogDataPacket(&data, this); + BuildPvPLogDataPacket(data); player->SendDirectMessage(&data); sBattlegroundMgr->BuildBattlegroundStatusPacket(&data, this, player->GetBattlegroundQueueIndex(bgQueueTypeId), STATUS_IN_PROGRESS, GetEndTime(), GetStartTime(), GetArenaType(), player->GetBGTeam()); diff --git a/src/server/game/Battlegrounds/Battleground.h b/src/server/game/Battlegrounds/Battleground.h index 4256e2fc09..7d1bf55069 100644 --- a/src/server/game/Battlegrounds/Battleground.h +++ b/src/server/game/Battlegrounds/Battleground.h @@ -32,6 +32,8 @@ class WorldObject; class WorldPacket; class BattlegroundMap; +struct ArenaTeamScore; +struct BattlegroundScore; struct Position; struct PvPDifficultyEntry; struct WorldSafeLocsEntry; @@ -171,34 +173,6 @@ struct BattlegroundObjectInfo uint32 spellid; }; -enum ScoreType -{ - SCORE_KILLING_BLOWS = 1, - SCORE_DEATHS = 2, - SCORE_HONORABLE_KILLS = 3, - SCORE_BONUS_HONOR = 4, - //EY, but in MSG_PVP_LOG_DATA opcode! - SCORE_DAMAGE_DONE = 5, - SCORE_HEALING_DONE = 6, - //WS - SCORE_FLAG_CAPTURES = 7, - SCORE_FLAG_RETURNS = 8, - //AB and IC - SCORE_BASES_ASSAULTED = 9, - SCORE_BASES_DEFENDED = 10, - //AV - SCORE_GRAVEYARDS_ASSAULTED = 11, - SCORE_GRAVEYARDS_DEFENDED = 12, - SCORE_TOWERS_ASSAULTED = 13, - SCORE_TOWERS_DEFENDED = 14, - SCORE_MINES_CAPTURED = 15, - SCORE_LEADERS_KILLED = 16, - SCORE_SECONDARY_OBJECTIVES = 17, - //SOTA - SCORE_DESTROYED_DEMOLISHER = 18, - SCORE_DESTROYED_WALL = 19 -}; - enum ArenaType { ARENA_TYPE_2v2 = 2, @@ -239,22 +213,6 @@ enum BattlegroundStartingEventsIds }; #define BG_STARTING_EVENT_COUNT 4 -struct BattlegroundScore -{ - BattlegroundScore() : KillingBlows(0), Deaths(0), HonorableKills(0), BonusHonor(0), - DamageDone(0), HealingDone(0) - { } - - virtual ~BattlegroundScore() { } //virtual destructor is used when deleting score from scores map - - uint32 KillingBlows; - uint32 Deaths; - uint32 HonorableKills; - uint32 BonusHonor; - uint32 DamageDone; - uint32 HealingDone; -}; - enum BGHonorMode { BG_NORMAL = 0, @@ -369,9 +327,7 @@ class Battleground BattlegroundPlayerMap const& GetPlayers() const { return m_Players; } uint32 GetPlayersSize() const { return m_Players.size(); } - typedef std::map BattlegroundScoreMap; - BattlegroundScoreMap::const_iterator GetPlayerScoresBegin() const { return PlayerScores.begin(); } - BattlegroundScoreMap::const_iterator GetPlayerScoresEnd() const { return PlayerScores.end(); } + typedef std::map BattlegroundScoreMap; uint32 GetPlayerScoresSize() const { return PlayerScores.size(); } uint32 GetReviveQueueSize() const { return m_ReviveQueue.size(); } @@ -443,7 +399,8 @@ class Battleground Group* GetBgRaid(uint32 TeamID) const { return TeamID == ALLIANCE ? m_BgRaids[TEAM_ALLIANCE] : m_BgRaids[TEAM_HORDE]; } void SetBgRaid(uint32 TeamID, Group* bg_raid); - virtual void UpdatePlayerScore(Player* player, uint32 type, uint32 value, bool doAddHonor = true); + void BuildPvPLogDataPacket(WorldPacket& data); + virtual bool UpdatePlayerScore(Player* player, uint32 type, uint32 value, bool doAddHonor = true); static TeamId GetTeamIndexByTeamId(uint32 Team) { return Team == ALLIANCE ? TEAM_ALLIANCE : TEAM_HORDE; } uint32 GetPlayersCountByTeam(uint32 Team) const { return m_PlayersCount[GetTeamIndexByTeamId(Team)]; } @@ -460,12 +417,8 @@ class Battleground void SetArenaTeamIdForTeam(uint32 Team, uint32 ArenaTeamId) { m_ArenaTeamIds[GetTeamIndexByTeamId(Team)] = ArenaTeamId; } uint32 GetArenaTeamIdForTeam(uint32 Team) const { return m_ArenaTeamIds[GetTeamIndexByTeamId(Team)]; } uint32 GetArenaTeamIdByIndex(uint32 index) const { return m_ArenaTeamIds[index]; } - void SetArenaTeamRatingChangeForTeam(uint32 Team, int32 RatingChange) { m_ArenaTeamRatingChanges[GetTeamIndexByTeamId(Team)] = RatingChange; } - int32 GetArenaTeamRatingChangeForTeam(uint32 Team) const { return m_ArenaTeamRatingChanges[GetTeamIndexByTeamId(Team)]; } - int32 GetArenaTeamRatingChangeByIndex(uint32 index) const { return m_ArenaTeamRatingChanges[index]; } void SetArenaMatchmakerRating(uint32 Team, uint32 MMR){ m_ArenaTeamMMR[GetTeamIndexByTeamId(Team)] = MMR; } uint32 GetArenaMatchmakerRating(uint32 Team) const { return m_ArenaTeamMMR[GetTeamIndexByTeamId(Team)]; } - uint32 GetArenaMatchmakerRatingByIndex(uint32 index) const { return m_ArenaTeamMMR[index]; } void CheckArenaAfterTimerConditions(); void CheckArenaWinConditions(); void UpdateArenaWorldState(); @@ -657,8 +610,8 @@ class Battleground // Arena team ids by team uint32 m_ArenaTeamIds[BG_TEAMS_COUNT]; - int32 m_ArenaTeamRatingChanges[BG_TEAMS_COUNT]; uint32 m_ArenaTeamMMR[BG_TEAMS_COUNT]; + ArenaTeamScore* _arenaTeamScores[BG_TEAMS_COUNT]; // Limits uint32 m_LevelMin; diff --git a/src/server/game/Battlegrounds/BattlegroundMgr.cpp b/src/server/game/Battlegrounds/BattlegroundMgr.cpp index ab69c950ff..ef78594626 100644 --- a/src/server/game/Battlegrounds/BattlegroundMgr.cpp +++ b/src/server/game/Battlegrounds/BattlegroundMgr.cpp @@ -216,176 +216,6 @@ void BattlegroundMgr::BuildBattlegroundStatusPacket(WorldPacket* data, Battlegro } } -void BattlegroundMgr::BuildPvpLogDataPacket(WorldPacket* data, Battleground* bg) -{ - uint8 type = (bg->isArena() ? 1 : 0); - - data->Initialize(MSG_PVP_LOG_DATA, (1+1+4+40*bg->GetPlayerScoresSize())); - *data << uint8(type); // type (battleground=0/arena=1) - - if (type) // arena - { - // it seems this must be according to BG_WINNER_A/H and _NOT_ TEAM_A/H - for (int8 i = 1; i >= 0; --i) - { - int32 rating_change = bg->GetArenaTeamRatingChangeByIndex(i); - - uint32 pointsLost = rating_change < 0 ? -rating_change : 0; - uint32 pointsGained = rating_change > 0 ? rating_change : 0; - uint32 MatchmakerRating = bg->GetArenaMatchmakerRatingByIndex(i); - - *data << uint32(pointsLost); // Rating Lost - *data << uint32(pointsGained); // Rating gained - *data << uint32(MatchmakerRating); // Matchmaking Value - TC_LOG_DEBUG("bg.battleground", "rating change: %d", rating_change); - } - for (int8 i = 1; i >= 0; --i) - { - if (ArenaTeam* at = sArenaTeamMgr->GetArenaTeamById(bg->GetArenaTeamIdByIndex(i))) - *data << at->GetName(); - else - *data << uint8(0); - } - } - - if (bg->GetStatus() != STATUS_WAIT_LEAVE) - *data << uint8(0); // bg not ended - else - { - *data << uint8(1); // bg ended - *data << uint8(bg->GetWinner()); // who win - } - - size_t wpos = data->wpos(); - uint32 scoreCount = 0; - *data << uint32(scoreCount); // placeholder - - Battleground::BattlegroundScoreMap::const_iterator itr2 = bg->GetPlayerScoresBegin(); - for (Battleground::BattlegroundScoreMap::const_iterator itr = itr2; itr != bg->GetPlayerScoresEnd();) - { - itr2 = itr++; - BattlegroundScore* score = itr2->second; - if (!bg->IsPlayerInBattleground(itr2->first)) - { - TC_LOG_ERROR("bg.battleground", "Player " UI64FMTD " has scoreboard entry for battleground %u but is not in battleground!", itr->first, bg->GetTypeID(true)); - continue; - } - - *data << uint64(itr2->first); - *data << uint32(score->KillingBlows); - if (type == 0) - { - *data << uint32(score->HonorableKills); - *data << uint32(score->Deaths); - *data << uint32(score->BonusHonor); - } - else - { - Player* player = ObjectAccessor::FindPlayer(itr2->first); - uint32 team = bg->GetPlayerTeam(itr2->first); - if (!team && player) - team = player->GetBGTeam(); - *data << uint8(team == ALLIANCE ? 1 : 0); // green or yellow - } - *data << uint32(score->DamageDone); // damage done - *data << uint32(score->HealingDone); // healing done - switch (bg->GetTypeID(true)) // battleground specific things - { - case BATTLEGROUND_RB: - switch (bg->GetMapId()) - { - case 489: - *data << uint32(0x00000002); // count of next fields - *data << uint32(((BattlegroundWGScore*)score)->FlagCaptures); // flag captures - *data << uint32(((BattlegroundWGScore*)score)->FlagReturns); // flag returns - break; - case 566: - *data << uint32(0x00000001); // count of next fields - *data << uint32(((BattlegroundEYScore*)score)->FlagCaptures); // flag captures - break; - case 529: - *data << uint32(0x00000002); // count of next fields - *data << uint32(((BattlegroundABScore*)score)->BasesAssaulted); // bases asssulted - *data << uint32(((BattlegroundABScore*)score)->BasesDefended); // bases defended - break; - case 30: - *data << uint32(0x00000005); // count of next fields - *data << uint32(((BattlegroundAVScore*)score)->GraveyardsAssaulted); // GraveyardsAssaulted - *data << uint32(((BattlegroundAVScore*)score)->GraveyardsDefended); // GraveyardsDefended - *data << uint32(((BattlegroundAVScore*)score)->TowersAssaulted); // TowersAssaulted - *data << uint32(((BattlegroundAVScore*)score)->TowersDefended); // TowersDefended - *data << uint32(((BattlegroundAVScore*)score)->MinesCaptured); // MinesCaptured - break; - case 607: - *data << uint32(0x00000002); // count of next fields - *data << uint32(((BattlegroundSAScore*)score)->demolishers_destroyed); - *data << uint32(((BattlegroundSAScore*)score)->gates_destroyed); - break; - case 628: // IC - *data << uint32(0x00000002); // count of next fields - *data << uint32(((BattlegroundICScore*)score)->BasesAssaulted); // bases asssulted - *data << uint32(((BattlegroundICScore*)score)->BasesDefended); // bases defended - default: - *data << uint32(0); - break; - } - break; - case BATTLEGROUND_AV: - *data << uint32(0x00000005); // count of next fields - *data << uint32(((BattlegroundAVScore*)score)->GraveyardsAssaulted); // GraveyardsAssaulted - *data << uint32(((BattlegroundAVScore*)score)->GraveyardsDefended); // GraveyardsDefended - *data << uint32(((BattlegroundAVScore*)score)->TowersAssaulted); // TowersAssaulted - *data << uint32(((BattlegroundAVScore*)score)->TowersDefended); // TowersDefended - *data << uint32(((BattlegroundAVScore*)score)->MinesCaptured); // MinesCaptured - break; - case BATTLEGROUND_WS: - *data << uint32(0x00000002); // count of next fields - *data << uint32(((BattlegroundWGScore*)score)->FlagCaptures); // flag captures - *data << uint32(((BattlegroundWGScore*)score)->FlagReturns); // flag returns - break; - case BATTLEGROUND_AB: - *data << uint32(0x00000002); // count of next fields - *data << uint32(((BattlegroundABScore*)score)->BasesAssaulted); // bases assaulted - *data << uint32(((BattlegroundABScore*)score)->BasesDefended); // bases defended - break; - case BATTLEGROUND_EY: - *data << uint32(0x00000001); // count of next fields - *data << uint32(((BattlegroundEYScore*)score)->FlagCaptures); // flag captures - break; - case BATTLEGROUND_SA: - *data << uint32(0x00000002); // count of next fields - *data << uint32(((BattlegroundSAScore*)score)->demolishers_destroyed); - *data << uint32(((BattlegroundSAScore*)score)->gates_destroyed); - break; - case BATTLEGROUND_IC: - *data << uint32(0x00000002); // count of next fields - *data << uint32(((BattlegroundICScore*)score)->BasesAssaulted); // bases assaulted - *data << uint32(((BattlegroundICScore*)score)->BasesDefended); // bases defended - break; - case BATTLEGROUND_NA: - case BATTLEGROUND_BE: - case BATTLEGROUND_AA: - case BATTLEGROUND_RL: - case BATTLEGROUND_DS: - case BATTLEGROUND_RV: - *data << uint32(0); - break; - default: - TC_LOG_DEBUG("network", "Unhandled MSG_PVP_LOG_DATA for BG id %u", bg->GetTypeID()); - *data << uint32(0); - break; - } - // should never happen - if (++scoreCount >= bg->GetMaxPlayers() && itr != bg->GetPlayerScoresEnd()) - { - TC_LOG_ERROR("bg.battleground", "Battleground %u scoreboard has more entries (%u) than allowed players in this bg (%u)", bg->GetTypeID(true), bg->GetPlayerScoresSize(), bg->GetMaxPlayers()); - break; - } - } - - data->put(wpos, scoreCount); -} - void BattlegroundMgr::BuildGroupJoinedBattlegroundPacket(WorldPacket* data, GroupJoinBattlegroundResult result) { data->Initialize(SMSG_GROUP_JOINED_BATTLEGROUND, 4); diff --git a/src/server/game/Battlegrounds/BattlegroundMgr.h b/src/server/game/Battlegrounds/BattlegroundMgr.h index 786e664c3f..b57efc045d 100644 --- a/src/server/game/Battlegrounds/BattlegroundMgr.h +++ b/src/server/game/Battlegrounds/BattlegroundMgr.h @@ -79,7 +79,6 @@ class BattlegroundMgr void BuildBattlegroundListPacket(WorldPacket* data, uint64 guid, Player* player, BattlegroundTypeId bgTypeId, uint8 fromWhere); void BuildGroupJoinedBattlegroundPacket(WorldPacket* data, GroupJoinBattlegroundResult result); void BuildUpdateWorldStatePacket(WorldPacket* data, uint32 field, uint32 value); - void BuildPvpLogDataPacket(WorldPacket* data, Battleground* bg); void BuildBattlegroundStatusPacket(WorldPacket* data, Battleground* bg, uint8 queueSlot, uint8 statusId, uint32 time1, uint32 time2, uint8 arenaType, uint32 arenaFaction); void BuildPlaySoundPacket(WorldPacket* data, uint32 soundId); void SendAreaSpiritHealerQueryOpcode(Player* player, Battleground* bg, uint64 guid); diff --git a/src/server/game/Battlegrounds/BattlegroundScore.h b/src/server/game/Battlegrounds/BattlegroundScore.h new file mode 100644 index 0000000000..7fedc4dbc4 --- /dev/null +++ b/src/server/game/Battlegrounds/BattlegroundScore.h @@ -0,0 +1,120 @@ +/* + * Copyright (C) 2008-2014 TrinityCore + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +#ifndef TRINITY_BATTLEGROUND_SCORE_H +#define TRINITY_BATTLEGROUND_SCORE_H + +#include "WorldPacket.h" + +enum ScoreType +{ + SCORE_KILLING_BLOWS = 1, + SCORE_DEATHS = 2, + SCORE_HONORABLE_KILLS = 3, + SCORE_BONUS_HONOR = 4, + SCORE_DAMAGE_DONE = 5, + SCORE_HEALING_DONE = 6, + + // WS and EY + SCORE_FLAG_CAPTURES = 7, + SCORE_FLAG_RETURNS = 8, + + // AB and IC + SCORE_BASES_ASSAULTED = 9, + SCORE_BASES_DEFENDED = 10, + + // AV + SCORE_GRAVEYARDS_ASSAULTED = 11, + SCORE_GRAVEYARDS_DEFENDED = 12, + SCORE_TOWERS_ASSAULTED = 13, + SCORE_TOWERS_DEFENDED = 14, + SCORE_MINES_CAPTURED = 15, + + // SOTA + SCORE_DESTROYED_DEMOLISHER = 16, + SCORE_DESTROYED_WALL = 17 +}; + +struct BattlegroundScore +{ + friend class Battleground; + + protected: + BattlegroundScore(uint64 playerGuid) : PlayerGuid(playerGuid), KillingBlows(0), Deaths(0), + HonorableKills(0), BonusHonor(0), DamageDone(0), HealingDone(0) { } + + virtual ~BattlegroundScore() { } + + virtual void UpdateScore(uint32 type, uint32 value) + { + switch (type) + { + case SCORE_KILLING_BLOWS: // Killing blows + KillingBlows += value; + break; + case SCORE_DEATHS: // Deaths + Deaths += value; + break; + case SCORE_HONORABLE_KILLS: // Honorable kills + HonorableKills += value; + break; + case SCORE_BONUS_HONOR: // Honor bonus + BonusHonor += value; + break; + case SCORE_DAMAGE_DONE: // Damage Done + DamageDone += value; + break; + case SCORE_HEALING_DONE: // Healing Done + HealingDone += value; + break; + default: + ASSERT(false && "Not implemented Battleground score type!"); + break; + } + } + + virtual void AppendToPacket(WorldPacket& data) + { + data << uint64(PlayerGuid); + + data << uint32(KillingBlows); + data << uint32(HonorableKills); + data << uint32(Deaths); + data << uint32(BonusHonor); + data << uint32(DamageDone); + data << uint32(HealingDone); + + BuildObjectivesBlock(data); + } + + virtual void BuildObjectivesBlock(WorldPacket& /*data*/) { } + + // For Logging purpose + virtual std::string ToString() const { return ""; } + + uint64 PlayerGuid; + + // Default score, present in every type + uint32 KillingBlows; + uint32 Deaths; + uint32 HonorableKills; + uint32 BonusHonor; + uint32 DamageDone; + uint32 HealingDone; +}; + +#endif // TRINITY_BATTLEGROUND_SCORE_H diff --git a/src/server/game/Battlegrounds/Zones/BattlegroundAB.cpp b/src/server/game/Battlegrounds/Zones/BattlegroundAB.cpp index 38b0e3e084..2622ab9501 100644 --- a/src/server/game/Battlegrounds/Zones/BattlegroundAB.cpp +++ b/src/server/game/Battlegrounds/Zones/BattlegroundAB.cpp @@ -224,15 +224,11 @@ void BattlegroundAB::StartingEventOpenDoors() void BattlegroundAB::AddPlayer(Player* player) { Battleground::AddPlayer(player); - //create score and add it to map, default values are set in the constructor - BattlegroundABScore* sc = new BattlegroundABScore; - - PlayerScores[player->GetGUID()] = sc; + PlayerScores[player->GetGUIDLow()] = new BattlegroundABScore(player->GetGUID()); } void BattlegroundAB::RemovePlayer(Player* /*player*/, uint64 /*guid*/, uint32 /*team*/) { - } void BattlegroundAB::HandleAreaTrigger(Player* player, uint32 trigger) @@ -696,26 +692,23 @@ WorldSafeLocsEntry const* BattlegroundAB::GetClosestGraveYard(Player* player) return good_entry; } -void BattlegroundAB::UpdatePlayerScore(Player* Source, uint32 type, uint32 value, bool doAddHonor) +bool BattlegroundAB::UpdatePlayerScore(Player* player, uint32 type, uint32 value, bool doAddHonor) { - BattlegroundScoreMap::iterator itr = PlayerScores.find(Source->GetGUID()); - if (itr == PlayerScores.end()) // player not found... - return; + if (!Battleground::UpdatePlayerScore(player, type, value, doAddHonor)) + return false; switch (type) { case SCORE_BASES_ASSAULTED: - ((BattlegroundABScore*)itr->second)->BasesAssaulted += value; - Source->UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_BG_OBJECTIVE_CAPTURE, AB_OBJECTIVE_ASSAULT_BASE); + player->UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_BG_OBJECTIVE_CAPTURE, AB_OBJECTIVE_ASSAULT_BASE); break; case SCORE_BASES_DEFENDED: - ((BattlegroundABScore*)itr->second)->BasesDefended += value; - Source->UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_BG_OBJECTIVE_CAPTURE, AB_OBJECTIVE_DEFEND_BASE); + player->UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_BG_OBJECTIVE_CAPTURE, AB_OBJECTIVE_DEFEND_BASE); break; default: - Battleground::UpdatePlayerScore(Source, type, value, doAddHonor); break; } + return true; } bool BattlegroundAB::IsAllNodesControlledByTeam(uint32 team) const diff --git a/src/server/game/Battlegrounds/Zones/BattlegroundAB.h b/src/server/game/Battlegrounds/Zones/BattlegroundAB.h index 9abc7627b2..a6b4be10fd 100644 --- a/src/server/game/Battlegrounds/Zones/BattlegroundAB.h +++ b/src/server/game/Battlegrounds/Zones/BattlegroundAB.h @@ -20,6 +20,7 @@ #define __BATTLEGROUNDAB_H #include "Battleground.h" +#include "BattlegroundScore.h" #include "Object.h" enum BG_AB_WorldStates @@ -236,12 +237,38 @@ struct BG_AB_BannerTimer uint8 teamIndex; }; -struct BattlegroundABScore : public BattlegroundScore +struct BattlegroundABScore final : public BattlegroundScore { - BattlegroundABScore(): BasesAssaulted(0), BasesDefended(0) { } - ~BattlegroundABScore() { } - uint32 BasesAssaulted; - uint32 BasesDefended; + friend class BattlegroundAB; + + protected: + BattlegroundABScore(uint64 playerGuid) : BattlegroundScore(playerGuid), BasesAssaulted(0), BasesDefended(0) { } + + void UpdateScore(uint32 type, uint32 value) override + { + switch (type) + { + case SCORE_BASES_ASSAULTED: + BasesAssaulted += value; + break; + case SCORE_BASES_DEFENDED: + BasesDefended += value; + break; + default: + BattlegroundScore::UpdateScore(type, value); + break; + } + } + + void BuildObjectivesBlock(WorldPacket& data) final + { + data << uint32(2); + data << uint32(BasesAssaulted); + data << uint32(BasesDefended); + } + + uint32 BasesAssaulted; + uint32 BasesDefended; }; class BattlegroundAB : public Battleground @@ -261,7 +288,7 @@ class BattlegroundAB : public Battleground WorldSafeLocsEntry const* GetClosestGraveYard(Player* player); /* Scorekeeping */ - void UpdatePlayerScore(Player* Source, uint32 type, uint32 value, bool doAddHonor = true); + bool UpdatePlayerScore(Player* player, uint32 type, uint32 value, bool doAddHonor = true) override; void FillInitialWorldStates(WorldPacket& data); diff --git a/src/server/game/Battlegrounds/Zones/BattlegroundAV.cpp b/src/server/game/Battlegrounds/Zones/BattlegroundAV.cpp index 16576aa888..070d79bc8e 100644 --- a/src/server/game/Battlegrounds/Zones/BattlegroundAV.cpp +++ b/src/server/game/Battlegrounds/Zones/BattlegroundAV.cpp @@ -459,9 +459,8 @@ void BattlegroundAV::StartingEventOpenDoors() void BattlegroundAV::AddPlayer(Player* player) { Battleground::AddPlayer(player); - //create score and add it to map, default values are set in constructor - BattlegroundAVScore* sc = new BattlegroundAVScore; - PlayerScores[player->GetGUID()] = sc; + PlayerScores[player->GetGUIDLow()] = new BattlegroundAVScore(player->GetGUID()); + if (m_MaxLevel == 0) m_MaxLevel=(player->getLevel()%10 == 0)? player->getLevel() : (player->getLevel()-(player->getLevel()%10))+10; /// @todo just look at the code \^_^/ --but queue-info should provide this information.. } @@ -553,43 +552,29 @@ void BattlegroundAV::HandleAreaTrigger(Player* player, uint32 trigger) } } -void BattlegroundAV::UpdatePlayerScore(Player* Source, uint32 type, uint32 value, bool doAddHonor) +bool BattlegroundAV::UpdatePlayerScore(Player* player, uint32 type, uint32 value, bool doAddHonor) { - BattlegroundScoreMap::iterator itr = PlayerScores.find(Source->GetGUID()); - if (itr == PlayerScores.end()) // player not found... - return; + if (!Battleground::UpdatePlayerScore(player, type, value, doAddHonor)) + return false; switch (type) { case SCORE_GRAVEYARDS_ASSAULTED: - ((BattlegroundAVScore*)itr->second)->GraveyardsAssaulted += value; - Source->UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_BG_OBJECTIVE_CAPTURE, AV_OBJECTIVE_ASSAULT_GRAVEYARD); + player->UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_BG_OBJECTIVE_CAPTURE, AV_OBJECTIVE_ASSAULT_GRAVEYARD); break; case SCORE_GRAVEYARDS_DEFENDED: - ((BattlegroundAVScore*)itr->second)->GraveyardsDefended += value; - Source->UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_BG_OBJECTIVE_CAPTURE, AV_OBJECTIVE_DEFEND_GRAVEYARD); + player->UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_BG_OBJECTIVE_CAPTURE, AV_OBJECTIVE_DEFEND_GRAVEYARD); break; case SCORE_TOWERS_ASSAULTED: - ((BattlegroundAVScore*)itr->second)->TowersAssaulted += value; - Source->UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_BG_OBJECTIVE_CAPTURE, AV_OBJECTIVE_ASSAULT_TOWER); + player->UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_BG_OBJECTIVE_CAPTURE, AV_OBJECTIVE_ASSAULT_TOWER); break; case SCORE_TOWERS_DEFENDED: - ((BattlegroundAVScore*)itr->second)->TowersDefended += value; - Source->UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_BG_OBJECTIVE_CAPTURE, AV_OBJECTIVE_DEFEND_TOWER); - break; - case SCORE_MINES_CAPTURED: - ((BattlegroundAVScore*)itr->second)->MinesCaptured += value; - break; - case SCORE_LEADERS_KILLED: - ((BattlegroundAVScore*)itr->second)->LeadersKilled += value; - break; - case SCORE_SECONDARY_OBJECTIVES: - ((BattlegroundAVScore*)itr->second)->SecondaryObjectives += value; + player->UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_BG_OBJECTIVE_CAPTURE, AV_OBJECTIVE_DEFEND_TOWER); break; default: - Battleground::UpdatePlayerScore(Source, type, value, doAddHonor); break; } + return true; } void BattlegroundAV::EventPlayerDestroyedPoint(BG_AV_Nodes node) diff --git a/src/server/game/Battlegrounds/Zones/BattlegroundAV.h b/src/server/game/Battlegrounds/Zones/BattlegroundAV.h index 03dd0ffcf5..b84b146004 100644 --- a/src/server/game/Battlegrounds/Zones/BattlegroundAV.h +++ b/src/server/game/Battlegrounds/Zones/BattlegroundAV.h @@ -20,6 +20,7 @@ #define __BATTLEGROUNDAV_H #include "Battleground.h" +#include "BattlegroundScore.h" #include "Object.h" #define LANG_BG_AV_A_CAPTAIN_BUFF "Begone. Uncouth scum! The Alliance shall prevail in Alterac Valley!" @@ -1525,18 +1526,53 @@ struct BG_AV_NodeInfo inline BG_AV_Nodes &operator++(BG_AV_Nodes &i){ return i = BG_AV_Nodes(i + 1); } -struct BattlegroundAVScore : public BattlegroundScore +struct BattlegroundAVScore final : public BattlegroundScore { - BattlegroundAVScore() : GraveyardsAssaulted(0), GraveyardsDefended(0), TowersAssaulted(0), - TowersDefended(0), MinesCaptured(0), LeadersKilled(0), SecondaryObjectives(0) { } - ~BattlegroundAVScore() { } - uint32 GraveyardsAssaulted; - uint32 GraveyardsDefended; - uint32 TowersAssaulted; - uint32 TowersDefended; - uint32 MinesCaptured; - uint32 LeadersKilled; - uint32 SecondaryObjectives; + friend class BattlegroundAV; + + protected: + BattlegroundAVScore(uint64 playerGuid) : BattlegroundScore(playerGuid), GraveyardsAssaulted(0), GraveyardsDefended(0), TowersAssaulted(0), TowersDefended(0), MinesCaptured(0) { } + + void UpdateScore(uint32 type, uint32 value) override + { + switch (type) + { + case SCORE_GRAVEYARDS_ASSAULTED: + GraveyardsAssaulted += value; + break; + case SCORE_GRAVEYARDS_DEFENDED: + GraveyardsDefended += value; + break; + case SCORE_TOWERS_ASSAULTED: + TowersAssaulted += value; + break; + case SCORE_TOWERS_DEFENDED: + TowersDefended += value; + break; + case SCORE_MINES_CAPTURED: + MinesCaptured += value; + break; + default: + BattlegroundScore::UpdateScore(type, value); + break; + } + } + + void BuildObjectivesBlock(WorldPacket& data) final + { + data << uint32(5); // Objectives Count + data << uint32(GraveyardsAssaulted); + data << uint32(GraveyardsDefended); + data << uint32(TowersAssaulted); + data << uint32(TowersDefended); + data << uint32(MinesCaptured); + } + + uint32 GraveyardsAssaulted; + uint32 GraveyardsDefended; + uint32 TowersAssaulted; + uint32 TowersDefended; + uint32 MinesCaptured; }; class BattlegroundAV : public Battleground @@ -1557,7 +1593,7 @@ class BattlegroundAV : public Battleground /*general stuff*/ void UpdateScore(uint16 team, int16 points); - void UpdatePlayerScore(Player* player, uint32 type, uint32 value, bool doAddHonor = true); + bool UpdatePlayerScore(Player* player, uint32 type, uint32 value, bool doAddHonor = true) override; /*handlestuff*/ //these are functions which get called from extern void EventPlayerClickedOnFlag(Player* source, GameObject* target_obj); diff --git a/src/server/game/Battlegrounds/Zones/BattlegroundBE.cpp b/src/server/game/Battlegrounds/Zones/BattlegroundBE.cpp index c89fc57b8a..548e0bf463 100644 --- a/src/server/game/Battlegrounds/Zones/BattlegroundBE.cpp +++ b/src/server/game/Battlegrounds/Zones/BattlegroundBE.cpp @@ -16,6 +16,7 @@ * with this program. If not, see . */ +#include "ArenaScore.h" #include "BattlegroundBE.h" #include "Language.h" #include "Object.h" @@ -64,7 +65,7 @@ void BattlegroundBE::StartingEventOpenDoors() void BattlegroundBE::AddPlayer(Player* player) { Battleground::AddPlayer(player); - PlayerScores[player->GetGUID()] = new BattlegroundScore; + PlayerScores[player->GetGUIDLow()] = new ArenaScore(player->GetGUID(), player->GetBGTeam()); UpdateArenaWorldState(); } @@ -139,13 +140,3 @@ bool BattlegroundBE::SetupBattleground() return true; } - -void BattlegroundBE::UpdatePlayerScore(Player* Source, uint32 type, uint32 value, bool doAddHonor) -{ - BattlegroundScoreMap::iterator itr = PlayerScores.find(Source->GetGUID()); - if (itr == PlayerScores.end()) // player not found... - return; - - //there is nothing special in this score - Battleground::UpdatePlayerScore(Source, type, value, doAddHonor); -} diff --git a/src/server/game/Battlegrounds/Zones/BattlegroundBE.h b/src/server/game/Battlegrounds/Zones/BattlegroundBE.h index be801dfff2..6fd4dc37fc 100644 --- a/src/server/game/Battlegrounds/Zones/BattlegroundBE.h +++ b/src/server/game/Battlegrounds/Zones/BattlegroundBE.h @@ -59,8 +59,5 @@ class BattlegroundBE : public Battleground void Reset(); void FillInitialWorldStates(WorldPacket &d); void HandleKillPlayer(Player* player, Player* killer); - - /* Scorekeeping */ - void UpdatePlayerScore(Player* Source, uint32 type, uint32 value, bool doAddHonor = true); }; #endif diff --git a/src/server/game/Battlegrounds/Zones/BattlegroundDS.cpp b/src/server/game/Battlegrounds/Zones/BattlegroundDS.cpp index 9e9e82b32b..1d6970f831 100644 --- a/src/server/game/Battlegrounds/Zones/BattlegroundDS.cpp +++ b/src/server/game/Battlegrounds/Zones/BattlegroundDS.cpp @@ -16,6 +16,7 @@ * with this program. If not, see . */ +#include "ArenaScore.h" #include "BattlegroundDS.h" #include "Creature.h" #include "GameObject.h" @@ -152,7 +153,7 @@ void BattlegroundDS::StartingEventOpenDoors() void BattlegroundDS::AddPlayer(Player* player) { Battleground::AddPlayer(player); - PlayerScores[player->GetGUID()] = new BattlegroundScore; + PlayerScores[player->GetGUIDLow()] = new ArenaScore(player->GetGUID(), player->GetBGTeam()); UpdateArenaWorldState(); } diff --git a/src/server/game/Battlegrounds/Zones/BattlegroundEY.cpp b/src/server/game/Battlegrounds/Zones/BattlegroundEY.cpp index 213a91bea8..ca96140f5d 100644 --- a/src/server/game/Battlegrounds/Zones/BattlegroundEY.cpp +++ b/src/server/game/Battlegrounds/Zones/BattlegroundEY.cpp @@ -363,12 +363,9 @@ void BattlegroundEY::UpdatePointsIcons(uint32 Team, uint32 Point) void BattlegroundEY::AddPlayer(Player* player) { Battleground::AddPlayer(player); - //create score and add it to map - BattlegroundEYScore* sc = new BattlegroundEYScore; + PlayerScores[player->GetGUIDLow()] = new BattlegroundEYScore(player->GetGUID()); m_PlayersNearPoint[EY_POINTS_MAX].push_back(player->GetGUID()); - - PlayerScores[player->GetGUID()] = sc; } void BattlegroundEY::RemovePlayer(Player* player, uint64 guid, uint32 /*team*/) @@ -832,22 +829,20 @@ void BattlegroundEY::EventPlayerCapturedFlag(Player* player, uint32 BgObjectType UpdatePlayerScore(player, SCORE_FLAG_CAPTURES, 1); } -void BattlegroundEY::UpdatePlayerScore(Player* player, uint32 type, uint32 value, bool doAddHonor) +bool BattlegroundEY::UpdatePlayerScore(Player* player, uint32 type, uint32 value, bool doAddHonor) { - BattlegroundScoreMap::iterator itr = PlayerScores.find(player->GetGUID()); - if (itr == PlayerScores.end()) // player not found - return; + if (!Battleground::UpdatePlayerScore(player, type, value, doAddHonor)) + return false; switch (type) { - case SCORE_FLAG_CAPTURES: // flags captured - ((BattlegroundEYScore*)itr->second)->FlagCaptures += value; + case SCORE_FLAG_CAPTURES: player->UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_BG_OBJECTIVE_CAPTURE, EY_OBJECTIVE_CAPTURE_FLAG); break; default: - Battleground::UpdatePlayerScore(player, type, value, doAddHonor); break; } + return true; } void BattlegroundEY::FillInitialWorldStates(WorldPacket& data) diff --git a/src/server/game/Battlegrounds/Zones/BattlegroundEY.h b/src/server/game/Battlegrounds/Zones/BattlegroundEY.h index 9e5088d7ba..056deb3498 100644 --- a/src/server/game/Battlegrounds/Zones/BattlegroundEY.h +++ b/src/server/game/Battlegrounds/Zones/BattlegroundEY.h @@ -20,6 +20,7 @@ #define __BATTLEGROUNDEY_H #include "Battleground.h" +#include "BattlegroundScore.h" #include "Language.h" #include "Object.h" @@ -322,11 +323,33 @@ const BattlegroundEYCapturingPointStruct m_CapturingPointTypes[EY_POINTS_MAX] = BattlegroundEYCapturingPointStruct(BG_EY_OBJECT_N_BANNER_MAGE_TOWER_CENTER, BG_EY_OBJECT_A_BANNER_MAGE_TOWER_CENTER, LANG_BG_EY_HAS_TAKEN_A_M_TOWER, BG_EY_OBJECT_H_BANNER_MAGE_TOWER_CENTER, LANG_BG_EY_HAS_TAKEN_H_M_TOWER, EY_GRAVEYARD_MAGE_TOWER) }; -struct BattlegroundEYScore : public BattlegroundScore +struct BattlegroundEYScore final : public BattlegroundScore { - BattlegroundEYScore() : FlagCaptures(0) { } - ~BattlegroundEYScore() { } - uint32 FlagCaptures; + friend class BattlegroundEY; + + protected: + BattlegroundEYScore(uint64 playerGuid) : BattlegroundScore(playerGuid), FlagCaptures(0) { } + + void UpdateScore(uint32 type, uint32 value) override + { + switch (type) + { + case SCORE_FLAG_CAPTURES: // Flags captured + FlagCaptures += value; + break; + default: + BattlegroundScore::UpdateScore(type, value); + break; + } + } + + void BuildObjectivesBlock(WorldPacket& data) final + { + data << uint32(1); // Objectives Count + data << uint32(FlagCaptures); + } + + uint32 FlagCaptures; }; class BattlegroundEY : public Battleground @@ -357,7 +380,7 @@ class BattlegroundEY : public Battleground void Reset(); void UpdateTeamScore(uint32 Team); void EndBattleground(uint32 winner); - void UpdatePlayerScore(Player* Source, uint32 type, uint32 value, bool doAddHonor = true); + bool UpdatePlayerScore(Player* player, uint32 type, uint32 value, bool doAddHonor = true) override; void FillInitialWorldStates(WorldPacket& data); void SetDroppedFlagGUID(uint64 guid, int32 /*TeamID*/ = -1) { m_DroppedFlagGUID = guid;} uint64 GetDroppedFlagGUID() const { return m_DroppedFlagGUID;} diff --git a/src/server/game/Battlegrounds/Zones/BattlegroundIC.cpp b/src/server/game/Battlegrounds/Zones/BattlegroundIC.cpp index faa71fd2ff..da0b00af40 100644 --- a/src/server/game/Battlegrounds/Zones/BattlegroundIC.cpp +++ b/src/server/game/Battlegrounds/Zones/BattlegroundIC.cpp @@ -273,7 +273,7 @@ void BattlegroundIC::StartingEventOpenDoors() void BattlegroundIC::AddPlayer(Player* player) { Battleground::AddPlayer(player); - PlayerScores[player->GetGUID()] = new BattlegroundICScore; + PlayerScores[player->GetGUIDLow()] = new BattlegroundICScore(player->GetGUID()); if (nodePoint[NODE_TYPE_QUARRY].nodeState == (player->GetTeamId() == TEAM_ALLIANCE ? NODE_STATE_CONTROLLED_A : NODE_STATE_CONTROLLED_H)) player->CastSpell(player, SPELL_QUARRY, true); @@ -314,27 +314,6 @@ void BattlegroundIC::HandleAreaTrigger(Player* player, uint32 trigger) } } -void BattlegroundIC::UpdatePlayerScore(Player* player, uint32 type, uint32 value, bool doAddHonor) -{ - std::map::iterator itr = PlayerScores.find(player->GetGUID()); - - if (itr == PlayerScores.end()) // player not found... - return; - - switch (type) - { - case SCORE_BASES_ASSAULTED: - ((BattlegroundICScore*)itr->second)->BasesAssaulted += value; - break; - case SCORE_BASES_DEFENDED: - ((BattlegroundICScore*)itr->second)->BasesDefended += value; - break; - default: - Battleground::UpdatePlayerScore(player, type, value, doAddHonor); - break; - } -} - void BattlegroundIC::FillInitialWorldStates(WorldPacket& data) { data << uint32(BG_IC_ALLIANCE_RENFORT_SET) << uint32(1); diff --git a/src/server/game/Battlegrounds/Zones/BattlegroundIC.h b/src/server/game/Battlegrounds/Zones/BattlegroundIC.h index 091a75e744..0b317cabef 100644 --- a/src/server/game/Battlegrounds/Zones/BattlegroundIC.h +++ b/src/server/game/Battlegrounds/Zones/BattlegroundIC.h @@ -20,6 +20,7 @@ #define __BATTLEGROUNDIC_H #include "Battleground.h" +#include "BattlegroundScore.h" #include "Language.h" #include "Object.h" @@ -847,12 +848,38 @@ enum HonorRewards WINNER_HONOR_AMOUNT = 500 }; -struct BattlegroundICScore : public BattlegroundScore +struct BattlegroundICScore final : public BattlegroundScore { - BattlegroundICScore() : BasesAssaulted(0), BasesDefended(0) { } - ~BattlegroundICScore() { } - uint32 BasesAssaulted; - uint32 BasesDefended; + friend class BattlegroundIC; + + protected: + BattlegroundICScore(uint64 playerGuid) : BattlegroundScore(playerGuid), BasesAssaulted(0), BasesDefended(0) { } + + void UpdateScore(uint32 type, uint32 value) override + { + switch (type) + { + case SCORE_BASES_ASSAULTED: + BasesAssaulted += value; + break; + case SCORE_BASES_DEFENDED: + BasesDefended += value; + break; + default: + BattlegroundScore::UpdateScore(type, value); + break; + } + } + + void BuildObjectivesBlock(WorldPacket& data) final + { + data << uint32(2); // Objectives Count + data << uint32(BasesAssaulted); + data << uint32(BasesDefended); + } + + uint32 BasesAssaulted; + uint32 BasesDefended; }; class BattlegroundIC : public Battleground @@ -881,8 +908,6 @@ class BattlegroundIC : public Battleground WorldSafeLocsEntry const* GetClosestGraveYard(Player* player); /* Scorekeeping */ - void UpdatePlayerScore(Player* player, uint32 type, uint32 value, bool doAddHonor = true); - void FillInitialWorldStates(WorldPacket& data); void DoAction(uint32 action, uint64 var); @@ -894,6 +919,7 @@ class BattlegroundIC : public Battleground bool IsAllNodesControlledByTeam(uint32 team) const; bool IsSpellAllowed(uint32 spellId, Player const* player) const; + private: uint32 closeFortressDoorsTimer; bool doorsClosed; diff --git a/src/server/game/Battlegrounds/Zones/BattlegroundNA.cpp b/src/server/game/Battlegrounds/Zones/BattlegroundNA.cpp index 70a940ec85..82fcb2f6f9 100644 --- a/src/server/game/Battlegrounds/Zones/BattlegroundNA.cpp +++ b/src/server/game/Battlegrounds/Zones/BattlegroundNA.cpp @@ -16,6 +16,7 @@ * with this program. If not, see . */ +#include "ArenaScore.h" #include "BattlegroundNA.h" #include "Language.h" #include "Object.h" @@ -61,7 +62,7 @@ void BattlegroundNA::StartingEventOpenDoors() void BattlegroundNA::AddPlayer(Player* player) { Battleground::AddPlayer(player); - PlayerScores[player->GetGUID()] = new BattlegroundScore; + PlayerScores[player->GetGUIDLow()] = new ArenaScore(player->GetGUID(), player->GetBGTeam()); UpdateArenaWorldState(); } diff --git a/src/server/game/Battlegrounds/Zones/BattlegroundRL.cpp b/src/server/game/Battlegrounds/Zones/BattlegroundRL.cpp index 5f77c57c06..712d9a6e29 100644 --- a/src/server/game/Battlegrounds/Zones/BattlegroundRL.cpp +++ b/src/server/game/Battlegrounds/Zones/BattlegroundRL.cpp @@ -16,6 +16,7 @@ * with this program. If not, see . */ +#include "ArenaScore.h" #include "BattlegroundRL.h" #include "Language.h" #include "Object.h" @@ -61,7 +62,7 @@ void BattlegroundRL::StartingEventOpenDoors() void BattlegroundRL::AddPlayer(Player* player) { Battleground::AddPlayer(player); - PlayerScores[player->GetGUID()] = new BattlegroundScore; + PlayerScores[player->GetGUIDLow()] = new ArenaScore(player->GetGUID(), player->GetBGTeam()); UpdateArenaWorldState(); } diff --git a/src/server/game/Battlegrounds/Zones/BattlegroundRV.cpp b/src/server/game/Battlegrounds/Zones/BattlegroundRV.cpp index dd61b8b9e6..1059124d04 100644 --- a/src/server/game/Battlegrounds/Zones/BattlegroundRV.cpp +++ b/src/server/game/Battlegrounds/Zones/BattlegroundRV.cpp @@ -16,6 +16,7 @@ * with this program. If not, see . */ +#include "ArenaScore.h" #include "Battleground.h" #include "BattlegroundRV.h" #include "ObjectAccessor.h" @@ -99,7 +100,7 @@ void BattlegroundRV::StartingEventOpenDoors() void BattlegroundRV::AddPlayer(Player* player) { Battleground::AddPlayer(player); - PlayerScores[player->GetGUID()] = new BattlegroundScore; + PlayerScores[player->GetGUIDLow()] = new ArenaScore(player->GetGUID(), player->GetBGTeam()); UpdateWorldState(BG_RV_WORLD_STATE_A, GetAlivePlayersCountByTeam(ALLIANCE)); UpdateWorldState(BG_RV_WORLD_STATE_H, GetAlivePlayersCountByTeam(HORDE)); diff --git a/src/server/game/Battlegrounds/Zones/BattlegroundSA.cpp b/src/server/game/Battlegrounds/Zones/BattlegroundSA.cpp index 0966ddd19b..7e90c0b3db 100644 --- a/src/server/game/Battlegrounds/Zones/BattlegroundSA.cpp +++ b/src/server/game/Battlegrounds/Zones/BattlegroundSA.cpp @@ -454,9 +454,7 @@ void BattlegroundSA::FillInitialWorldStates(WorldPacket& data) void BattlegroundSA::AddPlayer(Player* player) { Battleground::AddPlayer(player); - //create score and add it to map, default values are set in constructor - BattlegroundSAScore* sc = new BattlegroundSAScore; - PlayerScores[player->GetGUID()] = sc; + PlayerScores[player->GetGUIDLow()] = new BattlegroundSAScore(player->GetGUID()); SendTransportInit(player); @@ -493,20 +491,6 @@ void BattlegroundSA::HandleAreaTrigger(Player* /*Source*/, uint32 /*Trigger*/) return; } -void BattlegroundSA::UpdatePlayerScore(Player* Source, uint32 type, uint32 value, bool doAddHonor) -{ - BattlegroundScoreMap::iterator itr = PlayerScores.find(Source->GetGUID()); - if (itr == PlayerScores.end()) // player not found... - return; - - if (type == SCORE_DESTROYED_DEMOLISHER) - ((BattlegroundSAScore*)itr->second)->demolishers_destroyed += value; - else if (type == SCORE_DESTROYED_WALL) - ((BattlegroundSAScore*)itr->second)->gates_destroyed += value; - else - Battleground::UpdatePlayerScore(Source, type, value, doAddHonor); -} - void BattlegroundSA::TeleportPlayers() { for (BattlegroundPlayerMap::const_iterator itr = GetPlayers().begin(); itr != GetPlayers().end(); ++itr) diff --git a/src/server/game/Battlegrounds/Zones/BattlegroundSA.h b/src/server/game/Battlegrounds/Zones/BattlegroundSA.h index 880da0735f..6c15b56d28 100644 --- a/src/server/game/Battlegrounds/Zones/BattlegroundSA.h +++ b/src/server/game/Battlegrounds/Zones/BattlegroundSA.h @@ -20,16 +20,9 @@ #define __BATTLEGROUNDSA_H #include "Battleground.h" +#include "BattlegroundScore.h" #include "Object.h" -struct BattlegroundSAScore : public BattlegroundScore -{ - BattlegroundSAScore() : demolishers_destroyed(0), gates_destroyed(0) { } - ~BattlegroundSAScore() { } - uint8 demolishers_destroyed; - uint8 gates_destroyed; -}; - #define BG_SA_FLAG_AMOUNT 3 #define BG_SA_DEMOLISHER_AMOUNT 4 @@ -515,6 +508,40 @@ struct BG_SA_RoundScore uint32 time; }; +struct BattlegroundSAScore final : public BattlegroundScore +{ + friend class BattlegroundSA; + + protected: + BattlegroundSAScore(uint64 playerGuid) : BattlegroundScore(playerGuid), DemolishersDestroyed(0), GatesDestroyed(0) { } + + void UpdateScore(uint32 type, uint32 value) override + { + switch (type) + { + case SCORE_DESTROYED_DEMOLISHER: + DemolishersDestroyed += value; + break; + case SCORE_DESTROYED_WALL: + GatesDestroyed += value; + break; + default: + BattlegroundScore::UpdateScore(type, value); + break; + } + } + + void BuildObjectivesBlock(WorldPacket& data) final + { + data << uint32(2); // Objectives Count + data << uint32(DemolishersDestroyed); + data << uint32(GatesDestroyed); + } + + uint32 DemolishersDestroyed; + uint32 GatesDestroyed; +}; + /// Class for manage Strand of Ancient battleground class BattlegroundSA : public Battleground { @@ -568,8 +595,6 @@ class BattlegroundSA : public Battleground void HandleAreaTrigger(Player* Source, uint32 Trigger); /* Scorekeeping */ - /// Update score board - void UpdatePlayerScore(Player* Source, uint32 type, uint32 value, bool doAddHonor = true); // Achievement: Not Even a Scratch bool CheckAchievementCriteriaMeet(uint32 criteriaId, Player const* source, Unit const* target = NULL, uint32 miscValue = 0) override; diff --git a/src/server/game/Battlegrounds/Zones/BattlegroundWS.cpp b/src/server/game/Battlegrounds/Zones/BattlegroundWS.cpp index fcd55e2e16..1faa336197 100644 --- a/src/server/game/Battlegrounds/Zones/BattlegroundWS.cpp +++ b/src/server/game/Battlegrounds/Zones/BattlegroundWS.cpp @@ -229,10 +229,7 @@ void BattlegroundWS::StartingEventOpenDoors() void BattlegroundWS::AddPlayer(Player* player) { Battleground::AddPlayer(player); - //create score and add it to map, default values are set in constructor - BattlegroundWGScore* sc = new BattlegroundWGScore; - - PlayerScores[player->GetGUID()] = sc; + PlayerScores[player->GetGUIDLow()] = new BattlegroundWGScore(player->GetGUID()); } void BattlegroundWS::RespawnFlag(uint32 Team, bool captured) @@ -790,26 +787,23 @@ void BattlegroundWS::HandleKillPlayer(Player* player, Player* killer) Battleground::HandleKillPlayer(player, killer); } -void BattlegroundWS::UpdatePlayerScore(Player* player, uint32 type, uint32 value, bool doAddHonor) +bool BattlegroundWS::UpdatePlayerScore(Player* player, uint32 type, uint32 value, bool doAddHonor) { - BattlegroundScoreMap::iterator itr = PlayerScores.find(player->GetGUID()); - if (itr == PlayerScores.end()) // player not found - return; + if (!Battleground::UpdatePlayerScore(player, type, value, doAddHonor)) + return false; switch (type) { case SCORE_FLAG_CAPTURES: // flags captured - ((BattlegroundWGScore*)itr->second)->FlagCaptures += value; player->UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_BG_OBJECTIVE_CAPTURE, WS_OBJECTIVE_CAPTURE_FLAG); break; case SCORE_FLAG_RETURNS: // flags returned - ((BattlegroundWGScore*)itr->second)->FlagReturns += value; player->UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_BG_OBJECTIVE_CAPTURE, WS_OBJECTIVE_RETURN_FLAG); break; default: - Battleground::UpdatePlayerScore(player, type, value, doAddHonor); break; } + return true; } WorldSafeLocsEntry const* BattlegroundWS::GetClosestGraveYard(Player* player) diff --git a/src/server/game/Battlegrounds/Zones/BattlegroundWS.h b/src/server/game/Battlegrounds/Zones/BattlegroundWS.h index c6c25ec52d..3d449580fb 100644 --- a/src/server/game/Battlegrounds/Zones/BattlegroundWS.h +++ b/src/server/game/Battlegrounds/Zones/BattlegroundWS.h @@ -20,6 +20,7 @@ #define __BATTLEGROUNDWS_H #include "Battleground.h" +#include "BattlegroundScore.h" enum BG_WS_TimerOrScore { @@ -146,12 +147,38 @@ enum BG_WS_Objectives #define WS_EVENT_START_BATTLE 8563 -struct BattlegroundWGScore : public BattlegroundScore +struct BattlegroundWGScore final : public BattlegroundScore { - BattlegroundWGScore() : FlagCaptures(0), FlagReturns(0) { } - ~BattlegroundWGScore() { } - uint32 FlagCaptures; - uint32 FlagReturns; + friend class BattlegroundWS; + + protected: + BattlegroundWGScore(uint64 playerGuid) : BattlegroundScore(playerGuid), FlagCaptures(0), FlagReturns(0) { } + + void UpdateScore(uint32 type, uint32 value) override + { + switch (type) + { + case SCORE_FLAG_CAPTURES: // Flags captured + FlagCaptures += value; + break; + case SCORE_FLAG_RETURNS: // Flags returned + FlagReturns += value; + break; + default: + BattlegroundScore::UpdateScore(type, value); + break; + } + } + + void BuildObjectivesBlock(WorldPacket& data) final + { + data << uint32(2); // Objectives Count + data << uint32(FlagCaptures); + data << uint32(FlagReturns); + } + + uint32 FlagCaptures; + uint32 FlagReturns; }; class BattlegroundWS : public Battleground @@ -197,7 +224,7 @@ class BattlegroundWS : public Battleground void UpdateFlagState(uint32 team, uint32 value); void SetLastFlagCapture(uint32 team) { _lastFlagCaptureTeam = team; } void UpdateTeamScore(uint32 team); - void UpdatePlayerScore(Player* player, uint32 type, uint32 value, bool doAddHonor = true); + bool UpdatePlayerScore(Player* player, uint32 type, uint32 value, bool doAddHonor = true) override; void SetDroppedFlagGUID(uint64 guid, int32 team = -1) { if (team == TEAM_ALLIANCE || team == TEAM_HORDE) diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index 8d0223d224..8653106a86 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -26,6 +26,7 @@ #include "BattlefieldWG.h" #include "Battleground.h" #include "BattlegroundMgr.h" +#include "BattlegroundScore.h" #include "CellImpl.h" #include "Channel.h" #include "ChannelMgr.h" diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp index 363738f96e..ca4780b787 100644 --- a/src/server/game/Entities/Unit/Unit.cpp +++ b/src/server/game/Entities/Unit/Unit.cpp @@ -21,6 +21,7 @@ #include "Battlefield.h" #include "BattlefieldMgr.h" #include "Battleground.h" +#include "BattlegroundScore.h" #include "CellImpl.h" #include "ConditionMgr.h" #include "CreatureAI.h" @@ -3845,8 +3846,7 @@ void Unit::RemoveAurasByType(AuraType auraType, uint64 casterGUID, Aura* except, { Aura* aura = (*iter)->GetBase(); AuraApplication * aurApp = aura->GetApplicationOfTarget(GetGUID()); - if (!aurApp) - continue; + ASSERT(aurApp); ++iter; if (aura != except && (!casterGUID || aura->GetCasterGUID() == casterGUID) diff --git a/src/server/game/Handlers/BattleGroundHandler.cpp b/src/server/game/Handlers/BattleGroundHandler.cpp index 0dfe396b65..7def6b0f46 100644 --- a/src/server/game/Handlers/BattleGroundHandler.cpp +++ b/src/server/game/Handlers/BattleGroundHandler.cpp @@ -309,7 +309,7 @@ void WorldSession::HandlePVPLogDataOpcode(WorldPacket & /*recvData*/) return; WorldPacket data; - sBattlegroundMgr->BuildPvpLogDataPacket(&data, bg); + bg->BuildPvPLogDataPacket(data); SendPacket(&data); TC_LOG_DEBUG("network", "WORLD: Sent MSG_PVP_LOG_DATA Message"); From d20a5f3df080ce44d547ee98bca875e01598cd64 Mon Sep 17 00:00:00 2001 From: joschiwald Date: Sat, 28 Jun 2014 00:57:44 +0200 Subject: [PATCH 46/80] Scripts/Commands: fixed possible client crash with .go command --- src/server/game/Globals/ObjectMgr.cpp | 4 ++-- src/server/game/Globals/ObjectMgr.h | 2 ++ src/server/scripts/Commands/cs_go.cpp | 32 +++++++++++++++++---------- 3 files changed, 24 insertions(+), 14 deletions(-) diff --git a/src/server/game/Globals/ObjectMgr.cpp b/src/server/game/Globals/ObjectMgr.cpp index 72ea1b1686..9eb7d5ec1b 100644 --- a/src/server/game/Globals/ObjectMgr.cpp +++ b/src/server/game/Globals/ObjectMgr.cpp @@ -1668,7 +1668,7 @@ void ObjectMgr::LoadCreatures() } // Skip spawnMask check for transport maps - if (!_transportMaps.count(data.mapid) && data.spawnMask & ~spawnMasks[data.mapid]) + if (!IsTransportMap(data.mapid) && data.spawnMask & ~spawnMasks[data.mapid]) TC_LOG_ERROR("sql.sql", "Table `creature` have creature (GUID: %u) that have wrong spawn mask %u including not supported difficulty modes for map (Id: %u).", guid, data.spawnMask, data.mapid); bool ok = true; @@ -2003,7 +2003,7 @@ void ObjectMgr::LoadGameobjects() data.spawnMask = fields[14].GetUInt8(); - if (!_transportMaps.count(data.mapid) && data.spawnMask & ~spawnMasks[data.mapid]) + if (!IsTransportMap(data.mapid) && data.spawnMask & ~spawnMasks[data.mapid]) TC_LOG_ERROR("sql.sql", "Table `gameobject` has gameobject (GUID: %u Entry: %u) that has wrong spawn mask %u including not supported difficulty modes for map (Id: %u), skip", guid, data.id, data.spawnMask, data.mapid); data.phaseMask = fields[15].GetUInt32(); diff --git a/src/server/game/Globals/ObjectMgr.h b/src/server/game/Globals/ObjectMgr.h index ba5940d7e1..e5e55b847d 100644 --- a/src/server/game/Globals/ObjectMgr.h +++ b/src/server/game/Globals/ObjectMgr.h @@ -1293,6 +1293,8 @@ class ObjectMgr void LoadFactionChangeSpells(); void LoadFactionChangeTitles(); + bool IsTransportMap(uint32 mapId) const { return _transportMaps.count(mapId); } + private: // first free id for selected id type uint32 _auctionId; diff --git a/src/server/scripts/Commands/cs_go.cpp b/src/server/scripts/Commands/cs_go.cpp index 3c1fa973cd..44ebb1ea4e 100644 --- a/src/server/scripts/Commands/cs_go.cpp +++ b/src/server/scripts/Commands/cs_go.cpp @@ -29,6 +29,7 @@ EndScriptData */ #include "Chat.h" #include "Language.h" #include "Player.h" +#include "Transport.h" class go_commandscript : public CommandScript { @@ -132,21 +133,24 @@ public: float x = fields[0].GetFloat(); float y = fields[1].GetFloat(); float z = fields[2].GetFloat(); - float ort = fields[3].GetFloat(); - int mapId = fields[4].GetUInt16(); + float o = fields[3].GetFloat(); + uint32 mapId = fields[4].GetUInt16(); uint32 guid = fields[5].GetUInt32(); uint32 id = fields[6].GetUInt32(); - // if creature is in same map with caster go at its current location - if (Creature* creature = sObjectAccessor->GetCreature(*player, MAKE_NEW_GUID(guid, id, HIGHGUID_UNIT))) + Transport* transport = NULL; + + if (Creature* creature = ObjectAccessor::GetObjectInWorld(MAKE_NEW_GUID(guid, id, HIGHGUID_UNIT), (Creature*)NULL)) { x = creature->GetPositionX(); y = creature->GetPositionY(); z = creature->GetPositionZ(); - ort = creature->GetOrientation(); + o = creature->GetOrientation(); + mapId = creature->GetMapId(); + transport = creature->GetTransport(); } - if (!MapManager::IsValidMapCoord(mapId, x, y, z, ort)) + if (!MapManager::IsValidMapCoord(mapId, x, y, z, o) || sObjectMgr->IsTransportMap(mapId)) { handler->PSendSysMessage(LANG_INVALID_TARGET_COORD, x, y, mapId); handler->SetSentErrorMessage(true); @@ -163,7 +167,11 @@ public: else player->SaveRecallPosition(); - player->TeleportTo(mapId, x, y, z, ort); + if (player->TeleportTo(mapId, x, y, z, o)) + { + if (transport) + transport->AddPassenger(player); + } return true; } @@ -274,8 +282,8 @@ public: if (!guid) return false; - float x, y, z, ort; - int mapId; + float x, y, z, o; + uint32 mapId; // by DB guid if (GameObjectData const* goData = sObjectMgr->GetGOData(guid)) @@ -283,7 +291,7 @@ public: x = goData->posX; y = goData->posY; z = goData->posZ; - ort = goData->orientation; + o = goData->orientation; mapId = goData->mapid; } else @@ -293,7 +301,7 @@ public: return false; } - if (!MapManager::IsValidMapCoord(mapId, x, y, z, ort)) + if (!MapManager::IsValidMapCoord(mapId, x, y, z, o) || sObjectMgr->IsTransportMap(mapId)) { handler->PSendSysMessage(LANG_INVALID_TARGET_COORD, x, y, mapId); handler->SetSentErrorMessage(true); @@ -310,7 +318,7 @@ public: else player->SaveRecallPosition(); - player->TeleportTo(mapId, x, y, z, ort); + player->TeleportTo(mapId, x, y, z, o); return true; } From 16d66e809cb4ecdaeab428aa7a9f341a34f267ce Mon Sep 17 00:00:00 2001 From: Aokromes Date: Sat, 28 Jun 2014 01:55:21 +0200 Subject: [PATCH 47/80] DB/SAI: Fix more runtime errors By dr-j --- sql/updates/world/2014_06_28_00_world_smart_scripts.sql | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 sql/updates/world/2014_06_28_00_world_smart_scripts.sql diff --git a/sql/updates/world/2014_06_28_00_world_smart_scripts.sql b/sql/updates/world/2014_06_28_00_world_smart_scripts.sql new file mode 100644 index 0000000000..96ac504bc0 --- /dev/null +++ b/sql/updates/world/2014_06_28_00_world_smart_scripts.sql @@ -0,0 +1,3 @@ +-- +UPDATE `smart_scripts` SET `link`=0 WHERE `entryorguid`=27245 AND `source_type`=0 AND `id`=2 AND `link`=3; +UPDATE `smart_scripts` SET `link`=0 WHERE `entryorguid`=4987 AND `source_type`=2 AND `id`=0 AND `link`=1; From 745833ccbc09660209ec2c48db3d020c07ff22c2 Mon Sep 17 00:00:00 2001 From: joschiwald Date: Sat, 28 Jun 2014 02:30:02 +0200 Subject: [PATCH 48/80] Core/Battleground: removed hardcoded levels of alterac valley npcs --- .../game/Battlegrounds/Battleground.cpp | 2 +- .../Battlegrounds/Zones/BattlegroundAV.cpp | 194 +++++++--------- .../game/Battlegrounds/Zones/BattlegroundAV.h | 208 +++++++++--------- .../game/Battlegrounds/Zones/BattlegroundSA.h | 2 +- 4 files changed, 180 insertions(+), 226 deletions(-) diff --git a/src/server/game/Battlegrounds/Battleground.cpp b/src/server/game/Battlegrounds/Battleground.cpp index 96911cfeda..fd3e13cf58 100644 --- a/src/server/game/Battlegrounds/Battleground.cpp +++ b/src/server/game/Battlegrounds/Battleground.cpp @@ -793,7 +793,7 @@ void Battleground::EndBattleground(uint32 winner) SetArenaMatchmakerRating(GetOtherTeam(winner), loserMatchmakerRating + loserMatchmakerChange); uint8 winnerId = GetWinner(); - uint8 loserId = winnerId == WINNER_ALLIANCE ? WINNER_HORDE : winnerId; + uint8 loserId = winnerId == WINNER_ALLIANCE ? uint8(WINNER_HORDE) : winnerId; _arenaTeamScores[winnerId]->Assign(winnerChange, winnerMatchmakerRating + winnerMatchmakerChange, winnerArenaTeam->GetName()); _arenaTeamScores[loserId]->Assign(loserChange, loserMatchmakerRating + loserMatchmakerChange, loserArenaTeam->GetName()); diff --git a/src/server/game/Battlegrounds/Zones/BattlegroundAV.cpp b/src/server/game/Battlegrounds/Zones/BattlegroundAV.cpp index 070d79bc8e..eafb02f031 100644 --- a/src/server/game/Battlegrounds/Zones/BattlegroundAV.cpp +++ b/src/server/game/Battlegrounds/Zones/BattlegroundAV.cpp @@ -47,7 +47,6 @@ BattlegroundAV::BattlegroundAV() } m_Mine_Timer = 0; - m_MaxLevel = 0; for (BG_AV_Nodes i = BG_AV_NODES_FIRSTAID_STATION; i < BG_AV_NODES_MAX; ++i) InitNode(i, 0, false); @@ -60,11 +59,6 @@ BattlegroundAV::BattlegroundAV() BattlegroundAV::~BattlegroundAV() { } -uint16 BattlegroundAV::GetBonusHonor(uint8 kills) /// @todo move this function to Battleground.cpp (needs to find a way to get m_MaxLevel) -{ - return Trinity::Honor::hk_honor_at_level(m_MaxLevel, kills); -} - void BattlegroundAV::HandleKillPlayer(Player* player, Player* killer) { if (GetStatus() != STATUS_IN_PROGRESS) @@ -95,7 +89,7 @@ void BattlegroundAV::HandleKillUnit(Creature* unit, Player* killer) { CastSpellOnTeam(23658, HORDE); //this is a spell which finishes a quest where a player has to kill the boss RewardReputationToTeam(729, BG_AV_REP_BOSS, HORDE); - RewardHonorToTeam(GetBonusHonor(BG_AV_KILL_BOSS), HORDE); + RewardHonorToTeam(GetBonusHonorFromKill(BG_AV_KILL_BOSS), HORDE); EndBattleground(HORDE); DelCreature(AV_CPLACE_TRIGGER17); } @@ -103,7 +97,7 @@ void BattlegroundAV::HandleKillUnit(Creature* unit, Player* killer) { CastSpellOnTeam(23658, ALLIANCE); //this is a spell which finishes a quest where a player has to kill the boss RewardReputationToTeam(730, BG_AV_REP_BOSS, ALLIANCE); - RewardHonorToTeam(GetBonusHonor(BG_AV_KILL_BOSS), ALLIANCE); + RewardHonorToTeam(GetBonusHonorFromKill(BG_AV_KILL_BOSS), ALLIANCE); EndBattleground(ALLIANCE); DelCreature(AV_CPLACE_TRIGGER19); } @@ -116,7 +110,7 @@ void BattlegroundAV::HandleKillUnit(Creature* unit, Player* killer) } m_CaptainAlive[0]=false; RewardReputationToTeam(729, BG_AV_REP_CAPTAIN, HORDE); - RewardHonorToTeam(GetBonusHonor(BG_AV_KILL_CAPTAIN), HORDE); + RewardHonorToTeam(GetBonusHonorFromKill(BG_AV_KILL_CAPTAIN), HORDE); UpdateScore(ALLIANCE, (-1)*BG_AV_RES_CAPTAIN); //spawn destroyed aura for (uint8 i=0; i <= 9; i++) @@ -135,7 +129,7 @@ void BattlegroundAV::HandleKillUnit(Creature* unit, Player* killer) } m_CaptainAlive[1]=false; RewardReputationToTeam(730, BG_AV_REP_CAPTAIN, ALLIANCE); - RewardHonorToTeam(GetBonusHonor(BG_AV_KILL_CAPTAIN), ALLIANCE); + RewardHonorToTeam(GetBonusHonorFromKill(BG_AV_KILL_CAPTAIN), ALLIANCE); UpdateScore(HORDE, (-1)*BG_AV_RES_CAPTAIN); //spawn destroyed aura for (uint8 i=0; i <= 9; i++) @@ -279,33 +273,24 @@ void BattlegroundAV::UpdateScore(uint16 team, int16 points) Creature* BattlegroundAV::AddAVCreature(uint16 cinfoid, uint16 type) { - uint8 level; bool isStatic = false; Creature* creature = NULL; ASSERT(type <= AV_CPLACE_MAX + AV_STATICCPLACE_MAX); if (type >= AV_CPLACE_MAX) //static { type -= AV_CPLACE_MAX; - cinfoid=uint16(BG_AV_StaticCreaturePos[type][4]); - creature = AddCreature(BG_AV_StaticCreatureInfo[cinfoid][0], - (type+AV_CPLACE_MAX), + cinfoid = uint16(BG_AV_StaticCreaturePos[type][4]); + creature = AddCreature(BG_AV_StaticCreatureInfo[cinfoid], + type + AV_CPLACE_MAX, BG_AV_StaticCreaturePos[type][0], BG_AV_StaticCreaturePos[type][1], BG_AV_StaticCreaturePos[type][2], BG_AV_StaticCreaturePos[type][3]); - level = (BG_AV_StaticCreatureInfo[cinfoid][2] == BG_AV_StaticCreatureInfo[cinfoid][3]) - ? BG_AV_StaticCreatureInfo[cinfoid][2] - : urand(BG_AV_StaticCreatureInfo[cinfoid][2], BG_AV_StaticCreatureInfo[cinfoid][3]); isStatic = true; } else { - creature = AddCreature(BG_AV_CreatureInfo[cinfoid][0], - type, - BG_AV_CreaturePos[type]); - level = (BG_AV_CreatureInfo[cinfoid][2] == BG_AV_CreatureInfo[cinfoid][3]) - ? BG_AV_CreatureInfo[cinfoid][2] - : urand(BG_AV_CreatureInfo[cinfoid][2], BG_AV_CreatureInfo[cinfoid][3]); + creature = AddCreature(BG_AV_CreatureInfo[cinfoid][0], type, BG_AV_CreaturePos[type]); } if (!creature) return NULL; @@ -330,10 +315,6 @@ Creature* BattlegroundAV::AddAVCreature(uint16 cinfoid, uint16 type) //just copied this code from a gm-command } - if (level != 0) - level += m_MaxLevel - 60; //maybe we can do this more generic for custom level-range.. actually it's blizzlike - creature->SetLevel(level); - uint32 triggerSpawnID = 0; uint32 newFaction = 0; if (creature->GetEntry() == BG_AV_CreatureInfo[AV_NPC_A_CAPTAIN][0]) @@ -358,9 +339,7 @@ Creature* BattlegroundAV::AddAVCreature(uint16 cinfoid, uint16 type) } if (triggerSpawnID && newFaction) { - if (Creature* trigger = AddCreature(WORLD_TRIGGER, - triggerSpawnID, - BG_AV_CreaturePos[triggerSpawnID])) + if (Creature* trigger = AddCreature(WORLD_TRIGGER, triggerSpawnID, BG_AV_CreaturePos[triggerSpawnID])) { trigger->setFaction(newFaction); trigger->CastSpell(trigger, SPELL_HONORABLE_DEFENDER_25Y, false); @@ -460,9 +439,6 @@ void BattlegroundAV::AddPlayer(Player* player) { Battleground::AddPlayer(player); PlayerScores[player->GetGUIDLow()] = new BattlegroundAVScore(player->GetGUID()); - - if (m_MaxLevel == 0) - m_MaxLevel=(player->getLevel()%10 == 0)? player->getLevel() : (player->getLevel()-(player->getLevel()%10))+10; /// @todo just look at the code \^_^/ --but queue-info should provide this information.. } void BattlegroundAV::EndBattleground(uint32 winner) @@ -498,7 +474,7 @@ void BattlegroundAV::EndBattleground(uint32 winner) if (rep[i] != 0) RewardReputationToTeam(i == 0 ? 730 : 729, rep[i], i == 0 ? ALLIANCE : HORDE); if (kills[i] != 0) - RewardHonorToTeam(GetBonusHonor(kills[i]), i == 0 ? ALLIANCE : HORDE); + RewardHonorToTeam(GetBonusHonorFromKill(kills[i]), i == 0 ? ALLIANCE : HORDE); } /// @todo add enterevademode for all attacking creatures @@ -602,7 +578,7 @@ void BattlegroundAV::EventPlayerDestroyedPoint(BG_AV_Nodes node) UpdateScore((owner == ALLIANCE) ? HORDE : ALLIANCE, -1 * BG_AV_RES_TOWER); RewardReputationToTeam(owner == ALLIANCE ? 730 : 729, BG_AV_REP_TOWER, owner); - RewardHonorToTeam(GetBonusHonor(BG_AV_KILL_TOWER), owner); + RewardHonorToTeam(GetBonusHonorFromKill(BG_AV_KILL_TOWER), owner); SpawnBGObject(BG_AV_OBJECT_TAURA_A_DUNBALDAR_SOUTH+GetTeamIndexByTeamId(owner)+(2*tmp), RESPAWN_ONE_DAY); SpawnBGObject(BG_AV_OBJECT_TFLAG_A_DUNBALDAR_SOUTH+GetTeamIndexByTeamId(owner)+(2*tmp), RESPAWN_ONE_DAY); @@ -1207,27 +1183,27 @@ bool BattlegroundAV::SetupBattleground() if (i <= BG_AV_NODES_FROSTWOLF_HUT) { if (!AddObject(i, BG_AV_OBJECTID_BANNER_A_B, - BG_AV_ObjectPos[i][0], BG_AV_ObjectPos[i][1], BG_AV_ObjectPos[i][2], BG_AV_ObjectPos[i][3], - 0, 0, std::sin(BG_AV_ObjectPos[i][3]/2), std::cos(BG_AV_ObjectPos[i][3]/2), RESPAWN_ONE_DAY) + BG_AV_ObjectPos[i], + 0, 0, std::sin(BG_AV_ObjectPos[i].GetOrientation()/2), std::cos(BG_AV_ObjectPos[i].GetOrientation()/2), RESPAWN_ONE_DAY) || !AddObject(i+11, BG_AV_OBJECTID_BANNER_CONT_A_B, - BG_AV_ObjectPos[i][0], BG_AV_ObjectPos[i][1], BG_AV_ObjectPos[i][2], BG_AV_ObjectPos[i][3], - 0, 0, std::sin(BG_AV_ObjectPos[i][3]/2), std::cos(BG_AV_ObjectPos[i][3]/2), RESPAWN_ONE_DAY) + BG_AV_ObjectPos[i], + 0, 0, std::sin(BG_AV_ObjectPos[i].GetOrientation()/2), std::cos(BG_AV_ObjectPos[i].GetOrientation()/2), RESPAWN_ONE_DAY) || !AddObject(i+33, BG_AV_OBJECTID_BANNER_H_B, - BG_AV_ObjectPos[i][0], BG_AV_ObjectPos[i][1], BG_AV_ObjectPos[i][2], BG_AV_ObjectPos[i][3], - 0, 0, std::sin(BG_AV_ObjectPos[i][3]/2), std::cos(BG_AV_ObjectPos[i][3]/2), RESPAWN_ONE_DAY) + BG_AV_ObjectPos[i], + 0, 0, std::sin(BG_AV_ObjectPos[i].GetOrientation()/2), std::cos(BG_AV_ObjectPos[i].GetOrientation()/2), RESPAWN_ONE_DAY) || !AddObject(i+22, BG_AV_OBJECTID_BANNER_CONT_H_B, - BG_AV_ObjectPos[i][0], BG_AV_ObjectPos[i][1], BG_AV_ObjectPos[i][2], BG_AV_ObjectPos[i][3], - 0, 0, std::sin(BG_AV_ObjectPos[i][3]/2), std::cos(BG_AV_ObjectPos[i][3]/2), RESPAWN_ONE_DAY) + BG_AV_ObjectPos[i], + 0, 0, std::sin(BG_AV_ObjectPos[i].GetOrientation()/2), std::cos(BG_AV_ObjectPos[i].GetOrientation()/2), RESPAWN_ONE_DAY) //aura || !AddObject(BG_AV_OBJECT_AURA_N_FIRSTAID_STATION+i*3, BG_AV_OBJECTID_AURA_N, - BG_AV_ObjectPos[i][0], BG_AV_ObjectPos[i][1], BG_AV_ObjectPos[i][2], BG_AV_ObjectPos[i][3], - 0, 0, std::sin(BG_AV_ObjectPos[i][3]/2), std::cos(BG_AV_ObjectPos[i][3]/2), RESPAWN_ONE_DAY) + BG_AV_ObjectPos[i], + 0, 0, std::sin(BG_AV_ObjectPos[i].GetOrientation()/2), std::cos(BG_AV_ObjectPos[i].GetOrientation()/2), RESPAWN_ONE_DAY) || !AddObject(BG_AV_OBJECT_AURA_A_FIRSTAID_STATION+i*3, BG_AV_OBJECTID_AURA_A, - BG_AV_ObjectPos[i][0], BG_AV_ObjectPos[i][1], BG_AV_ObjectPos[i][2], BG_AV_ObjectPos[i][3], - 0, 0, std::sin(BG_AV_ObjectPos[i][3]/2), std::cos(BG_AV_ObjectPos[i][3]/2), RESPAWN_ONE_DAY) + BG_AV_ObjectPos[i], + 0, 0, std::sin(BG_AV_ObjectPos[i].GetOrientation()/2), std::cos(BG_AV_ObjectPos[i].GetOrientation()/2), RESPAWN_ONE_DAY) || !AddObject(BG_AV_OBJECT_AURA_H_FIRSTAID_STATION+i*3, BG_AV_OBJECTID_AURA_H, - BG_AV_ObjectPos[i][0], BG_AV_ObjectPos[i][1], BG_AV_ObjectPos[i][2], BG_AV_ObjectPos[i][3], - 0, 0, std::sin(BG_AV_ObjectPos[i][3]/2), std::cos(BG_AV_ObjectPos[i][3]/2), RESPAWN_ONE_DAY)) + BG_AV_ObjectPos[i], + 0, 0, std::sin(BG_AV_ObjectPos[i].GetOrientation()/2), std::cos(BG_AV_ObjectPos[i].GetOrientation()/2), RESPAWN_ONE_DAY)) { TC_LOG_ERROR("bg.battleground", "BatteGroundAV: Failed to spawn some object Battleground not created!2"); return false; @@ -1238,23 +1214,23 @@ bool BattlegroundAV::SetupBattleground() if (i <= BG_AV_NODES_STONEHEART_BUNKER) //alliance towers { if (!AddObject(i, BG_AV_OBJECTID_BANNER_A, - BG_AV_ObjectPos[i][0], BG_AV_ObjectPos[i][1], BG_AV_ObjectPos[i][2], BG_AV_ObjectPos[i][3], - 0, 0, std::sin(BG_AV_ObjectPos[i][3]/2), std::cos(BG_AV_ObjectPos[i][3]/2), RESPAWN_ONE_DAY) + BG_AV_ObjectPos[i], + 0, 0, std::sin(BG_AV_ObjectPos[i].GetOrientation()/2), std::cos(BG_AV_ObjectPos[i].GetOrientation()/2), RESPAWN_ONE_DAY) || !AddObject(i+22, BG_AV_OBJECTID_BANNER_CONT_H, - BG_AV_ObjectPos[i][0], BG_AV_ObjectPos[i][1], BG_AV_ObjectPos[i][2], BG_AV_ObjectPos[i][3], - 0, 0, std::sin(BG_AV_ObjectPos[i][3]/2), std::cos(BG_AV_ObjectPos[i][3]/2), RESPAWN_ONE_DAY) + BG_AV_ObjectPos[i], + 0, 0, std::sin(BG_AV_ObjectPos[i].GetOrientation()/2), std::cos(BG_AV_ObjectPos[i].GetOrientation()/2), RESPAWN_ONE_DAY) || !AddObject(BG_AV_OBJECT_TAURA_A_DUNBALDAR_SOUTH+(2*(i-BG_AV_NODES_DUNBALDAR_SOUTH)), BG_AV_OBJECTID_AURA_A, - BG_AV_ObjectPos[i+8][0], BG_AV_ObjectPos[i+8][1], BG_AV_ObjectPos[i+8][2], BG_AV_ObjectPos[i+8][3], - 0, 0, std::sin(BG_AV_ObjectPos[i+8][3]/2), std::cos(BG_AV_ObjectPos[i+8][3]/2), RESPAWN_ONE_DAY) + BG_AV_ObjectPos[i+8], + 0, 0, std::sin(BG_AV_ObjectPos[i+8].GetOrientation()/2), std::cos(BG_AV_ObjectPos[i+8].GetOrientation()/2), RESPAWN_ONE_DAY) || !AddObject(BG_AV_OBJECT_TAURA_H_DUNBALDAR_SOUTH+(2*(i-BG_AV_NODES_DUNBALDAR_SOUTH)), BG_AV_OBJECTID_AURA_N, - BG_AV_ObjectPos[i+8][0], BG_AV_ObjectPos[i+8][1], BG_AV_ObjectPos[i+8][2], BG_AV_ObjectPos[i+8][3], - 0, 0, std::sin(BG_AV_ObjectPos[i+8][3]/2), std::cos(BG_AV_ObjectPos[i+8][3]/2), RESPAWN_ONE_DAY) + BG_AV_ObjectPos[i+8], + 0, 0, std::sin(BG_AV_ObjectPos[i+8].GetOrientation()/2), std::cos(BG_AV_ObjectPos[i+8].GetOrientation()/2), RESPAWN_ONE_DAY) || !AddObject(BG_AV_OBJECT_TFLAG_A_DUNBALDAR_SOUTH+(2*(i-BG_AV_NODES_DUNBALDAR_SOUTH)), BG_AV_OBJECTID_TOWER_BANNER_A, - BG_AV_ObjectPos[i+8][0], BG_AV_ObjectPos[i+8][1], BG_AV_ObjectPos[i+8][2], BG_AV_ObjectPos[i+8][3], - 0, 0, std::sin(BG_AV_ObjectPos[i+8][3]/2), std::cos(BG_AV_ObjectPos[i+8][3]/2), RESPAWN_ONE_DAY) + BG_AV_ObjectPos[i+8], + 0, 0, std::sin(BG_AV_ObjectPos[i+8].GetOrientation()/2), std::cos(BG_AV_ObjectPos[i+8].GetOrientation()/2), RESPAWN_ONE_DAY) || !AddObject(BG_AV_OBJECT_TFLAG_H_DUNBALDAR_SOUTH+(2*(i-BG_AV_NODES_DUNBALDAR_SOUTH)), BG_AV_OBJECTID_TOWER_BANNER_PH, - BG_AV_ObjectPos[i+8][0], BG_AV_ObjectPos[i+8][1], BG_AV_ObjectPos[i+8][2], BG_AV_ObjectPos[i+8][3], - 0, 0, std::sin(BG_AV_ObjectPos[i+8][3]/2), std::cos(BG_AV_ObjectPos[i+8][3]/2), RESPAWN_ONE_DAY)) + BG_AV_ObjectPos[i+8], + 0, 0, std::sin(BG_AV_ObjectPos[i+8].GetOrientation()/2), std::cos(BG_AV_ObjectPos[i+8].GetOrientation()/2), RESPAWN_ONE_DAY)) { TC_LOG_ERROR("bg.battleground", "BatteGroundAV: Failed to spawn some object Battleground not created!3"); return false; @@ -1263,23 +1239,23 @@ bool BattlegroundAV::SetupBattleground() else //horde towers { if (!AddObject(i+7, BG_AV_OBJECTID_BANNER_CONT_A, - BG_AV_ObjectPos[i][0], BG_AV_ObjectPos[i][1], BG_AV_ObjectPos[i][2], BG_AV_ObjectPos[i][3], - 0, 0, std::sin(BG_AV_ObjectPos[i][3]/2), std::cos(BG_AV_ObjectPos[i][3]/2), RESPAWN_ONE_DAY) + BG_AV_ObjectPos[i], + 0, 0, std::sin(BG_AV_ObjectPos[i].GetOrientation()/2), std::cos(BG_AV_ObjectPos[i].GetOrientation()/2), RESPAWN_ONE_DAY) || !AddObject(i+29, BG_AV_OBJECTID_BANNER_H, - BG_AV_ObjectPos[i][0], BG_AV_ObjectPos[i][1], BG_AV_ObjectPos[i][2], BG_AV_ObjectPos[i][3], - 0, 0, std::sin(BG_AV_ObjectPos[i][3]/2), std::cos(BG_AV_ObjectPos[i][3]/2), RESPAWN_ONE_DAY) + BG_AV_ObjectPos[i], + 0, 0, std::sin(BG_AV_ObjectPos[i].GetOrientation()/2), std::cos(BG_AV_ObjectPos[i].GetOrientation()/2), RESPAWN_ONE_DAY) || !AddObject(BG_AV_OBJECT_TAURA_A_DUNBALDAR_SOUTH+(2*(i-BG_AV_NODES_DUNBALDAR_SOUTH)), BG_AV_OBJECTID_AURA_N, - BG_AV_ObjectPos[i+8][0], BG_AV_ObjectPos[i+8][1], BG_AV_ObjectPos[i+8][2], BG_AV_ObjectPos[i+8][3], - 0, 0, std::sin(BG_AV_ObjectPos[i+8][3]/2), std::cos(BG_AV_ObjectPos[i+8][3]/2), RESPAWN_ONE_DAY) + BG_AV_ObjectPos[i+8], + 0, 0, std::sin(BG_AV_ObjectPos[i+8].GetOrientation()/2), std::cos(BG_AV_ObjectPos[i+8].GetOrientation()/2), RESPAWN_ONE_DAY) || !AddObject(BG_AV_OBJECT_TAURA_H_DUNBALDAR_SOUTH+(2*(i-BG_AV_NODES_DUNBALDAR_SOUTH)), BG_AV_OBJECTID_AURA_H, - BG_AV_ObjectPos[i+8][0], BG_AV_ObjectPos[i+8][1], BG_AV_ObjectPos[i+8][2], BG_AV_ObjectPos[i+8][3], - 0, 0, std::sin(BG_AV_ObjectPos[i+8][3]/2), std::cos(BG_AV_ObjectPos[i+8][3]/2), RESPAWN_ONE_DAY) + BG_AV_ObjectPos[i+8], + 0, 0, std::sin(BG_AV_ObjectPos[i+8].GetOrientation()/2), std::cos(BG_AV_ObjectPos[i+8].GetOrientation()/2), RESPAWN_ONE_DAY) || !AddObject(BG_AV_OBJECT_TFLAG_A_DUNBALDAR_SOUTH+(2*(i-BG_AV_NODES_DUNBALDAR_SOUTH)), BG_AV_OBJECTID_TOWER_BANNER_PA, - BG_AV_ObjectPos[i+8][0], BG_AV_ObjectPos[i+8][1], BG_AV_ObjectPos[i+8][2], BG_AV_ObjectPos[i+8][3], - 0, 0, std::sin(BG_AV_ObjectPos[i+8][3]/2), std::cos(BG_AV_ObjectPos[i+8][3]/2), RESPAWN_ONE_DAY) + BG_AV_ObjectPos[i+8], + 0, 0, std::sin(BG_AV_ObjectPos[i+8].GetOrientation()/2), std::cos(BG_AV_ObjectPos[i+8].GetOrientation()/2), RESPAWN_ONE_DAY) || !AddObject(BG_AV_OBJECT_TFLAG_H_DUNBALDAR_SOUTH+(2*(i-BG_AV_NODES_DUNBALDAR_SOUTH)), BG_AV_OBJECTID_TOWER_BANNER_H, - BG_AV_ObjectPos[i+8][0], BG_AV_ObjectPos[i+8][1], BG_AV_ObjectPos[i+8][2], BG_AV_ObjectPos[i+8][3], - 0, 0, std::sin(BG_AV_ObjectPos[i+8][3]/2), std::cos(BG_AV_ObjectPos[i+8][3]/2), RESPAWN_ONE_DAY)) + BG_AV_ObjectPos[i+8], + 0, 0, std::sin(BG_AV_ObjectPos[i+8].GetOrientation()/2), std::cos(BG_AV_ObjectPos[i+8].GetOrientation()/2), RESPAWN_ONE_DAY)) { TC_LOG_ERROR("bg.battleground", "BatteGroundAV: Failed to spawn some object Battleground not created!4"); return false; @@ -1289,14 +1265,11 @@ bool BattlegroundAV::SetupBattleground() { if (!AddObject(BG_AV_OBJECT_BURN_DUNBALDAR_SOUTH+((i-BG_AV_NODES_DUNBALDAR_SOUTH)*10)+j, BG_AV_OBJECTID_FIRE, - BG_AV_ObjectPos[AV_OPLACE_BURN_DUNBALDAR_SOUTH+((i-BG_AV_NODES_DUNBALDAR_SOUTH)*10)+j][0], - BG_AV_ObjectPos[AV_OPLACE_BURN_DUNBALDAR_SOUTH+((i-BG_AV_NODES_DUNBALDAR_SOUTH)*10)+j][1], - BG_AV_ObjectPos[AV_OPLACE_BURN_DUNBALDAR_SOUTH+((i-BG_AV_NODES_DUNBALDAR_SOUTH)*10)+j][2], - BG_AV_ObjectPos[AV_OPLACE_BURN_DUNBALDAR_SOUTH+((i-BG_AV_NODES_DUNBALDAR_SOUTH)*10)+j][3], + BG_AV_ObjectPos[AV_OPLACE_BURN_DUNBALDAR_SOUTH+((i-BG_AV_NODES_DUNBALDAR_SOUTH)*10)+j], 0, 0, - std::sin(BG_AV_ObjectPos[AV_OPLACE_BURN_DUNBALDAR_SOUTH+((i-BG_AV_NODES_DUNBALDAR_SOUTH)*10)+j][3]/2), - std::cos(BG_AV_ObjectPos[AV_OPLACE_BURN_DUNBALDAR_SOUTH+((i-BG_AV_NODES_DUNBALDAR_SOUTH)*10)+j][3]/2), + std::sin(BG_AV_ObjectPos[AV_OPLACE_BURN_DUNBALDAR_SOUTH+((i-BG_AV_NODES_DUNBALDAR_SOUTH)*10)+j].GetOrientation()/2), + std::cos(BG_AV_ObjectPos[AV_OPLACE_BURN_DUNBALDAR_SOUTH+((i-BG_AV_NODES_DUNBALDAR_SOUTH)*10)+j].GetOrientation()/2), RESPAWN_ONE_DAY)) { TC_LOG_ERROR("bg.battleground", "BatteGroundAV: Failed to spawn some object Battleground not created!5.%i", i); @@ -1313,14 +1286,11 @@ bool BattlegroundAV::SetupBattleground() { if (!AddObject(BG_AV_OBJECT_BURN_BUILDING_ALLIANCE+(i*10)+j, BG_AV_OBJECTID_SMOKE, - BG_AV_ObjectPos[AV_OPLACE_BURN_BUILDING_A+(i*10)+j][0], - BG_AV_ObjectPos[AV_OPLACE_BURN_BUILDING_A+(i*10)+j][1], - BG_AV_ObjectPos[AV_OPLACE_BURN_BUILDING_A+(i*10)+j][2], - BG_AV_ObjectPos[AV_OPLACE_BURN_BUILDING_A+(i*10)+j][3], + BG_AV_ObjectPos[AV_OPLACE_BURN_BUILDING_A+(i*10)+j], 0, 0, - std::sin(BG_AV_ObjectPos[AV_OPLACE_BURN_BUILDING_A+(i*10)+j][3]/2), - std::cos(BG_AV_ObjectPos[AV_OPLACE_BURN_BUILDING_A+(i*10)+j][3]/2), + std::sin(BG_AV_ObjectPos[AV_OPLACE_BURN_BUILDING_A+(i*10)+j].GetOrientation()/2), + std::cos(BG_AV_ObjectPos[AV_OPLACE_BURN_BUILDING_A+(i*10)+j].GetOrientation()/2), RESPAWN_ONE_DAY)) { TC_LOG_ERROR("bg.battleground", "BatteGroundAV: Failed to spawn some object Battleground not created!6.%i", i); @@ -1331,14 +1301,11 @@ bool BattlegroundAV::SetupBattleground() { if (!AddObject(BG_AV_OBJECT_BURN_BUILDING_ALLIANCE+(i*10)+j, BG_AV_OBJECTID_FIRE, - BG_AV_ObjectPos[AV_OPLACE_BURN_BUILDING_A+(i*10)+j][0], - BG_AV_ObjectPos[AV_OPLACE_BURN_BUILDING_A+(i*10)+j][1], - BG_AV_ObjectPos[AV_OPLACE_BURN_BUILDING_A+(i*10)+j][2], - BG_AV_ObjectPos[AV_OPLACE_BURN_BUILDING_A+(i*10)+j][3], + BG_AV_ObjectPos[AV_OPLACE_BURN_BUILDING_A+(i*10)+j], 0, 0, - std::sin(BG_AV_ObjectPos[AV_OPLACE_BURN_BUILDING_A+(i*10)+j][3]/2), - std::cos(BG_AV_ObjectPos[AV_OPLACE_BURN_BUILDING_A+(i*10)+j][3]/2), + std::sin(BG_AV_ObjectPos[AV_OPLACE_BURN_BUILDING_A+(i*10)+j].GetOrientation()/2), + std::cos(BG_AV_ObjectPos[AV_OPLACE_BURN_BUILDING_A+(i*10)+j].GetOrientation()/2), RESPAWN_ONE_DAY)) { TC_LOG_ERROR("bg.battleground", "BatteGroundAV: Failed to spawn some object Battleground not created!7.%i", i); @@ -1351,14 +1318,11 @@ bool BattlegroundAV::SetupBattleground() { if (!AddObject(BG_AV_OBJECT_MINE_SUPPLY_N_MIN+i, BG_AV_OBJECTID_MINE_N, - BG_AV_ObjectPos[AV_OPLACE_MINE_SUPPLY_N_MIN+i][0], - BG_AV_ObjectPos[AV_OPLACE_MINE_SUPPLY_N_MIN+i][1], - BG_AV_ObjectPos[AV_OPLACE_MINE_SUPPLY_N_MIN+i][2], - BG_AV_ObjectPos[AV_OPLACE_MINE_SUPPLY_N_MIN+i][3], + BG_AV_ObjectPos[AV_OPLACE_MINE_SUPPLY_N_MIN+i], 0, 0, - std::sin(BG_AV_ObjectPos[AV_OPLACE_MINE_SUPPLY_N_MIN+i][3]/2), - std::cos(BG_AV_ObjectPos[AV_OPLACE_MINE_SUPPLY_N_MIN+i][3]/2), + std::sin(BG_AV_ObjectPos[AV_OPLACE_MINE_SUPPLY_N_MIN+i].GetOrientation()/2), + std::cos(BG_AV_ObjectPos[AV_OPLACE_MINE_SUPPLY_N_MIN+i].GetOrientation()/2), RESPAWN_ONE_DAY)) { TC_LOG_ERROR("bg.battleground", "BatteGroundAV: Failed to spawn some mine supplies Battleground not created!7.5.%i", i); @@ -1369,14 +1333,11 @@ bool BattlegroundAV::SetupBattleground() { if (!AddObject(BG_AV_OBJECT_MINE_SUPPLY_S_MIN+i, BG_AV_OBJECTID_MINE_S, - BG_AV_ObjectPos[AV_OPLACE_MINE_SUPPLY_S_MIN+i][0], - BG_AV_ObjectPos[AV_OPLACE_MINE_SUPPLY_S_MIN+i][1], - BG_AV_ObjectPos[AV_OPLACE_MINE_SUPPLY_S_MIN+i][2], - BG_AV_ObjectPos[AV_OPLACE_MINE_SUPPLY_S_MIN+i][3], + BG_AV_ObjectPos[AV_OPLACE_MINE_SUPPLY_S_MIN+i], 0, 0, - std::sin(BG_AV_ObjectPos[AV_OPLACE_MINE_SUPPLY_S_MIN+i][3]/2), - std::cos(BG_AV_ObjectPos[AV_OPLACE_MINE_SUPPLY_S_MIN+i][3]/2), + std::sin(BG_AV_ObjectPos[AV_OPLACE_MINE_SUPPLY_S_MIN+i].GetOrientation()/2), + std::cos(BG_AV_ObjectPos[AV_OPLACE_MINE_SUPPLY_S_MIN+i].GetOrientation()/2), RESPAWN_ONE_DAY)) { TC_LOG_ERROR("bg.battleground", "BatteGroundAV: Failed to spawn some mine supplies Battleground not created!7.6.%i", i); @@ -1386,14 +1347,11 @@ bool BattlegroundAV::SetupBattleground() if (!AddObject(BG_AV_OBJECT_FLAG_N_SNOWFALL_GRAVE, BG_AV_OBJECTID_BANNER_SNOWFALL_N, - BG_AV_ObjectPos[BG_AV_NODES_SNOWFALL_GRAVE][0], - BG_AV_ObjectPos[BG_AV_NODES_SNOWFALL_GRAVE][1], - BG_AV_ObjectPos[BG_AV_NODES_SNOWFALL_GRAVE][2], - BG_AV_ObjectPos[BG_AV_NODES_SNOWFALL_GRAVE][3], + BG_AV_ObjectPos[BG_AV_NODES_SNOWFALL_GRAVE], 0, 0, - std::sin(BG_AV_ObjectPos[BG_AV_NODES_SNOWFALL_GRAVE][3]/2), - std::cos(BG_AV_ObjectPos[BG_AV_NODES_SNOWFALL_GRAVE][3]/2), + std::sin(BG_AV_ObjectPos[BG_AV_NODES_SNOWFALL_GRAVE].GetOrientation()/2), + std::cos(BG_AV_ObjectPos[BG_AV_NODES_SNOWFALL_GRAVE].GetOrientation()/2), RESPAWN_ONE_DAY)) { TC_LOG_ERROR("bg.battleground", "BatteGroundAV: Failed to spawn some object Battleground not created!8"); @@ -1402,17 +1360,17 @@ bool BattlegroundAV::SetupBattleground() for (uint8 i = 0; i < 4; i++) { if (!AddObject(BG_AV_OBJECT_SNOW_EYECANDY_A+i, BG_AV_OBJECTID_SNOWFALL_CANDY_A, - BG_AV_ObjectPos[AV_OPLACE_SNOW_1+i][0], BG_AV_ObjectPos[AV_OPLACE_SNOW_1+i][1], BG_AV_ObjectPos[AV_OPLACE_SNOW_1+i][2], BG_AV_ObjectPos[AV_OPLACE_SNOW_1+i][3], - 0, 0, std::sin(BG_AV_ObjectPos[AV_OPLACE_SNOW_1+i][3]/2), std::cos(BG_AV_ObjectPos[AV_OPLACE_SNOW_1+i][3]/2), RESPAWN_ONE_DAY) + BG_AV_ObjectPos[AV_OPLACE_SNOW_1+i], + 0, 0, std::sin(BG_AV_ObjectPos[AV_OPLACE_SNOW_1+i].GetOrientation()/2), std::cos(BG_AV_ObjectPos[AV_OPLACE_SNOW_1+i].GetOrientation()/2), RESPAWN_ONE_DAY) || !AddObject(BG_AV_OBJECT_SNOW_EYECANDY_PA+i, BG_AV_OBJECTID_SNOWFALL_CANDY_PA, - BG_AV_ObjectPos[AV_OPLACE_SNOW_1+i][0], BG_AV_ObjectPos[AV_OPLACE_SNOW_1+i][1], BG_AV_ObjectPos[AV_OPLACE_SNOW_1+i][2], BG_AV_ObjectPos[AV_OPLACE_SNOW_1+i][3], - 0, 0, std::sin(BG_AV_ObjectPos[AV_OPLACE_SNOW_1+i][3]/2), std::cos(BG_AV_ObjectPos[AV_OPLACE_SNOW_1+i][3]/2), RESPAWN_ONE_DAY) + BG_AV_ObjectPos[AV_OPLACE_SNOW_1+i], + 0, 0, std::sin(BG_AV_ObjectPos[AV_OPLACE_SNOW_1+i].GetOrientation()/2), std::cos(BG_AV_ObjectPos[AV_OPLACE_SNOW_1+i].GetOrientation()/2), RESPAWN_ONE_DAY) || !AddObject(BG_AV_OBJECT_SNOW_EYECANDY_H+i, BG_AV_OBJECTID_SNOWFALL_CANDY_H, - BG_AV_ObjectPos[AV_OPLACE_SNOW_1+i][0], BG_AV_ObjectPos[AV_OPLACE_SNOW_1+i][1], BG_AV_ObjectPos[AV_OPLACE_SNOW_1+i][2], BG_AV_ObjectPos[AV_OPLACE_SNOW_1+i][3], - 0, 0, std::sin(BG_AV_ObjectPos[AV_OPLACE_SNOW_1+i][3]/2), std::cos(BG_AV_ObjectPos[AV_OPLACE_SNOW_1+i][3]/2), RESPAWN_ONE_DAY) + BG_AV_ObjectPos[AV_OPLACE_SNOW_1+i], + 0, 0, std::sin(BG_AV_ObjectPos[AV_OPLACE_SNOW_1+i].GetOrientation()/2), std::cos(BG_AV_ObjectPos[AV_OPLACE_SNOW_1+i].GetOrientation()/2), RESPAWN_ONE_DAY) || !AddObject(BG_AV_OBJECT_SNOW_EYECANDY_PH+i, BG_AV_OBJECTID_SNOWFALL_CANDY_PH, - BG_AV_ObjectPos[AV_OPLACE_SNOW_1+i][0], BG_AV_ObjectPos[AV_OPLACE_SNOW_1+i][1], BG_AV_ObjectPos[AV_OPLACE_SNOW_1+i][2], BG_AV_ObjectPos[AV_OPLACE_SNOW_1+i][3], - 0, 0, std::sin(BG_AV_ObjectPos[AV_OPLACE_SNOW_1+i][3]/2), std::cos(BG_AV_ObjectPos[AV_OPLACE_SNOW_1+i][3]/2), RESPAWN_ONE_DAY)) + BG_AV_ObjectPos[AV_OPLACE_SNOW_1+i], + 0, 0, std::sin(BG_AV_ObjectPos[AV_OPLACE_SNOW_1+i].GetOrientation()/2), std::cos(BG_AV_ObjectPos[AV_OPLACE_SNOW_1+i].GetOrientation()/2), RESPAWN_ONE_DAY)) { TC_LOG_ERROR("bg.battleground", "BatteGroundAV: Failed to spawn some object Battleground not created!9.%i", i); return false; @@ -1576,8 +1534,6 @@ void BattlegroundAV::DefendNode(BG_AV_Nodes node, uint16 team) void BattlegroundAV::ResetBGSubclass() { - m_MaxLevel=0; - for (uint8 i=0; i<2; i++) //forloop for both teams (it just make 0 == alliance and 1 == horde also for both mines 0=north 1=south { for (uint8 j=0; j<9; j++) diff --git a/src/server/game/Battlegrounds/Zones/BattlegroundAV.h b/src/server/game/Battlegrounds/Zones/BattlegroundAV.h index b84b146004..feb3c016e5 100644 --- a/src/server/game/Battlegrounds/Zones/BattlegroundAV.h +++ b/src/server/game/Battlegrounds/Zones/BattlegroundAV.h @@ -132,10 +132,10 @@ enum BG_AV_ObjectIds BG_AV_OBJECTID_SNOWFALL_CANDY_PH = 179425, //banners on top of towers: - BG_AV_OBJECTID_TOWER_BANNER_A = 178927, //[PH] Alliance A1 Tower Banner BIG - BG_AV_OBJECTID_TOWER_BANNER_H = 178955, //[PH] Horde H1 Tower Banner BIG - BG_AV_OBJECTID_TOWER_BANNER_PA = 179446, //[PH] Alliance H1 Tower Pre-Banner BIG - BG_AV_OBJECTID_TOWER_BANNER_PH = 179436, //[PH] Horde A1 Tower Pre-Banner BIG + BG_AV_OBJECTID_TOWER_BANNER_A = 178927, //[PH] Alliance A1 Tower Banner BIG + BG_AV_OBJECTID_TOWER_BANNER_H = 178955, //[PH] Horde H1 Tower Banner BIG + BG_AV_OBJECTID_TOWER_BANNER_PA = 179446, //[PH] Alliance H1 Tower Pre-Banner BIG + BG_AV_OBJECTID_TOWER_BANNER_PH = 179436, //[PH] Horde A1 Tower Pre-Banner BIG //Auras BG_AV_OBJECTID_AURA_A = 180421, @@ -149,11 +149,11 @@ enum BG_AV_ObjectIds BG_AV_OBJECTID_GATE_H = 180424, //mine supplies - BG_AV_OBJECTID_MINE_N = 178785, - BG_AV_OBJECTID_MINE_S = 178784, + BG_AV_OBJECTID_MINE_N = 178785, + BG_AV_OBJECTID_MINE_S = 178784, BG_AV_OBJECTID_FIRE = 179065, - BG_AV_OBJECTID_SMOKE = 179066 + BG_AV_OBJECTID_SMOKE = 179066 }; enum BG_AV_Nodes @@ -307,58 +307,58 @@ enum BG_AV_ObjectTypes BG_AV_OBJECT_MINE_SUPPLY_S_MIN = 225, BG_AV_OBJECT_MINE_SUPPLY_S_MAX = 236, - BG_AV_OBJECT_MAX = 237 + BG_AV_OBJECT_MAX = 237 }; enum BG_AV_OBJECTS { - AV_OPLACE_FIRSTAID_STATION = 0, - AV_OPLACE_STORMPIKE_GRAVE = 1, - AV_OPLACE_STONEHEART_GRAVE = 2, - AV_OPLACE_SNOWFALL_GRAVE = 3, - AV_OPLACE_ICEBLOOD_GRAVE = 4, - AV_OPLACE_FROSTWOLF_GRAVE = 5, - AV_OPLACE_FROSTWOLF_HUT = 6, - AV_OPLACE_DUNBALDAR_SOUTH = 7, - AV_OPLACE_DUNBALDAR_NORTH = 8, - AV_OPLACE_ICEWING_BUNKER = 9, - AV_OPLACE_STONEHEART_BUNKER = 10, - AV_OPLACE_ICEBLOOD_TOWER = 11, - AV_OPLACE_TOWER_POINT = 12, - AV_OPLACE_FROSTWOLF_ETOWER = 13, - AV_OPLACE_FROSTWOLF_WTOWER = 14, - AV_OPLACE_BIGBANNER_DUNBALDAR_SOUTH = 15, - AV_OPLACE_BIGBANNER_DUNBALDAR_NORTH = 16, - AV_OPLACE_BIGBANNER_ICEWING_BUNKER = 17, - AV_OPLACE_BIGBANNER_STONEHEART_BUNKER = 18, - AV_OPLACE_BIGBANNER_ICEBLOOD_TOWER = 19, - AV_OPLACE_BIGBANNER_TOWER_POINT = 20, - AV_OPLACE_BIGBANNER_FROSTWOLF_ETOWER = 21, - AV_OPLACE_BIGBANNER_FROSTWOLF_WTOWER = 22, + AV_OPLACE_FIRSTAID_STATION = 0, + AV_OPLACE_STORMPIKE_GRAVE = 1, + AV_OPLACE_STONEHEART_GRAVE = 2, + AV_OPLACE_SNOWFALL_GRAVE = 3, + AV_OPLACE_ICEBLOOD_GRAVE = 4, + AV_OPLACE_FROSTWOLF_GRAVE = 5, + AV_OPLACE_FROSTWOLF_HUT = 6, + AV_OPLACE_DUNBALDAR_SOUTH = 7, + AV_OPLACE_DUNBALDAR_NORTH = 8, + AV_OPLACE_ICEWING_BUNKER = 9, + AV_OPLACE_STONEHEART_BUNKER = 10, + AV_OPLACE_ICEBLOOD_TOWER = 11, + AV_OPLACE_TOWER_POINT = 12, + AV_OPLACE_FROSTWOLF_ETOWER = 13, + AV_OPLACE_FROSTWOLF_WTOWER = 14, + AV_OPLACE_BIGBANNER_DUNBALDAR_SOUTH = 15, + AV_OPLACE_BIGBANNER_DUNBALDAR_NORTH = 16, + AV_OPLACE_BIGBANNER_ICEWING_BUNKER = 17, + AV_OPLACE_BIGBANNER_STONEHEART_BUNKER = 18, + AV_OPLACE_BIGBANNER_ICEBLOOD_TOWER = 19, + AV_OPLACE_BIGBANNER_TOWER_POINT = 20, + AV_OPLACE_BIGBANNER_FROSTWOLF_ETOWER = 21, + AV_OPLACE_BIGBANNER_FROSTWOLF_WTOWER = 22, - AV_OPLACE_BURN_DUNBALDAR_SOUTH = 23, - AV_OPLACE_BURN_DUNBALDAR_NORTH = 33, - AV_OPLACE_BURN_ICEWING_BUNKER = 43, - AV_OPLACE_BURN_STONEHEART_BUNKER = 53, - AV_OPLACE_BURN_ICEBLOOD_TOWER = 63, - AV_OPLACE_BURN_TOWER_POINT = 73, - AV_OPLACE_BURN_FROSTWOLF_ETOWER = 83, - AV_OPLACE_BURN_FROSTWOLF_WTOWER = 93, - AV_OPLACE_BURN_BUILDING_A = 103, - AV_OPLACE_BURN_BUILDING_H = 113, - AV_OPLACE_SNOW_1 = 123, - AV_OPLACE_SNOW_2 = 124, - AV_OPLACE_SNOW_3 = 125, - AV_OPLACE_SNOW_4 = 126, - AV_OPLACE_MINE_SUPPLY_N_MIN = 127, - AV_OPLACE_MINE_SUPPLY_N_MAX = 136, - AV_OPLACE_MINE_SUPPLY_S_MIN = 137, - AV_OPLACE_MINE_SUPPLY_S_MAX = 148, + AV_OPLACE_BURN_DUNBALDAR_SOUTH = 23, + AV_OPLACE_BURN_DUNBALDAR_NORTH = 33, + AV_OPLACE_BURN_ICEWING_BUNKER = 43, + AV_OPLACE_BURN_STONEHEART_BUNKER = 53, + AV_OPLACE_BURN_ICEBLOOD_TOWER = 63, + AV_OPLACE_BURN_TOWER_POINT = 73, + AV_OPLACE_BURN_FROSTWOLF_ETOWER = 83, + AV_OPLACE_BURN_FROSTWOLF_WTOWER = 93, + AV_OPLACE_BURN_BUILDING_A = 103, + AV_OPLACE_BURN_BUILDING_H = 113, + AV_OPLACE_SNOW_1 = 123, + AV_OPLACE_SNOW_2 = 124, + AV_OPLACE_SNOW_3 = 125, + AV_OPLACE_SNOW_4 = 126, + AV_OPLACE_MINE_SUPPLY_N_MIN = 127, + AV_OPLACE_MINE_SUPPLY_N_MAX = 136, + AV_OPLACE_MINE_SUPPLY_S_MIN = 137, + AV_OPLACE_MINE_SUPPLY_S_MAX = 148, - AV_OPLACE_MAX = 149 + AV_OPLACE_MAX = 149 }; -const float BG_AV_ObjectPos[AV_OPLACE_MAX][4] = +Position const BG_AV_ObjectPos[AV_OPLACE_MAX] = { {638.592f, -32.422f, 46.0608f, -1.62316f }, //firstaid station {669.007f, -294.078f, 30.2909f, 2.77507f }, //stormpike @@ -1229,59 +1229,59 @@ const float BG_AV_StaticCreaturePos[AV_STATICCPLACE_MAX][5] = {-1370.9f, -219.793f, 98.4258f, 5.04381f, 47}, //drek thar }; -const uint32 BG_AV_StaticCreatureInfo[51][4] = +const uint32 BG_AV_StaticCreatureInfo[51] = { - { 2225, 1215, 55, 55 }, //Zora Guthrek - { 3343, 1215, 55, 55 }, //Grelkor - { 3625, 1215, 55, 55 }, //Rarck - { 4255, 1217, 55, 55 }, //Brogus Thunderbrew - { 4257, 1217, 55, 55 }, //Lana Thunderbrew - { 5134, 1217, 55, 55 }, //Jonivera Farmountain - { 5135, 1217, 55, 55 }, //Svalbrad Farmountain - { 5139, 1217, 55, 55 }, //Kurdrum Barleybeard - { 10364, 1215, 55, 55 }, //Yaelika Farclaw - { 10367, 1215, 55, 55 }, //Shrye Ragefist - { 10981, 38, 50, 51 }, //Frostwolf - { 10986, 514, 52, 53 }, //Snowblind Harpy - { 10990, 1274, 50, 51 }, //Alterac Ram - { 11675, 514, 53, 53 }, //Snowblind Windcaller - { 11678, 14, 52, 53 }, //Snowblind Ambusher - { 11839, 39, 56, 56 }, //Wildpaw Brute - { 11947, 1214, 61, 61 }, // Captain Galvangar /// @todo: Duplicate ? Check and confirm - { 11948, 1216, 63, 63 }, //Vanndar Stormpike - { 11949, 1216, 61, 61 }, //Captain Balinda Stonehearth - { 11997, 1334, 60, 60 }, //Stormpike Herald - { 12051, 1214, 57, 57 }, //Frostwolf Legionnaire - { 12096, 1217, 55, 55 }, //Stormpike Quartermaster - { 12097, 1215, 55, 55 }, //Frostwolf Quartermaster - { 12127, 1216, 57, 57 }, //Stormpike Guardsman - { 13176, 1215, 60, 60 }, //Smith Regzar - { 13179, 1215, 59, 59 }, //Wing Commander Guse - { 13216, 1217, 58, 58 }, //Gaelden Hammersmith - { 13218, 1215, 58, 58 }, //Grunnda Wolfheart - { 13236, 1214, 60, 60 }, //Primalist Thurloga - { 13257, 1216, 60, 60 }, //Murgot Deepforge - { 13284, 1214, 58, 58 }, //Frostwolf Shaman - { 13438, 1217, 58, 58 }, //Wing Commander Slidore - { 13442, 1216, 60, 60 }, //Arch Druid Renferal - { 13443, 1216, 60, 60 }, //Druid of the Grove - { 13447, 1216, 58, 58 }, //Corporal Noreg Stormpike - { 13577, 1216, 60, 60 }, //Stormpike Ram Rider Commander - { 13617, 1216, 60, 60 }, //Stormpike Stable Master - { 13797, 32, 60, 61 }, //Mountaineer Boombellow - { 13798, 1214, 60, 61 }, //Jotek - { 13816, 1216, 61, 61 }, //Prospector Stonehewer - { 14185, 877, 59, 59 }, //Najak Hexxen - { 14186, 105, 60, 60 }, //Ravak Grimtotem - { 14187, 1594, 60, 60 }, //Athramanis - { 14188, 57, 59, 59 }, //Dirk Swindle - { 14282, 1214, 53, 54 }, //Frostwolf Bloodhound - { 14283, 1216, 53, 54 }, //Stormpike Owl - { 14284, 1216, 61, 61 }, //Stormpike Battleguard - { 11946, 1214, 63, 63 }, //Drek'Thar /// @todo: Correct the level (Level 80 for boss ?) - { 11948, 1216, 63, 63 }, //Vanndar Stormpike - { 11947, 1214, 61, 61 }, //Captain Galvangar - { 11949, 1216, 61, 61 } //Captain Balinda Stonehearth + 2225, // Zora Guthrek + 3343, // Grelkor + 3625, // Rarck + 4255, // Brogus Thunderbrew + 4257, // Lana Thunderbrew + 5134, // Jonivera Farmountain + 5135, // Svalbrad Farmountain + 5139, // Kurdrum Barleybeard + 10364, // Yaelika Farclaw + 10367, // Shrye Ragefist + 10981, // Frostwolf + 10986, // Snowblind Harpy + 10990, // Alterac Ram + 11675, // Snowblind Windcaller + 11678, // Snowblind Ambusher + 11839, // Wildpaw Brute + 11947, // Captain Galvangar + 11948, // Vanndar Stormpike + 11949, // Captain Balinda Stonehearth + 11997, // Stormpike Herald + 12051, // Frostwolf Legionnaire + 12096, // Stormpike Quartermaster + 12097, // Frostwolf Quartermaster + 12127, // Stormpike Guardsman + 13176, // Smith Regzar + 13179, // Wing Commander Guse + 13216, // Gaelden Hammersmith + 13218, // Grunnda Wolfheart + 13236, // Primalist Thurloga + 13257, // Murgot Deepforge + 13284, // Frostwolf Shaman + 13438, // Wing Commander Slidore + 13442, // Arch Druid Renferal + 13443, // Druid of the Grove + 13447, // Corporal Noreg Stormpike + 13577, // Stormpike Ram Rider Commander + 13617, // Stormpike Stable Master + 13797, // Mountaineer Boombellow + 13798, // Jotek + 13816, // Prospector Stonehewer + 14185, // Najak Hexxen + 14186, // Ravak Grimtotem + 14187, // Athramanis + 14188, // Dirk Swindle + 14282, // Frostwolf Bloodhound + 14283, // Stormpike Owl + 14284, // Stormpike Battleguard + 11946, // Drek'Thar + 11948, // Vanndar Stormpike + 11947, // Captain Galvangar + 11949, // Captain Balinda Stonehearth }; enum BG_AV_Graveyards @@ -1643,7 +1643,6 @@ class BattlegroundAV : public Battleground /*general */ Creature* AddAVCreature(uint16 cinfoid, uint16 type); - uint16 GetBonusHonor(uint8 kills); /// @todo: Remove this when the core handles this properly /*variables */ int32 m_Team_Scores[2]; @@ -1658,7 +1657,6 @@ class BattlegroundAV : public Battleground uint32 m_CaptainBuffTimer[2]; bool m_CaptainAlive[2]; - uint8 m_MaxLevel; /// @todo: Remove this once battleground->getmaxlevel() returns something usefull/is reworked (?) bool m_IsInformedNearVictory[2]; }; diff --git a/src/server/game/Battlegrounds/Zones/BattlegroundSA.h b/src/server/game/Battlegrounds/Zones/BattlegroundSA.h index 6c15b56d28..a394733441 100644 --- a/src/server/game/Battlegrounds/Zones/BattlegroundSA.h +++ b/src/server/game/Battlegrounds/Zones/BattlegroundSA.h @@ -242,7 +242,7 @@ uint32 const BG_SA_NpcEntries[BG_SA_MAXNPC] = NPC_KANRETHAD }; -Position const BG_SA_NpcSpawnlocs[BG_SA_MAXNPC + BG_SA_DEMOLISHER_AMOUNT] = +Position const BG_SA_NpcSpawnlocs[BG_SA_MAXNPC] = { // Cannons { 1436.429f, 110.05f, 41.407f, 5.4f }, From 5ffce750df90cd715627a2daf80e8a106b0316ab Mon Sep 17 00:00:00 2001 From: Aokromes Date: Sat, 28 Jun 2014 03:34:47 +0200 Subject: [PATCH 49/80] DB/Misc: Missing Graveyard in Zone 44 Closes #9724 --- sql/updates/world/2014_06_28_00_world_misc.sql | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 sql/updates/world/2014_06_28_00_world_misc.sql diff --git a/sql/updates/world/2014_06_28_00_world_misc.sql b/sql/updates/world/2014_06_28_00_world_misc.sql new file mode 100644 index 0000000000..a960642b8f --- /dev/null +++ b/sql/updates/world/2014_06_28_00_world_misc.sql @@ -0,0 +1,7 @@ +-- +SET @CGUID:= 43496; +DELETE FROM `game_graveyard_zone` WHERE `id`=2; +INSERT INTO `game_graveyard_zone` (`id`, `ghost_zone`, `faction`) VALUES (2, 44, 469); + +INSERT INTO `creature` (`guid`,`id`,`map`,`spawnMask`,`phaseMask`,`position_x`,`position_y`,`position_z`,`orientation`,`spawntimesecs`,`spawndist`,`currentwaypoint`) VALUES +(@CGUID, 6491, 0, 1, 1, -9190.865, -2308.829, 89.63705, 4.13643, 120, 0, 0); -- 6491 (Area: 44) From c97a9945645e1c3be6fbbc52b0f2b96534fad1b2 Mon Sep 17 00:00:00 2001 From: Aokromes Date: Sat, 28 Jun 2014 03:37:46 +0200 Subject: [PATCH 50/80] DB/Misc: Karen No (and related quests) By Wylath, closes #9217 --- .../world/2014_06_28_02_world_misc.sql | 83 +++++++++++++++++++ 1 file changed, 83 insertions(+) create mode 100644 sql/updates/world/2014_06_28_02_world_misc.sql diff --git a/sql/updates/world/2014_06_28_02_world_misc.sql b/sql/updates/world/2014_06_28_02_world_misc.sql new file mode 100644 index 0000000000..017116f41e --- /dev/null +++ b/sql/updates/world/2014_06_28_02_world_misc.sql @@ -0,0 +1,83 @@ +-- +SET @CGUID:= 78207; -- needs 8 + +DELETE FROM `vehicle_template_accessory` WHERE `entry` IN (31406,32513); +INSERT INTO `vehicle_template_accessory` (`entry`,`accessory_entry`,`seat_id`,`minion`,`description`,`summontype`,`summontimer`) VALUES +-- Alliance Bomber +(31406,31408,0,1,'Alliance Bomber Seat on Alliance Infra-green Bomber',8,0), +(31406,31407,1,1,'Alliance Turret Seat on Alliance Infra-green Bomber',8,0), +(31406,31409,2,1,'Alliance Engineering Seat on rides Alliance Infra-green Bomber',8,0), +(31406,32217,3,1,'Banner Bunny, Hanging, Alliance on Alliance Infra-green Bomber',8,0), +(31406,32221,4,1,'Banner Bunny, Side, Alliance on Alliance Infra-green Bomber',8,0), +(31406,32221,5,1,'Banner Bunny, Side, Alliance on Alliance Infra-green Bomber',8,0), +(31406,32256,6,1,'Shield Visual Loc Bunny on Alliance Infra-green Bomber',8,0), +(31406,32274,7,0,'Alliance Bomber Pilot rides Alliance Infra-green Bomber',6,300), +-- Horde Bomber +(32513,31856,0,1,'Horde Bomber Seat on Horde Infra-green Bomber',8,0), +(32513,31840,1,1,'Horde Turret Seat on Horde Infra-green Bomber',8,0), +(32513,32152,2,1,'Horde Engineering Seat on rides Horde Infra-green Bomber',8,0), +(32513,32214,3,1,'Banner Bunny, Hanging, Horde on Horde Infra-green Bomber',8,0), +(32513,32215,4,1,'Banner Bunny, Side, Horde on Horde Infra-green Bomber',8,0), +(32513,32215,5,1,'Banner Bunny, Side, Horde on Horde Infra-green Bomber',8,0), +(32513,32256,6,1,'Shield Visual Loc Bunny on Horde Infra-green Bomber',8,0), +(32513,32317,7,0,'Horde Bomber Pilot rides Horde Infra-green Bomber',6,300); + +DELETE FROM `npc_spellclick_spells` WHERE `npc_entry` IN (31406, 31408, 31407, 31409, 31856, 31840, 32152, 32513); +INSERT INTO `npc_spellclick_spells` (`npc_entry`,`spell_id`,`cast_flags`,`user_type`) VALUES +(31406,46598,1,0), +(31408,46598,1,0), +(31407,46598,1,0), +(31409,46598,1,0), +(31856,46598,1,0), +(31840,46598,1,0), +(32152,46598,1,0), +(32513,46598,1,0); + +DELETE FROM `gossip_menu_option` WHERE menu_id = 10120 AND id = 0; +DELETE FROM `gossip_menu_option` WHERE menu_id = 10119 AND id = 0; +INSERT INTO `gossip_menu_option` (`menu_id`,`id`,`option_icon`,`option_text`,`option_id`,`npc_option_npcflag`) VALUES +(10120,0,0,'Give me a bomber!',1,1), +(10119,0,0,'Give me a bomber!',1,1); + +DELETE FROM `conditions` WHERE `SourceTypeOrReferenceID`=15 AND `SourceGroup`=10120; +DELETE FROM `conditions` WHERE `SourceTypeOrReferenceID`=15 AND `SourceGroup`=10119; +INSERT INTO `conditions` (`SourceTypeOrReferenceId`,`SourceGroup`,`SourceEntry`,`ElseGroup`,`ConditionTypeOrReference`,`ConditionValue1`,`ConditionValue2`,`ConditionValue3`,`ErrorTextId`,`Comment`) VALUES +(15,10120,0,0,9,13373,0,0,0,'show gossip on quest 13373 taken'), +(15,10120,0,1,9,13374,0,0,0,'show gossip on quest 13374 taken'), +(15,10120,0,2,9,13376,0,0,0,'show gossip on quest 13376 taken'), +(15,10120,0,3,9,13406,0,0,0,'show gossip on quest 13406 taken'), +(15,10119,0,0,9,13381,0,0,0,'show gossip on quest 13381 taken'), +(15,10119,0,1,9,13404,0,0,0,'show gossip on quest 13404 taken'), +(15,10119,0,2,9,13380,0,0,0,'show gossip on quest 13380 taken'), +(15,10119,0,3,9,13382,0,0,0,'show gossip on quest 13382 taken'); + +UPDATE `creature_template` SET `AIName`= 'SmartAI' WHERE `entry` IN (31648,31839); +DELETE FROM `smart_scripts` WHERE `entryorguid` IN (31648,31839); +INSERT INTO `smart_scripts` (`entryorguid`, `source_type`, `id`, `link`, `event_type`, `event_phase_mask`, `event_chance`, `event_flags`, `event_param1`, `event_param2`, `event_param3`, `event_param4`, `action_type`, `action_param1`, `action_param2`, `action_param3`, `action_param4`, `action_param5`, `action_param6`, `target_type`, `target_param1`, `target_param2`, `target_param3`, `target_x`, `target_y`, `target_z`, `target_o`, `comment`) VALUES +(31648, 0, 0, 0, 62, 0,100,0, 10119, 0, 0, 0, 11, 59563, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 'Karen No - Gossip select - Cast Waiting for Bomber'), +(31839, 0, 0, 0, 62, 0,100,0, 10120, 0, 0, 0, 11, 59779, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 'Rizzy Ratchwiggle - Gossip select - Cast Waiting for Bomber'); + +UPDATE creature_template SET `spell1`=59059, `spell4`=59194, `spell5`=59196, `spell6`=59193 WHERE `entry` IN (31408, 31856); +UPDATE creature_template SET `spell1`=64979, `spell2`=61313, `spell4`=59194, `spell5`=59196, `spell6`=59193 WHERE `entry` IN (31407, 31840); +UPDATE creature_template SET `spell1`=59061, `spell2`=61171, `spell4`=59194, `spell5`=59196, `spell6`=59193 WHERE `entry` IN (31409, 32152); + +UPDATE creature_template SET `VehicleId`=273 WHERE `entry`=31406; +UPDATE creature_template SET `VehicleId`=274 WHERE `entry` IN (31408,31856); +UPDATE creature_template SET `VehicleId`=277 WHERE `entry` IN (31407,31840); +UPDATE creature_template SET `VehicleId`=278 WHERE `entry` IN (31409,32152); +UPDATE creature_template SET `VehicleId`=287 WHERE `entry`=32513; +UPDATE creature_template SET `Unit_flags`= 33554432, `unit_class`=8 WHERE `entry` IN (31407,31408,31409,31840,31856,32152); +UPDATE creature_template SET `npcflag`=16777216, `minlevel`=80, `maxlevel`=80 WHERE `entry` IN (31406,31407,31408,31409,31840,31856,32152,32513); + +UPDATE creature_template SET `InhabitType`=4 WHERE `entry` IN (32513,31406); + +DELETE FROM `creature` WHERE `id` IN (31648,32523,32524,32526,32525,31839,32529,32528); +INSERT INTO `creature` (`guid`,`id`,`map`,`spawnMask`,`phaseMask`,`equipment_id`,`position_x`,`position_y`,`position_z`,`orientation`,`spawntimesecs`) VALUES +(@CGUID+0, 31648, 571, 1, 1, 0, 7624.19, 2059.96, 600.26, 3.18997,300), +(@CGUID+1, 32523, 571, 1, 1, 1, 7637.56, 2057.34, 600.26, 2.9082,300), +(@CGUID+2, 32524, 571, 1, 1, 1, 7632.78, 2052.75, 600.26, 2.07206,300), +(@CGUID+3, 32526, 571, 1, 1, 0, 7626.84, 2069.71, 600.26, 2.85573,300), +(@CGUID+4, 32525, 571, 1, 1, 0, 7622.21, 2071.61, 600.26, 2.01916,300), +(@CGUID+5, 31839, 571, 1, 1, 0, 7891.96, 2057.73, 600.26, 6.10727,300), +(@CGUID+6, 32529, 571, 1, 1, 0, 7888.79, 2066.53, 600.26, 0.333032,300), +(@CGUID+7, 32528, 571, 1, 1, 0, 7893.77, 2067.97, 600.26, 1.02339,300); From 68958205d40cbadd2e5ad7d88f68accceedfb041 Mon Sep 17 00:00:00 2001 From: Aokromes Date: Sat, 28 Jun 2014 03:57:17 +0200 Subject: [PATCH 51/80] DB/Conditions: Arcanum of the stalwart protector Closes #10163 --- sql/updates/world/2014_06_28_03_world_misc.sql | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 sql/updates/world/2014_06_28_03_world_misc.sql diff --git a/sql/updates/world/2014_06_28_03_world_misc.sql b/sql/updates/world/2014_06_28_03_world_misc.sql new file mode 100644 index 0000000000..bc59185281 --- /dev/null +++ b/sql/updates/world/2014_06_28_03_world_misc.sql @@ -0,0 +1,5 @@ +-- +INSERT INTO `conditions` (`SourceTypeOrReferenceId`, `SourceGroup`, `SourceEntry`, `ConditionTypeOrReference`, `ConditionValue1`, `ConditionValue2`, `Comment`) VALUES +(23, 30431, 44150, 5, 1106, 192, 'Arcanum of the stalwart protector - when Argent Crusade revered/exalted'), +(23, 30431, 50369, 5, 1106, 63, 'Arcanum of the stalwart protector - when Argent Crusade not revered/exalted'); +UPDATE `gossip_menu_option` SET `OptionBroadcastTextID`='32344' WHERE `menu_id` IN (10119,10120); From 1bd4ff6cb94aa2a1dd44931664947bfb4b4887a5 Mon Sep 17 00:00:00 2001 From: jackpoz Date: Sat, 28 Jun 2014 13:49:34 +0200 Subject: [PATCH 52/80] Revert "Scripts/Ulduar: Implement Mimiron" --- .../world/2014_06_12_02_world_misc.sql | 197 -- src/server/game/Spells/SpellMgr.cpp | 10 - .../Northrend/Ulduar/Ulduar/boss_mimiron.cpp | 2931 +---------------- .../Ulduar/Ulduar/instance_ulduar.cpp | 79 +- .../scripts/Northrend/Ulduar/Ulduar/ulduar.h | 34 - 5 files changed, 44 insertions(+), 3207 deletions(-) delete mode 100644 sql/updates/world/2014_06_12_02_world_misc.sql diff --git a/sql/updates/world/2014_06_12_02_world_misc.sql b/sql/updates/world/2014_06_12_02_world_misc.sql deleted file mode 100644 index da40f46d69..0000000000 --- a/sql/updates/world/2014_06_12_02_world_misc.sql +++ /dev/null @@ -1,197 +0,0 @@ --- -DELETE FROM `vehicle_template_accessory` WHERE `entry` IN (33651, 33432); -INSERT INTO `vehicle_template_accessory` (`entry`, `accessory_entry`, `seat_id`, `minion`, `description`, `summontype`) VALUES -(33651, 34050, 5, 1, 'VX-001 Rocket Seat 6', 8), -(33651, 34050, 6, 1, 'VX-001 Rocket Seat 7', 8), -(33432, 34071, 3, 1, 'MKII MKII turret Seat 1', 8); - -DELETE FROM `npc_spellclick_spells` WHERE `npc_entry` IN (33651, 33432); -INSERT INTO `npc_spellclick_spells` (`npc_entry`, `spell_id`, `cast_flags`, `user_type`) VALUES -(33651, 46598, 1, 1), -(33432, 46598, 1, 1); - -DELETE FROM `spell_script_names` WHERE `spell_id` IN (63274, 63414, 63667, 63382, 64402, 65034, 63681, 64542, 65192, 64570, 63027, 65346, 66351, 65224, 63009, 64620, 63820, 64425, 64426, 64621, 64398, 64567, 64619, 64623, 64627, 65354, 64626, 64618, 65333, 64436, 63340, 64383, 63339, 64562, 63041, 63801, 64463); -INSERT INTO `spell_script_names` (`spell_id`, `ScriptName`) VALUES -(63801, 'spell_mimiron_bomb_bot'), -(65192, 'spell_mimiron_clear_fires'), -(65224, 'spell_mimiron_clear_fires'), -(64619, 'spell_mimiron_clear_fires'), -(65354, 'spell_mimiron_clear_fires'), -(64463, 'spell_mimiron_despawn_assault_bots'), -(64618, 'spell_mimiron_fire_search'), -(64570, 'spell_mimiron_flame_suppressant'), -(64626, 'spell_mimiron_frost_bomb_explosion'), -(65333, 'spell_mimiron_frost_bomb_explosion'), -(64436, 'spell_mimiron_magnetic_core'), -(63667, 'spell_mimiron_napalm_shell'), -(63274, 'spell_mimiron_p3wx2_laser_barrage'), -(64542, 'spell_mimiron_plasma_blast'), -(63027, 'spell_mimiron_proximity_mines'), -(66351, 'spell_mimiron_proximity_explosion'), -(63009, 'spell_mimiron_proximity_explosion'), -(65346, 'spell_mimiron_proximity_trigger'), -(63382, 'spell_mimiron_rapid_burst'), -(64402, 'spell_mimiron_rocket_strike'), -(65034, 'spell_mimiron_rocket_strike'), -(63041, 'spell_mimiron_rocket_strike_damage'), -(63681, 'spell_mimiron_rocket_strike_target_select'), -(64383, 'spell_mimiron_self_repair'), -(63414, 'spell_mimiron_spinning_up'), -(64426, 'spell_mimiron_summon_assault_bot'), -(64425, 'spell_mimiron_summon_assault_bot_target'), -(64621, 'spell_mimiron_summon_fire_bot'), -(64620, 'spell_mimiron_summon_fire_bot_target'), -(64567, 'spell_mimiron_summon_flames_initial'), -(64562, 'spell_mimiron_summon_flames_spread'), -(64627, 'spell_mimiron_summon_frost_bomb'), -(64623, 'spell_mimiron_summon_frost_bomb_target'), -(64398, 'spell_mimiron_summon_junk_bot'), -(63820, 'spell_mimiron_summon_junk_bot_target'), -(63339, 'spell_mimiron_weld'); - -DELETE FROM `spelldifficulty_dbc` WHERE `id` IN (66351, 64019, 63387, 62997, 64348, 64352, 63689, 65647, 63677, 63679, 64626); -INSERT INTO `spelldifficulty_dbc` (`id`, `spellid0`, `spellid1`) VALUES -(64348, 64348, 64536), -(64352, 64352, 64537), -(66351, 66351, 63009), -(63387, 63387, 64531), -(64019, 64019, 64532), -(62997, 62997, 64529), -(63689, 63689, 64535), -(65647, 65647, 65648), -(63677, 63677, 64533), -(63679, 63679, 64534), -(64626, 64626, 65333); - -DELETE FROM `conditions` WHERE `SourceEntry` IN (65192, 64570, 63274, 66490, 63300, 63414, 64539, 64402, 65034, 63041, 65224, 64620, 63820, 64425, 64619, 64626, 65333, 65354, 64618, 64623, 64436, 64444, 65101, 64463); -INSERT INTO `conditions` (`SourceTypeOrReferenceId`, `SourceGroup`, `SourceEntry`, `SourceId`, `ElseGroup`, `ConditionTypeOrReference`, `ConditionTarget`, `ConditionValue1`, `ConditionValue2`, `ConditionValue3`, `NegativeCondition`, `ErrorType`, `ErrorTextId`, `ScriptName`, `Comment`) VALUES -(13, 1, 65192, 0, 0, 31, 0, 3, 34363, 0, 0, 0, 0, '', 'Flame Suppressant VX EFFECT_0 can only hit NPC_FLAME'), -(13, 1, 65192, 0, 1, 31, 0, 3, 34121, 0, 0, 0, 0, '', 'Flame Suppressant VX EFFECT_0 can only hit NPC_FLAME_SPREAD'), -(13, 2, 64570, 0, 0, 31, 0, 3, 34363, 0, 0, 0, 0, '', 'Flame Suppressant MK EFFECT_1 can only hit NPC_FLAME'), -(13, 2, 64570, 0, 1, 31, 0, 3, 34121, 0, 0, 0, 0, '', 'Flame Suppressant MK EFFECT_1 can only hit NPC_FLAME_SPREAD'), -(13, 1, 64623, 0, 0, 31, 0, 3, 34363, 0, 0, 0, 0, '', 'Frost Bomb EFFECT_0 can only hit NPC_FLAME'), -(13, 1, 64623, 0, 1, 31, 0, 3, 34121, 0, 0, 0, 0, '', 'Frost Bomb EFFECT_0 can only hit NPC_FLAME_SPREAD'), -(13, 4, 64626, 0, 0, 31, 0, 3, 34363, 0, 0, 0, 0, '', 'Explosion EFFECT_2 can only hit NPC_FLAME'), -(13, 4, 64626, 0, 1, 31, 0, 3, 34121, 0, 0, 0, 0, '', 'Explosion EFFECT_2 can only hit NPC_FLAME_SPREAD'), -(13, 4, 65333, 0, 0, 31, 0, 3, 34363, 0, 0, 0, 0, '', 'Explosion EFFECT_2 can only hit NPC_FLAME'), -(13, 4, 65333, 0, 1, 31, 0, 3, 34121, 0, 0, 0, 0, '', 'Explosion EFFECT_2 can only hit NPC_FLAME_SPREAD'), -(13, 1, 65224, 0, 0, 31, 0, 3, 34363, 0, 0, 0, 0, '', 'Clear Fires EFFECT_0 can only hit NPC_FLAME'), -(13, 1, 65224, 0, 1, 31, 0, 3, 34121, 0, 0, 0, 0, '', 'Clear Fires EFFECT_0 can only hit NPC_FLAME_SPREAD'), -(13, 1, 65354, 0, 0, 31, 0, 3, 34363, 0, 0, 0, 0, '', 'Clear Fires EFFECT_0 can only hit NPC_FLAME'), -(13, 1, 65354, 0, 1, 31, 0, 3, 34121, 0, 0, 0, 0, '', 'Clear Fires EFFECT_0 can only hit NPC_FLAME_SPREAD'), -(13, 1, 64619, 0, 0, 31, 0, 3, 34363, 0, 0, 0, 0, '', 'Water Spray EFFECT_0 can only hit NPC_FLAME'), -(13, 1, 64619, 0, 1, 31, 0, 3, 34121, 0, 0, 0, 0, '', 'Water Spray EFFECT_0 can only hit NPC_FLAME_SPREAD'), -(13, 1, 64618, 0, 0, 31, 0, 3, 34363, 0, 0, 0, 0, '', 'Fire Search EFFECT_0 can only hit NPC_FLAME'), -(13, 1, 64618, 0, 1, 31, 0, 3, 34121, 0, 0, 0, 0, '', 'Fire Search EFFECT_0 can only hit NPC_FLAME_SPREAD'), -(13, 1, 64539, 0, 0, 31, 0, 3, 34071, 0, 0, 0, 0, '', 'Napalm Shell EFFECT_0 can only hit NPC_LEVIATHAN_MKII_TURRET'), -(13, 1, 63274, 0, 0, 31, 0, 3, 33576, 0, 0, 0, 0, '', 'P3Wx2 Laser Barrage EFFECT_0 can only hit NPC_DB_TARGET'), -(17, 0, 66490, 0, 0, 31, 0, 3, 33432, 0, 0, 0, 0, '', 'P3Wx2 Laser Barrage can only hit NPC_LEVIATHAN_MKII'), -(17, 0, 63300, 0, 0, 31, 0, 3, 33651, 0, 0, 0, 0, '', 'P3Wx2 Laser Barrage can only hit NPC_VX_001'), -(13, 1, 63414, 0, 0, 31, 0, 3, 33576, 0, 0, 0, 0, '', 'Spinning Up EFFECT_0 can only hit NPC_DB_TARGET'), -(13, 2, 63414, 0, 1, 31, 0, 3, 33432, 0, 0, 0, 0, '', 'Spinning Up EFFECT_1 can only hit NPC_LEVIATHAN_MKII'), -(13, 1, 64620, 0, 0, 31, 0, 3, 33856, 0, 0, 0, 0, '', 'Summon Fire Bot Trigger EFFECT_0 can only hit NPC_BOT_SUMMON_TRIGGER'), -(13, 1, 63820, 0, 0, 31, 0, 3, 33856, 0, 0, 0, 0, '', 'Summon Junk Bot Trigger EFFECT_0 can only hit NPC_BOT_SUMMON_TRIGGER'), -(13, 1, 64425, 0, 0, 31, 0, 3, 33856, 0, 0, 0, 0, '', 'Summon Assault Bot Trigger EFFECT_0 can only hit NPC_BOT_SUMMON_TRIGGER'), -(13, 1, 64402, 0, 0, 31, 0, 3, 34050, 0, 0, 0, 0, '', 'Rocket Strike EFFECT_0 can only hit NPC_ROCKET_MIMIRON_VISUAL'), -(13, 1, 65034, 0, 0, 31, 0, 3, 34050, 0, 0, 0, 0, '', 'Rocket Strike EFFECT_0 can only hit NPC_ROCKET_MIMIRON_VISUAL'), -(13, 2, 64436, 0, 0, 31, 0, 3, 33670, 0, 0, 0, 0, '', 'Magnetic Core EFFECT_1 can only hit NPC_AERIAL_COMMAND_UNIT'), -(13, 1, 64444, 0, 0, 31, 0, 3, 33670, 0, 0, 0, 0, '', 'Magnetic Core EFFECT_0 can only hit NPC_AERIAL_COMMAND_UNIT'), -(13, 1, 65101, 0, 0, 31, 0, 3, 34071, 0, 0, 0, 0, '', 'Emergency Mode EFFECT_0 can only hit NPC_LEVIATHAN_MKII_TURRET'), -(13, 1, 64463, 0, 0, 31, 0, 3, 34057, 0, 0, 0, 0, '', 'Despawn Assault Bots EFFECT_1 can only hit NPC_ASSAULT_BOT'), -(13, 2, 63041, 0, 0, 31, 0, 3, 34057, 0, 0, 0, 0, '', 'Rocket Strike EFFECT_1 can only hit NPC_ASSAULT_BOT'), -(13, 4, 63041, 0, 1, 31, 0, 3, 34137, 0, 0, 0, 0, '', 'Rocket Strike EFFECT_2 can only hit NPC_EMERGENCY_FIRE_BOT'), -(13, 4, 63041, 0, 2, 31, 0, 3, 33855, 0, 0, 0, 0, '', 'Rocket Strike EFFECT_2 can only hit NPC_JUNK_BOT'), -(13, 4, 63041, 0, 3, 31, 0, 3, 33836, 0, 0, 0, 0, '', 'Rocket Strike EFFECT_2 can only hit NPC_BOMB_BOT'); - -UPDATE `creature_template` SET `HoverHeight` = 15, `ScriptName` = 'boss_aerial_command_unit', `InhabitType` = 3, `mechanic_immune_mask` = 646135679 WHERE `entry` = 33670; -UPDATE `creature_template` SET `HoverHeight` = 15, `InhabitType` = 3, `mechanic_immune_mask` = 646135679 WHERE `entry` = 34109; -UPDATE `creature_template` SET `unit_flags` = 33554438, `ScriptName` = 'boss_vx_001', `mechanic_immune_mask` = 646135679 WHERE `entry` = 33651; -UPDATE `creature_template` SET `unit_flags` = 33554438, `mechanic_immune_mask` = 646135679 WHERE `entry` = 34108; -UPDATE `creature_template` SET `ScriptName` = 'boss_leviathan_mk_ii', `mechanic_immune_mask` = 646135679 WHERE `entry` = 33432; -UPDATE `creature_template` SET `mechanic_immune_mask` = 646135679 WHERE `entry` = 34106; -UPDATE `creature_template` SET `ScriptName` = 'boss_mimiron' WHERE `entry` = 33350; -UPDATE `creature_template` SET `unit_flags` = 131078, `ScriptName` = 'npc_mimiron_proximity_mine' WHERE `entry` = 34362; -UPDATE `creature_template` SET `unit_flags` = 33685510, `faction` = 14, `ScriptName` = 'npc_mimiron_frost_bomb' WHERE `entry` = 34149; -UPDATE `creature_template` SET `unit_flags` = 33685510 WHERE `entry` IN( 34047, 34110, 34050, 34071); -UPDATE `creature_template` SET `unit_flags` = 33685510, `ScriptName` = 'npc_magnetic_core' WHERE `entry` = 34068; -UPDATE `creature_template` SET `unit_flags` = 33554436, `ScriptName` = 'npc_mimiron_flames' WHERE `entry` IN (34363, 34121); -UPDATE `creature_template` SET `faction` = 14 , `ScriptName` = 'npc_mimiron_emergency_fire_bot' WHERE `entry` = 34147; -UPDATE `creature_template` SET `faction` = 14 WHERE `entry` IN (34148, 34114, 33855); -UPDATE `creature_template` SET `dmg_multiplier` = 60, `ScriptName` = 'npc_mimiron_assault_bot' WHERE `entry` = 34057; -UPDATE `creature_template` SET `dmg_multiplier` = 75 WHERE `entry` = 34115; -UPDATE `creature_template` SET `ScriptName` = 'npc_mimiron_computer' WHERE `entry` = 34143; -UPDATE `creature_template` SET `speed_walk` = 2.5 , `speed_run` = 4.5 , `MovementType` = 2 , `InhabitType` = 7, `flags_extra` = 2 WHERE `entry` = 33576; - -UPDATE `creature_model_info` SET `bounding_radius` = 0.31 , `combat_reach` = 5 WHERE `modelid` = 28979; -UPDATE `creature_model_info` SET `bounding_radius` = 0.306 , `combat_reach` = 7 WHERE `modelid` = 28841; -UPDATE `creature_model_info` SET `bounding_radius` = 0.31 , `combat_reach` = 7.7 WHERE `modelid` = 28831; - -UPDATE `gameobject_template` SET `flags` = 32, `ScriptName` = 'go_mimiron_hardmode_button' WHERE `entry` = 194739; - -UPDATE `creature_addon` SET `path_id` = 1376200 WHERE `guid` = 137620; - -UPDATE `creature` SET `position_x` = 2785.423 , `position_y` = 2673.119, `MovementType` = 2 WHERE `guid` = 137620; - -DELETE FROM `creature_template_addon` WHERE `entry` IN (34047, 34148, 33836, 34149, 34147, 33670, 34363, 34121); -INSERT INTO `creature_template_addon` (`entry`, `bytes1`, `bytes2`, `auras`) VALUES -(33670, 50331648, 1, ''), -(34363, 0, 1, '64561'), -(34121, 0, 1, '64561'), -(34047, 0, 1, '64064'), -(34148, 0, 1, '64616 64617'), -(33836, 0, 1, '63767'), -(34149, 0, 1, '64624'), -(34147, 0, 1, '64617'); - -DELETE FROM `creature_text` WHERE `entry` = 34143; -INSERT INTO `creature_text` (`entry`, `groupid`, `text`, `type`, `probability`, `sound`, `BroadcastTextID`) VALUES -(34143, 0, 'Self-destruct sequence initiated.', 14, 100, 15413, 34268), -(34143, 1, 'Self-destruct sequence terminated. Override code A905..', 14, 100, 15414, 34284), -(34143, 2, 'This area will self-destruct in ten minutes.', 14, 100, 15415, 34283), -(34143, 3, 'This area will self-destruct in nine minutes.', 14, 100, 15416, 34282), -(34143, 4, 'This area will self-destruct in eight minutes.', 14, 100, 15417, 34281), -(34143, 5, 'This area will self-destruct in seven minutes.', 14, 100, 15418, 34280), -(34143, 6, 'This area will self-destruct in six minutes.', 14, 100, 15419, 34273), -(34143, 7, 'This area will self-destruct in five minutes.', 14, 100, 15420, 34274), -(34143, 8, 'This area will self-destruct in four minutes.', 14, 100, 15421, 34275), -(34143, 9, 'This area will self-destruct in three minutes.', 14, 100, 15422, 34276), -(34143, 10, 'This area will self-destruct in two minutes.', 14, 100, 15423, 34277), -(34143, 11, 'This area will self-destruct in one minute.', 14, 100, 15424, 34278), -(34143, 12, 'Self-destruct sequence finalized. Have a nice day.', 14, 100, 15425, 34279); - -DELETE FROM `waypoint_data` WHERE `id` = 1376200; -INSERT INTO `waypoint_data` (`id`, `point`, `position_x`, `position_y`, `position_z`, `orientation`, `delay`, `move_flag`, `action`, `action_chance`, `wpguid`) VALUES -(1376200, 0, 2785.423, 2673.119, 372.3605, 0, 0, 1, 0, 100, 0), -(1376200, 1, 2823.024, 2649.059, 371.9791, 0, 0, 1, 0, 100, 0), -(1376200, 2, 2854.097, 2590.826, 371.9791, 0, 0, 1, 0, 100, 0), -(1376200, 3, 2852.951, 2547.112, 372.3605, 0, 0, 1, 0, 100, 0), -(1376200, 4, 2822.796, 2489.515, 372.3605, 0, 0, 1, 0, 100, 0), -(1376200, 5, 2784.964, 2465.247, 372.3605, 0, 0, 1, 0, 100, 0), -(1376200, 6, 2741.24, 2456.771, 372.3605, 0, 0, 1, 0, 100, 0), -(1376200, 7, 2701.036, 2464.319, 372.3605, 0, 0, 1, 0, 100, 0), -(1376200, 8, 2660.476, 2489.578, 372.3605, 0, 0, 1, 0, 100, 0), -(1376200, 9, 2636.893, 2525.687, 372.3605, 0, 0, 1, 0, 100, 0), -(1376200, 10, 2631.294, 2547.831, 372.3605, 0, 0, 1, 0, 100, 0), -(1376200, 11, 2631.437, 2591.752, 372.3605, 0, 0, 1, 0, 100, 0), -(1376200, 12, 2637.362, 2613.7, 372.3605, 0, 0, 1, 0, 100, 0), -(1376200, 13, 2650.221, 2636.163, 372.3605, 0, 0, 1, 0, 100, 0), -(1376200, 14, 2661.572, 2649.715, 372.3605, 0, 0, 1, 0, 100, 0), -(1376200, 15, 2696.595, 2672.664, 372.3605, 0, 0, 1, 0, 100, 0), -(1376200, 16, 2711.099, 2677.791, 372.3605, 0, 0, 1, 0, 100, 0), -(1376200, 17, 2740.676, 2683.12, 372.3605, 0, 0, 1, 0, 100, 0), -(1376200, 18, 2771.834, 2677.767, 372.3605, 0, 0, 1, 0, 100, 0); - -DELETE FROM `disables` WHERE `sourceType` = 4 AND `entry` IN (10544, 10547, 10543, 10546, 10548, 10545, 10450, 10463, 10406, 10405); - -DELETE FROM `achievement_criteria_data` WHERE `criteria_id` IN (10406, 10405) AND `type` = 18; -DELETE FROM `achievement_criteria_data` WHERE `criteria_id` IN (10545,10548,10546,10543,10547,10544, 10450, 10463) AND `type` = 11; -INSERT INTO `achievement_criteria_data` (`criteria_id`, `type`, `value1`, `value2`, `ScriptName`) VALUES -(10544, 11, 0, 0, 'achievement_setup_rocket'), -(10547, 11, 0, 0, 'achievement_setup_rocket'), -(10543, 11, 0, 0, 'achievement_setup_mine'), -(10546, 11, 0, 0, 'achievement_setup_mine'), -(10548, 11, 0, 0, 'achievement_setup_boom'), -(10545, 11, 0, 0, 'achievement_setup_boom'), -(10450, 11, 0, 0, 'achievement_firefighter'), -(10463, 11, 0, 0, 'achievement_firefighter'); --- diff --git a/src/server/game/Spells/SpellMgr.cpp b/src/server/game/Spells/SpellMgr.cpp index e954b9173a..6f56c0ebc4 100644 --- a/src/server/game/Spells/SpellMgr.cpp +++ b/src/server/game/Spells/SpellMgr.cpp @@ -3347,16 +3347,6 @@ void SpellMgr::LoadSpellInfoCorrections() // that will be clear if we get more spells with problem like this spellInfo->AttributesEx |= SPELL_ATTR1_DISPEL_AURAS_ON_IMMUNITY; break; - case 63414: // Spinning Up (Mimiron) - spellInfo->Effects[EFFECT_0].TargetB = SpellImplicitTargetInfo(TARGET_UNIT_CASTER); - spellInfo->ChannelInterruptFlags = 0; - break; - case 63036: // Rocket Strike (Mimiron) - spellInfo->Speed = 0; - break; - case 64668: // Magnetic Field (Mimiron) - spellInfo->Mechanic = MECHANIC_NONE; - break; case 64468: // Empowering Shadows (Yogg-Saron) case 64486: // Empowering Shadows (Yogg-Saron) spellInfo->MaxAffectedTargets = 3; // same for both modes? diff --git a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_mimiron.cpp b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_mimiron.cpp index a3c3077fd7..f284aacf99 100644 --- a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_mimiron.cpp +++ b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_mimiron.cpp @@ -15,16 +15,10 @@ * with this program. If not, see . */ -#include "Cell.h" -#include "CellImpl.h" -#include "GridNotifiers.h" -#include "GridNotifiersImpl.h" #include "ScriptMgr.h" #include "ScriptedCreature.h" #include "SpellScript.h" -#include "SpellAuraEffects.h" #include "ulduar.h" -#include "Vehicle.h" enum Yells { @@ -45,2927 +39,78 @@ enum Yells SAY_BERSERK = 14 }; -enum ComputerYells -{ - SAY_SELF_DESTRUCT_INITIATED = 0, - SAY_SELF_DESTRUCT_TERMINATED = 1, - SAY_SELF_DESTRUCT_10 = 2, - SAY_SELF_DESTRUCT_9 = 3, - SAY_SELF_DESTRUCT_8 = 4, - SAY_SELF_DESTRUCT_7 = 5, - SAY_SELF_DESTRUCT_6 = 6, - SAY_SELF_DESTRUCT_5 = 7, - SAY_SELF_DESTRUCT_4 = 8, - SAY_SELF_DESTRUCT_3 = 9, - SAY_SELF_DESTRUCT_2 = 10, - SAY_SELF_DESTRUCT_1 = 11, - SAY_SELF_DESTRUCT_FINALIZED = 12 -}; - enum Spells { - // Mimiron - SPELL_WELD = 63339, // Idle aura. - SPELL_SEAT_1 = 52391, // Cast on all vehicles, Cycled on MKII - SPELL_SEAT_2 = 63313, // Cast on MKII and VX-001, Cycled on MKII - SPELL_SEAT_3 = 63314, // Cast on MKII, Cycled on MKII - SPELL_SEAT_5 = 63316, // Cast on MKII and VX-001, Cycled on MKII - SPELL_SEAT_6 = 63344, // Cast on MKII - SPELL_SEAT_7 = 63345, // Cast on MKII SPELL_JETPACK = 63341, - SPELL_DESPAWN_ASSAULT_BOTS = 64463, // only despawns assault bots... no equivalent spell for the other adds... - SPELL_TELEPORT_VISUAL = 41232, - SPELL_SLEEP_VISUAL_1 = 64393, - SPELL_SLEEP_VISUAL_2 = 64394, - + SPELL_EMERGENCY_MODE = 64582, + SPELL_SELF_REPAIR = 64383, + SPELL_MAGNETIC_CORE = 64444, // Leviathan MK II SPELL_FLAME_SUPPRESSANT_MK = 64570, SPELL_NAPALM_SHELL = 63666, - SPELL_FORCE_CAST_NAPALM_SHELL = 64539, - SPELL_PLASMA_BLAST = 62997, - SPELL_SCRIPT_EFFECT_PLASMA_BLAST = 64542, + SPELL_PLASMA_BLAST = 62977, + SPELL_PROXIMITY_MINES = 63027, SPELL_SHOCK_BLAST = 63631, - SPELL_SHOCK_BLAST_AURA = 63632, // Deprecated? It is never cast. - - // VX-001 + // VX 001 SPELL_FLAME_SUPPRESSANT_VX = 65192, + SPELL_FROSTBOMB = 64623, + SPELL_HAND_PULSE = 64348, SPELL_SPINNING_UP = 63414, - SPELL_HEAT_WAVE_AURA = 63679, - SPELL_HAND_PULSE_LEFT = 64348, - SPELL_HAND_PULSE_RIGHT = 64352, - SPELL_MOUNT_MKII = 64387, - SPELL_TORSO_DISABLED = 64120, - + SPELL_RAPID_BURST = 63387, + SPELL_P3WX2_LASER_BARRAGE = 63293, + SPELL_ROCKET_STRIKE = 63041, + SPELL_HEAT_WAVE = 63677, // Aerial Command Unit - SPELL_PLASMA_BALL_P1 = 63689, - SPELL_PLASMA_BALL_P2 = 65647, - SPELL_MOUNT_VX_001 = 64388, - - // Proximity Mines - SPELL_PROXIMITY_MINES = 63027, // Cast by Leviathan MK II - SPELL_PROXIMITY_MINE_EXPLOSION = 66351, - SPELL_PROXIMITY_MINE_TRIGGER = 65346, - SPELL_PROXIMITY_MINE_PERIODIC_TRIGGER = 65345, - SPELL_PERIODIC_PROXIMITY_AURA = 65345, - SPELL_SUMMON_PROXIMITY_MINE = 65347, - - // Rapid Burst - SPELL_RAPID_BURST_LEFT = 63387, - SPELL_RAPID_BURST_RIGHT = 64019, - SPELL_RAPID_BURST = 63382, // Cast by VX-001 - SPELL_RAPID_BURST_TARGET_ME = 64841, // Cast by Burst Target - SPELL_SUMMON_BURST_TARGET = 64840, // Cast by VX-001 - - // Rocket Strike - SPELL_SUMMON_ROCKET_STRIKE = 63036, - SPELL_SCRIPT_EFFECT_ROCKET_STRIKE = 63681, // Cast by Rocket (Mimiron Visual) - SPELL_ROCKET_STRIKE = 64064, // Added in creature_template_addon - SPELL_ROCKET_STRIKE_LEFT = 64402, // Cast by VX-001 - SPELL_ROCKET_STRIKE_BOTH = 65034, // Cast by VX-001 - - // Flames - SPELL_FLAMES_PERIODIC_TRIGGER = 64561, // Added in creature_template_addon - SPELL_SUMMON_FLAMES_SPREAD_TRIGGER = 64562, - SPELL_SUMMON_FLAMES_INITIAL = 64563, - SPELL_SUMMON_FLAMES_SPREAD = 64564, - SPELL_FLAMES = 64566, - SPELL_SCRIPT_EFFECT_SUMMON_FLAMES_INITIAL = 64567, - - // Frost Bomb - SPELL_SCRIPT_EFFECT_FROST_BOMB = 64623, // Cast by VX-001 - SPELL_FROST_BOMB_LINKED = 64624, // Added in creature_template_addon - SPELL_FROST_BOMB_DUMMY = 64625, - SPELL_SUMMON_FROST_BOMB = 64627, // Cast by VX-001 - SPELL_FROST_BOMB_EXPLOSION = 64626, - SPELL_CLEAR_FIRES = 65354, - - // Bots - SPELL_SUMMON_FIRE_BOT = 64622, - SPELL_SUMMON_FIRE_BOT_DUMMY = 64621, - SPELL_SUMMON_FIRE_BOT_TRIGGER = 64620, // Cast by Areal Command Unit - SPELL_DEAFENING_SIREN = 64616, // Added in creature_template_addon - SPELL_FIRE_SEARCH_AURA = 64617, // Added in creature_template_addon - SPELL_FIRE_SEARCH = 64618, - SPELL_WATER_SPRAY = 64619, - - SPELL_SUMMON_JUNK_BOT = 63819, - SPELL_SUMMON_JUNK_BOT_TRIGGER = 63820, // Cast by Areal Command Unit - SPELL_SUMMON_JUNK_BOT_DUMMY = 64398, - - SPELL_SUMMON_ASSAULT_BOT_TRIGGER = 64425, // Cast by Areal Command Unit - SPELL_SUMMON_ASSAULT_BOT_DUMMY = 64426, - SPELL_SUMMON_ASSAULT_BOT = 64427, + SPELL_PLASMA_BALL = 63689, + // Additonal spells SPELL_MAGNETIC_FIELD = 64668, - - SPELL_SUMMON_BOMB_BOT = 63811, // Cast by Areal Command Unit - SPELL_BOMB_BOT_AURA = 63767, // Added in creature_template_addon - - // Miscellaneous - SPELL_SELF_DESTRUCTION_AURA = 64610, - SPELL_SELF_DESTRUCTION_VISUAL = 64613, - SPELL_NOT_SO_FRIENDLY_FIRE = 65040, - SPELL_ELEVATOR_KNOCKBACK = 65096, // Cast by worldtrigger. - SPELL_VEHICLE_DAMAGED = 63415, - SPELL_EMERGENCY_MODE = 64582, // mkii, vx001, aerial, assault, junk - SPELL_EMERGENCY_MODE_TURRET = 65101, // Cast by Leviathan MK II, only hits Leviathan MK II turret - SPELL_SELF_REPAIR = 64383, - SPELL_MAGNETIC_CORE = 64436, - SPELL_MAGNETIC_CORE_VISUAL = 64438, - SPELL_HALF_HEAL = 64188, - SPELL_CLEAR_ALL_DEBUFFS = 34098, // TODO: make use of this spell... - SPELL_FREEZE_ANIM_STUN = 63354, // used to prevent mkii from doing stuff?.. - SPELL_FREEZE_ANIM = 16245 // Idle aura. Freezes animation. + SPELL_DEAFENING_SIREN = 64616, + SPELL_WATER_SPRAY = 64619, + SPELL_FROST_BOMB_HARD_MODE = 64627, + SPELL_EXPLOSION = 66351, + SPELL_DISARM = 1842, + SPELL_RIDE_VEHICLE = 46598, + SPELL_TRIGGER_MISSILE = 65347, }; -enum Data +enum Npc { - DATA_SETUP_MINE, - DATA_SETUP_BOMB, - DATA_SETUP_ROCKET, - DATA_NOT_SO_FRIENDLY_FIRE, - DATA_FIREFIGHTER, - DATA_WATERSPRAY, - DATA_MOVE_NEW + NPC_ASSAULT_BOT = 34057, + NPC_BOMB_BOT = 33836, + NPC_JUNK_BOT = 33855, + NPC_EMERGENCE_FIRE_BOT = 34147, + NPC_FROST_BOMB = 34149, }; -enum Events -{ - EVENT_NONE, - - // Leviathan MK II - EVENT_PROXIMITY_MINE, - EVENT_NAPALM_SHELL, - EVENT_PLASMA_BLAST, - EVENT_SHOCK_BLAST, - EVENT_FLAME_SUPPRESSANT_MK, - - // VX-001 - EVENT_RAPID_BURST, - EVENT_SPINNING_UP, - EVENT_ROCKET_STRIKE, - EVENT_HAND_PULSE, - EVENT_FROST_BOMB, - EVENT_FLAME_SUPPRESSANT_VX, - EVENT_RELOAD, - - // Aerial Command Unit - EVENT_SUMMON_FIRE_BOTS, - EVENT_SUMMON_JUNK_BOT, - EVENT_SUMMON_ASSAULT_BOT, - EVENT_SUMMON_BOMB_BOT, - - // Mimiron - EVENT_SUMMON_FLAMES, - EVENT_INTRO_1, - EVENT_INTRO_2, - EVENT_INTRO_3, - - EVENT_VX001_ACTIVATION_1, - EVENT_VX001_ACTIVATION_2, - EVENT_VX001_ACTIVATION_3, - EVENT_VX001_ACTIVATION_4, - EVENT_VX001_ACTIVATION_5, - EVENT_VX001_ACTIVATION_6, - EVENT_VX001_ACTIVATION_7, - EVENT_VX001_ACTIVATION_8, - EVENT_VX001_ACTIVATION_9, - - EVENT_AERIAL_ACTIVATION_1, - EVENT_AERIAL_ACTIVATION_2, - EVENT_AERIAL_ACTIVATION_3, - EVENT_AERIAL_ACTIVATION_4, - EVENT_AERIAL_ACTIVATION_5, - EVENT_AERIAL_ACTIVATION_6, - - EVENT_VOL7RON_ACTIVATION_1, - EVENT_VOL7RON_ACTIVATION_2, - EVENT_VOL7RON_ACTIVATION_3, - EVENT_VOL7RON_ACTIVATION_4, - EVENT_VOL7RON_ACTIVATION_5, - EVENT_VOL7RON_ACTIVATION_6, - EVENT_VOL7RON_ACTIVATION_7, - - EVENT_OUTTRO_1, - EVENT_OUTTRO_2, - EVENT_OUTTRO_3, - - // Computer - EVENT_SELF_DESTRUCT_10, - EVENT_SELF_DESTRUCT_9, - EVENT_SELF_DESTRUCT_8, - EVENT_SELF_DESTRUCT_7, - EVENT_SELF_DESTRUCT_6, - EVENT_SELF_DESTRUCT_5, - EVENT_SELF_DESTRUCT_4, - EVENT_SELF_DESTRUCT_3, - EVENT_SELF_DESTRUCT_2, - EVENT_SELF_DESTRUCT_1, - EVENT_SELF_DESTRUCT_FINALIZED -}; - -enum Actions -{ - DO_START_MKII, - DO_HARDMODE_MKII, - - DO_ACTIVATE_VX001, - DO_START_VX001, - DO_HARDMODE_VX001, - - DO_ACTIVATE_AERIAL, - DO_START_AERIAL, - DO_HARDMODE_AERIAL, - DO_DISABLE_AERIAL, - DO_ENABLE_AERIAL, - - DO_ACTIVATE_V0L7R0N_1, - DO_ACTIVATE_V0L7R0N_2, - DO_ASSEMBLED_COMBAT, // All 3 parts use this action, its done on purpose. - - DO_ACTIVATE_HARD_MODE, - DO_ACTIVATE_COMPUTER, - DO_DEACTIVATE_COMPUTER, - DO_ACTIVATE_SELF_DESTRUCT, - - DO_ENCOUNTER_DONE -}; - -enum Waypoints -{ - WP_NONE, - WP_MKII_P1_IDLE, - WP_MKII_P4_POS_1, - WP_MKII_P4_POS_2, - WP_MKII_P4_POS_3, - WP_MKII_P4_POS_4, - WP_MKII_P4_POS_5, - WP_AERIAL_P4_POS -}; - -enum Phases -{ - PHASE_NULL, - - // Leviathan MK II - PHASE_LEVIATHAN_SOLO, - PHASE_LEVIATHAN_ASSEMBLED, - - // VX-001 - PHASE_VX001_SOLO, - PHASE_VX001_ASSEMBLED, - - // Aerial Command Unit - PHASE_AERIAL_SOLO, - PHASE_AERIAL_ASSEMBLED -}; - -uint32 const repairSpells[4] = -{ - SPELL_SEAT_1, - SPELL_SEAT_2, - SPELL_SEAT_3, - SPELL_SEAT_5 -}; - -Position const pos[] = -{ - {}, - { 2792.070f, 2596.320f, 364.3136f }, // WP_MKII_P1_IDLE - { 2765.945f, 2571.095f, 364.0636f }, // WP_MKII_P4_POS_1 - { 2768.195f, 2573.095f, 364.0636f }, // WP_MKII_P4_POS_2 - { 2763.820f, 2568.870f, 364.3136f }, // WP_MKII_P4_POS_3 - { 2761.215f, 2568.875f, 364.0636f }, // WP_MKII_P4_POS_4 - { 2744.610f, 2569.380f, 364.3136f }, // WP_MKII_P4_POS_5 - { 2748.513f, 2569.051f, 364.3136f } // WP_AERIAL_P4_POS -}; - -class AerialTargetSelector +class spell_ulduar_proximity_mines : public SpellScriptLoader { public: - AerialTargetSelector() {} - - bool operator() (WorldObject* target) const + spell_ulduar_proximity_mines() : SpellScriptLoader("spell_ulduar_proximity_mines") { } + + class spell_ulduar_proximity_minesSpellScript : public SpellScript { - if (Unit* unit = target->ToUnit()) - { - if (unit->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE)) - return true; - - if (unit->GetVehicleBase()) - return true; - } - return false; - } -}; - -class EncounterDone -{ - Unit* _who; - Creature* _vehiclePart; - public: - EncounterDone(Unit* who, Creature* vehiclePart) : _who(who), _vehiclePart(vehiclePart) {} - - bool IsEncounterFinished() - { - if (InstanceScript* instance = _vehiclePart->GetInstanceScript()) - if (Creature* mkii = ObjectAccessor::GetCreature(*_vehiclePart, instance->GetData64(DATA_LEVIATHAN_MK_II))) - if (Creature* vx001 = ObjectAccessor::GetCreature(*_vehiclePart, instance->GetData64(DATA_VX_001))) - if (Creature* aerial = ObjectAccessor::GetCreature(*_vehiclePart, instance->GetData64(DATA_AERIAL_COMMAND_UNIT))) - if (mkii->getStandState() == UNIT_STAND_STATE_DEAD && vx001->getStandState() == UNIT_STAND_STATE_DEAD && aerial->getStandState() == UNIT_STAND_STATE_DEAD) - { - _who->Kill(mkii); - _who->Kill(vx001); - _who->Kill(aerial); - mkii->DespawnOrUnsummon(120000); - vx001->DespawnOrUnsummon(120000); - aerial->DespawnOrUnsummon(120000); - if (Creature* mimiron = ObjectAccessor::GetCreature(*_vehiclePart, instance->GetData64(BOSS_MIMIRON))) - mimiron->AI()->JustDied(_who); - return true; - } - return false; - } -}; - -class boss_mimiron : public CreatureScript -{ - public: - boss_mimiron() : CreatureScript("boss_mimiron") { } - - struct boss_mimironAI : public BossAI - { - boss_mimironAI(Creature* creature) : BossAI(creature, BOSS_MIMIRON) - { - me->SetReactState(REACT_PASSIVE); - me->SetDisableGravity(true); - } - - void DoAction(int32 action) override - { - switch (action) - { - case DO_ACTIVATE_VX001: - events.ScheduleEvent(EVENT_VX001_ACTIVATION_1, 1000); - break; - case DO_ACTIVATE_AERIAL: - events.ScheduleEvent(EVENT_AERIAL_ACTIVATION_1, 5000); - break; - case DO_ACTIVATE_V0L7R0N_1: - Talk(SAY_AERIAL_DEATH); - if (Creature* mkii = ObjectAccessor::GetCreature(*me, instance->GetData64(DATA_LEVIATHAN_MK_II))) - mkii->GetMotionMaster()->MovePoint(WP_MKII_P4_POS_3, pos[WP_MKII_P4_POS_3]); // MovePoint(WP_MKII_P4_POS_1, pos[WP_MKII_P4_POS_1]); - break; - case DO_ACTIVATE_V0L7R0N_2: - events.ScheduleEvent(EVENT_VOL7RON_ACTIVATION_1, 1000); - break; - case DO_ACTIVATE_HARD_MODE: - _fireFighter = true; - DoZoneInCombat(me); - break; - default: - break; - } - } - - void EnterCombat(Unit* /*who*/) override - { - if (!me->GetVehicleBase()) - return; - - _EnterCombat(); - me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); - me->RemoveAurasDueToSpell(SPELL_WELD); - DoCast(me->GetVehicleBase(), SPELL_SEAT_6); - - if (GameObject* button = ObjectAccessor::GetGameObject(*me, instance->GetData64(DATA_MIMIRON_BUTTON))) - button->SetFlag(GAMEOBJECT_FLAGS, GO_FLAG_NOT_SELECTABLE); - - if (_fireFighter) - events.ScheduleEvent(EVENT_SUMMON_FLAMES, 3000); - events.ScheduleEvent(EVENT_INTRO_1, 1500); - } - - void JustDied(Unit* /*who*/) override - { - instance->SetBossState(BOSS_MIMIRON, DONE); - events.Reset(); - me->CombatStop(true); - me->SetDisableGravity(false); - DoCast(me, SPELL_SLEEP_VISUAL_1); - DoCastAOE(SPELL_DESPAWN_ASSAULT_BOTS); - me->ExitVehicle(); - // ExitVehicle() offset position is not implemented, so we make up for that with MoveJump()... - me->GetMotionMaster()->MoveJump(me->GetPositionX() + (float)(10 * cos(me->GetOrientation())), me->GetPositionY() + (float)(10 * sin(me->GetOrientation())), me->GetPositionZ(), 10.0f, 5.0f); - events.ScheduleEvent(EVENT_OUTTRO_1, 7000); - } - - void Reset() override - { - if (instance->GetBossState(BOSS_MIMIRON) == DONE) // Mimiron will attempt to reset because he is not dead and will be set to friendly before despawning. - return; - - _Reset(); - me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); - instance->SetData(DATA_MIMIRON_ELEVATOR, GO_STATE_ACTIVE); - - if (_fireFighter) - if (Creature* computer = ObjectAccessor::GetCreature(*me, instance->GetData64(DATA_COMPUTER))) - computer->AI()->DoAction(DO_DEACTIVATE_COMPUTER); - - if (GameObject* button = ObjectAccessor::GetGameObject(*me, instance->GetData64(DATA_MIMIRON_BUTTON))) - { - button->SetGoState(GO_STATE_READY); - button->RemoveFlag(GAMEOBJECT_FLAGS,GO_FLAG_NOT_SELECTABLE); - } - - _fireFighter = false; - DoCast(me, SPELL_WELD); - - if (Unit* mkii = ObjectAccessor::GetUnit(*me, instance->GetData64(DATA_LEVIATHAN_MK_II))) - DoCast(mkii, SPELL_SEAT_3); - } - - void UpdateAI(uint32 diff) override - { - if ((!UpdateVictim() || !CheckInRoom()) && instance->GetBossState(BOSS_MIMIRON) != DONE) - return; - - events.Update(diff); - - if (me->HasUnitState(UNIT_STATE_CASTING)) - return; - - while (uint32 eventId = events.ExecuteEvent()) - { - switch (eventId) - { - case EVENT_SUMMON_FLAMES: - if (Unit* worldtrigger = ObjectAccessor::GetUnit(*me, instance->GetData64(DATA_MIMIRON_WORLD_TRIGGER))) - worldtrigger->CastSpell((Unit*)NULL, SPELL_SCRIPT_EFFECT_SUMMON_FLAMES_INITIAL, true, NULL, NULL, me->GetGUID()); - events.RescheduleEvent(EVENT_SUMMON_FLAMES, 28000); - break; - case EVENT_INTRO_1: - Talk(_fireFighter ? SAY_HARDMODE_ON : SAY_MKII_ACTIVATE); - events.ScheduleEvent(EVENT_INTRO_2, 5000); - break; - case EVENT_INTRO_2: - if (Unit* mkii = me->GetVehicleBase()) - { - DoCast(mkii, SPELL_SEAT_7); - mkii->RemoveAurasDueToSpell(SPELL_FREEZE_ANIM); - mkii->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE); - } - events.ScheduleEvent(EVENT_INTRO_3, 2000); - break; - case EVENT_INTRO_3: - me->GetVehicleBase()->GetAI()->DoAction(_fireFighter? DO_HARDMODE_MKII : DO_START_MKII); - break; - case EVENT_VX001_ACTIVATION_1: - me->GetVehicleBase()->SetFacingTo(3.686f); - events.ScheduleEvent(EVENT_VX001_ACTIVATION_2, 1000); - break; - case EVENT_VX001_ACTIVATION_2: - DoCast(me->GetVehicleBase(), SPELL_SEAT_6); - events.ScheduleEvent(EVENT_VX001_ACTIVATION_3, 1000); - break; - case EVENT_VX001_ACTIVATION_3: - Talk(SAY_MKII_DEATH); - events.ScheduleEvent(EVENT_VX001_ACTIVATION_4, 5000); - break; - case EVENT_VX001_ACTIVATION_4: - instance->SetData(DATA_MIMIRON_ELEVATOR, GO_STATE_READY); - if (Unit* worldtrigger = ObjectAccessor::GetUnit(*me, instance->GetData64(DATA_MIMIRON_WORLD_TRIGGER))) - worldtrigger->CastSpell(worldtrigger, SPELL_ELEVATOR_KNOCKBACK); - events.ScheduleEvent(EVENT_VX001_ACTIVATION_5, 6000); - break; - case EVENT_VX001_ACTIVATION_5: - me->SummonCreature(NPC_VX_001, 2744.431f, 2569.385f, 364.3968f, 3.141593f, TEMPSUMMON_MANUAL_DESPAWN); - instance->SetData(DATA_MIMIRON_ELEVATOR, GO_STATE_ACTIVE_ALTERNATIVE); - events.ScheduleEvent(EVENT_VX001_ACTIVATION_6, 19000); - break; - case EVENT_VX001_ACTIVATION_6: - if (Unit* vx001 = ObjectAccessor::GetUnit(*me, instance->GetData64(DATA_VX_001))) - DoCast(vx001, SPELL_SEAT_1); - events.ScheduleEvent(EVENT_VX001_ACTIVATION_7, 3500); - break; - case EVENT_VX001_ACTIVATION_7: - Talk(SAY_VX001_ACTIVATE); - events.ScheduleEvent(EVENT_VX001_ACTIVATION_8, 4000); - break; - case EVENT_VX001_ACTIVATION_8: - DoCast(me->GetVehicleBase(), SPELL_SEAT_2); - events.ScheduleEvent(EVENT_VX001_ACTIVATION_9, 3000); - break; - case EVENT_VX001_ACTIVATION_9: - me->GetVehicleBase()->GetAI()->DoAction(_fireFighter ? DO_HARDMODE_VX001 : DO_START_VX001); - break; - case EVENT_AERIAL_ACTIVATION_1: - DoCast(me->GetVehicleBase(), SPELL_SEAT_5); - events.ScheduleEvent(EVENT_AERIAL_ACTIVATION_2, 2500); - break; - case EVENT_AERIAL_ACTIVATION_2: - Talk(SAY_VX001_DEATH); - events.ScheduleEvent(EVENT_AERIAL_ACTIVATION_3, 5000); - break; - case EVENT_AERIAL_ACTIVATION_3: - me->SummonCreature(NPC_AERIAL_COMMAND_UNIT, 2744.65f, 2569.46f, 380, 0, TEMPSUMMON_MANUAL_DESPAWN); - events.ScheduleEvent(EVENT_AERIAL_ACTIVATION_4, 5000); - break; - case EVENT_AERIAL_ACTIVATION_4: - if (Unit* aerial = ObjectAccessor::GetUnit(*me, instance->GetData64(DATA_AERIAL_COMMAND_UNIT))) - me->CastSpell(aerial, SPELL_SEAT_1); - events.ScheduleEvent(EVENT_AERIAL_ACTIVATION_5, 2000); - break; - case EVENT_AERIAL_ACTIVATION_5: - Talk(SAY_AERIAL_ACTIVATE); - events.ScheduleEvent(EVENT_AERIAL_ACTIVATION_6, 8000); - break; - case EVENT_AERIAL_ACTIVATION_6: - me->GetVehicleBase()->GetAI()->DoAction(_fireFighter? DO_HARDMODE_AERIAL : DO_START_AERIAL); - break; - case EVENT_VOL7RON_ACTIVATION_1: - if (Creature* mkii = ObjectAccessor::GetCreature(*me, instance->GetData64(DATA_LEVIATHAN_MK_II))) - mkii->SetFacingTo(M_PI); - events.ScheduleEvent(EVENT_VOL7RON_ACTIVATION_2, 1000); - break; - case EVENT_VOL7RON_ACTIVATION_2: - if (Creature* mkii = ObjectAccessor::GetCreature(*me, instance->GetData64(DATA_LEVIATHAN_MK_II))) - if (Creature* vx001 = ObjectAccessor::GetCreature(*me, instance->GetData64(DATA_VX_001))) - { - vx001->RemoveAurasDueToSpell(SPELL_TORSO_DISABLED); - vx001->CastSpell(mkii, SPELL_MOUNT_MKII); - } - events.ScheduleEvent(EVENT_VOL7RON_ACTIVATION_3, 4500); - break; - case EVENT_VOL7RON_ACTIVATION_3: - if (Creature* mkii = ObjectAccessor::GetCreature(*me, instance->GetData64(DATA_LEVIATHAN_MK_II))) - mkii->GetMotionMaster()->MovePoint(WP_MKII_P4_POS_5, pos[WP_MKII_P4_POS_5]); // MovePoint(WP_MKII_P4_POS_4, pos[WP_MKII_P4_POS_4]); - events.ScheduleEvent(EVENT_VOL7RON_ACTIVATION_4, 5000); - break; - case EVENT_VOL7RON_ACTIVATION_4: - if (Creature* vx001 = ObjectAccessor::GetCreature(*me, instance->GetData64(DATA_VX_001))) - if (Creature* aerial = ObjectAccessor::GetCreature(*me, instance->GetData64(DATA_AERIAL_COMMAND_UNIT))) - { - aerial->GetMotionMaster()->MoveLand(0, (aerial->GetPositionX(), aerial->GetPositionY(), aerial->GetPositionZMinusOffset())); - //aerial->SetDisableGravity(false); - //aerial->SetHover(false); - aerial->SetUInt32Value(UNIT_FIELD_BYTES_1, 0); - aerial->CastSpell(vx001, SPELL_MOUNT_VX_001); - aerial->CastSpell(aerial, SPELL_HALF_HEAL); - } - events.ScheduleEvent(EVENT_VOL7RON_ACTIVATION_5, 4000); - break; - case EVENT_VOL7RON_ACTIVATION_5: - Talk(SAY_V07TRON_ACTIVATE); - events.ScheduleEvent(EVENT_VOL7RON_ACTIVATION_6, 3000); - break; - case EVENT_VOL7RON_ACTIVATION_6: - if (Creature* vx001 = ObjectAccessor::GetCreature(*me, instance->GetData64(DATA_VX_001))) - DoCast(vx001, SPELL_SEAT_2); - events.ScheduleEvent(EVENT_VOL7RON_ACTIVATION_7, 5000); - break; - case EVENT_VOL7RON_ACTIVATION_7: - for (uint8 data = DATA_LEVIATHAN_MK_II; data <= DATA_AERIAL_COMMAND_UNIT; data++) - if (Creature* mimironVehicle = ObjectAccessor::GetCreature(*me, instance->GetData64(data))) - mimironVehicle->AI()->DoAction(DO_ASSEMBLED_COMBAT); - break; - case EVENT_OUTTRO_1: - me->RemoveAurasDueToSpell(SPELL_SLEEP_VISUAL_1); - DoCast(me, SPELL_SLEEP_VISUAL_2); - me->setFaction(35); - events.ScheduleEvent(EVENT_OUTTRO_2, 3000); - break; - case EVENT_OUTTRO_2: - Talk(SAY_V07TRON_DEATH); - if (_fireFighter) - { - if (Creature* computer = ObjectAccessor::GetCreature(*me, instance->GetData64(DATA_COMPUTER))) - computer->AI()->DoAction(DO_DEACTIVATE_COMPUTER); - me->SummonGameObject(RAID_MODE(GO_CACHE_OF_INNOVATION_FIREFIGHTER, GO_CACHE_OF_INNOVATION_FIREFIGHTER_HERO), 2744.65f, 2569.46f, 364.314f, 3.14159f, 0, 0, 0.7f, 0.7f, 604800); - } - else - me->SummonGameObject(RAID_MODE(GO_CACHE_OF_INNOVATION, GO_CACHE_OF_INNOVATION_HERO), 2744.65f, 2569.46f, 364.314f, 3.14159f, 0, 0, 0.7f, 0.7f, 604800); - events.ScheduleEvent(EVENT_OUTTRO_3, 11000); - break; - case EVENT_OUTTRO_3: - DoCast(me, SPELL_TELEPORT_VISUAL); - me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); - me->DespawnOrUnsummon(1000); // sniffs say 6 sec after, but it doesnt matter. - break; - default: - break; - } - } - } - - private: - bool _fireFighter; - }; - - CreatureAI* GetAI(Creature* creature) const override - { - return GetUlduarAI(creature); - } -}; - -class boss_leviathan_mk_ii : public CreatureScript -{ - public: - boss_leviathan_mk_ii() : CreatureScript("boss_leviathan_mk_ii") { } - - struct boss_leviathan_mk_iiAI : public BossAI - { - boss_leviathan_mk_iiAI(Creature* creature) : BossAI(creature, BOSS_MIMIRON) - { - } - - void DamageTaken(Unit* who, uint32 &damage) override - { - if (damage >= me->GetHealth()) - { - damage = me->GetHealth() - 1; // Let creature fall to 1 hp, but do not let it die or damage itself with SetHealth(). - me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); - DoCast(me, SPELL_VEHICLE_DAMAGED, true); - me->AttackStop(); - me->SetReactState(REACT_PASSIVE); - me->RemoveAllAurasExceptType(SPELL_AURA_CONTROL_VEHICLE, SPELL_AURA_MOD_INCREASE_HEALTH_PERCENT); - - if (events.IsInPhase(PHASE_LEVIATHAN_SOLO)) - { - me->CastStop(); - if (Unit* turret = me->GetVehicleKit()->GetPassenger(3)) - turret->Kill(turret); - - me->SetSpeed(MOVE_RUN, 1.5f, true); - me->GetMotionMaster()->MovePoint(WP_MKII_P1_IDLE, pos[WP_MKII_P1_IDLE]); - } - else if (events.IsInPhase(PHASE_LEVIATHAN_ASSEMBLED)) - { - me->SetStandState(UNIT_STAND_STATE_DEAD); - - if (EncounterDone(who, me).IsEncounterFinished()) - return; - - me->CastStop(); - DoCast(me, SPELL_SELF_REPAIR); - } - events.Reset(); - } - } - - void DoAction(int32 action) override - { - switch (action) - { - case DO_HARDMODE_MKII: - _fireFighter = true; - DoCast(me, SPELL_EMERGENCY_MODE); - DoCastAOE(SPELL_EMERGENCY_MODE_TURRET); - events.ScheduleEvent(EVENT_FLAME_SUPPRESSANT_MK, 60000, 0, PHASE_LEVIATHAN_SOLO); - // Missing break intended. - case DO_START_MKII: - _setupMine = true; - _setupBomb = true; - _setupRocket = true; - - me->SetReactState(REACT_AGGRESSIVE); - events.SetPhase(PHASE_LEVIATHAN_SOLO); - - events.ScheduleEvent(EVENT_NAPALM_SHELL, 3000, 0, PHASE_LEVIATHAN_SOLO); - events.ScheduleEvent(EVENT_PLASMA_BLAST, 15000, 0, PHASE_LEVIATHAN_SOLO); - events.ScheduleEvent(EVENT_PROXIMITY_MINE, 5000); - events.ScheduleEvent(EVENT_SHOCK_BLAST, 18000); - break; - case DO_ASSEMBLED_COMBAT: - me->SetStandState(UNIT_STAND_STATE_STAND); - me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE); - me->SetReactState(REACT_AGGRESSIVE); - - events.SetPhase(PHASE_LEVIATHAN_ASSEMBLED); - events.ScheduleEvent(EVENT_PROXIMITY_MINE, 15000); - events.ScheduleEvent(EVENT_SHOCK_BLAST, 45000); - break; - default: - break; - } - } - - uint32 GetData(uint32 type) const override - { - switch (type) - { - case DATA_SETUP_MINE: - return _setupMine; - case DATA_SETUP_BOMB: - return _setupBomb; - case DATA_SETUP_ROCKET: - return _setupRocket; - case DATA_FIREFIGHTER: - return _fireFighter; - default: - return 0; - } - } - - void JustDied(Unit* /*who*/) override - { - _JustDied(); - me->SetStandState(UNIT_STAND_STATE_DEAD); - } - - void JustSummoned(Creature* summon) override - { - summons.Summon(summon); - } - - void KilledUnit(Unit* victim) override - { - if (victim->GetTypeId() == TYPEID_PLAYER) - if (Creature* mimiron = ObjectAccessor::GetCreature(*me, instance->GetData64(BOSS_MIMIRON))) - if (mimiron->AI()) - mimiron->AI()->Talk(events.IsInPhase(PHASE_LEVIATHAN_SOLO) ? SAY_MKII_SLAY : SAY_V07TRON_SLAY); - } - - void MovementInform(uint32 type, uint32 point) override - { - if (type != POINT_MOTION_TYPE) - return; - - switch (point) - { - case WP_MKII_P1_IDLE: - me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); - DoCast(me, SPELL_HALF_HEAL); - - if (Creature* mimiron = ObjectAccessor::GetCreature(*me, instance->GetData64(BOSS_MIMIRON))) - mimiron->AI()->DoAction(DO_ACTIVATE_VX001); - break; - case WP_MKII_P4_POS_1: - me->GetMotionMaster()->MovePoint(WP_MKII_P4_POS_2, pos[WP_MKII_P4_POS_2]); - break; - case WP_MKII_P4_POS_2: - me->GetMotionMaster()->MovePoint(WP_MKII_P4_POS_3, pos[WP_MKII_P4_POS_3]); - break; - case WP_MKII_P4_POS_3: - if (Creature* mimiron = ObjectAccessor::GetCreature(*me, instance->GetData64(BOSS_MIMIRON))) - mimiron->AI()->DoAction(DO_ACTIVATE_V0L7R0N_2); - break; - case WP_MKII_P4_POS_4: - me->GetMotionMaster()->Clear(); - me->GetMotionMaster()->MovePoint(WP_MKII_P4_POS_5, pos[WP_MKII_P4_POS_5]); - break; - default: - break; - } - } - - void Reset() override - { - _Reset(); - me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE); - me->SetReactState(REACT_PASSIVE); - _fireFighter = false; - _setupMine = false; - _setupBomb = false; - _setupRocket = false; - DoCast(me, SPELL_FREEZE_ANIM); - } - - void SetData(uint32 id, uint32 data) override - { - switch (id) - { - case DATA_SETUP_MINE: - _setupMine = data; - break; - case DATA_SETUP_BOMB: - _setupBomb = data; - break; - case DATA_SETUP_ROCKET: - _setupRocket = data; - break; - case DATA_FIREFIGHTER: - _fireFighter = data; - break; - default: - break; - } - } - - void UpdateAI(uint32 diff) override - { - if (!UpdateVictim() || !CheckInRoom()) - return; - - events.Update(diff); - - if (me->HasUnitState(UNIT_STATE_CASTING)) - return; - - while (uint32 eventId = events.ExecuteEvent()) - { - switch (eventId) - { - case EVENT_PROXIMITY_MINE: - DoCastAOE(SPELL_PROXIMITY_MINES); - events.RescheduleEvent(EVENT_PROXIMITY_MINE, 35000); - break; - case EVENT_PLASMA_BLAST: - DoCastVictim(SPELL_SCRIPT_EFFECT_PLASMA_BLAST); - events.RescheduleEvent(EVENT_PLASMA_BLAST, urand(30000, 45000), 0, PHASE_LEVIATHAN_SOLO); - - if (events.GetTimeUntilEvent(EVENT_NAPALM_SHELL) < 9000) - events.RescheduleEvent(EVENT_NAPALM_SHELL, 9000, 0, PHASE_LEVIATHAN_SOLO); // The actual spell is cast by the turret, we should not let it interrupt itself. - break; - case EVENT_SHOCK_BLAST: - DoCastAOE(SPELL_SHOCK_BLAST); - events.RescheduleEvent(EVENT_SHOCK_BLAST, urand(34000, 36000)); - break; - case EVENT_FLAME_SUPPRESSANT_MK: - DoCastAOE(SPELL_FLAME_SUPPRESSANT_MK); - events.RescheduleEvent(EVENT_FLAME_SUPPRESSANT_MK, 60000, 0, PHASE_LEVIATHAN_SOLO); - break; - case EVENT_NAPALM_SHELL: - DoCastAOE(SPELL_FORCE_CAST_NAPALM_SHELL); - events.RescheduleEvent(EVENT_NAPALM_SHELL, urand(6000, 15000), 0, PHASE_LEVIATHAN_SOLO); - - if (events.GetTimeUntilEvent(EVENT_PLASMA_BLAST) < 2000) - events.RescheduleEvent(EVENT_PLASMA_BLAST, 2000, 0, PHASE_LEVIATHAN_SOLO); // The actual spell is cast by the turret, we should not let it interrupt itself. - break; - default: - break; - } - } - DoMeleeAttackIfReady(); - } - - private: - bool _fireFighter; - bool _setupMine; - bool _setupBomb; - bool _setupRocket; - }; - - CreatureAI* GetAI(Creature* creature) const override - { - return GetUlduarAI(creature); - } -}; - -class boss_vx_001 : public CreatureScript -{ - public: - boss_vx_001() : CreatureScript("boss_vx_001") { } - - struct boss_vx_001AI : public BossAI - { - boss_vx_001AI(Creature* creature) : BossAI(creature, BOSS_MIMIRON) - { - me->SetDisableGravity(true); // This is the unfold visual state of VX-001, it has to be set on create as it requires an objectupdate if set later. - me->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_STATE_SPECIAL_UNARMED); // This is a hack to force the yet to be unfolded visual state. - me->SetReactState(REACT_PASSIVE); - DoCast(me, SPELL_FREEZE_ANIM); - _fireFighter = false; - } - - void DamageTaken(Unit* who, uint32 &damage) override - { - if (damage >= me->GetHealth()) - { - damage = me->GetHealth() - 1; // Let creature fall to 1 hp, but do not let it die or damage itself with SetHealth(). - me->AttackStop(); - me->SetUInt64Value(UNIT_FIELD_TARGET, NULL); // This should be removed when channel track target implementation is properly functioning. - DoCast(me, SPELL_VEHICLE_DAMAGED, true); - me->RemoveAllAurasExceptType(SPELL_AURA_CONTROL_VEHICLE, SPELL_AURA_MOD_INCREASE_HEALTH_PERCENT); - - if (events.IsInPhase(PHASE_VX001_SOLO)) - { - me->CastStop(); - me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE); - DoCast(me, SPELL_HALF_HEAL); - DoCast(me, SPELL_TORSO_DISABLED); - if (Creature* mimiron = ObjectAccessor::GetCreature(*me, instance->GetData64(BOSS_MIMIRON))) - mimiron->AI()->DoAction(DO_ACTIVATE_AERIAL); - } - else if (events.IsInPhase(PHASE_VX001_ASSEMBLED)) - { - me->SetStandState(UNIT_STAND_STATE_DEAD); - me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); - - if (EncounterDone(who, me).IsEncounterFinished()) - return; - - me->CastStop(); - DoCast(me, SPELL_SELF_REPAIR); - } - events.Reset(); - } - } - - void DoAction(int32 action) override - { - switch (action) - { - case DO_HARDMODE_VX001: - _fireFighter = true; - DoCast(me, SPELL_EMERGENCY_MODE); - events.ScheduleEvent(EVENT_FROST_BOMB, 1000); - events.ScheduleEvent(EVENT_FLAME_SUPPRESSANT_VX, 6000); - // Missing break intended. - case DO_START_VX001: - me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE); - me->RemoveAurasDueToSpell(SPELL_FREEZE_ANIM); - me->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_ONESHOT_NONE); // Remove emotestate. - //me->SetUInt32Value(UNIT_FIELD_BYTES_1, 33554432); Blizzard handles hover animation like this it seems. - DoCast(me, SPELL_HEAT_WAVE_AURA); - - events.SetPhase(PHASE_VX001_SOLO); - events.ScheduleEvent(EVENT_ROCKET_STRIKE, 20000); - events.ScheduleEvent(EVENT_SPINNING_UP, urand(30000, 35000)); - events.ScheduleEvent(EVENT_RAPID_BURST, 500, 0, PHASE_VX001_SOLO); - break; - case DO_ASSEMBLED_COMBAT: - me->SetStandState(UNIT_STAND_STATE_STAND); - me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE); - - events.SetPhase(PHASE_VX001_ASSEMBLED); - events.ScheduleEvent(EVENT_ROCKET_STRIKE, 20000); - events.ScheduleEvent(EVENT_SPINNING_UP, urand(30000, 35000)); - events.ScheduleEvent(EVENT_HAND_PULSE, 500, 0, PHASE_VX001_ASSEMBLED); - if (_fireFighter) - events.ScheduleEvent(EVENT_FROST_BOMB, 1000); - break; - default: - break; - } - } - - void EnterEvadeMode() override - { - summons.DespawnAll(); - } - - void JustSummoned(Creature* summon) override - { - summons.Summon(summon); - if (summon->GetEntry() == NPC_BURST_TARGET) - summon->CastSpell(me, SPELL_RAPID_BURST_TARGET_ME); - } - - void KilledUnit(Unit* victim) override - { - if (victim->GetTypeId() == TYPEID_PLAYER) - if (Creature* mimiron = ObjectAccessor::GetCreature(*me, instance->GetData64(BOSS_MIMIRON))) - if (mimiron->AI()) - mimiron->AI()->Talk(events.IsInPhase(PHASE_VX001_SOLO) ? SAY_VX001_SLAY : SAY_V07TRON_SLAY); - } - - void SpellHit(Unit* caster, SpellInfo const* /*spellProto*/) override - { - if (caster->GetEntry() == NPC_BURST_TARGET && !me->HasUnitState(UNIT_STATE_CASTING)) - DoCast(caster, SPELL_RAPID_BURST); - } - - void UpdateAI(uint32 diff) override - { - if (!UpdateVictim()) - return; - - events.Update(diff); - - // Handle rotation during SPELL_SPINNING_UP, SPELL_P3WX2_LASER_BARRAGE, SPELL_RAPID_BURST, and SPELL_HAND_PULSE_LEFT/RIGHT - if (me->HasUnitState(UNIT_STATE_CASTING)) - { - if (Creature* channelTarget = ObjectAccessor::GetCreature(*me, me->GetUInt64Value(UNIT_FIELD_CHANNEL_OBJECT))) - me->SetFacingToObject(channelTarget); - return; - } - - while (uint32 eventId = events.ExecuteEvent()) - { - switch (eventId) - { - case EVENT_RAPID_BURST: - if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 120, true)) - DoCast(target, SPELL_SUMMON_BURST_TARGET); - events.RescheduleEvent(EVENT_RAPID_BURST, 3000, 0, PHASE_VX001_SOLO); - break; - case EVENT_ROCKET_STRIKE: - DoCastAOE(events.IsInPhase(PHASE_VX001_SOLO) ? SPELL_ROCKET_STRIKE_LEFT : SPELL_ROCKET_STRIKE_BOTH); - events.ScheduleEvent(EVENT_RELOAD, 10000); - events.RescheduleEvent(EVENT_ROCKET_STRIKE, urand(20000, 25000)); - break; - case EVENT_RELOAD: - for (uint8 seat = 6; seat <= 7; seat++) - if (Unit* rocket = me->GetVehicleKit()->GetPassenger(seat)) - rocket->SetDisplayId(rocket->GetNativeDisplayId()); - break; - case EVENT_HAND_PULSE: - if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 120, true)) - DoCast(target, urand(0,1) == 0 ? SPELL_HAND_PULSE_LEFT : SPELL_HAND_PULSE_RIGHT); - events.RescheduleEvent(EVENT_HAND_PULSE, urand(1500, 3000), 0, PHASE_VX001_ASSEMBLED); - break; - case EVENT_FROST_BOMB: - DoCastAOE(SPELL_SCRIPT_EFFECT_FROST_BOMB); - events.RescheduleEvent(EVENT_FROST_BOMB, 45000); - break; - case EVENT_SPINNING_UP: - DoCastAOE(SPELL_SPINNING_UP); - events.DelayEvents(14000); - events.RescheduleEvent(EVENT_SPINNING_UP, urand(55000, 65000)); - break; - case EVENT_FLAME_SUPPRESSANT_VX: - DoCastAOE(SPELL_FLAME_SUPPRESSANT_VX); - events.RescheduleEvent(EVENT_FLAME_SUPPRESSANT_VX, urand(10000, 12000), 0, PHASE_VX001_SOLO); - break; - default: - break; - } - } - } - - private: - bool _fireFighter; - }; - - CreatureAI* GetAI(Creature* creature) const override - { - return GetUlduarAI(creature); - } -}; - -class boss_aerial_command_unit : public CreatureScript -{ - public: - boss_aerial_command_unit() : CreatureScript("boss_aerial_command_unit") { } - - struct boss_aerial_command_unitAI : public BossAI - { - boss_aerial_command_unitAI(Creature* creature) : BossAI(creature, BOSS_MIMIRON) - { - //me->SetDisableGravity(true); - me->SetReactState(REACT_PASSIVE); - fireFigther = false; - } - - void DamageTaken(Unit* who, uint32 &damage) override - { - if (damage >= me->GetHealth()) - { - damage = me->GetHealth() - 1; // Let creature fall to 1 hp, but do not let it die or damage itself with SetHealth(). - me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); - me->AttackStop(); - me->SetReactState(REACT_PASSIVE); - DoCast(me, SPELL_VEHICLE_DAMAGED, true); - me->RemoveAllAurasExceptType(SPELL_AURA_CONTROL_VEHICLE, SPELL_AURA_MOD_INCREASE_HEALTH_PERCENT); - - if (events.IsInPhase(PHASE_AERIAL_SOLO)) - { - me->GetMotionMaster()->Clear(true); - me->GetMotionMaster()->MovePoint(WP_AERIAL_P4_POS, pos[WP_AERIAL_P4_POS]); - } - else if (events.IsInPhase(PHASE_AERIAL_ASSEMBLED)) - { - me->SetStandState(UNIT_STAND_STATE_DEAD); - - if (EncounterDone(who, me).IsEncounterFinished()) - return; - - me->CastStop(); - DoCast(me, SPELL_SELF_REPAIR); - } - events.Reset(); - } - } - - void DoAction(int32 action) override - { - switch (action) - { - case DO_HARDMODE_AERIAL: - fireFigther = true; - DoCast(me, SPELL_EMERGENCY_MODE); - events.ScheduleEvent(EVENT_SUMMON_FIRE_BOTS, 1000, 0, PHASE_AERIAL_SOLO); - // Missing break intended. - case DO_START_AERIAL: - //me->SetDisableGravity(false); - //me->SetHover(true); - me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE); - me->SetReactState(REACT_AGGRESSIVE); - - events.SetPhase(PHASE_AERIAL_SOLO); - events.ScheduleEvent(EVENT_SUMMON_JUNK_BOT, 5000, 0, PHASE_AERIAL_SOLO); - events.ScheduleEvent(EVENT_SUMMON_ASSAULT_BOT, 9000, 0, PHASE_AERIAL_SOLO); - events.ScheduleEvent(EVENT_SUMMON_BOMB_BOT, 9000, 0, PHASE_AERIAL_SOLO); - break; - case DO_DISABLE_AERIAL: - //me->SetHover(false); - me->CastStop(); - me->AttackStop(); - me->SetReactState(REACT_PASSIVE); - me->GetMotionMaster()->MoveFall(); - events.DelayEvents(23000); - break; - case DO_ENABLE_AERIAL: - //me->SetHover(true); - me->SetReactState(REACT_AGGRESSIVE); - break; - case DO_ASSEMBLED_COMBAT: - me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE); - me->SetReactState(REACT_AGGRESSIVE); - me->SetStandState(UNIT_STAND_STATE_STAND); - events.SetPhase(PHASE_AERIAL_ASSEMBLED); - break; - default: - break; - } - } - - void EnterEvadeMode() override - { - summons.DespawnAll(); - } - - void JustSummoned(Creature* summon) override - { - if (fireFigther && (summon->GetEntry() == NPC_ASSAULT_BOT || summon->GetEntry() == NPC_JUNK_BOT)) - summon->AddAura(SPELL_EMERGENCY_MODE, summon); - BossAI::JustSummoned(summon); - } - - void KilledUnit(Unit* victim) override - { - if (victim->GetTypeId() == TYPEID_PLAYER) - if (Creature* mimiron = ObjectAccessor::GetCreature(*me, instance->GetData64(BOSS_MIMIRON))) - if (mimiron->AI()) - mimiron->AI()->Talk(events.IsInPhase(PHASE_AERIAL_SOLO) ? SAY_AERIAL_SLAY : SAY_V07TRON_SLAY); - } - - void MovementInform(uint32 type, uint32 point) override - { - if (type == POINT_MOTION_TYPE && point == WP_AERIAL_P4_POS) - { - me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); - - if (Creature* mimiron = ObjectAccessor::GetCreature(*me, instance->GetData64(BOSS_MIMIRON))) - mimiron->AI()->DoAction(DO_ACTIVATE_V0L7R0N_1); - } - } - - void UpdateAI(uint32 diff) override - { - if (!UpdateVictim()) - return; - - events.Update(diff); - - if (me->HasUnitState(UNIT_STATE_CASTING)) - return; - - while (uint32 eventId = events.ExecuteEvent()) - { - switch (eventId) - { - case EVENT_SUMMON_FIRE_BOTS: - DoCastAOE(SPELL_SUMMON_FIRE_BOT_TRIGGER); - events.RescheduleEvent(EVENT_SUMMON_FIRE_BOTS, 45000, 0, PHASE_AERIAL_SOLO); - break; - case EVENT_SUMMON_JUNK_BOT: - DoCastAOE(SPELL_SUMMON_JUNK_BOT_TRIGGER); - events.RescheduleEvent(EVENT_SUMMON_JUNK_BOT, urand(11000, 12000), 0, PHASE_AERIAL_SOLO); - break; - case EVENT_SUMMON_ASSAULT_BOT: - DoCastAOE(SPELL_SUMMON_ASSAULT_BOT_TRIGGER); - events.RescheduleEvent(EVENT_SUMMON_ASSAULT_BOT, 30000, 0, PHASE_AERIAL_SOLO); - break; - case EVENT_SUMMON_BOMB_BOT: - DoCast(me, SPELL_SUMMON_BOMB_BOT); - events.RescheduleEvent(EVENT_SUMMON_BOMB_BOT, urand(15000, 20000), 0, PHASE_AERIAL_SOLO); - break; - default: - break; - } - } - DoSpellAttackIfReady(events.IsInPhase(PHASE_AERIAL_SOLO) ? SPELL_PLASMA_BALL_P1 : SPELL_PLASMA_BALL_P2); - } - - private: - bool fireFigther; - }; - - CreatureAI* GetAI(Creature* creature) const override - { - return GetUlduarAI(creature); - } -}; - -class npc_magnetic_core : public CreatureScript -{ - public: - npc_magnetic_core() : CreatureScript("npc_magnetic_core") { } - - struct npc_magnetic_coreAI : public ScriptedAI - { - npc_magnetic_coreAI(Creature* creature) : ScriptedAI(creature) - { - DoCast(me, SPELL_MAGNETIC_CORE); - me->DespawnOrUnsummon(25000); // Just incase handling in aurascript fails to despawn. - } - }; - - CreatureAI* GetAI(Creature* creature) const override - { - return GetUlduarAI(creature); - } -}; - -class npc_mimiron_assault_bot : public CreatureScript -{ - public: - npc_mimiron_assault_bot() : CreatureScript("npc_mimiron_assault_bot") { } - - struct npc_mimiron_assault_botAI : public ScriptedAI - { - npc_mimiron_assault_botAI(Creature* creature) : ScriptedAI(creature) - { - magneticTimer = 14000; - } - - uint32 magneticTimer; - - void UpdateAI(uint32 diff) override - { - if (me->HasUnitState(UNIT_STATE_ROOT)) - if (Unit* newTarget = SelectTarget(SELECT_TARGET_NEAREST, 0, 30.0f, true)) - { - me->DeleteThreatList(); - AttackStart(newTarget); - } - - if (magneticTimer <= diff) - { - DoCastVictim(SPELL_MAGNETIC_FIELD); - me->ClearUnitState(UNIT_STATE_CASTING); - magneticTimer = 30000; - } - else magneticTimer -= diff; - - DoMeleeAttackIfReady(); - } - }; - - CreatureAI* GetAI(Creature* creature) const override - { - return GetUlduarAI(creature); - } -}; - -class npc_mimiron_emergency_fire_bot : public CreatureScript -{ - public: - npc_mimiron_emergency_fire_bot() : CreatureScript("npc_mimiron_emergency_fire_bot") { } - - struct npc_mimiron_emergency_fire_botAI : public ScriptedAI - { - npc_mimiron_emergency_fire_botAI(Creature* creature) : ScriptedAI(creature) - { - me->SetReactState(REACT_PASSIVE); - watersprayTimer = 7000; - isWaterSprayReady = true; - moveNew = true; - } - - uint32 watersprayTimer; - bool isWaterSprayReady; - bool moveNew; - - uint32 GetData(uint32 id) const override - { - if (id == DATA_WATERSPRAY) - return isWaterSprayReady; - if (id == DATA_MOVE_NEW) - return moveNew; - return 0; - } - - void SetData(uint32 id, uint32 data) override - { - if (id == DATA_WATERSPRAY) - isWaterSprayReady = false; - else if (id == DATA_MOVE_NEW) - moveNew = data; - } - - void UpdateAI(uint32 diff) override - { - if (!isWaterSprayReady) - { - if (watersprayTimer <= diff) - { - isWaterSprayReady = true; - watersprayTimer = urand(7000, 9000); - } - else watersprayTimer -= diff; - } - } - }; - - CreatureAI* GetAI(Creature* creature) const override - { - return GetUlduarAI(creature); - } -}; - -class npc_mimiron_computer : public CreatureScript -{ - public: - npc_mimiron_computer() : CreatureScript("npc_mimiron_computer") { } - - struct npc_mimiron_computerAI : public ScriptedAI - { - npc_mimiron_computerAI(Creature* creature) : ScriptedAI(creature) - { - instance = me->GetInstanceScript(); - } - - InstanceScript* instance; - EventMap events; - - void DoAction(int32 action) override - { - switch (action) - { - case DO_ACTIVATE_COMPUTER: - Talk(SAY_SELF_DESTRUCT_INITIATED); - events.ScheduleEvent(EVENT_SELF_DESTRUCT_10, 3000); - break; - case DO_DEACTIVATE_COMPUTER: - Talk(SAY_SELF_DESTRUCT_TERMINATED); - me->RemoveAurasDueToSpell(SPELL_SELF_DESTRUCTION_AURA); - me->RemoveAurasDueToSpell(SPELL_SELF_DESTRUCTION_VISUAL); - events.Reset(); - break; - default: - break; - } - } - - void UpdateAI(uint32 diff) override - { - events.Update(diff); - - while (uint32 eventId = events.ExecuteEvent()) - { - switch (eventId) - { - case EVENT_SELF_DESTRUCT_10: - Talk(SAY_SELF_DESTRUCT_10); - if (Creature* mimiron = ObjectAccessor::GetCreature(*me, instance->GetData64(BOSS_MIMIRON))) - mimiron->AI()->DoAction(DO_ACTIVATE_HARD_MODE); - events.ScheduleEvent(EVENT_SELF_DESTRUCT_9, 60000); - break; - case EVENT_SELF_DESTRUCT_9: - Talk(SAY_SELF_DESTRUCT_9); - events.ScheduleEvent(EVENT_SELF_DESTRUCT_8, 60000); - break; - case EVENT_SELF_DESTRUCT_8: - Talk(SAY_SELF_DESTRUCT_8); - events.ScheduleEvent(EVENT_SELF_DESTRUCT_7, 60000); - break; - case EVENT_SELF_DESTRUCT_7: - Talk(SAY_SELF_DESTRUCT_7); - events.ScheduleEvent(EVENT_SELF_DESTRUCT_6, 60000); - break; - case EVENT_SELF_DESTRUCT_6: - Talk(SAY_SELF_DESTRUCT_6); - events.ScheduleEvent(EVENT_SELF_DESTRUCT_5, 60000); - break; - case EVENT_SELF_DESTRUCT_5: - Talk(SAY_SELF_DESTRUCT_5); - events.ScheduleEvent(EVENT_SELF_DESTRUCT_4, 60000); - break; - case EVENT_SELF_DESTRUCT_4: - Talk(SAY_SELF_DESTRUCT_4); - events.ScheduleEvent(EVENT_SELF_DESTRUCT_3, 60000); - break; - case EVENT_SELF_DESTRUCT_3: - Talk(SAY_SELF_DESTRUCT_3); - events.ScheduleEvent(EVENT_SELF_DESTRUCT_2, 60000); - break; - case EVENT_SELF_DESTRUCT_2: - Talk(SAY_SELF_DESTRUCT_2); - events.ScheduleEvent(EVENT_SELF_DESTRUCT_1, 60000); - break; - case EVENT_SELF_DESTRUCT_1: - Talk(SAY_SELF_DESTRUCT_1); - events.ScheduleEvent(EVENT_SELF_DESTRUCT_FINALIZED, 60000); - break; - case EVENT_SELF_DESTRUCT_FINALIZED: - Talk(SAY_SELF_DESTRUCT_FINALIZED); - if (Creature* mimiron = ObjectAccessor::GetCreature(*me, instance->GetData64(BOSS_MIMIRON))) - mimiron->AI()->DoAction(DO_ACTIVATE_SELF_DESTRUCT); - DoCast(me, SPELL_SELF_DESTRUCTION_AURA); - DoCast(me, SPELL_SELF_DESTRUCTION_VISUAL); - break; - default: - break; - } - } - } - }; - - CreatureAI* GetAI(Creature* creature) const override - { - return GetUlduarAI(creature); - } -}; - -class npc_mimiron_flames : public CreatureScript -{ - public: - npc_mimiron_flames() : CreatureScript("npc_mimiron_flames") { } - - struct npc_mimiron_flamesAI : public ScriptedAI - { - npc_mimiron_flamesAI(Creature* creature) : ScriptedAI(creature) - { - instance = me->GetInstanceScript(); - timer = 4000; - spawnedFlames = false; - } - - uint32 timer; - bool spawnedFlames; - InstanceScript* instance; - - void UpdateAI(uint32 diff) override - { - if (instance->GetBossState(BOSS_MIMIRON) != IN_PROGRESS) - me->DespawnOrUnsummon(); - if (!spawnedFlames) - { - if (timer <= diff) - { - DoCastAOE(SPELL_SUMMON_FLAMES_SPREAD_TRIGGER); - spawnedFlames = true; - } - else timer -= diff; - } - } - }; - - CreatureAI* GetAI(Creature* creature) const override - { - return GetUlduarAI(creature); - } -}; - -class npc_mimiron_frost_bomb : public CreatureScript -{ - public: - npc_mimiron_frost_bomb() : CreatureScript("npc_mimiron_frost_bomb") { } - - struct npc_mimiron_frost_bombAI : public ScriptedAI - { - npc_mimiron_frost_bombAI(Creature* creature) : ScriptedAI(creature) - { - timer = 10000; - exploded = false; - } - - uint32 timer; - bool exploded; - - void UpdateAI(uint32 diff) override - { - if (timer <= diff) - { - if (!exploded) - { - DoCastAOE(SPELL_FROST_BOMB_EXPLOSION); - timer = 3000; - exploded = true; - } - else - { - DoCastAOE(SPELL_CLEAR_FIRES); - me->DespawnOrUnsummon(3000); - timer = 10000; - } - } - else timer -= diff; - } - }; - - CreatureAI* GetAI(Creature* creature) const override - { - return GetUlduarAI(creature); - } -}; - -class npc_mimiron_proximity_mine : public CreatureScript -{ - public: - npc_mimiron_proximity_mine() : CreatureScript("npc_mimiron_proximity_mine") { } - - struct npc_mimiron_proximity_mineAI : public ScriptedAI - { - npc_mimiron_proximity_mineAI(Creature* creature) : ScriptedAI(creature) - { - explosionTimer = 35000; - boom = false; - armed = false; - } - - uint32 explosionTimer; - bool boom; - bool armed; - - void UpdateAI(uint32 diff) override - { - if (!boom) - { - if (explosionTimer <= 33500 && !armed) - { - DoCast(me, SPELL_PROXIMITY_MINE_PERIODIC_TRIGGER); - armed = true; - } - if (explosionTimer <= diff) - { - if (me->HasAura(SPELL_PROXIMITY_MINE_PERIODIC_TRIGGER)) - DoCastAOE(SPELL_PROXIMITY_MINE_EXPLOSION); - boom = true; - me->DespawnOrUnsummon(1000); - } - else explosionTimer -= diff; - } - } - }; - - CreatureAI* GetAI(Creature* creature) const override - { - return GetUlduarAI(creature); - } -}; - -class go_mimiron_hardmode_button : public GameObjectScript -{ -public: - go_mimiron_hardmode_button() : GameObjectScript("go_mimiron_hardmode_button") { } - - bool OnGossipHello(Player* /*player*/, GameObject* go) - { - InstanceScript* instance = go->GetInstanceScript(); - - if (!instance) - return false; - - if (Creature* computer = ObjectAccessor::GetCreature(*go, instance->GetData64(DATA_COMPUTER))) - computer->AI()->DoAction(DO_ACTIVATE_COMPUTER); - go->SetGoState(GO_STATE_ACTIVE); - go->SetFlag(GAMEOBJECT_FLAGS,GO_FLAG_NOT_SELECTABLE); - return true; - } -}; - -// 63801 Bomb Bot -class spell_mimiron_bomb_bot : public SpellScriptLoader -{ - public: - spell_mimiron_bomb_bot() : SpellScriptLoader("spell_mimiron_bomb_bot") { } - - class spell_mimiron_bomb_bot_SpellScript : public SpellScript - { - PrepareSpellScript(spell_mimiron_bomb_bot_SpellScript); - - void HandleScript(SpellEffIndex /*effIndex*/) - { - if (GetHitPlayer()) - if (InstanceScript* instance = GetCaster()->GetInstanceScript()) - if (Creature* mkii = ObjectAccessor::GetCreature(*GetCaster(), instance->GetData64(DATA_LEVIATHAN_MK_II))) - mkii->AI()->SetData(DATA_SETUP_BOMB, 0); - } - - void HandleDespawn(SpellEffIndex /*effIndex*/) - { - if (GetHitUnit()->IsSummon()) - { - GetHitUnit()->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_PACIFIED); - GetHitUnit()->ToTempSummon()->DespawnOrUnsummon(1000); - } - } - - void Register() override - { - OnEffectHitTarget += SpellEffectFn(spell_mimiron_bomb_bot_SpellScript::HandleScript, EFFECT_0, SPELL_EFFECT_SCHOOL_DAMAGE); - OnEffectHitTarget += SpellEffectFn(spell_mimiron_bomb_bot_SpellScript::HandleDespawn, EFFECT_1, SPELL_EFFECT_APPLY_AURA); - } - }; - - SpellScript* GetSpellScript() const override - { - return new spell_mimiron_bomb_bot_SpellScript(); - } -}; - -// 65192 - Flame Suppressant, 65224 - Clear Fires, 65354 - Clear Fires, 64619 - Water Spray -class spell_mimiron_clear_fires : public SpellScriptLoader -{ - public: - spell_mimiron_clear_fires() : SpellScriptLoader("spell_mimiron_clear_fires") { } - - class spell_mimiron_clear_fires_SpellScript : public SpellScript - { - PrepareSpellScript(spell_mimiron_clear_fires_SpellScript); - - void HandleDummy(SpellEffIndex /*effIndex*/) - { - if (GetHitUnit()->IsSummon()) - GetHitUnit()->ToTempSummon()->DespawnOrUnsummon(); - } - - void Register() override - { - OnEffectHitTarget += SpellEffectFn(spell_mimiron_clear_fires_SpellScript::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY); - } - }; - - SpellScript* GetSpellScript() const override - { - return new spell_mimiron_clear_fires_SpellScript(); - } -}; - -// 64463 - Despawn Assault Bots -class spell_mimiron_despawn_assault_bots : public SpellScriptLoader -{ - public: - spell_mimiron_despawn_assault_bots() : SpellScriptLoader("spell_mimiron_despawn_assault_bots") { } - - class spell_mimiron_despawn_assault_bots_SpellScript : public SpellScript - { - PrepareSpellScript(spell_mimiron_despawn_assault_bots_SpellScript); - - void HandleScript(SpellEffIndex /*effIndex*/) - { - if (GetHitUnit()->IsSummon()) - GetHitUnit()->ToTempSummon()->DespawnOrUnsummon(); - } - - void Register() override - { - OnEffectHitTarget += SpellEffectFn(spell_mimiron_despawn_assault_bots_SpellScript::HandleScript, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT); - } - }; - - SpellScript* GetSpellScript() const override - { - return new spell_mimiron_despawn_assault_bots_SpellScript(); - } -}; - -// 64626 - Explosion -class spell_mimiron_frost_bomb_explosion : public SpellScriptLoader -{ - public: - spell_mimiron_frost_bomb_explosion() : SpellScriptLoader("spell_mimiron_frost_bomb_explosion") { } - - class spell_mimiron_frost_bomb_explosion_SpellScript : public SpellScript - { - PrepareSpellScript(spell_mimiron_frost_bomb_explosion_SpellScript); - - void HandleDummy(SpellEffIndex /*effIndex*/) - { - GetHitUnit()->RemoveAurasDueToSpell(SPELL_FLAMES_PERIODIC_TRIGGER); - } - - void HandleUnaura(SpellEffIndex /*effIndex*/) - { - GetCaster()->RemoveAurasDueToSpell(SPELL_FROST_BOMB_LINKED); - } - - void Register() override - { - OnEffectHit += SpellEffectFn(spell_mimiron_frost_bomb_explosion_SpellScript::HandleUnaura, EFFECT_2, SPELL_EFFECT_DUMMY); - OnEffectHitTarget += SpellEffectFn(spell_mimiron_frost_bomb_explosion_SpellScript::HandleDummy, EFFECT_2, SPELL_EFFECT_DUMMY); - } - }; - - SpellScript* GetSpellScript() const override - { - return new spell_mimiron_frost_bomb_explosion_SpellScript(); - } -}; - -// 64618 - Fire Search -class spell_mimiron_fire_search : public SpellScriptLoader -{ - public: - spell_mimiron_fire_search() : SpellScriptLoader("spell_mimiron_fire_search") { } - - class spell_mimiron_fire_search_SpellScript : public SpellScript - { - PrepareSpellScript(spell_mimiron_fire_search_SpellScript); - - bool Validate(SpellInfo const* /*spell*/) override - { - if (!sSpellMgr->GetSpellInfo(SPELL_WATER_SPRAY)) - return false; - return true; - } - - void FilterTargets(std::list& targets) - { - if (targets.empty()) - { - GetCaster()->GetMotionMaster()->MoveRandom(15.0f); - return; - } - - WorldObject* target = Trinity::Containers::SelectRandomContainerElement(targets); - - targets.clear(); - targets.push_back(target); - } - - void HandleScript(SpellEffIndex /*effIndex*/) - { - Unit* caster = GetCaster(); - - if (caster->GetAI()) - { - if (caster->GetDistance2d(GetHitUnit()) <= 15.0f && caster->GetAI()->GetData(DATA_WATERSPRAY)) - { - caster->CastSpell(GetHitUnit(), SPELL_WATER_SPRAY, true); - caster->GetAI()->SetData(DATA_WATERSPRAY, 0); - caster->GetAI()->SetData(DATA_MOVE_NEW, 1); - } - else if (caster->GetAI()->GetData(DATA_MOVE_NEW)) - { - caster->GetMotionMaster()->MoveChase(GetHitUnit()); - caster->GetAI()->SetData(DATA_MOVE_NEW, 0); - } - } - } - - void Register() override - { - OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_mimiron_fire_search_SpellScript::FilterTargets, EFFECT_0, TARGET_UNIT_SRC_AREA_ENTRY); - OnEffectHitTarget += SpellEffectFn(spell_mimiron_fire_search_SpellScript::HandleScript, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT); - } - }; - - SpellScript* GetSpellScript() const override - { - return new spell_mimiron_fire_search_SpellScript(); - } -}; - -// 64570 - Flame Suppressant -class spell_mimiron_flame_suppressant : public SpellScriptLoader -{ - public: - spell_mimiron_flame_suppressant() : SpellScriptLoader("spell_mimiron_flame_suppressant") { } - - class spell_mimiron_flame_suppressant_SpellScript : public SpellScript - { - PrepareSpellScript(spell_mimiron_flame_suppressant_SpellScript); - - void HandleDummy(SpellEffIndex /*effIndex*/) - { - GetHitUnit()->RemoveAurasDueToSpell(SPELL_FLAMES_PERIODIC_TRIGGER); - } - - void Register() override - { - OnEffectHitTarget += SpellEffectFn(spell_mimiron_flame_suppressant_SpellScript::HandleDummy, EFFECT_1, SPELL_EFFECT_DUMMY); - } - }; - - SpellScript* GetSpellScript() const override - { - return new spell_mimiron_flame_suppressant_SpellScript(); - } -}; - -// 64436 - Magnetic Core -class spell_mimiron_magnetic_core : public SpellScriptLoader -{ - public: - spell_mimiron_magnetic_core() : SpellScriptLoader("spell_mimiron_magnetic_core") { } - - class spell_mimiron_magnetic_core_SpellScript : public SpellScript - { - PrepareSpellScript(spell_mimiron_magnetic_core_SpellScript); - - void FilterTargets(std::list& targets) - { - targets.remove_if(AerialTargetSelector()); - } - - void Register() override - { - OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_mimiron_magnetic_core_SpellScript::FilterTargets, EFFECT_1, TARGET_UNIT_SRC_AREA_ENTRY); - } - }; - - SpellScript* GetSpellScript() const override - { - return new spell_mimiron_magnetic_core_SpellScript(); - } - - class spell_mimiron_magnetic_core_AuraScript : public AuraScript - { - PrepareAuraScript(spell_mimiron_magnetic_core_AuraScript); - - bool Validate(SpellInfo const* /*spell*/) override - { - if (!sSpellMgr->GetSpellInfo(SPELL_MAGNETIC_CORE_VISUAL)) - return false; - return true; - } - - void OnApply(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) - { - Unit* target = GetTarget(); - if (target->GetAI()) - target->GetAI()->DoAction(DO_DISABLE_AERIAL); - - target->CastSpell(target, SPELL_MAGNETIC_CORE_VISUAL, true); - } - - void OnRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) - { - Unit* target = GetTarget(); - if (target->GetAI()) - target->GetAI()->DoAction(DO_ENABLE_AERIAL); - - target->RemoveAurasDueToSpell(SPELL_MAGNETIC_CORE_VISUAL); - } - - void OnRemoveSelf(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) - { - if (GetTarget()->ToCreature()) - GetTarget()->ToCreature()->DespawnOrUnsummon(); - } - - void Register() override - { - AfterEffectApply += AuraEffectApplyFn(spell_mimiron_magnetic_core_AuraScript::OnApply, EFFECT_1, SPELL_AURA_MOD_DAMAGE_PERCENT_TAKEN, AURA_EFFECT_HANDLE_REAL); - AfterEffectRemove += AuraEffectRemoveFn(spell_mimiron_magnetic_core_AuraScript::OnRemove, EFFECT_1, SPELL_AURA_MOD_DAMAGE_PERCENT_TAKEN, AURA_EFFECT_HANDLE_REAL); - AfterEffectRemove += AuraEffectRemoveFn(spell_mimiron_magnetic_core_AuraScript::OnRemoveSelf, EFFECT_0, SPELL_AURA_DUMMY, AURA_EFFECT_HANDLE_REAL); - } - }; - - AuraScript* GetAuraScript() const override - { - return new spell_mimiron_magnetic_core_AuraScript(); - } -}; - -// 63667 - Napalm Shell -class spell_mimiron_napalm_shell : public SpellScriptLoader -{ - public: - spell_mimiron_napalm_shell() : SpellScriptLoader("spell_mimiron_napalm_shell") { } - - class spell_mimiron_napalm_shell_SpellScript : public SpellScript - { - PrepareSpellScript(spell_mimiron_napalm_shell_SpellScript); - - bool Validate(SpellInfo const* /*spell*/) override - { - if (!sSpellMgr->GetSpellInfo(SPELL_NAPALM_SHELL)) - return false; - return true; - } - - void FilterTargets(std::list& targets) - { - if (targets.empty()) - return; - - WorldObject* target = Trinity::Containers::SelectRandomContainerElement(targets); - - targets.remove_if(Trinity::AllWorldObjectsInRange(GetCaster(), 15.0f)); - - if (!targets.empty()) - target = Trinity::Containers::SelectRandomContainerElement(targets); - - targets.clear(); - targets.push_back(target); - } - - void HandleScript(SpellEffIndex /*effIndex*/) - { - GetCaster()->CastSpell(GetHitUnit(), SPELL_NAPALM_SHELL); - } - - void Register() override - { - OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_mimiron_napalm_shell_SpellScript::FilterTargets, EFFECT_0, TARGET_UNIT_SRC_AREA_ENEMY); - OnEffectHitTarget += SpellEffectFn(spell_mimiron_napalm_shell_SpellScript::HandleScript, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT); - } - }; - - SpellScript* GetSpellScript() const override - { - return new spell_mimiron_napalm_shell_SpellScript(); - } -}; - -// 63274 - P3Wx2 Laser Barrage -- This should be removed when channel track target implementation is properly functioning. -class spell_mimiron_p3wx2_laser_barrage : public SpellScriptLoader -{ - public: - spell_mimiron_p3wx2_laser_barrage() : SpellScriptLoader("spell_mimiron_p3wx2_laser_barrage") { } - - class spell_mimiron_p3wx2_laser_barrage_SpellScript : public SpellScript - { - PrepareSpellScript(spell_mimiron_p3wx2_laser_barrage_SpellScript); - - void OnHit(SpellEffIndex /*effIndex*/) - { - GetCaster()->SetUInt64Value(UNIT_FIELD_CHANNEL_OBJECT, GetHitUnit()->GetGUID()); - } - - void Register() override - { - OnEffectHitTarget += SpellEffectFn(spell_mimiron_p3wx2_laser_barrage_SpellScript::OnHit, EFFECT_0, SPELL_EFFECT_APPLY_AURA); - } - }; - - SpellScript* GetSpellScript() const override - { - return new spell_mimiron_p3wx2_laser_barrage_SpellScript(); - } -}; - -// 64542 - Plasma Blast -class spell_mimiron_plasma_blast : public SpellScriptLoader -{ - public: - spell_mimiron_plasma_blast() : SpellScriptLoader("spell_mimiron_plasma_blast") { } - - class spell_mimiron_plasma_blast_SpellScript : public SpellScript - { - PrepareSpellScript(spell_mimiron_plasma_blast_SpellScript); - - bool Validate(SpellInfo const* /*spell*/) override - { - if (!sSpellMgr->GetSpellInfo(SPELL_PLASMA_BLAST)) - return false; - return true; - } - - bool Load() override - { - return GetCaster()->GetVehicleKit(); - } - - void HandleScript(SpellEffIndex /*effIndex*/) - { - if (Unit* caster = GetCaster()->GetVehicleKit()->GetPassenger(3)) - caster->CastSpell(GetHitUnit(), SPELL_PLASMA_BLAST); - } - - void Register() override - { - OnEffectHitTarget += SpellEffectFn(spell_mimiron_plasma_blast_SpellScript::HandleScript, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT); - } - }; - - SpellScript* GetSpellScript() const override - { - return new spell_mimiron_plasma_blast_SpellScript(); - } -}; - -// 66351 - Explosion -class spell_mimiron_proximity_explosion : public SpellScriptLoader -{ - public: - spell_mimiron_proximity_explosion() : SpellScriptLoader("spell_mimiron_proximity_explosion") { } - - class spell_mimiron_proximity_explosion_SpellScript : public SpellScript - { - PrepareSpellScript(spell_mimiron_proximity_explosion_SpellScript); - - void OnHit(SpellEffIndex /*effIndex*/) - { - if (GetHitPlayer()) - if (InstanceScript* instance = GetCaster()->GetInstanceScript()) - if (Creature* mkII = ObjectAccessor::GetCreature(*GetCaster(), instance->GetData64(DATA_LEVIATHAN_MK_II))) - mkII->AI()->SetData(DATA_SETUP_MINE, 0); - } - - void HandleAura(SpellEffIndex /*effIndex*/) - { - GetCaster()->RemoveAurasDueToSpell(SPELL_PROXIMITY_MINE_PERIODIC_TRIGGER); - } - - void Register() override - { - OnEffectHitTarget += SpellEffectFn(spell_mimiron_proximity_explosion_SpellScript::OnHit, EFFECT_0, SPELL_EFFECT_SCHOOL_DAMAGE); - OnEffectHitTarget += SpellEffectFn(spell_mimiron_proximity_explosion_SpellScript::HandleAura, EFFECT_1, SPELL_EFFECT_APPLY_AURA); - } - }; - - SpellScript* GetSpellScript() const override - { - return new spell_mimiron_proximity_explosion_SpellScript(); - } -}; - -// 63027 - Proximity Mines -class spell_mimiron_proximity_mines : public SpellScriptLoader -{ - public: - spell_mimiron_proximity_mines() : SpellScriptLoader("spell_mimiron_proximity_mines") { } - - class spell_mimiron_proximity_mines_SpellScript : public SpellScript - { - PrepareSpellScript(spell_mimiron_proximity_mines_SpellScript); - - bool Validate(SpellInfo const* /*spell*/) override - { - if (!sSpellMgr->GetSpellInfo(SPELL_SUMMON_PROXIMITY_MINE)) - return false; - return true; - } + PrepareSpellScript(spell_ulduar_proximity_minesSpellScript); - void HandleScript(SpellEffIndex /*effIndex*/) + void HandleScript(SpellEffIndex effIndex) { + PreventHitDefaultEffect(effIndex); for (uint8 i = 0; i < 10; ++i) - GetCaster()->CastSpell(GetCaster(), SPELL_SUMMON_PROXIMITY_MINE, true); + GetCaster()->CastSpell(GetCaster(), SPELL_TRIGGER_MISSILE, true); } void Register() override { - OnEffectHitTarget += SpellEffectFn(spell_mimiron_proximity_mines_SpellScript::HandleScript, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT); + OnEffectHitTarget += SpellEffectFn(spell_ulduar_proximity_minesSpellScript::HandleScript, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT); } }; SpellScript* GetSpellScript() const override { - return new spell_mimiron_proximity_mines_SpellScript(); - } -}; - -// 65346 - Proximity Mine -class spell_mimiron_proximity_trigger : public SpellScriptLoader -{ - public: - spell_mimiron_proximity_trigger() : SpellScriptLoader("spell_mimiron_proximity_trigger") { } - - class spell_mimiron_proximity_trigger_SpellScript : public SpellScript - { - PrepareSpellScript(spell_mimiron_proximity_trigger_SpellScript); - - bool Validate(SpellInfo const* /*spell*/) override - { - if (!sSpellMgr->GetSpellInfo(SPELL_PROXIMITY_MINE_EXPLOSION)) - return false; - return true; - } - - void FilterTargets(std::list& targets) - { - targets.remove(GetExplTargetWorldObject()); - - if (targets.empty()) - FinishCast(SPELL_FAILED_NO_VALID_TARGETS); - } - - void HandleDummy(SpellEffIndex /*effIndex*/) - { - GetCaster()->CastSpell((Unit*)NULL, SPELL_PROXIMITY_MINE_EXPLOSION, true); - } - - void Register() override - { - OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_mimiron_proximity_trigger_SpellScript::FilterTargets, EFFECT_0, TARGET_UNIT_SRC_AREA_ENTRY); - OnEffectHit += SpellEffectFn(spell_mimiron_proximity_trigger_SpellScript::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY); - } - }; - - SpellScript* GetSpellScript() const override - { - return new spell_mimiron_proximity_trigger_SpellScript(); - } -}; - -// 63382 - Rapid Burst -class spell_mimiron_rapid_burst : public SpellScriptLoader -{ - public: - spell_mimiron_rapid_burst() : SpellScriptLoader("spell_mimiron_rapid_burst") { } - - class spell_mimiron_rapid_burst_AuraScript : public AuraScript - { - PrepareAuraScript(spell_mimiron_rapid_burst_AuraScript); - - bool Validate(SpellInfo const* /*spell*/) override - { - if (!sSpellMgr->GetSpellInfo(SPELL_RAPID_BURST_LEFT) || !sSpellMgr->GetSpellInfo(SPELL_RAPID_BURST_RIGHT)) - return false; - return true; - } - - void AfterRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) - { - if (GetTarget()->IsSummon()) - GetTarget()->ToTempSummon()->DespawnOrUnsummon(); - } - - void HandleDummyTick(AuraEffect const* aurEff) - { - if (GetCaster()) - GetCaster()->CastSpell(GetTarget(), aurEff->GetTickNumber() % 2 == 0 ? SPELL_RAPID_BURST_RIGHT : SPELL_RAPID_BURST_LEFT, true, NULL, aurEff); - } - - void Register() override - { - AfterEffectRemove += AuraEffectApplyFn(spell_mimiron_rapid_burst_AuraScript::AfterRemove, EFFECT_0, SPELL_AURA_PERIODIC_DUMMY, AURA_EFFECT_HANDLE_REAL); - OnEffectPeriodic += AuraEffectPeriodicFn(spell_mimiron_rapid_burst_AuraScript::HandleDummyTick, EFFECT_1, SPELL_AURA_PERIODIC_DUMMY); - } - }; - - AuraScript* GetAuraScript() const override - { - return new spell_mimiron_rapid_burst_AuraScript(); - } -}; - -// 64402 - Rocket Strike, 65034 - Rocket Strike -class spell_mimiron_rocket_strike : public SpellScriptLoader -{ - public: - spell_mimiron_rocket_strike() : SpellScriptLoader("spell_mimiron_rocket_strike") { } - - class spell_mimiron_rocket_strike_SpellScript : public SpellScript - { - PrepareSpellScript(spell_mimiron_rocket_strike_SpellScript); - - bool Validate(SpellInfo const* /*spell*/) override - { - if (!sSpellMgr->GetSpellInfo(SPELL_SCRIPT_EFFECT_ROCKET_STRIKE)) - return false; - return true; - } - - void FilterTargets(std::list& targets) - { - if (targets.empty()) - return; - - if (m_scriptSpellId == SPELL_ROCKET_STRIKE_LEFT && GetCaster()->IsVehicle()) - if (WorldObject* target = GetCaster()->GetVehicleKit()->GetPassenger(6)) - { - targets.clear(); - targets.push_back(target); - } - } - - void HandleDummy(SpellEffIndex /*effIndex*/) - { - GetHitUnit()->CastSpell((Unit*)NULL, SPELL_SCRIPT_EFFECT_ROCKET_STRIKE, true, NULL, NULL, GetCaster()->GetGUID()); - } - - void Register() override - { - OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_mimiron_rocket_strike_SpellScript::FilterTargets, EFFECT_0, TARGET_UNIT_SRC_AREA_ENTRY); - OnEffectHitTarget += SpellEffectFn(spell_mimiron_rocket_strike_SpellScript::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY); - } - }; - - SpellScript* GetSpellScript() const override - { - return new spell_mimiron_rocket_strike_SpellScript(); - } -}; - -// 63041 - Rocket Strike -class spell_mimiron_rocket_strike_damage : public SpellScriptLoader -{ - public: - spell_mimiron_rocket_strike_damage() : SpellScriptLoader("spell_mimiron_rocket_strike_damage") { } - - class spell_mimiron_rocket_strike_damage_SpellScript : public SpellScript - { - PrepareSpellScript(spell_mimiron_rocket_strike_damage_SpellScript); - - bool Validate(SpellInfo const* /*spell*/) override - { - if (!sSpellMgr->GetSpellInfo(SPELL_NOT_SO_FRIENDLY_FIRE)) - return false; - return true; - } - - void HandleDespawn(SpellEffIndex /*effIndex*/) - { - if (GetCaster()->IsSummon()) - GetCaster()->ToTempSummon()->DespawnOrUnsummon(1000); - } - - void HandleScript(SpellEffIndex /*effIndex*/) - { - if (GetHitPlayer()) - if (InstanceScript* instance = GetCaster()->GetInstanceScript()) - if (Creature* mkii = ObjectAccessor::GetCreature(*GetCaster(), instance->GetData64(DATA_LEVIATHAN_MK_II))) - mkii->AI()->SetData(DATA_SETUP_ROCKET, 0); - } - - void HandleFriendlyFire(SpellEffIndex /*effIndex*/) - { - GetHitUnit()->CastSpell((Unit*)NULL, SPELL_NOT_SO_FRIENDLY_FIRE, true); - } - - void Register() override - { - OnEffectHit += SpellEffectFn(spell_mimiron_rocket_strike_damage_SpellScript::HandleDespawn, EFFECT_0, SPELL_EFFECT_SCHOOL_DAMAGE); - OnEffectHitTarget += SpellEffectFn(spell_mimiron_rocket_strike_damage_SpellScript::HandleScript, EFFECT_0, SPELL_EFFECT_SCHOOL_DAMAGE); - OnEffectHitTarget += SpellEffectFn(spell_mimiron_rocket_strike_damage_SpellScript::HandleFriendlyFire, EFFECT_1, SPELL_EFFECT_SCHOOL_DAMAGE); - } - }; - - SpellScript* GetSpellScript() const override - { - return new spell_mimiron_rocket_strike_damage_SpellScript(); - } -}; - -// 63681 - Rocket Strike -class spell_mimiron_rocket_strike_target_select : public SpellScriptLoader -{ - public: - spell_mimiron_rocket_strike_target_select() : SpellScriptLoader("spell_mimiron_rocket_strike_target_select") { } - - class spell_mimiron_rocket_strike_target_select_SpellScript : public SpellScript - { - PrepareSpellScript(spell_mimiron_rocket_strike_target_select_SpellScript); - - bool Validate(SpellInfo const* /*spell*/) override - { - if (!sSpellMgr->GetSpellInfo(SPELL_SUMMON_ROCKET_STRIKE)) - return false; - return true; - } - - void FilterTargets(std::list& targets) - { - if (targets.empty()) - return; - - WorldObject* target = Trinity::Containers::SelectRandomContainerElement(targets); - - targets.remove_if(Trinity::AllWorldObjectsInRange(GetCaster(), 15.0f)); - - if (!targets.empty()) - target = Trinity::Containers::SelectRandomContainerElement(targets); - - targets.clear(); - targets.push_back(target); - } - - void HandleScript(SpellEffIndex /*effIndex*/) - { - InstanceScript* instance = GetCaster()->GetInstanceScript(); - GetCaster()->CastSpell(GetHitUnit(), SPELL_SUMMON_ROCKET_STRIKE, true, NULL, NULL, instance ? instance->GetData64(DATA_VX_001) : GetCaster()->GetGUID()); - GetCaster()->SetDisplayId(11686); - } - - void Register() override - { - OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_mimiron_rocket_strike_target_select_SpellScript::FilterTargets, EFFECT_0, TARGET_UNIT_SRC_AREA_ENEMY); - OnEffectHitTarget += SpellEffectFn(spell_mimiron_rocket_strike_target_select_SpellScript::HandleScript, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT); - } - }; - - SpellScript* GetSpellScript() const override - { - return new spell_mimiron_rocket_strike_target_select_SpellScript(); - } -}; - -// 64383 - Self Repair -class spell_mimiron_self_repair : public SpellScriptLoader -{ - public: - spell_mimiron_self_repair() : SpellScriptLoader("spell_mimiron_self_repair") { } - - class spell_mimiron_self_repair_SpellScript : public SpellScript - { - PrepareSpellScript(spell_mimiron_self_repair_SpellScript); - - void HandleScript() - { - if (GetCaster()->GetAI()) - GetCaster()->GetAI()->DoAction(DO_ASSEMBLED_COMBAT); - } - - void Register() override - { - AfterHit += SpellHitFn(spell_mimiron_self_repair_SpellScript::HandleScript); - } - }; - - SpellScript* GetSpellScript() const override - { - return new spell_mimiron_self_repair_SpellScript(); - } -}; - -// 63414 - Spinning Up -- This should be removed when channel track target implementation is properly functioning. -class spell_mimiron_spinning_up : public SpellScriptLoader -{ - public: - spell_mimiron_spinning_up() : SpellScriptLoader("spell_mimiron_spinning_up") { } - - class spell_mimiron_spinning_up_SpellScript : public SpellScript - { - PrepareSpellScript(spell_mimiron_spinning_up_SpellScript); - - void OnHit(SpellEffIndex /*effIndex*/) - { - if (GetHitUnit() != GetCaster()) - GetCaster()->SetUInt64Value(UNIT_FIELD_CHANNEL_OBJECT, GetHitUnit()->GetGUID()); - } - - void Register() override - { - OnEffectHitTarget += SpellEffectFn(spell_mimiron_spinning_up_SpellScript::OnHit, EFFECT_0, SPELL_EFFECT_APPLY_AURA); - } - }; - - SpellScript* GetSpellScript() const override - { - return new spell_mimiron_spinning_up_SpellScript(); - } -}; - -// 64426 - Summon Scrap Bot -class spell_mimiron_summon_assault_bot : public SpellScriptLoader -{ - public: - spell_mimiron_summon_assault_bot() : SpellScriptLoader("spell_mimiron_summon_assault_bot") { } - - class spell_mimiron_summon_assault_bot_AuraScript : public AuraScript - { - PrepareAuraScript(spell_mimiron_summon_assault_bot_AuraScript); - - bool Validate(SpellInfo const* /*spell*/) override - { - if (!sSpellMgr->GetSpellInfo(SPELL_SUMMON_ASSAULT_BOT)) - return false; - return true; - } - - void OnRemove(AuraEffect const* aurEff, AuraEffectHandleModes /*mode*/) - { - if (Unit* caster = GetCaster()) - if (InstanceScript* instance = caster->GetInstanceScript()) - if (instance->GetBossState(BOSS_MIMIRON) == IN_PROGRESS) - caster->CastSpell(caster, SPELL_SUMMON_ASSAULT_BOT, false, NULL, aurEff, instance->GetData64(DATA_AERIAL_COMMAND_UNIT)); - } - - void Register() override - { - OnEffectRemove += AuraEffectRemoveFn(spell_mimiron_summon_assault_bot_AuraScript::OnRemove, EFFECT_0, SPELL_AURA_DUMMY, AURA_EFFECT_HANDLE_REAL); - } - }; - - AuraScript* GetAuraScript() const override - { - return new spell_mimiron_summon_assault_bot_AuraScript(); - } -}; - -// 64425 - Summon Scrap Bot Trigger -class spell_mimiron_summon_assault_bot_target : public SpellScriptLoader -{ - public: - spell_mimiron_summon_assault_bot_target() : SpellScriptLoader("spell_mimiron_summon_assault_bot_target") { } - - class spell_mimiron_summon_assault_bot_target_SpellScript : public SpellScript - { - PrepareSpellScript(spell_mimiron_summon_assault_bot_target_SpellScript); - - bool Validate(SpellInfo const* /*spell*/) override - { - if (!sSpellMgr->GetSpellInfo(SPELL_SUMMON_ASSAULT_BOT_DUMMY)) - return false; - return true; - } - - void FilterTargets(std::list& targets) - { - if (targets.empty()) - return; - - WorldObject* target = Trinity::Containers::SelectRandomContainerElement(targets); - - targets.clear(); - targets.push_back(target); - } - - void HandleDummy(SpellEffIndex /*effIndex*/) - { - GetHitUnit()->CastSpell(GetHitUnit(), SPELL_SUMMON_ASSAULT_BOT_DUMMY, true); - } - - void Register() override - { - OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_mimiron_summon_assault_bot_target_SpellScript::FilterTargets, EFFECT_0, TARGET_UNIT_SRC_AREA_ENTRY); - OnEffectHitTarget += SpellEffectFn(spell_mimiron_summon_assault_bot_target_SpellScript::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY); - } - }; - - SpellScript* GetSpellScript() const override - { - return new spell_mimiron_summon_assault_bot_target_SpellScript(); - } -}; - -// 64621 - Summon Fire Bot -class spell_mimiron_summon_fire_bot : public SpellScriptLoader -{ - public: - spell_mimiron_summon_fire_bot() : SpellScriptLoader("spell_mimiron_summon_fire_bot") { } - - class spell_mimiron_summon_fire_bot_AuraScript : public AuraScript - { - PrepareAuraScript(spell_mimiron_summon_fire_bot_AuraScript); - - bool Validate(SpellInfo const* /*spell*/) override - { - if (!sSpellMgr->GetSpellInfo(SPELL_SUMMON_FIRE_BOT)) - return false; - - return true; - } - - void OnRemove(AuraEffect const* aurEff, AuraEffectHandleModes /*mode*/) - { - if (Unit* caster = GetCaster()) - if (InstanceScript* instance = caster->GetInstanceScript()) - if (instance->GetBossState(BOSS_MIMIRON) == IN_PROGRESS) - caster->CastSpell(caster, SPELL_SUMMON_FIRE_BOT, false, NULL, aurEff, instance->GetData64(DATA_AERIAL_COMMAND_UNIT)); - } - - void Register() override - { - OnEffectRemove += AuraEffectRemoveFn(spell_mimiron_summon_fire_bot_AuraScript::OnRemove, EFFECT_0, SPELL_AURA_DUMMY, AURA_EFFECT_HANDLE_REAL); - } - }; - - AuraScript* GetAuraScript() const override - { - return new spell_mimiron_summon_fire_bot_AuraScript(); - } -}; - -// 64620 - Summon Fire Bot Trigger -class spell_mimiron_summon_fire_bot_target : public SpellScriptLoader -{ - public: - spell_mimiron_summon_fire_bot_target() : SpellScriptLoader("spell_mimiron_summon_fire_bot_target") { } - - class spell_mimiron_summon_fire_bot_target_SpellScript : public SpellScript - { - PrepareSpellScript(spell_mimiron_summon_fire_bot_target_SpellScript); - - bool Validate(SpellInfo const* /*spell*/) override - { - if (!sSpellMgr->GetSpellInfo(SPELL_SUMMON_FIRE_BOT_DUMMY)) - return false; - return true; - } - - void FilterTargets(std::list& targets) - { - Trinity::Containers::RandomResizeList(targets, 3); - } - - void HandleDummy(SpellEffIndex /*effIndex*/) - { - GetHitUnit()->CastSpell(GetHitUnit(), SPELL_SUMMON_FIRE_BOT_DUMMY, true); - } - - void Register() override - { - OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_mimiron_summon_fire_bot_target_SpellScript::FilterTargets, EFFECT_0, TARGET_UNIT_SRC_AREA_ENTRY); - OnEffectHitTarget += SpellEffectFn(spell_mimiron_summon_fire_bot_target_SpellScript::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY); - } - }; - - SpellScript* GetSpellScript() const override - { - return new spell_mimiron_summon_fire_bot_target_SpellScript(); - } -}; - -// 64567 - Summon Flames Initial -class spell_mimiron_summon_flames_initial : public SpellScriptLoader -{ - public: - spell_mimiron_summon_flames_initial() : SpellScriptLoader("spell_mimiron_summon_flames_initial") { } - - class spell_mimiron_summon_flames_initial_SpellScript : public SpellScript - { - PrepareSpellScript(spell_mimiron_summon_flames_initial_SpellScript); - - bool Validate(SpellInfo const* /*spell*/) override - { - if (!sSpellMgr->GetSpellInfo(SPELL_SUMMON_FLAMES_INITIAL)) - return false; - return true; - } - - void FilterTargets(std::list& targets) - { - Trinity::Containers::RandomResizeList(targets, 3); - } - - void HandleScript(SpellEffIndex /*effIndex*/) - { - GetCaster()->CastSpell(GetHitUnit(), SPELL_SUMMON_FLAMES_INITIAL, true); - } - - void Register() override - { - OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_mimiron_summon_flames_initial_SpellScript::FilterTargets, EFFECT_0, TARGET_UNIT_SRC_AREA_ENEMY); - OnEffectHitTarget += SpellEffectFn(spell_mimiron_summon_flames_initial_SpellScript::HandleScript, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT); - } - }; - - SpellScript* GetSpellScript() const override - { - return new spell_mimiron_summon_flames_initial_SpellScript(); - } -}; - -// 64562 - Summon Flames Spread Trigger -class spell_mimiron_summon_flames_spread : public SpellScriptLoader -{ - public: - spell_mimiron_summon_flames_spread() : SpellScriptLoader("spell_mimiron_summon_flames_spread") { } - - class spell_mimiron_summon_flames_spread_SpellScript : public SpellScript - { - PrepareSpellScript(spell_mimiron_summon_flames_spread_SpellScript); - - void FilterTargets(std::list& targets) - { - if (targets.empty()) - return; - - WorldObject* target = targets.front(); - - for (std::list::const_iterator iter = targets.begin(); iter != targets.end(); iter++) - if (GetCaster()->GetDistance2d(*iter) < GetCaster()->GetDistance2d(target)) - target = *iter; - - targets.clear(); - targets.push_back(target); - } - - void OnHit(SpellEffIndex /*effIndex*/) - { - GetCaster()->SetInFront(GetHitUnit()); - } - - void Register() override - { - OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_mimiron_summon_flames_spread_SpellScript::FilterTargets, EFFECT_0, TARGET_UNIT_SRC_AREA_ENEMY); - OnEffectHitTarget += SpellEffectFn(spell_mimiron_summon_flames_spread_SpellScript::OnHit, EFFECT_0, SPELL_EFFECT_APPLY_AURA); - } - }; - - SpellScript* GetSpellScript() const override - { - return new spell_mimiron_summon_flames_spread_SpellScript(); - } - - class spell_mimiron_summon_flames_spread_AuraScript : public AuraScript - { - PrepareAuraScript(spell_mimiron_summon_flames_spread_AuraScript); - - bool Validate(SpellInfo const* /*spell*/) override - { - if (!sSpellMgr->GetSpellInfo(SPELL_SUMMON_FLAMES_SPREAD)) - return false; - return true; - } - - void HandleTick(AuraEffect const* /*aurEff*/) - { - PreventDefaultAction(); - if (Unit* caster = GetCaster()) - if (caster->HasAura(SPELL_FLAMES_PERIODIC_TRIGGER)) - caster->CastSpell(GetTarget(), SPELL_SUMMON_FLAMES_SPREAD, true); - } - - void Register() override - { - OnEffectPeriodic += AuraEffectPeriodicFn(spell_mimiron_summon_flames_spread_AuraScript::HandleTick, EFFECT_0, SPELL_AURA_PERIODIC_TRIGGER_SPELL); - } - }; - - AuraScript* GetAuraScript() const override - { - return new spell_mimiron_summon_flames_spread_AuraScript(); - } -}; - -// 64627 - Frost Bomb -class spell_mimiron_summon_frost_bomb : public SpellScriptLoader -{ - public: - spell_mimiron_summon_frost_bomb() : SpellScriptLoader("spell_mimiron_summon_frost_bomb") { } - - class spell_mimiron_summon_frost_bomb_SpellScript : public SpellScript - { - PrepareSpellScript(spell_mimiron_summon_frost_bomb_SpellScript); - - void HandleDummy(SpellEffIndex /*effIndex*/) - { - GetHitUnit()->RemoveAurasDueToSpell(SPELL_FLAMES_PERIODIC_TRIGGER); - } - - void Register() override - { - OnEffectHitTarget += SpellEffectFn(spell_mimiron_summon_frost_bomb_SpellScript::HandleDummy, EFFECT_1, SPELL_EFFECT_DUMMY); - } - }; - - SpellScript* GetSpellScript() const override - { - return new spell_mimiron_summon_frost_bomb_SpellScript(); - } -}; - -// 64623 - Frost Bomb -class spell_mimiron_summon_frost_bomb_target : public SpellScriptLoader -{ - public: - spell_mimiron_summon_frost_bomb_target() : SpellScriptLoader("spell_mimiron_summon_frost_bomb_target") { } - - class spell_mimiron_summon_frost_bomb_target_SpellScript : public SpellScript - { - PrepareSpellScript(spell_mimiron_summon_frost_bomb_target_SpellScript); - - bool Validate(SpellInfo const* /*spell*/) override - { - if (!sSpellMgr->GetSpellInfo(SPELL_SUMMON_FROST_BOMB)) - return false; - return true; - } - - void FilterTargets(std::list& targets) - { - if (targets.empty()) - return; - - targets.remove_if(Trinity::AllWorldObjectsInRange(GetCaster(), 15.0f)); - - if (targets.empty()) - return; - - WorldObject* target = Trinity::Containers::SelectRandomContainerElement(targets); - - targets.clear(); - targets.push_back(target); - } - - void HandleScript(SpellEffIndex /*effIndex*/) - { - GetCaster()->CastSpell(GetHitUnit(), SPELL_SUMMON_FROST_BOMB, true); - } - - void Register() override - { - OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_mimiron_summon_frost_bomb_target_SpellScript::FilterTargets, EFFECT_0, TARGET_UNIT_SRC_AREA_ENTRY); - OnEffectHitTarget += SpellEffectFn(spell_mimiron_summon_frost_bomb_target_SpellScript::HandleScript, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT); - } - }; - - SpellScript* GetSpellScript() const override - { - return new spell_mimiron_summon_frost_bomb_target_SpellScript(); - } -}; - -// 64398 - Summon Scrap Bot -class spell_mimiron_summon_junk_bot : public SpellScriptLoader -{ - public: - spell_mimiron_summon_junk_bot() : SpellScriptLoader("spell_mimiron_summon_junk_bot") { } - - class spell_mimiron_summon_junk_bot_AuraScript : public AuraScript - { - PrepareAuraScript(spell_mimiron_summon_junk_bot_AuraScript); - - bool Validate(SpellInfo const* /*spell*/) override - { - if (!sSpellMgr->GetSpellInfo(SPELL_SUMMON_JUNK_BOT)) - return false; - return true; - } - - void OnRemove(AuraEffect const* aurEff, AuraEffectHandleModes /*mode*/) - { - if (Unit* caster = GetCaster()) - if (InstanceScript* instance = caster->GetInstanceScript()) - if (instance->GetBossState(BOSS_MIMIRON) == IN_PROGRESS) - caster->CastSpell(caster, SPELL_SUMMON_JUNK_BOT, false, NULL, aurEff, instance->GetData64(DATA_AERIAL_COMMAND_UNIT)); - } - - void Register() override - { - OnEffectRemove += AuraEffectRemoveFn(spell_mimiron_summon_junk_bot_AuraScript::OnRemove, EFFECT_0, SPELL_AURA_DUMMY, AURA_EFFECT_HANDLE_REAL); - } - }; - - AuraScript* GetAuraScript() const override - { - return new spell_mimiron_summon_junk_bot_AuraScript(); - } -}; - -// 63820 - Summon Scrap Bot Trigger -class spell_mimiron_summon_junk_bot_target : public SpellScriptLoader -{ - public: - spell_mimiron_summon_junk_bot_target() : SpellScriptLoader("spell_mimiron_summon_junk_bot_target") { } - - class spell_mimiron_summon_junk_bot_target_SpellScript : public SpellScript - { - PrepareSpellScript(spell_mimiron_summon_junk_bot_target_SpellScript); - - bool Validate(SpellInfo const* /*spell*/) override - { - if (!sSpellMgr->GetSpellInfo(SPELL_SUMMON_JUNK_BOT_DUMMY)) - return false; - return true; - } - - void FilterTargets(std::list& targets) - { - if (targets.empty()) - return; - - WorldObject* target = Trinity::Containers::SelectRandomContainerElement(targets); - - targets.clear(); - targets.push_back(target); - } - - void HandleDummy(SpellEffIndex /*effIndex*/) - { - GetHitUnit()->CastSpell(GetHitUnit(), SPELL_SUMMON_JUNK_BOT_DUMMY, true); - } - - void Register() override - { - OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_mimiron_summon_junk_bot_target_SpellScript::FilterTargets, EFFECT_0, TARGET_UNIT_SRC_AREA_ENTRY); - OnEffectHitTarget += SpellEffectFn(spell_mimiron_summon_junk_bot_target_SpellScript::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY); - } - }; - - SpellScript* GetSpellScript() const override - { - return new spell_mimiron_summon_junk_bot_target_SpellScript(); - } -}; - -// 63339 - Weld -class spell_mimiron_weld : public SpellScriptLoader -{ - public: - spell_mimiron_weld() : SpellScriptLoader("spell_mimiron_weld") { } - - class spell_mimiron_weld_AuraScript : public AuraScript - { - PrepareAuraScript(spell_mimiron_weld_AuraScript); - - void HandleTick(AuraEffect const* aurEff) - { - Unit* caster = GetTarget(); - if (Unit* vehicle = caster->GetVehicleBase()) - { - if (aurEff->GetTickNumber() % 5 == 0) - caster->CastSpell(vehicle, repairSpells[urand(0, 3)]); - caster->SetFacingToObject(vehicle); - } - } - - void Register() override - { - OnEffectPeriodic += AuraEffectPeriodicFn(spell_mimiron_weld_AuraScript::HandleTick, EFFECT_0, SPELL_AURA_PERIODIC_TRIGGER_SPELL); - } - }; - - AuraScript* GetAuraScript() const override - { - return new spell_mimiron_weld_AuraScript(); - } -}; - -class achievement_setup_boom : public AchievementCriteriaScript -{ - public: - achievement_setup_boom() : AchievementCriteriaScript("achievement_setup_boom") { } - - bool OnCheck(Player* /*source*/, Unit* target) - { - return target && target->GetAI()->GetData(DATA_SETUP_BOMB); - } -}; - -class achievement_setup_mine : public AchievementCriteriaScript -{ - public: - achievement_setup_mine() : AchievementCriteriaScript("achievement_setup_mine") { } - - bool OnCheck(Player* /*source*/, Unit* target) - { - return target && target->GetAI()->GetData(DATA_SETUP_MINE); - } -}; - -class achievement_setup_rocket : public AchievementCriteriaScript -{ - public: - achievement_setup_rocket() : AchievementCriteriaScript("achievement_setup_rocket") { } - - bool OnCheck(Player* /*source*/, Unit* target) - { - return target && target->GetAI()->GetData(DATA_SETUP_ROCKET); - } -}; - -class achievement_firefighter : public AchievementCriteriaScript -{ - public: - achievement_firefighter() : AchievementCriteriaScript("achievement_firefighter") { } - - bool OnCheck(Player* /*source*/, Unit* target) - { - return target && target->GetAI()->GetData(DATA_FIREFIGHTER); + return new spell_ulduar_proximity_minesSpellScript(); } }; void AddSC_boss_mimiron() { - new boss_aerial_command_unit(); - new boss_leviathan_mk_ii(); - new boss_mimiron(); - new boss_vx_001(); - - new npc_magnetic_core(); - new npc_mimiron_assault_bot(); - new npc_mimiron_emergency_fire_bot(); - new npc_mimiron_computer(); - new npc_mimiron_flames(); - new npc_mimiron_frost_bomb(); - new npc_mimiron_proximity_mine(); - - new go_mimiron_hardmode_button(); - - new spell_mimiron_bomb_bot(); - new spell_mimiron_clear_fires(); - new spell_mimiron_despawn_assault_bots(); - new spell_mimiron_fire_search(); - new spell_mimiron_flame_suppressant(); - new spell_mimiron_frost_bomb_explosion(); - new spell_mimiron_magnetic_core(); - new spell_mimiron_napalm_shell(); - new spell_mimiron_p3wx2_laser_barrage(); - new spell_mimiron_plasma_blast(); - new spell_mimiron_proximity_explosion(); - new spell_mimiron_proximity_mines(); - new spell_mimiron_proximity_trigger(); - new spell_mimiron_rapid_burst(); - new spell_mimiron_rocket_strike(); - new spell_mimiron_rocket_strike_damage(); - new spell_mimiron_rocket_strike_target_select(); - new spell_mimiron_self_repair(); - new spell_mimiron_spinning_up(); - new spell_mimiron_summon_assault_bot(); - new spell_mimiron_summon_assault_bot_target(); - new spell_mimiron_summon_fire_bot(); - new spell_mimiron_summon_fire_bot_target(); - new spell_mimiron_summon_flames_initial(); - new spell_mimiron_summon_flames_spread(); - new spell_mimiron_summon_frost_bomb(); - new spell_mimiron_summon_frost_bomb_target(); - new spell_mimiron_summon_junk_bot(); - new spell_mimiron_summon_junk_bot_target(); - new spell_mimiron_weld(); - - new achievement_setup_boom(); - new achievement_setup_mine(); - new achievement_setup_rocket(); - new achievement_firefighter(); + new spell_ulduar_proximity_mines(); } diff --git a/src/server/scripts/Northrend/Ulduar/Ulduar/instance_ulduar.cpp b/src/server/scripts/Northrend/Ulduar/Ulduar/instance_ulduar.cpp index 257518d998..6437a76ee9 100644 --- a/src/server/scripts/Northrend/Ulduar/Ulduar/instance_ulduar.cpp +++ b/src/server/scripts/Northrend/Ulduar/Ulduar/instance_ulduar.cpp @@ -32,9 +32,6 @@ static DoorData const doorData[] = { GO_HODIR_ENTRANCE, BOSS_HODIR, DOOR_TYPE_ROOM, BOUNDARY_E }, { GO_HODIR_DOOR, BOSS_HODIR, DOOR_TYPE_PASSAGE, BOUNDARY_NONE }, { GO_HODIR_ICE_DOOR, BOSS_HODIR, DOOR_TYPE_PASSAGE, BOUNDARY_W }, - { GO_MIMIRON_DOOR_1, BOSS_MIMIRON, DOOR_TYPE_ROOM, BOUNDARY_W }, - { GO_MIMIRON_DOOR_2, BOSS_MIMIRON, DOOR_TYPE_ROOM, BOUNDARY_E }, - { GO_MIMIRON_DOOR_3, BOSS_MIMIRON, DOOR_TYPE_ROOM, BOUNDARY_S }, { GO_VEZAX_DOOR, BOSS_VEZAX, DOOR_TYPE_PASSAGE, BOUNDARY_E }, { GO_YOGG_SARON_DOOR, BOSS_YOGG_SARON, DOOR_TYPE_ROOM, BOUNDARY_S }, { GO_DOODAD_UL_SIGILDOOR_03, BOSS_ALGALON, DOOR_TYPE_ROOM, BOUNDARY_W }, @@ -73,14 +70,11 @@ class instance_ulduar : public InstanceMapScript uint64 AssemblyGUIDs[3]; uint64 KologarnGUID; uint64 AuriayaGUID; + uint64 MimironGUID; uint64 HodirGUID; uint64 ThorimGUID; uint64 FreyaGUID; uint64 ElderGUIDs[3]; - uint64 MimironGUID; - uint64 MimironVehicleGUIDs[3]; - uint64 MimironComputerGUID; - uint64 MimironWorldTriggerGUID; uint64 VezaxGUID; uint64 YoggSaronGUID; uint64 VoiceOfYoggSaronGUID; @@ -98,9 +92,6 @@ class instance_ulduar : public InstanceMapScript uint64 ThorimChestGUID; uint64 HodirRareCacheGUID; uint64 HodirChestGUID; - uint64 MimironTramGUID; - uint64 MimironElevatorGUID; - uint64 MimironButtonGUID; uint64 BrainRoomDoorGUIDs[3]; uint64 AlgalonSigilDoorGUID[3]; uint64 AlgalonFloorGUID[2]; @@ -135,8 +126,6 @@ class instance_ulduar : public InstanceMapScript KologarnGUID = 0; AuriayaGUID = 0; MimironGUID = 0; - MimironComputerGUID = 0; - MimironWorldTriggerGUID = 0; HodirGUID = 0; ThorimGUID = 0; FreyaGUID = 0; @@ -151,9 +140,6 @@ class instance_ulduar : public InstanceMapScript ThorimChestGUID = 0; HodirRareCacheGUID = 0; HodirChestGUID = 0; - MimironTramGUID = 0; - MimironElevatorGUID = 0; - MimironButtonGUID = 0; LeviathanGateGUID = 0; AlgalonUniverseGUID = 0; AlgalonTrapdoorGUID = 0; @@ -180,7 +166,6 @@ class instance_ulduar : public InstanceMapScript memset(AssemblyGUIDs, 0, sizeof(AssemblyGUIDs)); memset(RazorHarpoonGUIDs, 0, sizeof(RazorHarpoonGUIDs)); memset(ElderGUIDs, 0, sizeof(ElderGUIDs)); - memset(MimironVehicleGUIDs, 0, sizeof(MimironVehicleGUIDs)); memset(BrainRoomDoorGUIDs, 0, sizeof(BrainRoomDoorGUIDs)); memset(KeeperGUIDs, 0, sizeof(KeeperGUIDs)); memset(_summonObservationRingKeeper, false, sizeof(_summonObservationRingKeeper)); @@ -304,6 +289,9 @@ class instance_ulduar : public InstanceMapScript case NPC_AURIAYA: AuriayaGUID = creature->GetGUID(); break; + case NPC_MIMIRON: + MimironGUID = creature->GetGUID(); + break; // Hodir case NPC_HODIR: @@ -366,28 +354,6 @@ class instance_ulduar : public InstanceMapScript creature->DespawnOrUnsummon(); break; - // Mimiron - case NPC_MIMIRON: - MimironGUID = creature->GetGUID(); - break; - case NPC_LEVIATHAN_MKII: - MimironVehicleGUIDs[0] = creature->GetGUID(); - if (GetBossState(BOSS_MIMIRON) == DONE) - creature->DespawnOrUnsummon(); - break; - case NPC_VX_001: - MimironVehicleGUIDs[1] = creature->GetGUID(); - break; - case NPC_AERIAL_COMMAND_UNIT: - MimironVehicleGUIDs[2] = creature->GetGUID(); - break; - case NPC_COMPUTER: - MimironComputerGUID = creature->GetGUID(); - break; - case NPC_WORLD_TRIGGER_MIMIRON: - MimironWorldTriggerGUID = creature->GetGUID(); - break; - case NPC_VEZAX: VezaxGUID = creature->GetGUID(); break; @@ -504,15 +470,6 @@ class instance_ulduar : public InstanceMapScript case GO_HODIR_CHEST: HodirChestGUID = gameObject->GetGUID(); break; - case GO_MIMIRON_TRAM: - MimironTramGUID = gameObject->GetGUID(); - break; - case GO_MIMIRON_ELEVATOR: - MimironElevatorGUID = gameObject->GetGUID(); - break; - case GO_MIMIRON_BUTTON: - MimironButtonGUID = gameObject->GetGUID(); - break; case GO_LEVIATHAN_GATE: LeviathanGateGUID = gameObject->GetGUID(); if (GetBossState(BOSS_LEVIATHAN) == DONE) @@ -525,9 +482,6 @@ class instance_ulduar : public InstanceMapScript case GO_HODIR_ENTRANCE: case GO_HODIR_DOOR: case GO_HODIR_ICE_DOOR: - case GO_MIMIRON_DOOR_1: - case GO_MIMIRON_DOOR_2: - case GO_MIMIRON_DOOR_3: case GO_VEZAX_DOOR: case GO_YOGG_SARON_DOOR: AddDoor(gameObject, true); @@ -612,9 +566,6 @@ class instance_ulduar : public InstanceMapScript case GO_HODIR_ENTRANCE: case GO_HODIR_DOOR: case GO_HODIR_ICE_DOOR: - case GO_MIMIRON_DOOR_1: - case GO_MIMIRON_DOOR_2: - case GO_MIMIRON_DOOR_3: case GO_VEZAX_DOOR: case GO_YOGG_SARON_DOOR: case GO_DOODAD_UL_SIGILDOOR_03: @@ -823,10 +774,6 @@ class instance_ulduar : public InstanceMapScript break; case DATA_UNBROKEN: Unbroken = bool(data); - break; - case DATA_MIMIRON_ELEVATOR: - if (GameObject* gameObject = instance->GetGameObject(MimironElevatorGUID)) - gameObject->SetGoState((GOState)data); break; case DATA_ILLUSION: illusion = data; @@ -899,6 +846,8 @@ class instance_ulduar : public InstanceMapScript return KologarnGUID; case BOSS_AURIAYA: return AuriayaGUID; + case BOSS_MIMIRON: + return MimironGUID; case BOSS_HODIR: return HodirGUID; case BOSS_THORIM: @@ -913,22 +862,6 @@ class instance_ulduar : public InstanceMapScript return ElderGUIDs[1]; case BOSS_STONEBARK: return ElderGUIDs[2]; - - // Mimiron - case BOSS_MIMIRON: - return MimironGUID; - case DATA_LEVIATHAN_MK_II: - return MimironVehicleGUIDs[0]; - case DATA_VX_001: - return MimironVehicleGUIDs[1]; - case DATA_AERIAL_COMMAND_UNIT: - return MimironVehicleGUIDs[2]; - case DATA_COMPUTER: - return MimironComputerGUID; - case DATA_MIMIRON_WORLD_TRIGGER: - return MimironWorldTriggerGUID; - case DATA_MIMIRON_BUTTON: - return MimironButtonGUID; case BOSS_VEZAX: return VezaxGUID; diff --git a/src/server/scripts/Northrend/Ulduar/Ulduar/ulduar.h b/src/server/scripts/Northrend/Ulduar/Ulduar/ulduar.h index 1c9fa7575d..3544ff3c07 100644 --- a/src/server/scripts/Northrend/Ulduar/Ulduar/ulduar.h +++ b/src/server/scripts/Northrend/Ulduar/Ulduar/ulduar.h @@ -83,18 +83,6 @@ enum UlduarNPCs NPC_LEVIATHAN_MKII = 33432, NPC_VX_001 = 33651, NPC_AERIAL_COMMAND_UNIT = 33670, - NPC_ASSAULT_BOT = 34057, - NPC_BOMB_BOT = 33836, - NPC_JUNK_BOT = 33855, - NPC_EMERGENCY_FIRE_BOT = 34147, - NPC_FROST_BOMB = 34149, - NPC_BURST_TARGET = 34211, - NPC_FLAME = 34363, - NPC_FLAME_SPREAD = 34121, - NPC_DB_TARGET = 33576, - NPC_ROCKET_MIMIRON_VISUAL = 34050, - NPC_WORLD_TRIGGER_MIMIRON = 21252, - NPC_COMPUTER = 34143, // Freya's Keepers NPC_IRONBRANCH = 32913, @@ -216,18 +204,6 @@ enum UlduarGameObjects GO_THORIM_CHEST_HERO = 194315, GO_THORIM_CHEST = 194314, - // Mimiron - GO_MIMIRON_TRAM = 194675, - GO_MIMIRON_ELEVATOR = 194749, - GO_MIMIRON_BUTTON = 194739, - GO_MIMIRON_DOOR_1 = 194774, - GO_MIMIRON_DOOR_2 = 194775, - GO_MIMIRON_DOOR_3 = 194776, - GO_CACHE_OF_INNOVATION = 194789, - GO_CACHE_OF_INNOVATION_FIREFIGHTER = 194957, - GO_CACHE_OF_INNOVATION_HERO = 194956, - GO_CACHE_OF_INNOVATION_FIREFIGHTER_HERO = 194958, - // Vezax GO_VEZAX_DOOR = 194750, @@ -316,16 +292,6 @@ enum UlduarData // Hodir DATA_HODIR_RARE_CACHE, - // Mimiron - DATA_LEVIATHAN_MK_II, - DATA_VX_001, - DATA_AERIAL_COMMAND_UNIT, - DATA_COMPUTER, - DATA_MIMIRON_WORLD_TRIGGER, - DATA_MIMIRON_ELEVATOR, - DATA_MIMIRON_TRAM, - DATA_MIMIRON_BUTTON, - // Yogg-Saron DATA_VOICE_OF_YOGG_SARON, DATA_SARA, From f3852d19e7cc08ce827692c0bf590fa32ed09e5b Mon Sep 17 00:00:00 2001 From: jackpoz Date: Sat, 28 Jun 2014 17:53:17 +0200 Subject: [PATCH 53/80] COre/Mail: Don't allow to mark delete mails as read. --- src/server/game/Handlers/MailHandler.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/server/game/Handlers/MailHandler.cpp b/src/server/game/Handlers/MailHandler.cpp index 326d198903..ba363ced39 100644 --- a/src/server/game/Handlers/MailHandler.cpp +++ b/src/server/game/Handlers/MailHandler.cpp @@ -345,7 +345,7 @@ void WorldSession::HandleMailMarkAsRead(WorldPacket& recvData) Player* player = _player; Mail* m = player->GetMail(mailId); - if (m) + if (m && m->state != MAIL_STATE_DELETED) { if (player->unReadMails) --player->unReadMails; From 82df3f090bb80ce045520786549b6370dcdc5874 Mon Sep 17 00:00:00 2001 From: jackpoz Date: Sat, 28 Jun 2014 17:55:40 +0200 Subject: [PATCH 54/80] Auth/Misc: Move OpenSSL version log from Warning to Information --- src/server/authserver/Main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/server/authserver/Main.cpp b/src/server/authserver/Main.cpp index be6623c7fa..9f96d21e55 100644 --- a/src/server/authserver/Main.cpp +++ b/src/server/authserver/Main.cpp @@ -115,7 +115,7 @@ extern int main(int argc, char** argv) TC_LOG_INFO("server.authserver", " to stop.\n"); TC_LOG_INFO("server.authserver", "Using configuration file %s.", configFile); - TC_LOG_WARN("server.authserver", "%s (Library: %s)", OPENSSL_VERSION_TEXT, SSLeay_version(SSLEAY_VERSION)); + TC_LOG_INFO("server.authserver", "%s (Library: %s)", OPENSSL_VERSION_TEXT, SSLeay_version(SSLEAY_VERSION)); #if defined (ACE_HAS_EVENT_POLL) || defined (ACE_HAS_DEV_POLL) ACE_Reactor::instance(new ACE_Reactor(new ACE_Dev_Poll_Reactor(ACE::max_handles(), 1), 1), true); From e77c6fc98b705ecf3aed873d1f29b7434f2f48ca Mon Sep 17 00:00:00 2001 From: jackpoz Date: Sat, 28 Jun 2014 18:23:10 +0200 Subject: [PATCH 55/80] Core/Mails: Fix exploit that allowed to take COD items without paying --- src/server/game/Handlers/MailHandler.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/server/game/Handlers/MailHandler.cpp b/src/server/game/Handlers/MailHandler.cpp index ba363ced39..8ce2100acc 100644 --- a/src/server/game/Handlers/MailHandler.cpp +++ b/src/server/game/Handlers/MailHandler.cpp @@ -469,6 +469,13 @@ void WorldSession::HandleMailTakeItem(WorldPacket& recvData) return; } + // verify that the mail has the item to avoid cheaters taking COD items without paying + if (std::find_if(m->items.begin(), m->items.end(), [itemId](MailItemInfo info){ return info.item_guid == itemId; }) == m->items.end()) + { + player->SendMailResult(mailId, MAIL_ITEM_TAKEN, MAIL_ERR_INTERNAL_ERROR); + return; + } + // prevent cheating with skip client money check if (!player->HasEnoughMoney(m->COD)) { From b9ec488fd16440690c2f82b4f34256476b0a9fa4 Mon Sep 17 00:00:00 2001 From: jackpoz Date: Sat, 28 Jun 2014 19:15:20 +0200 Subject: [PATCH 56/80] Core/Mail: Do not allow to create multiple copies of mail --- src/server/game/Handlers/MailHandler.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/server/game/Handlers/MailHandler.cpp b/src/server/game/Handlers/MailHandler.cpp index 8ce2100acc..ffcf74ea7c 100644 --- a/src/server/game/Handlers/MailHandler.cpp +++ b/src/server/game/Handlers/MailHandler.cpp @@ -724,7 +724,7 @@ void WorldSession::HandleMailCreateTextItem(WorldPacket& recvData) Player* player = _player; Mail* m = player->GetMail(mailId); - if (!m || (m->body.empty() && !m->mailTemplateId) || m->state == MAIL_STATE_DELETED || m->deliver_time > time(NULL)) + if (!m || (m->body.empty() && !m->mailTemplateId) || m->state == MAIL_STATE_DELETED || m->deliver_time > time(NULL) || (m->checked & MAIL_CHECK_MASK_COPIED)) { player->SendMailResult(mailId, MAIL_MADE_PERMANENT, MAIL_ERR_INTERNAL_ERROR); return; From 95f864f4adcebc759d38e4fcd31c8fe3187beb8d Mon Sep 17 00:00:00 2001 From: Sebastian Valle Herrera Date: Sat, 28 Jun 2014 13:19:00 -0500 Subject: [PATCH 57/80] Core/Creatures: Fixed a typo in respawn code Creatures should now respawn correctly in their correct time. Closes #12378 --- src/server/game/Entities/Creature/Creature.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/server/game/Entities/Creature/Creature.cpp b/src/server/game/Entities/Creature/Creature.cpp index 19d32b41c9..fd8aaff815 100644 --- a/src/server/game/Entities/Creature/Creature.cpp +++ b/src/server/game/Entities/Creature/Creature.cpp @@ -2287,7 +2287,7 @@ void Creature::AllLootRemovedFromCorpse() else m_corpseRemoveTime = now + m_corpseDelay * decayRate; - m_respawnTime = m_corpseRemoveTime + m_respawnTime; + m_respawnTime = m_corpseRemoveTime + m_respawnDelay; } uint8 Creature::getLevelForTarget(WorldObject const* target) const From a36925937a9edee0afc3afd49440cae4ae9a73d1 Mon Sep 17 00:00:00 2001 From: Aokromes Date: Sat, 28 Jun 2014 22:04:46 +0200 Subject: [PATCH 58/80] DB/Misc: Add conditions to one mage trainer By dr-j. --- sql/updates/world/2014_06_28_04_world_misc.sql | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 sql/updates/world/2014_06_28_04_world_misc.sql diff --git a/sql/updates/world/2014_06_28_04_world_misc.sql b/sql/updates/world/2014_06_28_04_world_misc.sql new file mode 100644 index 0000000000..4c5c25e7df --- /dev/null +++ b/sql/updates/world/2014_06_28_04_world_misc.sql @@ -0,0 +1,8 @@ +-- +DELETE FROM `gossip_menu_option` WHERE `menu_id`=4824; +INSERT INTO `gossip_menu_option` (`menu_id`, `id`, `option_icon`, `option_text`, `OptionBroadcastTextID`, `option_id`, `npc_option_npcflag`, `action_menu_id`, `action_poi_id`, `box_coded`, `box_money`, `box_text`, `BoxBroadcastTextID`) VALUES +(4824, 0, 3, 'Please teach me.', 8442, 5, 16, 0, 0, 0, 0, '', 0); + +DELETE FROM `conditions` WHERE `SourceTypeOrReferenceId`=15 AND `SourceGroup`=4824; +INSERT INTO `conditions` (`SourceTypeOrReferenceId`, `SourceGroup`, `SourceEntry`, `SourceId`, `ElseGroup`, `ConditionTypeOrReference`, `ConditionTarget`, `ConditionValue1`, `ConditionValue2`, `ConditionValue3`, `NegativeCondition`, `ErrorType`, `ErrorTextId`, `ScriptName`, `Comment`) VALUES +(15, 4824, 0, 0, 0, 15, 0, 128, 0, 0, 0, 0, 0, '', 'Show gossip option if player is a mage'); From 8556d00c9c87a22b0c2cead3f268cf3c87e36967 Mon Sep 17 00:00:00 2001 From: Aokromes Date: Sun, 29 Jun 2014 05:17:07 +0200 Subject: [PATCH 59/80] DB/Misc: Add gossip_menu_options and conditions to multiple trainers By dr-j --- .../world/2014_06_29_00_world_misc.sql | 94 +++++++++++++++++++ 1 file changed, 94 insertions(+) create mode 100644 sql/updates/world/2014_06_29_00_world_misc.sql diff --git a/sql/updates/world/2014_06_29_00_world_misc.sql b/sql/updates/world/2014_06_29_00_world_misc.sql new file mode 100644 index 0000000000..33e3bf7466 --- /dev/null +++ b/sql/updates/world/2014_06_29_00_world_misc.sql @@ -0,0 +1,94 @@ +-- Lunaraa/ +DELETE FROM `gossip_menu_option` WHERE `menu_id`=4824; +INSERT INTO `gossip_menu_option` (`menu_id`, `id`, `option_icon`, `option_text`, `OptionBroadcastTextID`, `option_id`, `npc_option_npcflag`, `action_menu_id`, `action_poi_id`, `box_coded`, `box_money`, `box_text`, `BoxBroadcastTextID`) VALUES +(4824, 0, 3, 'Please teach me.', 8442, 5, 16, 0, 0, 0, 0, '', 0); + +DELETE FROM `conditions` WHERE `SourceTypeOrReferenceId`=15 AND `SourceGroup`=4824; +INSERT INTO `conditions` (`SourceTypeOrReferenceId`, `SourceGroup`, `SourceEntry`, `SourceId`, `ElseGroup`, `ConditionTypeOrReference`, `ConditionTarget`, `ConditionValue1`, `ConditionValue2`, `ConditionValue3`, `NegativeCondition`, `ErrorType`, `ErrorTextId`, `ScriptName`, `Comment`) VALUES +(15, 4824, 0, 0, 0, 15, 0, 128, 0, 0, 0, 0, 0, '', 'Show gossip option if player is a mage'); + +-- Mi'irku Farstep 8433 +DELETE FROM `gossip_menu_option` WHERE `menu_id`=8433; +INSERT INTO `gossip_menu_option` (`menu_id`, `id`, `option_icon`, `option_text`, `OptionBroadcastTextID`, `option_id`, `npc_option_npcflag`, `action_menu_id`, `action_poi_id`, `box_coded`, `box_money`, `box_text`, `BoxBroadcastTextID`) VALUES +(8433, 0, 3, 'Please teach me.', 8442, 5, 16, 0, 0, 0, 0, '', 0); + +DELETE FROM `conditions` WHERE `SourceTypeOrReferenceId`=15 AND `SourceGroup`=8433; +INSERT INTO `conditions` (`SourceTypeOrReferenceId`, `SourceGroup`, `SourceEntry`, `SourceId`, `ElseGroup`, `ConditionTypeOrReference`, `ConditionTarget`, `ConditionValue1`, `ConditionValue2`, `ConditionValue3`, `NegativeCondition`, `ErrorType`, `ErrorTextId`, `ScriptName`, `Comment`) VALUES +(15, 8433, 0, 0, 0, 15, 0, 128, 0, 0, 0, 0, 0, '', 'Show gossip option if player is a mage'); + +-- Ursyn Ghull +DELETE FROM `gossip_menu_option` WHERE `menu_id`=4536; +INSERT INTO `gossip_menu_option` (`menu_id`, `id`, `option_icon`, `option_text`, `OptionBroadcastTextID`, `option_id`, `npc_option_npcflag`, `action_menu_id`, `action_poi_id`, `box_coded`, `box_money`, `box_text`, `BoxBroadcastTextID`) VALUES +(4536, 0, 3, 'Please teach me.', 8442, 5, 16, 0, 0, 0, 0, '', 0); + +DELETE FROM `conditions` WHERE `SourceTypeOrReferenceId`=15 AND `SourceGroup`=4536; +INSERT INTO `conditions` (`SourceTypeOrReferenceId`, `SourceGroup`, `SourceEntry`, `SourceId`, `ElseGroup`, `ConditionTypeOrReference`, `ConditionTarget`, `ConditionValue1`, `ConditionValue2`, `ConditionValue3`, `NegativeCondition`, `ErrorType`, `ErrorTextId`, `ScriptName`, `Comment`) VALUES +(15, 4536, 0, 0, 0, 15, 0, 128, 0, 0, 0, 0, 0, '', 'Show gossip option if player is a mage'); + +-- Theridran +DELETE FROM `gossip_menu_option` WHERE `menu_id`=21228; +INSERT INTO `gossip_menu_option` (`menu_id`, `id`, `option_icon`, `option_text`, `OptionBroadcastTextID`, `option_id`, `npc_option_npcflag`, `action_menu_id`, `action_poi_id`, `box_coded`, `box_money`, `box_text`, `BoxBroadcastTextID`) VALUES +(21228, 0, 3, 'Please teach me.', 8442, 5, 16, 0, 0, 0, 0, '', 0); + +DELETE FROM `conditions` WHERE `SourceTypeOrReferenceId`=15 AND `SourceGroup`=21228; +INSERT INTO `conditions` (`SourceTypeOrReferenceId`, `SourceGroup`, `SourceEntry`, `SourceId`, `ElseGroup`, `ConditionTypeOrReference`, `ConditionTarget`, `ConditionValue1`, `ConditionValue2`, `ConditionValue3`, `NegativeCondition`, `ErrorType`, `ErrorTextId`, `ScriptName`, `Comment`) VALUES +(15, 21228, 0, 0, 0, 15, 0, 1024, 0, 0, 0, 0, 0, '', 'Show gossip option if player is a druid'); + +-- Larimaine Purdue +DELETE FROM `conditions` WHERE `SourceTypeOrReferenceId`=15 AND `SourceGroup`=4822; +INSERT INTO `conditions` (`SourceTypeOrReferenceId`, `SourceGroup`, `SourceEntry`, `SourceId`, `ElseGroup`, `ConditionTypeOrReference`, `ConditionTarget`, `ConditionValue1`, `ConditionValue2`, `ConditionValue3`, `NegativeCondition`, `ErrorType`, `ErrorTextId`, `ScriptName`, `Comment`) VALUES +(15, 4822, 0, 0, 0, 15, 0, 128, 0, 0, 0, 0, 0, '', 'Show gossip option if player is a mage'); + +-- Elissa Dumas +DELETE FROM `gossip_menu_option` WHERE `menu_id`=4821; +INSERT INTO `gossip_menu_option` (`menu_id`, `id`, `option_icon`, `option_text`, `OptionBroadcastTextID`, `option_id`, `npc_option_npcflag`, `action_menu_id`, `action_poi_id`, `box_coded`, `box_money`, `box_text`, `BoxBroadcastTextID`) VALUES +(4821, 0, 3, 'Please teach me.', 8442, 5, 16, 0, 0, 0, 0, '', 0); + +DELETE FROM `conditions` WHERE `SourceTypeOrReferenceId`=15 AND `SourceGroup`=4821; +INSERT INTO `conditions` (`SourceTypeOrReferenceId`, `SourceGroup`, `SourceEntry`, `SourceId`, `ElseGroup`, `ConditionTypeOrReference`, `ConditionTarget`, `ConditionValue1`, `ConditionValue2`, `ConditionValue3`, `NegativeCondition`, `ErrorType`, `ErrorTextId`, `ScriptName`, `Comment`) VALUES +(15, 4821, 0, 0, 0, 15, 0, 128, 0, 0, 0, 0, 0, '', 'Show gossip option if player is a mage'); + +-- Iorioa +DELETE FROM `gossip_menu_option` WHERE `menu_id`=8268; +INSERT INTO `gossip_menu_option` (`menu_id`, `id`, `option_icon`, `option_text`, `OptionBroadcastTextID`, `option_id`, `npc_option_npcflag`, `action_menu_id`, `action_poi_id`, `box_coded`, `box_money`, `box_text`, `BoxBroadcastTextID`) VALUES +(8268, 0, 3, 'Please teach me.', 8442, 5, 16, 0, 0, 0, 0, '', 0); + +DELETE FROM `conditions` WHERE `SourceTypeOrReferenceId`=15 AND `SourceGroup`=8268; +INSERT INTO `conditions` (`SourceTypeOrReferenceId`, `SourceGroup`, `SourceEntry`, `SourceId`, `ElseGroup`, `ConditionTypeOrReference`, `ConditionTarget`, `ConditionValue1`, `ConditionValue2`, `ConditionValue3`, `NegativeCondition`, `ErrorType`, `ErrorTextId`, `ScriptName`, `Comment`) VALUES +(15, 8268, 0, 0, 0, 15, 0, 128, 0, 0, 0, 0, 0, '', 'Show gossip option if player is a mage'); + +-- Vindicator Aesom +DELETE FROM `gossip_menu_option` WHERE `menu_id`=7517; +INSERT INTO `gossip_menu_option` (`menu_id`, `id`, `option_icon`, `option_text`, `OptionBroadcastTextID`, `option_id`, `npc_option_npcflag`, `action_menu_id`, `action_poi_id`, `box_coded`, `box_money`, `box_text`, `BoxBroadcastTextID`) VALUES +(7517, 0, 3, 'Please teach me.', 8442, 5, 16, 0, 0, 0, 0, '', 0); + +DELETE FROM `conditions` WHERE `SourceTypeOrReferenceId`=15 AND `SourceGroup`=7517; +INSERT INTO `conditions` (`SourceTypeOrReferenceId`, `SourceGroup`, `SourceEntry`, `SourceId`, `ElseGroup`, `ConditionTypeOrReference`, `ConditionTarget`, `ConditionValue1`, `ConditionValue2`, `ConditionValue3`, `NegativeCondition`, `ErrorType`, `ErrorTextId`, `ScriptName`, `Comment`) VALUES +(15, 7517, 0, 0, 0, 15, 0, 2, 0, 0, 0, 0, 0, '', 'Show gossip option if player is a paladin'); + +-- Lexington Mortaim +DELETE FROM `gossip_menu_option` WHERE `menu_id`=7517; +INSERT INTO `gossip_menu_option` (`menu_id`, `id`, `option_icon`, `option_text`, `OptionBroadcastTextID`, `option_id`, `npc_option_npcflag`, `action_menu_id`, `action_poi_id`, `box_coded`, `box_money`, `box_text`, `BoxBroadcastTextID`) VALUES +(7517, 0, 3, 'Please teach me.', 8442, 5, 16, 0, 0, 0, 0, '', 0); + +DELETE FROM `conditions` WHERE `SourceTypeOrReferenceId`=15 AND `SourceGroup`=7517; +INSERT INTO `conditions` (`SourceTypeOrReferenceId`, `SourceGroup`, `SourceEntry`, `SourceId`, `ElseGroup`, `ConditionTypeOrReference`, `ConditionTarget`, `ConditionValue1`, `ConditionValue2`, `ConditionValue3`, `NegativeCondition`, `ErrorType`, `ErrorTextId`, `ScriptName`, `Comment`) VALUES +(15, 7517, 0, 0, 0, 15, 0, 2, 0, 0, 0, 0, 0, '', 'Show gossip option if player is a paladin'); + +-- Lexington Mortaim +DELETE FROM `gossip_menu_option` WHERE `menu_id`=4827; +INSERT INTO `gossip_menu_option` (`menu_id`, `id`, `option_icon`, `option_text`, `OptionBroadcastTextID`, `option_id`, `npc_option_npcflag`, `action_menu_id`, `action_poi_id`, `box_coded`, `box_money`, `box_text`, `BoxBroadcastTextID`) VALUES +(4827, 0, 3, 'Please teach me.', 8442, 5, 16, 0, 0, 0, 0, '', 0); + +DELETE FROM `conditions` WHERE `SourceTypeOrReferenceId`=15 AND `SourceGroup`=4827; +INSERT INTO `conditions` (`SourceTypeOrReferenceId`, `SourceGroup`, `SourceEntry`, `SourceId`, `ElseGroup`, `ConditionTypeOrReference`, `ConditionTarget`, `ConditionValue1`, `ConditionValue2`, `ConditionValue3`, `NegativeCondition`, `ErrorType`, `ErrorTextId`, `ScriptName`, `Comment`) VALUES +(15, 4827, 0, 0, 0, 15, 0, 128, 0, 0, 0, 0, 0, '', 'Show gossip option if player is a mage'); + +-- Brother Sammuel +DELETE FROM `gossip_menu_option` WHERE `menu_id`=4663; +INSERT INTO `gossip_menu_option` (`menu_id`, `id`, `option_icon`, `option_text`, `OptionBroadcastTextID`, `option_id`, `npc_option_npcflag`, `action_menu_id`, `action_poi_id`, `box_coded`, `box_money`, `box_text`, `BoxBroadcastTextID`) VALUES +(4663, 0, 3, 'Please teach me.', 8442, 5, 16, 0, 0, 0, 0, '', 0); + +DELETE FROM `conditions` WHERE `SourceTypeOrReferenceId`=15 AND `SourceGroup`=4663; +INSERT INTO `conditions` (`SourceTypeOrReferenceId`, `SourceGroup`, `SourceEntry`, `SourceId`, `ElseGroup`, `ConditionTypeOrReference`, `ConditionTarget`, `ConditionValue1`, `ConditionValue2`, `ConditionValue3`, `NegativeCondition`, `ErrorType`, `ErrorTextId`, `ScriptName`, `Comment`) VALUES +(15, 4663, 0, 0, 0, 15, 0, 2, 0, 0, 0, 0, 0, '', 'Show gossip option if player is a paladin'); From f8720d5be0301ce123af28277d936961e7fa2c43 Mon Sep 17 00:00:00 2001 From: Aokromes Date: Sun, 29 Jun 2014 10:11:26 +0200 Subject: [PATCH 60/80] DB/Quest: Added the proper special flags to one quest --- sql/updates/world/2014_06_29_01_world_quest_template.sql | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 sql/updates/world/2014_06_29_01_world_quest_template.sql diff --git a/sql/updates/world/2014_06_29_01_world_quest_template.sql b/sql/updates/world/2014_06_29_01_world_quest_template.sql new file mode 100644 index 0000000000..509b947997 --- /dev/null +++ b/sql/updates/world/2014_06_29_01_world_quest_template.sql @@ -0,0 +1,2 @@ +-- +UPDATE `quest_template` SET `SpecialFlags`=2 WHERE `Id` = 12274; From ee4602db3d40f061b9ce69445359f47aaf78f278 Mon Sep 17 00:00:00 2001 From: jackpoz Date: Sun, 29 Jun 2014 14:03:29 +0200 Subject: [PATCH 61/80] Core/Battleground: Fix crash added in c28345e279f31af2fa32df2411325ccfabe06fc8 Fix a double-delete crash added in c28345e279f31af2fa32df2411325ccfabe06fc8 caused by copy-constructor copying pointers across different Battleground instances. Valgrind log: Invalid read of size 8 at : Battleground::~Battleground() (Battleground.cpp:231) by : BattlegroundWS::~BattlegroundWS() (BattlegroundWS.cpp:74) by : BattlegroundWS::~BattlegroundWS() (BattlegroundWS.cpp:74) by : BattlegroundMgr::Update(unsigned int) (BattlegroundMgr.cpp:103) by : World::Update(unsigned int) (World.cpp:2067) Address 0x5675d140 is 0 bytes inside a block of size 24 free'd at : operator delete(void*) (vg_replace_malloc.c:509) by : ArenaTeamScore::~ArenaTeamScore() (ArenaScore.h:64) by : Battleground::~Battleground() (Battleground.cpp:231) by : BattlegroundWS::~BattlegroundWS() (BattlegroundWS.cpp:74) by : BattlegroundWS::~BattlegroundWS() (BattlegroundWS.cpp:74) by : BattlegroundMgr::Update(unsigned int) (BattlegroundMgr.cpp:103) --- src/server/game/Battlegrounds/ArenaScore.h | 36 -------------- .../game/Battlegrounds/Battleground.cpp | 23 ++++----- src/server/game/Battlegrounds/Battleground.h | 48 ++++++++++++++++++- 3 files changed, 55 insertions(+), 52 deletions(-) diff --git a/src/server/game/Battlegrounds/ArenaScore.h b/src/server/game/Battlegrounds/ArenaScore.h index 7b25427c07..3b976a0a0d 100644 --- a/src/server/game/Battlegrounds/ArenaScore.h +++ b/src/server/game/Battlegrounds/ArenaScore.h @@ -54,40 +54,4 @@ struct ArenaScore : public BattlegroundScore uint8 TeamId; // TEAM_ALLIANCE or TEAM_HORDE }; -struct ArenaTeamScore -{ - friend class Battleground; - - protected: - ArenaTeamScore() : RatingChange(0), MatchmakerRating(0) { } - - virtual ~ArenaTeamScore() { } - - void Assign(int32 ratingChange, uint32 matchMakerRating, std::string const& teamName) - { - RatingChange = ratingChange; - MatchmakerRating = matchMakerRating; - TeamName = teamName; - } - - void BuildRatingInfoBlock(WorldPacket& data) - { - uint32 ratingLost = std::abs(std::min(RatingChange, 0)); - uint32 ratingWon = std::max(RatingChange, 0); - - data << uint32(ratingLost); - data << uint32(ratingWon); - data << uint32(MatchmakerRating); - } - - void BuildTeamInfoBlock(WorldPacket& data) - { - data << TeamName; - } - - int32 RatingChange; - uint32 MatchmakerRating; - std::string TeamName; -}; - #endif // TRINITY_ARENA_SCORE_H diff --git a/src/server/game/Battlegrounds/Battleground.cpp b/src/server/game/Battlegrounds/Battleground.cpp index fd3e13cf58..d840c351d1 100644 --- a/src/server/game/Battlegrounds/Battleground.cpp +++ b/src/server/game/Battlegrounds/Battleground.cpp @@ -170,10 +170,6 @@ Battleground::Battleground() m_ArenaTeamMMR[TEAM_ALLIANCE] = 0; m_ArenaTeamMMR[TEAM_HORDE] = 0; - // Iterate this way for consistency's sake - client expects it to be sent in this order - for (int8 i = WINNER_ALLIANCE; i >= WINNER_HORDE; --i) - _arenaTeamScores[i] = new ArenaTeamScore(); - m_BgRaids[TEAM_ALLIANCE] = NULL; m_BgRaids[TEAM_HORDE] = NULL; @@ -225,10 +221,6 @@ Battleground::~Battleground() for (BattlegroundScoreMap::const_iterator itr = PlayerScores.begin(); itr != PlayerScores.end(); ++itr) delete itr->second; - - // Iterate this way for consistency's sake - client expects it to be sent in this order - for (int8 i = WINNER_ALLIANCE; i >= WINNER_HORDE; --i) - delete _arenaTeamScores[i]; } void Battleground::Update(uint32 diff) @@ -795,8 +787,8 @@ void Battleground::EndBattleground(uint32 winner) uint8 winnerId = GetWinner(); uint8 loserId = winnerId == WINNER_ALLIANCE ? uint8(WINNER_HORDE) : winnerId; - _arenaTeamScores[winnerId]->Assign(winnerChange, winnerMatchmakerRating + winnerMatchmakerChange, winnerArenaTeam->GetName()); - _arenaTeamScores[loserId]->Assign(loserChange, loserMatchmakerRating + loserMatchmakerChange, loserArenaTeam->GetName()); + _arenaTeamScores[winnerId].Assign(winnerChange, winnerMatchmakerRating + winnerMatchmakerChange, winnerArenaTeam->GetName()); + _arenaTeamScores[loserId].Assign(loserChange, loserMatchmakerRating + loserMatchmakerChange, loserArenaTeam->GetName()); TC_LOG_DEBUG("bg.arena", "Arena match Type: %u for Team1Id: %u - Team2Id: %u ended. WinnerTeamId: %u. Winner rating: +%d, Loser rating: %d", m_ArenaType, m_ArenaTeamIds[TEAM_ALLIANCE], m_ArenaTeamIds[TEAM_HORDE], winnerArenaTeam->GetId(), winnerChange, loserChange); if (sWorld->getBoolConfig(CONFIG_ARENA_LOG_EXTENDED_INFO)) @@ -811,8 +803,8 @@ void Battleground::EndBattleground(uint32 winner) // Deduct 16 points from each teams arena-rating if there are no winners after 45+2 minutes else { - _arenaTeamScores[WINNER_ALLIANCE]->Assign(ARENA_TIMELIMIT_POINTS_LOSS, winnerMatchmakerRating + winnerMatchmakerChange, winnerArenaTeam->GetName()); - _arenaTeamScores[WINNER_HORDE]->Assign(ARENA_TIMELIMIT_POINTS_LOSS, loserMatchmakerRating + loserMatchmakerChange, loserArenaTeam->GetName()); + _arenaTeamScores[WINNER_ALLIANCE].Assign(ARENA_TIMELIMIT_POINTS_LOSS, winnerMatchmakerRating + winnerMatchmakerChange, winnerArenaTeam->GetName()); + _arenaTeamScores[WINNER_HORDE].Assign(ARENA_TIMELIMIT_POINTS_LOSS, loserMatchmakerRating + loserMatchmakerChange, loserArenaTeam->GetName()); winnerArenaTeam->FinishGame(ARENA_TIMELIMIT_POINTS_LOSS); loserArenaTeam->FinishGame(ARENA_TIMELIMIT_POINTS_LOSS); @@ -1107,6 +1099,9 @@ void Battleground::Reset() delete itr->second; PlayerScores.clear(); + for (int8 i = WINNER_ALLIANCE; i >= WINNER_HORDE; --i) + _arenaTeamScores[i].Reset(); + ResetBGSubclass(); } @@ -1368,10 +1363,10 @@ void Battleground::BuildPvPLogDataPacket(WorldPacket& data) { // it seems this must be according to BG_WINNER_A/H and _NOT_ TEAM_A/H for (int8 i = WINNER_ALLIANCE; i >= WINNER_HORDE; --i) - _arenaTeamScores[i]->BuildRatingInfoBlock(data); + _arenaTeamScores[i].BuildRatingInfoBlock(data); for (int8 i = WINNER_ALLIANCE; i >= WINNER_HORDE; --i) - _arenaTeamScores[i]->BuildTeamInfoBlock(data); + _arenaTeamScores[i].BuildTeamInfoBlock(data); } if (GetStatus() == STATUS_WAIT_LEAVE) diff --git a/src/server/game/Battlegrounds/Battleground.h b/src/server/game/Battlegrounds/Battleground.h index 7d1bf55069..e353cd1dc6 100644 --- a/src/server/game/Battlegrounds/Battleground.h +++ b/src/server/game/Battlegrounds/Battleground.h @@ -22,6 +22,7 @@ #include "Common.h" #include "SharedDefines.h" #include "DBCEnums.h" +#include "WorldPacket.h" class Creature; class GameObject; @@ -32,7 +33,6 @@ class WorldObject; class WorldPacket; class BattlegroundMap; -struct ArenaTeamScore; struct BattlegroundScore; struct Position; struct PvPDifficultyEntry; @@ -611,7 +611,51 @@ class Battleground uint32 m_ArenaTeamIds[BG_TEAMS_COUNT]; uint32 m_ArenaTeamMMR[BG_TEAMS_COUNT]; - ArenaTeamScore* _arenaTeamScores[BG_TEAMS_COUNT]; + + struct ArenaTeamScore + { + friend class Battleground; + + protected: + ArenaTeamScore() : RatingChange(0), MatchmakerRating(0) { } + + virtual ~ArenaTeamScore() { } + + void Assign(int32 ratingChange, uint32 matchMakerRating, std::string const& teamName) + { + RatingChange = ratingChange; + MatchmakerRating = matchMakerRating; + TeamName = teamName; + } + + void BuildRatingInfoBlock(WorldPacket& data) + { + uint32 ratingLost = std::abs(std::min(RatingChange, 0)); + uint32 ratingWon = std::max(RatingChange, 0); + + data << uint32(ratingLost); + data << uint32(ratingWon); + data << uint32(MatchmakerRating); + } + + void BuildTeamInfoBlock(WorldPacket& data) + { + data << TeamName; + } + + void Reset() + { + RatingChange = 0; + MatchmakerRating = 0; + TeamName.clear(); + } + + int32 RatingChange; + uint32 MatchmakerRating; + std::string TeamName; + }; + + ArenaTeamScore _arenaTeamScores[BG_TEAMS_COUNT]; // Limits uint32 m_LevelMin; From c674f229cb3d9abc723df9e7f87ffb15c9655cca Mon Sep 17 00:00:00 2001 From: jackpoz Date: Sun, 29 Jun 2014 15:51:34 +0200 Subject: [PATCH 62/80] Core/Mail: Fix exploit that didn't allow recipient to delete the email --- src/server/game/Handlers/MailHandler.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/server/game/Handlers/MailHandler.cpp b/src/server/game/Handlers/MailHandler.cpp index ffcf74ea7c..1270f4e641 100644 --- a/src/server/game/Handlers/MailHandler.cpp +++ b/src/server/game/Handlers/MailHandler.cpp @@ -322,6 +322,10 @@ void WorldSession::HandleSendMail(WorldPacket& recvData) // If theres is an item, there is a one hour delivery delay if sent to another account's character. uint32 deliver_delay = needItemDelay ? sWorld->getIntConfig(CONFIG_MAIL_DELIVERY_DELAY) : 0; + // don't ask for COD if there are no items + if (items_count == 0) + COD = 0; + // will delete item or place to receiver mail list draft .AddMoney(money) From 876e9dde01265edef31eb28167dd82797800bb1f Mon Sep 17 00:00:00 2001 From: joschiwald Date: Sun, 29 Jun 2014 16:53:28 +0200 Subject: [PATCH 63/80] Core/Battleground: addition to c28345e279f31af2fa32df2411325ccfabe06fc8 --- src/server/game/Battlegrounds/ArenaScore.h | 8 ++++++- .../game/Battlegrounds/Battleground.cpp | 23 ++++++++++--------- src/server/game/Battlegrounds/Battleground.h | 1 + .../game/Battlegrounds/BattlegroundScore.h | 2 +- 4 files changed, 21 insertions(+), 13 deletions(-) diff --git a/src/server/game/Battlegrounds/ArenaScore.h b/src/server/game/Battlegrounds/ArenaScore.h index 3b976a0a0d..fdeb13adc3 100644 --- a/src/server/game/Battlegrounds/ArenaScore.h +++ b/src/server/game/Battlegrounds/ArenaScore.h @@ -40,6 +40,12 @@ struct ArenaScore : public BattlegroundScore data << uint8(TeamId); data << uint32(DamageDone); data << uint32(HealingDone); + + BuildObjectivesBlock(data); + } + + void BuildObjectivesBlock(WorldPacket& data) final + { data << uint32(0); // Objectives Count } @@ -51,7 +57,7 @@ struct ArenaScore : public BattlegroundScore return stream.str(); } - uint8 TeamId; // TEAM_ALLIANCE or TEAM_HORDE + uint8 TeamId; // bgTeamId }; #endif // TRINITY_ARENA_SCORE_H diff --git a/src/server/game/Battlegrounds/Battleground.cpp b/src/server/game/Battlegrounds/Battleground.cpp index d840c351d1..4e077979b3 100644 --- a/src/server/game/Battlegrounds/Battleground.cpp +++ b/src/server/game/Battlegrounds/Battleground.cpp @@ -755,7 +755,7 @@ void Battleground::EndBattleground(uint32 winner) } else { - SetWinner(3); + SetWinner(3); // weird } SetStatus(STATUS_WAIT_LEAVE); @@ -784,11 +784,13 @@ void Battleground::EndBattleground(uint32 winner) SetArenaMatchmakerRating(winner, winnerMatchmakerRating + winnerMatchmakerChange); SetArenaMatchmakerRating(GetOtherTeam(winner), loserMatchmakerRating + loserMatchmakerChange); - uint8 winnerId = GetWinner(); - uint8 loserId = winnerId == WINNER_ALLIANCE ? uint8(WINNER_HORDE) : winnerId; + // bg team that the client expects is different to TeamId + // alliance 1, horde 0 + uint8 winnerTeam = winner == ALLIANCE ? WINNER_ALLIANCE : WINNER_HORDE; + uint8 loserTeam = winner == ALLIANCE ? WINNER_HORDE : WINNER_ALLIANCE; - _arenaTeamScores[winnerId].Assign(winnerChange, winnerMatchmakerRating + winnerMatchmakerChange, winnerArenaTeam->GetName()); - _arenaTeamScores[loserId].Assign(loserChange, loserMatchmakerRating + loserMatchmakerChange, loserArenaTeam->GetName()); + _arenaTeamScores[winnerTeam].Assign(winnerChange, winnerMatchmakerRating, winnerArenaTeam->GetName()); + _arenaTeamScores[loserTeam].Assign(loserChange, loserMatchmakerRating, loserArenaTeam->GetName()); TC_LOG_DEBUG("bg.arena", "Arena match Type: %u for Team1Id: %u - Team2Id: %u ended. WinnerTeamId: %u. Winner rating: +%d, Loser rating: %d", m_ArenaType, m_ArenaTeamIds[TEAM_ALLIANCE], m_ArenaTeamIds[TEAM_HORDE], winnerArenaTeam->GetId(), winnerChange, loserChange); if (sWorld->getBoolConfig(CONFIG_ARENA_LOG_EXTENDED_INFO)) @@ -803,8 +805,8 @@ void Battleground::EndBattleground(uint32 winner) // Deduct 16 points from each teams arena-rating if there are no winners after 45+2 minutes else { - _arenaTeamScores[WINNER_ALLIANCE].Assign(ARENA_TIMELIMIT_POINTS_LOSS, winnerMatchmakerRating + winnerMatchmakerChange, winnerArenaTeam->GetName()); - _arenaTeamScores[WINNER_HORDE].Assign(ARENA_TIMELIMIT_POINTS_LOSS, loserMatchmakerRating + loserMatchmakerChange, loserArenaTeam->GetName()); + _arenaTeamScores[WINNER_ALLIANCE].Assign(ARENA_TIMELIMIT_POINTS_LOSS, winnerMatchmakerRating, winnerArenaTeam->GetName()); + _arenaTeamScores[WINNER_HORDE].Assign(ARENA_TIMELIMIT_POINTS_LOSS, loserMatchmakerRating, loserArenaTeam->GetName()); winnerArenaTeam->FinishGame(ARENA_TIMELIMIT_POINTS_LOSS); loserArenaTeam->FinishGame(ARENA_TIMELIMIT_POINTS_LOSS); @@ -1099,7 +1101,7 @@ void Battleground::Reset() delete itr->second; PlayerScores.clear(); - for (int8 i = WINNER_ALLIANCE; i >= WINNER_HORDE; --i) + for (uint8 i = 0; i < BG_TEAMS_COUNT; ++i) _arenaTeamScores[i].Reset(); ResetBGSubclass(); @@ -1361,11 +1363,10 @@ void Battleground::BuildPvPLogDataPacket(WorldPacket& data) if (type) // arena { - // it seems this must be according to BG_WINNER_A/H and _NOT_ TEAM_A/H - for (int8 i = WINNER_ALLIANCE; i >= WINNER_HORDE; --i) + for (uint8 i = 0; i < BG_TEAMS_COUNT; ++i) _arenaTeamScores[i].BuildRatingInfoBlock(data); - for (int8 i = WINNER_ALLIANCE; i >= WINNER_HORDE; --i) + for (uint8 i = 0; i < BG_TEAMS_COUNT; ++i) _arenaTeamScores[i].BuildTeamInfoBlock(data); } diff --git a/src/server/game/Battlegrounds/Battleground.h b/src/server/game/Battlegrounds/Battleground.h index e353cd1dc6..2904ec1dc7 100644 --- a/src/server/game/Battlegrounds/Battleground.h +++ b/src/server/game/Battlegrounds/Battleground.h @@ -633,6 +633,7 @@ class Battleground uint32 ratingLost = std::abs(std::min(RatingChange, 0)); uint32 ratingWon = std::max(RatingChange, 0); + // should be old rating, new rating, and client will calculate rating change itself data << uint32(ratingLost); data << uint32(ratingWon); data << uint32(MatchmakerRating); diff --git a/src/server/game/Battlegrounds/BattlegroundScore.h b/src/server/game/Battlegrounds/BattlegroundScore.h index 7fedc4dbc4..ae65721b51 100644 --- a/src/server/game/Battlegrounds/BattlegroundScore.h +++ b/src/server/game/Battlegrounds/BattlegroundScore.h @@ -101,7 +101,7 @@ struct BattlegroundScore BuildObjectivesBlock(data); } - virtual void BuildObjectivesBlock(WorldPacket& /*data*/) { } + virtual void BuildObjectivesBlock(WorldPacket& /*data*/) = 0; // For Logging purpose virtual std::string ToString() const { return ""; } From ef8549c6a680f91d3bf44b024da94346d213512c Mon Sep 17 00:00:00 2001 From: Aokromes Date: Sun, 29 Jun 2014 18:42:49 +0200 Subject: [PATCH 64/80] DB/Misc: correct some trinity_string encoding By easy1, closes #11912 --- .../world/ \t2014_06_29_02_world_misc.sql" | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 "sql/updates/world/ \t2014_06_29_02_world_misc.sql" diff --git "a/sql/updates/world/ \t2014_06_29_02_world_misc.sql" "b/sql/updates/world/ \t2014_06_29_02_world_misc.sql" new file mode 100644 index 0000000000..dd99a1f4be --- /dev/null +++ "b/sql/updates/world/ \t2014_06_29_02_world_misc.sql" @@ -0,0 +1,27 @@ +-- +SET NAMES 'utf8'; +DELETE FROM `trinity_string` WHERE entry IN (453, 548, 549, 550, 714, 716, 749, 752, 843, 844, 845, 846, 847, 848, 849, 850, 851, 852, 853, 854, 871, 879); +INSERT INTO `trinity_string` (entry, content_default) VALUES +(453,'│Player %s %s (guid: %u)'), +(548,'│ GM Mode active, Phase: -1'), +(549,'├─ Banned: (Type: %s, Reason: %s, Time: %s, By: %s)'), +(550,'├─ Muted: (Reason: %s, Time: %s, By: %s)'), +(714,'│ Account: %s (ID: %u), GMLevel: %u'), +(716,'│ Last Login: %s (Failed Logins: %u)'), +(749,'│ OS: %s - Latency: %u ms'), +(752,'│ Last IP: %s (Locked: %s)'), +(843,'│ Level: %u (%u/%u XP (%u XP left))'), +(844,'│ Race: %s %s, %s'), +(845,'│ Alive ?: %s'), +(846,'│ Phase: %u'), +(847,'│ Money: %ug%us%uc'), +(848,'│ Map: %s, Area: %s, Zone: %s'), +(849,'│ Guild: %s (ID: %u)'), +(850,'├─ Rank: %s'), +(851,'├─ Note: %s'), +(852,'├─ O. Note: %s'), +(853,'│ Played time: %s'), +(854,'│ Mails: %d Read/%u Total'), +(871,'│ Level: %u'), +(879,'└ Registration Email: %s - Email: %s'); +SET NAMES 'latin1'; From a1bbadbfc74debf37f715408aadad6e3fef79731 Mon Sep 17 00:00:00 2001 From: Aokromes Date: Sun, 29 Jun 2014 19:28:47 +0200 Subject: [PATCH 65/80] Rename 2014_06_29_02_world_misc.sql to 2014_06_29_02_world_misc.sql --- .../updates/world/2014_06_29_02_world_misc.sql | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename "sql/updates/world/ \t2014_06_29_02_world_misc.sql" => sql/updates/world/2014_06_29_02_world_misc.sql (100%) diff --git "a/sql/updates/world/ \t2014_06_29_02_world_misc.sql" b/sql/updates/world/2014_06_29_02_world_misc.sql similarity index 100% rename from "sql/updates/world/ \t2014_06_29_02_world_misc.sql" rename to sql/updates/world/2014_06_29_02_world_misc.sql From 39140fe1ccb54442c4ba84692b6f1d1631ada64a Mon Sep 17 00:00:00 2001 From: Vincent-Michael Date: Sun, 29 Jun 2014 23:50:22 +0200 Subject: [PATCH 66/80] DB/Locale: Fix some locales problems Preparation for tdb release in soon soon (Fix first db errors) :P --- .../world/2014_04_21_06_world_locales_broadcast_text.sql | 4 ++++ sql/updates/world/2014_04_21_07_world_locales_item.sql | 4 ++++ .../world/2014_04_21_08_world_locales_item_set_names.sql | 4 ++++ sql/updates/world/2014_04_22_00_world_locales_gameobject.sql | 4 ++++ sql/updates/world/2014_04_22_01_world_locales_creature.sql | 4 ++++ sql/updates/world/2014_04_23_01_world_locales_quest.sql | 4 ++++ sql/updates/world/2014_04_23_04_world_locales_item.sql | 4 ++++ .../world/2014_04_24_00_world_locales_broadcast_text.sql | 4 ++++ sql/updates/world/2014_04_24_01_world_locales_gameobject.sql | 4 ++++ sql/updates/world/2014_04_26_01_world_locales_creature.sql | 4 ++++ sql/updates/world/2014_06_22_00_world_locales_creature.sql | 4 ++++ 11 files changed, 44 insertions(+) diff --git a/sql/updates/world/2014_04_21_06_world_locales_broadcast_text.sql b/sql/updates/world/2014_04_21_06_world_locales_broadcast_text.sql index 214a80e72c..a1401a2eef 100644 --- a/sql/updates/world/2014_04_21_06_world_locales_broadcast_text.sql +++ b/sql/updates/world/2014_04_21_06_world_locales_broadcast_text.sql @@ -1,3 +1,5 @@ +SET NAMES 'utf8'; + DROP TABLE IF EXISTS `locales_broadcast_text`; CREATE TABLE IF NOT EXISTS `locales_broadcast_text` ( `ID` MEDIUMINT(8) UNSIGNED NOT NULL DEFAULT '0', @@ -73832,3 +73834,5 @@ INSERT INTO `locales_broadcast_text` (`ID`, `MaleText_loc1`, `MaleText_loc2`, `M (77369, '', '|TInterface\\Icons\\inv_misc_bomb_05:20|tVous êtes |cFFFF0000|Hspell:145996|h[Prêt à exploser]|h|r ! Utilisez |cFFFF0000|Hspell:146364|h[Larguer la bombe]|h|r pour les larguer !', '|TInterface\\Icons\\inv_misc_bomb_05:20|t Ihr wurdet |cFFFF0000|Hspell:145996|h[scharfgemacht]|h|r! Benutzt |cFFFF0000|Hspell:146364|h[Bombe werfen]|h|r, um sie loszuwerden!', '', '', '|TInterface\\Icons\\inv_misc_bomb_05:20|tTienes |cFFFF0000|Hspell:145996|h[Listo para explotar]|h|r! Utiliza |cFFFF0000|Hspell:146364|h[Lanzar bomba]|h|r para lanzarlas!', '', '|TInterface\\Icons\\inv_misc_bomb_05:20|tВы использовали способность|cFFFF0000|Hspell:145996|h[Готовность к взрыву]|h|r! Используйте |cFFFF0000|Hspell:146364|h[Бросок бомбы]|h|r, чтобы метнуть заряд в цель.', '', '', '', '', '', '', '', ''), (77371, '', 'Pour Hurlenfer, c’est la fin.$b$b$b$bQue voulez-vous d’autre, $r ?', 'Das ist das Ende von Höllschrei.$b$b$b$bWas jetzt, $R?', '', '', 'Eh''te es el final de Grito Infernal.$b$b$b$b¿Y ahora qué, $r?', '', 'С Адским Криком покончено.$b$b<Вол''джин тревожно смотрит на вас.>$b$bЧто будет дальше, $r?', '', '', '', '', '', '', '', ''), (77865, '', '|TInterface\\Icons\\achievement_arena_2v2_5:20|t$n a accepté de commencer le combat. Joueurs supplémentaires requis pour commencer : $8589w.', '|TInterface\\Icons\\achievement_arena_2v2_5:20|t$n hat zugestimmt, die Begegnung zu starten. Zusätzliche Spieler benötigt, um zu beginnen: $8589w.', '', '', '|TInterface\\Icons\\achievement_arena_2v2_5:20|t$n ha aceptado el encuentro. Jugadores adicionales necesarios para comenzar: $8589w.', '', '|TInterface\\Icons\\achievement_arena_2v2_5:20|t$n $gсогласился:согласилась; начать бой. Число недостающих игроков: $8589w.', '', '', '', '', '', '', '', ''); + +SET NAMES 'latin1'; diff --git a/sql/updates/world/2014_04_21_07_world_locales_item.sql b/sql/updates/world/2014_04_21_07_world_locales_item.sql index 26fcbb6cdc..c3cdea2553 100644 --- a/sql/updates/world/2014_04_21_07_world_locales_item.sql +++ b/sql/updates/world/2014_04_21_07_world_locales_item.sql @@ -1,3 +1,5 @@ +SET NAMES 'utf8'; + TRUNCATE `locales_item`; INSERT INTO `locales_item` (`entry`, `name_loc1`, `name_loc2`, `name_loc3`, `name_loc4`, `name_loc5`, `name_loc6`, `name_loc7`, `name_loc8`, `description_loc1`, `description_loc2`, `description_loc3`, `description_loc4`, `description_loc5`, `description_loc6`, `description_loc7`, `description_loc8`) VALUES @@ -38287,3 +38289,5 @@ INSERT INTO `locales_item` (`entry`, `name_loc1`, `name_loc2`, `name_loc3`, `nam (54857, '', 'Petite lance de Bourbimus', 'Murkimus'' kleiner Speer', '', '', 'Pequeña lanza de Murkimus', '', 'Маленькое копье Мурчалия', '', 'Vous apprend à invoquer cette mascotte.', 'Lehrt Euch, wie man dieses Haustier beschwört.', '', '', 'Te enseña a invocar este compañero.', '', 'Учит призывать этого спутника.'), (54860, '', 'Fusée de tourisme X-53', 'X-53 Reiserakete', '', '', 'Cohete de paseo X-53', '', 'Прогулочная ракета X-53', '', 'Vous apprend à invoquer cette monture. Elle change selon l''endroit où vous vous trouvez et votre compétence de monte.', 'Lehrt Euch, wie man dieses Reittier beschwört. Dieses Reittier passt sich Eurer Reitfertigkeit und Eurem Aufenthaltsort an.', '', '', 'Te enseña a invocar esta montura. Cambia según tu habilidad en equitación y la zona en la que estés.', '', 'Обучает управлению этим средством передвижения. Свойства этого транспортного средства могут меняться в зависимости от вашего навыка верховой езды и местонахождения.'), (56806, '', 'Mini Thor', 'Mini-Thor', '', '', 'Mini thor', '', 'Игрушечный Тор', '', 'Vous apprend à invoquer le mini Thor.', 'Lehrt Euch, wie man einen winzigen Thor beschwört.', '', '', 'Te enseña a invocar un mini thor.', '', 'Учит призывать игрушечного Тора.'); + +SET NAMES 'latin1'; diff --git a/sql/updates/world/2014_04_21_08_world_locales_item_set_names.sql b/sql/updates/world/2014_04_21_08_world_locales_item_set_names.sql index 5b55ca6d06..83fbc7c349 100644 --- a/sql/updates/world/2014_04_21_08_world_locales_item_set_names.sql +++ b/sql/updates/world/2014_04_21_08_world_locales_item_set_names.sql @@ -1,3 +1,5 @@ +SET NAMES 'utf8'; + TRUNCATE `locales_item_set_names`; INSERT INTO `locales_item_set_names` (`entry`, `name_loc1`, `name_loc2`, `name_loc3`, `name_loc4`, `name_loc5`, `name_loc6`, `name_loc7`, `name_loc8`) VALUES @@ -2490,3 +2492,5 @@ INSERT INTO `locales_item_set_names` (`entry`, `name_loc1`, `name_loc2`, `name_l (51774, '', 'Grand heaume de seigneur ymirjar', 'Großhelm des Ymirjarfürsten', '', '', 'Gran yelmo de señor Ymirjar', '', 'Великий шлем имирьярского повелителя'), (51775, '', 'Garde-jambes de seigneur ymirjar', 'Beinschützer des Ymirjarfürsten', '', '', 'Musleras de señor Ymirjar', '', 'Набедренники имирьярского повелителя'), (51776, '', 'Espauliers de seigneur ymirjar', 'Schulterstücke des Ymirjarfürsten', '', '', 'Espaldares de señor Ymirjar', '', 'Наплечье имирьярского повелителя'); + +SET NAMES 'latin1'; diff --git a/sql/updates/world/2014_04_22_00_world_locales_gameobject.sql b/sql/updates/world/2014_04_22_00_world_locales_gameobject.sql index 7640918784..fd4c09ae48 100644 --- a/sql/updates/world/2014_04_22_00_world_locales_gameobject.sql +++ b/sql/updates/world/2014_04_22_00_world_locales_gameobject.sql @@ -1,3 +1,5 @@ +SET NAMES 'utf8'; + TRUNCATE `locales_gameobject`; INSERT INTO `locales_gameobject` (`entry`, `name_loc1`, `name_loc2`, `name_loc3`, `name_loc4`, `name_loc5`, `name_loc6`, `name_loc7`, `name_loc8`, `castbarcaption_loc1`, `castbarcaption_loc2`, `castbarcaption_loc3`, `castbarcaption_loc4`, `castbarcaption_loc5`, `castbarcaption_loc6`, `castbarcaption_loc7`, `castbarcaption_loc8`) VALUES @@ -19727,3 +19729,5 @@ INSERT INTO `locales_gameobject` (`entry`, `name_loc1`, `name_loc2`, `name_loc3` (203079, '', 'Œuf de dragon rouge (grand)', 'Großes Rotdrachenei', '', '', 'Huevo de dragón Rojo (grande)', '', 'Большое яйцо красного дракона', '', '', '', '', '', '', '', ''), (203080, '', 'Œuf de dragon rouge brisé (grand)', 'Zerbrochenes großes Rotdrachenei', '', '', 'Huevo de dragón Rojo roto (grande)', '', 'Большое разбитое яйцо красного дракона', '', '', '', '', '', '', '', ''), (203624, '', 'Anneau du Crépuscule d''Halion', 'Zwielichtring von Halion', '', '', 'Anillo Crepuscular de Halion', '', 'Сумеречное кольцо Халиона', '', '', '', '', '', '', '', ''); + +SET NAMES 'latin1'; diff --git a/sql/updates/world/2014_04_22_01_world_locales_creature.sql b/sql/updates/world/2014_04_22_01_world_locales_creature.sql index a0576578eb..2cfe1c35ea 100644 --- a/sql/updates/world/2014_04_22_01_world_locales_creature.sql +++ b/sql/updates/world/2014_04_22_01_world_locales_creature.sql @@ -1,3 +1,5 @@ +SET NAMES 'utf8'; + TRUNCATE `locales_creature`; INSERT INTO `locales_creature` (`entry`, `name_loc1`, `name_loc2`, `name_loc3`, `name_loc4`, `name_loc5`, `name_loc6`, `name_loc7`, `name_loc8`, `subname_loc1`, `subname_loc2`, `subname_loc3`, `subname_loc4`, `subname_loc5`, `subname_loc6`, `subname_loc7`, `subname_loc8`) VALUES @@ -27350,3 +27352,5 @@ INSERT INTO `locales_creature` (`entry`, `name_loc1`, `name_loc2`, `name_loc3`, (40870, '', 'Dragon rubis', 'Rubindrache', '', '', 'Dragón rubí', '', 'Рубиновый дракон', '', '', '', '', '', '', '', ''), (41839, '', '[DND] Controller', '[DND] Controller', '', '', '[DND] Controller', '', '[DND] Controller', '', '', '', '', '', '', '', ''), (42078, '', 'Mini Thor', 'Mini-Thor', '', '', 'Mini thor', '', 'Игрушечный Тор', '', 'Le destructeur', 'Der Zerstörer', '', '', 'El Destructor', '', 'Разрушитель'); + +SET NAMES 'latin1'; diff --git a/sql/updates/world/2014_04_23_01_world_locales_quest.sql b/sql/updates/world/2014_04_23_01_world_locales_quest.sql index b191439ea5..b19d251a4d 100644 --- a/sql/updates/world/2014_04_23_01_world_locales_quest.sql +++ b/sql/updates/world/2014_04_23_01_world_locales_quest.sql @@ -1,3 +1,5 @@ +SET NAMES 'utf8'; + TRUNCATE `locales_quest`; INSERT INTO `locales_quest` (`Id`, `Title_loc1`, `Title_loc2`, `Title_loc3`, `Title_loc4`, `Title_loc5`, `Title_loc6`, `Title_loc7`, `Title_loc8`, `Details_loc1`, `Details_loc2`, `Details_loc3`, `Details_loc4`, `Details_loc5`, `Details_loc6`, `Details_loc7`, `Details_loc8`, `Objectives_loc1`, `Objectives_loc2`, `Objectives_loc3`, `Objectives_loc4`, `Objectives_loc5`, `Objectives_loc6`, `Objectives_loc7`, `Objectives_loc8`, `OfferRewardText_loc1`, `OfferRewardText_loc2`, `OfferRewardText_loc3`, `OfferRewardText_loc4`, `OfferRewardText_loc5`, `OfferRewardText_loc6`, `OfferRewardText_loc7`, `OfferRewardText_loc8`, `RequestItemsText_loc1`, `RequestItemsText_loc2`, `RequestItemsText_loc3`, `RequestItemsText_loc4`, `RequestItemsText_loc5`, `RequestItemsText_loc6`, `RequestItemsText_loc7`, `RequestItemsText_loc8`, `EndText_loc1`, `EndText_loc2`, `EndText_loc3`, `EndText_loc4`, `EndText_loc5`, `EndText_loc6`, `EndText_loc7`, `EndText_loc8`, `CompletedText_loc1`, `CompletedText_loc2`, `CompletedText_loc3`, `CompletedText_loc4`, `CompletedText_loc5`, `CompletedText_loc6`, `CompletedText_loc7`, `CompletedText_loc8`, `ObjectiveText1_loc1`, `ObjectiveText1_loc2`, `ObjectiveText1_loc3`, `ObjectiveText1_loc4`, `ObjectiveText1_loc5`, `ObjectiveText1_loc6`, `ObjectiveText1_loc7`, `ObjectiveText1_loc8`, `ObjectiveText2_loc1`, `ObjectiveText2_loc2`, `ObjectiveText2_loc3`, `ObjectiveText2_loc4`, `ObjectiveText2_loc5`, `ObjectiveText2_loc6`, `ObjectiveText2_loc7`, `ObjectiveText2_loc8`, `ObjectiveText3_loc1`, `ObjectiveText3_loc2`, `ObjectiveText3_loc3`, `ObjectiveText3_loc4`, `ObjectiveText3_loc5`, `ObjectiveText3_loc6`, `ObjectiveText3_loc7`, `ObjectiveText3_loc8`, `ObjectiveText4_loc1`, `ObjectiveText4_loc2`, `ObjectiveText4_loc3`, `ObjectiveText4_loc4`, `ObjectiveText4_loc5`, `ObjectiveText4_loc6`, `ObjectiveText4_loc7`, `ObjectiveText4_loc8`) VALUES @@ -10178,3 +10180,5 @@ INSERT INTO `locales_quest` (`Id`, `Title_loc1`, `Title_loc2`, `Title_loc3`, `Ti (26013, '', 'Assaut sur le sanctum', 'Angriff auf das Sanktum', '', '', 'Asalto al Sagrario', 'Asalto al Sagrario', 'Нападение на святилище', '', '$R, quelque chose de terrible est survenu dans le sanctum Rubis, sous le temple.$B$BL’entrée du sanctum a été saccagée, et ses gardiens sauvagement assassinés. Nous avons envoyé des éclaireurs pour comprendre ce qui s’est passé, mais aucun d’entre eux n’est revenu. Je crains malheureusement le pire.$B$BEntrez dans le sanctum Rubis et découvrez ce qui est arrivé au foyer de mon Vol. Quand nous aurons plus d’informations, nous pourrons préparer nos plans.', '$R, etwas Schreckliches ist im Rubinsanktum unterhalb des Tempels geschehen.$B$BDer Eingang zum Sanktum liegt in Ruinen und seine Wächter sind auf grausame Art und Weise erschlagen worden. Wir haben Kundschafter ausgesandt, um der Sache auf den Grund zu gehen, aber sie sind nicht zurückgekommen. Nun befürchte ich das Schlimmste.$B$BBetretet das Rubinsanktum und bringt in Erfahrung, was in der Heimat meines Schwarmes passiert ist. Sobald wir mehr Informationen haben, können wir unsere nächsten Schritte entsprechend planen.', '', '', '$R, algo terrible ha ocurrido dentro del Sagrario Rubí, bajo el templo.$B$BLa entrada al Sagrario está en ruinas y sus guardianes han muerto de forma violenta. Enviamos exploradores a investigar, pero no han regresado. No puedo evitar temerme lo peor.$B$BEntra al Sagrario Rubí y descubre qué ha sucedido en el hogar de mi Vuelo. Una vez que tengamos más información, podremos planear nuestros siguientes pasos.', '$R, algo terrible ha ocurrido dentro del Sagrario Rubí, bajo el templo.$B$BLa entrada al Sagrario está en ruinas y sus guardianes han muerto de forma violenta. Enviamos exploradores a investigar, pero no han regresado. No puedo evitar temerme lo peor.$B$BEntra al Sagrario Rubí y descubre qué ha sucedido en el hogar de mi Vuelo. Una vez que tengamos más información, podremos planear nuestros siguientes pasos.', '$R, в Рубиновом святилище под храмом произошло нечто ужасное!$B$BГлавный вход разрушен, стражи мертвы... Мы послали туда разведчиков, но никто не вернулся живым. Боюсь, нам надо готовиться к худшему.$B$BОтправляйся в Рубиновое святилище и выясни, кто осмелился поднять руку на дом моего племени. Как только у нас будет больше информации, мы сможем спланировать дальнейшие действия.', '', 'Menez une enquête au Sanctum Rubis, sous le Temple du Repos du ver.', 'Untersucht das Rubinsanktum unterhalb des Wyrmruhtempels.', '', '', 'Investiga el Sagrario Rubí bajo el Templo del Reposo del Dragón.', 'Investiga el Sagrario Rubí bajo el Templo del Reposo del Dragón.', 'Осмотрите Рубиновое святилище под храмом Драконьего Покоя.', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', 'Menez une enquête au sanctum Rubis, sous le temple du Repos du ver.', 'Untersucht das Rubinsanktum unterhalb des Wyrmruhtempels.', '', '', 'Investiga el Sagrario Rubí bajo el Templo del Reposo del Dragón.', 'Investiga el Sagrario Rubí bajo el Templo del Reposo del Dragón.', 'Осмотрите Рубиновое святилище под храмом Драконьего Покоя.', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', ''); INSERT INTO `locales_quest` (`Id`, `Title_loc1`, `Title_loc2`, `Title_loc3`, `Title_loc4`, `Title_loc5`, `Title_loc6`, `Title_loc7`, `Title_loc8`, `Details_loc1`, `Details_loc2`, `Details_loc3`, `Details_loc4`, `Details_loc5`, `Details_loc6`, `Details_loc7`, `Details_loc8`, `Objectives_loc1`, `Objectives_loc2`, `Objectives_loc3`, `Objectives_loc4`, `Objectives_loc5`, `Objectives_loc6`, `Objectives_loc7`, `Objectives_loc8`, `OfferRewardText_loc1`, `OfferRewardText_loc2`, `OfferRewardText_loc3`, `OfferRewardText_loc4`, `OfferRewardText_loc5`, `OfferRewardText_loc6`, `OfferRewardText_loc7`, `OfferRewardText_loc8`, `RequestItemsText_loc1`, `RequestItemsText_loc2`, `RequestItemsText_loc3`, `RequestItemsText_loc4`, `RequestItemsText_loc5`, `RequestItemsText_loc6`, `RequestItemsText_loc7`, `RequestItemsText_loc8`, `EndText_loc1`, `EndText_loc2`, `EndText_loc3`, `EndText_loc4`, `EndText_loc5`, `EndText_loc6`, `EndText_loc7`, `EndText_loc8`, `CompletedText_loc1`, `CompletedText_loc2`, `CompletedText_loc3`, `CompletedText_loc4`, `CompletedText_loc5`, `CompletedText_loc6`, `CompletedText_loc7`, `CompletedText_loc8`, `ObjectiveText1_loc1`, `ObjectiveText1_loc2`, `ObjectiveText1_loc3`, `ObjectiveText1_loc4`, `ObjectiveText1_loc5`, `ObjectiveText1_loc6`, `ObjectiveText1_loc7`, `ObjectiveText1_loc8`, `ObjectiveText2_loc1`, `ObjectiveText2_loc2`, `ObjectiveText2_loc3`, `ObjectiveText2_loc4`, `ObjectiveText2_loc5`, `ObjectiveText2_loc6`, `ObjectiveText2_loc7`, `ObjectiveText2_loc8`, `ObjectiveText3_loc1`, `ObjectiveText3_loc2`, `ObjectiveText3_loc3`, `ObjectiveText3_loc4`, `ObjectiveText3_loc5`, `ObjectiveText3_loc6`, `ObjectiveText3_loc7`, `ObjectiveText3_loc8`, `ObjectiveText4_loc1`, `ObjectiveText4_loc2`, `ObjectiveText4_loc3`, `ObjectiveText4_loc4`, `ObjectiveText4_loc5`, `ObjectiveText4_loc6`, `ObjectiveText4_loc7`, `ObjectiveText4_loc8`) VALUES (26034, '', 'Le destructeur du Crépuscule', 'Der Zwielichtzerstörer', '', '', 'El Destructor del Crepúsculo', 'El Destructor del Crepúsculo', 'Сумеречный разрушитель', '', 'Ce n’était pas une attaque téméraire, mais plutôt une frappe soigneusement orchestrée contre le cœur du Vol draconique rouge.$B$BZarithrian, sur ce piton, là-bas, supervise l’assaut, mais le véritable chef de cette force est un dragon du Crépuscule arrogant et puissant nommé Halion. On n''avait plus vu de rejeton adulte issu des expériences malsaines de Sinestra depuis Dargonax.$B$BTuez-le, $n, et quand l’invasion aura été complètement repoussée, allez raconter à Krasus tout ce que vous avez vu ici.', 'Dies war kein leichtfertiger Angriff, sondern vielmehr ein sorgsam inszenierter Schlag gegen das Herz des roten Drachenschwarms.$B$BZarithrian, der sich auf dem Felsvorsprung dort drüben befindet, überwacht den Angriff, doch ist der wahre Anführer ein dreister und mächtiger Zwielichtdrache namens Halion. Seit Dargonax ist kein ausgewachsenes Exemplar der Brut aus Sinestras verdorbenen Experimenten mehr gesichtet worden.$B$BTötet ihn, $n. Sobald es Euch gelungen ist, diese Invasion zurückzudrängen, meldet Ihr Euch bei Krasus und berichtet ihm, was Ihr hier gesehen habt.', '', '', 'No se trató de un ataque imprudente, sino de un ataque bien organizado contra el corazón del Vuelo Rojo.$B$BZarithrian, en la cima de ahí arriba, está vigilando el asalto, pero el verdadero líder de esta fuerza es un dragón Crepuscular valiente y poderoso llamado Halion. Desde Dargonax no se había visto a ningún engendro adulto de los experimentos retorcidos de Sinestra.$B$BMátalo, $n, y cuando se haya repelido totalmente la invasión, informa a Krasus sobre todo lo que has visto aquí.', 'No se trató de un ataque imprudente, sino de un ataque bien organizado contra el corazón del Vuelo Rojo.$B$BZarithrian, en la cima de ahí arriba, está vigilando el asalto, pero el verdadero líder de esta fuerza es un dragón Crepuscular valiente y poderoso llamado Halion. Desde Dargonax no se había visto a ningún engendro adulto de los experimentos retorcidos de Sinestra.$B$BMátalo, $n, y cuando se haya repelido totalmente la invasión, informa a Krasus sobre todo lo que has visto aquí.', 'Нет, это было не случайное нападение, а организованная атака на род красных драконов.$B$BЯ вижу Заритриана, наблюдающего за битвой с вершины холма, но истинный лидер захватчиков – могущественный сумеречный дракон Халион. В первый раз после появления Даргонакса мы столкнулись со взрослым отпрыском Зловестины.$B$BУбей его, $n, изгони захватчиков и сообщи Красу обо всем, что ты здесь $Gвидел:видела;.', '', 'Détruisez Halion et repoussez les envahisseurs qui ont investi le Sanctum Rubis.', 'Besiegt Halion und drängt die Invasion des Rubinsanktums zurück.', '', '', 'Derrota a Halion y repele la invasión del Sagrario Rubí.', 'Derrota a Halion y repele la invasión del Sagrario Rubí.', 'Победите Халиона и отразите нападение на Рубиновое святилище.', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', 'Faites votre rapport à Krasus, au sommet du temple du Repos du ver.', 'Erstattet Krasus auf dem Dach des Wyrmruhtempels Bericht.', '', '', 'Informa a: Krasus. Zona: Parte superior del Templo del Reposo del Dragón.', 'Informa a: Krasus. Zona: Parte superior del Templo del Reposo del Dragón.', 'Вернитесь к Красу на вершину храма Драконьего Покоя.', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', ''); + +SET NAMES 'latin1'; diff --git a/sql/updates/world/2014_04_23_04_world_locales_item.sql b/sql/updates/world/2014_04_23_04_world_locales_item.sql index 5b0e08d531..f4b7831b8d 100644 --- a/sql/updates/world/2014_04_23_04_world_locales_item.sql +++ b/sql/updates/world/2014_04_23_04_world_locales_item.sql @@ -1,3 +1,5 @@ +SET NAMES 'utf8'; + UPDATE `locales_item` SET `name_loc1`='마틴 퓨리', `name_loc4`='马丁之怒', `name_loc5`='馬汀烈怒', `name_loc7`='Martin Furia' WHERE `entry`=17; UPDATE `locales_item` SET `name_loc1`='낡은 쇼트소드', `name_loc4`='破损的短剑', `name_loc5`='破損的短劍', `name_loc7`='Espada corta desgastada' WHERE `entry`=25; UPDATE `locales_item` SET `name_loc1`='구부러진 지팡이', `name_loc4`='弯曲的法杖', `name_loc5`='彎曲法杖', `name_loc7`='Bastón doblado' WHERE `entry`=35; @@ -38109,3 +38111,5 @@ UPDATE `locales_item` SET `name_loc1`='꼬마 해체자', `name_loc4`='小拆', UPDATE `locales_item` SET `name_loc1`='머키무스의 작은 창', `name_loc4`='莫叽姆斯的小矛', `name_loc5`='魚人穆斯的小矛', `name_loc7`='Pequeña lanza de Murkimus', `description_loc1`='애완동물 친구를 소환하는 법을 배웁니다.', `description_loc4`='教你学会召唤这种小伙伴。', `description_loc5`='教你學會如何召喚這個夥伴。', `description_loc7`='Te enseña a invocar este compañero.' WHERE `entry`=54857; UPDATE `locales_item` SET `name_loc1`='X-53 관광 로켓', `name_loc4`='X-53型观光火箭', `name_loc5`='X-53型觀光火箭', `name_loc7`='Cohete de paseo X-53', `description_loc1`='탈것 소환하는 법을 배웁니다. 타기 숙련도와 위치에 따라 달라지는 탈것입니다.', `description_loc4`='教你学会召唤这种坐骑。这种坐骑的速度会根据你的骑术等级和所在区域改变。', `description_loc5`='教你學會如何召喚這個坐騎。此坐騎將隨著你的騎術技能和地點而改變。', `description_loc7`='Te enseña a invocar esta montura. Cambia según tu habilidad en equitación y la zona en la que estés.' WHERE `entry`=54860; UPDATE `locales_item` SET `name_loc1`='꼬마 토르', `name_loc4`='迷你雷神', `name_loc5`='迷你雷神', `name_loc7`='Mini thor', `description_loc1`='꼬마 토르 소환하는 법을 배웁니다.', `description_loc4`='教会你如何召唤迷你雷神。', `description_loc5`='教你學會如何召喚迷你雷神。', `description_loc7`='Te enseña a invocar un mini thor.' WHERE `entry`=56806; + +SET NAMES 'latin1'; diff --git a/sql/updates/world/2014_04_24_00_world_locales_broadcast_text.sql b/sql/updates/world/2014_04_24_00_world_locales_broadcast_text.sql index abb838cd62..92c0e75fd8 100644 --- a/sql/updates/world/2014_04_24_00_world_locales_broadcast_text.sql +++ b/sql/updates/world/2014_04_24_00_world_locales_broadcast_text.sql @@ -1,3 +1,5 @@ +SET NAMES 'utf8'; + UPDATE `locales_broadcast_text` SET `MaleText_loc1`='제발, 제발 좀 도와주시오! 난 여기 갇혀 있소!', `MaleText_loc4`='救命啊!我动不了了!', `MaleText_loc5`='救命啊!我動不了了!', `MaleText_loc7`='¡Socorro! ¡Socorro! ¡Soy víctima de represión!' WHERE `ID`=1; UPDATE `locales_broadcast_text` SET `MaleText_loc1`='중대.. 제자리에 서! ... 정렬!', `MaleText_loc4`='全队……立正!……集合!', `MaleText_loc5`='全體...立正!...集合!', `MaleText_loc7`='Compañía... ¡ALTO! ¡Formen FILAS!' WHERE `ID`=3; UPDATE `locales_broadcast_text` SET `MaleText_loc1`='앞으로... 가!', `MaleText_loc4`='前进……前进!', `MaleText_loc5`='起步...走!!', `MaleText_loc7`='Adelante... ¡¡MARCHEN!!' WHERE `ID`=4; @@ -78650,3 +78652,5 @@ UPDATE `locales_broadcast_text` SET `MaleText_loc1`='볼진은 정말 보통이 UPDATE `locales_broadcast_text` SET `MaleText_loc1`='|TInterface\\Icons\\inv_misc_bomb_05:20|t |cFFFF0000|Hspell:145996|h[폭파 준비 완료]|h|r! |cFFFF0000|Hspell:146364|h[폭탄 투척]|h|r으로 폭탄을 던지십시오!', `MaleText_loc4`='|TInterface\\Icons\\inv_misc_bomb_05:20|t你身后被|cFFFF0000|Hspell:145996|h[设置炸弹]|h|r了!快使用|cFFFF0000|Hspell:146364|h[投掷炸弹]|h|r扔掉它们!', `MaleText_loc5`='|TInterface\\Icons\\inv_misc_bomb_05:20|t你獲得|cFFFF0000|Hspell:145996|h[引爆]|h|r!使用|cFFFF0000|Hspell:146364|h[投擲炸彈]|h|r來投擲炸彈!', `MaleText_loc7`='|TInterfaceIconsinv_misc_bomb_05:20|tTienes |cFFFF0000|Hspell:145996|h[Listo para explotar]|h|r! Utiliza |cFFFF0000|Hspell:146364|h[Lanzar bomba]|h|r para lanzarlas!' WHERE `ID`=77369; UPDATE `locales_broadcast_text` SET `MaleText_loc1`='헬스크림의 최후로군.$b$b<경계하는 눈길로 당신을 바라보는 볼진>$b$b$r|1이여;여;, 앞으로는 어쩔 셈인가?', `MaleText_loc4`='地狱咆哮已经完蛋了。$b$b<沃金警惕地看着你。>$b$b接下来呢,$r?', `MaleText_loc5`='這就是地獄吼最終的下場。$b$b<沃金小心翼翼地看著你。>$b$b下一步是什麼,$r?', `MaleText_loc7`='Eh''te es el final de Grito Infernal.$b$b$b$b¿Y ahora qué, $r?' WHERE `ID`=77371; UPDATE `locales_broadcast_text` SET `MaleText_loc1`='|TInterface\\Icons\\achievement_arena_2v2_5:20|t$n|1이;가; 전투를 시작하려고 합니다. 전투 시작에 필요한 추가 인원: $8589w', `MaleText_loc4`='|TInterface\\Icons\\achievement_arena_2v2_5:20|t$n已同意开始战斗。另外还需要$8589w名玩家才可开始。', `MaleText_loc5`='|TInterface\\Icons\\achievement_arena_2v2_5:20|t$n同意進行戰鬥。另外還需要$8589w名玩家才可開始。', `MaleText_loc7`='|TInterfaceIconsachievement_arena_2v2_5:20|t$n ha aceptado el encuentro. Jugadores adicionales necesarios para comenzar: $8589w.' WHERE `ID`=77865; + +SET NAMES 'latin1'; diff --git a/sql/updates/world/2014_04_24_01_world_locales_gameobject.sql b/sql/updates/world/2014_04_24_01_world_locales_gameobject.sql index 3ab380a7a9..b929eb0eed 100644 --- a/sql/updates/world/2014_04_24_01_world_locales_gameobject.sql +++ b/sql/updates/world/2014_04_24_01_world_locales_gameobject.sql @@ -1,3 +1,5 @@ +SET NAMES 'utf8'; + UPDATE `locales_gameobject` SET `name_loc1`='오래된 사자상', `name_loc4`='陈旧的石狮子', `name_loc5`='老舊獅子雕像', `name_loc7`='Estatua de león antigua' WHERE `entry`=31; UPDATE `locales_gameobject` SET `name_loc1`='가라앉은 상자', `name_loc4`='沉没的箱子', `name_loc5`='沉沒的箱子', `name_loc7`='Cofre sumergido' WHERE `entry`=32; UPDATE `locales_gameobject` SET `name_loc1`='잠긴 궤짝', `name_loc4`='锁住的箱子', `name_loc5`='鎖住的箱子', `name_loc7`='Cofre cerrado' WHERE `entry`=33; @@ -19666,3 +19668,5 @@ UPDATE `locales_gameobject` SET `name_loc1`='불타는 나무', `name_loc4`='燃 UPDATE `locales_gameobject` SET `name_loc1`='붉은 용 알 (대형)', `name_loc4`='Red Dragon Egg (Large)', `name_loc5`='紅龍蛋(大)', `name_loc7`='Huevo de dragón Rojo (grande)' WHERE `entry`=203079; UPDATE `locales_gameobject` SET `name_loc1`='깨진 붉은용 알 (대형)', `name_loc4`='Broken Red Dragon Egg (Large)', `name_loc5`='破掉的紅龍蛋(大)', `name_loc7`='Huevo de dragón Rojo roto (grande)' WHERE `entry`=203080; UPDATE `locales_gameobject` SET `name_loc1`='할리온 황혼의 고리', `name_loc4`='海里昂暮光之环', `name_loc5`='海萊恩暮光環', `name_loc7`='Anillo Crepuscular de Halion' WHERE `entry`=203624; + +SET NAMES 'latin1'; diff --git a/sql/updates/world/2014_04_26_01_world_locales_creature.sql b/sql/updates/world/2014_04_26_01_world_locales_creature.sql index c49e37f0bf..4e718cd8ed 100644 --- a/sql/updates/world/2014_04_26_01_world_locales_creature.sql +++ b/sql/updates/world/2014_04_26_01_world_locales_creature.sql @@ -1,3 +1,5 @@ +SET NAMES 'utf8'; + UPDATE `locales_creature` SET `name_loc1`='굶주린 시체', `name_loc4`='食腐者', `name_loc5`='食腐者', `name_loc7`='Comecarnes' WHERE `entry`=3; UPDATE `locales_creature` SET `name_loc1`='코볼트 졸개', `name_loc4`='狗头人歹徒', `name_loc5`='狗頭人惡黨', `name_loc7`='Alimaña kóbold' WHERE `entry`=6; UPDATE `locales_creature` SET `name_loc1`='퀘스트기버 베니', `name_loc4`='Benny Questgiver', `name_loc5`='本尼任務給予者', `name_loc7`='Asignador Benny', `subname_loc1`='시험용', `subname_loc4`='NPC', `subname_loc5`='測試', `subname_loc7`='PNJ' WHERE `entry`=19; @@ -27245,3 +27247,5 @@ UPDATE `locales_creature` SET `name_loc1`='루비 비룡', `name_loc4`='红玉 UPDATE `locales_creature` SET `name_loc1`='루비 용', `name_loc4`='红玉巨龙', `name_loc5`='晶紅龍', `name_loc7`='Dragón rubí' WHERE `entry`=40870; UPDATE `locales_creature` SET `name_loc1`='[DND] 제어기', `name_loc4`='[DND] Controller', `name_loc5`='[DND]控制器', `name_loc7`='[DND] Controller' WHERE `entry`=41839; UPDATE `locales_creature` SET `name_loc1`='꼬마 토르', `name_loc4`='迷你雷神', `name_loc5`='迷你雷神', `name_loc7`='Mini thor', `subname_loc1`='파괴자', `subname_loc4`='无坚不摧', `subname_loc5`='毀滅者', `subname_loc7`='El Destructor' WHERE `entry`=42078; + +SET NAMES 'latin1'; diff --git a/sql/updates/world/2014_06_22_00_world_locales_creature.sql b/sql/updates/world/2014_06_22_00_world_locales_creature.sql index d24a4dd1f3..5dc00dfa69 100644 --- a/sql/updates/world/2014_06_22_00_world_locales_creature.sql +++ b/sql/updates/world/2014_06_22_00_world_locales_creature.sql @@ -1,3 +1,5 @@ +SET NAMES 'utf8'; + -- Male frFR UPDATE `locales_creature` SET `name_loc2`='Contrebandier défias' WHERE `entry`=95; UPDATE `locales_creature` SET `name_loc2`='Eclaireur défias' WHERE `entry`=121; @@ -3774,3 +3776,5 @@ UPDATE `locales_creature` SET `name_loc7`='Espía creciente de la muerte captura -- ruRU UPDATE `locales_creature` SET `name_loc8`='Жрец Алого ордена' WHERE `entry`=10608; UPDATE `locales_creature` SET `name_loc8`='Разведчик Похитителей Солнца' WHERE `entry`=30233; + +SET NAMES 'latin1'; From fd910567ec470f73d52f805d8ac68b60e989c9a0 Mon Sep 17 00:00:00 2001 From: Shauren Date: Mon, 30 Jun 2014 00:24:40 +0200 Subject: [PATCH 67/80] Core/Loot: Fixed looting creatures that have only quest items in their loot Closes #12400 --- src/server/game/Entities/Unit/Unit.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp index ca4780b787..5eb48160e8 100644 --- a/src/server/game/Entities/Unit/Unit.cpp +++ b/src/server/game/Entities/Unit/Unit.cpp @@ -15393,7 +15393,7 @@ void Unit::Kill(Unit* victim, bool durabilityLoss) creature->DeleteThreatList(); // must be after setDeathState which resets dynamic flags - if (!creature->loot.empty()) + if (!creature->loot.isLooted()) creature->SetFlag(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_LOOTABLE); else creature->AllLootRemovedFromCorpse(); From 4a05a082fd671099463049552170ddbeaff6a257 Mon Sep 17 00:00:00 2001 From: Aokromes Date: Tue, 1 Jul 2014 12:18:16 +0200 Subject: [PATCH 68/80] DB/Quest: The Left Piece of Lord Valthalak's Amulet By dr-j, closes #11753 --- .../world/2014_07_01_00_world_misc.sql | 75 +++++++++++++++++++ 1 file changed, 75 insertions(+) create mode 100644 sql/updates/world/2014_07_01_00_world_misc.sql diff --git a/sql/updates/world/2014_07_01_00_world_misc.sql b/sql/updates/world/2014_07_01_00_world_misc.sql new file mode 100644 index 0000000000..ae9ffafe98 --- /dev/null +++ b/sql/updates/world/2014_07_01_00_world_misc.sql @@ -0,0 +1,75 @@ +-- +SET @Guid := 14218; + +UPDATE `gameobject_template` SET `AIName`='SmartGameObjectAI', `ScriptName`='' WHERE `entry`IN(181045,181051); +DELETE FROM `smart_scripts` WHERE `source_type`=1 AND `entryorguid`IN(181045,181051); + +DELETE FROM `gameobject` WHERE `id` IN(181096,181048); +INSERT INTO `gameobject` (`guid`,`id`,`map`,`spawnMask`,`phaseMask`,`position_x`,`position_y`,`position_z`,`orientation`,`rotation0`,`rotation1`,`rotation2`,`rotation3`,`spawntimesecs`,`animprogress`,`state`) VALUES +(@Guid,181096,429,1,1,263.38,-452.69,-119.96,0,0,0,0,0,180,0,1), +(@Guid+1,181096,229,1,1,-23.97,-451.98,-18.64,0,0,0,0,0,180,0,1), +(@Guid+2,181096,289,1,1,-3.82,141.88,83.9,0,0,0,0,0,180,0,1), +(@Guid+3,181096,229,1,1,25.92,-537.7,110.93,0,0,0,0,0,180,0,1), +(@Guid+4,181096,329,1,1,3423.47,-3055.73,136.499,6.00568,0,0,0.138306,-0.990389,300,0,1), +(@Guid+5, 181048, 329, 1, 1, 3423.47, -3055.73, 136.499, 6.00568, 0, 0, 0.138306, -0.990389, 300, 0, 1); + +INSERT INTO `smart_scripts` (`entryorguid`, `source_type`, `id`, `link`, `event_type`, `event_phase_mask`, `event_chance`, `event_flags`, `event_param1`, `event_param2`, `event_param3`, `event_param4`, `action_type`, `action_param1`, `action_param2`, `action_param3`, `action_param4`, `action_param5`, `action_param6`, `target_type`, `target_param1`, `target_param2`, `target_param3`, `target_x`, `target_y`, `target_z`, `target_o`, `comment`) VALUES +(181045, 1, 0 ,0, 11, 0, 100, 0, 0, 0, 0,0,45,1,1,0,0,0,0,10,45841,16044,0,0,0,0,0, 'Brazier of Beckoning - On Spawn - Set Data Mor Grayhoof Trigger'), +(181045, 1, 1 ,0, 11, 0, 100, 0, 0, 0, 0,0,45,1,1,0,0,0,0,10,48937,16047,0,0,0,0,0, 'Brazier of Beckoning - On Spawn - Set Data Kormok Trigger'), +(181045, 1, 2 ,0, 11, 0, 100, 0, 0, 0, 0,0,45,1,1,0,0,0,0,10,84386,16045,0,0,0,0,0, 'Brazier of Beckoning - On Spawn - Set Data Isalien Trigger'), +(181045, 1, 3 ,0, 11, 0, 100, 0, 0, 0, 0,0,45,1,1,0,0,0,0,10,53937,16046,0,0,0,0,0, 'Brazier of Beckoning - On Spawn - Set Data Jarien and Sothos Trigger'), +(181045, 1, 4 ,0, 11, 0, 100, 0, 0, 0, 0,0,45,1,1,0,0,0,0,10,137927,16048,0,0,0,0,0, 'Brazier of Beckoning - On Spawn - Set Data Lord Valthalak Trigger'); + +DELETE FROM `conditions` WHERE `SourceTypeOrReferenceId`=22 AND `SourceEntry`=181045; +INSERT INTO `conditions` (`SourceTypeOrReferenceId`, `SourceGroup`, `SourceEntry`, `SourceId`, `ElseGroup`, `ConditionTypeOrReference`, `ConditionTarget`, `ConditionValue1`, `ConditionValue2`, `ConditionValue3`, `NegativeCondition`, `ErrorType`, `ErrorTextId`, `ScriptName`, `Comment`) VALUES +(22, 1, 181045, 1, 0, 23, 1, 1583, 0, 0, 0, 0, 0, '', 'Brazier of Beckoning only run SAI in Blackrock Spire'), +(22, 2, 181045, 1, 0, 23, 1, 2057, 0, 0, 0, 0, 0, '', 'Brazier of Beckoning only run SAI in Scholomance'), +(22, 3, 181045, 1, 0, 23, 1, 2557, 0, 0, 0, 0, 0, '', 'Brazier of Beckoning only run SAI in Dire Maul'), +(22, 4, 181045, 1, 0, 23, 1, 2017, 0, 0, 0, 0, 0, '', 'Brazier of Beckoning only run SAI in Stratholme'), +(22, 5, 181045, 1, 0, 23, 1, 1583, 0, 0, 0, 0, 0, '', 'Brazier of Beckoning only run SAI in Blackrock Spire'); + +INSERT INTO `smart_scripts` (`entryorguid`, `source_type`, `id`, `link`, `event_type`, `event_phase_mask`, `event_chance`, `event_flags`, `event_param1`, `event_param2`, `event_param3`, `event_param4`, `action_type`, `action_param1`, `action_param2`, `action_param3`, `action_param4`, `action_param5`, `action_param6`, `target_type`, `target_param1`, `target_param2`, `target_param3`, `target_x`, `target_y`, `target_z`, `target_o`, `comment`) VALUES +(181051, 1, 0 ,0, 11, 0, 100, 0, 0, 0, 0,0,45,1,1,0,0,0,0,10,45841,16044,0,0,0,0,0, 'Brazier of Invocation - On Spawn - Set Data Mor Grayhoof Trigger'), +(181051, 1, 1 ,0, 11, 0, 100, 0, 0, 0, 0,0,45,1,1,0,0,0,0,10,48937,16047,0,0,0,0,0, 'Brazier of Invocation - On Spawn - Set Data Kormok Trigger'), +(181051, 1, 2 ,0, 11, 0, 100, 0, 0, 0, 0,0,45,1,1,0,0,0,0,10,84386,16045,0,0,0,0,0, 'Brazier of Invocation - On Spawn - Set Data Isalien Trigger'), +(181051, 1, 3 ,0, 11, 0, 100, 0, 0, 0, 0,0,45,1,1,0,0,0,0,10,53937,16046,0,0,0,0,0, 'Brazier of Invocation - On Spawn - Set Data Jarien and Sothos Trigger'), +(181051, 1, 4 ,0, 11, 0, 100, 0, 0, 0, 0,0,45,1,1,0,0,0,0,10,137927,16048,0,0,0,0,0, 'Brazier of Invocation - On Spawn - Set Data Lord Valthalak Trigger'); + +DELETE FROM `conditions` WHERE `SourceTypeOrReferenceId`=22 AND `SourceEntry`=181051; +INSERT INTO `conditions` (`SourceTypeOrReferenceId`, `SourceGroup`, `SourceEntry`, `SourceId`, `ElseGroup`, `ConditionTypeOrReference`, `ConditionTarget`, `ConditionValue1`, `ConditionValue2`, `ConditionValue3`, `NegativeCondition`, `ErrorType`, `ErrorTextId`, `ScriptName`, `Comment`) VALUES +(22, 1, 181051, 1, 0, 23, 1, 1583, 0, 0, 0, 0, 0, '', 'Brazier of Invocation only run SAI in Blackrock Spire'), +(22, 2, 181051, 1, 0, 23, 1, 2057, 0, 0, 0, 0, 0, '', 'Brazier of Invocation only run SAI in Scholomance'), +(22, 3, 181051, 1, 0, 23, 1, 2557, 0, 0, 0, 0, 0, '', 'Brazier of Invocation only run SAI in Dire Maul'), +(22, 4, 181051, 1, 0, 23, 1, 2017, 0, 0, 0, 0, 0, '', 'Brazier of Invocation only run SAI in Stratholme'), +(22, 5, 181051, 1, 0, 23, 1, 1583, 0, 0, 0, 0, 0, '', 'Brazier of Invocation only run SAI in Blackrock Spire'); + +UPDATE `creature_template` SET `AIName`= 'SmartAI' WHERE `entry`=16044; + +DELETE FROM `smart_scripts` WHERE `entryorguid` =16044; +INSERT INTO `smart_scripts` (`entryorguid`, `source_type`, `id`, `link`, `event_type`, `event_phase_mask`, `event_chance`, `event_flags`, `event_param1`, `event_param2`, `event_param3`, `event_param4`, `action_type`, `action_param1`, `action_param2`, `action_param3`, `action_param4`, `action_param5`, `action_param6`, `target_type`, `target_param1`, `target_param2`, `target_param3`, `target_x`, `target_y`, `target_z`, `target_o`, `comment`) VALUES +(16044, 0, 0, 0, 38, 0, 100, 1, 1, 1, 0, 0, 12, 16080, 8, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 'Mor Grayhoof Trigger - On Data Set - Spawn Mor Grayhoof'); + +UPDATE `creature_template` SET `AIName`= 'SmartAI' WHERE `entry`=16045; + +DELETE FROM `smart_scripts` WHERE `entryorguid` =16045; +INSERT INTO `smart_scripts` (`entryorguid`, `source_type`, `id`, `link`, `event_type`, `event_phase_mask`, `event_chance`, `event_flags`, `event_param1`, `event_param2`, `event_param3`, `event_param4`, `action_type`, `action_param1`, `action_param2`, `action_param3`, `action_param4`, `action_param5`, `action_param6`, `target_type`, `target_param1`, `target_param2`, `target_param3`, `target_x`, `target_y`, `target_z`, `target_o`, `comment`) VALUES +(16045, 0, 0, 0, 38, 0, 100, 1, 1, 1, 0, 0, 12, 16097, 8, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 'Isalien Trigger - On Data Set - Spawn Isalien'); + +UPDATE `creature_template` SET `AIName`= 'SmartAI' WHERE `entry`=16046; + +DELETE FROM `smart_scripts` WHERE `entryorguid` =16046; +INSERT INTO `smart_scripts` (`entryorguid`, `source_type`, `id`, `link`, `event_type`, `event_phase_mask`, `event_chance`, `event_flags`, `event_param1`, `event_param2`, `event_param3`, `event_param4`, `action_type`, `action_param1`, `action_param2`, `action_param3`, `action_param4`, `action_param5`, `action_param6`, `target_type`, `target_param1`, `target_param2`, `target_param3`, `target_x`, `target_y`, `target_z`, `target_o`, `comment`) VALUES +(16046, 0, 0, 1, 38, 0, 100, 1, 1, 1, 0, 0, 12, 16101, 8, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 'Jarien and Sothos Trigger - On Data Set - Spawn Jarien'), +(16046, 0, 1, 0, 61, 0, 100, 1, 1, 1, 0, 0, 12, 16102, 8, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 'Jarien and Sothos Trigger - Linked with Previous Event - Spawn Sothos'); + +UPDATE `creature_template` SET `AIName`= 'SmartAI' WHERE `entry`=16047; + +DELETE FROM `smart_scripts` WHERE `entryorguid` =16047; +INSERT INTO `smart_scripts` (`entryorguid`, `source_type`, `id`, `link`, `event_type`, `event_phase_mask`, `event_chance`, `event_flags`, `event_param1`, `event_param2`, `event_param3`, `event_param4`, `action_type`, `action_param1`, `action_param2`, `action_param3`, `action_param4`, `action_param5`, `action_param6`, `target_type`, `target_param1`, `target_param2`, `target_param3`, `target_x`, `target_y`, `target_z`, `target_o`, `comment`) VALUES +(16047, 0, 0, 0, 38, 0, 100, 1, 1, 1, 0, 0, 12, 16118, 8, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 'Kormok Trigger - On Data Set - Spawn Kormok'); + +UPDATE `creature_template` SET `AIName`= 'SmartAI' WHERE `entry`=16048; + +DELETE FROM `smart_scripts` WHERE `entryorguid` =16048; +INSERT INTO `smart_scripts` (`entryorguid`, `source_type`, `id`, `link`, `event_type`, `event_phase_mask`, `event_chance`, `event_flags`, `event_param1`, `event_param2`, `event_param3`, `event_param4`, `action_type`, `action_param1`, `action_param2`, `action_param3`, `action_param4`, `action_param5`, `action_param6`, `target_type`, `target_param1`, `target_param2`, `target_param3`, `target_x`, `target_y`, `target_z`, `target_o`, `comment`) VALUES +(16048, 0, 0, 0, 38, 0, 100, 1, 1, 1, 0, 0, 12, 16042, 8, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 'Lord Valthalak Trigger - On Data Set - Spawn Lord Valthalak'); From d4cec6b397839ee7ff8bc501c5069dad31787116 Mon Sep 17 00:00:00 2001 From: Aokromes Date: Tue, 1 Jul 2014 16:14:14 +0200 Subject: [PATCH 69/80] DB/Misc: Fix some startup errors By dr-j --- sql/updates/world/2014_07_01_01_world_misc.sql | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 sql/updates/world/2014_07_01_01_world_misc.sql diff --git a/sql/updates/world/2014_07_01_01_world_misc.sql b/sql/updates/world/2014_07_01_01_world_misc.sql new file mode 100644 index 0000000000..9806235da5 --- /dev/null +++ b/sql/updates/world/2014_07_01_01_world_misc.sql @@ -0,0 +1,7 @@ +-- +UPDATE `smart_scripts` SET `action_type`=11, `action_param1`=48830 WHERE `entryorguid`=27225 AND `source_type`=0 AND `id`=0 AND `link`=1; +UPDATE `smart_scripts` SET `action_type`=11, `action_param1`=48829 WHERE `entryorguid`=27226 AND `source_type`=0 AND `id`=0 AND `link`=1; +UPDATE `smart_scripts` SET `action_type`=11, `action_param1`=48832 WHERE `entryorguid`=27229 AND `source_type`=0 AND `id`=0 AND `link`=1; +UPDATE `smart_scripts` SET `action_type`=11, `action_param1`=48831 WHERE `entryorguid`=27224 AND `source_type`=0 AND `id`=0 AND `link`=1; +DELETE FROM `conditions` WHERE `SourceTypeOrReferenceId`=13 AND `SourceGroup`=1 AND `SourceEntry`=49734 AND `SourceId`=0 AND `ElseGroup`=0 AND `ConditionTypeOrReference`=31 AND `ConditionTarget`=0 AND `ConditionValue1`=3 AND `ConditionValue2`=27851 AND `ConditionValue3`=0; +DELETE FROM `conditions` WHERE `SourceTypeOrReferenceId`=13 AND `SourceEntry` IN (69548,69542,69544,69543); From 2a3546ca36468b81d673bdb27315006144af3165 Mon Sep 17 00:00:00 2001 From: Shauren Date: Tue, 1 Jul 2014 17:20:48 +0200 Subject: [PATCH 70/80] Core/Skills: Use SkillLineAbility.dbc to determine player initial spells - skill assignment done in a new table `playercreateinfo_skills` * Removed DK start skill hacks * Resolved missing skills in character skill tab Closes #12311 --- ...4_07_01_00_characters_character_skills.sql | 4 + ...07_01_02_world playercreateinfo_skills.sql | 90 ++++++ src/server/game/DataStores/DBCEnums.h | 6 +- src/server/game/DataStores/DBCStores.cpp | 2 +- src/server/game/DataStores/DBCStructure.h | 19 +- src/server/game/Entities/Player/Player.cpp | 257 ++++++++++-------- src/server/game/Entities/Player/Player.h | 15 +- src/server/game/Entities/Unit/Unit.cpp | 10 +- src/server/game/Globals/ObjectMgr.cpp | 131 ++++++--- src/server/game/Globals/ObjectMgr.h | 2 +- src/server/game/Spells/SpellInfo.cpp | 2 +- src/server/game/Spells/SpellMgr.cpp | 2 +- src/server/scripts/Commands/cs_learn.cpp | 3 +- 13 files changed, 368 insertions(+), 175 deletions(-) create mode 100644 sql/updates/characters/2014_07_01_00_characters_character_skills.sql create mode 100644 sql/updates/world/2014_07_01_02_world playercreateinfo_skills.sql diff --git a/sql/updates/characters/2014_07_01_00_characters_character_skills.sql b/sql/updates/characters/2014_07_01_00_characters_character_skills.sql new file mode 100644 index 0000000000..1987adb064 --- /dev/null +++ b/sql/updates/characters/2014_07_01_00_characters_character_skills.sql @@ -0,0 +1,4 @@ +-- Set fist weapon skill equal to current unarmed skill value +UPDATE character_skills cs_unarmed INNER JOIN character_skills cs_fist ON cs_unarmed.guid = cs_fist.guid +SET cs_fist.value = cs_unarmed.value, cs_fist.max = cs_unarmed.max +WHERE cs_unarmed.skill = 162 AND cs_fist.skill = 473; diff --git a/sql/updates/world/2014_07_01_02_world playercreateinfo_skills.sql b/sql/updates/world/2014_07_01_02_world playercreateinfo_skills.sql new file mode 100644 index 0000000000..8eb452afa9 --- /dev/null +++ b/sql/updates/world/2014_07_01_02_world playercreateinfo_skills.sql @@ -0,0 +1,90 @@ +DROP TABLE IF EXISTS `playercreateinfo_spells`; + +DROP TABLE IF EXISTS `playercreateinfo_skills`; +CREATE TABLE `playercreateinfo_skills` ( + `raceMask` int(10) unsigned NOT NULL, + `classMask` int(10) unsigned NOT NULL, + `skill` smallint(5) unsigned NOT NULL, + `rank` smallint(5) unsigned NOT NULL DEFAULT '0', + `comment` varchar(255) DEFAULT NULL, + PRIMARY KEY (`raceMask`,`classMask`,`skill`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +INSERT INTO `playercreateinfo_skills` VALUES +(0,0,95,0,'Defense'), +(0,0,162,0,'Unarmed'), +(0,0,183,0,'GENERIC (DND)'), +(0,0,415,0,'Cloth'), +(0,0,777,0,'Mounts'), +(0,0,778,0,'Companion Pets'), +(0,1,26,0,'Warrior - Arms'), +(0,1,256,0,'Warrior - Fury'), +(0,1,257,0,'Warrior - Protection'), +(0,2,184,0,'Paladin - Retribution'), +(0,2,267,0,'Paladin - Protection'), +(0,2,594,0,'Paladin - Holy'), +(0,4,50,0,'Hunter - Beast Mastery'), +(0,4,51,0,'Hunter - Survival'), +(0,4,163,0,'Hunter - Marksmanship'), +(0,8,38,0,'Rogue - Combat'), +(0,8,39,0,'Rogue - Subtlety'), +(0,8,176,0,'Thrown'), +(0,8,253,0,'Rogue - Assassination'), +(0,16,56,0,'Priest - Holy'), +(0,16,78,0,'Priest - Shadow'), +(0,16,613,0,'Priest - Discipline'), +(0,32,129,4,'Death Knight - First Aid'), +(0,32,229,0,'Polearms'), +(0,32,293,0,'Plate'), +(0,32,762,0,'Death Knight - Riding'), +(0,32,770,0,'Death Knight - Blood'), +(0,32,771,0,'Death Knight - Frost'), +(0,32,772,0,'Death Knight - Unholy'), +(0,35,55,0,'Two-Handed Swords'), +(0,35,413,0,'Mail'), +(0,37,44,0,'Axes'), +(0,37,172,0,'Two-Handed Axes'), +(0,39,43,0,'Swords'), +(0,40,118,0,'Dual Wield'), +(0,64,373,0,'Shaman - Enhancement'), +(0,64,374,0,'Shaman - Restoration'), +(0,64,375,0,'Shaman - Elemental'), +(0,67,433,0,'Shield'), +(0,128,6,0,'Mage - Frost'), +(0,128,8,0,'Mage - Fire'), +(0,128,237,0,'Mage - Arcane'), +(0,256,354,0,'Warlock - Demonology'), +(0,256,355,0,'Warlock - Affliction'), +(0,256,593,0,'Warlock - Destruction'), +(0,400,228,0,'Wands'), +(0,1024,134,0,'Druid - Feral'), +(0,1024,573,0,'Druid - Restoration'), +(0,1024,574,0,'Druid - Balance'), +(0,1107,54,0,'Maces'), +(0,1135,414,0,'Leather'), +(0,1488,136,0,'Staves'), +(1,0,754,0,'Human - Racial'), +(2,0,125,0,'Orc - Racial'), +(4,0,101,0,'Dwarf - Racial'), +(4,0,111,0,'Language: Dwarven'), +(8,0,126,0,'Night Elf - Racial'), +(8,0,137,0,'Language: Thalassian'), +(16,0,220,0,'Undead - Racial'), +(16,0,673,0,'Language: Forsaken'), +(32,0,113,0,'Language: Darnassian'), +(32,0,124,0,'Tauren - Racial'), +(36,4,46,0,'Guns'), +(64,0,313,0,'Language: Gnomish'), +(64,0,753,0,'Gnome - Racial'), +(128,0,315,0,'Language: Troll'), +(128,0,733,0,'Troll - Racial'), +(512,0,115,0,'Language: Taurahe'), +(512,0,756,0,'Blood Elf - Racial'), +(650,4,45,0,'Bows'), +(690,0,109,0,'Language: Orcish'), +(735,1293,173,0,'Daggers'), +(1024,0,759,0,'Language: Draenei'), +(1024,0,760,0,'Draenei - Racial'), +(1024,4,226,0,'Crossbows'), +(1061,3,160,0,'Two-Handed Maces'), +(1101,0,98,0,'Language: Common'); diff --git a/src/server/game/DataStores/DBCEnums.h b/src/server/game/DataStores/DBCEnums.h index 10b7c25bb1..03180b35cb 100644 --- a/src/server/game/DataStores/DBCEnums.h +++ b/src/server/game/DataStores/DBCEnums.h @@ -326,8 +326,8 @@ enum MapFlags enum AbilytyLearnType { - ABILITY_LEARNED_ON_GET_PROFESSION_SKILL = 1, - ABILITY_LEARNED_ON_GET_RACE_OR_CLASS_SKILL = 2 + SKILL_LINE_ABILITY_LEARNED_ON_SKILL_VALUE = 1, // Spell state will update depending on skill value + SKILL_LINE_ABILITY_LEARNED_ON_SKILL_LEARN = 2 // Spell will be learned/removed together with entire skill }; enum ItemEnchantmentType @@ -356,7 +356,7 @@ enum SkillRaceClassInfoFlags SKILL_FLAG_UNLEARNABLE = 0x20, // Skill can be unlearned SKILL_FLAG_INCLUDE_IN_SORT = 0x80, // Spells belonging to a skill with this flag will additionally compare skill ids when sorting spellbook in client SKILL_FLAG_NOT_TRAINABLE = 0x100, - SKILL_FLAG_MONO_VALUE = 0x400 // Skill always has value 1 + SKILL_FLAG_MONO_VALUE = 0x400 // Skill always has value 1 - clientside display flag, real value can be different }; enum SpellCategoryFlags diff --git a/src/server/game/DataStores/DBCStores.cpp b/src/server/game/DataStores/DBCStores.cpp index 92d00b2064..e960422cbb 100644 --- a/src/server/game/DataStores/DBCStores.cpp +++ b/src/server/game/DataStores/DBCStores.cpp @@ -450,7 +450,7 @@ void LoadDBCStores(const std::string& dataPath) if (spellInfo->spellLevel) continue; - if (skillLine->learnOnGetSkill != ABILITY_LEARNED_ON_GET_RACE_OR_CLASS_SKILL) + if (skillLine->AutolearnType != SKILL_LINE_ABILITY_LEARNED_ON_SKILL_LEARN) continue; sPetFamilySpellsStore[i].insert(spellInfo->Id); diff --git a/src/server/game/DataStores/DBCStructure.h b/src/server/game/DataStores/DBCStructure.h index 2da166fb04..946cbc42f2 100644 --- a/src/server/game/DataStores/DBCStructure.h +++ b/src/server/game/DataStores/DBCStructure.h @@ -1535,23 +1535,6 @@ struct ScalingStatValuesEntry // uint32 displayOrder; // 19 m_sortIndex //}; -//struct SkillRaceClassInfoEntry{ -// uint32 id; // 0 m_ID -// uint32 skillId; // 1 m_skillID -// uint32 raceMask; // 2 m_raceMask -// uint32 classMask; // 3 m_classMask -// uint32 flags; // 4 m_flags -// uint32 reqLevel; // 5 m_minLevel -// uint32 skillTierId; // 6 m_skillTierID -// uint32 skillCostID; // 7 m_skillCostIndex -//}; - -//struct SkillTiersEntry{ -// uint32 id; // 0 m_ID -// uint32 skillValue[16]; // 1-17 m_cost -// uint32 maxSkillValue[16]; // 18-32 m_valueMax -//}; - struct SkillLineEntry { uint32 id; // 0 m_ID @@ -1578,7 +1561,7 @@ struct SkillLineAbilityEntry //uint32 classmaskNot; // 6 m_excludeClass uint32 req_skill_value; // 7 m_minSkillLineRank uint32 forward_spellid; // 8 m_supercededBySpell - uint32 learnOnGetSkill; // 9 m_acquireMethod + uint32 AutolearnType; // 9 m_acquireMethod uint32 max_value; // 10 m_trivialSkillLineRankHigh uint32 min_value; // 11 m_trivialSkillLineRankLow //uint32 characterPoints[2]; // 12-13 m_characterPoints[2] diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index 8653106a86..b32ef34edb 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -1134,7 +1134,8 @@ bool Player::Create(uint32 guidlow, CharacterCreateInfo* createInfo) } // original spells - learnDefaultSpells(); + LearnDefaultSkills(); + LearnCustomSpells(); // original action bar for (PlayerCreateInfoActions::const_iterator action_itr = info->action.begin(); action_itr != info->action.end(); ++action_itr) @@ -3827,34 +3828,14 @@ bool Player::addSpell(uint32 spellId, bool active, bool learning, bool dependent if (!pSkill) continue; - if (!Has310Flyer(false) && pSkill->id == SKILL_MOUNTS) + if (!HasSkill(pSkill->id)) + LearnDefaultSkill(pSkill->id, 0); + + if (pSkill->id == SKILL_MOUNTS && !Has310Flyer(false)) for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i) if (spellInfo->Effects[i].ApplyAuraName == SPELL_AURA_MOD_INCREASE_MOUNTED_FLIGHT_SPEED && spellInfo->Effects[i].CalcValue() == 310) SetHas310Flyer(true); - - if (HasSkill(pSkill->id)) - continue; - - if (_spell_idx->second->learnOnGetSkill == ABILITY_LEARNED_ON_GET_RACE_OR_CLASS_SKILL || - // lockpicking/runeforging special case, not have ABILITY_LEARNED_ON_GET_RACE_OR_CLASS_SKILL - ((pSkill->id == SKILL_LOCKPICKING || pSkill->id == SKILL_RUNEFORGING) && _spell_idx->second->max_value == 0)) - { - switch (GetSkillRangeType(pSkill, _spell_idx->second->racemask != 0)) - { - case SKILL_RANGE_LANGUAGE: - SetSkill(pSkill->id, GetSkillStep(pSkill->id), 300, 300); - break; - case SKILL_RANGE_LEVEL: - SetSkill(pSkill->id, GetSkillStep(pSkill->id), 1, GetMaxSkillValueForLevel()); - break; - case SKILL_RANGE_MONO: - SetSkill(pSkill->id, GetSkillStep(pSkill->id), 1, 1); - break; - default: - break; - } - } } } @@ -4079,33 +4060,27 @@ void Player::removeSpell(uint32 spell_id, bool disabled, bool learn_low_rank) // not ranked skills SkillLineAbilityMapBounds bounds = sSpellMgr->GetSkillLineAbilityMapBounds(spell_id); - for (SkillLineAbilityMap::const_iterator _spell_idx = bounds.first; _spell_idx != bounds.second; ++_spell_idx) + // most likely will never be used, haven't heard of cases where players unlearn a mount + if (Has310Flyer(false) && spellInfo) { - SkillLineEntry const* pSkill = sSkillLineStore.LookupEntry(_spell_idx->second->skillId); - if (!pSkill) - continue; - - if ((_spell_idx->second->learnOnGetSkill == ABILITY_LEARNED_ON_GET_RACE_OR_CLASS_SKILL && - pSkill->categoryId != SKILL_CATEGORY_CLASS) ||// not unlearn class skills (spellbook/talent pages) - // lockpicking/runeforging special case, not have ABILITY_LEARNED_ON_GET_RACE_OR_CLASS_SKILL - ((pSkill->id == SKILL_LOCKPICKING || pSkill->id == SKILL_RUNEFORGING) && _spell_idx->second->max_value == 0)) + for (SkillLineAbilityMap::const_iterator _spell_idx = bounds.first; _spell_idx != bounds.second; ++_spell_idx) { - // not reset skills for professions and racial abilities - if ((pSkill->categoryId == SKILL_CATEGORY_SECONDARY || pSkill->categoryId == SKILL_CATEGORY_PROFESSION) && - (IsProfessionSkill(pSkill->id) || _spell_idx->second->racemask != 0)) + SkillLineEntry const* pSkill = sSkillLineStore.LookupEntry(_spell_idx->second->skillId); + if (!pSkill) continue; - SetSkill(pSkill->id, GetSkillStep(pSkill->id), 0, 0); - } - - // most likely will never be used, haven't heard of cases where players unlearn a mount - if (Has310Flyer(false) && _spell_idx->second->skillId == SKILL_MOUNTS) - { - if (spellInfo) + if (_spell_idx->second->skillId == SKILL_MOUNTS) + { for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i) + { if (spellInfo->Effects[i].ApplyAuraName == SPELL_AURA_MOD_INCREASE_MOUNTED_FLIGHT_SPEED && spellInfo->Effects[i].CalcValue() == 310) + { Has310Flyer(true, spell_id); // with true as first argument its also used to set/remove the flag + break; + } + } + } } } } @@ -6080,9 +6055,6 @@ bool Player::UpdateSkill(uint32 skill_id, uint32 step) if (!skill_id) return false; - if (skill_id == SKILL_FIST_WEAPONS) - skill_id = SKILL_UNARMED; - SkillStatusMap::iterator itr = mSkillStatus.find(skill_id); if (itr == mSkillStatus.end() || itr->second.uState == SKILL_DELETED) return false; @@ -6097,13 +6069,14 @@ bool Player::UpdateSkill(uint32 skill_id, uint32 step) if (value < max) { - uint32 new_value = value+step; + uint32 new_value = value + step; if (new_value > max) new_value = max; SetUInt32Value(valueIndex, MAKE_SKILL_VALUE(new_value, max)); if (itr->second.uState != SKILL_NEW) itr->second.uState = SKILL_CHANGED; + UpdateSkillEnchantments(skill_id, value, new_value); UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_REACH_SKILL_LEVEL, skill_id); return true; @@ -6277,9 +6250,25 @@ void Player::UpdateWeaponSkill(WeaponAttackType attType) Item* tmpitem = GetWeaponForAttack(attType, true); if (!tmpitem && attType == BASE_ATTACK) + { + // Keep unarmed & fist weapon skills in sync UpdateSkill(SKILL_UNARMED, weapon_skill_gain); + UpdateSkill(SKILL_FIST_WEAPONS, weapon_skill_gain); + } else if (tmpitem && tmpitem->GetTemplate()->SubClass != ITEM_SUBCLASS_WEAPON_FISHING_POLE) - UpdateSkill(tmpitem->GetSkill(), weapon_skill_gain); + { + switch (tmpitem->GetTemplate()->SubClass) + { + case ITEM_SUBCLASS_WEAPON_FISHING_POLE: + break; + case ITEM_SUBCLASS_WEAPON_FIST: + UpdateSkill(SKILL_UNARMED, weapon_skill_gain); + // no break intended + default: + UpdateSkill(tmpitem->GetSkill(), weapon_skill_gain); + break; + } + } UpdateAllCritPercentages(); } @@ -6352,11 +6341,11 @@ void Player::UpdateSkillsForLevel() continue; uint32 pskill = itr->first; - SkillLineEntry const* pSkill = sSkillLineStore.LookupEntry(pskill); - if (!pSkill) + SkillRaceClassInfoEntry const* rcEntry = GetSkillRaceClassInfo(pskill, getRace(), getClass()); + if (!rcEntry) continue; - if (GetSkillRangeType(pSkill, false) != SKILL_RANGE_LEVEL) + if (GetSkillRangeType(rcEntry) != SKILL_RANGE_LEVEL) continue; uint32 valueIndex = PLAYER_SKILL_VALUE_INDEX(itr->second.pos); @@ -17704,7 +17693,8 @@ bool Player::LoadFromDB(uint32 guid, SQLQueryHolder *holder) // after spell and quest load InitTalentForLevel(); - learnDefaultSpells(); + LearnDefaultSkills(); + LearnCustomSpells(); // must be before inventory (some items required reputation check) m_reputationMgr->LoadFromDB(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_REPUTATION)); @@ -23078,15 +23068,19 @@ void Player::resetSpells(bool myClassOnly) for (PlayerSpellMap::const_iterator iter = smap.begin(); iter != smap.end(); ++iter) removeSpell(iter->first, false, false); // only iter->first can be accessed, object by iter->second can be deleted already - learnDefaultSpells(); + LearnDefaultSkills(); + LearnCustomSpells(); learnQuestRewardedSpells(); } -void Player::learnDefaultSpells() +void Player::LearnCustomSpells() { + if (!sWorld->getBoolConfig(CONFIG_START_ALL_SPELLS)) + return; + // learn default race/class spells PlayerInfo const* info = sObjectMgr->GetPlayerInfo(getRace(), getClass()); - for (PlayerCreateInfoSpells::const_iterator itr = info->spell.begin(); itr != info->spell.end(); ++itr) + for (PlayerCreateInfoSpells::const_iterator itr = info->customSpells.begin(); itr != info->customSpells.end(); ++itr) { uint32 tspell = *itr; TC_LOG_DEBUG("entities.player.loading", "PLAYER (Class: %u Race: %u): Adding initial spell, id = %u", uint32(getClass()), uint32(getRace()), tspell); @@ -23097,6 +23091,63 @@ void Player::learnDefaultSpells() } } +void Player::LearnDefaultSkills() +{ + // learn default race/class skills + PlayerInfo const* info = sObjectMgr->GetPlayerInfo(getRace(), getClass()); + for (PlayerCreateInfoSkills::const_iterator itr = info->skills.begin(); itr != info->skills.end(); ++itr) + { + uint32 skillId = itr->SkillId; + if (HasSkill(skillId)) + continue; + + LearnDefaultSkill(skillId, itr->Rank); + } +} + +void Player::LearnDefaultSkill(uint32 skillId, uint16 rank) +{ + SkillRaceClassInfoEntry const* rcInfo = GetSkillRaceClassInfo(skillId, getRace(), getClass()); + if (!rcInfo) + return; + + TC_LOG_DEBUG("entities.player.loading", "PLAYER (Class: %u Race: %u): Adding initial skill, id = %u", uint32(getClass()), uint32(getRace()), skillId); + switch (GetSkillRangeType(rcInfo)) + { + case SKILL_RANGE_LANGUAGE: + SetSkill(skillId, 0, 300, 300); + break; + case SKILL_RANGE_LEVEL: + { + uint16 skillValue = 0; + uint16 maxValue = GetMaxSkillValueForLevel(); + if (rcInfo->Flags & SKILL_FLAG_ALWAYS_MAX_VALUE) + skillValue = maxValue; + else + skillValue = std::min(std::max({ 1, uint16((getLevel() - 1) * 5) }), maxValue); + + SetSkill(skillId, 0, skillValue, maxValue); + break; + } + case SKILL_RANGE_MONO: + SetSkill(skillId, 0, 1, 1); + break; + case SKILL_RANGE_RANK: + { + if (!rank) + break; + + SkillTiersEntry const* tier = sSkillTiersStore.LookupEntry(rcInfo->SkillTier); + uint16 maxValue = std::max(GetMaxSkillValue(skillId), tier->MaxSkill[std::max(rank - 1, 0)]); + uint16 skillValue = std::min(std::max({ uint16(1), uint16((getLevel() - 1) * 5) }), maxValue); + SetSkill(skillId, rank, skillValue, maxValue); + break; + } + default: + break; + } +} + void Player::learnQuestRewardedSpells(Quest const* quest) { int32 spell_id = quest->GetRewSpellCast(); @@ -23194,29 +23245,35 @@ void Player::learnSkillRewardedSpells(uint32 skill_id, uint32 skill_value) { uint32 raceMask = getRaceMask(); uint32 classMask = getClassMask(); - for (uint32 j=0; jskillId != skill_id || pAbility->learnOnGetSkill != ABILITY_LEARNED_ON_GET_PROFESSION_SKILL) + if (!pAbility || pAbility->skillId != skill_id) continue; + + SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(pAbility->spellId); + if (!spellInfo) + continue; + + if (pAbility->AutolearnType != SKILL_LINE_ABILITY_LEARNED_ON_SKILL_VALUE && pAbility->AutolearnType != SKILL_LINE_ABILITY_LEARNED_ON_SKILL_LEARN) + continue; + // Check race if set if (pAbility->racemask && !(pAbility->racemask & raceMask)) continue; + // Check class if set if (pAbility->classmask && !(pAbility->classmask & classMask)) continue; - if (sSpellMgr->GetSpellInfo(pAbility->spellId)) - { - // need unlearn spell - if (skill_value < pAbility->req_skill_value) - removeSpell(pAbility->spellId); - // need learn - else if (!IsInWorld()) - addSpell(pAbility->spellId, true, true, true, false); - else - learnSpell(pAbility->spellId, true); - } + // need unlearn spell + if (skill_value < pAbility->req_skill_value && pAbility->AutolearnType == SKILL_LINE_ABILITY_LEARNED_ON_SKILL_VALUE) + removeSpell(pAbility->spellId); + // need learn + else if (!IsInWorld()) + addSpell(pAbility->spellId, true, true, true, false); + else + learnSpell(pAbility->spellId, true); } } @@ -23944,7 +24001,7 @@ bool Player::IsAtRecruitAFriendDistance(WorldObject const* pOther) const return pOther->GetDistance(player) <= sWorld->getFloatConfig(CONFIG_MAX_RECRUIT_A_FRIEND_DISTANCE); } -uint32 Player::GetBaseWeaponSkillValue (WeaponAttackType attType) const +uint32 Player::GetBaseWeaponSkillValue(WeaponAttackType attType) const { Item* item = GetWeaponForAttack(attType, true); @@ -23952,8 +24009,8 @@ uint32 Player::GetBaseWeaponSkillValue (WeaponAttackType attType) const if (attType != BASE_ATTACK && !item) return 0; - // weapon skill or (unarmed for base attack and for fist weapons) - uint32 skill = (item && item->GetSkill() != SKILL_FIST_WEAPONS) ? item->GetSkill() : uint32(SKILL_UNARMED); + // weapon skill or (unarmed for base attack) + uint32 skill = item ? item->GetSkill() : uint32(SKILL_UNARMED); return GetBaseSkillValue(skill); } @@ -24907,15 +24964,15 @@ void Player::_LoadSkills(PreparedQueryResult result) uint16 value = fields[1].GetUInt16(); uint16 max = fields[2].GetUInt16(); - SkillLineEntry const* pSkill = sSkillLineStore.LookupEntry(skill); - if (!pSkill) + SkillRaceClassInfoEntry const* rcEntry = GetSkillRaceClassInfo(skill, getRace(), getClass()); + if (!rcEntry) { TC_LOG_ERROR("entities.player", "Character %u has skill %u that does not exist.", GetGUIDLow(), skill); continue; } // set fixed skill ranges - switch (GetSkillRangeType(pSkill, false)) + switch (GetSkillRangeType(rcEntry)) { case SKILL_RANGE_LANGUAGE: // 300..300 value = max = 300; @@ -24923,9 +24980,12 @@ void Player::_LoadSkills(PreparedQueryResult result) case SKILL_RANGE_MONO: // 1..1, grey monolite bar value = max = 1; break; + case SKILL_RANGE_LEVEL: + max = GetMaxSkillValueForLevel(); default: break; } + if (value == 0) { TC_LOG_ERROR("entities.player", "Character %u has skill %u with value 0. Will be deleted.", GetGUIDLow(), skill); @@ -24940,11 +25000,20 @@ void Player::_LoadSkills(PreparedQueryResult result) continue; } - // enable unlearn button for primary professions only - if (pSkill->categoryId == SKILL_CATEGORY_PROFESSION) - SetUInt32Value(PLAYER_SKILL_INDEX(count), MAKE_PAIR32(skill, 1)); - else - SetUInt32Value(PLAYER_SKILL_INDEX(count), MAKE_PAIR32(skill, 0)); + uint16 skillStep = 0; + if (SkillTiersEntry const* skillTier = sSkillTiersStore.LookupEntry(rcEntry->SkillTier)) + { + for (uint32 i = 0; i < MAX_SKILL_STEP; ++i) + { + if (skillTier->MaxSkill[skillStep] == max) + { + skillStep = i + 1; + break; + } + } + } + + SetUInt32Value(PLAYER_SKILL_INDEX(count), MAKE_PAIR32(skill, skillStep)); SetUInt32Value(PLAYER_SKILL_VALUE_INDEX(count), MAKE_SKILL_VALUE(value, max)); SetUInt32Value(PLAYER_SKILL_BONUS_INDEX(count), 0); @@ -24970,34 +25039,6 @@ void Player::_LoadSkills(PreparedQueryResult result) SetUInt32Value(PLAYER_SKILL_VALUE_INDEX(count), 0); SetUInt32Value(PLAYER_SKILL_BONUS_INDEX(count), 0); } - - // special settings - if (getClass() == CLASS_DEATH_KNIGHT) - { - uint8 base_level = std::min(getLevel(), uint8(sWorld->getIntConfig(CONFIG_START_HEROIC_PLAYER_LEVEL))); - if (base_level < 1) - base_level = 1; - uint16 base_skill = (base_level-1)*5; // 270 at starting level 55 - if (base_skill < 1) - base_skill = 1; // skill mast be known and then > 0 in any case - - if (GetPureSkillValue(SKILL_FIRST_AID) < base_skill) - SetSkill(SKILL_FIRST_AID, 4 /*artisan*/, base_skill, 300); - if (GetPureSkillValue(SKILL_AXES) < base_skill) - SetSkill(SKILL_AXES, 0, base_skill, base_skill); - if (GetPureSkillValue(SKILL_DEFENSE) < base_skill) - SetSkill(SKILL_DEFENSE, 0, base_skill, base_skill); - if (GetPureSkillValue(SKILL_POLEARMS) < base_skill) - SetSkill(SKILL_POLEARMS, 0, base_skill, base_skill); - if (GetPureSkillValue(SKILL_SWORDS) < base_skill) - SetSkill(SKILL_SWORDS, 0, base_skill, base_skill); - if (GetPureSkillValue(SKILL_2H_AXES) < base_skill) - SetSkill(SKILL_2H_AXES, 0, base_skill, base_skill); - if (GetPureSkillValue(SKILL_2H_SWORDS) < base_skill) - SetSkill(SKILL_2H_SWORDS, 0, base_skill, base_skill); - if (GetPureSkillValue(SKILL_UNARMED) < base_skill) - SetSkill(SKILL_UNARMED, 0, base_skill, base_skill); - } } uint32 Player::GetPhaseMaskForSpawn() const diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h index fdebbde0ae..5f500b7f1d 100644 --- a/src/server/game/Entities/Player/Player.h +++ b/src/server/game/Entities/Player/Player.h @@ -249,6 +249,14 @@ struct PlayerCreateInfoAction typedef std::list PlayerCreateInfoActions; +struct PlayerCreateInfoSkill +{ + uint16 SkillId; + uint16 Rank; +}; + +typedef std::list PlayerCreateInfoSkills; + struct PlayerInfo { // existence checked by displayId != 0 @@ -263,8 +271,9 @@ struct PlayerInfo uint16 displayId_m; uint16 displayId_f; PlayerCreateInfoItems item; - PlayerCreateInfoSpells spell; + PlayerCreateInfoSpells customSpells; PlayerCreateInfoActions action; + PlayerCreateInfoSkills skills; PlayerLevelInfo* levelInfo; //[level-1] 0..MaxPlayerLevel-1 }; @@ -1573,7 +1582,9 @@ class Player : public Unit, public GridObject void learnSpell(uint32 spell_id, bool dependent); void removeSpell(uint32 spell_id, bool disabled = false, bool learn_low_rank = true); void resetSpells(bool myClassOnly = false); - void learnDefaultSpells(); + void LearnCustomSpells(); + void LearnDefaultSkills(); + void LearnDefaultSkill(uint32 skillId, uint16 rank); void learnQuestRewardedSpells(); void learnQuestRewardedSpells(Quest const* quest); void learnSpellHighRank(uint32 spellid); diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp index 5eb48160e8..154688b2cf 100644 --- a/src/server/game/Entities/Unit/Unit.cpp +++ b/src/server/game/Entities/Unit/Unit.cpp @@ -2834,12 +2834,10 @@ uint32 Unit::GetWeaponSkillValue (WeaponAttackType attType, Unit const* target) if (IsInFeralForm()) return GetMaxSkillValueForLevel(); // always maximized SKILL_FERAL_COMBAT in fact - // weapon skill or (unarmed for base attack and fist weapons) - uint32 skill; - if (item && item->GetSkill() != SKILL_FIST_WEAPONS) + // weapon skill or (unarmed for base attack) + uint32 skill = SKILL_UNARMED; + if (item) skill = item->GetSkill(); - else - skill = SKILL_UNARMED; // in PvP use full skill instead current skill value value = (target && target->IsControlledByPlayer()) @@ -15288,7 +15286,7 @@ void Unit::Kill(Unit* victim, bool durabilityLoss) group->SendLooter(creature, NULL); // Update round robin looter only if the creature had loot - if (!creature->loot.empty()) + if (!loot->empty()) group->UpdateLooterGuid(creature); } } diff --git a/src/server/game/Globals/ObjectMgr.cpp b/src/server/game/Globals/ObjectMgr.cpp index 9eb7d5ec1b..278460e73d 100644 --- a/src/server/game/Globals/ObjectMgr.cpp +++ b/src/server/game/Globals/ObjectMgr.cpp @@ -3322,17 +3322,91 @@ void ObjectMgr::LoadPlayerInfo() } } + + // Load playercreate skills + TC_LOG_INFO("server.loading", "Loading Player Create Skill Data..."); + { + uint32 oldMSTime = getMSTime(); + + QueryResult result = WorldDatabase.PQuery("SELECT raceMask, classMask, skill, rank FROM playercreateinfo_skills"); + + if (!result) + { + TC_LOG_ERROR("server.loading", ">> Loaded 0 player create skills. DB table `playercreateinfo_skills` is empty."); + } + else + { + uint32 count = 0; + + do + { + Field* fields = result->Fetch(); + uint32 raceMask = fields[0].GetUInt32(); + uint32 classMask = fields[1].GetUInt32(); + PlayerCreateInfoSkill skill; + skill.SkillId = fields[2].GetUInt16(); + skill.Rank = fields[3].GetUInt16(); + + if (skill.Rank >= MAX_SKILL_STEP) + { + TC_LOG_ERROR("sql.sql", "Skill rank value %hu set for skill %hu raceMask %u classMask %u is too high, max allowed value is %d", skill.Rank, skill.SkillId, raceMask, classMask, MAX_SKILL_STEP); + continue; + } + + if (raceMask != 0 && !(raceMask & RACEMASK_ALL_PLAYABLE)) + { + TC_LOG_ERROR("sql.sql", "Wrong race mask %u in `playercreateinfo_skills` table, ignoring.", raceMask); + continue; + } + + if (classMask != 0 && !(classMask & CLASSMASK_ALL_PLAYABLE)) + { + TC_LOG_ERROR("sql.sql", "Wrong class mask %u in `playercreateinfo_skills` table, ignoring.", classMask); + continue; + } + + if (!sSkillLineStore.LookupEntry(skill.SkillId)) + { + TC_LOG_ERROR("sql.sql", "Wrong skill id %u in `playercreateinfo_skills` table, ignoring.", skill.SkillId); + continue; + } + + for (uint32 raceIndex = RACE_HUMAN; raceIndex < MAX_RACES; ++raceIndex) + { + if (raceMask == 0 || ((1 << (raceIndex - 1)) & raceMask)) + { + for (uint32 classIndex = CLASS_WARRIOR; classIndex < MAX_CLASSES; ++classIndex) + { + if (classMask == 0 || ((1 << (classIndex - 1)) & classMask)) + { + if (!GetSkillRaceClassInfo(skill.SkillId, raceIndex, classIndex)) + continue; + + if (PlayerInfo* info = _playerInfo[raceIndex][classIndex]) + { + info->skills.push_back(skill); + ++count; + } + } + } + } + } + } while (result->NextRow()); + + TC_LOG_INFO("server.loading", ">> Loaded %u player create skills in %u ms", count, GetMSTimeDiffToNow(oldMSTime)); + } + } + // Load playercreate spells TC_LOG_INFO("server.loading", "Loading Player Create Spell Data..."); { uint32 oldMSTime = getMSTime(); - std::string tableName = sWorld->getBoolConfig(CONFIG_START_ALL_SPELLS) ? "playercreateinfo_spell_custom" : "playercreateinfo_spell"; - QueryResult result = WorldDatabase.PQuery("SELECT racemask, classmask, Spell FROM %s", tableName.c_str()); + QueryResult result = WorldDatabase.PQuery("SELECT racemask, classmask, Spell FROM playercreateinfo_spell_custom"); if (!result) { - TC_LOG_ERROR("server.loading", ">> Loaded 0 player create spells. DB table `%s` is empty.", tableName.c_str()); + TC_LOG_ERROR("server.loading", ">> Loaded 0 player create spells. DB table `playercreateinfo_spell_custom` is empty."); } else { @@ -3347,13 +3421,13 @@ void ObjectMgr::LoadPlayerInfo() if (raceMask != 0 && !(raceMask & RACEMASK_ALL_PLAYABLE)) { - TC_LOG_ERROR("sql.sql", "Wrong race mask %u in `%s` table, ignoring.", raceMask, tableName.c_str()); + TC_LOG_ERROR("sql.sql", "Wrong race mask %u in `playercreateinfo_spell_custom` table, ignoring.", raceMask); continue; } if (classMask != 0 && !(classMask & CLASSMASK_ALL_PLAYABLE)) { - TC_LOG_ERROR("sql.sql", "Wrong class mask %u in `%s` table, ignoring.", classMask, tableName.c_str()); + TC_LOG_ERROR("sql.sql", "Wrong class mask %u in `playercreateinfo_spell_custom` table, ignoring.", classMask); continue; } @@ -3367,7 +3441,7 @@ void ObjectMgr::LoadPlayerInfo() { if (PlayerInfo* info = _playerInfo[raceIndex][classIndex]) { - info->spell.push_back(spellId); + info->customSpells.push_back(spellId); ++count; } // We need something better here, the check is not accounting for spells used by multiple races/classes but not all of them. @@ -3381,7 +3455,7 @@ void ObjectMgr::LoadPlayerInfo() } while (result->NextRow()); - TC_LOG_INFO("server.loading", ">> Loaded %u player create spells in %u ms", count, GetMSTimeDiffToNow(oldMSTime)); + TC_LOG_INFO("server.loading", ">> Loaded %u custom player create spells in %u ms", count, GetMSTimeDiffToNow(oldMSTime)); } } @@ -7808,36 +7882,27 @@ int32 ObjectMgr::GetBaseReputationOf(FactionEntry const* factionEntry, uint8 rac return 0; } -SkillRangeType GetSkillRangeType(SkillLineEntry const* pSkill, bool racial) +SkillRangeType GetSkillRangeType(SkillRaceClassInfoEntry const* rcEntry) { - switch (pSkill->categoryId) + SkillLineEntry const* skill = sSkillLineStore.LookupEntry(rcEntry->SkillId); + if (!skill) + return SKILL_RANGE_NONE; + + if (sSkillTiersStore.LookupEntry(rcEntry->SkillTier)) + return SKILL_RANGE_RANK; + + if (rcEntry->SkillId == SKILL_RUNEFORGING) + return SKILL_RANGE_MONO; + + switch (skill->categoryId) { - case SKILL_CATEGORY_LANGUAGES: return SKILL_RANGE_LANGUAGE; - case SKILL_CATEGORY_WEAPON: - if (pSkill->id != SKILL_FIST_WEAPONS) - return SKILL_RANGE_LEVEL; - else - return SKILL_RANGE_MONO; case SKILL_CATEGORY_ARMOR: - case SKILL_CATEGORY_CLASS: - if (pSkill->id != SKILL_LOCKPICKING) - return SKILL_RANGE_MONO; - else - return SKILL_RANGE_LEVEL; - case SKILL_CATEGORY_SECONDARY: - case SKILL_CATEGORY_PROFESSION: - // not set skills for professions and racial abilities - if (IsProfessionSkill(pSkill->id)) - return SKILL_RANGE_RANK; - else if (racial) - return SKILL_RANGE_NONE; - else - return SKILL_RANGE_MONO; - default: - case SKILL_CATEGORY_ATTRIBUTES: //not found in dbc - case SKILL_CATEGORY_GENERIC: //only GENERIC(DND) - return SKILL_RANGE_NONE; + return SKILL_RANGE_MONO; + case SKILL_CATEGORY_LANGUAGES: + return SKILL_RANGE_LANGUAGE; } + + return SKILL_RANGE_LEVEL; } void ObjectMgr::LoadGameTele() diff --git a/src/server/game/Globals/ObjectMgr.h b/src/server/game/Globals/ObjectMgr.h index e5e55b847d..3f8013bbd7 100644 --- a/src/server/game/Globals/ObjectMgr.h +++ b/src/server/game/Globals/ObjectMgr.h @@ -643,7 +643,7 @@ enum SkillRangeType SKILL_RANGE_NONE // 0..0 always }; -SkillRangeType GetSkillRangeType(SkillLineEntry const* pSkill, bool racial); +SkillRangeType GetSkillRangeType(SkillRaceClassInfoEntry const* rcEntry); #define MAX_PLAYER_NAME 12 // max allowed by client name length #define MAX_INTERNAL_PLAYER_NAME 15 // max server internal player name length (> MAX_PLAYER_NAME for support declined names) diff --git a/src/server/game/Spells/SpellInfo.cpp b/src/server/game/Spells/SpellInfo.cpp index 4d97dc97e5..8034002a47 100644 --- a/src/server/game/Spells/SpellInfo.cpp +++ b/src/server/game/Spells/SpellInfo.cpp @@ -967,7 +967,7 @@ bool SpellInfo::IsAbilityLearnedWithProfession() const for (SkillLineAbilityMap::const_iterator _spell_idx = bounds.first; _spell_idx != bounds.second; ++_spell_idx) { SkillLineAbilityEntry const* pAbility = _spell_idx->second; - if (!pAbility || pAbility->learnOnGetSkill != ABILITY_LEARNED_ON_GET_PROFESSION_SKILL) + if (!pAbility || pAbility->AutolearnType != SKILL_LINE_ABILITY_LEARNED_ON_SKILL_VALUE) continue; if (pAbility->req_skill_value > 0) diff --git a/src/server/game/Spells/SpellMgr.cpp b/src/server/game/Spells/SpellMgr.cpp index 6f56c0ebc4..77450dc859 100644 --- a/src/server/game/Spells/SpellMgr.cpp +++ b/src/server/game/Spells/SpellMgr.cpp @@ -2382,7 +2382,7 @@ void SpellMgr::LoadPetLevelupSpellMap() if (skillLine->skillId != creatureFamily->skillLine[j]) continue; - if (skillLine->learnOnGetSkill != ABILITY_LEARNED_ON_GET_RACE_OR_CLASS_SKILL) + if (skillLine->AutolearnType != SKILL_LINE_ABILITY_LEARNED_ON_SKILL_LEARN) continue; SpellInfo const* spell = GetSpellInfo(skillLine->spellId); diff --git a/src/server/scripts/Commands/cs_learn.cpp b/src/server/scripts/Commands/cs_learn.cpp index 12721c6193..03d10149ae 100644 --- a/src/server/scripts/Commands/cs_learn.cpp +++ b/src/server/scripts/Commands/cs_learn.cpp @@ -332,7 +332,8 @@ public: if (!handler->extractPlayerTarget((char*)args, &target)) return false; - target->learnDefaultSpells(); + target->LearnDefaultSkills(); + target->LearnCustomSpells(); target->learnQuestRewardedSpells(); handler->PSendSysMessage(LANG_COMMAND_LEARN_ALL_DEFAULT_AND_QUEST, handler->GetNameLink(target).c_str()); From abb16a521aa79d6b66e29a614dd2bbc5431beb9b Mon Sep 17 00:00:00 2001 From: Shauren Date: Tue, 1 Jul 2014 17:33:44 +0200 Subject: [PATCH 71/80] Update 2014_07_01_02_world playercreateinfo_skills.sql Fixed table name --- .../world/2014_07_01_02_world playercreateinfo_skills.sql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sql/updates/world/2014_07_01_02_world playercreateinfo_skills.sql b/sql/updates/world/2014_07_01_02_world playercreateinfo_skills.sql index 8eb452afa9..ad85276527 100644 --- a/sql/updates/world/2014_07_01_02_world playercreateinfo_skills.sql +++ b/sql/updates/world/2014_07_01_02_world playercreateinfo_skills.sql @@ -1,4 +1,4 @@ -DROP TABLE IF EXISTS `playercreateinfo_spells`; +DROP TABLE IF EXISTS `playercreateinfo_spell`; DROP TABLE IF EXISTS `playercreateinfo_skills`; CREATE TABLE `playercreateinfo_skills` ( From 22113d1626ce475b03de3854d27969179e5ec897 Mon Sep 17 00:00:00 2001 From: joschiwald Date: Wed, 2 Jul 2014 03:11:47 +0200 Subject: [PATCH 72/80] Core/Spells: restore some special aura proc handling Note: can cause some unexpected behavior Closes: #12413 --- src/server/game/Entities/Unit/Unit.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp index 154688b2cf..72c1a07148 100644 --- a/src/server/game/Entities/Unit/Unit.cpp +++ b/src/server/game/Entities/Unit/Unit.cpp @@ -7712,7 +7712,8 @@ bool Unit::HandleProcTriggerSpell(Unit* victim, uint32 damage, AuraEffect* trigg ? ToPlayer()->GetItemByGuid(triggeredByAura->GetBase()->GetCastItemGUID()) : NULL; // Try handle unknown trigger spells - if (sSpellMgr->GetSpellInfo(trigger_spell_id) == NULL) + // triggered spells exists only in serverside spell_dbc + /// @todo: reverify and move these spells to spellscripts { switch (auraSpellInfo->SpellFamilyName) { From 1e15c577eb53c1ef84c6a32e961673ed2318735d Mon Sep 17 00:00:00 2001 From: Aokromes Date: Wed, 2 Jul 2014 09:53:30 +0200 Subject: [PATCH 73/80] DB/Misc: Fix some runtime errors/incorrect triggers By dr-j --- sql/updates/world/2014_07_02_00_world_misc.sql | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 sql/updates/world/2014_07_02_00_world_misc.sql diff --git a/sql/updates/world/2014_07_02_00_world_misc.sql b/sql/updates/world/2014_07_02_00_world_misc.sql new file mode 100644 index 0000000000..03c2df0347 --- /dev/null +++ b/sql/updates/world/2014_07_02_00_world_misc.sql @@ -0,0 +1,5 @@ +-- +UPDATE `smart_scripts` SET `link`=0 WHERE `entryorguid`=32790 AND `source_type`=0 AND `id`=1 AND `link`=2; +UPDATE `smart_scripts` SET `link`=0 WHERE `entryorguid`=30630 AND `source_type`=0 AND `id`=0 AND `link`=1; +-- The below is also related to same quest for moonglade portal and return from moonglade portal same quest, these gained trigger flags resulting in the portals becoming non clickable. +UPDATE`creature_template` SET `flags_extra`=0 WHERE `entry`IN(32790,32788); From 2ca24b090669c2b3d0e559c7b697a92317839ab2 Mon Sep 17 00:00:00 2001 From: Aokromes Date: Wed, 2 Jul 2014 13:30:56 +0200 Subject: [PATCH 74/80] DB/SAI: Fix Cosmetic Chains on Kirgaraak By dr-j, closes #12420 --- sql/updates/world/2014_07_02_01_world_misc.sql | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 sql/updates/world/2014_07_02_01_world_misc.sql diff --git a/sql/updates/world/2014_07_02_01_world_misc.sql b/sql/updates/world/2014_07_02_01_world_misc.sql new file mode 100644 index 0000000000..ad5679406d --- /dev/null +++ b/sql/updates/world/2014_07_02_01_world_misc.sql @@ -0,0 +1,5 @@ +UPDATE `smart_scripts` SET `event_flags`=0,`event_type`=38, `event_param1`=1, `event_param2`=1, `target_param2`=200, `comment`='Cosmetic Trigger - LAB - On Data Set - Cast \'Cosmetic Chains\'' WHERE `entryorguid`IN(-103996,-103997,-103998,-103999,-104000,-104001,-104002,-104003,-104004,-104005,-104006,-104007,-104008)AND `source_type`=0 AND `id`=0 AND `link`=0; + +DELETE FROM `smart_scripts` WHERE `entryorguid`=29352 AND `id`=1; +INSERT INTO `smart_scripts` (`entryorguid`, `source_type`, `id`, `link`, `event_type`, `event_phase_mask`, `event_chance`, `event_flags`, `event_param1`, `event_param2`, `event_param3`, `event_param4`, `action_type`, `action_param1`, `action_param2`, `action_param3`, `action_param4`, `action_param5`, `action_param6`, `target_type`, `target_param1`, `target_param2`, `target_param3`, `target_x`, `target_y`, `target_z`, `target_o`, `comment`) VALUES +(29352, 0, 1, 0, 25, 0, 100, 0, 0, 0, 0, 0, 45, 1, 1, 0, 0, 0, 0, 11, 24921, 200, 0, 0, 0, 0, 0, 'Kirgaraak - On Respawn - Set Data Cosmetic Trigger'); From 4c3cb8038ad9fd9e387b7c5df1784c6012cd9986 Mon Sep 17 00:00:00 2001 From: Aokromes Date: Wed, 2 Jul 2014 13:33:10 +0200 Subject: [PATCH 75/80] DB/Items: Fix realtime duration of event items By dr-j closes #12421 --- sql/updates/world/2014_07_02_02_world_misc.sql | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 sql/updates/world/2014_07_02_02_world_misc.sql diff --git a/sql/updates/world/2014_07_02_02_world_misc.sql b/sql/updates/world/2014_07_02_02_world_misc.sql new file mode 100644 index 0000000000..5226606dc7 --- /dev/null +++ b/sql/updates/world/2014_07_02_02_world_misc.sql @@ -0,0 +1,2 @@ +-- +UPDATE `item_template` SET `flagsCustom`=1 WHERE `entry` IN(34000,34001,20562,20392,20565,20563,20569,20571,20567,20574,34002,34003,20561,20391,20566,20564,20570,20572,20568,20573,37585,37583,37582,37584,33226,44835, 44839, 44853,44840,46797,44836,46784,46796,44838,44855,46793,44854.44844,44812,46888,46887,44837,34412,34191,21711,21812,22261,18598,18597,31180,31181,46297,46396,34494,34684); From 9f0698b605a48578a0f2e82c8a6fc507ad54d0b8 Mon Sep 17 00:00:00 2001 From: Aokromes Date: Wed, 2 Jul 2014 14:48:04 +0200 Subject: [PATCH 76/80] DB/Vendor: Add Arcanum of the Stalwart Protector reputation version --- sql/updates/world/2014_07_02_03_world_npc_vendor.sql | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 sql/updates/world/2014_07_02_03_world_npc_vendor.sql diff --git a/sql/updates/world/2014_07_02_03_world_npc_vendor.sql b/sql/updates/world/2014_07_02_03_world_npc_vendor.sql new file mode 100644 index 0000000000..40003053b0 --- /dev/null +++ b/sql/updates/world/2014_07_02_03_world_npc_vendor.sql @@ -0,0 +1,2 @@ +-- +INSERT INTO `npc_vendor` (`entry`, `item`) VALUES (30431, 50369); From 531de49fe78436c30e504d2ea4b94469484333d8 Mon Sep 17 00:00:00 2001 From: Trisjdc Date: Thu, 3 Jul 2014 00:42:55 +0100 Subject: [PATCH 77/80] Core/Spells: Spells which do not apply auras should not apply DR - Solves issue where Intimidating Shout applied DR twice on the main target --- src/server/game/Spells/Spell.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/server/game/Spells/Spell.cpp b/src/server/game/Spells/Spell.cpp index 9086a304e1..814ae4d24c 100644 --- a/src/server/game/Spells/Spell.cpp +++ b/src/server/game/Spells/Spell.cpp @@ -2538,9 +2538,14 @@ SpellMissInfo Spell::DoSpellHitOnUnit(Unit* unit, uint32 effectMask, bool scaleA } } + uint8 aura_effmask = 0; + for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i) + if (effectMask & (1 << i) && m_spellInfo->Effects[i].IsUnitOwnedAuraEffect()) + aura_effmask |= 1 << i; + // Get Data Needed for Diminishing Returns, some effects may have multiple auras, so this must be done on spell hit, not aura add m_diminishGroup = GetDiminishingReturnsGroupForSpell(m_spellInfo, m_triggeredByAuraSpell); - if (m_diminishGroup) + if (m_diminishGroup && aura_effmask) { m_diminishLevel = unit->GetDiminishing(m_diminishGroup); DiminishingReturnsType type = GetDiminishingReturnsGroupType(m_diminishGroup); @@ -2551,11 +2556,6 @@ SpellMissInfo Spell::DoSpellHitOnUnit(Unit* unit, uint32 effectMask, bool scaleA unit->IncrDiminishing(m_diminishGroup); } - uint8 aura_effmask = 0; - for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i) - if (effectMask & (1 << i) && m_spellInfo->Effects[i].IsUnitOwnedAuraEffect()) - aura_effmask |= 1 << i; - if (aura_effmask) { // Select rank for aura with level requirements only in specific cases From 13f849aea8f8b2aab3bb638b39ef32b2a5c61f76 Mon Sep 17 00:00:00 2001 From: Aokromes Date: Thu, 3 Jul 2014 08:32:14 +0200 Subject: [PATCH 78/80] Rename 2014_06_20_01_world_spell_dbc.sql to 2014_06_20_01_world_creature_text.sql --- ..._world_spell_dbc.sql => 2014_06_20_01_world_creature_text.sql} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename sql/updates/world/{2014_06_20_01_world_spell_dbc.sql => 2014_06_20_01_world_creature_text.sql} (100%) diff --git a/sql/updates/world/2014_06_20_01_world_spell_dbc.sql b/sql/updates/world/2014_06_20_01_world_creature_text.sql similarity index 100% rename from sql/updates/world/2014_06_20_01_world_spell_dbc.sql rename to sql/updates/world/2014_06_20_01_world_creature_text.sql From 37f7cc4615163ae9a4800af211a9423774bf8223 Mon Sep 17 00:00:00 2001 From: Aokromes Date: Thu, 3 Jul 2014 12:26:40 +0200 Subject: [PATCH 79/80] DB/Items: Fix typos on 4c3cb80 By dr-j --- sql/updates/world/2014_07_03_00_world_misc.sql | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 sql/updates/world/2014_07_03_00_world_misc.sql diff --git a/sql/updates/world/2014_07_03_00_world_misc.sql b/sql/updates/world/2014_07_03_00_world_misc.sql new file mode 100644 index 0000000000..507b306342 --- /dev/null +++ b/sql/updates/world/2014_07_03_00_world_misc.sql @@ -0,0 +1,2 @@ +UPDATE `item_template` SET `flagsCustom`=0 WHERE `entry` IN(31180,31181,46297); +UPDATE `item_template` SET `flagsCustom`=1 WHERE `entry` IN(31880,31881,46397); From 7b74a725c89cca383acab8a002c162fc7ff1e0ac Mon Sep 17 00:00:00 2001 From: joschiwald Date: Thu, 3 Jul 2014 22:32:41 +0200 Subject: [PATCH 80/80] Core/Spells: fixed Deep Freeze after 2a3546ca36468b81d673bdb27315006144af3165 Closes #12424 --- .../world/2014_07_03_01_world_spell_proc_event.sql | 7 +++++++ src/server/game/Spells/Spell.cpp | 14 +++----------- src/server/game/Spells/SpellEffects.cpp | 8 -------- 3 files changed, 10 insertions(+), 19 deletions(-) create mode 100644 sql/updates/world/2014_07_03_01_world_spell_proc_event.sql diff --git a/sql/updates/world/2014_07_03_01_world_spell_proc_event.sql b/sql/updates/world/2014_07_03_01_world_spell_proc_event.sql new file mode 100644 index 0000000000..3ae38f8269 --- /dev/null +++ b/sql/updates/world/2014_07_03_01_world_spell_proc_event.sql @@ -0,0 +1,7 @@ +DELETE FROM `spell_linked_spell` WHERE `spell_effect`=71757; + +DELETE FROM `spell_proc_event` WHERE `entry`=71761; +INSERT INTO `spell_proc_event` (`entry`,`SchoolMask`,`SpellFamilyName`,`SpellFamilyMask0`,`SpellFamilyMask1`,`SpellFamilyMask2`,`procFlags`,`procEx`,`ppmRate`,`CustomChance`,`Cooldown`) VALUES +(71761, 0, 3, 0, 0x100000, 0, 0, 0x100, 0, 0, 0); -- Deep Freeze + +UPDATE `spell_proc_event` SET `procEx`=`procEx`|0x100 WHERE `entry`=74396; diff --git a/src/server/game/Spells/Spell.cpp b/src/server/game/Spells/Spell.cpp index 814ae4d24c..0e0443cc67 100644 --- a/src/server/game/Spells/Spell.cpp +++ b/src/server/game/Spells/Spell.cpp @@ -2265,7 +2265,7 @@ void Spell::DoAllEffectOnTarget(TargetInfo* target) m_spellAura = NULL; // Set aura to null for every target-make sure that pointer is not used for unit without aura applied //Spells with this flag cannot trigger if effect is cast on self - bool canEffectTrigger = !(m_spellInfo->AttributesEx3 & SPELL_ATTR3_CANT_TRIGGER_PROC) && unitTarget->CanProc() && CanExecuteTriggersOnHit(mask); + bool canEffectTrigger = !(m_spellInfo->AttributesEx3 & SPELL_ATTR3_CANT_TRIGGER_PROC) && unitTarget->CanProc() && (CanExecuteTriggersOnHit(mask) || missInfo == SPELL_MISS_IMMUNE || missInfo == SPELL_MISS_IMMUNE2); Unit* spellHitTarget = NULL; if (missInfo == SPELL_MISS_NONE) // In case spell hit target, do all effect on that target @@ -2311,15 +2311,8 @@ void Spell::DoAllEffectOnTarget(TargetInfo* target) if (m_damage > 0) positive = false; else if (!m_healing) - { - for (uint8 i = 0; i< MAX_SPELL_EFFECTS; ++i) - // If at least one effect negative spell is negative hit - if (mask & (1<IsPositiveEffect(i)) - { - positive = false; - break; - } - } + positive = m_spellInfo->IsPositive(); + switch (m_spellInfo->DmgClass) { case SPELL_DAMAGE_CLASS_MAGIC: @@ -2397,7 +2390,6 @@ void Spell::DoAllEffectOnTarget(TargetInfo* target) caster->ToPlayer()->CastItemCombatSpell(unitTarget, m_attackType, procVictim, procEx); } - m_damage = damageInfo.damage; caster->DealSpellDamage(&damageInfo, true); diff --git a/src/server/game/Spells/SpellEffects.cpp b/src/server/game/Spells/SpellEffects.cpp index 606851bf8f..0a5d265cbf 100644 --- a/src/server/game/Spells/SpellEffects.cpp +++ b/src/server/game/Spells/SpellEffects.cpp @@ -654,14 +654,6 @@ void Spell::EffectSchoolDMG(SpellEffIndex effIndex) } break; } - case SPELLFAMILY_MAGE: - { - // Deep Freeze should deal damage to permanently stun-immune targets. - if (m_spellInfo->Id == 71757) - if (unitTarget->GetTypeId() != TYPEID_UNIT || !(unitTarget->IsImmunedToSpellEffect(sSpellMgr->GetSpellInfo(44572), 0))) - return; - break; - } } if (m_originalCaster && damage > 0 && apply_direct_bonus)