aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--COPYING40
-rw-r--r--cmake/options.cmake2
-rw-r--r--cmake/showoptions.cmake1
-rw-r--r--dep/CMakeLists.txt7
-rw-r--r--dep/PackageList.txt4
-rw-r--r--dep/recastnavigation/CMakeLists.txt20
-rw-r--r--dep/recastnavigation/Detour/CMakeLists.txt28
-rw-r--r--dep/recastnavigation/Detour/DetourAlloc.cpp50
-rw-r--r--dep/recastnavigation/Detour/DetourAlloc.h36
-rw-r--r--dep/recastnavigation/Detour/DetourAssert.h33
-rw-r--r--dep/recastnavigation/Detour/DetourCommon.cpp329
-rw-r--r--dep/recastnavigation/Detour/DetourCommon.h248
-rw-r--r--dep/recastnavigation/Detour/DetourNavMesh.cpp1235
-rw-r--r--dep/recastnavigation/Detour/DetourNavMesh.h428
-rw-r--r--dep/recastnavigation/Detour/DetourNavMeshBuilder.cpp717
-rw-r--r--dep/recastnavigation/Detour/DetourNavMeshBuilder.h77
-rw-r--r--dep/recastnavigation/Detour/DetourNavMeshQuery.cpp2564
-rw-r--r--dep/recastnavigation/Detour/DetourNavMeshQuery.h407
-rw-r--r--dep/recastnavigation/Detour/DetourNode.cpp164
-rw-r--r--dep/recastnavigation/Detour/DetourNode.h157
-rw-r--r--dep/recastnavigation/Detour/DetourObstacleAvoidance.cpp532
-rw-r--r--dep/recastnavigation/Detour/DetourObstacleAvoidance.h148
-rw-r--r--dep/recastnavigation/License.txt18
-rw-r--r--dep/recastnavigation/Readme.txt120
-rw-r--r--dep/recastnavigation/Recast/CMakeLists.txt31
-rw-r--r--dep/recastnavigation/Recast/Recast.cpp423
-rw-r--r--dep/recastnavigation/Recast/Recast.h688
-rw-r--r--dep/recastnavigation/Recast/RecastAlloc.cpp67
-rw-r--r--dep/recastnavigation/Recast/RecastAlloc.h69
-rw-r--r--dep/recastnavigation/Recast/RecastArea.cpp413
-rw-r--r--dep/recastnavigation/Recast/RecastAssert.h33
-rw-r--r--dep/recastnavigation/Recast/RecastContour.cpp804
-rw-r--r--dep/recastnavigation/Recast/RecastFilter.cpp179
-rw-r--r--dep/recastnavigation/Recast/RecastMesh.cpp1322
-rw-r--r--dep/recastnavigation/Recast/RecastMeshDetail.cpp1237
-rw-r--r--dep/recastnavigation/Recast/RecastRasterization.cpp360
-rw-r--r--dep/recastnavigation/Recast/RecastRegion.cpp1283
-rw-r--r--dep/recastnavigation/TODO.txt20
-rw-r--r--sql/old/3.3.5a/2012_09_16_00_world_version.sql (renamed from sql/updates/world/2012_09_16_00_world_version.sql)0
-rw-r--r--sql/old/3.3.5a/2012_09_16_01_world_conditions.sql (renamed from sql/updates/world/2012_09_16_01_world_conditions.sql)0
-rw-r--r--sql/old/3.3.5a/2012_09_16_01_world_creature_loot_template.sql (renamed from sql/updates/world/2012_09_16_01_world_creature_loot_template.sql)0
-rw-r--r--sql/old/3.3.5a/2012_09_16_01_world_creature_template_addon.sql (renamed from sql/updates/world/2012_09_16_01_world_creature_template_addon.sql)0
-rw-r--r--sql/old/3.3.5a/2012_09_16_02_world_fires_over_skettis.sql (renamed from sql/updates/world/2012_09_16_02_world_fires_over_skettis.sql)0
-rw-r--r--sql/old/3.3.5a/2012_09_16_03_world_spell_script_names.sql (renamed from sql/updates/world/2012_09_16_03_world_spell_script_names.sql)0
-rw-r--r--sql/old/3.3.5a/2012_09_17_00_world_spell_bonus_data.sql (renamed from sql/updates/world/2012_09_17_00_world_spell_bonus_data.sql)0
-rw-r--r--sql/old/3.3.5a/2012_09_17_01_world_spell_bonus_data.sql (renamed from sql/updates/world/2012_09_17_01_world_spell_bonus_data.sql)0
-rw-r--r--sql/old/3.3.5a/2012_09_17_01_world_spell_script_names.sql (renamed from sql/updates/world/2012_09_17_01_world_spell_script_names.sql)0
-rw-r--r--sql/old/3.3.5a/2012_09_17_02_world_misc.sql (renamed from sql/updates/world/2012_09_17_02_world_misc.sql)0
-rw-r--r--sql/old/3.3.5a/2012_09_18_00_world_creature_template.sql (renamed from sql/updates/world/2012_09_18_00_world_creature_template.sql)0
-rw-r--r--sql/old/3.3.5a/2012_09_18_01_world_creature_template.sql (renamed from sql/updates/world/2012_09_18_01_world_creature_template.sql)0
-rw-r--r--sql/old/3.3.5a/2012_09_19_00_world_dwarfageddon.sql (renamed from sql/updates/world/2012_09_19_00_world_dwarfageddon.sql)0
-rw-r--r--sql/old/3.3.5a/2012_09_19_00_world_reference_loot_template.sql (renamed from sql/updates/world/2012_09_19_00_world_reference_loot_template.sql)0
-rw-r--r--sql/old/3.3.5a/2012_09_20_00_world_creature_loot_template.sql (renamed from sql/updates/world/2012_09_20_00_world_creature_loot_template.sql)0
-rw-r--r--sql/old/3.3.5a/2012_09_22_01_world_i_was_a_lot_of_things.sql (renamed from sql/updates/world/2012_09_22_01_world_i_was_a_lot_of_things.sql)0
-rw-r--r--sql/old/3.3.5a/2012_09_22_03_world_game_event.sql (renamed from sql/updates/world/2012_09_22_03_world_game_event.sql)0
-rw-r--r--sql/old/3.3.5a/2012_09_24_00_world_spell_script_names.sql (renamed from sql/updates/world/2012_09_24_00_world_spell_script_names.sql)0
-rw-r--r--sql/old/3.3.5a/2012_09_24_01_world_spell_script_names.sql (renamed from sql/updates/world/2012_09_24_01_world_spell_script_names.sql)0
-rw-r--r--sql/old/3.3.5a/2012_09_24_02_world_misc.sql (renamed from sql/updates/world/2012_09_24_02_world_misc.sql)0
-rw-r--r--sql/old/3.3.5a/2012_09_24_03_world_misc.sql (renamed from sql/updates/world/2012_09_24_03_world_misc.sql)0
-rw-r--r--sql/old/3.3.5a/2012_09_26_00_world_misc.sql (renamed from sql/updates/world/2012_09_26_00_world_misc.sql)0
-rw-r--r--sql/old/3.3.5a/2012_09_26_01_world_spell_bonus_data.sql (renamed from sql/updates/world/2012_09_26_01_world_spell_bonus_data.sql)0
-rw-r--r--sql/old/3.3.5a/2012_09_27_01_world_custodian_of_time.sql (renamed from sql/updates/world/2012_09_27_01_world_custodian_of_time.sql)0
-rw-r--r--sql/old/3.3.5a/2012_09_28_00_world_creature_model_info.sql (renamed from sql/updates/world/2012_09_28_00_world_creature_model_info.sql)0
-rw-r--r--sql/old/3.3.5a/2012_09_29_00_world_creature_model_info.sql (renamed from sql/updates/world/2012_09_29_00_world_creature_model_info.sql)0
-rw-r--r--sql/old/3.3.5a/2012_09_29_01_world_creature_text.sql (renamed from sql/updates/world/2012_09_29_01_world_creature_text.sql)0
-rw-r--r--sql/old/3.3.5a/2012_09_30_00_world_creature_text.sql (renamed from sql/updates/world/2012_09_30_00_world_creature_text.sql)0
-rw-r--r--sql/old/3.3.5a/2012_09_30_01_world_player_factionchange_titles.sql (renamed from sql/updates/world/2012_09_30_01_world_player_factionchange_titles.sql)0
-rw-r--r--sql/old/3.3.5a/2012_09_30_02_world_creature_text.sql (renamed from sql/updates/world/2012_09_30_02_world_creature_text.sql)0
-rw-r--r--sql/old/3.3.5a/2012_09_30_03_world_creature_text.sql (renamed from sql/updates/world/2012_09_30_03_world_creature_text.sql)0
-rw-r--r--sql/old/3.3.5a/2012_10_02_00_world_oculus.sql (renamed from sql/updates/world/2012_10_02_00_world_oculus.sql)0
-rw-r--r--sql/old/3.3.5a/2012_10_04_00_world_spelldifficulty_dbc.sql (renamed from sql/updates/world/2012_10_04_00_world_spelldifficulty_dbc.sql)0
-rw-r--r--sql/old/3.3.5a/2012_10_04_01_world_script_texts.sql (renamed from sql/updates/world/2012_10_04_01_world_script_texts.sql)0
-rw-r--r--sql/old/3.3.5a/2012_10_05_00_world_misc.sql (renamed from sql/updates/world/2012_10_05_00_world_misc.sql)0
-rw-r--r--sql/old/3.3.5a/2012_10_05_01_world_spell_script_names.sql (renamed from sql/updates/world/2012_10_05_01_world_spell_script_names.sql)0
-rw-r--r--sql/old/3.3.5a/2012_10_06_00_world_spell_difficulty.sql (renamed from sql/updates/world/2012_10_06_00_world_spell_difficulty.sql)0
-rw-r--r--sql/old/3.3.5a/2012_10_06_00_world_spell_script_names.sql (renamed from sql/updates/world/2012_10_06_00_world_spell_script_names.sql)0
-rw-r--r--sql/old/3.3.5a/2012_10_06_01_world_spell_dbc.sql (renamed from sql/updates/world/2012_10_06_01_world_spell_dbc.sql)0
-rw-r--r--sql/old/3.3.5a/2012_10_06_02_world_misc_templates.sql (renamed from sql/updates/world/2012_10_06_02_world_misc_templates.sql)0
-rw-r--r--sql/old/3.3.5a/2012_10_06_03_world_creature_text.sql (renamed from sql/updates/world/2012_10_06_03_world_creature_text.sql)0
-rw-r--r--sql/old/3.3.5a/2012_10_06_04_world_misc_spawns.sql (renamed from sql/updates/world/2012_10_06_04_world_misc_spawns.sql)0
-rw-r--r--sql/old/3.3.5a/2012_10_06_05_world_spell_script_names.sql (renamed from sql/updates/world/2012_10_06_05_world_spell_script_names.sql)0
-rw-r--r--sql/old/3.3.5a/2012_10_07_00_world_creature_loot_template.sql (renamed from sql/updates/world/2012_10_07_00_world_creature_loot_template.sql)0
-rw-r--r--sql/old/3.3.5a/2012_10_07_01_world_spell_proc_event.sql (renamed from sql/updates/world/2012_10_07_01_world_spell_proc_event.sql)0
-rw-r--r--sql/old/3.3.5a/2012_10_09_00_character_glyphs.sql (renamed from sql/updates/characters/2012_10_09_00_character_glyphs.sql)0
-rw-r--r--sql/old/3.3.5a/2012_10_09_00_world_spell_groups.sql (renamed from sql/updates/world/2012_10_09_00_world_spell_groups.sql)0
-rw-r--r--sql/old/3.3.5a/2012_10_09_01_world_spell_groups.sql (renamed from sql/updates/world/2012_10_09_01_world_spell_groups.sql)0
-rw-r--r--sql/old/3.3.5a/2012_10_09_02_world_spell_groups.sql (renamed from sql/updates/world/2012_10_09_02_world_spell_groups.sql)0
-rw-r--r--sql/old/3.3.5a/2012_10_10_00_world_battleground_template.sql (renamed from sql/updates/world/2012_10_10_00_world_battleground_template.sql)0
-rw-r--r--sql/old/3.3.5a/2012_10_11_00_world_gameobject.sql (renamed from sql/updates/world/2012_10_11_00_world_gameobject.sql)0
-rw-r--r--sql/old/3.3.5a/2012_10_13_00_world_creature_template.sql (renamed from sql/updates/world/2012_10_13_00_world_creature_template.sql)0
-rw-r--r--sql/old/3.3.5a/2012_10_14_00_world_creature.sql (renamed from sql/updates/world/2012_10_14_00_world_creature.sql)0
-rw-r--r--sql/old/3.3.5a/2012_10_16_00_world_spell_area.sql (renamed from sql/updates/world/2012_10_16_00_world_spell_area.sql)0
-rw-r--r--sql/old/3.3.5a/2012_10_17_00_character_gm_tickets.sql (renamed from sql/updates/characters/2012_10_17_00_character_gm_tickets.sql)0
-rw-r--r--sql/old/3.3.5a/2012_10_20_00_world_transports.sql (renamed from sql/updates/world/2012_10_20_00_world_transports.sql)0
-rw-r--r--sql/old/3.3.5a/2012_10_20_01_world_spell_script_names.sql (renamed from sql/updates/world/2012_10_20_01_world_spell_script_names.sql)0
-rw-r--r--sql/old/3.3.5a/2012_10_20_02_world_creature_text.sql (renamed from sql/updates/world/2012_10_20_02_world_creature_text.sql)0
-rw-r--r--sql/old/3.3.5a/2012_10_21_00_world_conditions.sql (renamed from sql/updates/world/2012_10_21_00_world_conditions.sql)0
-rw-r--r--sql/old/3.3.5a/2012_10_21_01_world_misc.sql (renamed from sql/updates/world/2012_10_21_01_world_misc.sql)0
-rw-r--r--sql/old/3.3.5a/2012_10_23_00_world_trinity_string.sql (renamed from sql/updates/world/2012_10_23_00_world_trinity_string.sql)0
-rw-r--r--sql/old/3.3.5a/2012_10_23_01_world_command.sql (renamed from sql/updates/world/2012_10_23_01_world_command.sql)0
-rw-r--r--sql/old/3.3.5a/2012_10_25_00_world_childrens_week.sql (renamed from sql/updates/world/2012_10_25_00_world_childrens_week.sql)0
-rw-r--r--sql/old/3.3.5a/2012_10_26_00_world_sai.sql (renamed from sql/updates/world/2012_10_26_00_world_sai.sql)0
-rw-r--r--sql/old/3.3.5a/2012_10_27_00_world_creature.sql (renamed from sql/updates/world/2012_10_27_00_world_creature.sql)0
-rw-r--r--sql/old/3.3.5a/2012_10_27_01_world_creature_loot_template.sql (renamed from sql/updates/world/2012_10_27_01_world_creature_loot_template.sql)0
-rw-r--r--sql/old/3.3.5a/2012_10_29_00_world_conditions.sql (renamed from sql/updates/world/2012_10_29_00_world_conditions.sql)0
-rw-r--r--sql/old/3.3.5a/2012_11_02_00_character_misc.sql (renamed from sql/updates/characters/2012_11_02_00_character_misc.sql)0
-rw-r--r--sql/old/3.3.5a/2012_11_03_00_world_creature_loot_template.sql (renamed from sql/updates/world/2012_11_03_00_world_creature_loot_template.sql)0
-rw-r--r--sql/old/3.3.5a/2012_11_07_00_world_misc.sql (renamed from sql/updates/world/2012_11_07_00_world_misc.sql)0
-rw-r--r--sql/old/3.3.5a/2012_11_11_00_world_sai.sql (renamed from sql/updates/world/2012_11_11_00_world_sai.sql)0
-rw-r--r--sql/old/3.3.5a/2012_11_11_01_world_quest_template.sql (renamed from sql/updates/world/2012_11_11_01_world_quest_template.sql)0
-rw-r--r--sql/old/3.3.5a/2012_11_11_02_world_gossip_menu.sql (renamed from sql/updates/world/2012_11_11_02_world_gossip_menu.sql)0
-rw-r--r--sql/old/3.3.5a/2012_11_11_03_world_gossip_menu.sql (renamed from sql/updates/world/2012_11_11_03_world_gossip_menu.sql)0
-rw-r--r--sql/old/3.3.5a/2012_11_12_00_world_quest_template.sql (renamed from sql/updates/world/2012_11_12_00_world_quest_template.sql)0
-rw-r--r--sql/old/3.3.5a/2012_11_12_01_world_misc.sql (renamed from sql/updates/world/2012_11_12_01_world_misc.sql)0
-rw-r--r--sql/old/3.3.5a/2012_11_12_02_world_quest_template.sql (renamed from sql/updates/world/2012_11_12_02_world_quest_template.sql)0
-rw-r--r--sql/old/3.3.5a/2012_11_12_03_world_misc.sql (renamed from sql/updates/world/2012_11_12_03_world_misc.sql)0
-rw-r--r--sql/old/3.3.5a/2012_11_13_00_world_waypoints.sql (renamed from sql/updates/world/2012_11_13_00_world_waypoints.sql)0
-rw-r--r--sql/old/3.3.5a/2012_11_13_01_world_misc.sql (renamed from sql/updates/world/2012_11_13_01_world_misc.sql)0
-rw-r--r--sql/old/3.3.5a/2012_11_13_02_world_creature.sql (renamed from sql/updates/world/2012_11_13_02_world_creature.sql)0
-rw-r--r--sql/old/3.3.5a/2012_11_13_03_world_gameevent.sql (renamed from sql/updates/world/2012_11_13_03_world_gameevent.sql)0
-rw-r--r--sql/old/3.3.5a/2012_11_13_04_world_gameeventquest.sql (renamed from sql/updates/world/2012_11_13_04_world_gameeventquest.sql)0
-rw-r--r--sql/old/3.3.5a/2012_11_13_05_world_player_factionchange_items.sql (renamed from sql/updates/world/2012_11_13_05_world_player_factionchange_items.sql)0
-rw-r--r--sql/old/3.3.5a/2012_11_13_06_world_trinity_string.sql (renamed from sql/updates/world/2012_11_13_06_world_trinity_string.sql)0
-rw-r--r--sql/old/3.3.5a/2012_11_14_00_world_sai.sql (renamed from sql/updates/world/2012_11_14_00_world_sai.sql)0
-rw-r--r--sql/old/3.3.5a/2012_11_14_00_world_various_fixes.sql (renamed from sql/updates/world/2012_11_14_00_world_various_fixes.sql)0
-rw-r--r--sql/old/3.3.5a/2012_11_14_02_world_battleground_template.sql (renamed from sql/updates/world/2012_11_14_02_world_battleground_template.sql)0
-rw-r--r--sql/old/3.3.5a/2012_11_16_00_world_utgarde.sql (renamed from sql/updates/world/2012_11_16_00_world_utgarde.sql)0
-rw-r--r--sql/old/3.3.5a/2012_11_16_01_world_utgarde.sql (renamed from sql/updates/world/2012_11_16_01_world_utgarde.sql)0
-rw-r--r--sql/old/3.3.5a/2012_11_16_02_world_creature_ai_summons.sql (renamed from sql/updates/world/2012_11_16_02_world_creature_ai_summons.sql)0
-rw-r--r--sql/old/3.3.5a/2012_11_17_00_world_various_fixes.sql (renamed from sql/updates/world/2012_11_17_00_world_various_fixes.sql)0
-rw-r--r--sql/old/3.3.5a/2012_11_18_00_character_calendar.sql (renamed from sql/updates/characters/2012_11_18_00_character_calendar.sql)0
-rw-r--r--sql/old/3.3.5a/2012_11_18_00_world_ormorok.sql (renamed from sql/updates/world/2012_11_18_00_world_ormorok.sql)0
-rw-r--r--sql/old/3.3.5a/2012_11_18_01_world_creature_text.sql (renamed from sql/updates/world/2012_11_18_01_world_creature_text.sql)0
-rw-r--r--sql/old/3.3.5a/2012_11_18_01_world_gameobject.sql (renamed from sql/updates/world/2012_11_18_01_world_gameobject.sql)0
-rw-r--r--sql/old/3.3.5a/2012_11_18_02_world_toc.sql (renamed from sql/updates/world/2012_11_18_02_world_toc.sql)0
-rw-r--r--sql/old/3.3.5a/2012_11_19_00_world_various_fixes.sql (renamed from sql/updates/world/2012_11_19_00_world_various_fixes.sql)0
-rw-r--r--sql/old/3.3.5a/2012_11_19_01_world_pilgrims_bounty.sql (renamed from sql/updates/world/2012_11_19_01_world_pilgrims_bounty.sql)0
-rw-r--r--sql/old/3.3.5a/2012_11_19_02_world_misc.sql (renamed from sql/updates/world/2012_11_19_02_world_misc.sql)0
-rw-r--r--sql/old/3.3.5a/2012_11_19_03_world_ysera.sql (renamed from sql/updates/world/2012_11_19_03_world_ysera.sql)0
-rw-r--r--sql/old/3.3.5a/2012_11_20_00_world_creature_text.sql (renamed from sql/updates/world/2012_11_20_00_world_creature_text.sql)0
-rw-r--r--sql/old/3.3.5a/2012_11_21_00_world_sai.sql (renamed from sql/updates/world/2012_11_21_00_world_sai.sql)0
-rw-r--r--sql/old/3.3.5a/2012_11_24_00_world_conditions.sql (renamed from sql/updates/world/2012_11_24_00_world_conditions.sql)0
-rw-r--r--sql/old/3.3.5a/2012_11_24_00_world_creature_text.sql (renamed from sql/updates/world/2012_11_24_00_world_creature_text.sql)0
-rw-r--r--sql/old/3.3.5a/2012_11_24_01_world_quest_start_scripts.sql (renamed from sql/updates/world/2012_11_24_01_world_quest_start_scripts.sql)0
-rw-r--r--sql/old/3.3.5a/2012_11_24_02_world_creature_text.sql (renamed from sql/updates/world/2012_11_24_02_world_creature_text.sql)0
-rw-r--r--sql/old/3.3.5a/2012_11_24_02_world_gossip_menu_option.sql (renamed from sql/updates/world/2012_11_24_02_world_gossip_menu_option.sql)0
-rw-r--r--sql/old/3.3.5a/2012_11_24_03_world_various_fixes.sql (renamed from sql/updates/world/2012_11_24_03_world_various_fixes.sql)0
-rw-r--r--sql/old/3.3.5a/2012_11_24_04_world_sai.sql (renamed from sql/updates/world/2012_11_24_04_world_sai.sql)0
-rw-r--r--sql/old/3.3.5a/2012_11_25_00_world_various_fixes.sql (renamed from sql/updates/world/2012_11_25_00_world_various_fixes.sql)0
-rw-r--r--sql/old/3.3.5a/2012_11_25_01_world_various_fixes.sql (renamed from sql/updates/world/2012_11_25_01_world_various_fixes.sql)0
-rw-r--r--sql/old/3.3.5a/2012_11_26_02_world_conditions.sql (renamed from sql/updates/world/2012_11_26_02_world_conditions.sql)0
-rw-r--r--sql/old/3.3.5a/2012_11_26_03_world_misc.sql (renamed from sql/updates/world/2012_11_26_03_world_misc.sql)0
-rw-r--r--sql/old/3.3.5a/2012_11_27_00_world_misc.sql (renamed from sql/updates/world/2012_11_27_00_world_misc.sql)0
-rw-r--r--sql/old/3.3.5a/2012_11_27_01_world_achievement_criteria_data.sql (renamed from sql/updates/world/2012_11_27_01_world_achievement_criteria_data.sql)0
-rw-r--r--sql/old/3.3.5a/2012_11_30_00_world_spell_script_names.sql (renamed from sql/updates/world/2012_11_30_00_world_spell_script_names.sql)0
-rw-r--r--sql/old/3.3.5a/2012_12_01_00_world_assembly_of_iron.sql (renamed from sql/updates/world/2012_12_01_00_world_assembly_of_iron.sql)0
-rw-r--r--sql/old/3.3.5a/2012_12_01_01_world_misc.sql (renamed from sql/updates/world/2012_12_01_01_world_misc.sql)0
-rw-r--r--sql/old/3.3.5a/2012_12_01_02_world_creature_text.sql (renamed from sql/updates/world/2012_12_01_02_world_creature_text.sql)0
-rw-r--r--sql/old/3.3.5a/2012_12_01_03_world_ulduar_creature_text.sql (renamed from sql/updates/world/2012_12_01_03_world_ulduar_creature_text.sql)0
-rw-r--r--sql/old/3.3.5a/2012_12_01_04_world_spelldifficulty_dbc.sql (renamed from sql/updates/world/2012_12_01_04_world_spelldifficulty_dbc.sql)0
-rw-r--r--sql/old/3.3.5a/2012_12_01_05_world_creature_template.sql (renamed from sql/updates/world/2012_12_01_05_world_creature_template.sql)0
-rw-r--r--sql/old/3.3.5a/2012_12_02_00_world_game_object.sql (renamed from sql/updates/world/2012_12_02_00_world_game_object.sql)0
-rw-r--r--sql/old/3.3.5a/2012_12_02_01_world_creature_text.sql (renamed from sql/updates/world/2012_12_02_01_world_creature_text.sql)0
-rw-r--r--sql/old/3.3.5a/2012_12_02_02_world_waypoint_data.sql (renamed from sql/updates/world/2012_12_02_02_world_waypoint_data.sql)0
-rw-r--r--sql/old/3.3.5a/2012_12_03_00_character_character_queststatus_monthly.sql (renamed from sql/updates/characters/2012_12_03_00_character_character_queststatus_monthly.sql)0
-rw-r--r--sql/old/3.3.5a/2012_12_04_00_world_creature_text.sql (renamed from sql/updates/world/2012_12_04_00_world_creature_text.sql)0
-rw-r--r--sql/old/3.3.5a/2012_12_04_01_world_quest_start_scripts.sql (renamed from sql/updates/world/2012_12_04_01_world_quest_start_scripts.sql)0
-rw-r--r--sql/old/3.3.5a/2012_12_04_02_world_reputation_reward_rate.sql (renamed from sql/updates/world/2012_12_04_02_world_reputation_reward_rate.sql)0
-rw-r--r--sql/old/3.3.5a/2012_12_04_03_world_reputation_reward_rate.sql (renamed from sql/updates/world/2012_12_04_03_world_reputation_reward_rate.sql)0
-rw-r--r--sql/old/3.3.5a/2012_12_04_04_world_creature_text.sql (renamed from sql/updates/world/2012_12_04_04_world_creature_text.sql)0
-rw-r--r--sql/old/3.3.5a/2012_12_04_05_world_spell_script_names.sql (renamed from sql/updates/world/2012_12_04_05_world_spell_script_names.sql)0
-rw-r--r--sql/old/3.3.5a/2012_12_04_06_world_spell_creature_text.sql (renamed from sql/updates/world/2012_12_04_06_world_spell_creature_text.sql)0
-rw-r--r--sql/old/3.3.5a/2012_12_05_00_world_creature_text.sql (renamed from sql/updates/world/2012_12_05_00_world_creature_text.sql)0
-rw-r--r--sql/old/3.3.5a/2012_12_06_00_world_creature_text.sql (renamed from sql/updates/world/2012_12_06_00_world_creature_text.sql)0
-rw-r--r--sql/old/3.3.5a/2012_12_07_00_world_creature_text.sql (renamed from sql/updates/world/2012_12_07_00_world_creature_text.sql)0
-rw-r--r--sql/old/3.3.5a/2012_12_07_00_world_script_texts.sql (renamed from sql/updates/world/2012_12_07_00_world_script_texts.sql)0
-rw-r--r--sql/old/3.3.5a/2012_12_07_01_world_misc.sql (renamed from sql/updates/world/2012_12_07_01_world_misc.sql)0
-rw-r--r--sql/old/3.3.5a/2012_12_07_02_world_tracker_pitcrawler.sql (renamed from sql/updates/world/2012_12_07_02_world_tracker_pitcrawler.sql)0
-rw-r--r--sql/old/3.3.5a/2012_12_07_03_world_tracker_trista.sql (renamed from sql/updates/world/2012_12_07_03_world_tracker_trista.sql)0
-rw-r--r--sql/old/3.3.5a/2012_12_07_04_world_misc.sql (renamed from sql/updates/world/2012_12_07_04_world_misc.sql)0
-rw-r--r--sql/old/3.3.5a/2012_12_07_05_world_creature_text.sql (renamed from sql/updates/world/2012_12_07_05_world_creature_text.sql)0
-rw-r--r--sql/old/3.3.5a/2012_12_07_06_world_sai.sql (renamed from sql/updates/world/2012_12_07_06_world_sai.sql)0
-rw-r--r--sql/old/3.3.5a/2012_12_07_07_world_sai.sql (renamed from sql/updates/world/2012_12_07_07_world_sai.sql)0
-rw-r--r--sql/old/3.3.5a/2012_12_07_08_world_misc.sql (renamed from sql/updates/world/2012_12_07_08_world_misc.sql)0
-rw-r--r--sql/old/3.3.5a/2012_12_07_09_world_db_script_string.sql (renamed from sql/updates/world/2012_12_07_09_world_db_script_string.sql)0
-rw-r--r--sql/old/3.3.5a/2012_12_08_00_world_creature_text.sql (renamed from sql/updates/world/2012_12_08_00_world_creature_text.sql)0
-rw-r--r--sql/old/3.3.5a/2012_12_08_01_world_sai.sql (renamed from sql/updates/world/2012_12_08_01_world_sai.sql)0
-rw-r--r--sql/old/3.3.5a/2012_12_09_00_world_conditions.sql (renamed from sql/updates/world/2012_12_09_00_world_conditions.sql)0
-rw-r--r--sql/old/3.3.5a/2012_12_09_00_world_creature_text.sql (renamed from sql/updates/world/2012_12_09_00_world_creature_text.sql)0
-rw-r--r--sql/old/3.3.5a/2012_12_09_01_world_misc.sql (renamed from sql/updates/world/2012_12_09_01_world_misc.sql)0
-rw-r--r--sql/old/3.3.5a/2012_12_09_02_world_quest_template.sql (renamed from sql/updates/world/2012_12_09_02_world_quest_template.sql)0
-rw-r--r--sql/old/3.3.5a/2012_12_10_00_world_smart_scripts.sql (renamed from sql/updates/world/2012_12_10_00_world_smart_scripts.sql)0
-rw-r--r--sql/old/3.3.5a/2012_12_10_01_world_spell_script_names.sql (renamed from sql/updates/world/2012_12_10_01_world_spell_script_names.sql)0
-rw-r--r--sql/old/3.3.5a/2012_12_10_02_world_spawns_waypoints.sql (renamed from sql/updates/world/2012_12_10_02_world_spawns_waypoints.sql)0
-rw-r--r--sql/old/3.3.5a/2012_12_13_00_world.sql (renamed from sql/updates/world/2012_12_13_00_world.sql)0
-rw-r--r--sql/old/3.3.5a/2012_12_14_00_world_conditions.sql (renamed from sql/updates/world/2012_12_14_00_world_conditions.sql)0
-rw-r--r--sql/old/3.3.5a/2012_12_14_01_world_waypoint_data.sql (renamed from sql/updates/world/2012_12_14_01_world_waypoint_data.sql)0
-rw-r--r--sql/old/3.3.5a/2012_12_14_02_world_misc.sql (renamed from sql/updates/world/2012_12_14_02_world_misc.sql)0
-rw-r--r--sql/old/3.3.5a/2012_12_14_03_world_npc_spellclick_spells.sql (renamed from sql/updates/world/2012_12_14_03_world_npc_spellclick_spells.sql)0
-rw-r--r--sql/old/3.3.5a/2012_12_14_04_world_misc.sql (renamed from sql/updates/world/2012_12_14_04_world_misc.sql)0
-rw-r--r--sql/old/3.3.5a/2012_12_14_05_world_spell_script_names.sql (renamed from sql/updates/world/2012_12_14_05_world_spell_script_names.sql)0
-rw-r--r--sql/old/3.3.5a/2012_12_15_00_world_waypoint_data.sql (renamed from sql/updates/world/2012_12_15_00_world_waypoint_data.sql)0
-rw-r--r--sql/old/3.3.5a/2012_12_15_01_world_sai.sql (renamed from sql/updates/world/2012_12_15_01_world_sai.sql)0
-rw-r--r--sql/old/3.3.5a/2012_12_15_02_world_sai.sql (renamed from sql/updates/world/2012_12_15_02_world_sai.sql)0
-rw-r--r--sql/old/3.3.5a/2012_12_15_03_world_quest_end_scripts.sql (renamed from sql/updates/world/2012_12_15_03_world_quest_end_scripts.sql)0
-rw-r--r--sql/old/3.3.5a/2012_12_16_00_world_creature_addon.sql (renamed from sql/updates/world/2012_12_16_00_world_creature_addon.sql)0
-rw-r--r--sql/old/3.3.5a/2012_12_16_01_world_npc_vendor.sql (renamed from sql/updates/world/2012_12_16_01_world_npc_vendor.sql)0
-rw-r--r--sql/old/3.3.5a/2012_12_16_02_world_creature_text.sql (renamed from sql/updates/world/2012_12_16_02_world_creature_text.sql)0
-rw-r--r--sql/old/3.3.5a/2012_12_16_03_world_sai.sql (renamed from sql/updates/world/2012_12_16_03_world_sai.sql)0
-rw-r--r--sql/old/3.3.5a/2012_12_17_00_world_creature_template.sql (renamed from sql/updates/world/2012_12_17_00_world_creature_template.sql)0
-rw-r--r--sql/old/3.3.5a/2012_12_17_00_world_song_of_wind_and_water.sql (renamed from sql/updates/world/2012_12_17_00_world_song_of_wind_and_water.sql)0
-rw-r--r--sql/old/3.3.5a/2012_12_17_00_world_spell_linked_spell.sql (renamed from sql/updates/world/2012_12_17_00_world_spell_linked_spell.sql)0
-rw-r--r--sql/old/3.3.5a/2012_12_18_00_character_worldstates.sql (renamed from sql/updates/characters/2012_12_18_00_character_worldstates.sql)0
-rw-r--r--sql/old/3.3.5a/2012_12_18_00_world_creature_text.sql (renamed from sql/updates/world/2012_12_18_00_world_creature_text.sql)0
-rw-r--r--sql/old/3.3.5a/2012_12_18_00_world_waking_the_sleeper.sql (renamed from sql/updates/world/2012_12_18_00_world_waking_the_sleeper.sql)0
-rw-r--r--sql/old/3.3.5a/2012_12_18_01_world_creature_text.sql (renamed from sql/updates/world/2012_12_18_01_world_creature_text.sql)0
-rw-r--r--sql/old/3.3.5a/2012_12_18_02_world_misc.sql (renamed from sql/updates/world/2012_12_18_02_world_misc.sql)0
-rw-r--r--sql/old/3.3.5a/2012_12_18_02_world_sai.sql (renamed from sql/updates/world/2012_12_18_02_world_sai.sql)0
-rw-r--r--sql/old/3.3.5a/2012_12_19_00_world_sai.sql (renamed from sql/updates/world/2012_12_19_00_world_sai.sql)0
-rw-r--r--sql/old/3.3.5a/2012_12_19_01_world_viscidus.sql (renamed from sql/updates/world/2012_12_19_01_world_viscidus.sql)0
-rw-r--r--sql/old/3.3.5a/2012_12_19_02_world_sai.sql (renamed from sql/updates/world/2012_12_19_02_world_sai.sql)0
-rw-r--r--sql/old/3.3.5a/2012_12_20_00_characters_create_item_loot.sql (renamed from sql/updates/characters/2012_12_20_00_characters_create_item_loot.sql)0
-rw-r--r--sql/old/3.3.5a/2012_12_20_00_world_conditions.sql (renamed from sql/updates/world/2012_12_20_00_world_conditions.sql)0
-rw-r--r--sql/old/3.3.5a/2012_12_20_01_world_conditions.sql (renamed from sql/updates/world/2012_12_20_01_world_conditions.sql)0
-rw-r--r--sql/old/3.3.5a/2012_12_20_02_world_misc.sql (renamed from sql/updates/world/2012_12_20_02_world_misc.sql)0
-rw-r--r--sql/old/3.3.5a/2012_12_20_03_world_conditions.sql (renamed from sql/updates/world/2012_12_20_03_world_conditions.sql)0
-rw-r--r--sql/old/3.3.5a/2012_12_21_00_world_conditions.sql (renamed from sql/updates/world/2012_12_21_00_world_conditions.sql)0
-rw-r--r--sql/old/3.3.5a/2012_12_21_01_world_conditions.sql (renamed from sql/updates/world/2012_12_21_01_world_conditions.sql)0
-rw-r--r--sql/old/3.3.5a/2012_12_22_01_world_sai.sql (renamed from sql/updates/world/2012_12_22_01_world_sai.sql)0
-rw-r--r--sql/old/3.3.5a/2012_12_22_02_world_the_crusaders_pinnacle.sql (renamed from sql/updates/world/2012_12_22_02_world_the_crusaders_pinnacle.sql)0
-rw-r--r--sql/old/3.3.5a/2012_12_23_00_world_creature_text.sql (renamed from sql/updates/world/2012_12_23_00_world_creature_text.sql)0
-rw-r--r--sql/old/3.3.5a/2012_12_23_01_world_creature_text.sql (renamed from sql/updates/world/2012_12_23_01_world_creature_text.sql)0
-rw-r--r--sql/old/3.3.5a/2012_12_23_02_world_trinity_string.sql (renamed from sql/updates/world/2012_12_23_02_world_trinity_string.sql)0
-rw-r--r--sql/old/3.3.5a/2012_12_23_03_world_misc.sql (renamed from sql/updates/world/2012_12_23_03_world_misc.sql)0
-rw-r--r--sql/old/3.3.5a/2012_12_24_00_world_defending_wyrmrest_temple.sql (renamed from sql/updates/world/2012_12_24_00_world_defending_wyrmrest_temple.sql)0
-rw-r--r--sql/old/3.3.5a/2012_12_24_01_world_sai.sql (renamed from sql/updates/world/2012_12_24_01_world_sai.sql)0
-rw-r--r--sql/old/3.3.5a/2012_12_24_02_world_sai.sql (renamed from sql/updates/world/2012_12_24_02_world_sai.sql)0
-rw-r--r--sql/old/3.3.5a/2012_12_24_03_world_sai.sql (renamed from sql/updates/world/2012_12_24_03_world_sai.sql)0
-rw-r--r--sql/old/3.3.5a/2012_12_24_04_world_creature.sql (renamed from sql/updates/world/2012_12_24_04_world_creature.sql)0
-rw-r--r--sql/old/3.3.5a/2012_12_24_05_world_object.sql (renamed from sql/updates/world/2012_12_24_05_world_object.sql)0
-rw-r--r--sql/old/3.3.5a/2012_12_24_06_world_reputation.sql (renamed from sql/updates/world/2012_12_24_06_world_reputation.sql)0
-rw-r--r--sql/old/3.3.5a/2012_12_24_07_world_sai.sql (renamed from sql/updates/world/2012_12_24_07_world_sai.sql)0
-rw-r--r--sql/old/3.3.5a/2012_12_24_08_world_conditions.sql (renamed from sql/updates/world/2012_12_24_08_world_conditions.sql)0
-rw-r--r--sql/old/3.3.5a/2012_12_24_09_world_sai.sql (renamed from sql/updates/world/2012_12_24_09_world_sai.sql)0
-rw-r--r--sql/old/3.3.5a/2012_12_25_00_world_sai.sql (renamed from sql/updates/world/2012_12_25_00_world_sai.sql)0
-rw-r--r--sql/old/3.3.5a/2012_12_25_01_world_sai.sql (renamed from sql/updates/world/2012_12_25_01_world_sai.sql)0
-rw-r--r--sql/old/3.3.5a/2012_12_25_02_world_sai.sql (renamed from sql/updates/world/2012_12_25_02_world_sai.sql)0
-rw-r--r--sql/old/3.3.5a/2012_12_25_03_world_sai.sql (renamed from sql/updates/world/2012_12_25_03_world_sai.sql)0
-rw-r--r--sql/old/3.3.5a/2012_12_25_04_world_sai.sql (renamed from sql/updates/world/2012_12_25_04_world_sai.sql)0
-rw-r--r--sql/old/3.3.5a/2012_12_26_01_world_creature_text.sql (renamed from sql/updates/world/2012_12_26_01_world_creature_text.sql)0
-rw-r--r--sql/old/3.3.5a/2012_12_26_02_world_sai.sql (renamed from sql/updates/world/2012_12_26_02_world_sai.sql)0
-rw-r--r--sql/old/3.3.5a/2012_12_26_03_world_sai.sql (renamed from sql/updates/world/2012_12_26_03_world_sai.sql)0
-rw-r--r--sql/old/3.3.5a/2012_12_26_04_world_sai.sql (renamed from sql/updates/world/2012_12_26_04_world_sai.sql)0
-rw-r--r--sql/old/3.3.5a/2012_12_26_05_world_sai.sql (renamed from sql/updates/world/2012_12_26_05_world_sai.sql)0
-rw-r--r--sql/old/3.3.5a/2012_12_26_06_world_sai.sql (renamed from sql/updates/world/2012_12_26_06_world_sai.sql)0
-rw-r--r--sql/old/3.3.5a/2012_12_26_07_world_game_event.sql (renamed from sql/updates/world/2012_12_26_07_world_game_event.sql)0
-rw-r--r--sql/old/3.3.5a/2012_12_26_08_world_sai.sql (renamed from sql/updates/world/2012_12_26_08_world_sai.sql)0
-rw-r--r--sql/old/3.3.5a/2012_12_26_09_world_sai.sql (renamed from sql/updates/world/2012_12_26_09_world_sai.sql)0
-rw-r--r--sql/old/3.3.5a/2012_12_26_10_world_sai.sql (renamed from sql/updates/world/2012_12_26_10_world_sai.sql)0
-rw-r--r--sql/old/3.3.5a/2012_12_26_11_world_sai.sql (renamed from sql/updates/world/2012_12_26_11_world_sai.sql)0
-rw-r--r--sql/old/3.3.5a/2012_12_26_12_world_quest_end_scripts.sql (renamed from sql/updates/world/2012_12_26_12_world_quest_end_scripts.sql)0
-rw-r--r--sql/old/3.3.5a/2012_12_26_13_world_sai.sql (renamed from sql/updates/world/2012_12_26_13_world_sai.sql)0
-rw-r--r--sql/old/3.3.5a/2012_12_26_14_world_sai.sql (renamed from sql/updates/world/2012_12_26_14_world_sai.sql)0
-rw-r--r--sql/old/3.3.5a/2012_12_26_15_world_sai.sql (renamed from sql/updates/world/2012_12_26_15_world_sai.sql)0
-rw-r--r--sql/old/3.3.5a/2012_12_26_16_world_sai.sql (renamed from sql/updates/world/2012_12_26_16_world_sai.sql)0
-rw-r--r--sql/old/3.3.5a/2012_12_26_17_world_sai.sql (renamed from sql/updates/world/2012_12_26_17_world_sai.sql)0
-rw-r--r--sql/old/3.3.5a/2012_12_26_18_world_sai.sql (renamed from sql/updates/world/2012_12_26_18_world_sai.sql)0
-rw-r--r--sql/old/3.3.5a/2012_12_26_19_world_sai.sql (renamed from sql/updates/world/2012_12_26_19_world_sai.sql)0
-rw-r--r--sql/old/3.3.5a/2012_12_26_20_world_sai.sql (renamed from sql/updates/world/2012_12_26_20_world_sai.sql)0
-rw-r--r--sql/old/3.3.5a/2012_12_27_00_world_orgrims_hammer.sql (renamed from sql/updates/world/2012_12_27_00_world_orgrims_hammer.sql)0
-rw-r--r--sql/old/3.3.5a/2012_12_27_01_world_locales_quest.sql (renamed from sql/updates/world/2012_12_27_01_world_locales_quest.sql)0
-rw-r--r--sql/old/3.3.5a/2012_12_27_02_world_locales.sql (renamed from sql/updates/world/2012_12_27_02_world_locales.sql)0
-rw-r--r--sql/old/3.3.5a/2012_12_27_03_world_sai.sql (renamed from sql/updates/world/2012_12_27_03_world_sai.sql)0
-rw-r--r--sql/old/3.3.5a/2012_12_27_04_world_the_shadows_vault.sql (renamed from sql/updates/world/2012_12_27_04_world_the_shadows_vault.sql)0
-rw-r--r--sql/old/3.3.5a/2012_12_28_00_world_command.sql (renamed from sql/updates/world/2012_12_28_00_world_command.sql)0
-rw-r--r--sql/old/3.3.5a/2012_12_28_01_world_misc.sql (renamed from sql/updates/world/2012_12_28_01_world_misc.sql)0
-rw-r--r--sql/old/3.3.5a/2012_12_28_02_world_sai.sql (renamed from sql/updates/world/2012_12_28_02_world_sai.sql)0
-rw-r--r--sql/old/3.3.5a/2012_12_28_03_world_disables.sql (renamed from sql/updates/world/2012_12_28_03_world_disables.sql)0
-rw-r--r--sql/old/3.3.5a/2012_12_28_04_world_citadel_footsteps.sql (renamed from sql/updates/world/2012_12_28_04_world_citadel_footsteps.sql)0
-rw-r--r--sql/old/3.3.5a/2012_12_30_01_world_trinity_string.sql (renamed from sql/updates/world/2012_12_30_01_world_trinity_string.sql)0
-rw-r--r--sql/old/3.3.5a/2012_12_31_00_world_sai.sql (renamed from sql/updates/world/2012_12_31_00_world_sai.sql)0
-rw-r--r--sql/old/3.3.5a/2012_12_31_01_world_sai.sql (renamed from sql/updates/world/2012_12_31_01_world_sai.sql)0
-rw-r--r--sql/old/3.3.5a/2013_01_01_00_item_template_restore.sql (renamed from sql/updates/world/2013_01_01_00_item_template_restore.sql)0
-rw-r--r--sql/old/3.3.5a/2013_01_01_01_world_creature_text.sql (renamed from sql/updates/world/2013_01_01_01_world_creature_text.sql)0
-rw-r--r--sql/old/3.3.5a/2013_01_02_00_world_misc.sql (renamed from sql/updates/world/2013_01_02_00_world_misc.sql)0
-rw-r--r--sql/old/3.3.5a/2013_01_02_01_world_spell_script.sql (renamed from sql/updates/world/2013_01_02_01_world_spell_script.sql)0
-rw-r--r--sql/old/3.3.5a/2013_01_02_02_world_instance_template.sql (renamed from sql/updates/world/2013_01_02_02_world_instance_template.sql)0
-rw-r--r--sql/old/3.3.5a/2013_01_02_03_world_sai.sql (renamed from sql/updates/world/2013_01_02_03_world_sai.sql)0
-rw-r--r--sql/old/3.3.5a/2013_01_02_04_world_creature.sql (renamed from sql/updates/world/2013_01_02_04_world_creature.sql)0
-rw-r--r--sql/old/3.3.5a/2013_01_02_05_eye_of_eternity.sql (renamed from sql/updates/world/2013_01_02_05_eye_of_eternity.sql)0
-rw-r--r--sql/old/3.3.5a/2013_01_02_06_world_sai.sql (renamed from sql/updates/world/2013_01_02_06_world_sai.sql)0
-rw-r--r--sql/old/3.3.5a/2013_01_02_06_world_waypoints.sql (renamed from sql/updates/world/2013_01_02_06_world_waypoints.sql)0
-rw-r--r--sql/old/3.3.5a/2013_01_02_07_world_waypoints.sql (renamed from sql/updates/world/2013_01_02_07_world_waypoints.sql)0
-rw-r--r--sql/old/3.3.5a/2013_01_02_08_world_sai.sql (renamed from sql/updates/world/2013_01_02_08_world_sai.sql)0
-rw-r--r--sql/old/3.3.5a/2013_01_02_09_world_waypoints.sql (renamed from sql/updates/world/2013_01_02_09_world_waypoints.sql)0
-rw-r--r--sql/old/3.3.5a/2013_01_02_10_world_sai.sql (renamed from sql/updates/world/2013_01_02_10_world_sai.sql)0
-rw-r--r--sql/old/3.3.5a/2013_01_02_11_world_sai.sql (renamed from sql/updates/world/2013_01_02_11_world_sai.sql)0
-rw-r--r--sql/old/3.3.5a/2013_01_02_12_world_go_script.sql (renamed from sql/updates/world/2013_01_02_12_world_go_script.sql)0
-rw-r--r--sql/old/3.3.5a/2013_01_02_12_world_waypoints.sql (renamed from sql/updates/world/2013_01_02_12_world_waypoints.sql)0
-rw-r--r--sql/old/3.3.5a/2013_01_03_00_world_gameobject_scripts.sql (renamed from sql/updates/world/2013_01_03_00_world_gameobject_scripts.sql)0
-rw-r--r--sql/old/3.3.5a/2013_01_03_01_world_sai.sql (renamed from sql/updates/world/2013_01_03_01_world_sai.sql)0
-rw-r--r--sql/old/3.3.5a/2013_01_03_02_world_sai.sql (renamed from sql/updates/world/2013_01_03_02_world_sai.sql)0
-rw-r--r--sql/old/3.3.5a/2013_01_03_03_world_spelldifficulty_dbc.sql (renamed from sql/updates/world/2013_01_03_03_world_spelldifficulty_dbc.sql)0
-rw-r--r--sql/old/3.3.5a/2013_01_03_04_world_sai.sql (renamed from sql/updates/world/2013_01_03_04_world_sai.sql)0
-rw-r--r--sql/old/3.3.5a/2013_01_03_05_world_sai.sql (renamed from sql/updates/world/2013_01_03_05_world_sai.sql)0
-rw-r--r--sql/old/3.3.5a/2013_01_03_06_world_sai.sql (renamed from sql/updates/world/2013_01_03_06_world_sai.sql)0
-rw-r--r--sql/old/3.3.5a/2013_01_03_07_world_sai.sql (renamed from sql/updates/world/2013_01_03_07_world_sai.sql)0
-rw-r--r--sql/old/3.3.5a/2013_01_03_08_world_misc.sql (renamed from sql/updates/world/2013_01_03_08_world_misc.sql)0
-rw-r--r--sql/old/3.3.5a/2013_01_03_09_world_sai.sql (renamed from sql/updates/world/2013_01_03_09_world_sai.sql)0
-rw-r--r--sql/old/3.3.5a/2013_01_03_10_world_gameobject.sql (renamed from sql/updates/world/2013_01_03_10_world_gameobject.sql)0
-rw-r--r--sql/old/3.3.5a/2013_01_03_11_world_item_template.sql (renamed from sql/updates/world/2013_01_03_11_world_item_template.sql)0
-rw-r--r--sql/old/3.3.5a/2013_01_03_12_world_gameobject_scripts.sql (renamed from sql/updates/world/2013_01_03_12_world_gameobject_scripts.sql)0
-rw-r--r--sql/old/3.3.5a/2013_01_03_12_world_misc.sql (renamed from sql/updates/world/2013_01_03_12_world_misc.sql)0
-rw-r--r--sql/old/3.3.5a/2013_01_03_13_world_quest_template.sql (renamed from sql/updates/world/2013_01_03_13_world_quest_template.sql)0
-rw-r--r--sql/old/3.3.5a/2013_01_03_14_world_spell_area.sql (renamed from sql/updates/world/2013_01_03_14_world_spell_area.sql)0
-rw-r--r--sql/old/3.3.5a/2013_01_03_15_world_various_fixes.sql (renamed from sql/updates/world/2013_01_03_15_world_various_fixes.sql)0
-rw-r--r--sql/old/3.3.5a/2013_01_03_16_world_various_fixes.sql (renamed from sql/updates/world/2013_01_03_16_world_various_fixes.sql)0
-rw-r--r--sql/old/3.3.5a/2013_01_04_00_world_creature_text.sql (renamed from sql/updates/world/2013_01_04_00_world_creature_text.sql)0
-rw-r--r--sql/old/3.3.5a/2013_01_04_01_world_sai.sql (renamed from sql/updates/world/2013_01_04_01_world_sai.sql)0
-rw-r--r--sql/old/3.3.5a/2013_01_04_02_world_misc.sql (renamed from sql/updates/world/2013_01_04_02_world_misc.sql)0
-rw-r--r--sql/old/3.3.5a/2013_01_04_02_world_novos_the_summoner.sql (renamed from sql/updates/world/2013_01_04_02_world_novos_the_summoner.sql)0
-rw-r--r--sql/old/3.3.5a/2013_01_05_00_world_waypoints.sql (renamed from sql/updates/world/2013_01_05_00_world_waypoints.sql)0
-rw-r--r--sql/old/3.3.5a/2013_01_06_00_world_creature_addon.sql (renamed from sql/updates/world/2013_01_06_00_world_creature_addon.sql)0
-rw-r--r--sql/old/3.3.5a/2013_01_06_00_world_creature_template.sql (renamed from sql/updates/world/2013_01_06_00_world_creature_template.sql)0
-rw-r--r--sql/old/3.3.5a/2013_01_06_01_world_waypoints.sql (renamed from sql/updates/world/2013_01_06_01_world_waypoints.sql)0
-rw-r--r--sql/old/3.3.5a/2013_01_06_02_world_misc.sql (renamed from sql/updates/world/2013_01_06_02_world_misc.sql)0
-rw-r--r--sql/old/3.3.5a/2013_01_06_03_world_sai.sql (renamed from sql/updates/world/2013_01_06_03_world_sai.sql)0
-rw-r--r--sql/old/3.3.5a/2013_01_06_04_world_misc.sql (renamed from sql/updates/world/2013_01_06_04_world_misc.sql)0
-rw-r--r--sql/old/3.3.5a/2013_01_07_00_world_sai.sql (renamed from sql/updates/world/2013_01_07_00_world_sai.sql)0
-rw-r--r--sql/old/3.3.5a/2013_01_07_01_world_creature_addon.sql (renamed from sql/updates/world/2013_01_07_01_world_creature_addon.sql)0
-rw-r--r--sql/old/3.3.5a/2013_01_07_02_world_creature_text.sql (renamed from sql/updates/world/2013_01_07_02_world_creature_text.sql)0
-rw-r--r--sql/old/3.3.5a/2013_01_07_03_world_creature_text.sql (renamed from sql/updates/world/2013_01_07_03_world_creature_text.sql)0
-rw-r--r--sql/old/3.3.5a/2013_01_07_04_world_creature_text.sql (renamed from sql/updates/world/2013_01_07_04_world_creature_text.sql)0
-rw-r--r--sql/old/3.3.5a/2013_01_07_05_world_creature_text.sql (renamed from sql/updates/world/2013_01_07_05_world_creature_text.sql)0
-rw-r--r--sql/old/3.3.5a/2013_01_07_06_world_creature_text.sql (renamed from sql/updates/world/2013_01_07_06_world_creature_text.sql)0
-rw-r--r--sql/old/3.3.5a/2013_01_07_07_world_creature_text.sql (renamed from sql/updates/world/2013_01_07_07_world_creature_text.sql)0
-rw-r--r--sql/old/3.3.5a/2013_01_07_08_world_creature_text.sql (renamed from sql/updates/world/2013_01_07_08_world_creature_text.sql)0
-rw-r--r--sql/old/3.3.5a/2013_01_07_09_world_buru.sql (renamed from sql/updates/world/2013_01_07_09_world_buru.sql)0
-rw-r--r--sql/old/3.3.5a/2013_01_07_10_world_skeram.sql (renamed from sql/updates/world/2013_01_07_10_world_skeram.sql)0
-rw-r--r--sql/old/3.3.5a/2013_01_07_11_world_sai.sql (renamed from sql/updates/world/2013_01_07_11_world_sai.sql)0
-rw-r--r--sql/old/3.3.5a/2013_01_07_12_world_sai.sql (renamed from sql/updates/world/2013_01_07_12_world_sai.sql)0
-rw-r--r--sql/old/3.3.5a/2013_01_07_13_world_creature_text.sql (renamed from sql/updates/world/2013_01_07_13_world_creature_text.sql)0
-rw-r--r--sql/old/3.3.5a/2013_01_07_14_world_creature_text.sql (renamed from sql/updates/world/2013_01_07_14_world_creature_text.sql)0
-rw-r--r--sql/old/3.3.5a/2013_01_07_15_world_creature_addon.sql (renamed from sql/updates/world/2013_01_07_15_world_creature_addon.sql)0
-rw-r--r--sql/old/3.3.5a/2013_01_07_16_world_creature_addon.sql (renamed from sql/updates/world/2013_01_07_16_world_creature_addon.sql)0
-rw-r--r--sql/old/3.3.5a/2013_01_07_17_world_creature_text.sql (renamed from sql/updates/world/2013_01_07_17_world_creature_text.sql)0
-rw-r--r--sql/old/3.3.5a/2013_01_07_18_world_creature_text.sql (renamed from sql/updates/world/2013_01_07_18_world_creature_text.sql)0
-rw-r--r--sql/old/3.3.5a/2013_01_08_01_world_raise_the_barricades.sql (renamed from sql/updates/world/2013_01_08_01_world_raise_the_barricades.sql)0
-rw-r--r--sql/old/3.3.5a/2013_01_09_00_world_misc.sql (renamed from sql/updates/world/2013_01_09_00_world_misc.sql)0
-rw-r--r--sql/old/3.3.5a/2013_01_09_01_world_lfg_dungeon_rewards.sql (renamed from sql/updates/world/2013_01_09_01_world_lfg_dungeon_rewards.sql)0
-rw-r--r--sql/old/3.3.5a/2013_01_09_02_world_sai.sql (renamed from sql/updates/world/2013_01_09_02_world_sai.sql)0
-rw-r--r--sql/old/3.3.5a/2013_01_09_03_world_spell_script_names.sql (renamed from sql/updates/world/2013_01_09_03_world_spell_script_names.sql)0
-rw-r--r--sql/old/3.3.5a/2013_01_10_00_world_trinity_string.sql (renamed from sql/updates/world/2013_01_10_00_world_trinity_string.sql)0
-rw-r--r--sql/old/3.3.5a/2013_01_10_01_world_sai.sql (renamed from sql/updates/world/2013_01_10_01_world_sai.sql)0
-rw-r--r--sql/old/3.3.5a/2013_01_10_02_world_creature_addon.sql (renamed from sql/updates/world/2013_01_10_02_world_creature_addon.sql)0
-rw-r--r--sql/old/3.3.5a/2013_01_10_03_world_creature_addon.sql (renamed from sql/updates/world/2013_01_10_03_world_creature_addon.sql)0
-rw-r--r--sql/old/3.3.5a/2013_01_10_04_world_creature_addon.sql (renamed from sql/updates/world/2013_01_10_04_world_creature_addon.sql)0
-rw-r--r--sql/old/3.3.5a/2013_01_10_05_world_sai.sql (renamed from sql/updates/world/2013_01_10_05_world_sai.sql)0
-rw-r--r--sql/old/3.3.5a/2013_01_11_00_world_ayamiss.sql (renamed from sql/updates/world/2013_01_11_00_world_ayamiss.sql)0
-rw-r--r--sql/old/3.3.5a/2013_01_11_01_world_conditions.sql (renamed from sql/updates/world/2013_01_11_01_world_conditions.sql)0
-rw-r--r--sql/old/3.3.5a/2013_01_11_02_world_sai.sql (renamed from sql/updates/world/2013_01_11_02_world_sai.sql)0
-rw-r--r--sql/old/3.3.5a/2013_01_11_03_world_sai.sql (renamed from sql/updates/world/2013_01_11_03_world_sai.sql)0
-rw-r--r--sql/old/3.3.5a/2013_01_11_04_world_sai.sql (renamed from sql/updates/world/2013_01_11_04_world_sai.sql)0
-rw-r--r--sql/old/3.3.5a/2013_01_11_05_world_creature_text.sql (renamed from sql/updates/world/2013_01_11_05_world_creature_text.sql)0
-rw-r--r--sql/old/3.3.5a/2013_01_12_00_world_smart_scripts.sql7
-rw-r--r--sql/old/3.3.5a/2013_01_12_01_world_string.sql3
-rw-r--r--sql/old/3.3.5a/2013_01_12_02_world_creature_template.sql1
-rw-r--r--sql/old/3.3.5a/2013_01_12_03_world_gameobject.sql7
-rw-r--r--sql/old/3.3.5a/2013_01_12_03_world_waypoints.sql53
-rw-r--r--sql/old/3.3.5a/2013_01_12_04_world_misc.sql28
-rw-r--r--sql/old/3.3.5a/2013_01_12_05_world_waypoint_script.sql1
-rw-r--r--sql/old/3.3.5a/2013_01_13_00_world_gameobject_template.sql1
-rw-r--r--sql/old/3.3.5a/2013_01_13_01_world_sai.sql41
-rw-r--r--sql/old/3.3.5a/2013_01_13_02_world_gluttonous_lurkers.sql36
-rw-r--r--sql/old/3.3.5a/2013_01_13_03_world_spell_script_names.sql4
-rw-r--r--sql/old/3.3.5a/2013_01_13_04_world_conditions.sql9
-rw-r--r--sql/updates/world/2013_01_14_00_world_version.sql1
-rw-r--r--sql/updates/world/2013_01_14_01_world_game_event.sql12
-rw-r--r--sql/updates/world/2013_01_14_02_world_ip2nationcountries.sql2
-rw-r--r--sql/updates/world/2013_01_14_03_world_lefty_loosy_righty_tighty.sql55
-rw-r--r--sql/updates/world/2013_01_15_00_world_creature.sql12
-rw-r--r--sql/updates/world/2013_01_15_01_world_misc.sql40
-rw-r--r--sql/updates/world/2013_01_15_02_world_game_event.sql1
-rw-r--r--sql/updates/world/2013_01_15_03_world_misc.sql88
-rw-r--r--sql/updates/world/2013_01_15_04_world_game_event.sql1
-rw-r--r--sql/updates/world/2013_01_15_05_world_creature_addon.sql1
-rw-r--r--sql/updates/world/2013_01_15_06_world_spell_script_names.sql3
-rw-r--r--sql/updates/world/2013_01_16_00_world_gossip_menu.sql4
-rw-r--r--sql/updates/world/2013_01_16_01_world_creature_text.sql7
-rw-r--r--sql/updates/world/2013_01_17_00_world_creature_template.sql2
-rw-r--r--sql/updates/world/2013_01_17_01_world_creature.sql8
-rw-r--r--sql/updates/world/2013_01_18_00_world_misc.sql72
-rw-r--r--sql/updates/world/2013_01_18_01_world_creature_template_addon.sql22
-rw-r--r--sql/updates/world/2013_01_18_02_world_creature_template_addon.sql634
-rw-r--r--sql/updates/world/2013_01_19_00_world_creature_text.sql1
-rw-r--r--sql/updates/world/2013_01_19_01_world_creature_text.sql2
-rw-r--r--sql/updates/world/2013_01_19_02_world_conditions.sql3
-rw-r--r--sql/updates/world/2013_01_19_03_world_creature_text.sql1
-rw-r--r--sql/updates/world/2013_01_19_04_world_creature_addon.sql29
-rw-r--r--sql/updates/world/2013_01_19_04_world_creature_text.sql1
-rw-r--r--sql/updates/world/2013_01_19_05_world_creature_addon.sql2
-rw-r--r--sql/updates/world/2013_01_19_06_world_misc.sql14
-rw-r--r--sql/updates/world/2013_01_19_06_world_trinity_string.sql28
-rw-r--r--sql/updates/world/2013_01_20_00_world_sai.sql1
-rw-r--r--sql/updates/world/2013_01_20_01_world_creature_text.sql10
-rw-r--r--sql/updates/world/2013_01_20_02_world_creature_text.sql10
-rw-r--r--sql/updates/world/2013_01_20_03_world_creature_text.sql15
-rw-r--r--sql/updates/world/2013_01_20_03_world_spell_script_names.sql81
-rw-r--r--sql/updates/world/2013_01_20_04_world_creature_text.sql12
-rw-r--r--sql/updates/world/2013_01_20_05_world_sai.sql41
-rw-r--r--sql/updates/world/2013_01_20_06_world_creature.sql24
-rw-r--r--sql/updates/world/2013_01_20_07_world_sai.sql5
-rw-r--r--sql/updates/world/2013_01_20_08_world_sai.sql4
-rw-r--r--sql/updates/world/2013_01_20_09_world_spell_dbc.sql18
-rw-r--r--sql/updates/world/2013_01_21_00_world_misc.sql2
-rw-r--r--sql/updates/world/2013_01_21_01_world_creature_template.sql1
-rw-r--r--sql/updates/world/2013_01_21_02_world_sai.sql7
-rw-r--r--sql/updates/world/2013_01_22_00_world_command.sql11
-rw-r--r--sql/updates/world/2013_01_22_01_world_sai.sql22
-rw-r--r--src/server/CMakeLists.txt1
-rw-r--r--src/server/authserver/Main.cpp4
-rw-r--r--src/server/authserver/Server/AuthSocket.cpp2
-rw-r--r--src/server/collision/BoundingIntervalHierarchyWrapper.h18
-rw-r--r--src/server/collision/CMakeLists.txt2
-rw-r--r--src/server/collision/DynamicTree.cpp6
-rw-r--r--src/server/collision/Management/MMapFactory.cpp52
-rw-r--r--src/server/collision/Management/MMapFactory.h50
-rw-r--r--src/server/collision/Management/MMapManager.cpp302
-rw-r--r--src/server/collision/Management/MMapManager.h84
-rw-r--r--src/server/collision/Management/VMapManager2.cpp12
-rw-r--r--src/server/collision/Management/VMapManager2.h2
-rw-r--r--src/server/collision/Maps/MapTree.cpp22
-rw-r--r--src/server/collision/Maps/MapTree.h1
-rw-r--r--src/server/collision/Maps/TileAssembler.cpp10
-rw-r--r--src/server/collision/Maps/TileAssembler.h4
-rw-r--r--src/server/collision/Models/GameObjectModel.cpp15
-rw-r--r--src/server/collision/Models/ModelInstance.h2
-rw-r--r--src/server/collision/Models/WorldModel.h6
-rw-r--r--src/server/collision/RegularGrid.h2
-rw-r--r--src/server/collision/VMapDefinitions.h12
-rw-r--r--src/server/game/AI/CoreAI/UnitAI.cpp4
-rw-r--r--src/server/game/AI/SmartScripts/SmartScript.cpp46
-rw-r--r--src/server/game/Accounts/AccountMgr.cpp39
-rw-r--r--src/server/game/Accounts/AccountMgr.h50
-rw-r--r--src/server/game/Battlefield/Battlefield.cpp8
-rw-r--r--src/server/game/Battlefield/Battlefield.h2
-rw-r--r--src/server/game/Battlefield/BattlefieldMgr.cpp8
-rw-r--r--src/server/game/Battlefield/BattlefieldMgr.h14
-rw-r--r--src/server/game/Battlefield/Zones/BattlefieldWG.cpp2
-rw-r--r--src/server/game/Battlefield/Zones/BattlefieldWG.h8
-rw-r--r--src/server/game/Battlegrounds/Battleground.cpp9
-rw-r--r--src/server/game/Battlegrounds/BattlegroundMgr.cpp32
-rw-r--r--src/server/game/Battlegrounds/BattlegroundMgr.h2
-rw-r--r--src/server/game/Battlegrounds/Zones/BattlegroundEY.cpp8
-rw-r--r--src/server/game/CMakeLists.txt2
-rw-r--r--src/server/game/Calendar/CalendarMgr.cpp2
-rw-r--r--src/server/game/Combat/ThreatManager.cpp2
-rw-r--r--src/server/game/Conditions/ConditionMgr.cpp2
-rw-r--r--src/server/game/Conditions/DisableMgr.cpp31
-rw-r--r--src/server/game/Conditions/DisableMgr.h8
-rw-r--r--src/server/game/DataStores/DBCStores.cpp20
-rw-r--r--src/server/game/DataStores/DBCStores.h2
-rw-r--r--src/server/game/DataStores/DBCStructure.h14
-rw-r--r--src/server/game/DataStores/DBCfmt.h203
-rw-r--r--src/server/game/DungeonFinding/LFGMgr.cpp15
-rw-r--r--src/server/game/DungeonFinding/LFGMgr.h5
-rw-r--r--src/server/game/DungeonFinding/LFGQueue.cpp2
-rw-r--r--src/server/game/Entities/Creature/Creature.cpp2
-rw-r--r--src/server/game/Entities/GameObject/GameObject.h2
-rw-r--r--src/server/game/Entities/Object/Object.cpp51
-rw-r--r--src/server/game/Entities/Object/Object.h5
-rw-r--r--src/server/game/Entities/Player/Player.cpp41
-rw-r--r--src/server/game/Entities/Player/Player.h8
-rw-r--r--src/server/game/Entities/Unit/Unit.cpp51
-rw-r--r--src/server/game/Entities/Unit/Unit.h19
-rwxr-xr-x[-rw-r--r--]src/server/game/Entities/Vehicle/Vehicle.cpp2
-rw-r--r--src/server/game/Globals/ObjectMgr.cpp7
-rw-r--r--src/server/game/Grids/Notifiers/GridNotifiers.h2
-rw-r--r--src/server/game/Guilds/Guild.cpp2
-rw-r--r--src/server/game/Handlers/BattleGroundHandler.cpp4
-rw-r--r--src/server/game/Handlers/BattlefieldHandler.cpp2
-rw-r--r--src/server/game/Handlers/GuildHandler.cpp2
-rw-r--r--src/server/game/Handlers/MiscHandler.cpp2
-rw-r--r--src/server/game/Handlers/MovementHandler.cpp2
-rw-r--r--src/server/game/Handlers/PetHandler.cpp7
-rw-r--r--src/server/game/Maps/Map.cpp37
-rw-r--r--src/server/game/Maps/Map.h4
-rw-r--r--src/server/game/Maps/MapInstanced.cpp2
-rw-r--r--src/server/game/Maps/ZoneScript.h8
-rw-r--r--src/server/game/Miscellaneous/Language.h28
-rw-r--r--src/server/game/Miscellaneous/SharedDefines.h34
-rw-r--r--src/server/game/Movement/FollowerRefManager.h1
-rw-r--r--src/server/game/Movement/FollowerReference.cpp1
-rw-r--r--src/server/game/Movement/FollowerReference.h1
-rw-r--r--src/server/game/Movement/MotionMaster.cpp86
-rw-r--r--src/server/game/Movement/MotionMaster.h7
-rwxr-xr-x[-rw-r--r--]src/server/game/Movement/MovementGenerator.h31
-rw-r--r--src/server/game/Movement/MovementGeneratorImpl.h1
-rwxr-xr-xsrc/server/game/Movement/MovementGenerators/ConfusedMovementGenerator.cpp150
-rwxr-xr-xsrc/server/game/Movement/MovementGenerators/ConfusedMovementGenerator.h15
-rw-r--r--[-rwxr-xr-x]src/server/game/Movement/MovementGenerators/FleeingMovementGenerator.cpp387
-rwxr-xr-xsrc/server/game/Movement/MovementGenerators/FleeingMovementGenerator.h29
-rw-r--r--src/server/game/Movement/MovementGenerators/HomeMovementGenerator.cpp49
-rw-r--r--src/server/game/Movement/MovementGenerators/HomeMovementGenerator.h11
-rwxr-xr-xsrc/server/game/Movement/MovementGenerators/IdleMovementGenerator.cpp55
-rwxr-xr-x[-rw-r--r--]src/server/game/Movement/MovementGenerators/IdleMovementGenerator.h27
-rwxr-xr-x[-rw-r--r--]src/server/game/Movement/MovementGenerators/PointMovementGenerator.cpp106
-rw-r--r--src/server/game/Movement/MovementGenerators/PointMovementGenerator.h30
-rw-r--r--src/server/game/Movement/MovementGenerators/RandomMovementGenerator.cpp52
-rw-r--r--src/server/game/Movement/MovementGenerators/RandomMovementGenerator.h13
-rw-r--r--[-rwxr-xr-x]src/server/game/Movement/MovementGenerators/TargetedMovementGenerator.cpp306
-rwxr-xr-xsrc/server/game/Movement/MovementGenerators/TargetedMovementGenerator.h63
-rwxr-xr-x[-rw-r--r--]src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.cpp426
-rwxr-xr-x[-rw-r--r--]src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.h33
-rw-r--r--src/server/game/Movement/PathGenerator.cpp803
-rw-r--r--src/server/game/Movement/PathGenerator.h135
-rw-r--r--src/server/game/Movement/Spline/MoveSpline.cpp6
-rw-r--r--src/server/game/Movement/Spline/MoveSpline.h9
-rw-r--r--src/server/game/Movement/Spline/MoveSplineFlag.h2
-rw-r--r--src/server/game/Movement/Spline/MoveSplineInit.cpp69
-rw-r--r--src/server/game/Movement/Spline/MoveSplineInit.h23
-rw-r--r--src/server/game/Movement/Spline/MoveSplineInitArgs.h2
-rw-r--r--src/server/game/Movement/Spline/MovementPacketBuilder.cpp2
-rw-r--r--src/server/game/Movement/Spline/MovementUtil.cpp130
-rw-r--r--src/server/game/Movement/Spline/Spline.cpp8
-rw-r--r--src/server/game/Movement/Spline/Spline.h18
-rw-r--r--src/server/game/Movement/Spline/SplineImpl.h2
-rw-r--r--src/server/game/Movement/Waypoints/Path.h25
-rw-r--r--src/server/game/Movement/Waypoints/WaypointManager.h1
-rw-r--r--src/server/game/OutdoorPvP/OutdoorPvP.cpp12
-rw-r--r--src/server/game/OutdoorPvP/OutdoorPvP.h2
-rw-r--r--src/server/game/Scripting/MapScripts.cpp2
-rw-r--r--src/server/game/Scripting/ScriptLoader.cpp4
-rw-r--r--src/server/game/Server/WorldSession.cpp37
-rw-r--r--src/server/game/Server/WorldSession.h3
-rw-r--r--src/server/game/Server/WorldSocket.cpp199
-rw-r--r--src/server/game/Server/WorldSocket.h43
-rw-r--r--src/server/game/Spells/Auras/SpellAuraEffects.cpp301
-rw-r--r--src/server/game/Spells/Auras/SpellAuras.cpp116
-rw-r--r--src/server/game/Spells/Auras/SpellAuras.h9
-rw-r--r--src/server/game/Spells/Spell.cpp38
-rw-r--r--src/server/game/Spells/Spell.h2
-rw-r--r--src/server/game/Spells/SpellEffects.cpp219
-rw-r--r--src/server/game/Spells/SpellInfo.cpp8
-rw-r--r--src/server/game/Spells/SpellMgr.cpp2
-rw-r--r--src/server/game/Spells/SpellScript.cpp67
-rw-r--r--src/server/game/Spells/SpellScript.h72
-rw-r--r--src/server/game/Tickets/TicketMgr.cpp2
-rw-r--r--src/server/game/Warden/Warden.h4
-rw-r--r--src/server/game/Warden/WardenMac.cpp2
-rw-r--r--src/server/game/Warden/WardenWin.cpp4
-rw-r--r--src/server/game/Warden/WardenWin.h2
-rw-r--r--src/server/game/World/World.cpp19
-rw-r--r--src/server/game/World/World.h2
-rw-r--r--src/server/scripts/CMakeLists.txt2
-rw-r--r--src/server/scripts/Commands/CMakeLists.txt1
-rw-r--r--src/server/scripts/Commands/cs_debug.cpp6
-rw-r--r--src/server/scripts/Commands/cs_disable.cpp32
-rw-r--r--src/server/scripts/Commands/cs_misc.cpp4
-rw-r--r--src/server/scripts/Commands/cs_mmaps.cpp294
-rw-r--r--src/server/scripts/Commands/cs_npc.cpp44
-rw-r--r--src/server/scripts/Commands/cs_quest.cpp2
-rw-r--r--src/server/scripts/Commands/cs_ticket.cpp3
-rw-r--r--src/server/scripts/EasternKingdoms/BlackrockSpire/boss_pyroguard_emberseer.cpp8
-rw-r--r--src/server/scripts/EasternKingdoms/BlackrockSpire/instance_blackrock_spire.cpp3
-rw-r--r--src/server/scripts/EasternKingdoms/Karazhan/boss_prince_malchezaar.cpp2
-rw-r--r--src/server/scripts/EasternKingdoms/ScarletEnclave/chapter1.cpp2
-rw-r--r--src/server/scripts/EasternKingdoms/ScarletEnclave/zone_the_scarlet_enclave.cpp2
-rw-r--r--src/server/scripts/EasternKingdoms/Scholomance/boss_darkmaster_gandling.cpp13
-rw-r--r--src/server/scripts/EasternKingdoms/Scholomance/boss_doctor_theolen_krastinov.cpp9
-rw-r--r--src/server/scripts/EasternKingdoms/Scholomance/boss_illucia_barov.cpp13
-rw-r--r--src/server/scripts/EasternKingdoms/Scholomance/boss_instructor_malicia.cpp10
-rw-r--r--src/server/scripts/EasternKingdoms/Scholomance/boss_kirtonos_the_herald.cpp115
-rw-r--r--src/server/scripts/EasternKingdoms/Scholomance/boss_lord_alexei_barov.cpp10
-rw-r--r--src/server/scripts/EasternKingdoms/Scholomance/boss_lorekeeper_polkelt.cpp9
-rw-r--r--src/server/scripts/EasternKingdoms/Scholomance/boss_the_ravenian.cpp10
-rw-r--r--src/server/scripts/EasternKingdoms/Scholomance/instance_scholomance.cpp311
-rw-r--r--src/server/scripts/EasternKingdoms/Scholomance/scholomance.h12
-rw-r--r--src/server/scripts/EasternKingdoms/SunwellPlateau/boss_eredar_twins.cpp8
-rw-r--r--src/server/scripts/EasternKingdoms/SunwellPlateau/instance_sunwell_plateau.cpp2
-rw-r--r--src/server/scripts/EasternKingdoms/Uldaman/boss_archaedas.cpp32
-rw-r--r--src/server/scripts/EasternKingdoms/Uldaman/instance_uldaman.cpp179
-rw-r--r--src/server/scripts/EasternKingdoms/ZulAman/boss_halazzi.cpp56
-rw-r--r--src/server/scripts/EasternKingdoms/ZulAman/zulaman.cpp16
-rw-r--r--src/server/scripts/EasternKingdoms/ZulGurub/boss_arlokk.cpp528
-rw-r--r--src/server/scripts/EasternKingdoms/ZulGurub/boss_gahzranka.cpp87
-rw-r--r--src/server/scripts/EasternKingdoms/ZulGurub/boss_grilek.cpp81
-rw-r--r--src/server/scripts/EasternKingdoms/ZulGurub/boss_hakkar.cpp292
-rw-r--r--src/server/scripts/EasternKingdoms/ZulGurub/boss_hazzarah.cpp103
-rw-r--r--src/server/scripts/EasternKingdoms/ZulGurub/boss_jeklik.cpp48
-rw-r--r--src/server/scripts/EasternKingdoms/ZulGurub/boss_jindo.cpp215
-rw-r--r--src/server/scripts/EasternKingdoms/ZulGurub/boss_mandokir.cpp551
-rw-r--r--src/server/scripts/EasternKingdoms/ZulGurub/boss_marli.cpp301
-rw-r--r--src/server/scripts/EasternKingdoms/ZulGurub/boss_renataki.cpp18
-rw-r--r--src/server/scripts/EasternKingdoms/ZulGurub/boss_thekal.cpp351
-rw-r--r--src/server/scripts/EasternKingdoms/ZulGurub/boss_venoxis.cpp58
-rw-r--r--src/server/scripts/EasternKingdoms/ZulGurub/boss_wushoolay.cpp66
-rw-r--r--src/server/scripts/EasternKingdoms/ZulGurub/instance_zulgurub.cpp193
-rw-r--r--src/server/scripts/EasternKingdoms/ZulGurub/zulgurub.h66
-rw-r--r--src/server/scripts/EasternKingdoms/boss_kruul.cpp17
-rw-r--r--src/server/scripts/Events/childrens_week.cpp10
-rw-r--r--src/server/scripts/Kalimdor/zone_feralas.cpp3
-rw-r--r--src/server/scripts/Kalimdor/zone_silithus.cpp2
-rw-r--r--src/server/scripts/Kalimdor/zone_the_barrens.cpp2
-rw-r--r--src/server/scripts/Northrend/AzjolNerub/Ahnkahet/boss_amanitar.cpp32
-rw-r--r--src/server/scripts/Northrend/AzjolNerub/Ahnkahet/boss_elder_nadox.cpp22
-rw-r--r--src/server/scripts/Northrend/ChamberOfAspects/RubySanctum/boss_general_zarithrian.cpp17
-rw-r--r--src/server/scripts/Northrend/ChamberOfAspects/RubySanctum/boss_halion.cpp25
-rw-r--r--src/server/scripts/Northrend/ChamberOfAspects/RubySanctum/instance_ruby_sanctum.cpp22
-rw-r--r--src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/boss_lord_jaraxxus.cpp2
-rw-r--r--src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/trial_of_the_crusader.h4
-rw-r--r--src/server/scripts/Northrend/FrozenHalls/ForgeOfSouls/boss_devourer_of_souls.cpp2
-rw-r--r--src/server/scripts/Northrend/Gundrak/boss_slad_ran.cpp2
-rw-r--r--src/server/scripts/Northrend/Naxxramas/boss_grobbulus.cpp36
-rw-r--r--src/server/scripts/Northrend/Naxxramas/boss_kelthuzad.cpp2
-rw-r--r--src/server/scripts/Northrend/Nexus/EyeOfEternity/boss_malygos.cpp4
-rw-r--r--src/server/scripts/Northrend/Ulduar/Ulduar/boss_algalon_the_observer.cpp2
-rw-r--r--src/server/scripts/Northrend/Ulduar/Ulduar/instance_ulduar.cpp2
-rw-r--r--src/server/scripts/Northrend/UtgardeKeep/UtgardeKeep/boss_ingvar_the_plunderer.cpp28
-rw-r--r--src/server/scripts/Northrend/UtgardeKeep/UtgardeKeep/boss_keleseth.cpp16
-rw-r--r--src/server/scripts/Northrend/UtgardeKeep/UtgardePinnacle/boss_svala.cpp6
-rw-r--r--src/server/scripts/Northrend/VioletHold/boss_xevozz.cpp4
-rw-r--r--src/server/scripts/Northrend/zone_icecrown.cpp102
-rw-r--r--src/server/scripts/Northrend/zone_sholazar_basin.cpp101
-rw-r--r--src/server/scripts/Northrend/zone_storm_peaks.cpp2
-rw-r--r--src/server/scripts/Northrend/zone_wintergrasp.cpp2
-rw-r--r--src/server/scripts/Outland/BlackTemple/boss_mother_shahraz.cpp2
-rw-r--r--src/server/scripts/Outland/CoilfangReservoir/underbog/boss_hungarfen.cpp7
-rw-r--r--src/server/scripts/Outland/HellfireCitadel/BloodFurnace/instance_blood_furnace.cpp10
-rw-r--r--src/server/scripts/Outland/TempestKeep/Eye/boss_astromancer.cpp2
-rw-r--r--src/server/scripts/Outland/TempestKeep/Mechanar/boss_gatewatcher_gyrokill.cpp48
-rw-r--r--src/server/scripts/Outland/TempestKeep/Mechanar/boss_gatewatcher_ironhand.cpp146
-rw-r--r--src/server/scripts/Outland/TempestKeep/Mechanar/boss_mechano_lord_capacitus.cpp130
-rw-r--r--src/server/scripts/Outland/TempestKeep/Mechanar/boss_nethermancer_sepethrea.cpp27
-rw-r--r--src/server/scripts/Outland/TempestKeep/Mechanar/boss_pathaleon_the_calculator.cpp80
-rw-r--r--src/server/scripts/Outland/TempestKeep/Mechanar/instance_mechanar.cpp121
-rw-r--r--src/server/scripts/Outland/TempestKeep/Mechanar/mechanar.h10
-rw-r--r--src/server/scripts/Outland/zone_nagrand.cpp4
-rw-r--r--src/server/scripts/Spells/spell_dk.cpp109
-rw-r--r--src/server/scripts/Spells/spell_druid.cpp205
-rw-r--r--src/server/scripts/Spells/spell_generic.cpp157
-rw-r--r--src/server/scripts/Spells/spell_holiday.cpp95
-rw-r--r--src/server/scripts/Spells/spell_hunter.cpp4
-rw-r--r--src/server/scripts/Spells/spell_item.cpp231
-rw-r--r--src/server/scripts/Spells/spell_mage.cpp178
-rw-r--r--src/server/scripts/Spells/spell_paladin.cpp111
-rw-r--r--src/server/scripts/Spells/spell_pet.cpp2
-rw-r--r--src/server/scripts/Spells/spell_priest.cpp148
-rw-r--r--src/server/scripts/Spells/spell_quest.cpp45
-rw-r--r--src/server/scripts/Spells/spell_rogue.cpp56
-rw-r--r--src/server/scripts/Spells/spell_shaman.cpp580
-rw-r--r--src/server/scripts/Spells/spell_warlock.cpp732
-rw-r--r--src/server/scripts/Spells/spell_warrior.cpp141
-rw-r--r--src/server/scripts/World/npcs_special.cpp6
-rw-r--r--src/server/shared/CMakeLists.txt1
-rw-r--r--src/server/shared/Cryptography/WardenKeyGeneration.h2
-rw-r--r--src/server/shared/Database/Implementation/CharacterDatabase.cpp3
-rw-r--r--src/server/shared/Database/Implementation/CharacterDatabase.h3
-rw-r--r--src/server/shared/Debugging/Errors.h2
-rw-r--r--src/server/shared/Dynamic/HashNamespace.h2
-rw-r--r--src/server/shared/Logging/Appender.cpp4
-rw-r--r--src/server/shared/Logging/Appender.h2
-rw-r--r--src/server/shared/Logging/AppenderConsole.cpp2
-rw-r--r--src/server/shared/Logging/AppenderConsole.h2
-rw-r--r--src/server/shared/Logging/AppenderDB.cpp16
-rw-r--r--src/server/shared/Logging/AppenderDB.h11
-rw-r--r--src/server/shared/Logging/AppenderFile.cpp3
-rw-r--r--src/server/shared/Logging/AppenderFile.h2
-rw-r--r--src/server/shared/Logging/Log.cpp35
-rw-r--r--src/server/shared/Logging/Log.h18
-rw-r--r--src/server/shared/Memory.h19
-rw-r--r--src/server/shared/Packets/ByteBuffer.h2
-rw-r--r--src/server/shared/Utilities/Util.h2
-rw-r--r--src/server/worldserver/CMakeLists.txt2
-rw-r--r--src/server/worldserver/Master.cpp2
-rw-r--r--src/server/worldserver/RemoteAccess/RASocket.cpp13
-rw-r--r--src/server/worldserver/worldserver.conf.dist24
-rw-r--r--src/tools/CMakeLists.txt2
-rw-r--r--src/tools/map_extractor/CMakeLists.txt19
-rw-r--r--src/tools/map_extractor/System.cpp48
-rw-r--r--src/tools/map_extractor/adt.cpp21
-rw-r--r--src/tools/map_extractor/loadlib.cpp4
-rw-r--r--src/tools/map_extractor/loadlib/loadlib.h6
-rw-r--r--src/tools/map_extractor/mpq_libmpq.cpp2
-rw-r--r--src/tools/map_extractor/mpq_libmpq04.h3
-rw-r--r--src/tools/map_extractor/wdt.cpp12
-rw-r--r--src/tools/mesh_extractor/ADT.cpp53
-rw-r--r--src/tools/mesh_extractor/ADT.h29
-rw-r--r--src/tools/mesh_extractor/CMakeLists.txt50
-rw-r--r--src/tools/mesh_extractor/Cache.h63
-rw-r--r--src/tools/mesh_extractor/Chunk.cpp31
-rw-r--r--src/tools/mesh_extractor/Chunk.h20
-rw-r--r--src/tools/mesh_extractor/ChunkedData.cpp74
-rw-r--r--src/tools/mesh_extractor/ChunkedData.h21
-rw-r--r--src/tools/mesh_extractor/Constants.h57
-rw-r--r--src/tools/mesh_extractor/ContinentBuilder.cpp144
-rw-r--r--src/tools/mesh_extractor/ContinentBuilder.h30
-rw-r--r--src/tools/mesh_extractor/DBC.cpp70
-rw-r--r--src/tools/mesh_extractor/DBC.h53
-rw-r--r--src/tools/mesh_extractor/DoodadHandler.cpp109
-rw-r--r--src/tools/mesh_extractor/DoodadHandler.h57
-rw-r--r--src/tools/mesh_extractor/Geometry.cpp123
-rw-r--r--src/tools/mesh_extractor/Geometry.h23
-rw-r--r--src/tools/mesh_extractor/LiquidHandler.cpp102
-rw-r--r--src/tools/mesh_extractor/LiquidHandler.h21
-rw-r--r--src/tools/mesh_extractor/MPQ.cpp118
-rw-r--r--src/tools/mesh_extractor/MPQ.h88
-rw-r--r--src/tools/mesh_extractor/MPQManager.cpp108
-rw-r--r--src/tools/mesh_extractor/MPQManager.h36
-rw-r--r--src/tools/mesh_extractor/MapChunk.cpp74
-rw-r--r--src/tools/mesh_extractor/MapChunk.h24
-rw-r--r--src/tools/mesh_extractor/MeshExtractor.cpp428
-rw-r--r--src/tools/mesh_extractor/Model.cpp67
-rw-r--r--src/tools/mesh_extractor/Model.h23
-rw-r--r--src/tools/mesh_extractor/ObjectDataHandler.cpp21
-rw-r--r--src/tools/mesh_extractor/ObjectDataHandler.h15
-rw-r--r--src/tools/mesh_extractor/TileBuilder.cpp311
-rw-r--r--src/tools/mesh_extractor/TileBuilder.h30
-rw-r--r--src/tools/mesh_extractor/Utils.cpp568
-rw-r--r--src/tools/mesh_extractor/Utils.h381
-rw-r--r--src/tools/mesh_extractor/WDT.cpp60
-rw-r--r--src/tools/mesh_extractor/WDT.h27
-rw-r--r--src/tools/mesh_extractor/WorldModelGroup.cpp143
-rw-r--r--src/tools/mesh_extractor/WorldModelGroup.h38
-rw-r--r--src/tools/mesh_extractor/WorldModelHandler.cpp204
-rw-r--r--src/tools/mesh_extractor/WorldModelHandler.h47
-rw-r--r--src/tools/mesh_extractor/WorldModelRoot.cpp74
-rw-r--r--src/tools/mesh_extractor/WorldModelRoot.h26
-rw-r--r--src/tools/mesh_extractor/readme6
-rw-r--r--src/tools/mmaps_generator/CMakeLists.txt55
-rw-r--r--src/tools/mmaps_generator/Info/readme.txt66
-rw-r--r--src/tools/mmaps_generator/IntermediateValues.cpp277
-rw-r--r--src/tools/mmaps_generator/IntermediateValues.h53
-rw-r--r--src/tools/mmaps_generator/MapBuilder.cpp982
-rw-r--r--src/tools/mmaps_generator/MapBuilder.h150
-rw-r--r--src/tools/mmaps_generator/PathCommon.h161
-rw-r--r--src/tools/mmaps_generator/PathGenerator.cpp296
-rw-r--r--src/tools/mmaps_generator/TerrainBuilder.cpp933
-rw-r--r--src/tools/mmaps_generator/TerrainBuilder.h132
-rw-r--r--src/tools/mmaps_generator/VMapExtensions.cpp70
-rw-r--r--src/tools/vmap4_assembler/CMakeLists.txt2
-rw-r--r--src/tools/vmap4_extractor/CMakeLists.txt25
-rw-r--r--src/tools/vmap4_extractor/adtfile.cpp3
-rw-r--r--src/tools/vmap4_extractor/model.cpp4
-rw-r--r--src/tools/vmap4_extractor/mpq_libmpq.cpp2
-rw-r--r--src/tools/vmap4_extractor/mpq_libmpq04.h3
-rw-r--r--src/tools/vmap4_extractor/vmapexport.cpp3
-rw-r--r--src/tools/vmap4_extractor/wdtfile.cpp2
-rw-r--r--src/tools/vmap4_extractor/wmo.cpp4
749 files changed, 31551 insertions, 5586 deletions
diff --git a/COPYING b/COPYING
index cdfdede6e8c..d159169d105 100644
--- a/COPYING
+++ b/COPYING
@@ -1,12 +1,12 @@
- GNU GENERAL PUBLIC LICENSE
- Version 2, June 1991
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
- Copyright (C) 1989, 1991 Free Software Foundation, Inc.
- 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
- Preamble
+ Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
@@ -15,7 +15,7 @@ software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
-the GNU Library General Public License instead.) You can apply it to
+the GNU Lesser General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
@@ -55,8 +55,8 @@ patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
-
- GNU GENERAL PUBLIC LICENSE
+
+ GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
@@ -110,7 +110,7 @@ above, provided that you also meet all of these conditions:
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
-
+
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
@@ -168,7 +168,7 @@ access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
-
+
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
@@ -225,7 +225,7 @@ impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
-
+
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
@@ -255,7 +255,7 @@ make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
- NO WARRANTY
+ NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
@@ -277,9 +277,9 @@ YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
- END OF TERMS AND CONDITIONS
-
- How to Apply These Terms to Your New Programs
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
@@ -303,16 +303,16 @@ the "copyright" line and a pointer to where the full notice is found.
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, write to the Free Software
- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
- Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision version 69, Copyright (C) year name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
@@ -335,5 +335,5 @@ necessary. Here is a sample; alter the names:
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
-library. If this is what you want to do, use the GNU Library General
+library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License.
diff --git a/cmake/options.cmake b/cmake/options.cmake
index 5fefbe34350..f58c88ba1d5 100644
--- a/cmake/options.cmake
+++ b/cmake/options.cmake
@@ -10,7 +10,7 @@
option(SERVERS "Build worldserver and authserver" 1)
option(SCRIPTS "Build core with scripts included" 1)
-option(TOOLS "Build map/vmap extraction/assembler tools" 0)
+option(TOOLS "Build map/vmap/mmap extraction/assembler tools" 0)
option(USE_SCRIPTPCH "Use precompiled headers when compiling scripts" 1)
option(USE_COREPCH "Use precompiled headers when compiling servers" 1)
option(WITH_WARNINGS "Show all warnings during compile" 0)
diff --git a/cmake/showoptions.cmake b/cmake/showoptions.cmake
index 47ad7b0889b..d3415e4e204 100644
--- a/cmake/showoptions.cmake
+++ b/cmake/showoptions.cmake
@@ -34,6 +34,7 @@ endif()
if( TOOLS )
message("* Build map/vmap tools : Yes")
+ add_definitions(-DNO_CORE_FUNCS)
else()
message("* Build map/vmap tools : No (default)")
endif()
diff --git a/dep/CMakeLists.txt b/dep/CMakeLists.txt
index 6646ca2c670..ad5e04cb321 100644
--- a/dep/CMakeLists.txt
+++ b/dep/CMakeLists.txt
@@ -21,11 +21,9 @@ if(CMAKE_SYSTEM_NAME MATCHES "Linux")
endif()
if(CMAKE_SYSTEM_NAME MATCHES "Windows")
- if(SERVERS)
- add_subdirectory(acelite)
- if(USE_MYSQL_SOURCES)
+ add_subdirectory(acelite)
+ if(USE_MYSQL_SOURCES)
add_subdirectory(mysqllite)
- endif()
endif()
if(TOOLS)
add_subdirectory(bzip2)
@@ -34,6 +32,7 @@ if(CMAKE_SYSTEM_NAME MATCHES "Windows")
endif()
add_subdirectory(g3dlite)
+add_subdirectory(recastnavigation)
if(SERVERS)
add_subdirectory(gsoap)
diff --git a/dep/PackageList.txt b/dep/PackageList.txt
index de08e0a461e..95503cee302 100644
--- a/dep/PackageList.txt
+++ b/dep/PackageList.txt
@@ -39,3 +39,7 @@ zlib (A Massively Spiffy Yet Delicately Unobtrusive Compression Library)
gSOAP (a portable development toolkit for C and C++ XML Web services and XML data bindings)
http://gsoap2.sourceforge.net/
Version: 2.8.10
+
+recastnavigation (Recast is state of the art navigation mesh construction toolset for games)
+ http://code.google.com/p/recastnavigation/
+ Version: 1.4
diff --git a/dep/recastnavigation/CMakeLists.txt b/dep/recastnavigation/CMakeLists.txt
new file mode 100644
index 00000000000..4a406f7833c
--- /dev/null
+++ b/dep/recastnavigation/CMakeLists.txt
@@ -0,0 +1,20 @@
+#
+# Copyright (C) 2005-2011 MaNGOS project <http://getmangos.com/>
+#
+# 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, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+#
+
+add_subdirectory(Detour)
+add_subdirectory(Recast)
diff --git a/dep/recastnavigation/Detour/CMakeLists.txt b/dep/recastnavigation/Detour/CMakeLists.txt
new file mode 100644
index 00000000000..303a003dee2
--- /dev/null
+++ b/dep/recastnavigation/Detour/CMakeLists.txt
@@ -0,0 +1,28 @@
+# Copyright (C) 2008-2013 TrinityCore <http://www.trinitycore.org/>
+#
+# This file is free software; as a special exception the author gives
+# unlimited permission to copy and/or distribute it, with or without
+# modifications, as long as this notice is preserved.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY, to the extent permitted by law; without even the
+# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+set(Detour_STAT_SRCS
+ DetourAlloc.cpp
+ DetourCommon.cpp
+ DetourNavMesh.cpp
+ DetourNavMeshBuilder.cpp
+ DetourNavMeshQuery.cpp
+ DetourNode.cpp
+)
+
+if(WIN32)
+ include_directories(
+ ${CMAKE_SOURCE_DIR}/dep/zlib
+ )
+endif()
+
+add_library(Detour STATIC ${Detour_STAT_SRCS})
+
+target_link_libraries(Detour ${ZLIB_LIBRARIES})
diff --git a/dep/recastnavigation/Detour/DetourAlloc.cpp b/dep/recastnavigation/Detour/DetourAlloc.cpp
new file mode 100644
index 00000000000..5f671df5bdb
--- /dev/null
+++ b/dep/recastnavigation/Detour/DetourAlloc.cpp
@@ -0,0 +1,50 @@
+//
+// Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
+//
+// This software is provided 'as-is', without any express or implied
+// warranty. In no event will the authors be held liable for any damages
+// arising from the use of this software.
+// Permission is granted to anyone to use this software for any purpose,
+// including commercial applications, and to alter it and redistribute it
+// freely, subject to the following restrictions:
+// 1. The origin of this software must not be misrepresented; you must not
+// claim that you wrote the original software. If you use this software
+// in a product, an acknowledgment in the product documentation would be
+// appreciated but is not required.
+// 2. Altered source versions must be plainly marked as such, and must not be
+// misrepresented as being the original software.
+// 3. This notice may not be removed or altered from any source distribution.
+//
+
+#include <stdlib.h>
+#include "DetourAlloc.h"
+
+static void *dtAllocDefault(int size, dtAllocHint)
+{
+ return malloc(size);
+}
+
+static void dtFreeDefault(void *ptr)
+{
+ free(ptr);
+}
+
+static dtAllocFunc* sAllocFunc = dtAllocDefault;
+static dtFreeFunc* sFreeFunc = dtFreeDefault;
+
+void dtAllocSetCustom(dtAllocFunc *allocFunc, dtFreeFunc *freeFunc)
+{
+ sAllocFunc = allocFunc ? allocFunc : dtAllocDefault;
+ sFreeFunc = freeFunc ? freeFunc : dtFreeDefault;
+}
+
+void* dtAlloc(int size, dtAllocHint hint)
+{
+ return sAllocFunc(size, hint);
+}
+
+void dtFree(void* ptr)
+{
+ if (ptr)
+ sFreeFunc(ptr);
+}
diff --git a/dep/recastnavigation/Detour/DetourAlloc.h b/dep/recastnavigation/Detour/DetourAlloc.h
new file mode 100644
index 00000000000..8693475419e
--- /dev/null
+++ b/dep/recastnavigation/Detour/DetourAlloc.h
@@ -0,0 +1,36 @@
+//
+// Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
+//
+// This software is provided 'as-is', without any express or implied
+// warranty. In no event will the authors be held liable for any damages
+// arising from the use of this software.
+// Permission is granted to anyone to use this software for any purpose,
+// including commercial applications, and to alter it and redistribute it
+// freely, subject to the following restrictions:
+// 1. The origin of this software must not be misrepresented; you must not
+// claim that you wrote the original software. If you use this software
+// in a product, an acknowledgment in the product documentation would be
+// appreciated but is not required.
+// 2. Altered source versions must be plainly marked as such, and must not be
+// misrepresented as being the original software.
+// 3. This notice may not be removed or altered from any source distribution.
+//
+
+#ifndef DETOURALLOCATOR_H
+#define DETOURALLOCATOR_H
+
+enum dtAllocHint
+{
+ DT_ALLOC_PERM, // Memory persist after a function call.
+ DT_ALLOC_TEMP // Memory used temporarily within a function.
+};
+
+typedef void* (dtAllocFunc)(int size, dtAllocHint hint);
+typedef void (dtFreeFunc)(void* ptr);
+
+void dtAllocSetCustom(dtAllocFunc *allocFunc, dtFreeFunc *freeFunc);
+
+void* dtAlloc(int size, dtAllocHint hint);
+void dtFree(void* ptr);
+
+#endif
diff --git a/dep/recastnavigation/Detour/DetourAssert.h b/dep/recastnavigation/Detour/DetourAssert.h
new file mode 100644
index 00000000000..709ebd9f5f8
--- /dev/null
+++ b/dep/recastnavigation/Detour/DetourAssert.h
@@ -0,0 +1,33 @@
+//
+// Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
+//
+// This software is provided 'as-is', without any express or implied
+// warranty. In no event will the authors be held liable for any damages
+// arising from the use of this software.
+// Permission is granted to anyone to use this software for any purpose,
+// including commercial applications, and to alter it and redistribute it
+// freely, subject to the following restrictions:
+// 1. The origin of this software must not be misrepresented; you must not
+// claim that you wrote the original software. If you use this software
+// in a product, an acknowledgment in the product documentation would be
+// appreciated but is not required.
+// 2. Altered source versions must be plainly marked as such, and must not be
+// misrepresented as being the original software.
+// 3. This notice may not be removed or altered from any source distribution.
+//
+
+#ifndef DETOURASSERT_H
+#define DETOURASSERT_H
+
+// Note: This header file's only purpose is to include define assert.
+// Feel free to change the file and include your own implementation instead.
+
+#ifdef NDEBUG
+// From http://cnicholson.net/2009/02/stupid-c-tricks-adventures-in-assert/
+# define dtAssert(x) do { (void)sizeof(x); } while(__LINE__==-1,false)
+#else
+# include <assert.h>
+# define dtAssert assert
+#endif
+
+#endif // DETOURASSERT_H
diff --git a/dep/recastnavigation/Detour/DetourCommon.cpp b/dep/recastnavigation/Detour/DetourCommon.cpp
new file mode 100644
index 00000000000..c0b973e4a75
--- /dev/null
+++ b/dep/recastnavigation/Detour/DetourCommon.cpp
@@ -0,0 +1,329 @@
+//
+// Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
+//
+// This software is provided 'as-is', without any express or implied
+// warranty. In no event will the authors be held liable for any damages
+// arising from the use of this software.
+// Permission is granted to anyone to use this software for any purpose,
+// including commercial applications, and to alter it and redistribute it
+// freely, subject to the following restrictions:
+// 1. The origin of this software must not be misrepresented; you must not
+// claim that you wrote the original software. If you use this software
+// in a product, an acknowledgment in the product documentation would be
+// appreciated but is not required.
+// 2. Altered source versions must be plainly marked as such, and must not be
+// misrepresented as being the original software.
+// 3. This notice may not be removed or altered from any source distribution.
+//
+
+#include <math.h>
+#include "DetourCommon.h"
+
+//////////////////////////////////////////////////////////////////////////////////////////
+
+float dtSqrt(float x)
+{
+ return sqrtf(x);
+}
+
+void dtClosestPtPointTriangle(float* closest, const float* p,
+ const float* a, const float* b, const float* c)
+{
+ // Check if P in vertex region outside A
+ float ab[3], ac[3], ap[3];
+ dtVsub(ab, b, a);
+ dtVsub(ac, c, a);
+ dtVsub(ap, p, a);
+ float d1 = dtVdot(ab, ap);
+ float d2 = dtVdot(ac, ap);
+ if (d1 <= 0.0f && d2 <= 0.0f)
+ {
+ // barycentric coordinates (1,0,0)
+ dtVcopy(closest, a);
+ return;
+ }
+
+ // Check if P in vertex region outside B
+ float bp[3];
+ dtVsub(bp, p, b);
+ float d3 = dtVdot(ab, bp);
+ float d4 = dtVdot(ac, bp);
+ if (d3 >= 0.0f && d4 <= d3)
+ {
+ // barycentric coordinates (0,1,0)
+ dtVcopy(closest, b);
+ return;
+ }
+
+ // Check if P in edge region of AB, if so return projection of P onto AB
+ float vc = d1*d4 - d3*d2;
+ if (vc <= 0.0f && d1 >= 0.0f && d3 <= 0.0f)
+ {
+ // barycentric coordinates (1-v,v,0)
+ float v = d1 / (d1 - d3);
+ closest[0] = a[0] + v * ab[0];
+ closest[1] = a[1] + v * ab[1];
+ closest[2] = a[2] + v * ab[2];
+ return;
+ }
+
+ // Check if P in vertex region outside C
+ float cp[3];
+ dtVsub(cp, p, c);
+ float d5 = dtVdot(ab, cp);
+ float d6 = dtVdot(ac, cp);
+ if (d6 >= 0.0f && d5 <= d6)
+ {
+ // barycentric coordinates (0,0,1)
+ dtVcopy(closest, c);
+ return;
+ }
+
+ // Check if P in edge region of AC, if so return projection of P onto AC
+ float vb = d5*d2 - d1*d6;
+ if (vb <= 0.0f && d2 >= 0.0f && d6 <= 0.0f)
+ {
+ // barycentric coordinates (1-w,0,w)
+ float w = d2 / (d2 - d6);
+ closest[0] = a[0] + w * ac[0];
+ closest[1] = a[1] + w * ac[1];
+ closest[2] = a[2] + w * ac[2];
+ return;
+ }
+
+ // Check if P in edge region of BC, if so return projection of P onto BC
+ float va = d3*d6 - d5*d4;
+ if (va <= 0.0f && (d4 - d3) >= 0.0f && (d5 - d6) >= 0.0f)
+ {
+ // barycentric coordinates (0,1-w,w)
+ float w = (d4 - d3) / ((d4 - d3) + (d5 - d6));
+ closest[0] = b[0] + w * (c[0] - b[0]);
+ closest[1] = b[1] + w * (c[1] - b[1]);
+ closest[2] = b[2] + w * (c[2] - b[2]);
+ return;
+ }
+
+ // P inside face region. Compute Q through its barycentric coordinates (u,v,w)
+ float denom = 1.0f / (va + vb + vc);
+ float v = vb * denom;
+ float w = vc * denom;
+ closest[0] = a[0] + ab[0] * v + ac[0] * w;
+ closest[1] = a[1] + ab[1] * v + ac[1] * w;
+ closest[2] = a[2] + ab[2] * v + ac[2] * w;
+}
+
+bool dtIntersectSegmentPoly2D(const float* p0, const float* p1,
+ const float* verts, int nverts,
+ float& tmin, float& tmax,
+ int& segMin, int& segMax)
+{
+ static const float EPS = 0.00000001f;
+
+ tmin = 0;
+ tmax = 1;
+ segMin = -1;
+ segMax = -1;
+
+ float dir[3];
+ dtVsub(dir, p1, p0);
+
+ for (int i = 0, j = nverts-1; i < nverts; j=i++)
+ {
+ float edge[3], diff[3];
+ dtVsub(edge, &verts[i*3], &verts[j*3]);
+ dtVsub(diff, p0, &verts[j*3]);
+ const float n = dtVperp2D(edge, diff);
+ const float d = dtVperp2D(dir, edge);
+ if (fabsf(d) < EPS)
+ {
+ // S is nearly parallel to this edge
+ if (n < 0)
+ return false;
+ else
+ continue;
+ }
+ const float t = n / d;
+ if (d < 0)
+ {
+ // segment S is entering across this edge
+ if (t > tmin)
+ {
+ tmin = t;
+ segMin = j;
+ // S enters after leaving polygon
+ if (tmin > tmax)
+ return false;
+ }
+ }
+ else
+ {
+ // segment S is leaving across this edge
+ if (t < tmax)
+ {
+ tmax = t;
+ segMax = j;
+ // S leaves before entering polygon
+ if (tmax < tmin)
+ return false;
+ }
+ }
+ }
+
+ return true;
+}
+
+float dtDistancePtSegSqr2D(const float* pt, const float* p, const float* q, float& t)
+{
+ float pqx = q[0] - p[0];
+ float pqz = q[2] - p[2];
+ float dx = pt[0] - p[0];
+ float dz = pt[2] - p[2];
+ float d = pqx*pqx + pqz*pqz;
+ t = pqx*dx + pqz*dz;
+ if (d > 0) t /= d;
+ if (t < 0) t = 0;
+ else if (t > 1) t = 1;
+ dx = p[0] + t*pqx - pt[0];
+ dz = p[2] + t*pqz - pt[2];
+ return dx*dx + dz*dz;
+}
+
+void dtCalcPolyCenter(float* tc, const unsigned short* idx, int nidx, const float* verts)
+{
+ tc[0] = 0.0f;
+ tc[1] = 0.0f;
+ tc[2] = 0.0f;
+ for (int j = 0; j < nidx; ++j)
+ {
+ const float* v = &verts[idx[j]*3];
+ tc[0] += v[0];
+ tc[1] += v[1];
+ tc[2] += v[2];
+ }
+ const float s = 1.0f / nidx;
+ tc[0] *= s;
+ tc[1] *= s;
+ tc[2] *= s;
+}
+
+bool dtClosestHeightPointTriangle(const float* p, const float* a, const float* b, const float* c, float& h)
+{
+ float v0[3], v1[3], v2[3];
+ dtVsub(v0, c,a);
+ dtVsub(v1, b,a);
+ dtVsub(v2, p,a);
+
+ const float dot00 = dtVdot2D(v0, v0);
+ const float dot01 = dtVdot2D(v0, v1);
+ const float dot02 = dtVdot2D(v0, v2);
+ const float dot11 = dtVdot2D(v1, v1);
+ const float dot12 = dtVdot2D(v1, v2);
+
+ // Compute barycentric coordinates
+ const float invDenom = 1.0f / (dot00 * dot11 - dot01 * dot01);
+ const float u = (dot11 * dot02 - dot01 * dot12) * invDenom;
+ const float v = (dot00 * dot12 - dot01 * dot02) * invDenom;
+
+ // The (sloppy) epsilon is needed to allow to get height of points which
+ // are interpolated along the edges of the triangles.
+ static const float EPS = 1e-4f;
+
+ // If point lies inside the triangle, return interpolated ycoord.
+ if (u >= -EPS && v >= -EPS && (u+v) <= 1+EPS)
+ {
+ h = a[1] + v0[1]*u + v1[1]*v;
+ return true;
+ }
+
+ return false;
+}
+
+bool dtPointInPolygon(const float* pt, const float* verts, const int nverts)
+{
+ // TODO: Replace pnpoly with triArea2D tests?
+ int i, j;
+ bool c = false;
+ for (i = 0, j = nverts-1; i < nverts; j = i++)
+ {
+ const float* vi = &verts[i*3];
+ const float* vj = &verts[j*3];
+ if (((vi[2] > pt[2]) != (vj[2] > pt[2])) &&
+ (pt[0] < (vj[0]-vi[0]) * (pt[2]-vi[2]) / (vj[2]-vi[2]) + vi[0]) )
+ c = !c;
+ }
+ return c;
+}
+
+bool dtDistancePtPolyEdgesSqr(const float* pt, const float* verts, const int nverts,
+ float* ed, float* et)
+{
+ // TODO: Replace pnpoly with triArea2D tests?
+ int i, j;
+ bool c = false;
+ for (i = 0, j = nverts-1; i < nverts; j = i++)
+ {
+ const float* vi = &verts[i*3];
+ const float* vj = &verts[j*3];
+ if (((vi[2] > pt[2]) != (vj[2] > pt[2])) &&
+ (pt[0] < (vj[0]-vi[0]) * (pt[2]-vi[2]) / (vj[2]-vi[2]) + vi[0]) )
+ c = !c;
+ ed[j] = dtDistancePtSegSqr2D(pt, vj, vi, et[j]);
+ }
+ return c;
+}
+
+static void projectPoly(const float* axis, const float* poly, const int npoly,
+ float& rmin, float& rmax)
+{
+ rmin = rmax = dtVdot2D(axis, &poly[0]);
+ for (int i = 1; i < npoly; ++i)
+ {
+ const float d = dtVdot2D(axis, &poly[i*3]);
+ rmin = dtMin(rmin, d);
+ rmax = dtMax(rmax, d);
+ }
+}
+
+inline bool overlapRange(const float amin, const float amax,
+ const float bmin, const float bmax,
+ const float eps)
+{
+ return ((amin+eps) > bmax || (amax-eps) < bmin) ? false : true;
+}
+
+bool dtOverlapPolyPoly2D(const float* polya, const int npolya,
+ const float* polyb, const int npolyb)
+{
+ const float eps = 1e-4f;
+
+ for (int i = 0, j = npolya-1; i < npolya; j=i++)
+ {
+ const float* va = &polya[j*3];
+ const float* vb = &polya[i*3];
+ const float n[3] = { vb[2]-va[2], 0, -(vb[0]-va[0]) };
+ float amin,amax,bmin,bmax;
+ projectPoly(n, polya, npolya, amin,amax);
+ projectPoly(n, polyb, npolyb, bmin,bmax);
+ if (!overlapRange(amin,amax, bmin,bmax, eps))
+ {
+ // Found separating axis
+ return false;
+ }
+ }
+ for (int i = 0, j = npolyb-1; i < npolyb; j=i++)
+ {
+ const float* va = &polyb[j*3];
+ const float* vb = &polyb[i*3];
+ const float n[3] = { vb[2]-va[2], 0, -(vb[0]-va[0]) };
+ float amin,amax,bmin,bmax;
+ projectPoly(n, polya, npolya, amin,amax);
+ projectPoly(n, polyb, npolyb, bmin,bmax);
+ if (!overlapRange(amin,amax, bmin,bmax, eps))
+ {
+ // Found separating axis
+ return false;
+ }
+ }
+ return true;
+}
+
diff --git a/dep/recastnavigation/Detour/DetourCommon.h b/dep/recastnavigation/Detour/DetourCommon.h
new file mode 100644
index 00000000000..3cee3f63510
--- /dev/null
+++ b/dep/recastnavigation/Detour/DetourCommon.h
@@ -0,0 +1,248 @@
+//
+// Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
+//
+// This software is provided 'as-is', without any express or implied
+// warranty. In no event will the authors be held liable for any damages
+// arising from the use of this software.
+// Permission is granted to anyone to use this software for any purpose,
+// including commercial applications, and to alter it and redistribute it
+// freely, subject to the following restrictions:
+// 1. The origin of this software must not be misrepresented; you must not
+// claim that you wrote the original software. If you use this software
+// in a product, an acknowledgment in the product documentation would be
+// appreciated but is not required.
+// 2. Altered source versions must be plainly marked as such, and must not be
+// misrepresented as being the original software.
+// 3. This notice may not be removed or altered from any source distribution.
+//
+
+#ifndef DETOURCOMMON_H
+#define DETOURCOMMON_H
+
+template<class T> inline void dtSwap(T& a, T& b) { T t = a; a = b; b = t; }
+template<class T> inline T dtMin(T a, T b) { return a < b ? a : b; }
+template<class T> inline T dtMax(T a, T b) { return a > b ? a : b; }
+template<class T> inline T dtAbs(T a) { return a < 0 ? -a : a; }
+template<class T> inline T dtSqr(T a) { return a*a; }
+template<class T> inline T dtClamp(T v, T mn, T mx) { return v < mn ? mn : (v > mx ? mx : v); }
+
+float dtSqrt(float x);
+
+inline void dtVcross(float* dest, const float* v1, const float* v2)
+{
+ dest[0] = v1[1]*v2[2] - v1[2]*v2[1];
+ dest[1] = v1[2]*v2[0] - v1[0]*v2[2];
+ dest[2] = v1[0]*v2[1] - v1[1]*v2[0];
+}
+
+inline float dtVdot(const float* v1, const float* v2)
+{
+ return v1[0]*v2[0] + v1[1]*v2[1] + v1[2]*v2[2];
+}
+
+inline void dtVmad(float* dest, const float* v1, const float* v2, const float s)
+{
+ dest[0] = v1[0]+v2[0]*s;
+ dest[1] = v1[1]+v2[1]*s;
+ dest[2] = v1[2]+v2[2]*s;
+}
+
+inline void dtVlerp(float* dest, const float* v1, const float* v2, const float t)
+{
+ dest[0] = v1[0]+(v2[0]-v1[0])*t;
+ dest[1] = v1[1]+(v2[1]-v1[1])*t;
+ dest[2] = v1[2]+(v2[2]-v1[2])*t;
+}
+
+inline void dtVadd(float* dest, const float* v1, const float* v2)
+{
+ dest[0] = v1[0]+v2[0];
+ dest[1] = v1[1]+v2[1];
+ dest[2] = v1[2]+v2[2];
+}
+
+inline void dtVsub(float* dest, const float* v1, const float* v2)
+{
+ dest[0] = v1[0]-v2[0];
+ dest[1] = v1[1]-v2[1];
+ dest[2] = v1[2]-v2[2];
+}
+
+inline void dtVscale(float* dest, const float* v, const float t)
+{
+ dest[0] = v[0]*t;
+ dest[1] = v[1]*t;
+ dest[2] = v[2]*t;
+}
+
+inline void dtVmin(float* mn, const float* v)
+{
+ mn[0] = dtMin(mn[0], v[0]);
+ mn[1] = dtMin(mn[1], v[1]);
+ mn[2] = dtMin(mn[2], v[2]);
+}
+
+inline void dtVmax(float* mx, const float* v)
+{
+ mx[0] = dtMax(mx[0], v[0]);
+ mx[1] = dtMax(mx[1], v[1]);
+ mx[2] = dtMax(mx[2], v[2]);
+}
+
+inline void dtVset(float* dest, const float x, const float y, const float z)
+{
+ dest[0] = x; dest[1] = y; dest[2] = z;
+}
+
+inline void dtVcopy(float* dest, const float* a)
+{
+ dest[0] = a[0];
+ dest[1] = a[1];
+ dest[2] = a[2];
+}
+
+inline float dtVlen(const float* v)
+{
+ return dtSqrt(v[0]*v[0] + v[1]*v[1] + v[2]*v[2]);
+}
+
+inline float dtVlenSqr(const float* v)
+{
+ return v[0]*v[0] + v[1]*v[1] + v[2]*v[2];
+}
+
+inline float dtVdist(const float* v1, const float* v2)
+{
+ const float dx = v2[0] - v1[0];
+ const float dy = v2[1] - v1[1];
+ const float dz = v2[2] - v1[2];
+ return dtSqrt(dx*dx + dy*dy + dz*dz);
+}
+
+inline float dtVdistSqr(const float* v1, const float* v2)
+{
+ const float dx = v2[0] - v1[0];
+ const float dy = v2[1] - v1[1];
+ const float dz = v2[2] - v1[2];
+ return dx*dx + dy*dy + dz*dz;
+}
+
+inline float dtVdist2D(const float* v1, const float* v2)
+{
+ const float dx = v2[0] - v1[0];
+ const float dz = v2[2] - v1[2];
+ return dtSqrt(dx*dx + dz*dz);
+}
+
+inline float dtVdist2DSqr(const float* v1, const float* v2)
+{
+ const float dx = v2[0] - v1[0];
+ const float dz = v2[2] - v1[2];
+ return dx*dx + dz*dz;
+}
+
+inline void dtVnormalize(float* v)
+{
+ float d = 1.0f / dtSqrt(dtSqr(v[0]) + dtSqr(v[1]) + dtSqr(v[2]));
+ v[0] *= d;
+ v[1] *= d;
+ v[2] *= d;
+}
+
+inline bool dtVequal(const float* p0, const float* p1)
+{
+ static const float thr = dtSqr(1.0f/16384.0f);
+ const float d = dtVdistSqr(p0, p1);
+ return d < thr;
+}
+
+inline unsigned int dtNextPow2(unsigned int v)
+{
+ v--;
+ v |= v >> 1;
+ v |= v >> 2;
+ v |= v >> 4;
+ v |= v >> 8;
+ v |= v >> 16;
+ v++;
+ return v;
+}
+
+inline unsigned int dtIlog2(unsigned int v)
+{
+ unsigned int r;
+ unsigned int shift;
+ r = (v > 0xffff) << 4; v >>= r;
+ shift = (v > 0xff) << 3; v >>= shift; r |= shift;
+ shift = (v > 0xf) << 2; v >>= shift; r |= shift;
+ shift = (v > 0x3) << 1; v >>= shift; r |= shift;
+ r |= (v >> 1);
+ return r;
+}
+
+inline int dtAlign4(int x) { return (x+3) & ~3; }
+
+inline int dtOppositeTile(int side) { return (side+4) & 0x7; }
+
+inline float dtVdot2D(const float* u, const float* v)
+{
+ return u[0]*v[0] + u[2]*v[2];
+}
+
+inline float dtVperp2D(const float* u, const float* v)
+{
+ return u[2]*v[0] - u[0]*v[2];
+}
+
+inline float dtTriArea2D(const float* a, const float* b, const float* c)
+{
+ const float abx = b[0] - a[0];
+ const float abz = b[2] - a[2];
+ const float acx = c[0] - a[0];
+ const float acz = c[2] - a[2];
+ return acx*abz - abx*acz;
+}
+
+inline bool dtOverlapQuantBounds(const unsigned short amin[3], const unsigned short amax[3],
+ const unsigned short bmin[3], const unsigned short bmax[3])
+{
+ bool overlap = true;
+ overlap = (amin[0] > bmax[0] || amax[0] < bmin[0]) ? false : overlap;
+ overlap = (amin[1] > bmax[1] || amax[1] < bmin[1]) ? false : overlap;
+ overlap = (amin[2] > bmax[2] || amax[2] < bmin[2]) ? false : overlap;
+ return overlap;
+}
+
+inline bool dtOverlapBounds(const float* amin, const float* amax,
+ const float* bmin, const float* bmax)
+{
+ bool overlap = true;
+ overlap = (amin[0] > bmax[0] || amax[0] < bmin[0]) ? false : overlap;
+ overlap = (amin[1] > bmax[1] || amax[1] < bmin[1]) ? false : overlap;
+ overlap = (amin[2] > bmax[2] || amax[2] < bmin[2]) ? false : overlap;
+ return overlap;
+}
+
+void dtClosestPtPointTriangle(float* closest, const float* p,
+ const float* a, const float* b, const float* c);
+
+bool dtClosestHeightPointTriangle(const float* p, const float* a, const float* b, const float* c, float& h);
+
+bool dtIntersectSegmentPoly2D(const float* p0, const float* p1,
+ const float* verts, int nverts,
+ float& tmin, float& tmax,
+ int& segMin, int& segMax);
+
+bool dtPointInPolygon(const float* pt, const float* verts, const int nverts);
+
+bool dtDistancePtPolyEdgesSqr(const float* pt, const float* verts, const int nverts,
+ float* ed, float* et);
+
+float dtDistancePtSegSqr2D(const float* pt, const float* p, const float* q, float& t);
+
+void dtCalcPolyCenter(float* tc, const unsigned short* idx, int nidx, const float* verts);
+
+bool dtOverlapPolyPoly2D(const float* polya, const int npolya,
+ const float* polyb, const int npolyb);
+
+#endif // DETOURCOMMON_H
diff --git a/dep/recastnavigation/Detour/DetourNavMesh.cpp b/dep/recastnavigation/Detour/DetourNavMesh.cpp
new file mode 100644
index 00000000000..e139e3f5c63
--- /dev/null
+++ b/dep/recastnavigation/Detour/DetourNavMesh.cpp
@@ -0,0 +1,1235 @@
+//
+// Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
+//
+// This software is provided 'as-is', without any express or implied
+// warranty. In no event will the authors be held liable for any damages
+// arising from the use of this software.
+// Permission is granted to anyone to use this software for any purpose,
+// including commercial applications, and to alter it and redistribute it
+// freely, subject to the following restrictions:
+// 1. The origin of this software must not be misrepresented; you must not
+// claim that you wrote the original software. If you use this software
+// in a product, an acknowledgment in the product documentation would be
+// appreciated but is not required.
+// 2. Altered source versions must be plainly marked as such, and must not be
+// misrepresented as being the original software.
+// 3. This notice may not be removed or altered from any source distribution.
+//
+
+#include <math.h>
+#include <float.h>
+#include <string.h>
+#include <stdio.h>
+#include "DetourNavMesh.h"
+#include "DetourNode.h"
+#include "DetourCommon.h"
+#include "DetourAlloc.h"
+#include "DetourAssert.h"
+#include <new>
+
+
+inline bool overlapSlabs(const float* amin, const float* amax,
+ const float* bmin, const float* bmax,
+ const float px, const float py)
+{
+ // Check for horizontal overlap.
+ // The segment is shrunken a little so that slabs which touch
+ // at end points are not connected.
+ const float minx = dtMax(amin[0]+px,bmin[0]+px);
+ const float maxx = dtMin(amax[0]-px,bmax[0]-px);
+ if (minx > maxx)
+ return false;
+
+ // Check vertical overlap.
+ const float ad = (amax[1]-amin[1]) / (amax[0]-amin[0]);
+ const float ak = amin[1] - ad*amin[0];
+ const float bd = (bmax[1]-bmin[1]) / (bmax[0]-bmin[0]);
+ const float bk = bmin[1] - bd*bmin[0];
+ const float aminy = ad*minx + ak;
+ const float amaxy = ad*maxx + ak;
+ const float bminy = bd*minx + bk;
+ const float bmaxy = bd*maxx + bk;
+ const float dmin = bminy - aminy;
+ const float dmax = bmaxy - amaxy;
+
+ // Crossing segments always overlap.
+ if (dmin*dmax < 0)
+ return true;
+
+ // Check for overlap at endpoints.
+ const float thr = dtSqr(py*2);
+ if (dmin*dmin <= thr || dmax*dmax <= thr)
+ return true;
+
+ return false;
+}
+
+static void calcSlabEndPoints(const float* va, const float* vb, float* bmin, float* bmax, const int side)
+{
+ if (side == 0 || side == 4)
+ {
+ if (va[2] < vb[2])
+ {
+ bmin[0] = va[2];
+ bmin[1] = va[1];
+ bmax[0] = vb[2];
+ bmax[1] = vb[1];
+ }
+ else
+ {
+ bmin[0] = vb[2];
+ bmin[1] = vb[1];
+ bmax[0] = va[2];
+ bmax[1] = va[1];
+ }
+ }
+ else if (side == 2 || side == 6)
+ {
+ if (va[0] < vb[0])
+ {
+ bmin[0] = va[0];
+ bmin[1] = va[1];
+ bmax[0] = vb[0];
+ bmax[1] = vb[1];
+ }
+ else
+ {
+ bmin[0] = vb[0];
+ bmin[1] = vb[1];
+ bmax[0] = va[0];
+ bmax[1] = va[1];
+ }
+ }
+}
+
+inline int computeTileHash(int x, int y, const int mask)
+{
+ const unsigned int h1 = 0x8da6b343; // Large multiplicative constants;
+ const unsigned int h2 = 0xd8163841; // here arbitrarily chosen primes
+ unsigned int n = h1 * x + h2 * y;
+ return (int)(n & mask);
+}
+
+inline unsigned int allocLink(dtMeshTile* tile)
+{
+ if (tile->linksFreeList == DT_NULL_LINK)
+ return DT_NULL_LINK;
+ unsigned int link = tile->linksFreeList;
+ tile->linksFreeList = tile->links[link].next;
+ return link;
+}
+
+inline void freeLink(dtMeshTile* tile, unsigned int link)
+{
+ tile->links[link].next = tile->linksFreeList;
+ tile->linksFreeList = link;
+}
+
+
+dtNavMesh* dtAllocNavMesh()
+{
+ void* mem = dtAlloc(sizeof(dtNavMesh), DT_ALLOC_PERM);
+ if (!mem) return 0;
+ return new(mem) dtNavMesh;
+}
+
+void dtFreeNavMesh(dtNavMesh* navmesh)
+{
+ if (!navmesh) return;
+ navmesh->~dtNavMesh();
+ dtFree(navmesh);
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////
+dtNavMesh::dtNavMesh() :
+ m_tileWidth(0),
+ m_tileHeight(0),
+ m_maxTiles(0),
+ m_tileLutSize(0),
+ m_tileLutMask(0),
+ m_posLookup(0),
+ m_nextFree(0),
+ m_tiles(0),
+ m_saltBits(0),
+ m_tileBits(0),
+ m_polyBits(0)
+{
+ m_orig[0] = 0;
+ m_orig[1] = 0;
+ m_orig[2] = 0;
+}
+
+dtNavMesh::~dtNavMesh()
+{
+ for (int i = 0; i < m_maxTiles; ++i)
+ {
+ if (m_tiles[i].flags & DT_TILE_FREE_DATA)
+ {
+ dtFree(m_tiles[i].data);
+ m_tiles[i].data = 0;
+ m_tiles[i].dataSize = 0;
+ }
+ }
+ dtFree(m_posLookup);
+ dtFree(m_tiles);
+}
+
+dtStatus dtNavMesh::init(const dtNavMeshParams* params)
+{
+ memcpy(&m_params, params, sizeof(dtNavMeshParams));
+ dtVcopy(m_orig, params->orig);
+ m_tileWidth = params->tileWidth;
+ m_tileHeight = params->tileHeight;
+
+ // Init tiles
+ m_maxTiles = params->maxTiles;
+ m_tileLutSize = dtNextPow2(params->maxTiles/4);
+ if (!m_tileLutSize) m_tileLutSize = 1;
+ m_tileLutMask = m_tileLutSize-1;
+
+ m_tiles = (dtMeshTile*)dtAlloc(sizeof(dtMeshTile)*m_maxTiles, DT_ALLOC_PERM);
+ if (!m_tiles)
+ return DT_FAILURE;
+ m_posLookup = (dtMeshTile**)dtAlloc(sizeof(dtMeshTile*)*m_tileLutSize, DT_ALLOC_PERM);
+ if (!m_posLookup)
+ return DT_FAILURE;
+ memset(m_tiles, 0, sizeof(dtMeshTile)*m_maxTiles);
+ memset(m_posLookup, 0, sizeof(dtMeshTile*)*m_tileLutSize);
+ m_nextFree = 0;
+ for (int i = m_maxTiles-1; i >= 0; --i)
+ {
+ m_tiles[i].salt = 1;
+ m_tiles[i].next = m_nextFree;
+ m_nextFree = &m_tiles[i];
+ }
+
+ // Init ID generator values.
+ m_tileBits = STATIC_TILE_BITS; //dtIlog2(dtNextPow2((unsigned int)params->maxTiles));
+ m_polyBits = STATIC_POLY_BITS; //dtIlog2(dtNextPow2((unsigned int)params->maxPolys));
+ m_saltBits = STATIC_SALT_BITS; //sizeof(dtPolyRef)*8 - m_tileBits - m_polyBits;
+ //if (m_saltBits < SALT_MIN_BITS)
+ //return DT_FAILURE;
+
+ return DT_SUCCESS;
+}
+
+dtStatus dtNavMesh::init(unsigned char* data, const int dataSize, const int flags)
+{
+ // Make sure the data is in right format.
+ dtMeshHeader* header = (dtMeshHeader*)data;
+ if (header->magic != DT_NAVMESH_MAGIC)
+ return DT_FAILURE;
+ if (header->version != DT_NAVMESH_VERSION)
+ return DT_FAILURE;
+
+ dtNavMeshParams params;
+ dtVcopy(params.orig, header->bmin);
+ params.tileWidth = header->bmax[0] - header->bmin[0];
+ params.tileHeight = header->bmax[2] - header->bmin[2];
+ params.maxTiles = 1;
+ params.maxPolys = header->polyCount;
+
+ dtStatus res = init(&params);
+ if (res != DT_SUCCESS)
+ return res;
+
+ return addTile(data, dataSize, flags, 0, 0);
+}
+
+const dtNavMeshParams* dtNavMesh::getParams() const
+{
+ return &m_params;
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////
+int dtNavMesh::findConnectingPolys(const float* va, const float* vb,
+ const dtMeshTile* tile, int side,
+ dtPolyRef* con, float* conarea, int maxcon) const
+{
+ if (!tile) return 0;
+
+ float amin[2], amax[2];
+ calcSlabEndPoints(va,vb, amin,amax, side);
+
+ // Remove links pointing to 'side' and compact the links array.
+ float bmin[2], bmax[2];
+ unsigned short m = DT_EXT_LINK | (unsigned short)side;
+ int n = 0;
+
+ dtPolyRef base = getPolyRefBase(tile);
+
+ for (int i = 0; i < tile->header->polyCount; ++i)
+ {
+ dtPoly* poly = &tile->polys[i];
+ const int nv = poly->vertCount;
+ for (int j = 0; j < nv; ++j)
+ {
+ // Skip edges which do not point to the right side.
+ if (poly->neis[j] != m) continue;
+ // Check if the segments touch.
+ const float* vc = &tile->verts[poly->verts[j]*3];
+ const float* vd = &tile->verts[poly->verts[(j+1) % nv]*3];
+ calcSlabEndPoints(vc,vd, bmin,bmax, side);
+
+ if (!overlapSlabs(amin,amax, bmin,bmax, 0.01f, tile->header->walkableClimb)) continue;
+
+ // Add return value.
+ if (n < maxcon)
+ {
+ conarea[n*2+0] = dtMax(amin[0], bmin[0]);
+ conarea[n*2+1] = dtMin(amax[0], bmax[0]);
+ con[n] = base | (dtPolyRef)i;
+ n++;
+ }
+ break;
+ }
+ }
+ return n;
+}
+
+void dtNavMesh::unconnectExtLinks(dtMeshTile* tile, int side)
+{
+ if (!tile) return;
+
+ for (int i = 0; i < tile->header->polyCount; ++i)
+ {
+ dtPoly* poly = &tile->polys[i];
+ unsigned int j = poly->firstLink;
+ unsigned int pj = DT_NULL_LINK;
+ while (j != DT_NULL_LINK)
+ {
+ if (tile->links[j].side == side)
+ {
+ // Revove link.
+ unsigned int nj = tile->links[j].next;
+ if (pj == DT_NULL_LINK)
+ poly->firstLink = nj;
+ else
+ tile->links[pj].next = nj;
+ freeLink(tile, j);
+ j = nj;
+ }
+ else
+ {
+ // Advance
+ pj = j;
+ j = tile->links[j].next;
+ }
+ }
+ }
+}
+
+void dtNavMesh::connectExtLinks(dtMeshTile* tile, dtMeshTile* target, int side)
+{
+ if (!tile) return;
+
+ // Connect border links.
+ for (int i = 0; i < tile->header->polyCount; ++i)
+ {
+ dtPoly* poly = &tile->polys[i];
+
+ // Create new links.
+ unsigned short m = DT_EXT_LINK | (unsigned short)side;
+ const int nv = poly->vertCount;
+ for (int j = 0; j < nv; ++j)
+ {
+ // Skip edges which do not point to the right side.
+ if (poly->neis[j] != m) continue;
+
+ // Create new links
+ const float* va = &tile->verts[poly->verts[j]*3];
+ const float* vb = &tile->verts[poly->verts[(j+1) % nv]*3];
+ dtPolyRef nei[4];
+ float neia[4*2];
+ int nnei = findConnectingPolys(va,vb, target, dtOppositeTile(side), nei,neia,4);
+ for (int k = 0; k < nnei; ++k)
+ {
+ unsigned int idx = allocLink(tile);
+ if (idx != DT_NULL_LINK)
+ {
+ dtLink* link = &tile->links[idx];
+ link->ref = nei[k];
+ link->edge = (unsigned char)j;
+ link->side = (unsigned char)side;
+
+ link->next = poly->firstLink;
+ poly->firstLink = idx;
+
+ // Compress portal limits to a byte value.
+ if (side == 0 || side == 4)
+ {
+ float tmin = (neia[k*2+0]-va[2]) / (vb[2]-va[2]);
+ float tmax = (neia[k*2+1]-va[2]) / (vb[2]-va[2]);
+ if (tmin > tmax)
+ dtSwap(tmin,tmax);
+ link->bmin = (unsigned char)(dtClamp(tmin, 0.0f, 1.0f)*255.0f);
+ link->bmax = (unsigned char)(dtClamp(tmax, 0.0f, 1.0f)*255.0f);
+ }
+ else if (side == 2 || side == 6)
+ {
+ float tmin = (neia[k*2+0]-va[0]) / (vb[0]-va[0]);
+ float tmax = (neia[k*2+1]-va[0]) / (vb[0]-va[0]);
+ if (tmin > tmax)
+ dtSwap(tmin,tmax);
+ link->bmin = (unsigned char)(dtClamp(tmin, 0.0f, 1.0f)*255.0f);
+ link->bmax = (unsigned char)(dtClamp(tmax, 0.0f, 1.0f)*255.0f);
+ }
+ }
+ }
+ }
+ }
+}
+
+void dtNavMesh::connectExtOffMeshLinks(dtMeshTile* tile, dtMeshTile* target, int side)
+{
+ if (!tile) return;
+
+ // Connect off-mesh links.
+ // We are interested on links which land from target tile to this tile.
+ const unsigned char oppositeSide = (unsigned char)dtOppositeTile(side);
+
+ for (int i = 0; i < target->header->offMeshConCount; ++i)
+ {
+ dtOffMeshConnection* targetCon = &target->offMeshCons[i];
+ if (targetCon->side != oppositeSide)
+ continue;
+
+ dtPoly* targetPoly = &target->polys[targetCon->poly];
+
+ const float ext[3] = { targetCon->rad, target->header->walkableClimb, targetCon->rad };
+
+ // Find polygon to connect to.
+ const float* p = &targetCon->pos[3];
+ float nearestPt[3];
+ dtPolyRef ref = findNearestPolyInTile(tile, p, ext, nearestPt);
+ if (!ref) continue;
+ // findNearestPoly may return too optimistic results, further check to make sure.
+ if (dtSqr(nearestPt[0]-p[0])+dtSqr(nearestPt[2]-p[2]) > dtSqr(targetCon->rad))
+ continue;
+ // Make sure the location is on current mesh.
+ float* v = &target->verts[targetPoly->verts[1]*3];
+ dtVcopy(v, nearestPt);
+
+ // Link off-mesh connection to target poly.
+ unsigned int idx = allocLink(target);
+ if (idx != DT_NULL_LINK)
+ {
+ dtLink* link = &target->links[idx];
+ link->ref = ref;
+ link->edge = (unsigned char)1;
+ link->side = oppositeSide;
+ link->bmin = link->bmax = 0;
+ // Add to linked list.
+ link->next = targetPoly->firstLink;
+ targetPoly->firstLink = idx;
+ }
+
+ // Link target poly to off-mesh connection.
+ if (targetCon->flags & DT_OFFMESH_CON_BIDIR)
+ {
+ unsigned int idx = allocLink(tile);
+ if (idx != DT_NULL_LINK)
+ {
+ const unsigned short landPolyIdx = (unsigned short)decodePolyIdPoly(ref);
+ dtPoly* landPoly = &tile->polys[landPolyIdx];
+ dtLink* link = &tile->links[idx];
+ link->ref = getPolyRefBase(target) | (dtPolyRef)(targetCon->poly);
+ link->edge = 0xff;
+ link->side = (unsigned char)side;
+ link->bmin = link->bmax = 0;
+ // Add to linked list.
+ link->next = landPoly->firstLink;
+ landPoly->firstLink = idx;
+ }
+ }
+ }
+
+}
+
+void dtNavMesh::connectIntLinks(dtMeshTile* tile)
+{
+ if (!tile) return;
+
+ dtPolyRef base = getPolyRefBase(tile);
+
+ for (int i = 0; i < tile->header->polyCount; ++i)
+ {
+ dtPoly* poly = &tile->polys[i];
+ poly->firstLink = DT_NULL_LINK;
+
+ if (poly->getType() == DT_POLYTYPE_OFFMESH_CONNECTION)
+ continue;
+
+ // Build edge links backwards so that the links will be
+ // in the linked list from lowest index to highest.
+ for (int j = poly->vertCount-1; j >= 0; --j)
+ {
+ // Skip hard and non-internal edges.
+ if (poly->neis[j] == 0 || (poly->neis[j] & DT_EXT_LINK)) continue;
+
+ unsigned int idx = allocLink(tile);
+ if (idx != DT_NULL_LINK)
+ {
+ dtLink* link = &tile->links[idx];
+ link->ref = base | (dtPolyRef)(poly->neis[j]-1);
+ link->edge = (unsigned char)j;
+ link->side = 0xff;
+ link->bmin = link->bmax = 0;
+ // Add to linked list.
+ link->next = poly->firstLink;
+ poly->firstLink = idx;
+ }
+ }
+ }
+}
+
+void dtNavMesh::connectIntOffMeshLinks(dtMeshTile* tile)
+{
+ if (!tile) return;
+
+ dtPolyRef base = getPolyRefBase(tile);
+
+ // Find Off-mesh connection end points.
+ for (int i = 0; i < tile->header->offMeshConCount; ++i)
+ {
+ dtOffMeshConnection* con = &tile->offMeshCons[i];
+ dtPoly* poly = &tile->polys[con->poly];
+
+ const float ext[3] = { con->rad, tile->header->walkableClimb, con->rad };
+
+ for (int j = 0; j < 2; ++j)
+ {
+ unsigned char side = j == 0 ? 0xff : con->side;
+
+ if (side == 0xff)
+ {
+ // Find polygon to connect to.
+ const float* p = &con->pos[j*3];
+ float nearestPt[3];
+ dtPolyRef ref = findNearestPolyInTile(tile, p, ext, nearestPt);
+ if (!ref) continue;
+ // findNearestPoly may return too optimistic results, further check to make sure.
+ if (dtSqr(nearestPt[0]-p[0])+dtSqr(nearestPt[2]-p[2]) > dtSqr(con->rad))
+ continue;
+ // Make sure the location is on current mesh.
+ float* v = &tile->verts[poly->verts[j]*3];
+ dtVcopy(v, nearestPt);
+
+ // Link off-mesh connection to target poly.
+ unsigned int idx = allocLink(tile);
+ if (idx != DT_NULL_LINK)
+ {
+ dtLink* link = &tile->links[idx];
+ link->ref = ref;
+ link->edge = (unsigned char)j;
+ link->side = 0xff;
+ link->bmin = link->bmax = 0;
+ // Add to linked list.
+ link->next = poly->firstLink;
+ poly->firstLink = idx;
+ }
+
+ // Start end-point is always connect back to off-mesh connection,
+ // Destination end-point only if it is bidirectional link.
+ if (j == 0 || (j == 1 && (con->flags & DT_OFFMESH_CON_BIDIR)))
+ {
+ // Link target poly to off-mesh connection.
+ unsigned int idx = allocLink(tile);
+ if (idx != DT_NULL_LINK)
+ {
+ const unsigned short landPolyIdx = (unsigned short)decodePolyIdPoly(ref);
+ dtPoly* landPoly = &tile->polys[landPolyIdx];
+ dtLink* link = &tile->links[idx];
+ link->ref = base | (dtPolyRef)(con->poly);
+ link->edge = 0xff;
+ link->side = 0xff;
+ link->bmin = link->bmax = 0;
+ // Add to linked list.
+ link->next = landPoly->firstLink;
+ landPoly->firstLink = idx;
+ }
+ }
+
+ }
+ }
+ }
+}
+
+dtStatus dtNavMesh::closestPointOnPolyInTile(const dtMeshTile* tile, unsigned int ip,
+ const float* pos, float* closest) const
+{
+ const dtPoly* poly = &tile->polys[ip];
+
+ float closestDistSqr = FLT_MAX;
+ const dtPolyDetail* pd = &tile->detailMeshes[ip];
+
+ for (int j = 0; j < pd->triCount; ++j)
+ {
+ const unsigned char* t = &tile->detailTris[(pd->triBase+j)*4];
+ const float* v[3];
+ for (int k = 0; k < 3; ++k)
+ {
+ if (t[k] < poly->vertCount)
+ v[k] = &tile->verts[poly->verts[t[k]]*3];
+ else
+ v[k] = &tile->detailVerts[(pd->vertBase+(t[k]-poly->vertCount))*3];
+ }
+ float pt[3];
+ dtClosestPtPointTriangle(pt, pos, v[0], v[1], v[2]);
+ float d = dtVdistSqr(pos, pt);
+ if (d < closestDistSqr)
+ {
+ dtVcopy(closest, pt);
+ closestDistSqr = d;
+ }
+ }
+
+ return DT_SUCCESS;
+}
+
+dtPolyRef dtNavMesh::findNearestPolyInTile(const dtMeshTile* tile,
+ const float* center, const float* extents,
+ float* nearestPt) const
+{
+ float bmin[3], bmax[3];
+ dtVsub(bmin, center, extents);
+ dtVadd(bmax, center, extents);
+
+ // Get nearby polygons from proximity grid.
+ dtPolyRef polys[128];
+ int polyCount = queryPolygonsInTile(tile, bmin, bmax, polys, 128);
+
+ // Find nearest polygon amongst the nearby polygons.
+ dtPolyRef nearest = 0;
+ float nearestDistanceSqr = FLT_MAX;
+ for (int i = 0; i < polyCount; ++i)
+ {
+ dtPolyRef ref = polys[i];
+ float closestPtPoly[3];
+ if (closestPointOnPolyInTile(tile, decodePolyIdPoly(ref), center, closestPtPoly) != DT_SUCCESS)
+ continue;
+ float d = dtVdistSqr(center, closestPtPoly);
+ if (d < nearestDistanceSqr)
+ {
+ if (nearestPt)
+ dtVcopy(nearestPt, closestPtPoly);
+ nearestDistanceSqr = d;
+ nearest = ref;
+ }
+ }
+
+ return nearest;
+}
+
+int dtNavMesh::queryPolygonsInTile(const dtMeshTile* tile, const float* qmin, const float* qmax,
+ dtPolyRef* polys, const int maxPolys) const
+{
+ if (tile->bvTree)
+ {
+ const dtBVNode* node = &tile->bvTree[0];
+ const dtBVNode* end = &tile->bvTree[tile->header->bvNodeCount];
+ const float* tbmin = tile->header->bmin;
+ const float* tbmax = tile->header->bmax;
+ const float qfac = tile->header->bvQuantFactor;
+
+ // Calculate quantized box
+ unsigned short bmin[3], bmax[3];
+ // dtClamp query box to world box.
+ float minx = dtClamp(qmin[0], tbmin[0], tbmax[0]) - tbmin[0];
+ float miny = dtClamp(qmin[1], tbmin[1], tbmax[1]) - tbmin[1];
+ float minz = dtClamp(qmin[2], tbmin[2], tbmax[2]) - tbmin[2];
+ float maxx = dtClamp(qmax[0], tbmin[0], tbmax[0]) - tbmin[0];
+ float maxy = dtClamp(qmax[1], tbmin[1], tbmax[1]) - tbmin[1];
+ float maxz = dtClamp(qmax[2], tbmin[2], tbmax[2]) - tbmin[2];
+ // Quantize
+ bmin[0] = (unsigned short)(qfac * minx) & 0xfffe;
+ bmin[1] = (unsigned short)(qfac * miny) & 0xfffe;
+ bmin[2] = (unsigned short)(qfac * minz) & 0xfffe;
+ bmax[0] = (unsigned short)(qfac * maxx + 1) | 1;
+ bmax[1] = (unsigned short)(qfac * maxy + 1) | 1;
+ bmax[2] = (unsigned short)(qfac * maxz + 1) | 1;
+
+ // Traverse tree
+ dtPolyRef base = getPolyRefBase(tile);
+ int n = 0;
+ while (node < end)
+ {
+ const bool overlap = dtOverlapQuantBounds(bmin, bmax, node->bmin, node->bmax);
+ const bool isLeafNode = node->i >= 0;
+
+ if (isLeafNode && overlap)
+ {
+ if (n < maxPolys)
+ polys[n++] = base | (dtPolyRef)node->i;
+ }
+
+ if (overlap || isLeafNode)
+ node++;
+ else
+ {
+ const int escapeIndex = -node->i;
+ node += escapeIndex;
+ }
+ }
+
+ return n;
+ }
+ else
+ {
+ float bmin[3], bmax[3];
+ int n = 0;
+ dtPolyRef base = getPolyRefBase(tile);
+ for (int i = 0; i < tile->header->polyCount; ++i)
+ {
+ // Calc polygon bounds.
+ dtPoly* p = &tile->polys[i];
+ const float* v = &tile->verts[p->verts[0]*3];
+ dtVcopy(bmin, v);
+ dtVcopy(bmax, v);
+ for (int j = 1; j < p->vertCount; ++j)
+ {
+ v = &tile->verts[p->verts[j]*3];
+ dtVmin(bmin, v);
+ dtVmax(bmax, v);
+ }
+ if (dtOverlapBounds(qmin,qmax, bmin,bmax))
+ {
+ if (n < maxPolys)
+ polys[n++] = base | (dtPolyRef)i;
+ }
+ }
+ return n;
+ }
+}
+
+dtStatus dtNavMesh::addTile(unsigned char* data, int dataSize, int flags,
+ dtTileRef lastRef, dtTileRef* result)
+{
+ // Make sure the data is in right format.
+ dtMeshHeader* header = (dtMeshHeader*)data;
+ if (header->magic != DT_NAVMESH_MAGIC)
+ return DT_FAILURE_DATA_MAGIC;
+ if (header->version != DT_NAVMESH_VERSION)
+ return DT_FAILURE_DATA_VERSION;
+
+ // Make sure the location is free.
+ if (getTileAt(header->x, header->y))
+ return DT_FAILURE;
+
+ // Allocate a tile.
+ dtMeshTile* tile = 0;
+ if (!lastRef)
+ {
+ if (m_nextFree)
+ {
+ tile = m_nextFree;
+ m_nextFree = tile->next;
+ tile->next = 0;
+ }
+ }
+ else
+ {
+ // Try to relocate the tile to specific index with same salt.
+ int tileIndex = (int)decodePolyIdTile((dtPolyRef)lastRef);
+ if (tileIndex >= m_maxTiles)
+ return DT_FAILURE_OUT_OF_MEMORY;
+ // Try to find the specific tile id from the free list.
+ dtMeshTile* target = &m_tiles[tileIndex];
+ dtMeshTile* prev = 0;
+ tile = m_nextFree;
+ while (tile && tile != target)
+ {
+ prev = tile;
+ tile = tile->next;
+ }
+ // Could not find the correct location.
+ if (tile != target)
+ return DT_FAILURE_OUT_OF_MEMORY;
+ // Remove from freelist
+ if (!prev)
+ m_nextFree = tile->next;
+ else
+ prev->next = tile->next;
+
+ // Restore salt.
+ tile->salt = decodePolyIdSalt((dtPolyRef)lastRef);
+ }
+
+ // Make sure we could allocate a tile.
+ if (!tile)
+ return DT_FAILURE_OUT_OF_MEMORY;
+
+ // Insert tile into the position lut.
+ int h = computeTileHash(header->x, header->y, m_tileLutMask);
+ tile->next = m_posLookup[h];
+ m_posLookup[h] = tile;
+
+ // Patch header pointers.
+ const int headerSize = dtAlign4(sizeof(dtMeshHeader));
+ const int vertsSize = dtAlign4(sizeof(float)*3*header->vertCount);
+ const int polysSize = dtAlign4(sizeof(dtPoly)*header->polyCount);
+ const int linksSize = dtAlign4(sizeof(dtLink)*(header->maxLinkCount));
+ const int detailMeshesSize = dtAlign4(sizeof(dtPolyDetail)*header->detailMeshCount);
+ const int detailVertsSize = dtAlign4(sizeof(float)*3*header->detailVertCount);
+ const int detailTrisSize = dtAlign4(sizeof(unsigned char)*4*header->detailTriCount);
+ const int bvtreeSize = dtAlign4(sizeof(dtBVNode)*header->bvNodeCount);
+ const int offMeshLinksSize = dtAlign4(sizeof(dtOffMeshConnection)*header->offMeshConCount);
+
+ unsigned char* d = data + headerSize;
+ tile->verts = (float*)d; d += vertsSize;
+ tile->polys = (dtPoly*)d; d += polysSize;
+ tile->links = (dtLink*)d; d += linksSize;
+ tile->detailMeshes = (dtPolyDetail*)d; d += detailMeshesSize;
+ tile->detailVerts = (float*)d; d += detailVertsSize;
+ tile->detailTris = (unsigned char*)d; d += detailTrisSize;
+ tile->bvTree = (dtBVNode*)d; d += bvtreeSize;
+ tile->offMeshCons = (dtOffMeshConnection*)d; d += offMeshLinksSize;
+
+ // Build links freelist
+ tile->linksFreeList = 0;
+ tile->links[header->maxLinkCount-1].next = DT_NULL_LINK;
+ for (int i = 0; i < header->maxLinkCount-1; ++i)
+ tile->links[i].next = i+1;
+
+ // Init tile.
+ tile->header = header;
+ tile->data = data;
+ tile->dataSize = dataSize;
+ tile->flags = flags;
+
+ connectIntLinks(tile);
+ connectIntOffMeshLinks(tile);
+
+ // Create connections connections.
+ for (int i = 0; i < 8; ++i)
+ {
+ dtMeshTile* nei = getNeighbourTileAt(header->x, header->y, i);
+ if (nei)
+ {
+ connectExtLinks(tile, nei, i);
+ connectExtLinks(nei, tile, dtOppositeTile(i));
+ connectExtOffMeshLinks(tile, nei, i);
+ connectExtOffMeshLinks(nei, tile, dtOppositeTile(i));
+ }
+ }
+
+ if (result)
+ *result = getTileRef(tile);
+
+ return DT_SUCCESS;
+}
+
+const dtMeshTile* dtNavMesh::getTileAt(int x, int y) const
+{
+ // Find tile based on hash.
+ int h = computeTileHash(x,y,m_tileLutMask);
+ dtMeshTile* tile = m_posLookup[h];
+ while (tile)
+ {
+ if (tile->header && tile->header->x == x && tile->header->y == y)
+ return tile;
+ tile = tile->next;
+ }
+ return 0;
+}
+
+dtMeshTile* dtNavMesh::getNeighbourTileAt(int x, int y, int side) const
+{
+ switch (side)
+ {
+ case 0: x++; break;
+ case 1: x++; y++; break;
+ case 2: y++; break;
+ case 3: x--; y++; break;
+ case 4: x--; break;
+ case 5: x--; y--; break;
+ case 6: y--; break;
+ case 7: x++; y--; break;
+ };
+
+ // Find tile based on hash.
+ int h = computeTileHash(x,y,m_tileLutMask);
+ dtMeshTile* tile = m_posLookup[h];
+ while (tile)
+ {
+ if (tile->header && tile->header->x == x && tile->header->y == y)
+ return tile;
+ tile = tile->next;
+ }
+ return 0;
+}
+
+dtTileRef dtNavMesh::getTileRefAt(int x, int y) const
+{
+ // Find tile based on hash.
+ int h = computeTileHash(x,y,m_tileLutMask);
+ dtMeshTile* tile = m_posLookup[h];
+ while (tile)
+ {
+ if (tile->header && tile->header->x == x && tile->header->y == y)
+ return getTileRef(tile);
+ tile = tile->next;
+ }
+ return 0;
+}
+
+const dtMeshTile* dtNavMesh::getTileByRef(dtTileRef ref) const
+{
+ if (!ref)
+ return 0;
+ unsigned int tileIndex = decodePolyIdTile((dtPolyRef)ref);
+ unsigned int tileSalt = decodePolyIdSalt((dtPolyRef)ref);
+ if ((int)tileIndex >= m_maxTiles)
+ return 0;
+ const dtMeshTile* tile = &m_tiles[tileIndex];
+ if (tile->salt != tileSalt)
+ return 0;
+ return tile;
+}
+
+int dtNavMesh::getMaxTiles() const
+{
+ return m_maxTiles;
+}
+
+dtMeshTile* dtNavMesh::getTile(int i)
+{
+ return &m_tiles[i];
+}
+
+const dtMeshTile* dtNavMesh::getTile(int i) const
+{
+ return &m_tiles[i];
+}
+
+void dtNavMesh::calcTileLoc(const float* pos, int* tx, int* ty) const
+{
+ *tx = (int)floorf((pos[0]-m_orig[0]) / m_tileWidth);
+ *ty = (int)floorf((pos[2]-m_orig[2]) / m_tileHeight);
+}
+
+dtStatus dtNavMesh::getTileAndPolyByRef(const dtPolyRef ref, const dtMeshTile** tile, const dtPoly** poly) const
+{
+ unsigned int salt, it, ip;
+ decodePolyId(ref, salt, it, ip);
+ if (it >= (unsigned int)m_maxTiles) return DT_FAILURE;
+ if (m_tiles[it].salt != salt || m_tiles[it].header == 0) return DT_FAILURE;
+ if (ip >= (unsigned int)m_tiles[it].header->polyCount) return DT_FAILURE;
+ *tile = &m_tiles[it];
+ *poly = &m_tiles[it].polys[ip];
+ return DT_SUCCESS;
+}
+
+void dtNavMesh::getTileAndPolyByRefUnsafe(const dtPolyRef ref, const dtMeshTile** tile, const dtPoly** poly) const
+{
+ unsigned int salt, it, ip;
+ decodePolyId(ref, salt, it, ip);
+ *tile = &m_tiles[it];
+ *poly = &m_tiles[it].polys[ip];
+}
+
+bool dtNavMesh::isValidPolyRef(dtPolyRef ref) const
+{
+ unsigned int salt, it, ip;
+ decodePolyId(ref, salt, it, ip);
+ if (it >= (unsigned int)m_maxTiles) return false;
+ if (m_tiles[it].salt != salt || m_tiles[it].header == 0) return false;
+ if (ip >= (unsigned int)m_tiles[it].header->polyCount) return false;
+ return true;
+}
+
+dtStatus dtNavMesh::removeTile(dtTileRef ref, unsigned char** data, int* dataSize)
+{
+ if (!ref)
+ return DT_FAILURE;
+ unsigned int tileIndex = decodePolyIdTile((dtPolyRef)ref);
+ unsigned int tileSalt = decodePolyIdSalt((dtPolyRef)ref);
+ if ((int)tileIndex >= m_maxTiles)
+ return DT_FAILURE;
+ dtMeshTile* tile = &m_tiles[tileIndex];
+ if (tile->salt != tileSalt)
+ return DT_FAILURE;
+
+ // Remove tile from hash lookup.
+ int h = computeTileHash(tile->header->x,tile->header->y,m_tileLutMask);
+ dtMeshTile* prev = 0;
+ dtMeshTile* cur = m_posLookup[h];
+ while (cur)
+ {
+ if (cur == tile)
+ {
+ if (prev)
+ prev->next = cur->next;
+ else
+ m_posLookup[h] = cur->next;
+ break;
+ }
+ prev = cur;
+ cur = cur->next;
+ }
+
+ // Remove connections to neighbour tiles.
+ for (int i = 0; i < 8; ++i)
+ {
+ dtMeshTile* nei = getNeighbourTileAt(tile->header->x,tile->header->y,i);
+ if (!nei) continue;
+ unconnectExtLinks(nei, dtOppositeTile(i));
+ }
+
+
+ // Reset tile.
+ if (tile->flags & DT_TILE_FREE_DATA)
+ {
+ // Owns data
+ dtFree(tile->data);
+ tile->data = 0;
+ tile->dataSize = 0;
+ if (data) *data = 0;
+ if (dataSize) *dataSize = 0;
+ }
+ else
+ {
+ if (data) *data = tile->data;
+ if (dataSize) *dataSize = tile->dataSize;
+ }
+
+ tile->header = 0;
+ tile->flags = 0;
+ tile->linksFreeList = 0;
+ tile->polys = 0;
+ tile->verts = 0;
+ tile->links = 0;
+ tile->detailMeshes = 0;
+ tile->detailVerts = 0;
+ tile->detailTris = 0;
+ tile->bvTree = 0;
+ tile->offMeshCons = 0;
+
+ // Update salt, salt should never be zero.
+ tile->salt = (tile->salt+1) & ((1<<m_saltBits)-1);
+ if (tile->salt == 0)
+ tile->salt++;
+
+ // Add to free list.
+ tile->next = m_nextFree;
+ m_nextFree = tile;
+
+ return DT_SUCCESS;
+}
+
+dtTileRef dtNavMesh::getTileRef(const dtMeshTile* tile) const
+{
+ if (!tile) return 0;
+ const unsigned int it = tile - m_tiles;
+ return (dtTileRef)encodePolyId(tile->salt, it, 0);
+}
+
+dtPolyRef dtNavMesh::getPolyRefBase(const dtMeshTile* tile) const
+{
+ if (!tile) return 0;
+ const unsigned int it = tile - m_tiles;
+ return encodePolyId(tile->salt, it, 0);
+}
+
+struct dtTileState
+{
+ int magic; // Magic number, used to identify the data.
+ int version; // Data version number.
+ dtTileRef ref; // Tile ref at the time of storing the data.
+};
+
+struct dtPolyState
+{
+ unsigned short flags; // Flags (see dtPolyFlags).
+ unsigned char area; // Area ID of the polygon.
+};
+
+int dtNavMesh::getTileStateSize(const dtMeshTile* tile) const
+{
+ if (!tile) return 0;
+ const int headerSize = dtAlign4(sizeof(dtTileState));
+ const int polyStateSize = dtAlign4(sizeof(dtPolyState) * tile->header->polyCount);
+ return headerSize + polyStateSize;
+}
+
+dtStatus dtNavMesh::storeTileState(const dtMeshTile* tile, unsigned char* data, const int maxDataSize) const
+{
+ // Make sure there is enough space to store the state.
+ const int sizeReq = getTileStateSize(tile);
+ if (maxDataSize < sizeReq)
+ return DT_FAILURE;
+
+ dtTileState* tileState = (dtTileState*)data; data += dtAlign4(sizeof(dtTileState));
+ dtPolyState* polyStates = (dtPolyState*)data; data += dtAlign4(sizeof(dtPolyState) * tile->header->polyCount);
+
+ // Store tile state.
+ tileState->magic = DT_NAVMESH_STATE_MAGIC;
+ tileState->version = DT_NAVMESH_STATE_VERSION;
+ tileState->ref = getTileRef(tile);
+
+ // Store per poly state.
+ for (int i = 0; i < tile->header->polyCount; ++i)
+ {
+ const dtPoly* p = &tile->polys[i];
+ dtPolyState* s = &polyStates[i];
+ s->flags = p->flags;
+ s->area = p->getArea();
+ }
+
+ return DT_SUCCESS;
+}
+
+dtStatus dtNavMesh::restoreTileState(dtMeshTile* tile, const unsigned char* data, const int maxDataSize)
+{
+ // Make sure there is enough space to store the state.
+ const int sizeReq = getTileStateSize(tile);
+ if (maxDataSize < sizeReq)
+ return DT_FAILURE;
+
+ const dtTileState* tileState = (const dtTileState*)data; data += dtAlign4(sizeof(dtTileState));
+ const dtPolyState* polyStates = (const dtPolyState*)data; data += dtAlign4(sizeof(dtPolyState) * tile->header->polyCount);
+
+ // Check that the restore is possible.
+ if (tileState->magic != DT_NAVMESH_STATE_MAGIC)
+ return DT_FAILURE_DATA_MAGIC;
+ if (tileState->version != DT_NAVMESH_STATE_VERSION)
+ return DT_FAILURE_DATA_VERSION;
+ if (tileState->ref != getTileRef(tile))
+ return DT_FAILURE;
+
+ // Restore per poly state.
+ for (int i = 0; i < tile->header->polyCount; ++i)
+ {
+ dtPoly* p = &tile->polys[i];
+ const dtPolyState* s = &polyStates[i];
+ p->flags = s->flags;
+ p->setArea(s->area);
+ }
+
+ return DT_SUCCESS;
+}
+
+// Returns start and end location of an off-mesh link polygon.
+dtStatus dtNavMesh::getOffMeshConnectionPolyEndPoints(dtPolyRef prevRef, dtPolyRef polyRef, float* startPos, float* endPos) const
+{
+ unsigned int salt, it, ip;
+
+ // Get current polygon
+ decodePolyId(polyRef, salt, it, ip);
+ if (it >= (unsigned int)m_maxTiles) return DT_FAILURE;
+ if (m_tiles[it].salt != salt || m_tiles[it].header == 0) return DT_FAILURE;
+ const dtMeshTile* tile = &m_tiles[it];
+ if (ip >= (unsigned int)tile->header->polyCount) return DT_FAILURE;
+ const dtPoly* poly = &tile->polys[ip];
+
+ // Make sure that the current poly is indeed off-mesh link.
+ if (poly->getType() != DT_POLYTYPE_OFFMESH_CONNECTION)
+ return DT_FAILURE;
+
+ // Figure out which way to hand out the vertices.
+ int idx0 = 0, idx1 = 1;
+
+ // Find link that points to first vertex.
+ for (unsigned int i = poly->firstLink; i != DT_NULL_LINK; i = tile->links[i].next)
+ {
+ if (tile->links[i].edge == 0)
+ {
+ if (tile->links[i].ref != prevRef)
+ {
+ idx0 = 1;
+ idx1 = 0;
+ }
+ break;
+ }
+ }
+
+ dtVcopy(startPos, &tile->verts[poly->verts[idx0]*3]);
+ dtVcopy(endPos, &tile->verts[poly->verts[idx1]*3]);
+
+ return DT_SUCCESS;
+}
+
+
+const dtOffMeshConnection* dtNavMesh::getOffMeshConnectionByRef(dtPolyRef ref) const
+{
+ unsigned int salt, it, ip;
+
+ // Get current polygon
+ decodePolyId(ref, salt, it, ip);
+ if (it >= (unsigned int)m_maxTiles) return 0;
+ if (m_tiles[it].salt != salt || m_tiles[it].header == 0) return 0;
+ const dtMeshTile* tile = &m_tiles[it];
+ if (ip >= (unsigned int)tile->header->polyCount) return 0;
+ const dtPoly* poly = &tile->polys[ip];
+
+ // Make sure that the current poly is indeed off-mesh link.
+ if (poly->getType() != DT_POLYTYPE_OFFMESH_CONNECTION)
+ return 0;
+
+ const unsigned int idx = ip - tile->header->offMeshBase;
+ dtAssert(idx < (unsigned int)tile->header->offMeshConCount);
+ return &tile->offMeshCons[idx];
+}
+
+
+dtStatus dtNavMesh::setPolyFlags(dtPolyRef ref, unsigned short flags)
+{
+ unsigned int salt, it, ip;
+ decodePolyId(ref, salt, it, ip);
+ if (it >= (unsigned int)m_maxTiles) return DT_FAILURE;
+ if (m_tiles[it].salt != salt || m_tiles[it].header == 0) return DT_FAILURE;
+ dtMeshTile* tile = &m_tiles[it];
+ if (ip >= (unsigned int)tile->header->polyCount) return DT_FAILURE;
+ dtPoly* poly = &tile->polys[ip];
+
+ // Change flags.
+ poly->flags = flags;
+
+ return DT_SUCCESS;
+}
+
+dtStatus dtNavMesh::getPolyFlags(dtPolyRef ref, unsigned short* resultFlags) const
+{
+ unsigned int salt, it, ip;
+ decodePolyId(ref, salt, it, ip);
+ if (it >= (unsigned int)m_maxTiles) return DT_FAILURE;
+ if (m_tiles[it].salt != salt || m_tiles[it].header == 0) return DT_FAILURE;
+ const dtMeshTile* tile = &m_tiles[it];
+ if (ip >= (unsigned int)tile->header->polyCount) return DT_FAILURE;
+ const dtPoly* poly = &tile->polys[ip];
+
+ *resultFlags = poly->flags;
+
+ return DT_SUCCESS;
+}
+
+dtStatus dtNavMesh::setPolyArea(dtPolyRef ref, unsigned char area)
+{
+ unsigned int salt, it, ip;
+ decodePolyId(ref, salt, it, ip);
+ if (it >= (unsigned int)m_maxTiles) return DT_FAILURE;
+ if (m_tiles[it].salt != salt || m_tiles[it].header == 0) return DT_FAILURE;
+ dtMeshTile* tile = &m_tiles[it];
+ if (ip >= (unsigned int)tile->header->polyCount) return DT_FAILURE;
+ dtPoly* poly = &tile->polys[ip];
+
+ poly->setArea(area);
+
+ return DT_SUCCESS;
+}
+
+dtStatus dtNavMesh::getPolyArea(dtPolyRef ref, unsigned char* resultArea) const
+{
+ unsigned int salt, it, ip;
+ decodePolyId(ref, salt, it, ip);
+ if (it >= (unsigned int)m_maxTiles) return DT_FAILURE;
+ if (m_tiles[it].salt != salt || m_tiles[it].header == 0) return DT_FAILURE;
+ const dtMeshTile* tile = &m_tiles[it];
+ if (ip >= (unsigned int)tile->header->polyCount) return DT_FAILURE;
+ const dtPoly* poly = &tile->polys[ip];
+
+ *resultArea = poly->getArea();
+
+ return DT_SUCCESS;
+}
+
diff --git a/dep/recastnavigation/Detour/DetourNavMesh.h b/dep/recastnavigation/Detour/DetourNavMesh.h
new file mode 100644
index 00000000000..52d2c505ec9
--- /dev/null
+++ b/dep/recastnavigation/Detour/DetourNavMesh.h
@@ -0,0 +1,428 @@
+//
+// Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
+//
+// This software is provided 'as-is', without any express or implied
+// warranty. In no event will the authors be held liable for any damages
+// arising from the use of this software.
+// Permission is granted to anyone to use this software for any purpose,
+// including commercial applications, and to alter it and redistribute it
+// freely, subject to the following restrictions:
+// 1. The origin of this software must not be misrepresented; you must not
+// claim that you wrote the original software. If you use this software
+// in a product, an acknowledgment in the product documentation would be
+// appreciated but is not required.
+// 2. Altered source versions must be plainly marked as such, and must not be
+// misrepresented as being the original software.
+// 3. This notice may not be removed or altered from any source distribution.
+//
+
+#ifndef DETOURNAVMESH_H
+#define DETOURNAVMESH_H
+
+#include "DetourAlloc.h"
+
+#ifdef WIN32
+ typedef unsigned __int64 uint64;
+#else
+#include <stdint.h>
+#ifndef uint64_t
+#ifdef __linux__
+#include <linux/types.h>
+#endif
+#endif
+ typedef uint64_t uint64;
+#endif
+
+// Note: If you want to use 64-bit refs, change the types of both dtPolyRef & dtTileRef.
+// It is also recommended to change dtHashRef() to proper 64-bit hash too.
+
+// Reference to navigation polygon.
+typedef uint64 dtPolyRef;
+
+// Reference to navigation mesh tile.
+typedef uint64 dtTileRef;
+
+// Maximum number of vertices per navigation polygon.
+static const int DT_VERTS_PER_POLYGON = 6;
+
+static const int DT_NAVMESH_MAGIC = 'D'<<24 | 'N'<<16 | 'A'<<8 | 'V'; //'DNAV';
+static const int DT_NAVMESH_VERSION = 6;
+
+static const int DT_NAVMESH_STATE_MAGIC = 'D'<<24 | 'N'<<16 | 'M'<<8 | 'S'; //'DNMS';
+static const int DT_NAVMESH_STATE_VERSION = 1;
+
+static const unsigned short DT_EXT_LINK = 0x8000;
+static const unsigned int DT_NULL_LINK = 0xffffffff;
+static const unsigned int DT_OFFMESH_CON_BIDIR = 1;
+
+static const int DT_MAX_AREAS = 64;
+
+static const int STATIC_SALT_BITS = 12;
+static const int STATIC_TILE_BITS = 21;
+static const int STATIC_POLY_BITS = 31;
+// we cannot have over 31 bits for either tile nor poly
+// without changing polyCount to use 64bits too.
+
+// Flags for addTile
+enum dtTileFlags
+{
+ DT_TILE_FREE_DATA = 0x01, // Navmesh owns the tile memory and should free it.
+};
+
+// Flags returned by findStraightPath().
+enum dtStraightPathFlags
+{
+ DT_STRAIGHTPATH_START = 0x01, // The vertex is the start position.
+ DT_STRAIGHTPATH_END = 0x02, // The vertex is the end position.
+ DT_STRAIGHTPATH_OFFMESH_CONNECTION = 0x04, // The vertex is start of an off-mesh link.
+};
+
+// Flags describing polygon properties.
+enum dtPolyTypes
+{
+ DT_POLYTYPE_GROUND = 0, // Regular ground polygons.
+ DT_POLYTYPE_OFFMESH_CONNECTION = 1, // Off-mesh connections.
+};
+
+enum dtStatus
+{
+ DT_FAILURE = 0, // Operation failed.
+ DT_FAILURE_DATA_MAGIC,
+ DT_FAILURE_DATA_VERSION,
+ DT_FAILURE_OUT_OF_MEMORY,
+ DT_SUCCESS, // Operation succeed.
+ DT_IN_PROGRESS, // Operation still in progress.
+};
+
+
+// Structure describing the navigation polygon data.
+struct dtPoly
+{
+ unsigned int firstLink; // Index to first link in linked list.
+ unsigned short verts[DT_VERTS_PER_POLYGON]; // Indices to vertices of the poly.
+ unsigned short neis[DT_VERTS_PER_POLYGON]; // Refs to neighbours of the poly.
+ unsigned short flags; // Flags (see dtPolyFlags).
+ unsigned char vertCount; // Number of vertices.
+ unsigned char areaAndtype; // Bit packed: Area ID of the polygon, and Polygon type, see dtPolyTypes..
+ inline void setArea(unsigned char a) { areaAndtype = (areaAndtype & 0xc0) | (a & 0x3f); }
+ inline void setType(unsigned char t) { areaAndtype = (areaAndtype & 0x3f) | (t << 6); }
+ inline unsigned char getArea() const { return areaAndtype & 0x3f; }
+ inline unsigned char getType() const { return areaAndtype >> 6; }
+};
+
+// Stucture describing polygon detail triangles.
+struct dtPolyDetail
+{
+ unsigned int vertBase; // Offset to detail vertex array.
+ unsigned int triBase; // Offset to detail triangle array.
+ unsigned char vertCount; // Number of vertices in the detail mesh.
+ unsigned char triCount; // Number of triangles.
+};
+
+// Stucture describing a link to another polygon.
+struct dtLink
+{
+ dtPolyRef ref; // Neighbour reference.
+ unsigned int next; // Index to next link.
+ unsigned char edge; // Index to polygon edge which owns this link.
+ unsigned char side; // If boundary link, defines on which side the link is.
+ unsigned char bmin, bmax; // If boundary link, defines the sub edge area.
+};
+
+struct dtBVNode
+{
+ unsigned short bmin[3], bmax[3]; // BVnode bounds
+ int i; // Index to item or if negative, escape index.
+};
+
+struct dtOffMeshConnection
+{
+ float pos[6]; // Both end point locations.
+ float rad; // Link connection radius.
+ unsigned short poly; // Poly Id
+ unsigned char flags; // Link flags
+ unsigned char side; // End point side.
+ unsigned int userId; // User ID to identify this connection.
+};
+
+struct dtMeshHeader
+{
+ int magic; // Magic number, used to identify the data.
+ int version; // Data version number.
+ int x, y; // Location of the time on the grid.
+ unsigned int userId; // User ID of the tile.
+ int polyCount; // Number of polygons in the tile.
+ int vertCount; // Number of vertices in the tile.
+ int maxLinkCount; // Number of allocated links.
+ int detailMeshCount; // Number of detail meshes.
+ int detailVertCount; // Number of detail vertices.
+ int detailTriCount; // Number of detail triangles.
+ int bvNodeCount; // Number of BVtree nodes.
+ int offMeshConCount; // Number of Off-Mesh links.
+ int offMeshBase; // Index to first polygon which is Off-Mesh link.
+ float walkableHeight; // Height of the agent.
+ float walkableRadius; // Radius of the agent
+ float walkableClimb; // Max climb height of the agent.
+ float bmin[3], bmax[3]; // Bounding box of the tile.
+ float bvQuantFactor; // BVtree quantization factor (world to bvnode coords)
+};
+
+struct dtMeshTile
+{
+ unsigned int salt; // Counter describing modifications to the tile.
+
+ unsigned int linksFreeList; // Index to next free link.
+ dtMeshHeader* header; // Pointer to tile header.
+ dtPoly* polys; // Pointer to the polygons (will be updated when tile is added).
+ float* verts; // Pointer to the vertices (will be updated when tile added).
+ dtLink* links; // Pointer to the links (will be updated when tile added).
+ dtPolyDetail* detailMeshes; // Pointer to detail meshes (will be updated when tile added).
+ float* detailVerts; // Pointer to detail vertices (will be updated when tile added).
+ unsigned char* detailTris; // Pointer to detail triangles (will be updated when tile added).
+ dtBVNode* bvTree; // Pointer to BVtree nodes (will be updated when tile added).
+ dtOffMeshConnection* offMeshCons; // Pointer to Off-Mesh links. (will be updated when tile added).
+
+ unsigned char* data; // Pointer to tile data.
+ int dataSize; // Size of the tile data.
+ int flags; // Tile flags, see dtTileFlags.
+ dtMeshTile* next; // Next free tile or, next tile in spatial grid.
+};
+
+struct dtNavMeshParams
+{
+ float orig[3]; // Origin of the nav mesh tile space.
+ float tileWidth, tileHeight; // Width and height of each tile.
+ int maxTiles; // Maximum number of tiles the navmesh can contain.
+ int maxPolys; // Maximum number of polygons each tile can contain.
+};
+
+
+class dtNavMesh
+{
+public:
+ dtNavMesh();
+ ~dtNavMesh();
+
+ // Initializes the nav mesh for tiled use.
+ // Params:
+ // params - (in) navmesh initialization params, see dtNavMeshParams.
+ // Returns: True if succeed, else false.
+ dtStatus init(const dtNavMeshParams* params);
+
+ // Initializes the nav mesh for single tile use.
+ // Params:
+ // data - (in) Data of the new tile mesh.
+ // dataSize - (in) Data size of the new tile mesh.
+ // flags - (in) Tile flags, see dtTileFlags.
+ // Returns: True if succeed, else false.
+ dtStatus init(unsigned char* data, const int dataSize, const int flags);
+
+ // Returns pointer to navmesh initialization params.
+ const dtNavMeshParams* getParams() const;
+
+ // Adds new tile into the navmesh.
+ // The add will fail if the data is in wrong format,
+ // there is not enough tiles left, or if there is a tile already at the location.
+ // Params:
+ // data - (in) Data of the new tile mesh.
+ // dataSize - (in) Data size of the new tile mesh.
+ // flags - (in) Tile flags, see dtTileFlags.
+ // lastRef - (in,optional) Last tile ref, the tile will be restored so that
+ // the reference (as well as poly references) will be the same. Default: 0.
+ // result - (out,optional) tile ref if the tile was succesfully added.
+ dtStatus addTile(unsigned char* data, int dataSize, int flags, dtTileRef lastRef, dtTileRef* result);
+
+ // Removes specified tile.
+ // Params:
+ // ref - (in) Reference to the tile to remove.
+ // data - (out) Data associated with deleted tile.
+ // dataSize - (out) Size of the data associated with deleted tile.
+ dtStatus removeTile(dtTileRef ref, unsigned char** data, int* dataSize);
+
+ // Calculates tile location based in input world position.
+ // Params:
+ // pos - (in) world position of the query.
+ // tx - (out) tile x location.
+ // ty - (out) tile y location.
+ void calcTileLoc(const float* pos, int* tx, int* ty) const;
+
+ // Returns pointer to tile at specified location.
+ // Params:
+ // x,y - (in) Location of the tile to get.
+ // Returns: pointer to tile if tile exists or 0 tile does not exists.
+ const dtMeshTile* getTileAt(int x, int y) const;
+
+ // Returns reference to tile at specified location.
+ // Params:
+ // x,y - (in) Location of the tile to get.
+ // Returns: reference to tile if tile exists or 0 tile does not exists.
+ dtTileRef getTileRefAt(int x, int y) const;
+
+ // Returns tile references of a tile based on tile pointer.
+ dtTileRef getTileRef(const dtMeshTile* tile) const;
+
+ // Returns tile based on references.
+ const dtMeshTile* getTileByRef(dtTileRef ref) const;
+
+ // Returns max number of tiles.
+ int getMaxTiles() const;
+
+ // Returns pointer to tile in the tile array.
+ // Params:
+ // i - (in) Index to the tile to retrieve, max index is getMaxTiles()-1.
+ // Returns: Pointer to specified tile.
+ const dtMeshTile* getTile(int i) const;
+
+ // Returns pointer to tile and polygon pointed by the polygon reference.
+ // Params:
+ // ref - (in) reference to a polygon.
+ // tile - (out) pointer to the tile containing the polygon.
+ // poly - (out) pointer to the polygon.
+ dtStatus getTileAndPolyByRef(const dtPolyRef ref, const dtMeshTile** tile, const dtPoly** poly) const;
+
+ // Returns pointer to tile and polygon pointed by the polygon reference.
+ // Note: this function does not check if 'ref' s valid, and is thus faster. Use only with valid refs!
+ // Params:
+ // ref - (in) reference to a polygon.
+ // tile - (out) pointer to the tile containing the polygon.
+ // poly - (out) pointer to the polygon.
+ void getTileAndPolyByRefUnsafe(const dtPolyRef ref, const dtMeshTile** tile, const dtPoly** poly) const;
+
+ // Returns true if polygon reference points to valid data.
+ bool isValidPolyRef(dtPolyRef ref) const;
+
+ // Returns base poly id for specified tile, polygon refs can be deducted from this.
+ dtPolyRef getPolyRefBase(const dtMeshTile* tile) const;
+
+ // Returns start and end location of an off-mesh link polygon.
+ // Params:
+ // prevRef - (in) ref to the polygon before the link (used to select direction).
+ // polyRef - (in) ref to the off-mesh link polygon.
+ // startPos[3] - (out) start point of the link.
+ // endPos[3] - (out) end point of the link.
+ // Returns: true if link is found.
+ dtStatus getOffMeshConnectionPolyEndPoints(dtPolyRef prevRef, dtPolyRef polyRef, float* startPos, float* endPos) const;
+
+ // Returns pointer to off-mesh connection based on polyref, or null if ref not valid.
+ const dtOffMeshConnection* getOffMeshConnectionByRef(dtPolyRef ref) const;
+
+ // Sets polygon flags.
+ dtStatus setPolyFlags(dtPolyRef ref, unsigned short flags);
+
+ // Return polygon flags.
+ dtStatus getPolyFlags(dtPolyRef ref, unsigned short* resultFlags) const;
+
+ // Set polygon type.
+ dtStatus setPolyArea(dtPolyRef ref, unsigned char area);
+
+ // Return polygon area type.
+ dtStatus getPolyArea(dtPolyRef ref, unsigned char* resultArea) const;
+
+
+ // Returns number of bytes required to store tile state.
+ int getTileStateSize(const dtMeshTile* tile) const;
+
+ // Stores tile state to buffer.
+ dtStatus storeTileState(const dtMeshTile* tile, unsigned char* data, const int maxDataSize) const;
+
+ // Restores tile state.
+ dtStatus restoreTileState(dtMeshTile* tile, const unsigned char* data, const int maxDataSize);
+
+
+ // Encodes a tile id.
+ inline dtPolyRef encodePolyId(unsigned int salt, unsigned int it, unsigned int ip) const
+ {
+ return ((dtPolyRef)salt << (m_polyBits+m_tileBits)) | ((dtPolyRef)it << m_polyBits) | (dtPolyRef)ip;
+ }
+
+ // Decodes a tile id.
+ inline void decodePolyId(dtPolyRef ref, unsigned int& salt, unsigned int& it, unsigned int& ip) const
+ {
+ const dtPolyRef saltMask = ((dtPolyRef)1<<m_saltBits)-1;
+ const dtPolyRef tileMask = ((dtPolyRef)1<<m_tileBits)-1;
+ const dtPolyRef polyMask = ((dtPolyRef)1<<m_polyBits)-1;
+ salt = (unsigned int)((ref >> (m_polyBits+m_tileBits)) & saltMask);
+ it = (unsigned int)((ref >> m_polyBits) & tileMask);
+ ip = (unsigned int)(ref & polyMask);
+ }
+
+ // Decodes a tile salt.
+ inline unsigned int decodePolyIdSalt(dtPolyRef ref) const
+ {
+ const dtPolyRef saltMask = ((dtPolyRef)1<<m_saltBits)-1;
+ return (unsigned int)((ref >> (m_polyBits+m_tileBits)) & saltMask);
+ }
+
+ // Decodes a tile id.
+ inline unsigned int decodePolyIdTile(dtPolyRef ref) const
+ {
+ const dtPolyRef tileMask = ((dtPolyRef)1<<m_tileBits)-1;
+ return (unsigned int)((ref >> m_polyBits) & tileMask);
+ }
+
+ // Decodes a poly id.
+ inline unsigned int decodePolyIdPoly(dtPolyRef ref) const
+ {
+ const dtPolyRef polyMask = ((dtPolyRef)1<<m_polyBits)-1;
+ return (unsigned int)(ref & polyMask);
+ }
+
+private:
+
+ // Returns pointer to tile in the tile array.
+ dtMeshTile* getTile(int i);
+
+ // Returns neighbour tile based on side.
+ dtMeshTile* getNeighbourTileAt(int x, int y, int side) const;
+ // Returns all polygons in neighbour tile based on portal defined by the segment.
+ int findConnectingPolys(const float* va, const float* vb,
+ const dtMeshTile* tile, int side,
+ dtPolyRef* con, float* conarea, int maxcon) const;
+
+ // Builds internal polygons links for a tile.
+ void connectIntLinks(dtMeshTile* tile);
+ // Builds internal polygons links for a tile.
+ void connectIntOffMeshLinks(dtMeshTile* tile);
+
+ // Builds external polygon links for a tile.
+ void connectExtLinks(dtMeshTile* tile, dtMeshTile* target, int side);
+ // Builds external polygon links for a tile.
+ void connectExtOffMeshLinks(dtMeshTile* tile, dtMeshTile* target, int side);
+
+ // Removes external links at specified side.
+ void unconnectExtLinks(dtMeshTile* tile, int side);
+
+
+ // TODO: These methods are duplicates from dtNavMeshQuery, but are needed for off-mesh connection finding.
+
+ // Queries polygons within a tile.
+ int queryPolygonsInTile(const dtMeshTile* tile, const float* qmin, const float* qmax,
+ dtPolyRef* polys, const int maxPolys) const;
+ // Find nearest polygon within a tile.
+ dtPolyRef findNearestPolyInTile(const dtMeshTile* tile, const float* center,
+ const float* extents, float* nearestPt) const;
+ // Returns closest point on polygon.
+ dtStatus closestPointOnPolyInTile(const dtMeshTile* tile, unsigned int ip,
+ const float* pos, float* closest) const;
+
+ dtNavMeshParams m_params; // Current initialization params. TODO: do not store this info twice.
+ float m_orig[3]; // Origin of the tile (0,0)
+ float m_tileWidth, m_tileHeight; // Dimensions of each tile.
+ int m_maxTiles; // Max number of tiles.
+ int m_tileLutSize; // Tile hash lookup size (must be pot).
+ int m_tileLutMask; // Tile hash lookup mask.
+
+ dtMeshTile** m_posLookup; // Tile hash lookup.
+ dtMeshTile* m_nextFree; // Freelist of tiles.
+ dtMeshTile* m_tiles; // List of tiles.
+
+ unsigned int m_saltBits; // Number of salt bits in the tile ID.
+ unsigned int m_tileBits; // Number of tile bits in the tile ID.
+ unsigned int m_polyBits; // Number of poly bits in the tile ID.
+};
+
+// Helper function to allocate navmesh class using Detour allocator.
+dtNavMesh* dtAllocNavMesh();
+void dtFreeNavMesh(dtNavMesh* navmesh);
+
+#endif // DETOURNAVMESH_H
diff --git a/dep/recastnavigation/Detour/DetourNavMeshBuilder.cpp b/dep/recastnavigation/Detour/DetourNavMeshBuilder.cpp
new file mode 100644
index 00000000000..f64857160db
--- /dev/null
+++ b/dep/recastnavigation/Detour/DetourNavMeshBuilder.cpp
@@ -0,0 +1,717 @@
+//
+// Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
+//
+// This software is provided 'as-is', without any express or implied
+// warranty. In no event will the authors be held liable for any damages
+// arising from the use of this software.
+// Permission is granted to anyone to use this software for any purpose,
+// including commercial applications, and to alter it and redistribute it
+// freely, subject to the following restrictions:
+// 1. The origin of this software must not be misrepresented; you must not
+// claim that you wrote the original software. If you use this software
+// in a product, an acknowledgment in the product documentation would be
+// appreciated but is not required.
+// 2. Altered source versions must be plainly marked as such, and must not be
+// misrepresented as being the original software.
+// 3. This notice may not be removed or altered from any source distribution.
+//
+
+#include <math.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "DetourNavMesh.h"
+#include "DetourCommon.h"
+#include "DetourNavMeshBuilder.h"
+#include "DetourAlloc.h"
+#include "DetourAssert.h"
+
+static unsigned short MESH_NULL_IDX = 0xffff;
+
+
+struct BVItem
+{
+ unsigned short bmin[3];
+ unsigned short bmax[3];
+ int i;
+};
+
+static int compareItemX(const void* va, const void* vb)
+{
+ const BVItem* a = (const BVItem*)va;
+ const BVItem* b = (const BVItem*)vb;
+ if (a->bmin[0] < b->bmin[0])
+ return -1;
+ if (a->bmin[0] > b->bmin[0])
+ return 1;
+ return 0;
+}
+
+static int compareItemY(const void* va, const void* vb)
+{
+ const BVItem* a = (const BVItem*)va;
+ const BVItem* b = (const BVItem*)vb;
+ if (a->bmin[1] < b->bmin[1])
+ return -1;
+ if (a->bmin[1] > b->bmin[1])
+ return 1;
+ return 0;
+}
+
+static int compareItemZ(const void* va, const void* vb)
+{
+ const BVItem* a = (const BVItem*)va;
+ const BVItem* b = (const BVItem*)vb;
+ if (a->bmin[2] < b->bmin[2])
+ return -1;
+ if (a->bmin[2] > b->bmin[2])
+ return 1;
+ return 0;
+}
+
+static void calcExtends(BVItem* items, const int /*nitems*/, const int imin, const int imax,
+ unsigned short* bmin, unsigned short* bmax)
+{
+ bmin[0] = items[imin].bmin[0];
+ bmin[1] = items[imin].bmin[1];
+ bmin[2] = items[imin].bmin[2];
+
+ bmax[0] = items[imin].bmax[0];
+ bmax[1] = items[imin].bmax[1];
+ bmax[2] = items[imin].bmax[2];
+
+ for (int i = imin+1; i < imax; ++i)
+ {
+ const BVItem& it = items[i];
+ if (it.bmin[0] < bmin[0]) bmin[0] = it.bmin[0];
+ if (it.bmin[1] < bmin[1]) bmin[1] = it.bmin[1];
+ if (it.bmin[2] < bmin[2]) bmin[2] = it.bmin[2];
+
+ if (it.bmax[0] > bmax[0]) bmax[0] = it.bmax[0];
+ if (it.bmax[1] > bmax[1]) bmax[1] = it.bmax[1];
+ if (it.bmax[2] > bmax[2]) bmax[2] = it.bmax[2];
+ }
+}
+
+inline int longestAxis(unsigned short x, unsigned short y, unsigned short z)
+{
+ int axis = 0;
+ unsigned short maxVal = x;
+ if (y > maxVal)
+ {
+ axis = 1;
+ maxVal = y;
+ }
+ if (z > maxVal)
+ {
+ axis = 2;
+ maxVal = z;
+ }
+ return axis;
+}
+
+static void subdivide(BVItem* items, int nitems, int imin, int imax, int& curNode, dtBVNode* nodes)
+{
+ int inum = imax - imin;
+ int icur = curNode;
+
+ dtBVNode& node = nodes[curNode++];
+
+ if (inum == 1)
+ {
+ // Leaf
+ node.bmin[0] = items[imin].bmin[0];
+ node.bmin[1] = items[imin].bmin[1];
+ node.bmin[2] = items[imin].bmin[2];
+
+ node.bmax[0] = items[imin].bmax[0];
+ node.bmax[1] = items[imin].bmax[1];
+ node.bmax[2] = items[imin].bmax[2];
+
+ node.i = items[imin].i;
+ }
+ else
+ {
+ // Split
+ calcExtends(items, nitems, imin, imax, node.bmin, node.bmax);
+
+ int axis = longestAxis(node.bmax[0] - node.bmin[0],
+ node.bmax[1] - node.bmin[1],
+ node.bmax[2] - node.bmin[2]);
+
+ if (axis == 0)
+ {
+ // Sort along x-axis
+ qsort(items+imin, inum, sizeof(BVItem), compareItemX);
+ }
+ else if (axis == 1)
+ {
+ // Sort along y-axis
+ qsort(items+imin, inum, sizeof(BVItem), compareItemY);
+ }
+ else
+ {
+ // Sort along z-axis
+ qsort(items+imin, inum, sizeof(BVItem), compareItemZ);
+ }
+
+ int isplit = imin+inum/2;
+
+ // Left
+ subdivide(items, nitems, imin, isplit, curNode, nodes);
+ // Right
+ subdivide(items, nitems, isplit, imax, curNode, nodes);
+
+ int iescape = curNode - icur;
+ // Negative index means escape.
+ node.i = -iescape;
+ }
+}
+
+static int createBVTree(const unsigned short* verts, const int /*nverts*/,
+ const unsigned short* polys, const int npolys, const int nvp,
+ const float cs, const float ch,
+ const int /*nnodes*/, dtBVNode* nodes)
+{
+ // Build tree
+ BVItem* items = (BVItem*)dtAlloc(sizeof(BVItem)*npolys, DT_ALLOC_TEMP);
+ for (int i = 0; i < npolys; i++)
+ {
+ BVItem& it = items[i];
+ it.i = i;
+ // Calc polygon bounds.
+ const unsigned short* p = &polys[i*nvp*2];
+ it.bmin[0] = it.bmax[0] = verts[p[0]*3+0];
+ it.bmin[1] = it.bmax[1] = verts[p[0]*3+1];
+ it.bmin[2] = it.bmax[2] = verts[p[0]*3+2];
+
+ for (int j = 1; j < nvp; ++j)
+ {
+ if (p[j] == MESH_NULL_IDX) break;
+ unsigned short x = verts[p[j]*3+0];
+ unsigned short y = verts[p[j]*3+1];
+ unsigned short z = verts[p[j]*3+2];
+
+ if (x < it.bmin[0]) it.bmin[0] = x;
+ if (y < it.bmin[1]) it.bmin[1] = y;
+ if (z < it.bmin[2]) it.bmin[2] = z;
+
+ if (x > it.bmax[0]) it.bmax[0] = x;
+ if (y > it.bmax[1]) it.bmax[1] = y;
+ if (z > it.bmax[2]) it.bmax[2] = z;
+ }
+ // Remap y
+ it.bmin[1] = (unsigned short)floorf((float)it.bmin[1]*ch/cs);
+ it.bmax[1] = (unsigned short)ceilf((float)it.bmax[1]*ch/cs);
+ }
+
+ int curNode = 0;
+ subdivide(items, npolys, 0, npolys, curNode, nodes);
+
+ dtFree(items);
+
+ return curNode;
+}
+
+static unsigned char classifyOffMeshPoint(const float* pt, const float* bmin, const float* bmax)
+{
+ static const unsigned char XP = 1<<0;
+ static const unsigned char ZP = 1<<1;
+ static const unsigned char XM = 1<<2;
+ static const unsigned char ZM = 1<<3;
+
+ unsigned char outcode = 0;
+ outcode |= (pt[0] >= bmax[0]) ? XP : 0;
+ outcode |= (pt[2] >= bmax[2]) ? ZP : 0;
+ outcode |= (pt[0] < bmin[0]) ? XM : 0;
+ outcode |= (pt[2] < bmin[2]) ? ZM : 0;
+
+ switch (outcode)
+ {
+ case XP: return 0;
+ case XP|ZP: return 1;
+ case ZP: return 2;
+ case XM|ZP: return 3;
+ case XM: return 4;
+ case XM|ZM: return 5;
+ case ZM: return 6;
+ case XP|ZM: return 7;
+ };
+ return 0xff;
+}
+
+// TODO: Better error handling.
+
+bool dtCreateNavMeshData(dtNavMeshCreateParams* params, unsigned char** outData, int* outDataSize)
+{
+ if (params->nvp > DT_VERTS_PER_POLYGON)
+ return false;
+ if (params->vertCount >= 0xffff)
+ return false;
+ if (!params->vertCount || !params->verts)
+ return false;
+ if (!params->polyCount || !params->polys)
+ return false;
+ if (!params->detailMeshes || !params->detailVerts || !params->detailTris)
+ return false;
+
+ const int nvp = params->nvp;
+
+ // Classify off-mesh connection points. We store only the connections
+ // whose start point is inside the tile.
+ unsigned char* offMeshConClass = 0;
+ int storedOffMeshConCount = 0;
+ int offMeshConLinkCount = 0;
+
+ if (params->offMeshConCount > 0)
+ {
+ offMeshConClass = (unsigned char*)dtAlloc(sizeof(unsigned char)*params->offMeshConCount*2, DT_ALLOC_TEMP);
+ if (!offMeshConClass)
+ return false;
+
+ for (int i = 0; i < params->offMeshConCount; ++i)
+ {
+ offMeshConClass[i*2+0] = classifyOffMeshPoint(&params->offMeshConVerts[(i*2+0)*3], params->bmin, params->bmax);
+ offMeshConClass[i*2+1] = classifyOffMeshPoint(&params->offMeshConVerts[(i*2+1)*3], params->bmin, params->bmax);
+
+ // Cound how many links should be allocated for off-mesh connections.
+ if (offMeshConClass[i*2+0] == 0xff)
+ offMeshConLinkCount++;
+ if (offMeshConClass[i*2+1] == 0xff)
+ offMeshConLinkCount++;
+
+ if (offMeshConClass[i*2+0] == 0xff)
+ storedOffMeshConCount++;
+ }
+ }
+
+ // Off-mesh connectionss are stored as polygons, adjust values.
+ const int totPolyCount = params->polyCount + storedOffMeshConCount;
+ const int totVertCount = params->vertCount + storedOffMeshConCount*2;
+
+ // Find portal edges which are at tile borders.
+ int edgeCount = 0;
+ int portalCount = 0;
+ for (int i = 0; i < params->polyCount; ++i)
+ {
+ const unsigned short* p = &params->polys[i*2*nvp];
+ for (int j = 0; j < nvp; ++j)
+ {
+ if (p[j] == MESH_NULL_IDX) break;
+ int nj = j+1;
+ if (nj >= nvp || p[nj] == MESH_NULL_IDX) nj = 0;
+ const unsigned short* va = &params->verts[p[j]*3];
+ const unsigned short* vb = &params->verts[p[nj]*3];
+
+ edgeCount++;
+
+ if (params->tileSize > 0)
+ {
+ if (va[0] == params->tileSize && vb[0] == params->tileSize)
+ portalCount++; // x+
+ else if (va[2] == params->tileSize && vb[2] == params->tileSize)
+ portalCount++; // z+
+ else if (va[0] == 0 && vb[0] == 0)
+ portalCount++; // x-
+ else if (va[2] == 0 && vb[2] == 0)
+ portalCount++; // z-
+ }
+ }
+ }
+
+ const int maxLinkCount = edgeCount + portalCount*2 + offMeshConLinkCount*2;
+
+ // Find unique detail vertices.
+ int uniqueDetailVertCount = 0;
+ for (int i = 0; i < params->polyCount; ++i)
+ {
+ const unsigned short* p = &params->polys[i*nvp*2];
+ int ndv = params->detailMeshes[i*4+1];
+ int nv = 0;
+ for (int j = 0; j < nvp; ++j)
+ {
+ if (p[j] == MESH_NULL_IDX) break;
+ nv++;
+ }
+ ndv -= nv;
+ uniqueDetailVertCount += ndv;
+ }
+
+ // Calculate data size
+ const int headerSize = dtAlign4(sizeof(dtMeshHeader));
+ const int vertsSize = dtAlign4(sizeof(float)*3*totVertCount);
+ const int polysSize = dtAlign4(sizeof(dtPoly)*totPolyCount);
+ const int linksSize = dtAlign4(sizeof(dtLink)*maxLinkCount);
+ const int detailMeshesSize = dtAlign4(sizeof(dtPolyDetail)*params->polyCount);
+ const int detailVertsSize = dtAlign4(sizeof(float)*3*uniqueDetailVertCount);
+ const int detailTrisSize = dtAlign4(sizeof(unsigned char)*4*params->detailTriCount);
+ const int bvTreeSize = dtAlign4(sizeof(dtBVNode)*params->polyCount*2);
+ const int offMeshConsSize = dtAlign4(sizeof(dtOffMeshConnection)*storedOffMeshConCount);
+
+ const int dataSize = headerSize + vertsSize + polysSize + linksSize +
+ detailMeshesSize + detailVertsSize + detailTrisSize +
+ bvTreeSize + offMeshConsSize;
+
+ unsigned char* data = (unsigned char*)dtAlloc(sizeof(unsigned char)*dataSize, DT_ALLOC_PERM);
+ if (!data)
+ {
+ dtFree(offMeshConClass);
+ return false;
+ }
+ memset(data, 0, dataSize);
+
+ unsigned char* d = data;
+ dtMeshHeader* header = (dtMeshHeader*)d; d += headerSize;
+ float* navVerts = (float*)d; d += vertsSize;
+ dtPoly* navPolys = (dtPoly*)d; d += polysSize;
+ d += linksSize;
+ dtPolyDetail* navDMeshes = (dtPolyDetail*)d; d += detailMeshesSize;
+ float* navDVerts = (float*)d; d += detailVertsSize;
+ unsigned char* navDTris = (unsigned char*)d; d += detailTrisSize;
+ dtBVNode* navBvtree = (dtBVNode*)d; d += bvTreeSize;
+ dtOffMeshConnection* offMeshCons = (dtOffMeshConnection*)d; d += offMeshConsSize;
+
+
+ // Store header
+ header->magic = DT_NAVMESH_MAGIC;
+ header->version = DT_NAVMESH_VERSION;
+ header->x = params->tileX;
+ header->y = params->tileY;
+ header->userId = params->userId;
+ header->polyCount = totPolyCount;
+ header->vertCount = totVertCount;
+ header->maxLinkCount = maxLinkCount;
+ dtVcopy(header->bmin, params->bmin);
+ dtVcopy(header->bmax, params->bmax);
+ header->detailMeshCount = params->polyCount;
+ header->detailVertCount = uniqueDetailVertCount;
+ header->detailTriCount = params->detailTriCount;
+ header->bvQuantFactor = 1.0f / params->cs;
+ header->offMeshBase = params->polyCount;
+ header->walkableHeight = params->walkableHeight;
+ header->walkableRadius = params->walkableRadius;
+ header->walkableClimb = params->walkableClimb;
+ header->offMeshConCount = storedOffMeshConCount;
+ header->bvNodeCount = params->polyCount*2;
+
+ const int offMeshVertsBase = params->vertCount;
+ const int offMeshPolyBase = params->polyCount;
+
+ // Store vertices
+ // Mesh vertices
+ for (int i = 0; i < params->vertCount; ++i)
+ {
+ const unsigned short* iv = &params->verts[i*3];
+ float* v = &navVerts[i*3];
+ v[0] = params->bmin[0] + iv[0] * params->cs;
+ v[1] = params->bmin[1] + iv[1] * params->ch;
+ v[2] = params->bmin[2] + iv[2] * params->cs;
+ }
+ // Off-mesh link vertices.
+ int n = 0;
+ for (int i = 0; i < params->offMeshConCount; ++i)
+ {
+ // Only store connections which start from this tile.
+ if (offMeshConClass[i*2+0] == 0xff)
+ {
+ const float* linkv = &params->offMeshConVerts[i*2*3];
+ float* v = &navVerts[(offMeshVertsBase + n*2)*3];
+ dtVcopy(&v[0], &linkv[0]);
+ dtVcopy(&v[3], &linkv[3]);
+ n++;
+ }
+ }
+
+ // Store polygons
+ // Mesh polys
+ const unsigned short* src = params->polys;
+ for (int i = 0; i < params->polyCount; ++i)
+ {
+ dtPoly* p = &navPolys[i];
+ p->vertCount = 0;
+ p->flags = params->polyFlags[i];
+ p->setArea(params->polyAreas[i]);
+ p->setType(DT_POLYTYPE_GROUND);
+ for (int j = 0; j < nvp; ++j)
+ {
+ if (src[j] == MESH_NULL_IDX) break;
+ p->verts[j] = src[j];
+ p->neis[j] = (src[nvp+j]+1) & 0xffff;
+ p->vertCount++;
+ }
+ src += nvp*2;
+ }
+ // Off-mesh connection vertices.
+ n = 0;
+ for (int i = 0; i < params->offMeshConCount; ++i)
+ {
+ // Only store connections which start from this tile.
+ if (offMeshConClass[i*2+0] == 0xff)
+ {
+ dtPoly* p = &navPolys[offMeshPolyBase+n];
+ p->vertCount = 2;
+ p->verts[0] = (unsigned short)(offMeshVertsBase + n*2+0);
+ p->verts[1] = (unsigned short)(offMeshVertsBase + n*2+1);
+ p->flags = params->offMeshConFlags[i];
+ p->setArea(params->offMeshConAreas[i]);
+ p->setType(DT_POLYTYPE_OFFMESH_CONNECTION);
+ n++;
+ }
+ }
+
+ // Store portal edges.
+ if (params->tileSize > 0)
+ {
+ for (int i = 0; i < params->polyCount; ++i)
+ {
+ dtPoly* poly = &navPolys[i];
+ for (int j = 0; j < poly->vertCount; ++j)
+ {
+ int nj = j+1;
+ if (nj >= poly->vertCount) nj = 0;
+
+ const unsigned short* va = &params->verts[poly->verts[j]*3];
+ const unsigned short* vb = &params->verts[poly->verts[nj]*3];
+
+ if (va[0] == params->tileSize && vb[0] == params->tileSize) // x+
+ poly->neis[j] = DT_EXT_LINK | 0;
+ else if (va[2] == params->tileSize && vb[2] == params->tileSize) // z+
+ poly->neis[j] = DT_EXT_LINK | 2;
+ else if (va[0] == 0 && vb[0] == 0) // x-
+ poly->neis[j] = DT_EXT_LINK | 4;
+ else if (va[2] == 0 && vb[2] == 0) // z-
+ poly->neis[j] = DT_EXT_LINK | 6;
+ }
+ }
+ }
+
+ // Store detail meshes and vertices.
+ // The nav polygon vertices are stored as the first vertices on each mesh.
+ // We compress the mesh data by skipping them and using the navmesh coordinates.
+ unsigned short vbase = 0;
+ for (int i = 0; i < params->polyCount; ++i)
+ {
+ dtPolyDetail& dtl = navDMeshes[i];
+ const int vb = (int)params->detailMeshes[i*4+0];
+ const int ndv = (int)params->detailMeshes[i*4+1];
+ const int nv = navPolys[i].vertCount;
+ dtl.vertBase = (unsigned int)vbase;
+ dtl.vertCount = (unsigned char)(ndv-nv);
+ dtl.triBase = (unsigned int)params->detailMeshes[i*4+2];
+ dtl.triCount = (unsigned char)params->detailMeshes[i*4+3];
+ // Copy vertices except the first 'nv' verts which are equal to nav poly verts.
+ if (ndv-nv)
+ {
+ memcpy(&navDVerts[vbase*3], &params->detailVerts[(vb+nv)*3], sizeof(float)*3*(ndv-nv));
+ vbase += (unsigned short)(ndv-nv);
+ }
+ }
+ // Store triangles.
+ memcpy(navDTris, params->detailTris, sizeof(unsigned char)*4*params->detailTriCount);
+
+ // Store and create BVtree.
+ // TODO: take detail mesh into account! use byte per bbox extent?
+ createBVTree(params->verts, params->vertCount, params->polys, params->polyCount,
+ nvp, params->cs, params->ch, params->polyCount*2, navBvtree);
+
+ // Store Off-Mesh connections.
+ n = 0;
+ for (int i = 0; i < params->offMeshConCount; ++i)
+ {
+ // Only store connections which start from this tile.
+ if (offMeshConClass[i*2+0] == 0xff)
+ {
+ dtOffMeshConnection* con = &offMeshCons[n];
+ con->poly = (unsigned short)(offMeshPolyBase + n);
+ // Copy connection end-points.
+ const float* endPts = &params->offMeshConVerts[i*2*3];
+ dtVcopy(&con->pos[0], &endPts[0]);
+ dtVcopy(&con->pos[3], &endPts[3]);
+ con->rad = params->offMeshConRad[i];
+ con->flags = params->offMeshConDir[i] ? DT_OFFMESH_CON_BIDIR : 0;
+ con->side = offMeshConClass[i*2+1];
+ if (params->offMeshConUserID)
+ con->userId = params->offMeshConUserID[i];
+ n++;
+ }
+ }
+
+ dtFree(offMeshConClass);
+
+ *outData = data;
+ *outDataSize = dataSize;
+
+ return true;
+}
+
+inline void swapByte(unsigned char* a, unsigned char* b)
+{
+ unsigned char tmp = *a;
+ *a = *b;
+ *b = tmp;
+}
+
+inline void swapEndian(unsigned short* v)
+{
+ unsigned char* x = (unsigned char*)v;
+ swapByte(x+0, x+1);
+}
+
+inline void swapEndian(short* v)
+{
+ unsigned char* x = (unsigned char*)v;
+ swapByte(x+0, x+1);
+}
+
+inline void swapEndian(unsigned int* v)
+{
+ unsigned char* x = (unsigned char*)v;
+ swapByte(x+0, x+3); swapByte(x+1, x+2);
+}
+
+inline void swapEndian(int* v)
+{
+ unsigned char* x = (unsigned char*)v;
+ swapByte(x+0, x+3); swapByte(x+1, x+2);
+}
+
+inline void swapEndian(float* v)
+{
+ unsigned char* x = (unsigned char*)v;
+ swapByte(x+0, x+3); swapByte(x+1, x+2);
+}
+
+bool dtNavMeshHeaderSwapEndian(unsigned char* data, const int /*dataSize*/)
+{
+ dtMeshHeader* header = (dtMeshHeader*)data;
+
+ int swappedMagic = DT_NAVMESH_MAGIC;
+ int swappedVersion = DT_NAVMESH_VERSION;
+ swapEndian(&swappedMagic);
+ swapEndian(&swappedVersion);
+
+ if ((header->magic != DT_NAVMESH_MAGIC || header->version != DT_NAVMESH_VERSION) &&
+ (header->magic != swappedMagic || header->version != swappedVersion))
+ {
+ return false;
+ }
+
+ swapEndian(&header->magic);
+ swapEndian(&header->version);
+ swapEndian(&header->x);
+ swapEndian(&header->y);
+ swapEndian(&header->userId);
+ swapEndian(&header->polyCount);
+ swapEndian(&header->vertCount);
+ swapEndian(&header->maxLinkCount);
+ swapEndian(&header->detailMeshCount);
+ swapEndian(&header->detailVertCount);
+ swapEndian(&header->detailTriCount);
+ swapEndian(&header->bvNodeCount);
+ swapEndian(&header->offMeshConCount);
+ swapEndian(&header->offMeshBase);
+ swapEndian(&header->walkableHeight);
+ swapEndian(&header->walkableRadius);
+ swapEndian(&header->walkableClimb);
+ swapEndian(&header->bmin[0]);
+ swapEndian(&header->bmin[1]);
+ swapEndian(&header->bmin[2]);
+ swapEndian(&header->bmax[0]);
+ swapEndian(&header->bmax[1]);
+ swapEndian(&header->bmax[2]);
+ swapEndian(&header->bvQuantFactor);
+
+ // Freelist index and pointers are updated when tile is added, no need to swap.
+
+ return true;
+}
+
+bool dtNavMeshDataSwapEndian(unsigned char* data, const int /*dataSize*/)
+{
+ // Make sure the data is in right format.
+ dtMeshHeader* header = (dtMeshHeader*)data;
+ if (header->magic != DT_NAVMESH_MAGIC)
+ return false;
+ if (header->version != DT_NAVMESH_VERSION)
+ return false;
+
+ // Patch header pointers.
+ const int headerSize = dtAlign4(sizeof(dtMeshHeader));
+ const int vertsSize = dtAlign4(sizeof(float)*3*header->vertCount);
+ const int polysSize = dtAlign4(sizeof(dtPoly)*header->polyCount);
+ const int linksSize = dtAlign4(sizeof(dtLink)*(header->maxLinkCount));
+ const int detailMeshesSize = dtAlign4(sizeof(dtPolyDetail)*header->detailMeshCount);
+ const int detailVertsSize = dtAlign4(sizeof(float)*3*header->detailVertCount);
+ const int detailTrisSize = dtAlign4(sizeof(unsigned char)*4*header->detailTriCount);
+ const int bvtreeSize = dtAlign4(sizeof(dtBVNode)*header->bvNodeCount);
+ const int offMeshLinksSize = dtAlign4(sizeof(dtOffMeshConnection)*header->offMeshConCount);
+
+ unsigned char* d = data + headerSize;
+ float* verts = (float*)d; d += vertsSize;
+ dtPoly* polys = (dtPoly*)d; d += polysSize;
+ /*dtLink* links = (dtLink*)d;*/ d += linksSize;
+ dtPolyDetail* detailMeshes = (dtPolyDetail*)d; d += detailMeshesSize;
+ float* detailVerts = (float*)d; d += detailVertsSize;
+ /*unsigned char* detailTris = (unsigned char*)d;*/ d += detailTrisSize;
+ dtBVNode* bvTree = (dtBVNode*)d; d += bvtreeSize;
+ dtOffMeshConnection* offMeshCons = (dtOffMeshConnection*)d; d += offMeshLinksSize;
+
+ // Vertices
+ for (int i = 0; i < header->vertCount*3; ++i)
+ {
+ swapEndian(&verts[i]);
+ }
+
+ // Polys
+ for (int i = 0; i < header->polyCount; ++i)
+ {
+ dtPoly* p = &polys[i];
+ // poly->firstLink is update when tile is added, no need to swap.
+ for (int j = 0; j < DT_VERTS_PER_POLYGON; ++j)
+ {
+ swapEndian(&p->verts[j]);
+ swapEndian(&p->neis[j]);
+ }
+ swapEndian(&p->flags);
+ }
+
+ // Links are rebuild when tile is added, no need to swap.
+
+ // Detail meshes
+ for (int i = 0; i < header->detailMeshCount; ++i)
+ {
+ dtPolyDetail* pd = &detailMeshes[i];
+ swapEndian(&pd->vertBase);
+ swapEndian(&pd->triBase);
+ }
+
+ // Detail verts
+ for (int i = 0; i < header->detailVertCount*3; ++i)
+ {
+ swapEndian(&detailVerts[i]);
+ }
+
+ // BV-tree
+ for (int i = 0; i < header->bvNodeCount; ++i)
+ {
+ dtBVNode* node = &bvTree[i];
+ for (int j = 0; j < 3; ++j)
+ {
+ swapEndian(&node->bmin[j]);
+ swapEndian(&node->bmax[j]);
+ }
+ swapEndian(&node->i);
+ }
+
+ // Off-mesh Connections.
+ for (int i = 0; i < header->offMeshConCount; ++i)
+ {
+ dtOffMeshConnection* con = &offMeshCons[i];
+ for (int j = 0; j < 6; ++j)
+ swapEndian(&con->pos[j]);
+ swapEndian(&con->rad);
+ swapEndian(&con->poly);
+ }
+
+ return true;
+}
diff --git a/dep/recastnavigation/Detour/DetourNavMeshBuilder.h b/dep/recastnavigation/Detour/DetourNavMeshBuilder.h
new file mode 100644
index 00000000000..8d8ef2e6546
--- /dev/null
+++ b/dep/recastnavigation/Detour/DetourNavMeshBuilder.h
@@ -0,0 +1,77 @@
+//
+// Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
+//
+// This software is provided 'as-is', without any express or implied
+// warranty. In no event will the authors be held liable for any damages
+// arising from the use of this software.
+// Permission is granted to anyone to use this software for any purpose,
+// including commercial applications, and to alter it and redistribute it
+// freely, subject to the following restrictions:
+// 1. The origin of this software must not be misrepresented; you must not
+// claim that you wrote the original software. If you use this software
+// in a product, an acknowledgment in the product documentation would be
+// appreciated but is not required.
+// 2. Altered source versions must be plainly marked as such, and must not be
+// misrepresented as being the original software.
+// 3. This notice may not be removed or altered from any source distribution.
+//
+
+#ifndef DETOURNAVMESHBUILDER_H
+#define DETOURNAVMESHBUILDER_H
+
+#include "DetourAlloc.h"
+
+
+// The units of the parameters are specified in parenthesis as follows:
+// (vx) voxels, (wu) world units
+struct dtNavMeshCreateParams
+{
+ // Navmesh vertices.
+ const unsigned short* verts; // Array of vertices, each vertex has 3 components. (vx).
+ int vertCount; // Vertex count
+ // Navmesh polygons
+ const unsigned short* polys; // Array of polygons, uses same format as rcPolyMesh.
+ const unsigned short* polyFlags; // Array of flags per polygon.
+ const unsigned char* polyAreas; // Array of area ids per polygon.
+ int polyCount; // Number of polygons
+ int nvp; // Number of verts per polygon.
+ // Navmesh Detail
+ const unsigned int* detailMeshes; // Detail meshes, uses same format as rcPolyMeshDetail.
+ const float* detailVerts; // Detail mesh vertices, uses same format as rcPolyMeshDetail (wu).
+ int detailVertsCount; // Total number of detail vertices
+ const unsigned char* detailTris; // Array of detail tris per detail mesh.
+ int detailTriCount; // Total number of detail triangles.
+ // Off-Mesh Connections.
+ const float* offMeshConVerts; // Off-mesh connection vertices (wu).
+ const float* offMeshConRad; // Off-mesh connection radii (wu).
+ const unsigned short* offMeshConFlags; // Off-mesh connection flags.
+ const unsigned char* offMeshConAreas; // Off-mesh connection area ids.
+ const unsigned char* offMeshConDir; // Off-mesh connection direction flags (1 = bidir, 0 = oneway).
+ const unsigned int* offMeshConUserID; // Off-mesh connection user id (optional).
+ int offMeshConCount; // Number of off-mesh connections
+ // Tile location
+ unsigned int userId; // User ID bound to the tile.
+ int tileX, tileY; // Tile location (tile coords).
+ float bmin[3], bmax[3]; // Tile bounds (wu).
+ // Settings
+ float walkableHeight; // Agent height (wu).
+ float walkableRadius; // Agent radius (wu).
+ float walkableClimb; // Agent max climb (wu).
+ float cs; // Cell size (xz) (wu).
+ float ch; // Cell height (y) (wu).
+ int tileSize; // Tile size (width & height) (vx).
+};
+
+// Build navmesh data from given input data.
+bool dtCreateNavMeshData(dtNavMeshCreateParams* params, unsigned char** outData, int* outDataSize);
+
+// Swaps endianess of navmesh header.
+bool dtNavMeshHeaderSwapEndian(unsigned char* data, const int dataSize);
+
+// Swaps endianess of the navmesh data. This function assumes that the header is in correct
+// endianess already. Call dtNavMeshHeaderSwapEndian() first on the data if the data is
+// assumed to be in wrong endianess to start with. If converting from native endianess to foreign,
+// call dtNavMeshHeaderSwapEndian() after the data has been swapped.
+bool dtNavMeshDataSwapEndian(unsigned char* data, const int dataSize);
+
+#endif // DETOURNAVMESHBUILDER_H
diff --git a/dep/recastnavigation/Detour/DetourNavMeshQuery.cpp b/dep/recastnavigation/Detour/DetourNavMeshQuery.cpp
new file mode 100644
index 00000000000..6a6eb94b6d4
--- /dev/null
+++ b/dep/recastnavigation/Detour/DetourNavMeshQuery.cpp
@@ -0,0 +1,2564 @@
+//
+// Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
+//
+// This software is provided 'as-is', without any express or implied
+// warranty. In no event will the authors be held liable for any damages
+// arising from the use of this software.
+// Permission is granted to anyone to use this software for any purpose,
+// including commercial applications, and to alter it and redistribute it
+// freely, subject to the following restrictions:
+// 1. The origin of this software must not be misrepresented; you must not
+// claim that you wrote the original software. If you use this software
+// in a product, an acknowledgment in the product documentation would be
+// appreciated but is not required.
+// 2. Altered source versions must be plainly marked as such, and must not be
+// misrepresented as being the original software.
+// 3. This notice may not be removed or altered from any source distribution.
+//
+
+#include <math.h>
+#include <float.h>
+#include <string.h>
+#include "DetourNavMeshQuery.h"
+#include "DetourNavMesh.h"
+#include "DetourNode.h"
+#include "DetourCommon.h"
+#include "DetourAlloc.h"
+#include "DetourAssert.h"
+#include <new>
+
+
+dtQueryFilter::dtQueryFilter() :
+ m_includeFlags(0xffff),
+ m_excludeFlags(0)
+{
+ for (int i = 0; i < DT_MAX_AREAS; ++i)
+ m_areaCost[i] = 1.0f;
+}
+
+#ifdef DT_VIRTUAL_QUERYFILTER
+bool dtQueryFilter::passFilter(const dtPolyRef /*ref*/,
+ const dtMeshTile* /*tile*/,
+ const dtPoly* poly) const
+{
+ return (poly->flags & m_includeFlags) != 0 && (poly->flags & m_excludeFlags) == 0;
+}
+
+float dtQueryFilter::getCost(const float* pa, const float* pb,
+ const dtPolyRef /*prevRef*/, const dtMeshTile* /*prevTile*/, const dtPoly* /*prevPoly*/,
+ const dtPolyRef /*curRef*/, const dtMeshTile* /*curTile*/, const dtPoly* curPoly,
+ const dtPolyRef /*nextRef*/, const dtMeshTile* /*nextTile*/, const dtPoly* /*nextPoly*/) const
+{
+ return dtVdist(pa, pb) * m_areaCost[curPoly->area];
+}
+#else
+inline bool dtQueryFilter::passFilter(const dtPolyRef /*ref*/,
+ const dtMeshTile* /*tile*/,
+ const dtPoly* poly) const
+{
+ return (poly->flags & m_includeFlags) != 0 && (poly->flags & m_excludeFlags) == 0;
+}
+
+inline float dtQueryFilter::getCost(const float* pa, const float* pb,
+ const dtPolyRef /*prevRef*/, const dtMeshTile* /*prevTile*/, const dtPoly* /*prevPoly*/,
+ const dtPolyRef /*curRef*/, const dtMeshTile* /*curTile*/, const dtPoly* curPoly,
+ const dtPolyRef /*nextRef*/, const dtMeshTile* /*nextTile*/, const dtPoly* /*nextPoly*/) const
+{
+ return dtVdist(pa, pb) * m_areaCost[curPoly->getArea()];
+}
+#endif
+
+static const float H_SCALE = 2.0f; // Search heuristic scale.
+
+
+dtNavMeshQuery* dtAllocNavMeshQuery()
+{
+ void* mem = dtAlloc(sizeof(dtNavMeshQuery), DT_ALLOC_PERM);
+ if (!mem) return 0;
+ return new(mem) dtNavMeshQuery;
+}
+
+void dtFreeNavMeshQuery(dtNavMeshQuery* navmesh)
+{
+ if (!navmesh) return;
+ navmesh->~dtNavMeshQuery();
+ dtFree(navmesh);
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////
+dtNavMeshQuery::dtNavMeshQuery() :
+ m_tinyNodePool(0),
+ m_nodePool(0),
+ m_openList(0)
+{
+ memset(&m_query, 0, sizeof(dtQueryData));
+}
+
+dtNavMeshQuery::~dtNavMeshQuery()
+{
+ if (m_tinyNodePool)
+ m_tinyNodePool->~dtNodePool();
+ if (m_nodePool)
+ m_nodePool->~dtNodePool();
+ if (m_openList)
+ m_openList->~dtNodeQueue();
+ dtFree(m_tinyNodePool);
+ dtFree(m_nodePool);
+ dtFree(m_openList);
+}
+
+dtStatus dtNavMeshQuery::init(const dtNavMesh* nav, const int maxNodes)
+{
+ m_nav = nav;
+
+ if (!m_nodePool || m_nodePool->getMaxNodes() < maxNodes)
+ {
+ if (m_nodePool)
+ {
+ m_nodePool->~dtNodePool();
+ dtFree(m_nodePool);
+ m_nodePool = 0;
+ }
+ m_nodePool = new (dtAlloc(sizeof(dtNodePool), DT_ALLOC_PERM)) dtNodePool(maxNodes, dtNextPow2(maxNodes/4));
+ if (!m_nodePool)
+ return DT_FAILURE_OUT_OF_MEMORY;
+ }
+ else
+ {
+ m_nodePool->clear();
+ }
+
+ if (!m_tinyNodePool)
+ {
+ m_tinyNodePool = new (dtAlloc(sizeof(dtNodePool), DT_ALLOC_PERM)) dtNodePool(64, 32);
+ if (!m_tinyNodePool)
+ return DT_FAILURE_OUT_OF_MEMORY;
+ }
+ else
+ {
+ m_tinyNodePool->clear();
+ }
+
+ // TODO: check the open list size too.
+ if (!m_openList || m_openList->getCapacity() < maxNodes)
+ {
+ if (m_openList)
+ {
+ m_openList->~dtNodeQueue();
+ dtFree(m_openList);
+ m_openList = 0;
+ }
+ m_openList = new (dtAlloc(sizeof(dtNodeQueue), DT_ALLOC_PERM)) dtNodeQueue(maxNodes);
+ if (!m_openList)
+ return DT_FAILURE_OUT_OF_MEMORY;
+ }
+ else
+ {
+ m_openList->clear();
+ }
+
+ return DT_SUCCESS;
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////
+dtStatus dtNavMeshQuery::closestPointOnPoly(dtPolyRef ref, const float* pos, float* closest) const
+{
+ dtAssert(m_nav);
+ const dtMeshTile* tile = 0;
+ const dtPoly* poly = 0;
+ if (m_nav->getTileAndPolyByRef(ref, &tile, &poly) != DT_SUCCESS)
+ return DT_FAILURE;
+ if (!tile) return DT_FAILURE;
+
+ if (poly->getType() == DT_POLYTYPE_OFFMESH_CONNECTION)
+ return DT_FAILURE;
+
+ if (closestPointOnPolyInTile(tile, poly, pos, closest) != DT_SUCCESS)
+ return DT_FAILURE;
+ return DT_SUCCESS;
+}
+
+dtStatus dtNavMeshQuery::closestPointOnPolyInTile(const dtMeshTile* tile, const dtPoly* poly,
+ const float* pos, float* closest) const
+{
+ const unsigned int ip = (unsigned int)(poly - tile->polys);
+ const dtPolyDetail* pd = &tile->detailMeshes[ip];
+
+ // TODO: The commented out version finds 'cylinder distance' instead of 'sphere distance' to the navmesh.
+ // Test and enable.
+/*
+ // Clamp point to be inside the polygon.
+ float verts[DT_VERTS_PER_POLYGON*3];
+ float edged[DT_VERTS_PER_POLYGON];
+ float edget[DT_VERTS_PER_POLYGON];
+ const int nv = poly->vertCount;
+ for (int i = 0; i < nv; ++i)
+ dtVcopy(&verts[i*3], &tile->verts[poly->verts[i]*3]);
+
+ dtVcopy(closest, pos);
+ if (!dtDistancePtPolyEdgesSqr(pos, verts, nv, edged, edget))
+ {
+ // Point is outside the polygon, dtClamp to nearest edge.
+ float dmin = FLT_MAX;
+ int imin = -1;
+ for (int i = 0; i < nv; ++i)
+ {
+ if (edged[i] < dmin)
+ {
+ dmin = edged[i];
+ imin = i;
+ }
+ }
+ const float* va = &verts[imin*3];
+ const float* vb = &verts[((imin+1)%nv)*3];
+ dtVlerp(closest, va, vb, edget[imin]);
+ }
+
+ // Find height at the location.
+ for (int j = 0; j < pd->triCount; ++j)
+ {
+ const unsigned char* t = &tile->detailTris[(pd->triBase+j)*4];
+ const float* v[3];
+ for (int k = 0; k < 3; ++k)
+ {
+ if (t[k] < poly->vertCount)
+ v[k] = &tile->verts[poly->verts[t[k]]*3];
+ else
+ v[k] = &tile->detailVerts[(pd->vertBase+(t[k]-poly->vertCount))*3];
+ }
+ float h;
+ if (dtClosestHeightPointTriangle(pos, v[0], v[1], v[2], h))
+ {
+ closest[1] = h;
+ break;
+ }
+ }
+*/
+ float closestDistSqr = FLT_MAX;
+ for (int j = 0; j < pd->triCount; ++j)
+ {
+ const unsigned char* t = &tile->detailTris[(pd->triBase+j)*4];
+ const float* v[3];
+ for (int k = 0; k < 3; ++k)
+ {
+ if (t[k] < poly->vertCount)
+ v[k] = &tile->verts[poly->verts[t[k]]*3];
+ else
+ v[k] = &tile->detailVerts[(pd->vertBase+(t[k]-poly->vertCount))*3];
+ }
+
+ float pt[3];
+ dtClosestPtPointTriangle(pt, pos, v[0], v[1], v[2]);
+ float d = dtVdistSqr(pos, pt);
+
+ if (d < closestDistSqr)
+ {
+ dtVcopy(closest, pt);
+ closestDistSqr = d;
+ }
+ }
+
+ return DT_SUCCESS;
+}
+
+dtStatus dtNavMeshQuery::closestPointOnPolyBoundary(dtPolyRef ref, const float* pos, float* closest) const
+{
+ dtAssert(m_nav);
+
+ const dtMeshTile* tile = 0;
+ const dtPoly* poly = 0;
+ if (m_nav->getTileAndPolyByRef(ref, &tile, &poly) != DT_SUCCESS)
+ return DT_FAILURE;
+
+ // Collect vertices.
+ float verts[DT_VERTS_PER_POLYGON*3];
+ float edged[DT_VERTS_PER_POLYGON];
+ float edget[DT_VERTS_PER_POLYGON];
+ int nv = 0;
+ for (int i = 0; i < (int)poly->vertCount; ++i)
+ {
+ dtVcopy(&verts[nv*3], &tile->verts[poly->verts[i]*3]);
+ nv++;
+ }
+
+ bool inside = dtDistancePtPolyEdgesSqr(pos, verts, nv, edged, edget);
+ if (inside)
+ {
+ // Point is inside the polygon, return the point.
+ dtVcopy(closest, pos);
+ }
+ else
+ {
+ // Point is outside the polygon, dtClamp to nearest edge.
+ float dmin = FLT_MAX;
+ int imin = -1;
+ for (int i = 0; i < nv; ++i)
+ {
+ if (edged[i] < dmin)
+ {
+ dmin = edged[i];
+ imin = i;
+ }
+ }
+ const float* va = &verts[imin*3];
+ const float* vb = &verts[((imin+1)%nv)*3];
+ dtVlerp(closest, va, vb, edget[imin]);
+ }
+
+ return DT_SUCCESS;
+}
+
+
+dtStatus dtNavMeshQuery::getPolyHeight(dtPolyRef ref, const float* pos, float* height) const
+{
+ dtAssert(m_nav);
+
+ const dtMeshTile* tile = 0;
+ const dtPoly* poly = 0;
+ if (m_nav->getTileAndPolyByRef(ref, &tile, &poly) != DT_SUCCESS)
+ return DT_FAILURE;
+
+ if (poly->getType() == DT_POLYTYPE_OFFMESH_CONNECTION)
+ {
+ const float* v0 = &tile->verts[poly->verts[0]*3];
+ const float* v1 = &tile->verts[poly->verts[1]*3];
+ const float d0 = dtVdist(pos, v0);
+ const float d1 = dtVdist(pos, v1);
+ const float u = d0 / (d0+d1);
+ if (height)
+ *height = v0[1] + (v1[1] - v0[1]) * u;
+ return DT_SUCCESS;
+ }
+ else
+ {
+ const unsigned int ip = (unsigned int)(poly - tile->polys);
+ const dtPolyDetail* pd = &tile->detailMeshes[ip];
+ for (int j = 0; j < pd->triCount; ++j)
+ {
+ const unsigned char* t = &tile->detailTris[(pd->triBase+j)*4];
+ const float* v[3];
+ for (int k = 0; k < 3; ++k)
+ {
+ if (t[k] < poly->vertCount)
+ v[k] = &tile->verts[poly->verts[t[k]]*3];
+ else
+ v[k] = &tile->detailVerts[(pd->vertBase+(t[k]-poly->vertCount))*3];
+ }
+ float h;
+ if (dtClosestHeightPointTriangle(pos, v[0], v[1], v[2], h))
+ {
+ if (height)
+ *height = h;
+ return DT_SUCCESS;
+ }
+ }
+ }
+
+ return DT_FAILURE;
+}
+
+dtStatus dtNavMeshQuery::findNearestPoly(const float* center, const float* extents,
+ const dtQueryFilter* filter,
+ dtPolyRef* nearestRef, float* nearestPt) const
+{
+ dtAssert(m_nav);
+
+ *nearestRef = 0;
+
+ // Get nearby polygons from proximity grid.
+ dtPolyRef polys[128];
+ int polyCount = 0;
+ if (queryPolygons(center, extents, filter, polys, &polyCount, 128) != DT_SUCCESS)
+ return DT_FAILURE;
+
+ // Find nearest polygon amongst the nearby polygons.
+ dtPolyRef nearest = 0;
+ float nearestDistanceSqr = FLT_MAX;
+ for (int i = 0; i < polyCount; ++i)
+ {
+ dtPolyRef ref = polys[i];
+ float closestPtPoly[3];
+ if (closestPointOnPoly(ref, center, closestPtPoly) != DT_SUCCESS)
+ continue;
+ float d = dtVdistSqr(center, closestPtPoly);
+ if (d < nearestDistanceSqr)
+ {
+ if (nearestPt)
+ dtVcopy(nearestPt, closestPtPoly);
+ nearestDistanceSqr = d;
+ nearest = ref;
+ }
+ }
+
+ if (nearestRef)
+ *nearestRef = nearest;
+
+ return DT_SUCCESS;
+}
+
+dtPolyRef dtNavMeshQuery::findNearestPolyInTile(const dtMeshTile* tile, const float* center, const float* extents,
+ const dtQueryFilter* filter, float* nearestPt) const
+{
+ dtAssert(m_nav);
+
+ float bmin[3], bmax[3];
+ dtVsub(bmin, center, extents);
+ dtVadd(bmax, center, extents);
+
+ // Get nearby polygons from proximity grid.
+ dtPolyRef polys[128];
+ int polyCount = queryPolygonsInTile(tile, bmin, bmax, filter, polys, 128);
+
+ // Find nearest polygon amongst the nearby polygons.
+ dtPolyRef nearest = 0;
+ float nearestDistanceSqr = FLT_MAX;
+ for (int i = 0; i < polyCount; ++i)
+ {
+ dtPolyRef ref = polys[i];
+ const dtPoly* poly = &tile->polys[m_nav->decodePolyIdPoly(ref)];
+ float closestPtPoly[3];
+ if (closestPointOnPolyInTile(tile, poly, center, closestPtPoly) != DT_SUCCESS)
+ continue;
+
+ float d = dtVdistSqr(center, closestPtPoly);
+ if (d < nearestDistanceSqr)
+ {
+ if (nearestPt)
+ dtVcopy(nearestPt, closestPtPoly);
+ nearestDistanceSqr = d;
+ nearest = ref;
+ }
+ }
+
+ return nearest;
+}
+
+int dtNavMeshQuery::queryPolygonsInTile(const dtMeshTile* tile, const float* qmin, const float* qmax,
+ const dtQueryFilter* filter,
+ dtPolyRef* polys, const int maxPolys) const
+{
+ dtAssert(m_nav);
+
+ if (tile->bvTree)
+ {
+ const dtBVNode* node = &tile->bvTree[0];
+ const dtBVNode* end = &tile->bvTree[tile->header->bvNodeCount];
+ const float* tbmin = tile->header->bmin;
+ const float* tbmax = tile->header->bmax;
+ const float qfac = tile->header->bvQuantFactor;
+
+ // Calculate quantized box
+ unsigned short bmin[3], bmax[3];
+ // dtClamp query box to world box.
+ float minx = dtClamp(qmin[0], tbmin[0], tbmax[0]) - tbmin[0];
+ float miny = dtClamp(qmin[1], tbmin[1], tbmax[1]) - tbmin[1];
+ float minz = dtClamp(qmin[2], tbmin[2], tbmax[2]) - tbmin[2];
+ float maxx = dtClamp(qmax[0], tbmin[0], tbmax[0]) - tbmin[0];
+ float maxy = dtClamp(qmax[1], tbmin[1], tbmax[1]) - tbmin[1];
+ float maxz = dtClamp(qmax[2], tbmin[2], tbmax[2]) - tbmin[2];
+ // Quantize
+ bmin[0] = (unsigned short)(qfac * minx) & 0xfffe;
+ bmin[1] = (unsigned short)(qfac * miny) & 0xfffe;
+ bmin[2] = (unsigned short)(qfac * minz) & 0xfffe;
+ bmax[0] = (unsigned short)(qfac * maxx + 1) | 1;
+ bmax[1] = (unsigned short)(qfac * maxy + 1) | 1;
+ bmax[2] = (unsigned short)(qfac * maxz + 1) | 1;
+
+ // Traverse tree
+ const dtPolyRef base = m_nav->getPolyRefBase(tile);
+ int n = 0;
+ while (node < end)
+ {
+ const bool overlap = dtOverlapQuantBounds(bmin, bmax, node->bmin, node->bmax);
+ const bool isLeafNode = node->i >= 0;
+
+ if (isLeafNode && overlap)
+ {
+ dtPolyRef ref = base | (dtPolyRef)node->i;
+ if (filter->passFilter(ref, tile, &tile->polys[node->i]))
+ {
+ if (n < maxPolys)
+ polys[n++] = ref;
+ }
+ }
+
+ if (overlap || isLeafNode)
+ node++;
+ else
+ {
+ const int escapeIndex = -node->i;
+ node += escapeIndex;
+ }
+ }
+
+ return n;
+ }
+ else
+ {
+ float bmin[3], bmax[3];
+ int n = 0;
+ const dtPolyRef base = m_nav->getPolyRefBase(tile);
+ for (int i = 0; i < tile->header->polyCount; ++i)
+ {
+ // Calc polygon bounds.
+ dtPoly* p = &tile->polys[i];
+ const float* v = &tile->verts[p->verts[0]*3];
+ dtVcopy(bmin, v);
+ dtVcopy(bmax, v);
+ for (int j = 1; j < p->vertCount; ++j)
+ {
+ v = &tile->verts[p->verts[j]*3];
+ dtVmin(bmin, v);
+ dtVmax(bmax, v);
+ }
+ if (dtOverlapBounds(qmin,qmax, bmin,bmax))
+ {
+ const dtPolyRef ref = base | (dtPolyRef)i;
+ if (filter->passFilter(ref, tile, p))
+ {
+ if (n < maxPolys)
+ polys[n++] = ref;
+ }
+ }
+ }
+ return n;
+ }
+}
+
+dtStatus dtNavMeshQuery::queryPolygons(const float* center, const float* extents,
+ const dtQueryFilter* filter,
+ dtPolyRef* polys, int* polyCount, const int maxPolys) const
+{
+ dtAssert(m_nav);
+
+ float bmin[3], bmax[3];
+ dtVsub(bmin, center, extents);
+ dtVadd(bmax, center, extents);
+
+ // Find tiles the query touches.
+ int minx, miny, maxx, maxy;
+ m_nav->calcTileLoc(bmin, &minx, &miny);
+ m_nav->calcTileLoc(bmax, &maxx, &maxy);
+
+ int n = 0;
+ for (int y = miny; y <= maxy; ++y)
+ {
+ for (int x = minx; x <= maxx; ++x)
+ {
+ const dtMeshTile* tile = m_nav->getTileAt(x,y);
+ if (!tile) continue;
+ n += queryPolygonsInTile(tile, bmin, bmax, filter, polys+n, maxPolys-n);
+ if (n >= maxPolys)
+ {
+ *polyCount = n;
+ return DT_SUCCESS;
+ }
+ }
+ }
+ *polyCount = n;
+
+ return DT_SUCCESS;
+}
+
+dtStatus dtNavMeshQuery::findPath(dtPolyRef startRef, dtPolyRef endRef,
+ const float* startPos, const float* endPos,
+ const dtQueryFilter* filter,
+ dtPolyRef* path, int* pathCount, const int maxPath) const
+{
+ dtAssert(m_nav);
+ dtAssert(m_nodePool);
+ dtAssert(m_openList);
+
+ *pathCount = 0;
+
+ if (!startRef || !endRef)
+ return DT_FAILURE;
+
+ if (!maxPath)
+ return DT_FAILURE;
+
+ // Validate input
+ if (!m_nav->isValidPolyRef(startRef) || !m_nav->isValidPolyRef(endRef))
+ return DT_FAILURE;
+
+ if (startRef == endRef)
+ {
+ path[0] = startRef;
+ *pathCount = 1;
+ return DT_SUCCESS;
+ }
+
+ m_nodePool->clear();
+ m_openList->clear();
+
+ dtNode* startNode = m_nodePool->getNode(startRef);
+ dtVcopy(startNode->pos, startPos);
+ startNode->pidx = 0;
+ startNode->cost = 0;
+ startNode->total = dtVdist(startPos, endPos) * H_SCALE;
+ startNode->id = startRef;
+ startNode->flags = DT_NODE_OPEN;
+ m_openList->push(startNode);
+
+ dtNode* lastBestNode = startNode;
+ float lastBestNodeCost = startNode->total;
+
+ while (!m_openList->empty())
+ {
+ // Remove node from open list and put it in closed list.
+ dtNode* bestNode = m_openList->pop();
+ bestNode->flags &= ~DT_NODE_OPEN;
+ bestNode->flags |= DT_NODE_CLOSED;
+
+ // Reached the goal, stop searching.
+ if (bestNode->id == endRef)
+ {
+ lastBestNode = bestNode;
+ break;
+ }
+
+ // Get current poly and tile.
+ // The API input has been cheked already, skip checking internal data.
+ const dtPolyRef bestRef = bestNode->id;
+ const dtMeshTile* bestTile = 0;
+ const dtPoly* bestPoly = 0;
+ m_nav->getTileAndPolyByRefUnsafe(bestRef, &bestTile, &bestPoly);
+
+ // Get parent poly and tile.
+ dtPolyRef parentRef = 0;
+ const dtMeshTile* parentTile = 0;
+ const dtPoly* parentPoly = 0;
+ if (bestNode->pidx)
+ parentRef = m_nodePool->getNodeAtIdx(bestNode->pidx)->id;
+ if (parentRef)
+ m_nav->getTileAndPolyByRefUnsafe(parentRef, &parentTile, &parentPoly);
+
+ for (unsigned int i = bestPoly->firstLink; i != DT_NULL_LINK; i = bestTile->links[i].next)
+ {
+ dtPolyRef neighbourRef = bestTile->links[i].ref;
+
+ // Skip invalid ids and do not expand back to where we came from.
+ if (!neighbourRef || neighbourRef == parentRef)
+ continue;
+
+ // Get neighbour poly and tile.
+ // The API input has been cheked already, skip checking internal data.
+ const dtMeshTile* neighbourTile = 0;
+ const dtPoly* neighbourPoly = 0;
+ m_nav->getTileAndPolyByRefUnsafe(neighbourRef, &neighbourTile, &neighbourPoly);
+
+ if (!filter->passFilter(neighbourRef, neighbourTile, neighbourPoly))
+ continue;
+
+ dtNode* neighbourNode = m_nodePool->getNode(neighbourRef);
+ if (!neighbourNode)
+ continue;
+
+ // If the node is visited the first time, calculate node position.
+ if (neighbourNode->flags == 0)
+ {
+ getEdgeMidPoint(bestRef, bestPoly, bestTile,
+ neighbourRef, neighbourPoly, neighbourTile,
+ neighbourNode->pos);
+ }
+
+ // Calculate cost and heuristic.
+ float cost = 0;
+ float heuristic = 0;
+
+ // Special case for last node.
+ if (neighbourRef == endRef)
+ {
+ // Cost
+ const float curCost = filter->getCost(bestNode->pos, neighbourNode->pos,
+ parentRef, parentTile, parentPoly,
+ bestRef, bestTile, bestPoly,
+ neighbourRef, neighbourTile, neighbourPoly);
+ const float endCost = filter->getCost(neighbourNode->pos, endPos,
+ bestRef, bestTile, bestPoly,
+ neighbourRef, neighbourTile, neighbourPoly,
+ 0, 0, 0);
+
+ cost = bestNode->cost + curCost + endCost;
+ heuristic = 0;
+ }
+ else
+ {
+ // Cost
+ const float curCost = filter->getCost(bestNode->pos, neighbourNode->pos,
+ parentRef, parentTile, parentPoly,
+ bestRef, bestTile, bestPoly,
+ neighbourRef, neighbourTile, neighbourPoly);
+ cost = bestNode->cost + curCost;
+ heuristic = dtVdist(neighbourNode->pos, endPos)*H_SCALE;
+ }
+
+ const float total = cost + heuristic;
+
+ // The node is already in open list and the new result is worse, skip.
+ if ((neighbourNode->flags & DT_NODE_OPEN) && total >= neighbourNode->total)
+ continue;
+ // The node is already visited and process, and the new result is worse, skip.
+ if ((neighbourNode->flags & DT_NODE_CLOSED) && total >= neighbourNode->total)
+ continue;
+
+ // Add or update the node.
+ neighbourNode->pidx = m_nodePool->getNodeIdx(bestNode);
+ neighbourNode->id = neighbourRef;
+ neighbourNode->flags &= ~DT_NODE_CLOSED;
+ neighbourNode->cost = cost;
+ neighbourNode->total = total;
+
+ if (neighbourNode->flags & DT_NODE_OPEN)
+ {
+ // Already in open, update node location.
+ m_openList->modify(neighbourNode);
+ }
+ else
+ {
+ // Put the node in open list.
+ neighbourNode->flags |= DT_NODE_OPEN;
+ m_openList->push(neighbourNode);
+ }
+
+ // Update nearest node to target so far.
+ if (heuristic < lastBestNodeCost)
+ {
+ lastBestNodeCost = heuristic;
+ lastBestNode = neighbourNode;
+ }
+ }
+ }
+
+ // Reverse the path.
+ dtNode* prev = 0;
+ dtNode* node = lastBestNode;
+ do
+ {
+ dtNode* next = m_nodePool->getNodeAtIdx(node->pidx);
+ node->pidx = m_nodePool->getNodeIdx(prev);
+ prev = node;
+ node = next;
+ }
+ while (node);
+
+ // Store path
+ node = prev;
+ int n = 0;
+ do
+ {
+ path[n++] = node->id;
+ node = m_nodePool->getNodeAtIdx(node->pidx);
+ }
+ while (node && n < maxPath);
+
+ *pathCount = n;
+
+ return DT_SUCCESS;
+}
+
+dtStatus dtNavMeshQuery::initSlicedFindPath(dtPolyRef startRef, dtPolyRef endRef,
+ const float* startPos, const float* endPos,
+ const dtQueryFilter* filter)
+{
+ dtAssert(m_nav);
+ dtAssert(m_nodePool);
+ dtAssert(m_openList);
+
+ // Init path state.
+ memset(&m_query, 0, sizeof(dtQueryData));
+ m_query.status = DT_FAILURE;
+ m_query.startRef = startRef;
+ m_query.endRef = endRef;
+ dtVcopy(m_query.startPos, startPos);
+ dtVcopy(m_query.endPos, endPos);
+ m_query.filter = filter;
+
+ if (!startRef || !endRef)
+ return DT_FAILURE;
+
+ // Validate input
+ if (!m_nav->isValidPolyRef(startRef) || !m_nav->isValidPolyRef(endRef))
+ return DT_FAILURE;
+
+ if (startRef == endRef)
+ {
+ m_query.status = DT_SUCCESS;
+ return DT_SUCCESS;
+ }
+
+ m_nodePool->clear();
+ m_openList->clear();
+
+ dtNode* startNode = m_nodePool->getNode(startRef);
+ dtVcopy(startNode->pos, startPos);
+ startNode->pidx = 0;
+ startNode->cost = 0;
+ startNode->total = dtVdist(startPos, endPos) * H_SCALE;
+ startNode->id = startRef;
+ startNode->flags = DT_NODE_OPEN;
+ m_openList->push(startNode);
+
+ m_query.status = DT_IN_PROGRESS;
+ m_query.lastBestNode = startNode;
+ m_query.lastBestNodeCost = startNode->total;
+
+ return m_query.status;
+}
+
+dtStatus dtNavMeshQuery::updateSlicedFindPath(const int maxIter)
+{
+ if (m_query.status!= DT_IN_PROGRESS)
+ return m_query.status;
+
+ // Make sure the request is still valid.
+ if (!m_nav->isValidPolyRef(m_query.startRef) || !m_nav->isValidPolyRef(m_query.endRef))
+ {
+ m_query.status = DT_FAILURE;
+ return DT_FAILURE;
+ }
+
+ int iter = 0;
+ while (iter < maxIter && !m_openList->empty())
+ {
+ iter++;
+
+ // Remove node from open list and put it in closed list.
+ dtNode* bestNode = m_openList->pop();
+ bestNode->flags &= ~DT_NODE_OPEN;
+ bestNode->flags |= DT_NODE_CLOSED;
+
+ // Reached the goal, stop searching.
+ if (bestNode->id == m_query.endRef)
+ {
+ m_query.lastBestNode = bestNode;
+ m_query.status = DT_SUCCESS;
+ return m_query.status;
+ }
+
+ // Get current poly and tile.
+ // The API input has been cheked already, skip checking internal data.
+ const dtPolyRef bestRef = bestNode->id;
+ const dtMeshTile* bestTile = 0;
+ const dtPoly* bestPoly = 0;
+ if (m_nav->getTileAndPolyByRef(bestRef, &bestTile, &bestPoly) != DT_SUCCESS)
+ {
+ // The polygon has disappeared during the sliced query, fail.
+ m_query.status = DT_FAILURE;
+ return m_query.status;
+ }
+
+ // Get parent poly and tile.
+ dtPolyRef parentRef = 0;
+ const dtMeshTile* parentTile = 0;
+ const dtPoly* parentPoly = 0;
+ if (bestNode->pidx)
+ parentRef = m_nodePool->getNodeAtIdx(bestNode->pidx)->id;
+ if (parentRef)
+ {
+ if (m_nav->getTileAndPolyByRef(parentRef, &parentTile, &parentPoly) != DT_SUCCESS)
+ {
+ // The polygon has disappeared during the sliced query, fail.
+ m_query.status = DT_FAILURE;
+ return m_query.status;
+ }
+ }
+
+ for (unsigned int i = bestPoly->firstLink; i != DT_NULL_LINK; i = bestTile->links[i].next)
+ {
+ dtPolyRef neighbourRef = bestTile->links[i].ref;
+
+ // Skip invalid ids and do not expand back to where we came from.
+ if (!neighbourRef || neighbourRef == parentRef)
+ continue;
+
+ // Get neighbour poly and tile.
+ // The API input has been cheked already, skip checking internal data.
+ const dtMeshTile* neighbourTile = 0;
+ const dtPoly* neighbourPoly = 0;
+ m_nav->getTileAndPolyByRefUnsafe(neighbourRef, &neighbourTile, &neighbourPoly);
+
+ if (!m_query.filter->passFilter(neighbourRef, neighbourTile, neighbourPoly))
+ continue;
+
+ dtNode* neighbourNode = m_nodePool->getNode(neighbourRef);
+ if (!neighbourNode)
+ continue;
+
+ // If the node is visited the first time, calculate node position.
+ if (neighbourNode->flags == 0)
+ {
+ getEdgeMidPoint(bestRef, bestPoly, bestTile,
+ neighbourRef, neighbourPoly, neighbourTile,
+ neighbourNode->pos);
+ }
+
+ // Calculate cost and heuristic.
+ float cost = 0;
+ float heuristic = 0;
+
+ // 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;
+ 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;
+ }
+
+ const float total = cost + heuristic;
+
+ // The node is already in open list and the new result is worse, skip.
+ if ((neighbourNode->flags & DT_NODE_OPEN) && total >= neighbourNode->total)
+ continue;
+ // The node is already visited and process, and the new result is worse, skip.
+ if ((neighbourNode->flags & DT_NODE_CLOSED) && total >= neighbourNode->total)
+ continue;
+
+ // Add or update the node.
+ neighbourNode->pidx = m_nodePool->getNodeIdx(bestNode);
+ neighbourNode->id = neighbourRef;
+ neighbourNode->flags &= ~DT_NODE_CLOSED;
+ neighbourNode->cost = cost;
+ neighbourNode->total = total;
+
+ if (neighbourNode->flags & DT_NODE_OPEN)
+ {
+ // Already in open, update node location.
+ m_openList->modify(neighbourNode);
+ }
+ else
+ {
+ // Put the node in open list.
+ neighbourNode->flags |= DT_NODE_OPEN;
+ m_openList->push(neighbourNode);
+ }
+
+ // Update nearest node to target so far.
+ if (heuristic < m_query.lastBestNodeCost)
+ {
+ m_query.lastBestNodeCost = heuristic;
+ m_query.lastBestNode = neighbourNode;
+ }
+ }
+ }
+
+ // Exhausted all nodes, but could not find path.
+ if (m_openList->empty())
+ m_query.status = DT_SUCCESS;
+
+ return m_query.status;
+}
+
+dtStatus dtNavMeshQuery::finalizeSlicedFindPath(dtPolyRef* path, int* pathCount, const int maxPath)
+{
+ *pathCount = 0;
+
+ if (m_query.status != DT_SUCCESS)
+ {
+ // Reset query.
+ memset(&m_query, 0, sizeof(dtQueryData));
+ return DT_FAILURE;
+ }
+
+ int n = 0;
+
+ if (m_query.startRef == m_query.endRef)
+ {
+ // Special case: the search starts and ends at same poly.
+ path[n++] = m_query.startRef;
+ }
+ else
+ {
+ // Reverse the path.
+ dtAssert(m_query.lastBestNode);
+ dtNode* prev = 0;
+ dtNode* node = m_query.lastBestNode;
+ do
+ {
+ dtNode* next = m_nodePool->getNodeAtIdx(node->pidx);
+ node->pidx = m_nodePool->getNodeIdx(prev);
+ prev = node;
+ node = next;
+ }
+ while (node);
+
+ // Store path
+ node = prev;
+ do
+ {
+ path[n++] = node->id;
+ node = m_nodePool->getNodeAtIdx(node->pidx);
+ }
+ while (node && n < maxPath);
+ }
+
+ // Reset query.
+ memset(&m_query, 0, sizeof(dtQueryData));
+
+ *pathCount = n;
+
+ return DT_SUCCESS;
+}
+
+dtStatus dtNavMeshQuery::finalizeSlicedFindPathPartial(const dtPolyRef* existing, const int existingSize,
+ dtPolyRef* path, int* pathCount, const int maxPath)
+{
+ *pathCount = 0;
+
+ if (existingSize == 0)
+ {
+ return DT_FAILURE;
+ }
+
+ if (m_query.status != DT_SUCCESS && m_query.status != DT_IN_PROGRESS)
+ {
+ // Reset query.
+ memset(&m_query, 0, sizeof(dtQueryData));
+ return DT_FAILURE;
+ }
+
+ int n = 0;
+
+ if (m_query.startRef == m_query.endRef)
+ {
+ // Special case: the search starts and ends at same poly.
+ path[n++] = m_query.startRef;
+ }
+ else
+ {
+ // Find furthest existing node that was visited.
+ dtNode* prev = 0;
+ dtNode* node = 0;
+ for (int i = existingSize-1; i >= 0; --i)
+ {
+ node = m_nodePool->findNode(existing[i]);
+ if (node)
+ break;
+ }
+
+ if (!node)
+ {
+ return DT_FAILURE;
+ }
+
+ // Reverse the path.
+ do
+ {
+ dtNode* next = m_nodePool->getNodeAtIdx(node->pidx);
+ node->pidx = m_nodePool->getNodeIdx(prev);
+ prev = node;
+ node = next;
+ }
+ while (node);
+
+ // Store path
+ node = prev;
+ do
+ {
+ path[n++] = node->id;
+ node = m_nodePool->getNodeAtIdx(node->pidx);
+ }
+ while (node && n < maxPath);
+ }
+
+ // Reset query.
+ memset(&m_query, 0, sizeof(dtQueryData));
+
+ *pathCount = n;
+
+ return DT_SUCCESS;
+}
+
+
+dtStatus dtNavMeshQuery::findStraightPath(const float* startPos, const float* endPos,
+ const dtPolyRef* path, const int pathSize,
+ float* straightPath, unsigned char* straightPathFlags, dtPolyRef* straightPathRefs,
+ int* straightPathCount, const int maxStraightPath) const
+{
+ dtAssert(m_nav);
+
+ *straightPathCount = 0;
+
+ if (!maxStraightPath)
+ return DT_FAILURE;
+
+ if (!path[0])
+ return DT_FAILURE;
+
+ int n = 0;
+
+ // TODO: Should this be callers responsibility?
+ float closestStartPos[3];
+ if (closestPointOnPolyBoundary(path[0], startPos, closestStartPos) != DT_SUCCESS)
+ return DT_FAILURE;
+
+ // Add start point.
+ dtVcopy(&straightPath[n*3], closestStartPos);
+ if (straightPathFlags)
+ straightPathFlags[n] = DT_STRAIGHTPATH_START;
+ if (straightPathRefs)
+ straightPathRefs[n] = path[0];
+ n++;
+ if (n >= maxStraightPath)
+ {
+ *straightPathCount = n;
+ return DT_SUCCESS;
+ }
+
+ float closestEndPos[3];
+ if (closestPointOnPolyBoundary(path[pathSize-1], endPos, closestEndPos) != DT_SUCCESS)
+ return DT_FAILURE;
+
+ if (pathSize > 1)
+ {
+ float portalApex[3], portalLeft[3], portalRight[3];
+ dtVcopy(portalApex, closestStartPos);
+ dtVcopy(portalLeft, portalApex);
+ dtVcopy(portalRight, portalApex);
+ int apexIndex = 0;
+ int leftIndex = 0;
+ int rightIndex = 0;
+
+ unsigned char leftPolyType = 0;
+ unsigned char rightPolyType = 0;
+
+ dtPolyRef leftPolyRef = path[0];
+ dtPolyRef rightPolyRef = path[0];
+
+ for (int i = 0; i < pathSize; ++i)
+ {
+ float left[3], right[3];
+ unsigned char fromType, toType;
+
+ if (i+1 < pathSize)
+ {
+ // Next portal.
+ if (getPortalPoints(path[i], path[i+1], left, right, fromType, toType) != DT_SUCCESS)
+ {
+ if (closestPointOnPolyBoundary(path[i], endPos, closestEndPos) != DT_SUCCESS)
+ return DT_FAILURE;
+
+ dtVcopy(&straightPath[n*3], closestEndPos);
+ if (straightPathFlags)
+ straightPathFlags[n] = 0;
+ if (straightPathRefs)
+ straightPathRefs[n] = path[i];
+ n++;
+
+ return DT_SUCCESS;
+ }
+
+ // If starting really close the portal, advance.
+ if (i == 0)
+ {
+ float t;
+ if (dtDistancePtSegSqr2D(portalApex, left, right, t) < dtSqr(0.001f))
+ continue;
+ }
+ }
+ else
+ {
+ // End of the path.
+ dtVcopy(left, closestEndPos);
+ dtVcopy(right, closestEndPos);
+
+ fromType = toType = DT_POLYTYPE_GROUND;
+ }
+
+ // Right vertex.
+ if (dtTriArea2D(portalApex, portalRight, right) <= 0.0f)
+ {
+ if (dtVequal(portalApex, portalRight) || dtTriArea2D(portalApex, portalLeft, right) > 0.0f)
+ {
+ dtVcopy(portalRight, right);
+ rightPolyRef = (i+1 < pathSize) ? path[i+1] : 0;
+ rightPolyType = toType;
+ rightIndex = i;
+ }
+ else
+ {
+ dtVcopy(portalApex, portalLeft);
+ apexIndex = leftIndex;
+
+ unsigned char flags = 0;
+ if (!leftPolyRef)
+ flags = DT_STRAIGHTPATH_END;
+ else if (leftPolyType == DT_POLYTYPE_OFFMESH_CONNECTION)
+ flags = DT_STRAIGHTPATH_OFFMESH_CONNECTION;
+ dtPolyRef ref = leftPolyRef;
+
+ if (!dtVequal(&straightPath[(n-1)*3], portalApex))
+ {
+ // Append new vertex.
+ dtVcopy(&straightPath[n*3], portalApex);
+ if (straightPathFlags)
+ straightPathFlags[n] = flags;
+ if (straightPathRefs)
+ straightPathRefs[n] = ref;
+ n++;
+ // If reached end of path or there is no space to append more vertices, return.
+ if (flags == DT_STRAIGHTPATH_END || n >= maxStraightPath)
+ {
+ *straightPathCount = n;
+ return DT_SUCCESS;
+ }
+ }
+ else
+ {
+ // The vertices are equal, update flags and poly.
+ if (straightPathFlags)
+ straightPathFlags[n-1] = flags;
+ if (straightPathRefs)
+ straightPathRefs[n-1] = ref;
+ }
+
+ dtVcopy(portalLeft, portalApex);
+ dtVcopy(portalRight, portalApex);
+ leftIndex = apexIndex;
+ rightIndex = apexIndex;
+
+ // Restart
+ i = apexIndex;
+
+ continue;
+ }
+ }
+
+ // Left vertex.
+ if (dtTriArea2D(portalApex, portalLeft, left) >= 0.0f)
+ {
+ if (dtVequal(portalApex, portalLeft) || dtTriArea2D(portalApex, portalRight, left) < 0.0f)
+ {
+ dtVcopy(portalLeft, left);
+ leftPolyRef = (i+1 < pathSize) ? path[i+1] : 0;
+ leftPolyType = toType;
+ leftIndex = i;
+ }
+ else
+ {
+ dtVcopy(portalApex, portalRight);
+ apexIndex = rightIndex;
+
+ unsigned char flags = 0;
+ if (!rightPolyRef)
+ flags = DT_STRAIGHTPATH_END;
+ else if (rightPolyType == DT_POLYTYPE_OFFMESH_CONNECTION)
+ flags = DT_STRAIGHTPATH_OFFMESH_CONNECTION;
+ dtPolyRef ref = rightPolyRef;
+
+ if (!dtVequal(&straightPath[(n-1)*3], portalApex))
+ {
+ // Append new vertex.
+ dtVcopy(&straightPath[n*3], portalApex);
+ if (straightPathFlags)
+ straightPathFlags[n] = flags;
+ if (straightPathRefs)
+ straightPathRefs[n] = ref;
+ n++;
+ // If reached end of path or there is no space to append more vertices, return.
+ if (flags == DT_STRAIGHTPATH_END || n >= maxStraightPath)
+ {
+ *straightPathCount = n;
+ return DT_SUCCESS;
+ }
+ }
+ else
+ {
+ // The vertices are equal, update flags and poly.
+ if (straightPathFlags)
+ straightPathFlags[n-1] = flags;
+ if (straightPathRefs)
+ straightPathRefs[n-1] = ref;
+ }
+
+ dtVcopy(portalLeft, portalApex);
+ dtVcopy(portalRight, portalApex);
+ leftIndex = apexIndex;
+ rightIndex = apexIndex;
+
+ // Restart
+ i = apexIndex;
+
+ continue;
+ }
+ }
+ }
+ }
+
+ // If the point already exists, remove it and add reappend the actual end location.
+ if (n > 0 && dtVequal(&straightPath[(n-1)*3], closestEndPos))
+ n--;
+
+ // Add end point.
+ if (n < maxStraightPath)
+ {
+ dtVcopy(&straightPath[n*3], closestEndPos);
+ if (straightPathFlags)
+ straightPathFlags[n] = DT_STRAIGHTPATH_END;
+ if (straightPathRefs)
+ straightPathRefs[n] = 0;
+ n++;
+ }
+
+ *straightPathCount = n;
+ return DT_SUCCESS;
+}
+
+dtStatus dtNavMeshQuery::moveAlongSurface(dtPolyRef startRef, const float* startPos, const float* endPos,
+ const dtQueryFilter* filter,
+ float* resultPos, dtPolyRef* visited, int* visitedCount, const int maxVisitedSize) const
+{
+ dtAssert(m_nav);
+ dtAssert(m_tinyNodePool);
+
+ *visitedCount = 0;
+
+ // Validate input
+ if (!startRef) return DT_FAILURE;
+ if (!m_nav->isValidPolyRef(startRef)) return DT_FAILURE;
+
+ static const int MAX_STACK = 48;
+ dtNode* stack[MAX_STACK];
+ int nstack = 0;
+
+ m_tinyNodePool->clear();
+
+ dtNode* startNode = m_tinyNodePool->getNode(startRef);
+ startNode->pidx = 0;
+ startNode->cost = 0;
+ startNode->total = 0;
+ startNode->id = startRef;
+ startNode->flags = DT_NODE_CLOSED;
+ stack[nstack++] = startNode;
+
+ float bestPos[3];
+ float bestDist = FLT_MAX;
+ dtNode* bestNode = 0;
+ dtVcopy(bestPos, startPos);
+
+ // Search constraints
+ float searchPos[3], searchRadSqr;
+ dtVlerp(searchPos, startPos, endPos, 0.5f);
+ searchRadSqr = dtSqr(dtVdist(startPos, endPos)/2.0f + 0.001f);
+
+ float verts[DT_VERTS_PER_POLYGON*3];
+
+ while (nstack)
+ {
+ // Pop front.
+ dtNode* curNode = stack[0];
+ for (int i = 0; i < nstack-1; ++i)
+ stack[i] = stack[i+1];
+ nstack--;
+
+ // Get poly and tile.
+ // The API input has been cheked already, skip checking internal data.
+ const dtPolyRef curRef = curNode->id;
+ const dtMeshTile* curTile = 0;
+ const dtPoly* curPoly = 0;
+ m_nav->getTileAndPolyByRefUnsafe(curRef, &curTile, &curPoly);
+
+ // Collect vertices.
+ const int nverts = curPoly->vertCount;
+ for (int i = 0; i < nverts; ++i)
+ dtVcopy(&verts[i*3], &curTile->verts[curPoly->verts[i]*3]);
+
+ // If target is inside the poly, stop search.
+ if (dtPointInPolygon(endPos, verts, nverts))
+ {
+ bestNode = curNode;
+ dtVcopy(bestPos, endPos);
+ break;
+ }
+
+ // Find wall edges and find nearest point inside the walls.
+ for (int i = 0, j = (int)curPoly->vertCount-1; i < (int)curPoly->vertCount; j = i++)
+ {
+ // Find links to neighbours.
+ static const int MAX_NEIS = 8;
+ int nneis = 0;
+ dtPolyRef neis[MAX_NEIS];
+
+ if (curPoly->neis[j] & DT_EXT_LINK)
+ {
+ // Tile border.
+ for (unsigned int k = curPoly->firstLink; k != DT_NULL_LINK; k = curTile->links[k].next)
+ {
+ const dtLink* link = &curTile->links[k];
+ if (link->edge == j)
+ {
+ if (link->ref != 0)
+ {
+ const dtMeshTile* neiTile = 0;
+ const dtPoly* neiPoly = 0;
+ m_nav->getTileAndPolyByRefUnsafe(link->ref, &neiTile, &neiPoly);
+ if (filter->passFilter(link->ref, neiTile, neiPoly))
+ {
+ if (nneis < MAX_NEIS)
+ neis[nneis++] = link->ref;
+ }
+ }
+ }
+ }
+ }
+ else if (curPoly->neis[j])
+ {
+ const unsigned int idx = (unsigned int)(curPoly->neis[j]-1);
+ const dtPolyRef ref = m_nav->getPolyRefBase(curTile) | idx;
+ if (filter->passFilter(ref, curTile, &curTile->polys[idx]))
+ {
+ // Internal edge, encode id.
+ neis[nneis++] = ref;
+ }
+ }
+
+ if (!nneis)
+ {
+ // Wall edge, calc distance.
+ const float* vj = &verts[j*3];
+ const float* vi = &verts[i*3];
+ float tseg;
+ const float distSqr = dtDistancePtSegSqr2D(endPos, vj, vi, tseg);
+ if (distSqr < bestDist)
+ {
+ // Update nearest distance.
+ dtVlerp(bestPos, vj,vi, tseg);
+ bestDist = distSqr;
+ bestNode = curNode;
+ }
+ }
+ else
+ {
+ for (int k = 0; k < nneis; ++k)
+ {
+ // Skip if no node can be allocated.
+ dtNode* neighbourNode = m_tinyNodePool->getNode(neis[k]);
+ if (!neighbourNode)
+ continue;
+ // Skip if already visited.
+ if (neighbourNode->flags & DT_NODE_CLOSED)
+ continue;
+
+ // Skip the link if it is too far from search constraint.
+ // TODO: Maybe should use getPortalPoints(), but this one is way faster.
+ const float* vj = &verts[j*3];
+ const float* vi = &verts[i*3];
+ float tseg;
+ float distSqr = dtDistancePtSegSqr2D(searchPos, vj, vi, tseg);
+ if (distSqr > searchRadSqr)
+ continue;
+
+ // Mark as the node as visited and push to queue.
+ if (nstack < MAX_STACK)
+ {
+ neighbourNode->pidx = m_tinyNodePool->getNodeIdx(curNode);
+ neighbourNode->flags |= DT_NODE_CLOSED;
+ stack[nstack++] = neighbourNode;
+ }
+ }
+ }
+ }
+ }
+
+ int n = 0;
+ if (bestNode)
+ {
+ // Reverse the path.
+ dtNode* prev = 0;
+ dtNode* node = bestNode;
+ do
+ {
+ dtNode* next = m_tinyNodePool->getNodeAtIdx(node->pidx);
+ node->pidx = m_tinyNodePool->getNodeIdx(prev);
+ prev = node;
+ node = next;
+ }
+ while (node);
+
+ // Store result
+ node = prev;
+ do
+ {
+ visited[n++] = node->id;
+ node = m_tinyNodePool->getNodeAtIdx(node->pidx);
+ }
+ while (node && n < maxVisitedSize);
+ }
+
+ dtVcopy(resultPos, bestPos);
+
+ *visitedCount = n;
+
+ return DT_SUCCESS;
+}
+
+
+dtStatus dtNavMeshQuery::getPortalPoints(dtPolyRef from, dtPolyRef to, float* left, float* right,
+ unsigned char& fromType, unsigned char& toType) const
+{
+ dtAssert(m_nav);
+
+ const dtMeshTile* fromTile = 0;
+ const dtPoly* fromPoly = 0;
+ if (m_nav->getTileAndPolyByRef(from, &fromTile, &fromPoly) != DT_SUCCESS)
+ return DT_FAILURE;
+ fromType = fromPoly->getType();
+
+ const dtMeshTile* toTile = 0;
+ const dtPoly* toPoly = 0;
+ if (m_nav->getTileAndPolyByRef(to, &toTile, &toPoly) != DT_SUCCESS)
+ return DT_FAILURE;
+ toType = toPoly->getType();
+
+ return getPortalPoints(from, fromPoly, fromTile, to, toPoly, toTile, left, right);
+}
+
+// Returns portal points between two polygons.
+dtStatus dtNavMeshQuery::getPortalPoints(dtPolyRef from, const dtPoly* fromPoly, const dtMeshTile* fromTile,
+ dtPolyRef to, const dtPoly* toPoly, const dtMeshTile* toTile,
+ float* left, float* right) const
+{
+ // Find the link that points to the 'to' polygon.
+ const dtLink* link = 0;
+ for (unsigned int i = fromPoly->firstLink; i != DT_NULL_LINK; i = fromTile->links[i].next)
+ {
+ if (fromTile->links[i].ref == to)
+ {
+ link = &fromTile->links[i];
+ break;
+ }
+ }
+ if (!link)
+ return DT_FAILURE;
+
+ // Handle off-mesh connections.
+ if (fromPoly->getType() == DT_POLYTYPE_OFFMESH_CONNECTION)
+ {
+ // Find link that points to first vertex.
+ for (unsigned int i = fromPoly->firstLink; i != DT_NULL_LINK; i = fromTile->links[i].next)
+ {
+ if (fromTile->links[i].ref == to)
+ {
+ const int v = fromTile->links[i].edge;
+ dtVcopy(left, &fromTile->verts[fromPoly->verts[v]*3]);
+ dtVcopy(right, &fromTile->verts[fromPoly->verts[v]*3]);
+ return DT_SUCCESS;
+ }
+ }
+ return DT_FAILURE;
+ }
+
+ if (toPoly->getType() == DT_POLYTYPE_OFFMESH_CONNECTION)
+ {
+ for (unsigned int i = toPoly->firstLink; i != DT_NULL_LINK; i = toTile->links[i].next)
+ {
+ if (toTile->links[i].ref == from)
+ {
+ const int v = toTile->links[i].edge;
+ dtVcopy(left, &toTile->verts[toPoly->verts[v]*3]);
+ dtVcopy(right, &toTile->verts[toPoly->verts[v]*3]);
+ return DT_SUCCESS;
+ }
+ }
+ return DT_FAILURE;
+ }
+
+ // Find portal vertices.
+ const int v0 = fromPoly->verts[link->edge];
+ const int v1 = fromPoly->verts[(link->edge+1) % (int)fromPoly->vertCount];
+ dtVcopy(left, &fromTile->verts[v0*3]);
+ dtVcopy(right, &fromTile->verts[v1*3]);
+
+ // If the link is at tile boundary, dtClamp the vertices to
+ // the link width.
+ if (link->side != 0xff)
+ {
+ // Unpack portal limits.
+ if (link->bmin != 0 || link->bmax != 255)
+ {
+ const float s = 1.0f/255.0f;
+ const float tmin = link->bmin*s;
+ const float tmax = link->bmax*s;
+ dtVlerp(left, &fromTile->verts[v0*3], &fromTile->verts[v1*3], tmin);
+ dtVlerp(right, &fromTile->verts[v0*3], &fromTile->verts[v1*3], tmax);
+ }
+ }
+
+ return DT_SUCCESS;
+}
+
+// Returns edge mid point between two polygons.
+dtStatus dtNavMeshQuery::getEdgeMidPoint(dtPolyRef from, dtPolyRef to, float* mid) const
+{
+ float left[3], right[3];
+ unsigned char fromType, toType;
+ if (!getPortalPoints(from, to, left,right, fromType, toType)) return DT_FAILURE;
+ mid[0] = (left[0]+right[0])*0.5f;
+ mid[1] = (left[1]+right[1])*0.5f;
+ mid[2] = (left[2]+right[2])*0.5f;
+ return DT_SUCCESS;
+}
+
+dtStatus dtNavMeshQuery::getEdgeMidPoint(dtPolyRef from, const dtPoly* fromPoly, const dtMeshTile* fromTile,
+ dtPolyRef to, const dtPoly* toPoly, const dtMeshTile* toTile,
+ float* mid) const
+{
+ float left[3], right[3];
+ if (getPortalPoints(from, fromPoly, fromTile, to, toPoly, toTile, left, right) != DT_SUCCESS)
+ return DT_FAILURE;
+ mid[0] = (left[0]+right[0])*0.5f;
+ mid[1] = (left[1]+right[1])*0.5f;
+ mid[2] = (left[2]+right[2])*0.5f;
+ return DT_SUCCESS;
+}
+
+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
+{
+ dtAssert(m_nav);
+
+ *t = 0;
+ if (pathCount)
+ *pathCount = 0;
+
+ // Validate input
+ if (!startRef || !m_nav->isValidPolyRef(startRef))
+ return DT_FAILURE;
+
+ dtPolyRef curRef = startRef;
+ float verts[DT_VERTS_PER_POLYGON*3];
+ int n = 0;
+
+ hitNormal[0] = 0;
+ hitNormal[1] = 0;
+ hitNormal[2] = 0;
+
+ 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;
+ return DT_SUCCESS;
+ }
+ // Keep track of furthest t so far.
+ if (tmax > *t)
+ *t = tmax;
+
+ // Store visited polygons.
+ if (n < maxPath)
+ path[n++] = curRef;
+
+ // Ray end is completely inside the polygon.
+ if (segMax == -1)
+ {
+ *t = FLT_MAX;
+ if (pathCount)
+ *pathCount = n;
+ return DT_SUCCESS;
+ }
+
+ // Follow neighbours.
+ dtPolyRef nextRef = 0;
+
+ for (unsigned int i = poly->firstLink; i != DT_NULL_LINK; i = tile->links[i].next)
+ {
+ const dtLink* link = &tile->links[i];
+
+ // Find link which contains this edge.
+ if ((int)link->edge != segMax)
+ continue;
+
+ // Get pointer to the next polygon.
+ const dtMeshTile* nextTile = 0;
+ const dtPoly* nextPoly = 0;
+ m_nav->getTileAndPolyByRefUnsafe(link->ref, &nextTile, &nextPoly);
+
+ // Skip off-mesh connections.
+ if (nextPoly->getType() == DT_POLYTYPE_OFFMESH_CONNECTION)
+ continue;
+
+ // Skip links based on filter.
+ if (!filter->passFilter(link->ref, nextTile, nextPoly))
+ continue;
+
+ // If the link is internal, just return the ref.
+ if (link->side == 0xff)
+ {
+ nextRef = link->ref;
+ break;
+ }
+
+ // If the link is at tile boundary,
+
+ // Check if the link spans the whole edge, and accept.
+ if (link->bmin == 0 && link->bmax == 255)
+ {
+ nextRef = link->ref;
+ break;
+ }
+
+ // Check for partial edge links.
+ const int v0 = poly->verts[link->edge];
+ const int v1 = poly->verts[(link->edge+1) % poly->vertCount];
+ const float* left = &tile->verts[v0*3];
+ const float* right = &tile->verts[v1*3];
+
+ // Check that the intersection lies inside the link portal.
+ if (link->side == 0 || link->side == 4)
+ {
+ // Calculate link size.
+ const float s = 1.0f/255.0f;
+ float lmin = left[2] + (right[2] - left[2])*(link->bmin*s);
+ float lmax = left[2] + (right[2] - left[2])*(link->bmax*s);
+ if (lmin > lmax) dtSwap(lmin, lmax);
+
+ // Find Z intersection.
+ float z = startPos[2] + (endPos[2]-startPos[2])*tmax;
+ if (z >= lmin && z <= lmax)
+ {
+ nextRef = link->ref;
+ break;
+ }
+ }
+ else if (link->side == 2 || link->side == 6)
+ {
+ // Calculate link size.
+ const float s = 1.0f/255.0f;
+ float lmin = left[0] + (right[0] - left[0])*(link->bmin*s);
+ float lmax = left[0] + (right[0] - left[0])*(link->bmax*s);
+ if (lmin > lmax) dtSwap(lmin, lmax);
+
+ // Find X intersection.
+ float x = startPos[0] + (endPos[0]-startPos[0])*tmax;
+ if (x >= lmin && x <= lmax)
+ {
+ nextRef = link->ref;
+ break;
+ }
+ }
+ }
+
+ if (!nextRef)
+ {
+ // No neighbour, we hit a wall.
+
+ // Calculate hit normal.
+ const int a = segMax;
+ const int b = segMax+1 < nv ? segMax+1 : 0;
+ const float* va = &verts[a*3];
+ 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);
+
+ if (pathCount)
+ *pathCount = n;
+ return DT_SUCCESS;
+ }
+
+ // No hit, advance to neighbour polygon.
+ curRef = nextRef;
+ }
+
+ if (pathCount)
+ *pathCount = n;
+
+ return DT_SUCCESS;
+}
+
+dtStatus dtNavMeshQuery::findPolysAroundCircle(dtPolyRef startRef, const float* centerPos, const float radius,
+ const dtQueryFilter* filter,
+ dtPolyRef* resultRef, dtPolyRef* resultParent, float* resultCost,
+ int* resultCount, const int maxResult) const
+{
+ dtAssert(m_nav);
+ dtAssert(m_nodePool);
+ dtAssert(m_openList);
+
+ *resultCount = 0;
+
+ // Validate input
+ if (!startRef) return DT_FAILURE;
+ if (!m_nav->isValidPolyRef(startRef)) return DT_FAILURE;
+
+ m_nodePool->clear();
+ m_openList->clear();
+
+ dtNode* startNode = m_nodePool->getNode(startRef);
+ dtVcopy(startNode->pos, centerPos);
+ startNode->pidx = 0;
+ startNode->cost = 0;
+ startNode->total = 0;
+ startNode->id = startRef;
+ startNode->flags = DT_NODE_OPEN;
+ m_openList->push(startNode);
+
+ int n = 0;
+ if (n < maxResult)
+ {
+ if (resultRef)
+ resultRef[n] = startNode->id;
+ if (resultParent)
+ resultParent[n] = 0;
+ if (resultCost)
+ resultCost[n] = 0;
+ ++n;
+ }
+
+ const float radiusSqr = dtSqr(radius);
+
+ while (!m_openList->empty())
+ {
+ dtNode* bestNode = m_openList->pop();
+ bestNode->flags &= ~DT_NODE_OPEN;
+ bestNode->flags |= DT_NODE_CLOSED;
+
+ // Get poly and tile.
+ // The API input has been cheked already, skip checking internal data.
+ const dtPolyRef bestRef = bestNode->id;
+ const dtMeshTile* bestTile = 0;
+ const dtPoly* bestPoly = 0;
+ m_nav->getTileAndPolyByRefUnsafe(bestRef, &bestTile, &bestPoly);
+
+ // Get parent poly and tile.
+ dtPolyRef parentRef = 0;
+ const dtMeshTile* parentTile = 0;
+ const dtPoly* parentPoly = 0;
+ if (bestNode->pidx)
+ parentRef = m_nodePool->getNodeAtIdx(bestNode->pidx)->id;
+ if (parentRef)
+ m_nav->getTileAndPolyByRefUnsafe(parentRef, &parentTile, &parentPoly);
+
+ for (unsigned int i = bestPoly->firstLink; i != DT_NULL_LINK; i = bestTile->links[i].next)
+ {
+ const dtLink* link = &bestTile->links[i];
+ dtPolyRef neighbourRef = link->ref;
+ // Skip invalid neighbours and do not follow back to parent.
+ if (!neighbourRef || neighbourRef == parentRef)
+ continue;
+
+ // Expand to neighbour
+ const dtMeshTile* neighbourTile = 0;
+ const dtPoly* neighbourPoly = 0;
+ m_nav->getTileAndPolyByRefUnsafe(neighbourRef, &neighbourTile, &neighbourPoly);
+
+ // Do not advance if the polygon is excluded by the filter.
+ if (!filter->passFilter(neighbourRef, neighbourTile, neighbourPoly))
+ continue;
+
+ // Find edge and calc distance to the edge.
+ float va[3], vb[3];
+ if (!getPortalPoints(bestRef, bestPoly, bestTile, neighbourRef, neighbourPoly, neighbourTile, va, vb))
+ continue;
+
+ // If the circle is not touching the next polygon, skip it.
+ float tseg;
+ float distSqr = dtDistancePtSegSqr2D(centerPos, va, vb, tseg);
+ if (distSqr > radiusSqr)
+ continue;
+
+ dtNode* neighbourNode = m_nodePool->getNode(neighbourRef);
+ if (!neighbourNode)
+ continue;
+
+ if (neighbourNode->flags & DT_NODE_CLOSED)
+ continue;
+
+ // Cost
+ if (neighbourNode->flags == 0)
+ dtVlerp(neighbourNode->pos, va, vb, 0.5f);
+
+ const float total = bestNode->total + dtVdist(bestNode->pos, neighbourNode->pos);
+
+ // The node is already in open list and the new result is worse, skip.
+ if ((neighbourNode->flags & DT_NODE_OPEN) && total >= neighbourNode->total)
+ continue;
+
+ neighbourNode->id = neighbourRef;
+ neighbourNode->flags &= ~DT_NODE_CLOSED;
+ neighbourNode->pidx = m_nodePool->getNodeIdx(bestNode);
+ neighbourNode->total = total;
+
+ if (neighbourNode->flags & DT_NODE_OPEN)
+ {
+ m_openList->modify(neighbourNode);
+ }
+ else
+ {
+ if (n < maxResult)
+ {
+ if (resultRef)
+ resultRef[n] = neighbourNode->id;
+ if (resultParent)
+ resultParent[n] = m_nodePool->getNodeAtIdx(neighbourNode->pidx)->id;
+ if (resultCost)
+ resultCost[n] = neighbourNode->total;
+ ++n;
+ }
+ neighbourNode->flags = DT_NODE_OPEN;
+ m_openList->push(neighbourNode);
+ }
+ }
+ }
+
+ *resultCount = n;
+
+ return DT_SUCCESS;
+}
+
+dtStatus dtNavMeshQuery::findPolysAroundShape(dtPolyRef startRef, const float* verts, const int nverts,
+ const dtQueryFilter* filter,
+ dtPolyRef* resultRef, dtPolyRef* resultParent, float* resultCost,
+ int* resultCount, const int maxResult) const
+{
+ dtAssert(m_nav);
+ dtAssert(m_nodePool);
+ dtAssert(m_openList);
+
+ *resultCount = 0;
+
+ // Validate input
+ if (!startRef) return DT_FAILURE;
+ if (!m_nav->isValidPolyRef(startRef)) return DT_FAILURE;
+
+ m_nodePool->clear();
+ m_openList->clear();
+
+ float centerPos[3] = {0,0,0};
+ for (int i = 0; i < nverts; ++i)
+ dtVadd(centerPos,centerPos,&verts[i*3]);
+ dtVscale(centerPos,centerPos,1.0f/nverts);
+
+ dtNode* startNode = m_nodePool->getNode(startRef);
+ dtVcopy(startNode->pos, centerPos);
+ startNode->pidx = 0;
+ startNode->cost = 0;
+ startNode->total = 0;
+ startNode->id = startRef;
+ startNode->flags = DT_NODE_OPEN;
+ m_openList->push(startNode);
+
+ int n = 0;
+ if (n < maxResult)
+ {
+ if (resultRef)
+ resultRef[n] = startNode->id;
+ if (resultParent)
+ resultParent[n] = 0;
+ if (resultCost)
+ resultCost[n] = 0;
+ ++n;
+ }
+
+ while (!m_openList->empty())
+ {
+ dtNode* bestNode = m_openList->pop();
+ bestNode->flags &= ~DT_NODE_OPEN;
+ bestNode->flags |= DT_NODE_CLOSED;
+
+ // Get poly and tile.
+ // The API input has been cheked already, skip checking internal data.
+ const dtPolyRef bestRef = bestNode->id;
+ const dtMeshTile* bestTile = 0;
+ const dtPoly* bestPoly = 0;
+ m_nav->getTileAndPolyByRefUnsafe(bestRef, &bestTile, &bestPoly);
+
+ // Get parent poly and tile.
+ dtPolyRef parentRef = 0;
+ const dtMeshTile* parentTile = 0;
+ const dtPoly* parentPoly = 0;
+ if (bestNode->pidx)
+ parentRef = m_nodePool->getNodeAtIdx(bestNode->pidx)->id;
+ if (parentRef)
+ m_nav->getTileAndPolyByRefUnsafe(parentRef, &parentTile, &parentPoly);
+
+ for (unsigned int i = bestPoly->firstLink; i != DT_NULL_LINK; i = bestTile->links[i].next)
+ {
+ const dtLink* link = &bestTile->links[i];
+ dtPolyRef neighbourRef = link->ref;
+ // Skip invalid neighbours and do not follow back to parent.
+ if (!neighbourRef || neighbourRef == parentRef)
+ continue;
+
+ // Expand to neighbour
+ const dtMeshTile* neighbourTile = 0;
+ const dtPoly* neighbourPoly = 0;
+ m_nav->getTileAndPolyByRefUnsafe(neighbourRef, &neighbourTile, &neighbourPoly);
+
+ // Do not advance if the polygon is excluded by the filter.
+ if (!filter->passFilter(neighbourRef, neighbourTile, neighbourPoly))
+ continue;
+
+ // Find edge and calc distance to the edge.
+ float va[3], vb[3];
+ if (!getPortalPoints(bestRef, bestPoly, bestTile, neighbourRef, neighbourPoly, neighbourTile, va, vb))
+ continue;
+
+ // If the poly is not touching the edge to the next polygon, skip the connection it.
+ float tmin, tmax;
+ int segMin, segMax;
+ if (!dtIntersectSegmentPoly2D(va, vb, verts, nverts, tmin, tmax, segMin, segMax))
+ continue;
+ if (tmin > 1.0f || tmax < 0.0f)
+ continue;
+
+ dtNode* neighbourNode = m_nodePool->getNode(neighbourRef);
+ if (!neighbourNode)
+ continue;
+
+ if (neighbourNode->flags & DT_NODE_CLOSED)
+ continue;
+
+ // Cost
+ if (neighbourNode->flags == 0)
+ dtVlerp(neighbourNode->pos, va, vb, 0.5f);
+
+ const float total = bestNode->total + dtVdist(bestNode->pos, neighbourNode->pos);
+
+ // The node is already in open list and the new result is worse, skip.
+ if ((neighbourNode->flags & DT_NODE_OPEN) && total >= neighbourNode->total)
+ continue;
+
+ neighbourNode->id = neighbourRef;
+ neighbourNode->flags &= ~DT_NODE_CLOSED;
+ neighbourNode->pidx = m_nodePool->getNodeIdx(bestNode);
+ neighbourNode->total = total;
+
+ if (neighbourNode->flags & DT_NODE_OPEN)
+ {
+ m_openList->modify(neighbourNode);
+ }
+ else
+ {
+ if (n < maxResult)
+ {
+ if (resultRef)
+ resultRef[n] = neighbourNode->id;
+ if (resultParent)
+ resultParent[n] = m_nodePool->getNodeAtIdx(neighbourNode->pidx)->id;
+ if (resultCost)
+ resultCost[n] = neighbourNode->total;
+ ++n;
+ }
+ neighbourNode->flags = DT_NODE_OPEN;
+ m_openList->push(neighbourNode);
+ }
+ }
+ }
+
+ *resultCount = n;
+
+ return DT_SUCCESS;
+}
+
+dtStatus dtNavMeshQuery::findLocalNeighbourhood(dtPolyRef startRef, const float* centerPos, const float radius,
+ const dtQueryFilter* filter,
+ dtPolyRef* resultRef, dtPolyRef* resultParent,
+ int* resultCount, const int maxResult) const
+{
+ dtAssert(m_nav);
+ dtAssert(m_tinyNodePool);
+
+ *resultCount = 0;
+
+ // Validate input
+ if (!startRef) return DT_FAILURE;
+ if (!m_nav->isValidPolyRef(startRef)) return DT_FAILURE;
+
+ static const int MAX_STACK = 48;
+ dtNode* stack[MAX_STACK];
+ int nstack = 0;
+
+ m_tinyNodePool->clear();
+
+ dtNode* startNode = m_tinyNodePool->getNode(startRef);
+ startNode->pidx = 0;
+ startNode->id = startRef;
+ startNode->flags = DT_NODE_CLOSED;
+ stack[nstack++] = startNode;
+
+ const float radiusSqr = dtSqr(radius);
+
+ float pa[DT_VERTS_PER_POLYGON*3];
+ float pb[DT_VERTS_PER_POLYGON*3];
+
+ int n = 0;
+ if (n < maxResult)
+ {
+ resultRef[n] = startNode->id;
+ if (resultParent)
+ resultParent[n] = 0;
+ ++n;
+ }
+
+ while (nstack)
+ {
+ // Pop front.
+ dtNode* curNode = stack[0];
+ for (int i = 0; i < nstack-1; ++i)
+ stack[i] = stack[i+1];
+ nstack--;
+
+ // Get poly and tile.
+ // The API input has been cheked already, skip checking internal data.
+ const dtPolyRef curRef = curNode->id;
+ const dtMeshTile* curTile = 0;
+ const dtPoly* curPoly = 0;
+ m_nav->getTileAndPolyByRefUnsafe(curRef, &curTile, &curPoly);
+
+ for (unsigned int i = curPoly->firstLink; i != DT_NULL_LINK; i = curTile->links[i].next)
+ {
+ const dtLink* link = &curTile->links[i];
+ dtPolyRef neighbourRef = link->ref;
+ // Skip invalid neighbours.
+ if (!neighbourRef)
+ continue;
+
+ // Skip if cannot alloca more nodes.
+ dtNode* neighbourNode = m_tinyNodePool->getNode(neighbourRef);
+ if (!neighbourNode)
+ continue;
+ // Skip visited.
+ if (neighbourNode->flags & DT_NODE_CLOSED)
+ continue;
+
+ // Expand to neighbour
+ const dtMeshTile* neighbourTile = 0;
+ const dtPoly* neighbourPoly = 0;
+ m_nav->getTileAndPolyByRefUnsafe(neighbourRef, &neighbourTile, &neighbourPoly);
+
+ // Skip off-mesh connections.
+ if (neighbourPoly->getType() == DT_POLYTYPE_OFFMESH_CONNECTION)
+ continue;
+
+ // Do not advance if the polygon is excluded by the filter.
+ if (!filter->passFilter(neighbourRef, neighbourTile, neighbourPoly))
+ continue;
+
+ // Find edge and calc distance to the edge.
+ float va[3], vb[3];
+ if (!getPortalPoints(curRef, curPoly, curTile, neighbourRef, neighbourPoly, neighbourTile, va, vb))
+ continue;
+
+ // If the circle is not touching the next polygon, skip it.
+ float tseg;
+ float distSqr = dtDistancePtSegSqr2D(centerPos, va, vb, tseg);
+ if (distSqr > radiusSqr)
+ continue;
+
+ // Mark node visited, this is done before the overlap test so that
+ // we will not visit the poly again if the test fails.
+ neighbourNode->flags |= DT_NODE_CLOSED;
+ neighbourNode->pidx = m_tinyNodePool->getNodeIdx(curNode);
+
+ // Check that the polygon does not collide with existing polygons.
+
+ // Collect vertices of the neighbour poly.
+ const int npa = neighbourPoly->vertCount;
+ for (int k = 0; k < npa; ++k)
+ dtVcopy(&pa[k*3], &neighbourTile->verts[neighbourPoly->verts[k]*3]);
+
+ bool overlap = false;
+ for (int j = 0; j < n; ++j)
+ {
+ dtPolyRef pastRef = resultRef[j];
+
+ // Connected polys do not overlap.
+ bool connected = false;
+ for (unsigned int k = curPoly->firstLink; k != DT_NULL_LINK; k = curTile->links[k].next)
+ {
+ if (curTile->links[k].ref == pastRef)
+ {
+ connected = true;
+ break;
+ }
+ }
+ if (connected)
+ continue;
+
+ // Potentially overlapping.
+ const dtMeshTile* pastTile = 0;
+ const dtPoly* pastPoly = 0;
+ m_nav->getTileAndPolyByRefUnsafe(pastRef, &pastTile, &pastPoly);
+
+ // Get vertices and test overlap
+ const int npb = pastPoly->vertCount;
+ for (int k = 0; k < npb; ++k)
+ dtVcopy(&pb[k*3], &pastTile->verts[pastPoly->verts[k]*3]);
+
+ if (dtOverlapPolyPoly2D(pa,npa, pb,npb))
+ {
+ overlap = true;
+ break;
+ }
+ }
+ if (overlap)
+ continue;
+
+ // This poly is fine, store and advance to the poly.
+ if (n < maxResult)
+ {
+ resultRef[n] = neighbourRef;
+ if (resultParent)
+ resultParent[n] = curRef;
+ ++n;
+ }
+
+ if (nstack < MAX_STACK)
+ {
+ stack[nstack++] = neighbourNode;
+ }
+ }
+ }
+
+ *resultCount = n;
+
+ return DT_SUCCESS;
+}
+
+
+struct dtSegInterval
+{
+ short tmin, tmax;
+};
+
+static void insertInterval(dtSegInterval* ints, int& nints, const int maxInts,
+ const short tmin, const short tmax)
+{
+ if (nints+1 > maxInts) return;
+ // Find insertion point.
+ int idx = 0;
+ while (idx < nints)
+ {
+ if (tmax <= ints[idx].tmin)
+ break;
+ idx++;
+ }
+ // Move current results.
+ if (nints-idx)
+ memmove(ints+idx+1, ints+idx, sizeof(dtSegInterval)*(nints-idx));
+ // Store
+ ints[idx].tmin = tmin;
+ ints[idx].tmax = tmax;
+ nints++;
+}
+
+dtStatus dtNavMeshQuery::getPolyWallSegments(dtPolyRef ref, const dtQueryFilter* filter,
+ float* segments, int* segmentCount, const int maxSegments) const
+{
+ dtAssert(m_nav);
+
+ *segmentCount = 0;
+
+ const dtMeshTile* tile = 0;
+ const dtPoly* poly = 0;
+ if (m_nav->getTileAndPolyByRef(ref, &tile, &poly) != DT_SUCCESS)
+ return DT_FAILURE;
+
+ int n = 0;
+ static const int MAX_INTERVAL = 16;
+ dtSegInterval ints[MAX_INTERVAL];
+ int nints;
+
+ for (int i = 0, j = (int)poly->vertCount-1; i < (int)poly->vertCount; j = i++)
+ {
+ // Skip non-solid edges.
+ nints = 0;
+ if (poly->neis[j] & DT_EXT_LINK)
+ {
+ // Tile border.
+ for (unsigned int k = poly->firstLink; k != DT_NULL_LINK; k = tile->links[k].next)
+ {
+ const dtLink* link = &tile->links[k];
+ if (link->edge == j)
+ {
+ if (link->ref != 0)
+ {
+ const dtMeshTile* neiTile = 0;
+ const dtPoly* neiPoly = 0;
+ m_nav->getTileAndPolyByRefUnsafe(link->ref, &neiTile, &neiPoly);
+ if (filter->passFilter(link->ref, neiTile, neiPoly))
+ {
+ insertInterval(ints, nints, MAX_INTERVAL, link->bmin, link->bmax);
+ }
+ }
+ }
+ }
+ }
+ else if (poly->neis[j])
+ {
+ // Internal edge
+ const unsigned int idx = (unsigned int)(poly->neis[j]-1);
+ const dtPolyRef ref = m_nav->getPolyRefBase(tile) | idx;
+ if (filter->passFilter(ref, tile, &tile->polys[idx]))
+ continue;
+ }
+
+ // Add sentinels
+ insertInterval(ints, nints, MAX_INTERVAL, -1, 0);
+ insertInterval(ints, nints, MAX_INTERVAL, 255, 256);
+
+ // Store segment.
+ const float* vj = &tile->verts[poly->verts[j]*3];
+ const float* vi = &tile->verts[poly->verts[i]*3];
+ for (int k = 1; k < nints; ++k)
+ {
+ // Find the space inbetween the opening areas.
+ const int imin = ints[k-1].tmax;
+ const int imax = ints[k].tmin;
+ if (imin == imax) continue;
+ if (imin == 0 && imax == 255)
+ {
+ if (n < maxSegments)
+ {
+ float* seg = &segments[n*6];
+ n++;
+ dtVcopy(seg+0, vj);
+ dtVcopy(seg+3, vi);
+ }
+ }
+ else
+ {
+ const float tmin = imin/255.0f;
+ const float tmax = imax/255.0f;
+ if (n < maxSegments)
+ {
+ float* seg = &segments[n*6];
+ n++;
+ dtVlerp(seg+0, vj,vi, tmin);
+ dtVlerp(seg+3, vj,vi, tmax);
+ }
+ }
+ }
+ }
+
+ *segmentCount = n;
+
+ return DT_SUCCESS;
+}
+
+dtStatus dtNavMeshQuery::findDistanceToWall(dtPolyRef startRef, const float* centerPos, const float maxRadius,
+ const dtQueryFilter* filter,
+ float* hitDist, float* hitPos, float* hitNormal) const
+{
+ dtAssert(m_nav);
+ dtAssert(m_nodePool);
+ dtAssert(m_openList);
+
+ // Validate input
+ if (!startRef) return DT_FAILURE;
+ if (!m_nav->isValidPolyRef(startRef)) return DT_FAILURE;
+
+ m_nodePool->clear();
+ m_openList->clear();
+
+ dtNode* startNode = m_nodePool->getNode(startRef);
+ dtVcopy(startNode->pos, centerPos);
+ startNode->pidx = 0;
+ startNode->cost = 0;
+ startNode->total = 0;
+ startNode->id = startRef;
+ startNode->flags = DT_NODE_OPEN;
+ m_openList->push(startNode);
+
+ float radiusSqr = dtSqr(maxRadius);
+
+ while (!m_openList->empty())
+ {
+ dtNode* bestNode = m_openList->pop();
+ bestNode->flags &= ~DT_NODE_OPEN;
+ bestNode->flags |= DT_NODE_CLOSED;
+
+ // Get poly and tile.
+ // The API input has been cheked already, skip checking internal data.
+ const dtPolyRef bestRef = bestNode->id;
+ const dtMeshTile* bestTile = 0;
+ const dtPoly* bestPoly = 0;
+ m_nav->getTileAndPolyByRefUnsafe(bestRef, &bestTile, &bestPoly);
+
+ // Get parent poly and tile.
+ dtPolyRef parentRef = 0;
+ const dtMeshTile* parentTile = 0;
+ const dtPoly* parentPoly = 0;
+ if (bestNode->pidx)
+ parentRef = m_nodePool->getNodeAtIdx(bestNode->pidx)->id;
+ if (parentRef)
+ m_nav->getTileAndPolyByRefUnsafe(parentRef, &parentTile, &parentPoly);
+
+ // Hit test walls.
+ for (int i = 0, j = (int)bestPoly->vertCount-1; i < (int)bestPoly->vertCount; j = i++)
+ {
+ // Skip non-solid edges.
+ if (bestPoly->neis[j] & DT_EXT_LINK)
+ {
+ // Tile border.
+ bool solid = true;
+ for (unsigned int k = bestPoly->firstLink; k != DT_NULL_LINK; k = bestTile->links[k].next)
+ {
+ const dtLink* link = &bestTile->links[k];
+ if (link->edge == j)
+ {
+ if (link->ref != 0)
+ {
+ const dtMeshTile* neiTile = 0;
+ const dtPoly* neiPoly = 0;
+ m_nav->getTileAndPolyByRefUnsafe(link->ref, &neiTile, &neiPoly);
+ if (filter->passFilter(link->ref, neiTile, neiPoly))
+ solid = false;
+ }
+ break;
+ }
+ }
+ if (!solid) continue;
+ }
+ else if (bestPoly->neis[j])
+ {
+ // Internal edge
+ const unsigned int idx = (unsigned int)(bestPoly->neis[j]-1);
+ const dtPolyRef ref = m_nav->getPolyRefBase(bestTile) | idx;
+ if (filter->passFilter(ref, bestTile, &bestTile->polys[idx]))
+ continue;
+ }
+
+ // Calc distance to the edge.
+ const float* vj = &bestTile->verts[bestPoly->verts[j]*3];
+ const float* vi = &bestTile->verts[bestPoly->verts[i]*3];
+ float tseg;
+ float distSqr = dtDistancePtSegSqr2D(centerPos, vj, vi, tseg);
+
+ // Edge is too far, skip.
+ if (distSqr > radiusSqr)
+ continue;
+
+ // Hit wall, update radius.
+ radiusSqr = distSqr;
+ // Calculate hit pos.
+ hitPos[0] = vj[0] + (vi[0] - vj[0])*tseg;
+ hitPos[1] = vj[1] + (vi[1] - vj[1])*tseg;
+ hitPos[2] = vj[2] + (vi[2] - vj[2])*tseg;
+ }
+
+ for (unsigned int i = bestPoly->firstLink; i != DT_NULL_LINK; i = bestTile->links[i].next)
+ {
+ const dtLink* link = &bestTile->links[i];
+ dtPolyRef neighbourRef = link->ref;
+ // Skip invalid neighbours and do not follow back to parent.
+ if (!neighbourRef || neighbourRef == parentRef)
+ continue;
+
+ // Expand to neighbour.
+ const dtMeshTile* neighbourTile = 0;
+ const dtPoly* neighbourPoly = 0;
+ m_nav->getTileAndPolyByRefUnsafe(neighbourRef, &neighbourTile, &neighbourPoly);
+
+ // Skip off-mesh connections.
+ if (neighbourPoly->getType() == DT_POLYTYPE_OFFMESH_CONNECTION)
+ continue;
+
+ // Calc distance to the edge.
+ const float* va = &bestTile->verts[bestPoly->verts[link->edge]*3];
+ const float* vb = &bestTile->verts[bestPoly->verts[(link->edge+1) % bestPoly->vertCount]*3];
+ float tseg;
+ float distSqr = dtDistancePtSegSqr2D(centerPos, va, vb, tseg);
+
+ // If the circle is not touching the next polygon, skip it.
+ if (distSqr > radiusSqr)
+ continue;
+
+ if (!filter->passFilter(neighbourRef, neighbourTile, neighbourPoly))
+ continue;
+
+ dtNode* neighbourNode = m_nodePool->getNode(neighbourRef);
+ if (!neighbourNode)
+ continue;
+
+ if (neighbourNode->flags & DT_NODE_CLOSED)
+ continue;
+
+ // Cost
+ if (neighbourNode->flags == 0)
+ {
+ getEdgeMidPoint(bestRef, bestPoly, bestTile,
+ neighbourRef, neighbourPoly, neighbourTile, neighbourNode->pos);
+ }
+
+ const float total = bestNode->total + dtVdist(bestNode->pos, neighbourNode->pos);
+
+ // The node is already in open list and the new result is worse, skip.
+ if ((neighbourNode->flags & DT_NODE_OPEN) && total >= neighbourNode->total)
+ continue;
+
+ neighbourNode->id = neighbourRef;
+ neighbourNode->flags &= ~DT_NODE_CLOSED;
+ neighbourNode->pidx = m_nodePool->getNodeIdx(bestNode);
+ neighbourNode->total = total;
+
+ if (neighbourNode->flags & DT_NODE_OPEN)
+ {
+ m_openList->modify(neighbourNode);
+ }
+ else
+ {
+ neighbourNode->flags |= DT_NODE_OPEN;
+ m_openList->push(neighbourNode);
+ }
+ }
+ }
+
+ // Calc hit normal.
+ dtVsub(hitNormal, centerPos, hitPos);
+ dtVnormalize(hitNormal);
+
+ *hitDist = sqrtf(radiusSqr);
+
+ return DT_SUCCESS;
+}
+
+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;
+}
diff --git a/dep/recastnavigation/Detour/DetourNavMeshQuery.h b/dep/recastnavigation/Detour/DetourNavMeshQuery.h
new file mode 100644
index 00000000000..f5046d83290
--- /dev/null
+++ b/dep/recastnavigation/Detour/DetourNavMeshQuery.h
@@ -0,0 +1,407 @@
+//
+// Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
+//
+// This software is provided 'as-is', without any express or implied
+// warranty. In no event will the authors be held liable for any damages
+// arising from the use of this software.
+// Permission is granted to anyone to use this software for any purpose,
+// including commercial applications, and to alter it and redistribute it
+// freely, subject to the following restrictions:
+// 1. The origin of this software must not be misrepresented; you must not
+// claim that you wrote the original software. If you use this software
+// in a product, an acknowledgment in the product documentation would be
+// appreciated but is not required.
+// 2. Altered source versions must be plainly marked as such, and must not be
+// misrepresented as being the original software.
+// 3. This notice may not be removed or altered from any source distribution.
+//
+
+#ifndef DETOURNAVMESHQUERY_H
+#define DETOURNAVMESHQUERY_H
+
+#include "DetourNavMesh.h"
+
+
+// Define DT_VIRTUAL_QUERYFILTER if you wish to derive a custom filter from dtQueryFilter.
+// On certain platforms indirect or virtual function call is expensive. The default
+// setting is to use non-virtual functions, the actualy implementations of the functions
+// are declared as inline for maximum speed.
+
+//#define DT_VIRTUAL_QUERYFILTER 1
+
+// Class for polygon filtering and cost calculation during query operations.
+// - It is possible to derive a custom query filter from dtQueryFilter by overriding
+// the virtual functions passFilter() and getCost().
+// - Both functions should be as fast as possible. Use cached local copy of data
+// instead of accessing your own objects where possible.
+// - You do not need to adhere to the flags and cost logic provided by the default
+// implementation.
+// - In order for the A* to work properly, the cost should be proportional to
+// the travel distance. Using cost modifier less than 1.0 is likely to lead
+// to problems during pathfinding.
+class dtQueryFilter
+{
+ float m_areaCost[DT_MAX_AREAS]; // Array storing cost per area type, used by default implementation.
+ unsigned short m_includeFlags; // Include poly flags, used by default implementation.
+ unsigned short m_excludeFlags; // Exclude poly flags, used by default implementation.
+
+public:
+ dtQueryFilter();
+
+ // Returns true if the polygon is can visited.
+ // Params:
+ // ref - (in) reference to the polygon test.
+ // tile - (in) pointer to the tile of the polygon test.
+ // poly - (in) pointer to the polygon test.
+#ifdef DT_VIRTUAL_QUERYFILTER
+ virtual bool passFilter(const dtPolyRef ref,
+ const dtMeshTile* tile,
+ const dtPoly* poly) const;
+#else
+ bool passFilter(const dtPolyRef ref,
+ const dtMeshTile* tile,
+ const dtPoly* poly) const;
+#endif
+
+ // Returns cost to travel from 'pa' to 'pb'.'
+ // The segment is fully contained inside 'cur'.
+ // 'pa' lies on the edge between 'prev' and 'cur',
+ // 'pb' lies on the edge between 'cur' and 'next'.
+ // Params:
+ // pa - (in) segment start position.
+ // pb - (in) segment end position.
+ // prevRef, prevTile, prevPoly - (in) data describing the previous polygon, can be null.
+ // curRef, curTile, curPoly - (in) data describing the current polygon.
+ // nextRef, nextTile, nextPoly - (in) data describing the next polygon, can be null.
+#ifdef DT_VIRTUAL_QUERYFILTER
+ virtual float getCost(const float* pa, const float* pb,
+ const dtPolyRef prevRef, const dtMeshTile* prevTile, const dtPoly* prevPoly,
+ const dtPolyRef curRef, const dtMeshTile* curTile, const dtPoly* curPoly,
+ const dtPolyRef nextRef, const dtMeshTile* nextTile, const dtPoly* nextPoly) const;
+#else
+ float getCost(const float* pa, const float* pb,
+ const dtPolyRef prevRef, const dtMeshTile* prevTile, const dtPoly* prevPoly,
+ const dtPolyRef curRef, const dtMeshTile* curTile, const dtPoly* curPoly,
+ const dtPolyRef nextRef, const dtMeshTile* nextTile, const dtPoly* nextPoly) const;
+#endif
+
+ // Getters and setters for the default implementation data.
+ inline float getAreaCost(const int i) const { return m_areaCost[i]; }
+ inline void setAreaCost(const int i, const float cost) { m_areaCost[i] = cost; }
+
+ inline unsigned short getIncludeFlags() const { return m_includeFlags; }
+ inline void setIncludeFlags(const unsigned short flags) { m_includeFlags = flags; }
+
+ inline unsigned short getExcludeFlags() const { return m_excludeFlags; }
+ inline void setExcludeFlags(const unsigned short flags) { m_excludeFlags = flags; }
+};
+
+class dtNavMeshQuery
+{
+public:
+ dtNavMeshQuery();
+ ~dtNavMeshQuery();
+
+ // Initializes the nav mesh query.
+ // Params:
+ // nav - (in) pointer to navigation mesh data.
+ // maxNodes - (in) Maximum number of search nodes to use (max 65536).
+ // Returns: True if succeed, else false.
+ dtStatus init(const dtNavMesh* nav, const int maxNodes);
+
+ // Finds the nearest navigation polygon around the center location.
+ // Params:
+ // center[3] - (in) The center of the search box.
+ // extents[3] - (in) The extents of the search box.
+ // filter - (in) path polygon filter.
+ // nearestRef - (out) Reference to the nearest polygon.
+ // nearestPt[3] - (out, opt) The nearest point on found polygon, null if not needed.
+ // Returns: Reference identifier for the polygon, or 0 if no polygons found.
+ dtStatus findNearestPoly(const float* center, const float* extents,
+ const dtQueryFilter* filter,
+ dtPolyRef* nearestRef, float* nearestPt) const;
+
+ // Returns polygons which overlap the query box.
+ // Params:
+ // center[3] - (in) the center of the search box.
+ // extents[3] - (in) the extents of the search box.
+ // filter - (in) path polygon filter.
+ // polys - (out) array holding the search result.
+ // polyCount - (out) Number of polygons in search result array.
+ // maxPolys - (in) The max number of polygons the polys array can hold.
+ dtStatus queryPolygons(const float* center, const float* extents,
+ const dtQueryFilter* filter,
+ dtPolyRef* polys, int* polyCount, const int maxPolys) const;
+
+ // Finds path from start polygon to end polygon.
+ // If target polygon canno be reached through the navigation graph,
+ // the last node on the array is nearest node to the end polygon.
+ // Start end end positions are needed to calculate more accurate
+ // traversal cost at start end end polygons.
+ // Params:
+ // startRef - (in) ref to path start polygon.
+ // endRef - (in) ref to path end polygon.
+ // startPos[3] - (in) Path start location.
+ // endPos[3] - (in) Path end location.
+ // filter - (in) path polygon filter.
+ // path - (out) array holding the search result.
+ // pathCount - (out) Number of polygons in search result array.
+ // maxPath - (in) The max number of polygons the path array can hold. Must be at least 1.
+ dtStatus findPath(dtPolyRef startRef, dtPolyRef endRef,
+ const float* startPos, const float* endPos,
+ const dtQueryFilter* filter,
+ dtPolyRef* path, int* pathCount, const int maxPath) const;
+
+ // Intializes sliced path find query.
+ // Note 1: calling any other dtNavMeshQuery method before calling findPathEnd()
+ // may results in corrupted data!
+ // Note 2: The pointer to filter is store, and used in subsequent
+ // calls to updateSlicedFindPath().
+ // Params:
+ // startRef - (in) ref to path start polygon.
+ // endRef - (in) ref to path end polygon.
+ // startPos[3] - (in) Path start location.
+ // endPos[3] - (in) Path end location.
+ // filter - (in) path polygon filter.
+ dtStatus initSlicedFindPath(dtPolyRef startRef, dtPolyRef endRef,
+ const float* startPos, const float* endPos,
+ const dtQueryFilter* filter);
+
+ // Updates sliced path find query.
+ // Params:
+ // maxIter - (in) max number of iterations to update.
+ // Returns: Path query state.
+ dtStatus updateSlicedFindPath(const int maxIter);
+
+ // Finalizes sliced path find query and returns found path.
+ // path - (out) array holding the search result.
+ // pathCount - (out) Number of polygons in search result array.
+ // maxPath - (in) The max number of polygons the path array can hold.
+ dtStatus finalizeSlicedFindPath(dtPolyRef* path, int* pathCount, const int maxPath);
+
+ // Finalizes partial sliced path find query and returns path to the furthest
+ // polygon on the existing path that was visited during the search.
+ // existing - (out) Array of polygons in the existing path.
+ // existingSize - (out) Number of polygons in existing path array.
+ // path - (out) array holding the search result.
+ // pathCount - (out) Number of polygons in search result array.
+ // maxPath - (in) The max number of polygons the path array can hold.
+ dtStatus finalizeSlicedFindPathPartial(const dtPolyRef* existing, const int existingSize,
+ dtPolyRef* path, int* pathCount, const int maxPath);
+
+ // Finds a straight path from start to end locations within the corridor
+ // described by the path polygons.
+ // Start and end locations will be clamped on the corridor.
+ // The returned polygon references are point to polygon which was entered when
+ // a path point was added. For the end point, zero will be returned. This allows
+ // to match for example off-mesh link points to their representative polygons.
+ // Params:
+ // startPos[3] - (in) Path start location.
+ // endPo[3] - (in) Path end location.
+ // path - (in) Array of connected polygons describing the corridor.
+ // pathSize - (in) Number of polygons in path array.
+ // straightPath - (out) Points describing the straight path.
+ // straightPathFlags - (out, opt) Flags describing each point type, see dtStraightPathFlags.
+ // straightPathRefs - (out, opt) References to polygons at point locations.
+ // straightPathCount - (out) Number of points in the path.
+ // maxStraightPath - (in) The max number of points the straight path array can hold. Must be at least 1.
+ dtStatus findStraightPath(const float* startPos, const float* endPos,
+ const dtPolyRef* path, const int pathSize,
+ float* straightPath, unsigned char* straightPathFlags, dtPolyRef* straightPathRefs,
+ int* straightPathCount, const int maxStraightPath) const;
+
+ // Moves from startPos to endPos constrained to the navmesh.
+ // If the endPos is reachable, the resultPos will be endPos,
+ // or else the resultPos will be the nearest point in navmesh.
+ // Note: The resulting point is not projected to the ground, use getPolyHeight() to get height.
+ // Note: The algorithm is optimized for small delta movement and small number of polygons.
+ // Params:
+ // startRef - (in) ref to the polygon where startPos lies.
+ // startPos[3] - (in) start position of the mover.
+ // endPos[3] - (in) desired end position of the mover.
+ // filter - (in) path polygon filter.
+ // resultPos[3] - (out) new position of the mover.
+ // visited - (out) array of visited polygons.
+ // visitedCount - (out) Number of entries in the visited array.
+ // maxVisitedSize - (in) max number of polygons in the visited array.
+ dtStatus moveAlongSurface(dtPolyRef startRef, const float* startPos, const float* endPos,
+ const dtQueryFilter* filter,
+ float* resultPos, dtPolyRef* visited, int* visitedCount, const int maxVisitedSize) const;
+
+ // Casts 'walkability' ray along the navmesh surface from startPos towards the endPos.
+ // Params:
+ // startRef - (in) ref to the polygon where the start lies.
+ // startPos[3] - (in) start position of the query.
+ // endPos[3] - (in) end position of the query.
+ // t - (out) hit parameter along the segment, FLT_MAX if no hit.
+ // hitNormal[3] - (out) normal of the nearest hit.
+ // filter - (in) path polygon filter.
+ // path - (out,opt) visited path polygons.
+ // pathCount - (out,opt) Number of polygons visited.
+ // maxPath - (in) max number of polygons in the path array.
+ dtStatus raycast(dtPolyRef startRef, const float* startPos, const float* endPos,
+ const dtQueryFilter* filter,
+ float* t, float* hitNormal, dtPolyRef* path, int* pathCount, const int maxPath) const;
+
+ // Returns distance to nearest wall from the specified location.
+ // Params:
+ // startRef - (in) ref to the polygon where the center lies.
+ // centerPos[3] - (in) center if the query circle.
+ // maxRadius - (in) max search radius.
+ // filter - (in) path polygon filter.
+ // hitDist - (out) distance to nearest wall from the test location.
+ // hitPos[3] - (out) location of the nearest hit.
+ // hitNormal[3] - (out) normal of the nearest hit.
+ dtStatus findDistanceToWall(dtPolyRef startRef, const float* centerPos, const float maxRadius,
+ const dtQueryFilter* filter,
+ float* hitDist, float* hitPos, float* hitNormal) const;
+
+ // Finds polygons found along the navigation graph which touch the specified circle.
+ // Params:
+ // startRef - (in) ref to the polygon where the search starts.
+ // centerPos[3] - (in) center if the query circle.
+ // radius - (in) radius of the query circle.
+ // filter - (in) path polygon filter.
+ // resultRef - (out, opt) refs to the polygons touched by the circle.
+ // resultParent - (out, opt) parent of each result polygon.
+ // resultCost - (out, opt) search cost at each result polygon.
+ // resultCount - (out, opt) Number of results.
+ // maxResult - (int) maximum capacity of search results.
+ dtStatus findPolysAroundCircle(dtPolyRef startRef, const float* centerPos, const float radius,
+ const dtQueryFilter* filter,
+ dtPolyRef* resultRef, dtPolyRef* resultParent, float* resultCost,
+ int* resultCount, const int maxResult) const;
+
+ // Finds polygons found along the navigation graph which touch the convex polygon shape.
+ // Params:
+ // startRef - (in) ref to the polygon where the search starts.
+ // verts[3*n] - (in) vertices describing convex polygon shape (CCW).
+ // nverts - (in) number of vertices in the polygon.
+ // filter - (in) path polygon filter.
+ // resultRef - (out, opt) refs to the polygons touched by the circle.
+ // resultParent - (out, opt) parent of each result polygon.
+ // resultCost - (out, opt) search cost at each result polygon.
+ // resultCount - (out) number of results.
+ // maxResult - (int) maximum capacity of search results.
+ dtStatus findPolysAroundShape(dtPolyRef startRef, const float* verts, const int nverts,
+ const dtQueryFilter* filter,
+ dtPolyRef* resultRef, dtPolyRef* resultParent, float* resultCost,
+ int* resultCount, const int maxResult) const;
+
+ // Finds non-overlapping local neighbourhood around center location.
+ // Note: The algorithm is optimized for small query radius and small number of polygons.
+ // Params:
+ // startRef - (in) ref to the polygon where the search starts.
+ // centerPos[3] - (in) center if the query circle.
+ // radius - (in) radius of the query circle.
+ // filter - (in) path polygon filter.
+ // resultRef - (out) refs to the polygons touched by the circle.
+ // resultParent - (out, opt) parent of each result polygon.
+ // resultCount - (out) number of results.
+ // maxResult - (int) maximum capacity of search results.
+ dtStatus findLocalNeighbourhood(dtPolyRef startRef, const float* centerPos, const float radius,
+ const dtQueryFilter* filter,
+ dtPolyRef* resultRef, dtPolyRef* resultParent,
+ int* resultCount, const int maxResult) const;
+
+ // Returns wall segments of specified polygon.
+ // Params:
+ // ref - (in) ref to the polygon.
+ // filter - (in) path polygon filter.
+ // segments[6*maxSegments] - (out) wall segments (2 endpoints per segment).
+ // segmentCount - (out) number of wall segments.
+ // maxSegments - (in) max number of segments that can be stored in 'segments'.
+ dtStatus getPolyWallSegments(dtPolyRef ref, const dtQueryFilter* filter,
+ float* segments, int* segmentCount, const int maxSegments) const;
+
+ // Returns closest point on navigation polygon.
+ // Uses detail polygons to find the closest point to the navigation polygon surface.
+ // Params:
+ // ref - (in) ref to the polygon.
+ // pos[3] - (in) the point to check.
+ // closest[3] - (out) closest point.
+ // Returns: true if closest point found.
+ dtStatus closestPointOnPoly(dtPolyRef ref, const float* pos, float* closest) const;
+
+ // Returns closest point on navigation polygon boundary.
+ // Uses the navigation polygon boundary to snap the point to poly boundary
+ // if it is outside the polygon. Much faster than closestPointToPoly. Does not affect height.
+ // Params:
+ // ref - (in) ref to the polygon.
+ // pos[3] - (in) the point to check.
+ // closest[3] - (out) closest point.
+ // Returns: true if closest point found.
+ dtStatus closestPointOnPolyBoundary(dtPolyRef ref, const float* pos, float* closest) const;
+
+ // Returns start and end location of an off-mesh link polygon.
+ // Params:
+ // prevRef - (in) ref to the polygon before the link (used to select direction).
+ // polyRef - (in) ref to the off-mesh link polygon.
+ // startPos[3] - (out) start point of the link.
+ // endPos[3] - (out) end point of the link.
+ // Returns: true if link is found.
+ dtStatus getOffMeshConnectionPolyEndPoints(dtPolyRef prevRef, dtPolyRef polyRef, float* startPos, float* endPos) const;
+
+ // Returns height of the polygon at specified location.
+ // Params:
+ // ref - (in) ref to the polygon.
+ // pos[3] - (in) the point where to locate the height.
+ // height - (out) height at the location.
+ // Returns: true if over polygon.
+ dtStatus getPolyHeight(dtPolyRef ref, const float* pos, float* height) const;
+
+ // Returns true if poly reference ins in closed list.
+ bool isInClosedList(dtPolyRef ref) const;
+
+ class dtNodePool* getNodePool() const { return m_nodePool; }
+
+private:
+
+ // Returns neighbour tile based on side.
+ dtMeshTile* getNeighbourTileAt(int x, int y, int side) const;
+
+ // Queries polygons within a tile.
+ int queryPolygonsInTile(const dtMeshTile* tile, const float* qmin, const float* qmax, const dtQueryFilter* filter,
+ dtPolyRef* polys, const int maxPolys) const;
+ // Find nearest polygon within a tile.
+ dtPolyRef findNearestPolyInTile(const dtMeshTile* tile, const float* center, const float* extents,
+ const dtQueryFilter* filter, float* nearestPt) const;
+ // Returns closest point on polygon.
+ dtStatus closestPointOnPolyInTile(const dtMeshTile* tile, const dtPoly* poly, const float* pos, float* closest) const;
+
+ // Returns portal points between two polygons.
+ dtStatus getPortalPoints(dtPolyRef from, dtPolyRef to, float* left, float* right,
+ unsigned char& fromType, unsigned char& toType) const;
+ dtStatus getPortalPoints(dtPolyRef from, const dtPoly* fromPoly, const dtMeshTile* fromTile,
+ dtPolyRef to, const dtPoly* toPoly, const dtMeshTile* toTile,
+ float* left, float* right) const;
+
+ // Returns edge mid point between two polygons.
+ dtStatus getEdgeMidPoint(dtPolyRef from, dtPolyRef to, float* mid) const;
+ dtStatus getEdgeMidPoint(dtPolyRef from, const dtPoly* fromPoly, const dtMeshTile* fromTile,
+ dtPolyRef to, const dtPoly* toPoly, const dtMeshTile* toTile,
+ float* mid) const;
+
+ const dtNavMesh* m_nav; // Pointer to navmesh data.
+
+ struct dtQueryData
+ {
+ dtStatus status;
+ struct dtNode* lastBestNode;
+ float lastBestNodeCost;
+ dtPolyRef startRef, endRef;
+ float startPos[3], endPos[3];
+ const dtQueryFilter* filter;
+ };
+ dtQueryData m_query; // Sliced query state.
+
+ class dtNodePool* m_tinyNodePool; // Pointer to small node pool.
+ class dtNodePool* m_nodePool; // Pointer to node pool.
+ class dtNodeQueue* m_openList; // Pointer to open list queue.
+};
+
+// Helper function to allocate navmesh query class using Detour allocator.
+dtNavMeshQuery* dtAllocNavMeshQuery();
+void dtFreeNavMeshQuery(dtNavMeshQuery* query);
+
+#endif // DETOURNAVMESHQUERY_H
diff --git a/dep/recastnavigation/Detour/DetourNode.cpp b/dep/recastnavigation/Detour/DetourNode.cpp
new file mode 100644
index 00000000000..0d1af837865
--- /dev/null
+++ b/dep/recastnavigation/Detour/DetourNode.cpp
@@ -0,0 +1,164 @@
+//
+// Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
+//
+// This software is provided 'as-is', without any express or implied
+// warranty. In no event will the authors be held liable for any damages
+// arising from the use of this software.
+// Permission is granted to anyone to use this software for any purpose,
+// including commercial applications, and to alter it and redistribute it
+// freely, subject to the following restrictions:
+// 1. The origin of this software must not be misrepresented; you must not
+// claim that you wrote the original software. If you use this software
+// in a product, an acknowledgment in the product documentation would be
+// appreciated but is not required.
+// 2. Altered source versions must be plainly marked as such, and must not be
+// misrepresented as being the original software.
+// 3. This notice may not be removed or altered from any source distribution.
+//
+
+#include "DetourNode.h"
+#include "DetourAlloc.h"
+#include "DetourAssert.h"
+#include "DetourCommon.h"
+#include <string.h>
+
+inline unsigned int dtHashRef(dtPolyRef a)
+{
+ a = (~a) + (a << 18);
+ a = a ^ (a >> 31);
+ a = a * 21;
+ a = a ^ (a >> 11);
+ a = a + (a << 6);
+ a = a ^ (a >> 22);
+ return (unsigned int)a;
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////
+dtNodePool::dtNodePool(int maxNodes, int hashSize) :
+ m_nodes(0),
+ m_first(0),
+ m_next(0),
+ m_maxNodes(maxNodes),
+ m_hashSize(hashSize),
+ m_nodeCount(0)
+{
+ dtAssert(dtNextPow2(m_hashSize) == (unsigned int)m_hashSize);
+ dtAssert(m_maxNodes > 0);
+
+ m_nodes = (dtNode*)dtAlloc(sizeof(dtNode)*m_maxNodes, DT_ALLOC_PERM);
+ m_next = (unsigned short*)dtAlloc(sizeof(unsigned short)*m_maxNodes, DT_ALLOC_PERM);
+ m_first = (unsigned short*)dtAlloc(sizeof(unsigned short)*hashSize, DT_ALLOC_PERM);
+
+ dtAssert(m_nodes);
+ dtAssert(m_next);
+ dtAssert(m_first);
+
+ memset(m_first, 0xff, sizeof(unsigned short)*m_hashSize);
+ memset(m_next, 0xff, sizeof(unsigned short)*m_maxNodes);
+}
+
+dtNodePool::~dtNodePool()
+{
+ dtFree(m_nodes);
+ dtFree(m_next);
+ dtFree(m_first);
+}
+
+void dtNodePool::clear()
+{
+ memset(m_first, 0xff, sizeof(unsigned short)*m_hashSize);
+ m_nodeCount = 0;
+}
+
+dtNode* dtNodePool::findNode(dtPolyRef id)
+{
+ unsigned int bucket = dtHashRef(id) & (m_hashSize-1);
+ unsigned short i = m_first[bucket];
+ while (i != DT_NULL_IDX)
+ {
+ if (m_nodes[i].id == id)
+ return &m_nodes[i];
+ i = m_next[i];
+ }
+ return 0;
+}
+
+dtNode* dtNodePool::getNode(dtPolyRef id)
+{
+ unsigned int bucket = dtHashRef(id) & (m_hashSize-1);
+ unsigned short i = m_first[bucket];
+ dtNode* node = 0;
+ while (i != DT_NULL_IDX)
+ {
+ if (m_nodes[i].id == id)
+ return &m_nodes[i];
+ i = m_next[i];
+ }
+
+ if (m_nodeCount >= m_maxNodes)
+ return 0;
+
+ i = (unsigned short)m_nodeCount;
+ m_nodeCount++;
+
+ // Init node
+ node = &m_nodes[i];
+ node->pidx = 0;
+ node->cost = 0;
+ node->total = 0;
+ node->id = id;
+ node->flags = 0;
+
+ m_next[i] = m_first[bucket];
+ m_first[bucket] = i;
+
+ return node;
+}
+
+
+//////////////////////////////////////////////////////////////////////////////////////////
+dtNodeQueue::dtNodeQueue(int n) :
+ m_heap(0),
+ m_capacity(n),
+ m_size(0)
+{
+ dtAssert(m_capacity > 0);
+
+ m_heap = (dtNode**)dtAlloc(sizeof(dtNode*)*(m_capacity+1), DT_ALLOC_PERM);
+ dtAssert(m_heap);
+}
+
+dtNodeQueue::~dtNodeQueue()
+{
+ dtFree(m_heap);
+}
+
+void dtNodeQueue::bubbleUp(int i, dtNode* node)
+{
+ int parent = (i-1)/2;
+ // note: (index > 0) means there is a parent
+ while ((i > 0) && (m_heap[parent]->total > node->total))
+ {
+ m_heap[i] = m_heap[parent];
+ i = parent;
+ parent = (i-1)/2;
+ }
+ m_heap[i] = node;
+}
+
+void dtNodeQueue::trickleDown(int i, dtNode* node)
+{
+ int child = (i*2)+1;
+ while (child < m_size)
+ {
+ if (((child+1) < m_size) &&
+ (m_heap[child]->total > m_heap[child+1]->total))
+ {
+ child++;
+ }
+ m_heap[i] = m_heap[child];
+ i = child;
+ child = (i*2)+1;
+ }
+ bubbleUp(i, node);
+}
diff --git a/dep/recastnavigation/Detour/DetourNode.h b/dep/recastnavigation/Detour/DetourNode.h
new file mode 100644
index 00000000000..e893f784dcc
--- /dev/null
+++ b/dep/recastnavigation/Detour/DetourNode.h
@@ -0,0 +1,157 @@
+//
+// Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
+//
+// This software is provided 'as-is', without any express or implied
+// warranty. In no event will the authors be held liable for any damages
+// arising from the use of this software.
+// Permission is granted to anyone to use this software for any purpose,
+// including commercial applications, and to alter it and redistribute it
+// freely, subject to the following restrictions:
+// 1. The origin of this software must not be misrepresented; you must not
+// claim that you wrote the original software. If you use this software
+// in a product, an acknowledgment in the product documentation would be
+// appreciated but is not required.
+// 2. Altered source versions must be plainly marked as such, and must not be
+// misrepresented as being the original software.
+// 3. This notice may not be removed or altered from any source distribution.
+//
+
+#ifndef DETOURNODE_H
+#define DETOURNODE_H
+
+#include "DetourNavMesh.h"
+
+enum dtNodeFlags
+{
+ DT_NODE_OPEN = 0x01,
+ DT_NODE_CLOSED = 0x02,
+};
+
+static const unsigned short DT_NULL_IDX = 0xffff;
+
+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.
+ dtPolyRef id; // Polygon ref the node corresponds to.
+};
+
+class dtNodePool
+{
+public:
+ dtNodePool(int maxNodes, int hashSize);
+ ~dtNodePool();
+ inline void operator=(const dtNodePool&) {}
+ void clear();
+ dtNode* getNode(dtPolyRef id);
+ dtNode* findNode(dtPolyRef id);
+
+ inline unsigned int getNodeIdx(const dtNode* node) const
+ {
+ if (!node) return 0;
+ return (unsigned int)(node - m_nodes)+1;
+ }
+
+ inline dtNode* getNodeAtIdx(unsigned int idx)
+ {
+ if (!idx) return 0;
+ return &m_nodes[idx-1];
+ }
+
+ inline const dtNode* getNodeAtIdx(unsigned int idx) const
+ {
+ if (!idx) return 0;
+ return &m_nodes[idx-1];
+ }
+
+ inline int getMemUsed() const
+ {
+ return sizeof(*this) +
+ sizeof(dtNode)*m_maxNodes +
+ sizeof(unsigned short)*m_maxNodes +
+ sizeof(unsigned short)*m_hashSize;
+ }
+
+ inline int getMaxNodes() const { return m_maxNodes; }
+
+ inline int getHashSize() const { return m_hashSize; }
+ inline unsigned short getFirst(int bucket) const { return m_first[bucket]; }
+ inline unsigned short getNext(int i) const { return m_next[i]; }
+
+private:
+
+ dtNode* m_nodes;
+ unsigned short* m_first;
+ unsigned short* m_next;
+ const int m_maxNodes;
+ const int m_hashSize;
+ int m_nodeCount;
+};
+
+class dtNodeQueue
+{
+public:
+ dtNodeQueue(int n);
+ ~dtNodeQueue();
+ inline void operator=(dtNodeQueue&) {}
+
+ inline void clear()
+ {
+ m_size = 0;
+ }
+
+ inline dtNode* top()
+ {
+ return m_heap[0];
+ }
+
+ inline dtNode* pop()
+ {
+ dtNode* result = m_heap[0];
+ m_size--;
+ trickleDown(0, m_heap[m_size]);
+ return result;
+ }
+
+ inline void push(dtNode* node)
+ {
+ m_size++;
+ bubbleUp(m_size-1, node);
+ }
+
+ inline void modify(dtNode* node)
+ {
+ for (int i = 0; i < m_size; ++i)
+ {
+ if (m_heap[i] == node)
+ {
+ bubbleUp(i, node);
+ return;
+ }
+ }
+ }
+
+ inline bool empty() const { return m_size == 0; }
+
+ inline int getMemUsed() const
+ {
+ return sizeof(*this) +
+ sizeof(dtNode*)*(m_capacity+1);
+ }
+
+ inline int getCapacity() const { return m_capacity; }
+
+private:
+ void bubbleUp(int i, dtNode* node);
+ void trickleDown(int i, dtNode* node);
+
+ dtNode** m_heap;
+ const int m_capacity;
+ int m_size;
+};
+
+
+#endif // DETOURNODE_H \ No newline at end of file
diff --git a/dep/recastnavigation/Detour/DetourObstacleAvoidance.cpp b/dep/recastnavigation/Detour/DetourObstacleAvoidance.cpp
new file mode 100644
index 00000000000..a255c9b3fd1
--- /dev/null
+++ b/dep/recastnavigation/Detour/DetourObstacleAvoidance.cpp
@@ -0,0 +1,532 @@
+//
+// Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
+//
+// This software is provided 'as-is', without any express or implied
+// warranty. In no event will the authors be held liable for any damages
+// arising from the use of this software.
+// Permission is granted to anyone to use this software for any purpose,
+// including commercial applications, and to alter it and redistribute it
+// freely, subject to the following restrictions:
+// 1. The origin of this software must not be misrepresented; you must not
+// claim that you wrote the original software. If you use this software
+// in a product, an acknowledgment in the product documentation would be
+// appreciated but is not required.
+// 2. Altered source versions must be plainly marked as such, and must not be
+// misrepresented as being the original software.
+// 3. This notice may not be removed or altered from any source distribution.
+//
+
+#include "DetourObstacleAvoidance.h"
+#include "DetourCommon.h"
+#include "DetourAlloc.h"
+#include "DetourAssert.h"
+#include <string.h>
+#include <math.h>
+#include <float.h>
+#include <new>
+
+
+static int sweepCircleCircle(const float* c0, const float r0, const float* v,
+ const float* c1, const float r1,
+ float& tmin, float& tmax)
+{
+ static const float EPS = 0.0001f;
+ float s[3];
+ dtVsub(s,c1,c0);
+ float r = r0+r1;
+ float c = dtVdot2D(s,s) - r*r;
+ float a = dtVdot2D(v,v);
+ if (a < EPS) return 0; // not moving
+
+ // Overlap, calc time to exit.
+ float b = dtVdot2D(v,s);
+ float d = b*b - a*c;
+ if (d < 0.0f) return 0; // no intersection.
+ a = 1.0f / a;
+ const float rd = dtSqrt(d);
+ tmin = (b - rd) * a;
+ tmax = (b + rd) * a;
+ return 1;
+}
+
+static int isectRaySeg(const float* ap, const float* u,
+ const float* bp, const float* bq,
+ float& t)
+{
+ float v[3], w[3];
+ dtVsub(v,bq,bp);
+ dtVsub(w,ap,bp);
+ float d = dtVperp2D(u,v);
+ if (fabsf(d) < 1e-6f) return 0;
+ d = 1.0f/d;
+ t = dtVperp2D(v,w) * d;
+ if (t < 0 || t > 1) return 0;
+ float s = dtVperp2D(u,w) * d;
+ if (s < 0 || s > 1) return 0;
+ return 1;
+}
+
+
+
+dtObstacleAvoidanceDebugData* dtAllocObstacleAvoidanceDebugData()
+{
+ void* mem = dtAlloc(sizeof(dtObstacleAvoidanceDebugData), DT_ALLOC_PERM);
+ if (!mem) return 0;
+ return new(mem) dtObstacleAvoidanceDebugData;
+}
+
+void dtFreeObstacleAvoidanceDebugData(dtObstacleAvoidanceDebugData* ptr)
+{
+ if (!ptr) return;
+ ptr->~dtObstacleAvoidanceDebugData();
+ dtFree(ptr);
+}
+
+
+dtObstacleAvoidanceDebugData::dtObstacleAvoidanceDebugData() :
+ m_nsamples(0),
+ m_maxSamples(0),
+ m_vel(0),
+ m_ssize(0),
+ m_pen(0),
+ m_vpen(0),
+ m_vcpen(0),
+ m_spen(0),
+ m_tpen(0)
+{
+}
+
+dtObstacleAvoidanceDebugData::~dtObstacleAvoidanceDebugData()
+{
+ dtFree(m_vel);
+ dtFree(m_ssize);
+ dtFree(m_pen);
+ dtFree(m_vpen);
+ dtFree(m_vcpen);
+ dtFree(m_spen);
+ dtFree(m_tpen);
+}
+
+bool dtObstacleAvoidanceDebugData::init(const int maxSamples)
+{
+ dtAssert(maxSamples);
+ m_maxSamples = maxSamples;
+
+ m_vel = (float*)dtAlloc(sizeof(float)*3*m_maxSamples, DT_ALLOC_PERM);
+ if (!m_vel)
+ return false;
+ m_pen = (float*)dtAlloc(sizeof(float)*m_maxSamples, DT_ALLOC_PERM);
+ if (!m_pen)
+ return false;
+ m_ssize = (float*)dtAlloc(sizeof(float)*m_maxSamples, DT_ALLOC_PERM);
+ if (!m_ssize)
+ return false;
+ m_vpen = (float*)dtAlloc(sizeof(float)*m_maxSamples, DT_ALLOC_PERM);
+ if (!m_vpen)
+ return false;
+ m_vcpen = (float*)dtAlloc(sizeof(float)*m_maxSamples, DT_ALLOC_PERM);
+ if (!m_vcpen)
+ return false;
+ m_spen = (float*)dtAlloc(sizeof(float)*m_maxSamples, DT_ALLOC_PERM);
+ if (!m_spen)
+ return false;
+ m_tpen = (float*)dtAlloc(sizeof(float)*m_maxSamples, DT_ALLOC_PERM);
+ if (!m_tpen)
+ return false;
+
+ return true;
+}
+
+void dtObstacleAvoidanceDebugData::reset()
+{
+ m_nsamples = 0;
+}
+
+void dtObstacleAvoidanceDebugData::addSample(const float* vel, const float ssize, const float pen,
+ const float vpen, const float vcpen, const float spen, const float tpen)
+{
+ if (m_nsamples >= m_maxSamples)
+ return;
+ dtAssert(m_vel);
+ dtAssert(m_ssize);
+ dtAssert(m_pen);
+ dtAssert(m_vpen);
+ dtAssert(m_vcpen);
+ dtAssert(m_spen);
+ dtAssert(m_tpen);
+ dtVcopy(&m_vel[m_nsamples*3], vel);
+ m_ssize[m_nsamples] = ssize;
+ m_pen[m_nsamples] = pen;
+ m_vpen[m_nsamples] = vpen;
+ m_vcpen[m_nsamples] = vcpen;
+ m_spen[m_nsamples] = spen;
+ m_tpen[m_nsamples] = tpen;
+ m_nsamples++;
+}
+
+static void normalizeArray(float* arr, const int n)
+{
+ // Normalize penaly range.
+ float minPen = FLT_MAX;
+ float maxPen = -FLT_MAX;
+ for (int i = 0; i < n; ++i)
+ {
+ minPen = dtMin(minPen, arr[i]);
+ maxPen = dtMax(maxPen, arr[i]);
+ }
+ const float penRange = maxPen-minPen;
+ const float s = penRange > 0.001f ? (1.0f / penRange) : 1;
+ for (int i = 0; i < n; ++i)
+ arr[i] = dtClamp((arr[i]-minPen)*s, 0.0f, 1.0f);
+}
+
+void dtObstacleAvoidanceDebugData::normalizeSamples()
+{
+ normalizeArray(m_pen, m_nsamples);
+ normalizeArray(m_vpen, m_nsamples);
+ normalizeArray(m_vcpen, m_nsamples);
+ normalizeArray(m_spen, m_nsamples);
+ normalizeArray(m_tpen, m_nsamples);
+}
+
+
+dtObstacleAvoidanceQuery* dtAllocObstacleAvoidanceQuery()
+{
+ void* mem = dtAlloc(sizeof(dtObstacleAvoidanceQuery), DT_ALLOC_PERM);
+ if (!mem) return 0;
+ return new(mem) dtObstacleAvoidanceQuery;
+}
+
+void dtFreeObstacleAvoidanceQuery(dtObstacleAvoidanceQuery* ptr)
+{
+ if (!ptr) return;
+ ptr->~dtObstacleAvoidanceQuery();
+ dtFree(ptr);
+}
+
+
+dtObstacleAvoidanceQuery::dtObstacleAvoidanceQuery() :
+ m_velBias(0.0f),
+ m_weightDesVel(0.0f),
+ m_weightCurVel(0.0f),
+ m_weightSide(0.0f),
+ m_weightToi(0.0f),
+ m_horizTime(0.0f),
+ m_maxCircles(0),
+ m_circles(0),
+ m_ncircles(0),
+ m_maxSegments(0),
+ m_segments(0),
+ m_nsegments(0)
+{
+}
+
+dtObstacleAvoidanceQuery::~dtObstacleAvoidanceQuery()
+{
+ dtFree(m_circles);
+ dtFree(m_segments);
+}
+
+bool dtObstacleAvoidanceQuery::init(const int maxCircles, const int maxSegments)
+{
+ m_maxCircles = maxCircles;
+ m_ncircles = 0;
+ m_circles = (dtObstacleCircle*)dtAlloc(sizeof(dtObstacleCircle)*m_maxCircles, DT_ALLOC_PERM);
+ if (!m_circles)
+ return false;
+ memset(m_circles, 0, sizeof(dtObstacleCircle)*m_maxCircles);
+
+ m_maxSegments = maxSegments;
+ m_nsegments = 0;
+ m_segments = (dtObstacleSegment*)dtAlloc(sizeof(dtObstacleSegment)*m_maxSegments, DT_ALLOC_PERM);
+ if (!m_segments)
+ return false;
+ memset(m_segments, 0, sizeof(dtObstacleSegment)*m_maxSegments);
+
+ return true;
+}
+
+void dtObstacleAvoidanceQuery::reset()
+{
+ m_ncircles = 0;
+ m_nsegments = 0;
+}
+
+void dtObstacleAvoidanceQuery::addCircle(const float* pos, const float rad,
+ const float* vel, const float* dvel)
+{
+ if (m_ncircles >= m_maxCircles)
+ return;
+
+ dtObstacleCircle* cir = &m_circles[m_ncircles++];
+ dtVcopy(cir->p, pos);
+ cir->rad = rad;
+ dtVcopy(cir->vel, vel);
+ dtVcopy(cir->dvel, dvel);
+}
+
+void dtObstacleAvoidanceQuery::addSegment(const float* p, const float* q)
+{
+ if (m_nsegments > m_maxSegments)
+ return;
+
+ dtObstacleSegment* seg = &m_segments[m_nsegments++];
+ dtVcopy(seg->p, p);
+ dtVcopy(seg->q, q);
+}
+
+void dtObstacleAvoidanceQuery::prepare(const float* pos, const float* dvel)
+{
+ // Prepare obstacles
+ for (int i = 0; i < m_ncircles; ++i)
+ {
+ dtObstacleCircle* cir = &m_circles[i];
+
+ // Side
+ const float* pa = pos;
+ const float* pb = cir->p;
+
+ const float orig[3] = {0,0};
+ float dv[3];
+ dtVsub(cir->dp,pb,pa);
+ dtVnormalize(cir->dp);
+ dtVsub(dv, cir->dvel, dvel);
+
+ const float a = dtTriArea2D(orig, cir->dp,dv);
+ if (a < 0.01f)
+ {
+ cir->np[0] = -cir->dp[2];
+ cir->np[2] = cir->dp[0];
+ }
+ else
+ {
+ cir->np[0] = cir->dp[2];
+ cir->np[2] = -cir->dp[0];
+ }
+ }
+
+ for (int i = 0; i < m_nsegments; ++i)
+ {
+ dtObstacleSegment* seg = &m_segments[i];
+
+ // Precalc if the agent is really close to the segment.
+ const float r = 0.01f;
+ float t;
+ seg->touch = dtDistancePtSegSqr2D(pos, seg->p, seg->q, t) < dtSqr(r);
+ }
+}
+
+float dtObstacleAvoidanceQuery::processSample(const float* vcand, const float cs,
+ const float* pos, const float rad,
+ const float vmax, const float* vel, const float* dvel,
+ dtObstacleAvoidanceDebugData* debug)
+{
+ // Find min time of impact and exit amongst all obstacles.
+ float tmin = m_horizTime;
+ float side = 0;
+ int nside = 0;
+
+ for (int i = 0; i < m_ncircles; ++i)
+ {
+ const dtObstacleCircle* cir = &m_circles[i];
+
+ // RVO
+ float vab[3];
+ dtVscale(vab, vcand, 2);
+ dtVsub(vab, vab, vel);
+ dtVsub(vab, vab, cir->vel);
+
+ // Side
+ side += dtClamp(dtMin(dtVdot2D(cir->dp,vab)*0.5f+0.5f, dtVdot2D(cir->np,vab)*2), 0.0f, 1.0f);
+ nside++;
+
+ float htmin = 0, htmax = 0;
+ if (!sweepCircleCircle(pos,rad, vab, cir->p,cir->rad, htmin, htmax))
+ continue;
+
+ // Handle overlapping obstacles.
+ if (htmin < 0.0f && htmax > 0.0f)
+ {
+ // Avoid more when overlapped.
+ htmin = -htmin * 0.5f;
+ }
+
+ if (htmin >= 0.0f)
+ {
+ // The closest obstacle is somewhere ahead of us, keep track of nearest obstacle.
+ if (htmin < tmin)
+ tmin = htmin;
+ }
+ }
+
+ for (int i = 0; i < m_nsegments; ++i)
+ {
+ const dtObstacleSegment* seg = &m_segments[i];
+ float htmin = 0;
+
+ if (seg->touch)
+ {
+ // Special case when the agent is very close to the segment.
+ float sdir[3], snorm[3];
+ dtVsub(sdir, seg->q, seg->p);
+ snorm[0] = -sdir[2];
+ snorm[2] = sdir[0];
+ // If the velocity is pointing towards the segment, no collision.
+ if (dtVdot2D(snorm, vcand) < 0.0f)
+ continue;
+ // Else immediate collision.
+ htmin = 0.0f;
+ }
+ else
+ {
+ if (!isectRaySeg(pos, vcand, seg->p, seg->q, htmin))
+ continue;
+ }
+
+ // Avoid less when facing walls.
+ htmin *= 2.0f;
+
+ // The closest obstacle is somewhere ahead of us, keep track of nearest obstacle.
+ if (htmin < tmin)
+ tmin = htmin;
+ }
+
+ // Normalize side bias, to prevent it dominating too much.
+ if (nside)
+ side /= nside;
+
+ const float ivmax = 1.0f / vmax;
+ const float vpen = m_weightDesVel * (dtVdist2D(vcand, dvel) * ivmax);
+ const float vcpen = m_weightCurVel * (dtVdist2D(vcand, vel) * ivmax);
+ const float spen = m_weightSide * side;
+ const float tpen = m_weightToi * (1.0f/(0.1f+tmin / m_horizTime));
+
+ const float penalty = vpen + vcpen + spen + tpen;
+
+ // Store different penalties for debug viewing
+ if (debug)
+ debug->addSample(vcand, cs, penalty, vpen, vcpen, spen, tpen);
+
+ return penalty;
+}
+
+void dtObstacleAvoidanceQuery::sampleVelocityGrid(const float* pos, const float rad, const float vmax,
+ const float* vel, const float* dvel,
+ float* nvel, const int gsize,
+ dtObstacleAvoidanceDebugData* debug)
+{
+ prepare(pos, dvel);
+
+ dtVset(nvel, 0,0,0);
+
+ if (debug)
+ debug->reset();
+
+ const float cvx = dvel[0] * m_velBias;
+ const float cvz = dvel[2] * m_velBias;
+ const float cs = vmax * 2 * (1 - m_velBias) / (float)(gsize-1);
+ const float half = (gsize-1)*cs*0.5f;
+
+ float minPenalty = FLT_MAX;
+
+ for (int y = 0; y < gsize; ++y)
+ {
+ for (int x = 0; x < gsize; ++x)
+ {
+ float vcand[3];
+ vcand[0] = cvx + x*cs - half;
+ vcand[1] = 0;
+ vcand[2] = cvz + y*cs - half;
+
+ if (dtSqr(vcand[0])+dtSqr(vcand[2]) > dtSqr(vmax+cs/2)) continue;
+
+ const float penalty = processSample(vcand, cs, pos,rad,vmax,vel,dvel, debug);
+ if (penalty < minPenalty)
+ {
+ minPenalty = penalty;
+ dtVcopy(nvel, vcand);
+ }
+ }
+ }
+}
+
+
+static const float DT_PI = 3.14159265f;
+
+void dtObstacleAvoidanceQuery::sampleVelocityAdaptive(const float* pos, const float rad, const float vmax,
+ const float* vel, const float* dvel, float* nvel,
+ const int ndivs, const int nrings, const int depth,
+ dtObstacleAvoidanceDebugData* debug)
+{
+ prepare(pos, dvel);
+
+ dtVset(nvel, 0,0,0);
+
+ if (debug)
+ debug->reset();
+
+ // Build sampling pattern aligned to desired velocity.
+ static const int MAX_PATTERN_DIVS = 32;
+ static const int MAX_PATTERN_RINGS = 4;
+ float pat[(MAX_PATTERN_DIVS*MAX_PATTERN_RINGS+1)*2];
+ int npat = 0;
+
+ const int nd = dtClamp(ndivs, 1, MAX_PATTERN_DIVS);
+ const int nr = dtClamp(nrings, 1, MAX_PATTERN_RINGS);
+ const float da = (1.0f/nd) * DT_PI*2;
+ const float dang = atan2f(dvel[2], dvel[0]);
+
+ // Always add sample at zero
+ pat[npat*2+0] = 0;
+ pat[npat*2+1] = 0;
+ npat++;
+
+ for (int j = 0; j < nr; ++j)
+ {
+ const float rad = (float)(nr-j)/(float)nr;
+ float a = dang + (j&1)*0.5f*da;
+ for (int i = 0; i < nd; ++i)
+ {
+ pat[npat*2+0] = cosf(a)*rad;
+ pat[npat*2+1] = sinf(a)*rad;
+ npat++;
+ a += da;
+ }
+ }
+
+ // Start sampling.
+ float cr = vmax * (1.0f-m_velBias);
+ float res[3];
+ dtVset(res, dvel[0] * m_velBias, 0, dvel[2] * m_velBias);
+
+ for (int k = 0; k < depth; ++k)
+ {
+ float minPenalty = FLT_MAX;
+ float bvel[3];
+ dtVset(bvel, 0,0,0);
+
+ for (int i = 0; i < npat; ++i)
+ {
+ float vcand[3];
+ vcand[0] = res[0] + pat[i*2+0]*cr;
+ vcand[1] = 0;
+ vcand[2] = res[2] + pat[i*2+1]*cr;
+
+ if (dtSqr(vcand[0])+dtSqr(vcand[2]) > dtSqr(vmax+0.001f)) continue;
+
+ const float penalty = processSample(vcand,cr/10, pos,rad,vmax,vel,dvel, debug);
+ if (penalty < minPenalty)
+ {
+ minPenalty = penalty;
+ dtVcopy(bvel, vcand);
+ }
+ }
+
+ dtVcopy(res, bvel);
+
+ cr *= 0.5f;
+ }
+
+ dtVcopy(nvel, res);
+}
+
diff --git a/dep/recastnavigation/Detour/DetourObstacleAvoidance.h b/dep/recastnavigation/Detour/DetourObstacleAvoidance.h
new file mode 100644
index 00000000000..4a7187a7998
--- /dev/null
+++ b/dep/recastnavigation/Detour/DetourObstacleAvoidance.h
@@ -0,0 +1,148 @@
+//
+// Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
+//
+// This software is provided 'as-is', without any express or implied
+// warranty. In no event will the authors be held liable for any damages
+// arising from the use of this software.
+// Permission is granted to anyone to use this software for any purpose,
+// including commercial applications, and to alter it and redistribute it
+// freely, subject to the following restrictions:
+// 1. The origin of this software must not be misrepresented; you must not
+// claim that you wrote the original software. If you use this software
+// in a product, an acknowledgment in the product documentation would be
+// appreciated but is not required.
+// 2. Altered source versions must be plainly marked as such, and must not be
+// misrepresented as being the original software.
+// 3. This notice may not be removed or altered from any source distribution.
+//
+
+#ifndef DETOUROBSTACLEAVOIDANCE_H
+#define DETOUROBSTACLEAVOIDANCE_H
+
+struct dtObstacleCircle
+{
+ float p[3]; // Position of the obstacle
+ float vel[3]; // Velocity of the obstacle
+ float dvel[3]; // Velocity of the obstacle
+ float rad; // Radius of the obstacle
+ float dp[3], np[3]; // Use for side selection during sampling.
+};
+
+struct dtObstacleSegment
+{
+ float p[3], q[3]; // End points of the obstacle segment
+ bool touch;
+};
+
+static const int RVO_SAMPLE_RAD = 15;
+static const int MAX_RVO_SAMPLES = (RVO_SAMPLE_RAD*2+1)*(RVO_SAMPLE_RAD*2+1) + 100;
+
+class dtObstacleAvoidanceDebugData
+{
+public:
+ dtObstacleAvoidanceDebugData();
+ ~dtObstacleAvoidanceDebugData();
+
+ bool init(const int maxSamples);
+ void reset();
+ void addSample(const float* vel, const float ssize, const float pen,
+ const float vpen, const float vcpen, const float spen, const float tpen);
+
+ void normalizeSamples();
+
+ inline int getSampleCount() const { return m_nsamples; }
+ inline const float* getSampleVelocity(const int i) const { return &m_vel[i*3]; }
+ inline float getSampleSize(const int i) const { return m_ssize[i]; }
+ inline float getSamplePenalty(const int i) const { return m_pen[i]; }
+ inline float getSampleDesiredVelocityPenalty(const int i) const { return m_vpen[i]; }
+ inline float getSampleCurrentVelocityPenalty(const int i) const { return m_vcpen[i]; }
+ inline float getSamplePreferredSidePenalty(const int i) const { return m_spen[i]; }
+ inline float getSampleCollisionTimePenalty(const int i) const { return m_tpen[i]; }
+
+private:
+ int m_nsamples;
+ int m_maxSamples;
+ float* m_vel;
+ float* m_ssize;
+ float* m_pen;
+ float* m_vpen;
+ float* m_vcpen;
+ float* m_spen;
+ float* m_tpen;
+};
+
+dtObstacleAvoidanceDebugData* dtAllocObstacleAvoidanceDebugData();
+void dtFreeObstacleAvoidanceDebugData(dtObstacleAvoidanceDebugData* ptr);
+
+
+class dtObstacleAvoidanceQuery
+{
+public:
+ dtObstacleAvoidanceQuery();
+ ~dtObstacleAvoidanceQuery();
+
+ bool init(const int maxCircles, const int maxSegments);
+
+ void reset();
+
+ void addCircle(const float* pos, const float rad,
+ const float* vel, const float* dvel);
+
+ void addSegment(const float* p, const float* q);
+
+ inline void setVelocitySelectionBias(float v) { m_velBias = v; }
+ inline void setDesiredVelocityWeight(float w) { m_weightDesVel = w; }
+ inline void setCurrentVelocityWeight(float w) { m_weightCurVel = w; }
+ inline void setPreferredSideWeight(float w) { m_weightSide = w; }
+ inline void setCollisionTimeWeight(float w) { m_weightToi = w; }
+ inline void setTimeHorizon(float t) { m_horizTime = t; }
+
+ void sampleVelocityGrid(const float* pos, const float rad, const float vmax,
+ const float* vel, const float* dvel, float* nvel,
+ const int gsize,
+ dtObstacleAvoidanceDebugData* debug = 0);
+
+ void sampleVelocityAdaptive(const float* pos, const float rad, const float vmax,
+ const float* vel, const float* dvel, float* nvel,
+ const int ndivs, const int nrings, const int depth,
+ dtObstacleAvoidanceDebugData* debug = 0);
+
+ inline int getObstacleCircleCount() const { return m_ncircles; }
+ const dtObstacleCircle* getObstacleCircle(const int i) { return &m_circles[i]; }
+
+ inline int getObstacleSegmentCount() const { return m_nsegments; }
+ const dtObstacleSegment* getObstacleSegment(const int i) { return &m_segments[i]; }
+
+private:
+
+ void prepare(const float* pos, const float* dvel);
+
+ float processSample(const float* vcand, const float cs,
+ const float* pos, const float rad,
+ const float vmax, const float* vel, const float* dvel,
+ dtObstacleAvoidanceDebugData* debug);
+
+ dtObstacleCircle* insertCircle(const float dist);
+ dtObstacleSegment* insertSegment(const float dist);
+
+ float m_velBias;
+ float m_weightDesVel;
+ float m_weightCurVel;
+ float m_weightSide;
+ float m_weightToi;
+ float m_horizTime;
+
+ int m_maxCircles;
+ dtObstacleCircle* m_circles;
+ int m_ncircles;
+
+ int m_maxSegments;
+ dtObstacleSegment* m_segments;
+ int m_nsegments;
+};
+
+dtObstacleAvoidanceQuery* dtAllocObstacleAvoidanceQuery();
+void dtFreeObstacleAvoidanceQuery(dtObstacleAvoidanceQuery* ptr);
+
+
+#endif // DETOUROBSTACLEAVOIDANCE_H \ No newline at end of file
diff --git a/dep/recastnavigation/License.txt b/dep/recastnavigation/License.txt
new file mode 100644
index 00000000000..95f4bfc9654
--- /dev/null
+++ b/dep/recastnavigation/License.txt
@@ -0,0 +1,18 @@
+Copyright (c) 2009 Mikko Mononen memon@inside.org
+
+This software is provided 'as-is', without any express or implied
+warranty. In no event will the authors be held liable for any damages
+arising from the use of this software.
+
+Permission is granted to anyone to use this software for any purpose,
+including commercial applications, and to alter it and redistribute it
+freely, subject to the following restrictions:
+
+1. The origin of this software must not be misrepresented; you must not
+claim that you wrote the original software. If you use this software
+in a product, an acknowledgment in the product documentation would be
+appreciated but is not required.
+2. Altered source versions must be plainly marked as such, and must not be
+misrepresented as being the original software.
+3. This notice may not be removed or altered from any source distribution.
+
diff --git a/dep/recastnavigation/Readme.txt b/dep/recastnavigation/Readme.txt
new file mode 100644
index 00000000000..0c2f7b1675f
--- /dev/null
+++ b/dep/recastnavigation/Readme.txt
@@ -0,0 +1,120 @@
+
+Recast & Detour Version 1.4
+
+
+Recast
+
+Recast is state of the art navigation mesh construction toolset for games.
+
+ * It is automatic, which means that you can throw any level geometry
+ at it and you will get robust mesh out
+ * It is fast which means swift turnaround times for level designers
+ * It is open source so it comes with full source and you can
+ customize it to your hearts content.
+
+The Recast process starts with constructing a voxel mold from a level geometry
+and then casting a navigation mesh over it. The process consists of three steps,
+building the voxel mold, partitioning the mold into simple regions, peeling off
+the regions as simple polygons.
+
+ 1. The voxel mold is build from the input triangle mesh by rasterizing
+ the triangles into a multi-layer heightfield. Some simple filters are
+ then applied to the mold to prune out locations where the character
+ would not be able to move.
+ 2. The walkable areas described by the mold are divided into simple
+ overlayed 2D regions. The resulting regions have only one non-overlapping
+ contour, which simplifies the final step of the process tremendously.
+ 3. The navigation polygons are peeled off from the regions by first tracing
+ the boundaries and then simplifying them. The resulting polygons are
+ finally converted to convex polygons which makes them perfect for
+ pathfinding and spatial reasoning about the level.
+
+The toolset code is located in the Recast folder and demo application using the Recast
+toolset is located in the RecastDemo folder.
+
+The project files with this distribution can be compiled with Microsoft Visual C++ 2008
+(you can download it for free) and XCode 3.1.
+
+
+Detour
+
+Recast is accompanied with Detour, path-finding and spatial reasoning toolkit. You can use any navigation mesh with Detour, but of course the data generated with Recast fits perfectly.
+
+Detour offers simple static navigation mesh which is suitable for many simple cases, as well as tiled navigation mesh which allows you to plug in and out pieces of the mesh. The tiled mesh allows to create systems where you stream new navigation data in and out as the player progresses the level, or you may regenerate tiles as the world changes.
+
+
+Latest code available at http://code.google.com/p/recastnavigation/
+
+
+--
+
+Release Notes
+
+----------------
+* Recast 1.4
+ Released August 24th, 2009
+
+- Added detail height mesh generation (RecastDetailMesh.cpp) for single,
+ tiled statmeshes as well as tilemesh.
+- Added feature to contour tracing which detects extra vertices along
+ tile edges which should be removed later.
+- Changed the tiled stat mesh preprocess, so that it first generated
+ polymeshes per tile and finally combines them.
+- Fixed bug in the GUI code where invisible buttons could be pressed.
+
+----------------
+* Recast 1.31
+ Released July 24th, 2009
+
+- Better cost and heuristic functions.
+- Fixed tile navmesh raycast on tile borders.
+
+----------------
+* Recast 1.3
+ Released July 14th, 2009
+
+- Added dtTileNavMesh which allows to dynamically add and remove navmesh pieces at runtime.
+- Renamed stat navmesh types to dtStat* (i.e. dtPoly is now dtStatPoly).
+- Moved common code used by tile and stat navmesh to DetourNode.h/cpp and DetourCommon.h/cpp.
+- Refactores the demo code.
+
+----------------
+* Recast 1.2
+ Released June 17th, 2009
+
+- Added tiled mesh generation. The tiled generation allows to generate navigation for
+ much larger worlds, it removes some of the artifacts that comes from distance fields
+ in open areas, and allows later streaming and dynamic runtime generation
+- Improved and added some debug draw modes
+- API change: The helper function rcBuildNavMesh does not exists anymore,
+ had to change few internal things to cope with the tiled processing,
+ similar API functionality will be added later once the tiled process matures
+- The demo is getting way too complicated, need to split demos
+- Fixed several filtering functions so that the mesh is tighter to the geometry,
+ sometimes there could be up error up to tow voxel units close to walls,
+ now it should be just one.
+
+----------------
+* Recast 1.1
+ Released April 11th, 2009
+
+This is the first release of Detour.
+
+----------------
+* Recast 1.0
+ Released March 29th, 2009
+
+This is the first release of Recast.
+
+The process is not always as robust as I would wish. The watershed phase sometimes swallows tiny islands
+which are close to edges. These droppings are handled in rcBuildContours, but the code is not
+particularly robust either.
+
+Another non-robust case is when portal contours (contours shared between two regions) are always
+assumed to be straight. That can lead to overlapping contours specially when the level has
+large open areas.
+
+
+
+Mikko Mononen
+memon@inside.org
diff --git a/dep/recastnavigation/Recast/CMakeLists.txt b/dep/recastnavigation/Recast/CMakeLists.txt
new file mode 100644
index 00000000000..726aff72c0c
--- /dev/null
+++ b/dep/recastnavigation/Recast/CMakeLists.txt
@@ -0,0 +1,31 @@
+# Copyright (C) 2008-2013 TrinityCore <http://www.trinitycore.org/>
+#
+# This file is free software; as a special exception the author gives
+# unlimited permission to copy and/or distribute it, with or without
+# modifications, as long as this notice is preserved.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY, to the extent permitted by law; without even the
+# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+set(Recast_STAT_SRCS
+ Recast.cpp
+ RecastAlloc.cpp
+ RecastArea.cpp
+ RecastContour.cpp
+ RecastFilter.cpp
+ RecastMesh.cpp
+ RecastMeshDetail.cpp
+ RecastRasterization.cpp
+ RecastRegion.cpp
+)
+
+if(WIN32)
+ include_directories(
+ ${CMAKE_SOURCE_DIR}/dep/zlib
+ )
+endif()
+
+add_library(Recast STATIC ${Recast_STAT_SRCS})
+
+target_link_libraries(Recast ${ZLIB_LIBRARIES}) \ No newline at end of file
diff --git a/dep/recastnavigation/Recast/Recast.cpp b/dep/recastnavigation/Recast/Recast.cpp
new file mode 100644
index 00000000000..d051418e810
--- /dev/null
+++ b/dep/recastnavigation/Recast/Recast.cpp
@@ -0,0 +1,423 @@
+//
+// Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
+//
+// This software is provided 'as-is', without any express or implied
+// warranty. In no event will the authors be held liable for any damages
+// arising from the use of this software.
+// Permission is granted to anyone to use this software for any purpose,
+// including commercial applications, and to alter it and redistribute it
+// freely, subject to the following restrictions:
+// 1. The origin of this software must not be misrepresented; you must not
+// claim that you wrote the original software. If you use this software
+// in a product, an acknowledgment in the product documentation would be
+// appreciated but is not required.
+// 2. Altered source versions must be plainly marked as such, and must not be
+// misrepresented as being the original software.
+// 3. This notice may not be removed or altered from any source distribution.
+//
+
+#include <float.h>
+#define _USE_MATH_DEFINES
+#include <math.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include "Recast.h"
+#include "RecastAlloc.h"
+#include "RecastAssert.h"
+
+float rcSqrt(float x)
+{
+ return sqrtf(x);
+}
+
+
+void rcContext::log(const rcLogCategory category, const char* format, ...)
+{
+ if (!m_logEnabled)
+ return;
+ static const int MSG_SIZE = 512;
+ char msg[MSG_SIZE];
+ va_list ap;
+ va_start(ap, format);
+ int len = vsnprintf(msg, MSG_SIZE, format, ap);
+ if (len >= MSG_SIZE)
+ {
+ len = MSG_SIZE-1;
+ msg[MSG_SIZE-1] = '\0';
+ }
+ va_end(ap);
+ doLog(category, msg, len);
+}
+
+rcHeightfield* rcAllocHeightfield()
+{
+ rcHeightfield* hf = (rcHeightfield*)rcAlloc(sizeof(rcHeightfield), RC_ALLOC_PERM);
+ memset(hf, 0, sizeof(rcHeightfield));
+ return hf;
+}
+
+void rcFreeHeightField(rcHeightfield* hf)
+{
+ if (!hf) return;
+ // Delete span array.
+ rcFree(hf->spans);
+ // Delete span pools.
+ while (hf->pools)
+ {
+ rcSpanPool* next = hf->pools->next;
+ rcFree(hf->pools);
+ hf->pools = next;
+ }
+ rcFree(hf);
+}
+
+rcCompactHeightfield* rcAllocCompactHeightfield()
+{
+ rcCompactHeightfield* chf = (rcCompactHeightfield*)rcAlloc(sizeof(rcCompactHeightfield), RC_ALLOC_PERM);
+ memset(chf, 0, sizeof(rcCompactHeightfield));
+ return chf;
+}
+
+void rcFreeCompactHeightfield(rcCompactHeightfield* chf)
+{
+ if (!chf) return;
+ rcFree(chf->cells);
+ rcFree(chf->spans);
+ rcFree(chf->dist);
+ rcFree(chf->areas);
+ rcFree(chf);
+}
+
+rcContourSet* rcAllocContourSet()
+{
+ rcContourSet* cset = (rcContourSet*)rcAlloc(sizeof(rcContourSet), RC_ALLOC_PERM);
+ memset(cset, 0, sizeof(rcContourSet));
+ return cset;
+}
+
+void rcFreeContourSet(rcContourSet* cset)
+{
+ if (!cset) return;
+ for (int i = 0; i < cset->nconts; ++i)
+ {
+ rcFree(cset->conts[i].verts);
+ rcFree(cset->conts[i].rverts);
+ }
+ rcFree(cset->conts);
+ rcFree(cset);
+}
+
+rcPolyMesh* rcAllocPolyMesh()
+{
+ rcPolyMesh* pmesh = (rcPolyMesh*)rcAlloc(sizeof(rcPolyMesh), RC_ALLOC_PERM);
+ memset(pmesh, 0, sizeof(rcPolyMesh));
+ return pmesh;
+}
+
+void rcFreePolyMesh(rcPolyMesh* pmesh)
+{
+ if (!pmesh) return;
+ rcFree(pmesh->verts);
+ rcFree(pmesh->polys);
+ rcFree(pmesh->regs);
+ rcFree(pmesh->flags);
+ rcFree(pmesh->areas);
+ rcFree(pmesh);
+}
+
+rcPolyMeshDetail* rcAllocPolyMeshDetail()
+{
+ rcPolyMeshDetail* dmesh = (rcPolyMeshDetail*)rcAlloc(sizeof(rcPolyMeshDetail), RC_ALLOC_PERM);
+ memset(dmesh, 0, sizeof(rcPolyMeshDetail));
+ return dmesh;
+}
+
+void rcFreePolyMeshDetail(rcPolyMeshDetail* dmesh)
+{
+ if (!dmesh) return;
+ rcFree(dmesh->meshes);
+ rcFree(dmesh->verts);
+ rcFree(dmesh->tris);
+ rcFree(dmesh);
+}
+
+
+void rcCalcBounds(const float* verts, int nv, float* bmin, float* bmax)
+{
+ // Calculate bounding box.
+ rcVcopy(bmin, verts);
+ rcVcopy(bmax, verts);
+ for (int i = 1; i < nv; ++i)
+ {
+ const float* v = &verts[i*3];
+ rcVmin(bmin, v);
+ rcVmax(bmax, v);
+ }
+}
+
+void rcCalcGridSize(const float* bmin, const float* bmax, float cs, int* w, int* h)
+{
+ *w = (int)((bmax[0] - bmin[0])/cs+0.5f);
+ *h = (int)((bmax[2] - bmin[2])/cs+0.5f);
+}
+
+bool rcCreateHeightfield(rcContext* /*ctx*/, rcHeightfield& hf, int width, int height,
+ const float* bmin, const float* bmax,
+ float cs, float ch)
+{
+ // TODO: VC complains about unref formal variable, figure out a way to handle this better.
+// rcAssert(ctx);
+
+ hf.width = width;
+ hf.height = height;
+ rcVcopy(hf.bmin, bmin);
+ rcVcopy(hf.bmax, bmax);
+ hf.cs = cs;
+ hf.ch = ch;
+ hf.spans = (rcSpan**)rcAlloc(sizeof(rcSpan*)*hf.width*hf.height, RC_ALLOC_PERM);
+ if (!hf.spans)
+ return false;
+ memset(hf.spans, 0, sizeof(rcSpan*)*hf.width*hf.height);
+ return true;
+}
+
+static void calcTriNormal(const float* v0, const float* v1, const float* v2, float* norm)
+{
+ float e0[3], e1[3];
+ rcVsub(e0, v1, v0);
+ rcVsub(e1, v2, v0);
+ rcVcross(norm, e0, e1);
+ rcVnormalize(norm);
+}
+
+void rcMarkWalkableTriangles(rcContext* /*ctx*/, const float walkableSlopeAngle,
+ const float* verts, int /*nv*/,
+ const int* tris, int nt,
+ unsigned char* areas)
+{
+ // TODO: VC complains about unref formal variable, figure out a way to handle this better.
+// rcAssert(ctx);
+
+ const float walkableThr = cosf(walkableSlopeAngle/180.0f*RC_PI);
+
+ float norm[3];
+
+ for (int i = 0; i < nt; ++i)
+ {
+ const int* tri = &tris[i*3];
+ calcTriNormal(&verts[tri[0]*3], &verts[tri[1]*3], &verts[tri[2]*3], norm);
+ // Check if the face is walkable.
+ if (norm[1] > walkableThr)
+ areas[i] = RC_WALKABLE_AREA;
+ }
+}
+
+void rcClearUnwalkableTriangles(rcContext* /*ctx*/, const float walkableSlopeAngle,
+ const float* verts, int /*nv*/,
+ const int* tris, int nt,
+ unsigned char* areas)
+{
+ // TODO: VC complains about unref formal variable, figure out a way to handle this better.
+// rcAssert(ctx);
+
+ const float walkableThr = cosf(walkableSlopeAngle/180.0f*RC_PI);
+
+ float norm[3];
+
+ for (int i = 0; i < nt; ++i)
+ {
+ const int* tri = &tris[i*3];
+ calcTriNormal(&verts[tri[0]*3], &verts[tri[1]*3], &verts[tri[2]*3], norm);
+ // Check if the face is walkable.
+ if (norm[1] <= walkableThr)
+ areas[i] = RC_NULL_AREA;
+ }
+}
+
+int rcGetHeightFieldSpanCount(rcContext* /*ctx*/, rcHeightfield& hf)
+{
+ // TODO: VC complains about unref formal variable, figure out a way to handle this better.
+// rcAssert(ctx);
+
+ const int w = hf.width;
+ const int h = hf.height;
+ int spanCount = 0;
+ for (int y = 0; y < h; ++y)
+ {
+ for (int x = 0; x < w; ++x)
+ {
+ for (rcSpan* s = hf.spans[x + y*w]; s; s = s->next)
+ {
+ if (s->area != RC_NULL_AREA)
+ spanCount++;
+ }
+ }
+ }
+ return spanCount;
+}
+
+bool rcBuildCompactHeightfield(rcContext* ctx, const int walkableHeight, const int walkableClimb,
+ rcHeightfield& hf, rcCompactHeightfield& chf)
+{
+ rcAssert(ctx);
+
+ ctx->startTimer(RC_TIMER_BUILD_COMPACTHEIGHTFIELD);
+
+ const int w = hf.width;
+ const int h = hf.height;
+ const int spanCount = rcGetHeightFieldSpanCount(ctx, hf);
+
+ // Fill in header.
+ chf.width = w;
+ chf.height = h;
+ chf.spanCount = spanCount;
+ chf.walkableHeight = walkableHeight;
+ chf.walkableClimb = walkableClimb;
+ chf.maxRegions = 0;
+ rcVcopy(chf.bmin, hf.bmin);
+ rcVcopy(chf.bmax, hf.bmax);
+ chf.bmax[1] += walkableHeight*hf.ch;
+ chf.cs = hf.cs;
+ chf.ch = hf.ch;
+ chf.cells = (rcCompactCell*)rcAlloc(sizeof(rcCompactCell)*w*h, RC_ALLOC_PERM);
+ if (!chf.cells)
+ {
+ ctx->log(RC_LOG_ERROR, "rcBuildCompactHeightfield: Out of memory 'chf.cells' (%d)", w*h);
+ return false;
+ }
+ memset(chf.cells, 0, sizeof(rcCompactCell)*w*h);
+ chf.spans = (rcCompactSpan*)rcAlloc(sizeof(rcCompactSpan)*spanCount, RC_ALLOC_PERM);
+ if (!chf.spans)
+ {
+ ctx->log(RC_LOG_ERROR, "rcBuildCompactHeightfield: Out of memory 'chf.spans' (%d)", spanCount);
+ return false;
+ }
+ memset(chf.spans, 0, sizeof(rcCompactSpan)*spanCount);
+ chf.areas = (unsigned char*)rcAlloc(sizeof(unsigned char)*spanCount, RC_ALLOC_PERM);
+ if (!chf.areas)
+ {
+ ctx->log(RC_LOG_ERROR, "rcBuildCompactHeightfield: Out of memory 'chf.areas' (%d)", spanCount);
+ return false;
+ }
+ memset(chf.areas, RC_NULL_AREA, sizeof(unsigned char)*spanCount);
+
+ const int MAX_HEIGHT = 0xffff;
+
+ // Fill in cells and spans.
+ int idx = 0;
+ for (int y = 0; y < h; ++y)
+ {
+ for (int x = 0; x < w; ++x)
+ {
+ const rcSpan* s = hf.spans[x + y*w];
+ // If there are no spans at this cell, just leave the data to index=0, count=0.
+ if (!s) continue;
+ rcCompactCell& c = chf.cells[x+y*w];
+ c.index = idx;
+ c.count = 0;
+ while (s)
+ {
+ if (s->area != RC_NULL_AREA)
+ {
+ const int bot = (int)s->smax;
+ const int top = s->next ? (int)s->next->smin : MAX_HEIGHT;
+ chf.spans[idx].y = (unsigned short)rcClamp(bot, 0, 0xffff);
+ chf.spans[idx].h = (unsigned char)rcClamp(top - bot, 0, 0xff);
+ chf.areas[idx] = s->area;
+ idx++;
+ c.count++;
+ }
+ s = s->next;
+ }
+ }
+ }
+
+ // Find neighbour connections.
+ const int MAX_LAYERS = RC_NOT_CONNECTED-1;
+ int tooHighNeighbour = 0;
+ for (int y = 0; y < h; ++y)
+ {
+ for (int x = 0; x < w; ++x)
+ {
+ const rcCompactCell& c = chf.cells[x+y*w];
+ for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i)
+ {
+ rcCompactSpan& s = chf.spans[i];
+
+ for (int dir = 0; dir < 4; ++dir)
+ {
+ rcSetCon(s, dir, RC_NOT_CONNECTED);
+ const int nx = x + rcGetDirOffsetX(dir);
+ const int ny = y + rcGetDirOffsetY(dir);
+ // First check that the neighbour cell is in bounds.
+ if (nx < 0 || ny < 0 || nx >= w || ny >= h)
+ continue;
+
+ // Iterate over all neighbour spans and check if any of the is
+ // accessible from current cell.
+ const rcCompactCell& nc = chf.cells[nx+ny*w];
+ for (int k = (int)nc.index, nk = (int)(nc.index+nc.count); k < nk; ++k)
+ {
+ const rcCompactSpan& ns = chf.spans[k];
+ const int bot = rcMax(s.y, ns.y);
+ const int top = rcMin(s.y+s.h, ns.y+ns.h);
+
+ // Check that the gap between the spans is walkable,
+ // and that the climb height between the gaps is not too high.
+ if ((top - bot) >= walkableHeight && rcAbs((int)ns.y - (int)s.y) <= walkableClimb)
+ {
+ // Mark direction as walkable.
+ const int idx = k - (int)nc.index;
+ if (idx < 0 || idx > MAX_LAYERS)
+ {
+ tooHighNeighbour = rcMax(tooHighNeighbour, idx);
+ continue;
+ }
+ rcSetCon(s, dir, idx);
+ break;
+ }
+ }
+
+ }
+ }
+ }
+ }
+
+ if (tooHighNeighbour > MAX_LAYERS)
+ {
+ ctx->log(RC_LOG_ERROR, "rcBuildCompactHeightfield: Heightfield has too many layers %d (max: %d)",
+ tooHighNeighbour, MAX_LAYERS);
+ }
+
+ ctx->stopTimer(RC_TIMER_BUILD_COMPACTHEIGHTFIELD);
+
+ return true;
+}
+
+/*
+static int getHeightfieldMemoryUsage(const rcHeightfield& hf)
+{
+ int size = 0;
+ size += sizeof(hf);
+ size += hf.width * hf.height * sizeof(rcSpan*);
+
+ rcSpanPool* pool = hf.pools;
+ while (pool)
+ {
+ size += (sizeof(rcSpanPool) - sizeof(rcSpan)) + sizeof(rcSpan)*RC_SPANS_PER_POOL;
+ pool = pool->next;
+ }
+ return size;
+}
+
+static int getCompactHeightFieldMemoryusage(const rcCompactHeightfield& chf)
+{
+ int size = 0;
+ size += sizeof(rcCompactHeightfield);
+ size += sizeof(rcCompactSpan) * chf.spanCount;
+ size += sizeof(rcCompactCell) * chf.width * chf.height;
+ return size;
+}
+*/ \ No newline at end of file
diff --git a/dep/recastnavigation/Recast/Recast.h b/dep/recastnavigation/Recast/Recast.h
new file mode 100644
index 00000000000..0e5f0742486
--- /dev/null
+++ b/dep/recastnavigation/Recast/Recast.h
@@ -0,0 +1,688 @@
+//
+// Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
+//
+// This software is provided 'as-is', without any express or implied
+// warranty. In no event will the authors be held liable for any damages
+// arising from the use of this software.
+// Permission is granted to anyone to use this software for any purpose,
+// including commercial applications, and to alter it and redistribute it
+// freely, subject to the following restrictions:
+// 1. The origin of this software must not be misrepresented; you must not
+// claim that you wrote the original software. If you use this software
+// in a product, an acknowledgment in the product documentation would be
+// appreciated but is not required.
+// 2. Altered source versions must be plainly marked as such, and must not be
+// misrepresented as being the original software.
+// 3. This notice may not be removed or altered from any source distribution.
+//
+
+#ifndef RECAST_H
+#define RECAST_H
+
+// Some math headers don't have PI defined.
+static const float RC_PI = 3.14159265f;
+
+enum rcLogCategory
+{
+ RC_LOG_PROGRESS = 1,
+ RC_LOG_WARNING,
+ RC_LOG_ERROR,
+};
+
+enum rcTimerLabel
+{
+ RC_TIMER_TOTAL,
+ RC_TIMER_TEMP,
+ RC_TIMER_RASTERIZE_TRIANGLES,
+ RC_TIMER_BUILD_COMPACTHEIGHTFIELD,
+ RC_TIMER_BUILD_CONTOURS,
+ RC_TIMER_BUILD_CONTOURS_TRACE,
+ RC_TIMER_BUILD_CONTOURS_SIMPLIFY,
+ RC_TIMER_FILTER_BORDER,
+ RC_TIMER_FILTER_WALKABLE,
+ RC_TIMER_MEDIAN_AREA,
+ RC_TIMER_FILTER_LOW_OBSTACLES,
+ RC_TIMER_BUILD_POLYMESH,
+ RC_TIMER_MERGE_POLYMESH,
+ RC_TIMER_ERODE_AREA,
+ RC_TIMER_MARK_BOX_AREA,
+ RC_TIMER_MARK_CONVEXPOLY_AREA,
+ RC_TIMER_BUILD_DISTANCEFIELD,
+ RC_TIMER_BUILD_DISTANCEFIELD_DIST,
+ RC_TIMER_BUILD_DISTANCEFIELD_BLUR,
+ RC_TIMER_BUILD_REGIONS,
+ RC_TIMER_BUILD_REGIONS_WATERSHED,
+ RC_TIMER_BUILD_REGIONS_EXPAND,
+ RC_TIMER_BUILD_REGIONS_FLOOD,
+ RC_TIMER_BUILD_REGIONS_FILTER,
+ RC_TIMER_BUILD_POLYMESHDETAIL,
+ RC_TIMER_MERGE_POLYMESHDETAIL,
+ RC_MAX_TIMERS
+};
+
+// Build context provides several optional utilities needed for the build process,
+// such as timing, logging, and build time collecting.
+class rcContext
+{
+public:
+ inline rcContext(bool state = true) : m_logEnabled(state), m_timerEnabled(state) {}
+ virtual ~rcContext() {}
+
+ // Enables or disables logging.
+ inline void enableLog(bool state) { m_logEnabled = state; }
+ // Resets log.
+ inline void resetLog() { if (m_logEnabled) doResetLog(); }
+ // Logs a message.
+ void log(const rcLogCategory category, const char* format, ...);
+
+ // Enables or disables timer.
+ inline void enableTimer(bool state) { m_timerEnabled = state; }
+ // Resets all timers.
+ inline void resetTimers() { if (m_timerEnabled) doResetTimers(); }
+ // Starts timer, used for performance timing.
+ inline void startTimer(const rcTimerLabel label) { if (m_timerEnabled) doStartTimer(label); }
+ // Stops timer, used for performance timing.
+ inline void stopTimer(const rcTimerLabel label) { if (m_timerEnabled) doStopTimer(label); }
+ // Returns time accumulated between timer start/stop.
+ inline int getAccumulatedTime(const rcTimerLabel label) const { return m_timerEnabled ? doGetAccumulatedTime(label) : -1; }
+
+protected:
+ // Virtual functions to override for custom implementations.
+ virtual void doResetLog() {}
+ virtual void doLog(const rcLogCategory /*category*/, const char* /*msg*/, const int /*len*/) {}
+ virtual void doResetTimers() {}
+ virtual void doStartTimer(const rcTimerLabel /*label*/) {}
+ virtual void doStopTimer(const rcTimerLabel /*label*/) {}
+ virtual int doGetAccumulatedTime(const rcTimerLabel /*label*/) const { return -1; }
+
+ bool m_logEnabled;
+ bool m_timerEnabled;
+};
+
+
+// The units of the parameters are specified in parenthesis as follows:
+// (vx) voxels, (wu) world units
+struct rcConfig
+{
+ int width, height; // Dimensions of the rasterized heightfield (vx)
+ int tileSize; // Width and Height of a tile (vx)
+ int borderSize; // Non-navigable Border around the heightfield (vx)
+ float cs, ch; // Grid cell size and height (wu)
+ float bmin[3], bmax[3]; // Grid bounds (wu)
+ float walkableSlopeAngle; // Maximum walkable slope angle in degrees.
+ int walkableHeight; // Minimum height where the agent can still walk (vx)
+ int walkableClimb; // Maximum height between grid cells the agent can climb (vx)
+ int walkableRadius; // Radius of the agent in cells (vx)
+ int maxEdgeLen; // Maximum contour edge length (vx)
+ float maxSimplificationError; // Maximum distance error from contour to cells (vx)
+ int minRegionArea; // Regions whose area is smaller than this threshold will be removed. (vx)
+ int mergeRegionArea; // Regions whose area is smaller than this threshold will be merged (vx)
+ int maxVertsPerPoly; // Max number of vertices per polygon
+ float detailSampleDist; // Detail mesh sample spacing.
+ float detailSampleMaxError; // Detail mesh simplification max sample error.
+};
+
+// Define number of bits in the above structure for smin/smax.
+// The max height is used for clamping rasterized values.
+static const int RC_SPAN_HEIGHT_BITS = 16;
+static const int RC_SPAN_MAX_HEIGHT = (1<<RC_SPAN_HEIGHT_BITS)-1;
+
+// Heightfield span.
+struct rcSpan
+{
+ unsigned int smin : 16; // Span min height.
+ unsigned int smax : 16; // Span max height.
+ unsigned char area; // Span area type.
+ rcSpan* next; // Next span in column.
+};
+
+// Number of spans allocated per pool.
+static const int RC_SPANS_PER_POOL = 2048;
+
+// Memory pool used for quick span allocation.
+struct rcSpanPool
+{
+ rcSpanPool* next; // Pointer to next pool.
+ rcSpan items[RC_SPANS_PER_POOL]; // Array of spans.
+};
+
+// Dynamic span-heightfield.
+struct rcHeightfield
+{
+ int width, height; // Dimension of the heightfield.
+ float bmin[3], bmax[3]; // Bounding box of the heightfield
+ float cs, ch; // Cell size and height.
+ rcSpan** spans; // Heightfield of spans (width*height).
+ rcSpanPool* pools; // Linked list of span pools.
+ rcSpan* freelist; // Pointer to next free span.
+};
+
+rcHeightfield* rcAllocHeightfield();
+void rcFreeHeightField(rcHeightfield* hf);
+
+
+struct rcCompactCell
+{
+ unsigned int index : 24; // Index to first span in column.
+ unsigned int count : 8; // Number of spans in this column.
+};
+
+struct rcCompactSpan
+{
+ unsigned short y; // Bottom coordinate of the span.
+ unsigned short reg;
+ unsigned int con : 24; // Connections to neighbour cells.
+ unsigned int h : 8; // Height of the span.
+};
+
+// Compact static heightfield.
+struct rcCompactHeightfield
+{
+ int width, height; // Width and height of the heightfield.
+ int spanCount; // Number of spans in the heightfield.
+ int walkableHeight, walkableClimb; // Agent properties.
+ unsigned short maxDistance; // Maximum distance value stored in heightfield.
+ unsigned short maxRegions; // Maximum Region Id stored in heightfield.
+ float bmin[3], bmax[3]; // Bounding box of the heightfield.
+ float cs, ch; // Cell size and height.
+ rcCompactCell* cells; // Pointer to width*height cells.
+ rcCompactSpan* spans; // Pointer to spans.
+ unsigned short* dist; // Pointer to per span distance to border.
+ unsigned char* areas; // Pointer to per span area ID.
+};
+
+rcCompactHeightfield* rcAllocCompactHeightfield();
+void rcFreeCompactHeightfield(rcCompactHeightfield* chf);
+
+
+struct rcContour
+{
+ int* verts; // Vertex coordinates, each vertex contains 4 components.
+ int nverts; // Number of vertices.
+ int* rverts; // Raw vertex coordinates, each vertex contains 4 components.
+ int nrverts; // Number of raw vertices.
+ unsigned short reg; // Region ID of the contour.
+ unsigned char area; // Area ID of the contour.
+};
+
+struct rcContourSet
+{
+ rcContour* conts; // Pointer to all contours.
+ int nconts; // Number of contours.
+ float bmin[3], bmax[3]; // Bounding box of the heightfield.
+ float cs, ch; // Cell size and height.
+};
+
+rcContourSet* rcAllocContourSet();
+void rcFreeContourSet(rcContourSet* cset);
+
+
+// Polymesh store a connected mesh of polygons.
+// The polygons are store in an array where each polygons takes
+// 'nvp*2' elements. The first 'nvp' elements are indices to vertices
+// and the second 'nvp' elements are indices to neighbour polygons.
+// If a polygon has less than 'bvp' vertices, the remaining indices
+// are set to RC_MESH_NULL_IDX. If an polygon edge does not have a neighbour
+// the neighbour index is set to RC_MESH_NULL_IDX.
+// Vertices can be transformed into world space as follows:
+// x = bmin[0] + verts[i*3+0]*cs;
+// y = bmin[1] + verts[i*3+1]*ch;
+// z = bmin[2] + verts[i*3+2]*cs;
+struct rcPolyMesh
+{
+ unsigned short* verts; // Vertices of the mesh, 3 elements per vertex.
+ unsigned short* polys; // Polygons of the mesh, nvp*2 elements per polygon.
+ unsigned short* regs; // Region ID of the polygons.
+ unsigned short* flags; // Per polygon flags.
+ unsigned char* areas; // Area ID of polygons.
+ int nverts; // Number of vertices.
+ int npolys; // Number of polygons.
+ int maxpolys; // Number of allocated polygons.
+ int nvp; // Max number of vertices per polygon.
+ float bmin[3], bmax[3]; // Bounding box of the mesh.
+ float cs, ch; // Cell size and height.
+};
+
+rcPolyMesh* rcAllocPolyMesh();
+void rcFreePolyMesh(rcPolyMesh* pmesh);
+
+
+// Detail mesh generated from a rcPolyMesh.
+// Each submesh represents a polygon in the polymesh and they are stored in
+// exactly same order. Each submesh is described as 4 values:
+// base vertex, vertex count, base triangle, triangle count. That is,
+// const unsigned char* t = &dmesh.tris[(tbase+i)*3]; and
+// const float* v = &dmesh.verts[(vbase+t[j])*3];
+// If the input polygon has 'n' vertices, those vertices are first in the
+// submesh vertex list. This allows to compres the mesh by not storing the
+// first vertices and using the polymesh vertices instead.
+// Max number of vertices per submesh is 127 and
+// max number of triangles per submesh is 255.
+
+struct rcPolyMeshDetail
+{
+ unsigned int* meshes; // Pointer to all mesh data.
+ float* verts; // Pointer to all vertex data.
+ unsigned char* tris; // Pointer to all triangle data.
+ int nmeshes; // Number of meshes.
+ int nverts; // Number of total vertices.
+ int ntris; // Number of triangles.
+};
+
+rcPolyMeshDetail* rcAllocPolyMeshDetail();
+void rcFreePolyMeshDetail(rcPolyMeshDetail* dmesh);
+
+
+// If heightfield region ID has the following bit set, the region is on border area
+// and excluded from many calculations.
+static const unsigned short RC_BORDER_REG = 0x8000;
+
+// If contour region ID has the following bit set, the vertex will be later
+// removed in order to match the segments and vertices at tile boundaries.
+static const int RC_BORDER_VERTEX = 0x10000;
+
+static const int RC_AREA_BORDER = 0x20000;
+
+enum rcBuildContoursFlags
+{
+ RC_CONTOUR_TESS_WALL_EDGES = 0x01, // Tessellate wall edges
+ RC_CONTOUR_TESS_AREA_EDGES = 0x02, // Tessellate edges between areas.
+};
+
+// Mask used with contours to extract region id.
+static const int RC_CONTOUR_REG_MASK = 0xffff;
+
+// Null index which is used with meshes to mark unset or invalid indices.
+static const unsigned short RC_MESH_NULL_IDX = 0xffff;
+
+// Area ID that is considered empty.
+static const unsigned char RC_NULL_AREA = 0;
+
+// Area ID that is considered generally walkable.
+static const unsigned char RC_WALKABLE_AREA = 63;
+
+// Value returned by rcGetCon() if the direction is not connected.
+static const int RC_NOT_CONNECTED = 0x3f;
+
+// Compact span neighbour helpers.
+inline void rcSetCon(rcCompactSpan& s, int dir, int i)
+{
+ const unsigned int shift = (unsigned int)dir*6;
+ unsigned int con = s.con;
+ s.con = (con & ~(0x3f << shift)) | (((unsigned int)i & 0x3f) << shift);
+}
+
+inline int rcGetCon(const rcCompactSpan& s, int dir)
+{
+ const unsigned int shift = (unsigned int)dir*6;
+ return (s.con >> shift) & 0x3f;
+}
+
+inline int rcGetDirOffsetX(int dir)
+{
+ const int offset[4] = { -1, 0, 1, 0, };
+ return offset[dir&0x03];
+}
+
+inline int rcGetDirOffsetY(int dir)
+{
+ const int offset[4] = { 0, 1, 0, -1 };
+ return offset[dir&0x03];
+}
+
+// Common helper functions
+template<class T> inline void rcSwap(T& a, T& b) { T t = a; a = b; b = t; }
+template<class T> inline T rcMin(T a, T b) { return a < b ? a : b; }
+template<class T> inline T rcMax(T a, T b) { return a > b ? a : b; }
+template<class T> inline T rcAbs(T a) { return a < 0 ? -a : a; }
+template<class T> inline T rcSqr(T a) { return a*a; }
+template<class T> inline T rcClamp(T v, T mn, T mx) { return v < mn ? mn : (v > mx ? mx : v); }
+float rcSqrt(float x);
+
+// Common vector helper functions.
+inline void rcVcross(float* dest, const float* v1, const float* v2)
+{
+ dest[0] = v1[1]*v2[2] - v1[2]*v2[1];
+ dest[1] = v1[2]*v2[0] - v1[0]*v2[2];
+ dest[2] = v1[0]*v2[1] - v1[1]*v2[0];
+}
+
+inline float rcVdot(const float* v1, const float* v2)
+{
+ return v1[0]*v2[0] + v1[1]*v2[1] + v1[2]*v2[2];
+}
+
+inline void rcVmad(float* dest, const float* v1, const float* v2, const float s)
+{
+ dest[0] = v1[0]+v2[0]*s;
+ dest[1] = v1[1]+v2[1]*s;
+ dest[2] = v1[2]+v2[2]*s;
+}
+
+inline void rcVadd(float* dest, const float* v1, const float* v2)
+{
+ dest[0] = v1[0]+v2[0];
+ dest[1] = v1[1]+v2[1];
+ dest[2] = v1[2]+v2[2];
+}
+
+inline void rcVsub(float* dest, const float* v1, const float* v2)
+{
+ dest[0] = v1[0]-v2[0];
+ dest[1] = v1[1]-v2[1];
+ dest[2] = v1[2]-v2[2];
+}
+
+inline void rcVmin(float* mn, const float* v)
+{
+ mn[0] = rcMin(mn[0], v[0]);
+ mn[1] = rcMin(mn[1], v[1]);
+ mn[2] = rcMin(mn[2], v[2]);
+}
+
+inline void rcVmax(float* mx, const float* v)
+{
+ mx[0] = rcMax(mx[0], v[0]);
+ mx[1] = rcMax(mx[1], v[1]);
+ mx[2] = rcMax(mx[2], v[2]);
+}
+
+inline void rcVcopy(float* dest, const float* v)
+{
+ dest[0] = v[0];
+ dest[1] = v[1];
+ dest[2] = v[2];
+}
+
+inline float rcVdist(const float* v1, const float* v2)
+{
+ float dx = v2[0] - v1[0];
+ float dy = v2[1] - v1[1];
+ float dz = v2[2] - v1[2];
+ return rcSqrt(dx*dx + dy*dy + dz*dz);
+}
+
+inline float rcVdistSqr(const float* v1, const float* v2)
+{
+ float dx = v2[0] - v1[0];
+ float dy = v2[1] - v1[1];
+ float dz = v2[2] - v1[2];
+ return dx*dx + dy*dy + dz*dz;
+}
+
+inline void rcVnormalize(float* v)
+{
+ float d = 1.0f / rcSqrt(rcSqr(v[0]) + rcSqr(v[1]) + rcSqr(v[2]));
+ v[0] *= d;
+ v[1] *= d;
+ v[2] *= d;
+}
+
+inline bool rcVequal(const float* p0, const float* p1)
+{
+ static const float thr = rcSqr(1.0f/16384.0f);
+ const float d = rcVdistSqr(p0, p1);
+ return d < thr;
+}
+
+// Calculated bounding box of array of vertices.
+// Params:
+// verts - (in) array of vertices
+// nv - (in) vertex count
+// bmin, bmax - (out) bounding box
+void rcCalcBounds(const float* verts, int nv, float* bmin, float* bmax);
+
+// Calculates grid size based on bounding box and grid cell size.
+// Params:
+// bmin, bmax - (in) bounding box
+// cs - (in) grid cell size
+// w - (out) grid width
+// h - (out) grid height
+void rcCalcGridSize(const float* bmin, const float* bmax, float cs, int* w, int* h);
+
+// Creates and initializes new heightfield.
+// Params:
+// hf - (in/out) heightfield to initialize.
+// width - (in) width of the heightfield.
+// height - (in) height of the heightfield.
+// bmin, bmax - (in) bounding box of the heightfield
+// cs - (in) grid cell size
+// ch - (in) grid cell height
+bool rcCreateHeightfield(rcContext* ctx, rcHeightfield& hf, int width, int height,
+ const float* bmin, const float* bmax,
+ float cs, float ch);
+
+// Sets the RC_WALKABLE_AREA for every triangle whose slope is below
+// the maximum walkable slope angle.
+// Params:
+// walkableSlopeAngle - (in) maximum slope angle in degrees.
+// verts - (in) array of vertices
+// nv - (in) vertex count
+// tris - (in) array of triangle vertex indices
+// nt - (in) triangle count
+// areas - (out) array of triangle area types
+void rcMarkWalkableTriangles(rcContext* ctx, const float walkableSlopeAngle, const float* verts, int nv,
+ const int* tris, int nt, unsigned char* areas);
+
+// Sets the RC_NULL_AREA for every triangle whose slope is steeper than
+// the maximum walkable slope angle.
+// Params:
+// walkableSlopeAngle - (in) maximum slope angle in degrees.
+// verts - (in) array of vertices
+// nv - (in) vertex count
+// tris - (in) array of triangle vertex indices
+// nt - (in) triangle count
+// areas - (out) array of triangle are types
+void rcClearUnwalkableTriangles(rcContext* ctx, const float walkableSlopeAngle, const float* verts, int nv,
+ const int* tris, int nt, unsigned char* areas);
+
+// Adds span to heightfield.
+// The span addition can set to favor flags. If the span is merged to
+// another span and the new smax is within 'flagMergeThr' units away
+// from the existing span the span flags are merged and stored.
+// Params:
+// solid - (in) heightfield where the spans is added to
+// x,y - (in) location on the heightfield where the span is added
+// smin,smax - (in) spans min/max height
+// flags - (in) span flags (zero or WALKABLE)
+// flagMergeThr - (in) merge threshold.
+void rcAddSpan(rcContext* ctx, rcHeightfield& solid, const int x, const int y,
+ const unsigned short smin, const unsigned short smax,
+ const unsigned short area, const int flagMergeThr);
+
+// Rasterizes a triangle into heightfield spans.
+// Params:
+// v0,v1,v2 - (in) the vertices of the triangle.
+// area - (in) area type of the triangle.
+// solid - (in) heightfield where the triangle is rasterized
+// flagMergeThr - (in) distance in voxel where walkable flag is favored over non-walkable.
+void rcRasterizeTriangle(rcContext* ctx, const float* v0, const float* v1, const float* v2,
+ const unsigned char area, rcHeightfield& solid,
+ const int flagMergeThr = 1);
+
+// Rasterizes indexed triangle mesh into heightfield spans.
+// Params:
+// verts - (in) array of vertices
+// nv - (in) vertex count
+// tris - (in) array of triangle vertex indices
+// area - (in) array of triangle area types.
+// nt - (in) triangle count
+// solid - (in) heightfield where the triangles are rasterized
+// flagMergeThr - (in) distance in voxel where walkable flag is favored over non-walkable.
+void rcRasterizeTriangles(rcContext* ctx, const float* verts, const int nv,
+ const int* tris, const unsigned char* areas, const int nt,
+ rcHeightfield& solid, const int flagMergeThr = 1);
+
+// Rasterizes indexed triangle mesh into heightfield spans.
+// Params:
+// verts - (in) array of vertices
+// nv - (in) vertex count
+// tris - (in) array of triangle vertex indices
+// area - (in) array of triangle area types.
+// nt - (in) triangle count
+// solid - (in) heightfield where the triangles are rasterized
+// flagMergeThr - (in) distance in voxel where walkable flag is favored over non-walkable.
+void rcRasterizeTriangles(rcContext* ctx, const float* verts, const int nv,
+ const unsigned short* tris, const unsigned char* areas, const int nt,
+ rcHeightfield& solid, const int flagMergeThr = 1);
+
+// Rasterizes the triangles into heightfield spans.
+// Params:
+// verts - (in) array of vertices
+// area - (in) array of triangle area types.
+// nt - (in) triangle count
+// solid - (in) heightfield where the triangles are rasterized
+void rcRasterizeTriangles(rcContext* ctx, const float* verts, const unsigned char* areas, const int nt,
+ rcHeightfield& solid, const int flagMergeThr = 1);
+
+// Marks non-walkable low obstacles as walkable if they are closer than walkableClimb
+// from a walkable surface. Applying this filter allows to step over low hanging
+// low obstacles.
+// Params:
+// walkableHeight - (in) minimum height where the agent can still walk
+// solid - (in/out) heightfield describing the solid space
+// TODO: Missuses ledge flag, must be called before rcFilterLedgeSpans!
+void rcFilterLowHangingWalkableObstacles(rcContext* ctx, const int walkableClimb, rcHeightfield& solid);
+
+// Removes WALKABLE flag from all spans that are at ledges. This filtering
+// removes possible overestimation of the conservative voxelization so that
+// the resulting mesh will not have regions hanging in air over ledges.
+// Params:
+// walkableHeight - (in) minimum height where the agent can still walk
+// walkableClimb - (in) maximum height between grid cells the agent can climb
+// solid - (in/out) heightfield describing the solid space
+void rcFilterLedgeSpans(rcContext* ctx, const int walkableHeight,
+ const int walkableClimb, rcHeightfield& solid);
+
+// Removes WALKABLE flag from all spans which have smaller than
+// 'walkableHeight' clearance above them.
+// Params:
+// walkableHeight - (in) minimum height where the agent can still walk
+// solid - (in/out) heightfield describing the solid space
+void rcFilterWalkableLowHeightSpans(rcContext* ctx, int walkableHeight, rcHeightfield& solid);
+
+// Returns number of spans contained in a heightfield.
+// Params:
+// hf - (in) heightfield to be compacted
+// Returns number of spans.
+int rcGetHeightFieldSpanCount(rcContext* ctx, rcHeightfield& hf);
+
+// Builds compact representation of the heightfield.
+// Params:
+// walkableHeight - (in) minimum height where the agent can still walk
+// walkableClimb - (in) maximum height between grid cells the agent can climb
+// flags - (in) require flags for a cell to be included in the compact heightfield.
+// hf - (in) heightfield to be compacted
+// chf - (out) compact heightfield representing the open space.
+// Returns false if operation ran out of memory.
+bool rcBuildCompactHeightfield(rcContext* ctx, const int walkableHeight, const int walkableClimb,
+ rcHeightfield& hf, rcCompactHeightfield& chf);
+
+// Erodes walkable area.
+// Params:
+// radius - (in) radius of erosion (max 255).
+// chf - (in/out) compact heightfield to erode.
+// Returns false if operation ran out of memory.
+bool rcErodeWalkableArea(rcContext* ctx, int radius, rcCompactHeightfield& chf);
+
+// Applies median filter to walkable area types, removing noise.
+// Params:
+// chf - (in/out) compact heightfield to erode.
+// Returns false if operation ran out of memory.
+bool rcMedianFilterWalkableArea(rcContext* ctx, rcCompactHeightfield& chf);
+
+// Marks the area of the convex polygon into the area type of the compact heightfield.
+// Params:
+// bmin/bmax - (in) bounds of the axis aligned box.
+// areaId - (in) area ID to mark.
+// chf - (in/out) compact heightfield to mark.
+void rcMarkBoxArea(rcContext* ctx, const float* bmin, const float* bmax, unsigned char areaId,
+ rcCompactHeightfield& chf);
+
+// Marks the area of the convex polygon into the area type of the compact heightfield.
+// Params:
+// verts - (in) vertices of the convex polygon.
+// nverts - (in) number of vertices in the polygon.
+// hmin/hmax - (in) min and max height of the polygon.
+// areaId - (in) area ID to mark.
+// chf - (in/out) compact heightfield to mark.
+void rcMarkConvexPolyArea(rcContext* ctx, const float* verts, const int nverts,
+ const float hmin, const float hmax, unsigned char areaId,
+ rcCompactHeightfield& chf);
+
+// Builds distance field and stores it into the combat heightfield.
+// Params:
+// chf - (in/out) compact heightfield representing the open space.
+// Returns false if operation ran out of memory.
+bool rcBuildDistanceField(rcContext* ctx, rcCompactHeightfield& chf);
+
+// Divides the walkable heighfied into simple regions using watershed partitioning.
+// Each region has only one contour and no overlaps.
+// The regions are stored in the compact heightfield 'reg' field.
+// The process sometimes creates small regions. If the area of a regions is
+// smaller than 'mergeRegionArea' then the region will be merged with a neighbour
+// region if possible. If multiple regions form an area which is smaller than
+// 'minRegionArea' all the regions belonging to that area will be removed.
+// Here area means the count of spans in an area.
+// Params:
+// chf - (in/out) compact heightfield representing the open space.
+// minRegionArea - (in) the smallest allowed region area.
+// maxMergeRegionArea - (in) the largest allowed region area which can be merged.
+// Returns false if operation ran out of memory.
+bool rcBuildRegions(rcContext* ctx, rcCompactHeightfield& chf,
+ const int borderSize, const int minRegionArea, const int mergeRegionArea);
+
+// Divides the walkable heighfied into simple regions using simple monotone partitioning.
+// Each region has only one contour and no overlaps.
+// The regions are stored in the compact heightfield 'reg' field.
+// The process sometimes creates small regions. If the area of a regions is
+// smaller than 'mergeRegionArea' then the region will be merged with a neighbour
+// region if possible. If multiple regions form an area which is smaller than
+// 'minRegionArea' all the regions belonging to that area will be removed.
+// Here area means the count of spans in an area.
+// Params:
+// chf - (in/out) compact heightfield representing the open space.
+// minRegionArea - (in) the smallest allowed regions size.
+// maxMergeRegionArea - (in) the largest allowed regions size which can be merged.
+// Returns false if operation ran out of memory.
+bool rcBuildRegionsMonotone(rcContext* ctx, rcCompactHeightfield& chf,
+ const int borderSize, const int minRegionArea, const int mergeRegionArea);
+
+// Builds simplified contours from the regions outlines.
+// Params:
+// chf - (in) compact heightfield which has regions set.
+// maxError - (in) maximum allowed distance between simplified contour and cells.
+// maxEdgeLen - (in) maximum allowed contour edge length in cells.
+// cset - (out) Resulting contour set.
+// flags - (in) build flags, see rcBuildContoursFlags.
+// Returns false if operation ran out of memory.
+bool rcBuildContours(rcContext* ctx, rcCompactHeightfield& chf,
+ const float maxError, const int maxEdgeLen,
+ rcContourSet& cset, const int flags = RC_CONTOUR_TESS_WALL_EDGES);
+
+// Builds connected convex polygon mesh from contour polygons.
+// Params:
+// cset - (in) contour set.
+// nvp - (in) maximum number of vertices per polygon.
+// mesh - (out) poly mesh.
+// Returns false if operation ran out of memory.
+bool rcBuildPolyMesh(rcContext* ctx, rcContourSet& cset, int nvp, rcPolyMesh& mesh);
+
+bool rcMergePolyMeshes(rcContext* ctx, rcPolyMesh** meshes, const int nmeshes, rcPolyMesh& mesh);
+
+// Builds detail triangle mesh for each polygon in the poly mesh.
+// Params:
+// mesh - (in) poly mesh to detail.
+// chf - (in) compact height field, used to query height for new vertices.
+// sampleDist - (in) spacing between height samples used to generate more detail into mesh.
+// sampleMaxError - (in) maximum allowed distance between simplified detail mesh and height sample.
+// pmdtl - (out) detail mesh.
+// Returns false if operation ran out of memory.
+bool rcBuildPolyMeshDetail(rcContext* ctx, const rcPolyMesh& mesh, const rcCompactHeightfield& chf,
+ const float sampleDist, const float sampleMaxError,
+ rcPolyMeshDetail& dmesh);
+
+bool rcMergePolyMeshDetails(rcContext* ctx, rcPolyMeshDetail** meshes, const int nmeshes, rcPolyMeshDetail& mesh);
+
+
+#endif // RECAST_H
diff --git a/dep/recastnavigation/Recast/RecastAlloc.cpp b/dep/recastnavigation/Recast/RecastAlloc.cpp
new file mode 100644
index 00000000000..2c7396a1bfa
--- /dev/null
+++ b/dep/recastnavigation/Recast/RecastAlloc.cpp
@@ -0,0 +1,67 @@
+//
+// Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
+//
+// This software is provided 'as-is', without any express or implied
+// warranty. In no event will the authors be held liable for any damages
+// arising from the use of this software.
+// Permission is granted to anyone to use this software for any purpose,
+// including commercial applications, and to alter it and redistribute it
+// freely, subject to the following restrictions:
+// 1. The origin of this software must not be misrepresented; you must not
+// claim that you wrote the original software. If you use this software
+// in a product, an acknowledgment in the product documentation would be
+// appreciated but is not required.
+// 2. Altered source versions must be plainly marked as such, and must not be
+// misrepresented as being the original software.
+// 3. This notice may not be removed or altered from any source distribution.
+//
+
+#include <stdlib.h>
+#include <string.h>
+#include "RecastAlloc.h"
+
+static void *rcAllocDefault(int size, rcAllocHint)
+{
+ return malloc(size);
+}
+
+static void rcFreeDefault(void *ptr)
+{
+ free(ptr);
+}
+
+static rcAllocFunc* sRecastAllocFunc = rcAllocDefault;
+static rcFreeFunc* sRecastFreeFunc = rcFreeDefault;
+
+void rcAllocSetCustom(rcAllocFunc *allocFunc, rcFreeFunc *freeFunc)
+{
+ sRecastAllocFunc = allocFunc ? allocFunc : rcAllocDefault;
+ sRecastFreeFunc = freeFunc ? freeFunc : rcFreeDefault;
+}
+
+void* rcAlloc(int size, rcAllocHint hint)
+{
+ return sRecastAllocFunc(size, hint);
+}
+
+void rcFree(void* ptr)
+{
+ if (ptr)
+ sRecastFreeFunc(ptr);
+}
+
+
+void rcIntArray::resize(int n)
+{
+ if (n > m_cap)
+ {
+ if (!m_cap) m_cap = n;
+ while (m_cap < n) m_cap *= 2;
+ int* newData = (int*)rcAlloc(m_cap*sizeof(int), RC_ALLOC_TEMP);
+ if (m_size && newData) memcpy(newData, m_data, m_size*sizeof(int));
+ rcFree(m_data);
+ m_data = newData;
+ }
+ m_size = n;
+}
+
diff --git a/dep/recastnavigation/Recast/RecastAlloc.h b/dep/recastnavigation/Recast/RecastAlloc.h
new file mode 100644
index 00000000000..9a316374a73
--- /dev/null
+++ b/dep/recastnavigation/Recast/RecastAlloc.h
@@ -0,0 +1,69 @@
+//
+// Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
+//
+// This software is provided 'as-is', without any express or implied
+// warranty. In no event will the authors be held liable for any damages
+// arising from the use of this software.
+// Permission is granted to anyone to use this software for any purpose,
+// including commercial applications, and to alter it and redistribute it
+// freely, subject to the following restrictions:
+// 1. The origin of this software must not be misrepresented; you must not
+// claim that you wrote the original software. If you use this software
+// in a product, an acknowledgment in the product documentation would be
+// appreciated but is not required.
+// 2. Altered source versions must be plainly marked as such, and must not be
+// misrepresented as being the original software.
+// 3. This notice may not be removed or altered from any source distribution.
+//
+
+#ifndef RECASTALLOC_H
+#define RECASTALLOC_H
+
+enum rcAllocHint
+{
+ RC_ALLOC_PERM, // Memory persist after a function call.
+ RC_ALLOC_TEMP // Memory used temporarily within a function.
+};
+
+typedef void* (rcAllocFunc)(int size, rcAllocHint hint);
+typedef void (rcFreeFunc)(void* ptr);
+
+void rcAllocSetCustom(rcAllocFunc *allocFunc, rcFreeFunc *freeFunc);
+
+void* rcAlloc(int size, rcAllocHint hint);
+void rcFree(void* ptr);
+
+
+
+// Simple dynamic array ints.
+class rcIntArray
+{
+ int* m_data;
+ int m_size, m_cap;
+ inline rcIntArray(const rcIntArray&);
+ inline rcIntArray& operator=(const rcIntArray&);
+public:
+ inline rcIntArray() : m_data(0), m_size(0), m_cap(0) {}
+ inline rcIntArray(int n) : m_data(0), m_size(0), m_cap(0) { resize(n); }
+ inline ~rcIntArray() { rcFree(m_data); }
+ void resize(int n);
+ inline void push(int item) { resize(m_size+1); m_data[m_size-1] = item; }
+ inline int pop() { if (m_size > 0) m_size--; return m_data[m_size]; }
+ inline const int& operator[](int i) const { return m_data[i]; }
+ inline int& operator[](int i) { return m_data[i]; }
+ inline int size() const { return m_size; }
+};
+
+// Simple internal helper class to delete array in scope
+template<class T> class rcScopedDelete
+{
+ T* ptr;
+ inline T* operator=(T* p);
+public:
+ inline rcScopedDelete() : ptr(0) {}
+ inline rcScopedDelete(T* p) : ptr(p) {}
+ inline ~rcScopedDelete() { rcFree(ptr); }
+ inline operator T*() { return ptr; }
+};
+
+#endif
diff --git a/dep/recastnavigation/Recast/RecastArea.cpp b/dep/recastnavigation/Recast/RecastArea.cpp
new file mode 100644
index 00000000000..e89caee2a49
--- /dev/null
+++ b/dep/recastnavigation/Recast/RecastArea.cpp
@@ -0,0 +1,413 @@
+//
+// Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
+//
+// This software is provided 'as-is', without any express or implied
+// warranty. In no event will the authors be held liable for any damages
+// arising from the use of this software.
+// Permission is granted to anyone to use this software for any purpose,
+// including commercial applications, and to alter it and redistribute it
+// freely, subject to the following restrictions:
+// 1. The origin of this software must not be misrepresented; you must not
+// claim that you wrote the original software. If you use this software
+// in a product, an acknowledgment in the product documentation would be
+// appreciated but is not required.
+// 2. Altered source versions must be plainly marked as such, and must not be
+// misrepresented as being the original software.
+// 3. This notice may not be removed or altered from any source distribution.
+//
+
+#include <float.h>
+#define _USE_MATH_DEFINES
+#include <math.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include "Recast.h"
+#include "RecastAlloc.h"
+#include "RecastAssert.h"
+
+
+bool rcErodeWalkableArea(rcContext* ctx, int radius, rcCompactHeightfield& chf)
+{
+ rcAssert(ctx);
+
+ const int w = chf.width;
+ const int h = chf.height;
+
+ ctx->startTimer(RC_TIMER_ERODE_AREA);
+
+ unsigned char* dist = (unsigned char*)rcAlloc(sizeof(unsigned char)*chf.spanCount, RC_ALLOC_TEMP);
+ if (!dist)
+ {
+ ctx->log(RC_LOG_ERROR, "erodeWalkableArea: Out of memory 'dist' (%d).", chf.spanCount);
+ return false;
+ }
+
+ // Init distance.
+ memset(dist, 0xff, sizeof(unsigned char)*chf.spanCount);
+
+ // Mark boundary cells.
+ for (int y = 0; y < h; ++y)
+ {
+ for (int x = 0; x < w; ++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 (chf.areas[i] != RC_NULL_AREA)
+ {
+ const rcCompactSpan& s = chf.spans[i];
+ int nc = 0;
+ for (int dir = 0; dir < 4; ++dir)
+ {
+ if (rcGetCon(s, dir) != RC_NOT_CONNECTED)
+ nc++;
+ }
+ // At least one missing neighbour.
+ if (nc != 4)
+ dist[i] = 0;
+ }
+ }
+ }
+ }
+
+ unsigned char nd;
+
+ // Pass 1
+ for (int y = 0; y < h; ++y)
+ {
+ for (int x = 0; x < w; ++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 (rcGetCon(s, 0) != RC_NOT_CONNECTED)
+ {
+ // (-1,0)
+ 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);
+ const rcCompactSpan& as = chf.spans[ai];
+ nd = (unsigned char)rcMin((int)dist[ai]+2, 255);
+ if (nd < dist[i])
+ dist[i] = nd;
+
+ // (-1,-1)
+ if (rcGetCon(as, 3) != RC_NOT_CONNECTED)
+ {
+ const int aax = ax + rcGetDirOffsetX(3);
+ const int aay = ay + rcGetDirOffsetY(3);
+ const int aai = (int)chf.cells[aax+aay*w].index + rcGetCon(as, 3);
+ nd = (unsigned char)rcMin((int)dist[aai]+3, 255);
+ if (nd < dist[i])
+ dist[i] = nd;
+ }
+ }
+ if (rcGetCon(s, 3) != RC_NOT_CONNECTED)
+ {
+ // (0,-1)
+ 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);
+ const rcCompactSpan& as = chf.spans[ai];
+ nd = (unsigned char)rcMin((int)dist[ai]+2, 255);
+ if (nd < dist[i])
+ dist[i] = nd;
+
+ // (1,-1)
+ if (rcGetCon(as, 2) != RC_NOT_CONNECTED)
+ {
+ const int aax = ax + rcGetDirOffsetX(2);
+ const int aay = ay + rcGetDirOffsetY(2);
+ const int aai = (int)chf.cells[aax+aay*w].index + rcGetCon(as, 2);
+ nd = (unsigned char)rcMin((int)dist[aai]+3, 255);
+ if (nd < dist[i])
+ dist[i] = nd;
+ }
+ }
+ }
+ }
+ }
+
+ // Pass 2
+ for (int y = h-1; y >= 0; --y)
+ {
+ for (int x = w-1; x >= 0; --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 (rcGetCon(s, 2) != RC_NOT_CONNECTED)
+ {
+ // (1,0)
+ const int ax = x + rcGetDirOffsetX(2);
+ const int ay = y + rcGetDirOffsetY(2);
+ const int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(s, 2);
+ const rcCompactSpan& as = chf.spans[ai];
+ nd = (unsigned char)rcMin((int)dist[ai]+2, 255);
+ if (nd < dist[i])
+ dist[i] = nd;
+
+ // (1,1)
+ if (rcGetCon(as, 1) != RC_NOT_CONNECTED)
+ {
+ const int aax = ax + rcGetDirOffsetX(1);
+ const int aay = ay + rcGetDirOffsetY(1);
+ const int aai = (int)chf.cells[aax+aay*w].index + rcGetCon(as, 1);
+ nd = (unsigned char)rcMin((int)dist[aai]+3, 255);
+ if (nd < dist[i])
+ dist[i] = nd;
+ }
+ }
+ if (rcGetCon(s, 1) != RC_NOT_CONNECTED)
+ {
+ // (0,1)
+ const int ax = x + rcGetDirOffsetX(1);
+ const int ay = y + rcGetDirOffsetY(1);
+ const int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(s, 1);
+ const rcCompactSpan& as = chf.spans[ai];
+ nd = (unsigned char)rcMin((int)dist[ai]+2, 255);
+ if (nd < dist[i])
+ dist[i] = nd;
+
+ // (-1,1)
+ if (rcGetCon(as, 0) != RC_NOT_CONNECTED)
+ {
+ const int aax = ax + rcGetDirOffsetX(0);
+ const int aay = ay + rcGetDirOffsetY(0);
+ const int aai = (int)chf.cells[aax+aay*w].index + rcGetCon(as, 0);
+ nd = (unsigned char)rcMin((int)dist[aai]+3, 255);
+ if (nd < dist[i])
+ dist[i] = nd;
+ }
+ }
+ }
+ }
+ }
+
+ const unsigned char thr = (unsigned char)(radius*2);
+ for (int i = 0; i < chf.spanCount; ++i)
+ if (dist[i] < thr)
+ chf.areas[i] = RC_NULL_AREA;
+
+ rcFree(dist);
+
+ ctx->stopTimer(RC_TIMER_ERODE_AREA);
+
+ return true;
+}
+
+static void insertSort(unsigned char* a, const int n)
+{
+ int i, j;
+ for (i = 1; i < n; i++)
+ {
+ const unsigned char value = a[i];
+ for (j = i - 1; j >= 0 && a[j] > value; j--)
+ a[j+1] = a[j];
+ a[j+1] = value;
+ }
+}
+
+
+bool rcMedianFilterWalkableArea(rcContext* ctx, rcCompactHeightfield& chf)
+{
+ rcAssert(ctx);
+
+ const int w = chf.width;
+ const int h = chf.height;
+
+ ctx->startTimer(RC_TIMER_MEDIAN_AREA);
+
+ unsigned char* areas = (unsigned char*)rcAlloc(sizeof(unsigned char)*chf.spanCount, RC_ALLOC_TEMP);
+ if (!areas)
+ {
+ ctx->log(RC_LOG_ERROR, "medianFilterWalkableArea: Out of memory 'areas' (%d).", chf.spanCount);
+ return false;
+ }
+
+ // Init distance.
+ memset(areas, 0xff, sizeof(unsigned char)*chf.spanCount);
+
+ for (int y = 0; y < h; ++y)
+ {
+ for (int x = 0; x < w; ++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)
+ {
+ areas[i] = chf.areas[i];
+ continue;
+ }
+
+ unsigned char nei[9];
+ for (int j = 0; j < 9; ++j)
+ nei[j] = chf.areas[i];
+
+ 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);
+ if (chf.areas[ai] != RC_NULL_AREA)
+ nei[dir*2+0] = chf.areas[ai];
+
+ const rcCompactSpan& as = chf.spans[ai];
+ const int dir2 = (dir+1) & 0x3;
+ if (rcGetCon(as, dir2) != RC_NOT_CONNECTED)
+ {
+ const int ax2 = ax + rcGetDirOffsetX(dir2);
+ const int ay2 = ay + rcGetDirOffsetY(dir2);
+ const int ai2 = (int)chf.cells[ax2+ay2*w].index + rcGetCon(as, dir2);
+ if (chf.areas[ai2] != RC_NULL_AREA)
+ nei[dir*2+1] = chf.areas[ai2];
+ }
+ }
+ }
+ insertSort(nei, 9);
+ areas[i] = nei[4];
+ }
+ }
+ }
+
+ memcpy(chf.areas, areas, sizeof(unsigned char)*chf.spanCount);
+
+ rcFree(areas);
+
+ ctx->stopTimer(RC_TIMER_MEDIAN_AREA);
+
+ return true;
+}
+
+void rcMarkBoxArea(rcContext* ctx, const float* bmin, const float* bmax, unsigned char areaId,
+ rcCompactHeightfield& chf)
+{
+ rcAssert(ctx);
+
+ ctx->startTimer(RC_TIMER_MARK_BOX_AREA);
+
+ int minx = (int)((bmin[0]-chf.bmin[0])/chf.cs);
+ int miny = (int)((bmin[1]-chf.bmin[1])/chf.ch);
+ int minz = (int)((bmin[2]-chf.bmin[2])/chf.cs);
+ int maxx = (int)((bmax[0]-chf.bmin[0])/chf.cs);
+ int maxy = (int)((bmax[1]-chf.bmin[1])/chf.ch);
+ int maxz = (int)((bmax[2]-chf.bmin[2])/chf.cs);
+
+ if (maxx < 0) return;
+ if (minx >= chf.width) return;
+ if (maxz < 0) return;
+ if (minz >= chf.height) return;
+
+ if (minx < 0) minx = 0;
+ if (maxx >= chf.width) maxx = chf.width-1;
+ if (minz < 0) minz = 0;
+ if (maxz >= chf.height) maxz = chf.height-1;
+
+ for (int z = minz; z <= maxz; ++z)
+ {
+ for (int x = minx; x <= maxx; ++x)
+ {
+ const rcCompactCell& c = chf.cells[x+z*chf.width];
+ for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i)
+ {
+ rcCompactSpan& s = chf.spans[i];
+ if ((int)s.y >= miny && (int)s.y <= maxy)
+ {
+ chf.areas[i] = areaId;
+ }
+ }
+ }
+ }
+
+ ctx->stopTimer(RC_TIMER_MARK_BOX_AREA);
+
+}
+
+
+static int pointInPoly(int nvert, const float* verts, const float* p)
+{
+ int i, j, c = 0;
+ for (i = 0, j = nvert-1; i < nvert; j = i++)
+ {
+ const float* vi = &verts[i*3];
+ const float* vj = &verts[j*3];
+ if (((vi[2] > p[2]) != (vj[2] > p[2])) &&
+ (p[0] < (vj[0]-vi[0]) * (p[2]-vi[2]) / (vj[2]-vi[2]) + vi[0]) )
+ c = !c;
+ }
+ return c;
+}
+
+void rcMarkConvexPolyArea(rcContext* ctx, const float* verts, const int nverts,
+ const float hmin, const float hmax, unsigned char areaId,
+ rcCompactHeightfield& chf)
+{
+ rcAssert(ctx);
+
+ ctx->startTimer(RC_TIMER_MARK_CONVEXPOLY_AREA);
+
+ float bmin[3], bmax[3];
+ rcVcopy(bmin, verts);
+ rcVcopy(bmax, verts);
+ for (int i = 1; i < nverts; ++i)
+ {
+ rcVmin(bmin, &verts[i*3]);
+ rcVmax(bmax, &verts[i*3]);
+ }
+ bmin[1] = hmin;
+ bmax[1] = hmax;
+
+ int minx = (int)((bmin[0]-chf.bmin[0])/chf.cs);
+ int miny = (int)((bmin[1]-chf.bmin[1])/chf.ch);
+ int minz = (int)((bmin[2]-chf.bmin[2])/chf.cs);
+ int maxx = (int)((bmax[0]-chf.bmin[0])/chf.cs);
+ int maxy = (int)((bmax[1]-chf.bmin[1])/chf.ch);
+ int maxz = (int)((bmax[2]-chf.bmin[2])/chf.cs);
+
+ if (maxx < 0) return;
+ if (minx >= chf.width) return;
+ if (maxz < 0) return;
+ if (minz >= chf.height) return;
+
+ if (minx < 0) minx = 0;
+ if (maxx >= chf.width) maxx = chf.width-1;
+ if (minz < 0) minz = 0;
+ if (maxz >= chf.height) maxz = chf.height-1;
+
+
+ // TODO: Optimize.
+ for (int z = minz; z <= maxz; ++z)
+ {
+ for (int x = minx; x <= maxx; ++x)
+ {
+ const rcCompactCell& c = chf.cells[x+z*chf.width];
+ for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i)
+ {
+ rcCompactSpan& s = chf.spans[i];
+ if ((int)s.y >= miny && (int)s.y <= maxy)
+ {
+ float p[3];
+ p[0] = chf.bmin[0] + (x+0.5f)*chf.cs;
+ p[1] = 0;
+ p[2] = chf.bmin[2] + (z+0.5f)*chf.cs;
+
+ if (pointInPoly(nverts, verts, p))
+ {
+ chf.areas[i] = areaId;
+ }
+ }
+ }
+ }
+ }
+
+ ctx->stopTimer(RC_TIMER_MARK_CONVEXPOLY_AREA);
+}
diff --git a/dep/recastnavigation/Recast/RecastAssert.h b/dep/recastnavigation/Recast/RecastAssert.h
new file mode 100644
index 00000000000..b58b8fcd286
--- /dev/null
+++ b/dep/recastnavigation/Recast/RecastAssert.h
@@ -0,0 +1,33 @@
+//
+// Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
+//
+// This software is provided 'as-is', without any express or implied
+// warranty. In no event will the authors be held liable for any damages
+// arising from the use of this software.
+// Permission is granted to anyone to use this software for any purpose,
+// including commercial applications, and to alter it and redistribute it
+// freely, subject to the following restrictions:
+// 1. The origin of this software must not be misrepresented; you must not
+// claim that you wrote the original software. If you use this software
+// in a product, an acknowledgment in the product documentation would be
+// appreciated but is not required.
+// 2. Altered source versions must be plainly marked as such, and must not be
+// misrepresented as being the original software.
+// 3. This notice may not be removed or altered from any source distribution.
+//
+
+#ifndef RECASTASSERT_H
+#define RECASTASSERT_H
+
+// Note: This header file's only purpose is to include define assert.
+// Feel free to change the file and include your own implementation instead.
+
+#ifdef NDEBUG
+// From http://cnicholson.net/2009/02/stupid-c-tricks-adventures-in-assert/
+# define rcAssert(x) do { (void)sizeof(x); } while(__LINE__==-1,false)
+#else
+# include <assert.h>
+# define rcAssert assert
+#endif
+
+#endif // RECASTASSERT_H
diff --git a/dep/recastnavigation/Recast/RecastContour.cpp b/dep/recastnavigation/Recast/RecastContour.cpp
new file mode 100644
index 00000000000..1906b6e6f44
--- /dev/null
+++ b/dep/recastnavigation/Recast/RecastContour.cpp
@@ -0,0 +1,804 @@
+//
+// Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
+//
+// This software is provided 'as-is', without any express or implied
+// warranty. In no event will the authors be held liable for any damages
+// arising from the use of this software.
+// Permission is granted to anyone to use this software for any purpose,
+// including commercial applications, and to alter it and redistribute it
+// freely, subject to the following restrictions:
+// 1. The origin of this software must not be misrepresented; you must not
+// claim that you wrote the original software. If you use this software
+// in a product, an acknowledgment in the product documentation would be
+// appreciated but is not required.
+// 2. Altered source versions must be plainly marked as such, and must not be
+// misrepresented as being the original software.
+// 3. This notice may not be removed or altered from any source distribution.
+//
+
+#define _USE_MATH_DEFINES
+#include <math.h>
+#include <string.h>
+#include <stdio.h>
+#include "Recast.h"
+#include "RecastAlloc.h"
+#include "RecastAssert.h"
+
+
+static int getCornerHeight(int x, int y, int i, int dir,
+ const rcCompactHeightfield& chf,
+ bool& isBorderVertex)
+{
+ const rcCompactSpan& s = chf.spans[i];
+ int ch = (int)s.y;
+ int dirp = (dir+1) & 0x3;
+
+ 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.
+ regs[0] = chf.spans[i].reg | (chf.areas[i] << 16);
+
+ 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*chf.width].index + rcGetCon(s, dir);
+ const rcCompactSpan& as = chf.spans[ai];
+ ch = rcMax(ch, (int)as.y);
+ regs[1] = chf.spans[ai].reg | (chf.areas[ai] << 16);
+ if (rcGetCon(as, dirp) != RC_NOT_CONNECTED)
+ {
+ const int ax2 = ax + rcGetDirOffsetX(dirp);
+ const int ay2 = ay + rcGetDirOffsetY(dirp);
+ const int ai2 = (int)chf.cells[ax2+ay2*chf.width].index + rcGetCon(as, dirp);
+ const rcCompactSpan& as2 = chf.spans[ai2];
+ ch = rcMax(ch, (int)as2.y);
+ regs[2] = chf.spans[ai2].reg | (chf.areas[ai2] << 16);
+ }
+ }
+ if (rcGetCon(s, dirp) != RC_NOT_CONNECTED)
+ {
+ const int ax = x + rcGetDirOffsetX(dirp);
+ const int ay = y + rcGetDirOffsetY(dirp);
+ const int ai = (int)chf.cells[ax+ay*chf.width].index + rcGetCon(s, dirp);
+ const rcCompactSpan& as = chf.spans[ai];
+ ch = rcMax(ch, (int)as.y);
+ regs[3] = chf.spans[ai].reg | (chf.areas[ai] << 16);
+ if (rcGetCon(as, dir) != RC_NOT_CONNECTED)
+ {
+ const int ax2 = ax + rcGetDirOffsetX(dir);
+ const int ay2 = ay + rcGetDirOffsetY(dir);
+ const int ai2 = (int)chf.cells[ax2+ay2*chf.width].index + rcGetCon(as, dir);
+ const rcCompactSpan& as2 = chf.spans[ai2];
+ ch = rcMax(ch, (int)as2.y);
+ regs[2] = chf.spans[ai2].reg | (chf.areas[ai2] << 16);
+ }
+ }
+
+ // Check if the vertex is special edge vertex, these vertices will be removed later.
+ for (int j = 0; j < 4; ++j)
+ {
+ const int a = j;
+ const int b = (j+1) & 0x3;
+ const int c = (j+2) & 0x3;
+ const int d = (j+3) & 0x3;
+
+ // The vertex is a border vertex there are two same exterior cells in a row,
+ // followed by two interior cells and none of the regions are out of bounds.
+ const bool twoSameExts = (regs[a] & regs[b] & RC_BORDER_REG) != 0 && regs[a] == regs[b];
+ const bool twoInts = ((regs[c] | regs[d]) & RC_BORDER_REG) == 0;
+ const bool intsSameArea = (regs[c]>>16) == (regs[d]>>16);
+ const bool noZeros = regs[a] != 0 && regs[b] != 0 && regs[c] != 0 && regs[d] != 0;
+ if (twoSameExts && twoInts && intsSameArea && noZeros)
+ {
+ isBorderVertex = true;
+ break;
+ }
+ }
+
+ return ch;
+}
+
+static void walkContour(int x, int y, int i,
+ rcCompactHeightfield& chf,
+ unsigned char* flags, rcIntArray& points)
+{
+ // Choose the first non-connected edge
+ unsigned char dir = 0;
+ while ((flags[i] & (1 << dir)) == 0)
+ dir++;
+
+ unsigned char startDir = dir;
+ int starti = i;
+
+ const unsigned char area = chf.areas[i];
+
+ int iter = 0;
+ while (++iter < 40000)
+ {
+ if (flags[i] & (1 << dir))
+ {
+ // Choose the edge corner
+ bool isBorderVertex = false;
+ bool isAreaBorder = false;
+ int px = x;
+ int py = getCornerHeight(x, y, i, dir, chf, isBorderVertex);
+ int pz = y;
+ switch(dir)
+ {
+ case 0: pz++; break;
+ case 1: px++; pz++; break;
+ case 2: px++; break;
+ }
+ int r = 0;
+ const rcCompactSpan& s = chf.spans[i];
+ 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*chf.width].index + rcGetCon(s, dir);
+ r = (int)chf.spans[ai].reg;
+ if (area != chf.areas[ai])
+ isAreaBorder = true;
+ }
+ if (isBorderVertex)
+ r |= RC_BORDER_VERTEX;
+ if (isAreaBorder)
+ r |= RC_AREA_BORDER;
+ points.push(px);
+ points.push(py);
+ points.push(pz);
+ points.push(r);
+
+ flags[i] &= ~(1 << dir); // Remove visited edges
+ dir = (dir+1) & 0x3; // Rotate CW
+ }
+ else
+ {
+ int ni = -1;
+ const int nx = x + rcGetDirOffsetX(dir);
+ const int ny = y + rcGetDirOffsetY(dir);
+ const rcCompactSpan& s = chf.spans[i];
+ if (rcGetCon(s, dir) != RC_NOT_CONNECTED)
+ {
+ const rcCompactCell& nc = chf.cells[nx+ny*chf.width];
+ ni = (int)nc.index + rcGetCon(s, dir);
+ }
+ if (ni == -1)
+ {
+ // Should not happen.
+ return;
+ }
+ x = nx;
+ y = ny;
+ i = ni;
+ dir = (dir+3) & 0x3; // Rotate CCW
+ }
+
+ if (starti == i && startDir == dir)
+ {
+ break;
+ }
+ }
+}
+
+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);
+ float dz = (float)(z - pz);
+ float d = pqx*pqx + pqz*pqz;
+ float t = pqx*dx + pqz*dz;
+ if (d > 0)
+ t /= d;
+ if (t < 0)
+ t = 0;
+ else if (t > 1)
+ t = 1;
+
+ dx = px + t*pqx - x;
+ dz = pz + t*pqz - z;
+
+ return dx*dx + dz*dz;
+}
+
+static void simplifyContour(rcIntArray& points, rcIntArray& simplified,
+ const float maxError, const int maxEdgeLen, const int buildFlags)
+{
+ // Add initial points.
+ bool hasConnections = false;
+ for (int i = 0; i < points.size(); i += 4)
+ {
+ if ((points[i+3] & RC_CONTOUR_REG_MASK) != 0)
+ {
+ hasConnections = true;
+ break;
+ }
+ }
+
+ if (hasConnections)
+ {
+ // The contour has some portals to other regions.
+ // Add a new point to every location where the region changes.
+ for (int i = 0, ni = points.size()/4; i < ni; ++i)
+ {
+ int ii = (i+1) % ni;
+ const bool differentRegs = (points[i*4+3] & RC_CONTOUR_REG_MASK) != (points[ii*4+3] & RC_CONTOUR_REG_MASK);
+ const bool areaBorders = (points[i*4+3] & RC_AREA_BORDER) != (points[ii*4+3] & RC_AREA_BORDER);
+ if (differentRegs || areaBorders)
+ {
+ simplified.push(points[i*4+0]);
+ simplified.push(points[i*4+1]);
+ 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.
+ // Find lower-left and upper-right vertices of the contour.
+ int llx = points[0];
+ int lly = points[1];
+ int llz = points[2];
+ int lli = 0;
+ int urx = points[0];
+ int ury = points[1];
+ int urz = points[2];
+ int uri = 0;
+ for (int i = 0; i < points.size(); i += 4)
+ {
+ int x = points[i+0];
+ int y = points[i+1];
+ int z = points[i+2];
+ if (x < llx || (x == llx && z < llz))
+ {
+ llx = x;
+ lly = y;
+ llz = z;
+ lli = i/4;
+ }
+ if (x > urx || (x == urx && z > urz))
+ {
+ urx = x;
+ ury = y;
+ urz = z;
+ uri = i/4;
+ }
+ }
+ simplified.push(llx);
+ simplified.push(lly);
+ simplified.push(llz);
+ simplified.push(lli);
+
+ simplified.push(urx);
+ simplified.push(ury);
+ simplified.push(urz);
+ simplified.push(uri);
+ }
+
+ // Add points until all raw points are within
+ // error tolerance to the simplified shape.
+ const int pn = points.size()/4;
+ for (int i = 0; i < simplified.size()/4; )
+ {
+ 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];
+
+ // 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.
+ if (bx > ax || (bx == ax && bz > az))
+ {
+ cinc = 1;
+ ci = (ai+cinc) % pn;
+ endi = bi;
+ }
+ else
+ {
+ cinc = pn-1;
+ ci = (bi+cinc) % pn;
+ endi = ai;
+ }
+
+ // Tessellate only outer edges oredges between areas.
+ if ((points[ci*4+3] & RC_CONTOUR_REG_MASK) == 0 ||
+ (points[ci*4+3] & RC_AREA_BORDER))
+ {
+ while (ci != endi)
+ {
+ float d = distancePtSeg(points[ci*4+0], points[ci*4+2], ax, az, bx, bz);
+ if (d > maxd)
+ {
+ maxd = d;
+ maxi = ci;
+ }
+ ci = (ci+cinc) % pn;
+ }
+ }
+
+
+ // If the max deviation is larger than accepted error,
+ // add new point, else continue to next segment.
+ if (maxi != -1 && maxd > (maxError*maxError))
+ {
+ // Add space for the new point.
+ simplified.resize(simplified.size()+4);
+ const int n = simplified.size()/4;
+ for (int j = n-1; j > i; --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];
+ }
+ // Add the point.
+ simplified[(i+1)*4+0] = points[maxi*4+0];
+ simplified[(i+1)*4+1] = points[maxi*4+1];
+ simplified[(i+1)*4+2] = points[maxi*4+2];
+ simplified[(i+1)*4+3] = maxi;
+ }
+ else
+ {
+ ++i;
+ }
+ }
+
+ // Split too long edges.
+ if (maxEdgeLen > 0 && (buildFlags & (RC_CONTOUR_TESS_WALL_EDGES|RC_CONTOUR_TESS_AREA_EDGES)) != 0)
+ {
+ for (int i = 0; i < simplified.size()/4; )
+ {
+ const 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];
+
+ // 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.
+ if ((buildFlags & RC_CONTOUR_TESS_WALL_EDGES) && (points[ci*4+3] & RC_CONTOUR_REG_MASK) == 0)
+ tess = true;
+ // Edges between areas.
+ if ((buildFlags & RC_CONTOUR_TESS_AREA_EDGES) && (points[ci*4+3] & RC_AREA_BORDER))
+ tess = true;
+
+ if (tess)
+ {
+ int dx = bx - ax;
+ int dz = bz - az;
+ if (dx*dx + dz*dz > maxEdgeLen*maxEdgeLen)
+ {
+ // Round based on the segments in lexilogical order so that the
+ // max tesselation is consistent regardles in which direction
+ // segments are traversed.
+ if (bx > ax || (bx == ax && bz > az))
+ {
+ const int n = bi < ai ? (bi+pn - ai) : (bi - ai);
+ maxi = (ai + n/2) % pn;
+ }
+ else
+ {
+ const int n = bi < ai ? (bi+pn - ai) : (bi - ai);
+ maxi = (ai + (n+1)/2) % pn;
+ }
+ }
+ }
+
+ // If the max deviation is larger than accepted error,
+ // add new point, else continue to next segment.
+ if (maxi != -1)
+ {
+ // Add space for the new point.
+ simplified.resize(simplified.size()+4);
+ const int n = simplified.size()/4;
+ for (int j = n-1; j > i; --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];
+ }
+ // Add the point.
+ simplified[(i+1)*4+0] = points[maxi*4+0];
+ simplified[(i+1)*4+1] = points[maxi*4+1];
+ simplified[(i+1)*4+2] = points[maxi*4+2];
+ simplified[(i+1)*4+3] = maxi;
+ }
+ else
+ {
+ ++i;
+ }
+ }
+ }
+
+ for (int i = 0; i < simplified.size()/4; ++i)
+ {
+ // The edge vertex flag is take from the current raw point,
+ // and the neighbour region is take from the next raw point.
+ const int ai = (simplified[i*4+3]+1) % pn;
+ const int bi = simplified[i*4+3];
+ simplified[i*4+3] = (points[ai*4+3] & RC_CONTOUR_REG_MASK) | (points[bi*4+3] & RC_BORDER_VERTEX);
+ }
+
+}
+
+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;
+ for (int i = 0, j = nverts-1; i < nverts; j=i++)
+ {
+ const int* vi = &verts[i*4];
+ const int* vj = &verts[j*4];
+ area += vi[0] * vj[2] - vj[0] * vi[2];
+ }
+ return (area+1) / 2;
+}
+
+inline bool ileft(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;
+}
+
+static void getClosestIndices(const int* vertsa, const int nvertsa,
+ const int* vertsb, const int nvertsb,
+ int& ia, int& ib)
+{
+ int closestDist = 0xfffffff;
+ ia = -1, ib = -1;
+ for (int i = 0; i < nvertsa; ++i)
+ {
+ 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];
+
+ for (int j = 0; j < nvertsb; ++j)
+ {
+ const int* vb = &vertsb[j*4];
+ // vb must be "infront" of va.
+ if (ileft(vap,va,vb) && ileft(va,van,vb))
+ {
+ 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;
+ }
+ }
+ }
+ }
+}
+
+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)
+ {
+ int* dst = &verts[nv*4];
+ const int* src = &ca.verts[((ia+i)%ca.nverts)*4];
+ dst[0] = src[0];
+ dst[1] = src[1];
+ dst[2] = src[2];
+ dst[3] = src[3];
+ nv++;
+ }
+
+ // Copy contour B
+ for (int i = 0; i <= cb.nverts; ++i)
+ {
+ int* dst = &verts[nv*4];
+ const int* src = &cb.verts[((ib+i)%cb.nverts)*4];
+ dst[0] = src[0];
+ dst[1] = src[1];
+ dst[2] = src[2];
+ dst[3] = src[3];
+ nv++;
+ }
+
+ rcFree(ca.verts);
+ ca.verts = verts;
+ ca.nverts = nv;
+
+ rcFree(cb.verts);
+ cb.verts = 0;
+ cb.nverts = 0;
+
+ return true;
+}
+
+bool rcBuildContours(rcContext* ctx, rcCompactHeightfield& chf,
+ const float maxError, const int maxEdgeLen,
+ rcContourSet& cset, const int buildFlags)
+{
+ rcAssert(ctx);
+
+ const int w = chf.width;
+ const int h = chf.height;
+
+ ctx->startTimer(RC_TIMER_BUILD_CONTOURS);
+
+ rcVcopy(cset.bmin, chf.bmin);
+ rcVcopy(cset.bmax, chf.bmax);
+ cset.cs = chf.cs;
+ cset.ch = chf.ch;
+
+ int maxContours = rcMax((int)chf.maxRegions, 8);
+ cset.conts = (rcContour*)rcAlloc(sizeof(rcContour)*maxContours, RC_ALLOC_PERM);
+ if (!cset.conts)
+ return false;
+ cset.nconts = 0;
+
+ rcScopedDelete<unsigned char> flags = (unsigned char*)rcAlloc(sizeof(unsigned char)*chf.spanCount, RC_ALLOC_TEMP);
+ if (!flags)
+ {
+ ctx->log(RC_LOG_ERROR, "rcBuildContours: Out of memory 'flags' (%d).", chf.spanCount);
+ return false;
+ }
+
+ ctx->startTimer(RC_TIMER_BUILD_CONTOURS_TRACE);
+
+ // Mark boundaries.
+ for (int y = 0; y < h; ++y)
+ {
+ for (int x = 0; x < w; ++x)
+ {
+ const rcCompactCell& c = chf.cells[x+y*w];
+ for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i)
+ {
+ unsigned char res = 0;
+ const rcCompactSpan& s = chf.spans[i];
+ if (!chf.spans[i].reg || (chf.spans[i].reg & RC_BORDER_REG))
+ {
+ flags[i] = 0;
+ continue;
+ }
+ for (int dir = 0; dir < 4; ++dir)
+ {
+ unsigned short r = 0;
+ 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);
+ r = chf.spans[ai].reg;
+ }
+ if (r == chf.spans[i].reg)
+ res |= (1 << dir);
+ }
+ flags[i] = res ^ 0xf; // Inverse, mark non connected edges.
+ }
+ }
+ }
+
+ ctx->stopTimer(RC_TIMER_BUILD_CONTOURS_TRACE);
+
+ ctx->startTimer(RC_TIMER_BUILD_CONTOURS_SIMPLIFY);
+
+ rcIntArray verts(256);
+ rcIntArray simplified(64);
+
+ for (int y = 0; y < h; ++y)
+ {
+ for (int x = 0; x < w; ++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 (flags[i] == 0 || flags[i] == 0xf)
+ {
+ flags[i] = 0;
+ continue;
+ }
+ const unsigned short reg = chf.spans[i].reg;
+ if (!reg || (reg & RC_BORDER_REG))
+ continue;
+ const unsigned char area = chf.areas[i];
+
+ verts.resize(0);
+ simplified.resize(0);
+ walkContour(x, y, i, chf, flags, verts);
+ simplifyContour(verts, simplified, maxError, maxEdgeLen, buildFlags);
+ removeDegenerateSegments(simplified);
+
+ // Store region->contour remap info.
+ // Create contour.
+ if (simplified.size()/4 >= 3)
+ {
+ if (cset.nconts >= maxContours)
+ {
+ // Allocate more contours.
+ // This can happen when there are tiny holes in the heightfield.
+ const int oldMax = maxContours;
+ maxContours *= 2;
+ rcContour* newConts = (rcContour*)rcAlloc(sizeof(rcContour)*maxContours, RC_ALLOC_PERM);
+ for (int j = 0; j < cset.nconts; ++j)
+ {
+ newConts[j] = cset.conts[j];
+ // Reset source pointers to prevent data deletion.
+ cset.conts[j].verts = 0;
+ cset.conts[j].rverts = 0;
+ }
+ 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;
+ cont->verts = (int*)rcAlloc(sizeof(int)*cont->nverts*4, RC_ALLOC_PERM);
+ if (!cont->verts)
+ {
+ ctx->log(RC_LOG_ERROR, "rcBuildContours: Out of memory 'verts' (%d).", cont->nverts);
+ return false;
+ }
+ memcpy(cont->verts, &simplified[0], sizeof(int)*cont->nverts*4);
+
+ cont->nrverts = verts.size()/4;
+ cont->rverts = (int*)rcAlloc(sizeof(int)*cont->nrverts*4, RC_ALLOC_PERM);
+ if (!cont->rverts)
+ {
+ ctx->log(RC_LOG_ERROR, "rcBuildContours: Out of memory 'rverts' (%d).", cont->nrverts);
+ return false;
+ }
+ memcpy(cont->rverts, &verts[0], sizeof(int)*cont->nrverts*4);
+
+/* 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;
+ }
+ }
+ }
+ }
+
+ // 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)
+ {
+ rcContour& cont = cset.conts[i];
+ // Check if the contour is would backwards.
+ if (calcAreaOfPolygon2D(cont.verts, cont.nverts) < 0)
+ {
+ // Find another contour which has the same region ID.
+ int mergeIdx = -1;
+ for (int j = 0; j < cset.nconts; ++j)
+ {
+ if (i == j) continue;
+ if (cset.conts[j].nverts && cset.conts[j].reg == cont.reg)
+ {
+ // Make sure the polygon is correctly oriented.
+ if (calcAreaOfPolygon2D(cset.conts[j].verts, cset.conts[j].nverts))
+ {
+ mergeIdx = j;
+ break;
+ }
+ }
+ }
+ if (mergeIdx == -1)
+ {
+ 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)
+ {
+ ctx->log(RC_LOG_WARNING, "rcBuildContours: Failed to find merge points for %d and %d.", i, mergeIdx);
+ continue;
+ }
+ if (!mergeContours(mcont, cont, ia, ib))
+ {
+ ctx->log(RC_LOG_WARNING, "rcBuildContours: Failed to merge contours %d and %d.", i, mergeIdx);
+ continue;
+ }
+ }
+ }
+ }
+
+ ctx->stopTimer(RC_TIMER_BUILD_CONTOURS_SIMPLIFY);
+
+ ctx->stopTimer(RC_TIMER_BUILD_CONTOURS);
+
+ return true;
+}
diff --git a/dep/recastnavigation/Recast/RecastFilter.cpp b/dep/recastnavigation/Recast/RecastFilter.cpp
new file mode 100644
index 00000000000..d01808a79a8
--- /dev/null
+++ b/dep/recastnavigation/Recast/RecastFilter.cpp
@@ -0,0 +1,179 @@
+//
+// Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
+//
+// This software is provided 'as-is', without any express or implied
+// warranty. In no event will the authors be held liable for any damages
+// arising from the use of this software.
+// Permission is granted to anyone to use this software for any purpose,
+// including commercial applications, and to alter it and redistribute it
+// freely, subject to the following restrictions:
+// 1. The origin of this software must not be misrepresented; you must not
+// claim that you wrote the original software. If you use this software
+// in a product, an acknowledgment in the product documentation would be
+// appreciated but is not required.
+// 2. Altered source versions must be plainly marked as such, and must not be
+// misrepresented as being the original software.
+// 3. This notice may not be removed or altered from any source distribution.
+//
+
+#define _USE_MATH_DEFINES
+#include <math.h>
+#include <stdio.h>
+#include "Recast.h"
+#include "RecastAssert.h"
+
+
+void rcFilterLowHangingWalkableObstacles(rcContext* ctx, const int walkableClimb, rcHeightfield& solid)
+{
+ rcAssert(ctx);
+
+ ctx->startTimer(RC_TIMER_FILTER_LOW_OBSTACLES);
+
+ const int w = solid.width;
+ const int h = solid.height;
+
+ for (int y = 0; y < h; ++y)
+ {
+ for (int x = 0; x < w; ++x)
+ {
+ rcSpan* ps = 0;
+ bool previousWalkable = false;
+
+ for (rcSpan* s = solid.spans[x + y*w]; s; ps = s, s = s->next)
+ {
+ const bool walkable = s->area != RC_NULL_AREA;
+ // If current span is not walkable, but there is walkable
+ // span just below it, mark the span above it walkable too.
+ if (!walkable && previousWalkable)
+ {
+ if (rcAbs((int)s->smax - (int)ps->smax) <= walkableClimb)
+ s->area = RC_NULL_AREA;
+ }
+ // Copy walkable flag so that it cannot propagate
+ // past multiple non-walkable objects.
+ previousWalkable = walkable;
+ }
+ }
+ }
+
+ ctx->stopTimer(RC_TIMER_FILTER_LOW_OBSTACLES);
+}
+
+void rcFilterLedgeSpans(rcContext* ctx, const int walkableHeight, const int walkableClimb,
+ rcHeightfield& solid)
+{
+ rcAssert(ctx);
+
+ ctx->startTimer(RC_TIMER_FILTER_BORDER);
+
+ const int w = solid.width;
+ const int h = solid.height;
+ const int MAX_HEIGHT = 0xffff;
+
+ // Mark border spans.
+ for (int y = 0; y < h; ++y)
+ {
+ for (int x = 0; x < w; ++x)
+ {
+ for (rcSpan* s = solid.spans[x + y*w]; s; s = s->next)
+ {
+ // Skip non walkable spans.
+ if (s->area == RC_NULL_AREA)
+ continue;
+
+ const int bot = (int)(s->smax);
+ const int top = s->next ? (int)(s->next->smin) : MAX_HEIGHT;
+
+ // Find neighbours minimum height.
+ int minh = MAX_HEIGHT;
+
+ // Min and max height of accessible neighbours.
+ int asmin = s->smax;
+ int asmax = s->smax;
+
+ for (int dir = 0; dir < 4; ++dir)
+ {
+ int dx = x + rcGetDirOffsetX(dir);
+ int dy = y + rcGetDirOffsetY(dir);
+ // Skip neighbours which are out of bounds.
+ if (dx < 0 || dy < 0 || dx >= w || dy >= h)
+ {
+ minh = rcMin(minh, -walkableClimb - bot);
+ continue;
+ }
+
+ // From minus infinity to the first span.
+ rcSpan* ns = solid.spans[dx + dy*w];
+ int nbot = -walkableClimb;
+ int ntop = ns ? (int)ns->smin : MAX_HEIGHT;
+ // Skip neightbour if the gap between the spans is too small.
+ if (rcMin(top,ntop) - rcMax(bot,nbot) > walkableHeight)
+ minh = rcMin(minh, nbot - bot);
+
+ // Rest of the spans.
+ for (ns = solid.spans[dx + dy*w]; ns; ns = ns->next)
+ {
+ nbot = (int)ns->smax;
+ ntop = ns->next ? (int)ns->next->smin : MAX_HEIGHT;
+ // Skip neightbour if the gap between the spans is too small.
+ if (rcMin(top,ntop) - rcMax(bot,nbot) > walkableHeight)
+ {
+ minh = rcMin(minh, nbot - bot);
+
+ // Find min/max accessible neighbour height.
+ if (rcAbs(nbot - bot) <= walkableClimb)
+ {
+ if (nbot < asmin) asmin = nbot;
+ if (nbot > asmax) asmax = nbot;
+ }
+
+ }
+ }
+ }
+
+ // The current span is close to a ledge if the drop to any
+ // neighbour span is less than the walkableClimb.
+ if (minh < -walkableClimb)
+ s->area = RC_NULL_AREA;
+
+ // If the difference between all neighbours is too large,
+ // we are at steep slope, mark the span as ledge.
+ if ((asmax - asmin) > walkableClimb)
+ {
+ s->area = RC_NULL_AREA;
+ }
+ }
+ }
+ }
+
+ ctx->stopTimer(RC_TIMER_FILTER_BORDER);
+}
+
+void rcFilterWalkableLowHeightSpans(rcContext* ctx, int walkableHeight, rcHeightfield& solid)
+{
+ rcAssert(ctx);
+
+ ctx->startTimer(RC_TIMER_FILTER_WALKABLE);
+
+ const int w = solid.width;
+ const int h = solid.height;
+ const int MAX_HEIGHT = 0xffff;
+
+ // Remove walkable flag from spans which do not have enough
+ // space above them for the agent to stand there.
+ for (int y = 0; y < h; ++y)
+ {
+ for (int x = 0; x < w; ++x)
+ {
+ for (rcSpan* s = solid.spans[x + y*w]; s; s = s->next)
+ {
+ const int bot = (int)(s->smax);
+ const int top = s->next ? (int)(s->next->smin) : MAX_HEIGHT;
+ if ((top - bot) <= walkableHeight)
+ s->area = RC_NULL_AREA;
+ }
+ }
+ }
+
+ ctx->stopTimer(RC_TIMER_FILTER_WALKABLE);
+}
diff --git a/dep/recastnavigation/Recast/RecastMesh.cpp b/dep/recastnavigation/Recast/RecastMesh.cpp
new file mode 100644
index 00000000000..4b33c106d10
--- /dev/null
+++ b/dep/recastnavigation/Recast/RecastMesh.cpp
@@ -0,0 +1,1322 @@
+//
+// Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
+//
+// This software is provided 'as-is', without any express or implied
+// warranty. In no event will the authors be held liable for any damages
+// arising from the use of this software.
+// Permission is granted to anyone to use this software for any purpose,
+// including commercial applications, and to alter it and redistribute it
+// freely, subject to the following restrictions:
+// 1. The origin of this software must not be misrepresented; you must not
+// claim that you wrote the original software. If you use this software
+// in a product, an acknowledgment in the product documentation would be
+// appreciated but is not required.
+// 2. Altered source versions must be plainly marked as such, and must not be
+// misrepresented as being the original software.
+// 3. This notice may not be removed or altered from any source distribution.
+//
+
+#define _USE_MATH_DEFINES
+#include <math.h>
+#include <string.h>
+#include <stdio.h>
+#include "Recast.h"
+#include "RecastAlloc.h"
+#include "RecastAssert.h"
+
+struct rcEdge
+{
+ unsigned short vert[2];
+ unsigned short polyEdge[2];
+ unsigned short poly[2];
+};
+
+static bool buildMeshAdjacency(unsigned short* polys, const int npolys,
+ const int nverts, const int vertsPerPoly)
+{
+ // Based on code by Eric Lengyel from:
+ // http://www.terathon.com/code/edges.php
+
+ int maxEdgeCount = npolys*vertsPerPoly;
+ unsigned short* firstEdge = (unsigned short*)rcAlloc(sizeof(unsigned short)*(nverts + maxEdgeCount), RC_ALLOC_TEMP);
+ if (!firstEdge)
+ return false;
+ unsigned short* nextEdge = firstEdge + nverts;
+ int edgeCount = 0;
+
+ rcEdge* edges = (rcEdge*)rcAlloc(sizeof(rcEdge)*maxEdgeCount, RC_ALLOC_TEMP);
+ if (!edges)
+ {
+ rcFree(firstEdge);
+ return false;
+ }
+
+ for (int i = 0; i < nverts; i++)
+ firstEdge[i] = RC_MESH_NULL_IDX;
+
+ for (int i = 0; i < npolys; ++i)
+ {
+ unsigned short* t = &polys[i*vertsPerPoly*2];
+ for (int j = 0; j < vertsPerPoly; ++j)
+ {
+ unsigned short v0 = t[j];
+ unsigned short v1 = (j+1 >= vertsPerPoly || t[j+1] == RC_MESH_NULL_IDX) ? t[0] : t[j+1];
+ if (v0 < v1)
+ {
+ rcEdge& edge = edges[edgeCount];
+ edge.vert[0] = v0;
+ edge.vert[1] = v1;
+ edge.poly[0] = (unsigned short)i;
+ edge.polyEdge[0] = (unsigned short)j;
+ edge.poly[1] = (unsigned short)i;
+ edge.polyEdge[1] = 0;
+ // Insert edge
+ nextEdge[edgeCount] = firstEdge[v0];
+ firstEdge[v0] = (unsigned short)edgeCount;
+ edgeCount++;
+ }
+ }
+ }
+
+ for (int i = 0; i < npolys; ++i)
+ {
+ unsigned short* t = &polys[i*vertsPerPoly*2];
+ for (int j = 0; j < vertsPerPoly; ++j)
+ {
+ unsigned short v0 = t[j];
+ unsigned short v1 = (j+1 >= vertsPerPoly || t[j+1] == RC_MESH_NULL_IDX) ? t[0] : t[j+1];
+ if (v0 > v1)
+ {
+ for (unsigned short e = firstEdge[v1]; e != RC_MESH_NULL_IDX; e = nextEdge[e])
+ {
+ rcEdge& edge = edges[e];
+ if (edge.vert[1] == v0 && edge.poly[0] == edge.poly[1])
+ {
+ edge.poly[1] = (unsigned short)i;
+ edge.polyEdge[1] = (unsigned short)j;
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ // Store adjacency
+ for (int i = 0; i < edgeCount; ++i)
+ {
+ const rcEdge& e = edges[i];
+ if (e.poly[0] != e.poly[1])
+ {
+ unsigned short* p0 = &polys[e.poly[0]*vertsPerPoly*2];
+ unsigned short* p1 = &polys[e.poly[1]*vertsPerPoly*2];
+ p0[vertsPerPoly + e.polyEdge[0]] = e.poly[1];
+ p1[vertsPerPoly + e.polyEdge[1]] = e.poly[0];
+ }
+ }
+
+ rcFree(firstEdge);
+ rcFree(edges);
+
+ return true;
+}
+
+
+static const int VERTEX_BUCKET_COUNT = (1<<12);
+
+inline int computeVertexHash(int x, int y, int z)
+{
+ const unsigned int h1 = 0x8da6b343; // Large multiplicative constants;
+ const unsigned int h2 = 0xd8163841; // here arbitrarily chosen primes
+ const unsigned int h3 = 0xcb1ab31f;
+ unsigned int n = h1 * x + h2 * y + h3 * z;
+ return (int)(n & (VERTEX_BUCKET_COUNT-1));
+}
+
+static unsigned short addVertex(unsigned short x, unsigned short y, unsigned short z,
+ unsigned short* verts, int* firstVert, int* nextVert, int& nv)
+{
+ int bucket = computeVertexHash(x, 0, z);
+ int i = firstVert[bucket];
+
+ while (i != -1)
+ {
+ const unsigned short* v = &verts[i*3];
+ if (v[0] == x && (rcAbs(v[1] - y) <= 2) && v[2] == z)
+ return (unsigned short)i;
+ i = nextVert[i]; // next
+ }
+
+ // Could not find, create new.
+ i = nv; nv++;
+ unsigned short* v = &verts[i*3];
+ v[0] = x;
+ v[1] = y;
+ v[2] = z;
+ nextVert[i] = firstVert[bucket];
+ firstVert[bucket] = i;
+
+ return (unsigned short)i;
+}
+
+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]);
+}
+
+// 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)
+{
+ 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.
+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];
+}
+
+// Returns T iff (v_i, v_j) is a proper internal *or* external
+// diagonal of P, *ignoring edges incident to v_i and v_j*.
+static bool diagonalie(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 (intersect(d0, d1, p0, p1))
+ return false;
+ }
+ }
+ return true;
+}
+
+// Returns true iff the diagonal (i,j) is strictly internal to the
+// polygon P in the neighborhood of the i endpoint.
+static bool inCone(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 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));
+}
+
+// Returns T iff (v_i, v_j) is a proper internal
+// diagonal of P.
+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 int triangulate(int n, const int* verts, int* indices, int* tris)
+{
+ int ntris = 0;
+ int* dst = tris;
+
+ // The last bit of the index is used to indicate if the vertex can be removed.
+ for (int i = 0; i < n; i++)
+ {
+ int i1 = next(i, n);
+ int i2 = next(i1, n);
+ if (diagonal(i, i2, n, verts, indices))
+ indices[i1] |= 0x80000000;
+ }
+
+ while (n > 3)
+ {
+ int minLen = -1;
+ int mini = -1;
+ for (int i = 0; i < n; i++)
+ {
+ int i1 = next(i, n);
+ if (indices[i1] & 0x80000000)
+ {
+ const int* p0 = &verts[(indices[i] & 0x0fffffff) * 4];
+ const int* p2 = &verts[(indices[next(i1, 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)
+ {
+ // Should not happen.
+/* printf("mini == -1 ntris=%d n=%d\n", ntris, n);
+ for (int i = 0; i < n; i++)
+ {
+ printf("%d ", indices[i] & 0x0fffffff);
+ }
+ printf("\n");*/
+ return -ntris;
+ }
+
+ int i = mini;
+ int i1 = next(i, n);
+ int i2 = next(i1, n);
+
+ *dst++ = indices[i] & 0x0fffffff;
+ *dst++ = indices[i1] & 0x0fffffff;
+ *dst++ = indices[i2] & 0x0fffffff;
+ ntris++;
+
+ // Removes P[i1] by copying P[i+1]...P[n-1] left one index.
+ n--;
+ for (int k = i1; k < n; k++)
+ indices[k] = indices[k+1];
+
+ if (i1 >= n) i1 = 0;
+ i = prev(i1,n);
+ // Update diagonal flags.
+ if (diagonal(prev(i, n), i1, n, verts, indices))
+ indices[i] |= 0x80000000;
+ else
+ indices[i] &= 0x0fffffff;
+
+ if (diagonal(i, next(i1, n), n, verts, indices))
+ indices[i1] |= 0x80000000;
+ else
+ indices[i1] &= 0x0fffffff;
+ }
+
+ // Append the remaining triangle.
+ *dst++ = indices[0] & 0x0fffffff;
+ *dst++ = indices[1] & 0x0fffffff;
+ *dst++ = indices[2] & 0x0fffffff;
+ ntris++;
+
+ return ntris;
+}
+
+static int countPolyVerts(const unsigned short* p, const int nvp)
+{
+ for (int i = 0; i < nvp; ++i)
+ if (p[i] == RC_MESH_NULL_IDX)
+ return i;
+ return nvp;
+}
+
+inline bool uleft(const unsigned short* a, const unsigned short* b, const unsigned short* c)
+{
+ return ((int)b[0] - (int)a[0]) * ((int)c[2] - (int)a[2]) -
+ ((int)c[0] - (int)a[0]) * ((int)b[2] - (int)a[2]) < 0;
+}
+
+static int getPolyMergeValue(unsigned short* pa, unsigned short* pb,
+ const unsigned short* verts, int& ea, int& eb,
+ const int nvp)
+{
+ const int na = countPolyVerts(pa, nvp);
+ const int nb = countPolyVerts(pb, nvp);
+
+ // If the merged polygon would be too big, do not merge.
+ if (na+nb-2 > nvp)
+ return -1;
+
+ // Check if the polygons share an edge.
+ ea = -1;
+ eb = -1;
+
+ for (int i = 0; i < na; ++i)
+ {
+ unsigned short va0 = pa[i];
+ unsigned short va1 = pa[(i+1) % na];
+ if (va0 > va1)
+ rcSwap(va0, va1);
+ for (int j = 0; j < nb; ++j)
+ {
+ unsigned short vb0 = pb[j];
+ unsigned short vb1 = pb[(j+1) % nb];
+ if (vb0 > vb1)
+ rcSwap(vb0, vb1);
+ if (va0 == vb0 && va1 == vb1)
+ {
+ ea = i;
+ eb = j;
+ break;
+ }
+ }
+ }
+
+ // No common edge, cannot merge.
+ if (ea == -1 || eb == -1)
+ return -1;
+
+ // Check to see if the merged polygon would be convex.
+ unsigned short va, vb, vc;
+
+ va = pa[(ea+na-1) % na];
+ vb = pa[ea];
+ vc = pb[(eb+2) % nb];
+ if (!uleft(&verts[va*3], &verts[vb*3], &verts[vc*3]))
+ return -1;
+
+ va = pb[(eb+nb-1) % nb];
+ vb = pb[eb];
+ vc = pa[(ea+2) % na];
+ if (!uleft(&verts[va*3], &verts[vb*3], &verts[vc*3]))
+ return -1;
+
+ va = pa[ea];
+ vb = pa[(ea+1)%na];
+
+ int dx = (int)verts[va*3+0] - (int)verts[vb*3+0];
+ int dy = (int)verts[va*3+2] - (int)verts[vb*3+2];
+
+ return dx*dx + dy*dy;
+}
+
+static void mergePolys(unsigned short* pa, unsigned short* pb, int ea, int eb,
+ unsigned short* tmp, const int nvp)
+{
+ const int na = countPolyVerts(pa, nvp);
+ const int nb = countPolyVerts(pb, nvp);
+
+ // Merge polygons.
+ memset(tmp, 0xff, sizeof(unsigned short)*nvp);
+ int n = 0;
+ // Add pa
+ for (int i = 0; i < na-1; ++i)
+ tmp[n++] = pa[(ea+1+i) % na];
+ // Add pb
+ for (int i = 0; i < nb-1; ++i)
+ tmp[n++] = pb[(eb+1+i) % nb];
+
+ memcpy(pa, tmp, sizeof(unsigned short)*nvp);
+}
+
+static void pushFront(int v, int* arr, int& an)
+{
+ an++;
+ for (int i = an-1; i > 0; --i) arr[i] = arr[i-1];
+ arr[0] = v;
+}
+
+static void pushBack(int v, int* arr, int& an)
+{
+ arr[an] = v;
+ an++;
+}
+
+static bool canRemoveVertex(rcContext* ctx, rcPolyMesh& mesh, const unsigned short rem)
+{
+ const int nvp = mesh.nvp;
+
+ // Count number of polygons to remove.
+ int numRemovedVerts = 0;
+ int numTouchedVerts = 0;
+ int numRemainingEdges = 0;
+ for (int i = 0; i < mesh.npolys; ++i)
+ {
+ unsigned short* p = &mesh.polys[i*nvp*2];
+ const int nv = countPolyVerts(p, nvp);
+ int numRemoved = 0;
+ int numVerts = 0;
+ for (int j = 0; j < nv; ++j)
+ {
+ if (p[j] == rem)
+ {
+ numTouchedVerts++;
+ numRemoved++;
+ }
+ numVerts++;
+ }
+ if (numRemoved)
+ {
+ numRemovedVerts += numRemoved;
+ numRemainingEdges += numVerts-(numRemoved+1);
+ }
+ }
+
+ // There would be too few edges remaining to create a polygon.
+ // This can happen for example when a tip of a triangle is marked
+ // as deletion, but there are no other polys that share the vertex.
+ // In this case, the vertex should not be removed.
+ if (numRemainingEdges <= 2)
+ return false;
+
+ // Find edges which share the removed vertex.
+ const int maxEdges = numTouchedVerts*2;
+ int nedges = 0;
+ rcScopedDelete<int> edges = (int*)rcAlloc(sizeof(int)*maxEdges*3, RC_ALLOC_TEMP);
+ if (!edges)
+ {
+ ctx->log(RC_LOG_WARNING, "canRemoveVertex: Out of memory 'edges' (%d).", maxEdges*3);
+ return false;
+ }
+
+ for (int i = 0; i < mesh.npolys; ++i)
+ {
+ unsigned short* p = &mesh.polys[i*nvp*2];
+ const int nv = countPolyVerts(p, nvp);
+
+ // Collect edges which touches the removed vertex.
+ for (int j = 0, k = nv-1; j < nv; k = j++)
+ {
+ if (p[j] == rem || p[k] == rem)
+ {
+ // Arrange edge so that a=rem.
+ int a = p[j], b = p[k];
+ if (b == rem)
+ rcSwap(a,b);
+
+ // Check if the edge exists
+ bool exists = false;
+ for (int k = 0; k < nedges; ++k)
+ {
+ int* e = &edges[k*3];
+ if (e[1] == b)
+ {
+ // Exists, increment vertex share count.
+ e[2]++;
+ exists = true;
+ }
+ }
+ // Add new edge.
+ if (!exists)
+ {
+ int* e = &edges[nedges*3];
+ e[0] = a;
+ e[1] = b;
+ e[2] = 1;
+ nedges++;
+ }
+ }
+ }
+ }
+
+ // There should be no more than 2 open edges.
+ // This catches the case that two non-adjacent polygons
+ // share the removed vertex. In that case, do not remove the vertex.
+ int numOpenEdges = 0;
+ for (int i = 0; i < nedges; ++i)
+ {
+ if (edges[i*3+2] < 2)
+ numOpenEdges++;
+ }
+ if (numOpenEdges > 2)
+ return false;
+
+ return true;
+}
+
+static bool removeVertex(rcContext* ctx, rcPolyMesh& mesh, const unsigned short rem, const int maxTris)
+{
+ const int nvp = mesh.nvp;
+
+ // Count number of polygons to remove.
+ int numRemovedVerts = 0;
+ for (int i = 0; i < mesh.npolys; ++i)
+ {
+ unsigned short* p = &mesh.polys[i*nvp*2];
+ const int nv = countPolyVerts(p, nvp);
+ for (int j = 0; j < nv; ++j)
+ {
+ if (p[j] == rem)
+ numRemovedVerts++;
+ }
+ }
+
+ int nedges = 0;
+ rcScopedDelete<int> edges = (int*)rcAlloc(sizeof(int)*numRemovedVerts*nvp*4, RC_ALLOC_TEMP);
+ if (!edges)
+ {
+ ctx->log(RC_LOG_WARNING, "removeVertex: Out of memory 'edges' (%d).", numRemovedVerts*nvp*4);
+ return false;
+ }
+
+ int nhole = 0;
+ rcScopedDelete<int> hole = (int*)rcAlloc(sizeof(int)*numRemovedVerts*nvp, RC_ALLOC_TEMP);
+ if (!hole)
+ {
+ ctx->log(RC_LOG_WARNING, "removeVertex: Out of memory 'hole' (%d).", numRemovedVerts*nvp);
+ return false;
+ }
+
+ int nhreg = 0;
+ rcScopedDelete<int> hreg = (int*)rcAlloc(sizeof(int)*numRemovedVerts*nvp, RC_ALLOC_TEMP);
+ if (!hreg)
+ {
+ ctx->log(RC_LOG_WARNING, "removeVertex: Out of memory 'hreg' (%d).", numRemovedVerts*nvp);
+ return false;
+ }
+
+ int nharea = 0;
+ rcScopedDelete<int> harea = (int*)rcAlloc(sizeof(int)*numRemovedVerts*nvp, RC_ALLOC_TEMP);
+ if (!harea)
+ {
+ ctx->log(RC_LOG_WARNING, "removeVertex: Out of memory 'harea' (%d).", numRemovedVerts*nvp);
+ return false;
+ }
+
+ for (int i = 0; i < mesh.npolys; ++i)
+ {
+ unsigned short* p = &mesh.polys[i*nvp*2];
+ const int nv = countPolyVerts(p, nvp);
+ bool hasRem = false;
+ for (int j = 0; j < nv; ++j)
+ if (p[j] == rem) hasRem = true;
+ if (hasRem)
+ {
+ // Collect edges which does not touch the removed vertex.
+ for (int j = 0, k = nv-1; j < nv; k = j++)
+ {
+ if (p[j] != rem && p[k] != rem)
+ {
+ int* e = &edges[nedges*4];
+ e[0] = p[k];
+ e[1] = p[j];
+ e[2] = mesh.regs[i];
+ e[3] = mesh.areas[i];
+ nedges++;
+ }
+ }
+ // Remove the polygon.
+ unsigned short* p2 = &mesh.polys[(mesh.npolys-1)*nvp*2];
+ memcpy(p,p2,sizeof(unsigned short)*nvp);
+ memset(p+nvp,0xff,sizeof(unsigned short)*nvp);
+ mesh.regs[i] = mesh.regs[mesh.npolys-1];
+ mesh.areas[i] = mesh.areas[mesh.npolys-1];
+ mesh.npolys--;
+ --i;
+ }
+ }
+
+ // Remove vertex.
+ for (int i = (int)rem; i < mesh.nverts; ++i)
+ {
+ mesh.verts[i*3+0] = mesh.verts[(i+1)*3+0];
+ mesh.verts[i*3+1] = mesh.verts[(i+1)*3+1];
+ mesh.verts[i*3+2] = mesh.verts[(i+1)*3+2];
+ }
+ mesh.nverts--;
+
+ // Adjust indices to match the removed vertex layout.
+ for (int i = 0; i < mesh.npolys; ++i)
+ {
+ unsigned short* p = &mesh.polys[i*nvp*2];
+ const int nv = countPolyVerts(p, nvp);
+ for (int j = 0; j < nv; ++j)
+ if (p[j] > rem) p[j]--;
+ }
+ for (int i = 0; i < nedges; ++i)
+ {
+ if (edges[i*4+0] > rem) edges[i*4+0]--;
+ if (edges[i*4+1] > rem) edges[i*4+1]--;
+ }
+
+ if (nedges == 0)
+ return true;
+
+ // Start with one vertex, keep appending connected
+ // segments to the start and end of the hole.
+ pushBack(edges[0], hole, nhole);
+ pushBack(edges[2], hreg, nhreg);
+ pushBack(edges[3], harea, nharea);
+
+ while (nedges)
+ {
+ bool match = false;
+
+ for (int i = 0; i < nedges; ++i)
+ {
+ const int ea = edges[i*4+0];
+ const int eb = edges[i*4+1];
+ const int r = edges[i*4+2];
+ const int a = edges[i*4+3];
+ bool add = false;
+ if (hole[0] == eb)
+ {
+ // The segment matches the beginning of the hole boundary.
+ pushFront(ea, hole, nhole);
+ pushFront(r, hreg, nhreg);
+ pushFront(a, harea, nharea);
+ add = true;
+ }
+ else if (hole[nhole-1] == ea)
+ {
+ // The segment matches the end of the hole boundary.
+ pushBack(eb, hole, nhole);
+ pushBack(r, hreg, nhreg);
+ pushBack(a, harea, nharea);
+ add = true;
+ }
+ if (add)
+ {
+ // The edge segment was added, remove it.
+ edges[i*4+0] = edges[(nedges-1)*4+0];
+ edges[i*4+1] = edges[(nedges-1)*4+1];
+ edges[i*4+2] = edges[(nedges-1)*4+2];
+ edges[i*4+3] = edges[(nedges-1)*4+3];
+ --nedges;
+ match = true;
+ --i;
+ }
+ }
+
+ if (!match)
+ break;
+ }
+
+ rcScopedDelete<int> tris = (int*)rcAlloc(sizeof(int)*nhole*3, RC_ALLOC_TEMP);
+ if (!tris)
+ {
+ ctx->log(RC_LOG_WARNING, "removeVertex: Out of memory 'tris' (%d).", nhole*3);
+ return false;
+ }
+
+ rcScopedDelete<int> tverts = (int*)rcAlloc(sizeof(int)*nhole*4, RC_ALLOC_TEMP);
+ if (!tverts)
+ {
+ ctx->log(RC_LOG_WARNING, "removeVertex: Out of memory 'tverts' (%d).", nhole*4);
+ return false;
+ }
+
+ rcScopedDelete<int> thole = (int*)rcAlloc(sizeof(int)*nhole, RC_ALLOC_TEMP);
+ if (!tverts)
+ {
+ ctx->log(RC_LOG_WARNING, "removeVertex: Out of memory 'thole' (%d).", nhole);
+ return false;
+ }
+
+ // Generate temp vertex array for triangulation.
+ for (int i = 0; i < nhole; ++i)
+ {
+ const int pi = hole[i];
+ tverts[i*4+0] = mesh.verts[pi*3+0];
+ tverts[i*4+1] = mesh.verts[pi*3+1];
+ tverts[i*4+2] = mesh.verts[pi*3+2];
+ tverts[i*4+3] = 0;
+ thole[i] = i;
+ }
+
+ // Triangulate the hole.
+ int ntris = triangulate(nhole, &tverts[0], &thole[0], tris);
+ if (ntris < 0)
+ {
+ ntris = -ntris;
+ ctx->log(RC_LOG_WARNING, "removeVertex: triangulate() returned bad results.");
+ }
+
+ // Merge the hole triangles back to polygons.
+ rcScopedDelete<unsigned short> polys = (unsigned short*)rcAlloc(sizeof(unsigned short)*(ntris+1)*nvp, RC_ALLOC_TEMP);
+ if (!polys)
+ {
+ ctx->log(RC_LOG_ERROR, "removeVertex: Out of memory 'polys' (%d).", (ntris+1)*nvp);
+ return false;
+ }
+ rcScopedDelete<unsigned short> pregs = (unsigned short*)rcAlloc(sizeof(unsigned short)*ntris, RC_ALLOC_TEMP);
+ if (!pregs)
+ {
+ ctx->log(RC_LOG_ERROR, "removeVertex: Out of memory 'pregs' (%d).", ntris);
+ return false;
+ }
+ rcScopedDelete<unsigned char> pareas = (unsigned char*)rcAlloc(sizeof(unsigned char)*ntris, RC_ALLOC_TEMP);
+ if (!pregs)
+ {
+ ctx->log(RC_LOG_ERROR, "removeVertex: Out of memory 'pareas' (%d).", ntris);
+ return false;
+ }
+
+ unsigned short* tmpPoly = &polys[ntris*nvp];
+
+ // Build initial polygons.
+ int npolys = 0;
+ memset(polys, 0xff, ntris*nvp*sizeof(unsigned short));
+ for (int j = 0; j < ntris; ++j)
+ {
+ int* t = &tris[j*3];
+ if (t[0] != t[1] && t[0] != t[2] && t[1] != t[2])
+ {
+ polys[npolys*nvp+0] = (unsigned short)hole[t[0]];
+ polys[npolys*nvp+1] = (unsigned short)hole[t[1]];
+ polys[npolys*nvp+2] = (unsigned short)hole[t[2]];
+ pregs[npolys] = (unsigned short)hreg[t[0]];
+ pareas[npolys] = (unsigned char)harea[t[0]];
+ npolys++;
+ }
+ }
+ if (!npolys)
+ return true;
+
+ // Merge polygons.
+ if (nvp > 3)
+ {
+ for (;;)
+ {
+ // Find best polygons to merge.
+ int bestMergeVal = 0;
+ int bestPa = 0, bestPb = 0, bestEa = 0, bestEb = 0;
+
+ for (int j = 0; j < npolys-1; ++j)
+ {
+ unsigned short* pj = &polys[j*nvp];
+ for (int k = j+1; k < npolys; ++k)
+ {
+ unsigned short* pk = &polys[k*nvp];
+ int ea, eb;
+ int v = getPolyMergeValue(pj, pk, mesh.verts, ea, eb, nvp);
+ if (v > bestMergeVal)
+ {
+ bestMergeVal = v;
+ bestPa = j;
+ bestPb = k;
+ bestEa = ea;
+ bestEb = eb;
+ }
+ }
+ }
+
+ if (bestMergeVal > 0)
+ {
+ // Found best, merge.
+ unsigned short* pa = &polys[bestPa*nvp];
+ unsigned short* pb = &polys[bestPb*nvp];
+ mergePolys(pa, pb, bestEa, bestEb, tmpPoly, nvp);
+ memcpy(pb, &polys[(npolys-1)*nvp], sizeof(unsigned short)*nvp);
+ pregs[bestPb] = pregs[npolys-1];
+ pareas[bestPb] = pareas[npolys-1];
+ npolys--;
+ }
+ else
+ {
+ // Could not merge any polygons, stop.
+ break;
+ }
+ }
+ }
+
+ // Store polygons.
+ for (int i = 0; i < npolys; ++i)
+ {
+ if (mesh.npolys >= maxTris) break;
+ unsigned short* p = &mesh.polys[mesh.npolys*nvp*2];
+ memset(p,0xff,sizeof(unsigned short)*nvp*2);
+ for (int j = 0; j < nvp; ++j)
+ p[j] = polys[i*nvp+j];
+ mesh.regs[mesh.npolys] = pregs[i];
+ mesh.areas[mesh.npolys] = pareas[i];
+ mesh.npolys++;
+ if (mesh.npolys > maxTris)
+ {
+ ctx->log(RC_LOG_ERROR, "removeVertex: Too many polygons %d (max:%d).", mesh.npolys, maxTris);
+ return false;
+ }
+ }
+
+ return true;
+}
+
+
+bool rcBuildPolyMesh(rcContext* ctx, rcContourSet& cset, int nvp, rcPolyMesh& mesh)
+{
+ rcAssert(ctx);
+
+ ctx->startTimer(RC_TIMER_BUILD_POLYMESH);
+
+ rcVcopy(mesh.bmin, cset.bmin);
+ rcVcopy(mesh.bmax, cset.bmax);
+ mesh.cs = cset.cs;
+ mesh.ch = cset.ch;
+
+ int maxVertices = 0;
+ int maxTris = 0;
+ int maxVertsPerCont = 0;
+ for (int i = 0; i < cset.nconts; ++i)
+ {
+ // Skip null contours.
+ if (cset.conts[i].nverts < 3) continue;
+ maxVertices += cset.conts[i].nverts;
+ maxTris += cset.conts[i].nverts - 2;
+ maxVertsPerCont = rcMax(maxVertsPerCont, cset.conts[i].nverts);
+ }
+
+ if (maxVertices >= 0xfffe)
+ {
+ ctx->log(RC_LOG_ERROR, "rcBuildPolyMesh: Too many vertices %d.", maxVertices);
+ return false;
+ }
+
+ rcScopedDelete<unsigned char> vflags = (unsigned char*)rcAlloc(sizeof(unsigned char)*maxVertices, RC_ALLOC_TEMP);
+ if (!vflags)
+ {
+ ctx->log(RC_LOG_ERROR, "rcBuildPolyMesh: Out of memory 'mesh.verts' (%d).", maxVertices);
+ return false;
+ }
+ memset(vflags, 0, maxVertices);
+
+ mesh.verts = (unsigned short*)rcAlloc(sizeof(unsigned short)*maxVertices*3, RC_ALLOC_PERM);
+ if (!mesh.verts)
+ {
+ ctx->log(RC_LOG_ERROR, "rcBuildPolyMesh: Out of memory 'mesh.verts' (%d).", maxVertices);
+ return false;
+ }
+ mesh.polys = (unsigned short*)rcAlloc(sizeof(unsigned short)*maxTris*nvp*2*2, RC_ALLOC_PERM);
+ if (!mesh.polys)
+ {
+ ctx->log(RC_LOG_ERROR, "rcBuildPolyMesh: Out of memory 'mesh.polys' (%d).", maxTris*nvp*2);
+ return false;
+ }
+ mesh.regs = (unsigned short*)rcAlloc(sizeof(unsigned short)*maxTris, RC_ALLOC_PERM);
+ if (!mesh.regs)
+ {
+ ctx->log(RC_LOG_ERROR, "rcBuildPolyMesh: Out of memory 'mesh.regs' (%d).", maxTris);
+ return false;
+ }
+ mesh.areas = (unsigned char*)rcAlloc(sizeof(unsigned char)*maxTris, RC_ALLOC_PERM);
+ if (!mesh.areas)
+ {
+ ctx->log(RC_LOG_ERROR, "rcBuildPolyMesh: Out of memory 'mesh.areas' (%d).", maxTris);
+ return false;
+ }
+
+ mesh.nverts = 0;
+ mesh.npolys = 0;
+ mesh.nvp = nvp;
+ mesh.maxpolys = maxTris;
+
+ memset(mesh.verts, 0, sizeof(unsigned short)*maxVertices*3);
+ memset(mesh.polys, 0xff, sizeof(unsigned short)*maxTris*nvp*2);
+ memset(mesh.regs, 0, sizeof(unsigned short)*maxTris);
+ memset(mesh.areas, 0, sizeof(unsigned char)*maxTris);
+
+ rcScopedDelete<int> nextVert = (int*)rcAlloc(sizeof(int)*maxVertices, RC_ALLOC_TEMP);
+ if (!nextVert)
+ {
+ ctx->log(RC_LOG_ERROR, "rcBuildPolyMesh: Out of memory 'nextVert' (%d).", maxVertices);
+ return false;
+ }
+ memset(nextVert, 0, sizeof(int)*maxVertices);
+
+ rcScopedDelete<int> firstVert = (int*)rcAlloc(sizeof(int)*VERTEX_BUCKET_COUNT, RC_ALLOC_TEMP);
+ if (!firstVert)
+ {
+ ctx->log(RC_LOG_ERROR, "rcBuildPolyMesh: Out of memory 'firstVert' (%d).", VERTEX_BUCKET_COUNT);
+ return false;
+ }
+ for (int i = 0; i < VERTEX_BUCKET_COUNT; ++i)
+ firstVert[i] = -1;
+
+ rcScopedDelete<int> indices = (int*)rcAlloc(sizeof(int)*maxVertsPerCont, RC_ALLOC_TEMP);
+ if (!indices)
+ {
+ ctx->log(RC_LOG_ERROR, "rcBuildPolyMesh: Out of memory 'indices' (%d).", maxVertsPerCont);
+ return false;
+ }
+ rcScopedDelete<int> tris = (int*)rcAlloc(sizeof(int)*maxVertsPerCont*3, RC_ALLOC_TEMP);
+ if (!tris)
+ {
+ ctx->log(RC_LOG_ERROR, "rcBuildPolyMesh: Out of memory 'tris' (%d).", maxVertsPerCont*3);
+ return false;
+ }
+ rcScopedDelete<unsigned short> polys = (unsigned short*)rcAlloc(sizeof(unsigned short)*(maxVertsPerCont+1)*nvp, RC_ALLOC_TEMP);
+ if (!polys)
+ {
+ ctx->log(RC_LOG_ERROR, "rcBuildPolyMesh: Out of memory 'polys' (%d).", maxVertsPerCont*nvp);
+ return false;
+ }
+ unsigned short* tmpPoly = &polys[maxVertsPerCont*nvp];
+
+ for (int i = 0; i < cset.nconts; ++i)
+ {
+ rcContour& cont = cset.conts[i];
+
+ // Skip null contours.
+ if (cont.nverts < 3)
+ continue;
+
+ // Triangulate contour
+ for (int j = 0; j < cont.nverts; ++j)
+ indices[j] = j;
+
+ int ntris = triangulate(cont.nverts, cont.verts, &indices[0], &tris[0]);
+ if (ntris <= 0)
+ {
+ // Bad triangulation, should not happen.
+/* printf("\tconst float bmin[3] = {%ff,%ff,%ff};\n", cset.bmin[0], cset.bmin[1], cset.bmin[2]);
+ printf("\tconst float cs = %ff;\n", cset.cs);
+ printf("\tconst float ch = %ff;\n", cset.ch);
+ printf("\tconst int verts[] = {\n");
+ for (int k = 0; k < cont.nverts; ++k)
+ {
+ const int* v = &cont.verts[k*4];
+ printf("\t\t%d,%d,%d,%d,\n", v[0], v[1], v[2], v[3]);
+ }
+ printf("\t};\n\tconst int nverts = sizeof(verts)/(sizeof(int)*4);\n");*/
+ ctx->log(RC_LOG_WARNING, "rcBuildPolyMesh: Bad triangulation Contour %d.", i);
+ ntris = -ntris;
+ }
+
+ // Add and merge vertices.
+ for (int j = 0; j < cont.nverts; ++j)
+ {
+ const int* v = &cont.verts[j*4];
+ indices[j] = addVertex((unsigned short)v[0], (unsigned short)v[1], (unsigned short)v[2],
+ mesh.verts, firstVert, nextVert, mesh.nverts);
+ if (v[3] & RC_BORDER_VERTEX)
+ {
+ // This vertex should be removed.
+ vflags[indices[j]] = 1;
+ }
+ }
+
+ // Build initial polygons.
+ int npolys = 0;
+ memset(polys, 0xff, maxVertsPerCont*nvp*sizeof(unsigned short));
+ for (int j = 0; j < ntris; ++j)
+ {
+ int* t = &tris[j*3];
+ if (t[0] != t[1] && t[0] != t[2] && t[1] != t[2])
+ {
+ polys[npolys*nvp+0] = (unsigned short)indices[t[0]];
+ polys[npolys*nvp+1] = (unsigned short)indices[t[1]];
+ polys[npolys*nvp+2] = (unsigned short)indices[t[2]];
+ npolys++;
+ }
+ }
+ if (!npolys)
+ continue;
+
+ // Merge polygons.
+ if (nvp > 3)
+ {
+ for(;;)
+ {
+ // Find best polygons to merge.
+ int bestMergeVal = 0;
+ int bestPa = 0, bestPb = 0, bestEa = 0, bestEb = 0;
+
+ for (int j = 0; j < npolys-1; ++j)
+ {
+ unsigned short* pj = &polys[j*nvp];
+ for (int k = j+1; k < npolys; ++k)
+ {
+ unsigned short* pk = &polys[k*nvp];
+ int ea, eb;
+ int v = getPolyMergeValue(pj, pk, mesh.verts, ea, eb, nvp);
+ if (v > bestMergeVal)
+ {
+ bestMergeVal = v;
+ bestPa = j;
+ bestPb = k;
+ bestEa = ea;
+ bestEb = eb;
+ }
+ }
+ }
+
+ if (bestMergeVal > 0)
+ {
+ // Found best, merge.
+ unsigned short* pa = &polys[bestPa*nvp];
+ unsigned short* pb = &polys[bestPb*nvp];
+ mergePolys(pa, pb, bestEa, bestEb, tmpPoly, nvp);
+ memcpy(pb, &polys[(npolys-1)*nvp], sizeof(unsigned short)*nvp);
+ npolys--;
+ }
+ else
+ {
+ // Could not merge any polygons, stop.
+ break;
+ }
+ }
+ }
+
+ // Store polygons.
+ for (int j = 0; j < npolys; ++j)
+ {
+ unsigned short* p = &mesh.polys[mesh.npolys*nvp*2];
+ unsigned short* q = &polys[j*nvp];
+ for (int k = 0; k < nvp; ++k)
+ p[k] = q[k];
+ mesh.regs[mesh.npolys] = cont.reg;
+ mesh.areas[mesh.npolys] = cont.area;
+ mesh.npolys++;
+ if (mesh.npolys > maxTris)
+ {
+ ctx->log(RC_LOG_ERROR, "rcBuildPolyMesh: Too many polygons %d (max:%d).", mesh.npolys, maxTris);
+ return false;
+ }
+ }
+ }
+
+
+ // Remove edge vertices.
+ for (int i = 0; i < mesh.nverts; ++i)
+ {
+ if (vflags[i])
+ {
+ if (!canRemoveVertex(ctx, mesh, (unsigned short)i))
+ continue;
+ if (!removeVertex(ctx, mesh, (unsigned short)i, maxTris))
+ {
+ // Failed to remove vertex
+ ctx->log(RC_LOG_ERROR, "rcBuildPolyMesh: Failed to remove edge vertex %d.", i);
+ return false;
+ }
+ // Remove vertex
+ // Note: mesh.nverts is already decremented inside removeVertex()!
+ for (int j = i; j < mesh.nverts; ++j)
+ vflags[j] = vflags[j+1];
+ --i;
+ }
+ }
+
+ // Calculate adjacency.
+ if (!buildMeshAdjacency(mesh.polys, mesh.npolys, mesh.nverts, nvp))
+ {
+ ctx->log(RC_LOG_ERROR, "rcBuildPolyMesh: Adjacency failed.");
+ return false;
+ }
+
+ // Just allocate the mesh flags array. The user is resposible to fill it.
+ mesh.flags = (unsigned short*)rcAlloc(sizeof(unsigned short)*mesh.npolys, RC_ALLOC_PERM);
+ if (!mesh.flags)
+ {
+ ctx->log(RC_LOG_ERROR, "rcBuildPolyMesh: Out of memory 'mesh.flags' (%d).", mesh.npolys);
+ return false;
+ }
+ memset(mesh.flags, 0, sizeof(unsigned short) * mesh.npolys);
+
+ if (mesh.nverts > 0xffff)
+ {
+ ctx->log(RC_LOG_ERROR, "rcMergePolyMeshes: The resulting mesh has too many vertices %d (max %d). Data can be corrupted.", mesh.nverts, 0xffff);
+ }
+ if (mesh.npolys > 0xffff)
+ {
+ ctx->log(RC_LOG_ERROR, "rcMergePolyMeshes: The resulting mesh has too many polygons %d (max %d). Data can be corrupted.", mesh.npolys, 0xffff);
+ }
+
+ ctx->stopTimer(RC_TIMER_BUILD_POLYMESH);
+
+ return true;
+}
+
+bool rcMergePolyMeshes(rcContext* ctx, rcPolyMesh** meshes, const int nmeshes, rcPolyMesh& mesh)
+{
+ rcAssert(ctx);
+
+ if (!nmeshes || !meshes)
+ return true;
+
+ ctx->startTimer(RC_TIMER_MERGE_POLYMESH);
+
+ mesh.nvp = meshes[0]->nvp;
+ mesh.cs = meshes[0]->cs;
+ mesh.ch = meshes[0]->ch;
+ rcVcopy(mesh.bmin, meshes[0]->bmin);
+ rcVcopy(mesh.bmax, meshes[0]->bmax);
+
+ int maxVerts = 0;
+ int maxPolys = 0;
+ int maxVertsPerMesh = 0;
+ for (int i = 0; i < nmeshes; ++i)
+ {
+ rcVmin(mesh.bmin, meshes[i]->bmin);
+ rcVmax(mesh.bmax, meshes[i]->bmax);
+ maxVertsPerMesh = rcMax(maxVertsPerMesh, meshes[i]->nverts);
+ maxVerts += meshes[i]->nverts;
+ maxPolys += meshes[i]->npolys;
+ }
+
+ mesh.nverts = 0;
+ mesh.verts = (unsigned short*)rcAlloc(sizeof(unsigned short)*maxVerts*3, RC_ALLOC_PERM);
+ if (!mesh.verts)
+ {
+ ctx->log(RC_LOG_ERROR, "rcMergePolyMeshes: Out of memory 'mesh.verts' (%d).", maxVerts*3);
+ return false;
+ }
+
+ mesh.npolys = 0;
+ mesh.polys = (unsigned short*)rcAlloc(sizeof(unsigned short)*maxPolys*2*mesh.nvp, RC_ALLOC_PERM);
+ if (!mesh.polys)
+ {
+ ctx->log(RC_LOG_ERROR, "rcMergePolyMeshes: Out of memory 'mesh.polys' (%d).", maxPolys*2*mesh.nvp);
+ return false;
+ }
+ memset(mesh.polys, 0xff, sizeof(unsigned short)*maxPolys*2*mesh.nvp);
+
+ mesh.regs = (unsigned short*)rcAlloc(sizeof(unsigned short)*maxPolys, RC_ALLOC_PERM);
+ if (!mesh.regs)
+ {
+ ctx->log(RC_LOG_ERROR, "rcMergePolyMeshes: Out of memory 'mesh.regs' (%d).", maxPolys);
+ return false;
+ }
+ memset(mesh.regs, 0, sizeof(unsigned short)*maxPolys);
+
+ mesh.areas = (unsigned char*)rcAlloc(sizeof(unsigned char)*maxPolys, RC_ALLOC_PERM);
+ if (!mesh.areas)
+ {
+ ctx->log(RC_LOG_ERROR, "rcMergePolyMeshes: Out of memory 'mesh.areas' (%d).", maxPolys);
+ return false;
+ }
+ memset(mesh.areas, 0, sizeof(unsigned char)*maxPolys);
+
+ mesh.flags = (unsigned short*)rcAlloc(sizeof(unsigned short)*maxPolys, RC_ALLOC_PERM);
+ if (!mesh.flags)
+ {
+ ctx->log(RC_LOG_ERROR, "rcMergePolyMeshes: Out of memory 'mesh.flags' (%d).", maxPolys);
+ return false;
+ }
+ memset(mesh.flags, 0, sizeof(unsigned short)*maxPolys);
+
+ rcScopedDelete<int> nextVert = (int*)rcAlloc(sizeof(int)*maxVerts, RC_ALLOC_TEMP);
+ if (!nextVert)
+ {
+ ctx->log(RC_LOG_ERROR, "rcMergePolyMeshes: Out of memory 'nextVert' (%d).", maxVerts);
+ return false;
+ }
+ memset(nextVert, 0, sizeof(int)*maxVerts);
+
+ rcScopedDelete<int> firstVert = (int*)rcAlloc(sizeof(int)*VERTEX_BUCKET_COUNT, RC_ALLOC_TEMP);
+ if (!firstVert)
+ {
+ ctx->log(RC_LOG_ERROR, "rcMergePolyMeshes: Out of memory 'firstVert' (%d).", VERTEX_BUCKET_COUNT);
+ return false;
+ }
+ for (int i = 0; i < VERTEX_BUCKET_COUNT; ++i)
+ firstVert[i] = -1;
+
+ rcScopedDelete<unsigned short> vremap = (unsigned short*)rcAlloc(sizeof(unsigned short)*maxVertsPerMesh, RC_ALLOC_PERM);
+ if (!vremap)
+ {
+ ctx->log(RC_LOG_ERROR, "rcMergePolyMeshes: Out of memory 'vremap' (%d).", maxVertsPerMesh);
+ return false;
+ }
+ memset(nextVert, 0, sizeof(int)*maxVerts);
+
+ for (int i = 0; i < nmeshes; ++i)
+ {
+ const rcPolyMesh* pmesh = meshes[i];
+
+ const unsigned short ox = (unsigned short)floorf((pmesh->bmin[0]-mesh.bmin[0])/mesh.cs+0.5f);
+ const unsigned short oz = (unsigned short)floorf((pmesh->bmin[2]-mesh.bmin[2])/mesh.cs+0.5f);
+
+ for (int j = 0; j < pmesh->nverts; ++j)
+ {
+ unsigned short* v = &pmesh->verts[j*3];
+ vremap[j] = addVertex(v[0]+ox, v[1], v[2]+oz,
+ mesh.verts, firstVert, nextVert, mesh.nverts);
+ }
+
+ for (int j = 0; j < pmesh->npolys; ++j)
+ {
+ unsigned short* tgt = &mesh.polys[mesh.npolys*2*mesh.nvp];
+ unsigned short* src = &pmesh->polys[j*2*mesh.nvp];
+ mesh.regs[mesh.npolys] = pmesh->regs[j];
+ mesh.areas[mesh.npolys] = pmesh->areas[j];
+ mesh.flags[mesh.npolys] = pmesh->flags[j];
+ mesh.npolys++;
+ for (int k = 0; k < mesh.nvp; ++k)
+ {
+ if (src[k] == RC_MESH_NULL_IDX) break;
+ tgt[k] = vremap[src[k]];
+ }
+ }
+ }
+
+ // Calculate adjacency.
+ if (!buildMeshAdjacency(mesh.polys, mesh.npolys, mesh.nverts, mesh.nvp))
+ {
+ ctx->log(RC_LOG_ERROR, "rcMergePolyMeshes: Adjacency failed.");
+ return false;
+ }
+
+ if (mesh.nverts > 0xffff)
+ {
+ ctx->log(RC_LOG_ERROR, "rcMergePolyMeshes: The resulting mesh has too many vertices %d (max %d). Data can be corrupted.", mesh.nverts, 0xffff);
+ }
+ if (mesh.npolys > 0xffff)
+ {
+ ctx->log(RC_LOG_ERROR, "rcMergePolyMeshes: The resulting mesh has too many polygons %d (max %d). Data can be corrupted.", mesh.npolys, 0xffff);
+ }
+
+ ctx->stopTimer(RC_TIMER_MERGE_POLYMESH);
+
+ return true;
+}
diff --git a/dep/recastnavigation/Recast/RecastMeshDetail.cpp b/dep/recastnavigation/Recast/RecastMeshDetail.cpp
new file mode 100644
index 00000000000..ffb4b58ee9c
--- /dev/null
+++ b/dep/recastnavigation/Recast/RecastMeshDetail.cpp
@@ -0,0 +1,1237 @@
+//
+// Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
+//
+// This software is provided 'as-is', without any express or implied
+// warranty. In no event will the authors be held liable for any damages
+// arising from the use of this software.
+// Permission is granted to anyone to use this software for any purpose,
+// including commercial applications, and to alter it and redistribute it
+// freely, subject to the following restrictions:
+// 1. The origin of this software must not be misrepresented; you must not
+// claim that you wrote the original software. If you use this software
+// in a product, an acknowledgment in the product documentation would be
+// appreciated but is not required.
+// 2. Altered source versions must be plainly marked as such, and must not be
+// misrepresented as being the original software.
+// 3. This notice may not be removed or altered from any source distribution.
+//
+
+#include <float.h>
+#define _USE_MATH_DEFINES
+#include <math.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include "Recast.h"
+#include "RecastAlloc.h"
+#include "RecastAssert.h"
+
+
+static const unsigned RC_UNSET_HEIGHT = 0xffff;
+
+struct rcHeightPatch
+{
+ inline rcHeightPatch() : data(0), xmin(0), ymin(0), width(0), height(0) {}
+ inline ~rcHeightPatch() { rcFree(data); }
+ unsigned short* data;
+ int xmin, ymin, width, height;
+};
+
+
+inline float vdot2(const float* a, const float* b)
+{
+ return a[0]*b[0] + a[2]*b[2];
+}
+
+inline float vdistSq2(const float* p, const float* q)
+{
+ const float dx = q[0] - p[0];
+ const float dy = q[2] - p[2];
+ return dx*dx + dy*dy;
+}
+
+inline float vdist2(const float* p, const float* q)
+{
+ return sqrtf(vdistSq2(p,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];
+ const float v2 = p3[2] - p1[2];
+ return u1 * v2 - v1 * u2;
+}
+
+static bool circumCircle(const float* p1, const float* p2, const float* p3,
+ float* c, float& r)
+{
+ static const float EPS = 1e-6f;
+
+ const float cp = vcross2(p1, p2, p3);
+ 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);
+ return true;
+ }
+
+ c[0] = p1[0];
+ c[2] = p1[2];
+ r = 0;
+ return false;
+}
+
+static float distPtTri(const float* p, const float* a, const float* b, const float* c)
+{
+ float v0[3], v1[3], v2[3];
+ 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);
+ const float dot11 = vdot2(v1, v1);
+ const float dot12 = vdot2(v1, v2);
+
+ // Compute barycentric coordinates
+ const float invDenom = 1.0f / (dot00 * dot11 - dot01 * dot01);
+ const float u = (dot11 * dot02 - dot01 * dot12) * invDenom;
+ float v = (dot00 * dot12 - dot01 * dot02) * invDenom;
+
+ // If point lies inside the triangle, return interpolated y-coord.
+ static const float EPS = 1e-4f;
+ if (u >= -EPS && v >= -EPS && (u+v) <= 1+EPS)
+ {
+ const float y = a[1] + v0[1]*u + v1[1]*v;
+ return fabsf(y-p[1]);
+ }
+ return FLT_MAX;
+}
+
+static float distancePtSeg(const float* pt, const float* p, const float* q)
+{
+ float pqx = q[0] - p[0];
+ float pqy = q[1] - p[1];
+ float pqz = q[2] - p[2];
+ float dx = pt[0] - p[0];
+ float dy = pt[1] - p[1];
+ float dz = pt[2] - p[2];
+ 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 = p[0] + t*pqx - pt[0];
+ dy = p[1] + t*pqy - pt[1];
+ dz = p[2] + t*pqz - pt[2];
+
+ return dx*dx + dy*dy + dz*dz;
+}
+
+static float distancePtSeg2d(const float* pt, const float* p, const float* q)
+{
+ float pqx = q[0] - p[0];
+ float pqz = q[2] - p[2];
+ float dx = pt[0] - p[0];
+ float dz = pt[2] - p[2];
+ float d = pqx*pqx + pqz*pqz;
+ float t = pqx*dx + pqz*dz;
+ if (d > 0)
+ t /= d;
+ if (t < 0)
+ t = 0;
+ else if (t > 1)
+ t = 1;
+
+ dx = p[0] + t*pqx - pt[0];
+ dz = p[2] + t*pqz - pt[2];
+
+ return dx*dx + dz*dz;
+}
+
+static float distToTriMesh(const float* p, const float* verts, const int /*nverts*/, const int* tris, const int ntris)
+{
+ float dmin = FLT_MAX;
+ for (int i = 0; i < ntris; ++i)
+ {
+ const float* va = &verts[tris[i*4+0]*3];
+ const float* vb = &verts[tris[i*4+1]*3];
+ const float* vc = &verts[tris[i*4+2]*3];
+ float d = distPtTri(p, va,vb,vc);
+ if (d < dmin)
+ dmin = d;
+ }
+ if (dmin == FLT_MAX) return -1;
+ return dmin;
+}
+
+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++)
+ {
+ const float* vi = &verts[i*3];
+ const float* vj = &verts[j*3];
+ if (((vi[2] > p[2]) != (vj[2] > p[2])) &&
+ (p[0] < (vj[0]-vi[0]) * (p[2]-vi[2]) / (vj[2]-vi[2]) + vi[0]) )
+ c = !c;
+ dmin = rcMin(dmin, distancePtSeg2d(p, vj, vi));
+ }
+ return c ? -dmin : dmin;
+}
+
+
+static unsigned short getHeight(const float fx, const float fy, const float fz,
+ const float /*cs*/, const float ics, const float ch,
+ const rcHeightPatch& hp)
+{
+ int ix = (int)floorf(fx*ics + 0.01f);
+ int iz = (int)floorf(fz*ics + 0.01f);
+ ix = rcClamp(ix-hp.xmin, 0, hp.width);
+ iz = rcClamp(iz-hp.ymin, 0, hp.height);
+ unsigned short h = hp.data[ix+iz*hp.width];
+ if (h == RC_UNSET_HEIGHT)
+ {
+ // Special case when data might be bad.
+ // Find nearest neighbour pixel which has valid height.
+ const int off[8*2] = { -1,0, -1,-1, 0,-1, 1,-1, 1,0, 1,1, 0,1, -1,1};
+ float dmin = FLT_MAX;
+ for (int i = 0; i < 8; ++i)
+ {
+ const int nx = ix+off[i*2+0];
+ const int nz = iz+off[i*2+1];
+ 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;
+}
+
+
+enum EdgeValues
+{
+ UNDEF = -1,
+ HULL = -2,
+};
+
+static int findEdge(const int* edges, int nedges, int s, int t)
+{
+ for (int i = 0; i < nedges; i++)
+ {
+ const int* e = &edges[i*4];
+ if ((e[0] == s && e[1] == t) || (e[0] == t && e[1] == s))
+ return i;
+ }
+ return UNDEF;
+}
+
+static int addEdge(rcContext* ctx, int* edges, int& nedges, const int maxEdges, int s, int t, int l, int r)
+{
+ if (nedges >= maxEdges)
+ {
+ ctx->log(RC_LOG_ERROR, "addEdge: Too many edges (%d/%d).", nedges, maxEdges);
+ return UNDEF;
+ }
+
+ // Add edge if not already in the triangulation.
+ int e = findEdge(edges, nedges, s, t);
+ if (e == UNDEF)
+ {
+ int* e = &edges[nedges*4];
+ e[0] = s;
+ e[1] = t;
+ e[2] = l;
+ e[3] = r;
+ return nedges++;
+ }
+ else
+ {
+ return UNDEF;
+ }
+}
+
+static void updateLeftFace(int* e, int s, int t, int f)
+{
+ if (e[0] == s && e[1] == t && e[2] == UNDEF)
+ 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)
+{
+ const float a1 = vcross2(a, b, d);
+ const float a2 = vcross2(a, b, c);
+ if (a1*a2 < 0.0f)
+ {
+ float a3 = vcross2(c, d, a);
+ float a4 = a3 + a2 - a1;
+ if (a3 * a4 < 0.0f)
+ return 1;
+ }
+ return 0;
+}
+
+static bool overlapEdges(const float* pts, const int* edges, int nedges, int s1, int t1)
+{
+ for (int i = 0; i < nedges; ++i)
+ {
+ const int s0 = edges[i*4+0];
+ const int t0 = edges[i*4+1];
+ // Same or connected edges do not overlap.
+ if (s0 == s1 || s0 == t1 || t0 == s1 || t0 == t1)
+ continue;
+ if (overlapSegSeg2d(&pts[s0*3],&pts[t0*3], &pts[s1*3],&pts[t1*3]))
+ return true;
+ }
+ return false;
+}
+
+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.
+ int s,t;
+ if (edge[2] == UNDEF)
+ {
+ s = edge[0];
+ t = edge[1];
+ }
+ else if (edge[3] == UNDEF)
+ {
+ s = edge[1];
+ t = edge[0];
+ }
+ else
+ {
+ // Edge already completed.
+ return;
+ }
+
+ // Find best point on left of edge.
+ int pt = npts;
+ float c[3] = {0,0,0};
+ float r = -1;
+ for (int u = 0; u < npts; ++u)
+ {
+ if (u == s || u == t) continue;
+ if (vcross2(&pts[s*3], &pts[t*3], &pts[u*3]) > EPS)
+ {
+ if (r < 0)
+ {
+ // The circle is not updated yet, do it now.
+ pt = u;
+ circumCircle(&pts[s*3], &pts[t*3], &pts[u*3], c, r);
+ continue;
+ }
+ const float d = vdist2(c, &pts[u*3]);
+ const float tol = 0.001f;
+ if (d > r*(1+tol))
+ {
+ // Outside current circumcircle, skip.
+ continue;
+ }
+ else if (d < r*(1-tol))
+ {
+ // Inside safe circumcircle, update circle.
+ pt = u;
+ circumCircle(&pts[s*3], &pts[t*3], &pts[u*3], c, r);
+ }
+ else
+ {
+ // Inside epsilon circum circle, do extra tests to make sure the edge is valid.
+ // s-u and t-u cannot overlap with s-pt nor t-pt if they exists.
+ if (overlapEdges(pts, edges, nedges, s,u))
+ continue;
+ if (overlapEdges(pts, edges, nedges, t,u))
+ continue;
+ // Edge is valid.
+ pt = u;
+ circumCircle(&pts[s*3], &pts[t*3], &pts[u*3], c, r);
+ }
+ }
+ }
+
+ // Add new triangle or update edge info if s-t is on hull.
+ if (pt < npts)
+ {
+ // Update face information of edge being completed.
+ updateLeftFace(&edges[e*4], s, t, nfaces);
+
+ // 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.
+ e = findEdge(edges, nedges, t, pt);
+ if (e == UNDEF)
+ addEdge(ctx, edges, nedges, maxEdges, t, pt, nfaces, UNDEF);
+ else
+ updateLeftFace(&edges[e*4], t, pt, nfaces);
+
+ nfaces++;
+ }
+ else
+ {
+ updateLeftFace(&edges[e*4], s, t, HULL);
+ }
+}
+
+static void delaunayHull(rcContext* ctx, const int npts, const float* pts,
+ const int nhull, const int* hull,
+ rcIntArray& tris, rcIntArray& edges)
+{
+ int nfaces = 0;
+ int nedges = 0;
+ const int maxEdges = npts*10;
+ edges.resize(maxEdges*4);
+
+ for (int i = 0, j = nhull-1; i < nhull; j=i++)
+ addEdge(ctx, &edges[0], nedges, maxEdges, hull[j],hull[i], HULL, UNDEF);
+
+ int currentEdge = 0;
+ while (currentEdge < nedges)
+ {
+ if (edges[currentEdge*4+2] == UNDEF)
+ completeFacet(ctx, pts, npts, &edges[0], nedges, maxEdges, nfaces, currentEdge);
+ if (edges[currentEdge*4+3] == UNDEF)
+ 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)
+ tris[i] = -1;
+
+ for (int i = 0; i < nedges; ++i)
+ {
+ const int* e = &edges[i*4];
+ if (e[3] >= 0)
+ {
+ // Left face
+ int* t = &tris[e[3]*4];
+ if (t[0] == -1)
+ {
+ t[0] = e[0];
+ t[1] = e[1];
+ }
+ else if (t[0] == e[1])
+ t[2] = e[0];
+ else if (t[1] == e[0])
+ t[2] = e[1];
+ }
+ if (e[2] >= 0)
+ {
+ // Right
+ int* t = &tris[e[2]*4];
+ if (t[0] == -1)
+ {
+ t[0] = e[1];
+ t[1] = e[0];
+ }
+ else if (t[0] == e[0])
+ t[2] = e[1];
+ else if (t[1] == e[1])
+ t[2] = e[0];
+ }
+ }
+
+ for (int i = 0; i < tris.size()/4; ++i)
+ {
+ int* t = &tris[i*4];
+ if (t[0] == -1 || t[1] == -1 || t[2] == -1)
+ {
+ ctx->log(RC_LOG_WARNING, "delaunayHull: Removing dangling face %d [%d,%d,%d].", i, t[0],t[1],t[2]);
+ t[0] = tris[tris.size()-4];
+ t[1] = tris[tris.size()-3];
+ t[2] = tris[tris.size()-2];
+ t[3] = tris[tris.size()-1];
+ tris.resize(tris.size()-4);
+ --i;
+ }
+ }
+}
+
+
+inline float getJitterX(const int i)
+{
+ return (((i * 0x8da6b343) & 0xffff) / 65535.0f * 2.0f) - 1.0f;
+}
+
+inline float getJitterY(const int i)
+{
+ return (((i * 0xd8163841) & 0xffff) / 65535.0f * 2.0f) - 1.0f;
+}
+
+static bool buildPolyDetail(rcContext* ctx, const float* in, const int nin,
+ const float sampleDist, const float sampleMaxError,
+ const rcCompactHeightfield& chf, const rcHeightPatch& hp,
+ float* verts, int& nverts, rcIntArray& tris,
+ rcIntArray& edges, rcIntArray& samples)
+{
+ static const int MAX_VERTS = 127;
+ static const int MAX_TRIS = 255; // Max tris for delaunay is 2n-2-k (n=num verts, k=num hull verts).
+ static const int MAX_VERTS_PER_EDGE = 32;
+ 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;
+
+ const float cs = chf.cs;
+ const float ics = 1.0f/cs;
+
+ // Tessellate outlines.
+ // This is done in separate pass in order to ensure
+ // seamless height values across the ply boundaries.
+ if (sampleDist > 0)
+ {
+ for (int i = 0, j = nin-1; i < nin; j=i++)
+ {
+ const float* vj = &in[j*3];
+ const float* vi = &in[i*3];
+ bool swapped = false;
+ // Make sure the segments are always handled in same order
+ // using lexological sort or else there will be seams.
+ if (fabsf(vj[0]-vi[0]) < 1e-6f)
+ {
+ if (vj[2] > vi[2])
+ {
+ rcSwap(vj,vi);
+ swapped = true;
+ }
+ }
+ else
+ {
+ if (vj[0] > vi[0])
+ {
+ rcSwap(vj,vi);
+ swapped = true;
+ }
+ }
+ // Create samples along the edge.
+ float dx = vi[0] - vj[0];
+ float dy = vi[1] - vj[1];
+ float dz = vi[2] - vj[2];
+ float d = sqrtf(dx*dx + dz*dz);
+ int nn = 1 + (int)floorf(d/sampleDist);
+ if (nn >= MAX_VERTS_PER_EDGE) nn = MAX_VERTS_PER_EDGE-1;
+ if (nverts+nn >= MAX_VERTS)
+ nn = MAX_VERTS-1-nverts;
+
+ for (int k = 0; k <= nn; ++k)
+ {
+ float u = (float)k/(float)nn;
+ float* pos = &edge[k*3];
+ pos[0] = vj[0] + dx*u;
+ pos[1] = vj[1] + dy*u;
+ pos[2] = vj[2] + dz*u;
+ pos[1] = getHeight(pos[0],pos[1],pos[2], cs, ics, chf.ch, hp)*chf.ch;
+ }
+ // Simplify samples.
+ int idx[MAX_VERTS_PER_EDGE] = {0,nn};
+ int nidx = 2;
+ for (int k = 0; k < nidx-1; )
+ {
+ const int a = idx[k];
+ const int b = idx[k+1];
+ const float* va = &edge[a*3];
+ const float* vb = &edge[b*3];
+ // Find maximum deviation along the segment.
+ float maxd = 0;
+ int maxi = -1;
+ for (int m = a+1; m < b; ++m)
+ {
+ float d = distancePtSeg(&edge[m*3],va,vb);
+ if (d > maxd)
+ {
+ maxd = d;
+ maxi = m;
+ }
+ }
+ // If the max deviation is larger than accepted error,
+ // add new point, else continue to next segment.
+ if (maxi != -1 && maxd > rcSqr(sampleMaxError))
+ {
+ for (int m = nidx; m > k; --m)
+ idx[m] = idx[m-1];
+ idx[k+1] = maxi;
+ nidx++;
+ }
+ else
+ {
+ ++k;
+ }
+ }
+
+ hull[nhull++] = j;
+ // Add new vertices.
+ if (swapped)
+ {
+ for (int k = nidx-2; k > 0; --k)
+ {
+ rcVcopy(&verts[nverts*3], &edge[idx[k]*3]);
+ hull[nhull++] = nverts;
+ nverts++;
+ }
+ }
+ else
+ {
+ for (int k = 1; k < nidx-1; ++k)
+ {
+ rcVcopy(&verts[nverts*3], &edge[idx[k]*3]);
+ hull[nhull++] = nverts;
+ nverts++;
+ }
+ }
+ }
+ }
+
+
+ // Tessellate the base mesh.
+ edges.resize(0);
+ tris.resize(0);
+
+ delaunayHull(ctx, nverts, verts, nhull, hull, tris, edges);
+
+ 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);
+ }
+ return true;
+ }
+
+ if (sampleDist > 0)
+ {
+ // Create sample locations in a grid.
+ float bmin[3], bmax[3];
+ rcVcopy(bmin, in);
+ rcVcopy(bmax, in);
+ for (int i = 1; i < nin; ++i)
+ {
+ rcVmin(bmin, &in[i*3]);
+ rcVmax(bmax, &in[i*3]);
+ }
+ int x0 = (int)floorf(bmin[0]/sampleDist);
+ int x1 = (int)ceilf(bmax[0]/sampleDist);
+ int z0 = (int)floorf(bmin[2]/sampleDist);
+ int z1 = (int)ceilf(bmax[2]/sampleDist);
+ samples.resize(0);
+ for (int z = z0; z < z1; ++z)
+ {
+ for (int x = x0; x < x1; ++x)
+ {
+ float pt[3];
+ pt[0] = x*sampleDist;
+ pt[1] = (bmax[1]+bmin[1])*0.5f;
+ pt[2] = z*sampleDist;
+ // Make sure the samples are not too close to the edges.
+ if (distToPoly(nin,in,pt) > -sampleDist/2) continue;
+ samples.push(x);
+ samples.push(getHeight(pt[0], pt[1], pt[2], cs, ics, chf.ch, hp));
+ samples.push(z);
+ 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.
+ const int nsamples = samples.size()/4;
+ for (int iter = 0; iter < nsamples; ++iter)
+ {
+ if (nverts >= MAX_VERTS)
+ break;
+
+ // Find sample with most error.
+ float bestpt[3] = {0,0,0};
+ float bestd = 0;
+ int besti = -1;
+ for (int i = 0; i < nsamples; ++i)
+ {
+ const int* s = &samples[i*4];
+ if (s[3]) continue; // skip added.
+ float pt[3];
+ // The sample location is jittered to get rid of some bad triangulations
+ // which are cause by symmetrical data from the grid structure.
+ pt[0] = s[0]*sampleDist + getJitterX(i)*cs*0.1f;
+ pt[1] = s[1]*chf.ch;
+ pt[2] = s[2]*sampleDist + getJitterY(i)*cs*0.1f;
+ float d = distToTriMesh(pt, verts, nverts, &tris[0], tris.size()/4);
+ if (d < 0) continue; // did not hit the mesh.
+ if (d > bestd)
+ {
+ bestd = d;
+ besti = i;
+ rcVcopy(bestpt,pt);
+ }
+ }
+ // If the max error is within accepted threshold, stop tesselating.
+ if (bestd <= sampleMaxError || besti == -1)
+ break;
+ // Mark sample as added.
+ samples[besti*4+3] = 1;
+ // Add the new sample point.
+ rcVcopy(&verts[nverts*3],bestpt);
+ nverts++;
+
+ // Create new triangulation.
+ // TODO: Incremental add instead of full rebuild.
+ 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 getHeightData(const rcCompactHeightfield& chf,
+ const unsigned short* poly, const int npoly,
+ const unsigned short* verts,
+ rcHeightPatch& hp, rcIntArray& stack)
+{
+ // Floodfill the heightfield to get 2D height data,
+ // starting at vertex locations as seeds.
+
+ memset(hp.data, 0, sizeof(unsigned short)*hp.width*hp.height);
+
+ stack.resize(0);
+
+ static const int offset[9*2] =
+ {
+ 0,0, -1,-1, 0,-1, 1,-1, 1,0, 1,1, 0,1, -1,1, -1,0,
+ };
+
+ // Use poly vertices as seed points for the flood fill.
+ for (int j = 0; j < npoly; ++j)
+ {
+ int cx = 0, cz = 0, ci =-1;
+ int dmin = RC_UNSET_HEIGHT;
+ for (int k = 0; k < 9; ++k)
+ {
+ const int ax = (int)verts[poly[j]*3+0] + offset[k*2+0];
+ const int ay = (int)verts[poly[j]*3+1];
+ const int az = (int)verts[poly[j]*3+2] + offset[k*2+1];
+ if (ax < hp.xmin || ax >= hp.xmin+hp.width ||
+ az < hp.ymin || az >= hp.ymin+hp.height)
+ continue;
+
+ const rcCompactCell& c = chf.cells[ax+az*chf.width];
+ for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i)
+ {
+ const rcCompactSpan& s = chf.spans[i];
+ int d = rcAbs(ay - (int)s.y);
+ if (d < dmin)
+ {
+ cx = ax;
+ cz = az;
+ ci = i;
+ dmin = d;
+ }
+ }
+ }
+ if (ci != -1)
+ {
+ stack.push(cx);
+ stack.push(cz);
+ stack.push(ci);
+ }
+ }
+
+ // Find center of the polygon using flood fill.
+ int pcx = 0, pcz = 0;
+ for (int j = 0; j < npoly; ++j)
+ {
+ pcx += (int)verts[poly[j]*3+0];
+ pcz += (int)verts[poly[j]*3+2];
+ }
+ pcx /= npoly;
+ pcz /= npoly;
+
+ for (int i = 0; i < stack.size(); i += 3)
+ {
+ int cx = stack[i+0];
+ int cy = stack[i+1];
+ int idx = cx-hp.xmin+(cy-hp.ymin)*hp.width;
+ hp.data[idx] = 1;
+ }
+
+ while (stack.size() > 0)
+ {
+ int ci = stack.pop();
+ int cy = stack.pop();
+ int cx = stack.pop();
+
+ // Check if close to center of the polygon.
+ if (rcAbs(cx-pcx) <= 1 && rcAbs(cy-pcz) <= 1)
+ {
+ stack.resize(0);
+ stack.push(cx);
+ stack.push(cy);
+ stack.push(ci);
+ break;
+ }
+
+ const rcCompactSpan& cs = chf.spans[ci];
+
+ for (int dir = 0; dir < 4; ++dir)
+ {
+ if (rcGetCon(cs, dir) == RC_NOT_CONNECTED) continue;
+
+ const int ax = cx + rcGetDirOffsetX(dir);
+ const int ay = cy + rcGetDirOffsetY(dir);
+
+ if (ax < hp.xmin || ax >= (hp.xmin+hp.width) ||
+ ay < hp.ymin || ay >= (hp.ymin+hp.height))
+ continue;
+
+ if (hp.data[ax-hp.xmin+(ay-hp.ymin)*hp.width] != 0)
+ continue;
+
+ const int ai = (int)chf.cells[ax+ay*chf.width].index + rcGetCon(cs, dir);
+
+ int idx = ax-hp.xmin+(ay-hp.ymin)*hp.width;
+ hp.data[idx] = 1;
+
+ stack.push(ax);
+ stack.push(ay);
+ 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)
+ {
+ int cx = stack[i+0];
+ int cy = stack[i+1];
+ int ci = stack[i+2];
+ int idx = cx-hp.xmin+(cy-hp.ymin)*hp.width;
+ const rcCompactSpan& cs = chf.spans[ci];
+ hp.data[idx] = cs.y;
+ }
+
+ static const int RETRACT_SIZE = 256;
+ int head = 0;
+
+ while (head*3 < stack.size())
+ {
+ int cx = stack[head*3+0];
+ int cy = stack[head*3+1];
+ int ci = stack[head*3+2];
+ head++;
+ if (head >= RETRACT_SIZE)
+ {
+ head = 0;
+ if (stack.size() > RETRACT_SIZE*3)
+ 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)
+ {
+ if (rcGetCon(cs, dir) == RC_NOT_CONNECTED) continue;
+
+ const int ax = cx + rcGetDirOffsetX(dir);
+ const int ay = cy + rcGetDirOffsetY(dir);
+
+ if (ax < hp.xmin || ax >= (hp.xmin+hp.width) ||
+ ay < hp.ymin || ay >= (hp.ymin+hp.height))
+ continue;
+
+ if (hp.data[ax-hp.xmin+(ay-hp.ymin)*hp.width] != RC_UNSET_HEIGHT)
+ continue;
+
+ const int ai = (int)chf.cells[ax+ay*chf.width].index + rcGetCon(cs, dir);
+
+ const rcCompactSpan& as = chf.spans[ai];
+ int idx = ax-hp.xmin+(ay-hp.ymin)*hp.width;
+ hp.data[idx] = as.y;
+
+ stack.push(ax);
+ stack.push(ay);
+ stack.push(ai);
+ }
+ }
+
+}
+
+static unsigned char getEdgeFlags(const float* va, const float* vb,
+ const float* vpoly, const int npoly)
+{
+ // Return true if edge (va,vb) is part of the polygon.
+ 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 &&
+ distancePtSeg2d(vb, &vpoly[j*3], &vpoly[i*3]) < thrSqr)
+ return 1;
+ }
+ return 0;
+}
+
+static unsigned char getTriFlags(const float* va, const float* vb, const float* vc,
+ const float* vpoly, const int npoly)
+{
+ unsigned char flags = 0;
+ flags |= getEdgeFlags(va,vb,vpoly,npoly) << 0;
+ flags |= getEdgeFlags(vb,vc,vpoly,npoly) << 2;
+ flags |= getEdgeFlags(vc,va,vpoly,npoly) << 4;
+ return flags;
+}
+
+
+
+bool rcBuildPolyMeshDetail(rcContext* ctx, const rcPolyMesh& mesh, const rcCompactHeightfield& chf,
+ const float sampleDist, const float sampleMaxError,
+ rcPolyMeshDetail& dmesh)
+{
+ rcAssert(ctx);
+
+ ctx->startTimer(RC_TIMER_BUILD_POLYMESHDETAIL);
+
+ if (mesh.nverts == 0 || mesh.npolys == 0)
+ return true;
+
+ const int nvp = mesh.nvp;
+ const float cs = mesh.cs;
+ const float ch = mesh.ch;
+ const float* orig = mesh.bmin;
+
+ rcIntArray edges(64);
+ rcIntArray tris(512);
+ rcIntArray stack(512);
+ rcIntArray samples(512);
+ float verts[256*3];
+ rcHeightPatch hp;
+ int nPolyVerts = 0;
+ int maxhw = 0, maxhh = 0;
+
+ rcScopedDelete<int> bounds = (int*)rcAlloc(sizeof(int)*mesh.npolys*4, RC_ALLOC_TEMP);
+ if (!bounds)
+ {
+ ctx->log(RC_LOG_ERROR, "rcBuildPolyMeshDetail: Out of memory 'bounds' (%d).", mesh.npolys*4);
+ return false;
+ }
+ rcScopedDelete<float> poly = (float*)rcAlloc(sizeof(float)*nvp*3, RC_ALLOC_TEMP);
+ if (!poly)
+ {
+ ctx->log(RC_LOG_ERROR, "rcBuildPolyMeshDetail: Out of memory 'poly' (%d).", nvp*3);
+ return false;
+ }
+
+ // Find max size for a polygon area.
+ for (int i = 0; i < mesh.npolys; ++i)
+ {
+ const unsigned short* p = &mesh.polys[i*nvp*2];
+ int& xmin = bounds[i*4+0];
+ int& xmax = bounds[i*4+1];
+ int& ymin = bounds[i*4+2];
+ int& ymax = bounds[i*4+3];
+ xmin = chf.width;
+ xmax = 0;
+ ymin = chf.height;
+ ymax = 0;
+ for (int j = 0; j < nvp; ++j)
+ {
+ if(p[j] == RC_MESH_NULL_IDX) break;
+ const unsigned short* v = &mesh.verts[p[j]*3];
+ xmin = rcMin(xmin, (int)v[0]);
+ xmax = rcMax(xmax, (int)v[0]);
+ ymin = rcMin(ymin, (int)v[2]);
+ ymax = rcMax(ymax, (int)v[2]);
+ nPolyVerts++;
+ }
+ xmin = rcMax(0,xmin-1);
+ xmax = rcMin(chf.width,xmax+1);
+ ymin = rcMax(0,ymin-1);
+ ymax = rcMin(chf.height,ymax+1);
+ if (xmin >= xmax || ymin >= ymax) continue;
+ maxhw = rcMax(maxhw, xmax-xmin);
+ maxhh = rcMax(maxhh, ymax-ymin);
+ }
+
+ hp.data = (unsigned short*)rcAlloc(sizeof(unsigned short)*maxhw*maxhh, RC_ALLOC_TEMP);
+ if (!hp.data)
+ {
+ ctx->log(RC_LOG_ERROR, "rcBuildPolyMeshDetail: Out of memory 'hp.data' (%d).", maxhw*maxhh);
+ return false;
+ }
+
+ dmesh.nmeshes = mesh.npolys;
+ dmesh.nverts = 0;
+ dmesh.ntris = 0;
+ dmesh.meshes = (unsigned int*)rcAlloc(sizeof(unsigned int)*dmesh.nmeshes*4, RC_ALLOC_PERM);
+ if (!dmesh.meshes)
+ {
+ 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)
+ {
+ ctx->log(RC_LOG_ERROR, "rcBuildPolyMeshDetail: Out of memory 'dmesh.verts' (%d).", vcap*3);
+ return false;
+ }
+ dmesh.ntris = 0;
+ 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);
+ return false;
+ }
+
+ for (int i = 0; i < mesh.npolys; ++i)
+ {
+ const unsigned short* p = &mesh.polys[i*nvp*2];
+
+ // Store polygon vertices for processing.
+ int npoly = 0;
+ for (int j = 0; j < nvp; ++j)
+ {
+ if(p[j] == RC_MESH_NULL_IDX) break;
+ const unsigned short* v = &mesh.verts[p[j]*3];
+ poly[j*3+0] = v[0]*cs;
+ poly[j*3+1] = v[1]*ch;
+ poly[j*3+2] = v[2]*cs;
+ npoly++;
+ }
+
+ // Get the height data from the area of the polygon.
+ hp.xmin = bounds[i*4+0];
+ hp.ymin = bounds[i*4+2];
+ hp.width = bounds[i*4+1]-bounds[i*4+0];
+ hp.height = bounds[i*4+3]-bounds[i*4+2];
+ getHeightData(chf, p, npoly, mesh.verts, hp, stack);
+
+ // Build detail mesh.
+ int nverts = 0;
+ if (!buildPolyDetail(ctx, poly, npoly,
+ sampleDist, sampleMaxError,
+ chf, hp, verts, nverts, tris,
+ edges, samples))
+ {
+ return false;
+ }
+
+ // Move detail verts to world space.
+ for (int j = 0; j < nverts; ++j)
+ {
+ verts[j*3+0] += orig[0];
+ verts[j*3+1] += orig[1] + chf.ch; // Is this offset necessary?
+ verts[j*3+2] += orig[2];
+ }
+ // Offset poly too, will be used to flag checking.
+ for (int j = 0; j < npoly; ++j)
+ {
+ poly[j*3+0] += orig[0];
+ 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;
+
+ // 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)
+ {
+ ctx->log(RC_LOG_ERROR, "rcBuildPolyMeshDetail: Out of memory 'newv' (%d).", vcap*3);
+ return false;
+ }
+ if (dmesh.nverts)
+ memcpy(newv, dmesh.verts, sizeof(float)*3*dmesh.nverts);
+ rcFree(dmesh.verts);
+ dmesh.verts = newv;
+ }
+ for (int j = 0; j < nverts; ++j)
+ {
+ dmesh.verts[dmesh.nverts*3+0] = verts[j*3+0];
+ dmesh.verts[dmesh.nverts*3+1] = verts[j*3+1];
+ dmesh.verts[dmesh.nverts*3+2] = verts[j*3+2];
+ dmesh.nverts++;
+ }
+
+ // Store triangles, allocate more memory if necessary.
+ if (dmesh.ntris+ntris > tcap)
+ {
+ while (dmesh.ntris+ntris > tcap)
+ tcap += 256;
+ unsigned char* newt = (unsigned char*)rcAlloc(sizeof(unsigned char)*tcap*4, RC_ALLOC_PERM);
+ if (!newt)
+ {
+ ctx->log(RC_LOG_ERROR, "rcBuildPolyMeshDetail: Out of memory 'newt' (%d).", tcap*4);
+ return false;
+ }
+ if (dmesh.ntris)
+ memcpy(newt, dmesh.tris, sizeof(unsigned char)*4*dmesh.ntris);
+ rcFree(dmesh.tris);
+ dmesh.tris = newt;
+ }
+ for (int j = 0; j < ntris; ++j)
+ {
+ const int* t = &tris[j*4];
+ dmesh.tris[dmesh.ntris*4+0] = (unsigned char)t[0];
+ dmesh.tris[dmesh.ntris*4+1] = (unsigned char)t[1];
+ dmesh.tris[dmesh.ntris*4+2] = (unsigned char)t[2];
+ dmesh.tris[dmesh.ntris*4+3] = getTriFlags(&verts[t[0]*3], &verts[t[1]*3], &verts[t[2]*3], poly, npoly);
+ dmesh.ntris++;
+ }
+ }
+
+ ctx->stopTimer(RC_TIMER_BUILD_POLYMESHDETAIL);
+
+ return true;
+}
+
+bool rcMergePolyMeshDetails(rcContext* ctx, rcPolyMeshDetail** meshes, const int nmeshes, rcPolyMeshDetail& mesh)
+{
+ 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;
+ maxVerts += meshes[i]->nverts;
+ 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)
+ {
+ 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)
+ {
+ 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)
+ {
+ ctx->log(RC_LOG_ERROR, "rcBuildPolyMeshDetail: Out of memory 'dmesh.verts' (%d).", maxVerts*3);
+ return false;
+ }
+
+ // Merge datas.
+ for (int i = 0; i < nmeshes; ++i)
+ {
+ rcPolyMeshDetail* dm = meshes[i];
+ if (!dm) continue;
+ for (int j = 0; j < dm->nmeshes; ++j)
+ {
+ unsigned int* dst = &mesh.meshes[mesh.nmeshes*4];
+ unsigned int* src = &dm->meshes[j*4];
+ dst[0] = (unsigned int)mesh.nverts+src[0];
+ dst[1] = src[1];
+ dst[2] = (unsigned int)mesh.ntris+src[2];
+ dst[3] = src[3];
+ mesh.nmeshes++;
+ }
+
+ for (int k = 0; k < dm->nverts; ++k)
+ {
+ rcVcopy(&mesh.verts[mesh.nverts*3], &dm->verts[k*3]);
+ mesh.nverts++;
+ }
+ for (int k = 0; k < dm->ntris; ++k)
+ {
+ mesh.tris[mesh.ntris*4+0] = dm->tris[k*4+0];
+ mesh.tris[mesh.ntris*4+1] = dm->tris[k*4+1];
+ mesh.tris[mesh.ntris*4+2] = dm->tris[k*4+2];
+ mesh.tris[mesh.ntris*4+3] = dm->tris[k*4+3];
+ mesh.ntris++;
+ }
+ }
+
+ ctx->stopTimer(RC_TIMER_MERGE_POLYMESHDETAIL);
+
+ return true;
+}
+
diff --git a/dep/recastnavigation/Recast/RecastRasterization.cpp b/dep/recastnavigation/Recast/RecastRasterization.cpp
new file mode 100644
index 00000000000..71adfb67322
--- /dev/null
+++ b/dep/recastnavigation/Recast/RecastRasterization.cpp
@@ -0,0 +1,360 @@
+//
+// Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
+//
+// This software is provided 'as-is', without any express or implied
+// warranty. In no event will the authors be held liable for any damages
+// arising from the use of this software.
+// Permission is granted to anyone to use this software for any purpose,
+// including commercial applications, and to alter it and redistribute it
+// freely, subject to the following restrictions:
+// 1. The origin of this software must not be misrepresented; you must not
+// claim that you wrote the original software. If you use this software
+// in a product, an acknowledgment in the product documentation would be
+// appreciated but is not required.
+// 2. Altered source versions must be plainly marked as such, and must not be
+// misrepresented as being the original software.
+// 3. This notice may not be removed or altered from any source distribution.
+//
+
+#define _USE_MATH_DEFINES
+#include <math.h>
+#include <stdio.h>
+#include "Recast.h"
+#include "RecastAlloc.h"
+#include "RecastAssert.h"
+
+inline bool overlapBounds(const float* amin, const float* amax, const float* bmin, const float* bmax)
+{
+ bool overlap = true;
+ overlap = (amin[0] > bmax[0] || amax[0] < bmin[0]) ? false : overlap;
+ overlap = (amin[1] > bmax[1] || amax[1] < bmin[1]) ? false : overlap;
+ overlap = (amin[2] > bmax[2] || amax[2] < bmin[2]) ? false : overlap;
+ return overlap;
+}
+
+inline bool overlapInterval(unsigned short amin, unsigned short amax,
+ unsigned short bmin, unsigned short bmax)
+{
+ if (amax < bmin) return false;
+ if (amin > bmax) return false;
+ return true;
+}
+
+
+static rcSpan* allocSpan(rcHeightfield& hf)
+{
+ // If running out of memory, allocate new page and update the freelist.
+ if (!hf.freelist || !hf.freelist->next)
+ {
+ // Create new page.
+ // Allocate memory for the new pool.
+ rcSpanPool* pool = (rcSpanPool*)rcAlloc(sizeof(rcSpanPool), RC_ALLOC_PERM);
+ if (!pool) return 0;
+ pool->next = 0;
+ // Add the pool into the list of pools.
+ pool->next = hf.pools;
+ hf.pools = pool;
+ // Add new items to the free list.
+ rcSpan* freelist = hf.freelist;
+ rcSpan* head = &pool->items[0];
+ rcSpan* it = &pool->items[RC_SPANS_PER_POOL];
+ do
+ {
+ --it;
+ it->next = freelist;
+ freelist = it;
+ }
+ while (it != head);
+ hf.freelist = it;
+ }
+
+ // Pop item from in front of the free list.
+ rcSpan* it = hf.freelist;
+ hf.freelist = hf.freelist->next;
+ return it;
+}
+
+static void freeSpan(rcHeightfield& hf, rcSpan* ptr)
+{
+ if (!ptr) return;
+ // Add the node in front of the free list.
+ ptr->next = hf.freelist;
+ hf.freelist = ptr;
+}
+
+static void addSpan(rcHeightfield& hf, const int x, const int y,
+ const unsigned short smin, const unsigned short smax,
+ const unsigned char area, const int flagMergeThr)
+{
+
+ int idx = x + y*hf.width;
+
+ rcSpan* s = allocSpan(hf);
+ s->smin = smin;
+ s->smax = smax;
+ s->area = area;
+ s->next = 0;
+
+ // Empty cell, add he first span.
+ if (!hf.spans[idx])
+ {
+ hf.spans[idx] = s;
+ return;
+ }
+ rcSpan* prev = 0;
+ rcSpan* cur = hf.spans[idx];
+
+ // Insert and merge spans.
+ while (cur)
+ {
+ if (cur->smin > s->smax)
+ {
+ // Current span is further than the new span, break.
+ break;
+ }
+ else if (cur->smax < s->smin)
+ {
+ // Current span is before the new span advance.
+ prev = cur;
+ cur = cur->next;
+ }
+ else
+ {
+ // Merge spans.
+ if (cur->smin < s->smin)
+ s->smin = cur->smin;
+ if (cur->smax > s->smax)
+ s->smax = cur->smax;
+
+ // Merge flags.
+ if (rcAbs((int)s->smax - (int)cur->smax) <= flagMergeThr)
+ s->area = rcMax(s->area, cur->area);
+
+ // Remove current span.
+ rcSpan* next = cur->next;
+ freeSpan(hf, cur);
+ if (prev)
+ prev->next = next;
+ else
+ hf.spans[idx] = next;
+ cur = next;
+ }
+ }
+
+ // Insert new span.
+ if (prev)
+ {
+ s->next = prev->next;
+ prev->next = s;
+ }
+ else
+ {
+ s->next = hf.spans[idx];
+ hf.spans[idx] = s;
+ }
+}
+
+void rcAddSpan(rcContext* /*ctx*/, rcHeightfield& hf, const int x, const int y,
+ const unsigned short smin, const unsigned short smax,
+ const unsigned char area, const int flagMergeThr)
+{
+// rcAssert(ctx);
+ addSpan(hf, x,y, smin, smax, area, flagMergeThr);
+}
+
+static int clipPoly(const float* in, int n, float* out, float pnx, float pnz, float pd)
+{
+ float d[12];
+ for (int i = 0; i < n; ++i)
+ d[i] = pnx*in[i*3+0] + pnz*in[i*3+2] + pd;
+
+ int m = 0;
+ for (int i = 0, j = n-1; i < n; j=i, ++i)
+ {
+ bool ina = d[j] >= 0;
+ bool inb = d[i] >= 0;
+ if (ina != inb)
+ {
+ float s = d[j] / (d[j] - d[i]);
+ out[m*3+0] = in[j*3+0] + (in[i*3+0] - in[j*3+0])*s;
+ out[m*3+1] = in[j*3+1] + (in[i*3+1] - in[j*3+1])*s;
+ out[m*3+2] = in[j*3+2] + (in[i*3+2] - in[j*3+2])*s;
+ m++;
+ }
+ if (inb)
+ {
+ out[m*3+0] = in[i*3+0];
+ out[m*3+1] = in[i*3+1];
+ out[m*3+2] = in[i*3+2];
+ m++;
+ }
+ }
+ return m;
+}
+
+static void rasterizeTri(const float* v0, const float* v1, const float* v2,
+ const unsigned char area, rcHeightfield& hf,
+ const float* bmin, const float* bmax,
+ const float cs, const float ics, const float ich,
+ const int flagMergeThr)
+{
+ const int w = hf.width;
+ const int h = hf.height;
+ float tmin[3], tmax[3];
+ const float by = bmax[1] - bmin[1];
+
+ // Calculate the bounding box of the triangle.
+ rcVcopy(tmin, v0);
+ rcVcopy(tmax, v0);
+ rcVmin(tmin, v1);
+ rcVmin(tmin, v2);
+ rcVmax(tmax, v1);
+ rcVmax(tmax, v2);
+
+ // If the triangle does not touch the bbox of the heightfield, skip the triagle.
+ if (!overlapBounds(bmin, bmax, tmin, tmax))
+ return;
+
+ // Calculate the footpring of the triangle on the grid.
+ int x0 = (int)((tmin[0] - bmin[0])*ics);
+ int y0 = (int)((tmin[2] - bmin[2])*ics);
+ int x1 = (int)((tmax[0] - bmin[0])*ics);
+ int y1 = (int)((tmax[2] - bmin[2])*ics);
+ x0 = rcClamp(x0, 0, w-1);
+ y0 = rcClamp(y0, 0, h-1);
+ x1 = rcClamp(x1, 0, w-1);
+ y1 = rcClamp(y1, 0, h-1);
+
+ // Clip the triangle into all grid cells it touches.
+ float in[7*3], out[7*3], inrow[7*3];
+
+ for (int y = y0; y <= y1; ++y)
+ {
+ // Clip polygon to row.
+ rcVcopy(&in[0], v0);
+ rcVcopy(&in[1*3], v1);
+ rcVcopy(&in[2*3], v2);
+ int nvrow = 3;
+ const float cz = bmin[2] + y*cs;
+ nvrow = clipPoly(in, nvrow, out, 0, 1, -cz);
+ if (nvrow < 3) continue;
+ nvrow = clipPoly(out, nvrow, inrow, 0, -1, cz+cs);
+ if (nvrow < 3) continue;
+
+ for (int x = x0; x <= x1; ++x)
+ {
+ // Clip polygon to column.
+ int nv = nvrow;
+ const float cx = bmin[0] + x*cs;
+ nv = clipPoly(inrow, nv, out, 1, 0, -cx);
+ if (nv < 3) continue;
+ nv = clipPoly(out, nv, in, -1, 0, cx+cs);
+ if (nv < 3) continue;
+
+ // Calculate min and max of the span.
+ float smin = in[1], smax = in[1];
+ for (int i = 1; i < nv; ++i)
+ {
+ smin = rcMin(smin, in[i*3+1]);
+ smax = rcMax(smax, in[i*3+1]);
+ }
+ smin -= bmin[1];
+ smax -= bmin[1];
+ // Skip the span if it is outside the heightfield bbox
+ if (smax < 0.0f) continue;
+ if (smin > by) continue;
+ // Clamp the span to the heightfield bbox.
+ if (smin < 0.0f) smin = 0;
+ if (smax > by) smax = by;
+
+ // Snap the span to the heightfield height grid.
+ unsigned short ismin = (unsigned short)rcClamp((int)floorf(smin * ich), 0, RC_SPAN_MAX_HEIGHT);
+ unsigned short ismax = (unsigned short)rcClamp((int)ceilf(smax * ich), (int)ismin+1, RC_SPAN_MAX_HEIGHT);
+
+ addSpan(hf, x, y, ismin, ismax, area, flagMergeThr);
+ }
+ }
+}
+
+void rcRasterizeTriangle(rcContext* ctx, const float* v0, const float* v1, const float* v2,
+ const unsigned char area, rcHeightfield& solid,
+ const int flagMergeThr)
+{
+ rcAssert(ctx);
+
+ ctx->startTimer(RC_TIMER_RASTERIZE_TRIANGLES);
+
+ const float ics = 1.0f/solid.cs;
+ const float ich = 1.0f/solid.ch;
+ rasterizeTri(v0, v1, v2, area, solid, solid.bmin, solid.bmax, solid.cs, ics, ich, flagMergeThr);
+
+ ctx->stopTimer(RC_TIMER_RASTERIZE_TRIANGLES);
+}
+
+void rcRasterizeTriangles(rcContext* ctx, const float* verts, const int /*nv*/,
+ const int* tris, const unsigned char* areas, const int nt,
+ rcHeightfield& solid, const int flagMergeThr)
+{
+ rcAssert(ctx);
+
+ ctx->startTimer(RC_TIMER_RASTERIZE_TRIANGLES);
+
+ const float ics = 1.0f/solid.cs;
+ const float ich = 1.0f/solid.ch;
+ // Rasterize triangles.
+ for (int i = 0; i < nt; ++i)
+ {
+ const float* v0 = &verts[tris[i*3+0]*3];
+ const float* v1 = &verts[tris[i*3+1]*3];
+ const float* v2 = &verts[tris[i*3+2]*3];
+ // Rasterize.
+ rasterizeTri(v0, v1, v2, areas[i], solid, solid.bmin, solid.bmax, solid.cs, ics, ich, flagMergeThr);
+ }
+
+ ctx->stopTimer(RC_TIMER_RASTERIZE_TRIANGLES);
+}
+
+void rcRasterizeTriangles(rcContext* ctx, const float* verts, const int /*nv*/,
+ const unsigned short* tris, const unsigned char* areas, const int nt,
+ rcHeightfield& solid, const int flagMergeThr)
+{
+ rcAssert(ctx);
+
+ ctx->startTimer(RC_TIMER_RASTERIZE_TRIANGLES);
+
+ const float ics = 1.0f/solid.cs;
+ const float ich = 1.0f/solid.ch;
+ // Rasterize triangles.
+ for (int i = 0; i < nt; ++i)
+ {
+ const float* v0 = &verts[tris[i*3+0]*3];
+ const float* v1 = &verts[tris[i*3+1]*3];
+ const float* v2 = &verts[tris[i*3+2]*3];
+ // Rasterize.
+ rasterizeTri(v0, v1, v2, areas[i], solid, solid.bmin, solid.bmax, solid.cs, ics, ich, flagMergeThr);
+ }
+
+ ctx->stopTimer(RC_TIMER_RASTERIZE_TRIANGLES);
+}
+
+void rcRasterizeTriangles(rcContext* ctx, const float* verts, const unsigned char* areas, const int nt,
+ rcHeightfield& solid, const int flagMergeThr)
+{
+ rcAssert(ctx);
+
+ ctx->startTimer(RC_TIMER_RASTERIZE_TRIANGLES);
+
+ const float ics = 1.0f/solid.cs;
+ const float ich = 1.0f/solid.ch;
+ // Rasterize triangles.
+ for (int i = 0; i < nt; ++i)
+ {
+ const float* v0 = &verts[(i*3+0)*3];
+ const float* v1 = &verts[(i*3+1)*3];
+ const float* v2 = &verts[(i*3+2)*3];
+ // Rasterize.
+ rasterizeTri(v0, v1, v2, areas[i], solid, solid.bmin, solid.bmax, solid.cs, ics, ich, flagMergeThr);
+ }
+
+ ctx->stopTimer(RC_TIMER_RASTERIZE_TRIANGLES);
+}
diff --git a/dep/recastnavigation/Recast/RecastRegion.cpp b/dep/recastnavigation/Recast/RecastRegion.cpp
new file mode 100644
index 00000000000..6ad9fa53186
--- /dev/null
+++ b/dep/recastnavigation/Recast/RecastRegion.cpp
@@ -0,0 +1,1283 @@
+//
+// Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
+//
+// This software is provided 'as-is', without any express or implied
+// warranty. In no event will the authors be held liable for any damages
+// arising from the use of this software.
+// Permission is granted to anyone to use this software for any purpose,
+// including commercial applications, and to alter it and redistribute it
+// freely, subject to the following restrictions:
+// 1. The origin of this software must not be misrepresented; you must not
+// claim that you wrote the original software. If you use this software
+// in a product, an acknowledgment in the product documentation would be
+// appreciated but is not required.
+// 2. Altered source versions must be plainly marked as such, and must not be
+// misrepresented as being the original software.
+// 3. This notice may not be removed or altered from any source distribution.
+//
+
+#include <float.h>
+#define _USE_MATH_DEFINES
+#include <math.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include "Recast.h"
+#include "RecastAlloc.h"
+#include "RecastAssert.h"
+#include <new>
+
+
+static void calculateDistanceField(rcCompactHeightfield& chf, unsigned short* src, unsigned short& maxDist)
+{
+ const int w = chf.width;
+ const int h = chf.height;
+
+ // Init distance and points.
+ for (int i = 0; i < chf.spanCount; ++i)
+ src[i] = 0xffff;
+
+ // Mark boundary cells.
+ for (int y = 0; y < h; ++y)
+ {
+ for (int x = 0; x < w; ++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];
+ const unsigned char area = chf.areas[i];
+
+ int nc = 0;
+ 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);
+ if (area == chf.areas[ai])
+ nc++;
+ }
+ }
+ if (nc != 4)
+ src[i] = 0;
+ }
+ }
+ }
+
+
+ // Pass 1
+ for (int y = 0; y < h; ++y)
+ {
+ for (int x = 0; x < w; ++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 (rcGetCon(s, 0) != RC_NOT_CONNECTED)
+ {
+ // (-1,0)
+ 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);
+ const rcCompactSpan& as = chf.spans[ai];
+ if (src[ai]+2 < src[i])
+ src[i] = src[ai]+2;
+
+ // (-1,-1)
+ if (rcGetCon(as, 3) != RC_NOT_CONNECTED)
+ {
+ const int aax = ax + rcGetDirOffsetX(3);
+ const int aay = ay + rcGetDirOffsetY(3);
+ const int aai = (int)chf.cells[aax+aay*w].index + rcGetCon(as, 3);
+ if (src[aai]+3 < src[i])
+ src[i] = src[aai]+3;
+ }
+ }
+ if (rcGetCon(s, 3) != RC_NOT_CONNECTED)
+ {
+ // (0,-1)
+ 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);
+ const rcCompactSpan& as = chf.spans[ai];
+ if (src[ai]+2 < src[i])
+ src[i] = src[ai]+2;
+
+ // (1,-1)
+ if (rcGetCon(as, 2) != RC_NOT_CONNECTED)
+ {
+ const int aax = ax + rcGetDirOffsetX(2);
+ const int aay = ay + rcGetDirOffsetY(2);
+ const int aai = (int)chf.cells[aax+aay*w].index + rcGetCon(as, 2);
+ if (src[aai]+3 < src[i])
+ src[i] = src[aai]+3;
+ }
+ }
+ }
+ }
+ }
+
+ // Pass 2
+ for (int y = h-1; y >= 0; --y)
+ {
+ for (int x = w-1; x >= 0; --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 (rcGetCon(s, 2) != RC_NOT_CONNECTED)
+ {
+ // (1,0)
+ const int ax = x + rcGetDirOffsetX(2);
+ const int ay = y + rcGetDirOffsetY(2);
+ const int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(s, 2);
+ const rcCompactSpan& as = chf.spans[ai];
+ if (src[ai]+2 < src[i])
+ src[i] = src[ai]+2;
+
+ // (1,1)
+ if (rcGetCon(as, 1) != RC_NOT_CONNECTED)
+ {
+ const int aax = ax + rcGetDirOffsetX(1);
+ const int aay = ay + rcGetDirOffsetY(1);
+ const int aai = (int)chf.cells[aax+aay*w].index + rcGetCon(as, 1);
+ if (src[aai]+3 < src[i])
+ src[i] = src[aai]+3;
+ }
+ }
+ if (rcGetCon(s, 1) != RC_NOT_CONNECTED)
+ {
+ // (0,1)
+ const int ax = x + rcGetDirOffsetX(1);
+ const int ay = y + rcGetDirOffsetY(1);
+ const int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(s, 1);
+ const rcCompactSpan& as = chf.spans[ai];
+ if (src[ai]+2 < src[i])
+ src[i] = src[ai]+2;
+
+ // (-1,1)
+ if (rcGetCon(as, 0) != RC_NOT_CONNECTED)
+ {
+ const int aax = ax + rcGetDirOffsetX(0);
+ const int aay = ay + rcGetDirOffsetY(0);
+ const int aai = (int)chf.cells[aax+aay*w].index + rcGetCon(as, 0);
+ if (src[aai]+3 < src[i])
+ src[i] = src[aai]+3;
+ }
+ }
+ }
+ }
+ }
+
+ maxDist = 0;
+ for (int i = 0; i < chf.spanCount; ++i)
+ maxDist = rcMax(src[i], maxDist);
+
+}
+
+static unsigned short* boxBlur(rcCompactHeightfield& chf, int thr,
+ unsigned short* src, unsigned short* dst)
+{
+ const int w = chf.width;
+ const int h = chf.height;
+
+ thr *= 2;
+
+ for (int y = 0; y < h; ++y)
+ {
+ for (int x = 0; x < w; ++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];
+ const unsigned short cd = src[i];
+ if (cd <= thr)
+ {
+ dst[i] = cd;
+ continue;
+ }
+
+ int d = (int)cd;
+ 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);
+ d += (int)src[ai];
+
+ const rcCompactSpan& as = chf.spans[ai];
+ const int dir2 = (dir+1) & 0x3;
+ if (rcGetCon(as, dir2) != RC_NOT_CONNECTED)
+ {
+ const int ax2 = ax + rcGetDirOffsetX(dir2);
+ const int ay2 = ay + rcGetDirOffsetY(dir2);
+ const int ai2 = (int)chf.cells[ax2+ay2*w].index + rcGetCon(as, dir2);
+ d += (int)src[ai2];
+ }
+ else
+ {
+ d += cd;
+ }
+ }
+ else
+ {
+ d += cd*2;
+ }
+ }
+ dst[i] = (unsigned short)((d+5)/9);
+ }
+ }
+ }
+ return dst;
+}
+
+
+static bool floodRegion(int x, int y, int i,
+ unsigned short level, unsigned short r,
+ rcCompactHeightfield& chf,
+ unsigned short* srcReg, unsigned short* srcDist,
+ rcIntArray& stack)
+{
+ const int w = chf.width;
+
+ const unsigned char area = chf.areas[i];
+
+ // Flood fill mark region.
+ stack.resize(0);
+ stack.push((int)x);
+ stack.push((int)y);
+ stack.push((int)i);
+ srcReg[i] = r;
+ srcDist[i] = 0;
+
+ unsigned short lev = level >= 2 ? level-2 : 0;
+ int count = 0;
+
+ while (stack.size() > 0)
+ {
+ int ci = stack.pop();
+ int cy = stack.pop();
+ int cx = stack.pop();
+
+ const rcCompactSpan& cs = chf.spans[ci];
+
+ // Check if any of the neighbours already have a valid region set.
+ unsigned short ar = 0;
+ for (int dir = 0; dir < 4; ++dir)
+ {
+ // 8 connected
+ if (rcGetCon(cs, dir) != RC_NOT_CONNECTED)
+ {
+ const int ax = cx + rcGetDirOffsetX(dir);
+ const int ay = cy + rcGetDirOffsetY(dir);
+ const int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(cs, dir);
+ if (chf.areas[ai] != area)
+ continue;
+ unsigned short nr = srcReg[ai];
+ if (nr != 0 && nr != r)
+ ar = nr;
+
+ const rcCompactSpan& as = chf.spans[ai];
+
+ const int dir2 = (dir+1) & 0x3;
+ if (rcGetCon(as, dir2) != RC_NOT_CONNECTED)
+ {
+ const int ax2 = ax + rcGetDirOffsetX(dir2);
+ const int ay2 = ay + rcGetDirOffsetY(dir2);
+ const int ai2 = (int)chf.cells[ax2+ay2*w].index + rcGetCon(as, dir2);
+ if (chf.areas[ai2] != area)
+ continue;
+ unsigned short nr = srcReg[ai2];
+ if (nr != 0 && nr != r)
+ ar = nr;
+ }
+ }
+ }
+ if (ar != 0)
+ {
+ srcReg[ci] = 0;
+ continue;
+ }
+ count++;
+
+ // Expand neighbours.
+ for (int dir = 0; dir < 4; ++dir)
+ {
+ if (rcGetCon(cs, dir) != RC_NOT_CONNECTED)
+ {
+ const int ax = cx + rcGetDirOffsetX(dir);
+ const int ay = cy + rcGetDirOffsetY(dir);
+ const int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(cs, dir);
+ if (chf.areas[ai] != area)
+ continue;
+ if (chf.dist[ai] >= lev)
+ {
+ if (srcReg[ai] == 0)
+ {
+ srcReg[ai] = r;
+ srcDist[ai] = 0;
+ stack.push(ax);
+ stack.push(ay);
+ stack.push(ai);
+ }
+ }
+ }
+ }
+ }
+
+ return count > 0;
+}
+
+static unsigned short* expandRegions(int maxIter, unsigned short level,
+ rcCompactHeightfield& chf,
+ unsigned short* srcReg, unsigned short* srcDist,
+ unsigned short* dstReg, unsigned short* dstDist,
+ rcIntArray& stack)
+{
+ const int w = chf.width;
+ const int h = chf.height;
+
+ // Find cells revealed by the raised level.
+ stack.resize(0);
+ for (int y = 0; y < h; ++y)
+ {
+ for (int x = 0; x < w; ++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 (chf.dist[i] >= level && srcReg[i] == 0 && chf.areas[i] != RC_NULL_AREA)
+ {
+ stack.push(x);
+ stack.push(y);
+ stack.push(i);
+ }
+ }
+ }
+ }
+
+ int iter = 0;
+ while (stack.size() > 0)
+ {
+ int failed = 0;
+
+ memcpy(dstReg, srcReg, sizeof(unsigned short)*chf.spanCount);
+ memcpy(dstDist, srcDist, sizeof(unsigned short)*chf.spanCount);
+
+ for (int j = 0; j < stack.size(); j += 3)
+ {
+ int x = stack[j+0];
+ int y = stack[j+1];
+ int i = stack[j+2];
+ if (i < 0)
+ {
+ failed++;
+ continue;
+ }
+
+ unsigned short r = srcReg[i];
+ unsigned short d2 = 0xffff;
+ const unsigned char area = chf.areas[i];
+ const rcCompactSpan& s = chf.spans[i];
+ for (int dir = 0; dir < 4; ++dir)
+ {
+ if (rcGetCon(s, dir) == RC_NOT_CONNECTED) continue;
+ 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);
+ if (chf.areas[ai] != area) continue;
+ if (srcReg[ai] > 0 && (srcReg[ai] & RC_BORDER_REG) == 0)
+ {
+ if ((int)srcDist[ai]+2 < (int)d2)
+ {
+ r = srcReg[ai];
+ d2 = srcDist[ai]+2;
+ }
+ }
+ }
+ if (r)
+ {
+ stack[j+2] = -1; // mark as used
+ dstReg[i] = r;
+ dstDist[i] = d2;
+ }
+ else
+ {
+ failed++;
+ }
+ }
+
+ // rcSwap source and dest.
+ rcSwap(srcReg, dstReg);
+ rcSwap(srcDist, dstDist);
+
+ if (failed*3 == stack.size())
+ break;
+
+ if (level > 0)
+ {
+ ++iter;
+ if (iter >= maxIter)
+ break;
+ }
+ }
+
+ return srcReg;
+}
+
+
+struct rcRegion
+{
+ inline rcRegion(unsigned short i) :
+ spanCount(0),
+ id(i),
+ areaType(0),
+ remap(false),
+ visited(false)
+ {}
+
+ int spanCount; // Number of spans belonging to this region
+ unsigned short id; // ID of the region
+ unsigned char areaType; // Are type.
+ bool remap;
+ bool visited;
+ rcIntArray connections;
+ rcIntArray floors;
+};
+
+static void removeAdjacentNeighbours(rcRegion& reg)
+{
+ // Remove adjacent duplicates.
+ for (int i = 0; i < reg.connections.size() && reg.connections.size() > 1; )
+ {
+ int ni = (i+1) % reg.connections.size();
+ if (reg.connections[i] == reg.connections[ni])
+ {
+ // Remove duplicate
+ for (int j = i; j < reg.connections.size()-1; ++j)
+ reg.connections[j] = reg.connections[j+1];
+ reg.connections.pop();
+ }
+ else
+ ++i;
+ }
+}
+
+static void replaceNeighbour(rcRegion& reg, unsigned short oldId, unsigned short newId)
+{
+ bool neiChanged = false;
+ for (int i = 0; i < reg.connections.size(); ++i)
+ {
+ if (reg.connections[i] == oldId)
+ {
+ reg.connections[i] = newId;
+ neiChanged = true;
+ }
+ }
+ for (int i = 0; i < reg.floors.size(); ++i)
+ {
+ if (reg.floors[i] == oldId)
+ reg.floors[i] = newId;
+ }
+ if (neiChanged)
+ removeAdjacentNeighbours(reg);
+}
+
+static bool canMergeWithRegion(const rcRegion& rega, const rcRegion& regb)
+{
+ if (rega.areaType != regb.areaType)
+ return false;
+ int n = 0;
+ for (int i = 0; i < rega.connections.size(); ++i)
+ {
+ if (rega.connections[i] == regb.id)
+ n++;
+ }
+ if (n > 1)
+ return false;
+ for (int i = 0; i < rega.floors.size(); ++i)
+ {
+ if (rega.floors[i] == regb.id)
+ return false;
+ }
+ return true;
+}
+
+static void addUniqueFloorRegion(rcRegion& reg, int n)
+{
+ for (int i = 0; i < reg.floors.size(); ++i)
+ if (reg.floors[i] == n)
+ return;
+ reg.floors.push(n);
+}
+
+static bool mergeRegions(rcRegion& rega, rcRegion& regb)
+{
+ unsigned short aid = rega.id;
+ unsigned short bid = regb.id;
+
+ // Duplicate current neighbourhood.
+ rcIntArray acon;
+ acon.resize(rega.connections.size());
+ for (int i = 0; i < rega.connections.size(); ++i)
+ acon[i] = rega.connections[i];
+ rcIntArray& bcon = regb.connections;
+
+ // Find insertion point on A.
+ int insa = -1;
+ for (int i = 0; i < acon.size(); ++i)
+ {
+ if (acon[i] == bid)
+ {
+ insa = i;
+ break;
+ }
+ }
+ if (insa == -1)
+ return false;
+
+ // Find insertion point on B.
+ int insb = -1;
+ for (int i = 0; i < bcon.size(); ++i)
+ {
+ if (bcon[i] == aid)
+ {
+ insb = i;
+ break;
+ }
+ }
+ if (insb == -1)
+ return false;
+
+ // Merge neighbours.
+ rega.connections.resize(0);
+ for (int i = 0, ni = acon.size(); i < ni-1; ++i)
+ rega.connections.push(acon[(insa+1+i) % ni]);
+
+ for (int i = 0, ni = bcon.size(); i < ni-1; ++i)
+ rega.connections.push(bcon[(insb+1+i) % ni]);
+
+ removeAdjacentNeighbours(rega);
+
+ for (int j = 0; j < regb.floors.size(); ++j)
+ addUniqueFloorRegion(rega, regb.floors[j]);
+ rega.spanCount += regb.spanCount;
+ regb.spanCount = 0;
+ regb.connections.resize(0);
+
+ return true;
+}
+
+static bool isRegionConnectedToBorder(const rcRegion& reg)
+{
+ // Region is connected to border if
+ // one of the neighbours is null id.
+ for (int i = 0; i < reg.connections.size(); ++i)
+ {
+ if (reg.connections[i] == 0)
+ return true;
+ }
+ return false;
+}
+
+static bool isSolidEdge(rcCompactHeightfield& chf, unsigned short* srcReg,
+ int x, int y, int i, int dir)
+{
+ const rcCompactSpan& s = chf.spans[i];
+ unsigned short r = 0;
+ 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*chf.width].index + rcGetCon(s, dir);
+ r = srcReg[ai];
+ }
+ if (r == srcReg[i])
+ return false;
+ return true;
+}
+
+static void walkContour(int x, int y, int i, int dir,
+ rcCompactHeightfield& chf,
+ unsigned short* srcReg,
+ rcIntArray& cont)
+{
+ int startDir = dir;
+ int starti = i;
+
+ const rcCompactSpan& ss = chf.spans[i];
+ unsigned short curReg = 0;
+ if (rcGetCon(ss, dir) != RC_NOT_CONNECTED)
+ {
+ const int ax = x + rcGetDirOffsetX(dir);
+ const int ay = y + rcGetDirOffsetY(dir);
+ const int ai = (int)chf.cells[ax+ay*chf.width].index + rcGetCon(ss, dir);
+ curReg = srcReg[ai];
+ }
+ cont.push(curReg);
+
+ int iter = 0;
+ while (++iter < 40000)
+ {
+ const rcCompactSpan& s = chf.spans[i];
+
+ if (isSolidEdge(chf, srcReg, x, y, i, dir))
+ {
+ // Choose the edge corner
+ unsigned short r = 0;
+ 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*chf.width].index + rcGetCon(s, dir);
+ r = srcReg[ai];
+ }
+ if (r != curReg)
+ {
+ curReg = r;
+ cont.push(curReg);
+ }
+
+ dir = (dir+1) & 0x3; // Rotate CW
+ }
+ else
+ {
+ int ni = -1;
+ const int nx = x + rcGetDirOffsetX(dir);
+ const int ny = y + rcGetDirOffsetY(dir);
+ if (rcGetCon(s, dir) != RC_NOT_CONNECTED)
+ {
+ const rcCompactCell& nc = chf.cells[nx+ny*chf.width];
+ ni = (int)nc.index + rcGetCon(s, dir);
+ }
+ if (ni == -1)
+ {
+ // Should not happen.
+ return;
+ }
+ x = nx;
+ y = ny;
+ i = ni;
+ dir = (dir+3) & 0x3; // Rotate CCW
+ }
+
+ if (starti == i && startDir == dir)
+ {
+ break;
+ }
+ }
+
+ // Remove adjacent duplicates.
+ if (cont.size() > 1)
+ {
+ for (int i = 0; i < cont.size(); )
+ {
+ int ni = (i+1) % cont.size();
+ if (cont[i] == cont[ni])
+ {
+ for (int j = i; j < cont.size()-1; ++j)
+ cont[j] = cont[j+1];
+ cont.pop();
+ }
+ else
+ ++i;
+ }
+ }
+}
+
+static bool filterSmallRegions(rcContext* ctx, int minRegionArea, int mergeRegionSize,
+ unsigned short& maxRegionId,
+ rcCompactHeightfield& chf,
+ unsigned short* srcReg)
+{
+ 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, "filterSmallRegions: Out of memory 'regions' (%d).", nreg);
+ return false;
+ }
+
+ // Construct regions
+ for (int i = 0; i < nreg; ++i)
+ new(&regions[i]) rcRegion((unsigned short)i);
+
+ // Find edge of a region and find connections around the contour.
+ for (int y = 0; y < h; ++y)
+ {
+ for (int x = 0; x < w; ++x)
+ {
+ const rcCompactCell& c = chf.cells[x+y*w];
+ for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i)
+ {
+ unsigned short r = srcReg[i];
+ if (r == 0 || r >= nreg)
+ continue;
+
+ rcRegion& reg = regions[r];
+ reg.spanCount++;
+
+
+ // Update floors.
+ for (int j = (int)c.index; j < ni; ++j)
+ {
+ if (i == j) continue;
+ unsigned short floorId = srcReg[j];
+ if (floorId == 0 || floorId >= nreg)
+ continue;
+ addUniqueFloorRegion(reg, floorId);
+ }
+
+ // Have found contour
+ if (reg.connections.size() > 0)
+ continue;
+
+ reg.areaType = chf.areas[i];
+
+ // Check if this cell is next to a border.
+ int ndir = -1;
+ for (int dir = 0; dir < 4; ++dir)
+ {
+ if (isSolidEdge(chf, srcReg, x, y, i, dir))
+ {
+ ndir = dir;
+ break;
+ }
+ }
+
+ if (ndir != -1)
+ {
+ // The cell is at border.
+ // Walk around the contour to find all the neighbours.
+ walkContour(x, y, i, ndir, chf, srcReg, reg.connections);
+ }
+ }
+ }
+ }
+
+ // Remove too small regions.
+ rcIntArray stack(32);
+ rcIntArray trace(32);
+ for (int i = 0; i < nreg; ++i)
+ {
+ rcRegion& reg = regions[i];
+ if (reg.id == 0 || (reg.id & RC_BORDER_REG))
+ continue;
+ if (reg.spanCount == 0)
+ continue;
+ if (reg.visited)
+ continue;
+
+ // Count the total size of all the connected regions.
+ // Also keep track of the regions connects to a tile border.
+ bool connectsToBorder = false;
+ int spanCount = 0;
+ stack.resize(0);
+ trace.resize(0);
+
+ reg.visited = true;
+ stack.push(i);
+
+ while (stack.size())
+ {
+ // Pop
+ int ri = stack.pop();
+
+ rcRegion& creg = regions[ri];
+
+ spanCount += creg.spanCount;
+ trace.push(ri);
+
+ for (int j = 0; j < creg.connections.size(); ++j)
+ {
+ if (creg.connections[j] & RC_BORDER_REG)
+ {
+ connectsToBorder = true;
+ continue;
+ }
+ rcRegion& nreg = regions[creg.connections[j]];
+ if (nreg.visited)
+ continue;
+ if (nreg.id == 0 || (nreg.id & RC_BORDER_REG))
+ continue;
+ // Visit
+ stack.push(nreg.id);
+ nreg.visited = true;
+ }
+ }
+
+ // If the accumulated regions size is too small, remove it.
+ // Do not remove areas which connect to tile borders
+ // as their size cannot be estimated correctly and removing them
+ // can potentially remove necessary areas.
+ if (spanCount < minRegionArea && !connectsToBorder)
+ {
+ // Kill all visited regions.
+ for (int j = 0; j < trace.size(); ++j)
+ {
+ regions[trace[j]].spanCount = 0;
+ regions[trace[j]].id = 0;
+ }
+ }
+ }
+
+ // Merge too small regions to neighbour regions.
+ int mergeCount = 0 ;
+ do
+ {
+ mergeCount = 0;
+ for (int i = 0; i < nreg; ++i)
+ {
+ rcRegion& reg = regions[i];
+ if (reg.id == 0 || (reg.id & RC_BORDER_REG))
+ continue;
+ if (reg.spanCount == 0)
+ continue;
+
+ // Check to see if the region should be merged.
+ if (reg.spanCount > mergeRegionSize && isRegionConnectedToBorder(reg))
+ continue;
+
+ // Small region with more than 1 connection.
+ // Or region which is not connected to a border at all.
+ // Find smallest neighbour region that connects to this one.
+ int smallest = 0xfffffff;
+ unsigned short mergeId = reg.id;
+ for (int j = 0; j < reg.connections.size(); ++j)
+ {
+ 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.spanCount < smallest &&
+ canMergeWithRegion(reg, mreg) &&
+ canMergeWithRegion(mreg, reg))
+ {
+ smallest = mreg.spanCount;
+ mergeId = mreg.id;
+ }
+ }
+ // Found new id.
+ if (mergeId != reg.id)
+ {
+ unsigned short oldId = reg.id;
+ rcRegion& target = regions[mergeId];
+
+ // Merge neighbours.
+ if (mergeRegions(target, reg))
+ {
+ // Fixup regions pointing to current region.
+ for (int j = 0; j < nreg; ++j)
+ {
+ if (regions[j].id == 0 || (regions[j].id & RC_BORDER_REG)) continue;
+ // If another region was already merged into current region
+ // change the nid of the previous region too.
+ if (regions[j].id == oldId)
+ regions[j].id = mergeId;
+ // Replace the current region with the new one if the
+ // current regions is neighbour.
+ replaceNeighbour(regions[j], oldId, mergeId);
+ }
+ mergeCount++;
+ }
+ }
+ }
+ }
+ while (mergeCount > 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)
+ {
+ if ((srcReg[i] & RC_BORDER_REG) == 0)
+ srcReg[i] = regions[srcReg[i]].id;
+ }
+
+ for (int i = 0; i < nreg; ++i)
+ regions[i].~rcRegion();
+ rcFree(regions);
+
+ return true;
+}
+
+
+bool rcBuildDistanceField(rcContext* ctx, rcCompactHeightfield& chf)
+{
+ rcAssert(ctx);
+
+ ctx->startTimer(RC_TIMER_BUILD_DISTANCEFIELD);
+
+ if (chf.dist)
+ {
+ rcFree(chf.dist);
+ chf.dist = 0;
+ }
+
+ unsigned short* src = (unsigned short*)rcAlloc(sizeof(unsigned short)*chf.spanCount, RC_ALLOC_TEMP);
+ if (!src)
+ {
+ ctx->log(RC_LOG_ERROR, "rcBuildDistanceField: Out of memory 'src' (%d).", chf.spanCount);
+ return false;
+ }
+ unsigned short* dst = (unsigned short*)rcAlloc(sizeof(unsigned short)*chf.spanCount, RC_ALLOC_TEMP);
+ if (!dst)
+ {
+ ctx->log(RC_LOG_ERROR, "rcBuildDistanceField: Out of memory 'dst' (%d).", chf.spanCount);
+ rcFree(src);
+ return false;
+ }
+
+ unsigned short maxDist = 0;
+
+ ctx->startTimer(RC_TIMER_BUILD_DISTANCEFIELD_DIST);
+
+ calculateDistanceField(chf, src, maxDist);
+ chf.maxDistance = maxDist;
+
+ ctx->stopTimer(RC_TIMER_BUILD_DISTANCEFIELD_DIST);
+
+ ctx->startTimer(RC_TIMER_BUILD_DISTANCEFIELD_BLUR);
+
+ // Blur
+ if (boxBlur(chf, 1, src, dst) != src)
+ rcSwap(src, dst);
+
+ // Store distance.
+ chf.dist = src;
+
+ ctx->stopTimer(RC_TIMER_BUILD_DISTANCEFIELD_BLUR);
+
+ ctx->stopTimer(RC_TIMER_BUILD_DISTANCEFIELD);
+
+ rcFree(dst);
+
+ return true;
+}
+
+static void paintRectRegion(int minx, int maxx, int miny, int maxy, unsigned short regId,
+ rcCompactHeightfield& chf, unsigned short* srcReg)
+{
+ const int w = chf.width;
+ for (int y = miny; y < maxy; ++y)
+ {
+ for (int x = minx; x < maxx; ++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 (chf.areas[i] != RC_NULL_AREA)
+ srcReg[i] = regId;
+ }
+ }
+ }
+}
+
+
+static const unsigned short RC_NULL_NEI = 0xffff;
+
+struct rcSweepSpan
+{
+ unsigned short rid; // row id
+ unsigned short id; // region id
+ unsigned short ns; // number samples
+ unsigned short nei; // neighbour id
+};
+
+bool rcBuildRegionsMonotone(rcContext* ctx, rcCompactHeightfield& chf,
+ const int borderSize, const int minRegionArea, const int mergeRegionArea)
+{
+ rcAssert(ctx);
+
+ ctx->startTimer(RC_TIMER_BUILD_REGIONS);
+
+ const int w = chf.width;
+ const int h = chf.height;
+ unsigned short id = 1;
+
+ rcScopedDelete<unsigned short> 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<rcSweepSpan> 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++;
+ }
+
+ 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);
+
+ // Filter out small regions.
+ chf.maxRegions = id;
+ if (!filterSmallRegions(ctx, minRegionArea, mergeRegionArea, chf.maxRegions, chf, srcReg))
+ 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;
+}
+
+bool rcBuildRegions(rcContext* ctx, rcCompactHeightfield& chf,
+ const int borderSize, const int minRegionArea, const int mergeRegionArea)
+{
+ rcAssert(ctx);
+
+ ctx->startTimer(RC_TIMER_BUILD_REGIONS);
+
+ const int w = chf.width;
+ const int h = chf.height;
+
+ rcScopedDelete<unsigned short> buf = (unsigned short*)rcAlloc(sizeof(unsigned short)*chf.spanCount*4, RC_ALLOC_TEMP);
+ if (!buf)
+ {
+ ctx->log(RC_LOG_ERROR, "rcBuildRegions: Out of memory 'tmp' (%d).", chf.spanCount*4);
+ return false;
+ }
+
+ ctx->startTimer(RC_TIMER_BUILD_REGIONS_WATERSHED);
+
+ rcIntArray stack(1024);
+ rcIntArray visited(1024);
+
+ unsigned short* srcReg = buf;
+ unsigned short* srcDist = buf+chf.spanCount;
+ unsigned short* dstReg = buf+chf.spanCount*2;
+ unsigned short* dstDist = buf+chf.spanCount*3;
+
+ memset(srcReg, 0, sizeof(unsigned short)*chf.spanCount);
+ memset(srcDist, 0, sizeof(unsigned short)*chf.spanCount);
+
+ unsigned short regionId = 1;
+ unsigned short level = (chf.maxDistance+1) & ~1;
+
+ // TODO: Figure better formula, expandIters defines how much the
+ // watershed "overflows" and simplifies the regions. Tying it to
+ // agent radius was usually good indication how greedy it could be.
+// const int expandIters = 4 + walkableRadius * 2;
+ const int expandIters = 8;
+
+ // Mark border regions.
+ paintRectRegion(0, borderSize, 0, h, regionId|RC_BORDER_REG, chf, srcReg); regionId++;
+ paintRectRegion(w-borderSize, w, 0, h, regionId|RC_BORDER_REG, chf, srcReg); regionId++;
+ paintRectRegion(0, w, 0, borderSize, regionId|RC_BORDER_REG, chf, srcReg); regionId++;
+ paintRectRegion(0, w, h-borderSize, h, regionId|RC_BORDER_REG, chf, srcReg); regionId++;
+
+ while (level > 0)
+ {
+ level = level >= 2 ? level-2 : 0;
+
+ ctx->startTimer(RC_TIMER_BUILD_REGIONS_EXPAND);
+
+ // Expand current regions until no empty connected cells found.
+ if (expandRegions(expandIters, level, chf, srcReg, srcDist, dstReg, dstDist, stack) != srcReg)
+ {
+ rcSwap(srcReg, dstReg);
+ rcSwap(srcDist, dstDist);
+ }
+
+ ctx->stopTimer(RC_TIMER_BUILD_REGIONS_EXPAND);
+
+ ctx->startTimer(RC_TIMER_BUILD_REGIONS_FLOOD);
+
+ // Mark new regions with IDs.
+ for (int y = 0; y < h; ++y)
+ {
+ for (int x = 0; x < w; ++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 (chf.dist[i] < level || srcReg[i] != 0 || chf.areas[i] == RC_NULL_AREA)
+ continue;
+
+ if (floodRegion(x, y, i, level, regionId, chf, srcReg, srcDist, stack))
+ regionId++;
+ }
+ }
+ }
+
+ ctx->stopTimer(RC_TIMER_BUILD_REGIONS_FLOOD);
+
+ }
+
+ // Expand current regions until no empty connected cells found.
+ if (expandRegions(expandIters*8, 0, chf, srcReg, srcDist, dstReg, dstDist, stack) != srcReg)
+ {
+ rcSwap(srcReg, dstReg);
+ rcSwap(srcDist, dstDist);
+ }
+
+ ctx->stopTimer(RC_TIMER_BUILD_REGIONS_WATERSHED);
+
+ ctx->startTimer(RC_TIMER_BUILD_REGIONS_FILTER);
+
+ // Filter out small regions.
+ chf.maxRegions = regionId;
+ if (!filterSmallRegions(ctx, minRegionArea, mergeRegionArea, chf.maxRegions, chf, srcReg))
+ return false;
+
+ ctx->stopTimer(RC_TIMER_BUILD_REGIONS_FILTER);
+
+ // Write 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/TODO.txt b/dep/recastnavigation/TODO.txt
new file mode 100644
index 00000000000..b911c0e4720
--- /dev/null
+++ b/dep/recastnavigation/TODO.txt
@@ -0,0 +1,20 @@
+TODO/Roadmap
+
+Summer/Autumn 2009
+
+- Off mesh links (jump links)
+- Area annotations
+- Embed extra data per polygon
+- Height conforming navmesh
+
+
+Autumn/Winter 2009/2010
+
+- Detour path following
+- More dynamic example with tile navmesh
+- Faster small tile process
+
+
+More info at http://digestingduck.blogspot.com/2009/07/recast-and-detour-roadmap.html
+
+-
diff --git a/sql/updates/world/2012_09_16_00_world_version.sql b/sql/old/3.3.5a/2012_09_16_00_world_version.sql
index 74a1cf67499..74a1cf67499 100644
--- a/sql/updates/world/2012_09_16_00_world_version.sql
+++ b/sql/old/3.3.5a/2012_09_16_00_world_version.sql
diff --git a/sql/updates/world/2012_09_16_01_world_conditions.sql b/sql/old/3.3.5a/2012_09_16_01_world_conditions.sql
index 64f87262d0e..64f87262d0e 100644
--- a/sql/updates/world/2012_09_16_01_world_conditions.sql
+++ b/sql/old/3.3.5a/2012_09_16_01_world_conditions.sql
diff --git a/sql/updates/world/2012_09_16_01_world_creature_loot_template.sql b/sql/old/3.3.5a/2012_09_16_01_world_creature_loot_template.sql
index 2d87ad3aa16..2d87ad3aa16 100644
--- a/sql/updates/world/2012_09_16_01_world_creature_loot_template.sql
+++ b/sql/old/3.3.5a/2012_09_16_01_world_creature_loot_template.sql
diff --git a/sql/updates/world/2012_09_16_01_world_creature_template_addon.sql b/sql/old/3.3.5a/2012_09_16_01_world_creature_template_addon.sql
index c23a38cd058..c23a38cd058 100644
--- a/sql/updates/world/2012_09_16_01_world_creature_template_addon.sql
+++ b/sql/old/3.3.5a/2012_09_16_01_world_creature_template_addon.sql
diff --git a/sql/updates/world/2012_09_16_02_world_fires_over_skettis.sql b/sql/old/3.3.5a/2012_09_16_02_world_fires_over_skettis.sql
index d50f45bb020..d50f45bb020 100644
--- a/sql/updates/world/2012_09_16_02_world_fires_over_skettis.sql
+++ b/sql/old/3.3.5a/2012_09_16_02_world_fires_over_skettis.sql
diff --git a/sql/updates/world/2012_09_16_03_world_spell_script_names.sql b/sql/old/3.3.5a/2012_09_16_03_world_spell_script_names.sql
index 8b7e6aa92ba..8b7e6aa92ba 100644
--- a/sql/updates/world/2012_09_16_03_world_spell_script_names.sql
+++ b/sql/old/3.3.5a/2012_09_16_03_world_spell_script_names.sql
diff --git a/sql/updates/world/2012_09_17_00_world_spell_bonus_data.sql b/sql/old/3.3.5a/2012_09_17_00_world_spell_bonus_data.sql
index 0d16742393e..0d16742393e 100644
--- a/sql/updates/world/2012_09_17_00_world_spell_bonus_data.sql
+++ b/sql/old/3.3.5a/2012_09_17_00_world_spell_bonus_data.sql
diff --git a/sql/updates/world/2012_09_17_01_world_spell_bonus_data.sql b/sql/old/3.3.5a/2012_09_17_01_world_spell_bonus_data.sql
index f9283233c8e..f9283233c8e 100644
--- a/sql/updates/world/2012_09_17_01_world_spell_bonus_data.sql
+++ b/sql/old/3.3.5a/2012_09_17_01_world_spell_bonus_data.sql
diff --git a/sql/updates/world/2012_09_17_01_world_spell_script_names.sql b/sql/old/3.3.5a/2012_09_17_01_world_spell_script_names.sql
index 255c76e6b70..255c76e6b70 100644
--- a/sql/updates/world/2012_09_17_01_world_spell_script_names.sql
+++ b/sql/old/3.3.5a/2012_09_17_01_world_spell_script_names.sql
diff --git a/sql/updates/world/2012_09_17_02_world_misc.sql b/sql/old/3.3.5a/2012_09_17_02_world_misc.sql
index 378833dfc52..378833dfc52 100644
--- a/sql/updates/world/2012_09_17_02_world_misc.sql
+++ b/sql/old/3.3.5a/2012_09_17_02_world_misc.sql
diff --git a/sql/updates/world/2012_09_18_00_world_creature_template.sql b/sql/old/3.3.5a/2012_09_18_00_world_creature_template.sql
index 5ead9430d60..5ead9430d60 100644
--- a/sql/updates/world/2012_09_18_00_world_creature_template.sql
+++ b/sql/old/3.3.5a/2012_09_18_00_world_creature_template.sql
diff --git a/sql/updates/world/2012_09_18_01_world_creature_template.sql b/sql/old/3.3.5a/2012_09_18_01_world_creature_template.sql
index 4e31e7f6f68..4e31e7f6f68 100644
--- a/sql/updates/world/2012_09_18_01_world_creature_template.sql
+++ b/sql/old/3.3.5a/2012_09_18_01_world_creature_template.sql
diff --git a/sql/updates/world/2012_09_19_00_world_dwarfageddon.sql b/sql/old/3.3.5a/2012_09_19_00_world_dwarfageddon.sql
index 1eda383b67d..1eda383b67d 100644
--- a/sql/updates/world/2012_09_19_00_world_dwarfageddon.sql
+++ b/sql/old/3.3.5a/2012_09_19_00_world_dwarfageddon.sql
diff --git a/sql/updates/world/2012_09_19_00_world_reference_loot_template.sql b/sql/old/3.3.5a/2012_09_19_00_world_reference_loot_template.sql
index 7db30ede52b..7db30ede52b 100644
--- a/sql/updates/world/2012_09_19_00_world_reference_loot_template.sql
+++ b/sql/old/3.3.5a/2012_09_19_00_world_reference_loot_template.sql
diff --git a/sql/updates/world/2012_09_20_00_world_creature_loot_template.sql b/sql/old/3.3.5a/2012_09_20_00_world_creature_loot_template.sql
index 8463b064bf9..8463b064bf9 100644
--- a/sql/updates/world/2012_09_20_00_world_creature_loot_template.sql
+++ b/sql/old/3.3.5a/2012_09_20_00_world_creature_loot_template.sql
diff --git a/sql/updates/world/2012_09_22_01_world_i_was_a_lot_of_things.sql b/sql/old/3.3.5a/2012_09_22_01_world_i_was_a_lot_of_things.sql
index 95fc92daa76..95fc92daa76 100644
--- a/sql/updates/world/2012_09_22_01_world_i_was_a_lot_of_things.sql
+++ b/sql/old/3.3.5a/2012_09_22_01_world_i_was_a_lot_of_things.sql
diff --git a/sql/updates/world/2012_09_22_03_world_game_event.sql b/sql/old/3.3.5a/2012_09_22_03_world_game_event.sql
index 5e05ba03898..5e05ba03898 100644
--- a/sql/updates/world/2012_09_22_03_world_game_event.sql
+++ b/sql/old/3.3.5a/2012_09_22_03_world_game_event.sql
diff --git a/sql/updates/world/2012_09_24_00_world_spell_script_names.sql b/sql/old/3.3.5a/2012_09_24_00_world_spell_script_names.sql
index 611325d4b11..611325d4b11 100644
--- a/sql/updates/world/2012_09_24_00_world_spell_script_names.sql
+++ b/sql/old/3.3.5a/2012_09_24_00_world_spell_script_names.sql
diff --git a/sql/updates/world/2012_09_24_01_world_spell_script_names.sql b/sql/old/3.3.5a/2012_09_24_01_world_spell_script_names.sql
index e14b6833a09..e14b6833a09 100644
--- a/sql/updates/world/2012_09_24_01_world_spell_script_names.sql
+++ b/sql/old/3.3.5a/2012_09_24_01_world_spell_script_names.sql
diff --git a/sql/updates/world/2012_09_24_02_world_misc.sql b/sql/old/3.3.5a/2012_09_24_02_world_misc.sql
index b4cf7fda0a5..b4cf7fda0a5 100644
--- a/sql/updates/world/2012_09_24_02_world_misc.sql
+++ b/sql/old/3.3.5a/2012_09_24_02_world_misc.sql
diff --git a/sql/updates/world/2012_09_24_03_world_misc.sql b/sql/old/3.3.5a/2012_09_24_03_world_misc.sql
index 3bcb40f79c0..3bcb40f79c0 100644
--- a/sql/updates/world/2012_09_24_03_world_misc.sql
+++ b/sql/old/3.3.5a/2012_09_24_03_world_misc.sql
diff --git a/sql/updates/world/2012_09_26_00_world_misc.sql b/sql/old/3.3.5a/2012_09_26_00_world_misc.sql
index d9ac468f4a6..d9ac468f4a6 100644
--- a/sql/updates/world/2012_09_26_00_world_misc.sql
+++ b/sql/old/3.3.5a/2012_09_26_00_world_misc.sql
diff --git a/sql/updates/world/2012_09_26_01_world_spell_bonus_data.sql b/sql/old/3.3.5a/2012_09_26_01_world_spell_bonus_data.sql
index 3aeeb7c6525..3aeeb7c6525 100644
--- a/sql/updates/world/2012_09_26_01_world_spell_bonus_data.sql
+++ b/sql/old/3.3.5a/2012_09_26_01_world_spell_bonus_data.sql
diff --git a/sql/updates/world/2012_09_27_01_world_custodian_of_time.sql b/sql/old/3.3.5a/2012_09_27_01_world_custodian_of_time.sql
index 521c55d507e..521c55d507e 100644
--- a/sql/updates/world/2012_09_27_01_world_custodian_of_time.sql
+++ b/sql/old/3.3.5a/2012_09_27_01_world_custodian_of_time.sql
diff --git a/sql/updates/world/2012_09_28_00_world_creature_model_info.sql b/sql/old/3.3.5a/2012_09_28_00_world_creature_model_info.sql
index 48134d7bbed..48134d7bbed 100644
--- a/sql/updates/world/2012_09_28_00_world_creature_model_info.sql
+++ b/sql/old/3.3.5a/2012_09_28_00_world_creature_model_info.sql
diff --git a/sql/updates/world/2012_09_29_00_world_creature_model_info.sql b/sql/old/3.3.5a/2012_09_29_00_world_creature_model_info.sql
index 0c63a0b2f73..0c63a0b2f73 100644
--- a/sql/updates/world/2012_09_29_00_world_creature_model_info.sql
+++ b/sql/old/3.3.5a/2012_09_29_00_world_creature_model_info.sql
diff --git a/sql/updates/world/2012_09_29_01_world_creature_text.sql b/sql/old/3.3.5a/2012_09_29_01_world_creature_text.sql
index 8fde66d2e6e..8fde66d2e6e 100644
--- a/sql/updates/world/2012_09_29_01_world_creature_text.sql
+++ b/sql/old/3.3.5a/2012_09_29_01_world_creature_text.sql
diff --git a/sql/updates/world/2012_09_30_00_world_creature_text.sql b/sql/old/3.3.5a/2012_09_30_00_world_creature_text.sql
index e0f9f210d7b..e0f9f210d7b 100644
--- a/sql/updates/world/2012_09_30_00_world_creature_text.sql
+++ b/sql/old/3.3.5a/2012_09_30_00_world_creature_text.sql
diff --git a/sql/updates/world/2012_09_30_01_world_player_factionchange_titles.sql b/sql/old/3.3.5a/2012_09_30_01_world_player_factionchange_titles.sql
index 99d7fc871d8..99d7fc871d8 100644
--- a/sql/updates/world/2012_09_30_01_world_player_factionchange_titles.sql
+++ b/sql/old/3.3.5a/2012_09_30_01_world_player_factionchange_titles.sql
diff --git a/sql/updates/world/2012_09_30_02_world_creature_text.sql b/sql/old/3.3.5a/2012_09_30_02_world_creature_text.sql
index 81e77bcd682..81e77bcd682 100644
--- a/sql/updates/world/2012_09_30_02_world_creature_text.sql
+++ b/sql/old/3.3.5a/2012_09_30_02_world_creature_text.sql
diff --git a/sql/updates/world/2012_09_30_03_world_creature_text.sql b/sql/old/3.3.5a/2012_09_30_03_world_creature_text.sql
index a591f8cfb7c..a591f8cfb7c 100644
--- a/sql/updates/world/2012_09_30_03_world_creature_text.sql
+++ b/sql/old/3.3.5a/2012_09_30_03_world_creature_text.sql
diff --git a/sql/updates/world/2012_10_02_00_world_oculus.sql b/sql/old/3.3.5a/2012_10_02_00_world_oculus.sql
index 8c9ea1bcb60..8c9ea1bcb60 100644
--- a/sql/updates/world/2012_10_02_00_world_oculus.sql
+++ b/sql/old/3.3.5a/2012_10_02_00_world_oculus.sql
diff --git a/sql/updates/world/2012_10_04_00_world_spelldifficulty_dbc.sql b/sql/old/3.3.5a/2012_10_04_00_world_spelldifficulty_dbc.sql
index 6587e35bf9b..6587e35bf9b 100644
--- a/sql/updates/world/2012_10_04_00_world_spelldifficulty_dbc.sql
+++ b/sql/old/3.3.5a/2012_10_04_00_world_spelldifficulty_dbc.sql
diff --git a/sql/updates/world/2012_10_04_01_world_script_texts.sql b/sql/old/3.3.5a/2012_10_04_01_world_script_texts.sql
index 8281d16794e..8281d16794e 100644
--- a/sql/updates/world/2012_10_04_01_world_script_texts.sql
+++ b/sql/old/3.3.5a/2012_10_04_01_world_script_texts.sql
diff --git a/sql/updates/world/2012_10_05_00_world_misc.sql b/sql/old/3.3.5a/2012_10_05_00_world_misc.sql
index f715b9491ef..f715b9491ef 100644
--- a/sql/updates/world/2012_10_05_00_world_misc.sql
+++ b/sql/old/3.3.5a/2012_10_05_00_world_misc.sql
diff --git a/sql/updates/world/2012_10_05_01_world_spell_script_names.sql b/sql/old/3.3.5a/2012_10_05_01_world_spell_script_names.sql
index c7269b981ec..c7269b981ec 100644
--- a/sql/updates/world/2012_10_05_01_world_spell_script_names.sql
+++ b/sql/old/3.3.5a/2012_10_05_01_world_spell_script_names.sql
diff --git a/sql/updates/world/2012_10_06_00_world_spell_difficulty.sql b/sql/old/3.3.5a/2012_10_06_00_world_spell_difficulty.sql
index 1dd1a468990..1dd1a468990 100644
--- a/sql/updates/world/2012_10_06_00_world_spell_difficulty.sql
+++ b/sql/old/3.3.5a/2012_10_06_00_world_spell_difficulty.sql
diff --git a/sql/updates/world/2012_10_06_00_world_spell_script_names.sql b/sql/old/3.3.5a/2012_10_06_00_world_spell_script_names.sql
index 4fa3b922d62..4fa3b922d62 100644
--- a/sql/updates/world/2012_10_06_00_world_spell_script_names.sql
+++ b/sql/old/3.3.5a/2012_10_06_00_world_spell_script_names.sql
diff --git a/sql/updates/world/2012_10_06_01_world_spell_dbc.sql b/sql/old/3.3.5a/2012_10_06_01_world_spell_dbc.sql
index 071d83582ac..071d83582ac 100644
--- a/sql/updates/world/2012_10_06_01_world_spell_dbc.sql
+++ b/sql/old/3.3.5a/2012_10_06_01_world_spell_dbc.sql
diff --git a/sql/updates/world/2012_10_06_02_world_misc_templates.sql b/sql/old/3.3.5a/2012_10_06_02_world_misc_templates.sql
index 5ec006c310d..5ec006c310d 100644
--- a/sql/updates/world/2012_10_06_02_world_misc_templates.sql
+++ b/sql/old/3.3.5a/2012_10_06_02_world_misc_templates.sql
diff --git a/sql/updates/world/2012_10_06_03_world_creature_text.sql b/sql/old/3.3.5a/2012_10_06_03_world_creature_text.sql
index 6716b12cea9..6716b12cea9 100644
--- a/sql/updates/world/2012_10_06_03_world_creature_text.sql
+++ b/sql/old/3.3.5a/2012_10_06_03_world_creature_text.sql
diff --git a/sql/updates/world/2012_10_06_04_world_misc_spawns.sql b/sql/old/3.3.5a/2012_10_06_04_world_misc_spawns.sql
index e53302d2c2f..e53302d2c2f 100644
--- a/sql/updates/world/2012_10_06_04_world_misc_spawns.sql
+++ b/sql/old/3.3.5a/2012_10_06_04_world_misc_spawns.sql
diff --git a/sql/updates/world/2012_10_06_05_world_spell_script_names.sql b/sql/old/3.3.5a/2012_10_06_05_world_spell_script_names.sql
index 354a359e45b..354a359e45b 100644
--- a/sql/updates/world/2012_10_06_05_world_spell_script_names.sql
+++ b/sql/old/3.3.5a/2012_10_06_05_world_spell_script_names.sql
diff --git a/sql/updates/world/2012_10_07_00_world_creature_loot_template.sql b/sql/old/3.3.5a/2012_10_07_00_world_creature_loot_template.sql
index 2829725c76c..2829725c76c 100644
--- a/sql/updates/world/2012_10_07_00_world_creature_loot_template.sql
+++ b/sql/old/3.3.5a/2012_10_07_00_world_creature_loot_template.sql
diff --git a/sql/updates/world/2012_10_07_01_world_spell_proc_event.sql b/sql/old/3.3.5a/2012_10_07_01_world_spell_proc_event.sql
index 810cd90290c..810cd90290c 100644
--- a/sql/updates/world/2012_10_07_01_world_spell_proc_event.sql
+++ b/sql/old/3.3.5a/2012_10_07_01_world_spell_proc_event.sql
diff --git a/sql/updates/characters/2012_10_09_00_character_glyphs.sql b/sql/old/3.3.5a/2012_10_09_00_character_glyphs.sql
index b90fa7d7682..b90fa7d7682 100644
--- a/sql/updates/characters/2012_10_09_00_character_glyphs.sql
+++ b/sql/old/3.3.5a/2012_10_09_00_character_glyphs.sql
diff --git a/sql/updates/world/2012_10_09_00_world_spell_groups.sql b/sql/old/3.3.5a/2012_10_09_00_world_spell_groups.sql
index 71f58c6322e..71f58c6322e 100644
--- a/sql/updates/world/2012_10_09_00_world_spell_groups.sql
+++ b/sql/old/3.3.5a/2012_10_09_00_world_spell_groups.sql
diff --git a/sql/updates/world/2012_10_09_01_world_spell_groups.sql b/sql/old/3.3.5a/2012_10_09_01_world_spell_groups.sql
index 75c57d0f77f..75c57d0f77f 100644
--- a/sql/updates/world/2012_10_09_01_world_spell_groups.sql
+++ b/sql/old/3.3.5a/2012_10_09_01_world_spell_groups.sql
diff --git a/sql/updates/world/2012_10_09_02_world_spell_groups.sql b/sql/old/3.3.5a/2012_10_09_02_world_spell_groups.sql
index 8e4864bc039..8e4864bc039 100644
--- a/sql/updates/world/2012_10_09_02_world_spell_groups.sql
+++ b/sql/old/3.3.5a/2012_10_09_02_world_spell_groups.sql
diff --git a/sql/updates/world/2012_10_10_00_world_battleground_template.sql b/sql/old/3.3.5a/2012_10_10_00_world_battleground_template.sql
index 2dcafc51161..2dcafc51161 100644
--- a/sql/updates/world/2012_10_10_00_world_battleground_template.sql
+++ b/sql/old/3.3.5a/2012_10_10_00_world_battleground_template.sql
diff --git a/sql/updates/world/2012_10_11_00_world_gameobject.sql b/sql/old/3.3.5a/2012_10_11_00_world_gameobject.sql
index e58d60590cb..e58d60590cb 100644
--- a/sql/updates/world/2012_10_11_00_world_gameobject.sql
+++ b/sql/old/3.3.5a/2012_10_11_00_world_gameobject.sql
diff --git a/sql/updates/world/2012_10_13_00_world_creature_template.sql b/sql/old/3.3.5a/2012_10_13_00_world_creature_template.sql
index 7dc7a463f3f..7dc7a463f3f 100644
--- a/sql/updates/world/2012_10_13_00_world_creature_template.sql
+++ b/sql/old/3.3.5a/2012_10_13_00_world_creature_template.sql
diff --git a/sql/updates/world/2012_10_14_00_world_creature.sql b/sql/old/3.3.5a/2012_10_14_00_world_creature.sql
index dd650f37aef..dd650f37aef 100644
--- a/sql/updates/world/2012_10_14_00_world_creature.sql
+++ b/sql/old/3.3.5a/2012_10_14_00_world_creature.sql
diff --git a/sql/updates/world/2012_10_16_00_world_spell_area.sql b/sql/old/3.3.5a/2012_10_16_00_world_spell_area.sql
index 3dbc29d077f..3dbc29d077f 100644
--- a/sql/updates/world/2012_10_16_00_world_spell_area.sql
+++ b/sql/old/3.3.5a/2012_10_16_00_world_spell_area.sql
diff --git a/sql/updates/characters/2012_10_17_00_character_gm_tickets.sql b/sql/old/3.3.5a/2012_10_17_00_character_gm_tickets.sql
index affb23f836c..affb23f836c 100644
--- a/sql/updates/characters/2012_10_17_00_character_gm_tickets.sql
+++ b/sql/old/3.3.5a/2012_10_17_00_character_gm_tickets.sql
diff --git a/sql/updates/world/2012_10_20_00_world_transports.sql b/sql/old/3.3.5a/2012_10_20_00_world_transports.sql
index 8cc2bae4b47..8cc2bae4b47 100644
--- a/sql/updates/world/2012_10_20_00_world_transports.sql
+++ b/sql/old/3.3.5a/2012_10_20_00_world_transports.sql
diff --git a/sql/updates/world/2012_10_20_01_world_spell_script_names.sql b/sql/old/3.3.5a/2012_10_20_01_world_spell_script_names.sql
index 814f1b40e65..814f1b40e65 100644
--- a/sql/updates/world/2012_10_20_01_world_spell_script_names.sql
+++ b/sql/old/3.3.5a/2012_10_20_01_world_spell_script_names.sql
diff --git a/sql/updates/world/2012_10_20_02_world_creature_text.sql b/sql/old/3.3.5a/2012_10_20_02_world_creature_text.sql
index bb153c6b2f6..bb153c6b2f6 100644
--- a/sql/updates/world/2012_10_20_02_world_creature_text.sql
+++ b/sql/old/3.3.5a/2012_10_20_02_world_creature_text.sql
diff --git a/sql/updates/world/2012_10_21_00_world_conditions.sql b/sql/old/3.3.5a/2012_10_21_00_world_conditions.sql
index 62161a932f9..62161a932f9 100644
--- a/sql/updates/world/2012_10_21_00_world_conditions.sql
+++ b/sql/old/3.3.5a/2012_10_21_00_world_conditions.sql
diff --git a/sql/updates/world/2012_10_21_01_world_misc.sql b/sql/old/3.3.5a/2012_10_21_01_world_misc.sql
index fe74f8a7a80..fe74f8a7a80 100644
--- a/sql/updates/world/2012_10_21_01_world_misc.sql
+++ b/sql/old/3.3.5a/2012_10_21_01_world_misc.sql
diff --git a/sql/updates/world/2012_10_23_00_world_trinity_string.sql b/sql/old/3.3.5a/2012_10_23_00_world_trinity_string.sql
index 60318667fbc..60318667fbc 100644
--- a/sql/updates/world/2012_10_23_00_world_trinity_string.sql
+++ b/sql/old/3.3.5a/2012_10_23_00_world_trinity_string.sql
diff --git a/sql/updates/world/2012_10_23_01_world_command.sql b/sql/old/3.3.5a/2012_10_23_01_world_command.sql
index b46882c5949..b46882c5949 100644
--- a/sql/updates/world/2012_10_23_01_world_command.sql
+++ b/sql/old/3.3.5a/2012_10_23_01_world_command.sql
diff --git a/sql/updates/world/2012_10_25_00_world_childrens_week.sql b/sql/old/3.3.5a/2012_10_25_00_world_childrens_week.sql
index f10cf92b409..f10cf92b409 100644
--- a/sql/updates/world/2012_10_25_00_world_childrens_week.sql
+++ b/sql/old/3.3.5a/2012_10_25_00_world_childrens_week.sql
diff --git a/sql/updates/world/2012_10_26_00_world_sai.sql b/sql/old/3.3.5a/2012_10_26_00_world_sai.sql
index 8e4c2fa0922..8e4c2fa0922 100644
--- a/sql/updates/world/2012_10_26_00_world_sai.sql
+++ b/sql/old/3.3.5a/2012_10_26_00_world_sai.sql
diff --git a/sql/updates/world/2012_10_27_00_world_creature.sql b/sql/old/3.3.5a/2012_10_27_00_world_creature.sql
index c00c450aeca..c00c450aeca 100644
--- a/sql/updates/world/2012_10_27_00_world_creature.sql
+++ b/sql/old/3.3.5a/2012_10_27_00_world_creature.sql
diff --git a/sql/updates/world/2012_10_27_01_world_creature_loot_template.sql b/sql/old/3.3.5a/2012_10_27_01_world_creature_loot_template.sql
index 3367dbf0fa8..3367dbf0fa8 100644
--- a/sql/updates/world/2012_10_27_01_world_creature_loot_template.sql
+++ b/sql/old/3.3.5a/2012_10_27_01_world_creature_loot_template.sql
diff --git a/sql/updates/world/2012_10_29_00_world_conditions.sql b/sql/old/3.3.5a/2012_10_29_00_world_conditions.sql
index d1fac5361fb..d1fac5361fb 100644
--- a/sql/updates/world/2012_10_29_00_world_conditions.sql
+++ b/sql/old/3.3.5a/2012_10_29_00_world_conditions.sql
diff --git a/sql/updates/characters/2012_11_02_00_character_misc.sql b/sql/old/3.3.5a/2012_11_02_00_character_misc.sql
index 9cbbd423bb8..9cbbd423bb8 100644
--- a/sql/updates/characters/2012_11_02_00_character_misc.sql
+++ b/sql/old/3.3.5a/2012_11_02_00_character_misc.sql
diff --git a/sql/updates/world/2012_11_03_00_world_creature_loot_template.sql b/sql/old/3.3.5a/2012_11_03_00_world_creature_loot_template.sql
index 0e5c39b796d..0e5c39b796d 100644
--- a/sql/updates/world/2012_11_03_00_world_creature_loot_template.sql
+++ b/sql/old/3.3.5a/2012_11_03_00_world_creature_loot_template.sql
diff --git a/sql/updates/world/2012_11_07_00_world_misc.sql b/sql/old/3.3.5a/2012_11_07_00_world_misc.sql
index db7c8f66fb1..db7c8f66fb1 100644
--- a/sql/updates/world/2012_11_07_00_world_misc.sql
+++ b/sql/old/3.3.5a/2012_11_07_00_world_misc.sql
diff --git a/sql/updates/world/2012_11_11_00_world_sai.sql b/sql/old/3.3.5a/2012_11_11_00_world_sai.sql
index b5eedd86898..b5eedd86898 100644
--- a/sql/updates/world/2012_11_11_00_world_sai.sql
+++ b/sql/old/3.3.5a/2012_11_11_00_world_sai.sql
diff --git a/sql/updates/world/2012_11_11_01_world_quest_template.sql b/sql/old/3.3.5a/2012_11_11_01_world_quest_template.sql
index 9abf2ea3cd1..9abf2ea3cd1 100644
--- a/sql/updates/world/2012_11_11_01_world_quest_template.sql
+++ b/sql/old/3.3.5a/2012_11_11_01_world_quest_template.sql
diff --git a/sql/updates/world/2012_11_11_02_world_gossip_menu.sql b/sql/old/3.3.5a/2012_11_11_02_world_gossip_menu.sql
index 59d1a10b337..59d1a10b337 100644
--- a/sql/updates/world/2012_11_11_02_world_gossip_menu.sql
+++ b/sql/old/3.3.5a/2012_11_11_02_world_gossip_menu.sql
diff --git a/sql/updates/world/2012_11_11_03_world_gossip_menu.sql b/sql/old/3.3.5a/2012_11_11_03_world_gossip_menu.sql
index 4f250597388..4f250597388 100644
--- a/sql/updates/world/2012_11_11_03_world_gossip_menu.sql
+++ b/sql/old/3.3.5a/2012_11_11_03_world_gossip_menu.sql
diff --git a/sql/updates/world/2012_11_12_00_world_quest_template.sql b/sql/old/3.3.5a/2012_11_12_00_world_quest_template.sql
index ee2206146b3..ee2206146b3 100644
--- a/sql/updates/world/2012_11_12_00_world_quest_template.sql
+++ b/sql/old/3.3.5a/2012_11_12_00_world_quest_template.sql
diff --git a/sql/updates/world/2012_11_12_01_world_misc.sql b/sql/old/3.3.5a/2012_11_12_01_world_misc.sql
index 7a5e81c4b1a..7a5e81c4b1a 100644
--- a/sql/updates/world/2012_11_12_01_world_misc.sql
+++ b/sql/old/3.3.5a/2012_11_12_01_world_misc.sql
diff --git a/sql/updates/world/2012_11_12_02_world_quest_template.sql b/sql/old/3.3.5a/2012_11_12_02_world_quest_template.sql
index 8f988bfa4ab..8f988bfa4ab 100644
--- a/sql/updates/world/2012_11_12_02_world_quest_template.sql
+++ b/sql/old/3.3.5a/2012_11_12_02_world_quest_template.sql
diff --git a/sql/updates/world/2012_11_12_03_world_misc.sql b/sql/old/3.3.5a/2012_11_12_03_world_misc.sql
index 20c91974bbb..20c91974bbb 100644
--- a/sql/updates/world/2012_11_12_03_world_misc.sql
+++ b/sql/old/3.3.5a/2012_11_12_03_world_misc.sql
diff --git a/sql/updates/world/2012_11_13_00_world_waypoints.sql b/sql/old/3.3.5a/2012_11_13_00_world_waypoints.sql
index 64e16cb2d0e..64e16cb2d0e 100644
--- a/sql/updates/world/2012_11_13_00_world_waypoints.sql
+++ b/sql/old/3.3.5a/2012_11_13_00_world_waypoints.sql
diff --git a/sql/updates/world/2012_11_13_01_world_misc.sql b/sql/old/3.3.5a/2012_11_13_01_world_misc.sql
index 97e3cd8b499..97e3cd8b499 100644
--- a/sql/updates/world/2012_11_13_01_world_misc.sql
+++ b/sql/old/3.3.5a/2012_11_13_01_world_misc.sql
diff --git a/sql/updates/world/2012_11_13_02_world_creature.sql b/sql/old/3.3.5a/2012_11_13_02_world_creature.sql
index 19f278ca5c6..19f278ca5c6 100644
--- a/sql/updates/world/2012_11_13_02_world_creature.sql
+++ b/sql/old/3.3.5a/2012_11_13_02_world_creature.sql
diff --git a/sql/updates/world/2012_11_13_03_world_gameevent.sql b/sql/old/3.3.5a/2012_11_13_03_world_gameevent.sql
index fbb24b863f2..fbb24b863f2 100644
--- a/sql/updates/world/2012_11_13_03_world_gameevent.sql
+++ b/sql/old/3.3.5a/2012_11_13_03_world_gameevent.sql
diff --git a/sql/updates/world/2012_11_13_04_world_gameeventquest.sql b/sql/old/3.3.5a/2012_11_13_04_world_gameeventquest.sql
index cce654859e0..cce654859e0 100644
--- a/sql/updates/world/2012_11_13_04_world_gameeventquest.sql
+++ b/sql/old/3.3.5a/2012_11_13_04_world_gameeventquest.sql
diff --git a/sql/updates/world/2012_11_13_05_world_player_factionchange_items.sql b/sql/old/3.3.5a/2012_11_13_05_world_player_factionchange_items.sql
index 17b9616624a..17b9616624a 100644
--- a/sql/updates/world/2012_11_13_05_world_player_factionchange_items.sql
+++ b/sql/old/3.3.5a/2012_11_13_05_world_player_factionchange_items.sql
diff --git a/sql/updates/world/2012_11_13_06_world_trinity_string.sql b/sql/old/3.3.5a/2012_11_13_06_world_trinity_string.sql
index 2834a8f900b..2834a8f900b 100644
--- a/sql/updates/world/2012_11_13_06_world_trinity_string.sql
+++ b/sql/old/3.3.5a/2012_11_13_06_world_trinity_string.sql
diff --git a/sql/updates/world/2012_11_14_00_world_sai.sql b/sql/old/3.3.5a/2012_11_14_00_world_sai.sql
index 98467fe4f3a..98467fe4f3a 100644
--- a/sql/updates/world/2012_11_14_00_world_sai.sql
+++ b/sql/old/3.3.5a/2012_11_14_00_world_sai.sql
diff --git a/sql/updates/world/2012_11_14_00_world_various_fixes.sql b/sql/old/3.3.5a/2012_11_14_00_world_various_fixes.sql
index 01655b674b1..01655b674b1 100644
--- a/sql/updates/world/2012_11_14_00_world_various_fixes.sql
+++ b/sql/old/3.3.5a/2012_11_14_00_world_various_fixes.sql
diff --git a/sql/updates/world/2012_11_14_02_world_battleground_template.sql b/sql/old/3.3.5a/2012_11_14_02_world_battleground_template.sql
index b69216ea380..b69216ea380 100644
--- a/sql/updates/world/2012_11_14_02_world_battleground_template.sql
+++ b/sql/old/3.3.5a/2012_11_14_02_world_battleground_template.sql
diff --git a/sql/updates/world/2012_11_16_00_world_utgarde.sql b/sql/old/3.3.5a/2012_11_16_00_world_utgarde.sql
index 1948796a77b..1948796a77b 100644
--- a/sql/updates/world/2012_11_16_00_world_utgarde.sql
+++ b/sql/old/3.3.5a/2012_11_16_00_world_utgarde.sql
diff --git a/sql/updates/world/2012_11_16_01_world_utgarde.sql b/sql/old/3.3.5a/2012_11_16_01_world_utgarde.sql
index 30a64b02e3a..30a64b02e3a 100644
--- a/sql/updates/world/2012_11_16_01_world_utgarde.sql
+++ b/sql/old/3.3.5a/2012_11_16_01_world_utgarde.sql
diff --git a/sql/updates/world/2012_11_16_02_world_creature_ai_summons.sql b/sql/old/3.3.5a/2012_11_16_02_world_creature_ai_summons.sql
index 7d2caf76e33..7d2caf76e33 100644
--- a/sql/updates/world/2012_11_16_02_world_creature_ai_summons.sql
+++ b/sql/old/3.3.5a/2012_11_16_02_world_creature_ai_summons.sql
diff --git a/sql/updates/world/2012_11_17_00_world_various_fixes.sql b/sql/old/3.3.5a/2012_11_17_00_world_various_fixes.sql
index 981d378217f..981d378217f 100644
--- a/sql/updates/world/2012_11_17_00_world_various_fixes.sql
+++ b/sql/old/3.3.5a/2012_11_17_00_world_various_fixes.sql
diff --git a/sql/updates/characters/2012_11_18_00_character_calendar.sql b/sql/old/3.3.5a/2012_11_18_00_character_calendar.sql
index a3e7c352788..a3e7c352788 100644
--- a/sql/updates/characters/2012_11_18_00_character_calendar.sql
+++ b/sql/old/3.3.5a/2012_11_18_00_character_calendar.sql
diff --git a/sql/updates/world/2012_11_18_00_world_ormorok.sql b/sql/old/3.3.5a/2012_11_18_00_world_ormorok.sql
index 0ec8d13fd64..0ec8d13fd64 100644
--- a/sql/updates/world/2012_11_18_00_world_ormorok.sql
+++ b/sql/old/3.3.5a/2012_11_18_00_world_ormorok.sql
diff --git a/sql/updates/world/2012_11_18_01_world_creature_text.sql b/sql/old/3.3.5a/2012_11_18_01_world_creature_text.sql
index 5d8da97f1f0..5d8da97f1f0 100644
--- a/sql/updates/world/2012_11_18_01_world_creature_text.sql
+++ b/sql/old/3.3.5a/2012_11_18_01_world_creature_text.sql
diff --git a/sql/updates/world/2012_11_18_01_world_gameobject.sql b/sql/old/3.3.5a/2012_11_18_01_world_gameobject.sql
index d82f9a07e7e..d82f9a07e7e 100644
--- a/sql/updates/world/2012_11_18_01_world_gameobject.sql
+++ b/sql/old/3.3.5a/2012_11_18_01_world_gameobject.sql
diff --git a/sql/updates/world/2012_11_18_02_world_toc.sql b/sql/old/3.3.5a/2012_11_18_02_world_toc.sql
index 9262aceed7a..9262aceed7a 100644
--- a/sql/updates/world/2012_11_18_02_world_toc.sql
+++ b/sql/old/3.3.5a/2012_11_18_02_world_toc.sql
diff --git a/sql/updates/world/2012_11_19_00_world_various_fixes.sql b/sql/old/3.3.5a/2012_11_19_00_world_various_fixes.sql
index ca78611fc0b..ca78611fc0b 100644
--- a/sql/updates/world/2012_11_19_00_world_various_fixes.sql
+++ b/sql/old/3.3.5a/2012_11_19_00_world_various_fixes.sql
diff --git a/sql/updates/world/2012_11_19_01_world_pilgrims_bounty.sql b/sql/old/3.3.5a/2012_11_19_01_world_pilgrims_bounty.sql
index 56cda092be6..56cda092be6 100644
--- a/sql/updates/world/2012_11_19_01_world_pilgrims_bounty.sql
+++ b/sql/old/3.3.5a/2012_11_19_01_world_pilgrims_bounty.sql
diff --git a/sql/updates/world/2012_11_19_02_world_misc.sql b/sql/old/3.3.5a/2012_11_19_02_world_misc.sql
index 1f792b8da41..1f792b8da41 100644
--- a/sql/updates/world/2012_11_19_02_world_misc.sql
+++ b/sql/old/3.3.5a/2012_11_19_02_world_misc.sql
diff --git a/sql/updates/world/2012_11_19_03_world_ysera.sql b/sql/old/3.3.5a/2012_11_19_03_world_ysera.sql
index 24a729f96dd..24a729f96dd 100644
--- a/sql/updates/world/2012_11_19_03_world_ysera.sql
+++ b/sql/old/3.3.5a/2012_11_19_03_world_ysera.sql
diff --git a/sql/updates/world/2012_11_20_00_world_creature_text.sql b/sql/old/3.3.5a/2012_11_20_00_world_creature_text.sql
index 5b89db507d8..5b89db507d8 100644
--- a/sql/updates/world/2012_11_20_00_world_creature_text.sql
+++ b/sql/old/3.3.5a/2012_11_20_00_world_creature_text.sql
diff --git a/sql/updates/world/2012_11_21_00_world_sai.sql b/sql/old/3.3.5a/2012_11_21_00_world_sai.sql
index 9cdd314bb8f..9cdd314bb8f 100644
--- a/sql/updates/world/2012_11_21_00_world_sai.sql
+++ b/sql/old/3.3.5a/2012_11_21_00_world_sai.sql
diff --git a/sql/updates/world/2012_11_24_00_world_conditions.sql b/sql/old/3.3.5a/2012_11_24_00_world_conditions.sql
index 1a5e5963c8e..1a5e5963c8e 100644
--- a/sql/updates/world/2012_11_24_00_world_conditions.sql
+++ b/sql/old/3.3.5a/2012_11_24_00_world_conditions.sql
diff --git a/sql/updates/world/2012_11_24_00_world_creature_text.sql b/sql/old/3.3.5a/2012_11_24_00_world_creature_text.sql
index 60e0f9c3aa1..60e0f9c3aa1 100644
--- a/sql/updates/world/2012_11_24_00_world_creature_text.sql
+++ b/sql/old/3.3.5a/2012_11_24_00_world_creature_text.sql
diff --git a/sql/updates/world/2012_11_24_01_world_quest_start_scripts.sql b/sql/old/3.3.5a/2012_11_24_01_world_quest_start_scripts.sql
index 0f0506206a2..0f0506206a2 100644
--- a/sql/updates/world/2012_11_24_01_world_quest_start_scripts.sql
+++ b/sql/old/3.3.5a/2012_11_24_01_world_quest_start_scripts.sql
diff --git a/sql/updates/world/2012_11_24_02_world_creature_text.sql b/sql/old/3.3.5a/2012_11_24_02_world_creature_text.sql
index 8797039d967..8797039d967 100644
--- a/sql/updates/world/2012_11_24_02_world_creature_text.sql
+++ b/sql/old/3.3.5a/2012_11_24_02_world_creature_text.sql
diff --git a/sql/updates/world/2012_11_24_02_world_gossip_menu_option.sql b/sql/old/3.3.5a/2012_11_24_02_world_gossip_menu_option.sql
index e6f9ab551d3..e6f9ab551d3 100644
--- a/sql/updates/world/2012_11_24_02_world_gossip_menu_option.sql
+++ b/sql/old/3.3.5a/2012_11_24_02_world_gossip_menu_option.sql
diff --git a/sql/updates/world/2012_11_24_03_world_various_fixes.sql b/sql/old/3.3.5a/2012_11_24_03_world_various_fixes.sql
index dfa2d277564..dfa2d277564 100644
--- a/sql/updates/world/2012_11_24_03_world_various_fixes.sql
+++ b/sql/old/3.3.5a/2012_11_24_03_world_various_fixes.sql
diff --git a/sql/updates/world/2012_11_24_04_world_sai.sql b/sql/old/3.3.5a/2012_11_24_04_world_sai.sql
index 76cf4c18963..76cf4c18963 100644
--- a/sql/updates/world/2012_11_24_04_world_sai.sql
+++ b/sql/old/3.3.5a/2012_11_24_04_world_sai.sql
diff --git a/sql/updates/world/2012_11_25_00_world_various_fixes.sql b/sql/old/3.3.5a/2012_11_25_00_world_various_fixes.sql
index 3aabc755dde..3aabc755dde 100644
--- a/sql/updates/world/2012_11_25_00_world_various_fixes.sql
+++ b/sql/old/3.3.5a/2012_11_25_00_world_various_fixes.sql
diff --git a/sql/updates/world/2012_11_25_01_world_various_fixes.sql b/sql/old/3.3.5a/2012_11_25_01_world_various_fixes.sql
index 6114451174a..6114451174a 100644
--- a/sql/updates/world/2012_11_25_01_world_various_fixes.sql
+++ b/sql/old/3.3.5a/2012_11_25_01_world_various_fixes.sql
diff --git a/sql/updates/world/2012_11_26_02_world_conditions.sql b/sql/old/3.3.5a/2012_11_26_02_world_conditions.sql
index 861e999236d..861e999236d 100644
--- a/sql/updates/world/2012_11_26_02_world_conditions.sql
+++ b/sql/old/3.3.5a/2012_11_26_02_world_conditions.sql
diff --git a/sql/updates/world/2012_11_26_03_world_misc.sql b/sql/old/3.3.5a/2012_11_26_03_world_misc.sql
index 5a6140e6bf3..5a6140e6bf3 100644
--- a/sql/updates/world/2012_11_26_03_world_misc.sql
+++ b/sql/old/3.3.5a/2012_11_26_03_world_misc.sql
diff --git a/sql/updates/world/2012_11_27_00_world_misc.sql b/sql/old/3.3.5a/2012_11_27_00_world_misc.sql
index 8c815a856fd..8c815a856fd 100644
--- a/sql/updates/world/2012_11_27_00_world_misc.sql
+++ b/sql/old/3.3.5a/2012_11_27_00_world_misc.sql
diff --git a/sql/updates/world/2012_11_27_01_world_achievement_criteria_data.sql b/sql/old/3.3.5a/2012_11_27_01_world_achievement_criteria_data.sql
index 65cfced17f3..65cfced17f3 100644
--- a/sql/updates/world/2012_11_27_01_world_achievement_criteria_data.sql
+++ b/sql/old/3.3.5a/2012_11_27_01_world_achievement_criteria_data.sql
diff --git a/sql/updates/world/2012_11_30_00_world_spell_script_names.sql b/sql/old/3.3.5a/2012_11_30_00_world_spell_script_names.sql
index cedee2fe51d..cedee2fe51d 100644
--- a/sql/updates/world/2012_11_30_00_world_spell_script_names.sql
+++ b/sql/old/3.3.5a/2012_11_30_00_world_spell_script_names.sql
diff --git a/sql/updates/world/2012_12_01_00_world_assembly_of_iron.sql b/sql/old/3.3.5a/2012_12_01_00_world_assembly_of_iron.sql
index 090780d2bd4..090780d2bd4 100644
--- a/sql/updates/world/2012_12_01_00_world_assembly_of_iron.sql
+++ b/sql/old/3.3.5a/2012_12_01_00_world_assembly_of_iron.sql
diff --git a/sql/updates/world/2012_12_01_01_world_misc.sql b/sql/old/3.3.5a/2012_12_01_01_world_misc.sql
index 7523484d179..7523484d179 100644
--- a/sql/updates/world/2012_12_01_01_world_misc.sql
+++ b/sql/old/3.3.5a/2012_12_01_01_world_misc.sql
diff --git a/sql/updates/world/2012_12_01_02_world_creature_text.sql b/sql/old/3.3.5a/2012_12_01_02_world_creature_text.sql
index 6d4b50b9e88..6d4b50b9e88 100644
--- a/sql/updates/world/2012_12_01_02_world_creature_text.sql
+++ b/sql/old/3.3.5a/2012_12_01_02_world_creature_text.sql
diff --git a/sql/updates/world/2012_12_01_03_world_ulduar_creature_text.sql b/sql/old/3.3.5a/2012_12_01_03_world_ulduar_creature_text.sql
index 82d93d7b7ae..82d93d7b7ae 100644
--- a/sql/updates/world/2012_12_01_03_world_ulduar_creature_text.sql
+++ b/sql/old/3.3.5a/2012_12_01_03_world_ulduar_creature_text.sql
diff --git a/sql/updates/world/2012_12_01_04_world_spelldifficulty_dbc.sql b/sql/old/3.3.5a/2012_12_01_04_world_spelldifficulty_dbc.sql
index fe5df812c71..fe5df812c71 100644
--- a/sql/updates/world/2012_12_01_04_world_spelldifficulty_dbc.sql
+++ b/sql/old/3.3.5a/2012_12_01_04_world_spelldifficulty_dbc.sql
diff --git a/sql/updates/world/2012_12_01_05_world_creature_template.sql b/sql/old/3.3.5a/2012_12_01_05_world_creature_template.sql
index aa2c6ebb898..aa2c6ebb898 100644
--- a/sql/updates/world/2012_12_01_05_world_creature_template.sql
+++ b/sql/old/3.3.5a/2012_12_01_05_world_creature_template.sql
diff --git a/sql/updates/world/2012_12_02_00_world_game_object.sql b/sql/old/3.3.5a/2012_12_02_00_world_game_object.sql
index 3205159a703..3205159a703 100644
--- a/sql/updates/world/2012_12_02_00_world_game_object.sql
+++ b/sql/old/3.3.5a/2012_12_02_00_world_game_object.sql
diff --git a/sql/updates/world/2012_12_02_01_world_creature_text.sql b/sql/old/3.3.5a/2012_12_02_01_world_creature_text.sql
index bd98fc8527c..bd98fc8527c 100644
--- a/sql/updates/world/2012_12_02_01_world_creature_text.sql
+++ b/sql/old/3.3.5a/2012_12_02_01_world_creature_text.sql
diff --git a/sql/updates/world/2012_12_02_02_world_waypoint_data.sql b/sql/old/3.3.5a/2012_12_02_02_world_waypoint_data.sql
index 6413ad00b62..6413ad00b62 100644
--- a/sql/updates/world/2012_12_02_02_world_waypoint_data.sql
+++ b/sql/old/3.3.5a/2012_12_02_02_world_waypoint_data.sql
diff --git a/sql/updates/characters/2012_12_03_00_character_character_queststatus_monthly.sql b/sql/old/3.3.5a/2012_12_03_00_character_character_queststatus_monthly.sql
index 256bb1f7ad6..256bb1f7ad6 100644
--- a/sql/updates/characters/2012_12_03_00_character_character_queststatus_monthly.sql
+++ b/sql/old/3.3.5a/2012_12_03_00_character_character_queststatus_monthly.sql
diff --git a/sql/updates/world/2012_12_04_00_world_creature_text.sql b/sql/old/3.3.5a/2012_12_04_00_world_creature_text.sql
index 654ed11de4f..654ed11de4f 100644
--- a/sql/updates/world/2012_12_04_00_world_creature_text.sql
+++ b/sql/old/3.3.5a/2012_12_04_00_world_creature_text.sql
diff --git a/sql/updates/world/2012_12_04_01_world_quest_start_scripts.sql b/sql/old/3.3.5a/2012_12_04_01_world_quest_start_scripts.sql
index f2ac40c7880..f2ac40c7880 100644
--- a/sql/updates/world/2012_12_04_01_world_quest_start_scripts.sql
+++ b/sql/old/3.3.5a/2012_12_04_01_world_quest_start_scripts.sql
diff --git a/sql/updates/world/2012_12_04_02_world_reputation_reward_rate.sql b/sql/old/3.3.5a/2012_12_04_02_world_reputation_reward_rate.sql
index a6b58583532..a6b58583532 100644
--- a/sql/updates/world/2012_12_04_02_world_reputation_reward_rate.sql
+++ b/sql/old/3.3.5a/2012_12_04_02_world_reputation_reward_rate.sql
diff --git a/sql/updates/world/2012_12_04_03_world_reputation_reward_rate.sql b/sql/old/3.3.5a/2012_12_04_03_world_reputation_reward_rate.sql
index 9f8341ea464..9f8341ea464 100644
--- a/sql/updates/world/2012_12_04_03_world_reputation_reward_rate.sql
+++ b/sql/old/3.3.5a/2012_12_04_03_world_reputation_reward_rate.sql
diff --git a/sql/updates/world/2012_12_04_04_world_creature_text.sql b/sql/old/3.3.5a/2012_12_04_04_world_creature_text.sql
index 1cf52a1417f..1cf52a1417f 100644
--- a/sql/updates/world/2012_12_04_04_world_creature_text.sql
+++ b/sql/old/3.3.5a/2012_12_04_04_world_creature_text.sql
diff --git a/sql/updates/world/2012_12_04_05_world_spell_script_names.sql b/sql/old/3.3.5a/2012_12_04_05_world_spell_script_names.sql
index f325ac731d4..f325ac731d4 100644
--- a/sql/updates/world/2012_12_04_05_world_spell_script_names.sql
+++ b/sql/old/3.3.5a/2012_12_04_05_world_spell_script_names.sql
diff --git a/sql/updates/world/2012_12_04_06_world_spell_creature_text.sql b/sql/old/3.3.5a/2012_12_04_06_world_spell_creature_text.sql
index 04f10fa494f..04f10fa494f 100644
--- a/sql/updates/world/2012_12_04_06_world_spell_creature_text.sql
+++ b/sql/old/3.3.5a/2012_12_04_06_world_spell_creature_text.sql
diff --git a/sql/updates/world/2012_12_05_00_world_creature_text.sql b/sql/old/3.3.5a/2012_12_05_00_world_creature_text.sql
index b3b382e46e2..b3b382e46e2 100644
--- a/sql/updates/world/2012_12_05_00_world_creature_text.sql
+++ b/sql/old/3.3.5a/2012_12_05_00_world_creature_text.sql
diff --git a/sql/updates/world/2012_12_06_00_world_creature_text.sql b/sql/old/3.3.5a/2012_12_06_00_world_creature_text.sql
index c0fec8e041e..c0fec8e041e 100644
--- a/sql/updates/world/2012_12_06_00_world_creature_text.sql
+++ b/sql/old/3.3.5a/2012_12_06_00_world_creature_text.sql
diff --git a/sql/updates/world/2012_12_07_00_world_creature_text.sql b/sql/old/3.3.5a/2012_12_07_00_world_creature_text.sql
index 6763834d668..6763834d668 100644
--- a/sql/updates/world/2012_12_07_00_world_creature_text.sql
+++ b/sql/old/3.3.5a/2012_12_07_00_world_creature_text.sql
diff --git a/sql/updates/world/2012_12_07_00_world_script_texts.sql b/sql/old/3.3.5a/2012_12_07_00_world_script_texts.sql
index b1362aac796..b1362aac796 100644
--- a/sql/updates/world/2012_12_07_00_world_script_texts.sql
+++ b/sql/old/3.3.5a/2012_12_07_00_world_script_texts.sql
diff --git a/sql/updates/world/2012_12_07_01_world_misc.sql b/sql/old/3.3.5a/2012_12_07_01_world_misc.sql
index aaee366bfce..aaee366bfce 100644
--- a/sql/updates/world/2012_12_07_01_world_misc.sql
+++ b/sql/old/3.3.5a/2012_12_07_01_world_misc.sql
diff --git a/sql/updates/world/2012_12_07_02_world_tracker_pitcrawler.sql b/sql/old/3.3.5a/2012_12_07_02_world_tracker_pitcrawler.sql
index 345a61cb7f5..345a61cb7f5 100644
--- a/sql/updates/world/2012_12_07_02_world_tracker_pitcrawler.sql
+++ b/sql/old/3.3.5a/2012_12_07_02_world_tracker_pitcrawler.sql
diff --git a/sql/updates/world/2012_12_07_03_world_tracker_trista.sql b/sql/old/3.3.5a/2012_12_07_03_world_tracker_trista.sql
index b717f4e53e8..b717f4e53e8 100644
--- a/sql/updates/world/2012_12_07_03_world_tracker_trista.sql
+++ b/sql/old/3.3.5a/2012_12_07_03_world_tracker_trista.sql
diff --git a/sql/updates/world/2012_12_07_04_world_misc.sql b/sql/old/3.3.5a/2012_12_07_04_world_misc.sql
index fb4c466d91c..fb4c466d91c 100644
--- a/sql/updates/world/2012_12_07_04_world_misc.sql
+++ b/sql/old/3.3.5a/2012_12_07_04_world_misc.sql
diff --git a/sql/updates/world/2012_12_07_05_world_creature_text.sql b/sql/old/3.3.5a/2012_12_07_05_world_creature_text.sql
index 8e3e2cd737f..8e3e2cd737f 100644
--- a/sql/updates/world/2012_12_07_05_world_creature_text.sql
+++ b/sql/old/3.3.5a/2012_12_07_05_world_creature_text.sql
diff --git a/sql/updates/world/2012_12_07_06_world_sai.sql b/sql/old/3.3.5a/2012_12_07_06_world_sai.sql
index d0d1be35766..d0d1be35766 100644
--- a/sql/updates/world/2012_12_07_06_world_sai.sql
+++ b/sql/old/3.3.5a/2012_12_07_06_world_sai.sql
diff --git a/sql/updates/world/2012_12_07_07_world_sai.sql b/sql/old/3.3.5a/2012_12_07_07_world_sai.sql
index d9acb9c310c..d9acb9c310c 100644
--- a/sql/updates/world/2012_12_07_07_world_sai.sql
+++ b/sql/old/3.3.5a/2012_12_07_07_world_sai.sql
diff --git a/sql/updates/world/2012_12_07_08_world_misc.sql b/sql/old/3.3.5a/2012_12_07_08_world_misc.sql
index a696ba401cc..a696ba401cc 100644
--- a/sql/updates/world/2012_12_07_08_world_misc.sql
+++ b/sql/old/3.3.5a/2012_12_07_08_world_misc.sql
diff --git a/sql/updates/world/2012_12_07_09_world_db_script_string.sql b/sql/old/3.3.5a/2012_12_07_09_world_db_script_string.sql
index 4880b84dff0..4880b84dff0 100644
--- a/sql/updates/world/2012_12_07_09_world_db_script_string.sql
+++ b/sql/old/3.3.5a/2012_12_07_09_world_db_script_string.sql
diff --git a/sql/updates/world/2012_12_08_00_world_creature_text.sql b/sql/old/3.3.5a/2012_12_08_00_world_creature_text.sql
index 5a5cd579e2b..5a5cd579e2b 100644
--- a/sql/updates/world/2012_12_08_00_world_creature_text.sql
+++ b/sql/old/3.3.5a/2012_12_08_00_world_creature_text.sql
diff --git a/sql/updates/world/2012_12_08_01_world_sai.sql b/sql/old/3.3.5a/2012_12_08_01_world_sai.sql
index 2880b01d07a..2880b01d07a 100644
--- a/sql/updates/world/2012_12_08_01_world_sai.sql
+++ b/sql/old/3.3.5a/2012_12_08_01_world_sai.sql
diff --git a/sql/updates/world/2012_12_09_00_world_conditions.sql b/sql/old/3.3.5a/2012_12_09_00_world_conditions.sql
index dbb3dac7fc3..dbb3dac7fc3 100644
--- a/sql/updates/world/2012_12_09_00_world_conditions.sql
+++ b/sql/old/3.3.5a/2012_12_09_00_world_conditions.sql
diff --git a/sql/updates/world/2012_12_09_00_world_creature_text.sql b/sql/old/3.3.5a/2012_12_09_00_world_creature_text.sql
index 6d850191938..6d850191938 100644
--- a/sql/updates/world/2012_12_09_00_world_creature_text.sql
+++ b/sql/old/3.3.5a/2012_12_09_00_world_creature_text.sql
diff --git a/sql/updates/world/2012_12_09_01_world_misc.sql b/sql/old/3.3.5a/2012_12_09_01_world_misc.sql
index 7c8bdfbb1c5..7c8bdfbb1c5 100644
--- a/sql/updates/world/2012_12_09_01_world_misc.sql
+++ b/sql/old/3.3.5a/2012_12_09_01_world_misc.sql
diff --git a/sql/updates/world/2012_12_09_02_world_quest_template.sql b/sql/old/3.3.5a/2012_12_09_02_world_quest_template.sql
index 52d0202d2a1..52d0202d2a1 100644
--- a/sql/updates/world/2012_12_09_02_world_quest_template.sql
+++ b/sql/old/3.3.5a/2012_12_09_02_world_quest_template.sql
diff --git a/sql/updates/world/2012_12_10_00_world_smart_scripts.sql b/sql/old/3.3.5a/2012_12_10_00_world_smart_scripts.sql
index ed0ee9bf764..ed0ee9bf764 100644
--- a/sql/updates/world/2012_12_10_00_world_smart_scripts.sql
+++ b/sql/old/3.3.5a/2012_12_10_00_world_smart_scripts.sql
diff --git a/sql/updates/world/2012_12_10_01_world_spell_script_names.sql b/sql/old/3.3.5a/2012_12_10_01_world_spell_script_names.sql
index bc3684011e8..bc3684011e8 100644
--- a/sql/updates/world/2012_12_10_01_world_spell_script_names.sql
+++ b/sql/old/3.3.5a/2012_12_10_01_world_spell_script_names.sql
diff --git a/sql/updates/world/2012_12_10_02_world_spawns_waypoints.sql b/sql/old/3.3.5a/2012_12_10_02_world_spawns_waypoints.sql
index 68613f3aaa9..68613f3aaa9 100644
--- a/sql/updates/world/2012_12_10_02_world_spawns_waypoints.sql
+++ b/sql/old/3.3.5a/2012_12_10_02_world_spawns_waypoints.sql
diff --git a/sql/updates/world/2012_12_13_00_world.sql b/sql/old/3.3.5a/2012_12_13_00_world.sql
index ab27796f933..ab27796f933 100644
--- a/sql/updates/world/2012_12_13_00_world.sql
+++ b/sql/old/3.3.5a/2012_12_13_00_world.sql
diff --git a/sql/updates/world/2012_12_14_00_world_conditions.sql b/sql/old/3.3.5a/2012_12_14_00_world_conditions.sql
index 6b6fbfd02c0..6b6fbfd02c0 100644
--- a/sql/updates/world/2012_12_14_00_world_conditions.sql
+++ b/sql/old/3.3.5a/2012_12_14_00_world_conditions.sql
diff --git a/sql/updates/world/2012_12_14_01_world_waypoint_data.sql b/sql/old/3.3.5a/2012_12_14_01_world_waypoint_data.sql
index e3589c53340..e3589c53340 100644
--- a/sql/updates/world/2012_12_14_01_world_waypoint_data.sql
+++ b/sql/old/3.3.5a/2012_12_14_01_world_waypoint_data.sql
diff --git a/sql/updates/world/2012_12_14_02_world_misc.sql b/sql/old/3.3.5a/2012_12_14_02_world_misc.sql
index 29aa47909e1..29aa47909e1 100644
--- a/sql/updates/world/2012_12_14_02_world_misc.sql
+++ b/sql/old/3.3.5a/2012_12_14_02_world_misc.sql
diff --git a/sql/updates/world/2012_12_14_03_world_npc_spellclick_spells.sql b/sql/old/3.3.5a/2012_12_14_03_world_npc_spellclick_spells.sql
index 2669abbc562..2669abbc562 100644
--- a/sql/updates/world/2012_12_14_03_world_npc_spellclick_spells.sql
+++ b/sql/old/3.3.5a/2012_12_14_03_world_npc_spellclick_spells.sql
diff --git a/sql/updates/world/2012_12_14_04_world_misc.sql b/sql/old/3.3.5a/2012_12_14_04_world_misc.sql
index 2cd99cfb12a..2cd99cfb12a 100644
--- a/sql/updates/world/2012_12_14_04_world_misc.sql
+++ b/sql/old/3.3.5a/2012_12_14_04_world_misc.sql
diff --git a/sql/updates/world/2012_12_14_05_world_spell_script_names.sql b/sql/old/3.3.5a/2012_12_14_05_world_spell_script_names.sql
index 6d6ab651365..6d6ab651365 100644
--- a/sql/updates/world/2012_12_14_05_world_spell_script_names.sql
+++ b/sql/old/3.3.5a/2012_12_14_05_world_spell_script_names.sql
diff --git a/sql/updates/world/2012_12_15_00_world_waypoint_data.sql b/sql/old/3.3.5a/2012_12_15_00_world_waypoint_data.sql
index c63180ebe5e..c63180ebe5e 100644
--- a/sql/updates/world/2012_12_15_00_world_waypoint_data.sql
+++ b/sql/old/3.3.5a/2012_12_15_00_world_waypoint_data.sql
diff --git a/sql/updates/world/2012_12_15_01_world_sai.sql b/sql/old/3.3.5a/2012_12_15_01_world_sai.sql
index 6d43bedc446..6d43bedc446 100644
--- a/sql/updates/world/2012_12_15_01_world_sai.sql
+++ b/sql/old/3.3.5a/2012_12_15_01_world_sai.sql
diff --git a/sql/updates/world/2012_12_15_02_world_sai.sql b/sql/old/3.3.5a/2012_12_15_02_world_sai.sql
index bf092bfb88d..bf092bfb88d 100644
--- a/sql/updates/world/2012_12_15_02_world_sai.sql
+++ b/sql/old/3.3.5a/2012_12_15_02_world_sai.sql
diff --git a/sql/updates/world/2012_12_15_03_world_quest_end_scripts.sql b/sql/old/3.3.5a/2012_12_15_03_world_quest_end_scripts.sql
index 91838bda93d..91838bda93d 100644
--- a/sql/updates/world/2012_12_15_03_world_quest_end_scripts.sql
+++ b/sql/old/3.3.5a/2012_12_15_03_world_quest_end_scripts.sql
diff --git a/sql/updates/world/2012_12_16_00_world_creature_addon.sql b/sql/old/3.3.5a/2012_12_16_00_world_creature_addon.sql
index a79bb407321..a79bb407321 100644
--- a/sql/updates/world/2012_12_16_00_world_creature_addon.sql
+++ b/sql/old/3.3.5a/2012_12_16_00_world_creature_addon.sql
diff --git a/sql/updates/world/2012_12_16_01_world_npc_vendor.sql b/sql/old/3.3.5a/2012_12_16_01_world_npc_vendor.sql
index 0e5ed38c88d..0e5ed38c88d 100644
--- a/sql/updates/world/2012_12_16_01_world_npc_vendor.sql
+++ b/sql/old/3.3.5a/2012_12_16_01_world_npc_vendor.sql
diff --git a/sql/updates/world/2012_12_16_02_world_creature_text.sql b/sql/old/3.3.5a/2012_12_16_02_world_creature_text.sql
index f2d605469f8..f2d605469f8 100644
--- a/sql/updates/world/2012_12_16_02_world_creature_text.sql
+++ b/sql/old/3.3.5a/2012_12_16_02_world_creature_text.sql
diff --git a/sql/updates/world/2012_12_16_03_world_sai.sql b/sql/old/3.3.5a/2012_12_16_03_world_sai.sql
index 40b1e32dbeb..40b1e32dbeb 100644
--- a/sql/updates/world/2012_12_16_03_world_sai.sql
+++ b/sql/old/3.3.5a/2012_12_16_03_world_sai.sql
diff --git a/sql/updates/world/2012_12_17_00_world_creature_template.sql b/sql/old/3.3.5a/2012_12_17_00_world_creature_template.sql
index dfe9132e7a6..dfe9132e7a6 100644
--- a/sql/updates/world/2012_12_17_00_world_creature_template.sql
+++ b/sql/old/3.3.5a/2012_12_17_00_world_creature_template.sql
diff --git a/sql/updates/world/2012_12_17_00_world_song_of_wind_and_water.sql b/sql/old/3.3.5a/2012_12_17_00_world_song_of_wind_and_water.sql
index 52c80334c0c..52c80334c0c 100644
--- a/sql/updates/world/2012_12_17_00_world_song_of_wind_and_water.sql
+++ b/sql/old/3.3.5a/2012_12_17_00_world_song_of_wind_and_water.sql
diff --git a/sql/updates/world/2012_12_17_00_world_spell_linked_spell.sql b/sql/old/3.3.5a/2012_12_17_00_world_spell_linked_spell.sql
index a2cfc7073d0..a2cfc7073d0 100644
--- a/sql/updates/world/2012_12_17_00_world_spell_linked_spell.sql
+++ b/sql/old/3.3.5a/2012_12_17_00_world_spell_linked_spell.sql
diff --git a/sql/updates/characters/2012_12_18_00_character_worldstates.sql b/sql/old/3.3.5a/2012_12_18_00_character_worldstates.sql
index 98fabde6776..98fabde6776 100644
--- a/sql/updates/characters/2012_12_18_00_character_worldstates.sql
+++ b/sql/old/3.3.5a/2012_12_18_00_character_worldstates.sql
diff --git a/sql/updates/world/2012_12_18_00_world_creature_text.sql b/sql/old/3.3.5a/2012_12_18_00_world_creature_text.sql
index 1cf9211343f..1cf9211343f 100644
--- a/sql/updates/world/2012_12_18_00_world_creature_text.sql
+++ b/sql/old/3.3.5a/2012_12_18_00_world_creature_text.sql
diff --git a/sql/updates/world/2012_12_18_00_world_waking_the_sleeper.sql b/sql/old/3.3.5a/2012_12_18_00_world_waking_the_sleeper.sql
index 4f8a203100d..4f8a203100d 100644
--- a/sql/updates/world/2012_12_18_00_world_waking_the_sleeper.sql
+++ b/sql/old/3.3.5a/2012_12_18_00_world_waking_the_sleeper.sql
diff --git a/sql/updates/world/2012_12_18_01_world_creature_text.sql b/sql/old/3.3.5a/2012_12_18_01_world_creature_text.sql
index 24779f67fc7..24779f67fc7 100644
--- a/sql/updates/world/2012_12_18_01_world_creature_text.sql
+++ b/sql/old/3.3.5a/2012_12_18_01_world_creature_text.sql
diff --git a/sql/updates/world/2012_12_18_02_world_misc.sql b/sql/old/3.3.5a/2012_12_18_02_world_misc.sql
index 5df6ef8eb95..5df6ef8eb95 100644
--- a/sql/updates/world/2012_12_18_02_world_misc.sql
+++ b/sql/old/3.3.5a/2012_12_18_02_world_misc.sql
diff --git a/sql/updates/world/2012_12_18_02_world_sai.sql b/sql/old/3.3.5a/2012_12_18_02_world_sai.sql
index 0843285550f..0843285550f 100644
--- a/sql/updates/world/2012_12_18_02_world_sai.sql
+++ b/sql/old/3.3.5a/2012_12_18_02_world_sai.sql
diff --git a/sql/updates/world/2012_12_19_00_world_sai.sql b/sql/old/3.3.5a/2012_12_19_00_world_sai.sql
index e11891cdfbd..e11891cdfbd 100644
--- a/sql/updates/world/2012_12_19_00_world_sai.sql
+++ b/sql/old/3.3.5a/2012_12_19_00_world_sai.sql
diff --git a/sql/updates/world/2012_12_19_01_world_viscidus.sql b/sql/old/3.3.5a/2012_12_19_01_world_viscidus.sql
index 16d33b80b8f..16d33b80b8f 100644
--- a/sql/updates/world/2012_12_19_01_world_viscidus.sql
+++ b/sql/old/3.3.5a/2012_12_19_01_world_viscidus.sql
diff --git a/sql/updates/world/2012_12_19_02_world_sai.sql b/sql/old/3.3.5a/2012_12_19_02_world_sai.sql
index c6367e1a8ca..c6367e1a8ca 100644
--- a/sql/updates/world/2012_12_19_02_world_sai.sql
+++ b/sql/old/3.3.5a/2012_12_19_02_world_sai.sql
diff --git a/sql/updates/characters/2012_12_20_00_characters_create_item_loot.sql b/sql/old/3.3.5a/2012_12_20_00_characters_create_item_loot.sql
index 3c1529e8bdf..3c1529e8bdf 100644
--- a/sql/updates/characters/2012_12_20_00_characters_create_item_loot.sql
+++ b/sql/old/3.3.5a/2012_12_20_00_characters_create_item_loot.sql
diff --git a/sql/updates/world/2012_12_20_00_world_conditions.sql b/sql/old/3.3.5a/2012_12_20_00_world_conditions.sql
index 53fc5733e53..53fc5733e53 100644
--- a/sql/updates/world/2012_12_20_00_world_conditions.sql
+++ b/sql/old/3.3.5a/2012_12_20_00_world_conditions.sql
diff --git a/sql/updates/world/2012_12_20_01_world_conditions.sql b/sql/old/3.3.5a/2012_12_20_01_world_conditions.sql
index 9d1851305a3..9d1851305a3 100644
--- a/sql/updates/world/2012_12_20_01_world_conditions.sql
+++ b/sql/old/3.3.5a/2012_12_20_01_world_conditions.sql
diff --git a/sql/updates/world/2012_12_20_02_world_misc.sql b/sql/old/3.3.5a/2012_12_20_02_world_misc.sql
index c04965d8258..c04965d8258 100644
--- a/sql/updates/world/2012_12_20_02_world_misc.sql
+++ b/sql/old/3.3.5a/2012_12_20_02_world_misc.sql
diff --git a/sql/updates/world/2012_12_20_03_world_conditions.sql b/sql/old/3.3.5a/2012_12_20_03_world_conditions.sql
index f07b18ed2e4..f07b18ed2e4 100644
--- a/sql/updates/world/2012_12_20_03_world_conditions.sql
+++ b/sql/old/3.3.5a/2012_12_20_03_world_conditions.sql
diff --git a/sql/updates/world/2012_12_21_00_world_conditions.sql b/sql/old/3.3.5a/2012_12_21_00_world_conditions.sql
index eddf541eaf3..eddf541eaf3 100644
--- a/sql/updates/world/2012_12_21_00_world_conditions.sql
+++ b/sql/old/3.3.5a/2012_12_21_00_world_conditions.sql
diff --git a/sql/updates/world/2012_12_21_01_world_conditions.sql b/sql/old/3.3.5a/2012_12_21_01_world_conditions.sql
index fdc5becd963..fdc5becd963 100644
--- a/sql/updates/world/2012_12_21_01_world_conditions.sql
+++ b/sql/old/3.3.5a/2012_12_21_01_world_conditions.sql
diff --git a/sql/updates/world/2012_12_22_01_world_sai.sql b/sql/old/3.3.5a/2012_12_22_01_world_sai.sql
index e2a61426679..e2a61426679 100644
--- a/sql/updates/world/2012_12_22_01_world_sai.sql
+++ b/sql/old/3.3.5a/2012_12_22_01_world_sai.sql
diff --git a/sql/updates/world/2012_12_22_02_world_the_crusaders_pinnacle.sql b/sql/old/3.3.5a/2012_12_22_02_world_the_crusaders_pinnacle.sql
index 5debd41a160..5debd41a160 100644
--- a/sql/updates/world/2012_12_22_02_world_the_crusaders_pinnacle.sql
+++ b/sql/old/3.3.5a/2012_12_22_02_world_the_crusaders_pinnacle.sql
diff --git a/sql/updates/world/2012_12_23_00_world_creature_text.sql b/sql/old/3.3.5a/2012_12_23_00_world_creature_text.sql
index 37b4d66b3fe..37b4d66b3fe 100644
--- a/sql/updates/world/2012_12_23_00_world_creature_text.sql
+++ b/sql/old/3.3.5a/2012_12_23_00_world_creature_text.sql
diff --git a/sql/updates/world/2012_12_23_01_world_creature_text.sql b/sql/old/3.3.5a/2012_12_23_01_world_creature_text.sql
index b663c5269e9..b663c5269e9 100644
--- a/sql/updates/world/2012_12_23_01_world_creature_text.sql
+++ b/sql/old/3.3.5a/2012_12_23_01_world_creature_text.sql
diff --git a/sql/updates/world/2012_12_23_02_world_trinity_string.sql b/sql/old/3.3.5a/2012_12_23_02_world_trinity_string.sql
index ac99945d5a5..ac99945d5a5 100644
--- a/sql/updates/world/2012_12_23_02_world_trinity_string.sql
+++ b/sql/old/3.3.5a/2012_12_23_02_world_trinity_string.sql
diff --git a/sql/updates/world/2012_12_23_03_world_misc.sql b/sql/old/3.3.5a/2012_12_23_03_world_misc.sql
index c9a42df10dd..c9a42df10dd 100644
--- a/sql/updates/world/2012_12_23_03_world_misc.sql
+++ b/sql/old/3.3.5a/2012_12_23_03_world_misc.sql
diff --git a/sql/updates/world/2012_12_24_00_world_defending_wyrmrest_temple.sql b/sql/old/3.3.5a/2012_12_24_00_world_defending_wyrmrest_temple.sql
index edf1f887d1c..edf1f887d1c 100644
--- a/sql/updates/world/2012_12_24_00_world_defending_wyrmrest_temple.sql
+++ b/sql/old/3.3.5a/2012_12_24_00_world_defending_wyrmrest_temple.sql
diff --git a/sql/updates/world/2012_12_24_01_world_sai.sql b/sql/old/3.3.5a/2012_12_24_01_world_sai.sql
index d151ee4aa2c..d151ee4aa2c 100644
--- a/sql/updates/world/2012_12_24_01_world_sai.sql
+++ b/sql/old/3.3.5a/2012_12_24_01_world_sai.sql
diff --git a/sql/updates/world/2012_12_24_02_world_sai.sql b/sql/old/3.3.5a/2012_12_24_02_world_sai.sql
index 03d0a9ef5c6..03d0a9ef5c6 100644
--- a/sql/updates/world/2012_12_24_02_world_sai.sql
+++ b/sql/old/3.3.5a/2012_12_24_02_world_sai.sql
diff --git a/sql/updates/world/2012_12_24_03_world_sai.sql b/sql/old/3.3.5a/2012_12_24_03_world_sai.sql
index f89f3c968aa..f89f3c968aa 100644
--- a/sql/updates/world/2012_12_24_03_world_sai.sql
+++ b/sql/old/3.3.5a/2012_12_24_03_world_sai.sql
diff --git a/sql/updates/world/2012_12_24_04_world_creature.sql b/sql/old/3.3.5a/2012_12_24_04_world_creature.sql
index 42f69d97a9e..42f69d97a9e 100644
--- a/sql/updates/world/2012_12_24_04_world_creature.sql
+++ b/sql/old/3.3.5a/2012_12_24_04_world_creature.sql
diff --git a/sql/updates/world/2012_12_24_05_world_object.sql b/sql/old/3.3.5a/2012_12_24_05_world_object.sql
index 2b9947a6bd5..2b9947a6bd5 100644
--- a/sql/updates/world/2012_12_24_05_world_object.sql
+++ b/sql/old/3.3.5a/2012_12_24_05_world_object.sql
diff --git a/sql/updates/world/2012_12_24_06_world_reputation.sql b/sql/old/3.3.5a/2012_12_24_06_world_reputation.sql
index 9320d76365d..9320d76365d 100644
--- a/sql/updates/world/2012_12_24_06_world_reputation.sql
+++ b/sql/old/3.3.5a/2012_12_24_06_world_reputation.sql
diff --git a/sql/updates/world/2012_12_24_07_world_sai.sql b/sql/old/3.3.5a/2012_12_24_07_world_sai.sql
index 3cce37295fd..3cce37295fd 100644
--- a/sql/updates/world/2012_12_24_07_world_sai.sql
+++ b/sql/old/3.3.5a/2012_12_24_07_world_sai.sql
diff --git a/sql/updates/world/2012_12_24_08_world_conditions.sql b/sql/old/3.3.5a/2012_12_24_08_world_conditions.sql
index ad641203453..ad641203453 100644
--- a/sql/updates/world/2012_12_24_08_world_conditions.sql
+++ b/sql/old/3.3.5a/2012_12_24_08_world_conditions.sql
diff --git a/sql/updates/world/2012_12_24_09_world_sai.sql b/sql/old/3.3.5a/2012_12_24_09_world_sai.sql
index d75dd937598..d75dd937598 100644
--- a/sql/updates/world/2012_12_24_09_world_sai.sql
+++ b/sql/old/3.3.5a/2012_12_24_09_world_sai.sql
diff --git a/sql/updates/world/2012_12_25_00_world_sai.sql b/sql/old/3.3.5a/2012_12_25_00_world_sai.sql
index 33363d33573..33363d33573 100644
--- a/sql/updates/world/2012_12_25_00_world_sai.sql
+++ b/sql/old/3.3.5a/2012_12_25_00_world_sai.sql
diff --git a/sql/updates/world/2012_12_25_01_world_sai.sql b/sql/old/3.3.5a/2012_12_25_01_world_sai.sql
index cad0ce2aec3..cad0ce2aec3 100644
--- a/sql/updates/world/2012_12_25_01_world_sai.sql
+++ b/sql/old/3.3.5a/2012_12_25_01_world_sai.sql
diff --git a/sql/updates/world/2012_12_25_02_world_sai.sql b/sql/old/3.3.5a/2012_12_25_02_world_sai.sql
index 35ac4ebb97b..35ac4ebb97b 100644
--- a/sql/updates/world/2012_12_25_02_world_sai.sql
+++ b/sql/old/3.3.5a/2012_12_25_02_world_sai.sql
diff --git a/sql/updates/world/2012_12_25_03_world_sai.sql b/sql/old/3.3.5a/2012_12_25_03_world_sai.sql
index 2a7553aabb3..2a7553aabb3 100644
--- a/sql/updates/world/2012_12_25_03_world_sai.sql
+++ b/sql/old/3.3.5a/2012_12_25_03_world_sai.sql
diff --git a/sql/updates/world/2012_12_25_04_world_sai.sql b/sql/old/3.3.5a/2012_12_25_04_world_sai.sql
index 46b2c4c2fd1..46b2c4c2fd1 100644
--- a/sql/updates/world/2012_12_25_04_world_sai.sql
+++ b/sql/old/3.3.5a/2012_12_25_04_world_sai.sql
diff --git a/sql/updates/world/2012_12_26_01_world_creature_text.sql b/sql/old/3.3.5a/2012_12_26_01_world_creature_text.sql
index ae4170ae5a8..ae4170ae5a8 100644
--- a/sql/updates/world/2012_12_26_01_world_creature_text.sql
+++ b/sql/old/3.3.5a/2012_12_26_01_world_creature_text.sql
diff --git a/sql/updates/world/2012_12_26_02_world_sai.sql b/sql/old/3.3.5a/2012_12_26_02_world_sai.sql
index b88b4044edb..b88b4044edb 100644
--- a/sql/updates/world/2012_12_26_02_world_sai.sql
+++ b/sql/old/3.3.5a/2012_12_26_02_world_sai.sql
diff --git a/sql/updates/world/2012_12_26_03_world_sai.sql b/sql/old/3.3.5a/2012_12_26_03_world_sai.sql
index 2e9ed5090e0..2e9ed5090e0 100644
--- a/sql/updates/world/2012_12_26_03_world_sai.sql
+++ b/sql/old/3.3.5a/2012_12_26_03_world_sai.sql
diff --git a/sql/updates/world/2012_12_26_04_world_sai.sql b/sql/old/3.3.5a/2012_12_26_04_world_sai.sql
index a29c8a22fdc..a29c8a22fdc 100644
--- a/sql/updates/world/2012_12_26_04_world_sai.sql
+++ b/sql/old/3.3.5a/2012_12_26_04_world_sai.sql
diff --git a/sql/updates/world/2012_12_26_05_world_sai.sql b/sql/old/3.3.5a/2012_12_26_05_world_sai.sql
index 143f1e9bdb0..143f1e9bdb0 100644
--- a/sql/updates/world/2012_12_26_05_world_sai.sql
+++ b/sql/old/3.3.5a/2012_12_26_05_world_sai.sql
diff --git a/sql/updates/world/2012_12_26_06_world_sai.sql b/sql/old/3.3.5a/2012_12_26_06_world_sai.sql
index 184282fe907..184282fe907 100644
--- a/sql/updates/world/2012_12_26_06_world_sai.sql
+++ b/sql/old/3.3.5a/2012_12_26_06_world_sai.sql
diff --git a/sql/updates/world/2012_12_26_07_world_game_event.sql b/sql/old/3.3.5a/2012_12_26_07_world_game_event.sql
index 21dcf12e1c4..21dcf12e1c4 100644
--- a/sql/updates/world/2012_12_26_07_world_game_event.sql
+++ b/sql/old/3.3.5a/2012_12_26_07_world_game_event.sql
diff --git a/sql/updates/world/2012_12_26_08_world_sai.sql b/sql/old/3.3.5a/2012_12_26_08_world_sai.sql
index 8fde794613e..8fde794613e 100644
--- a/sql/updates/world/2012_12_26_08_world_sai.sql
+++ b/sql/old/3.3.5a/2012_12_26_08_world_sai.sql
diff --git a/sql/updates/world/2012_12_26_09_world_sai.sql b/sql/old/3.3.5a/2012_12_26_09_world_sai.sql
index fe81c15fb3a..fe81c15fb3a 100644
--- a/sql/updates/world/2012_12_26_09_world_sai.sql
+++ b/sql/old/3.3.5a/2012_12_26_09_world_sai.sql
diff --git a/sql/updates/world/2012_12_26_10_world_sai.sql b/sql/old/3.3.5a/2012_12_26_10_world_sai.sql
index b6b925d8255..b6b925d8255 100644
--- a/sql/updates/world/2012_12_26_10_world_sai.sql
+++ b/sql/old/3.3.5a/2012_12_26_10_world_sai.sql
diff --git a/sql/updates/world/2012_12_26_11_world_sai.sql b/sql/old/3.3.5a/2012_12_26_11_world_sai.sql
index abbdfb88c24..abbdfb88c24 100644
--- a/sql/updates/world/2012_12_26_11_world_sai.sql
+++ b/sql/old/3.3.5a/2012_12_26_11_world_sai.sql
diff --git a/sql/updates/world/2012_12_26_12_world_quest_end_scripts.sql b/sql/old/3.3.5a/2012_12_26_12_world_quest_end_scripts.sql
index 501bb796557..501bb796557 100644
--- a/sql/updates/world/2012_12_26_12_world_quest_end_scripts.sql
+++ b/sql/old/3.3.5a/2012_12_26_12_world_quest_end_scripts.sql
diff --git a/sql/updates/world/2012_12_26_13_world_sai.sql b/sql/old/3.3.5a/2012_12_26_13_world_sai.sql
index d36affe8eb3..d36affe8eb3 100644
--- a/sql/updates/world/2012_12_26_13_world_sai.sql
+++ b/sql/old/3.3.5a/2012_12_26_13_world_sai.sql
diff --git a/sql/updates/world/2012_12_26_14_world_sai.sql b/sql/old/3.3.5a/2012_12_26_14_world_sai.sql
index 2929d590637..2929d590637 100644
--- a/sql/updates/world/2012_12_26_14_world_sai.sql
+++ b/sql/old/3.3.5a/2012_12_26_14_world_sai.sql
diff --git a/sql/updates/world/2012_12_26_15_world_sai.sql b/sql/old/3.3.5a/2012_12_26_15_world_sai.sql
index a40f73bffc8..a40f73bffc8 100644
--- a/sql/updates/world/2012_12_26_15_world_sai.sql
+++ b/sql/old/3.3.5a/2012_12_26_15_world_sai.sql
diff --git a/sql/updates/world/2012_12_26_16_world_sai.sql b/sql/old/3.3.5a/2012_12_26_16_world_sai.sql
index 39a246c9ccd..39a246c9ccd 100644
--- a/sql/updates/world/2012_12_26_16_world_sai.sql
+++ b/sql/old/3.3.5a/2012_12_26_16_world_sai.sql
diff --git a/sql/updates/world/2012_12_26_17_world_sai.sql b/sql/old/3.3.5a/2012_12_26_17_world_sai.sql
index 6f35bcc230b..6f35bcc230b 100644
--- a/sql/updates/world/2012_12_26_17_world_sai.sql
+++ b/sql/old/3.3.5a/2012_12_26_17_world_sai.sql
diff --git a/sql/updates/world/2012_12_26_18_world_sai.sql b/sql/old/3.3.5a/2012_12_26_18_world_sai.sql
index cd4989de5e0..cd4989de5e0 100644
--- a/sql/updates/world/2012_12_26_18_world_sai.sql
+++ b/sql/old/3.3.5a/2012_12_26_18_world_sai.sql
diff --git a/sql/updates/world/2012_12_26_19_world_sai.sql b/sql/old/3.3.5a/2012_12_26_19_world_sai.sql
index 5cfd2a29c62..5cfd2a29c62 100644
--- a/sql/updates/world/2012_12_26_19_world_sai.sql
+++ b/sql/old/3.3.5a/2012_12_26_19_world_sai.sql
diff --git a/sql/updates/world/2012_12_26_20_world_sai.sql b/sql/old/3.3.5a/2012_12_26_20_world_sai.sql
index 3574835ce32..3574835ce32 100644
--- a/sql/updates/world/2012_12_26_20_world_sai.sql
+++ b/sql/old/3.3.5a/2012_12_26_20_world_sai.sql
diff --git a/sql/updates/world/2012_12_27_00_world_orgrims_hammer.sql b/sql/old/3.3.5a/2012_12_27_00_world_orgrims_hammer.sql
index a7c8af76fa0..a7c8af76fa0 100644
--- a/sql/updates/world/2012_12_27_00_world_orgrims_hammer.sql
+++ b/sql/old/3.3.5a/2012_12_27_00_world_orgrims_hammer.sql
diff --git a/sql/updates/world/2012_12_27_01_world_locales_quest.sql b/sql/old/3.3.5a/2012_12_27_01_world_locales_quest.sql
index 047f8bb499f..047f8bb499f 100644
--- a/sql/updates/world/2012_12_27_01_world_locales_quest.sql
+++ b/sql/old/3.3.5a/2012_12_27_01_world_locales_quest.sql
diff --git a/sql/updates/world/2012_12_27_02_world_locales.sql b/sql/old/3.3.5a/2012_12_27_02_world_locales.sql
index 5caeb009447..5caeb009447 100644
--- a/sql/updates/world/2012_12_27_02_world_locales.sql
+++ b/sql/old/3.3.5a/2012_12_27_02_world_locales.sql
diff --git a/sql/updates/world/2012_12_27_03_world_sai.sql b/sql/old/3.3.5a/2012_12_27_03_world_sai.sql
index acd5c931c50..acd5c931c50 100644
--- a/sql/updates/world/2012_12_27_03_world_sai.sql
+++ b/sql/old/3.3.5a/2012_12_27_03_world_sai.sql
diff --git a/sql/updates/world/2012_12_27_04_world_the_shadows_vault.sql b/sql/old/3.3.5a/2012_12_27_04_world_the_shadows_vault.sql
index 96d176d6d1b..96d176d6d1b 100644
--- a/sql/updates/world/2012_12_27_04_world_the_shadows_vault.sql
+++ b/sql/old/3.3.5a/2012_12_27_04_world_the_shadows_vault.sql
diff --git a/sql/updates/world/2012_12_28_00_world_command.sql b/sql/old/3.3.5a/2012_12_28_00_world_command.sql
index 6de970d3b81..6de970d3b81 100644
--- a/sql/updates/world/2012_12_28_00_world_command.sql
+++ b/sql/old/3.3.5a/2012_12_28_00_world_command.sql
diff --git a/sql/updates/world/2012_12_28_01_world_misc.sql b/sql/old/3.3.5a/2012_12_28_01_world_misc.sql
index 44501390d9e..44501390d9e 100644
--- a/sql/updates/world/2012_12_28_01_world_misc.sql
+++ b/sql/old/3.3.5a/2012_12_28_01_world_misc.sql
diff --git a/sql/updates/world/2012_12_28_02_world_sai.sql b/sql/old/3.3.5a/2012_12_28_02_world_sai.sql
index 7a518449fde..7a518449fde 100644
--- a/sql/updates/world/2012_12_28_02_world_sai.sql
+++ b/sql/old/3.3.5a/2012_12_28_02_world_sai.sql
diff --git a/sql/updates/world/2012_12_28_03_world_disables.sql b/sql/old/3.3.5a/2012_12_28_03_world_disables.sql
index 017e3aea8c1..017e3aea8c1 100644
--- a/sql/updates/world/2012_12_28_03_world_disables.sql
+++ b/sql/old/3.3.5a/2012_12_28_03_world_disables.sql
diff --git a/sql/updates/world/2012_12_28_04_world_citadel_footsteps.sql b/sql/old/3.3.5a/2012_12_28_04_world_citadel_footsteps.sql
index 3d0ad779dc2..3d0ad779dc2 100644
--- a/sql/updates/world/2012_12_28_04_world_citadel_footsteps.sql
+++ b/sql/old/3.3.5a/2012_12_28_04_world_citadel_footsteps.sql
diff --git a/sql/updates/world/2012_12_30_01_world_trinity_string.sql b/sql/old/3.3.5a/2012_12_30_01_world_trinity_string.sql
index ce0578168f4..ce0578168f4 100644
--- a/sql/updates/world/2012_12_30_01_world_trinity_string.sql
+++ b/sql/old/3.3.5a/2012_12_30_01_world_trinity_string.sql
diff --git a/sql/updates/world/2012_12_31_00_world_sai.sql b/sql/old/3.3.5a/2012_12_31_00_world_sai.sql
index 9e30c29f784..9e30c29f784 100644
--- a/sql/updates/world/2012_12_31_00_world_sai.sql
+++ b/sql/old/3.3.5a/2012_12_31_00_world_sai.sql
diff --git a/sql/updates/world/2012_12_31_01_world_sai.sql b/sql/old/3.3.5a/2012_12_31_01_world_sai.sql
index 96cf4d1e724..96cf4d1e724 100644
--- a/sql/updates/world/2012_12_31_01_world_sai.sql
+++ b/sql/old/3.3.5a/2012_12_31_01_world_sai.sql
diff --git a/sql/updates/world/2013_01_01_00_item_template_restore.sql b/sql/old/3.3.5a/2013_01_01_00_item_template_restore.sql
index dc18ff71fe8..dc18ff71fe8 100644
--- a/sql/updates/world/2013_01_01_00_item_template_restore.sql
+++ b/sql/old/3.3.5a/2013_01_01_00_item_template_restore.sql
diff --git a/sql/updates/world/2013_01_01_01_world_creature_text.sql b/sql/old/3.3.5a/2013_01_01_01_world_creature_text.sql
index c0055f8e1d0..c0055f8e1d0 100644
--- a/sql/updates/world/2013_01_01_01_world_creature_text.sql
+++ b/sql/old/3.3.5a/2013_01_01_01_world_creature_text.sql
diff --git a/sql/updates/world/2013_01_02_00_world_misc.sql b/sql/old/3.3.5a/2013_01_02_00_world_misc.sql
index 8527b8ab145..8527b8ab145 100644
--- a/sql/updates/world/2013_01_02_00_world_misc.sql
+++ b/sql/old/3.3.5a/2013_01_02_00_world_misc.sql
diff --git a/sql/updates/world/2013_01_02_01_world_spell_script.sql b/sql/old/3.3.5a/2013_01_02_01_world_spell_script.sql
index a839ca6dc7b..a839ca6dc7b 100644
--- a/sql/updates/world/2013_01_02_01_world_spell_script.sql
+++ b/sql/old/3.3.5a/2013_01_02_01_world_spell_script.sql
diff --git a/sql/updates/world/2013_01_02_02_world_instance_template.sql b/sql/old/3.3.5a/2013_01_02_02_world_instance_template.sql
index f63a2c3ba2e..f63a2c3ba2e 100644
--- a/sql/updates/world/2013_01_02_02_world_instance_template.sql
+++ b/sql/old/3.3.5a/2013_01_02_02_world_instance_template.sql
diff --git a/sql/updates/world/2013_01_02_03_world_sai.sql b/sql/old/3.3.5a/2013_01_02_03_world_sai.sql
index 6f6318b55bd..6f6318b55bd 100644
--- a/sql/updates/world/2013_01_02_03_world_sai.sql
+++ b/sql/old/3.3.5a/2013_01_02_03_world_sai.sql
diff --git a/sql/updates/world/2013_01_02_04_world_creature.sql b/sql/old/3.3.5a/2013_01_02_04_world_creature.sql
index 686909ce57e..686909ce57e 100644
--- a/sql/updates/world/2013_01_02_04_world_creature.sql
+++ b/sql/old/3.3.5a/2013_01_02_04_world_creature.sql
diff --git a/sql/updates/world/2013_01_02_05_eye_of_eternity.sql b/sql/old/3.3.5a/2013_01_02_05_eye_of_eternity.sql
index 89cc8f08cce..89cc8f08cce 100644
--- a/sql/updates/world/2013_01_02_05_eye_of_eternity.sql
+++ b/sql/old/3.3.5a/2013_01_02_05_eye_of_eternity.sql
diff --git a/sql/updates/world/2013_01_02_06_world_sai.sql b/sql/old/3.3.5a/2013_01_02_06_world_sai.sql
index 2ce089188d6..2ce089188d6 100644
--- a/sql/updates/world/2013_01_02_06_world_sai.sql
+++ b/sql/old/3.3.5a/2013_01_02_06_world_sai.sql
diff --git a/sql/updates/world/2013_01_02_06_world_waypoints.sql b/sql/old/3.3.5a/2013_01_02_06_world_waypoints.sql
index c7302a457a2..c7302a457a2 100644
--- a/sql/updates/world/2013_01_02_06_world_waypoints.sql
+++ b/sql/old/3.3.5a/2013_01_02_06_world_waypoints.sql
diff --git a/sql/updates/world/2013_01_02_07_world_waypoints.sql b/sql/old/3.3.5a/2013_01_02_07_world_waypoints.sql
index b49fd0b28a6..b49fd0b28a6 100644
--- a/sql/updates/world/2013_01_02_07_world_waypoints.sql
+++ b/sql/old/3.3.5a/2013_01_02_07_world_waypoints.sql
diff --git a/sql/updates/world/2013_01_02_08_world_sai.sql b/sql/old/3.3.5a/2013_01_02_08_world_sai.sql
index a3f0dde4658..a3f0dde4658 100644
--- a/sql/updates/world/2013_01_02_08_world_sai.sql
+++ b/sql/old/3.3.5a/2013_01_02_08_world_sai.sql
diff --git a/sql/updates/world/2013_01_02_09_world_waypoints.sql b/sql/old/3.3.5a/2013_01_02_09_world_waypoints.sql
index 1c2492fcce8..1c2492fcce8 100644
--- a/sql/updates/world/2013_01_02_09_world_waypoints.sql
+++ b/sql/old/3.3.5a/2013_01_02_09_world_waypoints.sql
diff --git a/sql/updates/world/2013_01_02_10_world_sai.sql b/sql/old/3.3.5a/2013_01_02_10_world_sai.sql
index 0be2c5c2d6b..0be2c5c2d6b 100644
--- a/sql/updates/world/2013_01_02_10_world_sai.sql
+++ b/sql/old/3.3.5a/2013_01_02_10_world_sai.sql
diff --git a/sql/updates/world/2013_01_02_11_world_sai.sql b/sql/old/3.3.5a/2013_01_02_11_world_sai.sql
index 7496a765470..7496a765470 100644
--- a/sql/updates/world/2013_01_02_11_world_sai.sql
+++ b/sql/old/3.3.5a/2013_01_02_11_world_sai.sql
diff --git a/sql/updates/world/2013_01_02_12_world_go_script.sql b/sql/old/3.3.5a/2013_01_02_12_world_go_script.sql
index aea0a67c47e..aea0a67c47e 100644
--- a/sql/updates/world/2013_01_02_12_world_go_script.sql
+++ b/sql/old/3.3.5a/2013_01_02_12_world_go_script.sql
diff --git a/sql/updates/world/2013_01_02_12_world_waypoints.sql b/sql/old/3.3.5a/2013_01_02_12_world_waypoints.sql
index 6ec7875d8ef..6ec7875d8ef 100644
--- a/sql/updates/world/2013_01_02_12_world_waypoints.sql
+++ b/sql/old/3.3.5a/2013_01_02_12_world_waypoints.sql
diff --git a/sql/updates/world/2013_01_03_00_world_gameobject_scripts.sql b/sql/old/3.3.5a/2013_01_03_00_world_gameobject_scripts.sql
index 32faf4b03a0..32faf4b03a0 100644
--- a/sql/updates/world/2013_01_03_00_world_gameobject_scripts.sql
+++ b/sql/old/3.3.5a/2013_01_03_00_world_gameobject_scripts.sql
diff --git a/sql/updates/world/2013_01_03_01_world_sai.sql b/sql/old/3.3.5a/2013_01_03_01_world_sai.sql
index 9e0e56eb645..9e0e56eb645 100644
--- a/sql/updates/world/2013_01_03_01_world_sai.sql
+++ b/sql/old/3.3.5a/2013_01_03_01_world_sai.sql
diff --git a/sql/updates/world/2013_01_03_02_world_sai.sql b/sql/old/3.3.5a/2013_01_03_02_world_sai.sql
index ecd84c96800..ecd84c96800 100644
--- a/sql/updates/world/2013_01_03_02_world_sai.sql
+++ b/sql/old/3.3.5a/2013_01_03_02_world_sai.sql
diff --git a/sql/updates/world/2013_01_03_03_world_spelldifficulty_dbc.sql b/sql/old/3.3.5a/2013_01_03_03_world_spelldifficulty_dbc.sql
index 6a41f705491..6a41f705491 100644
--- a/sql/updates/world/2013_01_03_03_world_spelldifficulty_dbc.sql
+++ b/sql/old/3.3.5a/2013_01_03_03_world_spelldifficulty_dbc.sql
diff --git a/sql/updates/world/2013_01_03_04_world_sai.sql b/sql/old/3.3.5a/2013_01_03_04_world_sai.sql
index ccac9266af2..ccac9266af2 100644
--- a/sql/updates/world/2013_01_03_04_world_sai.sql
+++ b/sql/old/3.3.5a/2013_01_03_04_world_sai.sql
diff --git a/sql/updates/world/2013_01_03_05_world_sai.sql b/sql/old/3.3.5a/2013_01_03_05_world_sai.sql
index 9e5537835ff..9e5537835ff 100644
--- a/sql/updates/world/2013_01_03_05_world_sai.sql
+++ b/sql/old/3.3.5a/2013_01_03_05_world_sai.sql
diff --git a/sql/updates/world/2013_01_03_06_world_sai.sql b/sql/old/3.3.5a/2013_01_03_06_world_sai.sql
index e77122e908e..e77122e908e 100644
--- a/sql/updates/world/2013_01_03_06_world_sai.sql
+++ b/sql/old/3.3.5a/2013_01_03_06_world_sai.sql
diff --git a/sql/updates/world/2013_01_03_07_world_sai.sql b/sql/old/3.3.5a/2013_01_03_07_world_sai.sql
index 35a03b9078c..35a03b9078c 100644
--- a/sql/updates/world/2013_01_03_07_world_sai.sql
+++ b/sql/old/3.3.5a/2013_01_03_07_world_sai.sql
diff --git a/sql/updates/world/2013_01_03_08_world_misc.sql b/sql/old/3.3.5a/2013_01_03_08_world_misc.sql
index 816b4b4dcf6..816b4b4dcf6 100644
--- a/sql/updates/world/2013_01_03_08_world_misc.sql
+++ b/sql/old/3.3.5a/2013_01_03_08_world_misc.sql
diff --git a/sql/updates/world/2013_01_03_09_world_sai.sql b/sql/old/3.3.5a/2013_01_03_09_world_sai.sql
index c15eeb88843..c15eeb88843 100644
--- a/sql/updates/world/2013_01_03_09_world_sai.sql
+++ b/sql/old/3.3.5a/2013_01_03_09_world_sai.sql
diff --git a/sql/updates/world/2013_01_03_10_world_gameobject.sql b/sql/old/3.3.5a/2013_01_03_10_world_gameobject.sql
index 11700da1dc9..11700da1dc9 100644
--- a/sql/updates/world/2013_01_03_10_world_gameobject.sql
+++ b/sql/old/3.3.5a/2013_01_03_10_world_gameobject.sql
diff --git a/sql/updates/world/2013_01_03_11_world_item_template.sql b/sql/old/3.3.5a/2013_01_03_11_world_item_template.sql
index df0bd5cd1e7..df0bd5cd1e7 100644
--- a/sql/updates/world/2013_01_03_11_world_item_template.sql
+++ b/sql/old/3.3.5a/2013_01_03_11_world_item_template.sql
diff --git a/sql/updates/world/2013_01_03_12_world_gameobject_scripts.sql b/sql/old/3.3.5a/2013_01_03_12_world_gameobject_scripts.sql
index aede83aa0c8..aede83aa0c8 100644
--- a/sql/updates/world/2013_01_03_12_world_gameobject_scripts.sql
+++ b/sql/old/3.3.5a/2013_01_03_12_world_gameobject_scripts.sql
diff --git a/sql/updates/world/2013_01_03_12_world_misc.sql b/sql/old/3.3.5a/2013_01_03_12_world_misc.sql
index 79d2e2daea9..79d2e2daea9 100644
--- a/sql/updates/world/2013_01_03_12_world_misc.sql
+++ b/sql/old/3.3.5a/2013_01_03_12_world_misc.sql
diff --git a/sql/updates/world/2013_01_03_13_world_quest_template.sql b/sql/old/3.3.5a/2013_01_03_13_world_quest_template.sql
index 12adb1e94ba..12adb1e94ba 100644
--- a/sql/updates/world/2013_01_03_13_world_quest_template.sql
+++ b/sql/old/3.3.5a/2013_01_03_13_world_quest_template.sql
diff --git a/sql/updates/world/2013_01_03_14_world_spell_area.sql b/sql/old/3.3.5a/2013_01_03_14_world_spell_area.sql
index 352fe0e9d85..352fe0e9d85 100644
--- a/sql/updates/world/2013_01_03_14_world_spell_area.sql
+++ b/sql/old/3.3.5a/2013_01_03_14_world_spell_area.sql
diff --git a/sql/updates/world/2013_01_03_15_world_various_fixes.sql b/sql/old/3.3.5a/2013_01_03_15_world_various_fixes.sql
index d3db9afa6e5..d3db9afa6e5 100644
--- a/sql/updates/world/2013_01_03_15_world_various_fixes.sql
+++ b/sql/old/3.3.5a/2013_01_03_15_world_various_fixes.sql
diff --git a/sql/updates/world/2013_01_03_16_world_various_fixes.sql b/sql/old/3.3.5a/2013_01_03_16_world_various_fixes.sql
index bc5f4f4b6ec..bc5f4f4b6ec 100644
--- a/sql/updates/world/2013_01_03_16_world_various_fixes.sql
+++ b/sql/old/3.3.5a/2013_01_03_16_world_various_fixes.sql
diff --git a/sql/updates/world/2013_01_04_00_world_creature_text.sql b/sql/old/3.3.5a/2013_01_04_00_world_creature_text.sql
index ae38180e1ac..ae38180e1ac 100644
--- a/sql/updates/world/2013_01_04_00_world_creature_text.sql
+++ b/sql/old/3.3.5a/2013_01_04_00_world_creature_text.sql
diff --git a/sql/updates/world/2013_01_04_01_world_sai.sql b/sql/old/3.3.5a/2013_01_04_01_world_sai.sql
index 8e7127e5728..8e7127e5728 100644
--- a/sql/updates/world/2013_01_04_01_world_sai.sql
+++ b/sql/old/3.3.5a/2013_01_04_01_world_sai.sql
diff --git a/sql/updates/world/2013_01_04_02_world_misc.sql b/sql/old/3.3.5a/2013_01_04_02_world_misc.sql
index 6833a22ff23..6833a22ff23 100644
--- a/sql/updates/world/2013_01_04_02_world_misc.sql
+++ b/sql/old/3.3.5a/2013_01_04_02_world_misc.sql
diff --git a/sql/updates/world/2013_01_04_02_world_novos_the_summoner.sql b/sql/old/3.3.5a/2013_01_04_02_world_novos_the_summoner.sql
index 49f8b21e3a7..49f8b21e3a7 100644
--- a/sql/updates/world/2013_01_04_02_world_novos_the_summoner.sql
+++ b/sql/old/3.3.5a/2013_01_04_02_world_novos_the_summoner.sql
diff --git a/sql/updates/world/2013_01_05_00_world_waypoints.sql b/sql/old/3.3.5a/2013_01_05_00_world_waypoints.sql
index 4a2d273c552..4a2d273c552 100644
--- a/sql/updates/world/2013_01_05_00_world_waypoints.sql
+++ b/sql/old/3.3.5a/2013_01_05_00_world_waypoints.sql
diff --git a/sql/updates/world/2013_01_06_00_world_creature_addon.sql b/sql/old/3.3.5a/2013_01_06_00_world_creature_addon.sql
index e4412241dce..e4412241dce 100644
--- a/sql/updates/world/2013_01_06_00_world_creature_addon.sql
+++ b/sql/old/3.3.5a/2013_01_06_00_world_creature_addon.sql
diff --git a/sql/updates/world/2013_01_06_00_world_creature_template.sql b/sql/old/3.3.5a/2013_01_06_00_world_creature_template.sql
index 023dc88c870..023dc88c870 100644
--- a/sql/updates/world/2013_01_06_00_world_creature_template.sql
+++ b/sql/old/3.3.5a/2013_01_06_00_world_creature_template.sql
diff --git a/sql/updates/world/2013_01_06_01_world_waypoints.sql b/sql/old/3.3.5a/2013_01_06_01_world_waypoints.sql
index a460d33b086..a460d33b086 100644
--- a/sql/updates/world/2013_01_06_01_world_waypoints.sql
+++ b/sql/old/3.3.5a/2013_01_06_01_world_waypoints.sql
diff --git a/sql/updates/world/2013_01_06_02_world_misc.sql b/sql/old/3.3.5a/2013_01_06_02_world_misc.sql
index 8c24975754a..8c24975754a 100644
--- a/sql/updates/world/2013_01_06_02_world_misc.sql
+++ b/sql/old/3.3.5a/2013_01_06_02_world_misc.sql
diff --git a/sql/updates/world/2013_01_06_03_world_sai.sql b/sql/old/3.3.5a/2013_01_06_03_world_sai.sql
index 1cc0962781b..1cc0962781b 100644
--- a/sql/updates/world/2013_01_06_03_world_sai.sql
+++ b/sql/old/3.3.5a/2013_01_06_03_world_sai.sql
diff --git a/sql/updates/world/2013_01_06_04_world_misc.sql b/sql/old/3.3.5a/2013_01_06_04_world_misc.sql
index fb1e256fa1b..fb1e256fa1b 100644
--- a/sql/updates/world/2013_01_06_04_world_misc.sql
+++ b/sql/old/3.3.5a/2013_01_06_04_world_misc.sql
diff --git a/sql/updates/world/2013_01_07_00_world_sai.sql b/sql/old/3.3.5a/2013_01_07_00_world_sai.sql
index e289130ddef..e289130ddef 100644
--- a/sql/updates/world/2013_01_07_00_world_sai.sql
+++ b/sql/old/3.3.5a/2013_01_07_00_world_sai.sql
diff --git a/sql/updates/world/2013_01_07_01_world_creature_addon.sql b/sql/old/3.3.5a/2013_01_07_01_world_creature_addon.sql
index a83bd028343..a83bd028343 100644
--- a/sql/updates/world/2013_01_07_01_world_creature_addon.sql
+++ b/sql/old/3.3.5a/2013_01_07_01_world_creature_addon.sql
diff --git a/sql/updates/world/2013_01_07_02_world_creature_text.sql b/sql/old/3.3.5a/2013_01_07_02_world_creature_text.sql
index 3e916464615..3e916464615 100644
--- a/sql/updates/world/2013_01_07_02_world_creature_text.sql
+++ b/sql/old/3.3.5a/2013_01_07_02_world_creature_text.sql
diff --git a/sql/updates/world/2013_01_07_03_world_creature_text.sql b/sql/old/3.3.5a/2013_01_07_03_world_creature_text.sql
index c006dc5ef99..c006dc5ef99 100644
--- a/sql/updates/world/2013_01_07_03_world_creature_text.sql
+++ b/sql/old/3.3.5a/2013_01_07_03_world_creature_text.sql
diff --git a/sql/updates/world/2013_01_07_04_world_creature_text.sql b/sql/old/3.3.5a/2013_01_07_04_world_creature_text.sql
index 6fd1d3b95a7..6fd1d3b95a7 100644
--- a/sql/updates/world/2013_01_07_04_world_creature_text.sql
+++ b/sql/old/3.3.5a/2013_01_07_04_world_creature_text.sql
diff --git a/sql/updates/world/2013_01_07_05_world_creature_text.sql b/sql/old/3.3.5a/2013_01_07_05_world_creature_text.sql
index d9d91e784ba..d9d91e784ba 100644
--- a/sql/updates/world/2013_01_07_05_world_creature_text.sql
+++ b/sql/old/3.3.5a/2013_01_07_05_world_creature_text.sql
diff --git a/sql/updates/world/2013_01_07_06_world_creature_text.sql b/sql/old/3.3.5a/2013_01_07_06_world_creature_text.sql
index 5c7d09f9887..5c7d09f9887 100644
--- a/sql/updates/world/2013_01_07_06_world_creature_text.sql
+++ b/sql/old/3.3.5a/2013_01_07_06_world_creature_text.sql
diff --git a/sql/updates/world/2013_01_07_07_world_creature_text.sql b/sql/old/3.3.5a/2013_01_07_07_world_creature_text.sql
index 6f6a0b75713..6f6a0b75713 100644
--- a/sql/updates/world/2013_01_07_07_world_creature_text.sql
+++ b/sql/old/3.3.5a/2013_01_07_07_world_creature_text.sql
diff --git a/sql/updates/world/2013_01_07_08_world_creature_text.sql b/sql/old/3.3.5a/2013_01_07_08_world_creature_text.sql
index 94ab4d925ee..94ab4d925ee 100644
--- a/sql/updates/world/2013_01_07_08_world_creature_text.sql
+++ b/sql/old/3.3.5a/2013_01_07_08_world_creature_text.sql
diff --git a/sql/updates/world/2013_01_07_09_world_buru.sql b/sql/old/3.3.5a/2013_01_07_09_world_buru.sql
index a7e1d5a0ef1..a7e1d5a0ef1 100644
--- a/sql/updates/world/2013_01_07_09_world_buru.sql
+++ b/sql/old/3.3.5a/2013_01_07_09_world_buru.sql
diff --git a/sql/updates/world/2013_01_07_10_world_skeram.sql b/sql/old/3.3.5a/2013_01_07_10_world_skeram.sql
index fb62da5e994..fb62da5e994 100644
--- a/sql/updates/world/2013_01_07_10_world_skeram.sql
+++ b/sql/old/3.3.5a/2013_01_07_10_world_skeram.sql
diff --git a/sql/updates/world/2013_01_07_11_world_sai.sql b/sql/old/3.3.5a/2013_01_07_11_world_sai.sql
index bf4a1ed3c78..bf4a1ed3c78 100644
--- a/sql/updates/world/2013_01_07_11_world_sai.sql
+++ b/sql/old/3.3.5a/2013_01_07_11_world_sai.sql
diff --git a/sql/updates/world/2013_01_07_12_world_sai.sql b/sql/old/3.3.5a/2013_01_07_12_world_sai.sql
index bfa06980983..bfa06980983 100644
--- a/sql/updates/world/2013_01_07_12_world_sai.sql
+++ b/sql/old/3.3.5a/2013_01_07_12_world_sai.sql
diff --git a/sql/updates/world/2013_01_07_13_world_creature_text.sql b/sql/old/3.3.5a/2013_01_07_13_world_creature_text.sql
index 60d69e3c32a..60d69e3c32a 100644
--- a/sql/updates/world/2013_01_07_13_world_creature_text.sql
+++ b/sql/old/3.3.5a/2013_01_07_13_world_creature_text.sql
diff --git a/sql/updates/world/2013_01_07_14_world_creature_text.sql b/sql/old/3.3.5a/2013_01_07_14_world_creature_text.sql
index d68eb93ff4e..d68eb93ff4e 100644
--- a/sql/updates/world/2013_01_07_14_world_creature_text.sql
+++ b/sql/old/3.3.5a/2013_01_07_14_world_creature_text.sql
diff --git a/sql/updates/world/2013_01_07_15_world_creature_addon.sql b/sql/old/3.3.5a/2013_01_07_15_world_creature_addon.sql
index 2b5ec6c2413..2b5ec6c2413 100644
--- a/sql/updates/world/2013_01_07_15_world_creature_addon.sql
+++ b/sql/old/3.3.5a/2013_01_07_15_world_creature_addon.sql
diff --git a/sql/updates/world/2013_01_07_16_world_creature_addon.sql b/sql/old/3.3.5a/2013_01_07_16_world_creature_addon.sql
index bd5c17c6f67..bd5c17c6f67 100644
--- a/sql/updates/world/2013_01_07_16_world_creature_addon.sql
+++ b/sql/old/3.3.5a/2013_01_07_16_world_creature_addon.sql
diff --git a/sql/updates/world/2013_01_07_17_world_creature_text.sql b/sql/old/3.3.5a/2013_01_07_17_world_creature_text.sql
index 137308da214..137308da214 100644
--- a/sql/updates/world/2013_01_07_17_world_creature_text.sql
+++ b/sql/old/3.3.5a/2013_01_07_17_world_creature_text.sql
diff --git a/sql/updates/world/2013_01_07_18_world_creature_text.sql b/sql/old/3.3.5a/2013_01_07_18_world_creature_text.sql
index b9c6ef8d0c1..b9c6ef8d0c1 100644
--- a/sql/updates/world/2013_01_07_18_world_creature_text.sql
+++ b/sql/old/3.3.5a/2013_01_07_18_world_creature_text.sql
diff --git a/sql/updates/world/2013_01_08_01_world_raise_the_barricades.sql b/sql/old/3.3.5a/2013_01_08_01_world_raise_the_barricades.sql
index 636beb404de..636beb404de 100644
--- a/sql/updates/world/2013_01_08_01_world_raise_the_barricades.sql
+++ b/sql/old/3.3.5a/2013_01_08_01_world_raise_the_barricades.sql
diff --git a/sql/updates/world/2013_01_09_00_world_misc.sql b/sql/old/3.3.5a/2013_01_09_00_world_misc.sql
index a5402c60534..a5402c60534 100644
--- a/sql/updates/world/2013_01_09_00_world_misc.sql
+++ b/sql/old/3.3.5a/2013_01_09_00_world_misc.sql
diff --git a/sql/updates/world/2013_01_09_01_world_lfg_dungeon_rewards.sql b/sql/old/3.3.5a/2013_01_09_01_world_lfg_dungeon_rewards.sql
index dfc5f7cf075..dfc5f7cf075 100644
--- a/sql/updates/world/2013_01_09_01_world_lfg_dungeon_rewards.sql
+++ b/sql/old/3.3.5a/2013_01_09_01_world_lfg_dungeon_rewards.sql
diff --git a/sql/updates/world/2013_01_09_02_world_sai.sql b/sql/old/3.3.5a/2013_01_09_02_world_sai.sql
index bd164ff4b5e..bd164ff4b5e 100644
--- a/sql/updates/world/2013_01_09_02_world_sai.sql
+++ b/sql/old/3.3.5a/2013_01_09_02_world_sai.sql
diff --git a/sql/updates/world/2013_01_09_03_world_spell_script_names.sql b/sql/old/3.3.5a/2013_01_09_03_world_spell_script_names.sql
index 14d0eb8bfd9..14d0eb8bfd9 100644
--- a/sql/updates/world/2013_01_09_03_world_spell_script_names.sql
+++ b/sql/old/3.3.5a/2013_01_09_03_world_spell_script_names.sql
diff --git a/sql/updates/world/2013_01_10_00_world_trinity_string.sql b/sql/old/3.3.5a/2013_01_10_00_world_trinity_string.sql
index 35d0c1eba99..35d0c1eba99 100644
--- a/sql/updates/world/2013_01_10_00_world_trinity_string.sql
+++ b/sql/old/3.3.5a/2013_01_10_00_world_trinity_string.sql
diff --git a/sql/updates/world/2013_01_10_01_world_sai.sql b/sql/old/3.3.5a/2013_01_10_01_world_sai.sql
index da6c5f994f2..da6c5f994f2 100644
--- a/sql/updates/world/2013_01_10_01_world_sai.sql
+++ b/sql/old/3.3.5a/2013_01_10_01_world_sai.sql
diff --git a/sql/updates/world/2013_01_10_02_world_creature_addon.sql b/sql/old/3.3.5a/2013_01_10_02_world_creature_addon.sql
index 4ad14575d36..4ad14575d36 100644
--- a/sql/updates/world/2013_01_10_02_world_creature_addon.sql
+++ b/sql/old/3.3.5a/2013_01_10_02_world_creature_addon.sql
diff --git a/sql/updates/world/2013_01_10_03_world_creature_addon.sql b/sql/old/3.3.5a/2013_01_10_03_world_creature_addon.sql
index ef633331937..ef633331937 100644
--- a/sql/updates/world/2013_01_10_03_world_creature_addon.sql
+++ b/sql/old/3.3.5a/2013_01_10_03_world_creature_addon.sql
diff --git a/sql/updates/world/2013_01_10_04_world_creature_addon.sql b/sql/old/3.3.5a/2013_01_10_04_world_creature_addon.sql
index 74f2356e7a7..74f2356e7a7 100644
--- a/sql/updates/world/2013_01_10_04_world_creature_addon.sql
+++ b/sql/old/3.3.5a/2013_01_10_04_world_creature_addon.sql
diff --git a/sql/updates/world/2013_01_10_05_world_sai.sql b/sql/old/3.3.5a/2013_01_10_05_world_sai.sql
index 82f2a16a113..82f2a16a113 100644
--- a/sql/updates/world/2013_01_10_05_world_sai.sql
+++ b/sql/old/3.3.5a/2013_01_10_05_world_sai.sql
diff --git a/sql/updates/world/2013_01_11_00_world_ayamiss.sql b/sql/old/3.3.5a/2013_01_11_00_world_ayamiss.sql
index cbdb7a2ed16..cbdb7a2ed16 100644
--- a/sql/updates/world/2013_01_11_00_world_ayamiss.sql
+++ b/sql/old/3.3.5a/2013_01_11_00_world_ayamiss.sql
diff --git a/sql/updates/world/2013_01_11_01_world_conditions.sql b/sql/old/3.3.5a/2013_01_11_01_world_conditions.sql
index 352efb792b4..352efb792b4 100644
--- a/sql/updates/world/2013_01_11_01_world_conditions.sql
+++ b/sql/old/3.3.5a/2013_01_11_01_world_conditions.sql
diff --git a/sql/updates/world/2013_01_11_02_world_sai.sql b/sql/old/3.3.5a/2013_01_11_02_world_sai.sql
index 09466db2e94..09466db2e94 100644
--- a/sql/updates/world/2013_01_11_02_world_sai.sql
+++ b/sql/old/3.3.5a/2013_01_11_02_world_sai.sql
diff --git a/sql/updates/world/2013_01_11_03_world_sai.sql b/sql/old/3.3.5a/2013_01_11_03_world_sai.sql
index e08252a4894..e08252a4894 100644
--- a/sql/updates/world/2013_01_11_03_world_sai.sql
+++ b/sql/old/3.3.5a/2013_01_11_03_world_sai.sql
diff --git a/sql/updates/world/2013_01_11_04_world_sai.sql b/sql/old/3.3.5a/2013_01_11_04_world_sai.sql
index 3c885d85cc1..3c885d85cc1 100644
--- a/sql/updates/world/2013_01_11_04_world_sai.sql
+++ b/sql/old/3.3.5a/2013_01_11_04_world_sai.sql
diff --git a/sql/updates/world/2013_01_11_05_world_creature_text.sql b/sql/old/3.3.5a/2013_01_11_05_world_creature_text.sql
index 1283fd86106..1283fd86106 100644
--- a/sql/updates/world/2013_01_11_05_world_creature_text.sql
+++ b/sql/old/3.3.5a/2013_01_11_05_world_creature_text.sql
diff --git a/sql/old/3.3.5a/2013_01_12_00_world_smart_scripts.sql b/sql/old/3.3.5a/2013_01_12_00_world_smart_scripts.sql
new file mode 100644
index 00000000000..06562cbd122
--- /dev/null
+++ b/sql/old/3.3.5a/2013_01_12_00_world_smart_scripts.sql
@@ -0,0 +1,7 @@
+-- Hulking Abomination
+UPDATE `creature_template` SET `AIName`='SmartAI' WHERE `entry`=31140;
+DELETE FROM `smart_scripts` WHERE `entryorguid`=31140 AND `source_type`=0;
+INSERT INTO `smart_scripts`(`entryorguid`,`id`,`event_type`,`event_flags`,`event_param1`,`event_param2`,`event_param3`,`event_param4`,`action_type`,`action_param1`,`action_param2`,`target_type`,`comment`) VALUES
+(31140,0,6,0,0,0,0,0,11,58995,2,1,'Hulking Abomination - Just died - Cast Exploding Corpse'),
+(31140,1,9,1,8,40,0,0,11,50335,0,7,'Hulking Abomination - Invoker in range of 8 to 40 yards - Cast Scourge Hook'),
+(31140,2,0,0,3000,3000,7000,7000,11,40504,0,2,'Hulking Abomination - In combat - Cast Cleave');
diff --git a/sql/old/3.3.5a/2013_01_12_01_world_string.sql b/sql/old/3.3.5a/2013_01_12_01_world_string.sql
new file mode 100644
index 00000000000..8766e97231d
--- /dev/null
+++ b/sql/old/3.3.5a/2013_01_12_01_world_string.sql
@@ -0,0 +1,3 @@
+DELETE FROM `trinity_string` WHERE `entry`=2029;
+INSERT INTO `trinity_string` (`entry`,`content_default`,`content_loc1`,`content_loc2`,`content_loc3`,`content_loc4`,`content_loc5`,`content_loc6`,`content_loc7`,`content_loc8`) VALUES
+(2029,'|cff00ff00Ticket Response|r: [%s]|r',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);
diff --git a/sql/old/3.3.5a/2013_01_12_02_world_creature_template.sql b/sql/old/3.3.5a/2013_01_12_02_world_creature_template.sql
new file mode 100644
index 00000000000..608af749f15
--- /dev/null
+++ b/sql/old/3.3.5a/2013_01_12_02_world_creature_template.sql
@@ -0,0 +1 @@
+UPDATE `creature_template` SET `ScriptName`= 'boss_gatewatcher_gyrokill' WHERE `entry`=19218;
diff --git a/sql/old/3.3.5a/2013_01_12_03_world_gameobject.sql b/sql/old/3.3.5a/2013_01_12_03_world_gameobject.sql
new file mode 100644
index 00000000000..a7a914fceec
--- /dev/null
+++ b/sql/old/3.3.5a/2013_01_12_03_world_gameobject.sql
@@ -0,0 +1,7 @@
+-- Add missing doors to mechanar
+SET @GUID := 6032;
+DELETE FROM `gameobject` WHERE `guid` BETWEEN @GUID AND @GUID+2;
+INSERT INTO `gameobject` (`guid`,`id`,`map`,`spawnMask`,`phaseMask`,`position_x`,`position_y`,`position_z`,`orientation`,`rotation0`,`rotation1`,`rotation2`,`rotation3`,`spawntimesecs`,`animprogress`,`state`) VALUES
+(@GUID,184632,554,3,1,236.4597,52.36356,1.653544,3.141593,0,0,-1,0,120,0,1),
+(@GUID+1,184322,554,3,1,242.874,52.314810,1.596334,3.141593,0,0,-1,0,120,0,1),
+(@GUID+2,184449,554,3,1,267.9281,52.31481,27.04254,3.141593,0,0,-1,0,120,0,1);
diff --git a/sql/old/3.3.5a/2013_01_12_03_world_waypoints.sql b/sql/old/3.3.5a/2013_01_12_03_world_waypoints.sql
new file mode 100644
index 00000000000..3903e383ae8
--- /dev/null
+++ b/sql/old/3.3.5a/2013_01_12_03_world_waypoints.sql
@@ -0,0 +1,53 @@
+-- Pathing for Mechano-Lord Capacitus Entry: 19219
+SET @NPC := 83160;
+SET @PATH := @NPC * 10;
+UPDATE `creature` SET `spawndist`=0,`MovementType`=2,`position_x`=207.2308,`position_y`=-14.30226,`position_z`=-2.192125 WHERE `guid`=@NPC;
+DELETE FROM `creature_addon` WHERE `guid`=@NPC;
+INSERT INTO `creature_addon` (`guid`,`path_id`,`bytes2`,`mount`,`auras`) VALUES (@NPC,@PATH,1,0, '');
+DELETE FROM `waypoint_data` WHERE `id`=@PATH;
+INSERT INTO `waypoint_data` (`id`,`point`,`position_x`,`position_y`,`position_z`,`orientation`,`delay`,`move_flag`,`action`,`action_chance`,`wpguid`) VALUES
+(@PATH,1,207.2308,-14.30226,-2.192125,0,7000,0,0,100,0),
+(@PATH,2,204.4302,-15.78992,-2.192125,0,1000,0,0,100,0),
+(@PATH,3,207.7613,-18.93377,-2.192125,0,0,0,0,100,0),
+(@PATH,4,208.2819,-16.48228,-2.192125,0,1000,0,0,100,0),
+(@PATH,5,228.1113,-0.829701,-0.8854336,0,0,0,0,100,0),
+(@PATH,6,226.6032,19.45208,-1.726556,0,14000,0,0,100,0),
+(@PATH,7,209.7744,-12.69758,-2.192125,0,1000,0,0,100,0),
+(@PATH,8,209.7744,-12.69758,-2.192125,3.141593,1000,0,0,100,0),
+(@PATH,9,228.1113,-0.829701,-0.8854336,0,0,0,0,100,0),
+(@PATH,10,226.6032,19.45208,-1.726556,0,0,0,0,100,0),
+(@PATH,11,204.9646,26.38312,-0.005853632,0,13000,0,0,100,0),
+(@PATH,12,209.7744,-12.69758,-2.192125,0,1000,0,0,100,0),
+(@PATH,13,209.7744,-12.69758,-2.192125,3.141593,1000,0,0,100,0),
+(@PATH,14,224.3664,-23.35326,-2.192125,0,0,0,0,100,0),
+(@PATH,15,231.1084,-38.22675,8.909556E-07,0,15000,0,0,100,0),
+(@PATH,16,209.7744,-12.69758,-2.192125,0,0,0,0,100,0),
+(@PATH,17,209.7744,-12.69758,-2.192125,3.141593,1000,0,0,100,0),
+(@PATH,18,189.2316,-17.36449,-2.192126,0,0,0,0,100,0),
+(@PATH,19,185.7177,-4.297129,-1.142366,0,0,0,0,100,0),
+(@PATH,20,191.9589,6.435908,-0.7838742,0,0,0,0,100,0),
+(@PATH,21,196.1359,21.01234,-1.426585,0,0,0,0,100,0),
+(@PATH,22,207.8954,18.24695,-2.192125,0,0,0,0,100,0),
+(@PATH,23,213.9077,10.01895,-2.192125,0,11000,0,0,100,0),
+(@PATH,24,209.7744,-12.69758,-2.192125,0,0,0,0,100,0),
+(@PATH,25,209.7744,-12.69758,-2.192125,3.141593,2000,0,0,100,0),
+(@PATH,26,203.706,-11.63055,-2.192125,0,10000,0,0,100,0),
+(@PATH,27,206.4484,-14.27736,-2.192125,0,0,0,0,100,0),
+(@PATH,28,208.3172,-13.38252,-2.192125,0,0,0,0,100,0),
+(@PATH,29,209.3133,-12.82924,-2.192125,0,7000,0,0,100,0),
+(@PATH,30,204.3208,-10.91346,-2.192125,0,0,0,0,100,0),
+(@PATH,31,207.38,-13.36442,-2.192125,0,0,0,0,100,0),
+(@PATH,32,207.7303,-11.56095,-2.192125,0,7000,0,0,100,0),
+(@PATH,33,195.6332,-15.0944,-2.192125,0,0,0,0,100,0),
+(@PATH,34,195.4723,-29.45176,-2.192125,0,0,0,0,100,0),
+(@PATH,35,202.765,-41.79265,-2.192125,0,7000,0,0,100,0),
+(@PATH,36,209.7744,-12.69758,-2.192125,0,1000,0,0,100,0),
+(@PATH,37,209.7744,-12.69758,-2.192125,3.141593,1000,0,0,100,0),
+(@PATH,38,189.2316,-17.36449,-2.192126,0,0,0,0,100,0),
+(@PATH,39,185.7177,-4.297129,-1.142366,0,0,0,0,100,0),
+(@PATH,40,191.9589,6.435908,-0.7838742,0,0,0,0,100,0),
+(@PATH,41,196.1359,21.01234,-1.426585,0,0,0,0,100,0),
+(@PATH,42,207.8954,18.24695,-2.192125,0,0,0,0,100,0),
+(@PATH,43,213.9077,10.01895,-2.192125,0,6000,0,0,100,0),
+(@PATH,44,209.7744,-12.69758,-2.192125,0,20000,0,0,100,0),
+(@PATH,45,210.4122,-9.19214,-2.192125,0,0,0,0,100,0);
diff --git a/sql/old/3.3.5a/2013_01_12_04_world_misc.sql b/sql/old/3.3.5a/2013_01_12_04_world_misc.sql
new file mode 100644
index 00000000000..5ba81aa39f1
--- /dev/null
+++ b/sql/old/3.3.5a/2013_01_12_04_world_misc.sql
@@ -0,0 +1,28 @@
+UPDATE `creature_template` SET `AIName` = '',`ScriptName` = 'boss_mechano_lord_capacitus' WHERE `entry` =19219;
+UPDATE `creature_template` SET `unit_flags`=`unit_flags` |2 |33554432 WHERE `entry` IN (20405,21534); -- Nether Charge
+
+DELETE FROM `creature_ai_scripts` WHERE `creature_id` IN (19219,20405);
+DELETE FROM `creature_ai_texts` WHERE `entry` BETWEEN -92 AND -87;
+
+DELETE FROM `creature_template_addon` WHERE `entry` IN (20405,21534);
+INSERT INTO `creature_template_addon` (`entry`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`auras`) VALUES
+(20405,0,0,0,0,0,'37670 35150'), -- Nether Charge
+(21534,0,0,0,0,0,'37670 35150'); -- Nether Charge (1)
+
+SET @ENTRY := 20405; -- Nether Charge
+
+DELETE FROM `smart_scripts` WHERE `entryorguid`=@ENTRY;
+UPDATE creature_template SET AIName="SmartAI" WHERE entry=@ENTRY LIMIT 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
+(@ENTRY,0,0,0,0,0,100,0,14000,14000,1000,1000,11,35151,0,0,0,0,0,0,0,0,0,0.0,0.0,0.0,0.0,"Nether Bomb - Nether Charge Pulse"),
+(@ENTRY,0,1,0,0,0,100,0,0,0,0,0,20,0,0,0,0,0,0,0,0,0,0,0.0,0.0,0.0,0.0,"Nether Charge - No Melee"),
+(@ENTRY,0,2,0,0,0,100,0,0,0,14000,14000,21,0,0,0,0,0,0,0,0,0,0,0.0,0.0,0.0,0.0,"Nether Charge - Prevent Combat Movement When Start...");
+
+DELETE FROM `creature_text` WHERE `entry` = 19219;
+INSERT INTO `creature_text` (`entry`,`groupid`,`id`,`text`,`type`,`language`,`probability`,`emote`,`duration`,`sound`,`comment`) VALUES
+(19219, 0, 0, 'You should split while you can.', 14, 0, 100, 0, 0, 11162, 'Mechano-Lord Capacitus - Aggro'),
+(19219, 1, 0, 'Go ahead, gimme your best shot. I can take it!', 14, 0, 100, 0, 0, 11166, 'Mechano-Lord Capacitus - Yells'), -- Reflective Damage Shield
+(19219, 2, 0, 'Think you can hurt me, huh? Think I''m afraid a'' you?', 14, 0, 100, 0, 0, 11165, 'Mechano-Lord Capacitus - Yells'), -- Reflective Magic Shield
+(19219, 3, 0, 'Can''t say I didn''t warn you!', 14, 0, 100, 0, 0, 11163, 'Mechano-Lord Capacitus - Killing a player'),
+(19219, 3, 1, 'Damn, I''m good!', 14, 0, 100, 0, 0, 11164, 'Mechano-Lord Capacitus - Killing a player'),
+(19219, 4, 0, 'Bully!', 14, 0, 100, 0, 0, 11167, 'Mechano-Lord Capacitus - Death');
diff --git a/sql/old/3.3.5a/2013_01_12_05_world_waypoint_script.sql b/sql/old/3.3.5a/2013_01_12_05_world_waypoint_script.sql
new file mode 100644
index 00000000000..639b27b911e
--- /dev/null
+++ b/sql/old/3.3.5a/2013_01_12_05_world_waypoint_script.sql
@@ -0,0 +1 @@
+DELETE FROM `waypoint_scripts` WHERE `id` = 8316001;
diff --git a/sql/old/3.3.5a/2013_01_13_00_world_gameobject_template.sql b/sql/old/3.3.5a/2013_01_13_00_world_gameobject_template.sql
new file mode 100644
index 00000000000..056dfea0bc9
--- /dev/null
+++ b/sql/old/3.3.5a/2013_01_13_00_world_gameobject_template.sql
@@ -0,0 +1 @@
+UPDATE `gameobject_template` SET `ScriptName`='' WHERE `entry`=130511;
diff --git a/sql/old/3.3.5a/2013_01_13_01_world_sai.sql b/sql/old/3.3.5a/2013_01_13_01_world_sai.sql
new file mode 100644
index 00000000000..a8513d88ab0
--- /dev/null
+++ b/sql/old/3.3.5a/2013_01_13_01_world_sai.sql
@@ -0,0 +1,41 @@
+SET @CONTROL_WATCHER := 58524;
+SET @ETHREAL := 58548;
+SET @GOSSIP := 10028;
+SET @EYE := 193058;
+SET @WATCHER := 31110;
+SET @MIRROR_NPC := 31005;
+SET @FIGMENT_SPELL := 57530;
+SET @PLAYER_MIRROR := 58122;
+SET @CONTROL_START := 58120;
+SET @MIRROR_SPELL := 57528;
+SET @WEAPON_COPY := 41055;
+SET @COPY_OFF_HAND := 45205;
+
+UPDATE `creature_template` SET `spell1` = 58541, `spell2` = 58544, `spell3` = 58543, `spell4` = 58562, `spell5` = 58563, `spell6` = 58658 WHERE `entry` = @WATCHER;
+UPDATE `creature_template` SET `AIName`='SmartAI' WHERE `entry`=@MIRROR_NPC;
+UPDATE `gameobject_template` SET `AIName`='SmartGameObjectAI' WHERE `entry`= @EYE;
+UPDATE `creature_template` SET `AIName` = 'SmartAI' WHERE `entry` = @WATCHER;
+DELETE FROM `smart_scripts` WHERE `entryorguid`= @EYE;
+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
+(@EYE,1,0,1,62,0,100,0,@GOSSIP,0,0,0,85,@CONTROL_WATCHER,2,0,0,0,0,7,0,0,0,0,0,0,0, 'On Gossip Option Select - Cast Control Eidolon Watcher - Action Invoker'),
+(@EYE,1,1,2,61,0,100,0,0,0,0,0,72,0,0,0,0,0,0,7,0,0,0,0,0,0,0, 'Eye of Dominion - On link - Close Gossip Option'),
+(@EYE,1,2,3,61,0,100,0,0,0,0,0,85,@PLAYER_MIRROR,2,0,0,0,0,7,0,0,0,0,0,0,0, 'Eye of Dominion - On link - Summon Player Mirror'),
+(@EYE,1,3,0,61,0,100,0,0,0,0,0,15,13168,0,0,0,0,0,7,0,0,0,0,0,0,0, 'Eye of Dominion - On link - Complete quest');
+DELETE FROM `smart_scripts` WHERE `entryorguid`= @MIRROR_NPC;
+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
+(@MIRROR_NPC,0,0,1,54,0,100,1,0,0,0,0,85,@MIRROR_SPELL,2,0,0,0,0,1,0,0,0,0,0,0,0, 'On spawn - Cast Mirror Spell - Action Invoker'),
+(@MIRROR_NPC,0,1,2,61,0,100,1,0,0,0,0,85,@WEAPON_COPY,2,0,0,0,0,1,0,0,0,0,0,0,0,'On Link - Copy Main Hand - Action Invoker'),
+(@MIRROR_NPC,0,2,3,61,0,100,1,0,0,0,0,85,@COPY_OFF_HAND,2,0,0,0,0,1,0,0,0,0,0,0,0,'On Link - Copy Offhand - Action Invoker'),
+(@MIRROR_NPC,0,3,0,61,0,100,1,0,0,0,0,11,@CONTROL_START,2,0,0,0,0,1,0,0,0,0,0,0,0,'On Spawn - Cast Start Control - Action Invoker');
+
+DELETE FROM `smart_scripts` WHERE `entryorguid`=@WATCHER;
+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
+(@WATCHER,0,0,0,54,0,100,1,0,0,0,0,11,@ETHREAL,2,0,0,0,0,1,0,0,0,0,0,0,0, 'On spawn - Cast Ethreal Aura - On self');
+
+DELETE FROM `conditions` WHERE `SourceTypeOrReferenceId` = 22 AND `SourceEntry` = @EYE;
+INSERT INTO `conditions` (`SourceTypeOrReferenceId`, `SourceGroup`, `SourceEntry`, `SourceId`, `ElseGroup`, `ConditionTypeOrReference`, `ConditionTarget`, `ConditionValue1`, `ConditionValue2`, `ConditionValue3`, `NegativeCondition`, `ErrorTextId`, `ScriptName`, `Comment`) VALUES
+(22,4,@EYE,1,0,9,0,13168,0,0,0,0,'','Execute SAI line only if player has quest active');
+
+DELETE FROM `conditions` WHERE `SourceTypeOrReferenceId`=13 AND `SourceEntry`=@MIRROR_SPELL;
+INSERT INTO `conditions` (`SourceTypeOrReferenceId`,`SourceGroup`,`SourceEntry`,`SourceId`,`ElseGroup`,`ConditionTypeOrReference`,`ConditionTarget`,`ConditionValue1`,`ConditionValue2`,`ConditionValue3`,`NegativeCondition`,`ErrorTextId`,`ScriptName`,`Comment`) VALUES
+(13,7,@MIRROR_SPELL,0,0,31,0,3,@MIRROR_NPC,0,0,0,'','Spell target Mirror npc');
diff --git a/sql/old/3.3.5a/2013_01_13_02_world_gluttonous_lurkers.sql b/sql/old/3.3.5a/2013_01_13_02_world_gluttonous_lurkers.sql
new file mode 100644
index 00000000000..71f8efbe0c3
--- /dev/null
+++ b/sql/old/3.3.5a/2013_01_13_02_world_gluttonous_lurkers.sql
@@ -0,0 +1,36 @@
+-- Quest: Gluttonous Lurkers (12527)
+-- Zul'drak Rat: Spellclick spells
+DELETE FROM `npc_spellclick_spells` WHERE `npc_entry`=28202;
+INSERT INTO `npc_spellclick_spells`(`npc_entry`,`spell_id`,`cast_flags`) VALUE
+(28202,50926,1),
+(28202,50927,2);
+
+-- Zul'drak Rat: SAI
+UPDATE `creature_template` SET `AIName`='SmartAI' WHERE `entry`=28202;
+DELETE FROM `smart_scripts` WHERE `entryorguid`=28202 AND `source_type`=0;
+INSERT INTO `smart_scripts`(`entryorguid`,`event_type`,`event_param1`,`action_type`,`target_type`,`comment`) VALUES
+(28202,8,50926,41,1,'Zuldrak Rat - On spell hit of Gluttonous Lurkers: Create ZulDrak Rat Cover - Despawn');
+
+-- Zul'drak Rat spell: Script assignment
+DELETE FROM `spell_script_names` WHERE `spell_id`=50894;
+INSERT INTO `spell_script_names`(`spell_id`,`ScriptName`) VALUE
+(50894,'spell_zuldrak_rat');
+
+-- Zul'drak Rat spell: Conditions
+DELETE FROM `conditions` WHERE `SourceTypeOrReferenceId`=17 AND `SourceEntry`=50894;
+INSERT INTO `conditions`(`SourceTypeOrReferenceId`,`SourceEntry`,`ConditionTypeOrReference`,`ConditionTarget`,`ConditionValue1`,`ConditionValue2`,`ErrorType`,`Comment`) VALUE
+(17,50894,31,1,3,28145,12,'Zuldrak Rat - Target has to be Lurking Basilisk');
+
+-- Lurking Basilisk: SAI
+UPDATE `creature_template` SET `AIName`='SmartAI' WHERE `entry`=28145;
+DELETE FROM `creature_ai_scripts` WHERE `creature_id`=28145;
+DELETE FROM `smart_scripts` WHERE `entryorguid`=28145 AND `source_type`=0;
+INSERT INTO `smart_scripts`(`entryorguid`,`event_type`,`event_param1`,`event_param2`,`event_param3`,`event_param4`,`action_type`,`action_param1`,`target_type`,`comment`) VALUE
+(28145,0,5000,5000,17000,27000,11,54470,2,'Lurking Basilisk - In Combat - Cast Venemous Bite');
+
+-- Gorged Lurking Basilisk: SAI
+UPDATE `creature_template` SET `AIName`='SmartAI' WHERE `entry`=28203;
+DELETE FROM `creature_ai_scripts` WHERE `creature_id`=28203;
+DELETE FROM `smart_scripts` WHERE `entryorguid`=28203 AND `source_type`=0;
+INSERT INTO `smart_scripts`(`entryorguid`,`event_type`,`event_param1`,`action_type`,`target_type`,`comment`) VALUES
+(28203,8,50918,41,1,'Gorged Lurking Basilisk - On Spell Hit of Create Basilisk Crystals Cover - Despawn');
diff --git a/sql/old/3.3.5a/2013_01_13_03_world_spell_script_names.sql b/sql/old/3.3.5a/2013_01_13_03_world_spell_script_names.sql
new file mode 100644
index 00000000000..a776ea4a765
--- /dev/null
+++ b/sql/old/3.3.5a/2013_01_13_03_world_spell_script_names.sql
@@ -0,0 +1,4 @@
+-- Zul'drak Rat spell: Script assignment
+DELETE FROM `spell_script_names` WHERE `spell_id`=50894;
+INSERT INTO `spell_script_names`(`spell_id`,`ScriptName`) VALUE
+(50894,'spell_q12527_zuldrak_rat');
diff --git a/sql/old/3.3.5a/2013_01_13_04_world_conditions.sql b/sql/old/3.3.5a/2013_01_13_04_world_conditions.sql
new file mode 100644
index 00000000000..eaf45d5d696
--- /dev/null
+++ b/sql/old/3.3.5a/2013_01_13_04_world_conditions.sql
@@ -0,0 +1,9 @@
+SET @EYE := 193058;
+SET @MIRROR_SPELL := 57528;
+SET @MIRROR_NPC := 31005;
+
+DELETE FROM `conditions` WHERE `SourceTypeOrReferenceId` = 22 AND `SourceEntry` = @EYE;
+DELETE FROM `conditions` WHERE `SourceTypeOrReferenceId` IN (13,17) AND `SourceEntry`=@MIRROR_SPELL;
+INSERT INTO `conditions` (`SourceTypeOrReferenceId`, `SourceGroup`, `SourceEntry`, `SourceId`, `ElseGroup`, `ConditionTypeOrReference`, `ConditionTarget`, `ConditionValue1`, `ConditionValue2`, `ConditionValue3`, `NegativeCondition`, `ErrorTextId`, `ScriptName`, `Comment`) VALUES
+(22,1,@EYE,1,0,9,0,13168,0,0,0,0,'','Execute SAI line only if player has quest active'),
+(17,0,@MIRROR_SPELL,0,0,31,0,3,@MIRROR_NPC,0,0,0,'','Spell target Mirror npc');
diff --git a/sql/updates/world/2013_01_14_00_world_version.sql b/sql/updates/world/2013_01_14_00_world_version.sql
new file mode 100644
index 00000000000..0bc0a89a8a4
--- /dev/null
+++ b/sql/updates/world/2013_01_14_00_world_version.sql
@@ -0,0 +1 @@
+UPDATE `version` SET `db_version`='TDB 335.50', `cache_id`=50 LIMIT 1;
diff --git a/sql/updates/world/2013_01_14_01_world_game_event.sql b/sql/updates/world/2013_01_14_01_world_game_event.sql
new file mode 100644
index 00000000000..d4d30d54928
--- /dev/null
+++ b/sql/updates/world/2013_01_14_01_world_game_event.sql
@@ -0,0 +1,12 @@
+UPDATE `game_event` SET `start_time`='2013-01-27 00:01:00',`length`=20160 WHERE `eventEntry`=7; -- Lunar Festival
+UPDATE `game_event` SET `start_time`='2013-02-10 00:01:00' WHERE `eventEntry`=8; -- Love is in the Air
+UPDATE `game_event` SET `start_time`='2013-03-31 00:01:00' WHERE `eventEntry`=9; -- Noblegarden
+UPDATE `game_event` SET `start_time`='2013-04-28 00:01:00' WHERE `eventEntry`=10; -- Children's Week
+UPDATE `game_event` SET `start_time`='2013-06-21 00:01:00' WHERE `eventEntry`=1; -- Midsummer Fire Festival
+UPDATE `game_event` SET `start_time`='2013-09-13 00:01:00' WHERE `eventEntry`=11; -- Harvest Festival
+UPDATE `game_event` SET `start_time`='2013-09-19 00:01:00' WHERE `eventEntry`=50; -- Pirates' Day
+UPDATE `game_event` SET `start_time`='2013-09-20 00:01:00' WHERE `eventEntry`=24; -- Brewfest
+UPDATE `game_event` SET `start_time`='2013-10-18 01:00:00' WHERE `eventEntry`=11; -- Hallow's End
+UPDATE `game_event` SET `start_time`='2013-11-01 01:00:00' WHERE `eventEntry`=51; -- Day of the Dead
+UPDATE `game_event` SET `start_time`='2013-11-24 01:00:00' WHERE `eventEntry`=26; -- Pilgrim's Bounty
+UPDATE `game_event` SET `start_time`='2013-12-15 06:00:00' WHERE `eventEntry`=2; -- Winter Veil
diff --git a/sql/updates/world/2013_01_14_02_world_ip2nationcountries.sql b/sql/updates/world/2013_01_14_02_world_ip2nationcountries.sql
new file mode 100644
index 00000000000..fdb96fd895e
--- /dev/null
+++ b/sql/updates/world/2013_01_14_02_world_ip2nationcountries.sql
@@ -0,0 +1,2 @@
+RENAME TABLE ip2nationcountries TO ip2nationcountries_temp,
+ ip2nationcountries_temp TO ip2nationCountries;
diff --git a/sql/updates/world/2013_01_14_03_world_lefty_loosy_righty_tighty.sql b/sql/updates/world/2013_01_14_03_world_lefty_loosy_righty_tighty.sql
new file mode 100644
index 00000000000..5d322ac2dd9
--- /dev/null
+++ b/sql/updates/world/2013_01_14_03_world_lefty_loosy_righty_tighty.sql
@@ -0,0 +1,55 @@
+-- Valves
+UPDATE `gameobject` SET `spawntimesecs`=300,`animprogress`=100,`state`=1 WHERE `guid`=151895;
+UPDATE `gameobject` SET `spawntimesecs`=300,`animprogress`=100 WHERE `guid`=221;
+
+-- Max Blasto: Spawn point
+UPDATE `event_scripts` SET `x`=4029.0,`y`=4883.078,`z`=-12.71482,`o`=1.310609 WHERE `id`=17207 AND `command`=10 AND `datalong`=25832;
+-- Max Blasto: SAI
+UPDATE `creature_template` SET `AIName`='SmartAI' WHERE `entry`=25832;
+DELETE FROM `smart_scripts` WHERE `entryorguid`=25832 AND `source_type`=0;
+INSERT INTO `smart_scripts`(`entryorguid`,`id`,`event_type`,`action_type`,`action_param1`,`target_type`,`target_param1`,`comment`) VALUE
+(25832,0,54,49,0,21,20,'Max Blasto - Just summoned - Start attack'),
+(25832,1,4,1,0,0,0,'Max Blasto - On aggro - Say');
+-- Max Blasto: Texts
+DELETE FROM `creature_text` WHERE `entry`=25832;
+INSERT INTO `creature_text`(`entry`,`groupid`,`id`,`text`,`type`,`probability`,`comment`) VALUE
+(25832,0,0,'I am the herald of Mechazod. You will be decursed!',12,100,'Max Blasto - Just summoned'); -- Proofed from video: https://www.youtube.com/watch?v=-hi6a70U3gM
+
+-- The Grinder: Spawn point
+UPDATE `event_scripts` SET `x`=3781.2,`y`=4832.596,`z`=-13.04141,`o`=5.141372 WHERE `id`=17208 AND `command`=10 AND `datalong`=25833;
+-- The Grinder: SAI
+UPDATE `creature_template` SET `AIName`='SmartAI' WHERE `entry`=25833;
+DELETE FROM `smart_scripts` WHERE `entryorguid`=25833 AND `source_type`=0;
+INSERT INTO `smart_scripts`(`entryorguid`,`id`,`event_type`,`action_type`,`action_param1`,`target_type`,`target_param1`,`comment`) VALUE
+(25833,0,54,49,0,21,20,'The Grinder - Just summoned - Start attack'),
+(25833,1,4,1,0,0,0,'The Grinder - On aggro - Say');
+-- The Grinder: Texts
+DELETE FROM `creature_text` WHERE `entry`=25833;
+INSERT INTO `creature_text`(`entry`,`groupid`,`id`,`text`,`type`,`probability`,`comment`) VALUE
+(25833,0,0,'Your meddling is at an end. Mechazod will relieve your curse once I am done with you.',12,100,'The Grinder - Just summoned');
+
+-- ED-210: Spawn point
+UPDATE `event_scripts` SET `x`=4208.38,`y`=4807.071,`z`=-12.7529,`o`=5.809316 WHERE `id`=16909 AND `command`=10 AND `datalong`=25831;
+-- ED-210: SAI
+UPDATE `creature_template` SET `AIName`='SmartAI' WHERE `entry`=25831;
+DELETE FROM `smart_scripts` WHERE `entryorguid`=25831 AND `source_type`=0;
+INSERT INTO `smart_scripts`(`entryorguid`,`id`,`event_type`,`action_type`,`action_param1`,`target_type`,`target_param1`,`comment`) VALUE
+(25831,0,54,49,0,21,20,'ED-210 - Just summoned - Start attack'),
+(25831,1,4,1,0,0,0,'ED-210 - On aggro - Say');
+-- ED-210: Texts
+DELETE FROM `creature_text` WHERE `entry`=25831;
+INSERT INTO `creature_text`(`entry`,`groupid`,`id`,`text`,`type`,`probability`,`comment`) VALUE
+(25831,0,0,'ED-210 online!',12,100,'ED-210 - Just summoned');
+
+-- Twonky: Spawn point
+UPDATE `event_scripts` SET `x`=4118.113,`y`=5087.803,`z`=-1.433036,`o`=2.253069 WHERE `id`=16904 AND `command`=10 AND `datalong`=25830;
+-- Twonky: SAI
+UPDATE `creature_template` SET `AIName`='SmartAI' WHERE `entry`=25830;
+DELETE FROM `smart_scripts` WHERE `entryorguid`=25830 AND `source_type`=0;
+INSERT INTO `smart_scripts`(`entryorguid`,`id`,`event_type`,`action_type`,`action_param1`,`target_type`,`target_param1`,`comment`) VALUE
+(25830,0,54,49,0,21,20,'Twonky - Just summoned - Start attack'),
+(25830,1,4,1,0,0,0,'Twonky - On aggro - Say');
+-- Twonky: Texts
+DELETE FROM `creature_text` WHERE `entry`=25830;
+INSERT INTO `creature_text`(`entry`,`groupid`,`id`,`text`,`type`,`probability`,`comment`) VALUE
+(25830,0,0,'Twonky!',12,100,'Twonky - On aggro');
diff --git a/sql/updates/world/2013_01_15_00_world_creature.sql b/sql/updates/world/2013_01_15_00_world_creature.sql
new file mode 100644
index 00000000000..e49918aa0b6
--- /dev/null
+++ b/sql/updates/world/2013_01_15_00_world_creature.sql
@@ -0,0 +1,12 @@
+SET @CGUID :=43501;
+SET @ENTRY :=25233; -- Lunk-tusk
+
+UPDATE `creature_template` SET `unit_flags`=33536 WHERE `entry`=@ENTRY;
+
+DELETE FROM `creature` WHERE `id`=@ENTRY;
+INSERT INTO `creature` (`guid`, `id`, `map`, `spawnMask`, `phaseMask`, `position_x`, `position_y`, `position_z`, `orientation`, `spawntimesecs`, `spawndist`, `MovementType`) VALUES
+(@CGUID, @ENTRY, 571, 1, 1, 1230.617, -3349.278, 203.7188, 3.385939, 120, 0, 0);
+
+DELETE FROM `creature_template_addon` WHERE `entry`=@ENTRY;
+INSERT INTO `creature_template_addon` (`entry`, `mount`, `bytes1`, `bytes2`, `auras`) VALUES
+(@ENTRY, 0, 0x1, 0x1, '');
diff --git a/sql/updates/world/2013_01_15_01_world_misc.sql b/sql/updates/world/2013_01_15_01_world_misc.sql
new file mode 100644
index 00000000000..48238e5dacf
--- /dev/null
+++ b/sql/updates/world/2013_01_15_01_world_misc.sql
@@ -0,0 +1,40 @@
+-- Vilebranch Speaker
+UPDATE `creature_template` SET `AIName`= '', `ScriptName`= 'mob_vilebranch_speaker' WHERE `entry`=11391;
+DELETE FROM `smart_scripts` WHERE `source_type`=0 AND `entryorguid`=11391;
+
+-- NPC talk text for Bloodlord Mandokir from sniff
+DELETE FROM `creature_text` WHERE `entry`=11382 AND `groupid`=4;
+INSERT INTO `creature_text` (`entry`,`groupid`,`id`,`text`,`type`,`language`,`probability`,`emote`,`duration`,`sound`,`comment`) VALUES
+(11382,4,0, '%s goes into a rage after seeing his raptor fall in battle!',16,0,100,0,0,0, 'Bloodlord Mandokir - Ohgan Dead');
+
+-- Bloodlord Mandokir path from sniff
+SET @PATH := 492861;
+DELETE FROM `creature_template_addon` WHERE `entry`=11382;
+INSERT INTO `creature_template_addon` (`entry`,`path_id`,`bytes2`,`auras`) VALUES (11382,@PATH,1, '');
+DELETE FROM `waypoint_data` WHERE `id`=@PATH;
+INSERT INTO `waypoint_data` (`id`,`point`,`position_x`,`position_y`,`position_z`,`delay`,`move_flag`,`action`,`action_chance`,`wpguid`) VALUES
+(@PATH,1,-12312.66,-1889.255,131.5301,0,1,0,100,0),
+(@PATH,2,-12368.16,-1861.005,131.5301,0,1,0,100,0),
+(@PATH,3,-12352.66,-1875.505,131.5301,0,1,0,100,0),
+(@PATH,4,-12351.41,-1876.505,130.7801,0,1,0,100,0),
+(@PATH,5,-12347.41,-1877.505,131.0301,0,1,0,100,0),
+(@PATH,6,-12343.16,-1877.505,131.2801,0,1,0,100,0),
+(@PATH,7,-12334.91,-1879.755,131.5301,0,1,0,100,0),
+(@PATH,8,-12329.91,-1889.505,131.0301,0,1,0,100,0),
+(@PATH,9,-12328.66,-1892.255,131.0301,0,1,0,100,0),
+(@PATH,10,-12326.41,-1894.255,131.2801,0,1,0,100,0),
+(@PATH,11,-12318.16,-1896.255,131.2801,0,1,0,100,0),
+(@PATH,12,-12301.41,-1896.255,131.5301,0,1,0,100,0),
+(@PATH,13,-12293.16,-1899.005,131.7801,0,1,0,100,0),
+(@PATH,14,-12292.16,-1899.005,131.7801,0,1,0,100,0),
+(@PATH,15,-12291.16,-1899.005,131.7801,0,1,0,100,0),
+(@PATH,16,-12289.41,-1900.505,131.7801,0,1,0,100,0),
+(@PATH,17,-12287.41,-1902.505,131.7801,0,1,0,100,0),
+(@PATH,18,-12285.41,-1904.755,131.7801,0,1,0,100,0),
+(@PATH,19,-12280.66,-1906.755,131.7801,0,1,0,100,0),
+(@PATH,20,-12276.41,-1907.755,131.7801,0,1,0,100,0),
+(@PATH,21,-12275.41,-1908.755,131.7801,0,1,0,100,0),
+(@PATH,22,-12272.41,-1917.255,131.7801,0,1,0,100,0),
+(@PATH,23,-12268.16,-1921.255,131.5301,0,1,0,100,0),
+(@PATH,24,-12259.91,-1919.255,131.0301,0,1,0,100,0),
+(@PATH,25,-12255.66,-1919.255,130.5301,0,1,0,100,0);
diff --git a/sql/updates/world/2013_01_15_02_world_game_event.sql b/sql/updates/world/2013_01_15_02_world_game_event.sql
new file mode 100644
index 00000000000..3300c0667ce
--- /dev/null
+++ b/sql/updates/world/2013_01_15_02_world_game_event.sql
@@ -0,0 +1 @@
+UPDATE `game_event` SET `start_time`='2013-10-18 01:00:00' WHERE `eventEntry`=12; -- Hallow's End
diff --git a/sql/updates/world/2013_01_15_03_world_misc.sql b/sql/updates/world/2013_01_15_03_world_misc.sql
new file mode 100644
index 00000000000..5de9e0ab13a
--- /dev/null
+++ b/sql/updates/world/2013_01_15_03_world_misc.sql
@@ -0,0 +1,88 @@
+-- Reconnaissance Flight (12671)
+
+SET @QUEST := 12671;
+SET @PLANE := 28710; -- Vic's Flying Machine
+SET @PILOT := 28646; -- Pilot Vic
+SET @VIC := 28746; -- Pilot Vic
+SET @SPELL_PLANE := 52256; -- Vic's Flying Machine Validate (must have condition to target player)
+SET @SPELL_ROCKETS := 52254; -- Air-to-Air Rockets
+SET @NPC_SCREECHER := 28170; -- Frosthowl Screecher
+SET @TEMP_LANDING := 300215; -- TEMP Landing Pad
+
+UPDATE `creature_template` SET `AIName`='SmartAI' WHERE `entry`=@VIC;
+UPDATE `creature_template` SET `spell1`='52254',`spell2`='52226',`ScriptName`='npc_vics_flying_machine' WHERE `entry`=@PLANE;
+
+DELETE FROM `creature_template_addon` WHERE `entry`=@PLANE;
+INSERT INTO `creature_template_addon` (`entry`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`auras`) VALUES
+(@PLANE,0,0,0,1,0,'52211 52260'); -- Flight -- Vic's Flying Machine Aggro Periodic
+
+DELETE FROM `smart_scripts` WHERE `entryorguid` IN (@VIC,@PLANE) 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
+(@VIC,0,0,0,19,0,100,0,@QUEST,0,0,0,11,@SPELL_PLANE,0,0,0,0,0,7,0,0,0,0,0,0,0,"On quest accept - Cast spell - Invoker");
+
+DELETE FROM `waypoint_data` WHERE `id`=@PLANE;
+INSERT INTO `waypoint_data` (`id`,`point`,`position_x`,`position_y`,`position_z`,`orientation`,`delay`,`move_flag`,`action`,`action_chance`,`wpguid`) VALUES
+(@PLANE,1,5494.87,4747.031,-187.8783,0,0,0,0,100,0),
+(@PLANE,2,5485.906,4740.681,-184.5172,0,0,0,0,100,0),
+(@PLANE,3,5472.882,4732.441,-172.1562,0,0,0,0,100,0),
+(@PLANE,4,5460.913,4712.542,-157.8784,0,0,0,0,100,0),
+(@PLANE,5,5452.147,4673.518,-137.8906,0,0,0,0,100,0),
+(@PLANE,6,5449.777,4630.711,-126.6684,0,0,0,0,100,0),
+(@PLANE,7,5507.432,4506.089,-126.6684,0,0,0,0,100,0),
+(@PLANE,8,5586.811,4465.319,-126.6684,0,0,0,0,100,0),
+(@PLANE,9,5676.111,4437.874,-126.6684,0,0,0,0,100,0),
+(@PLANE,10,5756.449,4391.051,-91.25155,0,0,0,0,100,0),
+(@PLANE,11,5817.163,4269.269,-91.25155,0,0,0,0,100,0),
+(@PLANE,12,5856.145,4202.824,-68.29334,0,0,0,0,100,0),
+(@PLANE,13,5921.523,4105.534,-68.29334,0,0,0,0,100,0),
+(@PLANE,14,5995.021,4029.883,-13.97897,0,0,0,0,100,0),
+(@PLANE,15,6118.298,3883.733,94.11866,0,0,0,0,100,0),
+(@PLANE,16,6132.932,3750.75,176.5123,0,0,0,0,100,0),
+(@PLANE,17,6165.863,3748.196,198.9567,0,0,0,0,100,0),
+(@PLANE,18,6208.277,3782.189,196.8455,0,0,0,0,100,0),
+(@PLANE,19,6227.615,3836.871,191.6234,0,0,0,0,100,0),
+(@PLANE,20,6218.483,3885.17,191.6234,0,0,0,0,100,0),
+(@PLANE,21,6197.045,3890.053,191.6234,0,0,0,0,100,0),
+(@PLANE,22,6168.752,3864.161,191.6234,0,0,0,0,100,0),
+(@PLANE,23,6204.037,3807.239,191.6234,0,0,0,0,100,0),
+(@PLANE,24,6232.975,3820.852,191.6234,0,0,0,0,100,0),
+(@PLANE,25,6219.879,3854.341,166.6234,0,0,0,0,100,0),
+(@PLANE,26,6210.428,3855.185,154.4848,0,0,0,0,100,0);
+
+DELETE FROM `npc_spellclick_spells` WHERE `npc_entry`=@PLANE;
+INSERT INTO `npc_spellclick_spells` (`npc_entry`,`spell_id`,`cast_flags`,`user_type`) VALUES
+(@PLANE,46598,1,1);
+
+DELETE FROM `vehicle_template_accessory` WHERE `entry`=@PLANE;
+INSERT INTO `vehicle_template_accessory` (`entry`,`accessory_entry`,`seat_id`,`minion`,`description`,`summontype`,`summontimer`) VALUES
+(@PLANE,@PILOT,0,1,'Pilot Vic',7,0);
+
+DELETE FROM `creature_text` WHERE `entry` IN (@PILOT,@PLANE);
+INSERT INTO `creature_text` (`entry`,`groupid`,`id`,`text`,`type`,`language`,`probability`,`emote`,`duration`,`sound`,`comment`) VALUES
+(@PILOT,0,0,'We''re off! Watch out for those vines!',12,0,100,0,0,0,'Pilot Vic to Vic''s Flying Machine'),
+(@PILOT,1,0,'Looks like the Scourge have hit the area ahead pretty bad...',12,0,100,0,0,0,'Pilot Vic to Vic''s Flying Machine'),
+(@PILOT,2,0,'You see that? She''s... huge!',12,0,100,0,0,0,'Pilot Vic to Vic''s Flying Machine'),
+(@PILOT,3,0,'Here we go! Hold on tight -- there''s rough wind ahead!',12,0,100,0,0,0,'Pilot Vic to Vic''s Flying Machine'),
+(@PILOT,4,0,'The glacier is seeping in from Icecrown... and undead everywhere! Wait ''til the professor gets a hold of this!',12,0,100,0,0,0,'Pilot Vic to Vic''s Flying Machine'),
+(@PILOT,5,0,'They''re coming at us! Be quick with those rockets!',12,0,100,0,0,0,'Pilot Vic to Vic''s Flying Machine'),
+(@PILOT,6,0,'Aggggh! I''m hit! You''re going to have to get us back! Quick, before the plane explodes!',12,0,100,0,0,0,'Pilot Vic to Vic''s Flying Machine'),
+(@PLANE,0,0,'The engine''s blown! Fly Vic''s Flying Machine back to Lakeside Landing!',41,0,100,0,0,0,'Vic''s Flying Machine to Pilot Vic');
+
+DELETE FROM `conditions` WHERE `SourceEntry`=@SPELL_ROCKETS AND `SourceTypeOrReferenceId`=13;
+DELETE FROM `conditions` WHERE `SourceEntry`=52226 AND `SourceTypeOrReferenceId`=17;
+INSERT INTO `conditions` (`SourceTypeOrReferenceId`,`SourceGroup`,`SourceEntry`,`SourceId`,`ElseGroup`,`ConditionTypeOrReference`,`ConditionTarget`,`ConditionValue1`,`ConditionValue2`,`ConditionValue3`,`NegativeCondition`,`ErrorType`,`ErrorTextId`,`ScriptName`,`Comment`) VALUES
+(13,1,@SPELL_ROCKETS,0,0,31,0,3,28170,0,0,0,0,'','Air-to-Air Rockets can target Frosthowl Screecher'),
+-- because vehicles ignore spell focus we add an extra condition to fill in for this
+(17,0,52226,0,0,30,0,300215,10,0,0,0,0,'','Requires TEMP Landing Pad near to cast Land Flying Machine');
+
+-- guessed position
+DELETE FROM `gameobject` WHERE `id`=@TEMP_LANDING;
+INSERT INTO `gameobject` (`guid`,`id`,`map`,`spawnMask`,`phaseMask`,`position_x`,`position_y`,`position_z`,`orientation`,`rotation0`,`rotation1`,`rotation2`,`rotation3`,`spawntimesecs`,`animprogress`,`state`) VALUES
+(3552,@TEMP_LANDING,571,1,1,5505.58,4748.35,-194.434,0,0,0,0,0,300,0,1);
+
+-- Frosthowl Screecher
+UPDATE `creature_template` SET `AIName`='SmartAI' WHERE `entry`=@NPC_SCREECHER;
+DELETE FROM `creature_ai_scripts` WHERE `creature_id`=@NPC_SCREECHER;
+DELETE FROM `smart_scripts` WHERE `source_type`=0 AND `entryorguid`=@NPC_SCREECHER;
+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
+(@NPC_SCREECHER,0,0,0,0,0,100,0,3000,4000,3000,4000,11,52257,0,0,0,0,0,2,0,0,0,0,0,0,0,'Cast Shadow Bolt');
diff --git a/sql/updates/world/2013_01_15_04_world_game_event.sql b/sql/updates/world/2013_01_15_04_world_game_event.sql
new file mode 100644
index 00000000000..cfbd399e43d
--- /dev/null
+++ b/sql/updates/world/2013_01_15_04_world_game_event.sql
@@ -0,0 +1 @@
+UPDATE `game_event` SET `start_time`='2013-09-13 00:01:00' WHERE `eventEntry`=11; -- Harvest Festival
diff --git a/sql/updates/world/2013_01_15_05_world_creature_addon.sql b/sql/updates/world/2013_01_15_05_world_creature_addon.sql
new file mode 100644
index 00000000000..94782891ca1
--- /dev/null
+++ b/sql/updates/world/2013_01_15_05_world_creature_addon.sql
@@ -0,0 +1 @@
+DELETE FROM `creature_addon` WHERE guid=49286;
diff --git a/sql/updates/world/2013_01_15_06_world_spell_script_names.sql b/sql/updates/world/2013_01_15_06_world_spell_script_names.sql
new file mode 100644
index 00000000000..0e327bdc63d
--- /dev/null
+++ b/sql/updates/world/2013_01_15_06_world_spell_script_names.sql
@@ -0,0 +1,3 @@
+DELETE FROM `spell_script_names` WHERE `spell_id`=24314;
+INSERT INTO `spell_script_names` (`spell_id`,`ScriptName`) VALUES
+(24314, 'spell_threatening_gaze');
diff --git a/sql/updates/world/2013_01_16_00_world_gossip_menu.sql b/sql/updates/world/2013_01_16_00_world_gossip_menu.sql
new file mode 100644
index 00000000000..1b0554931fa
--- /dev/null
+++ b/sql/updates/world/2013_01_16_00_world_gossip_menu.sql
@@ -0,0 +1,4 @@
+-- Add some missing go gossip
+DELETE FROM `gossip_menu` WHERE `entry` IN (6448,6449,6450,6451);
+INSERT INTO `gossip_menu` (`entry`,`text_id`) VALUES
+(6448,7669),(6449,7670),(6450,7673),(6451,7674);
diff --git a/sql/updates/world/2013_01_16_01_world_creature_text.sql b/sql/updates/world/2013_01_16_01_world_creature_text.sql
new file mode 100644
index 00000000000..69bdd91b886
--- /dev/null
+++ b/sql/updates/world/2013_01_16_01_world_creature_text.sql
@@ -0,0 +1,7 @@
+-- Text for Crushridge Warmonger
+SET @ENTRY := 2287;
+DELETE FROM `creature_text` WHERE `entry`=@ENTRY;
+INSERT INTO `creature_text` (`entry`,`groupid`,`id`,`text`,`type`,`language`,`probability`,`emote`,`duration`,`sound`,`comment`) VALUES
+(@ENTRY,0,0,'Raaar!!! Me smash $R',12,0,100,0,0,0,'Crushridge Warmonger'),
+(@ENTRY,0,1,'Me smash! You die!',12,0,100,0,0,0,'Crushridge Warmonger'),
+(@ENTRY,0,2,'I''ll crush you!',12,0,100,0,0,0,'Crushridge Warmonger');
diff --git a/sql/updates/world/2013_01_17_00_world_creature_template.sql b/sql/updates/world/2013_01_17_00_world_creature_template.sql
new file mode 100644
index 00000000000..af602096440
--- /dev/null
+++ b/sql/updates/world/2013_01_17_00_world_creature_template.sql
@@ -0,0 +1,2 @@
+-- proper faction for Bran in Halls of Stone
+UPDATE `creature_template` SET `faction_A`=1665,`faction_H`=1665 WHERE `entry` IN (28070,31366);
diff --git a/sql/updates/world/2013_01_17_01_world_creature.sql b/sql/updates/world/2013_01_17_01_world_creature.sql
new file mode 100644
index 00000000000..84ded6edcb2
--- /dev/null
+++ b/sql/updates/world/2013_01_17_01_world_creature.sql
@@ -0,0 +1,8 @@
+-- Spawn 4 missing Zul'Gurub Panther Triggers
+SET @GUID := 48311;
+DELETE FROM `creature` WHERE `guid` IN (@GUID,@GUID+1,@GUID+2,@GUID+3);
+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,15091,309,1,1,0,0,-11518.29,-1649.965,41.38264,5.131268,7200,0,0,1,0,0,0,0,0),
+(@GUID+1,15091,309,1,1,0,0,-11518.51,-1649.303,41.38264,5.864306,7200,0,0,1,0,0,0,0,0),
+(@GUID+2,15091,309,1,1,0,0,-11518.46,-1651.542,41.38264,0.2617994,7200,0,0,1,0,0,0,0,0),
+(@GUID+3,15091,309,1,1,0,0,-11516.86,-1604.25,41.38264,5.288348,7200,0,0,1,0,0,0,0,0);
diff --git a/sql/updates/world/2013_01_18_00_world_misc.sql b/sql/updates/world/2013_01_18_00_world_misc.sql
new file mode 100644
index 00000000000..e15a13f51a5
--- /dev/null
+++ b/sql/updates/world/2013_01_18_00_world_misc.sql
@@ -0,0 +1,72 @@
+SET @BRANN := 29579;
+SET @SNOW_TARGET := 29599;
+SET @QUEST := 12920;
+SET @GOSSIP := 9853;
+
+UPDATE `creature_template` SET `gossip_menu_id`=@GOSSIP, `minlevel`=80,`maxlevel`=80,`npcflag`=`npcflag`|1,`unit_flags`=`unit_flags`|32776, `AIName` = 'SmartAI', `equipment_id`=2485 WHERE `entry`=@BRANN;
+UPDATE `creature_template` SET `AIName` = 'SmartAI' WHERE `entry`=@SNOW_TARGET;
+UPDATE `creature_model_info` SET `bounding_radius`=0.347,`combat_reach`=1.5,`gender`=0 WHERE `modelid`=26356;
+DELETE FROM `creature_template_addon` WHERE `entry` IN (@BRANN,@SNOW_TARGET);
+INSERT INTO `creature_template_addon` (`entry`,`mount`,`bytes1`,`bytes2`,`emote`,`auras`) VALUES
+(@BRANN,0,0,2,0, ''),
+(@SNOW_TARGET,0,0,1,0, '54717');
+DELETE FROM `creature_equip_template` WHERE `entry`=2485;
+INSERT INTO `creature_equip_template` (`entry`,`itemEntry1`,`itemEntry2`,`itemEntry3`) VALUES
+(2485,0,0,25972);
+
+DELETE FROM `npc_spellclick_spells` WHERE `npc_entry`=@BRANN;
+INSERT INTO `npc_spellclick_spells` (`npc_entry`,`spell_id`,`cast_flags`,`user_type`) VALUES
+(@BRANN,46598,1,1);
+DELETE FROM `vehicle_template_accessory` WHERE `entry`=@BRANN;
+INSERT INTO `vehicle_template_accessory` (`entry`,`accessory_entry`,`seat_id`,`minion`,`description`,`summontype`,`summontimer`) VALUES
+(@BRANN,@SNOW_TARGET,0,1,'Brann Snow Target',7,0);
+
+DELETE FROM `gossip_menu` WHERE `entry`=@GOSSIP AND `text_id`=13641;
+DELETE FROM `gossip_menu` WHERE `entry`=10145 AND `text_id`=14089;
+INSERT INTO `gossip_menu` (`entry`,`text_id`) VALUES
+(@GOSSIP,13641),(10145,14089);
+DELETE FROM `gossip_menu_option` WHERE `menu_id`=@GOSSIP AND `id` IN (0,1);
+INSERT INTO `gossip_menu_option` (`menu_id`, `id`, `option_icon`,`option_text`,`option_id`,`npc_option_npcflag`,`action_menu_id`,`action_poi_id`,`box_coded`,`box_money`,`box_text`)VALUES
+(@GOSSIP,0,0,'Do you understand me? We should be able to understand each other now.',1,1,0,0,0,0,''),
+(@GOSSIP,1,0,'What kind of help do you require?',1,1,10145,0,0,0,'');
+
+DELETE FROM `creature_text` WHERE `entry`=@BRANN;
+INSERT INTO `creature_text` (`entry`, `groupid`, `id`, `text`, `type`, `language`, `probability`, `emote`, `duration`, `sound`, `comment`) VALUES
+(@BRANN,0,0,"I... I can understand you now! Well, now that we can talk to each other, you have some explaining to do!",12,0,100,5,0,0,'Brann Bronzebeard text0'),
+(@BRANN,1,0,"How did you get my communicator from the Explorers League?",12,0,100,5,0,0,'Brann Bronzebeard text1'),
+(@BRANN,2,0,"If the Explorers League sent men, I didn't see any trace of them. I found your note buried in a camp overrun by iron dwarves.",12,0,100,0,0,0,'Player text2'),
+(@BRANN,3,0,"You have my thanks for dispatching the iron dwarves. But why would the Horde have an interest in me?",12,0,100,6,0,0,'Brann Bronzebeard text3'),
+(@BRANN,4,0,"A scout found the remains of your journal in Thor Modan. We've been looking for you ever since.",12,0,100,0,0,0,'Player text4'),
+(@BRANN,5,0,"That wouldn't be Scout Vor'takh, would it? Even I know of his reputation for ruthlessness. He means to abduct me, then?",12,0,100,5,0,0,'Brann Bronzebeard text5'),
+(@BRANN,6,0,"If you've seen the journal, then you know what I've been discovering. The titans' own creations are at war with each other!",12,0,100,5,0,0,'Brann Bronzebeard text6'),
+(@BRANN,7,0,"Loken and his iron dwarf minions have ousted the Earthen from Ulduar and taken over the city.",12,0,100,5,0,0,'Brann Bronzebeard text7'),
+(@BRANN,8,0,"If we spend our time and strength fighting with each other, Loken will use Ulduar's resources to destroy both Horde and Alliance.",12,0,100,6,0,0,'Brann Bronzebeard text8'),
+(@BRANN,9,0,"Speak to the commander at your post, lad, and persuade him to abandon Vor'takh's foolish plan.",12,0,100,6,0,0,'Brann Bronzebeard Text10');
+
+DELETE FROM `smart_scripts` WHERE `source_type`=0 AND `entryorguid` IN (@BRANN,@SNOW_TARGET);
+DELETE FROM `smart_scripts` WHERE `source_type`=9 AND `entryorguid`=@BRANN*100;
+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
+(@BRANN,0,0,1,62,0,100,0,@GOSSIP,0,0,0,11,55579,2,0,0,0,0,7,0,0,0,0,0,0,0,'Brann Bronzebeard - On gossip select - Spellcast Trigger Brann Signal'),
+(@BRANN,0,1,0,61,0,100,0,0,0,0,0,72,0,0,0,0,0,0,7,0,0,0,0,0,0,0,'Brann Bronzebeard - On gossip select - Close gossip'),
+(@BRANN,0,2,0,8,0,100,0,55578,0,0,0,80,@BRANN*100,0,0,0,0,0,1,0,0,0,0,0,0,0,'Brann Bronzebeard - On spellhit Brann Signal to Self - Start script'),
+(@SNOW_TARGET,0,0,0,11,0,100,0,0,0,0,0,3,0,26241,0,0,0,0,1,0,0,0,0,0,0,0,'Brann Snow Target - On spawn - Morph to model'),
+(@BRANN*100,9,0,0,0,0,100,0,0,0,0,0,66,0,0,0,0,0,0,23,0,0,0,0,0,0,0,'Brann Bronzebeard script - Say text0'),
+(@BRANN*100,9,1,0,0,0,100,0,2000,2000,0,0,1,0,0,0,0,0,0,7,0,0,0,0,0,0,0,'Brann Bronzebeard script - Say text0'),
+(@BRANN*100,9,2,0,0,0,100,0,3100,3100,0,0,5,25,0,0,0,0,0,1,0,0,0,0,0,0,0,'Brann Bronzebeard script - Play emote point'),
+(@BRANN*100,9,3,0,0,0,100,0,3200,3200,0,0,1,1,0,0,0,0,0,7,0,0,0,0,0,0,0,'Brann Bronzebeard script - Say text1'),
+(@BRANN*100,9,4,0,0,0,100,0,5600,5600,0,0,84,2,0,0,0,0,0,7,0,0,0,0,0,0,0,'Brann Bronzebeard script - Player say text2'),
+(@BRANN*100,9,5,0,0,0,100,0,6300,6300,0,0,1,3,0,0,0,0,0,7,0,0,0,0,0,0,0,'Brann Bronzebeard script - Say text3'),
+(@BRANN*100,9,6,0,0,0,100,0,7200,7200,0,0,84,4,0,0,0,0,0,7,0,0,0,0,0,0,0,'Brann Bronzebeard script - Player say text4'),
+(@BRANN*100,9,7,0,0,0,100,0,6400,6400,0,0,1,5,0,0,0,0,0,7,0,0,0,0,0,0,0,'Brann Bronzebeard script - Say text5'),
+(@BRANN*100,9,8,0,0,0,100,0,7200,7200,0,0,1,6,0,0,0,0,0,7,0,0,0,0,0,0,0,'Brann Bronzebeard script - Say text6'),
+(@BRANN*100,9,9,0,0,0,100,0,7200,7200,0,0,1,7,0,0,0,0,0,7,0,0,0,0,0,0,0,'Brann Bronzebeard script - Say text7'),
+(@BRANN*100,9,10,0,0,0,100,0,7200,7200,0,0,1,8,0,0,0,0,0,7,0,0,0,0,0,0,0,'Brann Bronzebeard script - Say text8'),
+(@BRANN*100,9,11,0,0,0,100,0,7100,7100,0,0,1,9,0,0,0,0,0,7,0,0,0,0,0,0,0,'Brann Bronzebeard script - Say text9'),
+(@BRANN*100,9,12,0,0,0,100,0,3000,3000,0,0,33,@BRANN,0,0,0,0,0,7,0,0,0,0,0,0,0,'Brann Bronzebeard script - Quest credit');
+
+DELETE FROM `conditions` WHERE `SourceTypeOrReferenceId`=13 AND `SourceEntry`=55578;
+DELETE FROM `conditions` WHERE `SourceTypeOrReferenceId`=15 AND `SourceGroup`=@GOSSIP;
+INSERT INTO `conditions` (`SourceTypeOrReferenceId`, `SourceGroup`, `SourceEntry`, `ElseGroup`, `ConditionTypeOrReference`, `ConditionValue1`, `ConditionValue2`, `ConditionValue3`, `ErrorTextId`, `ScriptName`, `Comment`) VALUES
+(13,1,55578,0,31,3,@BRANN,0,0,'', 'Spell Brann Signal to Self targets Brann'),
+(15,@GOSSIP,0,0,9,@QUEST,0,0,0,'', 'Show gossip menu option if player has quest 12920'),
+(15,@GOSSIP,1,0,9,12926,0,0,0,'', 'Show gossip menu option if player has quest 12926');
diff --git a/sql/updates/world/2013_01_18_01_world_creature_template_addon.sql b/sql/updates/world/2013_01_18_01_world_creature_template_addon.sql
new file mode 100644
index 00000000000..e3d6feaeecd
--- /dev/null
+++ b/sql/updates/world/2013_01_18_01_world_creature_template_addon.sql
@@ -0,0 +1,22 @@
+-- remove auras that are already applied in SAI
+UPDATE `creature_template_addon` SET `auras`='' WHERE `entry` IN (
+215, -- Defias Night Runner
+579, -- Shadowhide Assassin
+938, -- Kurzen Commando
+1040, -- Fen Creeper
+8550, -- Shadowmage
+9299, -- Gaeriyan
+10414, -- Patchwork Horror
+10426, -- Crimson Inquisitor
+10471, -- Scholomance Acolyte
+11885, -- Blighthound
+16017, -- Patchwork Golem
+16375, -- Sewage Slime
+21080, -- Dormant Infernal
+22853, -- Illidari Defiler
+24818, -- Anvilrage Taskmaster
+25596, -- Infected Kodo Beast
+25781, -- Oil Pool
+26624, -- Wretched Belcher
+26782, -- Crystalline Keeper
+28161); -- Chicken Escapee
diff --git a/sql/updates/world/2013_01_18_02_world_creature_template_addon.sql b/sql/updates/world/2013_01_18_02_world_creature_template_addon.sql
new file mode 100644
index 00000000000..716d59d8724
--- /dev/null
+++ b/sql/updates/world/2013_01_18_02_world_creature_template_addon.sql
@@ -0,0 +1,634 @@
+-- delete 632 creature_template_addon data that contain no information (bytes2=1 is the default)
+DELETE FROM `creature_template_addon` WHERE `entry` IN (
+890, -- Fawn
+6827, -- Crab
+8996, -- Voidwalker Minion
+9396, -- Ground Pounder
+10928, -- Succubus Minion
+15475, -- Beetle
+15547, -- Spectral Charger
+15548, -- Spectral Stallion
+15974, -- Dread Creeper
+15975, -- Carrion Spinner
+15979, -- Tomb Horror
+16017, -- Patchwork Golem
+16020, -- Mad Scientist
+16021, -- Living Monstrosity
+16022, -- Surgical Assistant
+16025, -- Stitched Giant
+16030, -- Maggot
+16036, -- Frenzied Bat
+16037, -- Plagued Bat
+16056, -- Diseased Maggot
+16057, -- Rotting Maggot
+16067, -- Deathcharger Steed
+16068, -- Larva
+16137, -- Naxxramas Military Sub-Boss Trigger
+16145, -- Death Knight Captain
+16154, -- Risen Squire
+16156, -- Dark Touched Warrior
+16165, -- Necro Knight
+16171, -- Coldmist Widow
+16173, -- Shadowbat
+16174, -- Greater Shadowbat
+16175, -- Vampiric Shadowbat
+16176, -- Shadowbeast
+16177, -- Dreadbeast
+16178, -- Phase Hound
+16194, -- Unholy Axe
+16211, -- Naxxramas Combat Dummy
+16215, -- Unholy Staff
+16216, -- Unholy Swords
+16244, -- Infectious Ghoul
+16297, -- Mutated Grub
+16375, -- Sewage Slime
+16412, -- Ghostly Baker
+16459, -- Wanton Hostess
+16460, -- Night Mistress
+16481, -- Ghastly Haunt
+16482, -- Trapped Soul
+16485, -- Arcane Watchman
+16488, -- Arcane Anomaly
+16489, -- Chaotic Sentience
+16491, -- Mana Feeder
+16492, -- Syphoner
+16506, -- Naxxramas Worshipper
+16525, -- Spell Shade
+16529, -- Magical Horror
+16530, -- Mana Warp
+16539, -- Homunculus
+16545, -- Ethereal Spellfilcher
+16595, -- Fleshbeast
+17260, -- Nightbane Helper Target
+17316, -- Chess Square, OUTSIDE BLACK (DND)
+17467, -- Skunk
+17644, -- Infernal Target
+17645, -- Infernal Relay
+17660, -- Skeletal Gryphon
+18955, -- Camera Shaker - 30-90 seconds
+20061, -- Frostbite Invisible Stalker
+21080, -- Dormant Infernal
+21728, -- Skettis Surger
+21804, -- Skettis Kaliri
+21921, -- Chess - Sound Bunny
+22519, -- Chess Piece: Karazhan Invisible Stalker
+22520, -- Chess Piece: Status Bar
+22523, -- Karazhan - Chess, Victory Dummy Tool
+22986, -- Skettis - Invis Raven Stone
+22991, -- Monstrous Kaliri Egg Trigger
+23225, -- Netherwing Drake Escape Point
+23638, -- Longtusk Fisherman
+23643, -- Unstable Mur'ghoul
+23644, -- Mur'ghoul Flesheater
+23645, -- Mur'ghoul Corrupter
+23667, -- Winterskorn Rune-Seer
+23668, -- Winterskorn Rune-Caster
+23669, -- Winterskorn Oracle
+23670, -- Winterskorn Elder
+23674, -- Iron Rune Sage
+23677, -- Frost Nymph
+23678, -- Chill Nymph
+23755, -- Blockade Pirate
+23771, -- Blockade Cannon
+23809, -- Vengeance Landing Cannoneer
+23810, -- Blockade Explosion Bunny
+23821, -- Valgarde Harpoon Target
+23867, -- Vengeance Landing Combatant Trigger
+23870, -- Ember Clutch Ancient
+23874, -- Thornvine Creeper
+23876, -- Spore
+23884, -- Vengeance Landing Crossbow Trigger
+23885, -- Lyana Trigger
+23886, -- Bull Lion Seal
+23887, -- Lion Seal
+23915, -- Westguard Cannon Trigger
+23916, -- Westguard Cannon Trigger (Water)
+23917, -- Cannon Source Trigger
+23919, -- Ice Elemental
+23929, -- Giant Tidecrawler
+23930, -- Trained Plaguehound
+23934, -- North Fleet Salvager
+24076, -- Winterskorn Worg
+24082, -- Proto-Drake Handler
+24084, -- Tunneling Ghoul
+24104, -- New Agamand Deathguard
+24110, -- ELM General Purpose Bunny Large
+24174, -- Fjord Rat
+24177, -- Decomposing Ghoul
+24182, -- Winterskorn Dwelling Credit
+24184, -- Winterskorn Barracks Credit
+24340, -- Rampaging Earth Elemental
+24439, -- Sack of Relics
+24440, -- Gjalerbron Gargoyle
+24469, -- Magnataur Huntress
+24562, -- Nerub'ar Invader
+24567, -- Den Vermin
+24613, -- Mammoth Calf
+24614, -- Wooly Mammoth
+24633, -- Rabid Brown Bear
+24635, -- Dragonflayer Harpooner
+24637, -- Great Reef Shark
+24642, -- Drunken Northsea Pirate
+24653, -- Flying Machine
+24673, -- Frostwing Chimaera
+24676, -- Crazed Northsea Slaver
+24677, -- Spearfang Worg
+24681, -- Island Shoveltusk
+24694, -- Vrykul Harpoon Gun (Wyrmskull)
+24846, -- Iron Dwarf
+24862, -- Mage Hunter Target
+24863, -- Frosthorn Kid
+24871, -- Risen Vrykul Ancestor
+24872, -- Blood Shade
+24883, -- Rodin Lightning Enabler
+24901, -- Maddened Frosthorn
+25198, -- Winterfin Gatherer
+25201, -- Winterfin Tadpole
+25204, -- Glimmer Bay Orca
+25215, -- Winterfin Shorestriker
+25216, -- Winterfin Oracle
+25217, -- Winterfin Warrior
+25225, -- Practice Dummy
+25350, -- Risen Longrunner
+25351, -- Ghostly Sage
+25355, -- Beryl Hound
+25362, -- Warsong Swine
+25377, -- Brittle Skeleton
+25378, -- En'kilah Necromancer
+25383, -- En'kilah Abomination
+25390, -- En'kilah Hatchling
+25391, -- En'kilah Focus Crystal
+25393, -- En'kilah Ghoul
+25396, -- Naxxanar Skeletal Mage
+25415, -- Enraged Tempest
+25417, -- Raging Boiler
+25419, -- Boiling Spirit
+25422, -- Mystical Webbing
+25428, -- Magmoth Shaman
+25429, -- Magmoth Forager
+25431, -- Kaskala Ancestor
+25432, -- Mate of Magmothregar
+25433, -- Offspring of Magmothregar
+25452, -- Scourged Mammoth
+25454, -- Tundra Crawler
+25464, -- Bloodspore Moth
+25468, -- Bloodspore Roaster
+25501, -- Gammoth Tender
+25534, -- En'kilah Blood Globe
+25600, -- Unliving Swine
+25610, -- Scourge Prisoner
+25615, -- Plagued Magnataur
+25623, -- Harvest Collector
+25651, -- Cultist Necrolyte
+25660, -- Festering Ghoul
+25668, -- Vengeful Taunka Spirit
+25670, -- ELM General Purpose Bunny (scale x3)
+25675, -- Tundra Wolf
+25677, -- Borean Frog
+25684, -- Talramas Abomination
+25685, -- Gorloc Waddler
+25686, -- Gorloc Gibberer
+25687, -- Gorloc Steam Belcher
+25699, -- Gorloc Mud Splasher
+25700, -- Gorloc Hunter
+25701, -- Gorloc Dredger
+25707, -- Magic-bound Ancient
+25709, -- Glacial Ancient
+25713, -- Blue Drakonid Supplicant
+25715, -- Frozen Elemental
+25717, -- Coldarra Scalesworn
+25718, -- Coldarra Mage Slayer
+25719, -- Coldarra Spellbinder
+25721, -- Arcane Serpent
+25722, -- Coldarra Spellweaver
+25724, -- Ascended Mage Hunter
+25728, -- Coldarra Wyrmkin
+25781, -- Oil Pool
+25829, -- Marsh Fawn
+25843, -- Northsea Thug
+25880, -- Minion of Kaw
+25981, -- Scourged Footman
+26047, -- Warsong Worg
+26126, -- Bone Warrior
+26161, -- Farshire Grain Credit
+26175, -- Coldarra - Drake Hunt Invisman
+26189, -- Fleeing Cultist
+26199, -- Snowfall Glade Den Mother
+26200, -- Snowfall Glade Pup
+26201, -- Snowfall Glade Shaman
+26202, -- Ziggurat Defender
+26402, -- Anub'ar Ambusher
+26411, -- Deranged Indu'le Villager
+26413, -- Anub'ar Dreadweaver
+26418, -- Longhoof Grazer
+26435, -- Taunka Move Trigger
+26446, -- Ice Serpent
+26455, -- Moonrest Highborne
+26458, -- Drakkari Plaguebringer
+26461, -- Scourge Corpserender
+26472, -- Highland Mustang
+26480, -- Magnataur Youngling
+26481, -- Magnataur Alpha
+26482, -- Arctic Grizzly
+26488, -- Taunka Pack Kodo
+26492, -- Wastes Digger
+26525, -- Cockroach
+26555, -- Scourge Hulk
+26606, -- Anub'ar Slayer
+26624, -- Wretched Belcher
+26625, -- Darkweb Recluse
+26628, -- Drakkari Scytheclaw
+26636, -- Risen Drakkari Soulmage
+26644, -- Ursus Mauler
+26646, -- Saronite Horror
+26658, -- Reckless Scavenger
+26662, -- Azjol-anak Battleguard
+26669, -- Ymirjar Savage
+26670, -- Ymirjar Flesh Hunter
+26675, -- Spider Summon Target
+26705, -- Snowplain Disciple
+26706, -- Infected Grizzly Bear
+26711, -- Injured Mammoth
+26712, -- Crystal Channel Target
+26728, -- Mage Hunter Initiate
+26729, -- Steward
+26782, -- Crystalline Keeper
+26792, -- Crystalline Protector
+26793, -- Crystalline Frayer
+26889, -- The End of the Line Area Trigger Kill Credit Bunny
+26891, -- Undead Miner
+26937, -- Gong Bunny
+26948, -- Hulking Atrocity
+27165, -- Drained Moonrest Highborne
+27230, -- Silvercoat Stag
+27247, -- Devout Bodyguard
+27254, -- Emerald Lasher
+27283, -- Risen Wintergarde Mage
+27286, -- Dreadbone Invader
+27290, -- Hungering Dead
+27358, -- Burning Depths Necromancer
+27363, -- Smoldering Geist
+27375, -- Risen Gryphon Rider Target
+27402, -- Bone Target Bunny
+27403, -- Strange Ore Target
+27408, -- Duskhowl Prowler
+27418, -- Rothin's Spell Bunny
+27421, -- Fern Feeder Moth
+27438, -- Rainbow Trout
+27449, -- Neltharion's Flame Fire Bunny
+27452, -- Invisible Stalker Grizzly Hills
+27460, -- Mother of Bambina
+27496, -- Refurbished Shredder
+27513, -- Covetous Geist
+27551, -- Enraged Apparition
+27552, -- Reanimated Noble
+27607, -- Plague Wagon
+27633, -- Azure Inquisitor
+27635, -- Azure Spellbinder
+27636, -- Azure Ley-Whelp
+27639, -- Ring-Lord Sorceress
+27640, -- Ring-Lord Conjurer
+27641, -- Centrifuge Construct
+27685, -- Frigid Ghoul Attacker
+27686, -- Frigid Geist Attacker
+27688, -- Alliance Lumberboat
+27689, -- Alliance Lumberboat Explosions
+27702, -- Horde Lumberboat
+27725, -- Ruby Guardian
+27737, -- Risen Zombie
+27745, -- Lordaeron Footman
+27746, -- Lordaeron Knight
+27747, -- High Elf Mage-Priest
+27752, -- High Elf Sorceress
+27823, -- Naxxramas Dreadguard
+27824, -- Naxxramas Shade
+27827, -- Grain Crate Helper
+27836, -- Wailing Soul
+27852, -- Wintergrasp Control Arms
+27869, -- Wintergrasp Detection Unit
+27909, -- Darkweb Victim
+27927, -- Dragonflayer Guardian
+27965, -- Dark Rune Shaper
+27970, -- Raging Construct
+27971, -- Unrelenting Construct
+28001, -- Dreadsaber
+28002, -- Mangal Crocolisk
+28003, -- Bittertide Hydra
+28005, -- Wastes Scavenger
+28008, -- Galakrond Spell Dummy
+28010, -- Stranded Thresher
+28011, -- Emperor Cobra
+28016, -- Drakuru
+28024, -- Rainspeaker Warrior
+28025, -- Rainspeaker Oracle
+28130, -- Invis Lightning Stalker
+28161, -- Chicken Escapee
+28169, -- Stratholme Resident
+28170, -- Frosthowl Screecher
+28202, -- Zul'Drak Rat
+28217, -- Injured Rainspeaker Oracle
+28218, -- Snowblind Ghoul
+28220, -- Frostbitten Corpse
+28221, -- Trapdoor Crawler
+28231, -- Crystalline Tender
+28233, -- Zul'Drak Bat
+28234, -- Tribunal of the Ages
+28242, -- Risen Reaver
+28246, -- Sky Terror
+28254, -- Mistwhisper Lightning Target
+28268, -- Scourged Argent Footman
+28274, -- Plague Sprayer
+28277, -- Harry's Bomber
+28351, -- Flame Breath Trigger (Skadi)
+28367, -- Acherus Dummy
+28407, -- Fjord Penguin
+28417, -- Priest of Rhunok
+28419, -- Frenzied Geist
+28440, -- Tundra Penguin
+28452, -- Elemental Rift
+28466, -- Fruit Tosser
+28492, -- Drak'Tharon - Drakuru Event Invisman 00
+28498, -- The Lich King
+28504, -- Jin'Alai Medicine Man
+28519, -- Withered Troll
+28523, -- Nass Target KC Bunny
+28559, -- Citizen of New Avalon
+28560, -- Citizen of New Avalon
+28584, -- Unbound Firestorm
+28585, -- Slag
+28605, -- Havenshire Stallion
+28627, -- Wood Pile Dummy
+28643, -- Rain of Darkness Dummy
+28655, -- Sky Darkener Target
+28660, -- Citizen of Havenshire
+28662, -- Citizen of Havenshire
+28717, -- Overlord Drakuru
+28733, -- Anub'ar Shadowcaster
+28739, -- Blight Cauldron Bunny 00
+28745, -- Alarmed Blightguard
+28750, -- Blight Geist
+28751, -- Geist WP Bunny
+28769, -- Shadowy Tormentor
+28778, -- Scourgewagon Bunny
+28789, -- Explosion Guy
+28804, -- Plague Spreader
+28826, -- Stormfury Revenant
+28833, -- Scarlet Cannon
+28835, -- Stormforged Construct
+28839, -- Scarlet Cover Dummy
+28850, -- Scarlet Land Cannon
+28901, -- Acherus Deathcharger
+28903, -- Scourge Plaguehound
+28905, -- Gluttonous Geist
+28906, -- Scourge Gryphon
+28920, -- Stormforged Giant
+28931, -- Blightblood Troll
+28932, -- Blight Effect Bunny
+28935, -- Acherus Dummy
+28960, -- Totally Generic Bunny (JSB)
+29013, -- Perch Guardian
+29026, -- Kolramas Slime
+29027, -- Wild Growth Stalker
+29036, -- Servant of Freya
+29048, -- Ulduar Monitor
+29104, -- Scarlet Ballista
+29128, -- Anub'ar Prime Guard
+29189, -- Howling Geist
+29301, -- Camp Winterhoof Wayfarer
+29326, -- Ichoron Summon Target
+29328, -- Arctic Hare
+29392, -- Ravenous Jaws
+29395, -- Erekem Guard
+29444, -- Drakkari Snake
+29449, -- Vargul Deathwaker
+29450, -- Vargul Runelord
+29452, -- Vargul Blighthound
+29461, -- Icetip Crawler
+29466, -- Goblin Prisoner
+29479, -- Shoveltusk Forager
+29483, -- K3 Perimeter Turret
+29485, -- Dolomite Giant
+29486, -- Tamed Shoveltusk
+29487, -- Wild Shoveltusk
+29504, -- Seething Revenant
+29517, -- Darkmender's Ghoul
+29521, -- Unworthy Initiate Anchor
+29549, -- Brunnhildar Riding Bear
+29551, -- Brunnhildar Bearhandler
+29558, -- Frost Giant Target Bunny
+29559, -- Lion Seal Whelp
+29562, -- Icemaw Bear
+29614, -- Onslaught Darkweaver
+29630, -- Fanged Pit Viper
+29682, -- Slad'ran Summon Target
+29697, -- Drakuru Prophet
+29700, -- Drakuru Shackles
+29710, -- Onslaught Destrier
+29730, -- Frostborn Stormrider
+29746, -- Databank
+29752, -- Databank Core
+29774, -- Spitting Cobra
+29798, -- Hyldsmeet Proto-Drake
+29805, -- Captive Proto Drake Beam Bunny
+29811, -- Frostborn Scout
+29820, -- Drakkari God Hunter
+29822, -- Drakkari Fire Weaver
+29826, -- Drakkari Medicine Man
+29830, -- Living Mojo
+29832, -- Drakkari Golem
+29912, -- Obedience Crystal
+29920, -- Ruins Dweller
+29958, -- Tundra Ram
+29960, -- Earthen Stoneguard
+30012, -- Victorious Challenger
+30046, -- Yulda the Stormspeaker
+30066, -- Valkyrion Harpoon Gun
+30071, -- Stitched Colossus
+30078, -- [DND]Wyrmrest Temple Beam Target
+30172, -- Ahn'kahar Swarm Egg
+30173, -- Ahn'kahar Guardian Egg
+30181, -- Jedoga Controller
+30236, -- Argent Cannon
+30250, -- Valhalas Vargul
+30277, -- Ahn'kahar Slasher
+30278, -- Ahn'kahar Spell Flinger
+30286, -- Frostbringer
+30298, -- Invisible Stalker (Float, Uninteractible, LargeAOI)
+30312, -- Shadow Vault Boneguard
+30335, -- Shadow Vault Gryphon
+30416, -- Bound Fire Elemental
+30418, -- Bound Air Elemental
+30419, -- Bound Water Elemental
+30430, -- Sentry Worg
+30576, -- Vile Like Fire! Kill Credit Bunny
+30599, -- Vile Like Fire! Fire Bunny
+30633, -- Water Terror
+30640, -- [DND] Icecrown Airship (A) - Cannon Target
+30642, -- Water Terror
+30649, -- [DND] Icecrown Airship (H) - Cannon Target
+30651, -- [DND] Icecrown Airship (A) - Cannon, Odd
+30675, -- Argent Champion
+30687, -- Skeletal Constructor
+30689, -- Chained Abomination
+30701, -- Vile Creeper
+30842, -- Wandering Shadow
+30845, -- Living Lasher
+30848, -- Whispering Wind
+30857, -- Defense Dummy Target
+30887, -- Scourge Package
+30894, -- Lithe Stalker
+30897, -- Marnak
+30898, -- Kaddrak
+30899, -- Abedneum
+30900, -- Argent Mason
+30920, -- Lumbering Atrocity
+30947, -- Eidolon Watcher
+30951, -- Restless Lookout
+30952, -- Hungering Plaguehound
+30960, -- Risen Soldier
+30985, -- Summoned Soldier
+31041, -- Dispirited Ent
+31043, -- Reanimated Crusader
+31049, -- Geist Return Bunny
+31075, -- Scourge Bomb
+31077, -- Safirdrang's Chill Target
+31126, -- Agitated Stratholme Citizen
+31127, -- Agitated Stratholme Resident
+31131, -- Containment Crystal
+31140, -- Hulking Abomination
+31142, -- Icy Ghoul
+31145, -- Shadow Adept
+31147, -- Vicious Geist
+31150, -- Plagued Fiend
+31155, -- Malefic Necromancer
+31205, -- Risen Alliance Soldier
+31233, -- Sinewy Wolf
+31245, -- Invisible Ooze Stalker
+31250, -- Ebon Blade Defender
+31251, -- Shadow Vault Skirmisher
+31262, -- Blight Falconer
+31266, -- Shadow Vault Assaulter
+31280, -- Ymirheim Spear Gun
+31328, -- Fleeing Horde Soldier
+31330, -- Fleeing Horde Soldier
+31353, -- [DND] Icecrown Airship (N) - Attack Controller
+31411, -- Hulking Horror
+31438, -- Shadow Vault Abomination
+31554, -- Restless Lookout
+31556, -- Hungering Plaguehound
+31644, -- Cosmetic Trigger - Phase 1 - LAB
+31685, -- Borean Marmot
+31718, -- Frostbrood Whelp
+31731, -- Wyrm Reanimator
+31738, -- Cultist Corrupter
+31747, -- Necrotic Webspinner
+31754, -- Glacial Bonelord
+31779, -- Skeletal Archmage
+31780, -- Fallen Spiderlord
+31783, -- Vrykul Necrolord
+31786, -- Oil Slick
+31797, -- Ancient Sentinel
+31812, -- Decomposed Ghoul
+31813, -- Frostskull Magus
+31836, -- Blue Dragon Egg
+31847, -- Scavenging Geist
+31900, -- Scourge Banner-Bearer
+31915, -- Horde Transport Dropoff Bunny
+32149, -- Fallen Hero's Spirit
+32155, -- Destroyed War Machine
+32161, -- Scourge War Engineer
+32202, -- Desolation KC Bunny
+32236, -- Dark Subjugator
+32250, -- Overseer Faedris
+32255, -- Converted Hero
+32257, -- Scourge Converter
+32258, -- Gold Beetle
+32260, -- Enslaved Minion
+32262, -- Shadow Channeler
+32264, -- Aldur'thar Channel Bunny
+32278, -- Harbinger of Horror
+32280, -- Corp'rethar Guardian
+32284, -- Scourge Soulbinder
+32349, -- Cultist Shard Watcher
+32469, -- Ebon Blade Geist
+32479, -- Bone Guard
+32482, -- Pustulent Colossus
+32490, -- Scourge Deathcharger
+32498, -- Glacier Penguin
+32502, -- Ravaged Ghoul
+32505, -- Vargul Wanderer
+32507, -- Cultist Acolyte
+32520, -- Totally Generic Bunny (All Phase)
+32541, -- Initiate's Training Dummy
+32542, -- Disciple's Training Dummy
+32543, -- Veteran's Training Dummy
+32545, -- Initiate's Training Dummy
+32546, -- Ebon Knight's Training Dummy
+32593, -- Skittering Swarmer
+32647, -- Warsong Hold Practice Dummy
+32720, -- Violetta
+32770, -- Enraged Fleshrender
+32771, -- Stitched Brute
+32772, -- Skeletal Footsoldier
+32874, -- Iron Ring Guard
+32875, -- Iron Honor Guard
+32879, -- Thorim Controller
+32885, -- Captured Mercenary Soldier
+32892, -- Thorim Event Bunny
+32922, -- Dark Rune Champion
+32923, -- Dark Rune Commoner
+32924, -- Dark Rune Evoker
+32925, -- Dark Rune Warbringer
+33140, -- Thorim Golem Right Hand Bunny
+33141, -- Thorim Golem Left Hand Bunny
+33229, -- Melee Target
+33337, -- XT-Toy Pile
+33378, -- Thunder Orb
+33430, -- Guardian Lasher
+33431, -- Forest Swarmer
+33500, -- Vezax Bunny
+33525, -- Mangrove Ent
+33526, -- Ironroot Lasher
+33527, -- Nature's Blade
+33575, -- Channel Stalker Freya
+33661, -- Armsweep Stalker Kologarn
+33699, -- Storm Tempered Keeper
+33722, -- Storm Tempered Keeper
+33772, -- Faceless Horror
+33809, -- Rubble Stalker Kologarn
+33818, -- Twilight Adherent
+33819, -- Twilight Frost Mage
+33820, -- Twilight Pyromancer
+33822, -- Twilight Guardian
+33824, -- Twilight Shadowblade
+33838, -- Enslaved Fire Elemental
+33856, -- Bot Summon Trigger
+34014, -- Sanctum Sentry
+34069, -- Molten Colossus
+34133, -- Champion of Hodir
+34134, -- Winter Revenant
+34135, -- Winter Rumbler
+34137, -- Winter Jormungar
+34144, -- Expedition Mercenary
+34145, -- Expedition Engineer
+34146, -- Snow Mound (4)
+34150, -- Snow Mound (6)
+34151, -- Snow Mound (8)
+34184, -- Clockwork Mechanic
+34190, -- Hardened Iron Golem
+34191, -- Trash
+34196, -- Rune Etched Sentry
+34198, -- Iron Mender
+34267, -- Parts Recovery Technician
+34271, -- XD-175 Compactobot
+34273, -- XB-488 Disposalbot
+34300, -- Mature Lasher
+34319, -- [DND] Champion Go-To Bunny
+34716, -- Captive Aspirant
+34907, -- Kvaldir Harpooner
+35106, -- Black Knight Caster
+35473, -- Argent Tournament Post
+35482, -- Hungry Jormungar
+36829, -- Deathspeaker High Priest
+40091, -- Orb Rotation Focus
+40146); -- Halion Controller
diff --git a/sql/updates/world/2013_01_19_00_world_creature_text.sql b/sql/updates/world/2013_01_19_00_world_creature_text.sql
new file mode 100644
index 00000000000..ed52fe13b29
--- /dev/null
+++ b/sql/updates/world/2013_01_19_00_world_creature_text.sql
@@ -0,0 +1 @@
+UPDATE `creature_text` SET `sound`=5802 WHERE `entry`=4832 AND `groupid`=0;
diff --git a/sql/updates/world/2013_01_19_01_world_creature_text.sql b/sql/updates/world/2013_01_19_01_world_creature_text.sql
new file mode 100644
index 00000000000..4fb6bc55c87
--- /dev/null
+++ b/sql/updates/world/2013_01_19_01_world_creature_text.sql
@@ -0,0 +1,2 @@
+UPDATE `creature_text` SET `text`='Just...Dust…',`sound`=5803 WHERE `entry`=4832 AND `groupid`=2;
+UPDATE `creature_text` SET `text`='Who dares disturb my meditation!' WHERE `entry`=4832 AND `groupid`=0;
diff --git a/sql/updates/world/2013_01_19_02_world_conditions.sql b/sql/updates/world/2013_01_19_02_world_conditions.sql
new file mode 100644
index 00000000000..744029f4fa0
--- /dev/null
+++ b/sql/updates/world/2013_01_19_02_world_conditions.sql
@@ -0,0 +1,3 @@
+DELETE FROM `conditions` WHERE `SourceTypeOrReferenceId`=13 AND `SourceEntry`=35475;
+INSERT INTO `conditions` (`SourceTypeOrReferenceId`, `SourceGroup`, `SourceEntry`, `SourceId`, `ElseGroup`, `ConditionTypeOrReference`, `ConditionTarget`, `ConditionValue1`, `ConditionValue2`, `ConditionValue3`, `NegativeCondition`, `ErrorType`, `ErrorTextId`, `ScriptName`, `Comment`) VALUES
+(13, 7, 35475, 0, 0, 27, 0, 80, 2, 0, 0, 0, 0, '', 'Drums of War - level restriction');
diff --git a/sql/updates/world/2013_01_19_03_world_creature_text.sql b/sql/updates/world/2013_01_19_03_world_creature_text.sql
new file mode 100644
index 00000000000..d9a15f4da7a
--- /dev/null
+++ b/sql/updates/world/2013_01_19_03_world_creature_text.sql
@@ -0,0 +1 @@
+UPDATE `creature_text` SET `text`='Just...Dust...' WHERE `entry`=4832 AND `groupid`=2;
diff --git a/sql/updates/world/2013_01_19_04_world_creature_addon.sql b/sql/updates/world/2013_01_19_04_world_creature_addon.sql
new file mode 100644
index 00000000000..fbe99d8edf2
--- /dev/null
+++ b/sql/updates/world/2013_01_19_04_world_creature_addon.sql
@@ -0,0 +1,29 @@
+UPDATE `creature_addon` SET `auras`='' WHERE `guid` IN (
+9785,9784,9771, -- Devotion Aura On Dragonmaw Swamprunner, Centurion, Bonewarder
+5056, -- Unholy Shield on Morbent Fel
+16707,16719,16733,16735,16737,16751,16754,16771,16796,16863,16962,16963,16987,17004,17005,17006,17010,17013,17017,17018,17021,17028, -- Stealth on Syndicate Spy
+16709,16880,16881,16882,16883,16901,16985,17031,17032,17044,17047,17048,17051,17097, -- Stealth on Syndicate Assassin
+11547,11550,11577,11608,11612,11654,11696,11812,11976,12904,12910,12912,13251,13303,13304,13310,13311,13313,13315, -- Stealth on Syndicate Highwayman
+14652, -- Shadow Channeling on Marez Cowl
+13594,13598, -- Defensive Stance on Bael'dun Excavator
+14055, -- Stealth on Mad Magglish
+33157,33158,33159,33160,33161,33162, -- Stealth on Felmusk Shadowstalker
+34187,34189,34191, -- Bear form on Cenarion Protector
+32349, -- Sleep visual on Relara Whitemoon
+40101,40087,40055,40050,40042,40030,40012,40001, -- Inner Fire on Scarlet Chaplain
+31208,31214,31360,31385,31387,31390,31391,31393,31463,31473,31607,31608,31617,31619,31622, -- Fire Shield on Fireman Scalebane
+81447,81446,81444,81440,43695,42624,42622,42607,42605, -- Demon Skin on Sandfury Shadowcaster
+91931,92324,92513,92665,92670,92671, -- Disease Cloud on Stiched Golem
+48477,48563,48585,48761,48765,48768,48769,48772,48809,48816,48818,48827,48832,48836,48837,48921,48922,48923,48924,48926,48983,48987,48993,48999,91411,91412,91415, -- Disease Cloud on Diseased Ghoul
+81927,81931,81940,81987,81995,82301,82316,82341,82355,82378,82394,82396,82397,82413,82426,82479,82510,82611,82612,82613,82615,82662,82763,82771, -- Draining Touch on Vampiric Mistbat
+61856,61928,61931,61940, -- Thorns on Barbed Crawler
+67462,67465,67468,67469, -- Arcane Shield on Warp Aberration
+74883, -- Freeze Anim on Legion Hold Fel Reaver
+132572, -- Shroud of Death on Time-Lost Skettis High Priest
+76348,76349,76351,76353,76355,76356,76358,76359,76360,76362,76363,76364,76365,76366,76368,76369,76370,76371,76372,76373,76374,76376, -- Ghostly Facade on Cleric of Karabor
+12803,12802,12801,12800,12799,12798, -- Lightning Shield on Ashtongue Stormcaller
+132849,132850,132852,132853,132855,132851,132854,132856,132857, -- Phasing Invisibility on Blackwind Warp Chaser
+32943, -- Cat form on Becanna Edune
+132863, -- Oil coat on Oil-Stained Wolf
+126539,126542,126553,126556,126562,126564,126567,126568,126569,126572,126574,126579,126582, -- Arcane Missiles on Crazed Mana-Wraith
+127067); -- Head Crack on Drakkari Earthhshaker
diff --git a/sql/updates/world/2013_01_19_04_world_creature_text.sql b/sql/updates/world/2013_01_19_04_world_creature_text.sql
new file mode 100644
index 00000000000..c14cf5c149a
--- /dev/null
+++ b/sql/updates/world/2013_01_19_04_world_creature_text.sql
@@ -0,0 +1 @@
+UPDATE `creature_text` SET `text`='Who dares disturb my meditation?' WHERE `entry`=4832 AND `groupid`=0;
diff --git a/sql/updates/world/2013_01_19_05_world_creature_addon.sql b/sql/updates/world/2013_01_19_05_world_creature_addon.sql
new file mode 100644
index 00000000000..0a384501e36
--- /dev/null
+++ b/sql/updates/world/2013_01_19_05_world_creature_addon.sql
@@ -0,0 +1,2 @@
+-- delete 31 creature_addon data that contain no information (bytes2=1 is the default)
+DELETE FROM `creature_addon` WHERE `guid` IN (132572,126539,126542,126553,126556,126562,126564,126567,126568,126569,126572,126574,126579,126582,127067,132849,132850,132851,132852,132853,132854,132855,132856,132857,132863,108034,108035,108036,108037,203372,203373);
diff --git a/sql/updates/world/2013_01_19_06_world_misc.sql b/sql/updates/world/2013_01_19_06_world_misc.sql
new file mode 100644
index 00000000000..55d9777a71c
--- /dev/null
+++ b/sql/updates/world/2013_01_19_06_world_misc.sql
@@ -0,0 +1,14 @@
+UPDATE `quest_template` SET `PrevQuestId`=12872 WHERE `Id` IN (12871,12885);
+UPDATE `quest_template` SET `PrevQuestId`=12928 WHERE `Id` IN (12929,13273);
+
+UPDATE `gameobject_template` SET `flags`=`flags`|4 WHERE `entry` IN
+(188364,188501,188502,188503,186684,186390,186950,186954,186955,186912,186662,186618,186587,186595,186607,186938,187027,186427,187026,187022,187023,188702,188703,188705,189983,186632,186619,186591,186397,
+186640,186679,186828,186830,186832,186885,186886,187033,187381,187577,187683,187684,187685,187686,187687,187885,187886,188015,188016,188017,188066,188120,188462,188489,188646,188650,188658,188659,189288,
+189293,189295,189298,189306,190127,190189,190354,190483,190484,190578,190612,190613,190614,190623,190624,190625,190643,190696,190720,191179,191567,191814,191815,192058,192171,192172,192556,192676,192693,
+193091,193092,193196,193197,193404,193560,193561,193767,193792,193793,193943,193945,193946,194158,194159,194238,194340,194341,194423,194424,195022,195037,195274,195344,201367,201384,201794,201937);
+
+UPDATE `gameobject_template` SET `flags`=`flags`|16 WHERE `entry` IN
+(193603,193905,193967,194158,194159,195046,195047,195323,195324,195374,195375,195631,195632,195633,195635,195709,195710,201710,201959);
+
+UPDATE `gameobject_template` SET `faction`=94 WHERE `entry` IN (195046,195047,195631,195632,195633,195635);
+UPDATE `gameobject_template` SET `faction`=35 WHERE `entry` IN (201710,201959);
diff --git a/sql/updates/world/2013_01_19_06_world_trinity_string.sql b/sql/updates/world/2013_01_19_06_world_trinity_string.sql
new file mode 100644
index 00000000000..322d43a7720
--- /dev/null
+++ b/sql/updates/world/2013_01_19_06_world_trinity_string.sql
@@ -0,0 +1,28 @@
+DELETE FROM `trinity_string` WHERE `entry` BETWEEN 820 AND 842;
+INSERT INTO `trinity_string`(`entry`,`content_default`) VALUES
+(820,'* has gossip (%u)'),
+(821,'* is quest giver (%u)'),
+(822,'* is class trainer (%u)'),
+(823,'* is profession trainer(%u)'),
+(824,'* is ammo vendor (%u)'),
+(825,'* is food vendor(%u)'),
+(826,'* is poison vendor (%u)'),
+(827,'* is reagent vendor (%u)'),
+(828,'* can repair (%u)'),
+(829,'* is flight master (%u)'),
+(830,'* is spirit healer (%u)'),
+(831,'* is spirit guide (%u)'),
+(832,'* is innkeeper (%u)'),
+(833,'* is banker (%u)'),
+(834,'* is petitioner (%u)'),
+(835,'* is tabard designer (%u)'),
+(836,'* is battle master (%u)'),
+(837,'* is auctioneer (%u)'),
+(838,'* is stable master (%u)'),
+(839,'* is guild banker (%u)'),
+(840,'* has spell click (%u)'),
+(841,'* is mailbox (%u)'),
+(842,'* is player vehicle (%u)');
+
+UPDATE `trinity_string` SET `content_default`='* is vendor (%u)' WHERE `entry`=545;
+UPDATE `trinity_string` SET `content_default`='* is trainer (%u)' WHERE `entry`=546;
diff --git a/sql/updates/world/2013_01_20_00_world_sai.sql b/sql/updates/world/2013_01_20_00_world_sai.sql
new file mode 100644
index 00000000000..d4e4bb81c41
--- /dev/null
+++ b/sql/updates/world/2013_01_20_00_world_sai.sql
@@ -0,0 +1 @@
+UPDATE `smart_scripts` SET `event_type`=25,`event_flags`=0,`event_param1`=0,`event_param2`=0 WHERE `entryorguid`=16029 AND `source_type`=0 AND `id`=0; -- Sludge Belcher
diff --git a/sql/updates/world/2013_01_20_01_world_creature_text.sql b/sql/updates/world/2013_01_20_01_world_creature_text.sql
new file mode 100644
index 00000000000..d543b02d9fc
--- /dev/null
+++ b/sql/updates/world/2013_01_20_01_world_creature_text.sql
@@ -0,0 +1,10 @@
+UPDATE `creature_text` SET `sound`=14344 WHERE `entry`=29310 AND `groupid`=1 AND `id`=0;
+UPDATE `creature_text` SET `sound`=14345 WHERE `entry`=29310 AND `groupid`=1 AND `id`=1;
+UPDATE `creature_text` SET `text`='Yogg-Saron! Grant me your power!', `sound`=14346 WHERE `entry`=29310 AND `groupid`=2 AND `id`=0;
+UPDATE `creature_text` SET `sound`=14347 WHERE `entry`=29310 AND `groupid`=2 AND `id`=1;
+UPDATE `creature_text` SET `sound`=14348 WHERE `entry`=29310 AND `groupid`=3 AND `id`=0;
+UPDATE `creature_text` SET `sound`=14349 WHERE `entry`=29310 AND `groupid`=3 AND `id`=1;
+UPDATE `creature_text` SET `sound`=14351 WHERE `entry`=29310 AND `groupid`=4;
+UPDATE `creature_text` SET `sound`=14354 WHERE `entry`=29310 AND `groupid`=5 AND `id`=2;
+UPDATE `creature_text` SET `sound`=14355 WHERE `entry`=29310 AND `groupid`=5 AND `id`=3;
+UPDATE `creature_text` SET `text`='The faithful shall be exalted, but there is more work to be done. We will press on until all of Azeroth lies beneath his shadow!', `sound`=14356 WHERE `entry`=29310 AND `groupid`=5 AND `id`=4;
diff --git a/sql/updates/world/2013_01_20_02_world_creature_text.sql b/sql/updates/world/2013_01_20_02_world_creature_text.sql
new file mode 100644
index 00000000000..25c8c046dfc
--- /dev/null
+++ b/sql/updates/world/2013_01_20_02_world_creature_text.sql
@@ -0,0 +1,10 @@
+UPDATE `creature_text` SET `sound`=14430, `type`=14 WHERE `entry`=29306 AND `groupid`=0;
+UPDATE `creature_text` SET `sound`=14431, `type`=14 WHERE `entry`=29306 AND `groupid`=4;
+UPDATE `creature_text` SET `sound`=14432, `type`=14 WHERE `entry`=29306 AND `groupid`=5;
+UPDATE `creature_text` SET `sound`=14433, `type`=14 WHERE `entry`=29306 AND `groupid`=3 AND `id`=0;
+UPDATE `creature_text` SET `sound`=14434, `type`=14 WHERE `entry`=29306 AND `groupid`=3 AND `id`=1;
+UPDATE `creature_text` SET `sound`=14435, `type`=14 WHERE `entry`=29306 AND `groupid`=3 AND `id`=2;
+UPDATE `creature_text` SET `sound`=14436, `type`=14 WHERE `entry`=29306 AND `groupid`=1 AND `id`=0;
+UPDATE `creature_text` SET `sound`=14437, `type`=14, `text`='Who needs gods when we ARE gods?' WHERE `entry`=29306 AND `groupid`=1 AND `id`=1;
+UPDATE `creature_text` SET `sound`=14438, `type`=14 WHERE `entry`=29306 AND `groupid`=1 AND `id`=2;
+UPDATE `creature_text` SET `sound`=14439, `type`=14 WHERE `entry`=29306 AND `groupid`=2;
diff --git a/sql/updates/world/2013_01_20_03_world_creature_text.sql b/sql/updates/world/2013_01_20_03_world_creature_text.sql
new file mode 100644
index 00000000000..1ea2c121e1e
--- /dev/null
+++ b/sql/updates/world/2013_01_20_03_world_creature_text.sql
@@ -0,0 +1,15 @@
+-- Moorabi
+UPDATE `creature_text` SET `sound`=14721, `text`='We fought back da Scourge. What chance joo thinkin'' JOO got?' WHERE `entry`=29305 AND `groupid`=0;
+UPDATE `creature_text` SET `sound`=14722 WHERE `entry`=29305 AND `groupid`=3;
+UPDATE `creature_text` SET `sound`=14723, `text`='Da ground gonna swallow you up!' WHERE `entry`=29305 AND `groupid`=4;
+UPDATE `creature_text` SET `sound`=14726, `text`='Who gonna stop me? You?' WHERE `entry`=29305 AND `groupid`=1 AND `id`=0;
+UPDATE `creature_text` SET `sound`=14727, `text`='Not so tough now!!' WHERE `entry`=29305 AND `groupid`=1 AND `id`=1;
+UPDATE `creature_text` SET `sound`=14728, `text`='If our gods can die... den so can we....' WHERE `entry`=29305 AND `groupid`=2;
+
+-- Slad'ran
+UPDATE `creature_text` SET `sound`=14444, `type`=14 WHERE `entry`=29304 AND `groupid`=3;
+UPDATE `creature_text` SET `sound`=14445, `type`=14, `text`='A thousssand fangsss gonna rend your flesh!' WHERE `entry`=29304 AND `groupid`=4;
+UPDATE `creature_text` SET `sound`=14446, `type`=14, `text`='You not breathin''? Good.' WHERE `entry`=29304 AND `groupid`=1 AND `id`=0;
+UPDATE `creature_text` SET `sound`=14447, `type`=14 WHERE `entry`=29304 AND `groupid`=1 AND `id`=1;
+UPDATE `creature_text` SET `sound`=14448, `type`=14, `text`='I eat you next, mon.' WHERE `entry`=29304 AND `groupid`=1 AND `id`=2;
+UPDATE `creature_text` SET `sound`=14449, `type`=14, `text`='I sssee now... Ssscourge wasss not... our greatessst enemy....' WHERE `entry`=29304 AND `groupid`=2;
diff --git a/sql/updates/world/2013_01_20_03_world_spell_script_names.sql b/sql/updates/world/2013_01_20_03_world_spell_script_names.sql
new file mode 100644
index 00000000000..0cd28de0472
--- /dev/null
+++ b/sql/updates/world/2013_01_20_03_world_spell_script_names.sql
@@ -0,0 +1,81 @@
+DELETE FROM `spell_script_names` WHERE `spell_id` IN (
+48792, -- spell_dk_icebound_fortitude
+59754, -- spell_dk_rune_tap_party
+55233, -- spell_dk_vampiric_blood
+-1850, -- spell_dru_dash
+48391, -- spell_dru_owlkin_frenzy
+29166, -- spell_dru_innervate
+34246, -- spell_dru_idol_lifebloom
+60779, -- spell_dru_idol_lifebloom
+-1079, -- spell_dru_rip
+-61391,-- spell_dru_typhoon
+63845, -- spell_gen_create_lance
+28702, -- spell_gen_netherbloom
+28720, -- spell_gen_nightmare_vine
+26400, -- spell_item_arcane_shroud
+8342, -- spell_item_goblin_jumper_cables
+22999, -- spell_item_goblin_jumper_cables_xl
+54732, -- spell_item_gnomish_army_knife
+17512, -- spell_item_piccolo_of_the_flaming_fire
+48129, -- spell_item_scroll_of_recall
+60320, -- spell_item_scroll_of_recall
+60321, -- spell_item_scroll_of_recall
+28862, -- spell_item_the_eye_of_diminution
+-543, -- spell_mage_fire_frost_ward
+-6143, -- spell_mage_fire_frost_ward
+-11426,-- spell_mage_ice_barrier
+-1463, -- spell_mage_mana_shield
+1038, -- spell_pal_hand_of_salvation
+58597, -- spell_pal_sacred_shield
+-7001, -- spell_pri_lightwell_renew
+-17, -- spell_pri_power_word_shield
+-1943, -- spell_rog_rupture
+-51490,-- spell_sha_thunderstorm
+-7235, -- spell_warl_shadow_ward
+5246, -- spell_warr_intimidating_shout
+-772, -- spell_warr_rend
+64380, -- spell_warr_shattering_throw
+65941, -- spell_warr_shattering_throw
+50725, -- spell_warr_vigilance_trigger
+26275 -- spell_winter_veil_px_238_winter_wondervolt
+);
+INSERT INTO `spell_script_names` (`spell_id`,`ScriptName`) VALUES
+(48792, 'spell_dk_icebound_fortitude'),
+(59754, 'spell_dk_rune_tap_party'),
+(55233, 'spell_dk_vampiric_blood'),
+(-1850, 'spell_dru_dash'),
+(48391, 'spell_dru_owlkin_frenzy'),
+(29166, 'spell_dru_innervate'),
+(34246, 'spell_dru_idol_lifebloom'),
+(60779, 'spell_dru_idol_lifebloom'),
+(-1079, 'spell_dru_rip'),
+(-61391,'spell_dru_typhoon'),
+(63845, 'spell_gen_create_lance'),
+(28702, 'spell_gen_netherbloom'),
+(28720, 'spell_gen_nightmare_vine'),
+(26400, 'spell_item_arcane_shroud'),
+(8342, 'spell_item_goblin_jumper_cables'),
+(22999, 'spell_item_goblin_jumper_cables_xl'),
+(54732, 'spell_item_gnomish_army_knife'),
+(17512, 'spell_item_piccolo_of_the_flaming_fire'),
+(48129, 'spell_item_scroll_of_recall'),
+(60320, 'spell_item_scroll_of_recall'),
+(60321, 'spell_item_scroll_of_recall'),
+(28862, 'spell_item_the_eye_of_diminution'),
+(-543, 'spell_mage_fire_frost_ward'),
+(-6143, 'spell_mage_fire_frost_ward'),
+(-11426,'spell_mage_ice_barrier'),
+(-1463, 'spell_mage_mana_shield'),
+(1038, 'spell_pal_hand_of_salvation'),
+(58597, 'spell_pal_sacred_shield'),
+(-7001, 'spell_pri_lightwell_renew'),
+(-17, 'spell_pri_power_word_shield'),
+(-1943, 'spell_rog_rupture'),
+(-51490,'spell_sha_thunderstorm'),
+(-7235, 'spell_warl_shadow_ward'),
+(5246, 'spell_warr_intimidating_shout'),
+(-772, 'spell_warr_rend'),
+(64380, 'spell_warr_shattering_throw'),
+(65941, 'spell_warr_shattering_throw'),
+(50725, 'spell_warr_vigilance_trigger'),
+(26275, 'spell_winter_veil_px_238_winter_wondervolt');
diff --git a/sql/updates/world/2013_01_20_04_world_creature_text.sql b/sql/updates/world/2013_01_20_04_world_creature_text.sql
new file mode 100644
index 00000000000..5c457530f92
--- /dev/null
+++ b/sql/updates/world/2013_01_20_04_world_creature_text.sql
@@ -0,0 +1,12 @@
+-- Text for Crushridge Warmonger
+SET @ENTRY := 2287;
+DELETE FROM `creature_text` WHERE `entry`=@ENTRY AND `groupid`=1;
+INSERT INTO `creature_text` (`entry`,`groupid`,`id`,`text`,`type`,`language`,`probability`,`emote`,`duration`,`sound`,`comment`) VALUES
+(@ENTRY,1,0,'%s goes into a frenzy!',16,0,100,0,0,0,'Crushridge Warmonger');
+
+UPDATE `smart_scripts` SET `event_type`=61 WHERE `entryorguid` = 12236 AND `id`=9;
+UPDATE `smart_scripts` SET `event_type`=61 WHERE `entryorguid` = 2428 AND `id`=10;
+UPDATE `smart_scripts` SET `link`=0 WHERE `entryorguid` = 2287 AND `id`=1;
+UPDATE `smart_scripts` SET `event_flags`=1 WHERE `entryorguid` = 2287 AND `id`=3;
+UPDATE `smart_scripts` SET `action_param1`=1 WHERE `entryorguid` = 2287 AND `id`=4;
+UPDATE `smart_scripts` SET `event_type`=61 WHERE `entryorguid` = 8561 AND `id`=11;
diff --git a/sql/updates/world/2013_01_20_05_world_sai.sql b/sql/updates/world/2013_01_20_05_world_sai.sql
new file mode 100644
index 00000000000..9539bc546b1
--- /dev/null
+++ b/sql/updates/world/2013_01_20_05_world_sai.sql
@@ -0,0 +1,41 @@
+UPDATE `smart_scripts` SET `event_type`=61 WHERE `entryorguid`=14390 AND `source_type`=0 AND `id`=9;
+UPDATE `smart_scripts` SET `event_type`=61 WHERE `entryorguid`=2587 AND `source_type`=0 AND `id`=8;
+UPDATE `smart_scripts` SET `event_type`=61 WHERE `entryorguid`=4064 AND `source_type`=0 AND `id`=9;
+UPDATE `smart_scripts` SET `link`=3 WHERE `entryorguid`=29181 AND `source_type`=0 AND `id`=2;
+UPDATE `smart_scripts` SET `link`=0,`event_type`=61,`event_param2`=0,`event_param3`=0 WHERE `entryorguid`=29181 AND `source_type`=0 AND `id`=3;
+UPDATE `smart_scripts` SET `link`=0 WHERE `entryorguid`=29186 AND `source_type`=0 AND `id`=1;
+UPDATE `smart_scripts` SET `link`=0 WHERE `entryorguid`=29199 AND `source_type`=0 AND `id`=1;
+UPDATE `smart_scripts` SET `link`=0 WHERE `entryorguid`=29199 AND `source_type`=0 AND `id`=2;
+UPDATE `smart_scripts` SET `link`=0 WHERE `entryorguid`=29204 AND `source_type`=0 AND `id`=1;
+UPDATE `smart_scripts` SET `link`=0 WHERE `entryorguid`=29204 AND `source_type`=0 AND `id`=2;
+UPDATE `smart_scripts` SET `link`=0 WHERE `entryorguid`=29200 AND `source_type`=0 AND `id`=1;
+UPDATE `smart_scripts` SET `link`=0 WHERE `entryorguid`=29200 AND `source_type`=0 AND `id`=2;
+UPDATE `smart_scripts` SET `link`=0 WHERE `entryorguid`=29176 AND `source_type`=0 AND `id`=1;
+UPDATE `smart_scripts` SET `event_type`=61 WHERE `entryorguid`=500 AND `source_type`=0 AND `id`=10;
+UPDATE `smart_scripts` SET `event_type`=61 WHERE `entryorguid`=4462 AND `source_type`=0 AND `id`=10;
+UPDATE `smart_scripts` SET `event_type`=61 WHERE `entryorguid`=14467 AND `source_type`=0 AND `id`=1;
+UPDATE `smart_scripts` SET `link`=0 WHERE `entryorguid`=2719 AND `source_type`=0 AND `id`=3;
+UPDATE `smart_scripts` SET `event_type`=61 WHERE `entryorguid`=6004 AND `source_type`=0 AND `id`=9;
+UPDATE `smart_scripts` SET `event_type`=61 WHERE `entryorguid`=23580 AND `source_type`=0 AND `id`=1;
+UPDATE `smart_scripts` SET `event_type`=61 WHERE `entryorguid`=17270 AND `source_type`=0 AND `id`=13;
+UPDATE `smart_scripts` SET `event_type`=61 WHERE `entryorguid`=29836 AND `source_type`=0 AND `id`=13;
+UPDATE `smart_scripts` SET `event_type`=61 WHERE `entryorguid`=578 AND `source_type`=0 AND `id`=8;
+UPDATE `smart_scripts` SET `event_type`=61 WHERE `entryorguid`=1397 AND `source_type`=0 AND `id`=14;
+UPDATE `smart_scripts` SET `event_type`=61 WHERE `entryorguid`=1123 AND `source_type`=0 AND `id`=10;
+UPDATE `smart_scripts` SET `event_type`=61 WHERE `entryorguid`=1123 AND `source_type`=0 AND `id`=16;
+UPDATE `smart_scripts` SET `link`=0 WHERE `entryorguid`=3142 AND `source_type`=0 AND `id`=0;
+UPDATE `smart_scripts` SET `event_type`=61 WHERE `entryorguid`=15641 AND `source_type`=0 AND `id`=9;
+UPDATE `smart_scripts` SET `event_type`=61 WHERE `entryorguid`=424 AND `source_type`=0 AND `id`=9;
+UPDATE `smart_scripts` SET `link`=0 WHERE `entryorguid`=19507 AND `source_type`=0 AND `id`=1;
+UPDATE `smart_scripts` SET `link`=0 WHERE `entryorguid`=3643 AND `source_type`=1 AND `id`=0;
+UPDATE `smart_scripts` SET `event_type`=61 WHERE `entryorguid`=1162 AND `source_type`=0 AND `id`=9;
+UPDATE `smart_scripts` SET `event_type`=61 WHERE `entryorguid`=1162 AND `source_type`=0 AND `id`=10;
+UPDATE `smart_scripts` SET `link`=0 WHERE `entryorguid`=3987 AND `source_type`=0 AND `id`=1;
+
+UPDATE `smart_scripts` SET `event_flags`=0 WHERE `entryorguid`=6066 AND `source_type`=0 AND `id`=0;
+UPDATE `smart_scripts` SET `event_flags`=1 WHERE `entryorguid`=8477 AND `source_type`=0 AND `id`=0;
+UPDATE `smart_scripts` SET `event_flags`=0 WHERE `entryorguid`=29206 AND `source_type`=0 AND `id`=0;
+UPDATE `smart_scripts` SET `event_flags`=0 WHERE `entryorguid`=29182 AND `source_type`=0 AND `id` IN (0,1);
+UPDATE `smart_scripts` SET `event_flags`=0 WHERE `entryorguid`=29177 AND `source_type`=0 AND `id`=0;
+UPDATE `smart_scripts` SET `event_flags`=1 WHERE `entryorguid`=29684 AND `source_type`=0 AND `id` IN (0,1);
+UPDATE `smart_scripts` SET `event_flags`=1 WHERE `entryorguid`=15631 AND `source_type`=0 AND `id`=0;
diff --git a/sql/updates/world/2013_01_20_06_world_creature.sql b/sql/updates/world/2013_01_20_06_world_creature.sql
new file mode 100644
index 00000000000..c00ff11ea4a
--- /dev/null
+++ b/sql/updates/world/2013_01_20_06_world_creature.sql
@@ -0,0 +1,24 @@
+-- Image of Commander Ameer <The Protectorate> (22919)
+SET @GUID := 43492;
+
+UPDATE `creature_template` SET `npcflag`=`npcflag`|2,`unit_flags`=`unit_flags`&~33554432 WHERE `entry`=22919;
+
+DELETE FROM `creature` WHERE `guid`=@GUID;
+INSERT INTO `creature` (`guid`,`id`,`map`,`position_x`,`position_y`,`position_z`,`orientation`,`spawntimesecs`,`curhealth`) VALUES
+(@GUID,22919,530,3866.55,5978.68,291.792,4.10061,300,6986);
+
+DELETE FROM `creature_questrelation` WHERE `id`=22919;
+INSERT INTO `creature_questrelation` (`id`,`quest`) VALUES
+(22919,10981), -- Nexus-Prince Shaffar's Personal Chamber
+(22919,10975), -- Purging the Chambers of Bash'ir
+(22919,10977), -- Stasis Chambers of the Mana-Tombs
+(22919,10976); -- The Mark of the Nexus-King
+
+DELETE FROM `creature_involvedrelation` WHERE `id`=22919;
+INSERT INTO `creature_involvedrelation` (`id`,`quest`) VALUES
+(22919,10981), -- Nexus-Prince Shaffar's Personal Chamber
+(22919,10975), -- Purging the Chambers of Bash'ir
+(22919,10974), -- Stasis Chambers of Bash'ir
+(22919,10977), -- Stasis Chambers of the Mana-Tombs
+(22919,10982), -- The Eye of Haramad
+(22919,10976); -- The Mark of the Nexus-King
diff --git a/sql/updates/world/2013_01_20_07_world_sai.sql b/sql/updates/world/2013_01_20_07_world_sai.sql
new file mode 100644
index 00000000000..f8d001e73db
--- /dev/null
+++ b/sql/updates/world/2013_01_20_07_world_sai.sql
@@ -0,0 +1,5 @@
+UPDATE `smart_scripts` SET `link`=0 WHERE `entryorguid`=234 AND `source_type`=0 AND `id`=0;
+UPDATE `smart_scripts` SET `event_type`=61 WHERE `entryorguid`=2554 AND `source_type`=0 AND `id`=9;
+UPDATE `smart_scripts` SET `link`=0 WHERE `entryorguid`=12265 AND `source_type`=0 AND `id`=1;
+UPDATE `smart_scripts` SET `link`=0 WHERE `entryorguid`=12265 AND `source_type`=0 AND `id`=3;
+UPDATE `smart_scripts` SET `event_type`=61 WHERE `entryorguid`=16704 AND `source_type`=0 AND `id`=13;
diff --git a/sql/updates/world/2013_01_20_08_world_sai.sql b/sql/updates/world/2013_01_20_08_world_sai.sql
new file mode 100644
index 00000000000..411d69e08d2
--- /dev/null
+++ b/sql/updates/world/2013_01_20_08_world_sai.sql
@@ -0,0 +1,4 @@
+UPDATE `smart_scripts` SET `link`=0,`event_type`=61 WHERE `entryorguid`=13601 AND `source_type`=0 AND `id`=4;
+UPDATE `smart_scripts` SET `event_type`=61 WHERE `entryorguid`=13601 AND `source_type`=0 AND `id`=10;
+UPDATE `smart_scripts` SET `event_type`=61 WHERE `entryorguid`=17670 AND `source_type`=0 AND `id`=13;
+UPDATE `smart_scripts` SET `event_type`=61 WHERE `entryorguid`=11878 AND `source_type`=0 AND `id`=13;
diff --git a/sql/updates/world/2013_01_20_09_world_spell_dbc.sql b/sql/updates/world/2013_01_20_09_world_spell_dbc.sql
new file mode 100644
index 00000000000..d8f466173f3
--- /dev/null
+++ b/sql/updates/world/2013_01_20_09_world_spell_dbc.sql
@@ -0,0 +1,18 @@
+-- Add missing spells to spell_dbc
+DELETE FROM `spell_dbc` WHERE `Id` IN (24211,24246,24235,7939);
+INSERT INTO `spell_dbc` (`Id`,`SchoolMask`,`Dispel`,`Mechanic`,`Attributes`,`AttributesEx`,`AttributesEx2`,`AttributesEx3`,`AttributesEx4`,`Stances`,`StancesNot`,`Targets`,`CastingTimeIndex`,`AuraInterruptFlags`,`ProcFlags`,`ProcChance`,`ProcCharges`,`MaxLevel`,`BaseLevel`,`SpellLevel`,`DurationIndex`,`RangeIndex`,`StackAmount`,`EquippedItemClass`,`EquippedItemSubClassMask`,`EquippedItemInventoryTypeMask`,`Effect1`,`Effect2`,`Effect3`,`EffectDieSides1`,`EffectDieSides2`,`EffectDieSides3`,`EffectRealPointsPerLevel1`,`EffectRealPointsPerLevel2`,`EffectRealPointsPerLevel3`,`EffectBasePoints1`,`EffectBasePoints2`,`EffectBasePoints3`,`EffectMechanic1`,`EffectMechanic2`,`EffectMechanic3`,`EffectImplicitTargetA1`,`EffectImplicitTargetA2`,`EffectImplicitTargetA3`,`EffectImplicitTargetB1`,`EffectImplicitTargetB2`,`EffectImplicitTargetB3`,`EffectRadiusIndex1`,`EffectRadiusIndex2`,`EffectRadiusIndex3`,`EffectApplyAuraName1`,`EffectApplyAuraName2`,`EffectApplyAuraName3`,`EffectAmplitude1`,`EffectAmplitude2`,`EffectAmplitude3`,`EffectMultipleValue1`,`EffectMultipleValue2`,`EffectMultipleValue3`,`EffectMiscValue1`,`EffectMiscValue2`,`EffectMiscValue3`,`EffectTriggerSpell1`,`EffectTriggerSpell2`,`EffectTriggerSpell3`,`Comment`,`MaxTargetLevel`,`SpellFamilyName`,`SpellFamilyFlags1`,`SpellFamilyFlags2`,`MaxAffectedTargets`,`DmgClass`,`PreventionType`,`DmgMultiplier1`,`DmgMultiplier2`,`DmgMultiplier3`,`EffectMiscValueB1`) VALUES
+(24211,0,0,0,256,0,4,0,0,0,0,0,1,0,0,101,0,0,0,0,0,0,0,-1,0,0,63,0,0,1,0,0,0,0,0,4999,0,0,0,0,0,22,0,0,0,0,0,7,0,0,18,0,0,0,0,0,0,0,0,0,0,0,0,0,0,'Mark of Arlokk',0,0,0,0,0,0,0,1,0,0,0),
+(24246,0,0,0,256,0,0,0,0,0,0,0,1,0,0,101,0,0,0,0,6,0,0,-1,0,0,28,0,0,1,0,0,0,0,0,0,0,0,0,0,0,18,0,0,0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,15101,0,0,0,0,0,'Summon Zulian Prowler',0,0,0,0,0,0,0,1,0,0,64),
+(24235,0,0,0,272,268435456,0,0,0,0,0,0,1,0,0,101,0,0,0,0,1,0,0,-1,0,0,6,0,0,0,0,0,0,0,0,9999,0,0,0,0,0,1,0,0,0,0,0,0,0,0,18,0,0,0,0,0,0,0,0,0,0,0,0,0,0,'Super Invis',0,0,0,0,0,0,0,1,1,1,0),
+(7939,0,5,0,402915728,268435456,0,0,0,0,0,0,1,6147,0,101,0,0,1,1,21,1,0,-1,-1,0,6,0,0,1,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 'Sneak Rank 1',0,0,0,0,0,0,0,-1,1,1,0);
+
+-- Add script name to Zulian Prowler
+UPDATE `creature_template` SET `AIName` = '', `ScriptName`='npc_zulian_prowler' WHERE `entry`=15101;
+
+-- Remove SmartAI
+DELETE FROM `smart_scripts` WHERE `entryorguid`=15101;
+
+-- Add condition for Mark of Arlokk
+DELETE FROM `conditions` WHERE `SourceTypeOrReferenceId`=13 AND `SourceEntry`=24211;
+INSERT INTO `conditions` (`SourceTypeOrReferenceId`, `SourceGroup`, `SourceEntry`, `SourceId`, `ElseGroup`, `ConditionTypeOrReference`, `ConditionTarget`, `ConditionValue1`, `ConditionValue2`, `ConditionValue3`, `NegativeCondition`, `ErrorType`, `ErrorTextId`, `ScriptName`, `Comment`) VALUES
+(13,1,24211,0,0,0,31,3,15101,0,0,0,0, '', 'Mark of Arlokk - Targets Zulian Prowler');
diff --git a/sql/updates/world/2013_01_21_00_world_misc.sql b/sql/updates/world/2013_01_21_00_world_misc.sql
new file mode 100644
index 00000000000..c023003783e
--- /dev/null
+++ b/sql/updates/world/2013_01_21_00_world_misc.sql
@@ -0,0 +1,2 @@
+UPDATE `spell_dbc` SET `EffectImplicitTargetB1`=7,`EffectRadiusIndex1`=18 WHERE `Id`=24211;
+UPDATE `conditions` SET `ConditionTypeOrReference`=31,`ConditionTarget`=0 WHERE `SourceTypeOrReferenceId`=13 AND `SourceEntry`=24211;
diff --git a/sql/updates/world/2013_01_21_01_world_creature_template.sql b/sql/updates/world/2013_01_21_01_world_creature_template.sql
new file mode 100644
index 00000000000..d5bf4525ff6
--- /dev/null
+++ b/sql/updates/world/2013_01_21_01_world_creature_template.sql
@@ -0,0 +1 @@
+UPDATE `creature_template` SET `faction_A`=14,`faction_H`=14 WHERE `entry`=22920;
diff --git a/sql/updates/world/2013_01_21_02_world_sai.sql b/sql/updates/world/2013_01_21_02_world_sai.sql
new file mode 100644
index 00000000000..31070c48718
--- /dev/null
+++ b/sql/updates/world/2013_01_21_02_world_sai.sql
@@ -0,0 +1,7 @@
+UPDATE `smart_scripts` SET `event_type`=61 WHERE `entryorguid`=16459 AND `source_type`=0 AND `id`=2;
+UPDATE `smart_scripts` SET `event_flags`=0 WHERE `entryorguid`=5263 AND `source_type`=0 AND `id`=0;
+UPDATE `smart_scripts` SET `link`=0 WHERE `entryorguid`=19255 AND `source_type`=0 AND `id`=0;
+UPDATE `smart_scripts` SET `link`=0 WHERE `entryorguid`=5888 AND `source_type`=0 AND `id`=0;
+UPDATE `smart_scripts` SET `link`=0 WHERE `entryorguid`=10828 AND `source_type`=0 AND `id`=0;
+UPDATE `smart_scripts` SET `link`=0 WHERE `entryorguid`=29181 AND `source_type`=0 AND `id`=1;
+UPDATE `smart_scripts` SET `event_type`=61 WHERE `entryorguid`=16403 AND `source_type`=0 AND `id`=9;
diff --git a/sql/updates/world/2013_01_22_00_world_command.sql b/sql/updates/world/2013_01_22_00_world_command.sql
new file mode 100644
index 00000000000..6c89951d036
--- /dev/null
+++ b/sql/updates/world/2013_01_22_00_world_command.sql
@@ -0,0 +1,11 @@
+DELETE FROM `command` WHERE `name`='mmap' OR `name` LIKE 'mmap%';
+DELETE FROM `command` WHERE `name` LIKE 'disable add mmap' OR `name` LIKE 'disable remove mmap';
+INSERT INTO `command` (`name`, `security`, `help`) VALUES
+('mmap', 3, 'Syntax: Syntax: .mmaps $subcommand Type .mmaps to see the list of possible subcommands or .help mmaps $subcommand to see info on subcommands'),
+('mmap path', 3, 'Syntax: .mmap path to calculate and show a path to current select unit'),
+('mmap loc', 3, 'Syntax: .mmap loc to print on which tile one is'),
+('mmap loadedtiles', 3, 'Syntax: .mmap loadedtiles to show which tiles are currently loaded'),
+('mmap stats', 3, 'Syntax: .mmap stats to show information about current state of mmaps'),
+('mmap testarea', 3, 'Syntax: .mmap testarea to calculate paths for all nearby npcs to player'),
+('disable add mmap', '3', 'Syntax: .disable add mmap $entry $flag $comment'),
+('disable remove mmap', '3', 'Syntax: .disable remove mmap $entry');
diff --git a/sql/updates/world/2013_01_22_01_world_sai.sql b/sql/updates/world/2013_01_22_01_world_sai.sql
new file mode 100644
index 00000000000..b41fb25110d
--- /dev/null
+++ b/sql/updates/world/2013_01_22_01_world_sai.sql
@@ -0,0 +1,22 @@
+UPDATE `smart_scripts` SET `event_flags`=1 WHERE `entryorguid`=20882 AND `source_type`=0 AND `id`=0;
+UPDATE `smart_scripts` SET `link`=0 WHERE `entryorguid`=20896 AND `source_type`=0 AND `id`=3;
+UPDATE `smart_scripts` SET `link`=0 WHERE `entryorguid`=20900 AND `source_type`=0 AND `id`=3;
+UPDATE `smart_scripts` SET `link`=0,`event_type`=61 WHERE `entryorguid`=4063 AND `source_type`=0 AND `id`=3;
+UPDATE `smart_scripts` SET `event_type`=61 WHERE `entryorguid`=2245 AND `source_type`=0 AND `id`=8;
+UPDATE `smart_scripts` SET `link`=0 WHERE `entryorguid`=7038 AND `source_type`=0 AND `id`=17;
+UPDATE `smart_scripts` SET `event_type`=61 WHERE `entryorguid`=2345 AND `source_type`=0 AND `id`=11;
+
+DELETE FROM `smart_scripts` WHERE `entryorguid`=314 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
+(314, 0, 1, 0, 11, 0, 100, 0, 0, 0, 0, 0, 21, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 'Eliza - Phase 0 - On spawn - Prevent combat movement'),
+(314, 0, 2, 3, 4, 0, 100, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 'Eliza - Phase 0 - On aggro - Say'),
+(314, 0, 3, 0, 61, 0, 100, 0, 0, 0, 0, 0, 22, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 'Eliza - Phase 0 - On aggro - Set phase 1'),
+(314, 0, 4, 0, 9, 1, 100, 0, 0, 40, 0, 0, 11, 20819, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 'Eliza - Phase 1 - In combat - Cast Frostbolt'),
+(314, 0, 5, 0, 9, 1, 100, 0, 0, 5, 0, 0, 21, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 'Eliza - Phase 1 - 0 to 5 yards - Activate combat movement'),
+(314, 0, 6, 0, 9, 1, 100, 0, 5, 35, 0, 0, 21, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 'Eliza - Phase 1 - 5 to 35 yards - Deactivate combat movement'),
+(314, 0, 7, 0, 9, 1, 100, 0, 35, 80, 0, 0, 21, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 'Eliza - Phase 1 - 35 to 80 yards - Activate combat movement'),
+(314, 0, 8, 0, 0, 1, 100, 0, 4100, 6400, 72300, 72300, 11, 3107, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 'Eliza - Phase 1 - In combat - Summon Elizas Guard'),
+(314, 0, 9, 0, 0, 1, 100, 0, 2100, 2900, 12500, 36300, 11, 11831, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 'Eliza - Phase 1 - In combat - Cast Frost Nova'),
+(314, 0, 10, 12, 3, 1, 100, 0, 0, 7, 0, 0, 21, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 'Eliza - Phase 1 - At 7% mana - Start combat movement'),
+(314, 0, 11, 0, 61, 1, 100, 0, 0, 0, 0, 0, 22, 2, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 'Eliza - Phase 1 - At 7% mana - Set phase 2'),
+(314, 0, 12, 0, 3, 2, 100, 0, 15, 100, 100, 100, 22, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 'Eliza - Phase 2 - At 15% mana - Set phase 1');
diff --git a/src/server/CMakeLists.txt b/src/server/CMakeLists.txt
index 7d85cdb6e21..e8816ea8816 100644
--- a/src/server/CMakeLists.txt
+++ b/src/server/CMakeLists.txt
@@ -30,5 +30,6 @@ if( SERVERS )
else()
if( TOOLS )
add_subdirectory(collision)
+ add_subdirectory(shared)
endif()
endif()
diff --git a/src/server/authserver/Main.cpp b/src/server/authserver/Main.cpp
index f4f73e2c33d..c87df4ef7ab 100644
--- a/src/server/authserver/Main.cpp
+++ b/src/server/authserver/Main.cpp
@@ -134,8 +134,6 @@ extern int main(int argc, char **argv)
if (!StartDB())
return 1;
- sLog->SetRealmID(0); // ensure we've set realm to 0 (authserver realmid)
-
// Get the list of realms for the server
sRealmList->Initialize(ConfigMgr::GetIntDefault("RealmsStateUpdateDelay", 20));
if (sRealmList->size() == 0)
@@ -272,7 +270,7 @@ bool StartDB()
}
sLog->outInfo(LOG_FILTER_AUTHSERVER, "Started auth database connection pool.");
- sLog->EnableDBAppenders();
+ sLog->SetRealmId(0); // Enables DB appenders when realm is set.
return true;
}
diff --git a/src/server/authserver/Server/AuthSocket.cpp b/src/server/authserver/Server/AuthSocket.cpp
index 9145c8413ff..8ab4ab8a1a2 100644
--- a/src/server/authserver/Server/AuthSocket.cpp
+++ b/src/server/authserver/Server/AuthSocket.cpp
@@ -365,7 +365,7 @@ bool AuthSocket::_HandleLogonChallenge()
if (result)
{
pkt << uint8(WOW_FAIL_BANNED);
- sLog->outDebug(LOG_FILTER_AUTHSERVER, "'%s:%d' [AuthChallenge] Banned ip tries to login!",socket().getRemoteAddress().c_str(), socket().getRemotePort());
+ sLog->outDebug(LOG_FILTER_AUTHSERVER, "'%s:%d' [AuthChallenge] Banned ip tries to login!", socket().getRemoteAddress().c_str(), socket().getRemotePort());
}
else
{
diff --git a/src/server/collision/BoundingIntervalHierarchyWrapper.h b/src/server/collision/BoundingIntervalHierarchyWrapper.h
index 8a99078caab..315f3004306 100644
--- a/src/server/collision/BoundingIntervalHierarchyWrapper.h
+++ b/src/server/collision/BoundingIntervalHierarchyWrapper.h
@@ -33,11 +33,14 @@ class BIHWrap
{
const T* const* objects;
RayCallback& _callback;
+ uint32 objects_size;
- MDLCallback(RayCallback& callback, const T* const* objects_array ) : objects(objects_array), _callback(callback) {}
+ MDLCallback(RayCallback& callback, const T* const* objects_array, uint32 objects_size ) : objects(objects_array), _callback(callback), objects_size(objects_size) {}
bool operator() (const Ray& ray, uint32 Idx, float& MaxDist, bool /*stopAtFirst*/)
{
+ if (Idx >= objects_size)
+ return false;
if (const T* obj = objects[Idx])
return _callback(ray, *obj, MaxDist/*, stopAtFirst*/);
return false;
@@ -45,6 +48,8 @@ class BIHWrap
void operator() (const Vector3& p, uint32 Idx)
{
+ if (Idx >= objects_size)
+ return false;
if (const T* obj = objects[Idx])
_callback(p, *obj);
}
@@ -87,21 +92,24 @@ public:
m_objects.fastClear();
m_obj2Idx.getKeys(m_objects);
m_objects_to_push.getMembers(m_objects);
+ //assert that m_obj2Idx has all the keys
m_tree.build(m_objects, BoundsFunc::getBounds2);
}
template<typename RayCallback>
- void intersectRay(const Ray& ray, RayCallback& intersectCallback, float& maxDist) const
+ void intersectRay(const Ray& ray, RayCallback& intersectCallback, float& maxDist)
{
- MDLCallback<RayCallback> temp_cb(intersectCallback, m_objects.getCArray());
+ balance();
+ MDLCallback<RayCallback> temp_cb(intersectCallback, m_objects.getCArray(), m_objects.size());
m_tree.intersectRay(ray, temp_cb, maxDist, true);
}
template<typename IsectCallback>
- void intersectPoint(const Vector3& point, IsectCallback& intersectCallback) const
+ void intersectPoint(const Vector3& point, IsectCallback& intersectCallback)
{
- MDLCallback<IsectCallback> callback(intersectCallback, m_objects.getCArray());
+ balance();
+ MDLCallback<IsectCallback> callback(intersectCallback, m_objects.getCArray(), m_objects.size());
m_tree.intersectPoint(point, callback);
}
};
diff --git a/src/server/collision/CMakeLists.txt b/src/server/collision/CMakeLists.txt
index 62af7dd081e..b4012b63812 100644
--- a/src/server/collision/CMakeLists.txt
+++ b/src/server/collision/CMakeLists.txt
@@ -33,7 +33,9 @@ set(collision_STAT_SRCS
include_directories(
${CMAKE_BINARY_DIR}
${CMAKE_SOURCE_DIR}/dep/g3dlite/include
+ ${CMAKE_SOURCE_DIR}/dep/recastnavigation/Detour
${CMAKE_SOURCE_DIR}/src/server/shared
+ ${CMAKE_SOURCE_DIR}/src/server/shared/Configuration
${CMAKE_SOURCE_DIR}/src/server/shared/Debugging
${CMAKE_SOURCE_DIR}/src/server/shared/Database
${CMAKE_SOURCE_DIR}/src/server/shared/Debugging
diff --git a/src/server/collision/DynamicTree.cpp b/src/server/collision/DynamicTree.cpp
index 1a2f721a69f..c6754278d17 100644
--- a/src/server/collision/DynamicTree.cpp
+++ b/src/server/collision/DynamicTree.cpp
@@ -227,7 +227,7 @@ bool DynamicMapTree::getObjectHitPos(const uint32 phasemask, const Vector3& star
bool DynamicMapTree::isInLineOfSight(float x1, float y1, float z1, float x2, float y2, float z2, uint32 phasemask) const
{
- Vector3 v1(x1,y1,z1), v2(x2,y2,z2);
+ Vector3 v1(x1, y1, z1), v2(x2, y2, z2);
float maxDist = (v2 - v1).magnitude();
@@ -243,8 +243,8 @@ bool DynamicMapTree::isInLineOfSight(float x1, float y1, float z1, float x2, flo
float DynamicMapTree::getHeight(float x, float y, float z, float maxSearchDist, uint32 phasemask) const
{
- Vector3 v(x,y,z);
- Ray r(v, Vector3(0,0,-1));
+ Vector3 v(x, y, z);
+ Ray r(v, Vector3(0, 0, -1));
DynamicTreeIntersectionCallback callback(phasemask);
impl.intersectZAllignedRay(r, callback, maxSearchDist);
diff --git a/src/server/collision/Management/MMapFactory.cpp b/src/server/collision/Management/MMapFactory.cpp
new file mode 100644
index 00000000000..7adf7fbfa66
--- /dev/null
+++ b/src/server/collision/Management/MMapFactory.cpp
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2008-2013 TrinityCore <http://www.trinitycore.org/>
+ * Copyright (C) 2005-2010 MaNGOS <http://getmangos.com/>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include "MMapFactory.h"
+#include "World.h"
+#include "Config.h"
+#include "DisableMgr.h"
+
+namespace MMAP
+{
+ // ######################## MMapFactory ########################
+ // our global singleton copy
+ MMapManager *g_MMapManager = NULL;
+
+ MMapManager* MMapFactory::createOrGetMMapManager()
+ {
+ if (g_MMapManager == NULL)
+ g_MMapManager = new MMapManager();
+
+ return g_MMapManager;
+ }
+
+ bool MMapFactory::IsPathfindingEnabled(uint32 mapId)
+ {
+ return sWorld->getBoolConfig(CONFIG_ENABLE_MMAPS)
+ && !DisableMgr::IsDisabledFor(DISABLE_TYPE_MMAP, mapId, NULL, MMAP_DISABLE_PATHFINDING);
+ }
+
+ void MMapFactory::clear()
+ {
+ if (g_MMapManager)
+ {
+ delete g_MMapManager;
+ g_MMapManager = NULL;
+ }
+ }
+} \ No newline at end of file
diff --git a/src/server/collision/Management/MMapFactory.h b/src/server/collision/Management/MMapFactory.h
new file mode 100644
index 00000000000..00f19a194d3
--- /dev/null
+++ b/src/server/collision/Management/MMapFactory.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2008-2013 TrinityCore <http://www.trinitycore.org/>
+ * Copyright (C) 2005-2010 MaNGOS <http://getmangos.com/>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _MMAP_FACTORY_H
+#define _MMAP_FACTORY_H
+
+#include "MMapManager.h"
+#include "UnorderedMap.h"
+#include "DetourAlloc.h"
+#include "DetourNavMesh.h"
+#include "DetourNavMeshQuery.h"
+
+namespace MMAP
+{
+ enum MMAP_LOAD_RESULT
+ {
+ MMAP_LOAD_RESULT_ERROR,
+ MMAP_LOAD_RESULT_OK,
+ MMAP_LOAD_RESULT_IGNORED,
+ };
+
+ // static class
+ // holds all mmap global data
+ // access point to MMapManager singleton
+ class MMapFactory
+ {
+ public:
+ static MMapManager* createOrGetMMapManager();
+ static void clear();
+ static bool IsPathfindingEnabled(uint32 mapId);
+ };
+}
+
+#endif
+
diff --git a/src/server/collision/Management/MMapManager.cpp b/src/server/collision/Management/MMapManager.cpp
new file mode 100644
index 00000000000..e3ed8f3310a
--- /dev/null
+++ b/src/server/collision/Management/MMapManager.cpp
@@ -0,0 +1,302 @@
+/*
+ * Copyright (C) 2008-2013 TrinityCore <http://www.trinitycore.org/>
+ * Copyright (C) 2005-2010 MaNGOS <http://getmangos.com/>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include "MMapManager.h"
+#include "Log.h"
+#include "World.h"
+
+namespace MMAP
+{
+ // ######################## MMapManager ########################
+ MMapManager::~MMapManager()
+ {
+ for (MMapDataSet::iterator i = loadedMMaps.begin(); i != loadedMMaps.end(); ++i)
+ delete i->second;
+
+ // by now we should not have maps loaded
+ // if we had, tiles in MMapData->mmapLoadedTiles, their actual data is lost!
+ }
+
+ bool MMapManager::loadMapData(uint32 mapId)
+ {
+ // we already have this map loaded?
+ if (loadedMMaps.find(mapId) != loadedMMaps.end())
+ return true;
+
+ // load and init dtNavMesh - read parameters from file
+ uint32 pathLen = sWorld->GetDataPath().length() + strlen("mmaps/%03i.mmap")+1;
+ char *fileName = new char[pathLen];
+ snprintf(fileName, pathLen, (sWorld->GetDataPath()+"mmaps/%03i.mmap").c_str(), mapId);
+
+ FILE* file = fopen(fileName, "rb");
+ if (!file)
+ {
+ sLog->outDebug(LOG_FILTER_MAPS, "MMAP:loadMapData: Error: Could not open mmap file '%s'", fileName);
+ delete [] fileName;
+ return false;
+ }
+
+ dtNavMeshParams params;
+ int count = fread(&params, sizeof(dtNavMeshParams), 1, file);
+ fclose(file);
+ if (count != 1)
+ {
+ sLog->outDebug(LOG_FILTER_MAPS, "MMAP:loadMapData: Error: Could not read params from file '%s'", fileName);
+ delete [] fileName;
+ return false;
+ }
+
+ dtNavMesh* mesh = dtAllocNavMesh();
+ ASSERT(mesh);
+ if (DT_SUCCESS != mesh->init(&params))
+ {
+ dtFreeNavMesh(mesh);
+ sLog->outError(LOG_FILTER_MAPS, "MMAP:loadMapData: Failed to initialize dtNavMesh for mmap %03u from file %s", mapId, fileName);
+ delete [] fileName;
+ return false;
+ }
+
+ delete [] fileName;
+
+ sLog->outInfo(LOG_FILTER_MAPS, "MMAP:loadMapData: Loaded %03i.mmap", mapId);
+
+ // store inside our map list
+ MMapData* mmap_data = new MMapData(mesh);
+ mmap_data->mmapLoadedTiles.clear();
+
+ loadedMMaps.insert(std::pair<uint32, MMapData*>(mapId, mmap_data));
+ return true;
+ }
+
+ uint32 MMapManager::packTileID(int32 x, int32 y)
+ {
+ return uint32(x << 16 | y);
+ }
+
+ bool MMapManager::loadMap(const std::string& /*basePath*/, uint32 mapId, int32 x, int32 y)
+ {
+ // make sure the mmap is loaded and ready to load tiles
+ if (!loadMapData(mapId))
+ return false;
+
+ // get this mmap data
+ MMapData* mmap = loadedMMaps[mapId];
+ ASSERT(mmap->navMesh);
+
+ // check if we already have this tile loaded
+ uint32 packedGridPos = packTileID(x, y);
+ if (mmap->mmapLoadedTiles.find(packedGridPos) != mmap->mmapLoadedTiles.end())
+ return false;
+
+ // load this tile :: mmaps/MMMXXYY.mmtile
+ uint32 pathLen = sWorld->GetDataPath().length() + strlen("mmaps/%03i%02i%02i.mmtile")+1;
+ char *fileName = new char[pathLen];
+
+ snprintf(fileName, pathLen, (sWorld->GetDataPath()+"mmaps/%03i%02i%02i.mmtile").c_str(), mapId, x, y);
+
+ FILE *file = fopen(fileName, "rb");
+ if (!file)
+ {
+ sLog->outDebug(LOG_FILTER_MAPS, "MMAP:loadMap: Could not open mmtile file '%s'", fileName);
+ delete [] fileName;
+ return false;
+ }
+ delete [] fileName;
+
+ // read header
+ MmapTileHeader fileHeader;
+ if (fread(&fileHeader, sizeof(MmapTileHeader), 1, file) != 1 || fileHeader.mmapMagic != MMAP_MAGIC)
+ {
+ sLog->outError(LOG_FILTER_MAPS, "MMAP:loadMap: Bad header in mmap %03u%02i%02i.mmtile", mapId, x, y);
+ return false;
+ }
+
+ if (fileHeader.mmapVersion != MMAP_VERSION)
+ {
+ sLog->outError(LOG_FILTER_MAPS, "MMAP:loadMap: %03u%02i%02i.mmtile was built with generator v%i, expected v%i",
+ mapId, x, y, fileHeader.mmapVersion, MMAP_VERSION);
+ return false;
+ }
+
+ unsigned char* data = (unsigned char*)dtAlloc(fileHeader.size, DT_ALLOC_PERM);
+ ASSERT(data);
+
+ size_t result = fread(data, fileHeader.size, 1, file);
+ if (!result)
+ {
+ sLog->outError(LOG_FILTER_MAPS, "MMAP:loadMap: Bad header or data in mmap %03u%02i%02i.mmtile", mapId, x, y);
+ fclose(file);
+ return false;
+ }
+
+ fclose(file);
+
+ dtMeshHeader* header = (dtMeshHeader*)data;
+ dtTileRef tileRef = 0;
+
+ // memory allocated for data is now managed by detour, and will be deallocated when the tile is removed
+ if (DT_SUCCESS == mmap->navMesh->addTile(data, fileHeader.size, DT_TILE_FREE_DATA, 0, &tileRef))
+ {
+ mmap->mmapLoadedTiles.insert(std::pair<uint32, dtTileRef>(packedGridPos, tileRef));
+ ++loadedTiles;
+ sLog->outInfo(LOG_FILTER_MAPS, "MMAP:loadMap: Loaded mmtile %03i[%02i,%02i] into %03i[%02i,%02i]", mapId, x, y, mapId, header->x, header->y);
+ return true;
+ }
+ else
+ {
+ sLog->outError(LOG_FILTER_MAPS, "MMAP:loadMap: Could not load %03u%02i%02i.mmtile into navmesh", mapId, x, y);
+ dtFree(data);
+ return false;
+ }
+
+ return false;
+ }
+
+ bool MMapManager::unloadMap(uint32 mapId, int32 x, int32 y)
+ {
+ // check if we have this map loaded
+ if (loadedMMaps.find(mapId) == loadedMMaps.end())
+ {
+ // file may not exist, therefore not loaded
+ sLog->outDebug(LOG_FILTER_MAPS, "MMAP:unloadMap: Asked to unload not loaded navmesh map. %03u%02i%02i.mmtile", mapId, x, y);
+ return false;
+ }
+
+ MMapData* mmap = loadedMMaps[mapId];
+
+ // check if we have this tile loaded
+ uint32 packedGridPos = packTileID(x, y);
+ if (mmap->mmapLoadedTiles.find(packedGridPos) == mmap->mmapLoadedTiles.end())
+ {
+ // file may not exist, therefore not loaded
+ sLog->outDebug(LOG_FILTER_MAPS, "MMAP:unloadMap: Asked to unload not loaded navmesh tile. %03u%02i%02i.mmtile", mapId, x, y);
+ return false;
+ }
+
+ dtTileRef tileRef = mmap->mmapLoadedTiles[packedGridPos];
+
+ // unload, and mark as non loaded
+ if (DT_SUCCESS != mmap->navMesh->removeTile(tileRef, NULL, NULL))
+ {
+ // this is technically a memory leak
+ // if the grid is later reloaded, dtNavMesh::addTile will return error but no extra memory is used
+ // we cannot recover from this error - assert out
+ sLog->outError(LOG_FILTER_MAPS, "MMAP:unloadMap: Could not unload %03u%02i%02i.mmtile from navmesh", mapId, x, y);
+ ASSERT(false);
+ }
+ else
+ {
+ mmap->mmapLoadedTiles.erase(packedGridPos);
+ --loadedTiles;
+ sLog->outInfo(LOG_FILTER_MAPS, "MMAP:unloadMap: Unloaded mmtile %03i[%02i,%02i] from %03i", mapId, x, y, mapId);
+ return true;
+ }
+
+ return false;
+ }
+
+ bool MMapManager::unloadMap(uint32 mapId)
+ {
+ if (loadedMMaps.find(mapId) == loadedMMaps.end())
+ {
+ // file may not exist, therefore not loaded
+ sLog->outDebug(LOG_FILTER_MAPS, "MMAP:unloadMap: Asked to unload not loaded navmesh map %03u", mapId);
+ return false;
+ }
+
+ // unload all tiles from given map
+ MMapData* mmap = loadedMMaps[mapId];
+ for (MMapTileSet::iterator i = mmap->mmapLoadedTiles.begin(); i != mmap->mmapLoadedTiles.end(); ++i)
+ {
+ uint32 x = (i->first >> 16);
+ uint32 y = (i->first & 0x0000FFFF);
+ if (DT_SUCCESS != mmap->navMesh->removeTile(i->second, NULL, NULL))
+ sLog->outError(LOG_FILTER_MAPS, "MMAP:unloadMap: Could not unload %03u%02i%02i.mmtile from navmesh", mapId, x, y);
+ else
+ {
+ --loadedTiles;
+ sLog->outInfo(LOG_FILTER_MAPS, "MMAP:unloadMap: Unloaded mmtile %03i[%02i,%02i] from %03i", mapId, x, y, mapId);
+ }
+ }
+
+ delete mmap;
+ loadedMMaps.erase(mapId);
+ sLog->outInfo(LOG_FILTER_MAPS, "MMAP:unloadMap: Unloaded %03i.mmap", mapId);
+
+ return true;
+ }
+
+ bool MMapManager::unloadMapInstance(uint32 mapId, uint32 instanceId)
+ {
+ // check if we have this map loaded
+ if (loadedMMaps.find(mapId) == loadedMMaps.end())
+ {
+ // file may not exist, therefore not loaded
+ sLog->outDebug(LOG_FILTER_MAPS, "MMAP:unloadMapInstance: Asked to unload not loaded navmesh map %03u", mapId);
+ return false;
+ }
+
+ MMapData* mmap = loadedMMaps[mapId];
+ if (mmap->navMeshQueries.find(instanceId) == mmap->navMeshQueries.end())
+ {
+ sLog->outDebug(LOG_FILTER_MAPS, "MMAP:unloadMapInstance: Asked to unload not loaded dtNavMeshQuery mapId %03u instanceId %u", mapId, instanceId);
+ return false;
+ }
+
+ dtNavMeshQuery* query = mmap->navMeshQueries[instanceId];
+
+ dtFreeNavMeshQuery(query);
+ mmap->navMeshQueries.erase(instanceId);
+ sLog->outInfo(LOG_FILTER_MAPS, "MMAP:unloadMapInstance: Unloaded mapId %03u instanceId %u", mapId, instanceId);
+
+ return true;
+ }
+
+ dtNavMesh const* MMapManager::GetNavMesh(uint32 mapId)
+ {
+ if (loadedMMaps.find(mapId) == loadedMMaps.end())
+ return NULL;
+
+ return loadedMMaps[mapId]->navMesh;
+ }
+
+ dtNavMeshQuery const* MMapManager::GetNavMeshQuery(uint32 mapId, uint32 instanceId)
+ {
+ if (loadedMMaps.find(mapId) == loadedMMaps.end())
+ return NULL;
+
+ MMapData* mmap = loadedMMaps[mapId];
+ if (mmap->navMeshQueries.find(instanceId) == mmap->navMeshQueries.end())
+ {
+ // allocate mesh query
+ dtNavMeshQuery* query = dtAllocNavMeshQuery();
+ ASSERT(query);
+ if (DT_SUCCESS != query->init(mmap->navMesh, 1024))
+ {
+ dtFreeNavMeshQuery(query);
+ sLog->outError(LOG_FILTER_MAPS, "MMAP:GetNavMeshQuery: Failed to initialize dtNavMeshQuery for mapId %03u instanceId %u", mapId, instanceId);
+ return NULL;
+ }
+
+ sLog->outInfo(LOG_FILTER_MAPS, "MMAP:GetNavMeshQuery: created dtNavMeshQuery for mapId %03u instanceId %u", mapId, instanceId);
+ mmap->navMeshQueries.insert(std::pair<uint32, dtNavMeshQuery*>(instanceId, query));
+ }
+
+ return mmap->navMeshQueries[instanceId];
+ }
+}
diff --git a/src/server/collision/Management/MMapManager.h b/src/server/collision/Management/MMapManager.h
new file mode 100644
index 00000000000..56b1b856d65
--- /dev/null
+++ b/src/server/collision/Management/MMapManager.h
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2008-2013 TrinityCore <http://www.trinitycore.org/>
+ * Copyright (C) 2005-2010 MaNGOS <http://getmangos.com/>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _MMAP_MANAGER_H
+#define _MMAP_MANAGER_H
+
+#include "UnorderedMap.h"
+#include "DetourAlloc.h"
+#include "DetourNavMesh.h"
+#include "DetourNavMeshQuery.h"
+
+// move map related classes
+namespace MMAP
+{
+ typedef UNORDERED_MAP<uint32, dtTileRef> MMapTileSet;
+ typedef UNORDERED_MAP<uint32, dtNavMeshQuery*> NavMeshQuerySet;
+
+ // dummy struct to hold map's mmap data
+ struct MMapData
+ {
+ MMapData(dtNavMesh* mesh) : navMesh(mesh) {}
+ ~MMapData()
+ {
+ for (NavMeshQuerySet::iterator i = navMeshQueries.begin(); i != navMeshQueries.end(); ++i)
+ dtFreeNavMeshQuery(i->second);
+
+ if (navMesh)
+ dtFreeNavMesh(navMesh);
+ }
+
+ dtNavMesh* navMesh;
+
+ // we have to use single dtNavMeshQuery for every instance, since those are not thread safe
+ NavMeshQuerySet navMeshQueries; // instanceId to query
+ MMapTileSet mmapLoadedTiles; // maps [map grid coords] to [dtTile]
+ };
+
+
+ typedef UNORDERED_MAP<uint32, MMapData*> MMapDataSet;
+
+ // singleton class
+ // holds all all access to mmap loading unloading and meshes
+ class MMapManager
+ {
+ public:
+ MMapManager() : loadedTiles(0) {}
+ ~MMapManager();
+
+ bool loadMap(const std::string& basePath, uint32 mapId, int32 x, int32 y);
+ bool unloadMap(uint32 mapId, int32 x, int32 y);
+ bool unloadMap(uint32 mapId);
+ bool unloadMapInstance(uint32 mapId, uint32 instanceId);
+
+ // the returned [dtNavMeshQuery const*] is NOT threadsafe
+ dtNavMeshQuery const* GetNavMeshQuery(uint32 mapId, uint32 instanceId);
+ dtNavMesh const* GetNavMesh(uint32 mapId);
+
+ uint32 getLoadedTilesCount() const { return loadedTiles; }
+ uint32 getLoadedMapsCount() const { return loadedMMaps.size(); }
+ private:
+ bool loadMapData(uint32 mapId);
+ uint32 packTileID(int32 x, int32 y);
+
+ MMapDataSet loadedMMaps;
+ uint32 loadedTiles;
+ };
+}
+
+#endif \ No newline at end of file
diff --git a/src/server/collision/Management/VMapManager2.cpp b/src/server/collision/Management/VMapManager2.cpp
index 5e3741ca753..8a1bd346957 100644
--- a/src/server/collision/Management/VMapManager2.cpp
+++ b/src/server/collision/Management/VMapManager2.cpp
@@ -24,13 +24,13 @@
#include "MapTree.h"
#include "ModelInstance.h"
#include "WorldModel.h"
-#include "VMapDefinitions.h"
-#include "Log.h"
#include <G3D/Vector3.h>
#include <ace/Null_Mutex.h>
#include <ace/Singleton.h>
#include "DisableMgr.h"
#include "DBCStores.h"
+#include "Log.h"
+#include "VMapDefinitions.h"
using G3D::Vector3;
@@ -257,11 +257,11 @@ namespace VMAP
WorldModel* worldmodel = new WorldModel();
if (!worldmodel->readFile(basepath + filename + ".vmo"))
{
- sLog->outError(LOG_FILTER_GENERAL, "VMapManager2: could not load '%s%s.vmo'", basepath.c_str(), filename.c_str());
+ VMAP_ERROR_LOG(LOG_FILTER_GENERAL, "VMapManager2: could not load '%s%s.vmo'", basepath.c_str(), filename.c_str());
delete worldmodel;
return NULL;
}
- sLog->outDebug(LOG_FILTER_MAPS, "VMapManager2: loading file '%s%s'", basepath.c_str(), filename.c_str());
+ VMAP_DEBUG_LOG(LOG_FILTER_MAPS, "VMapManager2: loading file '%s%s'", basepath.c_str(), filename.c_str());
model = iLoadedModelFiles.insert(std::pair<std::string, ManagedModel>(filename, ManagedModel())).first;
model->second.setModel(worldmodel);
}
@@ -277,12 +277,12 @@ namespace VMAP
ModelFileMap::iterator model = iLoadedModelFiles.find(filename);
if (model == iLoadedModelFiles.end())
{
- sLog->outError(LOG_FILTER_GENERAL, "VMapManager2: trying to unload non-loaded file '%s'", filename.c_str());
+ VMAP_ERROR_LOG(LOG_FILTER_GENERAL, "VMapManager2: trying to unload non-loaded file '%s'", filename.c_str());
return;
}
if (model->second.decRefCount() == 0)
{
- sLog->outDebug(LOG_FILTER_MAPS, "VMapManager2: unloading file '%s'", filename.c_str());
+ VMAP_DEBUG_LOG(LOG_FILTER_MAPS, "VMapManager2: unloading file '%s'", filename.c_str());
delete model->second.getModel();
iLoadedModelFiles.erase(model);
}
diff --git a/src/server/collision/Management/VMapManager2.h b/src/server/collision/Management/VMapManager2.h
index e2e9965dbfc..51f15f0fda4 100644
--- a/src/server/collision/Management/VMapManager2.h
+++ b/src/server/collision/Management/VMapManager2.h
@@ -112,6 +112,8 @@ namespace VMAP
return getMapFileName(mapId);
}
virtual bool existsMap(const char* basePath, unsigned int mapId, int x, int y);
+ public:
+ void getInstanceMapTree(InstanceTreeMap &instanceMapTree);
};
}
diff --git a/src/server/collision/Maps/MapTree.cpp b/src/server/collision/Maps/MapTree.cpp
index 5f4e2b6aa8b..eb4b4555cb4 100644
--- a/src/server/collision/Maps/MapTree.cpp
+++ b/src/server/collision/Maps/MapTree.cpp
@@ -21,18 +21,13 @@
#include "VMapManager2.h"
#include "VMapDefinitions.h"
#include "Log.h"
+#include "Errors.h"
#include <string>
#include <sstream>
#include <iomanip>
#include <limits>
-#ifndef NO_CORE_FUNCS
- #include "Errors.h"
-#else
- #define ASSERT(x)
-#endif
-
using G3D::Vector3;
namespace VMAP
@@ -277,7 +272,7 @@ namespace VMAP
bool StaticMapTree::InitMap(const std::string &fname, VMapManager2* vm)
{
- sLog->outDebug(LOG_FILTER_MAPS, "StaticMapTree::InitMap() : initializing StaticMapTree '%s'", fname.c_str());
+ VMAP_DEBUG_LOG(LOG_FILTER_MAPS, "StaticMapTree::InitMap() : initializing StaticMapTree '%s'", fname.c_str());
bool success = true;
std::string fullname = iBasePath + fname;
FILE* rf = fopen(fullname.c_str(), "rb");
@@ -310,7 +305,7 @@ namespace VMAP
if (!iIsTiled && ModelSpawn::readFromFile(rf, spawn))
{
WorldModel* model = vm->acquireModelInstance(iBasePath, spawn.name);
- sLog->outDebug(LOG_FILTER_MAPS, "StaticMapTree::InitMap() : loading %s", spawn.name.c_str());
+ VMAP_DEBUG_LOG(LOG_FILTER_MAPS, "StaticMapTree::InitMap() : loading %s", spawn.name.c_str());
if (model)
{
// assume that global model always is the first and only tree value (could be improved...)
@@ -320,7 +315,7 @@ namespace VMAP
else
{
success = false;
- sLog->outError(LOG_FILTER_GENERAL, "StaticMapTree::InitMap() : could not acquire WorldModel pointer for '%s'", spawn.name.c_str());
+ VMAP_ERROR_LOG(LOG_FILTER_GENERAL, "StaticMapTree::InitMap() : could not acquire WorldModel pointer for '%s'", spawn.name.c_str());
}
}
@@ -356,7 +351,7 @@ namespace VMAP
}
if (!iTreeValues)
{
- sLog->outError(LOG_FILTER_GENERAL, "StaticMapTree::LoadMapTile() : tree has not been initialized [%u, %u]", tileX, tileY);
+ VMAP_ERROR_LOG(LOG_FILTER_GENERAL, "StaticMapTree::LoadMapTile() : tree has not been initialized [%u, %u]", tileX, tileY);
return false;
}
bool result = true;
@@ -382,7 +377,7 @@ namespace VMAP
// acquire model instance
WorldModel* model = vm->acquireModelInstance(iBasePath, spawn.name);
if (!model)
- sLog->outError(LOG_FILTER_GENERAL, "StaticMapTree::LoadMapTile() : could not acquire WorldModel pointer [%u, %u]", tileX, tileY);
+ VMAP_ERROR_LOG(LOG_FILTER_GENERAL, "StaticMapTree::LoadMapTile() : could not acquire WorldModel pointer [%u, %u]", tileX, tileY);
// update tree
uint32 referencedVal;
@@ -432,7 +427,7 @@ namespace VMAP
loadedTileMap::iterator tile = iLoadedTiles.find(tileID);
if (tile == iLoadedTiles.end())
{
- sLog->outError(LOG_FILTER_GENERAL, "StaticMapTree::UnloadMapTile() : trying to unload non-loaded tile - Map:%u X:%u Y:%u", iMapID, tileX, tileY);
+ VMAP_ERROR_LOG(LOG_FILTER_GENERAL, "StaticMapTree::UnloadMapTile() : trying to unload non-loaded tile - Map:%u X:%u Y:%u", iMapID, tileX, tileY);
return;
}
if (tile->second) // file associated with tile
@@ -466,7 +461,7 @@ namespace VMAP
else
{
if (!iLoadedSpawns.count(referencedNode))
- sLog->outError(LOG_FILTER_GENERAL, "StaticMapTree::UnloadMapTile() : trying to unload non-referenced model '%s' (ID:%u)", spawn.name.c_str(), spawn.ID);
+ VMAP_ERROR_LOG(LOG_FILTER_GENERAL, "StaticMapTree::UnloadMapTile() : trying to unload non-referenced model '%s' (ID:%u)", spawn.name.c_str(), spawn.ID);
else if (--iLoadedSpawns[referencedNode] == 0)
{
iTreeValues[referencedNode].setUnloaded();
@@ -480,5 +475,4 @@ namespace VMAP
}
iLoadedTiles.erase(tile);
}
-
}
diff --git a/src/server/collision/Maps/MapTree.h b/src/server/collision/Maps/MapTree.h
index bd928d85c5a..c8e9628dff1 100644
--- a/src/server/collision/Maps/MapTree.h
+++ b/src/server/collision/Maps/MapTree.h
@@ -80,6 +80,7 @@ namespace VMAP
void UnloadMapTile(uint32 tileX, uint32 tileY, VMapManager2* vm);
bool isTiled() const { return iIsTiled; }
uint32 numLoadedTiles() const { return iLoadedTiles.size(); }
+ void getModelInstances(ModelInstance* &models, uint32 &count);
};
struct AreaInfo
diff --git a/src/server/collision/Maps/TileAssembler.cpp b/src/server/collision/Maps/TileAssembler.cpp
index 380d6b5df21..c087f773630 100644
--- a/src/server/collision/Maps/TileAssembler.cpp
+++ b/src/server/collision/Maps/TileAssembler.cpp
@@ -380,11 +380,11 @@ namespace VMAP
}
}
- fwrite(&displayId,sizeof(uint32),1,model_list_copy);
- fwrite(&name_length,sizeof(uint32),1,model_list_copy);
- fwrite(&buff,sizeof(char),name_length,model_list_copy);
- fwrite(&bounds.low(),sizeof(Vector3),1,model_list_copy);
- fwrite(&bounds.high(),sizeof(Vector3),1,model_list_copy);
+ fwrite(&displayId, sizeof(uint32), 1, model_list_copy);
+ fwrite(&name_length, sizeof(uint32), 1, model_list_copy);
+ fwrite(&buff, sizeof(char), name_length, model_list_copy);
+ fwrite(&bounds.low(), sizeof(Vector3), 1, model_list_copy);
+ fwrite(&bounds.high(), sizeof(Vector3), 1, model_list_copy);
}
fclose(model_list);
diff --git a/src/server/collision/Maps/TileAssembler.h b/src/server/collision/Maps/TileAssembler.h
index 087c2021b50..a11ce272d62 100644
--- a/src/server/collision/Maps/TileAssembler.h
+++ b/src/server/collision/Maps/TileAssembler.h
@@ -72,12 +72,12 @@ namespace VMAP
uint32 liquidflags;
std::vector<MeshTriangle> triangles;
std::vector<G3D::Vector3> vertexArray;
- class WmoLiquid *liquid;
+ class WmoLiquid* liquid;
GroupModel_Raw() : liquid(0) {}
~GroupModel_Raw();
- bool Read(FILE * f);
+ bool Read(FILE* f);
};
struct WorldModel_Raw
diff --git a/src/server/collision/Models/GameObjectModel.cpp b/src/server/collision/Models/GameObjectModel.cpp
index 237a5b33621..54283389387 100644
--- a/src/server/collision/Models/GameObjectModel.cpp
+++ b/src/server/collision/Models/GameObjectModel.cpp
@@ -34,8 +34,6 @@ using G3D::Vector3;
using G3D::Ray;
using G3D::AABox;
-#ifndef NO_CORE_FUNCS
-
struct GameobjectModelData
{
GameobjectModelData(const std::string& name_, const AABox& box) :
@@ -54,7 +52,7 @@ void LoadGameObjectModelList()
FILE* model_list_file = fopen((sWorld->GetDataPath() + "vmaps/" + VMAP::GAMEOBJECT_MODELS).c_str(), "rb");
if (!model_list_file)
{
- sLog->outError(LOG_FILTER_GENERAL, "Unable to open '%s' file.", VMAP::GAMEOBJECT_MODELS);
+ VMAP_ERROR_LOG(LOG_FILTER_GENERAL, "Unable to open '%s' file.", VMAP::GAMEOBJECT_MODELS);
return;
}
@@ -73,19 +71,18 @@ void LoadGameObjectModelList()
|| fread(&v1, sizeof(Vector3), 1, model_list_file) != 1
|| fread(&v2, sizeof(Vector3), 1, model_list_file) != 1)
{
- sLog->outError(LOG_FILTER_GENERAL, "File '%s' seems to be corrupted!", VMAP::GAMEOBJECT_MODELS);
+ VMAP_ERROR_LOG(LOG_FILTER_GENERAL, "File '%s' seems to be corrupted!", VMAP::GAMEOBJECT_MODELS);
break;
}
model_list.insert
(
- ModelList::value_type( displayId, GameobjectModelData(std::string(buff,name_length),AABox(v1,v2)) )
+ ModelList::value_type( displayId, GameobjectModelData(std::string(buff, name_length), AABox(v1, v2)) )
);
}
fclose(model_list_file);
- sLog->outInfo(LOG_FILTER_SERVER_LOADING, ">> Loaded %u GameObject models in %u ms", uint32(model_list.size()), GetMSTimeDiffToNow(oldMSTime));
-
+ VMAP_INFO_LOG(LOG_FILTER_SERVER_LOADING, ">> Loaded %u GameObject models in %u ms", uint32(model_list.size()), GetMSTimeDiffToNow(oldMSTime));
}
GameObjectModel::~GameObjectModel()
@@ -104,7 +101,7 @@ bool GameObjectModel::initialize(const GameObject& go, const GameObjectDisplayIn
// ignore models with no bounds
if (mdl_box == G3D::AABox::zero())
{
- sLog->outError(LOG_FILTER_GENERAL, "GameObject model %s has zero bounds, loading skipped", it->second.name.c_str());
+ VMAP_ERROR_LOG(LOG_FILTER_GENERAL, "GameObject model %s has zero bounds, loading skipped", it->second.name.c_str());
return false;
}
@@ -184,5 +181,3 @@ bool GameObjectModel::intersectRay(const G3D::Ray& ray, float& MaxDist, bool Sto
}
return hit;
}
-
-#endif
diff --git a/src/server/collision/Models/ModelInstance.h b/src/server/collision/Models/ModelInstance.h
index 5af02a1d1b1..f26089bb46c 100644
--- a/src/server/collision/Models/ModelInstance.h
+++ b/src/server/collision/Models/ModelInstance.h
@@ -74,6 +74,8 @@ namespace VMAP
G3D::Matrix3 iInvRot;
float iInvScale;
WorldModel* iModel;
+ public:
+ WorldModel* getWorldModel();
};
} // namespace VMAP
diff --git a/src/server/collision/Models/WorldModel.h b/src/server/collision/Models/WorldModel.h
index 8e046e561f7..ade9efbb040 100644
--- a/src/server/collision/Models/WorldModel.h
+++ b/src/server/collision/Models/WorldModel.h
@@ -66,6 +66,8 @@ namespace VMAP
uint32 iType; //!< liquid type
float *iHeight; //!< (tilesX + 1)*(tilesY + 1) height values
uint8 *iFlags; //!< info if liquid tile is used
+ public:
+ void getPosInfo(uint32 &tilesX, uint32 &tilesY, Vector3 &corner) const;
};
/*! holding additional info for WMO group files */
@@ -98,6 +100,8 @@ namespace VMAP
std::vector<MeshTriangle> triangles;
BIH meshTree;
WmoLiquid* iLiquid;
+ public:
+ void getMeshData(std::vector<Vector3> &vertices, std::vector<MeshTriangle> &triangles, WmoLiquid* &liquid);
};
/*! Holds a model (converted M2 or WMO) in its original coordinate space */
class WorldModel
@@ -117,6 +121,8 @@ namespace VMAP
uint32 RootWMOID;
std::vector<GroupModel> groupModels;
BIH groupTree;
+ public:
+ void getGroupModels(std::vector<GroupModel> &groupModels);
};
} // namespace VMAP
diff --git a/src/server/collision/RegularGrid.h b/src/server/collision/RegularGrid.h
index 5b7d1d74987..f38bf357a19 100644
--- a/src/server/collision/RegularGrid.h
+++ b/src/server/collision/RegularGrid.h
@@ -104,7 +104,7 @@ public:
{
ASSERT(x < CELL_NUMBER && y < CELL_NUMBER);
if (!nodes[x][y])
- nodes[x][y] = NodeCreatorFunc::makeNode(x,y);
+ nodes[x][y] = NodeCreatorFunc::makeNode(x, y);
return *nodes[x][y];
}
diff --git a/src/server/collision/VMapDefinitions.h b/src/server/collision/VMapDefinitions.h
index 609d00cd00f..56084389ad6 100644
--- a/src/server/collision/VMapDefinitions.h
+++ b/src/server/collision/VMapDefinitions.h
@@ -31,4 +31,16 @@ namespace VMAP
// defined in TileAssembler.cpp currently...
bool readChunk(FILE* rf, char *dest, const char *compare, uint32 len);
}
+
+// Set of helper macros for extractors (VMAP and MMAP)
+#ifndef NO_CORE_FUNCS
+#define VMAP_ERROR_LOG(FILTER, ...) sLog->outError(FILTER, __VA_ARGS__)
+#define VMAP_DEBUG_LOG(FILTER, ...) sLog->outDebug(FILTER, __VA_ARGS__)
+#define VMAP_INFO_LOG(FILTER, ...) sLog->outInfo(FILTER, __VA_ARGS__)
+#else
+#define VMAP_ERROR_LOG(FILTER, ...) printf(__VA_ARGS__)
+#define VMAP_DEBUG_LOG(FILTER, ...) printf(__VA_ARGS__)
+#define VMAP_INFO_LOG(FILTER, ...) printf(__VA_ARGS__)
+#endif
+
#endif
diff --git a/src/server/game/AI/CoreAI/UnitAI.cpp b/src/server/game/AI/CoreAI/UnitAI.cpp
index a3cb57b3332..09ba2cc19b1 100644
--- a/src/server/game/AI/CoreAI/UnitAI.cpp
+++ b/src/server/game/AI/CoreAI/UnitAI.cpp
@@ -172,7 +172,9 @@ void UnitAI::DoCast(Unit* victim, uint32 spellId, bool triggered)
void UnitAI::DoCastVictim(uint32 spellId, bool triggered)
{
- // Why don't we check for casting unit_state and existing target as we do in DoCast(.. ?
+ if (!me->getVictim() || (me->HasUnitState(UNIT_STATE_CASTING) && !triggered))
+ return;
+
me->CastSpell(me->getVictim(), spellId, triggered);
}
diff --git a/src/server/game/AI/SmartScripts/SmartScript.cpp b/src/server/game/AI/SmartScripts/SmartScript.cpp
index c08e085e816..84c9dffabd2 100644
--- a/src/server/game/AI/SmartScripts/SmartScript.cpp
+++ b/src/server/game/AI/SmartScripts/SmartScript.cpp
@@ -2121,18 +2121,20 @@ SmartScriptHolder SmartScript::CreateEvent(SMART_EVENT e, uint32 event_flags, ui
ObjectList* SmartScript::GetTargets(SmartScriptHolder const& e, Unit* invoker /*= NULL*/)
{
- Unit* trigger = NULL;
+ Unit* scriptTrigger = NULL;
if (invoker)
- trigger = invoker;
+ scriptTrigger = invoker;
else if (Unit* tempLastInvoker = GetLastInvoker())
- trigger = tempLastInvoker;
+ scriptTrigger = tempLastInvoker;
+
+ WorldObject* baseObject = GetBaseObject();
ObjectList* l = new ObjectList();
switch (e.GetTargetType())
{
case SMART_TARGET_SELF:
- if (GetBaseObject())
- l->push_back(GetBaseObject());
+ if (baseObject)
+ l->push_back(baseObject);
break;
case SMART_TARGET_VICTIM:
if (me && me->getVictim())
@@ -2160,17 +2162,17 @@ ObjectList* SmartScript::GetTargets(SmartScriptHolder const& e, Unit* invoker /*
break;
case SMART_TARGET_NONE:
case SMART_TARGET_ACTION_INVOKER:
- if (trigger)
- l->push_back(trigger);
+ if (scriptTrigger)
+ l->push_back(scriptTrigger);
break;
case SMART_TARGET_ACTION_INVOKER_VEHICLE:
- if (trigger && trigger->GetVehicle() && trigger->GetVehicle()->GetBase())
- l->push_back(trigger->GetVehicle()->GetBase());
+ if (scriptTrigger && scriptTrigger->GetVehicle() && scriptTrigger->GetVehicle()->GetBase())
+ l->push_back(scriptTrigger->GetVehicle()->GetBase());
break;
case SMART_TARGET_INVOKER_PARTY:
- if (trigger)
+ if (scriptTrigger)
{
- if (Player* player = trigger->ToPlayer())
+ if (Player* player = scriptTrigger->ToPlayer())
{
if (Group* group = player->GetGroup())
{
@@ -2182,7 +2184,7 @@ ObjectList* SmartScript::GetTargets(SmartScriptHolder const& e, Unit* invoker /*
// this even if there is a group (thus the else-check), it will add the
// same player to the list twice. We don't want that to happen.
else
- l->push_back(trigger);
+ l->push_back(scriptTrigger);
}
}
break;
@@ -2198,7 +2200,7 @@ ObjectList* SmartScript::GetTargets(SmartScriptHolder const& e, Unit* invoker /*
if (me && me == *itr)
continue;
- if (((e.target.unitRange.creature && (*itr)->ToCreature()->GetEntry() == e.target.unitRange.creature) || !e.target.unitRange.creature) && GetBaseObject()->IsInRange(*itr, (float)e.target.unitRange.minDist, (float)e.target.unitRange.maxDist))
+ if (((e.target.unitRange.creature && (*itr)->ToCreature()->GetEntry() == e.target.unitRange.creature) || !e.target.unitRange.creature) && baseObject->IsInRange(*itr, (float)e.target.unitRange.minDist, (float)e.target.unitRange.maxDist))
l->push_back(*itr);
}
@@ -2255,7 +2257,7 @@ ObjectList* SmartScript::GetTargets(SmartScriptHolder const& e, Unit* invoker /*
if (go && go == *itr)
continue;
- if (((e.target.goRange.entry && IsGameObject(*itr) && (*itr)->ToGameObject()->GetEntry() == e.target.goRange.entry) || !e.target.goRange.entry) && GetBaseObject()->IsInRange((*itr), (float)e.target.goRange.minDist, (float)e.target.goRange.maxDist))
+ if (((e.target.goRange.entry && IsGameObject(*itr) && (*itr)->ToGameObject()->GetEntry() == e.target.goRange.entry) || !e.target.goRange.entry) && baseObject->IsInRange((*itr), (float)e.target.goRange.minDist, (float)e.target.goRange.maxDist))
l->push_back(*itr);
}
@@ -2265,13 +2267,13 @@ ObjectList* SmartScript::GetTargets(SmartScriptHolder const& e, Unit* invoker /*
case SMART_TARGET_CREATURE_GUID:
{
Creature* target = NULL;
- if (!trigger && !GetBaseObject())
+ if (!scriptTrigger && !baseObject)
{
sLog->outError(LOG_FILTER_SQL, "SMART_TARGET_CREATURE_GUID can not be used without invoker");
break;
}
- target = FindCreatureNear(trigger ? trigger : GetBaseObject(), e.target.unitGUID.dbGuid);
+ target = FindCreatureNear(scriptTrigger ? scriptTrigger : baseObject, e.target.unitGUID.dbGuid);
if (target && (!e.target.unitGUID.entry || target->GetEntry() == e.target.unitGUID.entry))
l->push_back(target);
@@ -2280,13 +2282,13 @@ ObjectList* SmartScript::GetTargets(SmartScriptHolder const& e, Unit* invoker /*
case SMART_TARGET_GAMEOBJECT_GUID:
{
GameObject* target = NULL;
- if (!trigger && !GetBaseObject())
+ if (!scriptTrigger && !baseObject)
{
sLog->outError(LOG_FILTER_SQL, "SMART_TARGET_GAMEOBJECT_GUID can not be used without invoker");
break;
}
- target = FindGameObjectNear(trigger ? trigger : GetBaseObject(), e.target.goGUID.dbGuid);
+ target = FindGameObjectNear(scriptTrigger ? scriptTrigger : baseObject, e.target.goGUID.dbGuid);
if (target && (!e.target.goGUID.entry || target->GetEntry() == e.target.goGUID.entry))
l->push_back(target);
@@ -2296,9 +2298,9 @@ ObjectList* SmartScript::GetTargets(SmartScriptHolder const& e, Unit* invoker /*
{
// will always return a valid pointer, even if empty list
ObjectList* units = GetWorldObjectsInDist((float)e.target.playerRange.maxDist);
- if (!units->empty() && GetBaseObject())
+ if (!units->empty() && baseObject)
for (ObjectList::const_iterator itr = units->begin(); itr != units->end(); ++itr)
- if (IsPlayer(*itr) && GetBaseObject()->IsInRange(*itr, (float)e.target.playerRange.minDist, (float)e.target.playerRange.maxDist))
+ if (IsPlayer(*itr) && baseObject->IsInRange(*itr, (float)e.target.playerRange.minDist, (float)e.target.playerRange.maxDist))
l->push_back(*itr);
delete units;
@@ -2325,14 +2327,14 @@ ObjectList* SmartScript::GetTargets(SmartScriptHolder const& e, Unit* invoker /*
}
case SMART_TARGET_CLOSEST_CREATURE:
{
- Creature* target = GetClosestCreatureWithEntry(GetBaseObject(), e.target.closest.entry, (float)(e.target.closest.dist ? e.target.closest.dist : 100), e.target.closest.dead ? false : true);
+ Creature* target = GetClosestCreatureWithEntry(baseObject, e.target.closest.entry, (float)(e.target.closest.dist ? e.target.closest.dist : 100), e.target.closest.dead ? false : true);
if (target)
l->push_back(target);
break;
}
case SMART_TARGET_CLOSEST_GAMEOBJECT:
{
- GameObject* target = GetClosestGameObjectWithEntry(GetBaseObject(), e.target.closest.entry, (float)(e.target.closest.dist ? e.target.closest.dist : 100));
+ GameObject* target = GetClosestGameObjectWithEntry(baseObject, e.target.closest.entry, (float)(e.target.closest.dist ? e.target.closest.dist : 100));
if (target)
l->push_back(target);
break;
diff --git a/src/server/game/Accounts/AccountMgr.cpp b/src/server/game/Accounts/AccountMgr.cpp
index 01ba222d93b..3c3eded1f68 100644
--- a/src/server/game/Accounts/AccountMgr.cpp
+++ b/src/server/game/Accounts/AccountMgr.cpp
@@ -24,10 +24,11 @@
#include "SHA1.h"
#include "WorldSession.h"
-namespace AccountMgr
+AccountMgr::AccountMgr()
{
+}
-AccountOpResult CreateAccount(std::string username, std::string password)
+AccountOpResult AccountMgr::CreateAccount(std::string username, std::string password)
{
if (utf8length(username) > MAX_ACCOUNT_STR)
return AOR_NAME_TOO_LONG; // username's too long
@@ -52,7 +53,7 @@ AccountOpResult CreateAccount(std::string username, std::string password)
return AOR_OK; // everything's fine
}
-AccountOpResult DeleteAccount(uint32 accountId)
+AccountOpResult AccountMgr::DeleteAccount(uint32 accountId)
{
// Check if accounts exists
PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_ACCOUNT_BY_ID);
@@ -124,7 +125,7 @@ AccountOpResult DeleteAccount(uint32 accountId)
return AOR_OK;
}
-AccountOpResult ChangeUsername(uint32 accountId, std::string newUsername, std::string newPassword)
+AccountOpResult AccountMgr::ChangeUsername(uint32 accountId, std::string newUsername, std::string newPassword)
{
// Check if accounts exists
PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_ACCOUNT_BY_ID);
@@ -154,7 +155,7 @@ AccountOpResult ChangeUsername(uint32 accountId, std::string newUsername, std::s
return AOR_OK;
}
-AccountOpResult ChangePassword(uint32 accountId, std::string newPassword)
+AccountOpResult AccountMgr::ChangePassword(uint32 accountId, std::string newPassword)
{
std::string username;
@@ -177,7 +178,7 @@ AccountOpResult ChangePassword(uint32 accountId, std::string newPassword)
return AOR_OK;
}
-uint32 GetId(std::string const& username)
+uint32 AccountMgr::GetId(std::string const& username)
{
PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_GET_ACCOUNT_ID_BY_USERNAME);
stmt->setString(0, username);
@@ -186,7 +187,7 @@ uint32 GetId(std::string const& username)
return (result) ? (*result)[0].GetUInt32() : 0;
}
-uint32 GetSecurity(uint32 accountId)
+uint32 AccountMgr::GetSecurity(uint32 accountId)
{
PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_GET_ACCOUNT_ACCESS_GMLEVEL);
stmt->setUInt32(0, accountId);
@@ -195,7 +196,7 @@ uint32 GetSecurity(uint32 accountId)
return (result) ? (*result)[0].GetUInt8() : uint32(SEC_PLAYER);
}
-uint32 GetSecurity(uint32 accountId, int32 realmId)
+uint32 AccountMgr::GetSecurity(uint32 accountId, int32 realmId)
{
PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_GET_GMLEVEL_BY_REALMID);
stmt->setUInt32(0, accountId);
@@ -205,7 +206,7 @@ uint32 GetSecurity(uint32 accountId, int32 realmId)
return (result) ? (*result)[0].GetUInt8() : uint32(SEC_PLAYER);
}
-bool GetName(uint32 accountId, std::string& name)
+bool AccountMgr::GetName(uint32 accountId, std::string& name)
{
PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_GET_USERNAME_BY_ID);
stmt->setUInt32(0, accountId);
@@ -220,7 +221,7 @@ bool GetName(uint32 accountId, std::string& name)
return false;
}
-bool CheckPassword(uint32 accountId, std::string password)
+bool AccountMgr::CheckPassword(uint32 accountId, std::string password)
{
std::string username;
@@ -238,7 +239,7 @@ bool CheckPassword(uint32 accountId, std::string password)
return (result) ? true : false;
}
-uint32 GetCharactersCount(uint32 accountId)
+uint32 AccountMgr::GetCharactersCount(uint32 accountId)
{
// check character count
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_SUM_CHARS);
@@ -248,7 +249,7 @@ uint32 GetCharactersCount(uint32 accountId)
return (result) ? (*result)[0].GetUInt64() : 0;
}
-bool normalizeString(std::string& utf8String)
+bool AccountMgr::normalizeString(std::string& utf8String)
{
wchar_t buffer[MAX_ACCOUNT_STR+1];
@@ -266,7 +267,7 @@ bool normalizeString(std::string& utf8String)
return WStrToUtf8(buffer, maxLength, utf8String);
}
-std::string CalculateShaPassHash(std::string const& name, std::string const& password)
+std::string AccountMgr::CalculateShaPassHash(std::string const& name, std::string const& password)
{
SHA1Hash sha;
sha.Initialize();
@@ -278,29 +279,27 @@ std::string CalculateShaPassHash(std::string const& name, std::string const& pas
return ByteArrayToHexStr(sha.GetDigest(), sha.GetLength());
}
-bool IsPlayerAccount(uint32 gmlevel)
+bool AccountMgr::IsPlayerAccount(uint32 gmlevel)
{
return gmlevel == SEC_PLAYER;
}
-bool IsModeratorAccount(uint32 gmlevel)
+bool AccountMgr::IsModeratorAccount(uint32 gmlevel)
{
return gmlevel >= SEC_MODERATOR && gmlevel <= SEC_CONSOLE;
}
-bool IsGMAccount(uint32 gmlevel)
+bool AccountMgr::IsGMAccount(uint32 gmlevel)
{
return gmlevel >= SEC_GAMEMASTER && gmlevel <= SEC_CONSOLE;
}
-bool IsAdminAccount(uint32 gmlevel)
+bool AccountMgr::IsAdminAccount(uint32 gmlevel)
{
return gmlevel >= SEC_ADMINISTRATOR && gmlevel <= SEC_CONSOLE;
}
-bool IsConsoleAccount(uint32 gmlevel)
+bool AccountMgr::IsConsoleAccount(uint32 gmlevel)
{
return gmlevel == SEC_CONSOLE;
}
-
-} // Namespace AccountMgr
diff --git a/src/server/game/Accounts/AccountMgr.h b/src/server/game/Accounts/AccountMgr.h
index 7154cf0e867..c8de5688e73 100644
--- a/src/server/game/Accounts/AccountMgr.h
+++ b/src/server/game/Accounts/AccountMgr.h
@@ -21,6 +21,7 @@
#include "Define.h"
#include <string>
+#include <ace/Singleton.h>
enum AccountOpResult
{
@@ -34,27 +35,34 @@ enum AccountOpResult
#define MAX_ACCOUNT_STR 16
-namespace AccountMgr
+class AccountMgr
{
- AccountOpResult CreateAccount(std::string username, std::string password);
- AccountOpResult DeleteAccount(uint32 accountId);
- AccountOpResult ChangeUsername(uint32 accountId, std::string newUsername, std::string newPassword);
- AccountOpResult ChangePassword(uint32 accountId, std::string newPassword);
- bool CheckPassword(uint32 accountId, std::string password);
-
- uint32 GetId(std::string const& username);
- uint32 GetSecurity(uint32 accountId);
- uint32 GetSecurity(uint32 accountId, int32 realmId);
- bool GetName(uint32 accountId, std::string& name);
- uint32 GetCharactersCount(uint32 accountId);
- std::string CalculateShaPassHash(std::string const& name, std::string const& password);
-
- bool normalizeString(std::string& utf8String);
- bool IsPlayerAccount(uint32 gmlevel);
- bool IsModeratorAccount(uint32 gmlevel);
- bool IsGMAccount(uint32 gmlevel);
- bool IsAdminAccount(uint32 gmlevel);
- bool IsConsoleAccount(uint32 gmlevel);
-}
+ friend class ACE_Singleton<AccountMgr, ACE_Null_Mutex>;
+ private:
+ AccountMgr();
+
+ public:
+ static AccountOpResult CreateAccount(std::string username, std::string password);
+ static AccountOpResult DeleteAccount(uint32 accountId);
+ static AccountOpResult ChangeUsername(uint32 accountId, std::string newUsername, std::string newPassword);
+ static AccountOpResult ChangePassword(uint32 accountId, std::string newPassword);
+ static bool CheckPassword(uint32 accountId, std::string password);
+
+ static uint32 GetId(std::string const& username);
+ static uint32 GetSecurity(uint32 accountId);
+ static uint32 GetSecurity(uint32 accountId, int32 realmId);
+ static bool GetName(uint32 accountId, std::string& name);
+ static uint32 GetCharactersCount(uint32 accountId);
+
+ static std::string CalculateShaPassHash(std::string const& name, std::string const& password);
+ static bool normalizeString(std::string& utf8String);
+ static bool IsPlayerAccount(uint32 gmlevel);
+ static bool IsModeratorAccount(uint32 gmlevel);
+ static bool IsGMAccount(uint32 gmlevel);
+ static bool IsAdminAccount(uint32 gmlevel);
+ static bool IsConsoleAccount(uint32 gmlevel);
+};
+
+#define sAccountMgr ACE_Singleton<AccountMgr, ACE_Null_Mutex>::instance()
#endif
diff --git a/src/server/game/Battlefield/Battlefield.cpp b/src/server/game/Battlefield/Battlefield.cpp
index 32f71822737..dffdb2361d5 100644
--- a/src/server/game/Battlefield/Battlefield.cpp
+++ b/src/server/game/Battlefield/Battlefield.cpp
@@ -469,13 +469,13 @@ void Battlefield::SendWarningToAllInZone(uint32 entry)
sCreatureTextMgr->SendChat(stalker, (uint8) entry, 0, CHAT_MSG_BG_SYSTEM_NEUTRAL, LANG_ADDON, TEXT_RANGE_ZONE);
}
-/*void Battlefield::SendWarningToAllInWar(int32 entry,...)
+/*void Battlefield::SendWarningToAllInWar(int32 entry, ...)
{
const char *format = sObjectMgr->GetTrinityStringForDBCLocale(entry);
va_list ap;
char str [1024];
va_start(ap, entry);
- vsnprintf(str,1024,format, ap);
+ vsnprintf(str, 1024, format, ap);
va_end(ap);
std::string msg = (std::string)str;
@@ -604,7 +604,7 @@ BfGraveyard* Battlefield::GetGraveyardById(uint32 id) const
return NULL;
}
-WorldSafeLocsEntry const * Battlefield::GetClosestGraveYard(Player* player)
+WorldSafeLocsEntry const* Battlefield::GetClosestGraveYard(Player* player)
{
BfGraveyard* closestGY = NULL;
float maxdist = -1;
@@ -816,7 +816,7 @@ Creature* Battlefield::SpawnCreature(uint32 entry, Position pos, TeamId team)
Creature* Battlefield::SpawnCreature(uint32 entry, float x, float y, float z, float o, TeamId team)
{
//Get map object
- Map* map = const_cast < Map * >(sMapMgr->CreateBaseMap(m_MapId));
+ Map* map = const_cast<Map*>(sMapMgr->CreateBaseMap(m_MapId));
if (!map)
{
sLog->outError(LOG_FILTER_BATTLEFIELD, "Battlefield::SpawnCreature: Can't create creature entry: %u map not found", entry);
diff --git a/src/server/game/Battlefield/Battlefield.h b/src/server/game/Battlefield/Battlefield.h
index 0da776b9624..7a8d856aa8e 100644
--- a/src/server/game/Battlefield/Battlefield.h
+++ b/src/server/game/Battlefield/Battlefield.h
@@ -277,7 +277,7 @@ class Battlefield : public ZoneScript
// Graveyard methods
// Find which graveyard the player must be teleported to to be resurrected by spiritguide
- WorldSafeLocsEntry const * GetClosestGraveYard(Player* player);
+ WorldSafeLocsEntry const* GetClosestGraveYard(Player* player);
virtual void AddPlayerToResurrectQueue(uint64 npc_guid, uint64 player_guid);
void RemovePlayerFromResurrectQueue(uint64 player_guid);
diff --git a/src/server/game/Battlefield/BattlefieldMgr.cpp b/src/server/game/Battlefield/BattlefieldMgr.cpp
index 5aec0458b19..d30a0b17fd7 100644
--- a/src/server/game/Battlefield/BattlefieldMgr.cpp
+++ b/src/server/game/Battlefield/BattlefieldMgr.cpp
@@ -51,7 +51,7 @@ void BattlefieldMgr::InitBattlefield()
/* For Cataclysm: Tol Barad
pBf = new BattlefieldTB;
// respawn, init variables
- if(!pBf->SetupBattlefield())
+ if (!pBf->SetupBattlefield())
{
sLog->outDebug(LOG_FILTER_BATTLEFIELD, "Battlefield : Tol Barad init failed.");
delete pBf;
@@ -68,7 +68,7 @@ void BattlefieldMgr::AddZone(uint32 zoneid, Battlefield *handle)
m_BattlefieldMap[zoneid] = handle;
}
-void BattlefieldMgr::HandlePlayerEnterZone(Player * player, uint32 zoneid)
+void BattlefieldMgr::HandlePlayerEnterZone(Player* player, uint32 zoneid)
{
BattlefieldMap::iterator itr = m_BattlefieldMap.find(zoneid);
if (itr == m_BattlefieldMap.end())
@@ -81,7 +81,7 @@ void BattlefieldMgr::HandlePlayerEnterZone(Player * player, uint32 zoneid)
sLog->outDebug(LOG_FILTER_BATTLEFIELD, "Player %u entered outdoorpvp id %u", player->GetGUIDLow(), itr->second->GetTypeId());
}
-void BattlefieldMgr::HandlePlayerLeaveZone(Player * player, uint32 zoneid)
+void BattlefieldMgr::HandlePlayerLeaveZone(Player* player, uint32 zoneid)
{
BattlefieldMap::iterator itr = m_BattlefieldMap.find(zoneid);
if (itr == m_BattlefieldMap.end())
@@ -129,7 +129,7 @@ void BattlefieldMgr::Update(uint32 diff)
}
}
-ZoneScript *BattlefieldMgr::GetZoneScript(uint32 zoneId)
+ZoneScript* BattlefieldMgr::GetZoneScript(uint32 zoneId)
{
BattlefieldMap::iterator itr = m_BattlefieldMap.find(zoneId);
if (itr != m_BattlefieldMap.end())
diff --git a/src/server/game/Battlefield/BattlefieldMgr.h b/src/server/game/Battlefield/BattlefieldMgr.h
index 28b2a3c148a..af1cea763df 100644
--- a/src/server/game/Battlefield/BattlefieldMgr.h
+++ b/src/server/game/Battlefield/BattlefieldMgr.h
@@ -39,11 +39,11 @@ class BattlefieldMgr
// create battlefield events
void InitBattlefield();
// called when a player enters an battlefield area
- void HandlePlayerEnterZone(Player * player, uint32 areaflag);
+ void HandlePlayerEnterZone(Player* player, uint32 areaflag);
// called when player leaves an battlefield area
- void HandlePlayerLeaveZone(Player * player, uint32 areaflag);
+ void HandlePlayerLeaveZone(Player* player, uint32 areaflag);
// called when player resurrects
- void HandlePlayerResurrects(Player * player, uint32 areaflag);
+ void HandlePlayerResurrects(Player* player, uint32 areaflag);
// return assigned battlefield
Battlefield* GetBattlefieldToZoneId(uint32 zoneid);
Battlefield* GetBattlefieldByBattleId(uint32 battleid);
@@ -54,14 +54,14 @@ class BattlefieldMgr
void Update(uint32 diff);
- void HandleGossipOption(Player * player, uint64 guid, uint32 gossipid);
+ void HandleGossipOption(Player* player, uint64 guid, uint32 gossipid);
- bool CanTalkTo(Player * player, Creature * creature, GossipMenuItems gso);
+ bool CanTalkTo(Player* player, Creature* creature, GossipMenuItems gso);
- void HandleDropFlag(Player * player, uint32 spellId);
+ void HandleDropFlag(Player* player, uint32 spellId);
typedef std::vector < Battlefield * >BattlefieldSet;
- typedef std::map < uint32 /* zoneid */ , Battlefield * >BattlefieldMap;
+ typedef std::map < uint32 /* zoneid */, Battlefield * >BattlefieldMap;
private:
// contains all initiated battlefield events
// used when initing / cleaning up
diff --git a/src/server/game/Battlefield/Zones/BattlefieldWG.cpp b/src/server/game/Battlefield/Zones/BattlefieldWG.cpp
index a36d204a4b7..060aca3e5aa 100644
--- a/src/server/game/Battlefield/Zones/BattlefieldWG.cpp
+++ b/src/server/game/Battlefield/Zones/BattlefieldWG.cpp
@@ -916,7 +916,7 @@ void BattlefieldWG::UpdatedDestroyedTowerCount(TeamId team)
}
}
-void BattlefieldWG::ProcessEvent(WorldObject *obj, uint32 eventId)
+void BattlefieldWG::ProcessEvent(WorldObject* obj, uint32 eventId)
{
if (!obj || !IsWarTime())
return;
diff --git a/src/server/game/Battlefield/Zones/BattlefieldWG.h b/src/server/game/Battlefield/Zones/BattlefieldWG.h
index f313357e323..2f750de7b3a 100644
--- a/src/server/game/Battlefield/Zones/BattlefieldWG.h
+++ b/src/server/game/Battlefield/Zones/BattlefieldWG.h
@@ -405,7 +405,7 @@ class BattlefieldWG : public Battlefield
void PromotePlayer(Player* killer);
void UpdateTenacity();
- void ProcessEvent(WorldObject *obj, uint32 eventId);
+ void ProcessEvent(WorldObject* obj, uint32 eventId);
bool FindAndRemoveVehicleFromList(Unit* vehicle);
@@ -562,7 +562,7 @@ struct WintergraspObjectPositionData
};
// *****************************************************
-// ************ Destructible (Wall,Tower..) ************
+// ************ Destructible (Wall, Tower..) ************
// *****************************************************
struct WintergraspBuildingSpawnData
@@ -755,7 +755,7 @@ const WintergraspTeleporterData WGPortalDefenderData[WG_MAX_TELEPORTER] =
};
// *********************************************************
-// **********Tower Element(GameObject,Creature)*************
+// **********Tower Element(GameObject, Creature)*************
// *********************************************************
struct WintergraspTowerData
@@ -1055,7 +1055,7 @@ const WGWorkshopData WorkshopsData[WG_MAX_WORKSHOP] =
};
// ********************************************************************
-// * Structs using for Building,Graveyard,Workshop *
+// * Structs using for Building, Graveyard, Workshop *
// ********************************************************************
// Structure for different buildings that can be destroyed during battle
struct BfWGGameObjectBuilding
diff --git a/src/server/game/Battlegrounds/Battleground.cpp b/src/server/game/Battlegrounds/Battleground.cpp
index 1fe7e4c1035..264c2c333aa 100644
--- a/src/server/game/Battlegrounds/Battleground.cpp
+++ b/src/server/game/Battlegrounds/Battleground.cpp
@@ -1147,13 +1147,6 @@ void Battleground::AddPlayer(Player* player)
sBattlegroundMgr->BuildPlayerJoinedBattlegroundPacket(&data, player);
SendPacketToTeam(team, &data, player, false);
- // BG Status packet
- WorldPacket status;
- BattlegroundQueueTypeId bgQueueTypeId = sBattlegroundMgr->BGQueueTypeId(m_TypeID, GetArenaType());
- uint32 queueSlot = player->GetBattlegroundQueueIndex(bgQueueTypeId);
- sBattlegroundMgr->BuildBattlegroundStatusPacket(&status, this, queueSlot, STATUS_IN_PROGRESS, 0, GetStartTime(), GetArenaType(), team);
- player->GetSession()->SendPacket(&status);
-
player->RemoveAurasByType(SPELL_AURA_MOUNTED);
// add arena specific auras
@@ -1688,7 +1681,7 @@ void Battleground::SendWarningToAll(int32 entry, ...)
if (!entry)
return;
- char const *format = sObjectMgr->GetTrinityStringForDBCLocale(entry);
+ char const* format = sObjectMgr->GetTrinityStringForDBCLocale(entry);
char str[1024];
va_list ap;
diff --git a/src/server/game/Battlegrounds/BattlegroundMgr.cpp b/src/server/game/Battlegrounds/BattlegroundMgr.cpp
index 451fa70a8c9..24e69a151b5 100644
--- a/src/server/game/Battlegrounds/BattlegroundMgr.cpp
+++ b/src/server/game/Battlegrounds/BattlegroundMgr.cpp
@@ -162,7 +162,7 @@ void BattlegroundMgr::Update(uint32 diff)
}
}
-void BattlegroundMgr::BuildBattlegroundStatusPacket(WorldPacket* data, Battleground* bg, uint8 QueueSlot, uint8 StatusID, uint32 Time1, uint32 Time2, uint8 arenatype, uint8 arenaFaction)
+void BattlegroundMgr::BuildBattlegroundStatusPacket(WorldPacket* data, Battleground* bg, uint8 QueueSlot, uint8 StatusID, uint32 Time1, uint32 Time2, uint8 arenatype, uint32 arenaFaction)
{
// we can be in 2 queues in same time...
@@ -188,26 +188,26 @@ void BattlegroundMgr::BuildBattlegroundStatusPacket(WorldPacket* data, Battlegro
*data << uint32(bg->GetClientInstanceID());
// alliance/horde for BG and skirmish/rated for Arenas
// following displays the minimap-icon 0 = faction icon 1 = arenaicon
- *data << uint8(bg->isRated()); // 1 for rated match, 0 for bg or non rated match
+ *data << uint8(bg->isRated()); // 1 for rated match, 0 for bg or non rated match
- *data << uint32(StatusID); // status
+ *data << uint32(StatusID); // status
switch (StatusID)
{
- case STATUS_WAIT_QUEUE: // status_in_queue
- *data << uint32(Time1); // average wait time, milliseconds
- *data << uint32(Time2); // time in queue, updated every minute!, milliseconds
+ case STATUS_WAIT_QUEUE: // status_in_queue
+ *data << uint32(Time1); // average wait time, milliseconds
+ *data << uint32(Time2); // time in queue, updated every minute!, milliseconds
break;
- case STATUS_WAIT_JOIN: // status_invite
- *data << uint32(bg->GetMapId()); // map id
- *data << uint64(0); // 3.3.5, unknown
- *data << uint32(Time1); // time to remove from queue, milliseconds
+ case STATUS_WAIT_JOIN: // status_invite
+ *data << uint32(bg->GetMapId()); // map id
+ *data << uint64(0); // 3.3.5, unknown
+ *data << uint32(Time1); // time to remove from queue, milliseconds
break;
- case STATUS_IN_PROGRESS: // status_in_progress
- *data << uint32(bg->GetMapId()); // map id
- *data << uint64(0); // 3.3.5, unknown
- *data << uint32(Time1); // time to bg auto leave, 0 at bg start, 120000 after bg end, milliseconds
- *data << uint32(Time2); // time from bg start, milliseconds
- *data << uint8(arenaFaction); // arenafaction (0 for horde, 1 for alliance)
+ case STATUS_IN_PROGRESS: // status_in_progress
+ *data << uint32(bg->GetMapId()); // map id
+ *data << uint64(0); // 3.3.5, unknown
+ *data << uint32(Time1); // time to bg auto leave, 0 at bg start, 120000 after bg end, milliseconds
+ *data << uint32(Time2); // time from bg start, milliseconds
+ *data << uint8(arenaFaction == ALLIANCE ? 1 : 0); // arenafaction (0 for horde, 1 for alliance)
break;
default:
sLog->outError(LOG_FILTER_BATTLEGROUND, "Unknown BG status!");
diff --git a/src/server/game/Battlegrounds/BattlegroundMgr.h b/src/server/game/Battlegrounds/BattlegroundMgr.h
index ee96414c5f2..c66d9d64ab8 100644
--- a/src/server/game/Battlegrounds/BattlegroundMgr.h
+++ b/src/server/game/Battlegrounds/BattlegroundMgr.h
@@ -80,7 +80,7 @@ class BattlegroundMgr
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, uint8 arenaFaction);
+ 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/Zones/BattlegroundEY.cpp b/src/server/game/Battlegrounds/Zones/BattlegroundEY.cpp
index 4ddf7a8b052..b6452e76b5d 100644
--- a/src/server/game/Battlegrounds/Zones/BattlegroundEY.cpp
+++ b/src/server/game/Battlegrounds/Zones/BattlegroundEY.cpp
@@ -247,7 +247,7 @@ void BattlegroundEY::UpdatePointStatuses()
if (player)
{
this->UpdateWorldStateForPlayer(PROGRESS_BAR_STATUS, m_PointBarStatus[point], player);
- //if point owner changed we must evoke event!
+ //if point owner changed we must evoke event!
if (pointOwnerTeamId != m_PointOwnedByTeam[point])
{
//point was uncontrolled and player is from team which captured point
@@ -258,6 +258,12 @@ void BattlegroundEY::UpdatePointStatuses()
if (m_PointState[point] == EY_POINT_UNDER_CONTROL && player->GetTeam() != m_PointOwnedByTeam[point])
this->EventTeamLostPoint(player, point);
}
+
+ /// @workaround The original AreaTrigger is covered by a bigger one and not triggered on client side.
+ if (point == FEL_REAVER && m_PointOwnedByTeam[point] == player->GetTeam())
+ if (m_FlagState && GetFlagPickerGUID() == player->GetGUID())
+ if (player->GetDistance(2044.0f, 1729.729f, 1190.03f) < 3.0f)
+ EventPlayerCapturedFlag(player, BG_EY_OBJECT_FLAG_FEL_REAVER);
}
}
}
diff --git a/src/server/game/CMakeLists.txt b/src/server/game/CMakeLists.txt
index 368407e7924..f455610666e 100644
--- a/src/server/game/CMakeLists.txt
+++ b/src/server/game/CMakeLists.txt
@@ -102,6 +102,8 @@ set(game_STAT_SRCS
include_directories(
${CMAKE_BINARY_DIR}
+ ${CMAKE_SOURCE_DIR}/dep/recastnavigation/Detour
+ ${CMAKE_SOURCE_DIR}/dep/recastnavigation/Recast
${CMAKE_SOURCE_DIR}/dep/g3dlite/include
${CMAKE_SOURCE_DIR}/dep/SFMT
${CMAKE_SOURCE_DIR}/dep/zlib
diff --git a/src/server/game/Calendar/CalendarMgr.cpp b/src/server/game/Calendar/CalendarMgr.cpp
index f4dd059846e..763e0132c10 100644
--- a/src/server/game/Calendar/CalendarMgr.cpp
+++ b/src/server/game/Calendar/CalendarMgr.cpp
@@ -68,7 +68,7 @@ void CalendarMgr::LoadFromDB()
if (flags & CALENDAR_FLAG_GUILD_EVENT || flags & CALENDAR_FLAG_WITHOUT_INVITES)
guildId = Player::GetGuildIdFromDB(creatorGUID);
- CalendarEvent* calendarEvent = new CalendarEvent(eventId, creatorGUID , guildId, type, dungeonId, time_t(eventTime), flags, time_t(timezoneTime), title, description);
+ CalendarEvent* calendarEvent = new CalendarEvent(eventId, creatorGUID, guildId, type, dungeonId, time_t(eventTime), flags, time_t(timezoneTime), title, description);
_events.insert(calendarEvent);
_maxEventId = std::max(_maxEventId, eventId);
diff --git a/src/server/game/Combat/ThreatManager.cpp b/src/server/game/Combat/ThreatManager.cpp
index 0e13019c8a2..6d291ca301f 100644
--- a/src/server/game/Combat/ThreatManager.cpp
+++ b/src/server/game/Combat/ThreatManager.cpp
@@ -272,7 +272,7 @@ HostileReference* ThreatContainer::getReferenceByTarget(Unit* victim) const
uint64 const guid = victim->GetGUID();
for (ThreatContainer::StorageType::const_iterator i = iThreatList.begin(); i != iThreatList.end(); ++i)
{
- HostileReference *ref = (*i);
+ HostileReference* ref = (*i);
if (ref && ref->getUnitGuid() == guid)
return ref;
}
diff --git a/src/server/game/Conditions/ConditionMgr.cpp b/src/server/game/Conditions/ConditionMgr.cpp
index d476113edc4..5e1a05669ed 100644
--- a/src/server/game/Conditions/ConditionMgr.cpp
+++ b/src/server/game/Conditions/ConditionMgr.cpp
@@ -1364,7 +1364,7 @@ bool ConditionMgr::isSourceTypeValid(Condition* cond)
if ((cond->SourceGroup > MAX_EFFECT_MASK) || !cond->SourceGroup)
{
- sLog->outError(LOG_FILTER_SQL, "SourceEntry %u in `condition` table, has incorrect SourceGroup %u (spell effectMask) set , ignoring.", cond->SourceEntry, cond->SourceGroup);
+ sLog->outError(LOG_FILTER_SQL, "SourceEntry %u in `condition` table, has incorrect SourceGroup %u (spell effectMask) set, ignoring.", cond->SourceEntry, cond->SourceGroup);
return false;
}
diff --git a/src/server/game/Conditions/DisableMgr.cpp b/src/server/game/Conditions/DisableMgr.cpp
index fefb51323c4..7e379fdaded 100644
--- a/src/server/game/Conditions/DisableMgr.cpp
+++ b/src/server/game/Conditions/DisableMgr.cpp
@@ -42,7 +42,7 @@ namespace
DisableMap m_DisableMap;
- uint8 MAX_DISABLE_TYPES = 7;
+ uint8 MAX_DISABLE_TYPES = 8;
}
void LoadDisables()
@@ -222,6 +222,34 @@ void LoadDisables()
}
break;
}
+ case DISABLE_TYPE_MMAP:
+ {
+ MapEntry const* mapEntry = sMapStore.LookupEntry(entry);
+ if (!mapEntry)
+ {
+ sLog->outError(LOG_FILTER_SQL, "Map entry %u from `disables` doesn't exist in dbc, skipped.", entry);
+ continue;
+ }
+ switch (mapEntry->map_type)
+ {
+ case MAP_COMMON:
+ sLog->outInfo(LOG_FILTER_GENERAL, "Pathfinding disabled for world map %u.", entry);
+ break;
+ case MAP_INSTANCE:
+ case MAP_RAID:
+ sLog->outInfo(LOG_FILTER_GENERAL, "Pathfinding disabled for instance map %u.", entry);
+ break;
+ case MAP_BATTLEGROUND:
+ sLog->outInfo(LOG_FILTER_GENERAL, "Pathfinding disabled for battleground map %u.", entry);
+ break;
+ case MAP_ARENA:
+ sLog->outInfo(LOG_FILTER_GENERAL, "Pathfinding disabled for arena map %u.", entry);
+ break;
+ default:
+ break;
+ }
+ break;
+ }
default:
break;
}
@@ -350,6 +378,7 @@ bool IsDisabledFor(DisableType type, uint32 entry, Unit const* unit, uint8 flags
case DISABLE_TYPE_BATTLEGROUND:
case DISABLE_TYPE_OUTDOORPVP:
case DISABLE_TYPE_ACHIEVEMENT_CRITERIA:
+ case DISABLE_TYPE_MMAP:
return true;
case DISABLE_TYPE_VMAP:
return flags & itr->second.flags;
diff --git a/src/server/game/Conditions/DisableMgr.h b/src/server/game/Conditions/DisableMgr.h
index 89761931048..38751b8a89f 100644
--- a/src/server/game/Conditions/DisableMgr.h
+++ b/src/server/game/Conditions/DisableMgr.h
@@ -31,7 +31,8 @@ enum DisableType
DISABLE_TYPE_BATTLEGROUND = 3,
DISABLE_TYPE_ACHIEVEMENT_CRITERIA = 4,
DISABLE_TYPE_OUTDOORPVP = 5,
- DISABLE_TYPE_VMAP = 6
+ DISABLE_TYPE_VMAP = 6,
+ DISABLE_TYPE_MMAP = 7
};
enum SpellDisableTypes
@@ -56,6 +57,11 @@ enum VmapDisableTypes
VMAP_DISABLE_LIQUIDSTATUS = 0x8
};
+enum MMapDisableTypes
+{
+ MMAP_DISABLE_PATHFINDING = 0x0
+};
+
namespace DisableMgr
{
void LoadDisables();
diff --git a/src/server/game/DataStores/DBCStores.cpp b/src/server/game/DataStores/DBCStores.cpp
index 3fdff8e11d6..8a2dad902fa 100644
--- a/src/server/game/DataStores/DBCStores.cpp
+++ b/src/server/game/DataStores/DBCStores.cpp
@@ -62,6 +62,7 @@ DBCStorage <BankBagSlotPricesEntry> sBankBagSlotPricesStore(BankBagSlotPricesEnt
DBCStorage <BattlemasterListEntry> sBattlemasterListStore(BattlemasterListEntryfmt);
DBCStorage <BarberShopStyleEntry> sBarberShopStyleStore(BarberShopStyleEntryfmt);
DBCStorage <CharStartOutfitEntry> sCharStartOutfitStore(CharStartOutfitEntryfmt);
+std::map<uint32, CharStartOutfitEntry const*> sCharStartOutfitMap;
DBCStorage <CharTitlesEntry> sCharTitlesStore(CharTitlesEntryfmt);
DBCStorage <ChatChannelsEntry> sChatChannelsStore(ChatChannelsEntryfmt);
DBCStorage <ChrClassesEntry> sChrClassesStore(ChrClassesEntryfmt);
@@ -237,9 +238,10 @@ inline void LoadDBC(uint32& availableDbcLocales, StoreProblemList& errors, DBCSt
// sort problematic dbc to (1) non compatible and (2) non-existed
if (FILE* f = fopen(dbcFilename.c_str(), "rb"))
{
- char buf[100];
- snprintf(buf, 100, " exists, and has %u field(s) (expected " SIZEFMTD "). Extracted file might be from wrong client version or a database-update has been forgotten.", storage.GetFieldCount(), strlen(storage.GetFormat()));
- errors.push_back(dbcFilename + buf);
+ std::ostringstream stream;
+ stream << dbcFilename << " exists, and has " << storage.GetFieldCount() << " field(s) (expected " << strlen(storage.GetFormat()) << "). Extracted file might be from wrong client version or a database-update has been forgotten.";
+ std::string buf = stream.str();
+ errors.push_back(buf);
fclose(f);
}
else
@@ -284,6 +286,10 @@ void LoadDBCStores(const std::string& dataPath)
LoadDBC(availableDbcLocales, bad_dbc_files, sBattlemasterListStore, dbcPath, "BattlemasterList.dbc");
LoadDBC(availableDbcLocales, bad_dbc_files, sBarberShopStyleStore, dbcPath, "BarberShopStyle.dbc");
LoadDBC(availableDbcLocales, bad_dbc_files, sCharStartOutfitStore, dbcPath, "CharStartOutfit.dbc");
+ for (uint32 i = 0; i < sCharStartOutfitStore.GetNumRows(); ++i)
+ if (CharStartOutfitEntry const* outfit = sCharStartOutfitStore.LookupEntry(i))
+ sCharStartOutfitMap[outfit->Race | (outfit->Class << 8) | (outfit->Gender << 16)] = outfit;
+
LoadDBC(availableDbcLocales, bad_dbc_files, sCharTitlesStore, dbcPath, "CharTitles.dbc");
LoadDBC(availableDbcLocales, bad_dbc_files, sChatChannelsStore, dbcPath, "ChatChannels.dbc");
LoadDBC(availableDbcLocales, bad_dbc_files, sChrClassesStore, dbcPath, "ChrClasses.dbc");
@@ -872,3 +878,11 @@ uint32 GetLiquidFlags(uint32 liquidType)
return 0;
}
+CharStartOutfitEntry const* GetCharStartOutfitEntry(uint8 race, uint8 class_, uint8 gender)
+{
+ std::map<uint32, CharStartOutfitEntry const*>::const_iterator itr = sCharStartOutfitMap.find(race | (class_ << 8) | (gender << 16));
+ if (itr == sCharStartOutfitMap.end())
+ return NULL;
+
+ return itr->second;
+}
diff --git a/src/server/game/DataStores/DBCStores.h b/src/server/game/DataStores/DBCStores.h
index c77d0ee32f1..48a2cb3fe4e 100644
--- a/src/server/game/DataStores/DBCStores.h
+++ b/src/server/game/DataStores/DBCStores.h
@@ -65,6 +65,8 @@ uint32 GetLiquidFlags(uint32 liquidType);
PvPDifficultyEntry const* GetBattlegroundBracketByLevel(uint32 mapid, uint32 level);
PvPDifficultyEntry const* GetBattlegroundBracketById(uint32 mapid, BattlegroundBracketId id);
+CharStartOutfitEntry const* GetCharStartOutfitEntry(uint8 race, uint8 class_, uint8 gender);
+
extern DBCStorage <AchievementEntry> sAchievementStore;
extern DBCStorage <AchievementCriteriaEntry> sAchievementCriteriaStore;
extern DBCStorage <AreaTableEntry> sAreaStore;// recommend access using functions
diff --git a/src/server/game/DataStores/DBCStructure.h b/src/server/game/DataStores/DBCStructure.h
index aa19863ec29..529c84744bd 100644
--- a/src/server/game/DataStores/DBCStructure.h
+++ b/src/server/game/DataStores/DBCStructure.h
@@ -629,13 +629,13 @@ struct BattlemasterListEntry
struct CharStartOutfitEntry
{
//uint32 Id; // 0
- uint32 RaceClassGender; // 1 (UNIT_FIELD_BYTES_0 & 0x00FFFFFF) comparable (0 byte = race, 1 byte = class, 2 byte = gender)
- int32 ItemId[MAX_OUTFIT_ITEMS]; // 2-13
- //int32 ItemDisplayId[MAX_OUTFIT_ITEMS]; // 14-25 not required at server side
- //int32 ItemInventorySlot[MAX_OUTFIT_ITEMS]; // 26-37 not required at server side
- //uint32 Unknown1; // 38, unique values (index-like with gaps ordered in other way as ids)
- //uint32 Unknown2; // 39
- //uint32 Unknown3; // 40
+ uint8 Race; // 1
+ uint8 Class; // 2
+ uint8 Gender; // 3
+ //uint8 Unused; // 4
+ int32 ItemId[MAX_OUTFIT_ITEMS]; // 5-28
+ //int32 ItemDisplayId[MAX_OUTFIT_ITEMS]; // 29-52 not required at server side
+ //int32 ItemInventorySlot[MAX_OUTFIT_ITEMS]; // 53-76 not required at server side
};
struct CharTitlesEntry
diff --git a/src/server/game/DataStores/DBCfmt.h b/src/server/game/DataStores/DBCfmt.h
index d0784f05c4d..c82fdf868b3 100644
--- a/src/server/game/DataStores/DBCfmt.h
+++ b/src/server/game/DataStores/DBCfmt.h
@@ -19,111 +19,110 @@
#ifndef TRINITY_DBCSFRM_H
#define TRINITY_DBCSFRM_H
-char const Achievementfmt[]="niixssssssssssssssssxxxxxxxxxxxxxxxxxxiixixxxxxxxxxxxxxxxxxxii";
-const std::string CustomAchievementfmt="pppaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaapapaaaaaaaaaaaaaaaaaapp";
+char const Achievementfmt[] = "niixssssssssssssssssxxxxxxxxxxxxxxxxxxiixixxxxxxxxxxxxxxxxxxii";
+const std::string CustomAchievementfmt = "pppaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaapapaaaaaaaaaaaaaaaaaapp";
const std::string CustomAchievementIndex = "ID";
-char const AchievementCriteriafmt[]="niiiiiiiixxxxxxxxxxxxxxxxxiiiix";
-char const AreaTableEntryfmt[]="iiinixxxxxissssssssssssssssxiiiiixxx";
-char const AreaGroupEntryfmt[]="niiiiiii";
-char const AreaPOIEntryfmt[]="niiiiiiiiiiifffixixxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxix";
-char const AreaTriggerEntryfmt[]="niffffffff";
-char const AuctionHouseEntryfmt[]="niiixxxxxxxxxxxxxxxxx";
-char const BankBagSlotPricesEntryfmt[]="ni";
-char const BarberShopStyleEntryfmt[]="nixxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxiii";
-char const BattlemasterListEntryfmt[]="niiiiiiiiixssssssssssssssssxiixx";
-char const CharStartOutfitEntryfmt[]="xniiiiiiiiiiiiiiiiiiiiiiiixxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
-char const CharTitlesEntryfmt[]="nxssssssssssssssssxxxxxxxxxxxxxxxxxxi";
-char const ChatChannelsEntryfmt[]="nixssssssssssssssssxxxxxxxxxxxxxxxxxx";
- // ChatChannelsEntryfmt, index not used (more compact store)
-char const ChrClassesEntryfmt[]="nxixxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxixii";
-char const ChrRacesEntryfmt[]="nxixiixixxxxixssssssssssssssssxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxi";
-char const CinematicSequencesEntryfmt[]="nxxxxxxxxx";
-char const CreatureDisplayInfofmt[]="nixxfxxxxxxxxxxx";
-char const CreatureFamilyfmt[]="nfifiiiiixssssssssssssssssxx";
-char const CreatureModelDatafmt[]="nxxxfxxxxxxxxxxffxxxxxxxxxxx";
-char const CreatureSpellDatafmt[]="niiiixxxx";
-char const CreatureTypefmt[]="nxxxxxxxxxxxxxxxxxx";
-char const CurrencyTypesfmt[]="xnxi";
-char const DestructibleModelDatafmt[]="nxxixxxixxxixxxixxx";
-char const DungeonEncounterfmt[]="niixissssssssssssssssxx";
-char const DurabilityCostsfmt[]="niiiiiiiiiiiiiiiiiiiiiiiiiiiii";
-char const DurabilityQualityfmt[]="nf";
-char const EmotesEntryfmt[]="nxxiiix";
-char const EmotesTextEntryfmt[]="nxixxxxxxxxxxxxxxxx";
-char const FactionEntryfmt[]="niiiiiiiiiiiiiiiiiiffixssssssssssssssssxxxxxxxxxxxxxxxxxx";
-char const FactionTemplateEntryfmt[]="niiiiiiiiiiiii";
-char const GameObjectDisplayInfofmt[]="nsxxxxxxxxxxffffffx";
-char const GemPropertiesEntryfmt[]="nixxi";
-char const GlyphPropertiesfmt[]="niii";
-char const GlyphSlotfmt[]="nii";
-char const GtBarberShopCostBasefmt[]="f";
-char const GtCombatRatingsfmt[]="f";
-char const GtChanceToMeleeCritBasefmt[]="f";
-char const GtChanceToMeleeCritfmt[]="f";
-char const GtChanceToSpellCritBasefmt[]="f";
-char const GtChanceToSpellCritfmt[]="f";
-char const GtOCTClassCombatRatingScalarfmt[]="df";
-char const GtOCTRegenHPfmt[]="f";
-//char const GtOCTRegenMPfmt[]="f";
-char const GtRegenHPPerSptfmt[]="f";
-char const GtRegenMPPerSptfmt[]="f";
-char const Holidaysfmt[]="niiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiixxsiix";
-char const Itemfmt[]="niiiiiii";
-char const ItemBagFamilyfmt[]="nxxxxxxxxxxxxxxxxx";
-//char const ItemDisplayTemplateEntryfmt[]="nxxxxxxxxxxixxxxxxxxxxx";
-//char const ItemCondExtCostsEntryfmt[]="xiii";
-char const ItemExtendedCostEntryfmt[]="niiiiiiiiiiiiiix";
-char const ItemLimitCategoryEntryfmt[]="nxxxxxxxxxxxxxxxxxii";
-char const ItemRandomPropertiesfmt[]="nxiiixxssssssssssssssssx";
-char const ItemRandomSuffixfmt[]="nssssssssssssssssxxiiixxiiixx";
-char const ItemSetEntryfmt[]="dssssssssssssssssxiiiiiiiiiixxxxxxxiiiiiiiiiiiiiiiiii";
-char const LFGDungeonEntryfmt[]="nssssssssssssssssxiiiiiiiiixxixixxxxxxxxxxxxxxxxx";
-char const LiquidTypefmt[]="nxxixixxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
-char const LockEntryfmt[]="niiiiiiiiiiiiiiiiiiiiiiiixxxxxxxx";
-char const MailTemplateEntryfmt[]="nxxxxxxxxxxxxxxxxxssssssssssssssssx";
-char const MapEntryfmt[]="nxixxssssssssssssssssxixxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxixiffxiix";
-char const MapDifficultyEntryfmt[]="diisxxxxxxxxxxxxxxxxiix";
-char const MovieEntryfmt[]="nxx";
-char const OverrideSpellDatafmt[]="niiiiiiiiiix";
-char const QuestSortEntryfmt[]="nxxxxxxxxxxxxxxxxx";
-char const QuestXPfmt[]="niiiiiiiiii";
-char const QuestFactionRewardfmt[]="niiiiiiiiii";
-char const PvPDifficultyfmt[]="diiiii";
-char const RandomPropertiesPointsfmt[]="niiiiiiiiiiiiiii";
-char const ScalingStatDistributionfmt[]="niiiiiiiiiiiiiiiiiiiii";
-char const ScalingStatValuesfmt[]="iniiiiiiiiiiiiiiiiiiiiii";
-char const SkillLinefmt[]="nixssssssssssssssssxxxxxxxxxxxxxxxxxxixxxxxxxxxxxxxxxxxi";
-char const SkillLineAbilityfmt[]="niiiixxiiiiixx";
-char const SoundEntriesfmt[]="nxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
-char const SpellCastTimefmt[]="nixx";
-char const SpellDifficultyfmt[]="niiii";
-const std::string CustomSpellDifficultyfmt="ppppp";
-const std::string CustomSpellDifficultyIndex="id";
-char const SpellDurationfmt[]="niii";
-char const SpellEntryfmt[]="niiiiiiiiiiiixixiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiifxiiiiiiiiiiiiiiiiiiiiiiiiiiiifffiiiiiiiiiiiiiiiiiiiiifffiiiiiiiiiiiiiiifffiiiiiiiiiiiiixssssssssssssssssxssssssssssssssssxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxiiiiiiiiiiixfffxxxiiiiixxfffxx";
-const std::string CustomSpellEntryfmt="papppppppppppapapaaaaaaaaaaapaaapapppppppaaaaapaapaaaaaaaaaaaaaaaaaappppppppppppppppppppppppppppppppppppaaaaaapppppppppaaapppppppppaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaappppppppapppaaaaappaaaaaaa";
+char const AchievementCriteriafmt[] = "niiiiiiiixxxxxxxxxxxxxxxxxiiiix";
+char const AreaTableEntryfmt[] = "iiinixxxxxissssssssssssssssxiiiiixxx";
+char const AreaGroupEntryfmt[] = "niiiiiii";
+char const AreaPOIEntryfmt[] = "niiiiiiiiiiifffixixxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxix";
+char const AreaTriggerEntryfmt[] = "niffffffff";
+char const AuctionHouseEntryfmt[] = "niiixxxxxxxxxxxxxxxxx";
+char const BankBagSlotPricesEntryfmt[] = "ni";
+char const BarberShopStyleEntryfmt[] = "nixxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxiii";
+char const BattlemasterListEntryfmt[] = "niiiiiiiiixssssssssssssssssxiixx";
+char const CharStartOutfitEntryfmt[] = "dbbbXiiiiiiiiiiiiiiiiiiiiiiiixxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
+char const CharTitlesEntryfmt[] = "nxssssssssssssssssxxxxxxxxxxxxxxxxxxi";
+char const ChatChannelsEntryfmt[] = "nixssssssssssssssssxxxxxxxxxxxxxxxxxx";
+char const ChrClassesEntryfmt[] = "nxixxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxixii";
+char const ChrRacesEntryfmt[] = "nxixiixixxxxixssssssssssssssssxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxi";
+char const CinematicSequencesEntryfmt[] = "nxxxxxxxxx";
+char const CreatureDisplayInfofmt[] = "nixxfxxxxxxxxxxx";
+char const CreatureFamilyfmt[] = "nfifiiiiixssssssssssssssssxx";
+char const CreatureModelDatafmt[] = "nxxxfxxxxxxxxxxffxxxxxxxxxxx";
+char const CreatureSpellDatafmt[] = "niiiixxxx";
+char const CreatureTypefmt[] = "nxxxxxxxxxxxxxxxxxx";
+char const CurrencyTypesfmt[] = "xnxi";
+char const DestructibleModelDatafmt[] = "nxxixxxixxxixxxixxx";
+char const DungeonEncounterfmt[] = "niixissssssssssssssssxx";
+char const DurabilityCostsfmt[] = "niiiiiiiiiiiiiiiiiiiiiiiiiiiii";
+char const DurabilityQualityfmt[] = "nf";
+char const EmotesEntryfmt[] = "nxxiiix";
+char const EmotesTextEntryfmt[] = "nxixxxxxxxxxxxxxxxx";
+char const FactionEntryfmt[] = "niiiiiiiiiiiiiiiiiiffixssssssssssssssssxxxxxxxxxxxxxxxxxx";
+char const FactionTemplateEntryfmt[] = "niiiiiiiiiiiii";
+char const GameObjectDisplayInfofmt[] = "nsxxxxxxxxxxffffffx";
+char const GemPropertiesEntryfmt[] = "nixxi";
+char const GlyphPropertiesfmt[] = "niii";
+char const GlyphSlotfmt[] = "nii";
+char const GtBarberShopCostBasefmt[] = "f";
+char const GtCombatRatingsfmt[] = "f";
+char const GtChanceToMeleeCritBasefmt[] = "f";
+char const GtChanceToMeleeCritfmt[] = "f";
+char const GtChanceToSpellCritBasefmt[] = "f";
+char const GtChanceToSpellCritfmt[] = "f";
+char const GtOCTClassCombatRatingScalarfmt[] = "df";
+char const GtOCTRegenHPfmt[] = "f";
+//char const GtOCTRegenMPfmt[] = "f";
+char const GtRegenHPPerSptfmt[] = "f";
+char const GtRegenMPPerSptfmt[] = "f";
+char const Holidaysfmt[] = "niiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiixxsiix";
+char const Itemfmt[] = "niiiiiii";
+char const ItemBagFamilyfmt[] = "nxxxxxxxxxxxxxxxxx";
+//char const ItemDisplayTemplateEntryfmt[] = "nxxxxxxxxxxixxxxxxxxxxx";
+//char const ItemCondExtCostsEntryfmt[] = "xiii";
+char const ItemExtendedCostEntryfmt[] = "niiiiiiiiiiiiiix";
+char const ItemLimitCategoryEntryfmt[] = "nxxxxxxxxxxxxxxxxxii";
+char const ItemRandomPropertiesfmt[] = "nxiiixxssssssssssssssssx";
+char const ItemRandomSuffixfmt[] = "nssssssssssssssssxxiiixxiiixx";
+char const ItemSetEntryfmt[] = "dssssssssssssssssxiiiiiiiiiixxxxxxxiiiiiiiiiiiiiiiiii";
+char const LFGDungeonEntryfmt[] = "nssssssssssssssssxiiiiiiiiixxixixxxxxxxxxxxxxxxxx";
+char const LiquidTypefmt[] = "nxxixixxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
+char const LockEntryfmt[] = "niiiiiiiiiiiiiiiiiiiiiiiixxxxxxxx";
+char const MailTemplateEntryfmt[] = "nxxxxxxxxxxxxxxxxxssssssssssssssssx";
+char const MapEntryfmt[] = "nxixxssssssssssssssssxixxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxixiffxiix";
+char const MapDifficultyEntryfmt[] = "diisxxxxxxxxxxxxxxxxiix";
+char const MovieEntryfmt[] = "nxx";
+char const OverrideSpellDatafmt[] = "niiiiiiiiiix";
+char const QuestFactionRewardfmt[] = "niiiiiiiiii";
+char const QuestSortEntryfmt[] = "nxxxxxxxxxxxxxxxxx";
+char const QuestXPfmt[] = "niiiiiiiiii";
+char const PvPDifficultyfmt[] = "diiiii";
+char const RandomPropertiesPointsfmt[] = "niiiiiiiiiiiiiii";
+char const ScalingStatDistributionfmt[] = "niiiiiiiiiiiiiiiiiiiii";
+char const ScalingStatValuesfmt[] = "iniiiiiiiiiiiiiiiiiiiiii";
+char const SkillLinefmt[] = "nixssssssssssssssssxxxxxxxxxxxxxxxxxxixxxxxxxxxxxxxxxxxi";
+char const SkillLineAbilityfmt[] = "niiiixxiiiiixx";
+char const SoundEntriesfmt[] = "nxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
+char const SpellCastTimefmt[] = "nixx";
+char const SpellDifficultyfmt[] = "niiii";
+const std::string CustomSpellDifficultyfmt = "ppppp";
+const std::string CustomSpellDifficultyIndex = "id";
+char const SpellDurationfmt[] = "niii";
+char const SpellEntryfmt[] = "niiiiiiiiiiiixixiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiifxiiiiiiiiiiiiiiiiiiiiiiiiiiiifffiiiiiiiiiiiiiiiiiiiiifffiiiiiiiiiiiiiiifffiiiiiiiiiiiiixssssssssssssssssxssssssssssssssssxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxiiiiiiiiiiixfffxxxiiiiixxfffxx";
+const std::string CustomSpellEntryfmt = "papppppppppppapapaaaaaaaaaaapaaapapppppppaaaaapaapaaaaaaaaaaaaaaaaaappppppppppppppppppppppppppppppppppppaaaaaapppppppppaaapppppppppaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaappppppppapppaaaaappaaaaaaa";
const std::string CustomSpellEntryIndex = "Id";
-char const SpellFocusObjectfmt[]="nxxxxxxxxxxxxxxxxx";
-char const SpellItemEnchantmentfmt[]="nxiiiiiixxxiiissssssssssssssssxiiiiiii";
-char const SpellItemEnchantmentConditionfmt[]="nbbbbbxxxxxbbbbbbbbbbiiiiiXXXXX";
-char const SpellRadiusfmt[]="nfff";
-char const SpellRangefmt[]="nffffixxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
-char const SpellRuneCostfmt[]="niiii";
-char const SpellShapeshiftfmt[]="nxxxxxxxxxxxxxxxxxxiixiiixxiiiiiiii";
+char const SpellFocusObjectfmt[] = "nxxxxxxxxxxxxxxxxx";
+char const SpellItemEnchantmentfmt[] = "nxiiiiiixxxiiissssssssssssssssxiiiiiii";
+char const SpellItemEnchantmentConditionfmt[] = "nbbbbbxxxxxbbbbbbbbbbiiiiiXXXXX";
+char const SpellRadiusfmt[] = "nfff";
+char const SpellRangefmt[] = "nffffixxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
+char const SpellRuneCostfmt[] = "niiii";
+char const SpellShapeshiftfmt[] = "nxxxxxxxxxxxxxxxxxxiixiiixxiiiiiiii";
char const StableSlotPricesfmt[] = "ni";
char const SummonPropertiesfmt[] = "niiiii";
-char const TalentEntryfmt[]="niiiiiiiixxxxixxixxxxxx";
-char const TalentTabEntryfmt[]="nxxxxxxxxxxxxxxxxxxxiiix";
-char const TaxiNodesEntryfmt[]="nifffssssssssssssssssxii";
-char const TaxiPathEntryfmt[]="niii";
-char const TaxiPathNodeEntryfmt[]="diiifffiiii";
-char const TeamContributionPointsfmt[]="df";
-char const TotemCategoryEntryfmt[]="nxxxxxxxxxxxxxxxxxii";
-char const VehicleEntryfmt[]="niffffiiiiiiiifffffffffffffffssssfifiixx";
-char const VehicleSeatEntryfmt[]="niiffffffffffiiiiiifffffffiiifffiiiiiiiffiiiiixxxxxxxxxxxx";
-char const WMOAreaTableEntryfmt[]="niiixxxxxiixxxxxxxxxxxxxxxxx";
-char const WorldMapAreaEntryfmt[]="xinxffffixx";
-char const WorldMapOverlayEntryfmt[]="nxiiiixxxxxxxxxxx";
-char const WorldSafeLocsEntryfmt[]="nifffxxxxxxxxxxxxxxxxx";
+char const TalentEntryfmt[] = "niiiiiiiixxxxixxixxxxxx";
+char const TalentTabEntryfmt[] = "nxxxxxxxxxxxxxxxxxxxiiix";
+char const TaxiNodesEntryfmt[] = "nifffssssssssssssssssxii";
+char const TaxiPathEntryfmt[] = "niii";
+char const TaxiPathNodeEntryfmt[] = "diiifffiiii";
+char const TeamContributionPointsfmt[] = "df";
+char const TotemCategoryEntryfmt[] = "nxxxxxxxxxxxxxxxxxii";
+char const VehicleEntryfmt[] = "niffffiiiiiiiifffffffffffffffssssfifiixx";
+char const VehicleSeatEntryfmt[] = "niiffffffffffiiiiiifffffffiiifffiiiiiiiffiiiiixxxxxxxxxxxx";
+char const WMOAreaTableEntryfmt[] = "niiixxxxxiixxxxxxxxxxxxxxxxx";
+char const WorldMapAreaEntryfmt[] = "xinxffffixx";
+char const WorldMapOverlayEntryfmt[] = "nxiiiixxxxxxxxxxx";
+char const WorldSafeLocsEntryfmt[] = "nifffxxxxxxxxxxxxxxxxx";
#endif
diff --git a/src/server/game/DungeonFinding/LFGMgr.cpp b/src/server/game/DungeonFinding/LFGMgr.cpp
index 9d5d8e40b54..a931d61f740 100644
--- a/src/server/game/DungeonFinding/LFGMgr.cpp
+++ b/src/server/game/DungeonFinding/LFGMgr.cpp
@@ -1563,7 +1563,6 @@ void LFGMgr::FinishDungeon(uint64 gguid, const uint32 dungeonId)
LfgPlayerRewardData data = LfgPlayerRewardData(dungeon->Entry(), GetDungeon(gguid, false), done, quest);
player->GetSession()->SendLfgPlayerReward(data);
}
- SetDungeon(gguid, 0);
}
// --------------------------------------------------------------------------//
@@ -1632,6 +1631,18 @@ LfgState LFGMgr::GetState(uint64 guid)
return state;
}
+LfgState LFGMgr::GetOldState(uint64 guid)
+{
+ LfgState state;
+ if (IS_GROUP_GUID(guid))
+ state = GroupsStore[guid].GetOldState();
+ else
+ state = PlayersStore[guid].GetOldState();
+
+ sLog->outTrace(LOG_FILTER_LFG, "LFGMgr::GetOldState: [" UI64FMTD "] = %u", guid, state);
+ return state;
+}
+
uint32 LFGMgr::GetDungeon(uint64 guid, bool asId /*= true */)
{
uint32 dungeon = GroupsStore[guid].GetDungeon(asId);
@@ -1693,7 +1704,7 @@ uint8 LFGMgr::GetKicksLeft(uint64 guid)
return kicks;
}
-void LFGMgr::RestoreState(uint64 guid, char const *debugMsg)
+void LFGMgr::RestoreState(uint64 guid, char const* debugMsg)
{
if (IS_GROUP_GUID(guid))
{
diff --git a/src/server/game/DungeonFinding/LFGMgr.h b/src/server/game/DungeonFinding/LFGMgr.h
index 0a1b90ee04d..74c7ca9e3d3 100644
--- a/src/server/game/DungeonFinding/LFGMgr.h
+++ b/src/server/game/DungeonFinding/LFGMgr.h
@@ -347,6 +347,7 @@ class LFGMgr
uint32 GetDungeon(uint64 guid, bool asId = true);
uint32 GetDungeonMapId(uint64 guid);
LfgState GetState(uint64 guid);
+ LfgState GetOldState(uint64 guid);
uint8 GetKicksLeft(uint64 gguid);
uint64 GetLeader(uint64 guid);
bool IsLfgGroup(uint64 guid);
@@ -382,8 +383,8 @@ class LFGMgr
private:
uint8 GetTeam(uint64 guid);
- void RestoreState(uint64 guid, char const *debugMsg);
- void ClearState(uint64 guid, char const *debugMsg);
+ void RestoreState(uint64 guid, char const* debugMsg);
+ void ClearState(uint64 guid, char const* debugMsg);
void SetDungeon(uint64 guid, uint32 dungeon);
void SetSelectedDungeons(uint64 guid, LfgDungeonSet const& dungeons);
void SetLockedDungeons(uint64 guid, LfgLockMap const& lock);
diff --git a/src/server/game/DungeonFinding/LFGQueue.cpp b/src/server/game/DungeonFinding/LFGQueue.cpp
index e75a1bdc4b3..f1d2dbb313d 100644
--- a/src/server/game/DungeonFinding/LFGQueue.cpp
+++ b/src/server/game/DungeonFinding/LFGQueue.cpp
@@ -483,7 +483,7 @@ LfgCompatibility LFGQueue::CheckCompatibility(LfgGuidList check)
uint64 gguid = *check.begin();
proposal.queues = check;
- proposal.isNew = numLfgGroups != 1 || !sLFGMgr->GetDungeon(gguid);
+ proposal.isNew = numLfgGroups != 1 || sLFGMgr->GetOldState(gguid) != LFG_STATE_DUNGEON;
if (!sLFGMgr->AllQueued(check))
{
diff --git a/src/server/game/Entities/Creature/Creature.cpp b/src/server/game/Entities/Creature/Creature.cpp
index 5b3969c9b9d..93d4796f21a 100644
--- a/src/server/game/Entities/Creature/Creature.cpp
+++ b/src/server/game/Entities/Creature/Creature.cpp
@@ -2148,7 +2148,7 @@ bool Creature::LoadCreaturesAddon(bool reload)
if (HasAura(*itr))
{
if (!reload)
- sLog->outError(LOG_FILTER_SQL, "Creature (GUID: %u Entry: %u) has duplicate aura (spell %u) in `auras` field.", GetGUIDLow(), GetEntry(), *itr);
+ sLog->outError(LOG_FILTER_SQL, "Creature (GUID: %u Entry: %u) has duplicate aura (spell %u) in `auras` field.", GetDBTableGUIDLow(), GetEntry(), *itr);
continue;
}
diff --git a/src/server/game/Entities/GameObject/GameObject.h b/src/server/game/Entities/GameObject/GameObject.h
index 7b1ccbc9015..1049285605e 100644
--- a/src/server/game/Entities/GameObject/GameObject.h
+++ b/src/server/game/Entities/GameObject/GameObject.h
@@ -790,7 +790,7 @@ class GameObject : public WorldObject, public GridObject<GameObject>
void SetDisplayId(uint32 displayid);
uint32 GetDisplayId() const { return GetUInt32Value(GAMEOBJECT_DISPLAYID); }
- GameObjectModel * m_model;
+ GameObjectModel* m_model;
protected:
bool AIM_Initialize();
uint32 m_spellId;
diff --git a/src/server/game/Entities/Object/Object.cpp b/src/server/game/Entities/Object/Object.cpp
index 1cd36aec78f..f20d452dc96 100644
--- a/src/server/game/Entities/Object/Object.cpp
+++ b/src/server/game/Entities/Object/Object.cpp
@@ -75,7 +75,6 @@ Object::Object() : m_PackGUID(sizeof(uint64)+1)
m_objectType = TYPEMASK_OBJECT;
m_uint32Values = NULL;
- _changedFields = NULL;
m_valuesCount = 0;
_fieldNotifyFlags = UF_FLAG_DYNAMIC;
@@ -119,8 +118,6 @@ Object::~Object()
}
delete [] m_uint32Values;
- delete [] _changedFields;
-
}
void Object::_InitValues()
@@ -128,8 +125,7 @@ void Object::_InitValues()
m_uint32Values = new uint32[m_valuesCount];
memset(m_uint32Values, 0, m_valuesCount*sizeof(uint32));
- _changedFields = new bool[m_valuesCount];
- memset(_changedFields, 0, m_valuesCount*sizeof(bool));
+ _changesMask.SetCount(m_valuesCount);
m_objectUpdated = false;
}
@@ -757,7 +753,7 @@ void Object::_BuildValuesUpdate(uint8 updatetype, ByteBuffer * data, UpdateMask*
void Object::ClearUpdateMask(bool remove)
{
- memset(_changedFields, 0, m_valuesCount*sizeof(bool));
+ _changesMask.Clear();
if (m_objectUpdated)
{
@@ -854,13 +850,12 @@ void Object::_LoadIntoDataField(std::string const& data, uint32 startOffset, uin
for (uint32 index = 0; index < count; ++index)
{
m_uint32Values[startOffset + index] = atol(tokens[index]);
- _changedFields[startOffset + index] = true;
+ _changesMask.SetBit(startOffset + index);
}
}
void Object::_SetUpdateBits(UpdateMask* updateMask, Player* target) const
{
- bool* indexes = _changedFields;
uint32* flags = NULL;
bool isSelf = target == this;
bool isOwner = false;
@@ -870,8 +865,8 @@ void Object::_SetUpdateBits(UpdateMask* updateMask, Player* target) const
GetUpdateFieldData(target, flags, isOwner, isItemOwner, hasSpecialInfo, isPartyMember);
- for (uint16 index = 0; index < m_valuesCount; ++index, ++indexes)
- if (_fieldNotifyFlags & flags[index] || (flags[index] & UF_FLAG_SPECIAL_INFO && hasSpecialInfo) || (*indexes && IsUpdateFieldVisible(flags[index], isSelf, isOwner, isItemOwner, isPartyMember)))
+ for (uint16 index = 0; index < m_valuesCount; ++index)
+ if (_fieldNotifyFlags & flags[index] || (flags[index] & UF_FLAG_SPECIAL_INFO && hasSpecialInfo) || (_changesMask.GetBit(index) && IsUpdateFieldVisible(flags[index], isSelf, isOwner, isItemOwner, isPartyMember)))
updateMask->SetBit(index);
}
@@ -899,7 +894,7 @@ void Object::SetInt32Value(uint16 index, int32 value)
if (m_int32Values[index] != value)
{
m_int32Values[index] = value;
- _changedFields[index] = true;
+ _changesMask.SetBit(index);
if (m_inWorld && !m_objectUpdated)
{
@@ -916,7 +911,7 @@ void Object::SetUInt32Value(uint16 index, uint32 value)
if (m_uint32Values[index] != value)
{
m_uint32Values[index] = value;
- _changedFields[index] = true;
+ _changesMask.SetBit(index);
if (m_inWorld && !m_objectUpdated)
{
@@ -931,7 +926,7 @@ void Object::UpdateUInt32Value(uint16 index, uint32 value)
ASSERT(index < m_valuesCount || PrintIndexError(index, true));
m_uint32Values[index] = value;
- _changedFields[index] = true;
+ _changesMask.SetBit(index);
}
void Object::SetUInt64Value(uint16 index, uint64 value)
@@ -941,8 +936,8 @@ void Object::SetUInt64Value(uint16 index, uint64 value)
{
m_uint32Values[index] = PAIR64_LOPART(value);
m_uint32Values[index + 1] = PAIR64_HIPART(value);
- _changedFields[index] = true;
- _changedFields[index + 1] = true;
+ _changesMask.SetBit(index);
+ _changesMask.SetBit(index + 1);
if (m_inWorld && !m_objectUpdated)
{
@@ -959,8 +954,8 @@ bool Object::AddUInt64Value(uint16 index, uint64 value)
{
m_uint32Values[index] = PAIR64_LOPART(value);
m_uint32Values[index + 1] = PAIR64_HIPART(value);
- _changedFields[index] = true;
- _changedFields[index + 1] = true;
+ _changesMask.SetBit(index);
+ _changesMask.SetBit(index + 1);
if (m_inWorld && !m_objectUpdated)
{
@@ -981,8 +976,8 @@ bool Object::RemoveUInt64Value(uint16 index, uint64 value)
{
m_uint32Values[index] = 0;
m_uint32Values[index + 1] = 0;
- _changedFields[index] = true;
- _changedFields[index + 1] = true;
+ _changesMask.SetBit(index);
+ _changesMask.SetBit(index + 1);
if (m_inWorld && !m_objectUpdated)
{
@@ -1003,7 +998,7 @@ void Object::SetFloatValue(uint16 index, float value)
if (m_floatValues[index] != value)
{
m_floatValues[index] = value;
- _changedFields[index] = true;
+ _changesMask.SetBit(index);
if (m_inWorld && !m_objectUpdated)
{
@@ -1027,7 +1022,7 @@ void Object::SetByteValue(uint16 index, uint8 offset, uint8 value)
{
m_uint32Values[index] &= ~uint32(uint32(0xFF) << (offset * 8));
m_uint32Values[index] |= uint32(uint32(value) << (offset * 8));
- _changedFields[index] = true;
+ _changesMask.SetBit(index);
if (m_inWorld && !m_objectUpdated)
{
@@ -1051,7 +1046,7 @@ void Object::SetUInt16Value(uint16 index, uint8 offset, uint16 value)
{
m_uint32Values[index] &= ~uint32(uint32(0xFFFF) << (offset * 16));
m_uint32Values[index] |= uint32(uint32(value) << (offset * 16));
- _changedFields[index] = true;
+ _changesMask.SetBit(index);
if (m_inWorld && !m_objectUpdated)
{
@@ -1118,7 +1113,7 @@ void Object::SetFlag(uint16 index, uint32 newFlag)
if (oldval != newval)
{
m_uint32Values[index] = newval;
- _changedFields[index] = true;
+ _changesMask.SetBit(index);
if (m_inWorld && !m_objectUpdated)
{
@@ -1139,7 +1134,7 @@ void Object::RemoveFlag(uint16 index, uint32 oldFlag)
if (oldval != newval)
{
m_uint32Values[index] = newval;
- _changedFields[index] = true;
+ _changesMask.SetBit(index);
if (m_inWorld && !m_objectUpdated)
{
@@ -1162,7 +1157,7 @@ void Object::SetByteFlag(uint16 index, uint8 offset, uint8 newFlag)
if (!(uint8(m_uint32Values[index] >> (offset * 8)) & newFlag))
{
m_uint32Values[index] |= uint32(uint32(newFlag) << (offset * 8));
- _changedFields[index] = true;
+ _changesMask.SetBit(index);
if (m_inWorld && !m_objectUpdated)
{
@@ -1185,7 +1180,7 @@ void Object::RemoveByteFlag(uint16 index, uint8 offset, uint8 oldFlag)
if (uint8(m_uint32Values[index] >> (offset * 8)) & oldFlag)
{
m_uint32Values[index] &= ~uint32(uint32(oldFlag) << (offset * 8));
- _changedFields[index] = true;
+ _changesMask.SetBit(index);
if (m_inWorld && !m_objectUpdated)
{
@@ -1968,7 +1963,7 @@ void WorldObject::SendPlaySound(uint32 Sound, bool OnlySelf)
void Object::ForceValuesUpdateAtIndex(uint32 i)
{
- _changedFields[i] = true;
+ _changesMask.SetBit(i);
if (m_inWorld && !m_objectUpdated)
{
sObjectAccessor->AddUpdateObject(this);
@@ -2688,7 +2683,7 @@ void WorldObject::MovePosition(Position &pos, float dist, float angle)
desty = pos.m_positionY + dist * std::sin(angle);
// Prevent invalid coordinates here, position is unchanged
- if (!Trinity::IsValidMapCoord(destx, desty))
+ if (!Trinity::IsValidMapCoord(destx, desty, pos.m_positionZ))
{
sLog->outFatal(LOG_FILTER_GENERAL, "WorldObject::MovePosition invalid coordinates X: %f and Y: %f were passed!", destx, desty);
return;
diff --git a/src/server/game/Entities/Object/Object.h b/src/server/game/Entities/Object/Object.h
index a9afd074c67..641f9afb154 100644
--- a/src/server/game/Entities/Object/Object.h
+++ b/src/server/game/Entities/Object/Object.h
@@ -20,7 +20,7 @@
#define _OBJECT_H
#include "Common.h"
-#include "UpdateFields.h"
+#include "UpdateMask.h"
#include "UpdateData.h"
#include "GridReference.h"
#include "ObjectDefines.h"
@@ -107,7 +107,6 @@ class ByteBuffer;
class WorldSession;
class Creature;
class Player;
-class UpdateMask;
class InstanceScript;
class GameObject;
class TempSummon;
@@ -348,7 +347,7 @@ class Object
float *m_floatValues;
};
- bool* _changedFields;
+ UpdateMask _changesMask;
uint16 m_valuesCount;
diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp
index 14878b078bd..073fadde314 100644
--- a/src/server/game/Entities/Player/Player.cpp
+++ b/src/server/game/Entities/Player/Player.cpp
@@ -1120,7 +1120,7 @@ bool Player::Create(uint32 guidlow, CharacterCreateInfo* createInfo)
addActionButton(action_itr->button, action_itr->action, action_itr->type);
// original items
- if (CharStartOutfitEntry const* oEntry = sCharStartOutfitStore.LookupEntry(RaceClassGender))
+ if (CharStartOutfitEntry const* oEntry = GetCharStartOutfitEntry(createInfo->Race, createInfo->Class, createInfo->Gender))
{
for (int j = 0; j < MAX_OUTFIT_ITEMS; ++j)
{
@@ -2375,7 +2375,7 @@ void Player::ProcessDelayedOperations()
if (m_DelayedOperations & DELAYED_BG_GROUP_RESTORE)
{
- if (Group *g = GetGroup())
+ if (Group* g = GetGroup())
g->SendUpdateToPlayer(GetGUID());
}
@@ -3432,6 +3432,19 @@ void Player::AddNewMailDeliverTime(time_t deliver_time)
}
}
+void DeleteSpellFromAllPlayers(uint32 spellId)
+{
+ CharacterDatabaseStatements stmts[2] = {CHAR_DEL_INVALID_SPELL_SPELLS, CHAR_DEL_INVALID_SPELL_TALENTS};
+ for (uint8 i = 0; i < 2; i++)
+ {
+ PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(stmts[i]);
+
+ stmt->setUInt32(0, spellId);
+
+ CharacterDatabase.Execute(stmt);
+ }
+}
+
bool Player::AddTalent(uint32 spellId, uint8 spec, bool learning)
{
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId);
@@ -3442,11 +3455,7 @@ bool Player::AddTalent(uint32 spellId, uint8 spec, bool learning)
{
sLog->outError(LOG_FILTER_SPELLS_AURAS, "Player::addSpell: Non-existed in SpellStore spell #%u request, deleting for all characters in `character_spell`.", spellId);
- PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_INVALID_SPELL);
-
- stmt->setUInt32(0, spellId);
-
- CharacterDatabase.Execute(stmt);
+ DeleteSpellFromAllPlayers(spellId);
}
else
sLog->outError(LOG_FILTER_SPELLS_AURAS, "Player::addSpell: Non-existed in SpellStore spell #%u request.", spellId);
@@ -3461,11 +3470,7 @@ bool Player::AddTalent(uint32 spellId, uint8 spec, bool learning)
{
sLog->outError(LOG_FILTER_SPELLS_AURAS, "Player::addTalent: Broken spell #%u learning not allowed, deleting for all characters in `character_talent`.", spellId);
- PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_INVALID_SPELL);
-
- stmt->setUInt32(0, spellId);
-
- CharacterDatabase.Execute(stmt);
+ DeleteSpellFromAllPlayers(spellId);
}
else
sLog->outError(LOG_FILTER_SPELLS_AURAS, "Player::addTalent: Broken spell #%u learning not allowed.", spellId);
@@ -3515,11 +3520,7 @@ bool Player::addSpell(uint32 spellId, bool active, bool learning, bool dependent
{
sLog->outError(LOG_FILTER_SPELLS_AURAS, "Player::addSpell: Non-existed in SpellStore spell #%u request, deleting for all characters in `character_spell`.", spellId);
- PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_INVALID_SPELL);
-
- stmt->setUInt32(0, spellId);
-
- CharacterDatabase.Execute(stmt);
+ DeleteSpellFromAllPlayers(spellId);
}
else
sLog->outError(LOG_FILTER_SPELLS_AURAS, "Player::addSpell: Non-existed in SpellStore spell #%u request.", spellId);
@@ -3534,11 +3535,7 @@ bool Player::addSpell(uint32 spellId, bool active, bool learning, bool dependent
{
sLog->outError(LOG_FILTER_SPELLS_AURAS, "Player::addSpell: Broken spell #%u learning not allowed, deleting for all characters in `character_spell`.", spellId);
- PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_INVALID_SPELL);
-
- stmt->setUInt32(0, spellId);
-
- CharacterDatabase.Execute(stmt);
+ DeleteSpellFromAllPlayers(spellId);
}
else
sLog->outError(LOG_FILTER_SPELLS_AURAS, "Player::addSpell: Broken spell #%u learning not allowed.", spellId);
diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h
index fa82ac6f8c6..a1e6591413f 100644
--- a/src/server/game/Entities/Player/Player.h
+++ b/src/server/game/Entities/Player/Player.h
@@ -2219,7 +2219,11 @@ class Player : public Unit, public GridObject<Player>
WorldLocation const& GetBattlegroundEntryPoint() const { return m_bgData.joinPos; }
void SetBattlegroundEntryPoint();
- void SetBGTeam(uint32 team) { m_bgData.bgTeam = team; }
+ void SetBGTeam(uint32 team)
+ {
+ m_bgData.bgTeam = team;
+ SetByteValue(PLAYER_BYTES_3, 3, uint8(team == ALLIANCE ? 1 : 0));
+ }
uint32 GetBGTeam() const { return m_bgData.bgTeam ? m_bgData.bgTeam : GetTeam(); }
void LeaveBattleground(bool teleportToEntryPoint = true);
@@ -2563,7 +2567,7 @@ class Player : public Unit, public GridObject<Player>
//We allow only one timed quest active at the same time. Below can then be simple value instead of set.
typedef std::set<uint32> QuestSet;
typedef std::set<uint32> SeasonalQuestSet;
- typedef UNORDERED_MAP<uint32,SeasonalQuestSet> SeasonalEventQuestMap;
+ typedef UNORDERED_MAP<uint32, SeasonalQuestSet> SeasonalEventQuestMap;
QuestSet m_timedquests;
QuestSet m_weeklyquests;
QuestSet m_monthlyquests;
diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp
index 3381369b743..49ebb900189 100644
--- a/src/server/game/Entities/Unit/Unit.cpp
+++ b/src/server/game/Entities/Unit/Unit.cpp
@@ -385,10 +385,10 @@ bool Unit::haveOffhandWeapon() const
return m_canDualWield;
}
-void Unit::MonsterMoveWithSpeed(float x, float y, float z, float speed)
+void Unit::MonsterMoveWithSpeed(float x, float y, float z, float speed, bool generatePath, bool forceDestination)
{
- Movement::MoveSplineInit init(*this);
- init.MoveTo(x,y,z);
+ Movement::MoveSplineInit init(this);
+ init.MoveTo(x, y, z, generatePath, forceDestination);
init.SetVelocity(speed);
init.Launch();
}
@@ -6514,8 +6514,8 @@ bool Unit::HandleDummyAuraProc(Unit* victim, uint32 damage, AuraEffect* triggere
RemoveAura(57934);
if (!redirectTarget)
break;
- CastSpell(this,59628,true);
- CastSpell(redirectTarget,57933,true);
+ CastSpell(this, 59628, true);
+ CastSpell(redirectTarget, 57933, true);
break;
}
}
@@ -14452,7 +14452,8 @@ void Unit::ProcDamageAndSpellFor(bool isVictim, Unit* target, uint32 procFlag, u
Unit* actionTarget = !isVictim ? target : this;
DamageInfo damageInfo = DamageInfo(actor, actionTarget, damage, procSpell, procSpell ? SpellSchoolMask(procSpell->SchoolMask) : SPELL_SCHOOL_MASK_NORMAL, SPELL_DIRECT_DAMAGE);
- ProcEventInfo eventInfo = ProcEventInfo(actor, actionTarget, target, procFlag, 0, 0, procExtra, NULL, &damageInfo, NULL /*HealInfo*/);
+ HealInfo healInfo = HealInfo(actor, actionTarget, damage, procSpell, procSpell ? SpellSchoolMask(procSpell->SchoolMask) : SPELL_SCHOOL_MASK_NORMAL);
+ ProcEventInfo eventInfo = ProcEventInfo(actor, actionTarget, target, procFlag, 0, 0, procExtra, NULL, &damageInfo, &healInfo);
ProcTriggeredList procTriggered;
// Fill procTriggered list
@@ -14483,6 +14484,10 @@ void Unit::ProcDamageAndSpellFor(bool isVictim, Unit* target, uint32 procFlag, u
if (!sConditionMgr->IsObjectMeetToConditions(condInfo, conditions))
continue;
+ // AuraScript Hook
+ if (!triggerData.aura->CallScriptCheckProcHandlers(itr->second, eventInfo))
+ continue;
+
// Triggered spells not triggering additional spells
bool triggered = !(spellProto->AttributesEx3 & SPELL_ATTR3_CAN_PROC_WITH_TRIGGERED) ?
(procExtra & PROC_EX_INTERNAL_TRIGGERED && !(procFlag & PROC_FLAG_DONE_TRAP_ACTIVATION)) : false;
@@ -14531,15 +14536,21 @@ void Unit::ProcDamageAndSpellFor(bool isVictim, Unit* target, uint32 procFlag, u
SpellInfo const* spellInfo = i->aura->GetSpellInfo();
uint32 Id = i->aura->GetId();
+ AuraApplication const* aurApp = i->aura->GetApplicationOfTarget(GetGUID());
+
+ bool prepare = i->aura->CallScriptPrepareProcHandlers(aurApp, eventInfo);
+
// For players set spell cooldown if need
uint32 cooldown = 0;
- if (GetTypeId() == TYPEID_PLAYER && i->spellProcEvent && i->spellProcEvent->cooldown)
+ if (prepare && GetTypeId() == TYPEID_PLAYER && i->spellProcEvent && i->spellProcEvent->cooldown)
cooldown = i->spellProcEvent->cooldown;
// Note: must SetCantProc(false) before return
if (spellInfo->AttributesEx3 & SPELL_ATTR3_DISABLE_PROC)
SetCantProc(true);
+ i->aura->CallScriptProcHandlers(aurApp, eventInfo);
+
// This bool is needed till separate aura effect procs are still here
bool handled = false;
if (HandleAuraProc(target, damage, i->aura, procSpell, procFlag, procExtra, cooldown, &handled))
@@ -14558,6 +14569,13 @@ void Unit::ProcDamageAndSpellFor(bool isVictim, Unit* target, uint32 procFlag, u
AuraEffect* triggeredByAura = i->aura->GetEffect(effIndex);
ASSERT(triggeredByAura);
+ bool prevented = i->aura->CallScriptEffectProcHandlers(triggeredByAura, aurApp, eventInfo);
+ if (prevented)
+ {
+ takeCharges = true;
+ continue;
+ }
+
switch (triggeredByAura->GetAuraType())
{
case SPELL_AURA_PROC_TRIGGER_SPELL:
@@ -14725,13 +14743,16 @@ void Unit::ProcDamageAndSpellFor(bool isVictim, Unit* target, uint32 procFlag, u
takeCharges = true;
break;
} // switch (triggeredByAura->GetAuraType())
+ i->aura->CallScriptAfterEffectProcHandlers(triggeredByAura, aurApp, eventInfo);
} // for (uint8 effIndex = 0; effIndex < MAX_SPELL_EFFECTS; ++effIndex)
} // if (!handled)
// Remove charge (aura can be removed by triggers)
- if (useCharges && takeCharges)
+ if (prepare && useCharges && takeCharges)
i->aura->DropCharge();
+ i->aura->CallScriptAfterProcHandlers(aurApp, eventInfo);
+
if (spellInfo->AttributesEx3 & SPELL_ATTR3_DISABLE_PROC)
SetCantProc(false);
}
@@ -14752,7 +14773,7 @@ void Unit::GetProcAurasTriggeredOnEvent(std::list<AuraApplication*>& aurasTrigge
if (!(*itr)->GetRemoveMode())
if ((*itr)->GetBase()->IsProcTriggeredOnEvent(*itr, eventInfo))
{
- (*itr)->GetBase()->PrepareProcToTrigger();
+ (*itr)->GetBase()->PrepareProcToTrigger(*itr, eventInfo);
aurasTriggeringProc.push_back(*itr);
}
}
@@ -14764,7 +14785,7 @@ void Unit::GetProcAurasTriggeredOnEvent(std::list<AuraApplication*>& aurasTrigge
{
if (itr->second->GetBase()->IsProcTriggeredOnEvent(itr->second, eventInfo))
{
- itr->second->GetBase()->PrepareProcToTrigger();
+ itr->second->GetBase()->PrepareProcToTrigger(itr->second, eventInfo);
aurasTriggeringProc.push_back(itr->second);
}
}
@@ -14885,7 +14906,7 @@ void Unit::StopMoving()
if (!IsInWorld())
return;
- Movement::MoveSplineInit init(*this);
+ Movement::MoveSplineInit init(this);
init.MoveTo(GetPositionX(), GetPositionY(), GetPositionZMinusOffset());
init.SetFacing(GetOrientation());
init.Launch();
@@ -15228,7 +15249,7 @@ void Unit::UpdateAuraForGroup(uint8 slot)
}
}
-float Unit::CalculateDefaultCoefficient(SpellInfo const *spellInfo, DamageEffectType damagetype) const
+float Unit::CalculateDefaultCoefficient(SpellInfo const* spellInfo, DamageEffectType damagetype) const
{
// Damage over Time spells bonus calculation
float DotFactor = 1.0f;
@@ -17316,8 +17337,8 @@ void Unit::_ExitVehicle(Position const* exitPosition)
SendMessageToSet(&data, false);
}
- Movement::MoveSplineInit init(*this);
- init.MoveTo(pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ());
+ Movement::MoveSplineInit init(this);
+ init.MoveTo(pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ(), false);
init.SetFacing(GetOrientation());
init.SetTransportExit();
init.Launch();
@@ -17754,7 +17775,7 @@ void Unit::SetInFront(Unit const* target)
void Unit::SetFacingTo(float ori)
{
- Movement::MoveSplineInit init(*this);
+ Movement::MoveSplineInit init(this);
init.MoveTo(GetPositionX(), GetPositionY(), GetPositionZMinusOffset());
init.SetFacing(ori);
init.Launch();
diff --git a/src/server/game/Entities/Unit/Unit.h b/src/server/game/Entities/Unit/Unit.h
index c714303e8c7..73cb18e7aa2 100644
--- a/src/server/game/Entities/Unit/Unit.h
+++ b/src/server/game/Entities/Unit/Unit.h
@@ -493,9 +493,10 @@ enum UnitState
UNIT_STATE_FLEEING_MOVE = 0x02000000,
UNIT_STATE_CHASE_MOVE = 0x04000000,
UNIT_STATE_FOLLOW_MOVE = 0x08000000,
+ UNIT_STATE_IGNORE_PATHFINDING = 0x10000000, // do not use pathfinding in any MovementGenerator
UNIT_STATE_UNATTACKABLE = (UNIT_STATE_IN_FLIGHT | UNIT_STATE_ONVEHICLE),
// for real move using movegen check and stop (except unstoppable flight)
- UNIT_STATE_MOVING = UNIT_STATE_ROAMING_MOVE | UNIT_STATE_CONFUSED_MOVE | UNIT_STATE_FLEEING_MOVE | UNIT_STATE_CHASE_MOVE | UNIT_STATE_FOLLOW_MOVE ,
+ UNIT_STATE_MOVING = UNIT_STATE_ROAMING_MOVE | UNIT_STATE_CONFUSED_MOVE | UNIT_STATE_FLEEING_MOVE | UNIT_STATE_CHASE_MOVE | UNIT_STATE_FOLLOW_MOVE,
UNIT_STATE_CONTROLLED = (UNIT_STATE_CONFUSED | UNIT_STATE_STUNNED | UNIT_STATE_FLEEING),
UNIT_STATE_LOST_CONTROL = (UNIT_STATE_CONTROLLED | UNIT_STATE_JUMPING | UNIT_STATE_CHARGING),
UNIT_STATE_SIGHTLESS = (UNIT_STATE_LOST_CONTROL | UNIT_STATE_EVADE),
@@ -1611,7 +1612,7 @@ class Unit : public WorldObject
void JumpTo(float speedXY, float speedZ, bool forward = true);
void JumpTo(WorldObject* obj, float speedZ);
- void MonsterMoveWithSpeed(float x, float y, float z, float speed);
+ void MonsterMoveWithSpeed(float x, float y, float z, float speed, bool generatePath = false, bool forceDestination = false);
//void SetFacing(float ori, WorldObject* obj = NULL);
//void SendMonsterMove(float NewPosX, float NewPosY, float NewPosZ, uint8 type, uint32 MovementFlags, uint32 Time, Player* player = NULL);
void SendMovementFlagUpdate(bool self = false);
@@ -2020,15 +2021,15 @@ class Unit : public WorldObject
int32 SpellBaseDamageBonusDone(SpellSchoolMask schoolMask);
int32 SpellBaseDamageBonusTaken(SpellSchoolMask schoolMask);
- uint32 SpellDamageBonusDone(Unit* victim, SpellInfo const *spellProto, uint32 pdamage, DamageEffectType damagetype, uint32 stack = 1);
- uint32 SpellDamageBonusTaken(Unit* caster, SpellInfo const *spellProto, uint32 pdamage, DamageEffectType damagetype, uint32 stack = 1);
+ uint32 SpellDamageBonusDone(Unit* victim, SpellInfo const* spellProto, uint32 pdamage, DamageEffectType damagetype, uint32 stack = 1);
+ uint32 SpellDamageBonusTaken(Unit* caster, SpellInfo const* spellProto, uint32 pdamage, DamageEffectType damagetype, uint32 stack = 1);
int32 SpellBaseHealingBonusDone(SpellSchoolMask schoolMask);
int32 SpellBaseHealingBonusTaken(SpellSchoolMask schoolMask);
- uint32 SpellHealingBonusDone(Unit* victim, SpellInfo const *spellProto, uint32 healamount, DamageEffectType damagetype, uint32 stack = 1);
- uint32 SpellHealingBonusTaken(Unit* caster, SpellInfo const *spellProto, uint32 healamount, DamageEffectType damagetype, uint32 stack = 1);
+ uint32 SpellHealingBonusDone(Unit* victim, SpellInfo const* spellProto, uint32 healamount, DamageEffectType damagetype, uint32 stack = 1);
+ uint32 SpellHealingBonusTaken(Unit* caster, SpellInfo const* spellProto, uint32 healamount, DamageEffectType damagetype, uint32 stack = 1);
- uint32 MeleeDamageBonusDone(Unit *pVictim, uint32 damage, WeaponAttackType attType, SpellInfo const *spellProto = NULL);
- uint32 MeleeDamageBonusTaken(Unit* attacker, uint32 pdamage,WeaponAttackType attType, SpellInfo const *spellProto = NULL);
+ uint32 MeleeDamageBonusDone(Unit* pVictim, uint32 damage, WeaponAttackType attType, SpellInfo const* spellProto = NULL);
+ uint32 MeleeDamageBonusTaken(Unit* attacker, uint32 pdamage, WeaponAttackType attType, SpellInfo const* spellProto = NULL);
bool isSpellBlocked(Unit* victim, SpellInfo const* spellProto, WeaponAttackType attackType = BASE_ATTACK);
@@ -2043,7 +2044,7 @@ class Unit : public WorldObject
void SetContestedPvP(Player* attackedPlayer = NULL);
uint32 GetCastingTimeForBonus(SpellInfo const* spellProto, DamageEffectType damagetype, uint32 CastingTime) const;
- float CalculateDefaultCoefficient(SpellInfo const *spellInfo, DamageEffectType damagetype) const;
+ float CalculateDefaultCoefficient(SpellInfo const* spellInfo, DamageEffectType damagetype) const;
uint32 GetRemainingPeriodicAmount(uint64 caster, uint32 spellId, AuraType auraType, uint8 effectIndex = 0) const;
diff --git a/src/server/game/Entities/Vehicle/Vehicle.cpp b/src/server/game/Entities/Vehicle/Vehicle.cpp
index a3be443d014..8f040ab6a5e 100644..100755
--- a/src/server/game/Entities/Vehicle/Vehicle.cpp
+++ b/src/server/game/Entities/Vehicle/Vehicle.cpp
@@ -382,7 +382,7 @@ bool Vehicle::AddPassenger(Unit* unit, int8 seatId)
unit->SendClearTarget(); // SMSG_BREAK_TARGET
unit->SetControlled(true, UNIT_STATE_ROOT); // SMSG_FORCE_ROOT - In some cases we send SMSG_SPLINE_MOVE_ROOT here (for creatures)
// also adds MOVEMENTFLAG_ROOT
- Movement::MoveSplineInit init(*unit);
+ Movement::MoveSplineInit init(unit);
init.DisableTransportPathTransformations();
init.MoveTo(veSeat->m_attachmentOffsetX, veSeat->m_attachmentOffsetY, veSeat->m_attachmentOffsetZ);
init.SetFacing(0.0f);
diff --git a/src/server/game/Globals/ObjectMgr.cpp b/src/server/game/Globals/ObjectMgr.cpp
index 2ba456b5b3c..30257797470 100644
--- a/src/server/game/Globals/ObjectMgr.cpp
+++ b/src/server/game/Globals/ObjectMgr.cpp
@@ -2956,10 +2956,9 @@ void ObjectMgr::PlayerCreateInfoAddItemHelper(uint32 race_, uint32 class_, uint3
if (count < -1)
sLog->outError(LOG_FILTER_SQL, "Invalid count %i specified on item %u be removed from original player create info (use -1)!", count, itemId);
- uint32 RaceClass = (race_) | (class_ << 8);
for (uint32 gender = 0; gender < GENDER_NONE; ++gender)
{
- if (CharStartOutfitEntry const* entry = sCharStartOutfitStore.LookupEntry(RaceClass | (gender << 16)))
+ if (CharStartOutfitEntry const* entry = GetCharStartOutfitEntry(race_, class_, gender))
{
bool found = false;
for (uint8 x = 0; x < MAX_OUTFIT_ITEMS; ++x)
@@ -3180,10 +3179,10 @@ void ObjectMgr::LoadPlayerInfo()
uint32 max_class = current_class ? current_class + 1 : MAX_CLASSES;
for (uint32 r = min_race; r < max_race; ++r)
for (uint32 c = min_class; c < max_class; ++c)
- if (PlayerInfo * info = _playerInfo[r][c])
+ if (PlayerInfo* info = _playerInfo[r][c])
info->spell.push_back(fields[2].GetUInt32());
}
- else if (PlayerInfo * info = _playerInfo[current_race][current_class])
+ else if (PlayerInfo* info = _playerInfo[current_race][current_class])
info->spell.push_back(fields[2].GetUInt32());
else
{
diff --git a/src/server/game/Grids/Notifiers/GridNotifiers.h b/src/server/game/Grids/Notifiers/GridNotifiers.h
index d2069a7f5f4..f52b6443872 100644
--- a/src/server/game/Grids/Notifiers/GridNotifiers.h
+++ b/src/server/game/Grids/Notifiers/GridNotifiers.h
@@ -968,7 +968,7 @@ namespace Trinity
if (u->GetTypeId() == TYPEID_UNIT && ((Creature*)u)->isTotem())
return false;
- if (i_funit->_IsValidAttackTarget(u, _spellInfo,i_obj->GetTypeId() == TYPEID_DYNAMICOBJECT ? i_obj : NULL) && i_obj->IsWithinDistInMap(u, i_range))
+ if (i_funit->_IsValidAttackTarget(u, _spellInfo, i_obj->GetTypeId() == TYPEID_DYNAMICOBJECT ? i_obj : NULL) && i_obj->IsWithinDistInMap(u, i_range))
return true;
return false;
diff --git a/src/server/game/Guilds/Guild.cpp b/src/server/game/Guilds/Guild.cpp
index fc80a7d7635..376ee011638 100644
--- a/src/server/game/Guilds/Guild.cpp
+++ b/src/server/game/Guilds/Guild.cpp
@@ -235,7 +235,7 @@ void Guild::BankEventLogEntry::WritePacket(WorldPacket& data) const
data << uint8(m_eventType);
data << uint64(MAKE_NEW_GUID(m_playerGuid, 0, HIGHGUID_PLAYER));
- switch(m_eventType)
+ switch (m_eventType)
{
case GUILD_BANK_LOG_DEPOSIT_ITEM:
case GUILD_BANK_LOG_WITHDRAW_ITEM:
diff --git a/src/server/game/Handlers/BattleGroundHandler.cpp b/src/server/game/Handlers/BattleGroundHandler.cpp
index 5fb97a031f1..e70f127d600 100644
--- a/src/server/game/Handlers/BattleGroundHandler.cpp
+++ b/src/server/game/Handlers/BattleGroundHandler.cpp
@@ -453,8 +453,9 @@ void WorldSession::HandleBattleFieldPortOpcode(WorldPacket &recvData)
_player->CleanupAfterTaxiFlight();
}
- sBattlegroundMgr->BuildBattlegroundStatusPacket(&data, bg, queueSlot, STATUS_IN_PROGRESS, 0, bg->GetStartTime(), bg->GetArenaType(), _player->GetBGTeam());
+ sBattlegroundMgr->BuildBattlegroundStatusPacket(&data, bg, queueSlot, STATUS_IN_PROGRESS, 0, bg->GetStartTime(), bg->GetArenaType(), ginfo.Team);
_player->GetSession()->SendPacket(&data);
+
// remove battleground queue status from BGmgr
bgQueue.RemovePlayer(_player->GetGUID(), false);
// this is still needed here if battleground "jumping" shouldn't add deserter debuff
@@ -466,6 +467,7 @@ void WorldSession::HandleBattleFieldPortOpcode(WorldPacket &recvData)
_player->SetBattlegroundId(bg->GetInstanceID(), bgTypeId);
// set the destination team
_player->SetBGTeam(ginfo.Team);
+
// bg->HandleBeforeTeleportToBattleground(_player);
sBattlegroundMgr->SendToBattleground(_player, ginfo.IsInvitedToBGInstanceGUID, bgTypeId);
// add only in HandleMoveWorldPortAck()
diff --git a/src/server/game/Handlers/BattlefieldHandler.cpp b/src/server/game/Handlers/BattlefieldHandler.cpp
index 0263b64456f..4a29dee4fb0 100644
--- a/src/server/game/Handlers/BattlefieldHandler.cpp
+++ b/src/server/game/Handlers/BattlefieldHandler.cpp
@@ -60,7 +60,7 @@ void WorldSession::SendBfInvitePlayerToQueue(uint32 BattleId)
//Param2:(ZoneId) the zone where the battle is (4197 for wg)
//Param3:(CanQueue) if able to queue
//Param4:(Full) on log in is full
-void WorldSession::SendBfQueueInviteResponse(uint32 BattleId,uint32 ZoneId, bool CanQueue, bool Full)
+void WorldSession::SendBfQueueInviteResponse(uint32 BattleId, uint32 ZoneId, bool CanQueue, bool Full)
{
WorldPacket data(SMSG_BATTLEFIELD_MGR_QUEUE_REQUEST_RESPONSE, 11);
data << uint32(BattleId);
diff --git a/src/server/game/Handlers/GuildHandler.cpp b/src/server/game/Handlers/GuildHandler.cpp
index f57a5ce020f..60749932637 100644
--- a/src/server/game/Handlers/GuildHandler.cpp
+++ b/src/server/game/Handlers/GuildHandler.cpp
@@ -339,7 +339,7 @@ void WorldSession::HandleGuildBankerActivate(WorldPacket& recvData)
sLog->outDebug(LOG_FILTER_GUILD, "CMSG_GUILD_BANKER_ACTIVATE [%s]: Go: [" UI64FMTD "] AllSlots: %u"
, GetPlayerInfo().c_str(), guid, sendAllSlots);
- Guild * const guild = GetPlayer()->GetGuild();
+ Guild* const guild = GetPlayer()->GetGuild();
if (!guild)
{
Guild::SendCommandResult(this, GUILD_COMMAND_VIEW_TAB, ERR_GUILD_PLAYER_NOT_IN_GUILD);
diff --git a/src/server/game/Handlers/MiscHandler.cpp b/src/server/game/Handlers/MiscHandler.cpp
index 3ee14ed429a..c16a33ad97c 100644
--- a/src/server/game/Handlers/MiscHandler.cpp
+++ b/src/server/game/Handlers/MiscHandler.cpp
@@ -1684,7 +1684,7 @@ void WorldSession::HandleAreaSpiritHealerQueryOpcode(WorldPacket& recvData)
sBattlegroundMgr->SendAreaSpiritHealerQueryOpcode(_player, bg, guid);
if (Battlefield* bf = sBattlefieldMgr->GetBattlefieldToZoneId(_player->GetZoneId()))
- bf->SendAreaSpiritHealerQueryOpcode(_player,guid);
+ bf->SendAreaSpiritHealerQueryOpcode(_player, guid);
}
void WorldSession::HandleAreaSpiritHealerQueueOpcode(WorldPacket& recvData)
diff --git a/src/server/game/Handlers/MovementHandler.cpp b/src/server/game/Handlers/MovementHandler.cpp
index ee0d4f2a1f2..3308c7bee24 100644
--- a/src/server/game/Handlers/MovementHandler.cpp
+++ b/src/server/game/Handlers/MovementHandler.cpp
@@ -126,7 +126,7 @@ void WorldSession::HandleMoveWorldportAckOpcode()
{
// short preparations to continue flight
FlightPathMovementGenerator* flight = (FlightPathMovementGenerator*)(GetPlayer()->GetMotionMaster()->top());
- flight->Initialize(*GetPlayer());
+ flight->Initialize(GetPlayer());
return;
}
diff --git a/src/server/game/Handlers/PetHandler.cpp b/src/server/game/Handlers/PetHandler.cpp
index 98bcab81763..6de657b5d91 100644
--- a/src/server/game/Handlers/PetHandler.cpp
+++ b/src/server/game/Handlers/PetHandler.cpp
@@ -198,13 +198,6 @@ void WorldSession::HandlePetActionHelper(Unit* pet, uint64 guid1, uint32 spellid
if (!owner->IsValidAttackTarget(TargetUnit))
return;
- // Not let attack through obstructions
- if (sWorld->getBoolConfig(CONFIG_PET_LOS))
- {
- if (!pet->IsWithinLOSInMap(TargetUnit))
- return;
- }
-
pet->ClearUnitState(UNIT_STATE_FOLLOW);
// This is true if pet has no target or has target but targets differs.
if (pet->getVictim() != TargetUnit || (pet->getVictim() == TargetUnit && !pet->GetCharmInfo()->IsCommandAttack()))
diff --git a/src/server/game/Maps/Map.cpp b/src/server/game/Maps/Map.cpp
index 286604c10ac..9e6735efc26 100644
--- a/src/server/game/Maps/Map.cpp
+++ b/src/server/game/Maps/Map.cpp
@@ -18,6 +18,7 @@
#include "Map.h"
#include "Battleground.h"
+#include "MMapFactory.h"
#include "CellImpl.h"
#include "DynamicTree.h"
#include "GridNotifiers.h"
@@ -43,7 +44,7 @@ union u_map_magic
};
u_map_magic MapMagic = { {'M','A','P','S'} };
-u_map_magic MapVersionMagic = { {'v','1','.','2'} };
+u_map_magic MapVersionMagic = { {'v','1','.','3'} };
u_map_magic MapAreaMagic = { {'A','R','E','A'} };
u_map_magic MapHeightMagic = { {'M','H','G','T'} };
u_map_magic MapLiquidMagic = { {'M','L','I','Q'} };
@@ -71,6 +72,8 @@ Map::~Map()
if (!m_scriptSchedule.empty())
sScriptMgr->DecreaseScheduledScriptCount(m_scriptSchedule.size());
+
+ MMAP::MMapFactory::createOrGetMMapManager()->unloadMapInstance(GetId(), i_InstanceId);
}
bool Map::ExistMap(uint32 mapid, int gx, int gy)
@@ -119,6 +122,16 @@ bool Map::ExistVMap(uint32 mapid, int gx, int gy)
return true;
}
+void Map::LoadMMap(int gx, int gy)
+{
+ bool mmapLoadResult = MMAP::MMapFactory::createOrGetMMapManager()->loadMap((sWorld->GetDataPath() + "mmaps").c_str(), GetId(), gx, gy);
+
+ if (mmapLoadResult)
+ sLog->outInfo(LOG_FILTER_MAPS, "MMAP loaded name:%s, id:%d, x:%d, y:%d (mmap rep.: x:%d, y:%d)", GetMapName(), GetId(), gx, gy, gx, gy);
+ else
+ sLog->outInfo(LOG_FILTER_MAPS, "Could not load MMAP name:%s, id:%d, x:%d, y:%d (mmap rep.: x:%d, y:%d)", GetMapName(), GetId(), gx, gy, gx, gy);
+}
+
void Map::LoadVMap(int gx, int gy)
{
// x and y are swapped !!
@@ -167,18 +180,16 @@ void Map::LoadMap(int gx, int gy, bool reload)
}
// map file name
- char *tmp=NULL;
- int len = sWorld->GetDataPath().length()+strlen("maps/%03u%02u%02u.map")+1;
+ char* tmp = NULL;
+ int len = sWorld->GetDataPath().length() + strlen("maps/%03u%02u%02u.map") + 1;
tmp = new char[len];
- snprintf(tmp, len, (char *)(sWorld->GetDataPath()+"maps/%03u%02u%02u.map").c_str(), GetId(), gx, gy);
+ snprintf(tmp, len, (char *)(sWorld->GetDataPath() + "maps/%03u%02u%02u.map").c_str(), GetId(), gx, gy);
sLog->outInfo(LOG_FILTER_MAPS, "Loading map %s", tmp);
// loading data
GridMaps[gx][gy] = new GridMap();
if (!GridMaps[gx][gy]->loadData(tmp))
- {
sLog->outError(LOG_FILTER_MAPS, "Error loading map file: \n %s\n", tmp);
- }
- delete [] tmp;
+ delete[] tmp;
sScriptMgr->OnLoadGridMap(this, GridMaps[gx][gy], gx, gy);
}
@@ -186,8 +197,12 @@ void Map::LoadMap(int gx, int gy, bool reload)
void Map::LoadMapAndVMap(int gx, int gy)
{
LoadMap(gx, gy);
+ // Only load the data for the base map
if (i_InstanceId == 0)
- LoadVMap(gx, gy); // Only load the data for the base map
+ {
+ LoadVMap(gx, gy);
+ LoadMMap(gx, gy);
+ }
}
void Map::InitStateMachine()
@@ -311,7 +326,7 @@ void Map::DeleteFromWorld(Player* player)
void Map::EnsureGridCreated(const GridCoord &p)
{
- TRINITY_GUARD(ACE_Thread_Mutex, Lock);
+ TRINITY_GUARD(ACE_Thread_Mutex, GridLock);
EnsureGridCreated_i(p);
}
@@ -998,8 +1013,8 @@ bool Map::UnloadGrid(NGridType& ngrid, bool unloadAll)
GridMaps[gx][gy]->unloadData();
delete GridMaps[gx][gy];
}
- // x and y are swapped
VMAP::VMapFactory::createOrGetVMapManager()->unloadMap(GetId(), gx, gy);
+ MMAP::MMapFactory::createOrGetMMapManager()->unloadMap(GetId(), gx, gy);
}
else
((MapInstanced*)m_parentMap)->RemoveGridMapReference(GridCoord(gx, gy));
@@ -1071,7 +1086,7 @@ GridMap::~GridMap()
unloadData();
}
-bool GridMap::loadData(char *filename)
+bool GridMap::loadData(char* filename)
{
// Unload old data if exist
unloadData();
diff --git a/src/server/game/Maps/Map.h b/src/server/game/Maps/Map.h
index 47a26fa49e7..c9686d36a42 100644
--- a/src/server/game/Maps/Map.h
+++ b/src/server/game/Maps/Map.h
@@ -76,6 +76,8 @@ struct map_fileheader
uint32 heightMapSize;
uint32 liquidMapOffset;
uint32 liquidMapSize;
+ uint32 holesOffset;
+ uint32 holesSize;
};
#define MAP_AREA_NO_AREA 0x0001
@@ -479,6 +481,7 @@ class Map : public GridRefManager<NGridType>
void LoadMapAndVMap(int gx, int gy);
void LoadVMap(int gx, int gy);
void LoadMap(int gx, int gy, bool reload = false);
+ void LoadMMap(int gx, int gy);
GridMap* GetGrid(float x, float y);
void SetTimer(uint32 t) { i_gridExpiry = t < MIN_GRID_DELAY ? MIN_GRID_DELAY : t; }
@@ -526,6 +529,7 @@ class Map : public GridRefManager<NGridType>
void SetUnloadReferenceLock(const GridCoord &p, bool on) { getNGrid(p.x_coord, p.y_coord)->setUnloadReferenceLock(on); }
ACE_Thread_Mutex Lock;
+ ACE_Thread_Mutex GridLock;
MapEntry const* i_mapEntry;
uint8 i_spawnMode;
diff --git a/src/server/game/Maps/MapInstanced.cpp b/src/server/game/Maps/MapInstanced.cpp
index 27a1e0cc432..c2079c22e76 100644
--- a/src/server/game/Maps/MapInstanced.cpp
+++ b/src/server/game/Maps/MapInstanced.cpp
@@ -21,6 +21,7 @@
#include "MapManager.h"
#include "Battleground.h"
#include "VMapFactory.h"
+#include "MMapFactory.h"
#include "InstanceSaveMgr.h"
#include "World.h"
#include "Group.h"
@@ -261,6 +262,7 @@ bool MapInstanced::DestroyInstance(InstancedMaps::iterator &itr)
if (m_InstancedMaps.size() <= 1 && sWorld->getBoolConfig(CONFIG_GRID_UNLOAD))
{
VMAP::VMapFactory::createOrGetVMapManager()->unloadMap(itr->second->GetId());
+ MMAP::MMapFactory::createOrGetMMapManager()->unloadMap(itr->second->GetId());
// in that case, unload grids of the base map, too
// so in the next map creation, (EnsureGridCreated actually) VMaps will be reloaded
Map::UnloadAll();
diff --git a/src/server/game/Maps/ZoneScript.h b/src/server/game/Maps/ZoneScript.h
index 9cea5fec518..ce7168a4040 100644
--- a/src/server/game/Maps/ZoneScript.h
+++ b/src/server/game/Maps/ZoneScript.h
@@ -32,11 +32,11 @@ class ZoneScript
virtual uint32 GetCreatureEntry(uint32 /*guidlow*/, CreatureData const* data) { return data->id; }
virtual uint32 GetGameObjectEntry(uint32 /*guidlow*/, uint32 entry) { return entry; }
- virtual void OnCreatureCreate(Creature *) { }
- virtual void OnCreatureRemove(Creature *) { }
+ virtual void OnCreatureCreate(Creature* ) { }
+ virtual void OnCreatureRemove(Creature* ) { }
- virtual void OnGameObjectCreate(GameObject *) { }
- virtual void OnGameObjectRemove(GameObject *) { }
+ virtual void OnGameObjectCreate(GameObject* ) { }
+ virtual void OnGameObjectRemove(GameObject* ) { }
virtual void OnUnitDeath(Unit*) { }
diff --git a/src/server/game/Miscellaneous/Language.h b/src/server/game/Miscellaneous/Language.h
index f116d695e17..039e8b816b3 100644
--- a/src/server/game/Miscellaneous/Language.h
+++ b/src/server/game/Miscellaneous/Language.h
@@ -742,7 +742,32 @@ enum TrinityStrings
LANG_COMMAND_CREATURESTORAGE_NOTFOUND = 818,
LANG_CHANNEL_CITY = 819,
- // Room for in-game strings 820-999 not used
+
+ LANG_NPCINFO_GOSSIP = 820,
+ LANG_NPCINFO_QUESTGIVER = 821,
+ LANG_NPCINFO_TRAINER_CLASS = 822,
+ LANG_NPCINFO_TRAINER_PROFESSION = 823,
+ LANG_NPCINFO_VENDOR_AMMO = 824,
+ LANG_NPCINFO_VENDOR_FOOD = 825,
+ LANG_NPCINFO_VENDOR_POISON = 826,
+ LANG_NPCINFO_VENDOR_REAGENT = 827,
+ LANG_NPCINFO_REPAIR = 828,
+ LANG_NPCINFO_FLIGHTMASTER = 829,
+ LANG_NPCINFO_SPIRITHEALER = 830,
+ LANG_NPCINFO_SPIRITGUIDE = 831,
+ LANG_NPCINFO_INNKEEPER = 832,
+ LANG_NPCINFO_BANKER = 833,
+ LANG_NPCINFO_PETITIONER = 834,
+ LANG_NPCINFO_TABARDDESIGNER = 835,
+ LANG_NPCINFO_BATTLEMASTER = 836,
+ LANG_NPCINFO_AUCTIONEER = 837,
+ LANG_NPCINFO_STABLEMASTER = 838,
+ LANG_NPCINFO_GUILD_BANKER = 839,
+ LANG_NPCINFO_SPELLCLICK = 840,
+ LANG_NPCINFO_MAILBOX = 841,
+ LANG_NPCINFO_PLAYER_VEHICLE = 842,
+
+ // Room for in-game strings 843-999 not used
// Level 4 (CLI only commands)
LANG_COMMAND_EXIT = 1000,
@@ -928,6 +953,7 @@ enum TrinityStrings
LANG_COMMAND_TICKETSHOWESCALATEDLIST = 2026,
LANG_COMMAND_TICKETPENDING = 2027,
LANG_COMMAND_TICKETRESET = 2028,
+ LANG_COMMAND_TICKETLISTRESPONSE = 2029,
// Trinity strings 5000-9999
LANG_COMMAND_FREEZE = 5000,
diff --git a/src/server/game/Miscellaneous/SharedDefines.h b/src/server/game/Miscellaneous/SharedDefines.h
index 05696694033..3d8ad37c92d 100644
--- a/src/server/game/Miscellaneous/SharedDefines.h
+++ b/src/server/game/Miscellaneous/SharedDefines.h
@@ -19,6 +19,7 @@
#ifndef TRINITY_SHAREDDEFINES_H
#define TRINITY_SHAREDDEFINES_H
+#include "DetourNavMesh.h"
#include "Define.h"
#include <cassert>
@@ -432,7 +433,7 @@ enum SpellAttr4
SPELL_ATTR4_UNK19 = 0x00080000, // 19 proc dalayed, after damage or don't proc on absorb?
SPELL_ATTR4_NOT_CHECK_SELFCAST_POWER = 0x00100000, // 20 supersedes message "More powerful spell applied" for self casts.
SPELL_ATTR4_UNK21 = 0x00200000, // 21 Pally aura, dk presence, dudu form, warrior stance, shadowform, hunter track
- SPELL_ATTR4_UNK22 = 0x00400000, // 22 Seal of Command (42058,57770) and Gymer's Smash 55426
+ SPELL_ATTR4_UNK22 = 0x00400000, // 22 Seal of Command (42058, 57770) and Gymer's Smash 55426
SPELL_ATTR4_UNK23 = 0x00800000, // 23
SPELL_ATTR4_UNK24 = 0x01000000, // 24 some shoot spell
SPELL_ATTR4_IS_PET_SCALING = 0x02000000, // 25 pet scaling auras
@@ -441,7 +442,7 @@ enum SpellAttr4
SPELL_ATTR4_UNK28 = 0x10000000, // 28 Aimed Shot
SPELL_ATTR4_UNK29 = 0x20000000, // 29
SPELL_ATTR4_UNK30 = 0x40000000, // 30
- SPELL_ATTR4_UNK31 = 0x80000000 // 31 Polymorph (chicken) 228 and Sonic Boom (38052,38488)
+ SPELL_ATTR4_UNK31 = 0x80000000 // 31 Polymorph (chicken) 228 and Sonic Boom (38052, 38488)
};
enum SpellAttr5
@@ -3533,4 +3534,33 @@ enum PartyResult
ERR_PARTY_LFG_TELEPORT_IN_COMBAT = 30
};
+const uint32 MMAP_MAGIC = 0x4d4d4150; // 'MMAP'
+#define MMAP_VERSION 3
+
+struct MmapTileHeader
+{
+ uint32 mmapMagic;
+ uint32 dtVersion;
+ uint32 mmapVersion;
+ uint32 size;
+ bool usesLiquids : 1;
+
+ MmapTileHeader() : mmapMagic(MMAP_MAGIC), dtVersion(DT_NAVMESH_VERSION),
+ mmapVersion(MMAP_VERSION), size(0), usesLiquids(true) {}
+};
+
+enum NavTerrain
+{
+ NAV_EMPTY = 0x00,
+ NAV_GROUND = 0x01,
+ NAV_MAGMA = 0x02,
+ NAV_SLIME = 0x04,
+ NAV_WATER = 0x08,
+ NAV_UNUSED1 = 0x10,
+ NAV_UNUSED2 = 0x20,
+ NAV_UNUSED3 = 0x40,
+ NAV_UNUSED4 = 0x80
+ // we only have 8 bits
+};
+
#endif
diff --git a/src/server/game/Movement/FollowerRefManager.h b/src/server/game/Movement/FollowerRefManager.h
index 2bb31d27227..92904f8e4af 100644
--- a/src/server/game/Movement/FollowerRefManager.h
+++ b/src/server/game/Movement/FollowerRefManager.h
@@ -29,4 +29,3 @@ class FollowerRefManager : public RefManager<Unit, TargetedMovementGeneratorBase
};
#endif
-
diff --git a/src/server/game/Movement/FollowerReference.cpp b/src/server/game/Movement/FollowerReference.cpp
index 2ce23e214d1..30797bbaaca 100644
--- a/src/server/game/Movement/FollowerReference.cpp
+++ b/src/server/game/Movement/FollowerReference.cpp
@@ -34,4 +34,3 @@ void FollowerReference::sourceObjectDestroyLink()
{
getSource()->stopFollowing();
}
-
diff --git a/src/server/game/Movement/FollowerReference.h b/src/server/game/Movement/FollowerReference.h
index 9e373d2527e..43ad7e7fa58 100644
--- a/src/server/game/Movement/FollowerReference.h
+++ b/src/server/game/Movement/FollowerReference.h
@@ -32,4 +32,3 @@ class FollowerReference : public Reference<Unit, TargetedMovementGeneratorBase>
void sourceObjectDestroyLink();
};
#endif
-
diff --git a/src/server/game/Movement/MotionMaster.cpp b/src/server/game/Movement/MotionMaster.cpp
index ba648c72e26..c9ca7772186 100644
--- a/src/server/game/Movement/MotionMaster.cpp
+++ b/src/server/game/Movement/MotionMaster.cpp
@@ -87,7 +87,7 @@ void MotionMaster::UpdateMotion(uint32 diff)
ASSERT(!empty());
_cleanFlag |= MMCF_UPDATE;
- if (!top()->Update(*_owner, diff))
+ if (!top()->Update(_owner, diff))
{
_cleanFlag &= ~MMCF_UPDATE;
MovementExpired();
@@ -111,7 +111,7 @@ void MotionMaster::UpdateMotion(uint32 diff)
else if (needInitTop())
InitTop();
else if (_cleanFlag & MMCF_RESET)
- top()->Reset(*_owner);
+ top()->Reset(_owner);
_cleanFlag &= ~MMCF_RESET;
}
@@ -132,7 +132,7 @@ void MotionMaster::DirectClean(bool reset)
if (needInitTop())
InitTop();
else if (reset)
- top()->Reset(*_owner);
+ top()->Reset(_owner);
}
void MotionMaster::DelayedClean()
@@ -163,7 +163,7 @@ void MotionMaster::DirectExpire(bool reset)
else if (needInitTop())
InitTop();
else if (reset)
- top()->Reset(*_owner);
+ top()->Reset(_owner);
}
void MotionMaster::DelayedExpire()
@@ -199,19 +199,19 @@ void MotionMaster::MoveTargetedHome()
{
Clear(false);
- if (_owner->GetTypeId()==TYPEID_UNIT && !((Creature*)_owner)->GetCharmerOrOwnerGUID())
+ if (_owner->GetTypeId() == TYPEID_UNIT && !_owner->ToCreature()->GetCharmerOrOwnerGUID())
{
sLog->outDebug(LOG_FILTER_GENERAL, "Creature (Entry: %u GUID: %u) targeted home", _owner->GetEntry(), _owner->GetGUIDLow());
Mutate(new HomeMovementGenerator<Creature>(), MOTION_SLOT_ACTIVE);
}
- else if (_owner->GetTypeId()==TYPEID_UNIT && ((Creature*)_owner)->GetCharmerOrOwnerGUID())
+ else if (_owner->GetTypeId() == TYPEID_UNIT && _owner->ToCreature()->GetCharmerOrOwnerGUID())
{
sLog->outDebug(LOG_FILTER_GENERAL, "Pet or controlled creature (Entry: %u GUID: %u) targeting home", _owner->GetEntry(), _owner->GetGUIDLow());
- Unit *target = ((Creature*)_owner)->GetCharmerOrOwner();
+ Unit* target = _owner->ToCreature()->GetCharmerOrOwner();
if (target)
{
sLog->outDebug(LOG_FILTER_GENERAL, "Following %s (GUID: %u)", target->GetTypeId() == TYPEID_PLAYER ? "player" : "creature", target->GetTypeId() == TYPEID_PLAYER ? target->GetGUIDLow() : ((Creature*)target)->GetDBTableGUIDLow());
- Mutate(new FollowMovementGenerator<Creature>(*target,PET_FOLLOW_DIST,PET_FOLLOW_ANGLE), MOTION_SLOT_ACTIVE);
+ Mutate(new FollowMovementGenerator<Creature>(target, PET_FOLLOW_DIST, PET_FOLLOW_ANGLE), MOTION_SLOT_ACTIVE);
}
}
else
@@ -248,7 +248,7 @@ void MotionMaster::MoveChase(Unit* target, float dist, float angle)
_owner->GetGUIDLow(),
target->GetTypeId() == TYPEID_PLAYER ? "player" : "creature",
target->GetTypeId() == TYPEID_PLAYER ? target->GetGUIDLow() : target->ToCreature()->GetDBTableGUIDLow());
- Mutate(new ChaseMovementGenerator<Player>(*target,dist,angle), MOTION_SLOT_ACTIVE);
+ Mutate(new ChaseMovementGenerator<Player>(target, dist, angle), MOTION_SLOT_ACTIVE);
}
else
{
@@ -256,7 +256,7 @@ void MotionMaster::MoveChase(Unit* target, float dist, float angle)
_owner->GetEntry(), _owner->GetGUIDLow(),
target->GetTypeId() == TYPEID_PLAYER ? "player" : "creature",
target->GetTypeId() == TYPEID_PLAYER ? target->GetGUIDLow() : target->ToCreature()->GetDBTableGUIDLow());
- Mutate(new ChaseMovementGenerator<Creature>(*target,dist,angle), MOTION_SLOT_ACTIVE);
+ Mutate(new ChaseMovementGenerator<Creature>(target, dist, angle), MOTION_SLOT_ACTIVE);
}
}
@@ -272,7 +272,7 @@ void MotionMaster::MoveFollow(Unit* target, float dist, float angle, MovementSlo
sLog->outDebug(LOG_FILTER_GENERAL, "Player (GUID: %u) follow to %s (GUID: %u)", _owner->GetGUIDLow(),
target->GetTypeId() == TYPEID_PLAYER ? "player" : "creature",
target->GetTypeId() == TYPEID_PLAYER ? target->GetGUIDLow() : target->ToCreature()->GetDBTableGUIDLow());
- Mutate(new FollowMovementGenerator<Player>(*target,dist,angle), slot);
+ Mutate(new FollowMovementGenerator<Player>(target, dist, angle), slot);
}
else
{
@@ -280,22 +280,22 @@ void MotionMaster::MoveFollow(Unit* target, float dist, float angle, MovementSlo
_owner->GetEntry(), _owner->GetGUIDLow(),
target->GetTypeId() == TYPEID_PLAYER ? "player" : "creature",
target->GetTypeId() == TYPEID_PLAYER ? target->GetGUIDLow() : target->ToCreature()->GetDBTableGUIDLow());
- Mutate(new FollowMovementGenerator<Creature>(*target,dist,angle), slot);
+ Mutate(new FollowMovementGenerator<Creature>(target, dist, angle), slot);
}
}
-void MotionMaster::MovePoint(uint32 id, float x, float y, float z)
+void MotionMaster::MovePoint(uint32 id, float x, float y, float z, bool generatePath)
{
if (_owner->GetTypeId() == TYPEID_PLAYER)
{
sLog->outDebug(LOG_FILTER_GENERAL, "Player (GUID: %u) targeted point (Id: %u X: %f Y: %f Z: %f)", _owner->GetGUIDLow(), id, x, y, z);
- Mutate(new PointMovementGenerator<Player>(id, x, y, z), MOTION_SLOT_ACTIVE);
+ Mutate(new PointMovementGenerator<Player>(id, x, y, z, generatePath), MOTION_SLOT_ACTIVE);
}
else
{
sLog->outDebug(LOG_FILTER_GENERAL, "Creature (Entry: %u GUID: %u) targeted point (ID: %u X: %f Y: %f Z: %f)",
_owner->GetEntry(), _owner->GetGUIDLow(), id, x, y, z);
- Mutate(new PointMovementGenerator<Creature>(id, x, y, z), MOTION_SLOT_ACTIVE);
+ Mutate(new PointMovementGenerator<Creature>(id, x, y, z, generatePath), MOTION_SLOT_ACTIVE);
}
}
@@ -306,8 +306,8 @@ void MotionMaster::MoveLand(uint32 id, Position const& pos)
sLog->outDebug(LOG_FILTER_GENERAL, "Creature (Entry: %u) landing point (ID: %u X: %f Y: %f Z: %f)", _owner->GetEntry(), id, x, y, z);
- Movement::MoveSplineInit init(*_owner);
- init.MoveTo(x,y,z);
+ Movement::MoveSplineInit init(_owner);
+ init.MoveTo(x, y, z);
init.SetAnimation(Movement::ToGround);
init.Launch();
Mutate(new EffectMovementGenerator(id), MOTION_SLOT_ACTIVE);
@@ -320,8 +320,8 @@ void MotionMaster::MoveTakeoff(uint32 id, Position const& pos)
sLog->outDebug(LOG_FILTER_GENERAL, "Creature (Entry: %u) landing point (ID: %u X: %f Y: %f Z: %f)", _owner->GetEntry(), id, x, y, z);
- Movement::MoveSplineInit init(*_owner);
- init.MoveTo(x,y,z);
+ Movement::MoveSplineInit init(_owner);
+ init.MoveTo(x, y, z);
init.SetAnimation(Movement::ToFly);
init.Launch();
Mutate(new EffectMovementGenerator(id), MOTION_SLOT_ACTIVE);
@@ -336,13 +336,13 @@ void MotionMaster::MoveKnockbackFrom(float srcX, float srcY, float speedXY, floa
float x, y, z;
float moveTimeHalf = speedZ / Movement::gravity;
float dist = 2 * moveTimeHalf * speedXY;
- float max_height = -Movement::computeFallElevation(moveTimeHalf,false,-speedZ);
+ float max_height = -Movement::computeFallElevation(moveTimeHalf, false, -speedZ);
_owner->GetNearPoint(_owner, x, y, z, _owner->GetObjectSize(), dist, _owner->GetAngle(srcX, srcY) + M_PI);
- Movement::MoveSplineInit init(*_owner);
- init.MoveTo(x,y,z);
- init.SetParabolic(max_height,0);
+ Movement::MoveSplineInit init(_owner);
+ init.MoveTo(x, y, z);
+ init.SetParabolic(max_height, 0);
init.SetOrientationFixed(true);
init.SetVelocity(speedXY);
init.Launch();
@@ -368,11 +368,11 @@ void MotionMaster::MoveJump(float x, float y, float z, float speedXY, float spee
sLog->outDebug(LOG_FILTER_GENERAL, "Unit (GUID: %u) jump to point (X: %f Y: %f Z: %f)", _owner->GetGUIDLow(), x, y, z);
float moveTimeHalf = speedZ / Movement::gravity;
- float max_height = -Movement::computeFallElevation(moveTimeHalf,false,-speedZ);
+ float max_height = -Movement::computeFallElevation(moveTimeHalf, false, -speedZ);
- Movement::MoveSplineInit init(*_owner);
- init.MoveTo(x,y,z);
- init.SetParabolic(max_height,0);
+ Movement::MoveSplineInit init(_owner);
+ init.MoveTo(x, y, z, false);
+ init.SetParabolic(max_height, 0);
init.SetVelocity(speedXY);
init.Launch();
Mutate(new EffectMovementGenerator(id), MOTION_SLOT_CONTROLLED);
@@ -399,14 +399,14 @@ void MotionMaster::MoveFall(uint32 id /*=0*/)
_owner->m_movementInfo.SetFallTime(0);
}
- Movement::MoveSplineInit init(*_owner);
- init.MoveTo(_owner->GetPositionX(), _owner->GetPositionY(), tz);
+ Movement::MoveSplineInit init(_owner);
+ init.MoveTo(_owner->GetPositionX(), _owner->GetPositionY(), tz, false);
init.SetFall();
init.Launch();
Mutate(new EffectMovementGenerator(id), MOTION_SLOT_CONTROLLED);
}
-void MotionMaster::MoveCharge(float x, float y, float z, float speed, uint32 id)
+void MotionMaster::MoveCharge(float x, float y, float z, float speed, uint32 id, bool generatePath)
{
if (Impl[MOTION_SLOT_CONTROLLED] && Impl[MOTION_SLOT_CONTROLLED]->GetMovementGeneratorType() != DISTRACT_MOTION_TYPE)
return;
@@ -414,16 +414,28 @@ void MotionMaster::MoveCharge(float x, float y, float z, float speed, uint32 id)
if (_owner->GetTypeId() == TYPEID_PLAYER)
{
sLog->outDebug(LOG_FILTER_GENERAL, "Player (GUID: %u) charge point (X: %f Y: %f Z: %f)", _owner->GetGUIDLow(), x, y, z);
- Mutate(new PointMovementGenerator<Player>(id, x, y, z, speed), MOTION_SLOT_CONTROLLED);
+ Mutate(new PointMovementGenerator<Player>(id, x, y, z, generatePath, speed), MOTION_SLOT_CONTROLLED);
}
else
{
sLog->outDebug(LOG_FILTER_GENERAL, "Creature (Entry: %u GUID: %u) charge point (X: %f Y: %f Z: %f)",
_owner->GetEntry(), _owner->GetGUIDLow(), x, y, z);
- Mutate(new PointMovementGenerator<Creature>(id, x, y, z, speed), MOTION_SLOT_CONTROLLED);
+ Mutate(new PointMovementGenerator<Creature>(id, x, y, z, generatePath, speed), MOTION_SLOT_CONTROLLED);
}
}
+void MotionMaster::MoveCharge(PathGenerator path, float speed, uint32 id)
+{
+ Vector3 dest = path.GetActualEndPosition();
+
+ MoveCharge(dest.x, dest.y, dest.z, speed, id);
+
+ Movement::MoveSplineInit init(_owner);
+ init.MovebyPath(path.GetPath());
+ init.SetVelocity(speed);
+ init.Launch();
+}
+
void MotionMaster::MoveSeekAssistance(float x, float y, float z)
{
if (_owner->GetTypeId() == TYPEID_PLAYER)
@@ -546,7 +558,7 @@ void MotionMaster::Mutate(MovementGenerator *m, MovementSlot slot)
else
{
_needInit[slot] = false;
- m->Initialize(*_owner);
+ m->Initialize(_owner);
}
}
@@ -614,7 +626,7 @@ MovementGeneratorType MotionMaster::GetMotionSlotType(int slot) const
void MotionMaster::InitTop()
{
- top()->Initialize(*_owner);
+ top()->Initialize(_owner);
_needInit[_top] = false;
}
@@ -622,7 +634,7 @@ void MotionMaster::DirectDelete(_Ty curr)
{
if (isStatic(curr))
return;
- curr->Finalize(*_owner);
+ curr->Finalize(_owner);
delete curr;
}
@@ -639,9 +651,9 @@ void MotionMaster::DelayedDelete(_Ty curr)
bool MotionMaster::GetDestination(float &x, float &y, float &z)
{
if (_owner->movespline->Finalized())
- return false;
+ return false;
- const G3D::Vector3& dest = _owner->movespline->FinalDestination();
+ G3D::Vector3 const& dest = _owner->movespline->FinalDestination();
x = dest.x;
y = dest.y;
z = dest.z;
diff --git a/src/server/game/Movement/MotionMaster.h b/src/server/game/Movement/MotionMaster.h
index f6f58afef22..4b6075aac10 100644
--- a/src/server/game/Movement/MotionMaster.h
+++ b/src/server/game/Movement/MotionMaster.h
@@ -26,6 +26,7 @@
class MovementGenerator;
class Unit;
+class PathGenerator;
// Creature Entry ID used for waypoints show, visible only for GMs
#define VISUAL_WAYPOINT 1
@@ -154,13 +155,14 @@ class MotionMaster //: private std::stack<MovementGenerator *>
void MoveFleeing(Unit* enemy, uint32 time = 0);
void MovePoint(uint32 id, const Position &pos)
{ MovePoint(id, pos.m_positionX, pos.m_positionY, pos.m_positionZ); }
- void MovePoint(uint32 id, float x, float y, float z);
+ void MovePoint(uint32 id, float x, float y, float z, bool generatePath = true);
// These two movement types should only be used with creatures having landing/takeoff animations
void MoveLand(uint32 id, Position const& pos);
void MoveTakeoff(uint32 id, Position const& pos);
- void MoveCharge(float x, float y, float z, float speed = SPEED_CHARGE, uint32 id = EVENT_CHARGE);
+ void MoveCharge(float x, float y, float z, float speed = SPEED_CHARGE, uint32 id = EVENT_CHARGE, bool generatePath = false);
+ void MoveCharge(PathGenerator path, float speed = SPEED_CHARGE, uint32 id = EVENT_CHARGE);
void MoveKnockbackFrom(float srcX, float srcY, float speedXY, float speedZ);
void MoveJumpTo(float angle, float speedXY, float speedZ);
void MoveJump(Position const& pos, float speedXY, float speedZ, uint32 id = EVENT_JUMP)
@@ -199,4 +201,3 @@ class MotionMaster //: private std::stack<MovementGenerator *>
uint8 _cleanFlag;
};
#endif
-
diff --git a/src/server/game/Movement/MovementGenerator.h b/src/server/game/Movement/MovementGenerator.h
index 1c1c38bc72f..39394a75513 100644..100755
--- a/src/server/game/Movement/MovementGenerator.h
+++ b/src/server/game/Movement/MovementGenerator.h
@@ -33,41 +33,47 @@ class MovementGenerator
public:
virtual ~MovementGenerator();
- virtual void Initialize(Unit &) = 0;
- virtual void Finalize(Unit &) = 0;
+ virtual void Initialize(Unit*) = 0;
+ virtual void Finalize(Unit*) = 0;
- virtual void Reset(Unit &) = 0;
+ virtual void Reset(Unit*) = 0;
- virtual bool Update(Unit &, uint32 time_diff) = 0;
+ virtual bool Update(Unit*, uint32 time_diff) = 0;
virtual MovementGeneratorType GetMovementGeneratorType() = 0;
virtual void unitSpeedChanged() { }
+
+ // used by Evade code for select point to evade with expected restart default movement
+ virtual bool GetResetPosition(Unit*, float& /*x*/, float& /*y*/, float& /*z*/) { return false; }
};
template<class T, class D>
class MovementGeneratorMedium : public MovementGenerator
{
public:
- void Initialize(Unit &u)
+ void Initialize(Unit* u)
{
//u->AssertIsType<T>();
- (static_cast<D*>(this))->DoInitialize(*((T*)&u));
+ (static_cast<D*>(this))->DoInitialize(static_cast<T*>(u));
}
- void Finalize(Unit &u)
+
+ void Finalize(Unit* u)
{
//u->AssertIsType<T>();
- (static_cast<D*>(this))->DoFinalize(*((T*)&u));
+ (static_cast<D*>(this))->DoFinalize(static_cast<T*>(u));
}
- void Reset(Unit &u)
+
+ void Reset(Unit* u)
{
//u->AssertIsType<T>();
- (static_cast<D*>(this))->DoReset(*((T*)&u));
+ (static_cast<D*>(this))->DoReset(static_cast<T*>(u));
}
- bool Update(Unit &u, uint32 time_diff)
+
+ bool Update(Unit* u, uint32 time_diff)
{
//u->AssertIsType<T>();
- return (static_cast<D*>(this))->DoUpdate(*((T*)&u), time_diff);
+ return (static_cast<D*>(this))->DoUpdate(static_cast<T*>(u), time_diff);
}
};
@@ -88,4 +94,3 @@ typedef FactoryHolder<MovementGenerator, MovementGeneratorType> MovementGenerato
typedef FactoryHolder<MovementGenerator, MovementGeneratorType>::FactoryHolderRegistry MovementGeneratorRegistry;
typedef FactoryHolder<MovementGenerator, MovementGeneratorType>::FactoryHolderRepository MovementGeneratorRepository;
#endif
-
diff --git a/src/server/game/Movement/MovementGeneratorImpl.h b/src/server/game/Movement/MovementGeneratorImpl.h
index 2618cadc14f..b77db7b5b9d 100644
--- a/src/server/game/Movement/MovementGeneratorImpl.h
+++ b/src/server/game/Movement/MovementGeneratorImpl.h
@@ -28,4 +28,3 @@ MovementGeneratorFactory<MOVEMENT_GEN>::Create(void * /*data*/) const
return (new MOVEMENT_GEN());
}
#endif
-
diff --git a/src/server/game/Movement/MovementGenerators/ConfusedMovementGenerator.cpp b/src/server/game/Movement/MovementGenerators/ConfusedMovementGenerator.cpp
index 482c16997a0..72537f0898c 100755
--- a/src/server/game/Movement/MovementGenerators/ConfusedMovementGenerator.cpp
+++ b/src/server/game/Movement/MovementGenerators/ConfusedMovementGenerator.cpp
@@ -19,6 +19,7 @@
#include "Creature.h"
#include "MapManager.h"
#include "ConfusedMovementGenerator.h"
+#include "PathGenerator.h"
#include "VMapFactory.h"
#include "MoveSplineInit.h"
#include "MoveSpline.h"
@@ -30,100 +31,44 @@
#endif
template<class T>
-void ConfusedMovementGenerator<T>::DoInitialize(T &unit)
+void ConfusedMovementGenerator<T>::DoInitialize(T* unit)
{
- unit.StopMoving();
- float const wander_distance = 4;
- float x = unit.GetPositionX();
- float y = unit.GetPositionY();
- float z = unit.GetPositionZ();
+ unit->AddUnitState(UNIT_STATE_CONFUSED);
+ unit->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_CONFUSED);
+ unit->GetPosition(i_x, i_y, i_z);
- Map const* map = unit.GetBaseMap();
+ if (!unit->isAlive() || unit->IsStopped())
+ return;
- i_nextMove = 1;
-
- bool is_water_ok, is_land_ok;
- _InitSpecific(unit, is_water_ok, is_land_ok);
-
- for (uint8 idx = 0; idx < MAX_CONF_WAYPOINTS + 1; ++idx)
- {
- float wanderX = x + (wander_distance * (float)rand_norm() - wander_distance/2);
- float wanderY = y + (wander_distance * (float)rand_norm() - wander_distance/2);
-
- // prevent invalid coordinates generation
- Trinity::NormalizeMapCoord(wanderX);
- Trinity::NormalizeMapCoord(wanderY);
-
- if (unit.IsWithinLOS(wanderX, wanderY, z))
- {
- bool is_water = map->IsInWater(wanderX, wanderY, z);
-
- if ((is_water && !is_water_ok) || (!is_water && !is_land_ok))
- {
- //! Cannot use coordinates outside our InhabitType. Use the current or previous position.
- wanderX = idx > 0 ? i_waypoints[idx-1][0] : x;
- wanderY = idx > 0 ? i_waypoints[idx-1][1] : y;
- }
- }
- else
- {
- //! Trying to access path outside line of sight. Skip this by using the current or previous position.
- wanderX = idx > 0 ? i_waypoints[idx-1][0] : x;
- wanderY = idx > 0 ? i_waypoints[idx-1][1] : y;
- }
-
- unit.UpdateAllowedPositionZ(wanderX, wanderY, z);
-
- //! Positions are fine - apply them to this waypoint
- i_waypoints[idx][0] = wanderX;
- i_waypoints[idx][1] = wanderY;
- i_waypoints[idx][2] = z;
- }
-
- unit.StopMoving();
- unit.SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_CONFUSED);
- unit.AddUnitState(UNIT_STATE_CONFUSED | UNIT_STATE_CONFUSED_MOVE);
-}
-
-template<>
-void ConfusedMovementGenerator<Creature>::_InitSpecific(Creature &creature, bool &is_water_ok, bool &is_land_ok)
-{
- is_water_ok = creature.canSwim();
- is_land_ok = creature.canWalk();
-}
-
-template<>
-void ConfusedMovementGenerator<Player>::_InitSpecific(Player &, bool &is_water_ok, bool &is_land_ok)
-{
- is_water_ok = true;
- is_land_ok = true;
+ unit->StopMoving();
+ unit->AddUnitState(UNIT_STATE_CONFUSED_MOVE);
}
template<class T>
-void ConfusedMovementGenerator<T>::DoReset(T &unit)
+void ConfusedMovementGenerator<T>::DoReset(T* unit)
{
- i_nextMove = 1;
i_nextMoveTime.Reset(0);
- unit.StopMoving();
- unit.AddUnitState(UNIT_STATE_CONFUSED | UNIT_STATE_CONFUSED_MOVE);
+
+ if (!unit->isAlive() || unit->IsStopped())
+ return;
+
+ unit->StopMoving();
+ unit->AddUnitState(UNIT_STATE_CONFUSED | UNIT_STATE_CONFUSED_MOVE);
}
template<class T>
-bool ConfusedMovementGenerator<T>::DoUpdate(T &unit, uint32 diff)
+bool ConfusedMovementGenerator<T>::DoUpdate(T* unit, uint32 diff)
{
- if (unit.HasUnitState(UNIT_STATE_ROOT | UNIT_STATE_STUNNED | UNIT_STATE_DISTRACTED))
+ if (unit->HasUnitState(UNIT_STATE_ROOT | UNIT_STATE_STUNNED | UNIT_STATE_DISTRACTED))
return true;
if (i_nextMoveTime.Passed())
{
// currently moving, update location
- unit.AddUnitState(UNIT_STATE_CONFUSED_MOVE);
+ unit->AddUnitState(UNIT_STATE_CONFUSED_MOVE);
- if (unit.movespline->Finalized())
- {
- i_nextMove = urand(1, MAX_CONF_WAYPOINTS);
- i_nextMoveTime.Reset(urand(500, 1200)); // Guessed
- }
+ if (unit->movespline->Finalized())
+ i_nextMoveTime.Reset(urand(800, 1500));
}
else
{
@@ -132,14 +77,25 @@ bool ConfusedMovementGenerator<T>::DoUpdate(T &unit, uint32 diff)
if (i_nextMoveTime.Passed())
{
// start moving
- unit.AddUnitState(UNIT_STATE_CONFUSED_MOVE);
+ unit->AddUnitState(UNIT_STATE_CONFUSED_MOVE);
+
+ float dest = 4.0f * (float)rand_norm() - 2.0f;
+
+ Position pos;
+ pos.Relocate(i_x, i_y, i_z);
+ unit->MovePositionToFirstCollision(pos, dest, 0.0f);
+
+ PathGenerator path(unit);
+ path.SetPathLengthLimit(30.0f);
+ bool result = path.CalculatePath(pos.m_positionX, pos.m_positionY, pos.m_positionZ);
+ if (!result || (path.GetPathType() & PATHFIND_NOPATH))
+ {
+ i_nextMoveTime.Reset(100);
+ return true;
+ }
- ASSERT(i_nextMove <= MAX_CONF_WAYPOINTS);
- float x = i_waypoints[i_nextMove][0];
- float y = i_waypoints[i_nextMove][1];
- float z = i_waypoints[i_nextMove][2];
Movement::MoveSplineInit init(unit);
- init.MoveTo(x, y, z);
+ init.MovebyPath(path.GetPath());
init.SetWalk(true);
init.Launch();
}
@@ -149,25 +105,25 @@ bool ConfusedMovementGenerator<T>::DoUpdate(T &unit, uint32 diff)
}
template<>
-void ConfusedMovementGenerator<Player>::DoFinalize(Player &unit)
+void ConfusedMovementGenerator<Player>::DoFinalize(Player* unit)
{
- unit.RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_CONFUSED);
- unit.ClearUnitState(UNIT_STATE_CONFUSED | UNIT_STATE_CONFUSED_MOVE);
+ unit->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_CONFUSED);
+ unit->ClearUnitState(UNIT_STATE_CONFUSED | UNIT_STATE_CONFUSED_MOVE);
+ unit->StopMoving();
}
template<>
-void ConfusedMovementGenerator<Creature>::DoFinalize(Creature &unit)
+void ConfusedMovementGenerator<Creature>::DoFinalize(Creature* unit)
{
- unit.RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_CONFUSED);
- unit.ClearUnitState(UNIT_STATE_CONFUSED | UNIT_STATE_CONFUSED_MOVE);
- if (unit.getVictim())
- unit.SetTarget(unit.getVictim()->GetGUID());
+ unit->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_CONFUSED);
+ unit->ClearUnitState(UNIT_STATE_CONFUSED | UNIT_STATE_CONFUSED_MOVE);
+ if (unit->getVictim())
+ unit->SetTarget(unit->getVictim()->GetGUID());
}
-template void ConfusedMovementGenerator<Player>::DoInitialize(Player &player);
-template void ConfusedMovementGenerator<Creature>::DoInitialize(Creature &creature);
-template void ConfusedMovementGenerator<Player>::DoReset(Player &player);
-template void ConfusedMovementGenerator<Creature>::DoReset(Creature &creature);
-template bool ConfusedMovementGenerator<Player>::DoUpdate(Player &player, uint32 diff);
-template bool ConfusedMovementGenerator<Creature>::DoUpdate(Creature &creature, uint32 diff);
-
+template void ConfusedMovementGenerator<Player>::DoInitialize(Player*);
+template void ConfusedMovementGenerator<Creature>::DoInitialize(Creature*);
+template void ConfusedMovementGenerator<Player>::DoReset(Player*);
+template void ConfusedMovementGenerator<Creature>::DoReset(Creature*);
+template bool ConfusedMovementGenerator<Player>::DoUpdate(Player*, uint32 diff);
+template bool ConfusedMovementGenerator<Creature>::DoUpdate(Creature*, uint32 diff);
diff --git a/src/server/game/Movement/MovementGenerators/ConfusedMovementGenerator.h b/src/server/game/Movement/MovementGenerators/ConfusedMovementGenerator.h
index 20e84f3a97f..da29b8aa12e 100755
--- a/src/server/game/Movement/MovementGenerators/ConfusedMovementGenerator.h
+++ b/src/server/game/Movement/MovementGenerators/ConfusedMovementGenerator.h
@@ -22,25 +22,20 @@
#include "MovementGenerator.h"
#include "Timer.h"
-#define MAX_CONF_WAYPOINTS 24 //! Allows a twelve second confusion if i_nextMove always is the absolute minimum timer.
-
template<class T>
class ConfusedMovementGenerator : public MovementGeneratorMedium< T, ConfusedMovementGenerator<T> >
{
public:
explicit ConfusedMovementGenerator() : i_nextMoveTime(0) {}
- void DoInitialize(T &);
- void DoFinalize(T &);
- void DoReset(T &);
- bool DoUpdate(T &, uint32);
+ void DoInitialize(T*);
+ void DoFinalize(T*);
+ void DoReset(T*);
+ bool DoUpdate(T*, uint32);
MovementGeneratorType GetMovementGeneratorType() { return CONFUSED_MOTION_TYPE; }
private:
- void _InitSpecific(T &, bool &, bool &);
TimeTracker i_nextMoveTime;
- float i_waypoints[MAX_CONF_WAYPOINTS+1][3];
- uint32 i_nextMove;
+ float i_x, i_y, i_z;
};
#endif
-
diff --git a/src/server/game/Movement/MovementGenerators/FleeingMovementGenerator.cpp b/src/server/game/Movement/MovementGenerators/FleeingMovementGenerator.cpp
index e89d30c56f4..216fffbfee1 100755..100644
--- a/src/server/game/Movement/MovementGenerators/FleeingMovementGenerator.cpp
+++ b/src/server/game/Movement/MovementGenerators/FleeingMovementGenerator.cpp
@@ -20,6 +20,7 @@
#include "CreatureAI.h"
#include "MapManager.h"
#include "FleeingMovementGenerator.h"
+#include "PathGenerator.h"
#include "ObjectAccessor.h"
#include "MoveSplineInit.h"
#include "MoveSpline.h"
@@ -29,384 +30,163 @@
#define MAX_QUIET_DISTANCE 43.0f
template<class T>
-void FleeingMovementGenerator<T>::_setTargetLocation(T &owner)
+void FleeingMovementGenerator<T>::_setTargetLocation(T* owner)
{
- if (!&owner)
+ if (!owner)
return;
- if (owner.HasUnitState(UNIT_STATE_ROOT | UNIT_STATE_STUNNED))
+ if (owner->HasUnitState(UNIT_STATE_ROOT | UNIT_STATE_STUNNED))
return;
- if (!_setMoveData(owner))
- return;
+ owner->AddUnitState(UNIT_STATE_FLEEING_MOVE);
float x, y, z;
- if (!_getPoint(owner, x, y, z))
- return;
+ _getPoint(owner, x, y, z);
- owner.AddUnitState(UNIT_STATE_FLEEING_MOVE);
+ PathGenerator path(owner);
+ path.SetPathLengthLimit(30.0f);
+ bool result = path.CalculatePath(x, y, z);
+ if (!result || (path.GetPathType() & PATHFIND_NOPATH))
+ {
+ i_nextCheckTime.Reset(100);
+ return;
+ }
Movement::MoveSplineInit init(owner);
- init.MoveTo(x,y,z);
+ init.MovebyPath(path.GetPath());
init.SetWalk(false);
- init.Launch();
+ int32 traveltime = init.Launch();
+ i_nextCheckTime.Reset(traveltime + urand(800, 1500));
}
template<class T>
-bool FleeingMovementGenerator<T>::_getPoint(T &owner, float &x, float &y, float &z)
+void FleeingMovementGenerator<T>::_getPoint(T* owner, float &x, float &y, float &z)
{
- if (!&owner)
- return false;
-
- x = owner.GetPositionX();
- y = owner.GetPositionY();
- z = owner.GetPositionZ();
-
- float temp_x, temp_y, angle;
- const Map* _map = owner.GetBaseMap();
- // primitive path-finding
- for (uint8 i = 0; i < 18; ++i)
+ float dist_from_caster, angle_to_caster;
+ if (Unit* fright = ObjectAccessor::GetUnit(*owner, i_frightGUID))
{
- if (i_only_forward && i > 2)
- break;
-
- float distance = 5.0f;
-
- switch (i)
- {
- case 0:
- angle = i_cur_angle;
- break;
- case 1:
- angle = i_cur_angle;
- distance /= 2;
- break;
- case 2:
- angle = i_cur_angle;
- distance /= 4;
- break;
- case 3:
- angle = i_cur_angle + static_cast<float>(M_PI/4);
- break;
- case 4:
- angle = i_cur_angle - static_cast<float>(M_PI/4);
- break;
- case 5:
- angle = i_cur_angle + static_cast<float>(M_PI/4);
- distance /= 2;
- break;
- case 6:
- angle = i_cur_angle - static_cast<float>(M_PI/4);
- distance /= 2;
- break;
- case 7:
- angle = i_cur_angle + static_cast<float>(M_PI/2);
- break;
- case 8:
- angle = i_cur_angle - static_cast<float>(M_PI/2);
- break;
- case 9:
- angle = i_cur_angle + static_cast<float>(M_PI/2);
- distance /= 2;
- break;
- case 10:
- angle = i_cur_angle - static_cast<float>(M_PI/2);
- distance /= 2;
- break;
- case 11:
- angle = i_cur_angle + static_cast<float>(M_PI/4);
- distance /= 4;
- break;
- case 12:
- angle = i_cur_angle - static_cast<float>(M_PI/4);
- distance /= 4;
- break;
- case 13:
- angle = i_cur_angle + static_cast<float>(M_PI/2);
- distance /= 4;
- break;
- case 14:
- angle = i_cur_angle - static_cast<float>(M_PI/2);
- distance /= 4;
- break;
- case 15:
- angle = i_cur_angle + static_cast<float>(3*M_PI/4);
- distance /= 2;
- break;
- case 16:
- angle = i_cur_angle - static_cast<float>(3*M_PI/4);
- distance /= 2;
- break;
- case 17:
- angle = i_cur_angle + static_cast<float>(M_PI);
- distance /= 2;
- break;
- default:
- angle = 0.0f;
- distance = 0.0f;
- break;
- }
-
- temp_x = x + distance * std::cos(angle);
- temp_y = y + distance * std::sin(angle);
- Trinity::NormalizeMapCoord(temp_x);
- Trinity::NormalizeMapCoord(temp_y);
- if (owner.IsWithinLOS(temp_x, temp_y, z))
- {
- bool is_water_now = _map->IsInWater(x,y,z);
-
- if (is_water_now && _map->IsInWater(temp_x,temp_y,z))
- {
- x = temp_x;
- y = temp_y;
- return true;
- }
- float new_z = _map->GetHeight(owner.GetPhaseMask(), temp_x, temp_y, z, true);
-
- if (new_z <= INVALID_HEIGHT)
- continue;
-
- bool is_water_next = _map->IsInWater(temp_x, temp_y, new_z);
-
- if ((is_water_now && !is_water_next && !is_land_ok) || (!is_water_now && is_water_next && !is_water_ok))
- continue;
-
- if (!(new_z - z) || distance / fabs(new_z - z) > 1.0f)
- {
- float new_z_left = _map->GetHeight(owner.GetPhaseMask(), temp_x + 1.0f* std::cos(angle+static_cast<float>(M_PI/2)),temp_y + 1.0f* std::sin(angle+static_cast<float>(M_PI/2)),z,true);
- float new_z_right = _map->GetHeight(owner.GetPhaseMask(), temp_x + 1.0f* std::cos(angle-static_cast<float>(M_PI/2)),temp_y + 1.0f* std::sin(angle-static_cast<float>(M_PI/2)),z,true);
- if (fabs(new_z_left - new_z) < 1.2f && fabs(new_z_right - new_z) < 1.2f)
- {
- x = temp_x;
- y = temp_y;
- z = new_z;
- return true;
- }
- }
- }
- }
- i_to_distance_from_caster = 0.0f;
- i_nextCheckTime.Reset(urand(500,1000));
- return false;
-}
-
-template<class T>
-bool FleeingMovementGenerator<T>::_setMoveData(T &owner)
-{
- float cur_dist_xyz = owner.GetDistance(i_caster_x, i_caster_y, i_caster_z);
-
- if (i_to_distance_from_caster > 0.0f)
- {
- if ((i_last_distance_from_caster > i_to_distance_from_caster && cur_dist_xyz < i_to_distance_from_caster) ||
- // if we reach lower distance
- (i_last_distance_from_caster > i_to_distance_from_caster && cur_dist_xyz > i_last_distance_from_caster) ||
- // if we can't be close
- (i_last_distance_from_caster < i_to_distance_from_caster && cur_dist_xyz > i_to_distance_from_caster) ||
- // if we reach bigger distance
- (cur_dist_xyz > MAX_QUIET_DISTANCE) || // if we are too far
- (i_last_distance_from_caster > MIN_QUIET_DISTANCE && cur_dist_xyz < MIN_QUIET_DISTANCE))
- // if we leave 'quiet zone'
- {
- // we are very far or too close, stopping
- i_to_distance_from_caster = 0.0f;
- i_nextCheckTime.Reset(urand(500,1000));
- return false;
- }
+ dist_from_caster = fright->GetDistance(owner);
+ if (dist_from_caster > 0.2f)
+ angle_to_caster = fright->GetAngle(owner);
else
- {
- // now we are running, continue
- i_last_distance_from_caster = cur_dist_xyz;
- return true;
- }
- }
-
- float cur_dist;
- float angle_to_caster;
-
- if (Unit* fright = ObjectAccessor::GetUnit(owner, i_frightGUID))
- {
- cur_dist = fright->GetDistance(&owner);
- if (cur_dist < cur_dist_xyz)
- {
- i_caster_x = fright->GetPositionX();
- i_caster_y = fright->GetPositionY();
- i_caster_z = fright->GetPositionZ();
- angle_to_caster = fright->GetAngle(&owner);
- }
- else
- {
- cur_dist = cur_dist_xyz;
- angle_to_caster = owner.GetAngle(i_caster_x, i_caster_y) + static_cast<float>(M_PI);
- }
+ angle_to_caster = frand(0, 2 * static_cast<float>(M_PI));
}
else
{
- cur_dist = cur_dist_xyz;
- angle_to_caster = owner.GetAngle(i_caster_x, i_caster_y) + static_cast<float>(M_PI);
+ dist_from_caster = 0.0f;
+ angle_to_caster = frand(0, 2 * static_cast<float>(M_PI));
}
- // if we too close may use 'path-finding' else just stop
- i_only_forward = cur_dist >= MIN_QUIET_DISTANCE/3;
-
- //get angle and 'distance from caster' to run
- float angle;
-
- if (i_cur_angle == 0.0f && i_last_distance_from_caster == 0.0f) //just started, first time
+ float dist, angle;
+ if (dist_from_caster < MIN_QUIET_DISTANCE)
{
- angle = (float)rand_norm()*(1.0f - cur_dist/MIN_QUIET_DISTANCE) * static_cast<float>(M_PI/3) + (float)rand_norm()*static_cast<float>(M_PI*2/3);
- i_to_distance_from_caster = MIN_QUIET_DISTANCE;
- i_only_forward = true;
+ dist = frand(0.4f, 1.3f)*(MIN_QUIET_DISTANCE - dist_from_caster);
+ angle = angle_to_caster + frand(-static_cast<float>(M_PI)/8, static_cast<float>(M_PI)/8);
}
- else if (cur_dist < MIN_QUIET_DISTANCE)
+ else if (dist_from_caster > MAX_QUIET_DISTANCE)
{
- angle = static_cast<float>(M_PI/6) + (float)rand_norm()*static_cast<float>(M_PI*2/3);
- i_to_distance_from_caster = cur_dist*2/3 + (float)rand_norm()*(MIN_QUIET_DISTANCE - cur_dist*2/3);
+ dist = frand(0.4f, 1.0f)*(MAX_QUIET_DISTANCE - MIN_QUIET_DISTANCE);
+ angle = -angle_to_caster + frand(-static_cast<float>(M_PI)/4, static_cast<float>(M_PI)/4);
}
- else if (cur_dist > MAX_QUIET_DISTANCE)
+ else // we are inside quiet range
{
- angle = (float)rand_norm()*static_cast<float>(M_PI/3) + static_cast<float>(M_PI*2/3);
- i_to_distance_from_caster = MIN_QUIET_DISTANCE + 2.5f + (float)rand_norm()*(MAX_QUIET_DISTANCE - MIN_QUIET_DISTANCE - 2.5f);
+ dist = frand(0.6f, 1.2f)*(MAX_QUIET_DISTANCE - MIN_QUIET_DISTANCE);
+ angle = frand(0, 2*static_cast<float>(M_PI));
}
- else
- {
- angle = (float)rand_norm()*static_cast<float>(M_PI);
- i_to_distance_from_caster = MIN_QUIET_DISTANCE + 2.5f + (float)rand_norm()*(MAX_QUIET_DISTANCE - MIN_QUIET_DISTANCE - 2.5f);
- }
-
- int8 sign = (float)rand_norm() > 0.5f ? 1 : -1;
- i_cur_angle = sign*angle + angle_to_caster;
- // current distance
- i_last_distance_from_caster = cur_dist;
-
- return true;
+ Position pos;
+ owner->GetFirstCollisionPosition(pos, dist, angle);
+ x = pos.m_positionX;
+ y = pos.m_positionY;
+ z = pos.m_positionZ;
}
template<class T>
-void FleeingMovementGenerator<T>::DoInitialize(T &owner)
+void FleeingMovementGenerator<T>::DoInitialize(T* owner)
{
- if (!&owner)
+ if (!owner)
return;
- owner.SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_FLEEING);
- owner.AddUnitState(UNIT_STATE_FLEEING|UNIT_STATE_FLEEING_MOVE);
-
- _Init(owner);
-
- if (Unit *fright = ObjectAccessor::GetUnit(owner, i_frightGUID))
- {
- i_caster_x = fright->GetPositionX();
- i_caster_y = fright->GetPositionY();
- i_caster_z = fright->GetPositionZ();
- }
- else
- {
- i_caster_x = owner.GetPositionX();
- i_caster_y = owner.GetPositionY();
- i_caster_z = owner.GetPositionZ();
- }
-
- i_only_forward = true;
- i_cur_angle = 0.0f;
- i_last_distance_from_caster = 0.0f;
- i_to_distance_from_caster = 0.0f;
+ owner->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_FLEEING);
+ owner->AddUnitState(UNIT_STATE_FLEEING | UNIT_STATE_FLEEING_MOVE);
_setTargetLocation(owner);
}
template<>
-void FleeingMovementGenerator<Creature>::_Init(Creature &owner)
+void FleeingMovementGenerator<Player>::DoFinalize(Player* owner)
{
- if (!&owner)
- return;
-
- //owner.SetTargetGuid(ObjectGuid());
- is_water_ok = owner.canSwim();
- is_land_ok = owner.canWalk();
+ owner->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_FLEEING);
+ owner->ClearUnitState(UNIT_STATE_FLEEING | UNIT_STATE_FLEEING_MOVE);
+ owner->StopMoving();
}
template<>
-void FleeingMovementGenerator<Player>::_Init(Player &)
+void FleeingMovementGenerator<Creature>::DoFinalize(Creature* owner)
{
- is_water_ok = true;
- is_land_ok = true;
-}
-
-template<>
-void FleeingMovementGenerator<Player>::DoFinalize(Player &owner)
-{
- owner.RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_FLEEING);
- owner.ClearUnitState(UNIT_STATE_FLEEING|UNIT_STATE_FLEEING_MOVE);
- owner.StopMoving();
-}
-
-template<>
-void FleeingMovementGenerator<Creature>::DoFinalize(Creature &owner)
-{
- owner.RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_FLEEING);
- owner.ClearUnitState(UNIT_STATE_FLEEING|UNIT_STATE_FLEEING_MOVE);
- if (owner.getVictim())
- owner.SetTarget(owner.getVictim()->GetGUID());
+ owner->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_FLEEING);
+ owner->ClearUnitState(UNIT_STATE_FLEEING|UNIT_STATE_FLEEING_MOVE);
+ if (owner->getVictim())
+ owner->SetTarget(owner->getVictim()->GetGUID());
}
template<class T>
-void FleeingMovementGenerator<T>::DoReset(T &owner)
+void FleeingMovementGenerator<T>::DoReset(T* owner)
{
DoInitialize(owner);
}
template<class T>
-bool FleeingMovementGenerator<T>::DoUpdate(T &owner, uint32 time_diff)
+bool FleeingMovementGenerator<T>::DoUpdate(T* owner, uint32 time_diff)
{
- if (!&owner || !owner.isAlive())
+ if (!owner || !owner->isAlive())
return false;
- if (owner.HasUnitState(UNIT_STATE_ROOT | UNIT_STATE_STUNNED))
+
+ if (owner->HasUnitState(UNIT_STATE_ROOT | UNIT_STATE_STUNNED))
{
- owner.ClearUnitState(UNIT_STATE_FLEEING_MOVE);
+ owner->ClearUnitState(UNIT_STATE_FLEEING_MOVE);
return true;
}
i_nextCheckTime.Update(time_diff);
- if (i_nextCheckTime.Passed() && owner.movespline->Finalized())
+ if (i_nextCheckTime.Passed() && owner->movespline->Finalized())
_setTargetLocation(owner);
return true;
}
-template void FleeingMovementGenerator<Player>::DoInitialize(Player &);
-template void FleeingMovementGenerator<Creature>::DoInitialize(Creature &);
-template bool FleeingMovementGenerator<Player>::_setMoveData(Player &);
-template bool FleeingMovementGenerator<Creature>::_setMoveData(Creature &);
-template bool FleeingMovementGenerator<Player>::_getPoint(Player &, float &, float &, float &);
-template bool FleeingMovementGenerator<Creature>::_getPoint(Creature &, float &, float &, float &);
-template void FleeingMovementGenerator<Player>::_setTargetLocation(Player &);
-template void FleeingMovementGenerator<Creature>::_setTargetLocation(Creature &);
-template void FleeingMovementGenerator<Player>::DoReset(Player &);
-template void FleeingMovementGenerator<Creature>::DoReset(Creature &);
-template bool FleeingMovementGenerator<Player>::DoUpdate(Player &, uint32);
-template bool FleeingMovementGenerator<Creature>::DoUpdate(Creature &, uint32);
-
-void TimedFleeingMovementGenerator::Finalize(Unit &owner)
+template void FleeingMovementGenerator<Player>::DoInitialize(Player*);
+template void FleeingMovementGenerator<Creature>::DoInitialize(Creature*);
+template void FleeingMovementGenerator<Player>::_getPoint(Player*, float&, float&, float&);
+template void FleeingMovementGenerator<Creature>::_getPoint(Creature*, float&, float&, float&);
+template void FleeingMovementGenerator<Player>::_setTargetLocation(Player*);
+template void FleeingMovementGenerator<Creature>::_setTargetLocation(Creature*);
+template void FleeingMovementGenerator<Player>::DoReset(Player*);
+template void FleeingMovementGenerator<Creature>::DoReset(Creature*);
+template bool FleeingMovementGenerator<Player>::DoUpdate(Player*, uint32);
+template bool FleeingMovementGenerator<Creature>::DoUpdate(Creature*, uint32);
+
+void TimedFleeingMovementGenerator::Finalize(Unit* owner)
{
- owner.RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_FLEEING);
- owner.ClearUnitState(UNIT_STATE_FLEEING|UNIT_STATE_FLEEING_MOVE);
- if (Unit* victim = owner.getVictim())
+ owner->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_FLEEING);
+ owner->ClearUnitState(UNIT_STATE_FLEEING|UNIT_STATE_FLEEING_MOVE);
+ if (Unit* victim = owner->getVictim())
{
- if (owner.isAlive())
+ if (owner->isAlive())
{
- owner.AttackStop();
- owner.ToCreature()->AI()->AttackStart(victim);
+ owner->AttackStop();
+ owner->ToCreature()->AI()->AttackStart(victim);
}
}
}
-bool TimedFleeingMovementGenerator::Update(Unit & owner, uint32 time_diff)
+bool TimedFleeingMovementGenerator::Update(Unit* owner, uint32 time_diff)
{
- if (!owner.isAlive())
+ if (!owner->isAlive())
return false;
- if (owner.HasUnitState(UNIT_STATE_ROOT | UNIT_STATE_STUNNED))
+ if (owner->HasUnitState(UNIT_STATE_ROOT | UNIT_STATE_STUNNED))
{
- owner.ClearUnitState(UNIT_STATE_FLEEING_MOVE);
+ owner->ClearUnitState(UNIT_STATE_FLEEING_MOVE);
return true;
}
@@ -418,4 +198,3 @@ bool TimedFleeingMovementGenerator::Update(Unit & owner, uint32 time_diff)
// This is done instead of casting Unit& to Creature& and call parent method, then we can use Unit directly
return MovementGeneratorMedium< Creature, FleeingMovementGenerator<Creature> >::Update(owner, time_diff);
}
-
diff --git a/src/server/game/Movement/MovementGenerators/FleeingMovementGenerator.h b/src/server/game/Movement/MovementGenerators/FleeingMovementGenerator.h
index 8ad165c8206..33a7c705564 100755
--- a/src/server/game/Movement/MovementGenerators/FleeingMovementGenerator.h
+++ b/src/server/game/Movement/MovementGenerators/FleeingMovementGenerator.h
@@ -27,29 +27,17 @@ class FleeingMovementGenerator : public MovementGeneratorMedium< T, FleeingMovem
public:
FleeingMovementGenerator(uint64 fright) : i_frightGUID(fright), i_nextCheckTime(0) {}
- void DoInitialize(T &);
- void DoFinalize(T &);
- void DoReset(T &);
- bool DoUpdate(T &, uint32);
+ void DoInitialize(T*);
+ void DoFinalize(T*);
+ void DoReset(T*);
+ bool DoUpdate(T*, uint32);
MovementGeneratorType GetMovementGeneratorType() { return FLEEING_MOTION_TYPE; }
private:
- void _setTargetLocation(T &owner);
- bool _getPoint(T &owner, float &x, float &y, float &z);
- bool _setMoveData(T &owner);
- void _Init(T &);
+ void _setTargetLocation(T*);
+ void _getPoint(T*, float &x, float &y, float &z);
- bool is_water_ok :1;
- bool is_land_ok :1;
- bool i_only_forward:1;
-
- float i_caster_x;
- float i_caster_y;
- float i_caster_z;
- float i_last_distance_from_caster;
- float i_to_distance_from_caster;
- float i_cur_angle;
uint64 i_frightGUID;
TimeTracker i_nextCheckTime;
};
@@ -62,12 +50,11 @@ class TimedFleeingMovementGenerator : public FleeingMovementGenerator<Creature>
i_totalFleeTime(time) {}
MovementGeneratorType GetMovementGeneratorType() { return TIMED_FLEEING_MOTION_TYPE; }
- bool Update(Unit &, uint32);
- void Finalize(Unit &);
+ bool Update(Unit*, uint32);
+ void Finalize(Unit*);
private:
TimeTracker i_totalFleeTime;
};
#endif
-
diff --git a/src/server/game/Movement/MovementGenerators/HomeMovementGenerator.cpp b/src/server/game/Movement/MovementGenerators/HomeMovementGenerator.cpp
index 358362c9c15..a94ef5d4f87 100644
--- a/src/server/game/Movement/MovementGenerators/HomeMovementGenerator.cpp
+++ b/src/server/game/Movement/MovementGenerators/HomeMovementGenerator.cpp
@@ -23,49 +23,50 @@
#include "MoveSplineInit.h"
#include "MoveSpline.h"
-void HomeMovementGenerator<Creature>::DoInitialize(Creature & owner)
+void HomeMovementGenerator<Creature>::DoInitialize(Creature* owner)
{
_setTargetLocation(owner);
}
-void HomeMovementGenerator<Creature>::DoReset(Creature &)
+void HomeMovementGenerator<Creature>::DoFinalize(Creature* owner)
+{
+ if (arrived)
+ {
+ owner->ClearUnitState(UNIT_STATE_EVADE);
+ owner->SetWalk(true);
+ owner->LoadCreaturesAddon(true);
+ owner->AI()->JustReachedHome();
+ }
+}
+
+void HomeMovementGenerator<Creature>::DoReset(Creature*)
{
}
-void HomeMovementGenerator<Creature>::_setTargetLocation(Creature & owner)
+void HomeMovementGenerator<Creature>::_setTargetLocation(Creature* owner)
{
- if (owner.HasUnitState(UNIT_STATE_ROOT | UNIT_STATE_STUNNED | UNIT_STATE_DISTRACTED))
+ if (owner->HasUnitState(UNIT_STATE_ROOT | UNIT_STATE_STUNNED | UNIT_STATE_DISTRACTED))
return;
Movement::MoveSplineInit init(owner);
float x, y, z, o;
// at apply we can select more nice return points base at current movegen
- //if (owner.GetMotionMaster()->empty() || !owner.GetMotionMaster()->top()->GetResetPosition(owner,x,y,z))
- //{
- owner.GetHomePosition(x, y, z, o);
- init.SetFacing(o);
- //}
- init.MoveTo(x,y,z);
+ if (owner->GetMotionMaster()->empty() || !owner->GetMotionMaster()->top()->GetResetPosition(owner, x, y, z))
+ {
+ owner->GetHomePosition(x, y, z, o);
+ init.SetFacing(o);
+ }
+ init.MoveTo(x, y, z);
init.SetWalk(false);
init.Launch();
arrived = false;
- owner.ClearUnitState(uint32(UNIT_STATE_ALL_STATE & ~UNIT_STATE_EVADE));
-}
-bool HomeMovementGenerator<Creature>::DoUpdate(Creature &owner, const uint32 /*time_diff*/)
-{
- arrived = owner.movespline->Finalized();
- return !arrived;
+ owner->ClearUnitState(uint32(UNIT_STATE_ALL_STATE & ~UNIT_STATE_EVADE));
}
-void HomeMovementGenerator<Creature>::DoFinalize(Creature& owner)
+bool HomeMovementGenerator<Creature>::DoUpdate(Creature* owner, const uint32 /*time_diff*/)
{
- if (arrived)
- {
- owner.ClearUnitState(UNIT_STATE_EVADE);
- owner.SetWalk(true);
- owner.LoadCreaturesAddon(true);
- owner.AI()->JustReachedHome();
- }
+ arrived = owner->movespline->Finalized();
+ return !arrived;
}
diff --git a/src/server/game/Movement/MovementGenerators/HomeMovementGenerator.h b/src/server/game/Movement/MovementGenerators/HomeMovementGenerator.h
index c93241b82db..3d6c6ab18c9 100644
--- a/src/server/game/Movement/MovementGenerators/HomeMovementGenerator.h
+++ b/src/server/game/Movement/MovementGenerators/HomeMovementGenerator.h
@@ -34,15 +34,14 @@ class HomeMovementGenerator<Creature> : public MovementGeneratorMedium< Creature
HomeMovementGenerator() : arrived(false) {}
~HomeMovementGenerator() {}
- void DoInitialize(Creature &);
- void DoFinalize(Creature &);
- void DoReset(Creature &);
- bool DoUpdate(Creature &, const uint32);
+ void DoInitialize(Creature*);
+ void DoFinalize(Creature*);
+ void DoReset(Creature*);
+ bool DoUpdate(Creature*, const uint32);
MovementGeneratorType GetMovementGeneratorType() { return HOME_MOTION_TYPE; }
private:
- void _setTargetLocation(Creature &);
+ void _setTargetLocation(Creature*);
bool arrived;
};
#endif
-
diff --git a/src/server/game/Movement/MovementGenerators/IdleMovementGenerator.cpp b/src/server/game/Movement/MovementGenerators/IdleMovementGenerator.cpp
index c952ad9ba94..81442570940 100755
--- a/src/server/game/Movement/MovementGenerators/IdleMovementGenerator.cpp
+++ b/src/server/game/Movement/MovementGenerators/IdleMovementGenerator.cpp
@@ -24,33 +24,33 @@ IdleMovementGenerator si_idleMovement;
// StopMoving is needed to make unit stop if its last movement generator expires
// But it should not be sent otherwise there are many redundent packets
-void IdleMovementGenerator::Initialize(Unit &owner)
+void IdleMovementGenerator::Initialize(Unit* owner)
{
Reset(owner);
}
-void IdleMovementGenerator::Reset(Unit& owner)
+void IdleMovementGenerator::Reset(Unit* owner)
{
- if (!owner.IsStopped())
- owner.StopMoving();
+ if (!owner->IsStopped())
+ owner->StopMoving();
}
-void RotateMovementGenerator::Initialize(Unit& owner)
+void RotateMovementGenerator::Initialize(Unit* owner)
{
- if (!owner.IsStopped())
- owner.StopMoving();
+ if (!owner->IsStopped())
+ owner->StopMoving();
- if (owner.getVictim())
- owner.SetInFront(owner.getVictim());
+ if (owner->getVictim())
+ owner->SetInFront(owner->getVictim());
- owner.AddUnitState(UNIT_STATE_ROTATING);
+ owner->AddUnitState(UNIT_STATE_ROTATING);
- owner.AttackStop();
+ owner->AttackStop();
}
-bool RotateMovementGenerator::Update(Unit& owner, uint32 diff)
+bool RotateMovementGenerator::Update(Unit* owner, uint32 diff)
{
- float angle = owner.GetOrientation();
+ float angle = owner->GetOrientation();
if (m_direction == ROTATE_DIRECTION_LEFT)
{
angle += (float)diff * static_cast<float>(M_PI * 2) / m_maxDuration;
@@ -61,8 +61,8 @@ bool RotateMovementGenerator::Update(Unit& owner, uint32 diff)
angle -= (float)diff * static_cast<float>(M_PI * 2) / m_maxDuration;
while (angle < 0) angle += static_cast<float>(M_PI * 2);
}
- owner.SetOrientation(angle);
- owner.SendMovementFlagUpdate(); // this is a hack. we do not have anything correct to send in the beginning
+ owner->SetOrientation(angle);
+ owner->SendMovementFlagUpdate(); // this is a hack. we do not have anything correct to send in the beginning
if (m_duration > diff)
m_duration -= diff;
@@ -72,24 +72,24 @@ bool RotateMovementGenerator::Update(Unit& owner, uint32 diff)
return true;
}
-void RotateMovementGenerator::Finalize(Unit &unit)
+void RotateMovementGenerator::Finalize(Unit* unit)
{
- unit.ClearUnitState(UNIT_STATE_ROTATING);
- if (unit.GetTypeId() == TYPEID_UNIT)
- unit.ToCreature()->AI()->MovementInform(ROTATE_MOTION_TYPE, 0);
+ unit->ClearUnitState(UNIT_STATE_ROTATING);
+ if (unit->GetTypeId() == TYPEID_UNIT)
+ unit->ToCreature()->AI()->MovementInform(ROTATE_MOTION_TYPE, 0);
}
-void DistractMovementGenerator::Initialize(Unit& owner)
+void DistractMovementGenerator::Initialize(Unit* owner)
{
- owner.AddUnitState(UNIT_STATE_DISTRACTED);
+ owner->AddUnitState(UNIT_STATE_DISTRACTED);
}
-void DistractMovementGenerator::Finalize(Unit& owner)
+void DistractMovementGenerator::Finalize(Unit* owner)
{
- owner.ClearUnitState(UNIT_STATE_DISTRACTED);
+ owner->ClearUnitState(UNIT_STATE_DISTRACTED);
}
-bool DistractMovementGenerator::Update(Unit& /*owner*/, uint32 time_diff)
+bool DistractMovementGenerator::Update(Unit* /*owner*/, uint32 time_diff)
{
if (time_diff > m_timer)
return false;
@@ -98,9 +98,8 @@ bool DistractMovementGenerator::Update(Unit& /*owner*/, uint32 time_diff)
return true;
}
-void AssistanceDistractMovementGenerator::Finalize(Unit &unit)
+void AssistanceDistractMovementGenerator::Finalize(Unit* unit)
{
- unit.ClearUnitState(UNIT_STATE_DISTRACTED);
- unit.ToCreature()->SetReactState(REACT_AGGRESSIVE);
+ unit->ClearUnitState(UNIT_STATE_DISTRACTED);
+ unit->ToCreature()->SetReactState(REACT_AGGRESSIVE);
}
-
diff --git a/src/server/game/Movement/MovementGenerators/IdleMovementGenerator.h b/src/server/game/Movement/MovementGenerators/IdleMovementGenerator.h
index bbcc2413cae..0043891db2c 100644..100755
--- a/src/server/game/Movement/MovementGenerators/IdleMovementGenerator.h
+++ b/src/server/game/Movement/MovementGenerators/IdleMovementGenerator.h
@@ -25,10 +25,10 @@ class IdleMovementGenerator : public MovementGenerator
{
public:
- void Initialize(Unit &);
- void Finalize(Unit &) { }
- void Reset(Unit &);
- bool Update(Unit &, uint32) { return true; }
+ void Initialize(Unit*);
+ void Finalize(Unit*) { }
+ void Reset(Unit*);
+ bool Update(Unit*, uint32) { return true; }
MovementGeneratorType GetMovementGeneratorType() { return IDLE_MOTION_TYPE; }
};
@@ -39,10 +39,10 @@ class RotateMovementGenerator : public MovementGenerator
public:
explicit RotateMovementGenerator(uint32 time, RotateDirection direction) : m_duration(time), m_maxDuration(time), m_direction(direction) {}
- void Initialize(Unit& owner);
- void Finalize(Unit& owner);
- void Reset(Unit& owner) { Initialize(owner); }
- bool Update(Unit& owner, uint32 time_diff);
+ void Initialize(Unit*);
+ void Finalize(Unit*);
+ void Reset(Unit* owner) { Initialize(owner); }
+ bool Update(Unit*, uint32);
MovementGeneratorType GetMovementGeneratorType() { return ROTATE_MOTION_TYPE; }
private:
@@ -55,10 +55,10 @@ class DistractMovementGenerator : public MovementGenerator
public:
explicit DistractMovementGenerator(uint32 timer) : m_timer(timer) {}
- void Initialize(Unit& owner);
- void Finalize(Unit& owner);
- void Reset(Unit& owner) { Initialize(owner); }
- bool Update(Unit& owner, uint32 time_diff);
+ void Initialize(Unit*);
+ void Finalize(Unit*);
+ void Reset(Unit* owner) { Initialize(owner); }
+ bool Update(Unit*, uint32);
MovementGeneratorType GetMovementGeneratorType() { return DISTRACT_MOTION_TYPE; }
private:
@@ -72,8 +72,7 @@ class AssistanceDistractMovementGenerator : public DistractMovementGenerator
DistractMovementGenerator(timer) {}
MovementGeneratorType GetMovementGeneratorType() { return ASSISTANCE_DISTRACT_MOTION_TYPE; }
- void Finalize(Unit& unit);
+ void Finalize(Unit*);
};
#endif
-
diff --git a/src/server/game/Movement/MovementGenerators/PointMovementGenerator.cpp b/src/server/game/Movement/MovementGenerators/PointMovementGenerator.cpp
index 59c41d841f2..06b2315f294 100644..100755
--- a/src/server/game/Movement/MovementGenerators/PointMovementGenerator.cpp
+++ b/src/server/game/Movement/MovementGenerators/PointMovementGenerator.cpp
@@ -27,111 +27,115 @@
//----- Point Movement Generator
template<class T>
-void PointMovementGenerator<T>::DoInitialize(T &unit)
+void PointMovementGenerator<T>::DoInitialize(T* unit)
{
- if (!unit.IsStopped())
- unit.StopMoving();
+ if (!unit->IsStopped())
+ unit->StopMoving();
+
+ unit->AddUnitState(UNIT_STATE_ROAMING|UNIT_STATE_ROAMING_MOVE);
+
+ if (id == EVENT_CHARGE)
+ return;
- unit.AddUnitState(UNIT_STATE_ROAMING|UNIT_STATE_ROAMING_MOVE);
- i_recalculateSpeed = false;
Movement::MoveSplineInit init(unit);
- init.MoveTo(i_x, i_y, i_z);
+ init.MoveTo(i_x, i_y, i_z, m_generatePath);
if (speed > 0.0f)
init.SetVelocity(speed);
init.Launch();
}
template<class T>
-bool PointMovementGenerator<T>::DoUpdate(T &unit, uint32 /*diff*/)
+bool PointMovementGenerator<T>::DoUpdate(T* unit, uint32 /*diff*/)
{
- if (!&unit)
+ if (!unit)
return false;
- if (unit.HasUnitState(UNIT_STATE_ROOT | UNIT_STATE_STUNNED))
+ if (unit->HasUnitState(UNIT_STATE_ROOT | UNIT_STATE_STUNNED))
{
- unit.ClearUnitState(UNIT_STATE_ROAMING_MOVE);
+ unit->ClearUnitState(UNIT_STATE_ROAMING_MOVE);
return true;
}
- unit.AddUnitState(UNIT_STATE_ROAMING_MOVE);
+ unit->AddUnitState(UNIT_STATE_ROAMING_MOVE);
- if (i_recalculateSpeed && !unit.movespline->Finalized())
+ if (id != EVENT_CHARGE && i_recalculateSpeed && !unit->movespline->Finalized())
{
i_recalculateSpeed = false;
Movement::MoveSplineInit init(unit);
- init.MoveTo(i_x, i_y, i_z);
+ init.MoveTo(i_x, i_y, i_z, m_generatePath);
if (speed > 0.0f) // Default value for point motion type is 0.0, if 0.0 spline will use GetSpeed on unit
init.SetVelocity(speed);
init.Launch();
}
- return !unit.movespline->Finalized();
+ return !unit->movespline->Finalized();
}
template<class T>
-void PointMovementGenerator<T>::DoFinalize(T &unit)
+void PointMovementGenerator<T>::DoFinalize(T* unit)
{
- unit.ClearUnitState(UNIT_STATE_ROAMING|UNIT_STATE_ROAMING_MOVE);
+ if (unit->HasUnitState(UNIT_STATE_CHARGING))
+ unit->ClearUnitState(UNIT_STATE_ROAMING | UNIT_STATE_ROAMING_MOVE);
- if (unit.movespline->Finalized())
+ if (unit->movespline->Finalized())
MovementInform(unit);
}
template<class T>
-void PointMovementGenerator<T>::DoReset(T &unit)
+void PointMovementGenerator<T>::DoReset(T* unit)
{
- if (!unit.IsStopped())
- unit.StopMoving();
+ if (!unit->IsStopped())
+ unit->StopMoving();
- unit.AddUnitState(UNIT_STATE_ROAMING|UNIT_STATE_ROAMING_MOVE);
+ unit->AddUnitState(UNIT_STATE_ROAMING|UNIT_STATE_ROAMING_MOVE);
}
template<class T>
-void PointMovementGenerator<T>::MovementInform(T & /*unit*/)
+void PointMovementGenerator<T>::MovementInform(T* /*unit*/)
{
}
-template <> void PointMovementGenerator<Creature>::MovementInform(Creature &unit)
+template <> void PointMovementGenerator<Creature>::MovementInform(Creature* unit)
{
- if (unit.AI())
- unit.AI()->MovementInform(POINT_MOTION_TYPE, id);
+ if (unit->AI())
+ unit->AI()->MovementInform(POINT_MOTION_TYPE, id);
}
-template void PointMovementGenerator<Player>::DoInitialize(Player&);
-template void PointMovementGenerator<Creature>::DoInitialize(Creature&);
-template void PointMovementGenerator<Player>::DoFinalize(Player&);
-template void PointMovementGenerator<Creature>::DoFinalize(Creature&);
-template void PointMovementGenerator<Player>::DoReset(Player&);
-template void PointMovementGenerator<Creature>::DoReset(Creature&);
-template bool PointMovementGenerator<Player>::DoUpdate(Player &, uint32);
-template bool PointMovementGenerator<Creature>::DoUpdate(Creature&, uint32);
+template void PointMovementGenerator<Player>::DoInitialize(Player*);
+template void PointMovementGenerator<Creature>::DoInitialize(Creature*);
+template void PointMovementGenerator<Player>::DoFinalize(Player*);
+template void PointMovementGenerator<Creature>::DoFinalize(Creature*);
+template void PointMovementGenerator<Player>::DoReset(Player*);
+template void PointMovementGenerator<Creature>::DoReset(Creature*);
+template bool PointMovementGenerator<Player>::DoUpdate(Player*, uint32);
+template bool PointMovementGenerator<Creature>::DoUpdate(Creature*, uint32);
-void AssistanceMovementGenerator::Finalize(Unit &unit)
+void AssistanceMovementGenerator::Finalize(Unit* unit)
{
- unit.ToCreature()->SetNoCallAssistance(false);
- unit.ToCreature()->CallAssistance();
- if (unit.isAlive())
- unit.GetMotionMaster()->MoveSeekAssistanceDistract(sWorld->getIntConfig(CONFIG_CREATURE_FAMILY_ASSISTANCE_DELAY));
+ unit->ToCreature()->SetNoCallAssistance(false);
+ unit->ToCreature()->CallAssistance();
+ if (unit->isAlive())
+ unit->GetMotionMaster()->MoveSeekAssistanceDistract(sWorld->getIntConfig(CONFIG_CREATURE_FAMILY_ASSISTANCE_DELAY));
}
-bool EffectMovementGenerator::Update(Unit &unit, uint32)
+bool EffectMovementGenerator::Update(Unit* unit, uint32)
{
- return !unit.movespline->Finalized();
+ return !unit->movespline->Finalized();
}
-void EffectMovementGenerator::Finalize(Unit &unit)
+void EffectMovementGenerator::Finalize(Unit* unit)
{
- if (unit.GetTypeId() != TYPEID_UNIT)
+ if (unit->GetTypeId() != TYPEID_UNIT)
return;
- if (((Creature&)unit).AI())
- ((Creature&)unit).AI()->MovementInform(EFFECT_MOTION_TYPE, m_Id);
+ if (unit->ToCreature()->AI())
+ unit->ToCreature()->AI()->MovementInform(EFFECT_MOTION_TYPE, m_Id);
// Need restore previous movement since we have no proper states system
- //if (unit.isAlive() && !unit.HasUnitState(UNIT_STATE_CONFUSED|UNIT_STATE_FLEEING))
- //{
- // if (Unit * victim = unit.getVictim())
- // unit.GetMotionMaster()->MoveChase(victim);
- // else
- // unit.GetMotionMaster()->Initialize();
- //}
+ if (unit->isAlive() && !unit->HasUnitState(UNIT_STATE_CONFUSED | UNIT_STATE_FLEEING))
+ {
+ if (Unit* victim = unit->getVictim())
+ unit->GetMotionMaster()->MoveChase(victim);
+ else
+ unit->GetMotionMaster()->Initialize();
+ }
}
diff --git a/src/server/game/Movement/MovementGenerators/PointMovementGenerator.h b/src/server/game/Movement/MovementGenerators/PointMovementGenerator.h
index f9a51d0c5f3..421736ca4ec 100644
--- a/src/server/game/Movement/MovementGenerators/PointMovementGenerator.h
+++ b/src/server/game/Movement/MovementGenerators/PointMovementGenerator.h
@@ -26,25 +26,26 @@ template<class T>
class PointMovementGenerator : public MovementGeneratorMedium< T, PointMovementGenerator<T> >
{
public:
- PointMovementGenerator(uint32 _id, float _x, float _y, float _z, float _speed = 0.0f) : id(_id),
- i_x(_x), i_y(_y), i_z(_z), speed(_speed) {}
+ PointMovementGenerator(uint32 _id, float _x, float _y, float _z, bool _generatePath, float _speed = 0.0f) : id(_id),
+ i_x(_x), i_y(_y), i_z(_z), speed(_speed), m_generatePath(_generatePath), i_recalculateSpeed(false) {}
- void DoInitialize(T &);
- void DoFinalize(T &);
- void DoReset(T &);
- bool DoUpdate(T &, uint32);
+ void DoInitialize(T*);
+ void DoFinalize(T*);
+ void DoReset(T*);
+ bool DoUpdate(T*, uint32);
- void MovementInform(T &);
+ void MovementInform(T*);
void unitSpeedChanged() { i_recalculateSpeed = true; }
MovementGeneratorType GetMovementGeneratorType() { return POINT_MOTION_TYPE; }
- bool GetDestination(float& x, float& y, float& z) const { x=i_x; y=i_y; z=i_z; return true; }
+ void GetDestination(float& x, float& y, float& z) const { x = i_x; y = i_y; z = i_z; }
private:
uint32 id;
float i_x, i_y, i_z;
float speed;
+ bool m_generatePath;
bool i_recalculateSpeed;
};
@@ -52,10 +53,10 @@ class AssistanceMovementGenerator : public PointMovementGenerator<Creature>
{
public:
AssistanceMovementGenerator(float _x, float _y, float _z) :
- PointMovementGenerator<Creature>(0, _x, _y, _z) {}
+ PointMovementGenerator<Creature>(0, _x, _y, _z, true) {}
MovementGeneratorType GetMovementGeneratorType() { return ASSISTANCE_MOTION_TYPE; }
- void Finalize(Unit &);
+ void Finalize(Unit*);
};
// Does almost nothing - just doesn't allows previous movegen interrupt current effect.
@@ -63,14 +64,13 @@ class EffectMovementGenerator : public MovementGenerator
{
public:
explicit EffectMovementGenerator(uint32 Id) : m_Id(Id) {}
- void Initialize(Unit &) {}
- void Finalize(Unit &unit);
- void Reset(Unit &) {}
- bool Update(Unit &u, uint32);
+ void Initialize(Unit*) {}
+ void Finalize(Unit*);
+ void Reset(Unit*) {}
+ bool Update(Unit*, uint32);
MovementGeneratorType GetMovementGeneratorType() { return EFFECT_MOTION_TYPE; }
private:
uint32 m_Id;
};
#endif
-
diff --git a/src/server/game/Movement/MovementGenerators/RandomMovementGenerator.cpp b/src/server/game/Movement/MovementGenerators/RandomMovementGenerator.cpp
index f733fa3331c..723b0748494 100644
--- a/src/server/game/Movement/MovementGenerators/RandomMovementGenerator.cpp
+++ b/src/server/game/Movement/MovementGenerators/RandomMovementGenerator.cpp
@@ -33,16 +33,16 @@
#endif
template<>
-void RandomMovementGenerator<Creature>::_setRandomLocation(Creature& creature)
+void RandomMovementGenerator<Creature>::_setRandomLocation(Creature* creature)
{
float respX, respY, respZ, respO, destX, destY, destZ, travelDistZ;
- creature.GetHomePosition(respX, respY, respZ, respO);
- Map const* map = creature.GetBaseMap();
+ creature->GetHomePosition(respX, respY, respZ, respO);
+ Map const* map = creature->GetBaseMap();
// For 2D/3D system selection
//bool is_land_ok = creature.CanWalk(); // not used?
//bool is_water_ok = creature.CanSwim(); // not used?
- bool is_air_ok = creature.CanFly();
+ bool is_air_ok = creature->CanFly();
const float angle = float(rand_norm()) * static_cast<float>(M_PI*2.0f);
const float range = float(rand_norm()) * wander_distance;
@@ -77,17 +77,17 @@ void RandomMovementGenerator<Creature>::_setRandomLocation(Creature& creature)
// The fastest way to get an accurate result 90% of the time.
// Better result can be obtained like 99% accuracy with a ray light, but the cost is too high and the code is too long.
- destZ = map->GetHeight(creature.GetPhaseMask(), destX, destY, respZ+travelDistZ-2.0f, false);
+ destZ = map->GetHeight(creature->GetPhaseMask(), destX, destY, respZ+travelDistZ-2.0f, false);
if (fabs(destZ - respZ) > travelDistZ) // Map check
{
// Vmap Horizontal or above
- destZ = map->GetHeight(creature.GetPhaseMask(), destX, destY, respZ - 2.0f, true);
+ destZ = map->GetHeight(creature->GetPhaseMask(), destX, destY, respZ - 2.0f, true);
if (fabs(destZ - respZ) > travelDistZ)
{
// Vmap Higher
- destZ = map->GetHeight(creature.GetPhaseMask(), destX, destY, respZ+travelDistZ-2.0f, true);
+ destZ = map->GetHeight(creature->GetPhaseMask(), destX, destY, respZ+travelDistZ-2.0f, true);
// let's forget this bad coords where a z cannot be find and retry at next tick
if (fabs(destZ - respZ) > travelDistZ)
@@ -101,7 +101,7 @@ void RandomMovementGenerator<Creature>::_setRandomLocation(Creature& creature)
else
i_nextMoveTime.Reset(urand(500, 10000));
- creature.AddUnitState(UNIT_STATE_ROAMING_MOVE);
+ creature->AddUnitState(UNIT_STATE_ROAMING_MOVE);
Movement::MoveSplineInit init(creature);
init.MoveTo(destX, destY, destZ);
@@ -109,47 +109,47 @@ void RandomMovementGenerator<Creature>::_setRandomLocation(Creature& creature)
init.Launch();
//Call for creature group update
- if (creature.GetFormation() && creature.GetFormation()->getLeader() == &creature)
- creature.GetFormation()->LeaderMoveTo(destX, destY, destZ);
+ if (creature->GetFormation() && creature->GetFormation()->getLeader() == creature)
+ creature->GetFormation()->LeaderMoveTo(destX, destY, destZ);
}
template<>
-void RandomMovementGenerator<Creature>::DoInitialize(Creature &creature)
+void RandomMovementGenerator<Creature>::DoInitialize(Creature* creature)
{
- if (!creature.isAlive())
+ if (!creature->isAlive())
return;
if (!wander_distance)
- wander_distance = creature.GetRespawnRadius();
+ wander_distance = creature->GetRespawnRadius();
- creature.AddUnitState(UNIT_STATE_ROAMING|UNIT_STATE_ROAMING_MOVE);
+ creature->AddUnitState(UNIT_STATE_ROAMING | UNIT_STATE_ROAMING_MOVE);
_setRandomLocation(creature);
}
template<>
-void RandomMovementGenerator<Creature>::DoReset(Creature &creature)
+void RandomMovementGenerator<Creature>::DoReset(Creature* creature)
{
DoInitialize(creature);
}
template<>
-void RandomMovementGenerator<Creature>::DoFinalize(Creature &creature)
+void RandomMovementGenerator<Creature>::DoFinalize(Creature* creature)
{
- creature.ClearUnitState(UNIT_STATE_ROAMING|UNIT_STATE_ROAMING_MOVE);
- creature.SetWalk(false);
+ creature->ClearUnitState(UNIT_STATE_ROAMING|UNIT_STATE_ROAMING_MOVE);
+ creature->SetWalk(false);
}
template<>
-bool RandomMovementGenerator<Creature>::DoUpdate(Creature &creature, const uint32 diff)
+bool RandomMovementGenerator<Creature>::DoUpdate(Creature* creature, const uint32 diff)
{
- if (creature.HasUnitState(UNIT_STATE_ROOT | UNIT_STATE_STUNNED | UNIT_STATE_DISTRACTED))
+ if (creature->HasUnitState(UNIT_STATE_ROOT | UNIT_STATE_STUNNED | UNIT_STATE_DISTRACTED))
{
i_nextMoveTime.Reset(0); // Expire the timer
- creature.ClearUnitState(UNIT_STATE_ROAMING_MOVE);
+ creature->ClearUnitState(UNIT_STATE_ROAMING_MOVE);
return true;
}
- if (creature.movespline->Finalized())
+ if (creature->movespline->Finalized())
{
i_nextMoveTime.Update(diff);
if (i_nextMoveTime.Passed())
@@ -159,14 +159,14 @@ bool RandomMovementGenerator<Creature>::DoUpdate(Creature &creature, const uint3
}
template<>
-bool RandomMovementGenerator<Creature>::GetResetPosition(Creature &creature, float& x, float& y, float& z)
+bool RandomMovementGenerator<Creature>::GetResetPos(Creature* creature, float& x, float& y, float& z)
{
float radius;
- creature.GetRespawnPosition(x, y, z, NULL, &radius);
+ creature->GetRespawnPosition(x, y, z, NULL, &radius);
// use current if in range
- if (creature.IsWithinDist2d(x,y,radius))
- creature.GetPosition(x,y,z);
+ if (creature->IsWithinDist2d(x, y, radius))
+ creature->GetPosition(x, y, z);
return true;
}
diff --git a/src/server/game/Movement/MovementGenerators/RandomMovementGenerator.h b/src/server/game/Movement/MovementGenerators/RandomMovementGenerator.h
index 3e74753bc91..a6159e995fe 100644
--- a/src/server/game/Movement/MovementGenerators/RandomMovementGenerator.h
+++ b/src/server/game/Movement/MovementGenerators/RandomMovementGenerator.h
@@ -27,12 +27,12 @@ class RandomMovementGenerator : public MovementGeneratorMedium< T, RandomMovemen
public:
RandomMovementGenerator(float spawn_dist = 0.0f) : i_nextMoveTime(0), wander_distance(spawn_dist) {}
- void _setRandomLocation(T &);
- void DoInitialize(T &);
- void DoFinalize(T &);
- void DoReset(T &);
- bool DoUpdate(T &, const uint32);
- bool GetResetPosition(T&, float& x, float& y, float& z);
+ void _setRandomLocation(T*);
+ void DoInitialize(T*);
+ void DoFinalize(T*);
+ void DoReset(T*);
+ bool DoUpdate(T*, const uint32);
+ bool GetResetPos(T*, float& x, float& y, float& z);
MovementGeneratorType GetMovementGeneratorType() { return RANDOM_MOTION_TYPE; }
private:
TimeTrackerSmall i_nextMoveTime;
@@ -41,4 +41,3 @@ class RandomMovementGenerator : public MovementGeneratorMedium< T, RandomMovemen
float wander_distance;
};
#endif
-
diff --git a/src/server/game/Movement/MovementGenerators/TargetedMovementGenerator.cpp b/src/server/game/Movement/MovementGenerators/TargetedMovementGenerator.cpp
index ebcb830cf61..abb4ac9964b 100755..100644
--- a/src/server/game/Movement/MovementGenerators/TargetedMovementGenerator.cpp
+++ b/src/server/game/Movement/MovementGenerators/TargetedMovementGenerator.cpp
@@ -26,140 +26,116 @@
#include "MoveSpline.h"
#include "Player.h"
-#include <cmath>
-
template<class T, typename D>
-void TargetedMovementGeneratorMedium<T,D>::_setTargetLocation(T &owner)
+void TargetedMovementGeneratorMedium<T,D>::_setTargetLocation(T* owner, bool updateDestination)
{
if (!i_target.isValid() || !i_target->IsInWorld())
return;
- if (owner.HasUnitState(UNIT_STATE_NOT_MOVE))
+ if (owner->HasUnitState(UNIT_STATE_NOT_MOVE))
+ return;
+
+ if (owner->GetTypeId() == TYPEID_UNIT && !i_target->isInAccessiblePlaceFor(owner->ToCreature()))
return;
float x, y, z;
- //! Following block of code deleted by MrSmite in issue 4891
- //! Code kept for learning and diagnostical purposes
-//
-// if (i_offset && i_target->IsWithinDistInMap(&owner,2*i_offset))
-// {
-// if (!owner.movespline->Finalized())
-// return;
-//
-// owner.GetPosition(x, y, z);
-// }
-// else
- if (!i_offset)
- {
- if (i_target->IsWithinMeleeRange(&owner))
- return;
- // to nearest random contact position
- i_target->GetRandomContactPoint(&owner, x, y, z, 0, MELEE_RANGE - 0.5f);
- }
- else
+ if (updateDestination || !i_path)
{
- float dist;
- float size;
-
- // Pets need special handling.
- // We need to subtract GetObjectSize() because it gets added back further down the chain
- // and that makes pets too far away. Subtracting it allows pets to properly
- // be (GetCombatReach() + i_offset) away.
- // Only applies when i_target is pet's owner otherwise pets and mobs end up
- // doing a "dance" while fighting
- if (owner.isPet() && i_target->GetTypeId() == TYPEID_PLAYER)
+ if (!i_offset)
{
- dist = i_target->GetCombatReach();
- size = i_target->GetCombatReach() - i_target->GetObjectSize();
+ // to nearest contact position
+ i_target->GetContactPoint(owner, x, y, z);
}
else
{
- dist = i_offset + 1.0f;
- size = owner.GetObjectSize();
+ float dist;
+ float size;
+
+ // Pets need special handling.
+ // We need to subtract GetObjectSize() because it gets added back further down the chain
+ // and that makes pets too far away. Subtracting it allows pets to properly
+ // be (GetCombatReach() + i_offset) away.
+ // Only applies when i_target is pet's owner otherwise pets and mobs end up
+ // doing a "dance" while fighting
+ if (owner->isPet() && i_target->GetTypeId() == TYPEID_PLAYER)
+ {
+ dist = i_target->GetCombatReach();
+ size = i_target->GetCombatReach() - i_target->GetObjectSize();
+ }
+ else
+ {
+ dist = i_offset + 1.0f;
+ size = owner->GetObjectSize();
+ }
+
+ if (i_target->IsWithinDistInMap(owner, dist))
+ return;
+
+ // to at i_offset distance from target and i_angle from target facing
+ i_target->GetClosePoint(x, y, z, size, i_offset, i_angle);
}
-
- if (i_target->IsWithinDistInMap(&owner, dist))
- return;
-
- // to at i_offset distance from target and i_angle from target facing
- i_target->GetClosePoint(x, y, z, size, i_offset, i_angle);
+ }
+ else
+ {
+ // the destination has not changed, we just need to refresh the path (usually speed change)
+ G3D::Vector3 end = i_path->GetEndPosition();
+ x = end.x;
+ y = end.y;
+ z = end.z;
}
- /*
- We MUST not check the distance difference and avoid setting the new location for smaller distances.
- By that we risk having far too many GetContactPoint() calls freezing the whole system.
- In TargetedMovementGenerator<T>::Update() we check the distance to the target and at
- some range we calculate a new position. The calculation takes some processor cycles due to vmaps.
- If the distance to the target it too large to ignore,
- but the distance to the new contact point is short enough to be ignored,
- we will calculate a new contact point each update loop, but will never move to it.
- The system will freeze.
- ralf
+ if (!i_path)
+ i_path = new PathGenerator(owner);
- //We don't update Mob Movement, if the difference between New destination and last destination is < BothObjectSize
- float bothObjectSize = i_target->GetObjectBoundingRadius() + owner.GetObjectBoundingRadius() + CONTACT_DISTANCE;
- if ( i_destinationHolder.HasDestination() && i_destinationHolder.GetDestinationDiff(x,y,z) < bothObjectSize )
- return;
- */
+ // allow pets to use shortcut if no path found when following their master
+ bool forceDest = (owner->GetTypeId() == TYPEID_UNIT && owner->ToCreature()->isPet()
+ && owner->HasUnitState(UNIT_STATE_FOLLOW));
+ bool result = i_path->CalculatePath(x, y, z, forceDest);
+ if (!result || (i_path->GetPathType() & PATHFIND_NOPATH))
+ {
+ // Cant reach target
+ i_recalculateTravel = true;
+ return;
+ }
D::_addUnitStateMove(owner);
i_targetReached = false;
i_recalculateTravel = false;
+ owner->AddUnitState(UNIT_STATE_CHASE);
Movement::MoveSplineInit init(owner);
- init.MoveTo(x,y,z);
+ init.MovebyPath(i_path->GetPath());
init.SetWalk(((D*)this)->EnableWalking());
- init.Launch();
-}
-
-template<>
-void TargetedMovementGeneratorMedium<Player,ChaseMovementGenerator<Player> >::UpdateFinalDistance(float /*fDistance*/)
-{
- // nothing to do for Player
-}
+ // Using the same condition for facing target as the one that is used for SetInFront on movement end
+ // - applies to ChaseMovementGenerator mostly
+ if (i_angle == 0.f)
+ init.SetFacing(i_target.getTarget());
-template<>
-void TargetedMovementGeneratorMedium<Player,FollowMovementGenerator<Player> >::UpdateFinalDistance(float /*fDistance*/)
-{
- // nothing to do for Player
-}
-
-template<>
-void TargetedMovementGeneratorMedium<Creature,ChaseMovementGenerator<Creature> >::UpdateFinalDistance(float fDistance)
-{
- i_offset = fDistance;
- i_recalculateTravel = true;
-}
-
-template<>
-void TargetedMovementGeneratorMedium<Creature,FollowMovementGenerator<Creature> >::UpdateFinalDistance(float fDistance)
-{
- i_offset = fDistance;
- i_recalculateTravel = true;
+ init.Launch();
}
template<class T, typename D>
-bool TargetedMovementGeneratorMedium<T,D>::DoUpdate(T &owner, uint32 time_diff)
+bool TargetedMovementGeneratorMedium<T,D>::DoUpdate(T* owner, uint32 time_diff)
{
if (!i_target.isValid() || !i_target->IsInWorld())
return false;
- if (!owner.isAlive())
- return true;
+ if (!owner || !owner->isAlive())
+ return false;
- if (owner.HasUnitState(UNIT_STATE_NOT_MOVE))
+ if (owner->HasUnitState(UNIT_STATE_NOT_MOVE))
{
D::_clearUnitStateMove(owner);
return true;
}
// prevent movement while casting spells with cast time or channel time
- if (owner.HasUnitState(UNIT_STATE_CASTING))
+ if (owner->HasUnitState(UNIT_STATE_CASTING))
{
- if (!owner.IsStopped())
- owner.StopMoving();
+ if (!owner->IsStopped())
+ owner->StopMoving();
return true;
}
@@ -170,22 +146,29 @@ bool TargetedMovementGeneratorMedium<T,D>::DoUpdate(T &owner, uint32 time_diff)
return true;
}
+ bool targetMoved = false;
i_recheckDistance.Update(time_diff);
if (i_recheckDistance.Passed())
{
- i_recheckDistance.Reset(50);
+ i_recheckDistance.Reset(100);
//More distance let have better performance, less distance let have more sensitive reaction at target move.
- float allowed_dist = i_target->GetObjectSize() + owner.GetObjectSize() + MELEE_RANGE - 0.5f;
- float dist = (owner.movespline->FinalDestination() - G3D::Vector3(i_target->GetPositionX(),i_target->GetPositionY(),i_target->GetPositionZ())).squaredLength();
- if (dist >= allowed_dist * allowed_dist)
- _setTargetLocation(owner);
+ float allowed_dist = owner->GetCombatReach() + sWorld->getRate(RATE_TARGET_POS_RECALCULATION_RANGE);
+ G3D::Vector3 dest = owner->movespline->FinalDestination();
+
+ if (owner->GetTypeId() == TYPEID_UNIT && owner->ToCreature()->CanFly())
+ targetMoved = !i_target->IsWithinDist3d(dest.x, dest.y, dest.z, allowed_dist);
+ else
+ targetMoved = !i_target->IsWithinDist2d(dest.x, dest.y, allowed_dist);
}
- if (owner.movespline->Finalized())
+ if (i_recalculateTravel || targetMoved)
+ _setTargetLocation(owner, targetMoved);
+
+ if (owner->movespline->Finalized())
{
static_cast<D*>(this)->MovementInform(owner);
- if (i_angle == 0.f && !owner.HasInArc(0.01f, i_target.getTarget()))
- owner.SetInFront(i_target.getTarget());
+ if (i_angle == 0.f && !owner->HasInArc(0.01f, i_target.getTarget()))
+ owner->SetInFront(i_target.getTarget());
if (!i_targetReached)
{
@@ -193,60 +176,56 @@ bool TargetedMovementGeneratorMedium<T,D>::DoUpdate(T &owner, uint32 time_diff)
static_cast<D*>(this)->_reachTarget(owner);
}
}
- else
- {
- if (i_recalculateTravel)
- _setTargetLocation(owner);
- }
+
return true;
}
//-----------------------------------------------//
template<class T>
-void ChaseMovementGenerator<T>::_reachTarget(T &owner)
+void ChaseMovementGenerator<T>::_reachTarget(T* owner)
{
- if (owner.IsWithinMeleeRange(this->i_target.getTarget()))
- owner.Attack(this->i_target.getTarget(),true);
+ if (owner->IsWithinMeleeRange(this->i_target.getTarget()))
+ owner->Attack(this->i_target.getTarget(),true);
}
template<>
-void ChaseMovementGenerator<Player>::DoInitialize(Player &owner)
+void ChaseMovementGenerator<Player>::DoInitialize(Player* owner)
{
- owner.AddUnitState(UNIT_STATE_CHASE|UNIT_STATE_CHASE_MOVE);
- _setTargetLocation(owner);
+ owner->AddUnitState(UNIT_STATE_CHASE | UNIT_STATE_CHASE_MOVE);
+ _setTargetLocation(owner, true);
}
template<>
-void ChaseMovementGenerator<Creature>::DoInitialize(Creature &owner)
+void ChaseMovementGenerator<Creature>::DoInitialize(Creature* owner)
{
- owner.SetWalk(false);
- owner.AddUnitState(UNIT_STATE_CHASE|UNIT_STATE_CHASE_MOVE);
- _setTargetLocation(owner);
+ owner->SetWalk(false);
+ owner->AddUnitState(UNIT_STATE_CHASE | UNIT_STATE_CHASE_MOVE);
+ _setTargetLocation(owner, true);
}
template<class T>
-void ChaseMovementGenerator<T>::DoFinalize(T &owner)
+void ChaseMovementGenerator<T>::DoFinalize(T* owner)
{
- owner.ClearUnitState(UNIT_STATE_CHASE|UNIT_STATE_CHASE_MOVE);
+ owner->ClearUnitState(UNIT_STATE_CHASE | UNIT_STATE_CHASE_MOVE);
}
template<class T>
-void ChaseMovementGenerator<T>::DoReset(T &owner)
+void ChaseMovementGenerator<T>::DoReset(T* owner)
{
DoInitialize(owner);
}
template<class T>
-void ChaseMovementGenerator<T>::MovementInform(T & /*unit*/)
+void ChaseMovementGenerator<T>::MovementInform(T* /*unit*/)
{
}
template<>
-void ChaseMovementGenerator<Creature>::MovementInform(Creature &unit)
+void ChaseMovementGenerator<Creature>::MovementInform(Creature* unit)
{
// Pass back the GUIDLow of the target. If it is pet's owner then PetAI will handle
- if (unit.AI())
- unit.AI()->MovementInform(CHASE_MOTION_TYPE, i_target.getTarget()->GetGUIDLow());
+ if (unit->AI())
+ unit->AI()->MovementInform(CHASE_MOTION_TYPE, i_target.getTarget()->GetGUIDLow());
}
//-----------------------------------------------//
@@ -263,85 +242,86 @@ bool FollowMovementGenerator<Player>::EnableWalking() const
}
template<>
-void FollowMovementGenerator<Player>::_updateSpeed(Player &/*u*/)
+void FollowMovementGenerator<Player>::_updateSpeed(Player* /*owner*/)
{
// nothing to do for Player
}
template<>
-void FollowMovementGenerator<Creature>::_updateSpeed(Creature &u)
+void FollowMovementGenerator<Creature>::_updateSpeed(Creature* owner)
{
// pet only sync speed with owner
- if (!((Creature&)u).isPet() || !i_target.isValid() || i_target->GetGUID() != u.GetOwnerGUID())
+ /// Make sure we are not in the process of a map change (IsInWorld)
+ if (!owner->isPet() || !owner->IsInWorld() || !i_target.isValid() || i_target->GetGUID() != owner->GetOwnerGUID())
return;
- u.UpdateSpeed(MOVE_RUN,true);
- u.UpdateSpeed(MOVE_WALK,true);
- u.UpdateSpeed(MOVE_SWIM,true);
+ owner->UpdateSpeed(MOVE_RUN, true);
+ owner->UpdateSpeed(MOVE_WALK, true);
+ owner->UpdateSpeed(MOVE_SWIM, true);
}
template<>
-void FollowMovementGenerator<Player>::DoInitialize(Player &owner)
+void FollowMovementGenerator<Player>::DoInitialize(Player* owner)
{
- owner.AddUnitState(UNIT_STATE_FOLLOW|UNIT_STATE_FOLLOW_MOVE);
+ owner->AddUnitState(UNIT_STATE_FOLLOW | UNIT_STATE_FOLLOW_MOVE);
_updateSpeed(owner);
- _setTargetLocation(owner);
+ _setTargetLocation(owner, true);
}
template<>
-void FollowMovementGenerator<Creature>::DoInitialize(Creature &owner)
+void FollowMovementGenerator<Creature>::DoInitialize(Creature* owner)
{
- owner.AddUnitState(UNIT_STATE_FOLLOW|UNIT_STATE_FOLLOW_MOVE);
+ owner->AddUnitState(UNIT_STATE_FOLLOW | UNIT_STATE_FOLLOW_MOVE);
_updateSpeed(owner);
- _setTargetLocation(owner);
+ _setTargetLocation(owner, true);
}
template<class T>
-void FollowMovementGenerator<T>::DoFinalize(T &owner)
+void FollowMovementGenerator<T>::DoFinalize(T* owner)
{
- owner.ClearUnitState(UNIT_STATE_FOLLOW|UNIT_STATE_FOLLOW_MOVE);
+ owner->ClearUnitState(UNIT_STATE_FOLLOW | UNIT_STATE_FOLLOW_MOVE);
_updateSpeed(owner);
}
template<class T>
-void FollowMovementGenerator<T>::DoReset(T &owner)
+void FollowMovementGenerator<T>::DoReset(T* owner)
{
DoInitialize(owner);
}
template<class T>
-void FollowMovementGenerator<T>::MovementInform(T & /*unit*/)
+void FollowMovementGenerator<T>::MovementInform(T* /*unit*/)
{
}
template<>
-void FollowMovementGenerator<Creature>::MovementInform(Creature &unit)
+void FollowMovementGenerator<Creature>::MovementInform(Creature* unit)
{
// Pass back the GUIDLow of the target. If it is pet's owner then PetAI will handle
- if (unit.AI())
- unit.AI()->MovementInform(FOLLOW_MOTION_TYPE, i_target.getTarget()->GetGUIDLow());
+ if (unit->AI())
+ unit->AI()->MovementInform(FOLLOW_MOTION_TYPE, i_target.getTarget()->GetGUIDLow());
}
//-----------------------------------------------//
-template void TargetedMovementGeneratorMedium<Player,ChaseMovementGenerator<Player> >::_setTargetLocation(Player &);
-template void TargetedMovementGeneratorMedium<Player,FollowMovementGenerator<Player> >::_setTargetLocation(Player &);
-template void TargetedMovementGeneratorMedium<Creature,ChaseMovementGenerator<Creature> >::_setTargetLocation(Creature &);
-template void TargetedMovementGeneratorMedium<Creature,FollowMovementGenerator<Creature> >::_setTargetLocation(Creature &);
-template bool TargetedMovementGeneratorMedium<Player,ChaseMovementGenerator<Player> >::DoUpdate(Player &, uint32);
-template bool TargetedMovementGeneratorMedium<Player,FollowMovementGenerator<Player> >::DoUpdate(Player &, uint32);
-template bool TargetedMovementGeneratorMedium<Creature,ChaseMovementGenerator<Creature> >::DoUpdate(Creature &, uint32);
-template bool TargetedMovementGeneratorMedium<Creature,FollowMovementGenerator<Creature> >::DoUpdate(Creature &, uint32);
-
-template void ChaseMovementGenerator<Player>::_reachTarget(Player &);
-template void ChaseMovementGenerator<Creature>::_reachTarget(Creature &);
-template void ChaseMovementGenerator<Player>::DoFinalize(Player &);
-template void ChaseMovementGenerator<Creature>::DoFinalize(Creature &);
-template void ChaseMovementGenerator<Player>::DoReset(Player &);
-template void ChaseMovementGenerator<Creature>::DoReset(Creature &);
-template void ChaseMovementGenerator<Player>::MovementInform(Player &unit);
-
-template void FollowMovementGenerator<Player>::DoFinalize(Player &);
-template void FollowMovementGenerator<Creature>::DoFinalize(Creature &);
-template void FollowMovementGenerator<Player>::DoReset(Player &);
-template void FollowMovementGenerator<Creature>::DoReset(Creature &);
-template void FollowMovementGenerator<Player>::MovementInform(Player &unit);
+template void TargetedMovementGeneratorMedium<Player,ChaseMovementGenerator<Player> >::_setTargetLocation(Player*, bool);
+template void TargetedMovementGeneratorMedium<Player,FollowMovementGenerator<Player> >::_setTargetLocation(Player*, bool);
+template void TargetedMovementGeneratorMedium<Creature,ChaseMovementGenerator<Creature> >::_setTargetLocation(Creature*, bool);
+template void TargetedMovementGeneratorMedium<Creature,FollowMovementGenerator<Creature> >::_setTargetLocation(Creature*, bool);
+template bool TargetedMovementGeneratorMedium<Player,ChaseMovementGenerator<Player> >::DoUpdate(Player*, uint32);
+template bool TargetedMovementGeneratorMedium<Player,FollowMovementGenerator<Player> >::DoUpdate(Player*, uint32);
+template bool TargetedMovementGeneratorMedium<Creature,ChaseMovementGenerator<Creature> >::DoUpdate(Creature*, uint32);
+template bool TargetedMovementGeneratorMedium<Creature,FollowMovementGenerator<Creature> >::DoUpdate(Creature*, uint32);
+
+template void ChaseMovementGenerator<Player>::_reachTarget(Player*);
+template void ChaseMovementGenerator<Creature>::_reachTarget(Creature*);
+template void ChaseMovementGenerator<Player>::DoFinalize(Player*);
+template void ChaseMovementGenerator<Creature>::DoFinalize(Creature*);
+template void ChaseMovementGenerator<Player>::DoReset(Player*);
+template void ChaseMovementGenerator<Creature>::DoReset(Creature*);
+template void ChaseMovementGenerator<Player>::MovementInform(Player*);
+
+template void FollowMovementGenerator<Player>::DoFinalize(Player*);
+template void FollowMovementGenerator<Creature>::DoFinalize(Creature*);
+template void FollowMovementGenerator<Player>::DoReset(Player*);
+template void FollowMovementGenerator<Creature>::DoReset(Creature*);
+template void FollowMovementGenerator<Player>::MovementInform(Player*);
diff --git a/src/server/game/Movement/MovementGenerators/TargetedMovementGenerator.h b/src/server/game/Movement/MovementGenerators/TargetedMovementGenerator.h
index 4105668838d..3edeb348d54 100755
--- a/src/server/game/Movement/MovementGenerators/TargetedMovementGenerator.h
+++ b/src/server/game/Movement/MovementGenerators/TargetedMovementGenerator.h
@@ -23,11 +23,12 @@
#include "FollowerReference.h"
#include "Timer.h"
#include "Unit.h"
+#include "PathGenerator.h"
class TargetedMovementGeneratorBase
{
public:
- TargetedMovementGeneratorBase(Unit &target) { i_target.link(&target, this); }
+ TargetedMovementGeneratorBase(Unit* target) { i_target.link(target, this); }
void stopFollowing() { }
protected:
FollowerReference i_target;
@@ -37,24 +38,24 @@ template<class T, typename D>
class TargetedMovementGeneratorMedium : public MovementGeneratorMedium< T, D >, public TargetedMovementGeneratorBase
{
protected:
- TargetedMovementGeneratorMedium(Unit &target, float offset, float angle) :
- TargetedMovementGeneratorBase(target), i_recheckDistance(0),
- i_offset(offset), i_angle(angle),
+ TargetedMovementGeneratorMedium(Unit* target, float offset, float angle) :
+ TargetedMovementGeneratorBase(target), i_path(NULL),
+ i_recheckDistance(0), i_offset(offset), i_angle(angle),
i_recalculateTravel(false), i_targetReached(false)
{
}
- ~TargetedMovementGeneratorMedium() {}
+ ~TargetedMovementGeneratorMedium() { delete i_path; }
public:
- bool DoUpdate(T &, uint32);
+ bool DoUpdate(T*, uint32);
Unit* GetTarget() const { return i_target.getTarget(); }
- void unitSpeedChanged() { i_recalculateTravel=true; }
- void UpdateFinalDistance(float fDistance);
-
+ void unitSpeedChanged() { i_recalculateTravel = true; }
+ bool IsReachable() const { return (i_path) ? (i_path->GetPathType() & PATHFIND_NORMAL) : true; }
protected:
- void _setTargetLocation(T &);
+ void _setTargetLocation(T* owner, bool updateDestination);
+ PathGenerator* i_path;
TimeTrackerSmall i_recheckDistance;
float i_offset;
float i_angle;
@@ -66,50 +67,50 @@ template<class T>
class ChaseMovementGenerator : public TargetedMovementGeneratorMedium<T, ChaseMovementGenerator<T> >
{
public:
- ChaseMovementGenerator(Unit &target)
+ ChaseMovementGenerator(Unit* target)
: TargetedMovementGeneratorMedium<T, ChaseMovementGenerator<T> >(target) {}
- ChaseMovementGenerator(Unit &target, float offset, float angle)
+ ChaseMovementGenerator(Unit* target, float offset, float angle)
: TargetedMovementGeneratorMedium<T, ChaseMovementGenerator<T> >(target, offset, angle) {}
~ChaseMovementGenerator() {}
MovementGeneratorType GetMovementGeneratorType() { return CHASE_MOTION_TYPE; }
- void DoInitialize(T &);
- void DoFinalize(T &);
- void DoReset(T &);
- void MovementInform(T &);
+ void DoInitialize(T*);
+ void DoFinalize(T*);
+ void DoReset(T*);
+ void MovementInform(T*);
- static void _clearUnitStateMove(T &u) { u.ClearUnitState(UNIT_STATE_CHASE_MOVE); }
- static void _addUnitStateMove(T &u) { u.AddUnitState(UNIT_STATE_CHASE_MOVE); }
+ static void _clearUnitStateMove(T* u) { u->ClearUnitState(UNIT_STATE_CHASE_MOVE); }
+ static void _addUnitStateMove(T* u) { u->AddUnitState(UNIT_STATE_CHASE_MOVE); }
bool EnableWalking() const { return false;}
- bool _lostTarget(T &u) const { return u.getVictim() != this->GetTarget(); }
- void _reachTarget(T &);
+ bool _lostTarget(T* u) const { return u->getVictim() != this->GetTarget(); }
+ void _reachTarget(T*);
};
template<class T>
class FollowMovementGenerator : public TargetedMovementGeneratorMedium<T, FollowMovementGenerator<T> >
{
public:
- FollowMovementGenerator(Unit &target)
+ FollowMovementGenerator(Unit* target)
: TargetedMovementGeneratorMedium<T, FollowMovementGenerator<T> >(target){}
- FollowMovementGenerator(Unit &target, float offset, float angle)
+ FollowMovementGenerator(Unit* target, float offset, float angle)
: TargetedMovementGeneratorMedium<T, FollowMovementGenerator<T> >(target, offset, angle) {}
~FollowMovementGenerator() {}
MovementGeneratorType GetMovementGeneratorType() { return FOLLOW_MOTION_TYPE; }
- void DoInitialize(T &);
- void DoFinalize(T &);
- void DoReset(T &);
- void MovementInform(T &);
+ void DoInitialize(T*);
+ void DoFinalize(T*);
+ void DoReset(T*);
+ void MovementInform(T*);
- static void _clearUnitStateMove(T &u) { u.ClearUnitState(UNIT_STATE_FOLLOW_MOVE); }
- static void _addUnitStateMove(T &u) { u.AddUnitState(UNIT_STATE_FOLLOW_MOVE); }
+ static void _clearUnitStateMove(T* u) { u->ClearUnitState(UNIT_STATE_FOLLOW_MOVE); }
+ static void _addUnitStateMove(T* u) { u->AddUnitState(UNIT_STATE_FOLLOW_MOVE); }
bool EnableWalking() const;
- bool _lostTarget(T &) const { return false; }
- void _reachTarget(T &) {}
+ bool _lostTarget(T*) const { return false; }
+ void _reachTarget(T*) {}
private:
- void _updateSpeed(T &u);
+ void _updateSpeed(T* owner);
};
#endif
diff --git a/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.cpp b/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.cpp
index 4b825544bdf..98f4c9581c6 100644..100755
--- a/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.cpp
+++ b/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.cpp
@@ -31,64 +31,64 @@
#include "MoveSplineInit.h"
#include "MoveSpline.h"
-void WaypointMovementGenerator<Creature>::LoadPath(Creature &creature)
+void WaypointMovementGenerator<Creature>::LoadPath(Creature* creature)
{
if (!path_id)
- path_id = creature.GetWaypointPath();
+ path_id = creature->GetWaypointPath();
i_path = sWaypointMgr->GetPath(path_id);
if (!i_path)
{
// No movement found for entry
- sLog->outError(LOG_FILTER_SQL, "WaypointMovementGenerator::LoadPath: creature %s (Entry: %u GUID: %u) doesn't have waypoint path id: %u", creature.GetName().c_str(), creature.GetEntry(), creature.GetGUIDLow(), path_id);
+ sLog->outError(LOG_FILTER_SQL, "WaypointMovementGenerator::LoadPath: creature %s (Entry: %u GUID: %u) doesn't have waypoint path id: %u", creature->GetName().c_str(), creature->GetEntry(), creature->GetGUIDLow(), path_id);
return;
}
StartMoveNow(creature);
}
-void WaypointMovementGenerator<Creature>::DoInitialize(Creature &creature)
+void WaypointMovementGenerator<Creature>::DoInitialize(Creature* creature)
{
LoadPath(creature);
- creature.AddUnitState(UNIT_STATE_ROAMING|UNIT_STATE_ROAMING_MOVE);
+ creature->AddUnitState(UNIT_STATE_ROAMING|UNIT_STATE_ROAMING_MOVE);
}
-void WaypointMovementGenerator<Creature>::DoFinalize(Creature &creature)
+void WaypointMovementGenerator<Creature>::DoFinalize(Creature* creature)
{
- creature.ClearUnitState(UNIT_STATE_ROAMING|UNIT_STATE_ROAMING_MOVE);
- creature.SetWalk(false);
+ creature->ClearUnitState(UNIT_STATE_ROAMING|UNIT_STATE_ROAMING_MOVE);
+ creature->SetWalk(false);
}
-void WaypointMovementGenerator<Creature>::DoReset(Creature &creature)
+void WaypointMovementGenerator<Creature>::DoReset(Creature* creature)
{
- creature.AddUnitState(UNIT_STATE_ROAMING|UNIT_STATE_ROAMING_MOVE);
+ creature->AddUnitState(UNIT_STATE_ROAMING|UNIT_STATE_ROAMING_MOVE);
StartMoveNow(creature);
}
-void WaypointMovementGenerator<Creature>::OnArrived(Creature& creature)
+void WaypointMovementGenerator<Creature>::OnArrived(Creature* creature)
{
if (!i_path || i_path->empty())
return;
if (m_isArrivalDone)
return;
- creature.ClearUnitState(UNIT_STATE_ROAMING_MOVE);
+ creature->ClearUnitState(UNIT_STATE_ROAMING_MOVE);
m_isArrivalDone = true;
if (i_path->at(i_currentNode)->event_id && urand(0, 99) < i_path->at(i_currentNode)->event_chance)
{
- sLog->outDebug(LOG_FILTER_MAPSCRIPTS, "Creature movement start script %u at point %u for "UI64FMTD".", i_path->at(i_currentNode)->event_id, i_currentNode, creature.GetGUID());
- creature.GetMap()->ScriptsStart(sWaypointScripts, i_path->at(i_currentNode)->event_id, &creature, NULL);
+ sLog->outDebug(LOG_FILTER_MAPSCRIPTS, "Creature movement start script %u at point %u for "UI64FMTD".", i_path->at(i_currentNode)->event_id, i_currentNode, creature->GetGUID());
+ creature->GetMap()->ScriptsStart(sWaypointScripts, i_path->at(i_currentNode)->event_id, creature, NULL);
}
// Inform script
MovementInform(creature);
- creature.UpdateWaypointID(i_currentNode);
+ creature->UpdateWaypointID(i_currentNode);
Stop(i_path->at(i_currentNode)->delay);
}
-bool WaypointMovementGenerator<Creature>::StartMove(Creature &creature)
+bool WaypointMovementGenerator<Creature>::StartMove(Creature* creature)
{
if (!i_path || i_path->empty())
return false;
@@ -99,8 +99,8 @@ bool WaypointMovementGenerator<Creature>::StartMove(Creature &creature)
{
if ((i_currentNode == i_path->size() - 1) && !repeating) // If that's our last waypoint
{
- creature.SetHomePosition(i_path->at(i_currentNode)->x, i_path->at(i_currentNode)->y, i_path->at(i_currentNode)->z, creature.GetOrientation());
- creature.GetMotionMaster()->Initialize();
+ creature->SetHomePosition(i_path->at(i_currentNode)->x, i_path->at(i_currentNode)->y, i_path->at(i_currentNode)->z, creature->GetOrientation());
+ creature->GetMotionMaster()->Initialize();
return false;
}
@@ -111,7 +111,7 @@ bool WaypointMovementGenerator<Creature>::StartMove(Creature &creature)
m_isArrivalDone = false;
- creature.AddUnitState(UNIT_STATE_ROAMING_MOVE);
+ creature->AddUnitState(UNIT_STATE_ROAMING_MOVE);
Movement::MoveSplineInit init(creature);
init.MoveTo(node->x, node->y, node->z);
@@ -124,19 +124,19 @@ bool WaypointMovementGenerator<Creature>::StartMove(Creature &creature)
init.Launch();
//Call for creature group update
- if (creature.GetFormation() && creature.GetFormation()->getLeader() == &creature)
- creature.GetFormation()->LeaderMoveTo(node->x, node->y, node->z);
+ if (creature->GetFormation() && creature->GetFormation()->getLeader() == creature)
+ creature->GetFormation()->LeaderMoveTo(node->x, node->y, node->z);
return true;
}
-bool WaypointMovementGenerator<Creature>::DoUpdate(Creature &creature, uint32 diff)
+bool WaypointMovementGenerator<Creature>::DoUpdate(Creature* creature, uint32 diff)
{
// Waypoint movement can be switched on/off
// This is quite handy for escort quests and other stuff
- if (creature.HasUnitState(UNIT_STATE_NOT_MOVE))
+ if (creature->HasUnitState(UNIT_STATE_NOT_MOVE))
{
- creature.ClearUnitState(UNIT_STATE_ROAMING_MOVE);
+ creature->ClearUnitState(UNIT_STATE_ROAMING_MOVE);
return true;
}
// prevent a crash at empty waypoint path.
@@ -150,9 +150,9 @@ bool WaypointMovementGenerator<Creature>::DoUpdate(Creature &creature, uint32 di
}
else
{
- if (creature.IsStopped())
+ if (creature->IsStopped())
Stop(STOP_TIME_FOR_PLAYER);
- else if (creature.movespline->Finalized())
+ else if (creature->movespline->Finalized())
{
OnArrived(creature);
return StartMove(creature);
@@ -161,13 +161,13 @@ bool WaypointMovementGenerator<Creature>::DoUpdate(Creature &creature, uint32 di
return true;
}
-void WaypointMovementGenerator<Creature>::MovementInform(Creature &creature)
+void WaypointMovementGenerator<Creature>::MovementInform(Creature* creature)
{
- if (creature.AI())
- creature.AI()->MovementInform(WAYPOINT_MOTION_TYPE, i_currentNode);
+ if (creature->AI())
+ creature->AI()->MovementInform(WAYPOINT_MOTION_TYPE, i_currentNode);
}
-bool WaypointMovementGenerator<Creature>::GetResetPosition(Creature&, float& x, float& y, float& z)
+bool WaypointMovementGenerator<Creature>::GetResetPos(Creature*, float& x, float& y, float& z)
{
// prevent a crash at empty waypoint path.
if (!i_path || i_path->empty())
@@ -196,37 +196,37 @@ uint32 FlightPathMovementGenerator::GetPathAtMapEnd() const
return i_path->size();
}
-void FlightPathMovementGenerator::DoInitialize(Player &player)
+void FlightPathMovementGenerator::DoInitialize(Player* player)
{
- DoReset(player);
+ Reset(player);
InitEndGridInfo();
}
-void FlightPathMovementGenerator::DoFinalize(Player& player)
+void FlightPathMovementGenerator::DoFinalize(Player* player)
{
// remove flag to prevent send object build movement packets for flight state and crash (movement generator already not at top of stack)
- player.ClearUnitState(UNIT_STATE_IN_FLIGHT);
+ player->ClearUnitState(UNIT_STATE_IN_FLIGHT);
- player.Dismount();
- player.RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE | UNIT_FLAG_TAXI_FLIGHT);
+ player->Dismount();
+ player->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE | UNIT_FLAG_TAXI_FLIGHT);
- if (player.m_taxi.empty())
+ if (player->m_taxi.empty())
{
- player.getHostileRefManager().setOnlineOfflineState(true);
+ player->getHostileRefManager().setOnlineOfflineState(true);
// update z position to ground and orientation for landing point
// this prevent cheating with landing point at lags
// when client side flight end early in comparison server side
- player.StopMoving();
+ player->StopMoving();
}
}
#define PLAYER_FLIGHT_SPEED 32.0f
-void FlightPathMovementGenerator::DoReset(Player & player)
+void FlightPathMovementGenerator::DoReset(Player* player)
{
- player.getHostileRefManager().setOnlineOfflineState(false);
- player.AddUnitState(UNIT_STATE_IN_FLIGHT);
- player.SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE | UNIT_FLAG_TAXI_FLIGHT);
+ player->getHostileRefManager().setOnlineOfflineState(false);
+ player->AddUnitState(UNIT_STATE_IN_FLIGHT);
+ player->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE | UNIT_FLAG_TAXI_FLIGHT);
Movement::MoveSplineInit init(player);
uint32 end = GetPathAtMapEnd();
@@ -241,9 +241,9 @@ void FlightPathMovementGenerator::DoReset(Player & player)
init.Launch();
}
-bool FlightPathMovementGenerator::DoUpdate(Player &player, uint32 /*diff*/)
+bool FlightPathMovementGenerator::DoUpdate(Player* player, uint32 /*diff*/)
{
- uint32 pointId = (uint32)player.movespline->currentPathIdx();
+ uint32 pointId = (uint32)player->movespline->currentPathIdx();
if (pointId > i_currentNode)
{
bool departureEvent = true;
@@ -279,16 +279,16 @@ void FlightPathMovementGenerator::SetCurrentNodeAfterTeleport()
}
}
-void FlightPathMovementGenerator::DoEventIfAny(Player& player, TaxiPathNodeEntry const& node, bool departure)
+void FlightPathMovementGenerator::DoEventIfAny(Player* player, TaxiPathNodeEntry const& node, bool departure)
{
if (uint32 eventid = departure ? node.departureEventID : node.arrivalEventID)
{
- sLog->outDebug(LOG_FILTER_MAPSCRIPTS, "Taxi %s event %u of node %u of path %u for player %s", departure ? "departure" : "arrival", eventid, node.index, node.path, player.GetName().c_str());
- player.GetMap()->ScriptsStart(sEventScripts, eventid, &player, &player);
+ sLog->outDebug(LOG_FILTER_MAPSCRIPTS, "Taxi %s event %u of node %u of path %u for player %s", departure ? "departure" : "arrival", eventid, node.index, node.path, player->GetName().c_str());
+ player->GetMap()->ScriptsStart(sEventScripts, eventid, player, player);
}
}
-bool FlightPathMovementGenerator::GetResetPosition(Player&, float& x, float& y, float& z)
+bool FlightPathMovementGenerator::GetResetPos(Player*, float& x, float& y, float& z)
{
const TaxiPathNodeEntry& node = (*i_path)[i_currentNode];
x = node.x; y = node.y; z = node.z;
@@ -320,331 +320,3 @@ void FlightPathMovementGenerator::PreloadEndGrid()
else
sLog->outInfo(LOG_FILTER_GENERAL, "Unable to determine map to preload flightmaster grid");
}
-
-
-//
-// Unique1's ASTAR Pathfinding Code... For future use & reference...
-//
-
-#ifdef __PATHFINDING__
-
-int GetFCost(int to, int num, int parentNum, float *gcost); // Below...
-
-int ShortenASTARRoute(short int *pathlist, int number)
-{ // Wrote this to make the routes a little smarter (shorter)... No point looping back to the same places... Unique1
- short int temppathlist[MAX_PATHLIST_NODES];
- int count = 0;
- // int count2 = 0;
- int temp, temp2;
- int link;
- int upto = 0;
-
- for (temp = number; temp >= 0; temp--)
- {
- qboolean shortened = qfalse;
-
- for (temp2 = 0; temp2 < temp; temp2++)
- {
- for (link = 0; link < nodes[pathlist[temp]].enodenum; link++)
- {
- if (nodes[pathlist[temp]].links[link].flags & PATH_BLOCKED)
- continue;
-
- //if ((bot->client->ps.eFlags & EF_TANK) && nodes[bot->current_node].links[link].flags & PATH_NOTANKS) //if this path is blocked, skip it
- // continue;
-
- //if (nodes[nodes[pathlist[temp]].links[link].targetNode].origin[2] > nodes[pathlist[temp]].origin[2] + 32)
- // continue;
-
- if (nodes[pathlist[temp]].links[link].targetNode == pathlist[temp2])
- { // Found a shorter route...
- //if (OrgVisible(nodes[pathlist[temp2]].origin, nodes[pathlist[temp]].origin, -1))
- {
- temppathlist[count] = pathlist[temp2];
- temp = temp2;
- ++count;
- shortened = qtrue;
- }
- }
- }
- }
-
- if (!shortened)
- {
- temppathlist[count] = pathlist[temp];
- ++count;
- }
- }
-
- upto = count;
-
- for (temp = 0; temp < count; temp++)
- {
- pathlist[temp] = temppathlist[upto];
- --upto;
- }
-
- G_Printf("ShortenASTARRoute: Path size reduced from %i to %i nodes...n", number, count);
- return count;
-}
-
-/*
-===========================================================================
-CreatePathAStar
-This function uses the A* pathfinding algorithm to determine the
-shortest path between any two nodes.
-It's fairly complex, so I'm not really going to explain it much.
-Look up A* and binary heaps for more info.
-pathlist stores the ideal path between the nodes, in reverse order,
-and the return value is the number of nodes in that path
-===========================================================================
-*/
-int CreatePathAStar(gentity_t *bot, int from, int to, short int *pathlist)
-{
- //all the data we have to hold...since we can't do dynamic allocation, has to be MAX_NODES
- //we can probably lower this later - eg, the open list should never have more than at most a few dozen items on it
- short int openlist[MAX_NODES+1]; //add 1 because it's a binary heap, and they don't use 0 - 1 is the first used index
- float gcost[MAX_NODES];
- int fcost[MAX_NODES];
- char list[MAX_NODES]; //0 is neither, 1 is open, 2 is closed - char because it's the smallest data type
- short int parent[MAX_NODES];
-
- short int numOpen = 0;
- short int atNode, temp, newnode=-1;
- qboolean found = qfalse;
- int count = -1;
- float gc;
- int i, u, v, m;
- vec3_t vec;
-
- //clear out all the arrays
- memset(openlist, 0, sizeof(short int)*(MAX_NODES+1));
- memset(fcost, 0, sizeof(int)*MAX_NODES);
- memset(list, 0, sizeof(char)*MAX_NODES);
- memset(parent, 0, sizeof(short int)*MAX_NODES);
- memset(gcost, -1, sizeof(float)*MAX_NODES);
-
- //make sure we have valid data before calculating everything
- if ((from == NODE_INVALID) || (to == NODE_INVALID) || (from >= MAX_NODES) || (to >= MAX_NODES) || (from == to))
- return -1;
-
- openlist[1] = from; //add the starting node to the open list
- ++numOpen;
- gcost[from] = 0; //its f and g costs are obviously 0
- fcost[from] = 0;
-
- while (1)
- {
- if (numOpen != 0) //if there are still items in the open list
- {
- //pop the top item off of the list
- atNode = openlist[1];
- list[atNode] = 2; //put the node on the closed list so we don't check it again
- --numOpen;
-
- openlist[1] = openlist[numOpen+1]; //move the last item in the list to the top position
- v = 1;
-
- //this while loop reorders the list so that the new lowest fcost is at the top again
- while (1)
- {
- u = v;
- if ((2*u+1) < numOpen) //if both children exist
- {
- if (fcost[openlist[u]] >= fcost[openlist[2*u]])
- v = 2*u;
- if (fcost[openlist[v]] >= fcost[openlist[2*u+1]])
- v = 2*u+1;
- }
- else
- {
- if ((2*u) < numOpen) //if only one child exists
- {
- if (fcost[openlist[u]] >= fcost[openlist[2*u]])
- v = 2*u;
- }
- }
-
- if (u != v) //if they're out of order, swap this item with its parent
- {
- temp = openlist[u];
- openlist[u] = openlist[v];
- openlist[v] = temp;
- }
- else
- break;
- }
-
- for (i = 0; i < nodes[atNode].enodenum; ++i) //loop through all the links for this node
- {
- newnode = nodes[atNode].links[i].targetNode;
-
- //if this path is blocked, skip it
- if (nodes[atNode].links[i].flags & PATH_BLOCKED)
- continue;
- //if this path is blocked, skip it
- if (bot->client && (bot->client->ps.eFlags & EF_TANK) && nodes[atNode].links[i].flags & PATH_NOTANKS)
- continue;
- //skip any unreachable nodes
- if (bot->client && (nodes[newnode].type & NODE_ALLY_UNREACHABLE) && (bot->client->sess.sessionTeam == TEAM_ALLIES))
- continue;
- if (bot->client && (nodes[newnode].type & NODE_AXIS_UNREACHABLE) && (bot->client->sess.sessionTeam == TEAM_AXIS))
- continue;
-
- if (list[newnode] == 2) //if this node is on the closed list, skip it
- continue;
-
- if (list[newnode] != 1) //if this node is not already on the open list
- {
- openlist[++numOpen] = newnode; //add the new node to the open list
- list[newnode] = 1;
- parent[newnode] = atNode; //record the node's parent
-
- if (newnode == to) //if we've found the goal, don't keep computing paths!
- break; //this will break the 'for' and go all the way to 'if (list[to] == 1)'
-
- //store it's f cost value
- fcost[newnode] = GetFCost(to, newnode, parent[newnode], gcost);
-
- //this loop re-orders the heap so that the lowest fcost is at the top
- m = numOpen;
- while (m != 1) //while this item isn't at the top of the heap already
- {
- //if it has a lower fcost than its parent
- if (fcost[openlist[m]] <= fcost[openlist[m/2]])
- {
- temp = openlist[m/2];
- openlist[m/2] = openlist[m];
- openlist[m] = temp; //swap them
- m /= 2;
- }
- else
- break;
- }
- }
- else //if this node is already on the open list
- {
- gc = gcost[atNode];
- VectorSubtract(nodes[newnode].origin, nodes[atNode].origin, vec);
- gc += VectorLength(vec); //calculate what the gcost would be if we reached this node along the current path
-
- if (gc < gcost[newnode]) //if the new gcost is less (ie, this path is shorter than what we had before)
- {
- parent[newnode] = atNode; //set the new parent for this node
- gcost[newnode] = gc; //and the new g cost
-
- for (i = 1; i < numOpen; ++i) //loop through all the items on the open list
- {
- if (openlist[i] == newnode) //find this node in the list
- {
- //calculate the new fcost and store it
- fcost[newnode] = GetFCost(to, newnode, parent[newnode], gcost);
-
- //reorder the list again, with the lowest fcost item on top
- m = i;
- while (m != 1)
- {
- //if the item has a lower fcost than it's parent
- if (fcost[openlist[m]] < fcost[openlist[m/2]])
- {
- temp = openlist[m/2];
- openlist[m/2] = openlist[m];
- openlist[m] = temp; //swap them
- m /= 2;
- }
- else
- break;
- }
- break; //exit the 'for' loop because we already changed this node
- } //if
- } //for
- } //if (gc < gcost[newnode])
- } //if (list[newnode] != 1) --> else
- } //for (loop through links)
- } //if (numOpen != 0)
- else
- {
- found = qfalse; //there is no path between these nodes
- break;
- }
-
- if (list[to] == 1) //if the destination node is on the open list, we're done
- {
- found = qtrue;
- break;
- }
- } //while (1)
-
- if (found == qtrue) //if we found a path
- {
- //G_Printf("%s - path found!n", bot->client->pers.netname);
- count = 0;
-
- temp = to; //start at the end point
- while (temp != from) //travel along the path (backwards) until we reach the starting point
- {
- pathlist[count++] = temp; //add the node to the pathlist and increment the count
- temp = parent[temp]; //move to the parent of this node to continue the path
- }
-
- pathlist[count++] = from; //add the beginning node to the end of the pathlist
-
- #ifdef __BOT_SHORTEN_ROUTING__
- count = ShortenASTARRoute(pathlist, count); // This isn't working... Dunno why.. Unique1
- #endif //__BOT_SHORTEN_ROUTING__
- }
- else
- {
- //G_Printf("^1*** ^4BOT DEBUG^5: (CreatePathAStar) There is no route between node ^7%i^5 and node ^7%i^5.n", from, to);
- count = CreateDumbRoute(from, to, pathlist);
-
- if (count > 0)
- {
- #ifdef __BOT_SHORTEN_ROUTING__
- count = ShortenASTARRoute(pathlist, count); // This isn't working... Dunno why.. Unique1
- #endif //__BOT_SHORTEN_ROUTING__
- return count;
- }
- }
-
- return count; //return the number of nodes in the path, -1 if not found
-}
-
-/*
-===========================================================================
-GetFCost
-Utility function used by A* pathfinding to calculate the
-cost to move between nodes towards a goal. Using the A*
-algorithm F = G + H, G here is the distance along the node
-paths the bot must travel, and H is the straight-line distance
-to the goal node.
-Returned as an int because more precision is unnecessary and it
-will slightly speed up heap access
-===========================================================================
-*/
-int GetFCost(int to, int num, int parentNum, float *gcost)
-{
- float gc = 0;
- float hc = 0;
- vec3_t v;
-
- if (gcost[num] == -1)
- {
- if (parentNum != -1)
- {
- gc = gcost[parentNum];
- VectorSubtract(nodes[num].origin, nodes[parentNum].origin, v);
- gc += VectorLength(v);
- }
- gcost[num] = gc;
- }
- else
- gc = gcost[num];
-
- VectorSubtract(nodes[to].origin, nodes[num].origin, v);
- hc = VectorLength(v);
-
- return (int)(gc + hc);
-}
-#endif //__PATHFINDING__
-
diff --git a/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.h b/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.h
index 319a5c66a03..72650570e12 100644..100755
--- a/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.h
+++ b/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.h
@@ -65,19 +65,19 @@ class WaypointMovementGenerator<Creature> : public MovementGeneratorMedium< Crea
WaypointMovementGenerator(uint32 _path_id = 0, bool _repeating = true)
: i_nextMoveTime(0), m_isArrivalDone(false), path_id(_path_id), repeating(_repeating) {}
~WaypointMovementGenerator() { i_path = NULL; }
- void DoInitialize(Creature &);
- void DoFinalize(Creature &);
- void DoReset(Creature &);
- bool DoUpdate(Creature &, uint32 diff);
+ void DoInitialize(Creature*);
+ void DoFinalize(Creature*);
+ void DoReset(Creature*);
+ bool DoUpdate(Creature*, uint32 diff);
- void MovementInform(Creature &);
+ void MovementInform(Creature*);
MovementGeneratorType GetMovementGeneratorType() { return WAYPOINT_MOTION_TYPE; }
// now path movement implmementation
- void LoadPath(Creature &c);
+ void LoadPath(Creature*);
- bool GetResetPosition(Creature&, float& x, float& y, float& z);
+ bool GetResetPos(Creature*, float& x, float& y, float& z);
private:
@@ -91,10 +91,10 @@ class WaypointMovementGenerator<Creature> : public MovementGeneratorMedium< Crea
return i_nextMoveTime.Passed();
}
- void OnArrived(Creature&);
- bool StartMove(Creature&);
+ void OnArrived(Creature*);
+ bool StartMove(Creature*);
- void StartMoveNow(Creature& creature)
+ void StartMoveNow(Creature* creature)
{
i_nextMoveTime.Reset(0);
StartMove(creature);
@@ -118,10 +118,10 @@ class FlightPathMovementGenerator : public MovementGeneratorMedium< Player, Flig
i_path = &pathnodes;
i_currentNode = startNode;
}
- void DoInitialize(Player &);
- void DoReset(Player &);
- void DoFinalize(Player &);
- bool DoUpdate(Player &, uint32);
+ void DoInitialize(Player*);
+ void DoReset(Player*);
+ void DoFinalize(Player*);
+ bool DoUpdate(Player*, uint32);
MovementGeneratorType GetMovementGeneratorType() { return FLIGHT_MOTION_TYPE; }
TaxiPathNodeList const& GetPath() { return *i_path; }
@@ -129,9 +129,9 @@ class FlightPathMovementGenerator : public MovementGeneratorMedium< Player, Flig
bool HasArrived() const { return (i_currentNode >= i_path->size()); }
void SetCurrentNodeAfterTeleport();
void SkipCurrentNode() { ++i_currentNode; }
- void DoEventIfAny(Player& player, TaxiPathNodeEntry const& node, bool departure);
+ void DoEventIfAny(Player* player, TaxiPathNodeEntry const& node, bool departure);
- bool GetResetPosition(Player&, float& x, float& y, float& z);
+ bool GetResetPos(Player*, float& x, float& y, float& z);
void InitEndGridInfo();
void PreloadEndGrid();
@@ -143,4 +143,3 @@ class FlightPathMovementGenerator : public MovementGeneratorMedium< Player, Flig
uint32 _preloadTargetNode; //! node index where preloading starts
};
#endif
-
diff --git a/src/server/game/Movement/PathGenerator.cpp b/src/server/game/Movement/PathGenerator.cpp
new file mode 100644
index 00000000000..dbda4aa2411
--- /dev/null
+++ b/src/server/game/Movement/PathGenerator.cpp
@@ -0,0 +1,803 @@
+/*
+ * Copyright (C) 2005-2011 MaNGOS <http://getmangos.com/>
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "PathGenerator.h"
+#include "Map.h"
+#include "Creature.h"
+#include "MMapFactory.h"
+#include "MMapManager.h"
+#include "Log.h"
+
+#include "DetourCommon.h"
+#include "DetourNavMeshQuery.h"
+
+////////////////// PathGenerator //////////////////
+PathGenerator::PathGenerator(const Unit* owner) :
+ _polyLength(0), _type(PATHFIND_BLANK),
+ _useStraightPath(false), _forceDestination(false), _pointPathLimit(MAX_POINT_PATH_LENGTH),
+ _endPosition(Vector3::zero()), _sourceUnit(owner), _navMesh(NULL), _navMeshQuery(NULL)
+{
+ sLog->outDebug(LOG_FILTER_MAPS, "++ PathGenerator::PathGenerator for %u \n", _sourceUnit->GetGUIDLow());
+
+ uint32 mapId = _sourceUnit->GetMapId();
+ if (MMAP::MMapFactory::IsPathfindingEnabled(mapId))
+ {
+ MMAP::MMapManager* mmap = MMAP::MMapFactory::createOrGetMMapManager();
+ _navMesh = mmap->GetNavMesh(mapId);
+ _navMeshQuery = mmap->GetNavMeshQuery(mapId, _sourceUnit->GetInstanceId());
+ }
+
+ CreateFilter();
+}
+
+PathGenerator::~PathGenerator()
+{
+ sLog->outDebug(LOG_FILTER_MAPS, "++ PathGenerator::~PathGenerator() for %u \n", _sourceUnit->GetGUIDLow());
+}
+
+bool PathGenerator::CalculatePath(float destX, float destY, float destZ, bool forceDest)
+{
+ float x, y, z;
+ _sourceUnit->GetPosition(x, y, z);
+
+ if (!Trinity::IsValidMapCoord(destX, destY, destZ) || !Trinity::IsValidMapCoord(x, y, z))
+ return false;
+
+ Vector3 dest(destX, destY, destZ);
+ SetEndPosition(dest);
+
+ Vector3 start(x, y, z);
+ SetStartPosition(start);
+
+ _forceDestination = forceDest;
+
+ sLog->outDebug(LOG_FILTER_MAPS, "++ PathGenerator::CalculatePath() for %u \n", _sourceUnit->GetGUIDLow());
+
+ // make sure navMesh works - we can run on map w/o mmap
+ // check if the start and end point have a .mmtile loaded (can we pass via not loaded tile on the way?)
+ if (!_navMesh || !_navMeshQuery || _sourceUnit->HasUnitState(UNIT_STATE_IGNORE_PATHFINDING) ||
+ !HaveTile(start) || !HaveTile(dest))
+ {
+ BuildShortcut();
+ _type = PathType(PATHFIND_NORMAL | PATHFIND_NOT_USING_PATH);
+ return true;
+ }
+
+ UpdateFilter();
+
+ BuildPolyPath(start, dest);
+ return true;
+}
+
+dtPolyRef PathGenerator::GetPathPolyByPosition(dtPolyRef const* polyPath, uint32 polyPathSize, float const* point, float* distance) const
+{
+ if (!polyPath || !polyPathSize)
+ return INVALID_POLYREF;
+
+ dtPolyRef nearestPoly = INVALID_POLYREF;
+ float minDist2d = FLT_MAX;
+ float minDist3d = 0.0f;
+
+ for (uint32 i = 0; i < polyPathSize; ++i)
+ {
+ float closestPoint[VERTEX_SIZE];
+ if (DT_SUCCESS != _navMeshQuery->closestPointOnPoly(polyPath[i], point, closestPoint))
+ continue;
+
+ float d = dtVdist2DSqr(point, closestPoint);
+ if (d < minDist2d)
+ {
+ minDist2d = d;
+ nearestPoly = polyPath[i];
+ minDist3d = dtVdistSqr(point, closestPoint);
+ }
+
+ if (minDist2d < 1.0f) // shortcut out - close enough for us
+ break;
+ }
+
+ if (distance)
+ *distance = dtSqrt(minDist3d);
+
+ return (minDist2d < 3.0f) ? nearestPoly : INVALID_POLYREF;
+}
+
+dtPolyRef PathGenerator::GetPolyByLocation(float const* point, float* distance) const
+{
+ // first we check the current path
+ // if the current path doesn't contain the current poly,
+ // we need to use the expensive navMesh.findNearestPoly
+ dtPolyRef polyRef = GetPathPolyByPosition(_pathPolyRefs, _polyLength, point, distance);
+ if (polyRef != INVALID_POLYREF)
+ return polyRef;
+
+ // we don't have it in our old path
+ // try to get it by findNearestPoly()
+ // first try with low search box
+ float extents[VERTEX_SIZE] = {3.0f, 5.0f, 3.0f}; // bounds of poly search area
+ float closestPoint[VERTEX_SIZE] = {0.0f, 0.0f, 0.0f};
+ dtStatus result = _navMeshQuery->findNearestPoly(point, extents, &_filter, &polyRef, closestPoint);
+ if (DT_SUCCESS == result && polyRef != INVALID_POLYREF)
+ {
+ *distance = dtVdist(closestPoint, point);
+ return polyRef;
+ }
+
+ // still nothing ..
+ // try with bigger search box
+ extents[1] = 200.0f;
+ result = _navMeshQuery->findNearestPoly(point, extents, &_filter, &polyRef, closestPoint);
+ if (DT_SUCCESS == result && polyRef != INVALID_POLYREF)
+ {
+ *distance = dtVdist(closestPoint, point);
+ return polyRef;
+ }
+
+ return INVALID_POLYREF;
+}
+
+void PathGenerator::BuildPolyPath(Vector3 const& startPos, Vector3 const& endPos)
+{
+ // *** getting start/end poly logic ***
+
+ float distToStartPoly, distToEndPoly;
+ float startPoint[VERTEX_SIZE] = {startPos.y, startPos.z, startPos.x};
+ float endPoint[VERTEX_SIZE] = {endPos.y, endPos.z, endPos.x};
+
+ dtPolyRef startPoly = GetPolyByLocation(startPoint, &distToStartPoly);
+ dtPolyRef endPoly = GetPolyByLocation(endPoint, &distToEndPoly);
+
+ // we have a hole in our mesh
+ // make shortcut path and mark it as NOPATH ( with flying and swimming exception )
+ // its up to caller how he will use this info
+ if (startPoly == INVALID_POLYREF || endPoly == INVALID_POLYREF)
+ {
+ sLog->outDebug(LOG_FILTER_MAPS, "++ BuildPolyPath :: (startPoly == 0 || endPoly == 0)\n");
+ BuildShortcut();
+ bool path = _sourceUnit->GetTypeId() == TYPEID_UNIT && _sourceUnit->ToCreature()->CanFly();
+
+ bool waterPath = _sourceUnit->GetTypeId() == TYPEID_UNIT && _sourceUnit->ToCreature()->canSwim();
+ if (waterPath)
+ {
+ // Check both start and end points, if they're both in water, then we can *safely* let the creature move
+ for (uint32 i = 0; i < _pathPoints.size(); ++i)
+ {
+ ZLiquidStatus status = _sourceUnit->GetBaseMap()->getLiquidStatus(_pathPoints[i].x, _pathPoints[i].y, _pathPoints[i].z, MAP_ALL_LIQUIDS, NULL);
+ // One of the points is not in the water, cancel movement.
+ if (status == LIQUID_MAP_NO_WATER)
+ {
+ waterPath = false;
+ break;
+ }
+ }
+ }
+
+ _type = (path || waterPath) ? PathType(PATHFIND_NORMAL | PATHFIND_NOT_USING_PATH) : PATHFIND_NOPATH;
+ return;
+ }
+
+ // we may need a better number here
+ bool farFromPoly = (distToStartPoly > 7.0f || distToEndPoly > 7.0f);
+ if (farFromPoly)
+ {
+ sLog->outDebug(LOG_FILTER_MAPS, "++ BuildPolyPath :: farFromPoly distToStartPoly=%.3f distToEndPoly=%.3f\n", distToStartPoly, distToEndPoly);
+
+ bool buildShotrcut = false;
+ if (_sourceUnit->GetTypeId() == TYPEID_UNIT)
+ {
+ Creature* owner = (Creature*)_sourceUnit;
+
+ Vector3 p = (distToStartPoly > 7.0f) ? startPos : endPos;
+ if (_sourceUnit->GetBaseMap()->IsUnderWater(p.x, p.y, p.z))
+ {
+ sLog->outDebug(LOG_FILTER_MAPS, "++ BuildPolyPath :: underWater case\n");
+ if (owner->canSwim())
+ buildShotrcut = true;
+ }
+ else
+ {
+ sLog->outDebug(LOG_FILTER_MAPS, "++ BuildPolyPath :: flying case\n");
+ if (owner->CanFly())
+ buildShotrcut = true;
+ }
+ }
+
+ if (buildShotrcut)
+ {
+ BuildShortcut();
+ _type = PathType(PATHFIND_NORMAL | PATHFIND_NOT_USING_PATH);
+ return;
+ }
+ else
+ {
+ float closestPoint[VERTEX_SIZE];
+ // we may want to use closestPointOnPolyBoundary instead
+ if (DT_SUCCESS == _navMeshQuery->closestPointOnPoly(endPoly, endPoint, closestPoint))
+ {
+ dtVcopy(endPoint, closestPoint);
+ SetActualEndPosition(Vector3(endPoint[2], endPoint[0], endPoint[1]));
+ }
+
+ _type = PATHFIND_INCOMPLETE;
+ }
+ }
+
+ // *** poly path generating logic ***
+
+ // start and end are on same polygon
+ // just need to move in straight line
+ if (startPoly == endPoly)
+ {
+ sLog->outDebug(LOG_FILTER_MAPS, "++ BuildPolyPath :: (startPoly == endPoly)\n");
+
+ BuildShortcut();
+
+ _pathPolyRefs[0] = startPoly;
+ _polyLength = 1;
+
+ _type = farFromPoly ? PATHFIND_INCOMPLETE : PATHFIND_NORMAL;
+ sLog->outDebug(LOG_FILTER_MAPS, "++ BuildPolyPath :: path type %d\n", _type);
+ return;
+ }
+
+ // look for startPoly/endPoly in current path
+ // TODO: we can merge it with getPathPolyByPosition() loop
+ bool startPolyFound = false;
+ bool endPolyFound = false;
+ uint32 pathStartIndex = 0;
+ uint32 pathEndIndex = 0;
+
+ if (_polyLength)
+ {
+ for (; pathStartIndex < _polyLength; ++pathStartIndex)
+ {
+ // here to carch few bugs
+ ASSERT(_pathPolyRefs[pathStartIndex] != INVALID_POLYREF);
+
+ if (_pathPolyRefs[pathStartIndex] == startPoly)
+ {
+ startPolyFound = true;
+ break;
+ }
+ }
+
+ for (pathEndIndex = _polyLength-1; pathEndIndex > pathStartIndex; --pathEndIndex)
+ if (_pathPolyRefs[pathEndIndex] == endPoly)
+ {
+ endPolyFound = true;
+ break;
+ }
+ }
+
+ if (startPolyFound && endPolyFound)
+ {
+ sLog->outDebug(LOG_FILTER_MAPS, "++ BuildPolyPath :: (startPolyFound && endPolyFound)\n");
+
+ // we moved along the path and the target did not move out of our old poly-path
+ // our path is a simple subpath case, we have all the data we need
+ // just "cut" it out
+
+ _polyLength = pathEndIndex - pathStartIndex + 1;
+ memmove(_pathPolyRefs, _pathPolyRefs + pathStartIndex, _polyLength * sizeof(dtPolyRef));
+ }
+ else if (startPolyFound && !endPolyFound)
+ {
+ sLog->outDebug(LOG_FILTER_MAPS, "++ BuildPolyPath :: (startPolyFound && !endPolyFound)\n");
+
+ // we are moving on the old path but target moved out
+ // so we have atleast part of poly-path ready
+
+ _polyLength -= pathStartIndex;
+
+ // try to adjust the suffix of the path instead of recalculating entire length
+ // at given interval the target cannot get too far from its last location
+ // thus we have less poly to cover
+ // sub-path of optimal path is optimal
+
+ // take ~80% of the original length
+ // TODO : play with the values here
+ uint32 prefixPolyLength = uint32(_polyLength * 0.8f + 0.5f);
+ memmove(_pathPolyRefs, _pathPolyRefs+pathStartIndex, prefixPolyLength * sizeof(dtPolyRef));
+
+ dtPolyRef suffixStartPoly = _pathPolyRefs[prefixPolyLength-1];
+
+ // we need any point on our suffix start poly to generate poly-path, so we need last poly in prefix data
+ float suffixEndPoint[VERTEX_SIZE];
+ if (DT_SUCCESS != _navMeshQuery->closestPointOnPoly(suffixStartPoly, endPoint, suffixEndPoint))
+ {
+ // we can hit offmesh connection as last poly - closestPointOnPoly() don't like that
+ // try to recover by using prev polyref
+ --prefixPolyLength;
+ suffixStartPoly = _pathPolyRefs[prefixPolyLength-1];
+ if (DT_SUCCESS != _navMeshQuery->closestPointOnPoly(suffixStartPoly, endPoint, suffixEndPoint))
+ {
+ // suffixStartPoly is still invalid, error state
+ BuildShortcut();
+ _type = PATHFIND_NOPATH;
+ return;
+ }
+ }
+
+ // generate suffix
+ uint32 suffixPolyLength = 0;
+ dtStatus dtResult = _navMeshQuery->findPath(
+ suffixStartPoly, // start polygon
+ endPoly, // end polygon
+ suffixEndPoint, // start position
+ endPoint, // end position
+ &_filter, // polygon search filter
+ _pathPolyRefs + prefixPolyLength - 1, // [out] path
+ (int*)&suffixPolyLength,
+ MAX_PATH_LENGTH-prefixPolyLength); // max number of polygons in output path
+
+ if (!suffixPolyLength || dtResult != DT_SUCCESS)
+ {
+ // this is probably an error state, but we'll leave it
+ // and hopefully recover on the next Update
+ // we still need to copy our preffix
+ sLog->outError(LOG_FILTER_MAPS, "%u's Path Build failed: 0 length path", _sourceUnit->GetGUIDLow());
+ }
+
+ sLog->outDebug(LOG_FILTER_MAPS, "++ m_polyLength=%u prefixPolyLength=%u suffixPolyLength=%u \n", _polyLength, prefixPolyLength, suffixPolyLength);
+
+ // new path = prefix + suffix - overlap
+ _polyLength = prefixPolyLength + suffixPolyLength - 1;
+ }
+ else
+ {
+ sLog->outDebug(LOG_FILTER_MAPS, "++ BuildPolyPath :: (!startPolyFound && !endPolyFound)\n");
+
+ // either we have no path at all -> first run
+ // or something went really wrong -> we aren't moving along the path to the target
+ // just generate new path
+
+ // free and invalidate old path data
+ Clear();
+
+ dtStatus dtResult = _navMeshQuery->findPath(
+ startPoly, // start polygon
+ endPoly, // end polygon
+ startPoint, // start position
+ endPoint, // end position
+ &_filter, // polygon search filter
+ _pathPolyRefs, // [out] path
+ (int*)&_polyLength,
+ MAX_PATH_LENGTH); // max number of polygons in output path
+
+ if (!_polyLength || dtResult != DT_SUCCESS)
+ {
+ // only happens if we passed bad data to findPath(), or navmesh is messed up
+ sLog->outError(LOG_FILTER_MAPS, "%u's Path Build failed: 0 length path", _sourceUnit->GetGUIDLow());
+ BuildShortcut();
+ _type = PATHFIND_NOPATH;
+ return;
+ }
+ }
+
+ // by now we know what type of path we can get
+ if (_pathPolyRefs[_polyLength - 1] == endPoly && !(_type & PATHFIND_INCOMPLETE))
+ _type = PATHFIND_NORMAL;
+ else
+ _type = PATHFIND_INCOMPLETE;
+
+ // generate the point-path out of our up-to-date poly-path
+ BuildPointPath(startPoint, endPoint);
+}
+
+void PathGenerator::BuildPointPath(const float *startPoint, const float *endPoint)
+{
+ float pathPoints[MAX_POINT_PATH_LENGTH*VERTEX_SIZE];
+ uint32 pointCount = 0;
+ dtStatus dtResult = DT_FAILURE;
+ if (_useStraightPath)
+ {
+ dtResult = _navMeshQuery->findStraightPath(
+ startPoint, // start position
+ endPoint, // end position
+ _pathPolyRefs, // current path
+ _polyLength, // lenth of current path
+ pathPoints, // [out] path corner points
+ NULL, // [out] flags
+ NULL, // [out] shortened path
+ (int*)&pointCount,
+ _pointPathLimit); // maximum number of points/polygons to use
+ }
+ else
+ {
+ dtResult = FindSmoothPath(
+ startPoint, // start position
+ endPoint, // end position
+ _pathPolyRefs, // current path
+ _polyLength, // length of current path
+ pathPoints, // [out] path corner points
+ (int*)&pointCount,
+ _pointPathLimit); // maximum number of points
+ }
+
+ if (pointCount < 2 || dtResult != DT_SUCCESS)
+ {
+ // only happens if pass bad data to findStraightPath or navmesh is broken
+ // single point paths can be generated here
+ // TODO : check the exact cases
+ sLog->outDebug(LOG_FILTER_MAPS, "++ PathGenerator::BuildPointPath FAILED! path sized %d returned\n", pointCount);
+ BuildShortcut();
+ _type = PATHFIND_NOPATH;
+ return;
+ }
+ else if (pointCount == _pointPathLimit)
+ {
+ sLog->outDebug(LOG_FILTER_MAPS, "++ PathGenerator::BuildPointPath FAILED! path sized %d returned, lower than limit set to %d\n", pointCount, _pointPathLimit);
+ BuildShortcut();
+ _type = PATHFIND_SHORT;
+ return;
+ }
+
+ _pathPoints.resize(pointCount);
+ for (uint32 i = 0; i < pointCount; ++i)
+ _pathPoints[i] = Vector3(pathPoints[i*VERTEX_SIZE+2], pathPoints[i*VERTEX_SIZE], pathPoints[i*VERTEX_SIZE+1]);
+
+ NormalizePath();
+
+ // first point is always our current location - we need the next one
+ SetActualEndPosition(_pathPoints[pointCount-1]);
+
+ // force the given destination, if needed
+ if (_forceDestination &&
+ (!(_type & PATHFIND_NORMAL) || !InRange(GetEndPosition(), GetActualEndPosition(), 1.0f, 1.0f)))
+ {
+ // we may want to keep partial subpath
+ if (Dist3DSqr(GetActualEndPosition(), GetEndPosition()) < 0.3f * Dist3DSqr(GetStartPosition(), GetEndPosition()))
+ {
+ SetActualEndPosition(GetEndPosition());
+ _pathPoints[_pathPoints.size()-1] = GetEndPosition();
+ }
+ else
+ {
+ SetActualEndPosition(GetEndPosition());
+ BuildShortcut();
+ }
+
+ _type = PathType(PATHFIND_NORMAL | PATHFIND_NOT_USING_PATH);
+ }
+
+ sLog->outDebug(LOG_FILTER_MAPS, "++ PathGenerator::BuildPointPath path type %d size %d poly-size %d\n", _type, pointCount, _polyLength);
+}
+
+void PathGenerator::NormalizePath()
+{
+ for (uint32 i = 0; i < _pathPoints.size(); ++i)
+ _sourceUnit->UpdateAllowedPositionZ(_pathPoints[i].x, _pathPoints[i].y, _pathPoints[i].z);
+}
+
+void PathGenerator::BuildShortcut()
+{
+ sLog->outDebug(LOG_FILTER_MAPS, "++ BuildShortcut :: making shortcut\n");
+
+ Clear();
+
+ // make two point path, our curr pos is the start, and dest is the end
+ _pathPoints.resize(2);
+
+ // set start and a default next position
+ _pathPoints[0] = GetStartPosition();
+ _pathPoints[1] = GetActualEndPosition();
+
+ NormalizePath();
+
+ _type = PATHFIND_SHORTCUT;
+}
+
+void PathGenerator::CreateFilter()
+{
+ uint16 includeFlags = 0;
+ uint16 excludeFlags = 0;
+
+ if (_sourceUnit->GetTypeId() == TYPEID_UNIT)
+ {
+ Creature* creature = (Creature*)_sourceUnit;
+ if (creature->canWalk())
+ includeFlags |= NAV_GROUND; // walk
+
+ // creatures don't take environmental damage
+ if (creature->canSwim())
+ includeFlags |= (NAV_WATER | NAV_MAGMA | NAV_SLIME); // swim
+ }
+ else // assume Player
+ {
+ // perfect support not possible, just stay 'safe'
+ includeFlags |= (NAV_GROUND | NAV_WATER | NAV_MAGMA | NAV_SLIME);
+ }
+
+ _filter.setIncludeFlags(includeFlags);
+ _filter.setExcludeFlags(excludeFlags);
+
+ UpdateFilter();
+}
+
+void PathGenerator::UpdateFilter()
+{
+ // allow creatures to cheat and use different movement types if they are moved
+ // forcefully into terrain they can't normally move in
+ if (_sourceUnit->IsInWater() || _sourceUnit->IsUnderWater())
+ {
+ uint16 includedFlags = _filter.getIncludeFlags();
+ includedFlags |= GetNavTerrain(_sourceUnit->GetPositionX(),
+ _sourceUnit->GetPositionY(),
+ _sourceUnit->GetPositionZ());
+
+ _filter.setIncludeFlags(includedFlags);
+ }
+}
+
+NavTerrain PathGenerator::GetNavTerrain(float x, float y, float z)
+{
+ LiquidData data;
+ _sourceUnit->GetBaseMap()->getLiquidStatus(x, y, z, MAP_ALL_LIQUIDS, &data);
+
+ switch (data.type_flags)
+ {
+ case MAP_LIQUID_TYPE_WATER:
+ case MAP_LIQUID_TYPE_OCEAN:
+ return NAV_WATER;
+ case MAP_LIQUID_TYPE_MAGMA:
+ return NAV_MAGMA;
+ case MAP_LIQUID_TYPE_SLIME:
+ return NAV_SLIME;
+ default:
+ return NAV_GROUND;
+ }
+}
+
+bool PathGenerator::HaveTile(const Vector3& p) const
+{
+ int tx = -1, ty = -1;
+ float point[VERTEX_SIZE] = {p.y, p.z, p.x};
+
+ _navMesh->calcTileLoc(point, &tx, &ty);
+
+ /// Workaround
+ /// For some reason, often the tx and ty variables wont get a valid value
+ /// Use this check to prevent getting negative tile coords and crashing on getTileAt
+ if (tx < 0 || ty < 0)
+ return false;
+
+ return (_navMesh->getTileAt(tx, ty) != NULL);
+}
+
+uint32 PathGenerator::FixupCorridor(dtPolyRef* path, uint32 npath, uint32 maxPath, dtPolyRef const* visited, uint32 nvisited)
+{
+ int32 furthestPath = -1;
+ int32 furthestVisited = -1;
+
+ // Find furthest common polygon.
+ for (int32 i = npath-1; i >= 0; --i)
+ {
+ bool found = false;
+ for (int32 j = nvisited-1; j >= 0; --j)
+ {
+ if (path[i] == visited[j])
+ {
+ furthestPath = i;
+ furthestVisited = j;
+ found = true;
+ }
+ }
+ if (found)
+ break;
+ }
+
+ // If no intersection found just return current path.
+ if (furthestPath == -1 || furthestVisited == -1)
+ return npath;
+
+ // Concatenate paths.
+
+ // Adjust beginning of the buffer to include the visited.
+ uint32 req = nvisited - furthestVisited;
+ uint32 orig = uint32(furthestPath + 1) < npath ? furthestPath + 1 : npath;
+ uint32 size = npath > orig ? npath - orig : 0;
+ if (req + size > maxPath)
+ size = maxPath-req;
+
+ if (size)
+ memmove(path + req, path + orig, size * sizeof(dtPolyRef));
+
+ // Store visited
+ for (uint32 i = 0; i < req; ++i)
+ path[i] = visited[(nvisited - 1) - i];
+
+ return req+size;
+}
+
+bool PathGenerator::GetSteerTarget(float const* startPos, float const* endPos,
+ float minTargetDist, dtPolyRef const* path, uint32 pathSize,
+ float* steerPos, unsigned char& steerPosFlag, dtPolyRef& steerPosRef)
+{
+ // Find steer target.
+ static const uint32 MAX_STEER_POINTS = 3;
+ float steerPath[MAX_STEER_POINTS*VERTEX_SIZE];
+ unsigned char steerPathFlags[MAX_STEER_POINTS];
+ dtPolyRef steerPathPolys[MAX_STEER_POINTS];
+ uint32 nsteerPath = 0;
+ dtStatus dtResult = _navMeshQuery->findStraightPath(startPos, endPos, path, pathSize,
+ steerPath, steerPathFlags, steerPathPolys, (int*)&nsteerPath, MAX_STEER_POINTS);
+ if (!nsteerPath || DT_SUCCESS != dtResult)
+ return false;
+
+ // Find vertex far enough to steer to.
+ uint32 ns = 0;
+ while (ns < nsteerPath)
+ {
+ // Stop at Off-Mesh link or when point is further than slop away.
+ if ((steerPathFlags[ns] & DT_STRAIGHTPATH_OFFMESH_CONNECTION) ||
+ !InRangeYZX(&steerPath[ns*VERTEX_SIZE], startPos, minTargetDist, 1000.0f))
+ break;
+ ns++;
+ }
+ // Failed to find good point to steer to.
+ if (ns >= nsteerPath)
+ return false;
+
+ dtVcopy(steerPos, &steerPath[ns*VERTEX_SIZE]);
+ steerPos[1] = startPos[1]; // keep Z value
+ steerPosFlag = steerPathFlags[ns];
+ steerPosRef = steerPathPolys[ns];
+
+ return true;
+}
+
+dtStatus PathGenerator::FindSmoothPath(float const* startPos, float const* endPos,
+ dtPolyRef const* polyPath, uint32 polyPathSize,
+ float* smoothPath, int* smoothPathSize, uint32 maxSmoothPathSize)
+{
+ *smoothPathSize = 0;
+ uint32 nsmoothPath = 0;
+
+ dtPolyRef polys[MAX_PATH_LENGTH];
+ memcpy(polys, polyPath, sizeof(dtPolyRef)*polyPathSize);
+ uint32 npolys = polyPathSize;
+
+ float iterPos[VERTEX_SIZE], targetPos[VERTEX_SIZE];
+ if (DT_SUCCESS != _navMeshQuery->closestPointOnPolyBoundary(polys[0], startPos, iterPos))
+ return DT_FAILURE;
+
+ if (DT_SUCCESS != _navMeshQuery->closestPointOnPolyBoundary(polys[npolys-1], endPos, targetPos))
+ return DT_FAILURE;
+
+ dtVcopy(&smoothPath[nsmoothPath*VERTEX_SIZE], iterPos);
+ nsmoothPath++;
+
+ // Move towards target a small advancement at a time until target reached or
+ // when ran out of memory to store the path.
+ while (npolys && nsmoothPath < maxSmoothPathSize)
+ {
+ // Find location to steer towards.
+ float steerPos[VERTEX_SIZE];
+ unsigned char steerPosFlag;
+ dtPolyRef steerPosRef = INVALID_POLYREF;
+
+ if (!GetSteerTarget(iterPos, targetPos, SMOOTH_PATH_SLOP, polys, npolys, steerPos, steerPosFlag, steerPosRef))
+ break;
+
+ bool endOfPath = (steerPosFlag & DT_STRAIGHTPATH_END);
+ bool offMeshConnection = (steerPosFlag & DT_STRAIGHTPATH_OFFMESH_CONNECTION);
+
+ // Find movement delta.
+ float delta[VERTEX_SIZE];
+ dtVsub(delta, steerPos, iterPos);
+ float len = dtSqrt(dtVdot(delta,delta));
+ // If the steer target is end of path or off-mesh link, do not move past the location.
+ if ((endOfPath || offMeshConnection) && len < SMOOTH_PATH_STEP_SIZE)
+ len = 1.0f;
+ else
+ len = SMOOTH_PATH_STEP_SIZE / len;
+
+ float moveTgt[VERTEX_SIZE];
+ dtVmad(moveTgt, iterPos, delta, len);
+
+ // Move
+ float result[VERTEX_SIZE];
+ const static uint32 MAX_VISIT_POLY = 16;
+ dtPolyRef visited[MAX_VISIT_POLY];
+
+ uint32 nvisited = 0;
+ _navMeshQuery->moveAlongSurface(polys[0], iterPos, moveTgt, &_filter, result, visited, (int*)&nvisited, MAX_VISIT_POLY);
+ npolys = FixupCorridor(polys, npolys, MAX_PATH_LENGTH, visited, nvisited);
+
+ _navMeshQuery->getPolyHeight(polys[0], result, &result[1]);
+ result[1] += 0.5f;
+ dtVcopy(iterPos, result);
+
+ // Handle end of path and off-mesh links when close enough.
+ if (endOfPath && InRangeYZX(iterPos, steerPos, SMOOTH_PATH_SLOP, 1.0f))
+ {
+ // Reached end of path.
+ dtVcopy(iterPos, targetPos);
+ if (nsmoothPath < maxSmoothPathSize)
+ {
+ dtVcopy(&smoothPath[nsmoothPath*VERTEX_SIZE], iterPos);
+ nsmoothPath++;
+ }
+ break;
+ }
+ else if (offMeshConnection && InRangeYZX(iterPos, steerPos, SMOOTH_PATH_SLOP, 1.0f))
+ {
+ // Advance the path up to and over the off-mesh connection.
+ dtPolyRef prevRef = INVALID_POLYREF;
+ dtPolyRef polyRef = polys[0];
+ uint32 npos = 0;
+ while (npos < npolys && polyRef != steerPosRef)
+ {
+ prevRef = polyRef;
+ polyRef = polys[npos];
+ npos++;
+ }
+
+ for (uint32 i = npos; i < npolys; ++i)
+ polys[i-npos] = polys[i];
+
+ npolys -= npos;
+
+ // Handle the connection.
+ float startPos[VERTEX_SIZE], endPos[VERTEX_SIZE];
+ if (DT_SUCCESS == _navMesh->getOffMeshConnectionPolyEndPoints(prevRef, polyRef, startPos, endPos))
+ {
+ if (nsmoothPath < maxSmoothPathSize)
+ {
+ dtVcopy(&smoothPath[nsmoothPath*VERTEX_SIZE], startPos);
+ nsmoothPath++;
+ }
+ // Move position at the other side of the off-mesh link.
+ dtVcopy(iterPos, endPos);
+ _navMeshQuery->getPolyHeight(polys[0], iterPos, &iterPos[1]);
+ iterPos[1] += 0.5f;
+ }
+ }
+
+ // Store results.
+ if (nsmoothPath < maxSmoothPathSize)
+ {
+ dtVcopy(&smoothPath[nsmoothPath*VERTEX_SIZE], iterPos);
+ nsmoothPath++;
+ }
+ }
+
+ *smoothPathSize = nsmoothPath;
+
+ // this is most likely a loop
+ return nsmoothPath < MAX_POINT_PATH_LENGTH ? DT_SUCCESS : DT_FAILURE;
+}
+
+bool PathGenerator::InRangeYZX(const float* v1, const float* v2, float r, float h) const
+{
+ const float dx = v2[0] - v1[0];
+ const float dy = v2[1] - v1[1]; // elevation
+ const float dz = v2[2] - v1[2];
+ return (dx * dx + dz * dz) < r * r && fabsf(dy) < h;
+}
+
+bool PathGenerator::InRange(Vector3 const& p1, Vector3 const& p2, float r, float h) const
+{
+ Vector3 d = p1 - p2;
+ return (d.x * d.x + d.y * d.y) < r * r && fabsf(d.z) < h;
+}
+
+float PathGenerator::Dist3DSqr(Vector3 const& p1, Vector3 const& p2) const
+{
+ return (p1 - p2).squaredLength();
+}
diff --git a/src/server/game/Movement/PathGenerator.h b/src/server/game/Movement/PathGenerator.h
new file mode 100644
index 00000000000..a20f900b584
--- /dev/null
+++ b/src/server/game/Movement/PathGenerator.h
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2005-2011 MaNGOS <http://getmangos.com/>
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _PATH_GENERATOR_H
+#define _PATH_GENERATOR_H
+
+#include "SharedDefines.h"
+#include "DetourNavMesh.h"
+#include "DetourNavMeshQuery.h"
+#include "MoveSplineInitArgs.h"
+
+using Movement::Vector3;
+using Movement::PointsArray;
+
+class Unit;
+
+// 74*4.0f=296y number_of_points*interval = max_path_len
+// this is way more than actual evade range
+// I think we can safely cut those down even more
+#define MAX_PATH_LENGTH 74
+#define MAX_POINT_PATH_LENGTH 74
+
+#define SMOOTH_PATH_STEP_SIZE 4.0f
+#define SMOOTH_PATH_SLOP 0.3f
+
+#define VERTEX_SIZE 3
+#define INVALID_POLYREF 0
+
+enum PathType
+{
+ PATHFIND_BLANK = 0x00, // path not built yet
+ PATHFIND_NORMAL = 0x01, // normal path
+ PATHFIND_SHORTCUT = 0x02, // travel through obstacles, terrain, air, etc (old behavior)
+ PATHFIND_INCOMPLETE = 0x04, // we have partial path to follow - getting closer to target
+ PATHFIND_NOPATH = 0x08, // no valid path at all or error in generating one
+ PATHFIND_NOT_USING_PATH = 0x10, // used when we are either flying/swiming or on map w/o mmaps
+ PATHFIND_SHORT = 0x20, // path is longer or equal to its limited path length
+};
+
+class PathGenerator
+{
+ public:
+ PathGenerator(Unit const* owner);
+ ~PathGenerator();
+
+ // Calculate the path from owner to given destination
+ // return: true if new path was calculated, false otherwise (no change needed)
+ bool CalculatePath(float destX, float destY, float destZ, bool forceDest = false);
+
+ // option setters - use optional
+ void SetUseStraightPath(bool useStraightPath) { _useStraightPath = useStraightPath; };
+ void SetPathLengthLimit(float distance) { _pointPathLimit = std::min<uint32>(uint32(distance/SMOOTH_PATH_STEP_SIZE), MAX_POINT_PATH_LENGTH); };
+
+ // result getters
+ Vector3 const& GetStartPosition() const { return _startPosition; }
+ Vector3 const& GetEndPosition() const { return _endPosition; }
+ Vector3 const& GetActualEndPosition() const { return _actualEndPosition; }
+
+ PointsArray& GetPath() { return _pathPoints; }
+ PathType GetPathType() const { return _type; }
+
+ private:
+
+ dtPolyRef _pathPolyRefs[MAX_PATH_LENGTH]; // array of detour polygon references
+ uint32 _polyLength; // number of polygons in the path
+
+ PointsArray _pathPoints; // our actual (x,y,z) path to the target
+ PathType _type; // tells what kind of path this is
+
+ bool _useStraightPath; // type of path will be generated
+ bool _forceDestination; // when set, we will always arrive at given point
+ uint32 _pointPathLimit; // limit point path size; min(this, MAX_POINT_PATH_LENGTH)
+
+ Vector3 _startPosition; // {x, y, z} of current location
+ Vector3 _endPosition; // {x, y, z} of the destination
+ Vector3 _actualEndPosition;// {x, y, z} of the closest possible point to given destination
+
+ Unit const* const _sourceUnit; // the unit that is moving
+ dtNavMesh const* _navMesh; // the nav mesh
+ dtNavMeshQuery const* _navMeshQuery; // the nav mesh query used to find the path
+
+ dtQueryFilter _filter; // use single filter for all movements, update it when needed
+
+ void SetStartPosition(Vector3 Point) { _startPosition = Point; }
+ void SetEndPosition(Vector3 Point) { _actualEndPosition = Point; _endPosition = Point; }
+ void SetActualEndPosition(Vector3 Point) { _actualEndPosition = Point; }
+ void NormalizePath();
+
+ void Clear()
+ {
+ _polyLength = 0;
+ _pathPoints.clear();
+ }
+
+ bool InRange(Vector3 const& p1, Vector3 const& p2, float r, float h) const;
+ float Dist3DSqr(Vector3 const& p1, Vector3 const& p2) const;
+ bool InRangeYZX(float const* v1, float const* v2, float r, float h) const;
+
+ dtPolyRef GetPathPolyByPosition(dtPolyRef const* polyPath, uint32 polyPathSize, float const* Point, float* Distance = NULL) const;
+ dtPolyRef GetPolyByLocation(float const* Point, float* Distance) const;
+ bool HaveTile(Vector3 const& p) const;
+
+ void BuildPolyPath(Vector3 const& startPos, Vector3 const& endPos);
+ void BuildPointPath(float const* startPoint, float const* endPoint);
+ void BuildShortcut();
+
+ NavTerrain GetNavTerrain(float x, float y, float z);
+ void CreateFilter();
+ void UpdateFilter();
+
+ // smooth path aux functions
+ uint32 FixupCorridor(dtPolyRef* path, uint32 npath, uint32 maxPath, dtPolyRef const* visited, uint32 nvisited);
+ bool GetSteerTarget(float const* startPos, float const* endPos, float minTargetDist, dtPolyRef const* path, uint32 pathSize, float* steerPos,
+ unsigned char& steerPosFlag, dtPolyRef& steerPosRef);
+ dtStatus FindSmoothPath(float const* startPos, float const* endPos,
+ dtPolyRef const* polyPath, uint32 polyPathSize,
+ float* smoothPath, int* smoothPathSize, uint32 smoothPathMaxSize);
+};
+
+#endif
diff --git a/src/server/game/Movement/Spline/MoveSpline.cpp b/src/server/game/Movement/Spline/MoveSpline.cpp
index 1e96cd00fd3..f6b1c07a927 100644
--- a/src/server/game/Movement/Spline/MoveSpline.cpp
+++ b/src/server/game/Movement/Spline/MoveSpline.cpp
@@ -32,7 +32,7 @@ Location MoveSpline::ComputePosition() const
ASSERT(Initialized());
float u = 1.f;
- int32 seg_time = spline.length(point_Idx,point_Idx+1);
+ int32 seg_time = spline.length(point_Idx, point_Idx+1);
if (seg_time > 0)
u = (time_passed - spline.length(point_Idx)) / (float)seg_time;
Location c;
@@ -103,7 +103,7 @@ struct FallInitializer
float start_elevation;
inline int32 operator()(Spline<int32>& s, int32 i)
{
- return Movement::computeFallTime(start_elevation - s.getPoint(i+1).z,false) * 1000.f;
+ return Movement::computeFallTime(start_elevation - s.getPoint(i+1).z, false) * 1000.f;
}
};
@@ -125,7 +125,7 @@ struct CommonInitializer
void MoveSpline::init_spline(const MoveSplineInitArgs& args)
{
- const SplineBase::EvaluationMode modes[2] = {SplineBase::ModeLinear,SplineBase::ModeCatmullrom};
+ const SplineBase::EvaluationMode modes[2] = {SplineBase::ModeLinear, SplineBase::ModeCatmullrom};
if (args.flags.cyclic)
{
uint32 cyclic_point = 0;
diff --git a/src/server/game/Movement/Spline/MoveSpline.h b/src/server/game/Movement/Spline/MoveSpline.h
index 46611e58447..b18166ea615 100644
--- a/src/server/game/Movement/Spline/MoveSpline.h
+++ b/src/server/game/Movement/Spline/MoveSpline.h
@@ -27,7 +27,7 @@ namespace Movement
struct Location : public Vector3
{
Location() : orientation(0) {}
- Location(float x, float y, float z, float o) : Vector3(x,y,z), orientation(o) {}
+ Location(float x, float y, float z, float o) : Vector3(x, y, z), orientation(o) {}
Location(const Vector3& v) : Vector3(v), orientation(0) {}
Location(const Vector3& v, float o) : Vector3(v), orientation(o) {}
@@ -78,18 +78,17 @@ namespace Movement
UpdateResult _updateState(int32& ms_time_diff);
int32 next_timestamp() const { return spline.length(point_Idx+1); }
int32 segment_time_elapsed() const { return next_timestamp()-time_passed; }
- int32 Duration() const { return spline.length(); }
int32 timeElapsed() const { return Duration() - time_passed; }
int32 timePassed() const { return time_passed; }
public:
+ int32 Duration() const { return spline.length(); }
const MySpline& _Spline() const { return spline; }
int32 _currentSplineIdx() const { return point_Idx; }
void _Finalize();
void _Interrupt() { splineflags.done = true;}
public:
-
void Initialize(const MoveSplineInitArgs&);
bool Initialized() const { return !spline.empty(); }
@@ -101,14 +100,14 @@ namespace Movement
ASSERT(Initialized());
do
handler(_updateState(difftime));
- while(difftime > 0);
+ while (difftime > 0);
}
void updateState(int32 difftime)
{
ASSERT(Initialized());
do _updateState(difftime);
- while(difftime > 0);
+ while (difftime > 0);
}
Location ComputePosition() const;
diff --git a/src/server/game/Movement/Spline/MoveSplineFlag.h b/src/server/game/Movement/Spline/MoveSplineFlag.h
index f3f436c9d00..ec7eaf7e763 100644
--- a/src/server/game/Movement/Spline/MoveSplineFlag.h
+++ b/src/server/game/Movement/Spline/MoveSplineFlag.h
@@ -27,7 +27,7 @@ namespace Movement
#if defined( __GNUC__ )
#pragma pack(1)
#else
-#pragma pack(push,1)
+#pragma pack(push, 1)
#endif
class MoveSplineFlag
diff --git a/src/server/game/Movement/Spline/MoveSplineInit.cpp b/src/server/game/Movement/Spline/MoveSplineInit.cpp
index b327d0d0cd6..1e4d4950ea0 100644
--- a/src/server/game/Movement/Spline/MoveSplineInit.cpp
+++ b/src/server/game/Movement/Spline/MoveSplineInit.cpp
@@ -58,38 +58,38 @@ namespace Movement
return MOVE_RUN;
}
- void MoveSplineInit::Launch()
+ int32 MoveSplineInit::Launch()
{
- MoveSpline& move_spline = *unit.movespline;
+ MoveSpline& move_spline = *unit->movespline;
bool transport = false;
- Location real_position(unit.GetPositionX(), unit.GetPositionY(), unit.GetPositionZMinusOffset(), unit.GetOrientation());
+ Location real_position(unit->GetPositionX(), unit->GetPositionY(), unit->GetPositionZMinusOffset(), unit->GetOrientation());
// Elevators also use MOVEMENTFLAG_ONTRANSPORT but we do not keep track of their position changes
- if (unit.HasUnitMovementFlag(MOVEMENTFLAG_ONTRANSPORT) && unit.GetTransGUID())
+ if (unit->HasUnitMovementFlag(MOVEMENTFLAG_ONTRANSPORT) && unit->GetTransGUID())
{
transport = true;
- real_position.x = unit.GetTransOffsetX();
- real_position.y = unit.GetTransOffsetY();
- real_position.z = unit.GetTransOffsetZ();
- real_position.orientation = unit.GetTransOffsetO();
+ real_position.x = unit->GetTransOffsetX();
+ real_position.y = unit->GetTransOffsetY();
+ real_position.z = unit->GetTransOffsetZ();
+ real_position.orientation = unit->GetTransOffsetO();
}
// there is a big chance that current position is unknown if current state is not finalized, need compute it
- // this also allows calculate spline position and update map position in much greater intervals
+ // this also allows CalculatePath spline position and update map position in much greater intervals
// Don't compute for transport movement if the unit is in a motion between two transports
if (!move_spline.Finalized() && move_spline.onTransport == transport)
real_position = move_spline.ComputePosition();
// should i do the things that user should do? - no.
if (args.path.empty())
- return;
+ return 0;
// corrent first vertex
args.path[0] = real_position;
args.initialOrientation = real_position.orientation;
move_spline.onTransport = transport;
- uint32 moveFlags = unit.m_movementInfo.GetMovementFlags();
+ uint32 moveFlags = unit->m_movementInfo.GetMovementFlags();
if (args.flags.walkmode)
moveFlags |= MOVEMENTFLAG_WALKING;
else
@@ -98,39 +98,41 @@ namespace Movement
moveFlags |= (MOVEMENTFLAG_SPLINE_ENABLED|MOVEMENTFLAG_FORWARD);
if (!args.HasVelocity)
- args.velocity = unit.GetSpeed(SelectSpeedType(moveFlags));
+ args.velocity = unit->GetSpeed(SelectSpeedType(moveFlags));
- if (!args.Validate(&unit))
- return;
+ if (!args.Validate(unit))
+ return 0;
if (moveFlags & MOVEMENTFLAG_ROOT)
moveFlags &= ~MOVEMENTFLAG_MASK_MOVING;
- unit.m_movementInfo.SetMovementFlags((MovementFlags)moveFlags);
+ unit->m_movementInfo.SetMovementFlags((MovementFlags)moveFlags);
move_spline.Initialize(args);
WorldPacket data(!transport ? SMSG_MONSTER_MOVE : SMSG_MONSTER_MOVE_TRANSPORT, 64);
- data.append(unit.GetPackGUID());
+ data.append(unit->GetPackGUID());
if (transport)
{
- data.appendPackGUID(unit.GetTransGUID());
- data << int8(unit.GetTransSeat());
+ data.appendPackGUID(unit->GetTransGUID());
+ data << int8(unit->GetTransSeat());
}
PacketBuilder::WriteMonsterMove(move_spline, data);
- unit.SendMessageToSet(&data,true);
+ unit->SendMessageToSet(&data,true);
+
+ return move_spline.Duration();
}
- MoveSplineInit::MoveSplineInit(Unit& m) : unit(m)
+ MoveSplineInit::MoveSplineInit(Unit* m) : unit(m)
{
// Elevators also use MOVEMENTFLAG_ONTRANSPORT but we do not keep track of their position changes
- args.TransformForTransport = unit.HasUnitMovementFlag(MOVEMENTFLAG_ONTRANSPORT) && unit.GetTransGUID();
+ args.TransformForTransport = unit->HasUnitMovementFlag(MOVEMENTFLAG_ONTRANSPORT) && unit->GetTransGUID();
// mix existing state into new
- args.flags.walkmode = unit.m_movementInfo.HasMovementFlag(MOVEMENTFLAG_WALKING);
- args.flags.flying = unit.m_movementInfo.HasMovementFlag((MovementFlags)(MOVEMENTFLAG_CAN_FLY|MOVEMENTFLAG_DISABLE_GRAVITY));
+ args.flags.walkmode = unit->m_movementInfo.HasMovementFlag(MOVEMENTFLAG_WALKING);
+ args.flags.flying = unit->m_movementInfo.HasMovementFlag((MovementFlags)(MOVEMENTFLAG_CAN_FLY | MOVEMENTFLAG_DISABLE_GRAVITY));
}
- void MoveSplineInit::SetFacing(const Unit * target)
+ void MoveSplineInit::SetFacing(const Unit* target)
{
args.flags.EnableFacingTarget();
args.facing.target = target->GetGUID();
@@ -140,9 +142,9 @@ namespace Movement
{
if (args.TransformForTransport)
{
- if (Unit* vehicle = unit.GetVehicleBase())
+ if (Unit* vehicle = unit->GetVehicleBase())
angle -= vehicle->GetOrientation();
- else if (Transport* transport = unit.GetTransport())
+ else if (Transport* transport = unit->GetTransport())
angle -= transport->GetOrientation();
}
@@ -150,8 +152,19 @@ namespace Movement
args.flags.EnableFacingAngle();
}
- void MoveSplineInit::MoveTo(Vector3 const& dest)
+ void MoveSplineInit::MoveTo(const Vector3& dest, bool generatePath, bool forceDestination)
{
+ if (generatePath)
+ {
+ PathGenerator path(unit);
+ bool result = path.CalculatePath(dest.x, dest.y, dest.z, forceDestination);
+ if (result && path.GetPathType() & ~PATHFIND_NOPATH)
+ {
+ MovebyPath(path.GetPath());
+ return;
+ }
+ }
+
args.path_Idx_offset = 0;
args.path.resize(2);
TransportPathTransform transform(unit, args.TransformForTransport);
@@ -162,7 +175,7 @@ namespace Movement
{
if (_transformForTransport)
{
- if (TransportBase* transport = _owner.GetDirectTransport())
+ if (TransportBase* transport = _owner->GetDirectTransport())
{
float unused = 0.0f; // need reference
transport->CalculatePassengerOffset(input.x, input.y, input.z, unused);
diff --git a/src/server/game/Movement/Spline/MoveSplineInit.h b/src/server/game/Movement/Spline/MoveSplineInit.h
index 8ebac4aec06..441bad66142 100644
--- a/src/server/game/Movement/Spline/MoveSplineInit.h
+++ b/src/server/game/Movement/Spline/MoveSplineInit.h
@@ -20,6 +20,7 @@
#define TRINITYSERVER_MOVESPLINEINIT_H
#include "MoveSplineInitArgs.h"
+#include "PathGenerator.h"
class Unit;
@@ -37,12 +38,12 @@ namespace Movement
class TransportPathTransform
{
public:
- TransportPathTransform(Unit& owner, bool transformForTransport)
+ TransportPathTransform(Unit* owner, bool transformForTransport)
: _owner(owner), _transformForTransport(transformForTransport) { }
Vector3 operator()(Vector3 input);
private:
- Unit& _owner;
+ Unit* _owner;
bool _transformForTransport;
};
@@ -52,11 +53,11 @@ namespace Movement
{
public:
- explicit MoveSplineInit(Unit& m);
+ explicit MoveSplineInit(Unit* m);
/* Final pass of initialization that launches spline movement.
*/
- void Launch();
+ int32 Launch();
/* Adds movement by parabolic trajectory
* @param amplitude - the maximum height of parabola, value could be negative and positive
@@ -75,7 +76,7 @@ namespace Movement
*/
void SetFacing(float angle);
void SetFacing(Vector3 const& point);
- void SetFacing(const Unit * target);
+ void SetFacing(const Unit* target);
/* Initializes movement by path
* @param path - array of points, shouldn't be empty
@@ -83,10 +84,10 @@ namespace Movement
*/
void MovebyPath(const PointsArray& path, int32 pointId = 0);
- /* Initializes simple A to B mition, A is current unit's position, B is destination
+ /* Initializes simple A to B motion, A is current unit's position, B is destination
*/
- void MoveTo(const Vector3& destination);
- void MoveTo(float x, float y, float z);
+ void MoveTo(const Vector3& destination, bool generatePath = true, bool forceDestination = false);
+ void MoveTo(float x, float y, float z, bool generatePath = true, bool forceDestination = false);
/* Sets Id of fisrt point of the path. When N-th path point will be done ILisener will notify that pointId + N done
* Needed for waypoint movement where path splitten into parts
@@ -137,7 +138,7 @@ namespace Movement
protected:
MoveSplineInitArgs args;
- Unit& unit;
+ Unit* unit;
};
inline void MoveSplineInit::SetFly() { args.flags.EnableFlying(); }
@@ -158,9 +159,9 @@ namespace Movement
std::transform(controls.begin(), controls.end(), args.path.begin(), TransportPathTransform(unit, args.TransformForTransport));
}
- inline void MoveSplineInit::MoveTo(float x, float y, float z)
+ inline void MoveSplineInit::MoveTo(float x, float y, float z, bool generatePath, bool forceDestination)
{
- MoveTo(G3D::Vector3(x, y, z));
+ MoveTo(G3D::Vector3(x, y, z), generatePath, forceDestination);
}
inline void MoveSplineInit::SetParabolic(float amplitude, float time_shift)
diff --git a/src/server/game/Movement/Spline/MoveSplineInitArgs.h b/src/server/game/Movement/Spline/MoveSplineInitArgs.h
index 32045629c9f..4a086094c3f 100644
--- a/src/server/game/Movement/Spline/MoveSplineInitArgs.h
+++ b/src/server/game/Movement/Spline/MoveSplineInitArgs.h
@@ -31,7 +31,7 @@ namespace Movement
union FacingInfo
{
struct{
- float x,y,z;
+ float x, y, z;
}f;
uint64 target;
float angle;
diff --git a/src/server/game/Movement/Spline/MovementPacketBuilder.cpp b/src/server/game/Movement/Spline/MovementPacketBuilder.cpp
index d747d6f69a6..7133fc50e9f 100644
--- a/src/server/game/Movement/Spline/MovementPacketBuilder.cpp
+++ b/src/server/game/Movement/Spline/MovementPacketBuilder.cpp
@@ -141,7 +141,7 @@ namespace Movement
void PacketBuilder::WriteCreate(const MoveSpline& move_spline, ByteBuffer& data)
{
- //WriteClientStatus(mov,data);
+ //WriteClientStatus(mov, data);
//data.append<float>(&mov.m_float_values[SpeedWalk], SpeedMaxCount);
//if (mov.SplineEnabled())
{
diff --git a/src/server/game/Movement/Spline/MovementUtil.cpp b/src/server/game/Movement/Spline/MovementUtil.cpp
index b69d4b39e19..99413384dd8 100644
--- a/src/server/game/Movement/Spline/MovementUtil.cpp
+++ b/src/server/game/Movement/Spline/MovementUtil.cpp
@@ -103,39 +103,39 @@ namespace Movement
char const* g_MovementFlag_names[] =
{
- STR(Forward ),// 0x00000001,
- STR(Backward ),// 0x00000002,
- STR(Strafe_Left ),// 0x00000004,
- STR(Strafe_Right ),// 0x00000008,
- STR(Turn_Left ),// 0x00000010,
- STR(Turn_Right ),// 0x00000020,
- STR(Pitch_Up ),// 0x00000040,
- STR(Pitch_Down ),// 0x00000080,
-
- STR(Walk ),// 0x00000100, // Walking
- STR(Ontransport ),// 0x00000200,
- STR(Levitation ),// 0x00000400,
- STR(Root ),// 0x00000800,
- STR(Falling ),// 0x00001000,
- STR(Fallingfar ),// 0x00002000,
- STR(Pendingstop ),// 0x00004000,
- STR(PendingSTRafestop ),// 0x00008000,
- STR(Pendingforward ),// 0x00010000,
- STR(Pendingbackward ),// 0x00020000,
- STR(PendingSTRafeleft ),// 0x00040000,
- STR(PendingSTRaferight ),// 0x00080000,
- STR(Pendingroot ),// 0x00100000,
- STR(Swimming ),// 0x00200000, // Appears With Fly Flag Also
- STR(Ascending ),// 0x00400000, // Swim Up Also
- STR(Descending ),// 0x00800000, // Swim Down Also
- STR(Can_Fly ),// 0x01000000, // Can Fly In 3.3?
- STR(Flying ),// 0x02000000, // Actual Flying Mode
- STR(Spline_Elevation ),// 0x04000000, // Used For Flight Paths
- STR(Spline_Enabled ),// 0x08000000, // Used For Flight Paths
- STR(Waterwalking ),// 0x10000000, // Prevent Unit From Falling Through Water
- STR(Safe_Fall ),// 0x20000000, // Active Rogue Safe Fall Spell (Passive)
- STR(Hover ),// 0x40000000
- STR(Unknown13 ),// 0x80000000
+ STR(Forward ), // 0x00000001,
+ STR(Backward ), // 0x00000002,
+ STR(Strafe_Left ), // 0x00000004,
+ STR(Strafe_Right ), // 0x00000008,
+ STR(Turn_Left ), // 0x00000010,
+ STR(Turn_Right ), // 0x00000020,
+ STR(Pitch_Up ), // 0x00000040,
+ STR(Pitch_Down ), // 0x00000080,
+
+ STR(Walk ), // 0x00000100, // Walking
+ STR(Ontransport ), // 0x00000200,
+ STR(Levitation ), // 0x00000400,
+ STR(Root ), // 0x00000800,
+ STR(Falling ), // 0x00001000,
+ STR(Fallingfar ), // 0x00002000,
+ STR(Pendingstop ), // 0x00004000,
+ STR(PendingSTRafestop ), // 0x00008000,
+ STR(Pendingforward ), // 0x00010000,
+ STR(Pendingbackward ), // 0x00020000,
+ STR(PendingSTRafeleft ), // 0x00040000,
+ STR(PendingSTRaferight ), // 0x00080000,
+ STR(Pendingroot ), // 0x00100000,
+ STR(Swimming ), // 0x00200000, // Appears With Fly Flag Also
+ STR(Ascending ), // 0x00400000, // Swim Up Also
+ STR(Descending ), // 0x00800000, // Swim Down Also
+ STR(Can_Fly ), // 0x01000000, // Can Fly In 3.3?
+ STR(Flying ), // 0x02000000, // Actual Flying Mode
+ STR(Spline_Elevation ), // 0x04000000, // Used For Flight Paths
+ STR(Spline_Enabled ), // 0x08000000, // Used For Flight Paths
+ STR(Waterwalking ), // 0x10000000, // Prevent Unit From Falling Through Water
+ STR(Safe_Fall ), // 0x20000000, // Active Rogue Safe Fall Spell (Passive)
+ STR(Hover ), // 0x40000000
+ STR(Unknown13 ), // 0x80000000
STR(Unk1 ),
STR(Unk2 ),
STR(Unk3 ),
@@ -156,38 +156,38 @@ namespace Movement
char const* g_SplineFlag_names[32] =
{
- STR(AnimBit1 ),// 0x00000001,
- STR(AnimBit2 ),// 0x00000002,
- STR(AnimBit3 ),// 0x00000004,
- STR(AnimBit4 ),// 0x00000008,
- STR(AnimBit5 ),// 0x00000010,
- STR(AnimBit6 ),// 0x00000020,
- STR(AnimBit7 ),// 0x00000040,
- STR(AnimBit8 ),// 0x00000080,
- STR(Done ),// 0x00000100,
- STR(Falling ),// 0x00000200, // Not Compartible With Trajectory Movement
- STR(No_Spline ),// 0x00000400,
- STR(Trajectory ),// 0x00000800, // Not Compartible With Fall Movement
- STR(Walkmode ),// 0x00001000,
- STR(Flying ),// 0x00002000, // Smooth Movement(Catmullrom Interpolation Mode), Flying Animation
- STR(Knockback ),// 0x00004000, // Model Orientation Fixed
- STR(Final_Point ),// 0x00008000,
- STR(Final_Target ),// 0x00010000,
- STR(Final_Angle ),// 0x00020000,
- STR(Catmullrom ),// 0x00040000, // Used Catmullrom Interpolation Mode
- STR(Cyclic ),// 0x00080000, // Movement By Cycled Spline
- STR(Enter_Cycle ),// 0x00100000, // Everytime Appears With Cyclic Flag In Monster Move Packet
- STR(Animation ),// 0x00200000, // Animationid (0...3), Uint32 Time, Not Compartible With Trajectory And Fall Movement
- STR(Unknown4 ),// 0x00400000, // Disables Movement By Path
- STR(Unknown5 ),// 0x00800000,
- STR(Unknown6 ),// 0x01000000,
- STR(Unknown7 ),// 0x02000000,
- STR(Unknown8 ),// 0x04000000,
- STR(OrientationInversed ),// 0x08000000, // Appears With Runmode Flag, Nodes ),// 1, Handles Orientation
- STR(Unknown10 ),// 0x10000000,
- STR(Unknown11 ),// 0x20000000,
- STR(Unknown12 ),// 0x40000000,
- STR(Unknown13 ),// 0x80000000,
+ STR(AnimBit1 ), // 0x00000001,
+ STR(AnimBit2 ), // 0x00000002,
+ STR(AnimBit3 ), // 0x00000004,
+ STR(AnimBit4 ), // 0x00000008,
+ STR(AnimBit5 ), // 0x00000010,
+ STR(AnimBit6 ), // 0x00000020,
+ STR(AnimBit7 ), // 0x00000040,
+ STR(AnimBit8 ), // 0x00000080,
+ STR(Done ), // 0x00000100,
+ STR(Falling ), // 0x00000200, // Not Compartible With Trajectory Movement
+ STR(No_Spline ), // 0x00000400,
+ STR(Trajectory ), // 0x00000800, // Not Compartible With Fall Movement
+ STR(Walkmode ), // 0x00001000,
+ STR(Flying ), // 0x00002000, // Smooth Movement(Catmullrom Interpolation Mode), Flying Animation
+ STR(Knockback ), // 0x00004000, // Model Orientation Fixed
+ STR(Final_Point ), // 0x00008000,
+ STR(Final_Target ), // 0x00010000,
+ STR(Final_Angle ), // 0x00020000,
+ STR(Catmullrom ), // 0x00040000, // Used Catmullrom Interpolation Mode
+ STR(Cyclic ), // 0x00080000, // Movement By Cycled Spline
+ STR(Enter_Cycle ), // 0x00100000, // Everytime Appears With Cyclic Flag In Monster Move Packet
+ STR(Animation ), // 0x00200000, // Animationid (0...3), Uint32 Time, Not Compartible With Trajectory And Fall Movement
+ STR(Unknown4 ), // 0x00400000, // Disables Movement By Path
+ STR(Unknown5 ), // 0x00800000,
+ STR(Unknown6 ), // 0x01000000,
+ STR(Unknown7 ), // 0x02000000,
+ STR(Unknown8 ), // 0x04000000,
+ STR(OrientationInversed ), // 0x08000000, // Appears With Runmode Flag, Nodes ), // 1, Handles Orientation
+ STR(Unknown10 ), // 0x10000000,
+ STR(Unknown11 ), // 0x20000000,
+ STR(Unknown12 ), // 0x40000000,
+ STR(Unknown13 ), // 0x80000000,
};
template<class Flags, int N>
diff --git a/src/server/game/Movement/Spline/Spline.cpp b/src/server/game/Movement/Spline/Spline.cpp
index 6970acf5415..887541e2289 100644
--- a/src/server/game/Movement/Spline/Spline.cpp
+++ b/src/server/game/Movement/Spline/Spline.cpp
@@ -59,7 +59,7 @@ SplineBase::InitMethtod SplineBase::initializers[SplineBase::ModesEnd] =
using G3D::Matrix4;
static const Matrix4 s_catmullRomCoeffs(
- -0.5f, 1.5f,-1.5f, 0.5f,
+ -0.5f, 1.5f, -1.5f, 0.5f,
1.f, -2.5f, 2.f, -0.5f,
-0.5f, 0.f, 0.5f, 0.f,
0.f, 1.f, 0.f, 0.f);
@@ -222,7 +222,7 @@ void SplineBase::InitLinear(const Vector3* controls, index_type count, bool cycl
points.resize(real_size);
- memcpy(&points[0],controls, sizeof(Vector3) * count);
+ memcpy(&points[0], controls, sizeof(Vector3) * count);
// first and last two indexes are space for special 'virtual points'
// these points are required for proper C_Evaluate and C_Evaluate_Derivative methtod work
@@ -244,7 +244,7 @@ void SplineBase::InitCatmullRom(const Vector3* controls, index_type count, bool
int lo_index = 1;
int high_index = lo_index + count - 1;
- memcpy(&points[lo_index],controls, sizeof(Vector3) * count);
+ memcpy(&points[lo_index], controls, sizeof(Vector3) * count);
// first and last two indexes are space for special 'virtual points'
// these points are required for proper C_Evaluate and C_Evaluate_Derivative methtod work
@@ -274,7 +274,7 @@ void SplineBase::InitBezier3(const Vector3* controls, index_type count, bool /*c
index_type t = c / 3u;
points.resize(c);
- memcpy(&points[0],controls, sizeof(Vector3) * c);
+ memcpy(&points[0], controls, sizeof(Vector3) * c);
index_lo = 0;
index_hi = t-1;
diff --git a/src/server/game/Movement/Spline/Spline.h b/src/server/game/Movement/Spline/Spline.h
index 63e61b84579..2a2f3fa8f43 100644
--- a/src/server/game/Movement/Spline/Spline.h
+++ b/src/server/game/Movement/Spline/Spline.h
@@ -61,7 +61,7 @@ protected:
void EvaluateLinear(index_type, float, Vector3&) const;
void EvaluateCatmullRom(index_type, float, Vector3&) const;
void EvaluateBezier3(index_type, float, Vector3&) const;
- typedef void (SplineBase::*EvaluationMethtod)(index_type,float,Vector3&) const;
+ typedef void (SplineBase::*EvaluationMethtod)(index_type, float, Vector3&) const;
static EvaluationMethtod evaluators[ModesEnd];
void EvaluateDerivativeLinear(index_type, float, Vector3&) const;
@@ -91,13 +91,13 @@ public:
@param t - percent of segment length, assumes that t in range [0, 1]
@param Idx - spline segment index, should be in range [first, last)
*/
- void evaluate_percent(index_type Idx, float u, Vector3& c) const {(this->*evaluators[m_mode])(Idx,u,c);}
+ void evaluate_percent(index_type Idx, float u, Vector3& c) const {(this->*evaluators[m_mode])(Idx, u,c);}
/** Caclulates derivation in index Idx, and percent of segment length t
@param Idx - spline segment index, should be in range [first, last)
@param t - percent of spline segment length, assumes that t in range [0, 1]
*/
- void evaluate_derivative(index_type Idx, float u, Vector3& hermite) const {(this->*derivative_evaluators[m_mode])(Idx,u,hermite);}
+ void evaluate_derivative(index_type Idx, float u, Vector3& hermite) const {(this->*derivative_evaluators[m_mode])(Idx, u,hermite);}
/** Bounds for spline indexes. All indexes should be in range [first, last). */
index_type first() const { return index_lo;}
@@ -119,7 +119,7 @@ public:
would be no harm to have some custom initializers. */
template<class Init> inline void init_spline(Init& initializer)
{
- initializer(m_mode,cyclic,points,index_lo,index_hi);
+ initializer(m_mode, cyclic, points, index_lo, index_hi);
}
void clear();
@@ -156,20 +156,20 @@ public:
/** Calculates the position for given segment Idx, and percent of segment length t
@param t = partial_segment_length / whole_segment_length
@param Idx - spline segment index, should be in range [first, last). */
- void evaluate_percent(index_type Idx, float u, Vector3& c) const { SplineBase::evaluate_percent(Idx,u,c);}
+ void evaluate_percent(index_type Idx, float u, Vector3& c) const { SplineBase::evaluate_percent(Idx, u,c);}
/** Caclulates derivation for index Idx, and percent of segment length t
@param Idx - spline segment index, should be in range [first, last)
@param t - percent of spline segment length, assumes that t in range [0, 1]. */
- void evaluate_derivative(index_type Idx, float u, Vector3& c) const { SplineBase::evaluate_derivative(Idx,u,c);}
+ void evaluate_derivative(index_type Idx, float u, Vector3& c) const { SplineBase::evaluate_derivative(Idx, u,c);}
// Assumes that t in range [0, 1]
index_type computeIndexInBounds(float t) const;
void computeIndex(float t, index_type& out_idx, float& out_u) const;
/** Initializes spline. Don't call other methods while spline not initialized. */
- void init_spline(const Vector3 * controls, index_type count, EvaluationMode m) { SplineBase::init_spline(controls,count,m);}
- void init_cyclic_spline(const Vector3 * controls, index_type count, EvaluationMode m, index_type cyclic_point) { SplineBase::init_cyclic_spline(controls,count,m,cyclic_point);}
+ void init_spline(const Vector3 * controls, index_type count, EvaluationMode m) { SplineBase::init_spline(controls, count, m);}
+ void init_cyclic_spline(const Vector3 * controls, index_type count, EvaluationMode m, index_type cyclic_point) { SplineBase::init_cyclic_spline(controls, count, m,cyclic_point);}
/** Initializes lengths with SplineBase::SegLength method. */
void initLengths();
@@ -181,7 +181,7 @@ public:
index_type i = index_lo;
lengths.resize(index_hi+1);
length_type prev_length = 0, new_length = 0;
- while(i < index_hi)
+ while (i < index_hi)
{
new_length = cacher(*this, i);
lengths[++i] = new_length;
diff --git a/src/server/game/Movement/Spline/SplineImpl.h b/src/server/game/Movement/Spline/SplineImpl.h
index eded2d8c903..98f3f5fe079 100644
--- a/src/server/game/Movement/Spline/SplineImpl.h
+++ b/src/server/game/Movement/Spline/SplineImpl.h
@@ -81,7 +81,7 @@ template<typename length_type> void Spline<length_type>::initLengths()
index_type i = index_lo;
length_type length = 0;
lengths.resize(index_hi+1);
- while(i < index_hi )
+ while (i < index_hi)
{
length += SegLength(i);
lengths[++i] = length;
diff --git a/src/server/game/Movement/Waypoints/Path.h b/src/server/game/Movement/Waypoints/Path.h
index 3f78a640384..39f05184cf6 100644
--- a/src/server/game/Movement/Waypoints/Path.h
+++ b/src/server/game/Movement/Waypoints/Path.h
@@ -20,10 +20,12 @@
#define TRINITYCORE_PATH_H
#include "Common.h"
-#include <vector>
+#include <deque>
-struct SimplePathNode
+struct PathNode
{
+ PathNode(): x(0.0f), y(0.0f), z(0.0f) { }
+ PathNode(float _x, float _y, float _z): x(_x), y(_y), z(_z) { }
float x, y, z;
};
template<typename PathElem, typename PathNode = PathElem>
@@ -36,6 +38,20 @@ class Path
void resize(unsigned int sz) { i_nodes.resize(sz); }
void clear() { i_nodes.clear(); }
void erase(uint32 idx) { i_nodes.erase(i_nodes.begin()+idx); }
+ void crop(unsigned int start, unsigned int end)
+ {
+ while(start && !i_nodes.empty())
+ {
+ i_nodes.pop_front();
+ --start;
+ }
+
+ while(end && !i_nodes.empty())
+ {
+ i_nodes.pop_back();
+ --end;
+ }
+ }
float GetTotalLength(uint32 start, uint32 end) const
{
@@ -76,10 +92,9 @@ class Path
void set(size_t idx, PathElem elem) { i_nodes[idx] = elem; }
protected:
- std::vector<PathElem> i_nodes;
+ std::deque<PathElem> i_nodes;
};
-typedef Path<SimplePathNode> SimplePath;
+typedef Path<PathNode> SimplePath;
#endif
-
diff --git a/src/server/game/Movement/Waypoints/WaypointManager.h b/src/server/game/Movement/Waypoints/WaypointManager.h
index 07f855380f9..b17714f1a51 100644
--- a/src/server/game/Movement/Waypoints/WaypointManager.h
+++ b/src/server/game/Movement/Waypoints/WaypointManager.h
@@ -68,4 +68,3 @@ class WaypointMgr
#define sWaypointMgr ACE_Singleton<WaypointMgr, ACE_Null_Mutex>::instance()
#endif
-
diff --git a/src/server/game/OutdoorPvP/OutdoorPvP.cpp b/src/server/game/OutdoorPvP/OutdoorPvP.cpp
index bb7dea76461..93bf7edc7d5 100644
--- a/src/server/game/OutdoorPvP/OutdoorPvP.cpp
+++ b/src/server/game/OutdoorPvP/OutdoorPvP.cpp
@@ -392,7 +392,7 @@ void OutdoorPvP::SendUpdateWorldState(uint32 field, uint32 value)
if (m_sendUpdate)
for (int i = 0; i < 2; ++i)
for (PlayerSet::iterator itr = m_players[i].begin(); itr != m_players[i].end(); ++itr)
- if (Player * const player = ObjectAccessor::FindPlayer(*itr))
+ if (Player* const player = ObjectAccessor::FindPlayer(*itr))
player->SendUpdateWorldState(field, value);
}
@@ -402,7 +402,7 @@ void OPvPCapturePoint::SendUpdateWorldState(uint32 field, uint32 value)
{
// send to all players present in the area
for (PlayerSet::iterator itr = m_activePlayers[team].begin(); itr != m_activePlayers[team].end(); ++itr)
- if (Player * const player = ObjectAccessor::FindPlayer(*itr))
+ if (Player* const player = ObjectAccessor::FindPlayer(*itr))
player->SendUpdateWorldState(field, value);
}
}
@@ -424,7 +424,7 @@ void OPvPCapturePoint::SendObjectiveComplete(uint32 id, uint64 guid)
// send to all players present in the area
for (PlayerSet::iterator itr = m_activePlayers[team].begin(); itr != m_activePlayers[team].end(); ++itr)
- if (Player * const player = ObjectAccessor::FindPlayer(*itr))
+ if (Player* const player = ObjectAccessor::FindPlayer(*itr))
player->KilledMonsterCredit(id, guid);
}
@@ -563,7 +563,7 @@ void OutdoorPvP::BroadcastPacket(WorldPacket &data) const
// This is faster than sWorld->SendZoneMessage
for (uint32 team = 0; team < 2; ++team)
for (PlayerSet::const_iterator itr = m_players[team].begin(); itr != m_players[team].end(); ++itr)
- if (Player * const player = ObjectAccessor::FindPlayer(*itr))
+ if (Player* const player = ObjectAccessor::FindPlayer(*itr))
player->GetSession()->SendPacket(&data);
}
@@ -583,13 +583,13 @@ void OutdoorPvP::TeamCastSpell(TeamId team, int32 spellId)
if (spellId > 0)
{
for (PlayerSet::iterator itr = m_players[team].begin(); itr != m_players[team].end(); ++itr)
- if (Player * const player = ObjectAccessor::FindPlayer(*itr))
+ if (Player* const player = ObjectAccessor::FindPlayer(*itr))
player->CastSpell(player, (uint32)spellId, true);
}
else
{
for (PlayerSet::iterator itr = m_players[team].begin(); itr != m_players[team].end(); ++itr)
- if (Player * const player = ObjectAccessor::FindPlayer(*itr))
+ if (Player* const player = ObjectAccessor::FindPlayer(*itr))
player->RemoveAura((uint32)-spellId); // by stack?
}
}
diff --git a/src/server/game/OutdoorPvP/OutdoorPvP.h b/src/server/game/OutdoorPvP/OutdoorPvP.h
index 5f1607843fb..1e23f87a52e 100644
--- a/src/server/game/OutdoorPvP/OutdoorPvP.h
+++ b/src/server/game/OutdoorPvP/OutdoorPvP.h
@@ -285,7 +285,7 @@ class OutdoorPvP : public ZoneScript
void RegisterZone(uint32 zoneid);
- bool HasPlayer(Player const * player) const;
+ bool HasPlayer(Player const* player) const;
void TeamCastSpell(TeamId team, int32 spellId);
};
diff --git a/src/server/game/Scripting/MapScripts.cpp b/src/server/game/Scripting/MapScripts.cpp
index 6cd6a72a06a..7cee754bba7 100644
--- a/src/server/game/Scripting/MapScripts.cpp
+++ b/src/server/game/Scripting/MapScripts.cpp
@@ -476,7 +476,7 @@ void Map::ScriptsProcess()
// Source or target must be Creature.
if (Creature* cSource = _GetScriptCreatureSourceOrTarget(source, target, step.script))
{
- Unit * unit = (Unit*)cSource;
+ Unit* unit = (Unit*)cSource;
if (step.script->MoveTo.TravelTime != 0)
{
float speed = unit->GetDistance(step.script->MoveTo.DestX, step.script->MoveTo.DestY, step.script->MoveTo.DestZ) / ((float)step.script->MoveTo.TravelTime * 0.001f);
diff --git a/src/server/game/Scripting/ScriptLoader.cpp b/src/server/game/Scripting/ScriptLoader.cpp
index 479979f177f..3da9ac8fade 100644
--- a/src/server/game/Scripting/ScriptLoader.cpp
+++ b/src/server/game/Scripting/ScriptLoader.cpp
@@ -76,6 +76,7 @@ void AddSC_tele_commandscript();
void AddSC_ticket_commandscript();
void AddSC_titles_commandscript();
void AddSC_wp_commandscript();
+void AddSC_mmaps_commandscript();
#ifdef SCRIPTS
//world
@@ -590,6 +591,7 @@ void AddSC_boss_high_astromancer_solarian();
void AddSC_instance_the_eye();
void AddSC_the_eye();
void AddSC_boss_gatewatcher_iron_hand(); //TK The Mechanar
+void AddSC_boss_gatewatcher_gyrokill();
void AddSC_boss_nethermancer_sepethrea();
void AddSC_boss_pathaleon_the_calculator();
void AddSC_boss_mechano_lord_capacitus();
@@ -704,6 +706,7 @@ void AddCommandScripts()
AddSC_ticket_commandscript();
AddSC_titles_commandscript();
AddSC_wp_commandscript();
+ AddSC_mmaps_commandscript();
}
void AddWorldScripts()
@@ -1073,6 +1076,7 @@ void AddOutlandScripts()
AddSC_instance_the_eye();
AddSC_the_eye();
AddSC_boss_gatewatcher_iron_hand(); //TK The Mechanar
+ AddSC_boss_gatewatcher_gyrokill();
AddSC_boss_nethermancer_sepethrea();
AddSC_boss_pathaleon_the_calculator();
AddSC_boss_mechano_lord_capacitus();
diff --git a/src/server/game/Server/WorldSession.cpp b/src/server/game/Server/WorldSession.cpp
index 484a382c988..6a26dafde79 100644
--- a/src/server/game/Server/WorldSession.cpp
+++ b/src/server/game/Server/WorldSession.cpp
@@ -95,17 +95,28 @@ bool WorldSessionFilter::Process(WorldPacket* packet)
/// WorldSession constructor
WorldSession::WorldSession(uint32 id, WorldSocket* sock, AccountTypes sec, uint8 expansion, time_t mute_time, LocaleConstant locale, uint32 recruiter, bool isARecruiter):
-m_muteTime(mute_time), m_timeOutTime(0), _player(NULL), m_Socket(sock),
-_security(sec), _accountId(id), m_expansion(expansion), _logoutTime(0),
-m_inQueue(false), m_playerLoading(false), m_playerLogout(false),
-m_playerRecentlyLogout(false), m_playerSave(false),
-m_sessionDbcLocale(sWorld->GetAvailableDbcLocale(locale)),
-m_sessionDbLocaleIndex(locale),
-m_latency(0), m_TutorialsChanged(false), recruiterId(recruiter),
-isRecruiter(isARecruiter), timeLastWhoCommand(0)
+ m_muteTime(mute_time),
+ m_timeOutTime(0),
+ _player(NULL),
+ m_Socket(sock),
+ _security(sec),
+ _accountId(id),
+ m_expansion(expansion),
+ _warden(NULL),
+ _logoutTime(0),
+ m_inQueue(false),
+ m_playerLoading(false),
+ m_playerLogout(false),
+ m_playerRecentlyLogout(false),
+ m_playerSave(false),
+ m_sessionDbcLocale(sWorld->GetAvailableDbcLocale(locale)),
+ m_sessionDbLocaleIndex(locale),
+ m_latency(0),
+ m_TutorialsChanged(false),
+ recruiterId(recruiter),
+ isRecruiter(isARecruiter),
+ timeLastWhoCommand(0)
{
- _warden = NULL;
-
if (sock)
{
m_Address = sock->GetRemoteAddress();
@@ -727,8 +738,8 @@ void WorldSession::SetAccountData(AccountDataType type, time_t tm, std::string c
void WorldSession::SendAccountDataTimes(uint32 mask)
{
- WorldPacket data(SMSG_ACCOUNT_DATA_TIMES, 4 + 1 + 4 + 8 * 4); // changed in WotLK
- data << uint32(time(NULL)); // unix time of something
+ WorldPacket data(SMSG_ACCOUNT_DATA_TIMES, 4 + 1 + 4 + NUM_ACCOUNT_DATA_TYPES * 4);
+ data << uint32(time(NULL)); // Server time
data << uint8(1);
data << uint32(mask); // type mask
for (uint32 i = 0; i < NUM_ACCOUNT_DATA_TYPES; ++i)
@@ -815,7 +826,7 @@ void WorldSession::ReadMovementInfo(WorldPacket &data, MovementInfo* mi)
if (mi->HasMovementFlag(MOVEMENTFLAG_SPLINE_ELEVATION))
data >> mi->splineElevation;
- //! Anti-cheat checks. Please keep them in seperate if() blocks to maintain a clear overview.
+ //! Anti-cheat checks. Please keep them in seperate if () blocks to maintain a clear overview.
//! Might be subject to latency, so just remove improper flags.
#ifdef TRINITY_DEBUG
#define REMOVE_VIOLATING_FLAGS(check, maskToRemove) \
diff --git a/src/server/game/Server/WorldSession.h b/src/server/game/Server/WorldSession.h
index 39f5425d9df..abe048279db 100644
--- a/src/server/game/Server/WorldSession.h
+++ b/src/server/game/Server/WorldSession.h
@@ -181,9 +181,6 @@ class CharacterCreateInfo
/// Server side data
uint8 CharCount;
-
- private:
- virtual ~CharacterCreateInfo(){};
};
/// Player session in the World
diff --git a/src/server/game/Server/WorldSocket.cpp b/src/server/game/Server/WorldSocket.cpp
index b47f801ab29..b9955fac523 100644
--- a/src/server/game/Server/WorldSocket.cpp
+++ b/src/server/game/Server/WorldSocket.cpp
@@ -159,27 +159,29 @@ int WorldSocket::SendPacket(WorldPacket const& pct)
if (closing_)
return -1;
- // Dump outgoing packet.
+ // Dump outgoing packet
if (sPacketLog->CanLogPacket())
sPacketLog->LogPacket(pct, SERVER_TO_CLIENT);
+ WorldPacket const* pkt = &pct;
+
+
if (m_Session)
- sLog->outTrace(LOG_FILTER_OPCODES, "S->C %s %s", m_Session->GetPlayerInfo().c_str(), GetOpcodeNameForLogging(pct.GetOpcode()).c_str());
+ sLog->outTrace(LOG_FILTER_OPCODES, "S->C: %s %s", m_Session->GetPlayerInfo().c_str(), GetOpcodeNameForLogging(pkt->GetOpcode()).c_str());
- // Create a copy of the original packet; this is to avoid issues if a hook modifies it.
- sScriptMgr->OnPacketSend(this, WorldPacket(pct));
+ sScriptMgr->OnPacketSend(this, *pkt);
- ServerPktHeader header(pct.size()+2, pct.GetOpcode());
+ ServerPktHeader header(pkt->size()+2, pkt->GetOpcode());
m_Crypt.EncryptSend ((uint8*)header.header, header.getHeaderLength());
- if (m_OutBuffer->space() >= pct.size() + header.getHeaderLength() && msg_queue()->is_empty())
+ if (m_OutBuffer->space() >= pkt->size() + header.getHeaderLength() && msg_queue()->is_empty())
{
// Put the packet on the buffer.
if (m_OutBuffer->copy((char*) header.header, header.getHeaderLength()) == -1)
ACE_ASSERT (false);
- if (!pct.empty())
- if (m_OutBuffer->copy((char*) pct.contents(), pct.size()) == -1)
+ if (!pkt->empty())
+ if (m_OutBuffer->copy((char*) pkt->contents(), pkt->size()) == -1)
ACE_ASSERT (false);
}
else
@@ -187,12 +189,12 @@ int WorldSocket::SendPacket(WorldPacket const& pct)
// Enqueue the packet.
ACE_Message_Block* mb;
- ACE_NEW_RETURN(mb, ACE_Message_Block(pct.size() + header.getHeaderLength()), -1);
+ ACE_NEW_RETURN(mb, ACE_Message_Block(pkt->size() + header.getHeaderLength()), -1);
mb->copy((char*) header.header, header.getHeaderLength());
- if (!pct.empty())
- mb->copy((const char*)pct.contents(), pct.size());
+ if (!pkt->empty())
+ mb->copy((const char*)pkt->contents(), pkt->size());
if (msg_queue()->enqueue_tail(mb, (ACE_Time_Value*)&ACE_Time_Value::zero) == -1)
{
@@ -245,20 +247,7 @@ int WorldSocket::open (void *a)
m_Address = remote_addr.get_host_addr();
- // Send startup packet.
- WorldPacket packet (SMSG_AUTH_CHALLENGE, 24);
- packet << uint32(1); // 1...31
- packet << m_Seed;
-
- BigNumber seed1;
- seed1.SetRand(16 * 8);
- packet.append(seed1.AsByteArray(16), 16); // new encryption seeds
-
- BigNumber seed2;
- seed2.SetRand(16 * 8);
- packet.append(seed2.AsByteArray(16), 16); // new encryption seeds
-
- if (SendPacket(packet) == -1)
+ if (HandleSendAuthSession() == -1)
return -1;
// Register with ACE Reactor
@@ -461,7 +450,7 @@ int WorldSocket::Update (void)
int ret;
do
- ret = handle_output (get_handle());
+ ret = handle_output(get_handle());
while (ret > 0);
return ret;
@@ -469,18 +458,18 @@ int WorldSocket::Update (void)
int WorldSocket::handle_input_header (void)
{
- ACE_ASSERT (m_RecvWPct == NULL);
+ ACE_ASSERT(m_RecvWPct == NULL);
- ACE_ASSERT (m_Header.length() == sizeof(ClientPktHeader));
+ ACE_ASSERT(m_Header.length() == sizeof(ClientPktHeader));
- m_Crypt.DecryptRecv ((uint8*) m_Header.rd_ptr(), sizeof(ClientPktHeader));
+ m_Crypt.DecryptRecv ((uint8*)m_Header.rd_ptr(), sizeof(ClientPktHeader));
- ClientPktHeader& header = *((ClientPktHeader*) m_Header.rd_ptr());
+ ClientPktHeader& header = *((ClientPktHeader*)m_Header.rd_ptr());
EndianConvertReverse(header.size);
EndianConvert(header.cmd);
- if ((header.size < 4) || (header.size > 10240) || (header.cmd > 10240))
+ if ((header.size < 4) || (header.size > 10240) || (header.cmd > 10240))
{
Player* _player = m_Session ? m_Session->GetPlayer() : NULL;
sLog->outError(LOG_FILTER_NETWORKIO, "WorldSocket::handle_input_header(): client (account: %u, char [GUID: %u, name: %s]) sent malformed packet (size: %d, cmd: %d)",
@@ -495,11 +484,11 @@ int WorldSocket::handle_input_header (void)
header.size -= 4;
- ACE_NEW_RETURN (m_RecvWPct, WorldPacket ((uint16) header.cmd, header.size), -1);
+ ACE_NEW_RETURN(m_RecvWPct, WorldPacket ((uint16)header.cmd, header.size), -1);
if (header.size > 0)
{
- m_RecvWPct->resize (header.size);
+ m_RecvWPct->resize(header.size);
m_RecvPct.base ((char*) m_RecvWPct->contents(), m_RecvWPct->size());
}
else
@@ -666,7 +655,7 @@ int WorldSocket::ProcessIncoming(WorldPacket* new_pct)
ACE_ASSERT (new_pct);
// manage memory ;)
- ACE_Auto_Ptr<WorldPacket> aptr (new_pct);
+ ACE_Auto_Ptr<WorldPacket> aptr(new_pct);
const ACE_UINT16 opcode = new_pct->GetOpcode();
@@ -677,15 +666,16 @@ int WorldSocket::ProcessIncoming(WorldPacket* new_pct)
if (sPacketLog->CanLogPacket())
sPacketLog->LogPacket(*new_pct, CLIENT_TO_SERVER);
+ std::string opcodeName = GetOpcodeNameForLogging(opcode);
if (m_Session)
- sLog->outTrace(LOG_FILTER_OPCODES, "C->S %s %s", m_Session->GetPlayerInfo().c_str(), GetOpcodeNameForLogging(new_pct->GetOpcode()).c_str());
+ sLog->outTrace(LOG_FILTER_OPCODES, "C->S: %s %s", m_Session->GetPlayerInfo().c_str(), opcodeName.c_str());
try
{
switch (opcode)
{
case CMSG_PING:
- return HandlePing (*new_pct);
+ return HandlePing(*new_pct);
case CMSG_AUTH_SESSION:
if (m_Session)
{
@@ -694,18 +684,17 @@ int WorldSocket::ProcessIncoming(WorldPacket* new_pct)
}
sScriptMgr->OnPacketReceive(this, WorldPacket(*new_pct));
- return HandleAuthSession (*new_pct);
+ return HandleAuthSession(*new_pct);
case CMSG_KEEP_ALIVE:
- sLog->outDebug(LOG_FILTER_NETWORKIO, "%s", GetOpcodeNameForLogging(opcode).c_str());
+ sLog->outDebug(LOG_FILTER_NETWORKIO, "%s", opcodeName.c_str());
sScriptMgr->OnPacketReceive(this, WorldPacket(*new_pct));
return 0;
default:
{
- ACE_GUARD_RETURN (LockType, Guard, m_SessionLock, -1);
-
+ ACE_GUARD_RETURN(LockType, Guard, m_SessionLock, -1);
if (!m_Session)
{
- sLog->outError(LOG_FILTER_NETWORKIO, "ProcessIncoming: Client not authed opcode = %u", uint32(opcode));
+ sLog->outError(LOG_FILTER_OPCODES, "ProcessIncoming: Client not authed opcode = %u", uint32(opcode));
return -1;
}
@@ -715,7 +704,7 @@ int WorldSocket::ProcessIncoming(WorldPacket* new_pct)
// OK, give the packet to WorldSession
aptr.release();
- // WARNINIG here we call it with locks held.
+ // WARNING here we call it with locks held.
// Its possible to cause deadlock if QueuePacket calls back
m_Session->QueuePacket(new_pct);
return 0;
@@ -724,8 +713,8 @@ int WorldSocket::ProcessIncoming(WorldPacket* new_pct)
}
catch (ByteBufferException &)
{
- sLog->outError(LOG_FILTER_NETWORKIO, "WorldSocket::ProcessIncoming ByteBufferException occured while parsing an instant handled packet (opcode: %u) from client %s, accountid=%i. Disconnected client.",
- opcode, GetRemoteAddress().c_str(), m_Session ? int32(m_Session->GetAccountId()) : -1);
+ sLog->outError(LOG_FILTER_NETWORKIO, "WorldSocket::ProcessIncoming ByteBufferException occured while parsing an instant handled packet %s from client %s, accountid=%i. Disconnected client.",
+ opcodeName.c_str(), GetRemoteAddress().c_str(), m_Session ? int32(m_Session->GetAccountId()) : -1);
new_pct->hexlike();
return -1;
}
@@ -733,36 +722,47 @@ int WorldSocket::ProcessIncoming(WorldPacket* new_pct)
ACE_NOTREACHED (return 0);
}
+int WorldSocket::HandleSendAuthSession()
+{
+ WorldPacket packet(SMSG_AUTH_CHALLENGE, 37);
+ packet << uint32(1); // 1...31
+ packet << uint32(m_Seed);
+
+ BigNumber seed1;
+ seed1.SetRand(16 * 8);
+ packet.append(seed1.AsByteArray(16), 16); // new encryption seeds
+
+ BigNumber seed2;
+ seed2.SetRand(16 * 8);
+ packet.append(seed2.AsByteArray(16), 16); // new encryption seeds
+ return SendPacket(packet);
+}
+
int WorldSocket::HandleAuthSession(WorldPacket& recvPacket)
{
- // NOTE: ATM the socket is singlethread, have this in mind ...
uint8 digest[20];
uint32 clientSeed;
- uint32 unk2, unk3, unk5, unk6, unk7;
- uint64 unk4;
- uint32 BuiltNumberClient;
- uint32 id, security;
- //uint8 expansion = 0;
+ uint8 security;
+ uint32 id;
LocaleConstant locale;
std::string account;
SHA1Hash sha;
+ uint32 clientBuild;
+ uint32 unk2, unk3, unk5, unk6, unk7;
+ uint64 unk4;
BigNumber v, s, g, N;
WorldPacket packet, SendAddonPacked;
-
BigNumber k;
if (sWorld->IsClosed())
{
- packet.Initialize(SMSG_AUTH_RESPONSE, 1);
- packet << uint8(AUTH_REJECT);
- SendPacket(packet);
-
+ SendAuthResponseError(AUTH_REJECT);
sLog->outError(LOG_FILTER_NETWORKIO, "WorldSocket::HandleAuthSession: World closed, denying client (%s).", GetRemoteAddress().c_str());
return -1;
}
// Read the content of the packet
- recvPacket >> BuiltNumberClient; // for now no use
+ recvPacket >> clientBuild;
recvPacket >> unk2;
recvPacket >> account;
recvPacket >> unk3;
@@ -772,7 +772,7 @@ int WorldSocket::HandleAuthSession(WorldPacket& recvPacket)
recvPacket.read(digest, 20);
sLog->outDebug(LOG_FILTER_NETWORKIO, "WorldSocket::HandleAuthSession: client %u, unk2 %u, account %s, unk3 %u, clientseed %u",
- BuiltNumberClient,
+ clientBuild,
unk2,
account.c_str(),
unk3,
@@ -788,11 +788,7 @@ int WorldSocket::HandleAuthSession(WorldPacket& recvPacket)
// Stop if the account is not found
if (!result)
{
- packet.Initialize (SMSG_AUTH_RESPONSE, 1);
- packet << uint8 (AUTH_UNKNOWN_ACCOUNT);
-
- SendPacket(packet);
-
+ SendAuthResponseError(AUTH_UNKNOWN_ACCOUNT);
sLog->outError(LOG_FILTER_NETWORKIO, "WorldSocket::HandleAuthSession: Sent Auth Response (unknown account).");
return -1;
}
@@ -810,37 +806,30 @@ int WorldSocket::HandleAuthSession(WorldPacket& recvPacket)
v.SetHexStr(fields[4].GetCString());
s.SetHexStr (fields[5].GetCString());
- const char* sStr = s.AsHexStr(); //Must be freed by OPENSSL_free()
- const char* vStr = v.AsHexStr(); //Must be freed by OPENSSL_free()
+ const char* sStr = s.AsHexStr(); // Must be freed by OPENSSL_free()
+ const char* vStr = v.AsHexStr(); // Must be freed by OPENSSL_free()
sLog->outDebug(LOG_FILTER_NETWORKIO, "WorldSocket::HandleAuthSession: (s, v) check s: %s v: %s",
- sStr,
- vStr);
+ sStr,
+ vStr);
- OPENSSL_free ((void*) sStr);
- OPENSSL_free ((void*) vStr);
+ OPENSSL_free((void*)sStr);
+ OPENSSL_free((void*)vStr);
///- Re-check ip locking (same check as in realmd).
if (fields[3].GetUInt8() == 1) // if ip is locked
{
if (strcmp (fields[2].GetCString(), GetRemoteAddress().c_str()))
{
- packet.Initialize (SMSG_AUTH_RESPONSE, 1);
- packet << uint8 (AUTH_FAILED);
- SendPacket(packet);
-
+ SendAuthResponseError(AUTH_FAILED);
sLog->outDebug(LOG_FILTER_NETWORKIO, "WorldSocket::HandleAuthSession: Sent Auth Response (Account IP differs).");
return -1;
}
}
id = fields[0].GetUInt32();
- /*
- if (security > SEC_ADMINISTRATOR) // prevent invalid security settings in DB
- security = SEC_ADMINISTRATOR;
- */
- k.SetHexStr (fields[1].GetCString());
+ k.SetHexStr(fields[1].GetCString());
int64 mutetime = fields[7].GetInt64();
//! Negative mutetime indicates amount of seconds to be muted effective on next login - which is now.
@@ -866,10 +855,7 @@ int WorldSocket::HandleAuthSession(WorldPacket& recvPacket)
// Must be done before WorldSession is created
if (sWorld->getBoolConfig(CONFIG_WARDEN_ENABLED) && os != "Win" && os != "OSX")
{
- packet.Initialize(SMSG_AUTH_RESPONSE, 1);
- packet << uint8(AUTH_REJECT);
- SendPacket(packet);
-
+ SendAuthResponseError(AUTH_REJECT);
sLog->outError(LOG_FILTER_NETWORKIO, "WorldSocket::HandleAuthSession: Client %s attempted to log in using invalid client OS (%s).", GetRemoteAddress().c_str(), os.c_str());
return -1;
}
@@ -900,10 +886,7 @@ int WorldSocket::HandleAuthSession(WorldPacket& recvPacket)
if (banresult) // if account banned
{
- packet.Initialize (SMSG_AUTH_RESPONSE, 1);
- packet << uint8 (AUTH_BANNED);
- SendPacket(packet);
-
+ SendAuthResponseError(AUTH_BANNED);
sLog->outError(LOG_FILTER_NETWORKIO, "WorldSocket::HandleAuthSession: Sent Auth Response (Account banned).");
return -1;
}
@@ -911,13 +894,9 @@ int WorldSocket::HandleAuthSession(WorldPacket& recvPacket)
// Check locked state for server
AccountTypes allowedAccountType = sWorld->GetPlayerSecurityLimit();
sLog->outDebug(LOG_FILTER_NETWORKIO, "Allowed Level: %u Player Level %u", allowedAccountType, AccountTypes(security));
- if (AccountTypes(security) < allowedAccountType)
+ if (allowedAccountType > SEC_PLAYER && AccountTypes(security) < allowedAccountType)
{
- WorldPacket Packet (SMSG_AUTH_RESPONSE, 1);
- Packet << uint8 (AUTH_UNAVAILABLE);
-
- SendPacket(packet);
-
+ SendAuthResponseError(AUTH_UNAVAILABLE);
sLog->outInfo(LOG_FILTER_NETWORKIO, "WorldSocket::HandleAuthSession: User tries to login but his security level is not enough");
return -1;
}
@@ -926,29 +905,25 @@ int WorldSocket::HandleAuthSession(WorldPacket& recvPacket)
uint32 t = 0;
uint32 seed = m_Seed;
- sha.UpdateData (account);
- sha.UpdateData ((uint8 *) & t, 4);
- sha.UpdateData ((uint8 *) & clientSeed, 4);
- sha.UpdateData ((uint8 *) & seed, 4);
- sha.UpdateBigNumbers (&k, NULL);
+ sha.UpdateData(account);
+ sha.UpdateData((uint8*)&t, 4);
+ sha.UpdateData((uint8*)&clientSeed, 4);
+ sha.UpdateData((uint8*)&seed, 4);
+ sha.UpdateBigNumbers(&k, NULL);
sha.Finalize();
std::string address = GetRemoteAddress();
- if (memcmp (sha.GetDigest(), digest, 20))
+ if (memcmp(sha.GetDigest(), digest, 20))
{
- packet.Initialize (SMSG_AUTH_RESPONSE, 1);
- packet << uint8 (AUTH_FAILED);
-
- SendPacket(packet);
-
+ SendAuthResponseError(AUTH_FAILED);
sLog->outError(LOG_FILTER_NETWORKIO, "WorldSocket::HandleAuthSession: Authentication failed for account: %u ('%s') address: %s", id, account.c_str(), address.c_str());
return -1;
}
sLog->outDebug(LOG_FILTER_NETWORKIO, "WorldSocket::HandleAuthSession: Client '%s' authenticated successfully from %s.",
- account.c_str(),
- address.c_str());
+ account.c_str(),
+ address.c_str());
// Check if this user is by any chance a recruiter
stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_ACCOUNT_RECRUITER);
@@ -971,7 +946,7 @@ int WorldSocket::HandleAuthSession(WorldPacket& recvPacket)
LoginDatabase.Execute(stmt);
// NOTE ATM the socket is single-threaded, have this in mind ...
- ACE_NEW_RETURN (m_Session, WorldSession (id, this, AccountTypes(security), expansion, mutetime, locale, recruiter, isRecruiter), -1);
+ ACE_NEW_RETURN(m_Session, WorldSession(id, this, AccountTypes(security), expansion, mutetime, locale, recruiter, isRecruiter), -1);
m_Crypt.Init(&k);
@@ -985,10 +960,9 @@ int WorldSocket::HandleAuthSession(WorldPacket& recvPacket)
// Sleep this Network thread for
uint32 sleepTime = sWorld->getIntConfig(CONFIG_SESSION_ADD_DELAY);
- ACE_OS::sleep (ACE_Time_Value (0, sleepTime));
-
- sWorld->AddSession (m_Session);
+ ACE_OS::sleep(ACE_Time_Value(0, sleepTime));
+ sWorld->AddSession(m_Session);
return 0;
}
@@ -1049,7 +1023,14 @@ int WorldSocket::HandlePing (WorldPacket& recvPacket)
}
}
- WorldPacket packet (SMSG_PONG, 4);
+ WorldPacket packet(SMSG_PONG, 4);
packet << ping;
return SendPacket(packet);
}
+
+void WorldSocket::SendAuthResponseError(uint8 code)
+{
+ WorldPacket packet(SMSG_AUTH_RESPONSE, 1);
+ packet << uint8(code);
+ SendPacket(packet);
+} \ No newline at end of file
diff --git a/src/server/game/Server/WorldSocket.h b/src/server/game/Server/WorldSocket.h
index 6b59647bb6c..2d5762ef60e 100644
--- a/src/server/game/Server/WorldSocket.h
+++ b/src/server/game/Server/WorldSocket.h
@@ -97,13 +97,13 @@ class WorldSocket : public WorldHandler
typedef ACE_Guard<LockType> GuardType;
/// Check if socket is closed.
- bool IsClosed (void) const;
+ bool IsClosed(void) const;
/// Close the socket.
- void CloseSocket (void);
+ void CloseSocket(void);
/// Get address of connected peer.
- const std::string& GetRemoteAddress (void) const;
+ const std::string& GetRemoteAddress(void) const;
/// Send A packet on the socket, this function is reentrant.
/// @param pct packet to send
@@ -111,57 +111,60 @@ class WorldSocket : public WorldHandler
int SendPacket(const WorldPacket& pct);
/// Add reference to this object.
- long AddReference (void);
+ long AddReference(void);
/// Remove reference to this object.
- long RemoveReference (void);
+ long RemoveReference(void);
/// things called by ACE framework.
/// Called on open, the void* is the acceptor.
- virtual int open (void *);
+ virtual int open(void *);
/// Called on failures inside of the acceptor, don't call from your code.
- virtual int close (u_long);
+ virtual int close(u_long);
/// Called when we can read from the socket.
- virtual int handle_input (ACE_HANDLE = ACE_INVALID_HANDLE);
+ virtual int handle_input(ACE_HANDLE = ACE_INVALID_HANDLE);
/// Called when the socket can write.
- virtual int handle_output (ACE_HANDLE = ACE_INVALID_HANDLE);
+ virtual int handle_output(ACE_HANDLE = ACE_INVALID_HANDLE);
/// Called when connection is closed or error happens.
- virtual int handle_close (ACE_HANDLE = ACE_INVALID_HANDLE,
+ virtual int handle_close(ACE_HANDLE = ACE_INVALID_HANDLE,
ACE_Reactor_Mask = ACE_Event_Handler::ALL_EVENTS_MASK);
/// Called by WorldSocketMgr/ReactorRunnable.
- int Update (void);
+ int Update(void);
private:
/// Helper functions for processing incoming data.
- int handle_input_header (void);
- int handle_input_payload (void);
- int handle_input_missing_data (void);
+ int handle_input_header(void);
+ int handle_input_payload(void);
+ int handle_input_missing_data(void);
/// Help functions to mark/unmark the socket for output.
/// @param g the guard is for m_OutBufferLock, the function will release it
- int cancel_wakeup_output (GuardType& g);
- int schedule_wakeup_output (GuardType& g);
+ int cancel_wakeup_output(GuardType& g);
+ int schedule_wakeup_output(GuardType& g);
/// Drain the queue if its not empty.
- int handle_output_queue (GuardType& g);
+ int handle_output_queue(GuardType& g);
/// process one incoming packet.
/// @param new_pct received packet, note that you need to delete it.
- int ProcessIncoming (WorldPacket* new_pct);
+ int ProcessIncoming(WorldPacket* new_pct);
/// Called by ProcessIncoming() on CMSG_AUTH_SESSION.
- int HandleAuthSession (WorldPacket& recvPacket);
+ int HandleAuthSession(WorldPacket& recvPacket);
/// Called by ProcessIncoming() on CMSG_PING.
- int HandlePing (WorldPacket& recvPacket);
+ int HandlePing(WorldPacket& recvPacket);
+
+ int HandleSendAuthSession();
private:
+ void SendAuthResponseError(uint8);
/// Time in which the last ping was received
ACE_Time_Value m_LastPingTime;
diff --git a/src/server/game/Spells/Auras/SpellAuraEffects.cpp b/src/server/game/Spells/Auras/SpellAuraEffects.cpp
index 12551c93346..d029d2884c9 100644
--- a/src/server/game/Spells/Auras/SpellAuraEffects.cpp
+++ b/src/server/game/Spells/Auras/SpellAuraEffects.cpp
@@ -449,8 +449,6 @@ int32 AuraEffect::CalculateAmount(Unit* caster)
}
}
- float DoneActualBenefit = 0.0f;
-
// custom amount calculations go here
switch (GetAuraType())
{
@@ -483,112 +481,8 @@ int32 AuraEffect::CalculateAmount(Unit* caster)
}
break;
case SPELL_AURA_SCHOOL_ABSORB:
- m_canBeRecalculated = false;
- if (!caster)
- break;
- switch (GetSpellInfo()->SpellFamilyName)
- {
- case SPELLFAMILY_MAGE:
- // Ice Barrier
- if (GetSpellInfo()->SpellFamilyFlags[1] & 0x1 && GetSpellInfo()->SpellFamilyFlags[2] & 0x8)
- {
- // +80.68% from sp bonus
- DoneActualBenefit += caster->SpellBaseDamageBonusDone(m_spellInfo->GetSchoolMask()) * 0.8068f;
- // Glyph of Ice Barrier: its weird having a SPELLMOD_ALL_EFFECTS here but its blizzards doing :)
- // Glyph of Ice Barrier is only applied at the spell damage bonus because it was already applied to the base value in CalculateSpellDamage
- DoneActualBenefit = caster->ApplyEffectModifiers(GetSpellInfo(), m_effIndex, DoneActualBenefit);
- }
- // Fire Ward
- else if (GetSpellInfo()->SpellFamilyFlags[0] & 0x8 && GetSpellInfo()->SpellFamilyFlags[2] & 0x8)
- {
- // +80.68% from sp bonus
- DoneActualBenefit += caster->SpellBaseDamageBonusDone(m_spellInfo->GetSchoolMask()) * 0.8068f;
- }
- // Frost Ward
- else if (GetSpellInfo()->SpellFamilyFlags[0] & 0x100 && GetSpellInfo()->SpellFamilyFlags[2] & 0x8)
- {
- // +80.68% from sp bonus
- DoneActualBenefit += caster->SpellBaseDamageBonusDone(m_spellInfo->GetSchoolMask()) * 0.8068f;
- }
- break;
- case SPELLFAMILY_WARLOCK:
- // Shadow Ward
- if (m_spellInfo->SpellFamilyFlags[2] & 0x40)
- {
- // +80.68% from sp bonus
- DoneActualBenefit += caster->SpellBaseDamageBonusDone(m_spellInfo->GetSchoolMask()) * 0.8068f;
- }
- break;
- case SPELLFAMILY_PRIEST:
- // Power Word: Shield
- if (GetSpellInfo()->SpellFamilyFlags[0] & 0x1 && GetSpellInfo()->SpellFamilyFlags[2] & 0x400)
- {
- // +80.68% from sp bonus
- float bonus = 0.8068f;
-
- // Borrowed Time
- if (AuraEffect const* pAurEff = caster->GetDummyAuraEffect(SPELLFAMILY_PRIEST, 2899, 1))
- bonus += CalculatePct(1.0f, pAurEff->GetAmount());
-
- DoneActualBenefit += caster->SpellBaseHealingBonusDone(m_spellInfo->GetSchoolMask()) * bonus;
- // Improved PW: Shield: its weird having a SPELLMOD_ALL_EFFECTS here but its blizzards doing :)
- // Improved PW: Shield is only applied at the spell healing bonus because it was already applied to the base value in CalculateSpellDamage
- DoneActualBenefit = caster->ApplyEffectModifiers(GetSpellInfo(), m_effIndex, DoneActualBenefit);
- DoneActualBenefit *= caster->CalculateLevelPenalty(GetSpellInfo());
-
- amount += int32(DoneActualBenefit);
-
- // Twin Disciplines
- if (AuraEffect const* pAurEff = caster->GetAuraEffect(SPELL_AURA_ADD_PCT_MODIFIER, SPELLFAMILY_PRIEST, 0x400000, 0, 0, caster->GetGUID()))
- AddPct(amount, pAurEff->GetAmount());
-
- // Focused Power
- // Reuse variable, not sure if this code below can be moved before Twin Disciplines
- DoneActualBenefit = float(amount);
- DoneActualBenefit *= caster->GetTotalAuraMultiplier(SPELL_AURA_MOD_HEALING_DONE_PERCENT);
- amount = int32(DoneActualBenefit);
-
- return amount;
- }
- break;
- case SPELLFAMILY_PALADIN:
- // Sacred Shield
- if (m_spellInfo->SpellFamilyFlags[1] & 0x80000)
- {
- //+75.00% from sp bonus
- float bonus = 0.75f;
-
- DoneActualBenefit += caster->SpellBaseHealingBonusDone(m_spellInfo->GetSchoolMask()) * bonus;
- // Divine Guardian is only applied at the spell healing bonus because it was already applied to the base value in CalculateSpellDamage
- DoneActualBenefit = caster->ApplyEffectModifiers(GetSpellInfo(), m_effIndex, DoneActualBenefit);
- DoneActualBenefit *= caster->CalculateLevelPenalty(GetSpellInfo());
-
- amount += (int32)DoneActualBenefit;
-
- // Arena - Dampening
- AuraEffect const* pAurEff = caster->GetAuraEffect(74410, 0);
- if (!pAurEff)
- pAurEff = caster->GetAuraEffect(74411, 0); // Battleground - Dampening
- if (pAurEff)
- AddPct(amount, pAurEff->GetAmount());
-
- return amount;
- }
- break;
- default:
- break;
- }
- break;
case SPELL_AURA_MANA_SHIELD:
m_canBeRecalculated = false;
- if (!caster)
- break;
- // Mana Shield
- if (GetSpellInfo()->SpellFamilyName == SPELLFAMILY_MAGE && GetSpellInfo()->SpellFamilyFlags[0] & 0x8000 && m_spellInfo->SpellFamilyFlags[2] & 0x8)
- {
- // +80.53% from +spd bonus
- DoneActualBenefit += caster->SpellBaseDamageBonusDone(m_spellInfo->GetSchoolMask()) * 0.8053f;
- }
break;
case SPELL_AURA_DUMMY:
if (!caster)
@@ -600,161 +494,9 @@ int32 AuraEffect::CalculateAmount(Unit* caster)
amount = GetBase()->GetUnitOwner()->SpellHealingBonusTaken(caster, GetSpellInfo(), amount, SPELL_DIRECT_DAMAGE);
}
break;
- case SPELL_AURA_PERIODIC_DAMAGE:
- if (!caster)
- break;
- // Rupture
- if (GetSpellInfo()->SpellFamilyName == SPELLFAMILY_ROGUE && m_spellInfo->SpellFamilyFlags[0] & 0x100000)
- {
- m_canBeRecalculated = false;
- if (caster->GetTypeId() != TYPEID_PLAYER)
- break;
- //1 point : ${($m1+$b1*1+0.015*$AP)*4} damage over 8 secs
- //2 points: ${($m1+$b1*2+0.024*$AP)*5} damage over 10 secs
- //3 points: ${($m1+$b1*3+0.03*$AP)*6} damage over 12 secs
- //4 points: ${($m1+$b1*4+0.03428571*$AP)*7} damage over 14 secs
- //5 points: ${($m1+$b1*5+0.0375*$AP)*8} damage over 16 secs
- float AP_per_combo[6] = {0.0f, 0.015f, 0.024f, 0.03f, 0.03428571f, 0.0375f};
- uint8 cp = caster->ToPlayer()->GetComboPoints();
- if (cp > 5) cp = 5;
- amount += int32(caster->GetTotalAttackPowerValue(BASE_ATTACK) * AP_per_combo[cp]);
- }
- // Rip
- else if (GetSpellInfo()->SpellFamilyName == SPELLFAMILY_DRUID && m_spellInfo->SpellFamilyFlags[0] & 0x00800000 && GetAuraType() == SPELL_AURA_PERIODIC_DAMAGE)
- {
- m_canBeRecalculated = false;
- // 0.01*$AP*cp
- if (caster->GetTypeId() != TYPEID_PLAYER)
- break;
-
- uint8 cp = caster->ToPlayer()->GetComboPoints();
-
- // Idol of Feral Shadows. Cant be handled as SpellMod in SpellAura:Dummy due its dependency from CPs
- if (AuraEffect const* aurEff = caster->GetAuraEffect(34241, EFFECT_0))
- amount += cp * aurEff->GetAmount();
- // Idol of Worship. Cant be handled as SpellMod in SpellAura:Dummy due its dependency from CPs
- else if (AuraEffect const* aurEff = caster->GetAuraEffect(60774, EFFECT_0))
- amount += cp * aurEff->GetAmount();
-
- amount += uint32(CalculatePct(caster->GetTotalAttackPowerValue(BASE_ATTACK), cp));
- }
- // Rend
- else if (GetSpellInfo()->SpellFamilyName == SPELLFAMILY_WARRIOR && GetSpellInfo()->SpellFamilyFlags[0] & 0x20)
- {
- m_canBeRecalculated = false;
- // $0.2 * (($MWB + $mwb) / 2 + $AP / 14 * $MWS) bonus per tick
- float ap = caster->GetTotalAttackPowerValue(BASE_ATTACK);
- int32 mws = caster->GetAttackTime(BASE_ATTACK);
- float mwb_min = caster->GetWeaponDamageRange(BASE_ATTACK, MINDAMAGE);
- float mwb_max = caster->GetWeaponDamageRange(BASE_ATTACK, MAXDAMAGE);
- float mwb = ((mwb_min + mwb_max) / 2 + ap * mws / 14000) * 0.2f;
- amount += int32(caster->ApplyEffectModifiers(m_spellInfo, m_effIndex, mwb));
- // "If used while your target is above 75% health, Rend does 35% more damage."
- // as for 3.1.3 only ranks above 9 (wrong tooltip?)
- if (m_spellInfo->GetRank() >= 9)
- {
- if (GetBase()->GetUnitOwner()->HasAuraState(AURA_STATE_HEALTH_ABOVE_75_PERCENT, m_spellInfo, caster))
- AddPct(amount, m_spellInfo->Effects[EFFECT_2].CalcValue(caster));
- }
- }
- break;
- case SPELL_AURA_PERIODIC_ENERGIZE:
- switch (m_spellInfo->Id)
- {
- case 29166: // Innervate
- ApplyPct(amount, float(GetBase()->GetUnitOwner()->GetCreatePowers(POWER_MANA)) / GetTotalTicks());
- break;
- case 48391: // Owlkin Frenzy
- ApplyPct(amount, GetBase()->GetUnitOwner()->GetCreatePowers(POWER_MANA));
- break;
- default:
- break;
- }
- break;
- case SPELL_AURA_PERIODIC_HEAL:
- if (!caster)
- break;
- // Lightwell Renew
- if (GetSpellInfo()->SpellFamilyName == SPELLFAMILY_PRIEST && m_spellInfo->SpellFamilyFlags[2] & 0x4000)
- {
- if (caster->GetTypeId() == TYPEID_PLAYER)
- // Bonus from Glyph of Lightwell
- if (AuraEffect* modHealing = caster->GetAuraEffect(55673, 0))
- AddPct(amount, modHealing->GetAmount());
- }
- break;
- case SPELL_AURA_MOD_DAMAGE_PERCENT_TAKEN:
- if (!caster)
- break;
- // Icebound Fortitude
- if (GetSpellInfo()->SpellFamilyName == SPELLFAMILY_DEATHKNIGHT && m_spellInfo->SpellFamilyFlags[0] & 0x00100000)
- {
- if (caster->GetTypeId() == TYPEID_PLAYER)
- {
- int32 value = (-1 * amount) - 10;
- uint32 defva = uint32(caster->ToPlayer()->GetSkillValue(SKILL_DEFENSE) + caster->ToPlayer()->GetRatingBonusValue(CR_DEFENSE_SKILL));
-
- if (defva > 400)
- value += int32((defva - 400) * 0.15);
-
- // Glyph of Icebound Fortitude
- if (AuraEffect const* aurEff = caster->GetAuraEffect(58625, 0))
- {
- int32 valMax = aurEff->GetAmount();
- if (value < valMax)
- value = valMax;
- }
- amount = -value;
- }
- }
- // Hand of Salvation
- else if (GetSpellInfo()->SpellFamilyName == SPELLFAMILY_PALADIN && GetSpellInfo()->SpellFamilyFlags[0] & 0x00000100)
- {
- //Glyph of Salvation
- if (caster->GetGUID() == GetBase()->GetUnitOwner()->GetGUID())
- if (AuraEffect const* aurEff = caster->GetAuraEffect(63225, 0))
- amount = -aurEff->GetAmount();
- }
- break;
- case SPELL_AURA_MOD_THREAT:
- {
- uint8 level_diff = 0;
- float multiplier = 0.0f;
- switch (GetId())
- {
- // Arcane Shroud
- case 26400:
- level_diff = GetBase()->GetUnitOwner()->getLevel() - 60;
- multiplier = 2;
- break;
- // The Eye of Diminution
- case 28862:
- level_diff = GetBase()->GetUnitOwner()->getLevel() - 60;
- multiplier = 1;
- break;
- }
- if (level_diff > 0)
- amount += int32(multiplier * level_diff);
- break;
- }
- case SPELL_AURA_MOD_INCREASE_HEALTH:
- // Vampiric Blood
- if (GetId() == 55233)
- amount = GetBase()->GetUnitOwner()->CountPctFromMaxHealth(amount);
- break;
- case SPELL_AURA_MOD_INCREASE_SPEED:
- // Dash - do not set speed if not in cat form
- if (GetSpellInfo()->SpellFamilyName == SPELLFAMILY_DRUID && GetSpellInfo()->SpellFamilyFlags[2] & 0x00000008)
- amount = GetBase()->GetUnitOwner()->GetShapeshiftForm() == FORM_CAT ? amount : 0;
- break;
default:
break;
}
- if (DoneActualBenefit != 0.0f)
- {
- DoneActualBenefit *= caster->CalculateLevelPenalty(GetSpellInfo());
- amount += (int32)DoneActualBenefit;
- }
GetBase()->CallScriptEffectCalcAmountHandlers(const_cast<AuraEffect const*>(this), amount, m_canBeRecalculated);
amount *= GetBase()->GetStackAmount();
@@ -848,32 +590,6 @@ void AuraEffect::CalculateSpellMod()
{
switch (GetAuraType())
{
- case SPELL_AURA_DUMMY:
- switch (GetSpellInfo()->SpellFamilyName)
- {
- case SPELLFAMILY_DRUID:
- switch (GetId())
- {
- case 34246: // Idol of the Emerald Queen
- case 60779: // Idol of Lush Moss
- {
- if (!m_spellmod)
- {
- m_spellmod = new SpellModifier(GetBase());
- m_spellmod->op = SPELLMOD_DOT;
- m_spellmod->type = SPELLMOD_FLAT;
- m_spellmod->spellId = GetId();
- m_spellmod->mask[1] = 0x0010;
- }
- m_spellmod->value = GetAmount()/7;
- }
- break;
- }
- break;
- default:
- break;
- }
- break;
case SPELL_AURA_ADD_FLAT_MODIFIER:
case SPELL_AURA_ADD_PCT_MODIFIER:
if (!m_spellmod)
@@ -882,7 +598,7 @@ void AuraEffect::CalculateSpellMod()
m_spellmod->op = SpellModOp(GetMiscValue());
ASSERT(m_spellmod->op < MAX_SPELLMOD);
- m_spellmod->type = SpellModType(GetAuraType()); // SpellModType value == spell aura types
+ m_spellmod->type = SpellModType(GetAuraType()); // SpellModType value == spell aura types
m_spellmod->spellId = GetId();
m_spellmod->mask = GetSpellInfo()->Effects[GetEffIndex()].SpellClassMask;
m_spellmod->charges = GetBase()->GetCharges();
@@ -1274,7 +990,10 @@ void AuraEffect::PeriodicTick(AuraApplication * aurApp, Unit* caster) const
void AuraEffect::HandleProc(AuraApplication* aurApp, ProcEventInfo& eventInfo)
{
- // TODO: effect script handlers here
+ bool prevented = GetBase()->CallScriptEffectProcHandlers(const_cast<AuraEffect const*>(this), const_cast<AuraApplication const*>(aurApp), eventInfo);
+ if (prevented)
+ return;
+
switch (GetAuraType())
{
case SPELL_AURA_PROC_TRIGGER_SPELL:
@@ -1295,6 +1014,8 @@ void AuraEffect::HandleProc(AuraApplication* aurApp, ProcEventInfo& eventInfo)
default:
break;
}
+
+ GetBase()->CallScriptAfterEffectProcHandlers(const_cast<AuraEffect const*>(this), const_cast<AuraApplication const*>(aurApp), eventInfo);
}
void AuraEffect::CleanupTriggeredSpells(Unit* target)
@@ -1854,7 +1575,7 @@ void AuraEffect::HandlePhase(AuraApplication const* aurApp, uint8 mode, bool app
// call functions which may have additional effects after chainging state of unit
// phase auras normally not expected at BG but anyway better check
- if (apply && (mode & AURA_EFFECT_HANDLE_REAL))
+ if (apply)
{
// drop flag at invisibiliy in bg
target->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_IMMUNE_OR_LOST_SELECTION);
@@ -5077,13 +4798,13 @@ void AuraEffect::HandleAuraDummy(AuraApplication const* aurApp, uint8 mode, bool
switch (GetId())
{
case 59628: //Tricks of the trade buff on rogue (6sec duration)
- target->SetReducedThreatPercent(0,0);
+ target->SetReducedThreatPercent(0, 0);
break;
case 57934: //Tricks of the trade buff on rogue (30sec duration)
if (aurApp->GetRemoveMode() == AURA_REMOVE_BY_EXPIRE || !caster->GetMisdirectionTarget())
- target->SetReducedThreatPercent(0,0);
+ target->SetReducedThreatPercent(0, 0);
else
- target->SetReducedThreatPercent(0,caster->GetMisdirectionTarget()->GetGUID());
+ target->SetReducedThreatPercent(0, caster->GetMisdirectionTarget()->GetGUID());
break;
}
default:
diff --git a/src/server/game/Spells/Auras/SpellAuras.cpp b/src/server/game/Spells/Auras/SpellAuras.cpp
index b33546ee988..a41d25eae09 100644
--- a/src/server/game/Spells/Auras/SpellAuras.cpp
+++ b/src/server/game/Spells/Auras/SpellAuras.cpp
@@ -1350,7 +1350,7 @@ void Aura::HandleAuraSpecificMods(AuraApplication const* aurApp, Unit* caster, b
if (removeMode == AURA_REMOVE_BY_ENEMY_SPELL && GetSpellInfo()->SpellFamilyFlags[1] & 0x1)
{
// Shattered Barrier
- if (AuraEffect * dummy = caster->GetDummyAuraEffect(SPELLFAMILY_MAGE, 2945, 0))
+ if (AuraEffect* dummy = caster->GetDummyAuraEffect(SPELLFAMILY_MAGE, 2945, 0))
if (roll_chance_i(dummy->GetSpellInfo()->ProcChance))
caster->CastSpell(target, 55080, true, NULL, GetEffect(0));
}
@@ -1940,9 +1940,12 @@ void Aura::AddProcCooldown(uint32 /*msec*/)
//m_procCooldown = time(NULL) + msec;
}
-void Aura::PrepareProcToTrigger()
+void Aura::PrepareProcToTrigger(AuraApplication* aurApp, ProcEventInfo& eventInfo)
{
- // TODO: allow scripts to prevent charge drop/cooldown
+ bool prepare = CallScriptPrepareProcHandlers(aurApp, eventInfo);
+ if (!prepare)
+ return;
+
// take one charge, aura expiration will be handled in Aura::TriggerProcOnEvent (if needed)
if (IsUsingCharges())
{
@@ -1981,14 +1984,18 @@ bool Aura::IsProcTriggeredOnEvent(AuraApplication* aurApp, ProcEventInfo& eventI
return false;
// do checks using conditions table
- ConditionList conditions = sConditionMgr->GetConditionsForNotGroupedEntry(CONDITION_SOURCE_TYPE_SPELL_PROC, GetSpellInfo()->Id);
+ ConditionList conditions = sConditionMgr->GetConditionsForNotGroupedEntry(CONDITION_SOURCE_TYPE_SPELL_PROC, GetId());
ConditionSourceInfo condInfo = ConditionSourceInfo(eventInfo.GetActor(), eventInfo.GetActionTarget());
if (!sConditionMgr->IsObjectMeetToConditions(condInfo, conditions))
return false;
+ // AuraScript Hook
+ bool check = const_cast<Aura*>(this)->CallScriptCheckProcHandlers(aurApp, eventInfo);
+ if (!check)
+ return false;
+
// TODO:
- // - add DoCheckProc() AuraScript hook
- // to allow additional requirements for procs
+ // do allow additional requirements for procs
// this is needed because this is the last moment in which you can prevent aura charge drop on proc
// and possibly a way to prevent default checks (if there're going to be any)
@@ -2052,14 +2059,14 @@ float Aura::CalcProcChance(SpellProcEntry const& procEntry, ProcEventInfo& event
void Aura::TriggerProcOnEvent(AuraApplication* aurApp, ProcEventInfo& eventInfo)
{
- // TODO: OnProc hook here
+ CallScriptProcHandlers(const_cast<AuraApplication const*>(aurApp), eventInfo);
+
for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i)
if (aurApp->HasEffect(i))
- // TODO: OnEffectProc hook here (allowing prevention of selected effects)
+ // OnEffectProc / AfterEffectProc hooks handled in AuraEffect::HandleProc()
GetEffect(i)->HandleProc(aurApp, eventInfo);
- // TODO: AfterEffectProc hook here
- // TODO: AfterProc hook here
+ CallScriptAfterProcHandlers(const_cast<AuraApplication const*>(aurApp), eventInfo);
// Remove aura if we've used last charge to proc
if (IsUsingCharges() && !GetCharges())
@@ -2355,6 +2362,95 @@ void Aura::CallScriptEffectSplitHandlers(AuraEffect* aurEff, AuraApplication con
}
}
+bool Aura::CallScriptCheckProcHandlers(AuraApplication const* aurApp, ProcEventInfo& eventInfo)
+{
+ for (std::list<AuraScript*>::iterator scritr = m_loadedScripts.begin(); scritr != m_loadedScripts.end(); ++scritr)
+ {
+ (*scritr)->_PrepareScriptCall(AURA_SCRIPT_HOOK_CHECK_PROC, aurApp);
+ std::list<AuraScript::CheckProcHandler>::iterator hookItrEnd = (*scritr)->DoCheckProc.end(), hookItr = (*scritr)->DoCheckProc.begin();
+ for (; hookItr != hookItrEnd; ++hookItr)
+ if (!(*hookItr).Call(*scritr, eventInfo))
+ return false;
+ (*scritr)->_FinishScriptCall();
+ }
+ return true;
+}
+
+bool Aura::CallScriptPrepareProcHandlers(AuraApplication const* aurApp, ProcEventInfo& eventInfo)
+{
+ bool prepare = true;
+ for (std::list<AuraScript*>::iterator scritr = m_loadedScripts.begin(); scritr != m_loadedScripts.end(); ++scritr)
+ {
+ (*scritr)->_PrepareScriptCall(AURA_SCRIPT_HOOK_PREPARE_PROC, aurApp);
+ std::list<AuraScript::AuraProcHandler>::iterator effEndItr = (*scritr)->DoPrepareProc.end(), effItr = (*scritr)->DoPrepareProc.begin();
+ for (; effItr != effEndItr; ++effItr)
+ (*effItr).Call(*scritr, eventInfo);
+
+ if (prepare && (*scritr)->_IsDefaultActionPrevented())
+ prepare = false;
+ (*scritr)->_FinishScriptCall();
+ }
+ return prepare;
+}
+
+void Aura::CallScriptProcHandlers(AuraApplication const* aurApp, ProcEventInfo& eventInfo)
+{
+ for (std::list<AuraScript*>::iterator scritr = m_loadedScripts.begin(); scritr != m_loadedScripts.end(); ++scritr)
+ {
+ (*scritr)->_PrepareScriptCall(AURA_SCRIPT_HOOK_PROC, aurApp);
+ std::list<AuraScript::AuraProcHandler>::iterator hookItrEnd = (*scritr)->OnProc.end(), hookItr = (*scritr)->OnProc.begin();
+ for (; hookItr != hookItrEnd; ++hookItr)
+ (*hookItr).Call(*scritr, eventInfo);
+ (*scritr)->_FinishScriptCall();
+ }
+}
+
+void Aura::CallScriptAfterProcHandlers(AuraApplication const* aurApp, ProcEventInfo& eventInfo)
+{
+ for (std::list<AuraScript*>::iterator scritr = m_loadedScripts.begin(); scritr != m_loadedScripts.end(); ++scritr)
+ {
+ (*scritr)->_PrepareScriptCall(AURA_SCRIPT_HOOK_AFTER_PROC, aurApp);
+ std::list<AuraScript::AuraProcHandler>::iterator hookItrEnd = (*scritr)->AfterProc.end(), hookItr = (*scritr)->AfterProc.begin();
+ for (; hookItr != hookItrEnd; ++hookItr)
+ (*hookItr).Call(*scritr, eventInfo);
+ (*scritr)->_FinishScriptCall();
+ }
+}
+
+bool Aura::CallScriptEffectProcHandlers(AuraEffect const* aurEff, AuraApplication const* aurApp, ProcEventInfo& eventInfo)
+{
+ bool preventDefault = false;
+ for (std::list<AuraScript*>::iterator scritr = m_loadedScripts.begin(); scritr != m_loadedScripts.end(); ++scritr)
+ {
+ (*scritr)->_PrepareScriptCall(AURA_SCRIPT_HOOK_EFFECT_PROC, aurApp);
+ std::list<AuraScript::EffectProcHandler>::iterator effEndItr = (*scritr)->OnEffectProc.end(), effItr = (*scritr)->OnEffectProc.begin();
+ for (; effItr != effEndItr; ++effItr)
+ {
+ if ((*effItr).IsEffectAffected(m_spellInfo, aurEff->GetEffIndex()))
+ (*effItr).Call(*scritr, aurEff, eventInfo);
+ }
+ if (!preventDefault)
+ preventDefault = (*scritr)->_IsDefaultActionPrevented();
+ (*scritr)->_FinishScriptCall();
+ }
+ return preventDefault;
+}
+
+void Aura::CallScriptAfterEffectProcHandlers(AuraEffect const* aurEff, AuraApplication const* aurApp, ProcEventInfo& eventInfo)
+{
+ for (std::list<AuraScript*>::iterator scritr = m_loadedScripts.begin(); scritr != m_loadedScripts.end(); ++scritr)
+ {
+ (*scritr)->_PrepareScriptCall(AURA_SCRIPT_HOOK_EFFECT_AFTER_PROC, aurApp);
+ std::list<AuraScript::EffectProcHandler>::iterator effEndItr = (*scritr)->AfterEffectProc.end(), effItr = (*scritr)->AfterEffectProc.begin();
+ for (; effItr != effEndItr; ++effItr)
+ {
+ if ((*effItr).IsEffectAffected(m_spellInfo, aurEff->GetEffIndex()))
+ (*effItr).Call(*scritr, aurEff, eventInfo);
+ }
+ (*scritr)->_FinishScriptCall();
+ }
+}
+
UnitAura::UnitAura(SpellInfo const* spellproto, uint8 effMask, WorldObject* owner, Unit* caster, int32 *baseAmount, Item* castItem, uint64 casterGUID)
: Aura(spellproto, owner, caster, castItem, casterGUID)
{
diff --git a/src/server/game/Spells/Auras/SpellAuras.h b/src/server/game/Spells/Auras/SpellAuras.h
index 88ebee18981..bd351548255 100644
--- a/src/server/game/Spells/Auras/SpellAuras.h
+++ b/src/server/game/Spells/Auras/SpellAuras.h
@@ -194,7 +194,7 @@ class Aura
void AddProcCooldown(uint32 msec);
bool IsUsingCharges() const { return m_isUsingCharges; }
void SetUsingCharges(bool val) { m_isUsingCharges = val; }
- void PrepareProcToTrigger();
+ void PrepareProcToTrigger(AuraApplication* aurApp, ProcEventInfo& eventInfo);
bool IsProcTriggeredOnEvent(AuraApplication* aurApp, ProcEventInfo& eventInfo) const;
float CalcProcChance(SpellProcEntry const& procEntry, ProcEventInfo& eventInfo) const;
void TriggerProcOnEvent(AuraApplication* aurApp, ProcEventInfo& eventInfo);
@@ -218,6 +218,13 @@ class Aura
void CallScriptEffectManaShieldHandlers(AuraEffect* aurEff, AuraApplication const* aurApp, DamageInfo & dmgInfo, uint32 & absorbAmount, bool & defaultPrevented);
void CallScriptEffectAfterManaShieldHandlers(AuraEffect* aurEff, AuraApplication const* aurApp, DamageInfo & dmgInfo, uint32 & absorbAmount);
void CallScriptEffectSplitHandlers(AuraEffect* aurEff, AuraApplication const* aurApp, DamageInfo & dmgInfo, uint32 & splitAmount);
+ // Spell Proc Hooks
+ bool CallScriptCheckProcHandlers(AuraApplication const* aurApp, ProcEventInfo& eventInfo);
+ bool CallScriptPrepareProcHandlers(AuraApplication const* aurApp, ProcEventInfo& eventInfo);
+ void CallScriptProcHandlers(AuraApplication const* aurApp, ProcEventInfo& eventInfo);
+ void CallScriptAfterProcHandlers(AuraApplication const* aurApp, ProcEventInfo& eventInfo);
+ bool CallScriptEffectProcHandlers(AuraEffect const* aurEff, AuraApplication const* aurApp, ProcEventInfo& eventInfo);
+ void CallScriptAfterEffectProcHandlers(AuraEffect const* aurEff, AuraApplication const* aurApp, ProcEventInfo& eventInfo);
std::list<AuraScript*> m_loadedScripts;
private:
diff --git a/src/server/game/Spells/Spell.cpp b/src/server/game/Spells/Spell.cpp
index af6ed55ed58..4d75aec2cae 100644
--- a/src/server/game/Spells/Spell.cpp
+++ b/src/server/game/Spells/Spell.cpp
@@ -490,7 +490,7 @@ SpellValue::SpellValue(SpellInfo const* proto)
Spell::Spell(Unit* caster, SpellInfo const* info, TriggerCastFlags triggerFlags, uint64 originalCasterGUID, bool skipCheck) :
m_spellInfo(sSpellMgr->GetSpellForDifficultyFromSpell(info, caster)),
m_caster((info->AttributesEx6 & SPELL_ATTR6_CAST_BY_CHARMER && caster->GetCharmerOrOwner()) ? caster->GetCharmerOrOwner() : caster)
-, m_spellValue(new SpellValue(m_spellInfo))
+, m_spellValue(new SpellValue(m_spellInfo)), m_preGeneratedPath(PathGenerator(m_caster))
{
m_customError = SPELL_CUSTOM_ERROR_NONE;
m_skipCheck = skipCheck;
@@ -610,6 +610,7 @@ Spell::~Spell()
if (m_caster && m_caster->GetTypeId() == TYPEID_PLAYER)
ASSERT(m_caster->ToPlayer()->m_spellModTakingSpell != this);
+
delete m_spellValue;
CheckEffectExecuteData();
@@ -1345,11 +1346,6 @@ void Spell::SelectImplicitAreaTargets(SpellEffIndex effIndex, SpellImplicitTarge
}
}
- // todo: move to scripts, but we must call it before resize list by MaxAffectedTargets
- // Intimidating Shout
- if (m_spellInfo->Id == 5246 && effIndex != EFFECT_0)
- unitTargets.remove(m_targets.GetUnitTarget());
-
// Other special target selection goes here
if (uint32 maxTargets = m_spellValue->MaxAffectedTargets)
{
@@ -1880,7 +1876,7 @@ void Spell::SearchTargets(SEARCHER& searcher, uint32 containerMask, Unit* refere
bool searchInWorld = containerMask & (GRID_MAP_TYPE_MASK_CREATURE | GRID_MAP_TYPE_MASK_PLAYER | GRID_MAP_TYPE_MASK_CORPSE);
if (searchInGrid || searchInWorld)
{
- float x,y;
+ float x, y;
x = pos->GetPositionX();
y = pos->GetPositionY();
@@ -1898,7 +1894,7 @@ void Spell::SearchTargets(SEARCHER& searcher, uint32 containerMask, Unit* refere
if (searchInGrid)
{
TypeContainerVisitor<SEARCHER, GridTypeMapContainer > grid_object_notifier(searcher);
- cell.Visit(p, grid_object_notifier, map, radius, x , y);
+ cell.Visit(p, grid_object_notifier, map, radius, x, y);
}
}
}
@@ -2591,7 +2587,7 @@ SpellMissInfo Spell::DoSpellHitOnUnit(Unit* unit, uint32 effectMask, bool scaleA
debuff_resist_chance += unit->GetMaxNegativeAuraModifierByMiscValue(SPELL_AURA_MOD_DEBUFF_RESISTANCE, int32(m_spellInfo->Dispel));
if (debuff_resist_chance > 0)
- if (irand(0,10000) <= (debuff_resist_chance * 100))
+ if (irand(0, 10000) <= (debuff_resist_chance * 100))
{
effectMask &= ~(1 << effectNumber);
returnVal = SPELL_MISS_RESIST;
@@ -5148,12 +5144,30 @@ SpellCastResult Spell::CheckCast(bool strict)
if (strict && m_caster->IsScriptOverriden(m_spellInfo, 6953))
m_caster->RemoveMovementImpairingAuras();
}
+
if (m_caster->HasUnitState(UNIT_STATE_ROOT))
return SPELL_FAILED_ROOTED;
+
+ Unit* target = m_targets.GetUnitTarget();
+
+ if (!target)
+ return SPELL_FAILED_DONT_REPORT;
+
if (m_caster->GetTypeId() == TYPEID_PLAYER)
- if (Unit* target = m_targets.GetUnitTarget())
- if (!target->isAlive())
- return SPELL_FAILED_BAD_TARGETS;
+ if (!target->isAlive())
+ return SPELL_FAILED_BAD_TARGETS;
+
+ Position pos;
+ target->GetContactPoint(m_caster, pos.m_positionX, pos.m_positionY, pos.m_positionZ);
+ target->GetFirstCollisionPosition(pos, CONTACT_DISTANCE, target->GetRelativeAngle(m_caster));
+
+ m_preGeneratedPath.SetPathLengthLimit(m_spellInfo->GetMaxRange(true) * 1.5f);
+ bool result = m_preGeneratedPath.CalculatePath(pos.m_positionX, pos.m_positionY, pos.m_positionZ + target->GetObjectSize());
+ if (m_preGeneratedPath.GetPathType() & PATHFIND_SHORT)
+ return SPELL_FAILED_OUT_OF_RANGE;
+ else if (!result)
+ return SPELL_FAILED_NOPATH;
+
break;
}
case SPELL_EFFECT_SKINNING:
diff --git a/src/server/game/Spells/Spell.h b/src/server/game/Spells/Spell.h
index 1fe8affc407..903d4487c31 100644
--- a/src/server/game/Spells/Spell.h
+++ b/src/server/game/Spells/Spell.h
@@ -23,6 +23,7 @@
#include "SharedDefines.h"
#include "ObjectMgr.h"
#include "SpellInfo.h"
+#include "PathGenerator.h"
class Unit;
class Player;
@@ -665,6 +666,7 @@ class Spell
bool m_skipCheck;
uint8 m_auraScaleMask;
+ PathGenerator m_preGeneratedPath;
ByteBuffer * m_effectExecuteData[MAX_SPELL_EFFECTS];
diff --git a/src/server/game/Spells/SpellEffects.cpp b/src/server/game/Spells/SpellEffects.cpp
index 4e4698eb01b..f02c6d6ee3d 100644
--- a/src/server/game/Spells/SpellEffects.cpp
+++ b/src/server/game/Spells/SpellEffects.cpp
@@ -63,6 +63,7 @@
#include "GameObjectAI.h"
#include "AccountMgr.h"
#include "InstanceScript.h"
+#include "PathGenerator.h"
#include "ReputationMgr.h"
pEffect SpellEffects[TOTAL_SPELL_EFFECTS]=
@@ -883,12 +884,6 @@ void Spell::EffectTriggerSpell(SpellEffIndex effIndex)
m_caster->CastSpell(unitTarget, spell->Id, true);
return;
}
- // Righteous Defense
- case 31980:
- {
- m_caster->CastSpell(unitTarget, 31790, true);
- return;
- }
// Cloak of Shadows
case 35729:
{
@@ -1139,32 +1134,8 @@ void Spell::EffectTeleportUnits(SpellEffIndex /*effIndex*/)
return;
// Pre effects
- uint8 uiMaxSafeLevel = 0;
switch (m_spellInfo->Id)
{
- case 48129: // Scroll of Recall
- uiMaxSafeLevel = 40;
- case 60320: // Scroll of Recall II
- if (!uiMaxSafeLevel)
- uiMaxSafeLevel = 70;
- case 60321: // Scroll of Recal III
- if (!uiMaxSafeLevel)
- uiMaxSafeLevel = 80;
-
- if (unitTarget->getLevel() > uiMaxSafeLevel)
- {
- unitTarget->AddAura(60444, unitTarget); //Apply Lost! Aura
-
- // ALLIANCE from 60323 to 60330 - HORDE from 60328 to 60335
- uint32 spellId = 60323;
- if (m_caster->ToPlayer()->GetTeam() == HORDE)
- spellId += 5;
-
- spellId += urand(0, 7);
- m_caster->CastSpell(m_caster, spellId, true);
- return;
- }
- break;
case 66550: // teleports outside (Isle of Conquest)
if (Player* target = unitTarget->ToPlayer())
{
@@ -1188,7 +1159,7 @@ void Spell::EffectTeleportUnits(SpellEffIndex /*effIndex*/)
// If not exist data for dest location - return
if (!m_targets.HasDst())
{
- sLog->outError(LOG_FILTER_SPELLS_AURAS, "Spell::EffectTeleportUnits - does not have destination for spell ID %u\n", m_spellInfo->Id);
+ sLog->outError(LOG_FILTER_SPELLS_AURAS, "Spell::EffectTeleportUnits - does not have destination for spellId %u.", m_spellInfo->Id);
return;
}
@@ -1585,10 +1556,6 @@ void Spell::EffectHealPct(SpellEffIndex /*effIndex*/)
if (!m_originalCaster)
return;
- // Rune Tap - Party
- if (m_spellInfo->Id == 59754 && unitTarget == m_caster)
- return;
-
uint32 heal = m_originalCaster->SpellHealingBonusDone(unitTarget, m_spellInfo, unitTarget->CountPctFromMaxHealth(damage), HEAL);
heal = unitTarget->SpellHealingBonusTaken(m_originalCaster, m_spellInfo, heal, HEAL);
@@ -3283,7 +3250,7 @@ void Spell::EffectWeaponDmg(SpellEffIndex effIndex)
}
if (m_spellInfo->SpellFamilyFlags[0] & 0x8000000) // Mocking Blow
{
- if (unitTarget->IsImmunedToSpellEffect(m_spellInfo,EFFECT_1) || unitTarget->GetTypeId() == TYPEID_PLAYER)
+ if (unitTarget->IsImmunedToSpellEffect(m_spellInfo, EFFECT_1) || unitTarget->GetTypeId() == TYPEID_PLAYER)
{
m_damage = 0;
return;
@@ -3732,23 +3699,6 @@ void Spell::EffectScriptEffect(SpellEffIndex effIndex)
return;
unitTarget->RemoveAurasDueToSpell(m_spellInfo->Effects[effIndex].CalcValue());
break;
- // PX-238 Winter Wondervolt TRAP
- case 26275:
- {
- uint32 spells[4] = { 26272, 26157, 26273, 26274 };
-
- // check presence
- for (uint8 j = 0; j < 4; ++j)
- if (unitTarget->HasAuraEffect(spells[j], 0))
- return;
-
- // select spell
- uint32 iTmpSpellId = spells[urand(0, 3)];
-
- // cast
- unitTarget->CastSpell(unitTarget, iTmpSpellId, true);
- return;
- }
// Bending Shinbone
case 8856:
{
@@ -3794,14 +3744,6 @@ void Spell::EffectScriptEffect(SpellEffIndex effIndex)
m_caster->CastSpell(unitTarget, 22682, true);
return;
}
- // Piccolo of the Flaming Fire
- case 17512:
- {
- if (!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER)
- return;
- unitTarget->HandleEmoteCommand(EMOTE_STATE_DANCE);
- return;
- }
// Decimate
case 28374:
case 54426:
@@ -3944,17 +3886,6 @@ void Spell::EffectScriptEffect(SpellEffIndex effIndex)
m_caster->MonsterTextEmote(buf, 0);
break;
}
- // Vigilance
- case 50725:
- {
- if (!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER)
- return;
-
- // Remove Taunt cooldown
- unitTarget->ToPlayer()->RemoveSpellCooldown(355, true);
-
- return;
- }
// Death Knight Initiate Visual
case 51519:
{
@@ -4113,17 +4044,6 @@ void Spell::EffectScriptEffect(SpellEffIndex effIndex)
}
return;
}
- case 63845: // Create Lance
- {
- if (m_caster->GetTypeId() != TYPEID_PLAYER)
- return;
-
- if (m_caster->ToPlayer()->GetTeam() == ALLIANCE)
- m_caster->CastSpell(m_caster, 63914, true);
- else
- m_caster->CastSpell(m_caster, 63919, true);
- return;
- }
case 59317: // Teleporting
{
@@ -4317,46 +4237,6 @@ void Spell::EffectScriptEffect(SpellEffIndex effIndex)
return;
}
}
- case SPELLFAMILY_POTION:
- {
- switch (m_spellInfo->Id)
- {
- // Netherbloom
- case 28702:
- {
- if (!unitTarget)
- return;
- // 25% chance of casting a random buff
- if (roll_chance_i(75))
- return;
-
- // triggered spells are 28703 to 28707
- // Note: some sources say, that there was the possibility of
- // receiving a debuff. However, this seems to be removed by a patch.
- const uint32 spellid = 28703;
-
- // don't overwrite an existing aura
- for (uint8 i = 0; i < 5; ++i)
- if (unitTarget->HasAura(spellid + i))
- return;
- unitTarget->CastSpell(unitTarget, spellid+urand(0, 4), true);
- break;
- }
-
- // Nightmare Vine
- case 28720:
- {
- if (!unitTarget)
- return;
- // 25% chance of casting Nightmare Pollen
- if (roll_chance_i(75))
- return;
- unitTarget->CastSpell(unitTarget, 28721, true);
- break;
- }
- }
- break;
- }
case SPELLFAMILY_DEATHKNIGHT:
{
// Pestilence
@@ -4377,19 +4257,6 @@ void Spell::EffectScriptEffect(SpellEffIndex effIndex)
}
break;
}
- case SPELLFAMILY_WARRIOR:
- {
- // Shattering Throw
- if (m_spellInfo->SpellFamilyFlags[1] & 0x00400000)
- {
- if (!unitTarget)
- return;
- // remove shields, will still display immune to damage part
- unitTarget->RemoveAurasWithMechanic(1<<MECHANIC_IMMUNE_SHIELD, AURA_REMOVE_BY_ENEMY_SPELL);
- return;
- }
- break;
- }
}
// normal DB scripted effect
@@ -4868,45 +4735,12 @@ void Spell::EffectResurrect(SpellEffIndex effIndex)
if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET)
return;
- if (!unitTarget)
- return;
- if (unitTarget->GetTypeId() != TYPEID_PLAYER)
+ if (!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER)
return;
- if (unitTarget->isAlive())
- return;
- if (!unitTarget->IsInWorld())
+ if (unitTarget->isAlive() || !unitTarget->IsInWorld())
return;
- switch (m_spellInfo->Id)
- {
- // Defibrillate (Goblin Jumper Cables) have 33% chance on success
- case 8342:
- if (roll_chance_i(67))
- {
- m_caster->CastSpell(m_caster, 8338, true, m_CastItem);
- return;
- }
- break;
- // Defibrillate (Goblin Jumper Cables XL) have 50% chance on success
- case 22999:
- if (roll_chance_i(50))
- {
- m_caster->CastSpell(m_caster, 23055, true, m_CastItem);
- return;
- }
- break;
- // Defibrillate (Gnomish Army Knife) have 67% chance on success_list
- case 54732:
- if (roll_chance_i(33))
- {
- return;
- }
- break;
- default:
- break;
- }
-
Player* target = unitTarget->ToPlayer();
if (target->isRessurectRequested()) // already have one active request
@@ -5097,25 +4931,24 @@ void Spell::EffectSkinning(SpellEffIndex /*effIndex*/)
void Spell::EffectCharge(SpellEffIndex /*effIndex*/)
{
+ if (!unitTarget)
+ return;
+
if (effectHandleMode == SPELL_EFFECT_HANDLE_LAUNCH_TARGET)
{
- if (!unitTarget)
- return;
-
- float angle = unitTarget->GetRelativeAngle(m_caster);
- Position pos;
-
- unitTarget->GetContactPoint(m_caster, pos.m_positionX, pos.m_positionY, pos.m_positionZ);
- unitTarget->GetFirstCollisionPosition(pos, unitTarget->GetObjectSize(), angle);
-
- m_caster->GetMotionMaster()->MoveCharge(pos.m_positionX, pos.m_positionY, pos.m_positionZ + unitTarget->GetObjectSize());
+ if (m_preGeneratedPath.GetPathType() & PATHFIND_NOPATH)
+ {
+ Position pos;
+ unitTarget->GetContactPoint(m_caster, pos.m_positionX, pos.m_positionY, pos.m_positionZ);
+ unitTarget->GetFirstCollisionPosition(pos, unitTarget->GetObjectSize(), unitTarget->GetRelativeAngle(m_caster));
+ m_caster->GetMotionMaster()->MoveCharge(pos.m_positionX, pos.m_positionY, pos.m_positionZ);
+ }
+ else
+ m_caster->GetMotionMaster()->MoveCharge(m_preGeneratedPath);
}
if (effectHandleMode == SPELL_EFFECT_HANDLE_HIT_TARGET)
{
- if (!unitTarget)
- return;
-
// not all charge effects used in negative spells
if (!m_spellInfo->IsPositive() && m_caster->GetTypeId() == TYPEID_PLAYER)
m_caster->Attack(unitTarget, true);
@@ -5151,26 +4984,10 @@ void Spell::EffectKnockBack(SpellEffIndex effIndex)
if (creatureTarget->isWorldBoss() || creatureTarget->IsDungeonBoss())
return;
- // Spells with SPELL_EFFECT_KNOCK_BACK(like Thunderstorm) can't knoback target if target has ROOT/STUN
+ // Spells with SPELL_EFFECT_KNOCK_BACK (like Thunderstorm) can't knockback target if target has ROOT/STUN
if (unitTarget->HasUnitState(UNIT_STATE_ROOT | UNIT_STATE_STUNNED))
return;
- // Typhoon
- if (m_spellInfo->SpellFamilyName == SPELLFAMILY_DRUID && m_spellInfo->SpellFamilyFlags[1] & 0x01000000)
- {
- // Glyph of Typhoon
- if (m_caster->HasAura(62135))
- return;
- }
-
- // Thunderstorm
- if (m_spellInfo->SpellFamilyName == SPELLFAMILY_SHAMAN && m_spellInfo->SpellFamilyFlags[1] & 0x00002000)
- {
- // Glyph of Thunderstorm
- if (m_caster->HasAura(62132))
- return;
- }
-
// Instantly interrupt non melee spells being casted
if (unitTarget->IsNonMeleeSpellCasted(true))
unitTarget->InterruptNonMeleeSpells(true);
diff --git a/src/server/game/Spells/SpellInfo.cpp b/src/server/game/Spells/SpellInfo.cpp
index afbf85a0a13..0c298efd83e 100644
--- a/src/server/game/Spells/SpellInfo.cpp
+++ b/src/server/game/Spells/SpellInfo.cpp
@@ -232,7 +232,7 @@ SpellImplicitTargetInfo::StaticData SpellImplicitTargetInfo::_data[TOTAL_SPELL_
{TARGET_OBJECT_TYPE_GOBJ, TARGET_REFERENCE_TYPE_TARGET, TARGET_SELECT_CATEGORY_DEFAULT, TARGET_CHECK_DEFAULT, TARGET_DIR_NONE}, // 23 TARGET_GAMEOBJECT_TARGET
{TARGET_OBJECT_TYPE_UNIT, TARGET_REFERENCE_TYPE_CASTER, TARGET_SELECT_CATEGORY_CONE, TARGET_CHECK_ENEMY, TARGET_DIR_FRONT}, // 24 TARGET_UNIT_CONE_ENEMY_24
{TARGET_OBJECT_TYPE_UNIT, TARGET_REFERENCE_TYPE_TARGET, TARGET_SELECT_CATEGORY_DEFAULT, TARGET_CHECK_DEFAULT, TARGET_DIR_NONE}, // 25 TARGET_UNIT_TARGET_ANY
- {TARGET_OBJECT_TYPE_GOBJ_ITEM, TARGET_REFERENCE_TYPE_TARGET, TARGET_SELECT_CATEGORY_DEFAULT, TARGET_CHECK_DEFAULT, TARGET_DIR_NONE}, // 26 TARGET_GAMEOBJECT_ITEM_TARGET
+ {TARGET_OBJECT_TYPE_GOBJ_ITEM, TARGET_REFERENCE_TYPE_TARGET, TARGET_SELECT_CATEGORY_DEFAULT, TARGET_CHECK_DEFAULT, TARGET_DIR_NONE}, // 26 TARGET_GAMEOBJECT_ITEM_TARGET
{TARGET_OBJECT_TYPE_UNIT, TARGET_REFERENCE_TYPE_CASTER, TARGET_SELECT_CATEGORY_DEFAULT, TARGET_CHECK_DEFAULT, TARGET_DIR_NONE}, // 27 TARGET_UNIT_MASTER
{TARGET_OBJECT_TYPE_DEST, TARGET_REFERENCE_TYPE_DEST, TARGET_SELECT_CATEGORY_DEFAULT, TARGET_CHECK_ENEMY, TARGET_DIR_NONE}, // 28 TARGET_DEST_DYNOBJ_ENEMY
{TARGET_OBJECT_TYPE_DEST, TARGET_REFERENCE_TYPE_DEST, TARGET_SELECT_CATEGORY_DEFAULT, TARGET_CHECK_ALLY, TARGET_DIR_NONE}, // 29 TARGET_DEST_DYNOBJ_ALLY
@@ -267,7 +267,7 @@ SpellImplicitTargetInfo::StaticData SpellImplicitTargetInfo::_data[TOTAL_SPELL_
{TARGET_OBJECT_TYPE_UNIT, TARGET_REFERENCE_TYPE_CASTER, TARGET_SELECT_CATEGORY_NEARBY, TARGET_CHECK_RAID, TARGET_DIR_NONE}, // 58 TARGET_UNIT_NEARBY_RAID
{TARGET_OBJECT_TYPE_UNIT, TARGET_REFERENCE_TYPE_CASTER, TARGET_SELECT_CATEGORY_CONE, TARGET_CHECK_ALLY, TARGET_DIR_FRONT}, // 59 TARGET_UNIT_CONE_ALLY
{TARGET_OBJECT_TYPE_UNIT, TARGET_REFERENCE_TYPE_CASTER, TARGET_SELECT_CATEGORY_CONE, TARGET_CHECK_ENTRY, TARGET_DIR_FRONT}, // 60 TARGET_UNIT_CONE_ENTRY
- {TARGET_OBJECT_TYPE_UNIT, TARGET_REFERENCE_TYPE_TARGET, TARGET_SELECT_CATEGORY_AREA, TARGET_CHECK_RAID_CLASS,TARGET_DIR_NONE}, // 61 TARGET_UNIT_TARGET_AREA_RAID_CLASS
+ {TARGET_OBJECT_TYPE_UNIT, TARGET_REFERENCE_TYPE_TARGET, TARGET_SELECT_CATEGORY_AREA, TARGET_CHECK_RAID_CLASS, TARGET_DIR_NONE}, // 61 TARGET_UNIT_TARGET_AREA_RAID_CLASS
{TARGET_OBJECT_TYPE_NONE, TARGET_REFERENCE_TYPE_NONE, TARGET_SELECT_CATEGORY_NYI, TARGET_CHECK_DEFAULT, TARGET_DIR_NONE}, // 62 TARGET_UNK_62
{TARGET_OBJECT_TYPE_DEST, TARGET_REFERENCE_TYPE_TARGET, TARGET_SELECT_CATEGORY_DEFAULT, TARGET_CHECK_DEFAULT, TARGET_DIR_NONE}, // 63 TARGET_DEST_TARGET_ANY
{TARGET_OBJECT_TYPE_DEST, TARGET_REFERENCE_TYPE_TARGET, TARGET_SELECT_CATEGORY_DEFAULT, TARGET_CHECK_DEFAULT, TARGET_DIR_FRONT}, // 64 TARGET_DEST_TARGET_FRONT
@@ -299,9 +299,9 @@ SpellImplicitTargetInfo::StaticData SpellImplicitTargetInfo::_data[TOTAL_SPELL_
{TARGET_OBJECT_TYPE_UNIT, TARGET_REFERENCE_TYPE_TARGET, TARGET_SELECT_CATEGORY_DEFAULT, TARGET_CHECK_DEFAULT, TARGET_DIR_NONE}, // 90 TARGET_UNIT_TARGET_MINIPET
{TARGET_OBJECT_TYPE_DEST, TARGET_REFERENCE_TYPE_DEST, TARGET_SELECT_CATEGORY_DEFAULT, TARGET_CHECK_DEFAULT, TARGET_DIR_RANDOM}, // 91 TARGET_DEST_DEST_RADIUS
{TARGET_OBJECT_TYPE_UNIT, TARGET_REFERENCE_TYPE_CASTER, TARGET_SELECT_CATEGORY_DEFAULT, TARGET_CHECK_DEFAULT, TARGET_DIR_NONE}, // 92 TARGET_UNIT_SUMMONER
- {TARGET_OBJECT_TYPE_CORPSE, TARGET_REFERENCE_TYPE_SRC, TARGET_SELECT_CATEGORY_NYI, TARGET_CHECK_ENEMY, TARGET_DIR_NONE}, // 93 TARGET_CORPSE_SRC_AREA_ENEMY
+ {TARGET_OBJECT_TYPE_CORPSE, TARGET_REFERENCE_TYPE_SRC, TARGET_SELECT_CATEGORY_NYI, TARGET_CHECK_ENEMY, TARGET_DIR_NONE}, // 93 TARGET_CORPSE_SRC_AREA_ENEMY
{TARGET_OBJECT_TYPE_UNIT, TARGET_REFERENCE_TYPE_CASTER, TARGET_SELECT_CATEGORY_DEFAULT, TARGET_CHECK_DEFAULT, TARGET_DIR_NONE}, // 94 TARGET_UNIT_VEHICLE
- {TARGET_OBJECT_TYPE_UNIT, TARGET_REFERENCE_TYPE_TARGET, TARGET_SELECT_CATEGORY_DEFAULT, TARGET_CHECK_PASSENGER, TARGET_DIR_NONE}, // 95 TARGET_UNIT_TARGET_PASSENGER
+ {TARGET_OBJECT_TYPE_UNIT, TARGET_REFERENCE_TYPE_TARGET, TARGET_SELECT_CATEGORY_DEFAULT, TARGET_CHECK_PASSENGER, TARGET_DIR_NONE}, // 95 TARGET_UNIT_TARGET_PASSENGER
{TARGET_OBJECT_TYPE_UNIT, TARGET_REFERENCE_TYPE_CASTER, TARGET_SELECT_CATEGORY_DEFAULT, TARGET_CHECK_DEFAULT, TARGET_DIR_NONE}, // 96 TARGET_UNIT_PASSENGER_0
{TARGET_OBJECT_TYPE_UNIT, TARGET_REFERENCE_TYPE_CASTER, TARGET_SELECT_CATEGORY_DEFAULT, TARGET_CHECK_DEFAULT, TARGET_DIR_NONE}, // 97 TARGET_UNIT_PASSENGER_1
{TARGET_OBJECT_TYPE_UNIT, TARGET_REFERENCE_TYPE_CASTER, TARGET_SELECT_CATEGORY_DEFAULT, TARGET_CHECK_DEFAULT, TARGET_DIR_NONE}, // 98 TARGET_UNIT_PASSENGER_2
diff --git a/src/server/game/Spells/SpellMgr.cpp b/src/server/game/Spells/SpellMgr.cpp
index 582f13284bb..b258f01ccd5 100644
--- a/src/server/game/Spells/SpellMgr.cpp
+++ b/src/server/game/Spells/SpellMgr.cpp
@@ -3633,6 +3633,8 @@ void SpellMgr::LoadDbcDataCorrections()
case 49345: // Call Emerald Drake
spellInfo->Effect[1] = 0;
break;
+ case 24314: // Threatening Gaze
+ spellInfo->AuraInterruptFlags |= AURA_INTERRUPT_FLAG_CAST | AURA_INTERRUPT_FLAG_MOVE | AURA_INTERRUPT_FLAG_JUMP;
default:
break;
}
diff --git a/src/server/game/Spells/SpellScript.cpp b/src/server/game/Spells/SpellScript.cpp
index c29b08242a0..89ed223545f 100644
--- a/src/server/game/Spells/SpellScript.cpp
+++ b/src/server/game/Spells/SpellScript.cpp
@@ -470,7 +470,7 @@ WorldLocation* SpellScript::GetHitDest()
{
if (!IsInEffectHook())
{
- sLog->outError(LOG_FILTER_TSCR, "Script: `%s` Spell: `%u`: function SpellScript::GetHitGObj was called, but function has no effect in current hook!", m_scriptName->c_str(), m_scriptSpellId);
+ sLog->outError(LOG_FILTER_TSCR, "Script: `%s` Spell: `%u`: function SpellScript::GetHitDest was called, but function has no effect in current hook!", m_scriptName->c_str(), m_scriptSpellId);
return NULL;
}
return m_spell->destTarget;
@@ -679,6 +679,30 @@ bool AuraScript::_Validate(SpellInfo const* entry)
if (!(*itr).GetAffectedEffectsMask(entry))
sLog->outError(LOG_FILTER_TSCR, "Spell `%u` Effect `%s` of script `%s` did not match dbc effect data - handler bound to hook `OnEffectSplit` of AuraScript won't be executed", entry->Id, (*itr).ToString().c_str(), m_scriptName->c_str());
+ for (std::list<CheckProcHandler>::iterator itr = DoCheckProc.begin(); itr != DoCheckProc.end(); ++itr)
+ if (!entry->HasEffect(SPELL_EFFECT_APPLY_AURA) && !entry->HasAreaAuraEffect())
+ sLog->outError(LOG_FILTER_TSCR, "Spell `%u` of script `%s` does not have apply aura effect - handler bound to hook `DoCheckProc` of AuraScript won't be executed", entry->Id, m_scriptName->c_str());
+
+ for (std::list<AuraProcHandler>::iterator itr = DoPrepareProc.begin(); itr != DoPrepareProc.end(); ++itr)
+ if (!entry->HasEffect(SPELL_EFFECT_APPLY_AURA) && !entry->HasAreaAuraEffect())
+ sLog->outError(LOG_FILTER_TSCR, "Spell `%u` of script `%s` does not have apply aura effect - handler bound to hook `DoPrepareProc` of AuraScript won't be executed", entry->Id, m_scriptName->c_str());
+
+ for (std::list<AuraProcHandler>::iterator itr = OnProc.begin(); itr != OnProc.end(); ++itr)
+ if (!entry->HasEffect(SPELL_EFFECT_APPLY_AURA) && !entry->HasAreaAuraEffect())
+ sLog->outError(LOG_FILTER_TSCR, "Spell `%u` of script `%s` does not have apply aura effect - handler bound to hook `OnProc` of AuraScript won't be executed", entry->Id, m_scriptName->c_str());
+
+ for (std::list<AuraProcHandler>::iterator itr = AfterProc.begin(); itr != AfterProc.end(); ++itr)
+ if (!entry->HasEffect(SPELL_EFFECT_APPLY_AURA) && !entry->HasAreaAuraEffect())
+ sLog->outError(LOG_FILTER_TSCR, "Spell `%u` of script `%s` does not have apply aura effect - handler bound to hook `AfterProc` of AuraScript won't be executed", entry->Id, m_scriptName->c_str());
+
+ for (std::list<EffectProcHandler>::iterator itr = OnEffectProc.begin(); itr != OnEffectProc.end(); ++itr)
+ if (!(*itr).GetAffectedEffectsMask(entry))
+ sLog->outError(LOG_FILTER_TSCR, "Spell `%u` Effect `%s` of script `%s` did not match dbc effect data - handler bound to hook `OnEffectProc` of AuraScript won't be executed", entry->Id, (*itr).ToString().c_str(), m_scriptName->c_str());
+
+ for (std::list<EffectProcHandler>::iterator itr = AfterEffectProc.begin(); itr != AfterEffectProc.end(); ++itr)
+ if (!(*itr).GetAffectedEffectsMask(entry))
+ sLog->outError(LOG_FILTER_TSCR, "Spell `%u` Effect `%s` of script `%s` did not match dbc effect data - handler bound to hook `AfterEffectProc` of AuraScript won't be executed", entry->Id, (*itr).ToString().c_str(), m_scriptName->c_str());
+
return _SpellScript::_Validate(entry);
}
@@ -818,6 +842,37 @@ void AuraScript::EffectSplitHandler::Call(AuraScript* auraScript, AuraEffect* au
(auraScript->*pEffectHandlerScript)(aurEff, dmgInfo, splitAmount);
}
+AuraScript::CheckProcHandler::CheckProcHandler(AuraCheckProcFnType handlerScript)
+{
+ _HandlerScript = handlerScript;
+}
+
+bool AuraScript::CheckProcHandler::Call(AuraScript* auraScript, ProcEventInfo& eventInfo)
+{
+ return (auraScript->*_HandlerScript)(eventInfo);
+}
+
+AuraScript::AuraProcHandler::AuraProcHandler(AuraProcFnType handlerScript)
+{
+ _HandlerScript = handlerScript;
+}
+
+void AuraScript::AuraProcHandler::Call(AuraScript* auraScript, ProcEventInfo& eventInfo)
+{
+ (auraScript->*_HandlerScript)(eventInfo);
+}
+
+AuraScript::EffectProcHandler::EffectProcHandler(AuraEffectProcFnType effectHandlerScript, uint8 effIndex, uint16 effName)
+ : AuraScript::EffectBase(effIndex, effName)
+{
+ _EffectHandlerScript = effectHandlerScript;
+}
+
+void AuraScript::EffectProcHandler::Call(AuraScript* auraScript, AuraEffect const* aurEff, ProcEventInfo& eventInfo)
+{
+ (auraScript->*_EffectHandlerScript)(aurEff, eventInfo);
+}
+
bool AuraScript::_Load(Aura* aura)
{
m_aura = aura;
@@ -853,6 +908,8 @@ bool AuraScript::_IsDefaultActionPrevented()
case AURA_SCRIPT_HOOK_EFFECT_PERIODIC:
case AURA_SCRIPT_HOOK_EFFECT_ABSORB:
case AURA_SCRIPT_HOOK_EFFECT_SPLIT:
+ case AURA_SCRIPT_HOOK_PREPARE_PROC:
+ case AURA_SCRIPT_HOOK_EFFECT_PROC:
return m_defaultActionPrevented;
default:
ASSERT(false && "AuraScript::_IsDefaultActionPrevented is called in a wrong place");
@@ -869,6 +926,8 @@ void AuraScript::PreventDefaultAction()
case AURA_SCRIPT_HOOK_EFFECT_PERIODIC:
case AURA_SCRIPT_HOOK_EFFECT_ABSORB:
case AURA_SCRIPT_HOOK_EFFECT_SPLIT:
+ case AURA_SCRIPT_HOOK_PREPARE_PROC:
+ case AURA_SCRIPT_HOOK_EFFECT_PROC:
m_defaultActionPrevented = true;
break;
default:
@@ -1051,6 +1110,12 @@ Unit* AuraScript::GetTarget() const
case AURA_SCRIPT_HOOK_EFFECT_MANASHIELD:
case AURA_SCRIPT_HOOK_EFFECT_AFTER_MANASHIELD:
case AURA_SCRIPT_HOOK_EFFECT_SPLIT:
+ case AURA_SCRIPT_HOOK_CHECK_PROC:
+ case AURA_SCRIPT_HOOK_PREPARE_PROC:
+ case AURA_SCRIPT_HOOK_PROC:
+ case AURA_SCRIPT_HOOK_AFTER_PROC:
+ case AURA_SCRIPT_HOOK_EFFECT_PROC:
+ case AURA_SCRIPT_HOOK_EFFECT_AFTER_PROC:
return m_auraApplication->GetTarget();
default:
sLog->outError(LOG_FILTER_TSCR, "Script: `%s` Spell: `%u` AuraScript::GetTarget called in a hook in which the call won't have effect!", m_scriptName->c_str(), m_scriptSpellId);
diff --git a/src/server/game/Spells/SpellScript.h b/src/server/game/Spells/SpellScript.h
index 5791c701c07..6f1df2560dc 100644
--- a/src/server/game/Spells/SpellScript.h
+++ b/src/server/game/Spells/SpellScript.h
@@ -428,14 +428,22 @@ enum AuraScriptHookType
AURA_SCRIPT_HOOK_EFFECT_SPLIT,
AURA_SCRIPT_HOOK_CHECK_AREA_TARGET,
AURA_SCRIPT_HOOK_DISPEL,
- AURA_SCRIPT_HOOK_AFTER_DISPEL
+ AURA_SCRIPT_HOOK_AFTER_DISPEL,
+ // Spell Proc Hooks
+ AURA_SCRIPT_HOOK_CHECK_PROC,
+ AURA_SCRIPT_HOOK_PREPARE_PROC,
+ AURA_SCRIPT_HOOK_PROC,
+ AURA_SCRIPT_HOOK_EFFECT_PROC,
+ AURA_SCRIPT_HOOK_EFFECT_AFTER_PROC,
+ AURA_SCRIPT_HOOK_AFTER_PROC,
/*AURA_SCRIPT_HOOK_APPLY,
AURA_SCRIPT_HOOK_REMOVE, */
};
+/*
#define HOOK_AURA_EFFECT_START HOOK_AURA_EFFECT_APPLY
#define HOOK_AURA_EFFECT_END HOOK_AURA_EFFECT_CALC_SPELLMOD + 1
#define HOOK_AURA_EFFECT_COUNT HOOK_AURA_EFFECT_END - HOOK_AURA_EFFECT_START
-
+*/
class AuraScript : public _SpellScript
{
// internal use classes & functions
@@ -453,6 +461,9 @@ class AuraScript : public _SpellScript
typedef void(CLASSNAME::*AuraEffectCalcSpellModFnType)(AuraEffect const*, SpellModifier* &); \
typedef void(CLASSNAME::*AuraEffectAbsorbFnType)(AuraEffect*, DamageInfo &, uint32 &); \
typedef void(CLASSNAME::*AuraEffectSplitFnType)(AuraEffect*, DamageInfo &, uint32 &); \
+ typedef bool(CLASSNAME::*AuraCheckProcFnType)(ProcEventInfo&); \
+ typedef void(CLASSNAME::*AuraProcFnType)(ProcEventInfo&); \
+ typedef void(CLASSNAME::*AuraEffectProcFnType)(AuraEffect const*, ProcEventInfo&); \
AURASCRIPT_FUNCTION_TYPE_DEFINES(AuraScript)
@@ -552,6 +563,30 @@ class AuraScript : public _SpellScript
private:
AuraEffectSplitFnType pEffectHandlerScript;
};
+ class CheckProcHandler
+ {
+ public:
+ CheckProcHandler(AuraCheckProcFnType handlerScript);
+ bool Call(AuraScript* auraScript, ProcEventInfo& eventInfo);
+ private:
+ AuraCheckProcFnType _HandlerScript;
+ };
+ class AuraProcHandler
+ {
+ public:
+ AuraProcHandler(AuraProcFnType handlerScript);
+ void Call(AuraScript* auraScript, ProcEventInfo& eventInfo);
+ private:
+ AuraProcFnType _HandlerScript;
+ };
+ class EffectProcHandler : public EffectBase
+ {
+ public:
+ EffectProcHandler(AuraEffectProcFnType effectHandlerScript, uint8 effIndex, uint16 effName);
+ void Call(AuraScript* auraScript, AuraEffect const* aurEff, ProcEventInfo& eventInfo);
+ private:
+ AuraEffectProcFnType _EffectHandlerScript;
+ };
#define AURASCRIPT_FUNCTION_CAST_DEFINES(CLASSNAME) \
class CheckAreaTargetFunction : public AuraScript::CheckAreaTargetHandler { public: CheckAreaTargetFunction(AuraCheckAreaTargetFnType _pHandlerScript) : AuraScript::CheckAreaTargetHandler((AuraScript::AuraCheckAreaTargetFnType)_pHandlerScript) {} }; \
@@ -565,6 +600,9 @@ class AuraScript : public _SpellScript
class EffectAbsorbFunction : public AuraScript::EffectAbsorbHandler { public: EffectAbsorbFunction(AuraEffectAbsorbFnType _pEffectHandlerScript, uint8 _effIndex) : AuraScript::EffectAbsorbHandler((AuraScript::AuraEffectAbsorbFnType)_pEffectHandlerScript, _effIndex) {} }; \
class EffectManaShieldFunction : public AuraScript::EffectManaShieldHandler { public: EffectManaShieldFunction(AuraEffectAbsorbFnType _pEffectHandlerScript, uint8 _effIndex) : AuraScript::EffectManaShieldHandler((AuraScript::AuraEffectAbsorbFnType)_pEffectHandlerScript, _effIndex) {} }; \
class EffectSplitFunction : public AuraScript::EffectSplitHandler { public: EffectSplitFunction(AuraEffectSplitFnType _pEffectHandlerScript, uint8 _effIndex) : AuraScript::EffectSplitHandler((AuraScript::AuraEffectSplitFnType)_pEffectHandlerScript, _effIndex) {} }; \
+ class CheckProcHandlerFunction : public AuraScript::CheckProcHandler { public: CheckProcHandlerFunction(AuraCheckProcFnType handlerScript) : AuraScript::CheckProcHandler((AuraScript::AuraCheckProcFnType)handlerScript) {} }; \
+ class AuraProcHandlerFunction : public AuraScript::AuraProcHandler { public: AuraProcHandlerFunction(AuraProcFnType handlerScript) : AuraScript::AuraProcHandler((AuraScript::AuraProcFnType)handlerScript) {} }; \
+ class EffectProcHandlerFunction : public AuraScript::EffectProcHandler { public: EffectProcHandlerFunction(AuraEffectProcFnType effectHandlerScript, uint8 effIndex, uint16 effName) : AuraScript::EffectProcHandler((AuraScript::AuraEffectProcFnType)effectHandlerScript, effIndex, effName) {} }; \
#define PrepareAuraScript(CLASSNAME) AURASCRIPT_FUNCTION_TYPE_DEFINES(CLASSNAME) AURASCRIPT_FUNCTION_CAST_DEFINES(CLASSNAME)
@@ -695,6 +733,36 @@ class AuraScript : public _SpellScript
HookList<EffectSplitHandler> OnEffectSplit;
#define AuraEffectSplitFn(F, I) EffectSplitFunction(&F, I)
+ // executed when aura checks if it can proc
+ // example: DoCheckProc += AuraCheckProcFn(class::function);
+ // where function is: bool function (ProcEventInfo& eventInfo);
+ HookList<CheckProcHandler> DoCheckProc;
+ #define AuraCheckProcFn(F) CheckProcHandlerFunction(&F)
+
+ // executed before aura procs (possibility to prevent charge drop/cooldown)
+ // example: DoPrepareProc += AuraProcFn(class::function);
+ // where function is: void function (ProcEventInfo& eventInfo);
+ HookList<AuraProcHandler> DoPrepareProc;
+ // executed when aura procs
+ // example: OnProc += AuraProcFn(class::function);
+ // where function is: void function (ProcEventInfo& eventInfo);
+ HookList<AuraProcHandler> OnProc;
+ // executed after aura proced
+ // example: AfterProc += AuraProcFn(class::function);
+ // where function is: void function (ProcEventInfo& eventInfo);
+ HookList<AuraProcHandler> AfterProc;
+ #define AuraProcFn(F) AuraProcHandlerFunction(&F)
+
+ // executed when aura effect procs
+ // example: OnEffectProc += AuraEffectProcFn(class::function, EffectIndexSpecifier, EffectAuraNameSpecifier);
+ // where function is: void function (AuraEffect const* aurEff, ProcEventInfo& procInfo);
+ HookList<EffectProcHandler> OnEffectProc;
+ // executed after aura effect proced
+ // example: AfterEffectProc += AuraEffectProcFn(class::function, EffectIndexSpecifier, EffectAuraNameSpecifier);
+ // where function is: void function (AuraEffect const* aurEff, ProcEventInfo& procInfo);
+ HookList<EffectProcHandler> AfterEffectProc;
+ #define AuraEffectProcFn(F, I, N) EffectProcHandlerFunction(&F, I, N)
+
// AuraScript interface - hook/effect execution manipulators
// prevents default action of a hook from being executed (works only while called in a hook which default action can be prevented)
diff --git a/src/server/game/Tickets/TicketMgr.cpp b/src/server/game/Tickets/TicketMgr.cpp
index 1b7ecd34051..fe10b81b09d 100644
--- a/src/server/game/Tickets/TicketMgr.cpp
+++ b/src/server/game/Tickets/TicketMgr.cpp
@@ -182,6 +182,8 @@ std::string GmTicket::FormatMessageString(ChatHandler& handler, bool detailed) c
ss << handler.PGetParseString(LANG_COMMAND_TICKETLISTMESSAGE, _message.c_str());
if (!_comment.empty())
ss << handler.PGetParseString(LANG_COMMAND_TICKETLISTCOMMENT, _comment.c_str());
+ if (!_response.empty())
+ ss << handler.PGetParseString(LANG_COMMAND_TICKETLISTRESPONSE, _response.c_str());
}
return ss.str();
}
diff --git a/src/server/game/Warden/Warden.h b/src/server/game/Warden/Warden.h
index 7a61f3d5723..b9470b0c8a7 100644
--- a/src/server/game/Warden/Warden.h
+++ b/src/server/game/Warden/Warden.h
@@ -40,7 +40,7 @@ enum WardenOpcodes
WARDEN_SMSG_MODULE_CACHE = 1,
WARDEN_SMSG_CHEAT_CHECKS_REQUEST = 2,
WARDEN_SMSG_MODULE_INITIALIZE = 3,
- WARDEN_SMSG_MEM_CHECKS_REQUEST = 4, // byte len; while(!EOF) { byte unk(1); byte index(++); string module(can be 0); int offset; byte len; byte[] bytes_to_compare[len]; }
+ WARDEN_SMSG_MEM_CHECKS_REQUEST = 4, // byte len; while (!EOF) { byte unk(1); byte index(++); string module(can be 0); int offset; byte len; byte[] bytes_to_compare[len]; }
WARDEN_SMSG_HASH_REQUEST = 5
};
@@ -60,7 +60,7 @@ enum WardenCheckType
#if defined(__GNUC__)
#pragma pack(1)
#else
-#pragma pack(push,1)
+#pragma pack(push, 1)
#endif
struct WardenModuleUse
diff --git a/src/server/game/Warden/WardenMac.cpp b/src/server/game/Warden/WardenMac.cpp
index d1df94d7f07..7c2979e0dc6 100644
--- a/src/server/game/Warden/WardenMac.cpp
+++ b/src/server/game/Warden/WardenMac.cpp
@@ -38,7 +38,7 @@ WardenMac::~WardenMac()
{
}
-void WardenMac::Init(WorldSession *pClient, BigNumber *K)
+void WardenMac::Init(WorldSession* pClient, BigNumber* K)
{
_session = pClient;
// Generate Warden Key
diff --git a/src/server/game/Warden/WardenWin.cpp b/src/server/game/Warden/WardenWin.cpp
index bff12280d89..4da05eded0c 100644
--- a/src/server/game/Warden/WardenWin.cpp
+++ b/src/server/game/Warden/WardenWin.cpp
@@ -42,7 +42,7 @@ WardenWin::~WardenWin()
{
}
-void WardenWin::Init(WorldSession* session, BigNumber *k)
+void WardenWin::Init(WorldSession* session, BigNumber* k)
{
_session = session;
// Generate Warden Key
@@ -368,7 +368,7 @@ void WardenWin::HandleData(ByteBuffer &buff)
sLog->outDebug(LOG_FILTER_WARDEN, "Ticks diff %u", ourTicks - newClientTicks);
}
- WardenCheckResult *rs;
+ WardenCheckResult* rs;
WardenCheck *rd;
uint8 type;
uint16 checkFailed = 0;
diff --git a/src/server/game/Warden/WardenWin.h b/src/server/game/Warden/WardenWin.h
index 102a64c6895..1b1cc6746f4 100644
--- a/src/server/game/Warden/WardenWin.h
+++ b/src/server/game/Warden/WardenWin.h
@@ -28,7 +28,7 @@
#if defined(__GNUC__)
#pragma pack(1)
#else
-#pragma pack(push,1)
+#pragma pack(push, 1)
#endif
struct WardenInitModuleRequest
diff --git a/src/server/game/World/World.cpp b/src/server/game/World/World.cpp
index 264119ee99c..67444475849 100644
--- a/src/server/game/World/World.cpp
+++ b/src/server/game/World/World.cpp
@@ -21,6 +21,7 @@
*/
#include "Common.h"
+#include "Memory.h"
#include "DatabaseEnv.h"
#include "Config.h"
#include "SystemConfig.h"
@@ -54,6 +55,7 @@
#include "TemporarySummon.h"
#include "WaypointMovementGenerator.h"
#include "VMapFactory.h"
+#include "MMapFactory.h"
#include "GameEventMgr.h"
#include "PoolMgr.h"
#include "GridNotifiersImpl.h"
@@ -135,6 +137,7 @@ World::~World()
delete command;
VMAP::VMapFactory::clear();
+ MMAP::MMapFactory::clear();
//TODO free addSessQueue
}
@@ -1138,22 +1141,23 @@ void World::LoadConfigSettings(bool reload)
sLog->outInfo(LOG_FILTER_SERVER_LOADING, "Using DataDir %s", m_dataPath.c_str());
}
+ m_bool_configs[CONFIG_ENABLE_MMAPS] = ConfigMgr::GetBoolDefault("mmap.enablePathFinding", false);
+ sLog->outInfo(LOG_FILTER_SERVER_LOADING, "WORLD: MMap data directory is: %smmaps", m_dataPath.c_str());
+
m_bool_configs[CONFIG_VMAP_INDOOR_CHECK] = ConfigMgr::GetBoolDefault("vmap.enableIndoorCheck", 0);
bool enableIndoor = ConfigMgr::GetBoolDefault("vmap.enableIndoorCheck", true);
bool enableLOS = ConfigMgr::GetBoolDefault("vmap.enableLOS", true);
bool enableHeight = ConfigMgr::GetBoolDefault("vmap.enableHeight", true);
- bool enablePetLOS = ConfigMgr::GetBoolDefault("vmap.petLOS", true);
if (!enableHeight)
sLog->outError(LOG_FILTER_SERVER_LOADING, "VMap height checking disabled! Creatures movements and other various things WILL be broken! Expect no support.");
VMAP::VMapFactory::createOrGetVMapManager()->setEnableLineOfSightCalc(enableLOS);
VMAP::VMapFactory::createOrGetVMapManager()->setEnableHeightCalc(enableHeight);
- sLog->outInfo(LOG_FILTER_SERVER_LOADING, "VMap support included. LineOfSight: %i, getHeight: %i, indoorCheck: %i PetLOS: %i", enableLOS, enableHeight, enableIndoor, enablePetLOS);
+ sLog->outInfo(LOG_FILTER_SERVER_LOADING, "VMap support included. LineOfSight: %i, getHeight: %i, indoorCheck: %i", enableLOS, enableHeight, enableIndoor);
sLog->outInfo(LOG_FILTER_SERVER_LOADING, "VMap data directory is: %svmaps", m_dataPath.c_str());
m_int_configs[CONFIG_MAX_WHO] = ConfigMgr::GetIntDefault("MaxWhoListReturns", 49);
- m_bool_configs[CONFIG_PET_LOS] = ConfigMgr::GetBoolDefault("vmap.petLOS", true);
m_bool_configs[CONFIG_START_ALL_SPELLS] = ConfigMgr::GetBoolDefault("PlayerStart.AllSpells", false);
if (m_bool_configs[CONFIG_START_ALL_SPELLS])
sLog->outWarn(LOG_FILTER_SERVER_LOADING, "PlayerStart.AllSpells enabled - may not function as intended!");
@@ -1245,6 +1249,9 @@ void World::SetInitialWorldSettings()
///- Initialize the random number generator
srand((unsigned int)time(NULL));
+ ///- Initialize detour memory management
+ dtAllocSetCustom(dtCustomAlloc, dtCustomFree);
+
///- Initialize config settings
LoadConfigSettings();
@@ -1790,7 +1797,9 @@ void World::SetInitialWorldSettings()
uint32 startupDuration = GetMSTimeDiffToNow(startupBegin);
sLog->outInfo(LOG_FILTER_WORLDSERVER, "World initialized in %u minutes %u seconds", (startupDuration / 60000), ((startupDuration % 60000) / 1000));
- sLog->EnableDBAppenders();
+
+ if (uint32 realmId = ConfigMgr::GetIntDefault("RealmID", 0)) // 0 reserved for auth
+ sLog->SetRealmId(realmId);
}
void World::DetectDBCLang()
@@ -2898,7 +2907,7 @@ void World::ResetMonthlyQuests()
void World::ResetEventSeasonalQuests(uint16 event_id)
{
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_QUEST_STATUS_SEASONAL);
- stmt->setUInt16(0,event_id);
+ stmt->setUInt16(0, event_id);
CharacterDatabase.Execute(stmt);
for (SessionMap::const_iterator itr = m_sessions.begin(); itr != m_sessions.end(); ++itr)
diff --git a/src/server/game/World/World.h b/src/server/game/World/World.h
index 768f1d327b8..4a7629cb3af 100644
--- a/src/server/game/World/World.h
+++ b/src/server/game/World/World.h
@@ -138,7 +138,6 @@ enum WorldBoolConfigs
CONFIG_ARENA_LOG_EXTENDED_INFO,
CONFIG_OFFHAND_CHECK_AT_SPELL_UNLEARN,
CONFIG_VMAP_INDOOR_CHECK,
- CONFIG_PET_LOS,
CONFIG_START_ALL_SPELLS,
CONFIG_START_ALL_EXPLORED,
CONFIG_START_ALL_REP,
@@ -164,6 +163,7 @@ enum WorldBoolConfigs
CONFIG_QUEST_IGNORE_AUTO_ACCEPT,
CONFIG_QUEST_IGNORE_AUTO_COMPLETE,
CONFIG_WARDEN_ENABLED,
+ CONFIG_ENABLE_MMAPS,
CONFIG_WINTERGRASP_ENABLE,
BOOL_CONFIG_VALUE_COUNT
};
diff --git a/src/server/scripts/CMakeLists.txt b/src/server/scripts/CMakeLists.txt
index 9726cc1a937..cf4618022b3 100644
--- a/src/server/scripts/CMakeLists.txt
+++ b/src/server/scripts/CMakeLists.txt
@@ -45,6 +45,8 @@ message("")
include_directories(
${CMAKE_BINARY_DIR}
+ ${CMAKE_SOURCE_DIR}/dep/recastnavigation/Detour
+ ${CMAKE_SOURCE_DIR}/dep/recastnavigation/Recast
${CMAKE_SOURCE_DIR}/dep/g3dlite/include
${CMAKE_SOURCE_DIR}/dep/SFMT
${CMAKE_SOURCE_DIR}/dep/zlib
diff --git a/src/server/scripts/Commands/CMakeLists.txt b/src/server/scripts/Commands/CMakeLists.txt
index 97e9b35e6cd..7b9e2444952 100644
--- a/src/server/scripts/Commands/CMakeLists.txt
+++ b/src/server/scripts/Commands/CMakeLists.txt
@@ -42,6 +42,7 @@ set(scripts_STAT_SRCS
Commands/cs_server.cpp
Commands/cs_titles.cpp
Commands/cs_wp.cpp
+ Commands/cs_mmaps.cpp
# Commands/cs_pdump.cpp
# Commands/cs_channel.cpp
# Commands/cs_pet.cpp
diff --git a/src/server/scripts/Commands/cs_debug.cpp b/src/server/scripts/Commands/cs_debug.cpp
index 030bc136333..400e35b705b 100644
--- a/src/server/scripts/Commands/cs_debug.cpp
+++ b/src/server/scripts/Commands/cs_debug.cpp
@@ -1120,13 +1120,13 @@ public:
if (isInt32)
{
uint32 value = (uint32)atoi(y);
- target->SetUInt32Value(opcode , value);
+ target->SetUInt32Value(opcode, value);
handler->PSendSysMessage(LANG_SET_UINT_FIELD, GUID_LOPART(guid), opcode, value);
}
else
{
float value = (float)atof(y);
- target->SetFloatValue(opcode , value);
+ target->SetFloatValue(opcode, value);
handler->PSendSysMessage(LANG_SET_FLOAT_FIELD, GUID_LOPART(guid), opcode, value);
}
@@ -1328,7 +1328,7 @@ public:
{
Player* player = handler->GetSession()->GetPlayer();
- sLog->outInfo(LOG_FILTER_SQL_DEV, "(@PATH, XX, %.3f, %.3f, %.5f, 0,0, 0,100, 0),", player->GetPositionX(), player->GetPositionY(), player->GetPositionZ());
+ sLog->outInfo(LOG_FILTER_SQL_DEV, "(@PATH, XX, %.3f, %.3f, %.5f, 0, 0, 0, 100, 0),", player->GetPositionX(), player->GetPositionY(), player->GetPositionZ());
handler->PSendSysMessage("Waypoint SQL written to SQL Developer log");
return true;
diff --git a/src/server/scripts/Commands/cs_disable.cpp b/src/server/scripts/Commands/cs_disable.cpp
index 37e282cac8e..34738777c85 100644
--- a/src/server/scripts/Commands/cs_disable.cpp
+++ b/src/server/scripts/Commands/cs_disable.cpp
@@ -48,6 +48,7 @@ public:
{ "achievement_criteria", SEC_ADMINISTRATOR, true, &HandleRemoveDisableAchievementCriteriaCommand, "", NULL },
{ "outdoorpvp", SEC_ADMINISTRATOR, true, &HandleRemoveDisableOutdoorPvPCommand, "", NULL },
{ "vmap", SEC_ADMINISTRATOR, true, &HandleRemoveDisableVmapCommand, "", NULL },
+ { "mmap", SEC_ADMINISTRATOR, true, &HandleRemoveDisableMMapCommand, "", NULL },
{ NULL, 0, false, NULL, "", NULL }
};
static ChatCommand addDisableCommandTable[] =
@@ -59,6 +60,7 @@ public:
{ "achievement_criteria", SEC_ADMINISTRATOR, true, &HandleAddDisableAchievementCriteriaCommand, "", NULL },
{ "outdoorpvp", SEC_ADMINISTRATOR, true, &HandleAddDisableOutdoorPvPCommand, "", NULL },
{ "vmap", SEC_ADMINISTRATOR, true, &HandleAddDisableVmapCommand, "", NULL },
+ { "mmap", SEC_ADMINISTRATOR, true, &HandleAddDisableMMapCommand, "", NULL },
{ NULL, 0, false, NULL, "", NULL }
};
static ChatCommand disableCommandTable[] =
@@ -172,6 +174,17 @@ public:
disableTypeStr = "vmap";
break;
}
+ case DISABLE_TYPE_MMAP:
+ {
+ if (!sMapStore.LookupEntry(entry))
+ {
+ handler->PSendSysMessage(LANG_COMMAND_NOMAPFOUND);
+ handler->SetSentErrorMessage(true);
+ return false;
+ }
+ disableTypeStr = "mmap";
+ break;
+ }
default:
break;
}
@@ -256,6 +269,14 @@ public:
return HandleAddDisables(handler, args, DISABLE_TYPE_VMAP);
}
+ static bool HandleAddDisableMMapCommand(ChatHandler* handler, char const* args)
+ {
+ if (!*args)
+ return false;
+
+ return HandleAddDisables(handler, args, DISABLE_TYPE_MMAP);
+ }
+
static bool HandleRemoveDisables(ChatHandler* handler, char const* args, uint8 disableType)
{
char* entryStr = strtok((char*)args, " ");
@@ -289,6 +310,9 @@ public:
case DISABLE_TYPE_VMAP:
disableTypeStr = "vmap";
break;
+ case DISABLE_TYPE_MMAP:
+ disableTypeStr = "mmap";
+ break;
}
PreparedStatement* stmt = NULL;
@@ -367,6 +391,14 @@ public:
return HandleRemoveDisables(handler, args, DISABLE_TYPE_VMAP);
}
+
+ static bool HandleRemoveDisableMMapCommand(ChatHandler* handler, char const* args)
+ {
+ if (!*args)
+ return false;
+
+ return HandleRemoveDisables(handler, args, DISABLE_TYPE_MMAP);
+ }
};
void AddSC_disable_commandscript()
diff --git a/src/server/scripts/Commands/cs_misc.cpp b/src/server/scripts/Commands/cs_misc.cpp
index 59315e1a813..54fe41a8a4d 100644
--- a/src/server/scripts/Commands/cs_misc.cpp
+++ b/src/server/scripts/Commands/cs_misc.cpp
@@ -1534,7 +1534,7 @@ public:
return false;
Field* fields = result->Fetch();
- totalPlayerTime = fields[0].GetUInt32();
+ totalPlayerTime = fields[0].GetUInt32();
level = fields[1].GetUInt8();
money = fields[2].GetUInt32();
accId = fields[3].GetUInt32();
@@ -1617,7 +1617,7 @@ public:
if (result2)
{
Field* fields = result2->Fetch();
- banTime = int64(fields[1].GetBool() ? 0 : fields[0].GetUInt32());
+ banTime = int64(fields[1].GetUInt64() ? 0 : fields[0].GetUInt32());
bannedby = fields[2].GetString();
banreason = fields[3].GetString();
}
diff --git a/src/server/scripts/Commands/cs_mmaps.cpp b/src/server/scripts/Commands/cs_mmaps.cpp
new file mode 100644
index 00000000000..97861133983
--- /dev/null
+++ b/src/server/scripts/Commands/cs_mmaps.cpp
@@ -0,0 +1,294 @@
+/*
+ * Copyright (C) 2008-2013 TrinityCore <http://www.trinitycore.org/>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+/**
+* @file cs_mmaps.cpp
+* @brief .mmap related commands
+*
+* This file contains the CommandScripts for all
+* mmap sub-commands
+*/
+
+#include "ScriptMgr.h"
+#include "Chat.h"
+#include "ObjectMgr.h"
+#include "Player.h"
+#include "PointMovementGenerator.h"
+#include "PathGenerator.h"
+#include "MMapFactory.h"
+#include "Map.h"
+#include "TargetedMovementGenerator.h"
+#include "GridNotifiers.h"
+#include "GridNotifiersImpl.h"
+#include "CellImpl.h"
+
+class mmaps_commandscript : public CommandScript
+{
+public:
+ mmaps_commandscript() : CommandScript("mmaps_commandscript") { }
+
+ ChatCommand* GetCommands() const
+ {
+ static ChatCommand mmapCommandTable[] =
+ {
+ { "path", SEC_ADMINISTRATOR, false, &HandleMmapPathCommand, "", NULL },
+ { "loc", SEC_ADMINISTRATOR, false, &HandleMmapLocCommand, "", NULL },
+ { "loadedtiles", SEC_ADMINISTRATOR, false, &HandleMmapLoadedTilesCommand, "", NULL },
+ { "stats", SEC_ADMINISTRATOR, false, &HandleMmapStatsCommand, "", NULL },
+ { "testarea", SEC_ADMINISTRATOR, false, &HandleMmapTestArea, "", NULL },
+ { NULL, 0, false, NULL, "", NULL }
+ };
+
+ static ChatCommand commandTable[] =
+ {
+ { "mmap", SEC_ADMINISTRATOR, true, NULL, "", mmapCommandTable },
+ { NULL, 0, false, NULL, "", NULL }
+ };
+ return commandTable;
+ }
+
+ static bool HandleMmapPathCommand(ChatHandler* handler, char const* args)
+ {
+ if (!MMAP::MMapFactory::createOrGetMMapManager()->GetNavMesh(handler->GetSession()->GetPlayer()->GetMapId()))
+ {
+ handler->PSendSysMessage("NavMesh not loaded for current map.");
+ return true;
+ }
+
+ handler->PSendSysMessage("mmap path:");
+
+ // units
+ Player* player = handler->GetSession()->GetPlayer();
+ Unit* target = handler->getSelectedUnit();
+ if (!player || !target)
+ {
+ handler->PSendSysMessage("Invalid target/source selection.");
+ return true;
+ }
+
+ char* para = strtok((char*)args, " ");
+
+ bool useStraightPath = false;
+ if (para && strcmp(para, "true") == 0)
+ useStraightPath = true;
+
+ // unit locations
+ float x, y, z;
+ player->GetPosition(x, y, z);
+
+ // path
+ PathGenerator path(target);
+ path.SetUseStraightPath(useStraightPath);
+ bool result = path.CalculatePath(x, y, z);
+
+ PointsArray pointPath = path.GetPath();
+ handler->PSendSysMessage("%s's path to %s:", target->GetName().c_str(), player->GetName().c_str());
+ handler->PSendSysMessage("Building: %s", useStraightPath ? "StraightPath" : "SmoothPath");
+ handler->PSendSysMessage("Result: %s - Length: "SIZEFMTD" - Type: %u", (result ? "true" : "false"), pointPath.size(), path.GetPathType());
+
+ Vector3 start = path.GetStartPosition();
+ Vector3 end = path.GetEndPosition();
+ Vector3 actualEnd = path.GetActualEndPosition();
+
+ handler->PSendSysMessage("StartPosition (%.3f, %.3f, %.3f)", start.x, start.y, start.z);
+ handler->PSendSysMessage("EndPosition (%.3f, %.3f, %.3f)", end.x, end.y, end.z);
+ handler->PSendSysMessage("ActualEndPosition (%.3f, %.3f, %.3f)", actualEnd.x, actualEnd.y, actualEnd.z);
+
+ if (!player->isGameMaster())
+ handler->PSendSysMessage("Enable GM mode to see the path points.");
+
+ for (uint32 i = 0; i < pointPath.size(); ++i)
+ player->SummonCreature(VISUAL_WAYPOINT, pointPath[i].x, pointPath[i].y, pointPath[i].z, 0, TEMPSUMMON_TIMED_DESPAWN, 9000);
+
+ return true;
+ }
+
+ static bool HandleMmapLocCommand(ChatHandler* handler, char const* /*args*/)
+ {
+ handler->PSendSysMessage("mmap tileloc:");
+
+ // grid tile location
+ Player* player = handler->GetSession()->GetPlayer();
+
+ int32 gx = 32 - player->GetPositionX() / SIZE_OF_GRIDS;
+ int32 gy = 32 - player->GetPositionY() / SIZE_OF_GRIDS;
+
+ handler->PSendSysMessage("%03u%02i%02i.mmtile", player->GetMapId(), gy, gx);
+ handler->PSendSysMessage("gridloc [%i,%i]", gx, gy);
+
+ // calculate navmesh tile location
+ dtNavMesh const* navmesh = MMAP::MMapFactory::createOrGetMMapManager()->GetNavMesh(handler->GetSession()->GetPlayer()->GetMapId());
+ dtNavMeshQuery const* navmeshquery = MMAP::MMapFactory::createOrGetMMapManager()->GetNavMeshQuery(handler->GetSession()->GetPlayer()->GetMapId(), player->GetInstanceId());
+ if (!navmesh || !navmeshquery)
+ {
+ handler->PSendSysMessage("NavMesh not loaded for current map.");
+ return true;
+ }
+
+ float const* min = navmesh->getParams()->orig;
+ float x, y, z;
+ player->GetPosition(x, y, z);
+ float location[VERTEX_SIZE] = {y, z, x};
+ float extents[VERTEX_SIZE] = {3.0f, 5.0f, 3.0f};
+
+ int32 tilex = int32((y - min[0]) / SIZE_OF_GRIDS);
+ int32 tiley = int32((x - min[2]) / SIZE_OF_GRIDS);
+
+ handler->PSendSysMessage("Calc [%02i,%02i]", tilex, tiley);
+
+ // navmesh poly -> navmesh tile location
+ dtQueryFilter filter = dtQueryFilter();
+ dtPolyRef polyRef = INVALID_POLYREF;
+ navmeshquery->findNearestPoly(location, extents, &filter, &polyRef, NULL);
+
+ if (polyRef == INVALID_POLYREF)
+ handler->PSendSysMessage("Dt [??,??] (invalid poly, probably no tile loaded)");
+ else
+ {
+ dtMeshTile const* tile;
+ dtPoly const* poly;
+ navmesh->getTileAndPolyByRef(polyRef, &tile, &poly);
+ if (tile)
+ handler->PSendSysMessage("Dt [%02i,%02i]", tile->header->x, tile->header->y);
+ else
+ handler->PSendSysMessage("Dt [??,??] (no tile loaded)");
+ }
+
+ return true;
+ }
+
+ static bool HandleMmapLoadedTilesCommand(ChatHandler* handler, char const* /*args*/)
+ {
+ uint32 mapid = handler->GetSession()->GetPlayer()->GetMapId();
+ dtNavMesh const* navmesh = MMAP::MMapFactory::createOrGetMMapManager()->GetNavMesh(mapid);
+ dtNavMeshQuery const* navmeshquery = MMAP::MMapFactory::createOrGetMMapManager()->GetNavMeshQuery(mapid, handler->GetSession()->GetPlayer()->GetInstanceId());
+ if (!navmesh || !navmeshquery)
+ {
+ handler->PSendSysMessage("NavMesh not loaded for current map.");
+ return true;
+ }
+
+ handler->PSendSysMessage("mmap loadedtiles:");
+
+ for (int32 i = 0; i < navmesh->getMaxTiles(); ++i)
+ {
+ dtMeshTile const* tile = navmesh->getTile(i);
+ if (!tile || !tile->header)
+ continue;
+
+ handler->PSendSysMessage("[%02i,%02i]", tile->header->x, tile->header->y);
+ }
+
+ return true;
+ }
+
+ static bool HandleMmapStatsCommand(ChatHandler* handler, char const* /*args*/)
+ {
+ uint32 mapId = handler->GetSession()->GetPlayer()->GetMapId();
+ handler->PSendSysMessage("mmap stats:");
+ handler->PSendSysMessage(" global mmap pathfinding is %sabled", MMAP::MMapFactory::IsPathfindingEnabled(mapId) ? "en" : "dis");
+
+ MMAP::MMapManager* manager = MMAP::MMapFactory::createOrGetMMapManager();
+ handler->PSendSysMessage(" %u maps loaded with %u tiles overall", manager->getLoadedMapsCount(), manager->getLoadedTilesCount());
+
+ dtNavMesh const* navmesh = manager->GetNavMesh(handler->GetSession()->GetPlayer()->GetMapId());
+ if (!navmesh)
+ {
+ handler->PSendSysMessage("NavMesh not loaded for current map.");
+ return true;
+ }
+
+ uint32 tileCount = 0;
+ uint32 nodeCount = 0;
+ uint32 polyCount = 0;
+ uint32 vertCount = 0;
+ uint32 triCount = 0;
+ uint32 triVertCount = 0;
+ uint32 dataSize = 0;
+ for (int32 i = 0; i < navmesh->getMaxTiles(); ++i)
+ {
+ dtMeshTile const* tile = navmesh->getTile(i);
+ if (!tile || !tile->header)
+ continue;
+
+ tileCount++;
+ nodeCount += tile->header->bvNodeCount;
+ polyCount += tile->header->polyCount;
+ vertCount += tile->header->vertCount;
+ triCount += tile->header->detailTriCount;
+ triVertCount += tile->header->detailVertCount;
+ dataSize += tile->dataSize;
+ }
+
+ handler->PSendSysMessage("Navmesh stats:");
+ handler->PSendSysMessage(" %u tiles loaded", tileCount);
+ handler->PSendSysMessage(" %u BVTree nodes", nodeCount);
+ handler->PSendSysMessage(" %u polygons (%u vertices)", polyCount, vertCount);
+ handler->PSendSysMessage(" %u triangles (%u vertices)", triCount, triVertCount);
+ handler->PSendSysMessage(" %.2f MB of data (not including pointers)", ((float)dataSize / sizeof(unsigned char)) / 1048576);
+
+ return true;
+ }
+
+ static bool HandleMmapTestArea(ChatHandler* handler, char const* /*args*/)
+ {
+ float radius = 40.0f;
+ WorldObject* object = handler->GetSession()->GetPlayer();
+
+ CellCoord pair(Trinity::ComputeCellCoord(object->GetPositionX(), object->GetPositionY()));
+ Cell cell(pair);
+ cell.SetNoCreate();
+
+ std::list<Creature*> creatureList;
+
+ Trinity::AnyUnitInObjectRangeCheck go_check(object, radius);
+ Trinity::CreatureListSearcher<Trinity::AnyUnitInObjectRangeCheck> go_search(object, creatureList, go_check);
+ TypeContainerVisitor<Trinity::CreatureListSearcher<Trinity::AnyUnitInObjectRangeCheck>, GridTypeMapContainer> go_visit(go_search);
+
+ // Get Creatures
+ cell.Visit(pair, go_visit, *(object->GetMap()), *object, radius);
+
+ if (!creatureList.empty())
+ {
+ handler->PSendSysMessage("Found "SIZEFMTD" Creatures.", creatureList.size());
+
+ uint32 paths = 0;
+ uint32 uStartTime = getMSTime();
+
+ float gx, gy, gz;
+ object->GetPosition(gx, gy, gz);
+ for (std::list<Creature*>::iterator itr = creatureList.begin(); itr != creatureList.end(); ++itr)
+ {
+ PathGenerator path(*itr);
+ path.CalculatePath(gx, gy, gz);
+ ++paths;
+ }
+
+ uint32 uPathLoadTime = getMSTimeDiff(uStartTime, getMSTime());
+ handler->PSendSysMessage("Generated %i paths in %i ms", paths, uPathLoadTime);
+ }
+ else
+ handler->PSendSysMessage("No creatures in %f yard range.", radius);
+
+ return true;
+ }
+};
+
+void AddSC_mmaps_commandscript()
+{
+ new mmaps_commandscript();
+}
diff --git a/src/server/scripts/Commands/cs_npc.cpp b/src/server/scripts/Commands/cs_npc.cpp
index 8679e288282..7ccd5ed1177 100644
--- a/src/server/scripts/Commands/cs_npc.cpp
+++ b/src/server/scripts/Commands/cs_npc.cpp
@@ -33,6 +33,42 @@ EndScriptData */
#include "Player.h"
#include "Pet.h"
+struct NpcFlagText
+{
+ uint32 flag;
+ int32 text;
+};
+
+#define NPCFLAG_COUNT 24
+
+const NpcFlagText npcFlagTexts[NPCFLAG_COUNT] =
+{
+ { UNIT_NPC_FLAG_AUCTIONEER, LANG_NPCINFO_AUCTIONEER },
+ { UNIT_NPC_FLAG_BANKER, LANG_NPCINFO_BANKER },
+ { UNIT_NPC_FLAG_BATTLEMASTER, LANG_NPCINFO_BATTLEMASTER },
+ { UNIT_NPC_FLAG_FLIGHTMASTER, LANG_NPCINFO_FLIGHTMASTER },
+ { UNIT_NPC_FLAG_GOSSIP, LANG_NPCINFO_GOSSIP },
+ { UNIT_NPC_FLAG_GUILD_BANKER, LANG_NPCINFO_GUILD_BANKER },
+ { UNIT_NPC_FLAG_INNKEEPER, LANG_NPCINFO_INNKEEPER },
+ { UNIT_NPC_FLAG_PETITIONER, LANG_NPCINFO_PETITIONER },
+ { UNIT_NPC_FLAG_PLAYER_VEHICLE, LANG_NPCINFO_PLAYER_VEHICLE },
+ { UNIT_NPC_FLAG_QUESTGIVER, LANG_NPCINFO_QUESTGIVER },
+ { UNIT_NPC_FLAG_REPAIR, LANG_NPCINFO_REPAIR },
+ { UNIT_NPC_FLAG_SPELLCLICK, LANG_NPCINFO_SPELLCLICK },
+ { UNIT_NPC_FLAG_SPIRITGUIDE, LANG_NPCINFO_SPIRITGUIDE },
+ { UNIT_NPC_FLAG_SPIRITHEALER, LANG_NPCINFO_SPIRITHEALER },
+ { UNIT_NPC_FLAG_STABLEMASTER, LANG_NPCINFO_STABLEMASTER },
+ { UNIT_NPC_FLAG_TABARDDESIGNER, LANG_NPCINFO_TABARDDESIGNER },
+ { UNIT_NPC_FLAG_TRAINER, LANG_NPCINFO_TRAINER },
+ { UNIT_NPC_FLAG_TRAINER_CLASS, LANG_NPCINFO_TRAINER_CLASS },
+ { UNIT_NPC_FLAG_TRAINER_PROFESSION, LANG_NPCINFO_TRAINER_PROFESSION },
+ { UNIT_NPC_FLAG_VENDOR, LANG_NPCINFO_VENDOR },
+ { UNIT_NPC_FLAG_VENDOR_AMMO, LANG_NPCINFO_VENDOR_AMMO },
+ { UNIT_NPC_FLAG_VENDOR_FOOD, LANG_NPCINFO_VENDOR_FOOD },
+ { UNIT_NPC_FLAG_VENDOR_POISON, LANG_NPCINFO_VENDOR_POISON },
+ { UNIT_NPC_FLAG_VENDOR_REAGENT, LANG_NPCINFO_VENDOR_REAGENT }
+};
+
class npc_commandscript : public CommandScript
{
public:
@@ -619,11 +655,9 @@ public:
handler->PSendSysMessage(LANG_NPCINFO_POSITION, float(target->GetPositionX()), float(target->GetPositionY()), float(target->GetPositionZ()));
handler->PSendSysMessage(LANG_NPCINFO_AIINFO, target->GetAIName().c_str(), target->GetScriptName().c_str());
- if (npcflags & UNIT_NPC_FLAG_VENDOR)
- handler->SendSysMessage(LANG_NPCINFO_VENDOR);
-
- if (npcflags & UNIT_NPC_FLAG_TRAINER)
- handler->SendSysMessage(LANG_NPCINFO_TRAINER);
+ for (uint8 i = 0; i < NPCFLAG_COUNT; i++)
+ if (npcflags & npcFlagTexts[i].flag)
+ handler->PSendSysMessage(npcFlagTexts[i].text, npcFlagTexts[i].flag);
return true;
}
diff --git a/src/server/scripts/Commands/cs_quest.cpp b/src/server/scripts/Commands/cs_quest.cpp
index a8748bc1adf..e10cf883518 100644
--- a/src/server/scripts/Commands/cs_quest.cpp
+++ b/src/server/scripts/Commands/cs_quest.cpp
@@ -80,7 +80,7 @@ public:
// check item starting quest (it can work incorrectly if added without item in inventory)
ItemTemplateContainer const* itc = sObjectMgr->GetItemTemplateStore();
- ItemTemplateContainer::const_iterator result = find_if(itc->begin(), itc->end(), Finder<uint32, ItemTemplate>(entry, &ItemTemplate::StartQuest));
+ ItemTemplateContainer::const_iterator result = find_if (itc->begin(), itc->end(), Finder<uint32, ItemTemplate>(entry, &ItemTemplate::StartQuest));
if (result != itc->end())
{
diff --git a/src/server/scripts/Commands/cs_ticket.cpp b/src/server/scripts/Commands/cs_ticket.cpp
index 3215b533bce..aee01f47581 100644
--- a/src/server/scripts/Commands/cs_ticket.cpp
+++ b/src/server/scripts/Commands/cs_ticket.cpp
@@ -386,12 +386,13 @@ public:
return true;
}
+ std::string assignedTo = ticket->GetAssignedToName(); // copy assignedto name because we need it after the ticket has been unnassigned
SQLTransaction trans = SQLTransaction(NULL);
ticket->SetUnassigned();
ticket->SaveToDB(trans);
sTicketMgr->UpdateLastChange();
- std::string msg = ticket->FormatMessageString(*handler, NULL, ticket->GetAssignedToName().c_str(),
+ std::string msg = ticket->FormatMessageString(*handler, NULL, assignedTo.c_str(),
handler->GetSession() ? handler->GetSession()->GetPlayer()->GetName().c_str() : "Console", NULL);
handler->SendGlobalGMSysMessage(msg.c_str());
diff --git a/src/server/scripts/EasternKingdoms/BlackrockSpire/boss_pyroguard_emberseer.cpp b/src/server/scripts/EasternKingdoms/BlackrockSpire/boss_pyroguard_emberseer.cpp
index 047ad3f7096..da38fe9f54b 100644
--- a/src/server/scripts/EasternKingdoms/BlackrockSpire/boss_pyroguard_emberseer.cpp
+++ b/src/server/scripts/EasternKingdoms/BlackrockSpire/boss_pyroguard_emberseer.cpp
@@ -66,7 +66,6 @@ public:
{
if (instance->GetBossState(DATA_PYROGAURD_EMBERSEER) == IN_PROGRESS)
OpenDoors(false);
- instance->SetBossState(DATA_PYROGAURD_EMBERSEER,NOT_STARTED);
// respawn any dead Blackhand Incarcerators
DoCast(me, SPELL_ENCAGED_EMBERSEER);
//DoCast(me, SPELL_FIRE_SHIELD_TRIGGER);
@@ -83,7 +82,6 @@ public:
void JustDied(Unit* /*killer*/)
{
- instance->SetBossState(DATA_PYROGAURD_EMBERSEER,DONE);
OpenDoors(true);
_JustDied();
}
@@ -102,7 +100,6 @@ public:
void UpdateAI(uint32 const diff)
{
if (!UpdateVictim())
-
return;
events.Update(diff);
@@ -115,11 +112,11 @@ public:
switch (eventId)
{
case EVENT_FIRENOVA:
- DoCast(me->getVictim(), SPELL_FIRENOVA);
+ DoCastVictim(SPELL_FIRENOVA);
events.ScheduleEvent(EVENT_FIRENOVA, 6 * IN_MILLISECONDS);
break;
case EVENT_FLAMEBUFFET:
- DoCast(me->getVictim(), SPELL_FLAMEBUFFET);
+ DoCastVictim(SPELL_FLAMEBUFFET);
events.ScheduleEvent(EVENT_FLAMEBUFFET, 14 * IN_MILLISECONDS);
break;
case EVENT_PYROBLAST:
@@ -129,6 +126,7 @@ public:
break;
}
}
+
DoMeleeAttackIfReady();
}
};
diff --git a/src/server/scripts/EasternKingdoms/BlackrockSpire/instance_blackrock_spire.cpp b/src/server/scripts/EasternKingdoms/BlackrockSpire/instance_blackrock_spire.cpp
index 72a5712181e..06ba0036ef3 100644
--- a/src/server/scripts/EasternKingdoms/BlackrockSpire/instance_blackrock_spire.cpp
+++ b/src/server/scripts/EasternKingdoms/BlackrockSpire/instance_blackrock_spire.cpp
@@ -215,7 +215,8 @@ public:
switch (eventId)
{
case EVENT_PYROGUARD_EMBERSEER:
- SetBossState(DATA_PYROGAURD_EMBERSEER,IN_PROGRESS);
+ if (GetBossState(DATA_PYROGAURD_EMBERSEER) == NOT_STARTED)
+ SetBossState(DATA_PYROGAURD_EMBERSEER, IN_PROGRESS);
break;
default:
break;
diff --git a/src/server/scripts/EasternKingdoms/Karazhan/boss_prince_malchezaar.cpp b/src/server/scripts/EasternKingdoms/Karazhan/boss_prince_malchezaar.cpp
index 3a026d23faa..ae4b6197f4c 100644
--- a/src/server/scripts/EasternKingdoms/Karazhan/boss_prince_malchezaar.cpp
+++ b/src/server/scripts/EasternKingdoms/Karazhan/boss_prince_malchezaar.cpp
@@ -603,7 +603,7 @@ public:
void netherspite_infernal::netherspite_infernalAI::Cleanup()
{
- Creature *pMalchezaar = Unit::GetCreature(*me, malchezaar);
+ Creature* pMalchezaar = Unit::GetCreature(*me, malchezaar);
if (pMalchezaar && pMalchezaar->isAlive())
CAST_AI(boss_malchezaar::boss_malchezaarAI, pMalchezaar->AI())->Cleanup(me, point);
diff --git a/src/server/scripts/EasternKingdoms/ScarletEnclave/chapter1.cpp b/src/server/scripts/EasternKingdoms/ScarletEnclave/chapter1.cpp
index 97fe57c5434..96293c635d6 100644
--- a/src/server/scripts/EasternKingdoms/ScarletEnclave/chapter1.cpp
+++ b/src/server/scripts/EasternKingdoms/ScarletEnclave/chapter1.cpp
@@ -146,7 +146,7 @@ public:
me->CastSpell(me, SPELL_DK_INITIATE_VISUAL, true);
if (Player* starter = Unit::GetPlayer(*me, playerGUID))
- Talk(SAY_EVENT_ATTACK);
+ sCreatureTextMgr->SendChat(me, SAY_EVENT_ATTACK, 0, CHAT_MSG_ADDON, LANG_ADDON, TEXT_RANGE_NORMAL, 0, TEAM_OTHER, false, starter);
phase = PHASE_TO_ATTACK;
}
diff --git a/src/server/scripts/EasternKingdoms/ScarletEnclave/zone_the_scarlet_enclave.cpp b/src/server/scripts/EasternKingdoms/ScarletEnclave/zone_the_scarlet_enclave.cpp
index 32ce484f715..27e057ce6aa 100644
--- a/src/server/scripts/EasternKingdoms/ScarletEnclave/zone_the_scarlet_enclave.cpp
+++ b/src/server/scripts/EasternKingdoms/ScarletEnclave/zone_the_scarlet_enclave.cpp
@@ -104,7 +104,7 @@ public:
{
me->HandleEmoteCommand(EMOTE_ONESHOT_CUSTOM_SPELL_01);
DoCast(player, SPELL_REVIVE, true);
- Talk(WHISPER_REVIVE,player->GetGUID());
+ Talk(WHISPER_REVIVE, player->GetGUID());
}
FlyBackTimer = 5000;
break;
diff --git a/src/server/scripts/EasternKingdoms/Scholomance/boss_darkmaster_gandling.cpp b/src/server/scripts/EasternKingdoms/Scholomance/boss_darkmaster_gandling.cpp
index 4e2a1b6fa19..5d164fbfafa 100644
--- a/src/server/scripts/EasternKingdoms/Scholomance/boss_darkmaster_gandling.cpp
+++ b/src/server/scripts/EasternKingdoms/Scholomance/boss_darkmaster_gandling.cpp
@@ -58,20 +58,21 @@ class boss_darkmaster_gandling : public CreatureScript
void Reset()
{
+ _Reset();
if (GameObject* gate = me->GetMap()->GetGameObject(instance->GetData64(GO_GATE_GANDLING)))
gate->SetGoState(GO_STATE_ACTIVE);
}
void JustDied(Unit* /*killer*/)
{
- if (instance)
- instance->SetData(DATA_DARKMASTERGANDLING, DONE);
+ _JustDied();
if (GameObject* gate = me->GetMap()->GetGameObject(instance->GetData64(GO_GATE_GANDLING)))
gate->SetGoState(GO_STATE_ACTIVE);
}
void EnterCombat(Unit* /*who*/)
{
+ _EnterCombat();
events.ScheduleEvent(EVENT_ARCANEMISSILES, 4500);
events.ScheduleEvent(EVENT_SHADOWSHIELD, 12000);
events.ScheduleEvent(EVENT_CURSE, 2000);
@@ -116,7 +117,7 @@ class boss_darkmaster_gandling : public CreatureScript
case EVENT_SHADOW_PORTAL:
if (HealthAbovePct(3))
{
- DoCast(SelectTarget(SELECT_TARGET_RANDOM,0, 100, true),SPELL_SHADOW_PORTAL,true);
+ DoCast(SelectTarget(SELECT_TARGET_RANDOM, 0, 100, true), SPELL_SHADOW_PORTAL, true);
events.ScheduleEvent(EVENT_SHADOW_PORTAL, urand(17000, 27000));
}
}
@@ -171,7 +172,7 @@ class spell_shadow_portal : public SpellScriptLoader
{
if (attempts++ >= 6) break;
- switch (urand(0,5))
+ switch (urand(0, 5))
{
case ROOM_HALL_OF_SECRETS:
if (InstanceScript* instance = GetCaster()->GetInstanceScript())
@@ -222,7 +223,7 @@ class spell_shadow_portal : public SpellScriptLoader
}
};
-// Script for Shadow Portal spells 17863,17939,17943,17944,17946,17948
+// Script for Shadow Portal spells 17863, 17939, 17943, 17944, 17946, 17948
Position const SummonPos[18] =
{
// Hall of Secrects
@@ -330,7 +331,7 @@ class spell_shadow_portal_rooms : public SpellScriptLoader
if (Summoned)
{
Summoned->GetMotionMaster()->MoveRandom(5);
- Summoned->AI()->SetData(0,phase_to_set);
+ Summoned->AI()->SetData(0, phase_to_set);
}
}
diff --git a/src/server/scripts/EasternKingdoms/Scholomance/boss_doctor_theolen_krastinov.cpp b/src/server/scripts/EasternKingdoms/Scholomance/boss_doctor_theolen_krastinov.cpp
index fc54b90ab45..b2a91f4efe4 100644
--- a/src/server/scripts/EasternKingdoms/Scholomance/boss_doctor_theolen_krastinov.cpp
+++ b/src/server/scripts/EasternKingdoms/Scholomance/boss_doctor_theolen_krastinov.cpp
@@ -53,16 +53,9 @@ class boss_doctor_theolen_krastinov : public CreatureScript
{
boss_theolenkrastinovAI(Creature* creature) : BossAI(creature, DATA_DOCTORTHEOLENKRASTINOV) {}
- void Reset() {}
-
- void JustDied(Unit* /*killer*/)
- {
- if (instance)
- instance->SetData(DATA_DOCTORTHEOLENKRASTINOV, DONE);
- }
-
void EnterCombat(Unit* /*who*/)
{
+ _EnterCombat();
events.ScheduleEvent(EVENT_REND, 8000);
events.ScheduleEvent(EVENT_BACKHAND, 9000);
events.ScheduleEvent(EVENT_FRENZY, 1000);
diff --git a/src/server/scripts/EasternKingdoms/Scholomance/boss_illucia_barov.cpp b/src/server/scripts/EasternKingdoms/Scholomance/boss_illucia_barov.cpp
index 64d827e41f1..617dff17220 100644
--- a/src/server/scripts/EasternKingdoms/Scholomance/boss_illucia_barov.cpp
+++ b/src/server/scripts/EasternKingdoms/Scholomance/boss_illucia_barov.cpp
@@ -48,18 +48,11 @@ class boss_illucia_barov : public CreatureScript
struct boss_illuciabarovAI : public BossAI
{
- boss_illuciabarovAI(Creature* creature) : BossAI(creature,DATA_LADYILLUCIABAROV) {}
-
- void Reset() {}
-
- void JustDied(Unit* /*killer*/)
- {
- if (instance)
- instance->SetData(DATA_LADYILLUCIABAROV, DONE);
- }
+ boss_illuciabarovAI(Creature* creature) : BossAI(creature, DATA_LADYILLUCIABAROV) {}
void EnterCombat(Unit* /*who*/)
{
+ _EnterCombat();
events.ScheduleEvent(EVENT_CURSEOFAGONY, 18000);
events.ScheduleEvent(EVENT_SHADOWSHOCK, 9000);
events.ScheduleEvent(EVENT_SILENCE, 5000);
@@ -85,7 +78,7 @@ class boss_illucia_barov : public CreatureScript
events.ScheduleEvent(EVENT_CURSEOFAGONY, 30000);
break;
case EVENT_SHADOWSHOCK:
- DoCast(SelectTarget(SELECT_TARGET_RANDOM,0, 100, true),SPELL_SHADOWSHOCK,true);
+ DoCast(SelectTarget(SELECT_TARGET_RANDOM, 0, 100, true), SPELL_SHADOWSHOCK, true);
events.ScheduleEvent(EVENT_SHADOWSHOCK, 12000);
break;
case EVENT_SILENCE:
diff --git a/src/server/scripts/EasternKingdoms/Scholomance/boss_instructor_malicia.cpp b/src/server/scripts/EasternKingdoms/Scholomance/boss_instructor_malicia.cpp
index 1f55666acc3..e2c7287f178 100644
--- a/src/server/scripts/EasternKingdoms/Scholomance/boss_instructor_malicia.cpp
+++ b/src/server/scripts/EasternKingdoms/Scholomance/boss_instructor_malicia.cpp
@@ -57,18 +57,14 @@ class boss_instructor_malicia : public CreatureScript
void Reset()
{
+ _Reset();
FlashCounter = 0;
TouchCounter = 0;
}
- void JustDied(Unit* /*killer*/)
- {
- if (instance)
- instance->SetData(DATA_INSTRUCTORMALICIA, DONE);
- }
-
void EnterCombat(Unit* /*who*/)
{
+ _EnterCombat();
events.ScheduleEvent(EVENT_CALLOFGRAVES, 4000);
events.ScheduleEvent(EVENT_CORRUPTION, 8000);
events.ScheduleEvent(EVENT_RENEW, 32000);
@@ -95,7 +91,7 @@ class boss_instructor_malicia : public CreatureScript
events.ScheduleEvent(EVENT_CALLOFGRAVES, 65000);
break;
case EVENT_CORRUPTION:
- DoCast(SelectTarget(SELECT_TARGET_RANDOM,0, 100, true),SPELL_CORRUPTION,true);
+ DoCast(SelectTarget(SELECT_TARGET_RANDOM, 0, 100, true), SPELL_CORRUPTION, true);
events.ScheduleEvent(EVENT_CORRUPTION, 24000);
break;
case EVENT_RENEW:
diff --git a/src/server/scripts/EasternKingdoms/Scholomance/boss_kirtonos_the_herald.cpp b/src/server/scripts/EasternKingdoms/Scholomance/boss_kirtonos_the_herald.cpp
index 9d8f448964c..19660cec4af 100644
--- a/src/server/scripts/EasternKingdoms/Scholomance/boss_kirtonos_the_herald.cpp
+++ b/src/server/scripts/EasternKingdoms/Scholomance/boss_kirtonos_the_herald.cpp
@@ -64,6 +64,12 @@ enum eMisc
KIRTONOS_PATH = 105061
};
+Position const PosMove[2] =
+{
+ { 299.4884f, 92.76137f, 105.6335f, 0.0f },
+ { 314.8673f, 90.30210f, 101.6459f, 0.0f }
+};
+
class boss_kirtonos_the_herald : public CreatureScript
{
public: boss_kirtonos_the_herald() : CreatureScript("boss_kirtonos_the_herald") { }
@@ -74,15 +80,11 @@ class boss_kirtonos_the_herald : public CreatureScript
void Reset()
{
- _introEvent = 0;
- _introTimer = 0;
_Reset();
}
void EnterCombat(Unit* /*who*/)
{
- _introTimer = 0;
- _introEvent = 0;
events.ScheduleEvent(EVENT_SWOOP, urand(8000, 8000));
events.ScheduleEvent(EVENT_WING_FLAP, urand(15000, 15000));
events.ScheduleEvent(EVENT_PIERCE_ARMOR, urand(18000, 18000));
@@ -103,8 +105,7 @@ class boss_kirtonos_the_herald : public CreatureScript
brazier->ResetDoorOrButton();
brazier->SetGoState(GO_STATE_READY);
}
- if (instance)
- instance->SetData(DATA_KIRTONOS, DONE);
+ _JustDied();
}
void EnterEvadeMode()
@@ -121,12 +122,10 @@ class boss_kirtonos_the_herald : public CreatureScript
void IsSummonedBy(Unit* /*summoner*/)
{
+ events.ScheduleEvent(INTRO_1, 500);
me->SetDisableGravity(true);
me->SetReactState(REACT_PASSIVE);
me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE|UNIT_FLAG_NOT_SELECTABLE);
- _introEvent = INTRO_1;
- _introTimer = 1;
- _currentPoint = 0;
Talk(EMOTE_SUMMONED);
}
@@ -139,68 +138,58 @@ class boss_kirtonos_the_herald : public CreatureScript
{
if (type == WAYPOINT_MOTION_TYPE && id == POINT_KIRTONOS_LAND)
{
- _introTimer = 1500;
- _introEvent = INTRO_2;
+ events.ScheduleEvent(INTRO_2, 1500);
}
}
void UpdateAI(uint32 const diff)
{
- if (_introEvent)
+ events.Update(diff);
+
+ while (uint32 eventId = events.ExecuteEvent() && !UpdateVictim())
{
- if (_introTimer <= diff)
+ switch (eventId)
{
- switch (_introEvent)
- {
- case INTRO_1:
- me->GetMotionMaster()->MovePath(KIRTONOS_PATH,false);
- _introEvent = 0;
- break;
- case INTRO_2:
- me->GetMotionMaster()->MovePoint(0, 299.4884f, 92.76137f, 105.6335f);
- _introTimer = 1000;
- _introEvent = INTRO_3;
- break;
- case INTRO_3:
- if (GameObject* gate = me->GetMap()->GetGameObject(instance->GetData64(GO_GATE_KIRTONOS)))
- gate->SetGoState(GO_STATE_READY);
- me->SetFacingTo(0.01745329f);
- _introTimer = 3000;
- _introEvent = INTRO_4;
- break;
- case INTRO_4:
- if (GameObject* brazier = me->GetMap()->GetGameObject(instance->GetData64(GO_BRAZIER_OF_THE_HERALD)))
- brazier->SetGoState(GO_STATE_READY);
- me->SetWalk(true);
- me->SetDisableGravity(false);
- DoCast(me, SPELL_KIRTONOS_TRANSFORM);
- me->SetCanFly(false);
- _introTimer = 1000;
- _introEvent = INTRO_5;
- break;
- case INTRO_5:
- me->HandleEmoteCommand(EMOTE_ONESHOT_ROAR);
- me->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID + 0, uint32(WEAPON_KIRTONOS_STAFF));
- me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE|UNIT_FLAG_NOT_SELECTABLE);
- me->SetReactState(REACT_AGGRESSIVE);
- _introTimer = 5000;
- _introEvent = INTRO_6;
- case INTRO_6:
- me->GetMotionMaster()->MovePoint(0, 314.8673f, 90.3021f, 101.6459f);
- _introTimer = 0;
- _introEvent = 0;
+ case INTRO_1:
+ me->GetMotionMaster()->MovePath(KIRTONOS_PATH, false);
+ break;
+ case INTRO_2:
+ me->GetMotionMaster()->MovePoint(0, PosMove[0]);
+ events.ScheduleEvent(INTRO_3, 1000);
+ break;
+ case INTRO_3:
+ if (GameObject* gate = me->GetMap()->GetGameObject(instance->GetData64(GO_GATE_KIRTONOS)))
+ gate->SetGoState(GO_STATE_READY);
+ me->SetFacingTo(0.01745329f);
+ events.ScheduleEvent(INTRO_4, 3000);
+ break;
+ case INTRO_4:
+ if (GameObject* brazier = me->GetMap()->GetGameObject(instance->GetData64(GO_BRAZIER_OF_THE_HERALD)))
+ brazier->SetGoState(GO_STATE_READY);
+ me->SetWalk(true);
+ me->SetDisableGravity(false);
+ DoCast(me, SPELL_KIRTONOS_TRANSFORM);
+ me->SetCanFly(false);
+ events.ScheduleEvent(INTRO_5, 1000);
+ break;
+ case INTRO_5:
+ me->HandleEmoteCommand(EMOTE_ONESHOT_ROAR);
+ me->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID + 0, uint32(WEAPON_KIRTONOS_STAFF));
+ me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE|UNIT_FLAG_NOT_SELECTABLE);
+ me->SetReactState(REACT_AGGRESSIVE);
+ events.ScheduleEvent(INTRO_6, 5000);
+ break;
+ case INTRO_6:
+ me->GetMotionMaster()->MovePoint(0, PosMove[1]);
+ break;
+ default:
break;
- }
}
- else
- _introTimer -= diff;
}
if (!UpdateVictim())
return;
- events.Update(diff);
-
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
@@ -258,11 +247,6 @@ class boss_kirtonos_the_herald : public CreatureScript
DoMeleeAttackIfReady();
}
-
- private:
- uint8 _introEvent;
- uint32 _introTimer;
- uint32 _currentPoint;
};
CreatureAI* GetAI(Creature* creature) const
@@ -281,6 +265,11 @@ enum Brazier_Of_The_Herald
SOUND_SCREECH = 557
};
+Position const PosSummon[1] =
+{
+ { 315.028f, 70.53845f, 102.1496f, 0.3859715f }
+};
+
class go_brazier_of_the_herald : public GameObjectScript
{
public:
@@ -290,7 +279,7 @@ class go_brazier_of_the_herald : public GameObjectScript
{
go->UseDoorOrButton();
go->PlayDirectSound(SOUND_SCREECH, 0);
- player->SummonCreature(NPC_KIRTONOS, 315.028f, 70.53845f, 102.1496f, 0.3859715f, TEMPSUMMON_DEAD_DESPAWN, 900000);
+ player->SummonCreature(NPC_KIRTONOS, PosSummon[0], TEMPSUMMON_DEAD_DESPAWN, 900000);
return true;
}
};
diff --git a/src/server/scripts/EasternKingdoms/Scholomance/boss_lord_alexei_barov.cpp b/src/server/scripts/EasternKingdoms/Scholomance/boss_lord_alexei_barov.cpp
index c246e781bd8..3a7aee0d6c6 100644
--- a/src/server/scripts/EasternKingdoms/Scholomance/boss_lord_alexei_barov.cpp
+++ b/src/server/scripts/EasternKingdoms/Scholomance/boss_lord_alexei_barov.cpp
@@ -48,17 +48,13 @@ class boss_lord_alexei_barov : public CreatureScript
void Reset()
{
+ _Reset();
me->LoadCreaturesAddon();
}
- void JustDied(Unit* /*killer*/)
- {
- if (instance)
- instance->SetData(DATA_LORDALEXEIBAROV, DONE);
- }
-
void EnterCombat(Unit* /*who*/)
{
+ _EnterCombat();
events.ScheduleEvent(EVENT_IMMOLATE, 7000);
events.ScheduleEvent(EVENT_VEILOFSHADOW, 15000);
}
@@ -78,7 +74,7 @@ class boss_lord_alexei_barov : public CreatureScript
switch (eventId)
{
case EVENT_IMMOLATE:
- DoCast(SelectTarget(SELECT_TARGET_RANDOM,0, 100, true),SPELL_IMMOLATE,true);
+ DoCast(SelectTarget(SELECT_TARGET_RANDOM, 0, 100, true), SPELL_IMMOLATE, true);
events.ScheduleEvent(EVENT_IMMOLATE, 12000);
break;
case EVENT_VEILOFSHADOW:
diff --git a/src/server/scripts/EasternKingdoms/Scholomance/boss_lorekeeper_polkelt.cpp b/src/server/scripts/EasternKingdoms/Scholomance/boss_lorekeeper_polkelt.cpp
index 2239194a5e3..d06189d5054 100644
--- a/src/server/scripts/EasternKingdoms/Scholomance/boss_lorekeeper_polkelt.cpp
+++ b/src/server/scripts/EasternKingdoms/Scholomance/boss_lorekeeper_polkelt.cpp
@@ -50,16 +50,9 @@ class boss_lorekeeper_polkelt : public CreatureScript
{
boss_lorekeeperpolkeltAI(Creature* creature) : BossAI(creature, DATA_LOREKEEPERPOLKELT) {}
- void Reset() {}
-
- void JustDied(Unit* /*killer*/)
- {
- if (instance)
- instance->SetData(DATA_LOREKEEPERPOLKELT, DONE);
- }
-
void EnterCombat(Unit* /*who*/)
{
+ _EnterCombat();
events.ScheduleEvent(EVENT_VOLATILEINFECTION, 38000);
events.ScheduleEvent(EVENT_DARKPLAGUE, 8000);
events.ScheduleEvent(EVENT_CORROSIVEACID, 45000);
diff --git a/src/server/scripts/EasternKingdoms/Scholomance/boss_the_ravenian.cpp b/src/server/scripts/EasternKingdoms/Scholomance/boss_the_ravenian.cpp
index e2e04ae7b81..99b9fba958c 100644
--- a/src/server/scripts/EasternKingdoms/Scholomance/boss_the_ravenian.cpp
+++ b/src/server/scripts/EasternKingdoms/Scholomance/boss_the_ravenian.cpp
@@ -42,7 +42,6 @@ enum Events
EVENT_KNOCKAWAY = 4
};
-
class boss_the_ravenian : public CreatureScript
{
public: boss_the_ravenian() : CreatureScript("boss_the_ravenian") { }
@@ -51,16 +50,9 @@ class boss_the_ravenian : public CreatureScript
{
boss_theravenianAI(Creature* creature) : BossAI(creature, DATA_THERAVENIAN) {}
- void Reset() {}
-
- void JustDied(Unit* /*killer*/)
- {
- if (instance)
- instance->SetData(DATA_THERAVENIAN, DONE);
- }
-
void EnterCombat(Unit* /*who*/)
{
+ _EnterCombat();
events.ScheduleEvent(EVENT_TRAMPLE, 24000);
events.ScheduleEvent(EVENT_CLEAVE, 15000);
events.ScheduleEvent(EVENT_SUNDERINCLEAVE, 40000);
diff --git a/src/server/scripts/EasternKingdoms/Scholomance/instance_scholomance.cpp b/src/server/scripts/EasternKingdoms/Scholomance/instance_scholomance.cpp
index d97372d4379..0da31fbe428 100644
--- a/src/server/scripts/EasternKingdoms/Scholomance/instance_scholomance.cpp
+++ b/src/server/scripts/EasternKingdoms/Scholomance/instance_scholomance.cpp
@@ -15,160 +15,215 @@
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-/* ScriptData
-Name: Instance_Scholomance
-%Complete: 100
-Comment:
-Category: Scholomance
-EndScriptData */
-
#include "ScriptMgr.h"
#include "InstanceScript.h"
-#include "scholomance.h"
#include "Player.h"
+#include "scholomance.h"
-enum CreatureId
-{
- NPC_DARKMASTER_GANDLING = 1853
-};
-
-Position const GandlingLoc = {180.7712f, -5.428603f, 75.57024f, 1.291544f};
+Position const GandlingLoc = { 180.7712f, -5.428603f, 75.57024f, 1.291544f };
class instance_scholomance : public InstanceMapScript
{
-public:
- instance_scholomance() : InstanceMapScript("instance_scholomance", 289) { }
-
- InstanceScript* GetInstanceScript(InstanceMap* map) const
- {
- return new instance_scholomance_InstanceMapScript(map);
- }
-
- struct instance_scholomance_InstanceMapScript : public InstanceScript
- {
- instance_scholomance_InstanceMapScript(Map* map) : InstanceScript(map) {}
-
- //Lord Alexei Barov, Doctor Theolen Krastinov, The Ravenian, Lorekeeper Polkelt, Instructor Malicia and the Lady Illucia Barov.
- uint32 m_auiEncounter[MAX_ENCOUNTER];
- uint64 GateKirtonosGUID;
- uint64 GateGandlingGUID;
- uint64 GateMiliciaGUID;
- uint64 GateTheolenGUID;
- uint64 GatePolkeltGUID;
- uint64 GateRavenianGUID;
- uint64 GateBarovGUID;
- uint64 GateIlluciaGUID;
- uint64 BrazierOfTheHeraldGUID;
-
- void Initialize()
+ public:
+ instance_scholomance() : InstanceMapScript("instance_scholomance", 289) { }
+
+ InstanceScript* GetInstanceScript(InstanceMap* map) const
{
- GateKirtonosGUID = 0;
- GateGandlingGUID = 0;
- GateMiliciaGUID = 0;
- GateTheolenGUID = 0;
- GatePolkeltGUID = 0;
- GateRavenianGUID = 0;
- GateBarovGUID = 0;
- GateIlluciaGUID = 0;
- BrazierOfTheHeraldGUID = 0;
-
- for (uint8 i = 0; i < MAX_ENCOUNTER; ++i)
- m_auiEncounter[i] = NOT_STARTED;
+ return new instance_scholomance_InstanceMapScript(map);
}
- void OnGameObjectCreate(GameObject* go)
+ struct instance_scholomance_InstanceMapScript : public InstanceScript
{
- switch (go->GetEntry())
+ instance_scholomance_InstanceMapScript(Map* map) : InstanceScript(map)
{
- case GO_GATE_KIRTONOS: GateKirtonosGUID = go->GetGUID(); break;
- case GO_GATE_GANDLING: GateGandlingGUID = go->GetGUID(); break;
- case GO_GATE_MALICIA: GateMiliciaGUID = go->GetGUID(); break;
- case GO_GATE_THEOLEN: GateTheolenGUID = go->GetGUID(); break;
- case GO_GATE_POLKELT: GatePolkeltGUID = go->GetGUID(); break;
- case GO_GATE_RAVENIAN: GateRavenianGUID = go->GetGUID(); break;
- case GO_GATE_BAROV: GateBarovGUID = go->GetGUID(); break;
- case GO_GATE_ILLUCIA: GateIlluciaGUID = go->GetGUID(); break;
- case GO_BRAZIER_OF_THE_HERALD: BrazierOfTheHeraldGUID = go->GetGUID(); break;
+ SetBossNumber(EncounterCount);
+ GateKirtonosGUID = 0;
+ GateGandlingGUID = 0;
+ GateMiliciaGUID = 0;
+ GateTheolenGUID = 0;
+ GatePolkeltGUID = 0;
+ GateRavenianGUID = 0;
+ GateBarovGUID = 0;
+ GateIlluciaGUID = 0;
+ BrazierOfTheHeraldGUID = 0;
}
- }
- void SetData(uint32 type, uint32 data)
- {
- switch (type)
+ void OnGameObjectCreate(GameObject* go)
{
- case DATA_LORDALEXEIBAROV:
- m_auiEncounter[DATA_LORDALEXEIBAROV] = data;
- CheckToSpawnGandling();
- break;
- case DATA_DOCTORTHEOLENKRASTINOV:
- m_auiEncounter[DATA_DOCTORTHEOLENKRASTINOV] = data;
- CheckToSpawnGandling();
- break;
- case DATA_THERAVENIAN:
- m_auiEncounter[DATA_THERAVENIAN] = data;
- CheckToSpawnGandling();
- break;
- case DATA_LOREKEEPERPOLKELT:
- m_auiEncounter[DATA_LOREKEEPERPOLKELT] = data;
- CheckToSpawnGandling();
- break;
- case DATA_INSTRUCTORMALICIA:
- m_auiEncounter[DATA_INSTRUCTORMALICIA] = data;
- CheckToSpawnGandling();
- break;
- case DATA_LADYILLUCIABAROV:
- m_auiEncounter[DATA_LADYILLUCIABAROV] = data;
- CheckToSpawnGandling();
- break;
- case DATA_DARKMASTERGANDLING:
- m_auiEncounter[DATA_DARKMASTERGANDLING] = data;
- break;
- case DATA_KIRTONOS:
- m_auiEncounter[DATA_KIRTONOS] = data;
- break;
+ switch (go->GetEntry())
+ {
+ case GO_GATE_KIRTONOS:
+ GateKirtonosGUID = go->GetGUID();
+ break;
+ case GO_GATE_GANDLING:
+ GateGandlingGUID = go->GetGUID();
+ break;
+ case GO_GATE_MALICIA:
+ GateMiliciaGUID = go->GetGUID();
+ break;
+ case GO_GATE_THEOLEN:
+ GateTheolenGUID = go->GetGUID();
+ break;
+ case GO_GATE_POLKELT:
+ GatePolkeltGUID = go->GetGUID();
+ break;
+ case GO_GATE_RAVENIAN:
+ GateRavenianGUID = go->GetGUID();
+ break;
+ case GO_GATE_BAROV:
+ GateBarovGUID = go->GetGUID();
+ break;
+ case GO_GATE_ILLUCIA:
+ GateIlluciaGUID = go->GetGUID();
+ break;
+ case GO_BRAZIER_OF_THE_HERALD:
+ BrazierOfTheHeraldGUID = go->GetGUID();
+ break;
+ default:
+ break;
+ }
}
- }
- uint32 GetData(uint32 type) const
- {
- return type == (m_auiEncounter[DATA_LORDALEXEIBAROV] == DONE) && (m_auiEncounter[DATA_DOCTORTHEOLENKRASTINOV] == DONE) &&
- (m_auiEncounter[DATA_THERAVENIAN] == DONE) && (m_auiEncounter[DATA_LOREKEEPERPOLKELT] == DONE) &&
- (m_auiEncounter[DATA_INSTRUCTORMALICIA] == DONE) && (m_auiEncounter[DATA_LADYILLUCIABAROV] == DONE)
- ? IN_PROGRESS : 0;
- }
+ bool SetBossState(uint32 type, EncounterState state)
+ {
+ if (!InstanceScript::SetBossState(type, state))
+ return false;
+
+ switch (type)
+ {
+ case DATA_LORDALEXEIBAROV:
+ case DATA_DOCTORTHEOLENKRASTINOV:
+ case DATA_THERAVENIAN:
+ case DATA_LOREKEEPERPOLKELT:
+ case DATA_INSTRUCTORMALICIA:
+ case DATA_LADYILLUCIABAROV:
+ CheckToSpawnGandling();
+ break;
+ default:
+ break;
+ }
+
+ return true;
+ }
- uint64 GetData64(uint32 type) const
- {
- switch (type)
+ uint64 GetData64(uint32 type) const
{
- case GO_GATE_KIRTONOS: return GateKirtonosGUID; break;
- case GO_GATE_GANDLING: return GateGandlingGUID; break;
- case GO_GATE_MALICIA: return GateMiliciaGUID; break;
- case GO_GATE_THEOLEN: return GateTheolenGUID; break;
- case GO_GATE_POLKELT: return GatePolkeltGUID; break;
- case GO_GATE_RAVENIAN: return GateRavenianGUID; break;
- case GO_GATE_BAROV: return GateBarovGUID; break;
- case GO_GATE_ILLUCIA: return GateIlluciaGUID; break;
- case GO_BRAZIER_OF_THE_HERALD: return BrazierOfTheHeraldGUID; break;
+ switch (type)
+ {
+ case GO_GATE_KIRTONOS:
+ return GateKirtonosGUID;
+ case GO_GATE_GANDLING:
+ return GateGandlingGUID;
+ case GO_GATE_MALICIA:
+ return GateMiliciaGUID;
+ case GO_GATE_THEOLEN:
+ return GateTheolenGUID;
+ case GO_GATE_POLKELT:
+ return GatePolkeltGUID;
+ case GO_GATE_RAVENIAN:
+ return GateRavenianGUID;
+ case GO_GATE_BAROV:
+ return GateBarovGUID;
+ case GO_GATE_ILLUCIA:
+ return GateIlluciaGUID;
+ case GO_BRAZIER_OF_THE_HERALD:
+ return BrazierOfTheHeraldGUID;
+ default:
+ break;
+ }
+
+ return 0;
}
- return 0;
- }
+ bool CheckPreBosses(uint32 bossId) const
+ {
+ switch (bossId)
+ {
+ case DATA_DARKMASTERGANDLING:
+ if (GetBossState(DATA_LORDALEXEIBAROV) != DONE)
+ return false;
+ if (GetBossState(DATA_DOCTORTHEOLENKRASTINOV) != DONE)
+ return false;
+ if (GetBossState(DATA_THERAVENIAN) != DONE)
+ return false;
+ if (GetBossState(DATA_LOREKEEPERPOLKELT) != DONE)
+ return false;
+ if (GetBossState(DATA_INSTRUCTORMALICIA) != DONE)
+ return false;
+ if (GetBossState(DATA_LADYILLUCIABAROV) != DONE)
+ return false;
+ if (GetBossState(DATA_DARKMASTERGANDLING) == DONE)
+ return false;
+ break;
+ default:
+ break;
+ }
+
+ return true;
+ }
- void CheckToSpawnGandling()
- {
- if (GetData(DATA_DARKMASTERGANDLING) == IN_PROGRESS)
+ void CheckToSpawnGandling()
+ {
+ if (CheckPreBosses(DATA_DARKMASTERGANDLING))
+ instance->SummonCreature(NPC_DARKMASTER_GANDLING, GandlingLoc);
+ }
+
+ std::string GetSaveData()
+ {
+ OUT_SAVE_INST_DATA;
+
+ std::ostringstream saveStream;
+ saveStream << "S O " << GetBossSaveData();
+
+ OUT_SAVE_INST_DATA_COMPLETE;
+ return saveStream.str();
+ }
+
+ void Load(const char* str)
{
- Map::PlayerList const &PlayerList = instance->GetPlayers();
- if (PlayerList.isEmpty())
+ if (!str)
+ {
+ OUT_LOAD_INST_DATA_FAIL;
return;
+ }
+
+ OUT_LOAD_INST_DATA(str);
+
+ char dataHead1, dataHead2;
+
+ std::istringstream loadStream(str);
+ loadStream >> dataHead1 >> dataHead2;
- Map::PlayerList::const_iterator i = PlayerList.begin();
- if (Player* i_pl = i->getSource())
- i_pl->SummonCreature(NPC_DARKMASTER_GANDLING, GandlingLoc);
+ if (dataHead1 == 'S' && dataHead2 == 'O')
+ {
+ for (uint32 i = 0; i < EncounterCount; ++i)
+ {
+ uint32 tmpState;
+ loadStream >> tmpState;
+ if (tmpState == IN_PROGRESS || tmpState > SPECIAL)
+ tmpState = NOT_STARTED;
+ SetBossState(i, EncounterState(tmpState));
+ }
+
+ CheckToSpawnGandling();
+ }
+ else
+ OUT_LOAD_INST_DATA_FAIL;
+
+ OUT_LOAD_INST_DATA_COMPLETE;
}
- }
- };
+
+ protected:
+ uint64 GateKirtonosGUID;
+ uint64 GateGandlingGUID;
+ uint64 GateMiliciaGUID;
+ uint64 GateTheolenGUID;
+ uint64 GatePolkeltGUID;
+ uint64 GateRavenianGUID;
+ uint64 GateBarovGUID;
+ uint64 GateIlluciaGUID;
+ uint64 BrazierOfTheHeraldGUID;
+ };
};
void AddSC_instance_scholomance()
diff --git a/src/server/scripts/EasternKingdoms/Scholomance/scholomance.h b/src/server/scripts/EasternKingdoms/Scholomance/scholomance.h
index 2d9c8c7bbe7..5c38cc39e3d 100644
--- a/src/server/scripts/EasternKingdoms/Scholomance/scholomance.h
+++ b/src/server/scripts/EasternKingdoms/Scholomance/scholomance.h
@@ -18,6 +18,8 @@
#ifndef DEF_SCHOLOMANCE_H
#define DEF_SCHOLOMANCE_H
+uint32 const EncounterCount = 8;
+
enum DataTypes
{
DATA_DOCTORTHEOLENKRASTINOV = 0,
@@ -30,6 +32,11 @@ enum DataTypes
DATA_KIRTONOS = 7
};
+enum CreatureIds
+{
+ NPC_DARKMASTER_GANDLING = 1853
+};
+
enum GameobjectIds
{
GO_GATE_KIRTONOS = 175570,
@@ -43,9 +50,4 @@ enum GameobjectIds
GO_BRAZIER_OF_THE_HERALD = 175564
};
-enum Misc
-{
- MAX_ENCOUNTER = 8
-};
-
#endif
diff --git a/src/server/scripts/EasternKingdoms/SunwellPlateau/boss_eredar_twins.cpp b/src/server/scripts/EasternKingdoms/SunwellPlateau/boss_eredar_twins.cpp
index 6e3f024ac1d..c186ada72b7 100644
--- a/src/server/scripts/EasternKingdoms/SunwellPlateau/boss_eredar_twins.cpp
+++ b/src/server/scripts/EasternKingdoms/SunwellPlateau/boss_eredar_twins.cpp
@@ -114,7 +114,7 @@ public:
if (instance)
{
- if (Creature *temp = Unit::GetCreature(*me, instance->GetData64(DATA_ALYTHESS)))
+ if (Creature* temp = Unit::GetCreature(*me, instance->GetData64(DATA_ALYTHESS)))
{
if (temp->isDead())
temp->Respawn();
@@ -145,7 +145,7 @@ public:
if (instance)
{
- Creature *temp = Unit::GetCreature(*me, instance->GetData64(DATA_ALYTHESS));
+ Creature* temp = Unit::GetCreature(*me, instance->GetData64(DATA_ALYTHESS));
if (temp && temp->isAlive() && !temp->getVictim())
temp->AI()->AttackStart(who);
}
@@ -374,7 +374,7 @@ public:
if (instance)
{
- if (Creature *temp = Unit::GetCreature((*me), instance->GetData64(DATA_SACROLASH)))
+ if (Creature* temp = Unit::GetCreature((*me), instance->GetData64(DATA_SACROLASH)))
{
if (temp->isDead())
temp->Respawn();
@@ -406,7 +406,7 @@ public:
if (instance)
{
- Creature *temp = Unit::GetCreature(*me, instance->GetData64(DATA_SACROLASH));
+ Creature* temp = Unit::GetCreature(*me, instance->GetData64(DATA_SACROLASH));
if (temp && temp->isAlive() && !temp->getVictim())
temp->AI()->AttackStart(who);
}
diff --git a/src/server/scripts/EasternKingdoms/SunwellPlateau/instance_sunwell_plateau.cpp b/src/server/scripts/EasternKingdoms/SunwellPlateau/instance_sunwell_plateau.cpp
index d575b7633cb..57699a34e17 100644
--- a/src/server/scripts/EasternKingdoms/SunwellPlateau/instance_sunwell_plateau.cpp
+++ b/src/server/scripts/EasternKingdoms/SunwellPlateau/instance_sunwell_plateau.cpp
@@ -122,7 +122,7 @@ public:
return false;
}
- Player const * GetPlayerInMap() const
+ Player const* GetPlayerInMap() const
{
Map::PlayerList const& players = instance->GetPlayers();
diff --git a/src/server/scripts/EasternKingdoms/Uldaman/boss_archaedas.cpp b/src/server/scripts/EasternKingdoms/Uldaman/boss_archaedas.cpp
index 87301f43745..81f64ce6de0 100644
--- a/src/server/scripts/EasternKingdoms/Uldaman/boss_archaedas.cpp
+++ b/src/server/scripts/EasternKingdoms/Uldaman/boss_archaedas.cpp
@@ -102,7 +102,7 @@ class boss_archaedas : public CreatureScript
DoCast(minion, SPELL_AWAKEN_VAULT_WALKER, flag);
minion->CastSpell(minion, SPELL_ARCHAEDAS_AWAKEN, true);
minion->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
- minion->RemoveFlag(UNIT_FIELD_FLAGS,UNIT_FLAG_DISABLE_MOVE);
+ minion->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE);
minion->setFaction(14);
}
}
@@ -399,35 +399,6 @@ class go_altar_of_archaedas : public GameObjectScript
}
};
-/* ScriptData
-SDName: go_altar_of_the_keepers
-SD%Complete: 100
-SDComment: Need 1 person to activate to open the altar. One by one the StoneKeepers will activate. After all four are dead than the door will open.
-SDCategory: Uldaman
-EndScriptData */
-
-class go_altar_of_the_keepers : public GameObjectScript
-{
- public:
-
- go_altar_of_the_keepers()
- : GameObjectScript("go_altar_of_the_keepers")
- {
- }
-
- bool OnGossipHello(Player* player, GameObject* /*go*/)
- {
- InstanceScript* instance = player->GetInstanceScript();
- if (!instance)
- return false;
-
- player->CastSpell (player, SPELL_BOSS_OBJECT_VISUAL, false);
-
- instance->SetData(DATA_STONE_KEEPERS, IN_PROGRESS); // activate the Stone Keepers
- return false;
- }
-};
-
//This is the actual function called only once durring InitScripts()
//It must define all handled functions that are to be run in this script
void AddSC_boss_archaedas()
@@ -436,6 +407,5 @@ void AddSC_boss_archaedas()
new mob_archaedas_minions();
new mob_stonekeepers();
new go_altar_of_archaedas();
- new go_altar_of_the_keepers();
}
diff --git a/src/server/scripts/EasternKingdoms/Uldaman/instance_uldaman.cpp b/src/server/scripts/EasternKingdoms/Uldaman/instance_uldaman.cpp
index 138243a9ce2..df87eaca5a8 100644
--- a/src/server/scripts/EasternKingdoms/Uldaman/instance_uldaman.cpp
+++ b/src/server/scripts/EasternKingdoms/Uldaman/instance_uldaman.cpp
@@ -33,38 +33,38 @@ enum eSpells
SPELL_AWAKEN_VAULT_WALKER = 10258,
};
+enum Events
+{
+ EVENT_SUB_BOSS_AGGRO = 2228
+};
+
class instance_uldaman : public InstanceMapScript
{
public:
- instance_uldaman()
- : InstanceMapScript("instance_uldaman", 70)
- {
- }
+ instance_uldaman() : InstanceMapScript("instance_uldaman", 70) {}
struct instance_uldaman_InstanceMapScript : public InstanceScript
{
- instance_uldaman_InstanceMapScript(Map* map) : InstanceScript(map)
- {
- }
+ instance_uldaman_InstanceMapScript(Map* map) : InstanceScript(map) {}
void Initialize()
{
memset(&m_auiEncounter, 0, sizeof(m_auiEncounter));
- uiArchaedasGUID = 0;
- uiIronayaGUID = 0;
- uiWhoWokeuiArchaedasGUID = 0;
+ archaedasGUID = 0;
+ ironayaGUID = 0;
+ whoWokeuiArchaedasGUID = 0;
- uiAltarOfTheKeeperTempleDoor = 0;
- uiArchaedasTempleDoor = 0;
- uiAncientVaultDoor = 0;
+ altarOfTheKeeperTempleDoor = 0;
+ archaedasTempleDoor = 0;
+ ancientVaultDoor = 0;
- uiIronayaSealDoor = 0;
+ ironayaSealDoor = 0;
- uiKeystoneGUID = 0;
+ keystoneGUID = 0;
- uiIronayaSealDoorTimer = 27000; //animation time
- bKeystoneCheck = false;
+ ironayaSealDoorTimer = 27000; //animation time
+ keystoneCheck = false;
}
bool IsEncounterInProgress() const
@@ -76,25 +76,25 @@ class instance_uldaman : public InstanceMapScript
return false;
}
- uint64 uiArchaedasGUID;
- uint64 uiIronayaGUID;
- uint64 uiWhoWokeuiArchaedasGUID;
+ uint64 archaedasGUID;
+ uint64 ironayaGUID;
+ uint64 whoWokeuiArchaedasGUID;
- uint64 uiAltarOfTheKeeperTempleDoor;
- uint64 uiArchaedasTempleDoor;
- uint64 uiAncientVaultDoor;
- uint64 uiIronayaSealDoor;
+ uint64 altarOfTheKeeperTempleDoor;
+ uint64 archaedasTempleDoor;
+ uint64 ancientVaultDoor;
+ uint64 ironayaSealDoor;
- uint64 uiKeystoneGUID;
+ uint64 keystoneGUID;
- uint32 uiIronayaSealDoorTimer;
- bool bKeystoneCheck;
+ uint32 ironayaSealDoorTimer;
+ bool keystoneCheck;
- std::vector<uint64> vStoneKeeper;
- std::vector<uint64> vAltarOfTheKeeperCount;
- std::vector<uint64> vVaultWalker;
- std::vector<uint64> vEarthenGuardian;
- std::vector<uint64> vArchaedasWallMinions; // minions lined up around the wall
+ std::vector<uint64> stoneKeepers;
+ std::vector<uint64> altarOfTheKeeperCounts;
+ std::vector<uint64> vaultWalkers;
+ std::vector<uint64> earthenGuardians;
+ std::vector<uint64> archaedasWallMinions; // minions lined up around the wall
uint32 m_auiEncounter[MAX_ENCOUNTER];
std::string str_data;
@@ -104,14 +104,14 @@ class instance_uldaman : public InstanceMapScript
switch (go->GetEntry())
{
case GO_ALTAR_OF_THE_KEEPER_TEMPLE_DOOR: // lock the door
- uiAltarOfTheKeeperTempleDoor = go->GetGUID();
+ altarOfTheKeeperTempleDoor = go->GetGUID();
if (m_auiEncounter[0] == DONE)
HandleGameObject(0, true, go);
break;
case GO_ARCHAEDAS_TEMPLE_DOOR:
- uiArchaedasTempleDoor = go->GetGUID();
+ archaedasTempleDoor = go->GetGUID();
if (m_auiEncounter[0] == DONE)
HandleGameObject(0, true, go);
@@ -120,21 +120,21 @@ class instance_uldaman : public InstanceMapScript
case GO_ANCIENT_VAULT_DOOR:
go->SetGoState(GO_STATE_READY);
go->SetUInt32Value(GAMEOBJECT_FLAGS, 33);
- uiAncientVaultDoor = go->GetGUID();
+ ancientVaultDoor = go->GetGUID();
if (m_auiEncounter[1] == DONE)
HandleGameObject(0, true, go);
break;
case GO_IRONAYA_SEAL_DOOR:
- uiIronayaSealDoor = go->GetGUID();
+ ironayaSealDoor = go->GetGUID();
if (m_auiEncounter[2] == DONE)
HandleGameObject(0, true, go);
break;
case GO_KEYSTONE:
- uiKeystoneGUID = go->GetGUID();
+ keystoneGUID = go->GetGUID();
if (m_auiEncounter[2] == DONE)
{
@@ -174,35 +174,38 @@ class instance_uldaman : public InstanceMapScript
void ActivateStoneKeepers()
{
- for (std::vector<uint64>::const_iterator i = vStoneKeeper.begin(); i != vStoneKeeper.end(); ++i)
+ if (GetData(DATA_ALTAR_DOORS) != DONE)
{
- Creature* target = instance->GetCreature(*i);
- if (!target || !target->isAlive() || target->getFaction() == 14)
- continue;
- target->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE);
- target->setFaction(14);
- target->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
- return; // only want the first one we find
+ for (std::vector<uint64>::const_iterator i = stoneKeepers.begin(); i != stoneKeepers.end(); ++i)
+ {
+ Creature* target = instance->GetCreature(*i);
+ if (!target || !target->isAlive())
+ continue;
+ target->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE);
+ target->setFaction(14);
+ target->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
+ return; // only want the first one we find
+ }
+ // if we get this far than all four are dead so open the door
+ SetData(DATA_ALTAR_DOORS, DONE);
+ SetDoor(archaedasTempleDoor, true); //open next the door too
}
- // if we get this far than all four are dead so open the door
- SetData(DATA_ALTAR_DOORS, DONE);
- SetDoor(uiArchaedasTempleDoor, true); //open next the door too
}
void ActivateWallMinions()
{
- Creature* archaedas = instance->GetCreature(uiArchaedasGUID);
+ Creature* archaedas = instance->GetCreature(archaedasGUID);
if (!archaedas)
return;
- for (std::vector<uint64>::const_iterator i = vArchaedasWallMinions.begin(); i != vArchaedasWallMinions.end(); ++i)
+ for (std::vector<uint64>::const_iterator i = archaedasWallMinions.begin(); i != archaedasWallMinions.end(); ++i)
{
Creature* target = instance->GetCreature(*i);
if (!target || !target->isAlive() || target->getFaction() == 14)
continue;
archaedas->CastSpell(target, SPELL_AWAKEN_VAULT_WALKER, true);
target->CastSpell(target, SPELL_ARCHAEDAS_AWAKEN, true);
- target->RemoveFlag(UNIT_FIELD_FLAGS,UNIT_FLAG_DISABLE_MOVE);
+ target->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE);
target->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
target->setFaction(14);
return; // only want the first one we find
@@ -213,7 +216,7 @@ class instance_uldaman : public InstanceMapScript
void DeActivateMinions()
{
// first despawn any aggroed wall minions
- for (std::vector<uint64>::const_iterator i = vArchaedasWallMinions.begin(); i != vArchaedasWallMinions.end(); ++i)
+ for (std::vector<uint64>::const_iterator i = archaedasWallMinions.begin(); i != archaedasWallMinions.end(); ++i)
{
Creature* target = instance->GetCreature(*i);
if (!target || target->isDead() || target->getFaction() != 14)
@@ -223,7 +226,7 @@ class instance_uldaman : public InstanceMapScript
}
// Vault Walkers
- for (std::vector<uint64>::const_iterator i = vVaultWalker.begin(); i != vVaultWalker.end(); ++i)
+ for (std::vector<uint64>::const_iterator i = vaultWalkers.begin(); i != vaultWalkers.end(); ++i)
{
Creature* target = instance->GetCreature(*i);
if (!target || target->isDead() || target->getFaction() != 14)
@@ -233,7 +236,7 @@ class instance_uldaman : public InstanceMapScript
}
// Earthen Guardians
- for (std::vector<uint64>::const_iterator i = vEarthenGuardian.begin(); i != vEarthenGuardian.end(); ++i)
+ for (std::vector<uint64>::const_iterator i = earthenGuardians.begin(); i != earthenGuardians.end(); ++i)
{
Creature* target = instance->GetCreature(*i);
if (!target || target->isDead() || target->getFaction() != 14)
@@ -245,20 +248,20 @@ class instance_uldaman : public InstanceMapScript
void ActivateArchaedas(uint64 target)
{
- Creature* archaedas = instance->GetCreature(uiArchaedasGUID);
+ Creature* archaedas = instance->GetCreature(archaedasGUID);
if (!archaedas)
return;
if (Unit::GetUnit(*archaedas, target))
{
archaedas->CastSpell(archaedas, SPELL_ARCHAEDAS_AWAKEN, false);
- uiWhoWokeuiArchaedasGUID = target;
+ whoWokeuiArchaedasGUID = target;
}
}
void ActivateIronaya()
{
- Creature* ironaya = instance->GetCreature(uiIronayaGUID);
+ Creature* ironaya = instance->GetCreature(ironayaGUID);
if (!ironaya)
return;
@@ -270,7 +273,7 @@ class instance_uldaman : public InstanceMapScript
void RespawnMinions()
{
// first respawn any aggroed wall minions
- for (std::vector<uint64>::const_iterator i = vArchaedasWallMinions.begin(); i != vArchaedasWallMinions.end(); ++i)
+ for (std::vector<uint64>::const_iterator i = archaedasWallMinions.begin(); i != archaedasWallMinions.end(); ++i)
{
Creature* target = instance->GetCreature(*i);
if (target && target->isDead())
@@ -282,7 +285,7 @@ class instance_uldaman : public InstanceMapScript
}
// Vault Walkers
- for (std::vector<uint64>::const_iterator i = vVaultWalker.begin(); i != vVaultWalker.end(); ++i)
+ for (std::vector<uint64>::const_iterator i = vaultWalkers.begin(); i != vaultWalkers.end(); ++i)
{
Creature* target = instance->GetCreature(*i);
if (target && target->isDead())
@@ -294,7 +297,7 @@ class instance_uldaman : public InstanceMapScript
}
// Earthen Guardians
- for (std::vector<uint64>::const_iterator i = vEarthenGuardian.begin(); i != vEarthenGuardian.end(); ++i)
+ for (std::vector<uint64>::const_iterator i = earthenGuardians.begin(); i != earthenGuardians.end(); ++i)
{
Creature* target = instance->GetCreature(*i);
if (target && target->isDead())
@@ -307,21 +310,21 @@ class instance_uldaman : public InstanceMapScript
}
void Update(uint32 diff)
{
- if (!bKeystoneCheck)
+ if (!keystoneCheck)
return;
- if (uiIronayaSealDoorTimer <= diff)
+ if (ironayaSealDoorTimer <= diff)
{
ActivateIronaya();
- SetDoor(uiIronayaSealDoor, true);
- BlockGO(uiKeystoneGUID);
+ SetDoor(ironayaSealDoor, true);
+ BlockGO(keystoneGUID);
SetData(DATA_IRONAYA_DOOR, DONE); //save state
- bKeystoneCheck = false;
+ keystoneCheck = false;
}
else
- uiIronayaSealDoorTimer -= diff;
+ ironayaSealDoorTimer -= diff;
}
void SetData(uint32 type, uint32 data)
@@ -331,15 +334,15 @@ class instance_uldaman : public InstanceMapScript
case DATA_ALTAR_DOORS:
m_auiEncounter[0] = data;
if (data == DONE)
- SetDoor(uiAltarOfTheKeeperTempleDoor, true);
+ SetDoor(altarOfTheKeeperTempleDoor, true);
break;
case DATA_ANCIENT_DOOR:
m_auiEncounter[1] = data;
if (data == DONE) //archeadas defeat
{
- SetDoor(uiArchaedasTempleDoor, true); //re open enter door
- SetDoor(uiAncientVaultDoor, true);
+ SetDoor(archaedasTempleDoor, true); //re open enter door
+ SetDoor(ancientVaultDoor, true);
}
break;
@@ -356,7 +359,7 @@ class instance_uldaman : public InstanceMapScript
{
case NOT_STARTED:
if (m_auiEncounter[0] == DONE) //if players opened the doors
- SetDoor(uiArchaedasTempleDoor, true);
+ SetDoor(archaedasTempleDoor, true);
RespawnMinions();
break;
@@ -372,7 +375,7 @@ class instance_uldaman : public InstanceMapScript
break;
case DATA_IRONAYA_SEAL:
- bKeystoneCheck = true;
+ keystoneCheck = true;
break;
}
@@ -396,7 +399,7 @@ class instance_uldaman : public InstanceMapScript
if (type == 0)
{
ActivateArchaedas (data);
- SetDoor(uiArchaedasTempleDoor, false); //close when event is started
+ SetDoor(archaedasTempleDoor, false); //close when event is started
}
}
@@ -433,34 +436,34 @@ class instance_uldaman : public InstanceMapScript
{
case 4857: // Stone Keeper
SetFrozenState (creature);
- vStoneKeeper.push_back(creature->GetGUID());
+ stoneKeepers.push_back(creature->GetGUID());
break;
case 7309: // Earthen Custodian
- vArchaedasWallMinions.push_back(creature->GetGUID());
+ archaedasWallMinions.push_back(creature->GetGUID());
break;
case 7077: // Earthen Hallshaper
- vArchaedasWallMinions.push_back(creature->GetGUID());
+ archaedasWallMinions.push_back(creature->GetGUID());
break;
case 7076: // Earthen Guardian
- vEarthenGuardian.push_back(creature->GetGUID());
+ earthenGuardians.push_back(creature->GetGUID());
break;
case 7228: // Ironaya
- uiIronayaGUID = creature->GetGUID();
+ ironayaGUID = creature->GetGUID();
if (m_auiEncounter[2] != DONE)
SetFrozenState (creature);
break;
case 10120: // Vault Walker
- vVaultWalker.push_back(creature->GetGUID());
+ vaultWalkers.push_back(creature->GetGUID());
break;
case 2748: // Archaedas
- uiArchaedasGUID = creature->GetGUID();
+ archaedasGUID = creature->GetGUID();
break;
}
@@ -471,25 +474,37 @@ class instance_uldaman : public InstanceMapScript
switch (identifier)
{
case 0:
- return uiWhoWokeuiArchaedasGUID;
+ return whoWokeuiArchaedasGUID;
case 1:
case 2:
case 3:
case 4:
- return vVaultWalker.at(identifier - 1);
+ return vaultWalkers.at(identifier - 1);
case 5:
case 6:
case 7:
case 8:
case 9:
case 10:
- return vEarthenGuardian.at(identifier - 5);
+ return earthenGuardians.at(identifier - 5);
default:
break;
}
return 0;
} // end GetData64
+
+ void ProcessEvent(WorldObject* /*gameObject*/, uint32 eventId)
+ {
+ switch (eventId)
+ {
+ case EVENT_SUB_BOSS_AGGRO:
+ SetData(DATA_STONE_KEEPERS, IN_PROGRESS); // activate the Stone Keepers
+ break;
+ default:
+ break;
+ }
+ }
};
InstanceScript* GetInstanceScript(InstanceMap* map) const
diff --git a/src/server/scripts/EasternKingdoms/ZulAman/boss_halazzi.cpp b/src/server/scripts/EasternKingdoms/ZulAman/boss_halazzi.cpp
index 935e54eae0c..577423adb39 100644
--- a/src/server/scripts/EasternKingdoms/ZulAman/boss_halazzi.cpp
+++ b/src/server/scripts/EasternKingdoms/ZulAman/boss_halazzi.cpp
@@ -45,32 +45,37 @@ EndScriptData */
#define YELL_BERSERK "Whatch you be doing? Pissin' yourselves..."
#define SOUND_BERSERK 12025
-#define SPELL_DUAL_WIELD 29651
-#define SPELL_SABER_LASH 43267
-#define SPELL_FRENZY 43139
-#define SPELL_FLAMESHOCK 43303
-#define SPELL_EARTHSHOCK 43305
-#define SPELL_TRANSFORM_SPLIT 43142
-#define SPELL_TRANSFORM_SPLIT2 43573
-#define SPELL_TRANSFORM_MERGE 43271
-#define SPELL_SUMMON_LYNX 43143
-#define SPELL_SUMMON_TOTEM 43302
-#define SPELL_BERSERK 45078
-
-#define MOB_SPIRIT_LYNX 24143
-#define SPELL_LYNX_FRENZY 43290
-#define SPELL_SHRED_ARMOR 43243
-
-#define MOB_TOTEM 24224
+enum Spells
+{
+ SPELL_DUAL_WIELD = 29651,
+ SPELL_SABER_LASH = 43267,
+ SPELL_FRENZY = 43139,
+ SPELL_FLAMESHOCK = 43303,
+ SPELL_EARTHSHOCK = 43305,
+ SPELL_TRANSFORM_SPLIT = 43142,
+ SPELL_TRANSFORM_SPLIT2 = 43573,
+ SPELL_TRANSFORM_MERGE = 43271,
+ SPELL_SUMMON_LYNX = 43143,
+ SPELL_SUMMON_TOTEM = 43302,
+ SPELL_BERSERK = 45078,
+ SPELL_LYNX_FRENZY = 43290, // Used by Spirit Lynx
+ SPELL_SHRED_ARMOR = 43243 // Used by Spirit Lynx
+};
+
+enum Hal_CreatureIds
+{
+ NPC_SPIRIT_LYNX = 24143,
+ NPC_TOTEM = 24224
+};
enum PhaseHalazzi
{
- PHASE_NONE = 0,
- PHASE_LYNX = 1,
- PHASE_SPLIT = 2,
- PHASE_HUMAN = 3,
- PHASE_MERGE = 4,
- PHASE_ENRAGE = 5
+ PHASE_NONE = 0,
+ PHASE_LYNX = 1,
+ PHASE_SPLIT = 2,
+ PHASE_HUMAN = 3,
+ PHASE_MERGE = 4,
+ PHASE_ENRAGE = 5
};
class boss_halazzi : public CreatureScript
@@ -134,7 +139,7 @@ class boss_halazzi : public CreatureScript
void JustSummoned(Creature* summon)
{
summon->AI()->AttackStart(me->getVictim());
- if (summon->GetEntry() == MOB_SPIRIT_LYNX)
+ if (summon->GetEntry() == NPC_SPIRIT_LYNX)
LynxGUID = summon->GetGUID();
}
@@ -183,7 +188,7 @@ class boss_halazzi : public CreatureScript
break;
case PHASE_HUMAN:
//DoCast(me, SPELL_SUMMON_LYNX, true);
- DoSpawnCreature(MOB_SPIRIT_LYNX, 5, 5, 0, 0, TEMPSUMMON_CORPSE_DESPAWN, 0);
+ DoSpawnCreature(NPC_SPIRIT_LYNX, 5, 5, 0, 0, TEMPSUMMON_CORPSE_DESPAWN, 0);
me->SetMaxHealth(400000);
me->SetHealth(400000);
ShockTimer = 10000;
@@ -413,3 +418,4 @@ void AddSC_boss_halazzi()
new mob_halazzi_lynx();
}
+
diff --git a/src/server/scripts/EasternKingdoms/ZulAman/zulaman.cpp b/src/server/scripts/EasternKingdoms/ZulAman/zulaman.cpp
index 59997ebaa31..44fa0f87177 100644
--- a/src/server/scripts/EasternKingdoms/ZulAman/zulaman.cpp
+++ b/src/server/scripts/EasternKingdoms/ZulAman/zulaman.cpp
@@ -314,7 +314,7 @@ class npc_harrison_jones : public CreatureScript
me->SetEntry(NPC_HARRISON_JONES_2);
me->SetDisplayId(MODEL_HARRISON_JONES_2);
me->SetTarget(0);
- me->SetByteValue(UNIT_FIELD_BYTES_1,0,UNIT_STAND_STATE_DEAD);
+ me->SetByteValue(UNIT_FIELD_BYTES_1, 0,UNIT_STAND_STATE_DEAD);
me->SetFlag(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_DEAD);
if (instance)
instance->SetData(DATA_GONGEVENT, DONE);
@@ -330,7 +330,7 @@ class npc_harrison_jones : public CreatureScript
switch (_gongEvent)
{
case GONG_EVENT_1:
- me->GetMotionMaster()->MovePath(HARRISON_MOVE_1,false);
+ me->GetMotionMaster()->MovePath(HARRISON_MOVE_1, false);
_gongEvent = GONG_EVENT_2;
_gongTimer = 12000;
break;
@@ -345,14 +345,14 @@ class npc_harrison_jones : public CreatureScript
break;
case GONG_EVENT_3:
if (GameObject* gong = me->GetMap()->GetGameObject(instance->GetData64(GO_STRANGE_GONG)))
- gong->RemoveFlag(GAMEOBJECT_FLAGS,GO_FLAG_NOT_SELECTABLE);
+ gong->RemoveFlag(GAMEOBJECT_FLAGS, GO_FLAG_NOT_SELECTABLE);
_gongEvent = GONG_EVENT_4;
_gongTimer = 105000;
break;
case GONG_EVENT_4:
me->RemoveAura(SPELL_BANGING_THE_GONG);
if (GameObject* gong = me->GetMap()->GetGameObject(instance->GetData64(GO_STRANGE_GONG)))
- gong->SetFlag(GAMEOBJECT_FLAGS,GO_FLAG_NOT_SELECTABLE);
+ gong->SetFlag(GAMEOBJECT_FLAGS, GO_FLAG_NOT_SELECTABLE);
// trigger or gong will need to be scripted to set IN_PROGRESS after enough hits.
// This is temp workaround.
@@ -362,7 +362,7 @@ class npc_harrison_jones : public CreatureScript
if (instance->GetData(DATA_GONGEVENT) == IN_PROGRESS)
{
// Players are Now Saved to instance at SPECIAL (Player should be notified?)
- me->GetMotionMaster()->MovePath(HARRISON_MOVE_2,false);
+ me->GetMotionMaster()->MovePath(HARRISON_MOVE_2, false);
_gongEvent = GONG_EVENT_5;
_gongTimer = 5000;
}
@@ -401,12 +401,12 @@ class npc_harrison_jones : public CreatureScript
ptarget->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID + 0, uint32(WEAPON_SPEAR));
ptarget->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC);
ptarget->SetReactState(REACT_PASSIVE);
- ptarget->AI()->SetData(0,1);
+ ptarget->AI()->SetData(0, 1);
}
else
ptarget->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC);
ptarget->SetReactState(REACT_PASSIVE);
- ptarget->AI()->SetData(0,2);
+ ptarget->AI()->SetData(0, 2);
}
}
}
@@ -421,7 +421,7 @@ class npc_harrison_jones : public CreatureScript
DoCast(me, SPELL_STEALTH);
me->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID + 0, uint32(0));
me->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_ONESHOT_NONE);
- me->GetMotionMaster()->MovePath(HARRISON_MOVE_3,false);
+ me->GetMotionMaster()->MovePath(HARRISON_MOVE_3, false);
_gongTimer = 1000;
_gongEvent = 0;
break;
diff --git a/src/server/scripts/EasternKingdoms/ZulGurub/boss_arlokk.cpp b/src/server/scripts/EasternKingdoms/ZulGurub/boss_arlokk.cpp
index 54a5f5c706c..c2806c395f4 100644
--- a/src/server/scripts/EasternKingdoms/ZulGurub/boss_arlokk.cpp
+++ b/src/server/scripts/EasternKingdoms/ZulGurub/boss_arlokk.cpp
@@ -1,6 +1,5 @@
/*
* Copyright (C) 2008-2013 TrinityCore <http://www.trinitycore.org/>
- * Copyright (C) 2006-2009 ScriptDev2 <https://scriptdev2.svn.sourceforge.net/>
*
* 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
@@ -17,270 +16,433 @@
*/
/* ScriptData
-SDName: Boss_Arlokk
-SD%Complete: 95
-SDComment: Wrong cleave and red aura is missing.
-SDCategory: Zul'Gurub
+TCName: Boss_Arlokk
+TC%Complete: 95
+TCComment: Wrong cleave and red aura is missing not yet added.
+TCComment: Prowlers moving through wall hopefully mmaps will fix.
+TCComment: Can't test LOS until mmaps.
+TCCategory: Zul'Gurub
EndScriptData */
#include "ScriptMgr.h"
#include "ScriptedCreature.h"
+#include "SpellInfo.h"
#include "zulgurub.h"
-enum eYells
+enum Says
{
SAY_AGGRO = 0,
- SAY_FEAST_PANTHER = 1,
- SAY_DEATH = 2,
+ SAY_FEAST_PROWLER = 1,
+ SAY_DEATH = 2
};
-enum eSpells
+enum Spells
{
- SPELL_SHADOWWORDPAIN = 23952,
- SPELL_GOUGE = 24698,
- SPELL_MARK = 24210,
- SPELL_CLEAVE = 26350, //Perhaps not right. Not a red aura...
- SPELL_PANTHER_TRANSFORM = 24190,
-
- MODEL_ID_NORMAL = 15218,
- MODEL_ID_PANTHER = 15215,
- MODEL_ID_BLANK = 11686,
-
- NPC_ZULIAN_PROWLER = 15101
+ SPELL_SHADOW_WORD_PAIN = 24212, // Corrected
+ SPELL_GOUGE = 12540, // Corrected
+ SPELL_MARK_OF_ARLOKK = 24210, // triggered spell 24211 Added to spell_dbc
+ SPELL_RAVAGE = 24213, // Corrected
+ SPELL_CLEAVE = 25174, // Searching for right spell
+ SPELL_PANTHER_TRANSFORM = 24190, // Transform to panther now used
+ SPELL_SUMMON_PROWLER = 24246, // Added to Spell_dbc
+ SPELL_VANISH_VISUAL = 24222, // Added
+ SPELL_VANISH = 24223, // Added
+ SPELL_SUPER_INVIS = 24235 // Added to Spell_dbc
};
-class boss_arlokk : public CreatureScript
+enum Events
{
- public:
-
- boss_arlokk()
- : CreatureScript("boss_arlokk")
- {
- }
+ EVENT_SHADOW_WORD_PAIN = 1,
+ EVENT_GOUGE = 2,
+ EVENT_MARK_OF_ARLOKK = 3,
+ EVENT_RAVAGE = 4,
+ EVENT_TRANSFORM = 5,
+ EVENT_VANISH = 6,
+ EVENT_VANISH_2 = 7,
+ EVENT_TRANSFORM_BACK = 8,
+ EVENT_VISIBLE = 9,
+ EVENT_SUMMON_PROWLERS = 10
+};
- struct boss_arlokkAI : public ScriptedAI
- {
- boss_arlokkAI(Creature* creature) : ScriptedAI(creature)
- {
- instance = creature->GetInstanceScript();
- }
+enum Phases
+{
+ PHASE_ALL = 0,
+ PHASE_ONE = 1,
+ PHASE_TWO = 2
+};
- InstanceScript* instance;
+enum Weapon
+{
+ WEAPON_DAGGER = 10616
+};
- uint32 m_uiShadowWordPain_Timer;
- uint32 m_uiGouge_Timer;
- uint32 m_uiMark_Timer;
- uint32 m_uiCleave_Timer;
- uint32 m_uiVanish_Timer;
- uint32 m_uiVisible_Timer;
+enum Misc
+{
+ MAX_PROWLERS_PER_SIDE = 15
+};
- uint32 m_uiSummon_Timer;
- uint32 m_uiSummonCount;
+Position const PosMoveOnSpawn[1] =
+{
+ { -11561.9f, -1627.868f, 41.29941f, 0.0f }
+};
- Unit* m_pMarkedTarget;
- uint64 MarkedTargetGUID;
+class boss_arlokk : public CreatureScript
+{
+ public: boss_arlokk() : CreatureScript("boss_arlokk") {}
- bool m_bIsPhaseTwo;
- bool m_bIsVanished;
+ struct boss_arlokkAI : public BossAI
+ {
+ boss_arlokkAI(Creature* creature) : BossAI(creature, DATA_ARLOKK) { }
void Reset()
{
- m_uiShadowWordPain_Timer = 8000;
- m_uiGouge_Timer = 14000;
- m_uiMark_Timer = 35000;
- m_uiCleave_Timer = 4000;
- m_uiVanish_Timer = 60000;
- m_uiVisible_Timer = 6000;
-
- m_uiSummon_Timer = 5000;
- m_uiSummonCount = 0;
-
- m_bIsPhaseTwo = false;
- m_bIsVanished = false;
-
- MarkedTargetGUID = 0;
-
- me->SetDisplayId(MODEL_ID_NORMAL);
- me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
+ _summonCountA = 0;
+ _summonCountB = 0;
+ me->RemoveAllAuras();
+ me->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID + 0, uint32(WEAPON_DAGGER));
+ me->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID + 1, uint32(WEAPON_DAGGER));
+ if (instance)
+ {
+ if (GameObject* gate = me->GetMap()->GetGameObject(instance->GetData64(GO_FORCEFIELD)))
+ gate->SetGoState(GO_STATE_READY);
+ me->SetWalk(false);
+ me->GetMotionMaster()->MovePoint(0, PosMoveOnSpawn[0]);
+ }
}
- void EnterCombat(Unit* /*who*/)
+ void JustDied(Unit* /*killer*/)
{
- Talk(SAY_AGGRO);
+ Talk(SAY_DEATH);
+ me->RemoveAllAuras();
+ if (instance)
+ {
+ if (GameObject* gate = me->GetMap()->GetGameObject(instance->GetData64(GO_FORCEFIELD)))
+ gate->SetGoState(GO_STATE_ACTIVE);
+ instance->SetBossState(DATA_ARLOKK, DONE);
+ }
}
- void JustReachedHome()
+ void EnterCombat(Unit* /*who*/)
{
+ _EnterCombat();
+ events.ScheduleEvent(EVENT_SHADOW_WORD_PAIN, urand(7000, 9000), 0, PHASE_ONE);
+ events.ScheduleEvent(EVENT_GOUGE, urand(12000, 15000), 0, PHASE_ONE);
if (instance)
- instance->SetData(DATA_ARLOKK, NOT_STARTED);
+ events.ScheduleEvent(EVENT_SUMMON_PROWLERS, 6000, 0, PHASE_ALL);
+ events.ScheduleEvent(EVENT_MARK_OF_ARLOKK, urand(9000, 11000), 0, PHASE_ALL);
+ events.ScheduleEvent(EVENT_TRANSFORM, urand(15000, 20000), 0, PHASE_ONE);
+ Talk(SAY_AGGRO);
- //we should be summoned, so despawn
- me->DespawnOrUnsummon();
+ // Sets up list of Panther spawners to cast on
+ std::list<Creature*> triggerList;
+ GetCreatureListWithEntryInGrid(triggerList, me, NPC_PANTHER_TRIGGER, 100.0f);
+ if (!triggerList.empty())
+ {
+ uint8 sideA = 0;
+ uint8 sideB = 0;
+ for (std::list<Creature*>::const_iterator itr = triggerList.begin(); itr != triggerList.end(); ++itr)
+ {
+ if (Creature* trigger = *itr)
+ {
+ if (trigger->GetPositionY() < -1625.0f)
+ {
+ _triggersSideAGUID[sideA] = trigger->GetGUID();
+ ++sideA;
+ }
+ else
+ {
+ _triggersSideBGUID[sideB] = trigger->GetGUID();
+ ++sideB;
+ }
+ }
+ }
+ }
}
- void JustDied(Unit* /*killer*/)
+ void EnterEvadeMode()
{
- Talk(SAY_DEATH);
-
- me->SetDisplayId(MODEL_ID_NORMAL);
- me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
-
if (instance)
- instance->SetData(DATA_ARLOKK, DONE);
+ {
+ if (GameObject* object = me->GetMap()->GetGameObject(instance->GetData64(GO_FORCEFIELD)))
+ object->SetGoState(GO_STATE_ACTIVE);
+ if (GameObject* object = me->GetMap()->GetGameObject(instance->GetData64(GO_GONG_OF_BETHEKK)))
+ object->RemoveFlag(GAMEOBJECT_FLAGS, GO_FLAG_NOT_SELECTABLE);
+ instance->SetBossState(DATA_ARLOKK, NOT_STARTED);
+ }
+ me->DespawnOrUnsummon(4000);
}
- void DoSummonPhanters()
+ void SetData(uint32 id, uint32 /*value*/)
{
- if (MarkedTargetGUID)
- Talk(SAY_FEAST_PANTHER, MarkedTargetGUID);
-
- me->SummonCreature(NPC_ZULIAN_PROWLER, -11532.7998f, -1649.6734f, 41.4800f, 0.0f, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 15000);
- me->SummonCreature(NPC_ZULIAN_PROWLER, -11532.9970f, -1606.4840f, 41.2979f, 0.0f, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 15000);
+ if (id == 1)
+ --_summonCountA;
+ else if (id == 2)
+ --_summonCountB;
}
- void JustSummoned(Creature* summoned)
+ void UpdateAI(uint32 const diff)
{
- if (Unit* pMarkedTarget = Unit::GetUnit(*me, MarkedTargetGUID))
- summoned->AI()->AttackStart(pMarkedTarget);
+ if (!UpdateVictim())
+ return;
- ++m_uiSummonCount;
- }
+ events.Update(diff);
- void UpdateAI(const uint32 uiDiff)
- {
- if (!UpdateVictim())
+ if (me->HasUnitState(UNIT_STATE_CASTING))
return;
- if (!m_bIsPhaseTwo)
+ while (uint32 eventId = events.ExecuteEvent())
{
- if (m_uiShadowWordPain_Timer <= uiDiff)
- {
- DoCast(me->getVictim(), SPELL_SHADOWWORDPAIN);
- m_uiShadowWordPain_Timer = 15000;
- }
- else
- m_uiShadowWordPain_Timer -= uiDiff;
-
- if (m_uiMark_Timer <= uiDiff)
+ switch (eventId)
{
- Unit* pMarkedTarget = SelectTarget(SELECT_TARGET_RANDOM, 0);
-
- if (pMarkedTarget)
+ case EVENT_SHADOW_WORD_PAIN:
+ DoCastVictim(SPELL_SHADOW_WORD_PAIN, true);
+ events.ScheduleEvent(EVENT_SHADOW_WORD_PAIN, urand(5000, 7000), 0, PHASE_ONE);
+ break;
+ case EVENT_GOUGE:
+ DoCastVictim(SPELL_GOUGE, true);
+ break;
+ case EVENT_SUMMON_PROWLERS:
+ if (_summonCountA < MAX_PROWLERS_PER_SIDE)
+ {
+ if (Unit* trigger = me->GetUnit(*me, _triggersSideAGUID[urand(0, 4)]))
+ {
+ trigger->CastSpell(trigger, SPELL_SUMMON_PROWLER);
+ ++_summonCountA;
+ }
+ }
+ if (_summonCountB < MAX_PROWLERS_PER_SIDE)
+ {
+ if (Unit* trigger = me->GetUnit(*me, _triggersSideBGUID[urand(0, 4)]))
+ {
+ trigger->CastSpell(trigger, SPELL_SUMMON_PROWLER);
+ ++_summonCountB;
+ }
+ }
+ events.ScheduleEvent(EVENT_SUMMON_PROWLERS, 6000, 0, PHASE_ALL);
+ break;
+ case EVENT_MARK_OF_ARLOKK:
{
- DoCast(pMarkedTarget, SPELL_MARK);
- MarkedTargetGUID = pMarkedTarget->GetGUID();
+ Unit* target = SelectTarget(SELECT_TARGET_TOPAGGRO, urand(1,3), 0.0f, false, -SPELL_MARK_OF_ARLOKK);
+ if (!target)
+ target = me->getVictim();
+ if (target)
+ {
+ DoCast(target, SPELL_MARK_OF_ARLOKK, true);
+ Talk(SAY_FEAST_PROWLER, target->GetGUID());
+ }
+ events.ScheduleEvent(EVENT_MARK_OF_ARLOKK, urand(120000, 130000));
+ break;
}
- else
- sLog->outError(LOG_FILTER_TSCR, "boss_arlokk could not accuire pMarkedTarget.");
-
- m_uiMark_Timer = 15000;
+ case EVENT_TRANSFORM:
+ {
+ DoCast(me, SPELL_PANTHER_TRANSFORM);
+ me->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID + 0, uint32(EQUIP_UNEQUIP));
+ me->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID + 1, uint32(EQUIP_UNEQUIP));
+ const CreatureTemplate* cinfo = me->GetCreatureTemplate();
+ me->SetBaseWeaponDamage(BASE_ATTACK, MINDAMAGE, (cinfo->mindmg +((cinfo->mindmg/100) * 35)));
+ me->SetBaseWeaponDamage(BASE_ATTACK, MAXDAMAGE, (cinfo->maxdmg +((cinfo->maxdmg/100) * 35)));
+ me->UpdateDamagePhysical(BASE_ATTACK);
+ me->AttackStop();
+ DoResetThreat();
+ me->SetReactState(REACT_PASSIVE);
+ me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE|UNIT_FLAG_NOT_SELECTABLE);
+ DoCast(me, SPELL_VANISH_VISUAL);
+ DoCast(me, SPELL_VANISH);
+ events.ScheduleEvent(EVENT_VANISH, 1000, 0, PHASE_ONE);
+ break;
+ }
+ case EVENT_VANISH:
+ DoCast(me, SPELL_SUPER_INVIS);
+ me->SetWalk(false);
+ if (instance)
+ me->GetMotionMaster()->MovePoint(0, frand(-11551.0f, -11508.0f), frand(-1638.0f, -1617.0f), me->GetPositionZ());
+ events.ScheduleEvent(EVENT_VANISH_2, 9000, 0, PHASE_ONE);
+ break;
+ case EVENT_VANISH_2:
+ DoCast(me, SPELL_VANISH);
+ DoCast(me, SPELL_SUPER_INVIS);
+ events.ScheduleEvent(EVENT_VISIBLE, urand(7000, 10000), 0, PHASE_ONE);
+ break;
+ case EVENT_VISIBLE:
+ me->SetReactState(REACT_AGGRESSIVE);
+ me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE|UNIT_FLAG_NOT_SELECTABLE);
+ if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0))
+ AttackStart(target);
+ me->RemoveAura(SPELL_SUPER_INVIS);
+ me->RemoveAura(SPELL_VANISH);
+ events.ScheduleEvent(EVENT_RAVAGE, urand(10000, 14000), 0, PHASE_TWO);
+ events.ScheduleEvent(EVENT_TRANSFORM_BACK, urand(15000, 18000), 0, PHASE_TWO);
+ events.SetPhase(PHASE_TWO);
+ break;
+ case EVENT_RAVAGE:
+ DoCastVictim(SPELL_RAVAGE, true);
+ events.ScheduleEvent(EVENT_RAVAGE, urand(10000, 14000), 0, PHASE_TWO);
+ break;
+ case EVENT_TRANSFORM_BACK:
+ {
+ me->RemoveAura(SPELL_PANTHER_TRANSFORM);
+ DoCast(me, SPELL_VANISH_VISUAL);
+ me->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID + 0, uint32(WEAPON_DAGGER));
+ me->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID + 1, uint32(WEAPON_DAGGER));
+ const CreatureTemplate* cinfo = me->GetCreatureTemplate();
+ me->SetBaseWeaponDamage(BASE_ATTACK, MINDAMAGE, (cinfo->mindmg));
+ me->SetBaseWeaponDamage(BASE_ATTACK, MAXDAMAGE, (cinfo->maxdmg));
+ me->UpdateDamagePhysical(BASE_ATTACK);
+ events.ScheduleEvent(EVENT_SHADOW_WORD_PAIN, urand(4000, 7000), 0, PHASE_ONE);
+ events.ScheduleEvent(EVENT_GOUGE, urand(12000, 15000), 0, PHASE_ONE);
+ events.ScheduleEvent(EVENT_TRANSFORM, urand(16000, 20000), 0, PHASE_ONE);
+ events.SetPhase(PHASE_ONE);
+ break;
+ }
+ default:
+ break;
}
- else
- m_uiMark_Timer -= uiDiff;
}
- else
- {
- //Cleave_Timer
- if (m_uiCleave_Timer <= uiDiff)
- {
- DoCast(me->getVictim(), SPELL_CLEAVE);
- m_uiCleave_Timer = 16000;
- }
- else
- m_uiCleave_Timer -= uiDiff;
- //Gouge_Timer
- if (m_uiGouge_Timer <= uiDiff)
- {
- DoCast(me->getVictim(), SPELL_GOUGE);
+ DoMeleeAttackIfReady();
+ }
- DoModifyThreatPercent(me->getVictim(), -80);
+ private:
+ uint8 _summonCountA;
+ uint8 _summonCountB;
+ uint64 _triggersSideAGUID[5];
+ uint64 _triggersSideBGUID[5];
+ };
- m_uiGouge_Timer = 17000+rand()%10000;
- }
- else
- m_uiGouge_Timer -= uiDiff;
- }
+ CreatureAI* GetAI(Creature* creature) const
+ {
+ return GetZulGurubAI<boss_arlokkAI>(creature);
+ }
+};
- if (m_uiSummonCount <= 30)
- {
- if (m_uiSummon_Timer <= uiDiff)
- {
- DoSummonPhanters();
- m_uiSummon_Timer = 5000;
- }
- else
- m_uiSummon_Timer -= uiDiff;
- }
+/*######
+## npc_zulian_prowler
+######*/
- if (m_uiVanish_Timer <= uiDiff)
- {
- //Invisble Model
- me->SetDisplayId(MODEL_ID_BLANK);
- me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
+enum ZulianProwlerSpells
+{
+ SPELL_SNEAK_RANK_1_1 = 22766,
+ SPELL_SNEAK_RANK_1_2 = 7939, // Added to Spell_dbc
+ SPELL_MARK_OF_ARLOKK_TRIGGER = 24211 // Added to Spell_dbc
+};
- me->AttackStop();
- DoResetThreat();
+enum ZulianProwlerEvents
+{
+ EVENT_ATTACK = 1
+};
- m_bIsVanished = true;
+Position const PosProwlerCenter[1] =
+{
+ { -11556.7f, -1631.344f, 41.2994f, 0.0f }
+};
- m_uiVanish_Timer = 45000;
- m_uiVisible_Timer = 6000;
- }
+class npc_zulian_prowler : public CreatureScript
+{
+ public: npc_zulian_prowler() : CreatureScript("npc_zulian_prowler") {}
+
+ struct npc_zulian_prowlerAI : public ScriptedAI
+ {
+ npc_zulian_prowlerAI(Creature* creature) : ScriptedAI(creature), _instance(creature->GetInstanceScript()) { }
+
+ void Reset()
+ {
+ if (me->GetPositionY() < -1625.0f)
+ _sideData = 1;
else
- m_uiVanish_Timer -= uiDiff;
+ _sideData = 2;
+
+ DoCast(me, SPELL_SNEAK_RANK_1_1);
+ DoCast(me, SPELL_SNEAK_RANK_1_2);
+
+ if (_instance)
+ if (Unit* arlokk = me->GetUnit(*me, _instance->GetData64(NPC_ARLOKK)))
+ me->GetMotionMaster()->MovePoint(0, arlokk->GetPositionX(), arlokk->GetPositionY(), arlokk->GetPositionZ());
+ _events.ScheduleEvent(EVENT_ATTACK, 6000);
+ }
+
+ void EnterCombat(Unit* /*who*/)
+ {
+ me->GetMotionMaster()->Clear(false);
+ me->RemoveAura(SPELL_SNEAK_RANK_1_1);
+ me->RemoveAura(SPELL_SNEAK_RANK_1_2);
+ }
+
+ void SpellHit(Unit* caster, SpellInfo const* spell)
+ {
+ if (spell->Id == SPELL_MARK_OF_ARLOKK_TRIGGER) // Should only hit if line of sight
+ me->Attack(caster, true);
+ }
- if (m_bIsVanished)
+ void JustDied(Unit* /*killer*/)
+ {
+ if (_instance)
{
- if (m_uiVisible_Timer <= uiDiff)
+ if (Unit* arlokk = me->GetUnit(*me, _instance->GetData64(NPC_ARLOKK)))
{
- //The Panther Model
- me->SetDisplayId(MODEL_ID_PANTHER);
- me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
+ if (arlokk->isAlive())
+ arlokk->GetAI()->SetData(_sideData, 0);
+ }
+ }
+ me->DespawnOrUnsummon(4000);
+ }
- const CreatureTemplate* cinfo = me->GetCreatureTemplate();
- me->SetBaseWeaponDamage(BASE_ATTACK, MINDAMAGE, (cinfo->mindmg +((cinfo->mindmg/100) * 35)));
- me->SetBaseWeaponDamage(BASE_ATTACK, MAXDAMAGE, (cinfo->maxdmg +((cinfo->maxdmg/100) * 35)));
- me->UpdateDamagePhysical(BASE_ATTACK);
+ void UpdateAI(const uint32 diff)
+ {
+ if (UpdateVictim())
+ {
+ DoMeleeAttackIfReady();
+ return;
+ }
- if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0))
- AttackStart(target);
+ _events.Update(diff);
- m_bIsPhaseTwo = true;
- m_bIsVanished = false;
+ while (uint32 eventId = _events.ExecuteEvent())
+ {
+ switch (eventId)
+ {
+ case EVENT_ATTACK:
+ if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0.0f, 100, false))
+ me->Attack(target, true);
+ break;
+ default:
+ break;
}
- else
- m_uiVisible_Timer -= uiDiff;
}
- else
- DoMeleeAttackIfReady();
}
+
+ private:
+ int32 _sideData;
+ EventMap _events;
+ InstanceScript* _instance;
};
CreatureAI* GetAI(Creature* creature) const
{
- return new boss_arlokkAI(creature);
+ return GetZulGurubAI<npc_zulian_prowlerAI>(creature);
}
};
+/*######
+## go_gong_of_bethekk
+######*/
+
+Position const PosSummonArlokk[1] =
+{
+ { -11507.22f, -1628.062f, 41.38264f, 3.159046f }
+};
+
class go_gong_of_bethekk : public GameObjectScript
{
- public:
- go_gong_of_bethekk() : GameObjectScript("go_gong_of_bethekk")
- {
- }
+ public: go_gong_of_bethekk() : GameObjectScript("go_gong_of_bethekk") {}
bool OnGossipHello(Player* /*player*/, GameObject* go)
{
- if (InstanceScript* instance = go->GetInstanceScript())
+ if (go->GetInstanceScript())
{
- if (instance->GetData(DATA_ARLOKK) == DONE || instance->GetData(DATA_ARLOKK) == IN_PROGRESS)
- return true;
-
- instance->SetData(DATA_ARLOKK, IN_PROGRESS);
- return true;
+ go->SetFlag(GAMEOBJECT_FLAGS, GO_FLAG_NOT_SELECTABLE);
+ go->SendCustomAnim(0);
+ go->SummonCreature(NPC_ARLOKK, PosSummonArlokk[0], TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 600000);
}
-
return true;
}
};
@@ -288,6 +450,6 @@ class go_gong_of_bethekk : public GameObjectScript
void AddSC_boss_arlokk()
{
new boss_arlokk();
+ new npc_zulian_prowler();
new go_gong_of_bethekk();
}
-
diff --git a/src/server/scripts/EasternKingdoms/ZulGurub/boss_gahzranka.cpp b/src/server/scripts/EasternKingdoms/ZulGurub/boss_gahzranka.cpp
index e7d54aecb31..f3f12bc35c5 100644
--- a/src/server/scripts/EasternKingdoms/ZulGurub/boss_gahzranka.cpp
+++ b/src/server/scripts/EasternKingdoms/ZulGurub/boss_gahzranka.cpp
@@ -25,62 +25,78 @@ EndScriptData */
#include "ScriptMgr.h"
#include "ScriptedCreature.h"
+#include "zulgurub.h"
-#define SPELL_FROSTBREATH 16099
-#define SPELL_MASSIVEGEYSER 22421 //Not working. Cause its a summon...
-#define SPELL_SLAM 24326
+enum Spells
+{
+ SPELL_FROSTBREATH = 16099,
+ SPELL_MASSIVEGEYSER = 22421, // Not working. (summon)
+ SPELL_SLAM = 24326
+};
-class boss_gahzranka : public CreatureScript
+enum Events
{
- public:
- boss_gahzranka() : CreatureScript("boss_gahzranka") { }
+ EVENT_FROSTBREATH = 0,
+ EVENT_MASSIVEGEYSER = 1,
+ EVENT_SLAM = 2
+};
- struct boss_gahzrankaAI : public ScriptedAI
+class boss_gahzranka : public CreatureScript // gahzranka
+{
+ public: boss_gahzranka() : CreatureScript("boss_gahzranka") {}
+
+ struct boss_gahzrankaAI : public BossAI
{
- boss_gahzrankaAI(Creature* creature) : ScriptedAI(creature) { }
- uint32 Frostbreath_Timer;
- uint32 MassiveGeyser_Timer;
- uint32 Slam_Timer;
+ boss_gahzrankaAI(Creature* creature) : BossAI(creature, DATA_GAHZRANKA) {}
void Reset()
{
- Frostbreath_Timer = 8000;
- MassiveGeyser_Timer = 25000;
- Slam_Timer = 17000;
+ _Reset();
+ }
+
+ void JustDied(Unit* /*killer*/)
+ {
+ _JustDied();
}
void EnterCombat(Unit* /*who*/)
{
+ _EnterCombat();
+ events.ScheduleEvent(EVENT_FROSTBREATH, 8000);
+ events.ScheduleEvent(EVENT_MASSIVEGEYSER, 25000);
+ events.ScheduleEvent(EVENT_SLAM, 17000);
}
- void UpdateAI(const uint32 diff)
+ void UpdateAI(uint32 const diff)
{
- //Return since we have no target
if (!UpdateVictim())
return;
- //Frostbreath_Timer
- if (Frostbreath_Timer <= diff)
- {
- DoCast(me->getVictim(), SPELL_FROSTBREATH);
- Frostbreath_Timer = urand(7000, 11000);
- } else Frostbreath_Timer -= diff;
-
- //MassiveGeyser_Timer
- if (MassiveGeyser_Timer <= diff)
- {
- DoCast(me->getVictim(), SPELL_MASSIVEGEYSER);
- DoResetThreat();
+ events.Update(diff);
- MassiveGeyser_Timer = urand(22000, 32000);
- } else MassiveGeyser_Timer -= diff;
+ if (me->HasUnitState(UNIT_STATE_CASTING))
+ return;
- //Slam_Timer
- if (Slam_Timer <= diff)
+ while (uint32 eventId = events.ExecuteEvent())
{
- DoCast(me->getVictim(), SPELL_SLAM);
- Slam_Timer = urand(12000, 20000);
- } else Slam_Timer -= diff;
+ switch (eventId)
+ {
+ case EVENT_FROSTBREATH:
+ DoCastVictim(SPELL_FROSTBREATH, true);
+ events.ScheduleEvent(EVENT_FROSTBREATH, urand(7000, 11000));
+ break;
+ case EVENT_MASSIVEGEYSER:
+ DoCastVictim(SPELL_MASSIVEGEYSER, true);
+ events.ScheduleEvent(EVENT_MASSIVEGEYSER, urand(22000, 32000));
+ break;
+ case EVENT_SLAM:
+ DoCastVictim(SPELL_SLAM, true);
+ events.ScheduleEvent(EVENT_SLAM, urand(12000, 20000));
+ break;
+ default:
+ break;
+ }
+ }
DoMeleeAttackIfReady();
}
@@ -96,4 +112,3 @@ void AddSC_boss_gahzranka()
{
new boss_gahzranka();
}
-
diff --git a/src/server/scripts/EasternKingdoms/ZulGurub/boss_grilek.cpp b/src/server/scripts/EasternKingdoms/ZulGurub/boss_grilek.cpp
index 4ccdbceb466..f06500694a5 100644
--- a/src/server/scripts/EasternKingdoms/ZulGurub/boss_grilek.cpp
+++ b/src/server/scripts/EasternKingdoms/ZulGurub/boss_grilek.cpp
@@ -27,60 +27,73 @@ EndScriptData */
#include "ScriptedCreature.h"
#include "zulgurub.h"
-#define SPELL_AVARTAR 24646 //The Enrage Spell
-#define SPELL_GROUNDTREMOR 6524
+enum Spells
+{
+ SPELL_AVATAR = 24646, // Enrage Spell
+ SPELL_GROUND_TREMOR = 6524
+};
-class boss_grilek : public CreatureScript
+enum Events
{
- public:
- boss_grilek() : CreatureScript("boss_grilek") { }
+ EVENT_AVATAR = 0,
+ EVENT_GROUND_TREMOR = 1
+};
- struct boss_grilekAI : public ScriptedAI
- {
- boss_grilekAI(Creature* creature) : ScriptedAI(creature) { }
+class boss_grilek : public CreatureScript // grilek
+{
+ public: boss_grilek() : CreatureScript("boss_grilek") {}
- uint32 Avartar_Timer;
- uint32 GroundTremor_Timer;
+ struct boss_grilekAI : public BossAI
+ {
+ boss_grilekAI(Creature* creature) : BossAI(creature, DATA_EDGE_OF_MADNESS) {}
void Reset()
{
- Avartar_Timer = urand(15000, 25000);
- GroundTremor_Timer = urand(8000, 16000);
+ _Reset();
+ }
+
+ void JustDied(Unit* /*killer*/)
+ {
+ _JustDied();
}
void EnterCombat(Unit* /*who*/)
{
+ _EnterCombat();
+ events.ScheduleEvent(EVENT_AVATAR, urand(15000, 25000));
+ events.ScheduleEvent(EVENT_GROUND_TREMOR, urand(15000, 25000));
}
- void UpdateAI(const uint32 diff)
+ void UpdateAI(uint32 const diff)
{
- //Return since we have no target
if (!UpdateVictim())
return;
- //Avartar_Timer
- if (Avartar_Timer <= diff)
- {
-
- DoCast(me, SPELL_AVARTAR);
- Unit* target = NULL;
+ events.Update(diff);
- target = SelectTarget(SELECT_TARGET_RANDOM, 1);
-
- if (DoGetThreat(me->getVictim()))
- DoModifyThreatPercent(me->getVictim(), -50);
- if (target)
- AttackStart(target);
-
- Avartar_Timer = urand(25000, 35000);
- } else Avartar_Timer -= diff;
+ if (me->HasUnitState(UNIT_STATE_CASTING))
+ return;
- //GroundTremor_Timer
- if (GroundTremor_Timer <= diff)
+ while (uint32 eventId = events.ExecuteEvent())
{
- DoCast(me->getVictim(), SPELL_GROUNDTREMOR);
- GroundTremor_Timer = urand(12000, 16000);
- } else GroundTremor_Timer -= diff;
+ switch (eventId)
+ {
+ case EVENT_AVATAR:
+ DoCast(me, SPELL_AVATAR);
+ if (DoGetThreat(me->getVictim()))
+ DoModifyThreatPercent(me->getVictim(), -50);
+ if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 1))
+ AttackStart(target);
+ events.ScheduleEvent(EVENT_AVATAR, urand(25000, 35000));
+ break;
+ case EVENT_GROUND_TREMOR:
+ DoCastVictim(SPELL_GROUND_TREMOR, true);
+ events.ScheduleEvent(EVENT_GROUND_TREMOR, urand(12000, 16000));
+ break;
+ default:
+ break;
+ }
+ }
DoMeleeAttackIfReady();
}
diff --git a/src/server/scripts/EasternKingdoms/ZulGurub/boss_hakkar.cpp b/src/server/scripts/EasternKingdoms/ZulGurub/boss_hakkar.cpp
index 781b68bfcb0..68aac7547df 100644
--- a/src/server/scripts/EasternKingdoms/ZulGurub/boss_hakkar.cpp
+++ b/src/server/scripts/EasternKingdoms/ZulGurub/boss_hakkar.cpp
@@ -1,6 +1,5 @@
/*
* Copyright (C) 2008-2013 TrinityCore <http://www.trinitycore.org/>
- * Copyright (C) 2006-2009 ScriptDev2 <https://scriptdev2.svn.sourceforge.net/>
*
* 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,31 +15,33 @@
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-/* ScriptData
-SDName: Boss_Hakkar
-SD%Complete: 95
-SDComment: Blood siphon spell buggy cause of Core Issue.
-SDCategory: Zul'Gurub
-EndScriptData */
+/*
+Name: Boss_Hakkar
+%Complete: 95
+Comment: Blood siphon spell buggy cause of Core Issue.
+Category: Zul'Gurub
+*/
#include "ScriptMgr.h"
#include "ScriptedCreature.h"
#include "zulgurub.h"
-enum Hakkar
+enum Says
{
SAY_AGGRO = 0,
SAY_FLEEING = 1,
- SAY_MINION_DESTROY = 2, //where does it belong?
- SAY_PROTECT_ALTAR = 3, //where does it belong?
+ SAY_MINION_DESTROY = 2, // Where does it belong?
+ SAY_PROTECT_ALTAR = 3 // Where does it belong?
+};
- SPELL_BLOODSIPHON = 24322,
- SPELL_CORRUPTEDBLOOD = 24328,
- SPELL_CAUSEINSANITY = 24327, //Not working disabled.
- SPELL_WILLOFHAKKAR = 24178,
+enum Spells
+{
+ SPELL_BLOOD_SIPHON = 24322, // Buggy ?
+ SPELL_CORRUPTED_BLOOD = 24328,
+ SPELL_CAUSE_INSANITY = 24327, // Spell needs scripting.
+ SPELL_WILL_OF_HAKKAR = 24178,
SPELL_ENRAGE = 24318,
-
-// The Aspects of all High Priests
+ // The Aspects of all High Priests spells
SPELL_ASPECT_OF_JEKLIK = 24687,
SPELL_ASPECT_OF_VENOXIS = 24688,
SPELL_ASPECT_OF_MARLI = 24686,
@@ -48,202 +49,119 @@ enum Hakkar
SPELL_ASPECT_OF_ARLOKK = 24690
};
+enum Events
+{
+ EVENT_BLOOD_SIPHON = 0,
+ EVENT_CORRUPTED_BLOOD = 1,
+ EVENT_CAUSE_INSANITY = 2, // Spell needs scripting. Event disabled
+ EVENT_WILL_OF_HAKKAR = 3,
+ EVENT_ENRAGE = 4,
+ // The Aspects of all High Priests events
+ EVENT_ASPECT_OF_JEKLIK = 5,
+ EVENT_ASPECT_OF_VENOXIS = 6,
+ EVENT_ASPECT_OF_MARLI = 7,
+ EVENT_ASPECT_OF_THEKAL = 8,
+ EVENT_ASPECT_OF_ARLOKK = 9
+};
+
class boss_hakkar : public CreatureScript
{
- public:
+ public: boss_hakkar() : CreatureScript("boss_hakkar") {}
- boss_hakkar()
- : CreatureScript("boss_hakkar")
+ struct boss_hakkarAI : public BossAI
{
- }
+ boss_hakkarAI(Creature* creature) : BossAI(creature, DATA_HAKKAR) {}
- struct boss_hakkarAI : public ScriptedAI
- {
- boss_hakkarAI(Creature* creature) : ScriptedAI(creature)
+ void Reset()
{
- instance = creature->GetInstanceScript();
+ _Reset();
}
- InstanceScript* instance;
-
- uint32 BloodSiphon_Timer;
- uint32 CorruptedBlood_Timer;
- uint32 CauseInsanity_Timer;
- uint32 WillOfHakkar_Timer;
- uint32 Enrage_Timer;
-
- uint32 CheckJeklik_Timer;
- uint32 CheckVenoxis_Timer;
- uint32 CheckMarli_Timer;
- uint32 CheckThekal_Timer;
- uint32 CheckArlokk_Timer;
-
- uint32 AspectOfJeklik_Timer;
- uint32 AspectOfVenoxis_Timer;
- uint32 AspectOfMarli_Timer;
- uint32 AspectOfThekal_Timer;
- uint32 AspectOfArlokk_Timer;
-
- bool Enraged;
-
- void Reset()
+ void JustDied(Unit* /*killer*/)
{
- BloodSiphon_Timer = 90000;
- CorruptedBlood_Timer = 25000;
- CauseInsanity_Timer = 17000;
- WillOfHakkar_Timer = 17000;
- Enrage_Timer = 600000;
-
- CheckJeklik_Timer = 1000;
- CheckVenoxis_Timer = 2000;
- CheckMarli_Timer = 3000;
- CheckThekal_Timer = 4000;
- CheckArlokk_Timer = 5000;
-
- AspectOfJeklik_Timer = 4000;
- AspectOfVenoxis_Timer = 7000;
- AspectOfMarli_Timer = 12000;
- AspectOfThekal_Timer = 8000;
- AspectOfArlokk_Timer = 18000;
-
- Enraged = false;
+ _JustDied();
}
void EnterCombat(Unit* /*who*/)
{
+ _EnterCombat();
+ events.ScheduleEvent(EVENT_BLOOD_SIPHON, 90000);
+ events.ScheduleEvent(EVENT_CORRUPTED_BLOOD, 25000);
+ events.ScheduleEvent(EVENT_CAUSE_INSANITY, 17000);
+ events.ScheduleEvent(EVENT_WILL_OF_HAKKAR, 17000);
+ events.ScheduleEvent(EVENT_ENRAGE, 600000);
+ if (instance->GetBossState(DATA_JEKLIK) != DONE)
+ events.ScheduleEvent(EVENT_ASPECT_OF_JEKLIK, 4000);
+ if (instance->GetBossState(DATA_VENOXIS) != DONE)
+ events.ScheduleEvent(EVENT_ASPECT_OF_VENOXIS, 7000);
+ if (instance->GetBossState(DATA_MARLI) != DONE)
+ events.ScheduleEvent(EVENT_ASPECT_OF_MARLI, 12000);
+ if (instance->GetBossState(DATA_THEKAL) != DONE)
+ events.ScheduleEvent(EVENT_ASPECT_OF_THEKAL, 8000);
+ if (instance->GetBossState(DATA_ARLOKK) != DONE)
+ events.ScheduleEvent(EVENT_ASPECT_OF_ARLOKK, 18000);
Talk(SAY_AGGRO);
}
- void UpdateAI(const uint32 diff)
+ void UpdateAI(uint32 const diff)
{
if (!UpdateVictim())
return;
- //BloodSiphon_Timer
- if (BloodSiphon_Timer <= diff)
- {
- DoCast(me->getVictim(), SPELL_BLOODSIPHON);
- BloodSiphon_Timer = 90000;
- } else BloodSiphon_Timer -= diff;
-
- //CorruptedBlood_Timer
- if (CorruptedBlood_Timer <= diff)
- {
- DoCast(me->getVictim(), SPELL_CORRUPTEDBLOOD);
- CorruptedBlood_Timer = urand(30000, 45000);
- } else CorruptedBlood_Timer -= diff;
-
- //CauseInsanity_Timer
- /*if (CauseInsanity_Timer <= diff)
- {
- if (Unit* target = SelectUnit(SELECT_TARGET_RANDOM, 0))
- DoCast(target, SPELL_CAUSEINSANITY);
-
- CauseInsanity_Timer = urand(35000, 43000);
- } else CauseInsanity_Timer -= diff;*/
-
- //WillOfHakkar_Timer
- if (WillOfHakkar_Timer <= diff)
- {
- if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0))
- DoCast(target, SPELL_WILLOFHAKKAR);
-
- WillOfHakkar_Timer = urand(25000, 35000);
- } else WillOfHakkar_Timer -= diff;
-
- if (!Enraged && Enrage_Timer <= diff)
- {
- DoCast(me, SPELL_ENRAGE);
- Enraged = true;
- } else Enrage_Timer -= diff;
-
- //Checking if Jeklik is dead. If not we cast her Aspect
- if (CheckJeklik_Timer <= diff)
- {
- if (instance)
- {
- if (instance->GetData(DATA_JEKLIK) != DONE)
- {
- if (AspectOfJeklik_Timer <= diff)
- {
- DoCast(me->getVictim(), SPELL_ASPECT_OF_JEKLIK);
- AspectOfJeklik_Timer = urand(10000, 14000);
- } else AspectOfJeklik_Timer -= diff;
- }
- }
- CheckJeklik_Timer = 1000;
- } else CheckJeklik_Timer -= diff;
-
- //Checking if Venoxis is dead. If not we cast his Aspect
- if (CheckVenoxis_Timer <= diff)
- {
- if (instance)
- {
- if (instance->GetData(DATA_VENOXIS) != DONE)
- {
- if (AspectOfVenoxis_Timer <= diff)
- {
- DoCast(me->getVictim(), SPELL_ASPECT_OF_VENOXIS);
- AspectOfVenoxis_Timer = 8000;
- } else AspectOfVenoxis_Timer -= diff;
- }
- }
- CheckVenoxis_Timer = 1000;
- } else CheckVenoxis_Timer -= diff;
-
- //Checking if Marli is dead. If not we cast her Aspect
- if (CheckMarli_Timer <= diff)
- {
- if (instance)
- {
- if (instance->GetData(DATA_MARLI) != DONE)
- {
- if (AspectOfMarli_Timer <= diff)
- {
- DoCast(me->getVictim(), SPELL_ASPECT_OF_MARLI);
- AspectOfMarli_Timer = 10000;
- } else AspectOfMarli_Timer -= diff;
-
- }
- }
- CheckMarli_Timer = 1000;
- } else CheckMarli_Timer -= diff;
+ events.Update(diff);
- //Checking if Thekal is dead. If not we cast his Aspect
- if (CheckThekal_Timer <= diff)
- {
- if (instance)
- {
- if (instance->GetData(DATA_THEKAL) != DONE)
- {
- if (AspectOfThekal_Timer <= diff)
- {
- DoCast(me, SPELL_ASPECT_OF_THEKAL);
- AspectOfThekal_Timer = 15000;
- } else AspectOfThekal_Timer -= diff;
- }
- }
- CheckThekal_Timer = 1000;
- } else CheckThekal_Timer -= diff;
+ if (me->HasUnitState(UNIT_STATE_CASTING))
+ return;
- //Checking if Arlokk is dead. If yes we cast her Aspect
- if (CheckArlokk_Timer <= diff)
+ while (uint32 eventId = events.ExecuteEvent())
{
- if (instance)
+ switch (eventId)
{
- if (instance->GetData(DATA_ARLOKK) != DONE)
- {
- if (AspectOfArlokk_Timer <= diff)
- {
- DoCast(me, SPELL_ASPECT_OF_ARLOKK);
- DoResetThreat();
-
- AspectOfArlokk_Timer = urand(10000, 15000);
- } else AspectOfArlokk_Timer -= diff;
- }
+ case EVENT_BLOOD_SIPHON:
+ DoCastVictim(SPELL_BLOOD_SIPHON, true);
+ events.ScheduleEvent(EVENT_BLOOD_SIPHON, 90000);
+ break;
+ case EVENT_CORRUPTED_BLOOD:
+ DoCastVictim(SPELL_CORRUPTED_BLOOD, true);
+ events.ScheduleEvent(EVENT_CORRUPTED_BLOOD, urand(30000, 45000));
+ break;
+ case EVENT_CAUSE_INSANITY:
+ // DoCast(SelectTarget(SELECT_TARGET_RANDOM, 0, 100, true), SPELL_CAUSE_INSANITY);
+ // events.ScheduleEvent(EVENT_CAUSE_INSANITY, urand(35000, 45000));
+ break;
+ case EVENT_WILL_OF_HAKKAR:
+ DoCast(SelectTarget(SELECT_TARGET_RANDOM, 0, 100, true), SPELL_WILL_OF_HAKKAR);
+ events.ScheduleEvent(EVENT_WILL_OF_HAKKAR, urand(25000, 35000));
+ break;
+ case EVENT_ENRAGE:
+ if (!me->HasAura(SPELL_ENRAGE))
+ DoCast(me, SPELL_ENRAGE);
+ events.ScheduleEvent(EVENT_ENRAGE, 90000);
+ break;
+ case EVENT_ASPECT_OF_JEKLIK:
+ DoCastVictim(SPELL_ASPECT_OF_JEKLIK, true);
+ events.ScheduleEvent(EVENT_ASPECT_OF_JEKLIK, urand(10000, 14000));
+ break;
+ case EVENT_ASPECT_OF_VENOXIS:
+ DoCastVictim(SPELL_ASPECT_OF_VENOXIS, true);
+ events.ScheduleEvent(EVENT_ASPECT_OF_VENOXIS, 8000);
+ break;
+ case EVENT_ASPECT_OF_MARLI:
+ DoCastVictim(SPELL_ASPECT_OF_MARLI, true);
+ events.ScheduleEvent(EVENT_ASPECT_OF_MARLI, 10000);
+ break;
+ case EVENT_ASPECT_OF_THEKAL:
+ DoCastVictim(SPELL_ASPECT_OF_THEKAL, true);
+ events.ScheduleEvent(EVENT_ASPECT_OF_THEKAL, 15000);
+ break;
+ case EVENT_ASPECT_OF_ARLOKK:
+ DoCastVictim(SPELL_ASPECT_OF_ARLOKK, true);
+ events.ScheduleEvent(EVENT_ASPECT_OF_ARLOKK, urand(10000, 15000));
+ break;
+ default:
+ break;
}
- CheckArlokk_Timer = 1000;
- } else CheckArlokk_Timer -= diff;
+ }
DoMeleeAttackIfReady();
}
diff --git a/src/server/scripts/EasternKingdoms/ZulGurub/boss_hazzarah.cpp b/src/server/scripts/EasternKingdoms/ZulGurub/boss_hazzarah.cpp
index bbdc2905874..809403bb325 100644
--- a/src/server/scripts/EasternKingdoms/ZulGurub/boss_hazzarah.cpp
+++ b/src/server/scripts/EasternKingdoms/ZulGurub/boss_hazzarah.cpp
@@ -27,75 +27,85 @@ EndScriptData */
#include "ScriptedCreature.h"
#include "zulgurub.h"
-#define SPELL_MANABURN 26046
-#define SPELL_SLEEP 24664
+enum Spells
+{
+ SPELL_MANABURN = 26046,
+ SPELL_SLEEP = 24664
+};
-class boss_hazzarah : public CreatureScript
+enum Events
{
- public:
+ EVENT_MANABURN = 0,
+ EVENT_SLEEP = 1,
+ EVENT_ILLUSIONS = 2
+};
- boss_hazzarah()
- : CreatureScript("boss_hazzarah")
- {
- }
+class boss_hazzarah : public CreatureScript
+{
+ public: boss_hazzarah() : CreatureScript("boss_hazzarah") {}
- struct boss_hazzarahAI : public ScriptedAI
+ struct boss_hazzarahAI : public BossAI
{
- boss_hazzarahAI(Creature* creature) : ScriptedAI(creature) {}
-
- uint32 ManaBurn_Timer;
- uint32 Sleep_Timer;
- uint32 Illusions_Timer;
+ boss_hazzarahAI(Creature* creature) : BossAI(creature, DATA_EDGE_OF_MADNESS) {}
void Reset()
{
- ManaBurn_Timer = urand(4000, 10000);
- Sleep_Timer = urand(10000, 18000);
- Illusions_Timer = urand(10000, 18000);
+ _Reset();
+ }
+
+ void JustDied(Unit* /*killer*/)
+ {
+ _JustDied();
}
void EnterCombat(Unit* /*who*/)
{
+ _EnterCombat();
+ events.ScheduleEvent(EVENT_MANABURN, urand(4000, 10000));
+ events.ScheduleEvent(EVENT_SLEEP, urand(10000, 18000));
+ events.ScheduleEvent(EVENT_ILLUSIONS, urand(10000, 18000));
}
- void UpdateAI(const uint32 diff)
+ void UpdateAI(uint32 const diff)
{
if (!UpdateVictim())
return;
- //ManaBurn_Timer
- if (ManaBurn_Timer <= diff)
- {
- DoCast(me->getVictim(), SPELL_MANABURN);
- ManaBurn_Timer = urand(8000, 16000);
- } else ManaBurn_Timer -= diff;
+ events.Update(diff);
- //Sleep_Timer
- if (Sleep_Timer <= diff)
- {
- DoCast(me->getVictim(), SPELL_SLEEP);
- Sleep_Timer = urand(12000, 20000);
- } else Sleep_Timer -= diff;
+ if (me->HasUnitState(UNIT_STATE_CASTING))
+ return;
- //Illusions_Timer
- if (Illusions_Timer <= diff)
+ while (uint32 eventId = events.ExecuteEvent())
{
- //We will summon 3 illusions that will spawn on a random gamer and attack this gamer
- //We will just use one model for the beginning
- Unit* target = NULL;
- for (uint8 i = 0; i < 3; ++i)
+ switch (eventId)
{
- target = SelectTarget(SELECT_TARGET_RANDOM, 0);
- if (!target)
- return;
-
- Creature* Illusion = me->SummonCreature(15163, target->GetPositionX(), target->GetPositionY(), target->GetPositionZ(), 0, TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN, 30000);
- if (Illusion)
- Illusion->AI()->AttackStart(target);
+ case EVENT_MANABURN:
+ DoCastVictim(SPELL_MANABURN, true);
+ events.ScheduleEvent(EVENT_MANABURN, urand(8000, 16000));
+ break;
+ case EVENT_SLEEP:
+ DoCastVictim(SPELL_SLEEP, true);
+ events.ScheduleEvent(EVENT_SLEEP, urand(12000, 20000));
+ break;
+ case EVENT_ILLUSIONS:
+ // We will summon 3 illusions that will spawn on a random gamer and attack this gamer
+ // We will just use one model for the beginning
+ for (uint8 i = 0; i < 3; ++i)
+ {
+ if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0))
+ {
+ Creature* Illusion = me->SummonCreature(NPC_NIGHTMARE_ILLUSION, target->GetPositionX(), target->GetPositionY(), target->GetPositionZ(), 0, TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN, 30000);
+ if (Illusion)
+ Illusion->AI()->AttackStart(target);
+ }
+ }
+ events.ScheduleEvent(EVENT_ILLUSIONS, urand(15000, 25000));
+ break;
+ default:
+ break;
}
-
- Illusions_Timer = urand(15000, 25000);
- } else Illusions_Timer -= diff;
+ }
DoMeleeAttackIfReady();
}
@@ -111,4 +121,3 @@ void AddSC_boss_hazzarah()
{
new boss_hazzarah();
}
-
diff --git a/src/server/scripts/EasternKingdoms/ZulGurub/boss_jeklik.cpp b/src/server/scripts/EasternKingdoms/ZulGurub/boss_jeklik.cpp
index 10431ec84bd..dc02c895327 100644
--- a/src/server/scripts/EasternKingdoms/ZulGurub/boss_jeklik.cpp
+++ b/src/server/scripts/EasternKingdoms/ZulGurub/boss_jeklik.cpp
@@ -27,42 +27,34 @@ EndScriptData */
#include "ScriptedCreature.h"
#include "zulgurub.h"
-enum Jeklik
+enum Says
{
SAY_AGGRO = 0,
SAY_RAIN_FIRE = 1,
- SAY_DEATH = 2,
+ SAY_DEATH = 2
+};
+enum Spells
+{
SPELL_CHARGE = 22911,
SPELL_SONICBURST = 23918,
SPELL_SCREECH = 6605,
SPELL_SHADOW_WORD_PAIN = 23952,
SPELL_MIND_FLAY = 23953,
- SPELL_CHAIN_MIND_FLAY = 26044, //Right ID unknown. So disabled
+ SPELL_CHAIN_MIND_FLAY = 26044, // Right ID unknown. So disabled
SPELL_GREATERHEAL = 23954,
SPELL_BAT_FORM = 23966,
-
// Batriders Spell
- SPELL_BOMB = 40332 //Wrong ID but Magmadars bomb is not working...
+ SPELL_BOMB = 40332 // Wrong ID but Magmadars bomb is not working...
};
-class boss_jeklik : public CreatureScript
+class boss_jeklik : public CreatureScript //jeklik
{
- public:
-
- boss_jeklik()
- : CreatureScript("boss_jeklik")
- {
- }
+ public: boss_jeklik() : CreatureScript("boss_jeklik") {}
- struct boss_jeklikAI : public ScriptedAI
+ struct boss_jeklikAI : public BossAI
{
- boss_jeklikAI(Creature* creature) : ScriptedAI(creature)
- {
- instance = creature->GetInstanceScript();
- }
-
- InstanceScript* instance;
+ boss_jeklikAI(Creature* creature) : BossAI(creature, DATA_JEKLIK) {}
uint32 Charge_Timer;
uint32 SonicBurst_Timer;
@@ -78,6 +70,7 @@ class boss_jeklik : public CreatureScript
void Reset()
{
+ _Reset();
Charge_Timer = 20000;
SonicBurst_Timer = 8000;
Screech_Timer = 13000;
@@ -91,18 +84,17 @@ class boss_jeklik : public CreatureScript
PhaseTwo = false;
}
- void EnterCombat(Unit* /*who*/)
- {
- Talk(SAY_AGGRO);
- DoCast(me, SPELL_BAT_FORM);
- }
-
void JustDied(Unit* /*killer*/)
{
+ _JustDied();
Talk(SAY_DEATH);
+ }
- if (instance)
- instance->SetData(DATA_JEKLIK, DONE);
+ void EnterCombat(Unit* /*who*/)
+ {
+ _EnterCombat();
+ Talk(SAY_AGGRO);
+ DoCast(me, SPELL_BAT_FORM);
}
void UpdateAI(const uint32 diff)
@@ -279,7 +271,7 @@ class mob_batrider : public CreatureScript
{
if (instance)
{
- if (instance->GetData(DATA_JEKLIK) == DONE)
+ if (instance->GetBossState(DATA_JEKLIK) == DONE)
{
me->setDeathState(JUST_DIED);
me->RemoveCorpse();
diff --git a/src/server/scripts/EasternKingdoms/ZulGurub/boss_jindo.cpp b/src/server/scripts/EasternKingdoms/ZulGurub/boss_jindo.cpp
index 7c4c9626f9a..005609e88db 100644
--- a/src/server/scripts/EasternKingdoms/ZulGurub/boss_jindo.cpp
+++ b/src/server/scripts/EasternKingdoms/ZulGurub/boss_jindo.cpp
@@ -27,147 +27,147 @@ EndScriptData */
#include "ScriptedCreature.h"
#include "zulgurub.h"
-enum Jindo
+enum Say
{
- SAY_AGGRO = 1,
+ SAY_AGGRO = 1
+};
+enum Spells
+{
SPELL_BRAINWASHTOTEM = 24262,
- SPELL_POWERFULLHEALINGWARD = 24309, //We will not use this spell. We will summon a totem by script cause the spell totems will not cast.
+ SPELL_POWERFULLHEALINGWARD = 24309, // HACKED Totem summoned by script because the spell totems will not cast.
SPELL_HEX = 24053,
SPELL_DELUSIONSOFJINDO = 24306,
- SPELL_SHADEOFJINDO = 24308, //We will not use this spell. We will summon a shade by script.
-
+ SPELL_SHADEOFJINDO = 24308, // HACKED
//Healing Ward Spell
- SPELL_HEAL = 38588, //Totems are not working right. Right heal spell ID is 24311 but this spell is not casting...
-
+ SPELL_HEAL = 38588, // HACKED Totems are not working right. Right heal spell ID is 24311 but this spell is not casting...
//Shade of Jindo Spell
SPELL_SHADOWSHOCK = 19460,
SPELL_INVISIBLE = 24699
};
-class boss_jindo : public CreatureScript
+enum Events
{
- public:
+ EVENT_BRAINWASHTOTEM = 0,
+ EVENT_POWERFULLHEALINGWARD = 1,
+ EVENT_HEX = 2,
+ EVENT_DELUSIONSOFJINDO = 3,
+ EVENT_TELEPORT = 4
+};
- boss_jindo()
- : CreatureScript("boss_jindo")
- {
- }
+Position const TeleportLoc = {-11583.7783f, -1249.4278f, 77.5471f, 4.745f};
- struct boss_jindoAI : public ScriptedAI
- {
- boss_jindoAI(Creature* creature) : ScriptedAI(creature) {}
+class boss_jindo : public CreatureScript
+{
+ public: boss_jindo() : CreatureScript("boss_jindo") {}
- uint32 BrainWashTotem_Timer;
- uint32 HealingWard_Timer;
- uint32 Hex_Timer;
- uint32 Delusions_Timer;
- uint32 Teleport_Timer;
+ struct boss_jindoAI : public BossAI
+ {
+ boss_jindoAI(Creature* creature) : BossAI(creature, DATA_JINDO) {}
void Reset()
{
- BrainWashTotem_Timer = 20000;
- HealingWard_Timer = 16000;
- Hex_Timer = 8000;
- Delusions_Timer = 10000;
- Teleport_Timer = 5000;
+ _Reset();
+ }
+
+ void JustDied(Unit* /*killer*/)
+ {
+ _JustDied();
}
void EnterCombat(Unit* /*who*/)
{
+ _EnterCombat();
+ events.ScheduleEvent(EVENT_BRAINWASHTOTEM, 20000);
+ events.ScheduleEvent(EVENT_POWERFULLHEALINGWARD, 16000);
+ events.ScheduleEvent(EVENT_HEX, 8000);
+ events.ScheduleEvent(EVENT_DELUSIONSOFJINDO, 10000);
+ events.ScheduleEvent(EVENT_TELEPORT, 5000);
Talk(SAY_AGGRO);
}
- void UpdateAI(const uint32 diff)
+ void UpdateAI(uint32 const diff)
{
if (!UpdateVictim())
return;
- //BrainWashTotem_Timer
- if (BrainWashTotem_Timer <= diff)
- {
- DoCast(me, SPELL_BRAINWASHTOTEM);
- BrainWashTotem_Timer = urand(18000, 26000);
- } else BrainWashTotem_Timer -= diff;
-
- //HealingWard_Timer
- if (HealingWard_Timer <= diff)
- {
- //DoCast(me, SPELL_POWERFULLHEALINGWARD);
- me->SummonCreature(14987, me->GetPositionX()+3, me->GetPositionY()-2, me->GetPositionZ(), 0, TEMPSUMMON_TIMED_OR_DEAD_DESPAWN, 30000);
- HealingWard_Timer = urand(14000, 20000);
- } else HealingWard_Timer -= diff;
-
- //Hex_Timer
- if (Hex_Timer <= diff)
- {
- DoCast(me->getVictim(), SPELL_HEX);
-
- if (DoGetThreat(me->getVictim()))
- DoModifyThreatPercent(me->getVictim(), -80);
+ events.Update(diff);
- Hex_Timer = urand(12000, 20000);
- } else Hex_Timer -= diff;
-
- //Casting the delusion curse with a shade. So shade will attack the same target with the curse.
- if (Delusions_Timer <= diff)
- {
- if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0))
- {
- DoCast(target, SPELL_DELUSIONSOFJINDO);
-
- Creature* Shade = me->SummonCreature(14986, target->GetPositionX(), target->GetPositionY(), target->GetPositionZ(), 0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 15000);
- if (Shade)
- Shade->AI()->AttackStart(target);
- }
-
- Delusions_Timer = urand(4000, 12000);
- } else Delusions_Timer -= diff;
+ if (me->HasUnitState(UNIT_STATE_CASTING))
+ return;
- //Teleporting a random gamer and spawning 9 skeletons that will attack this gamer
- if (Teleport_Timer <= diff)
+ while (uint32 eventId = events.ExecuteEvent())
{
- Unit* target = NULL;
- target = SelectTarget(SELECT_TARGET_RANDOM, 0);
- if (target && target->GetTypeId() == TYPEID_PLAYER)
+ switch (eventId)
{
- DoTeleportPlayer(target, -11583.7783f, -1249.4278f, 77.5471f, 4.745f);
-
- if (DoGetThreat(me->getVictim()))
- DoModifyThreatPercent(target, -100);
-
- Creature* Skeletons;
- Skeletons = me->SummonCreature(14826, target->GetPositionX()+2, target->GetPositionY(), target->GetPositionZ(), 0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 15000);
- if (Skeletons)
- Skeletons->AI()->AttackStart(target);
- Skeletons = me->SummonCreature(14826, target->GetPositionX()-2, target->GetPositionY(), target->GetPositionZ(), 0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 15000);
- if (Skeletons)
- Skeletons->AI()->AttackStart(target);
- Skeletons = me->SummonCreature(14826, target->GetPositionX()+4, target->GetPositionY(), target->GetPositionZ(), 0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 15000);
- if (Skeletons)
- Skeletons->AI()->AttackStart(target);
- Skeletons = me->SummonCreature(14826, target->GetPositionX()-4, target->GetPositionY(), target->GetPositionZ(), 0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 15000);
- if (Skeletons)
- Skeletons->AI()->AttackStart(target);
- Skeletons = me->SummonCreature(14826, target->GetPositionX(), target->GetPositionY()+2, target->GetPositionZ(), 0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 15000);
- if (Skeletons)
- Skeletons->AI()->AttackStart(target);
- Skeletons = me->SummonCreature(14826, target->GetPositionX(), target->GetPositionY()-2, target->GetPositionZ(), 0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 15000);
- if (Skeletons)
- Skeletons->AI()->AttackStart(target);
- Skeletons = me->SummonCreature(14826, target->GetPositionX(), target->GetPositionY()+4, target->GetPositionZ(), 0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 15000);
- if (Skeletons)
- Skeletons->AI()->AttackStart(target);
- Skeletons = me->SummonCreature(14826, target->GetPositionX(), target->GetPositionY()-4, target->GetPositionZ(), 0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 15000);
- if (Skeletons)
- Skeletons->AI()->AttackStart(target);
- Skeletons = me->SummonCreature(14826, target->GetPositionX()+3, target->GetPositionY(), target->GetPositionZ(), 0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 15000);
- if (Skeletons)
- Skeletons->AI()->AttackStart(target);
+ case EVENT_BRAINWASHTOTEM:
+ DoCast(me, SPELL_BRAINWASHTOTEM);
+ events.ScheduleEvent(EVENT_BRAINWASHTOTEM, urand(18000, 26000));
+ break;
+ case EVENT_POWERFULLHEALINGWARD: // HACK
+ //DoCast(me, SPELL_POWERFULLHEALINGWARD);
+ me->SummonCreature(14987, me->GetPositionX()+3, me->GetPositionY()-2, me->GetPositionZ(), 0, TEMPSUMMON_TIMED_OR_DEAD_DESPAWN, 30000);
+ events.ScheduleEvent(EVENT_POWERFULLHEALINGWARD, urand(14000, 20000));
+ break;
+ case EVENT_HEX:
+ DoCastVictim(SPELL_HEX, true);
+ if (DoGetThreat(me->getVictim()))
+ DoModifyThreatPercent(me->getVictim(), -80);
+ events.ScheduleEvent(EVENT_HEX, urand(12000, 20000));
+ break;
+ case EVENT_DELUSIONSOFJINDO: // HACK
+ // Casting the delusion curse with a shade so shade will attack the same target with the curse.
+ if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0))
+ {
+ DoCast(target, SPELL_DELUSIONSOFJINDO);
+ Creature* Shade = me->SummonCreature(NPC_SHADE_OF_JINDO, target->GetPositionX(), target->GetPositionY(), target->GetPositionZ(), 0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 15000);
+ if (Shade)
+ Shade->AI()->AttackStart(target);
+ }
+ events.ScheduleEvent(EVENT_DELUSIONSOFJINDO, urand(4000, 12000));
+ break;
+ case EVENT_TELEPORT: // Possible HACK
+ // Teleports a random player and spawns 9 Sacrificed Trolls to attack player
+ if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0))
+ {
+ DoTeleportPlayer(target, TeleportLoc.m_positionX, TeleportLoc.m_positionY, TeleportLoc.m_positionZ, TeleportLoc.m_orientation);
+ if (DoGetThreat(me->getVictim()))
+ DoModifyThreatPercent(target, -100);
+ Creature* SacrificedTroll;
+ SacrificedTroll = me->SummonCreature(NPC_SACRIFICED_TROLL, target->GetPositionX()+2, target->GetPositionY(), target->GetPositionZ(), 0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 15000);
+ if (SacrificedTroll)
+ SacrificedTroll->AI()->AttackStart(target);
+ SacrificedTroll = me->SummonCreature(NPC_SACRIFICED_TROLL, target->GetPositionX()-2, target->GetPositionY(), target->GetPositionZ(), 0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 15000);
+ if (SacrificedTroll)
+ SacrificedTroll->AI()->AttackStart(target);
+ SacrificedTroll = me->SummonCreature(NPC_SACRIFICED_TROLL, target->GetPositionX()+4, target->GetPositionY(), target->GetPositionZ(), 0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 15000);
+ if (SacrificedTroll)
+ SacrificedTroll->AI()->AttackStart(target);
+ SacrificedTroll = me->SummonCreature(NPC_SACRIFICED_TROLL, target->GetPositionX()-4, target->GetPositionY(), target->GetPositionZ(), 0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 15000);
+ if (SacrificedTroll)
+ SacrificedTroll->AI()->AttackStart(target);
+ SacrificedTroll = me->SummonCreature(NPC_SACRIFICED_TROLL, target->GetPositionX(), target->GetPositionY()+2, target->GetPositionZ(), 0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 15000);
+ if (SacrificedTroll)
+ SacrificedTroll->AI()->AttackStart(target);
+ SacrificedTroll = me->SummonCreature(NPC_SACRIFICED_TROLL, target->GetPositionX(), target->GetPositionY()-2, target->GetPositionZ(), 0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 15000);
+ if (SacrificedTroll)
+ SacrificedTroll->AI()->AttackStart(target);
+ SacrificedTroll = me->SummonCreature(NPC_SACRIFICED_TROLL, target->GetPositionX(), target->GetPositionY()+4, target->GetPositionZ(), 0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 15000);
+ if (SacrificedTroll)
+ SacrificedTroll->AI()->AttackStart(target);
+ SacrificedTroll = me->SummonCreature(NPC_SACRIFICED_TROLL, target->GetPositionX(), target->GetPositionY()-4, target->GetPositionZ(), 0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 15000);
+ if (SacrificedTroll)
+ SacrificedTroll->AI()->AttackStart(target);
+ SacrificedTroll = me->SummonCreature(NPC_SACRIFICED_TROLL, target->GetPositionX()+3, target->GetPositionY(), target->GetPositionZ(), 0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 15000);
+ if (SacrificedTroll)
+ SacrificedTroll->AI()->AttackStart(target);
+ }
+ events.ScheduleEvent(EVENT_TELEPORT, urand(15000, 23000));
+ break;
+ default:
+ break;
}
-
- Teleport_Timer = urand(15000, 23000);
- } else Teleport_Timer -= diff;
+ }
DoMeleeAttackIfReady();
}
@@ -283,4 +283,3 @@ void AddSC_boss_jindo()
new mob_healing_ward();
new mob_shade_of_jindo();
}
-
diff --git a/src/server/scripts/EasternKingdoms/ZulGurub/boss_mandokir.cpp b/src/server/scripts/EasternKingdoms/ZulGurub/boss_mandokir.cpp
index bcc080bb8a5..e402480609b 100644
--- a/src/server/scripts/EasternKingdoms/ZulGurub/boss_mandokir.cpp
+++ b/src/server/scripts/EasternKingdoms/ZulGurub/boss_mandokir.cpp
@@ -25,336 +25,404 @@ EndScriptData */
#include "ScriptMgr.h"
#include "ScriptedCreature.h"
+#include "Spell.h"
+#include "SpellAuras.h"
+#include "SpellScript.h"
#include "zulgurub.h"
-enum Mandokir
+enum Says
{
- SAY_AGGRO = 0,
- SAY_DING_KILL = 1,
- SAY_WATCH = 2,
- SAY_WATCH_WHISPER = 3, //is this text for real? easter egg?
- SAY_GRATS_JINDO = 0,
-
- SPELL_CHARGE = 24408,
- SPELL_CLEAVE = 7160,
- SPELL_FEAR = 29321,
- SPELL_WHIRLWIND = 15589,
- SPELL_MORTAL_STRIKE = 16856,
- SPELL_ENRAGE = 24318,
- SPELL_WATCH = 24314,
- SPELL_LEVEL_UP = 24312,
-
-//Ohgans Spells
- SPELL_SUNDERARMOR = 24317,
-
- NPC_SPEAKER = 11391
+ SAY_AGGRO = 0,
+ SAY_DING_KILL = 1,
+ SAY_WATCH = 2,
+ SAY_WATCH_WHISPER = 3,
+ SAY_OHGAN_DEAD = 4,
+ SAY_GRATS_JINDO = 0,
};
-class boss_mandokir : public CreatureScript
+enum Spells
{
- public:
+ SPELL_CHARGE = 24408, // seen
+ SPELL_OVERPOWER = 24407, // Seen
+ SPELL_FEAR = 29321,
+ SPELL_WHIRLWIND = 13736, // Triggers 15589
+ SPELL_MORTAL_STRIKE = 16856, // Seen
+ SPELL_FRENZY = 24318, // seen
+ SPELL_WATCH = 24314, // seen 24315, 24316
+ SPELL_WATCH_CHARGE = 24315, // Triggers 24316
+ SPELL_LEVEL_UP = 24312 //
+};
- boss_mandokir()
- : CreatureScript("boss_mandokir")
- {
- }
+enum Events
+{
+ EVENT_CHECK_SPEAKER = 1,
+ EVENT_CHECK_START = 2,
+ EVENT_STARTED = 3,
+ EVENT_OVERPOWER = 4,
+ EVENT_MORTAL_STRIKE = 5,
+ EVENT_WHIRLWIND = 6,
+ EVENT_CHECK_OHGAN = 7,
+ EVENT_WATCH_PLAYER = 8,
+ EVENT_CHARGE_PLAYER = 9
+};
- struct boss_mandokirAI : public ScriptedAI
- {
- boss_mandokirAI(Creature* creature) : ScriptedAI(creature)
- {
- instance = creature->GetInstanceScript();
- }
+enum Misc
+{
+ MODEL_OHGAN_MOUNT = 15271,
+ PATH_MANDOKIR = 492861,
+ POINT_MANDOKIR_END = 24,
+ CHAINED_SPIRT_COUNT = 20
+};
- uint32 KillCount;
- uint32 Watch_Timer;
- uint32 TargetInRange;
- uint32 Cleave_Timer;
- uint32 Whirlwind_Timer;
- uint32 Fear_Timer;
- uint32 MortalStrike_Timer;
- uint32 Check_Timer;
- float targetX;
- float targetY;
- float targetZ;
+Position const PosSummonChainedSpirits[CHAINED_SPIRT_COUNT] =
+{
+ { -12167.17f, -1979.330f, 133.0992f, 2.268928f },
+ { -12262.74f, -1953.394f, 133.5496f, 0.593412f },
+ { -12176.89f, -1983.068f, 133.7841f, 2.129302f },
+ { -12226.45f, -1977.933f, 132.7982f, 1.466077f },
+ { -12204.74f, -1890.431f, 135.7569f, 4.415683f },
+ { -12216.70f, -1891.806f, 136.3496f, 4.677482f },
+ { -12236.19f, -1892.034f, 134.1041f, 5.044002f },
+ { -12248.24f, -1893.424f, 134.1182f, 5.270895f },
+ { -12257.36f, -1897.663f, 133.1484f, 5.462881f },
+ { -12265.84f, -1903.077f, 133.1649f, 5.654867f },
+ { -12158.69f, -1972.707f, 133.8751f, 2.408554f },
+ { -12178.82f, -1891.974f, 134.1786f, 3.944444f },
+ { -12193.36f, -1890.039f, 135.1441f, 4.188790f },
+ { -12275.59f, -1932.845f, 134.9017f, 0.174533f },
+ { -12273.51f, -1941.539f, 136.1262f, 0.314159f },
+ { -12247.02f, -1963.497f, 133.9476f, 0.872665f },
+ { -12238.68f, -1969.574f, 133.6273f, 1.134464f },
+ { -12192.78f, -1982.116f, 132.6966f, 1.919862f },
+ { -12210.81f, -1979.316f, 133.8700f, 1.797689f },
+ { -12283.51f, -1924.839f, 133.5170f, 0.069813f }
+};
- InstanceScript* instance;
+Position const PosMandokir[2] =
+{
+ { -12167.8f, -1927.25f, 153.73f, 3.76991f },
+ { -12197.86f, -1949.392f, 130.2745f, 0.0f }
+};
- bool endWatch;
- bool someWatched;
- bool RaptorDead;
- bool CombatStart;
- bool SpeakerDead;
+class boss_mandokir : public CreatureScript
+{
+ public: boss_mandokir() : CreatureScript("boss_mandokir") {}
- uint64 WatchTarget;
+ struct boss_mandokirAI : public BossAI
+ {
+ boss_mandokirAI(Creature* creature) : BossAI(creature, DATA_MANDOKIR) { }
void Reset()
{
- KillCount = 0;
- Watch_Timer = 33000;
- Cleave_Timer = 7000;
- Whirlwind_Timer = 20000;
- Fear_Timer = 1000;
- MortalStrike_Timer = 1000;
- Check_Timer = 1000;
-
- targetX = 0.0f;
- targetY = 0.0f;
- targetZ = 0.0f;
- TargetInRange = 0;
-
- WatchTarget = 0;
-
- someWatched = false;
- endWatch = false;
- RaptorDead = false;
- CombatStart = false;
- SpeakerDead = false;
-
- DoCast(me, 23243);
+ if (me->GetPositionZ() > 140.0f)
+ {
+ _Reset();
+ killCount = 0;
+ me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC|UNIT_FLAG_IMMUNE_TO_NPC);
+ events.ScheduleEvent(EVENT_CHECK_START, 1000);
+ if (Creature* speaker = Creature::GetCreature(*me, instance->GetData64(NPC_VILEBRANCH_SPEAKER)))
+ if (!speaker->isAlive())
+ speaker->Respawn(true);
+ }
+ summons.DespawnAll();
+ me->Mount(MODEL_OHGAN_MOUNT);
}
- void KilledUnit(Unit* victim)
+ void JustDied(Unit* /*killer*/)
{
- if (victim->GetTypeId() == TYPEID_PLAYER)
- {
- ++KillCount;
-
- if (KillCount == 3)
- {
- Talk(SAY_DING_KILL);
-
- if (instance)
- {
- uint64 JindoGUID = instance->GetData64(DATA_JINDO);
- if (JindoGUID)
- {
- if (Creature* jTemp = Creature::GetCreature(*me, JindoGUID))
- {
- if (jTemp->isAlive())
- jTemp->AI()->Talk(SAY_GRATS_JINDO);
- }
- }
- }
- DoCast(me, SPELL_LEVEL_UP, true);
- KillCount = 0;
- }
- }
+ // Do not want to unsummon Ohgan
+ for (int i = 0; i < CHAINED_SPIRT_COUNT; ++i)
+ if (Creature* unsummon = Creature::GetCreature(*me, chainedSpirtGUIDs[i]))
+ unsummon->DespawnOrUnsummon();
+ instance->SetBossState(DATA_MANDOKIR, DONE);
+ instance->SaveToDB();
}
void EnterCombat(Unit* /*who*/)
{
+ _EnterCombat();
+ events.ScheduleEvent(EVENT_OVERPOWER, urand(7000, 9000));
+ events.ScheduleEvent(EVENT_MORTAL_STRIKE, urand(12000, 18000));
+ events.ScheduleEvent(EVENT_WHIRLWIND, urand(24000, 30000));
+ events.ScheduleEvent(EVENT_CHECK_OHGAN, 1000);
+ events.ScheduleEvent(EVENT_WATCH_PLAYER, urand(13000, 15000));
+ events.ScheduleEvent(EVENT_CHARGE_PLAYER, urand(33000, 38000));
+ me->SetHomePosition(me->GetPositionX(), me->GetPositionY(), me->GetPositionZ(), me->GetOrientation());
Talk(SAY_AGGRO);
+ me->Dismount();
+ // Summon Ohgan (Spell missing) TEMP HACK
+ me->SummonCreature(NPC_OHGAN, me->GetPositionX()-3, me->GetPositionY(), me->GetPositionZ(), me->GetOrientation(), TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 35000);
+ // Summon Chained Spirits
+ for (int i = 0; i < CHAINED_SPIRT_COUNT; ++i)
+ {
+ Creature* chainedSpirt = me->SummonCreature(NPC_CHAINED_SPIRT, PosSummonChainedSpirits[i], TEMPSUMMON_CORPSE_DESPAWN);
+ chainedSpirtGUIDs[i] = chainedSpirt->GetGUID();
+ }
+ DoZoneInCombat();
}
- void UpdateAI(const uint32 diff)
+ void KilledUnit(Unit* victim)
{
- if (!SpeakerDead)
+ if (victim->GetTypeId() != TYPEID_PLAYER)
+ return;
+
+ if (++killCount == 3)
{
- if (!me->FindNearestCreature(NPC_SPEAKER, 100.0f, true))
+ Talk(SAY_DING_KILL);
+ if (Creature* jindo = Creature::GetCreature(*me, instance->GetData64(DATA_JINDO)))
+ if (jindo->isAlive())
+ jindo->AI()->Talk(SAY_GRATS_JINDO);
+ DoCast(me, SPELL_LEVEL_UP, true);
+ killCount = 0;
+ }
+ }
+
+ void MovementInform(uint32 type, uint32 id)
+ {
+ if (type == WAYPOINT_MOTION_TYPE)
+ {
+ me->SetWalk(false);
+ if (id == POINT_MANDOKIR_END)
{
- me->GetMotionMaster()->MovePoint(0, -12196.3f, -1948.37f, 130.36f);
- SpeakerDead = true;
+ me->SetHomePosition(PosMandokir[0]);
+ instance->SetBossState(DATA_MANDOKIR, NOT_STARTED);
+ me->DespawnOrUnsummon(6000); // No idea how to respawn on wipe.
}
}
+ }
- if (me->GetMotionMaster()->GetCurrentMovementGeneratorType() != POINT_MOTION_TYPE && SpeakerDead)
- me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC);
+ void UpdateAI(uint32 const diff)
+ {
+ events.Update(diff);
if (!UpdateVictim())
- return;
-
- if (me->getVictim() && me->isAlive())
{
- if (!CombatStart)
- {
- //At combat Start Mandokir is mounted so we must unmount it first
- me->Dismount();
-
- //And summon his raptor
- me->SummonCreature(14988, me->getVictim()->GetPositionX(), me->getVictim()->GetPositionY(), me->getVictim()->GetPositionZ(), 0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 35000);
- CombatStart = true;
- }
-
- if (Watch_Timer <= diff) //Every 20 Sec Mandokir will check this
+ if (instance->GetBossState(DATA_MANDOKIR) == NOT_STARTED || instance->GetBossState(DATA_MANDOKIR) == SPECIAL)
{
- if (WatchTarget) //If someone is watched and If the Position of the watched target is different from the one stored, or are attacking, mandokir will charge him
+ while (uint32 eventId = events.ExecuteEvent())
{
- Unit* unit = Unit::GetUnit(*me, WatchTarget);
-
- if (unit && (
- targetX != unit->GetPositionX() ||
- targetY != unit->GetPositionY() ||
- targetZ != unit->GetPositionZ() ||
- unit->isInCombat()))
+ switch (eventId)
{
- if (me->IsWithinMeleeRange(unit))
- {
- DoCast(unit, 24316);
- }
- else
- {
- DoCast(unit, SPELL_CHARGE);
- //me->SendMonsterMove(unit->GetPositionX(), unit->GetPositionY(), unit->GetPositionZ(), 0, true, 1);
- AttackStart(unit);
- }
+ case EVENT_CHECK_START:
+ if (instance->GetBossState(DATA_MANDOKIR) == SPECIAL)
+ {
+ me->GetMotionMaster()->MovePoint(0, PosMandokir[1].m_positionX, PosMandokir[1].m_positionY, PosMandokir[1].m_positionZ);
+ events.ScheduleEvent(EVENT_STARTED, 6000);
+ }
+ else
+ events.ScheduleEvent(EVENT_CHECK_START, 1000);
+ break;
+ case EVENT_STARTED:
+ me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC|UNIT_FLAG_IMMUNE_TO_NPC);
+ me->GetMotionMaster()->MovePath(PATH_MANDOKIR, false);
+ break;
+ default:
+ break;
}
}
- someWatched = false;
- Watch_Timer = 20000;
- } else Watch_Timer -= diff;
-
- if ((Watch_Timer < 8000) && !someWatched) //8 sec(cast time + expire time) before the check for the watch effect mandokir will cast watch debuff on a random target
- {
- if (Unit* p = SelectTarget(SELECT_TARGET_RANDOM, 0))
- {
- Talk(SAY_WATCH, p->GetGUID());
- DoCast(p, SPELL_WATCH);
- WatchTarget = p->GetGUID();
- someWatched = true;
- endWatch = true;
- }
}
+ return;
+ }
- if ((Watch_Timer < 1000) && endWatch) //1 sec before the debuf expire, store the target position
- {
- Unit* unit = Unit::GetUnit(*me, WatchTarget);
- if (unit)
- {
- targetX = unit->GetPositionX();
- targetY = unit->GetPositionY();
- targetZ = unit->GetPositionZ();
- }
- endWatch = false;
- }
+ if (me->HasUnitState(UNIT_STATE_CASTING))
+ return;
- if (!someWatched)
+ while (uint32 eventId = events.ExecuteEvent())
+ {
+ switch (eventId)
{
- //Cleave
- if (Cleave_Timer <= diff)
- {
- DoCast(me->getVictim(), SPELL_CLEAVE);
- Cleave_Timer = 7000;
- } else Cleave_Timer -= diff;
-
- //Whirlwind
- if (Whirlwind_Timer <= diff)
- {
+ case EVENT_OVERPOWER:
+ DoCastVictim(SPELL_OVERPOWER, true);
+ events.ScheduleEvent(EVENT_OVERPOWER, urand(6000, 12000));
+ break;
+ case EVENT_MORTAL_STRIKE:
+ if (me->getVictim() && me->getVictim()->HealthBelowPct(50))
+ DoCastVictim(SPELL_MORTAL_STRIKE, true);
+ events.ScheduleEvent(EVENT_MORTAL_STRIKE, urand(12000, 18000));
+ break;
+ case EVENT_WHIRLWIND:
DoCast(me, SPELL_WHIRLWIND);
- Whirlwind_Timer = 18000;
- } else Whirlwind_Timer -= diff;
-
- //If more then 3 targets in melee range mandokir will cast fear
- if (Fear_Timer <= diff)
- {
- TargetInRange = 0;
-
- std::list<HostileReference*>::const_iterator i = me->getThreatManager().getThreatList().begin();
- for (; i != me->getThreatManager().getThreatList().end(); ++i)
+ events.ScheduleEvent(EVENT_WHIRLWIND, urand(22000, 26000));
+ break;
+ case EVENT_CHECK_OHGAN:
+ if (instance->GetBossState(DATA_OHGAN) == DONE)
{
- Unit* unit = Unit::GetUnit(*me, (*i)->getUnitGuid());
- if (unit && me->IsWithinMeleeRange(unit))
- ++TargetInRange;
+ DoCast(me, SPELL_FRENZY);
+ Talk(SAY_OHGAN_DEAD);
}
-
- if (TargetInRange > 3)
- DoCast(me->getVictim(), SPELL_FEAR);
-
- Fear_Timer = 4000;
- } else Fear_Timer -=diff;
-
- //Mortal Strike if target below 50% hp
- if (me->getVictim() && me->getVictim()->HealthBelowPct(50))
- {
- if (MortalStrike_Timer <= diff)
+ else
+ events.ScheduleEvent(EVENT_CHECK_OHGAN, 1000);
+ break;
+ case EVENT_WATCH_PLAYER:
+ if (Unit* player = SelectTarget(SELECT_TARGET_RANDOM, 0, 100, true))
{
- DoCast(me->getVictim(), SPELL_MORTAL_STRIKE);
- MortalStrike_Timer = 15000;
- } else MortalStrike_Timer -= diff;
- }
- }
- //Checking if Ohgan is dead. If yes Mandokir will enrage.
- if (Check_Timer <= diff)
- {
- if (instance)
- {
- if (instance->GetData(DATA_OHGAN) == DONE)
- {
- if (!RaptorDead)
- {
- DoCast(me, SPELL_ENRAGE);
- RaptorDead = true;
- }
+ DoCast(player, SPELL_WATCH);
+ Talk(SAY_WATCH, player->GetGUID());
}
- }
-
- Check_Timer = 1000;
- } else Check_Timer -= diff;
-
- DoMeleeAttackIfReady();
+ events.ScheduleEvent(EVENT_WATCH_PLAYER, urand(12000, 15000));
+ break;
+ case EVENT_CHARGE_PLAYER:
+ DoCast(SelectTarget(SELECT_TARGET_RANDOM, 0, 40, true), SPELL_CHARGE);
+ events.ScheduleEvent(EVENT_CHARGE_PLAYER, urand(22000, 30000));
+ break;
+ default:
+ break;
+ }
}
+
+ DoMeleeAttackIfReady();
}
+
+ private:
+ uint8 killCount;
+ uint64 chainedSpirtGUIDs[CHAINED_SPIRT_COUNT];
};
CreatureAI* GetAI(Creature* creature) const
{
- return new boss_mandokirAI(creature);
+ return GetZulGurubAI<boss_mandokirAI>(creature);
}
};
-//Ohgan
-class mob_ohgan : public CreatureScript
+// Ohgan
+
+enum OhganSpells
{
- public:
+ SPELL_SUNDERARMOR = 24317
+};
- mob_ohgan()
- : CreatureScript("mob_ohgan")
- {
- }
+class mob_ohgan : public CreatureScript
+{
+ public: mob_ohgan() : CreatureScript("mob_ohgan") {}
struct mob_ohganAI : public ScriptedAI
{
- mob_ohganAI(Creature* creature) : ScriptedAI(creature)
+ mob_ohganAI(Creature* creature) : ScriptedAI(creature), instance(creature->GetInstanceScript()) { }
+
+ void Reset()
{
- instance = creature->GetInstanceScript();
+ SunderArmor_Timer = 5000;
+ }
+
+ void EnterCombat(Unit* /*who*/) {}
+
+ void JustDied(Unit* /*killer*/)
+ {
+ instance->SetBossState(DATA_OHGAN, DONE);
+ }
+
+ void UpdateAI(const uint32 diff)
+ {
+ // Return since we have no target
+ if (!UpdateVictim())
+ return;
+
+ if (SunderArmor_Timer <= diff)
+ {
+ DoCastVictim(SPELL_SUNDERARMOR, true);
+ SunderArmor_Timer = urand(10000, 15000);
+ } else SunderArmor_Timer -= diff;
+
+ DoMeleeAttackIfReady();
}
+ private:
uint32 SunderArmor_Timer;
InstanceScript* instance;
+ };
+
+ CreatureAI* GetAI(Creature* creature) const
+ {
+ return GetZulGurubAI<mob_ohganAI>(creature);
+ }
+};
+
+enum VilebranchSpells
+{
+ SPELL_DEMORALIZING_SHOUT = 13730,
+ SPELL_CLEAVE = 15284
+};
+
+class mob_vilebranch_speaker : public CreatureScript
+{
+ public: mob_vilebranch_speaker() : CreatureScript("mob_vilebranch_speaker") {}
+
+ struct mob_vilebranch_speakerAI : public ScriptedAI
+ {
+ mob_vilebranch_speakerAI(Creature* creature) : ScriptedAI(creature), instance(creature->GetInstanceScript()) { }
void Reset()
{
- SunderArmor_Timer = 5000;
+ demoralizing_Shout_Timer = urand (2000, 4000);
+ cleave_Timer = urand (5000, 8000);
}
void EnterCombat(Unit* /*who*/) {}
void JustDied(Unit* /*killer*/)
{
- if (instance)
- instance->SetData(DATA_OHGAN, DONE);
+ instance->SetBossState(DATA_MANDOKIR, SPECIAL);
}
- void UpdateAI (const uint32 diff)
+ void UpdateAI(const uint32 diff)
{
- //Return since we have no target
+ // Return since we have no target
if (!UpdateVictim())
return;
- //SunderArmor_Timer
- if (SunderArmor_Timer <= diff)
+ if (demoralizing_Shout_Timer <= diff)
{
- DoCast(me->getVictim(), SPELL_SUNDERARMOR);
- SunderArmor_Timer = urand(10000, 15000);
- } else SunderArmor_Timer -= diff;
+ DoCast(me, SPELL_DEMORALIZING_SHOUT);
+ demoralizing_Shout_Timer = urand(22000, 30000);
+ } else demoralizing_Shout_Timer -= diff;
+
+ if (cleave_Timer <= diff)
+ {
+ DoCastVictim(SPELL_CLEAVE, true);
+ cleave_Timer = urand(6000, 9000);
+ } else cleave_Timer -= diff;
DoMeleeAttackIfReady();
}
+
+ private:
+ uint32 demoralizing_Shout_Timer;
+ uint32 cleave_Timer;
+ InstanceScript* instance;
};
CreatureAI* GetAI(Creature* creature) const
{
- return new mob_ohganAI(creature);
+ return new mob_vilebranch_speakerAI(creature);
+ }
+};
+
+class spell_threatening_gaze : public SpellScriptLoader
+{
+ public:
+ spell_threatening_gaze() : SpellScriptLoader("spell_threatening_gaze") { }
+
+ class spell_threatening_gaze_AuraScript : public AuraScript
+ {
+ PrepareAuraScript(spell_threatening_gaze_AuraScript);
+
+ void OnRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/)
+ {
+ if (Unit* caster = GetCaster())
+ if(Unit* target = GetTarget())
+ if (GetTargetApplication()->GetRemoveMode() != AURA_REMOVE_BY_EXPIRE && GetTargetApplication()->GetRemoveMode() != AURA_REMOVE_BY_DEATH)
+ caster->CastSpell(target, SPELL_WATCH_CHARGE);
+ }
+
+ void Register()
+ {
+ OnEffectRemove += AuraEffectRemoveFn(spell_threatening_gaze_AuraScript::OnRemove, EFFECT_0, SPELL_AURA_DUMMY, AURA_EFFECT_HANDLE_REAL);
+ }
+ };
+
+ AuraScript* GetAuraScript() const
+ {
+ return new spell_threatening_gaze_AuraScript();
}
};
@@ -362,5 +430,6 @@ void AddSC_boss_mandokir()
{
new boss_mandokir();
new mob_ohgan();
+ new mob_vilebranch_speaker();
+ new spell_threatening_gaze();
}
-
diff --git a/src/server/scripts/EasternKingdoms/ZulGurub/boss_marli.cpp b/src/server/scripts/EasternKingdoms/ZulGurub/boss_marli.cpp
index 56aeee93ee2..40850a988e4 100644
--- a/src/server/scripts/EasternKingdoms/ZulGurub/boss_marli.cpp
+++ b/src/server/scripts/EasternKingdoms/ZulGurub/boss_marli.cpp
@@ -27,192 +27,189 @@ EndScriptData */
#include "ScriptedCreature.h"
#include "zulgurub.h"
-enum Marli
+enum Says
{
SAY_AGGRO = 0,
SAY_TRANSFORM = 1,
SAY_SPIDER_SPAWN = 2,
- SAY_DEATH = 3,
+ SAY_DEATH = 3
+};
+
+enum Spells
+{
+ SPELL_CHARGE = 22911,
+ SPELL_ASPECT_OF_MARLI = 24686, // A stun spell
+ SPELL_ENVOLWINGWEB = 24110,
+ SPELL_POISON_VOLLEY = 24099,
+ SPELL_SPIDER_FORM = 24084,
+ // The Spider Spell
+ SPELL_LEVELUP = 24312 // Not right Spell.
+};
- SPELL_CHARGE = 22911,
- SPELL_ASPECT_OF_MARLI = 24686, // A stun spell
- SPELL_ENVOLWINGWEB = 24110,
- SPELL_POISONVOLLEY = 24099,
- SPELL_SPIDER_FORM = 24084,
+enum Events
+{
+ EVENT_SPAWN_START_SPIDERS = 0, // Phase 1
+ EVENT_POISON_VOLLEY = 1, // Phase All
+ EVENT_SPAWN_SPIDER = 2, // Phase All
+ EVENT_CHARGE_PLAYER = 3, // Phase 3
+ EVENT_ASPECT_OF_MARLI = 4, // Phase 2
+ EVENT_TRANSFORM = 5, // Phase 2
+ EVENT_TRANSFORM_BACK = 6 // Phase 3
+};
-//The Spider Spells
- SPELL_LEVELUP = 24312 //Not right Spell.
+enum Phases
+{
+ PHASE_ONE = 1,
+ PHASE_TWO = 2,
+ PHASE_THREE = 3
+};
+
+enum ModelId
+{
+ MODEL_MARLI = 15220
};
class boss_marli : public CreatureScript
{
- public:
+ public: boss_marli() : CreatureScript("boss_marli") {}
- boss_marli()
- : CreatureScript("boss_marli")
+ struct boss_marliAI : public BossAI
{
- }
+ boss_marliAI(Creature* creature) : BossAI(creature, DATA_MARLI) {}
- struct boss_marliAI : public ScriptedAI
- {
- boss_marliAI(Creature* creature) : ScriptedAI(creature)
+ void Reset()
{
- instance = creature->GetInstanceScript();
+ _Reset();
}
- InstanceScript* instance;
-
- uint32 SpawnStartSpiders_Timer;
- uint32 PoisonVolley_Timer;
- uint32 SpawnSpider_Timer;
- uint32 Charge_Timer;
- uint32 Aspect_Timer;
- uint32 Transform_Timer;
- uint32 TransformBack_Timer;
-
- bool Spawned;
- bool PhaseTwo;
-
- void Reset()
+ void JustDied(Unit* /*killer*/)
{
- SpawnStartSpiders_Timer = 1000;
- PoisonVolley_Timer = 15000;
- SpawnSpider_Timer = 30000;
- Charge_Timer = 1500;
- Aspect_Timer = 12000;
- Transform_Timer = 45000;
- TransformBack_Timer = 25000;
-
- Spawned = false;
- PhaseTwo = false;
+ _JustDied();
+ Talk(SAY_DEATH);
}
void EnterCombat(Unit* /*who*/)
{
+ _EnterCombat();
+ events.ScheduleEvent(EVENT_SPAWN_START_SPIDERS, 1000, 0, PHASE_ONE);
Talk(SAY_AGGRO);
}
- void JustDied(Unit* /*killer*/)
- {
- Talk(SAY_DEATH);
- if (instance)
- instance->SetData(DATA_MARLI, DONE);
- }
-
- void UpdateAI(const uint32 diff)
+ void UpdateAI(uint32 const diff)
{
if (!UpdateVictim())
return;
- if (me->getVictim() && me->isAlive())
- {
- if (PoisonVolley_Timer <= diff)
- {
- DoCast(me->getVictim(), SPELL_POISONVOLLEY);
- PoisonVolley_Timer = urand(10000, 20000);
- } else PoisonVolley_Timer -= diff;
+ events.Update(diff);
- if (!PhaseTwo && Aspect_Timer <= diff)
- {
- DoCast(me->getVictim(), SPELL_ASPECT_OF_MARLI);
- Aspect_Timer = urand(13000, 18000);
- } else Aspect_Timer -= diff;
-
- if (!Spawned && SpawnStartSpiders_Timer <= diff)
- {
- Talk(SAY_SPIDER_SPAWN);
-
- Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0);
- if (!target)
- return;
-
- Creature* Spider = NULL;
-
- Spider = me->SummonCreature(15041, target->GetPositionX(), target->GetPositionY(), target->GetPositionZ(), 0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 15000);
- if (Spider)
- Spider->AI()->AttackStart(target);
- Spider = me->SummonCreature(15041, target->GetPositionX(), target->GetPositionY(), target->GetPositionZ(), 0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 15000);
- if (Spider)
- Spider->AI()->AttackStart(target);
- Spider = me->SummonCreature(15041, target->GetPositionX(), target->GetPositionY(), target->GetPositionZ(), 0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 15000);
- if (Spider)
- Spider->AI()->AttackStart(target);
- Spider = me->SummonCreature(15041, target->GetPositionX(), target->GetPositionY(), target->GetPositionZ(), 0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 15000);
- if (Spider)
- Spider->AI()->AttackStart(target);
-
- Spawned = true;
- } else SpawnStartSpiders_Timer -= diff;
-
- if (SpawnSpider_Timer <= diff)
- {
- Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0);
- if (!target)
- return;
-
- Creature* Spider = me->SummonCreature(15041, target->GetPositionX(), target->GetPositionY(), target->GetPositionZ(), 0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 15000);
- if (Spider)
- Spider->AI()->AttackStart(target);
- SpawnSpider_Timer = urand(12000, 17000);
- } else SpawnSpider_Timer -= diff;
+ if (me->HasUnitState(UNIT_STATE_CASTING))
+ return;
- if (!PhaseTwo && Transform_Timer <= diff)
+ while (uint32 eventId = events.ExecuteEvent())
+ {
+ switch (eventId)
{
- Talk(SAY_TRANSFORM);
- DoCast(me, SPELL_SPIDER_FORM);
- const CreatureTemplate* cinfo = me->GetCreatureTemplate();
- me->SetBaseWeaponDamage(BASE_ATTACK, MINDAMAGE, (cinfo->mindmg +((cinfo->mindmg/100) * 35)));
- me->SetBaseWeaponDamage(BASE_ATTACK, MAXDAMAGE, (cinfo->maxdmg +((cinfo->maxdmg/100) * 35)));
- me->UpdateDamagePhysical(BASE_ATTACK);
- DoCast(me->getVictim(), SPELL_ENVOLWINGWEB);
-
- if (DoGetThreat(me->getVictim()))
- DoModifyThreatPercent(me->getVictim(), -100);
+ case EVENT_SPAWN_START_SPIDERS:
- PhaseTwo = true;
- Transform_Timer = urand(35000, 60000);
- } else Transform_Timer -= diff;
-
- if (PhaseTwo)
- {
- if (Charge_Timer <= diff)
- {
- Unit* target = NULL;
- int i = 0;
- while (i < 3) // max 3 tries to get a random target with power_mana
+ if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0))
{
- ++i;
- target = SelectTarget(SELECT_TARGET_RANDOM, 1, 100, true); // not aggro leader
- if (target && target->getPowerType() == POWER_MANA)
+ Talk(SAY_SPIDER_SPAWN);
+ Creature* Spider = NULL;
+ Spider = me->SummonCreature(15041, target->GetPositionX(), target->GetPositionY(), target->GetPositionZ(), 0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 15000);
+ if (Spider)
+ Spider->AI()->AttackStart(target);
+ Spider = me->SummonCreature(15041, target->GetPositionX(), target->GetPositionY(), target->GetPositionZ(), 0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 15000);
+ if (Spider)
+ Spider->AI()->AttackStart(target);
+ Spider = me->SummonCreature(15041, target->GetPositionX(), target->GetPositionY(), target->GetPositionZ(), 0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 15000);
+ if (Spider)
+ Spider->AI()->AttackStart(target);
+ Spider = me->SummonCreature(15041, target->GetPositionX(), target->GetPositionY(), target->GetPositionZ(), 0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 15000);
+ if (Spider)
+ Spider->AI()->AttackStart(target);
+ }
+ events.ScheduleEvent(EVENT_ASPECT_OF_MARLI, 12000, 0, PHASE_TWO);
+ events.ScheduleEvent(EVENT_TRANSFORM, 45000, 0, PHASE_TWO);
+ events.ScheduleEvent(EVENT_POISON_VOLLEY, 15000);
+ events.ScheduleEvent(EVENT_SPAWN_SPIDER, 30000);
+ events.ScheduleEvent(EVENT_TRANSFORM, 45000, 0, PHASE_TWO);
+ events.SetPhase(PHASE_TWO);
+ break;
+ case EVENT_POISON_VOLLEY:
+ DoCastVictim(SPELL_POISON_VOLLEY, true);
+ events.ScheduleEvent(EVENT_POISON_VOLLEY, urand(10000, 20000));
+ break;
+ case EVENT_ASPECT_OF_MARLI:
+ DoCastVictim(SPELL_ASPECT_OF_MARLI, true);
+ events.ScheduleEvent(EVENT_ASPECT_OF_MARLI, urand(13000, 18000), 0, PHASE_TWO);
+ break;
+ case EVENT_SPAWN_SPIDER:
+ if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0))
+ {
+ Creature* Spider = me->SummonCreature(15041, target->GetPositionX(), target->GetPositionY(), target->GetPositionZ(), 0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 15000);
+ if (Spider)
+ Spider->AI()->AttackStart(target);
+ }
+ events.ScheduleEvent(EVENT_SPAWN_SPIDER, urand(12000, 17000));
+ break;
+ case EVENT_TRANSFORM:
+ {
+ Talk(SAY_TRANSFORM);
+ DoCast(me, SPELL_SPIDER_FORM);
+ const CreatureTemplate* cinfo = me->GetCreatureTemplate();
+ me->SetBaseWeaponDamage(BASE_ATTACK, MINDAMAGE, (cinfo->mindmg +((cinfo->mindmg/100) * 35)));
+ me->SetBaseWeaponDamage(BASE_ATTACK, MAXDAMAGE, (cinfo->maxdmg +((cinfo->maxdmg/100) * 35)));
+ me->UpdateDamagePhysical(BASE_ATTACK);
+ DoCast(me->getVictim(), SPELL_ENVOLWINGWEB);
+ if (DoGetThreat(me->getVictim()))
+ DoModifyThreatPercent(me->getVictim(), -100);
+ events.ScheduleEvent(EVENT_CHARGE_PLAYER, 1500, 0, PHASE_THREE);
+ events.ScheduleEvent(EVENT_TRANSFORM_BACK, 25000, 0, PHASE_THREE);
+ events.SetPhase(PHASE_THREE);
+ break;
+ }
+ case EVENT_CHARGE_PLAYER:
+ {
+ Unit* target = NULL;
+ int i = 0;
+ while (i < 3) // max 3 tries to get a random target with power_mana
+ {
+ ++i;
+ Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 1, 100, true); // not aggro leader
+ if (target && target->getPowerType() == POWER_MANA)
i = 3;
+ }
+ if (target)
+ {
+ DoCast(target, SPELL_CHARGE);
+ //me->SetPosition(target->GetPositionX(), target->GetPositionY(), target->GetPositionZ(), 0);
+ //me->SendMonsterMove(target->GetPositionX(), target->GetPositionY(), target->GetPositionZ(), 0, true, 1);
+ AttackStart(target);
+ }
+ events.ScheduleEvent(EVENT_CHARGE_PLAYER, 8000, 0, PHASE_THREE);
+ break;
}
- if (target)
+ case EVENT_TRANSFORM_BACK:
{
- DoCast(target, SPELL_CHARGE);
- //me->SetPosition(target->GetPositionX(), target->GetPositionY(), target->GetPositionZ(), 0);
- //me->SendMonsterMove(target->GetPositionX(), target->GetPositionY(), target->GetPositionZ(), 0, true, 1);
- AttackStart(target);
+ me->SetDisplayId(MODEL_MARLI);
+ const CreatureTemplate* cinfo = me->GetCreatureTemplate();
+ me->SetBaseWeaponDamage(BASE_ATTACK, MINDAMAGE, (cinfo->mindmg +((cinfo->mindmg/100) * 1)));
+ me->SetBaseWeaponDamage(BASE_ATTACK, MAXDAMAGE, (cinfo->maxdmg +((cinfo->maxdmg/100) * 1)));
+ me->UpdateDamagePhysical(BASE_ATTACK);
+ events.ScheduleEvent(EVENT_ASPECT_OF_MARLI, 12000, 0, PHASE_TWO);
+ events.ScheduleEvent(EVENT_TRANSFORM, 45000, 0, PHASE_TWO);
+ events.ScheduleEvent(EVENT_POISON_VOLLEY, 15000);
+ events.ScheduleEvent(EVENT_SPAWN_SPIDER, 30000);
+ events.ScheduleEvent(EVENT_TRANSFORM, urand(35000, 60000), 0, PHASE_TWO);
+ events.SetPhase(PHASE_TWO);
+ break;
}
-
- Charge_Timer = 8000;
- } else Charge_Timer -= diff;
-
- if (TransformBack_Timer <= diff)
- {
- me->SetDisplayId(15220);
- const CreatureTemplate* cinfo = me->GetCreatureTemplate();
- me->SetBaseWeaponDamage(BASE_ATTACK, MINDAMAGE, (cinfo->mindmg +((cinfo->mindmg/100) * 1)));
- me->SetBaseWeaponDamage(BASE_ATTACK, MAXDAMAGE, (cinfo->maxdmg +((cinfo->maxdmg/100) * 1)));
- me->UpdateDamagePhysical(BASE_ATTACK);
-
- PhaseTwo = false;
- TransformBack_Timer = urand(25000, 40000);
- } else TransformBack_Timer -= diff;
-
+ default:
+ break;
}
-
- DoMeleeAttackIfReady();
}
+
+ DoMeleeAttackIfReady();
}
};
@@ -222,15 +219,10 @@ class boss_marli : public CreatureScript
}
};
-//Spawn of Marli
+// Spawn of Marli
class mob_spawn_of_marli : public CreatureScript
{
- public:
-
- mob_spawn_of_marli()
- : CreatureScript("mob_spawn_of_marli")
- {
- }
+ public: mob_spawn_of_marli() : CreatureScript("mob_spawn_of_marli") {}
struct mob_spawn_of_marliAI : public ScriptedAI
{
@@ -275,4 +267,3 @@ void AddSC_boss_marli()
new boss_marli();
new mob_spawn_of_marli();
}
-
diff --git a/src/server/scripts/EasternKingdoms/ZulGurub/boss_renataki.cpp b/src/server/scripts/EasternKingdoms/ZulGurub/boss_renataki.cpp
index d562b7cdf9b..6bf87decf7e 100644
--- a/src/server/scripts/EasternKingdoms/ZulGurub/boss_renataki.cpp
+++ b/src/server/scripts/EasternKingdoms/ZulGurub/boss_renataki.cpp
@@ -40,16 +40,11 @@ enum Misc
class boss_renataki : public CreatureScript
{
- public:
+ public: boss_renataki() : CreatureScript("boss_renataki") {}
- boss_renataki()
- : CreatureScript("boss_renataki")
+ struct boss_renatakiAI : public BossAI
{
- }
-
- struct boss_renatakiAI : public ScriptedAI
- {
- boss_renatakiAI(Creature* creature) : ScriptedAI(creature) {}
+ boss_renatakiAI(Creature* creature) : BossAI(creature, DATA_EDGE_OF_MADNESS) {}
uint32 Invisible_Timer;
uint32 Ambush_Timer;
@@ -62,6 +57,7 @@ class boss_renataki : public CreatureScript
void Reset()
{
+ _Reset();
Invisible_Timer = urand(8000, 18000);
Ambush_Timer = 3000;
Visible_Timer = 4000;
@@ -72,8 +68,14 @@ class boss_renataki : public CreatureScript
Ambushed = false;
}
+ void JustDied(Unit* /*killer*/)
+ {
+ _JustDied();
+ }
+
void EnterCombat(Unit* /*who*/)
{
+ _EnterCombat();
}
void UpdateAI(const uint32 diff)
diff --git a/src/server/scripts/EasternKingdoms/ZulGurub/boss_thekal.cpp b/src/server/scripts/EasternKingdoms/ZulGurub/boss_thekal.cpp
index d91b8f0b483..61eb695719a 100644
--- a/src/server/scripts/EasternKingdoms/ZulGurub/boss_thekal.cpp
+++ b/src/server/scripts/EasternKingdoms/ZulGurub/boss_thekal.cpp
@@ -27,28 +27,29 @@ EndScriptData */
#include "ScriptedCreature.h"
#include "zulgurub.h"
-enum Thekal
+enum Says
{
SAY_AGGRO = 0,
- SAY_DEATH = 1,
-
- SPELL_MORTALCLEAVE = 22859,
- SPELL_SILENCE = 22666,
- SPELL_FRENZY = 8269,
- SPELL_FORCEPUNCH = 24189,
- SPELL_CHARGE = 24193,
- SPELL_ENRAGE = 8269,
- SPELL_SUMMONTIGERS = 24183,
- SPELL_TIGER_FORM = 24169,
- SPELL_RESURRECT = 24173, //We will not use this spell.
-
-//Zealot Lor'Khan Spells
+ SAY_DEATH = 1
+};
+
+enum Spells
+{
+ SPELL_MORTALCLEAVE = 22859, // Phase 1
+ SPELL_SILENCE = 22666, // Phase 1
+ SPELL_TIGER_FORM = 24169, // Phase 1
+ SPELL_RESURRECT = 24173, // Phase 1 // Not used in script.
+ SPELL_FRENZY = 8269, // Phase 2
+ SPELL_FORCEPUNCH = 24189, // Phase 2
+ SPELL_CHARGE = 24193, // Phase 2
+ SPELL_ENRAGE = 8269, // Phase 2
+ SPELL_SUMMONTIGERS = 24183, // Phase 2
+ // Zealot Lor'Khan Spells
SPELL_SHIELD = 20545,
SPELL_BLOODLUST = 24185,
SPELL_GREATERHEAL = 24208,
SPELL_DISARM = 6713,
-
-//Zealot Zath Spells
+ // Zealot Zath Spells
SPELL_SWEEPINGSTRIKES = 18765,
SPELL_SINISTERSTRIKE = 15581,
SPELL_GOUGE = 12540,
@@ -56,128 +57,182 @@ enum Thekal
SPELL_BLIND = 21060
};
-class boss_thekal : public CreatureScript
+enum Events
{
- public:
+ EVENT_MORTALCLEAVE = 0, // Phase 1
+ EVENT_SILENCE = 1, // Phase 1
+ EVENT_CHECK_TIMER = 2, // Phase 1
+ EVENT_RESURRECT_TIMER = 3, // Phase 1
+ EVENT_FRENZY = 4, // Phase 2
+ EVENT_FORCEPUNCH = 5, // Phase 2
+ EVENT_SPELL_CHARGE = 6, // Phase 2
+ EVENT_ENRAGE = 7, // Phase 2
+ EVENT_SUMMONTIGERS = 8 // Phase 2
+};
- boss_thekal()
- : CreatureScript("boss_thekal")
- {
- }
+enum Phases
+{
+ PHASE_ONE = 1,
+ PHASE_TWO = 2
+};
- struct boss_thekalAI : public ScriptedAI
- {
- boss_thekalAI(Creature* creature) : ScriptedAI(creature)
- {
- instance = creature->GetInstanceScript();
- }
+class boss_thekal : public CreatureScript
+{
+ public: boss_thekal() : CreatureScript("boss_thekal") {}
- uint32 MortalCleave_Timer;
- uint32 Silence_Timer;
- uint32 Frenzy_Timer;
- uint32 ForcePunch_Timer;
- uint32 Charge_Timer;
- uint32 Enrage_Timer;
- uint32 SummonTigers_Timer;
- uint32 Check_Timer;
- uint32 Resurrect_Timer;
+ struct boss_thekalAI : public BossAI
+ {
+ boss_thekalAI(Creature* creature) : BossAI(creature, DATA_THEKAL) {}
- InstanceScript* instance;
bool Enraged;
- bool PhaseTwo;
bool WasDead;
void Reset()
{
- MortalCleave_Timer = 4000;
- Silence_Timer = 9000;
- Frenzy_Timer = 30000;
- ForcePunch_Timer = 4000;
- Charge_Timer = 12000;
- Enrage_Timer = 32000;
- SummonTigers_Timer = 25000;
- Check_Timer = 10000;
- Resurrect_Timer = 10000;
-
+ _Reset();
Enraged = false;
- PhaseTwo = false;
WasDead = false;
}
- void EnterCombat(Unit* /*who*/)
+ void JustDied(Unit* /*killer*/)
{
- Talk(SAY_AGGRO);
+ _JustDied();
+ Talk(SAY_DEATH);
}
- void JustDied(Unit* /*killer*/)
+ void EnterCombat(Unit* /*who*/)
{
- Talk(SAY_DEATH);
- if (instance)
- instance->SetData(DATA_THEKAL, DONE);
+ _EnterCombat();
+ events.ScheduleEvent(EVENT_MORTALCLEAVE, 4000, 0, PHASE_ONE); // Phase 1
+ events.ScheduleEvent(EVENT_SILENCE, 9000, 0, PHASE_ONE); // Phase 1
+ events.ScheduleEvent(EVENT_CHECK_TIMER, 10000, 0, PHASE_ONE); // Phase 1
+ events.ScheduleEvent(EVENT_RESURRECT_TIMER, 10000, 0, PHASE_ONE); // Phase 1
+ Talk(SAY_AGGRO);
}
void JustReachedHome()
{
if (instance)
- instance->SetData(DATA_THEKAL, NOT_STARTED);
+ instance->SetBossState(DATA_THEKAL, NOT_STARTED);
}
- void UpdateAI(const uint32 diff)
+ void UpdateAI(uint32 const diff)
{
if (!UpdateVictim())
return;
- //Check_Timer for the death of LorKhan and Zath.
- if (!WasDead && Check_Timer <= diff)
+ events.Update(diff);
+
+ if (me->HasUnitState(UNIT_STATE_CASTING))
+ return;
+
+ while (uint32 eventId = events.ExecuteEvent())
+ {
+ switch (eventId)
{
- if (instance)
- {
- if (instance->GetData(DATA_LORKHAN) == SPECIAL)
+ case EVENT_MORTALCLEAVE:
+ DoCastVictim(SPELL_MORTALCLEAVE, true);
+ events.ScheduleEvent(EVENT_MORTALCLEAVE, urand(15000, 20000), 0, PHASE_ONE);
+ break;
+ case EVENT_SILENCE:
+ DoCastVictim(SPELL_SILENCE, true);
+ events.ScheduleEvent(EVENT_SILENCE, urand(20000, 25000), 0, PHASE_ONE);
+ break;
+ case EVENT_RESURRECT_TIMER:
+ //Thekal will transform to Tiger if he died and was not resurrected after 10 seconds.
+ if (WasDead)
{
- //Resurrect LorKhan
- if (Unit* pLorKhan = Unit::GetUnit(*me, instance->GetData64(DATA_LORKHAN)))
- {
- pLorKhan->SetUInt32Value(UNIT_FIELD_BYTES_1, 0);
- pLorKhan->setFaction(14);
- pLorKhan->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
- pLorKhan->SetFullHealth();
-
- instance->SetData(DATA_LORKHAN, DONE);
- }
+ DoCast(me, SPELL_TIGER_FORM);
+ me->SetObjectScale(2.00f);
+ me->SetStandState(UNIT_STAND_STATE_STAND);
+ me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
+ me->SetFullHealth();
+ const CreatureTemplate* cinfo = me->GetCreatureTemplate();
+ me->SetBaseWeaponDamage(BASE_ATTACK, MINDAMAGE, (cinfo->mindmg +((cinfo->mindmg/100) * 40)));
+ me->SetBaseWeaponDamage(BASE_ATTACK, MAXDAMAGE, (cinfo->maxdmg +((cinfo->maxdmg/100) * 40)));
+ me->UpdateDamagePhysical(BASE_ATTACK);
+ DoResetThreat();
+ events.ScheduleEvent(EVENT_FRENZY, 30000, 0, PHASE_TWO); // Phase 2
+ events.ScheduleEvent(EVENT_FORCEPUNCH, 4000, 0, PHASE_TWO); // Phase 2
+ events.ScheduleEvent(EVENT_SPELL_CHARGE, 12000, 0, PHASE_TWO); // Phase 2
+ events.ScheduleEvent(EVENT_ENRAGE, 32000, 0, PHASE_TWO); // Phase 2
+ events.ScheduleEvent(EVENT_SUMMONTIGERS, 25000, 0, PHASE_TWO); // Phase 2
+ events.SetPhase(PHASE_TWO);
}
-
- if (instance->GetData(DATA_ZATH) == SPECIAL)
+ events.ScheduleEvent(EVENT_RESURRECT_TIMER, 10000, 0, PHASE_ONE);
+ break;
+ case EVENT_CHECK_TIMER:
+ //Check_Timer for the death of LorKhan and Zath.
+ if (!WasDead)
{
- //Resurrect Zath
- Unit* pZath = Unit::GetUnit(*me, instance->GetData64(DATA_ZATH));
- if (pZath)
+ if (instance)
{
- pZath->SetUInt32Value(UNIT_FIELD_BYTES_1, 0);
- pZath->setFaction(14);
- pZath->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
- pZath->SetFullHealth();
-
- instance->SetData(DATA_ZATH, DONE);
+ if (instance->GetBossState(DATA_LORKHAN) == SPECIAL)
+ {
+ //Resurrect LorKhan
+ if (Unit* pLorKhan = Unit::GetUnit(*me, instance->GetData64(DATA_LORKHAN)))
+ {
+ pLorKhan->SetUInt32Value(UNIT_FIELD_BYTES_1, 0);
+ pLorKhan->setFaction(14);
+ pLorKhan->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
+ pLorKhan->SetFullHealth();
+ instance->SetData(DATA_LORKHAN, DONE);
+ }
+ }
+
+ if (instance->GetBossState(DATA_ZATH) == SPECIAL)
+ {
+ //Resurrect Zath
+ if (Unit* pZath = Unit::GetUnit(*me, instance->GetData64(DATA_ZATH)))
+ {
+ pZath->SetUInt32Value(UNIT_FIELD_BYTES_1, 0);
+ pZath->setFaction(14);
+ pZath->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
+ pZath->SetFullHealth();
+ instance->SetBossState(DATA_ZATH, DONE);
+ }
+ }
}
}
- }
-
- Check_Timer = 5000;
- } else Check_Timer -= diff;
-
- if (!PhaseTwo && MortalCleave_Timer <= diff)
- {
- DoCast(me->getVictim(), SPELL_MORTALCLEAVE);
- MortalCleave_Timer = urand(15000, 20000);
- } else MortalCleave_Timer -= diff;
+ events.ScheduleEvent(EVENT_CHECK_TIMER, 5000, 0, PHASE_ONE);
+ break;
+ case EVENT_FRENZY:
+ DoCast(me, SPELL_FRENZY);
+ events.ScheduleEvent(EVENT_FRENZY, 30000, 0, PHASE_TWO);
+ break;
+ case EVENT_FORCEPUNCH:
+ DoCastVictim(SPELL_FORCEPUNCH, true);
+ events.ScheduleEvent(EVENT_FORCEPUNCH, urand(16000, 21000), 0, PHASE_TWO);
+ break;
+ case EVENT_CHARGE:
+ if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0))
+ {
+ DoCast(target, SPELL_CHARGE);
+ DoResetThreat();
+ AttackStart(target);
+ }
+ events.ScheduleEvent(EVENT_CHARGE, urand(15000, 22000), 0, PHASE_TWO);
+ break;
+ case EVENT_ENRAGE:
+ if (HealthBelowPct(11) && !Enraged)
+ {
+ DoCast(me, SPELL_ENRAGE);
+ Enraged = true;
+ }
+ events.ScheduleEvent(EVENT_ENRAGE, 30000);
+ break;
+ case EVENT_SUMMONTIGERS:
+ DoCastVictim(SPELL_SUMMONTIGERS, true);
+ events.ScheduleEvent(EVENT_SUMMONTIGERS, urand(10000, 14000), 0, PHASE_TWO);
+ break;
+ default:
+ break;
+ }
- if (!PhaseTwo && Silence_Timer <= diff)
- {
- DoCast(me->getVictim(), SPELL_SILENCE);
- Silence_Timer = urand(20000, 25000);
- } else Silence_Timer -= diff;
+ if (me->IsFullHealth() && WasDead)
+ WasDead = false;
- if (!PhaseTwo && !WasDead && !HealthAbovePct(5))
+ if ((events.GetPhaseMask() == PHASE_ONE) && !WasDead && !HealthAbovePct(5))
{
me->RemoveAurasByType(SPELL_AURA_PERIODIC_DAMAGE_PERCENT);
me->RemoveAurasByType(SPELL_AURA_PERIODIC_DAMAGE);
@@ -185,78 +240,12 @@ class boss_thekal : public CreatureScript
me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
me->SetStandState(UNIT_STAND_STATE_SLEEP);
me->AttackStop();
-
if (instance)
- instance->SetData(DATA_THEKAL, SPECIAL);
-
+ instance->SetBossState(DATA_THEKAL, SPECIAL);
WasDead=true;
}
-
- //Thekal will transform to Tiger if he died and was not resurrected after 10 seconds.
- if (!PhaseTwo && WasDead)
- {
- if (Resurrect_Timer <= diff)
- {
- DoCast(me, SPELL_TIGER_FORM);
- me->SetObjectScale(2.00f);
- me->SetStandState(UNIT_STAND_STATE_STAND);
- me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
- me->SetFullHealth();
- const CreatureTemplate* cinfo = me->GetCreatureTemplate();
- me->SetBaseWeaponDamage(BASE_ATTACK, MINDAMAGE, (cinfo->mindmg +((cinfo->mindmg/100) * 40)));
- me->SetBaseWeaponDamage(BASE_ATTACK, MAXDAMAGE, (cinfo->maxdmg +((cinfo->maxdmg/100) * 40)));
- me->UpdateDamagePhysical(BASE_ATTACK);
- DoResetThreat();
- PhaseTwo = true;
- } else Resurrect_Timer -= diff;
- }
-
- if (me->IsFullHealth() && WasDead)
- {
- WasDead = false;
- }
-
- if (PhaseTwo)
- {
- if (Charge_Timer <= diff)
- {
- if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0))
- {
- DoCast(target, SPELL_CHARGE);
- DoResetThreat();
- AttackStart(target);
- }
-
- Charge_Timer = urand(15000, 22000);
- } else Charge_Timer -= diff;
-
- if (Frenzy_Timer <= diff)
- {
- DoCast(me, SPELL_FRENZY);
- Frenzy_Timer = 30000;
- } else Frenzy_Timer -= diff;
-
- if (ForcePunch_Timer <= diff)
- {
- DoCast(me->getVictim(), SPELL_SILENCE);
- ForcePunch_Timer = urand(16000, 21000);
- } else ForcePunch_Timer -= diff;
-
- if (SummonTigers_Timer <= diff)
- {
- DoCast(me->getVictim(), SPELL_SUMMONTIGERS);
- SummonTigers_Timer = urand(10000, 14000);
- } else SummonTigers_Timer -= diff;
-
- if (HealthBelowPct(11) && !Enraged)
- {
- DoCast(me, SPELL_ENRAGE);
- Enraged = true;
- }
- }
-
- DoMeleeAttackIfReady();
-
+ }
+ DoMeleeAttackIfReady();
}
};
@@ -269,12 +258,7 @@ class boss_thekal : public CreatureScript
//Zealot Lor'Khan
class mob_zealot_lorkhan : public CreatureScript
{
- public:
-
- mob_zealot_lorkhan()
- : CreatureScript("mob_zealot_lorkhan")
- {
- }
+ public: mob_zealot_lorkhan() : CreatureScript("mob_zealot_lorkhan") {}
struct mob_zealot_lorkhanAI : public ScriptedAI
{
@@ -304,7 +288,7 @@ class mob_zealot_lorkhan : public CreatureScript
FakeDeath = false;
if (instance)
- instance->SetData(DATA_LORKHAN, NOT_STARTED);
+ instance->SetBossState(DATA_LORKHAN, NOT_STARTED);
me->SetUInt32Value(UNIT_FIELD_BYTES_1, 0);
me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
@@ -372,7 +356,7 @@ class mob_zealot_lorkhan : public CreatureScript
{
if (instance)
{
- if (instance->GetData(DATA_THEKAL) == SPECIAL)
+ if (instance->GetBossState(DATA_THEKAL) == SPECIAL)
{
//Resurrect Thekal
if (Unit* pThekal = Unit::GetUnit(*me, instance->GetData64(DATA_THEKAL)))
@@ -384,7 +368,7 @@ class mob_zealot_lorkhan : public CreatureScript
}
}
- if (instance->GetData(DATA_ZATH) == SPECIAL)
+ if (instance->GetBossState(DATA_ZATH) == SPECIAL)
{
//Resurrect Zath
if (Unit* pZath = Unit::GetUnit(*me, instance->GetData64(DATA_ZATH)))
@@ -411,7 +395,7 @@ class mob_zealot_lorkhan : public CreatureScript
me->AttackStop();
if (instance)
- instance->SetData(DATA_LORKHAN, SPECIAL);
+ instance->SetBossState(DATA_LORKHAN, SPECIAL);
FakeDeath = true;
}
@@ -466,7 +450,7 @@ class mob_zealot_zath : public CreatureScript
FakeDeath = false;
if (instance)
- instance->SetData(DATA_ZATH, NOT_STARTED);
+ instance->SetBossState(DATA_ZATH, NOT_STARTED);
me->SetStandState(UNIT_STAND_STATE_STAND);
me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
@@ -525,7 +509,7 @@ class mob_zealot_zath : public CreatureScript
{
if (instance)
{
- if (instance->GetData(DATA_LORKHAN) == SPECIAL)
+ if (instance->GetBossState(DATA_LORKHAN) == SPECIAL)
{
//Resurrect LorKhan
if (Unit* pLorKhan = Unit::GetUnit(*me, instance->GetData64(DATA_LORKHAN)))
@@ -537,7 +521,7 @@ class mob_zealot_zath : public CreatureScript
}
}
- if (instance->GetData(DATA_THEKAL) == SPECIAL)
+ if (instance->GetBossState(DATA_THEKAL) == SPECIAL)
{
//Resurrect Thekal
if (Unit* pThekal = Unit::GetUnit(*me, instance->GetData64(DATA_THEKAL)))
@@ -564,7 +548,7 @@ class mob_zealot_zath : public CreatureScript
me->AttackStop();
if (instance)
- instance->SetData(DATA_ZATH, SPECIAL);
+ instance->SetBossState(DATA_ZATH, SPECIAL);
FakeDeath = true;
}
@@ -585,4 +569,3 @@ void AddSC_boss_thekal()
new mob_zealot_lorkhan();
new mob_zealot_zath();
}
-
diff --git a/src/server/scripts/EasternKingdoms/ZulGurub/boss_venoxis.cpp b/src/server/scripts/EasternKingdoms/ZulGurub/boss_venoxis.cpp
index ff63e4a8adb..11a6e710c3c 100644
--- a/src/server/scripts/EasternKingdoms/ZulGurub/boss_venoxis.cpp
+++ b/src/server/scripts/EasternKingdoms/ZulGurub/boss_venoxis.cpp
@@ -27,10 +27,10 @@
* - Fix timers (research some more)
*/
-enum Texts
+enum Says
{
SAY_VENOXIS_TRANSFORM = 1, // Let the coils of hate unfurl!
- SAY_VENOXIS_DEATH = 2, // Ssserenity.. at lassst!
+ SAY_VENOXIS_DEATH = 2 // Ssserenity.. at lassst!
};
enum Spells
@@ -42,7 +42,6 @@ enum Spells
SPELL_HOLY_NOVA = 23858,
SPELL_HOLY_FIRE = 23860,
SPELL_HOLY_WRATH = 23979,
-
// snake form
SPELL_POISON_CLOUD = 23861,
SPELL_VENOM_SPIT = 23862,
@@ -50,15 +49,9 @@ enum Spells
SPELL_PARASITIC_SERPENT = 23865,
SPELL_SUMMON_PARASITIC_SERPENT = 23866,
SPELL_PARASITIC_SERPENT_TRIGGER = 23867,
-
// used when swapping event-stages
SPELL_VENOXIS_TRANSFORM = 23849, // 50% health - shapechange to cobra
- SPELL_FRENZY = 8269, // 20% health - frenzy
-};
-
-enum NPCs
-{
- NPC_PARASITIC_SERPENT = 14884,
+ SPELL_FRENZY = 8269 // 20% health - frenzy
};
enum Events
@@ -70,10 +63,8 @@ enum Events
EVENT_HOLY_NOVA = 4,
EVENT_HOLY_FIRE = 5,
EVENT_HOLY_WRATH = 6,
-
// phase-changing
EVENT_TRANSFORM = 7,
-
// snake form events
EVENT_POISON_CLOUD = 8,
EVENT_VENOM_SPIT = 9,
@@ -84,49 +75,48 @@ enum Events
enum Phases
{
PHASE_ONE = 1, // troll form
- PHASE_TWO = 2, // snake form
+ PHASE_TWO = 2 // snake form
+};
+
+enum NPCs
+{
+ NPC_PARASITIC_SERPENT = 14884
};
class boss_venoxis : public CreatureScript
{
- public:
- boss_venoxis() : CreatureScript("boss_venoxis") {}
+ public: boss_venoxis() : CreatureScript("boss_venoxis") {}
struct boss_venoxisAI : public BossAI
{
- boss_venoxisAI(Creature* creature) : BossAI(creature, DATA_VENOXIS)
- {
- }
+ boss_venoxisAI(Creature* creature) : BossAI(creature, DATA_VENOXIS) {}
void Reset()
{
- events.Reset();
- summons.DespawnAll();
-
- // make sure this boss is properly reset
- instance->SetBossState(DATA_VENOXIS, NOT_STARTED);
-
+ _Reset();
// remove all spells and auras from previous attempts
me->RemoveAllAuras();
me->SetReactState(REACT_PASSIVE);
-
// set some internally used variables to their defaults
_inMeleeRange = 0;
_transformed = false;
_frenzied = false;
-
events.SetPhase(PHASE_ONE);
}
+ void JustDied(Unit* /*killer*/)
+ {
+ _JustDied();
+ Talk(SAY_VENOXIS_DEATH);
+ me->RemoveAllAuras();
+ }
+
void EnterCombat(Unit* /*who*/)
{
+ _EnterCombat();
me->SetReactState(REACT_AGGRESSIVE);
-
- instance->SetBossState(DATA_VENOXIS, IN_PROGRESS);
-
// Always running events
events.ScheduleEvent(EVENT_THRASH, 5000);
-
// Phase one events (regular form)
events.ScheduleEvent(EVENT_HOLY_NOVA, 5000, 0, PHASE_ONE);
events.ScheduleEvent(EVENT_DISPEL_MAGIC, 35000, 0, PHASE_ONE);
@@ -157,14 +147,6 @@ class boss_venoxis : public CreatureScript
}
}
- void JustDied(Unit* /*killer*/)
- {
- Talk(SAY_VENOXIS_DEATH);
- // venoxis is dead, mark him as such for the instance
- instance->SetBossState(DATA_VENOXIS, DONE);
- me->RemoveAllAuras();
- }
-
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
diff --git a/src/server/scripts/EasternKingdoms/ZulGurub/boss_wushoolay.cpp b/src/server/scripts/EasternKingdoms/ZulGurub/boss_wushoolay.cpp
index 810ee5e900e..d1e7edf0218 100644
--- a/src/server/scripts/EasternKingdoms/ZulGurub/boss_wushoolay.cpp
+++ b/src/server/scripts/EasternKingdoms/ZulGurub/boss_wushoolay.cpp
@@ -33,53 +33,63 @@ enum Spells
SPELL_LIGHTNINGWAVE = 24819
};
-class boss_wushoolay : public CreatureScript
+enum Events
{
- public:
+ EVENT_LIGHTNINGCLOUD = 0,
+ EVENT_LIGHTNINGWAVE = 1
+};
- boss_wushoolay()
- : CreatureScript("boss_wushoolay")
- {
- }
+class boss_wushoolay : public CreatureScript
+{
+ public: boss_wushoolay() : CreatureScript("boss_wushoolay") {}
- struct boss_wushoolayAI : public ScriptedAI
+ struct boss_wushoolayAI : public BossAI
{
- boss_wushoolayAI(Creature* creature) : ScriptedAI(creature) {}
-
- uint32 LightningCloud_Timer;
- uint32 LightningWave_Timer;
+ boss_wushoolayAI(Creature* creature) : BossAI(creature, DATA_EDGE_OF_MADNESS) {}
void Reset()
{
- LightningCloud_Timer = urand(5000, 10000);
- LightningWave_Timer = urand(8000, 16000);
+ _Reset();
+ }
+
+ void JustDied(Unit* /*killer*/)
+ {
+ _JustDied();
}
void EnterCombat(Unit* /*who*/)
{
+ _EnterCombat();
+ events.ScheduleEvent(EVENT_LIGHTNINGCLOUD, urand(5000, 10000));
+ events.ScheduleEvent(EVENT_LIGHTNINGWAVE, urand(8000, 16000));
}
- void UpdateAI(const uint32 diff)
+ void UpdateAI(uint32 const diff)
{
if (!UpdateVictim())
return;
- //LightningCloud_Timer
- if (LightningCloud_Timer <= diff)
- {
- DoCast(me->getVictim(), SPELL_LIGHTNINGCLOUD);
- LightningCloud_Timer = urand(15000, 20000);
- } else LightningCloud_Timer -= diff;
+ events.Update(diff);
- //LightningWave_Timer
- if (LightningWave_Timer <= diff)
- {
- Unit* target = NULL;
- target = SelectTarget(SELECT_TARGET_RANDOM, 0);
- if (target) DoCast(target, SPELL_LIGHTNINGWAVE);
+ if (me->HasUnitState(UNIT_STATE_CASTING))
+ return;
- LightningWave_Timer = urand(12000, 16000);
- } else LightningWave_Timer -= diff;
+ while (uint32 eventId = events.ExecuteEvent())
+ {
+ switch (eventId)
+ {
+ case EVENT_LIGHTNINGCLOUD:
+ DoCastVictim(SPELL_LIGHTNINGCLOUD, true);
+ events.ScheduleEvent(EVENT_LIGHTNINGCLOUD, urand(15000, 20000));
+ break;
+ case EVENT_LIGHTNINGWAVE:
+ DoCast(SelectTarget(SELECT_TARGET_RANDOM, 0, 100, true), SPELL_LIGHTNINGWAVE);
+ events.ScheduleEvent(EVENT_LIGHTNINGWAVE, urand(12000, 16000));
+ break;
+ default:
+ break;
+ }
+ }
DoMeleeAttackIfReady();
}
diff --git a/src/server/scripts/EasternKingdoms/ZulGurub/instance_zulgurub.cpp b/src/server/scripts/EasternKingdoms/ZulGurub/instance_zulgurub.cpp
index cfaf19642f2..01c5ef998f5 100644
--- a/src/server/scripts/EasternKingdoms/ZulGurub/instance_zulgurub.cpp
+++ b/src/server/scripts/EasternKingdoms/ZulGurub/instance_zulgurub.cpp
@@ -29,38 +29,30 @@ EndScriptData */
class instance_zulgurub : public InstanceMapScript
{
- public:
- instance_zulgurub()
- : InstanceMapScript("instance_zulgurub", 309)
- {
- }
+ public: instance_zulgurub(): InstanceMapScript(ZGScriptName, 309) {}
struct instance_zulgurub_InstanceMapScript : public InstanceScript
{
- instance_zulgurub_InstanceMapScript(Map* map) : InstanceScript(map) {}
-
- //If all High Priest bosses were killed. Lorkhan, Zath and Ohgan are added too.
- uint32 m_auiEncounter[MAX_ENCOUNTERS];
-
- //Storing Lorkhan, Zath and Thekal because we need to cast on them later. Jindo is needed for healfunction too.
- uint64 m_uiLorKhanGUID;
- uint64 m_uiZathGUID;
- uint64 m_uiThekalGUID;
- uint64 m_uiJindoGUID;
+ instance_zulgurub_InstanceMapScript(Map* map) : InstanceScript(map)
+ {
+ SetBossNumber(EncounterCount);
+ }
void Initialize()
{
- memset(&m_auiEncounter, 0, sizeof(m_auiEncounter));
-
- m_uiLorKhanGUID = 0;
- m_uiZathGUID = 0;
- m_uiThekalGUID = 0;
- m_uiJindoGUID = 0;
+ _zealotLorkhanGUID = 0;
+ _zealotZathGUID = 0;
+ _highPriestTekalGUID = 0;
+ _jindoTheHexxerGUID = 0;
+ _vilebranchSpeakerGUID = 0;
+ _arlokkGUID = 0;
+ _goForcefieldGUID = 0;
+ _goGongOfBethekkGUID = 0;
}
bool IsEncounterInProgress() const
{
- //not active in Zul'Gurub
+ // not active in Zul'Gurub
return false;
}
@@ -68,73 +60,44 @@ class instance_zulgurub : public InstanceMapScript
{
switch (creature->GetEntry())
{
- case 11347: m_uiLorKhanGUID = creature->GetGUID(); break;
- case 11348: m_uiZathGUID = creature->GetGUID(); break;
- case 14509: m_uiThekalGUID = creature->GetGUID(); break;
- case 11380: m_uiJindoGUID = creature->GetGUID(); break;
- }
- }
-
- void SetData(uint32 uiType, uint32 uiData)
- {
- switch (uiType)
- {
- case DATA_ARLOKK:
- m_auiEncounter[0] = uiData;
- break;
-
- case DATA_JEKLIK:
- m_auiEncounter[1] = uiData;
+ case NPC_ZEALOT_LORKHAN:
+ _zealotLorkhanGUID = creature->GetGUID();
break;
-
- case DATA_VENOXIS:
- m_auiEncounter[2] = uiData;
+ case NPC_ZEALOT_ZATH:
+ _zealotZathGUID = creature->GetGUID();
break;
-
- case DATA_MARLI:
- m_auiEncounter[3] = uiData;
+ case NPC_HIGH_PRIEST_THEKAL:
+ _highPriestTekalGUID = creature->GetGUID();
break;
-
- case DATA_THEKAL:
- m_auiEncounter[4] = uiData;
+ case NPC_JINDO_THE_HEXXER:
+ _jindoTheHexxerGUID = creature->GetGUID();
break;
-
- case DATA_LORKHAN:
- m_auiEncounter[5] = uiData;
+ case NPC_VILEBRANCH_SPEAKER:
+ _vilebranchSpeakerGUID = creature->GetGUID();
break;
-
- case DATA_ZATH:
- m_auiEncounter[6] = uiData;
- break;
-
- case DATA_OHGAN:
- m_auiEncounter[7] = uiData;
+ case NPC_ARLOKK:
+ _arlokkGUID = creature->GetGUID();
break;
}
}
- uint32 GetData(uint32 uiType) const
+ void OnGameObjectCreate(GameObject* go)
{
- switch (uiType)
+ switch (go->GetEntry())
{
- case DATA_ARLOKK:
- return m_auiEncounter[0];
- case DATA_JEKLIK:
- return m_auiEncounter[1];
- case DATA_VENOXIS:
- return m_auiEncounter[2];
- case DATA_MARLI:
- return m_auiEncounter[3];
- case DATA_THEKAL:
- return m_auiEncounter[4];
- case DATA_LORKHAN:
- return m_auiEncounter[5];
- case DATA_ZATH:
- return m_auiEncounter[6];
- case DATA_OHGAN:
- return m_auiEncounter[7];
+ case GO_FORCEFIELD:
+ _goForcefieldGUID = go->GetGUID();
+ break;
+ case GO_GONG_OF_BETHEKK:
+ _goGongOfBethekkGUID = go->GetGUID();
+ if (GetBossState(DATA_ARLOKK) == DONE)
+ go->SetFlag(GAMEOBJECT_FLAGS, GO_FLAG_NOT_SELECTABLE);
+ else
+ go->RemoveFlag(GAMEOBJECT_FLAGS, GO_FLAG_NOT_SELECTABLE);
+ break;
+ default:
+ break;
}
- return 0;
}
uint64 GetData64(uint32 uiData) const
@@ -142,16 +105,84 @@ class instance_zulgurub : public InstanceMapScript
switch (uiData)
{
case DATA_LORKHAN:
- return m_uiLorKhanGUID;
+ return _zealotLorkhanGUID;
+ break;
case DATA_ZATH:
- return m_uiZathGUID;
+ return _zealotZathGUID;
+ break;
case DATA_THEKAL:
- return m_uiThekalGUID;
+ return _highPriestTekalGUID;
+ break;
case DATA_JINDO:
- return m_uiJindoGUID;
+ return _jindoTheHexxerGUID;
+ break;
+ case NPC_ARLOKK:
+ return _arlokkGUID;
+ break;
+ case GO_FORCEFIELD:
+ return _goForcefieldGUID;
+ break;
+ case GO_GONG_OF_BETHEKK:
+ return _goGongOfBethekkGUID;
+ break;
}
return 0;
}
+
+ std::string GetSaveData()
+ {
+ OUT_SAVE_INST_DATA;
+
+ std::ostringstream saveStream;
+ saveStream << "Z G " << GetBossSaveData();
+
+ OUT_SAVE_INST_DATA_COMPLETE;
+ return saveStream.str();
+ }
+
+ void Load(const char* str)
+ {
+ 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 == 'Z' && dataHead2 == 'G')
+ {
+ for (uint32 i = 0; i < EncounterCount; ++i)
+ {
+ uint32 tmpState;
+ loadStream >> tmpState;
+ if (tmpState == IN_PROGRESS || tmpState > SPECIAL)
+ tmpState = NOT_STARTED;
+ SetBossState(i, EncounterState(tmpState));
+ }
+ }
+ else
+ OUT_LOAD_INST_DATA_FAIL;
+
+ OUT_LOAD_INST_DATA_COMPLETE;
+ }
+ private:
+ //If all High Priest bosses were killed. Lorkhan, Zath and Ohgan are added too.
+ //Storing Lorkhan, Zath and Thekal because we need to cast on them later. Jindo is needed for healfunction too.
+
+ uint64 _zealotLorkhanGUID;
+ uint64 _zealotZathGUID;
+ uint64 _highPriestTekalGUID;
+ uint64 _jindoTheHexxerGUID;
+ uint64 _vilebranchSpeakerGUID;
+ uint64 _arlokkGUID;
+ uint64 _goForcefieldGUID;
+ uint64 _goGongOfBethekkGUID;
};
InstanceScript* GetInstanceScript(InstanceMap* map) const
diff --git a/src/server/scripts/EasternKingdoms/ZulGurub/zulgurub.h b/src/server/scripts/EasternKingdoms/ZulGurub/zulgurub.h
index 22637315066..77767153a96 100644
--- a/src/server/scripts/EasternKingdoms/ZulGurub/zulgurub.h
+++ b/src/server/scripts/EasternKingdoms/ZulGurub/zulgurub.h
@@ -1,6 +1,5 @@
/*
* Copyright (C) 2008-2013 TrinityCore <http://www.trinitycore.org/>
- * Copyright (C) 2006-2009 ScriptDev2 <https://scriptdev2.svn.sourceforge.net/>
*
* 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
@@ -19,20 +18,61 @@
#ifndef DEF_ZULGURUB_H
#define DEF_ZULGURUB_H
+uint32 const EncounterCount = 13;
+
+#define ZGScriptName "instance_zulgurub"
+
enum DataTypes
{
- MAX_ENCOUNTERS = 8,
-
- DATA_ARLOKK = 1,
- DATA_JEKLIK = 2,
- DATA_VENOXIS = 3,
- DATA_MARLI = 4,
- DATA_OHGAN = 5,
- DATA_THEKAL = 6,
- DATA_ZATH = 7,
- DATA_LORKHAN = 8,
- DATA_JINDO = 10,
+ DATA_JEKLIK = 0, // Main boss
+ DATA_VENOXIS = 1, // Main boss
+ DATA_MARLI = 2, // Main boss
+ DATA_ARLOKK = 3, // Main boss
+ DATA_THEKAL = 4, // Main boss
+ DATA_HAKKAR = 5, // End boss
+ DATA_MANDOKIR = 6, // Optional boss
+ DATA_JINDO = 7, // Optional boss
+ DATA_GAHZRANKA = 8, // Optional boss
+ DATA_EDGE_OF_MADNESS = 9, // Optional Event Edge of Madness - one of: Gri'lek, Renataki, Hazza'rah, or Wushoolay
+ DATA_LORKHAN = 10, // Zealot Lor'Khan add to High priest Thekal!
+ DATA_ZATH = 11, // Zealot Zath add to High priest Thekal!
+ DATA_OHGAN = 12, // Bloodlord Mandokir's raptor mount
+ TYPE_EDGE_OF_MADNESS = 13 // Boss storage
};
-#endif
+enum CreatureIds
+{
+ NPC_ARLOKK = 14515, // Arlokk Event
+ NPC_PANTHER_TRIGGER = 15091, // Arlokk Event
+ NPC_ZULIAN_PROWLER = 15101, // Arlokk Event
+ NPC_ZEALOT_LORKHAN = 11347,
+ NPC_ZEALOT_ZATH = 11348,
+ NPC_HIGH_PRIEST_THEKAL = 14509,
+ NPC_JINDO_THE_HEXXER = 11380,
+ NPC_NIGHTMARE_ILLUSION = 15163,
+ NPC_SHADE_OF_JINDO = 14986,
+ NPC_SACRIFICED_TROLL = 14826,
+ NPC_MANDOKIR = 11382, // Mandokir Event
+ NPC_OHGAN = 14988, // Mandokir Event
+ NPC_VILEBRANCH_SPEAKER = 11391, // Mandokir Event
+ NPC_CHAINED_SPIRT = 15117 // Mandokir Event
+
+};
+enum GameobjectIds
+{
+ GO_FORCEFIELD = 180497, // Arlokk Event
+ GO_GONG_OF_BETHEKK = 180526 // Arlokk Event
+};
+
+template<class AI>
+CreatureAI* GetZulGurubAI(Creature* creature)
+{
+ if (InstanceMap* instance = creature->GetMap()->ToInstanceMap())
+ if (instance->GetInstanceScript())
+ if (instance->GetScriptId() == sObjectMgr->GetScriptId(ZGScriptName))
+ return new AI(creature);
+ return NULL;
+}
+
+#endif
diff --git a/src/server/scripts/EasternKingdoms/boss_kruul.cpp b/src/server/scripts/EasternKingdoms/boss_kruul.cpp
index 226ff794a9f..158861e90c7 100644
--- a/src/server/scripts/EasternKingdoms/boss_kruul.cpp
+++ b/src/server/scripts/EasternKingdoms/boss_kruul.cpp
@@ -26,13 +26,16 @@ EndScriptData */
#include "ScriptMgr.h"
#include "ScriptedCreature.h"
-#define SPELL_SHADOWVOLLEY 21341
-#define SPELL_CLEAVE 20677
-#define SPELL_THUNDERCLAP 23931
-#define SPELL_TWISTEDREFLECTION 21063
-#define SPELL_VOIDBOLT 21066
-#define SPELL_RAGE 21340
-#define SPELL_CAPTURESOUL 21054
+enum Spells
+{
+ SPELL_SHADOWVOLLEY = 21341,
+ SPELL_CLEAVE = 20677,
+ SPELL_THUNDERCLAP = 23931,
+ SPELL_TWISTEDREFLECTION = 21063,
+ SPELL_VOIDBOLT = 21066,
+ SPELL_RAGE = 21340,
+ SPELL_CAPTURESOUL = 21054
+};
class boss_kruul : public CreatureScript
{
diff --git a/src/server/scripts/Events/childrens_week.cpp b/src/server/scripts/Events/childrens_week.cpp
index 26f83922f87..9d35b510f6e 100644
--- a/src/server/scripts/Events/childrens_week.cpp
+++ b/src/server/scripts/Events/childrens_week.cpp
@@ -189,10 +189,10 @@ class npc_winterfin_playmate : public CreatureScript
return;
}
- switch(phase)
+ switch (phase)
{
case 1:
- orphan->GetMotionMaster()->MovePoint(0, me->GetPositionX() + cos(me->GetOrientation()) * 5,me->GetPositionY() + sin(me->GetOrientation()) * 5, me->GetPositionZ());
+ orphan->GetMotionMaster()->MovePoint(0, me->GetPositionX() + cos(me->GetOrientation()) * 5, me->GetPositionY() + sin(me->GetOrientation()) * 5, me->GetPositionZ());
orphan->AI()->Talk(TEXT_ORACLE_ORPHAN_1);
timer = 3000;
break;
@@ -290,7 +290,7 @@ class npc_snowfall_glade_playmate : public CreatureScript
switch (phase)
{
case 1:
- orphan->GetMotionMaster()->MovePoint(0, me->GetPositionX() + cos(me->GetOrientation()) * 5,me->GetPositionY() + sin(me->GetOrientation()) * 5, me->GetPositionZ());
+ orphan->GetMotionMaster()->MovePoint(0, me->GetPositionX() + cos(me->GetOrientation()) * 5, me->GetPositionY() + sin(me->GetOrientation()) * 5, me->GetPositionZ());
orphan->AI()->Talk(TEXT_WOLVAR_ORPHAN_1);
timer = 5000;
break;
@@ -810,7 +810,7 @@ class npc_alexstraza_the_lifebinder : public CreatureScript
timer = 5000;
break;
case 8:
- if(Creature* krasus = me->FindNearestCreature(NPC_KRASUS, 10.0f))
+ if (Creature* krasus = me->FindNearestCreature(NPC_KRASUS, 10.0f))
{
orphan->SetFacingToObject(krasus);
krasus->AI()->Talk(TEXT_KRASUS_8);
@@ -974,7 +974,7 @@ class npc_cw_area_trigger : public CreatureScript
if (Creature* samuro = me->FindNearestCreature(25151, 20.0f))
{
uint32 emote = 0;
- switch(urand(1,5))
+ switch (urand(1, 5))
{
case 1:
emote = EMOTE_ONESHOT_WAVE;
diff --git a/src/server/scripts/Kalimdor/zone_feralas.cpp b/src/server/scripts/Kalimdor/zone_feralas.cpp
index b2326de86ab..2d5b383cefc 100644
--- a/src/server/scripts/Kalimdor/zone_feralas.cpp
+++ b/src/server/scripts/Kalimdor/zone_feralas.cpp
@@ -221,7 +221,10 @@ class spell_gordunni_trap : public SpellScriptLoader
{
if (Unit* caster = GetCaster())
if (GameObject* chest = caster->SummonGameObject(GO_GORDUNNI_DIRT_MOUND, caster->GetPositionX(), caster->GetPositionY(), caster->GetPositionZ(), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0))
+ {
chest->SetSpellId(GetSpellInfo()->Id);
+ caster->RemoveGameObject(chest, false);
+ }
}
void Register()
diff --git a/src/server/scripts/Kalimdor/zone_silithus.cpp b/src/server/scripts/Kalimdor/zone_silithus.cpp
index 58665224bdd..a612269eff7 100644
--- a/src/server/scripts/Kalimdor/zone_silithus.cpp
+++ b/src/server/scripts/Kalimdor/zone_silithus.cpp
@@ -544,7 +544,7 @@ public:
switch (AnimationCount)
{
case 0:
- Talk(ANACHRONOS_SAY_1,Fandral->GetGUID());
+ Talk(ANACHRONOS_SAY_1, Fandral->GetGUID());
break;
case 1:
Fandral->SetTarget(me->GetGUID());
diff --git a/src/server/scripts/Kalimdor/zone_the_barrens.cpp b/src/server/scripts/Kalimdor/zone_the_barrens.cpp
index 8f7ab09260b..b349de8f083 100644
--- a/src/server/scripts/Kalimdor/zone_the_barrens.cpp
+++ b/src/server/scripts/Kalimdor/zone_the_barrens.cpp
@@ -146,7 +146,7 @@ public:
Talk(SAY_GIL_FREEBOOTERS, player->GetGUID());
break;
case 37:
- Talk(SAY_GIL_ALMOST,player->GetGUID());
+ Talk(SAY_GIL_ALMOST, player->GetGUID());
break;
case 47:
Talk(SAY_GIL_SWEET, player->GetGUID());
diff --git a/src/server/scripts/Northrend/AzjolNerub/Ahnkahet/boss_amanitar.cpp b/src/server/scripts/Northrend/AzjolNerub/Ahnkahet/boss_amanitar.cpp
index 02245c92efd..d484e2a4279 100644
--- a/src/server/scripts/Northrend/AzjolNerub/Ahnkahet/boss_amanitar.cpp
+++ b/src/server/scripts/Northrend/AzjolNerub/Ahnkahet/boss_amanitar.cpp
@@ -89,11 +89,11 @@ public:
{
_EnterCombat();
- events.ScheduleEvent(EVENT_ROOT, urand(5,9)*IN_MILLISECONDS);
- events.ScheduleEvent(EVENT_BASH, urand(10,14)*IN_MILLISECONDS);
- events.ScheduleEvent(EVENT_BOLT, urand(15,20)*IN_MILLISECONDS);
- events.ScheduleEvent(EVENT_MINI, urand(12,18)*IN_MILLISECONDS);
- events.ScheduleEvent(EVENT_SPAWN, 5 *IN_MILLISECONDS);
+ events.ScheduleEvent(EVENT_ROOT, urand(5, 9) * IN_MILLISECONDS);
+ events.ScheduleEvent(EVENT_BASH, urand(10, 14) * IN_MILLISECONDS);
+ events.ScheduleEvent(EVENT_BOLT, urand(15, 20) * IN_MILLISECONDS);
+ events.ScheduleEvent(EVENT_MINI, urand(12, 18) * IN_MILLISECONDS);
+ events.ScheduleEvent(EVENT_SPAWN, 5 * IN_MILLISECONDS);
me->SetInCombatWithZone();
if (instance)
@@ -123,7 +123,7 @@ public:
{
u = 1 - u;
trigger->DisappearAndDie();
- me->SummonCreature(u > 0 ? NPC_POISONOUS_MUSHROOM : NPC_HEALTHY_MUSHROOM, pos, TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN, 60*IN_MILLISECONDS);
+ me->SummonCreature(u > 0 ? NPC_POISONOUS_MUSHROOM : NPC_HEALTHY_MUSHROOM, pos, TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN, 60 * IN_MILLISECONDS);
}
}
}
@@ -145,23 +145,23 @@ public:
{
case EVENT_SPAWN:
SpawnAdds();
- events.ScheduleEvent(EVENT_SPAWN, 20*IN_MILLISECONDS);
+ events.ScheduleEvent(EVENT_SPAWN, 20 * IN_MILLISECONDS);
break;
case EVENT_MINI:
DoCast(SPELL_MINI);
- events.ScheduleEvent(EVENT_MINI, urand(25,30)*IN_MILLISECONDS);
+ events.ScheduleEvent(EVENT_MINI, urand(25, 30) * IN_MILLISECONDS);
break;
case EVENT_ROOT:
- DoCast(SelectTarget(SELECT_TARGET_RANDOM,0, 100, true),SPELL_ENTANGLING_ROOTS,true);
- events.ScheduleEvent(EVENT_ROOT, urand(10,15)*IN_MILLISECONDS);
+ DoCast(SelectTarget(SELECT_TARGET_RANDOM, 0, 100, true), SPELL_ENTANGLING_ROOTS, true);
+ events.ScheduleEvent(EVENT_ROOT, urand(10, 15) * IN_MILLISECONDS);
break;
case EVENT_BASH:
DoCastVictim(SPELL_BASH);
- events.ScheduleEvent(EVENT_BASH, urand(7,12)*IN_MILLISECONDS);
+ events.ScheduleEvent(EVENT_BASH, urand(7, 12) * IN_MILLISECONDS);
break;
case EVENT_BOLT:
- DoCast(SelectTarget(SELECT_TARGET_RANDOM,0, 100, true),SPELL_VENOM_BOLT_VOLLEY,true);
- events.ScheduleEvent(EVENT_BOLT, urand(18,22)*IN_MILLISECONDS);
+ DoCast(SelectTarget(SELECT_TARGET_RANDOM, 0, 100, true), SPELL_VENOM_BOLT_VOLLEY, true);
+ events.ScheduleEvent(EVENT_BOLT, urand(18, 22) * IN_MILLISECONDS);
break;
default:
break;
@@ -191,7 +191,7 @@ public:
void Reset()
{
events.Reset();
- events.ScheduleEvent(EVENT_AURA, 1*IN_MILLISECONDS);
+ events.ScheduleEvent(EVENT_AURA, 1 * IN_MILLISECONDS);
me->SetDisplayId(me->GetCreatureTemplate()->Modelid2);
DoCast(SPELL_PUTRID_MUSHROOM);
@@ -231,7 +231,7 @@ public:
DoCast(me, SPELL_POISONOUS_MUSHROOM_VISUAL_AREA, true);
DoCast(me, SPELL_POISONOUS_MUSHROOM_POISON_CLOUD);
}
- events.ScheduleEvent(EVENT_AURA, 7*IN_MILLISECONDS);
+ events.ScheduleEvent(EVENT_AURA, 7 * IN_MILLISECONDS);
break;
default:
break;
@@ -250,4 +250,4 @@ void AddSC_boss_amanitar()
{
new boss_amanitar();
new mob_amanitar_mushrooms();
-} \ No newline at end of file
+}
diff --git a/src/server/scripts/Northrend/AzjolNerub/Ahnkahet/boss_elder_nadox.cpp b/src/server/scripts/Northrend/AzjolNerub/Ahnkahet/boss_elder_nadox.cpp
index cd583603734..ac668c33874 100644
--- a/src/server/scripts/Northrend/AzjolNerub/Ahnkahet/boss_elder_nadox.cpp
+++ b/src/server/scripts/Northrend/AzjolNerub/Ahnkahet/boss_elder_nadox.cpp
@@ -94,13 +94,13 @@ class boss_elder_nadox : public CreatureScript
if (instance)
instance->SetData(DATA_ELDER_NADOX_EVENT, IN_PROGRESS);
- events.ScheduleEvent(EVENT_PLAGUE, 13*IN_MILLISECONDS);
- events.ScheduleEvent(EVENT_SUMMON_SWARMER, 10*IN_MILLISECONDS);
+ events.ScheduleEvent(EVENT_PLAGUE, 13 * IN_MILLISECONDS);
+ events.ScheduleEvent(EVENT_SUMMON_SWARMER, 10 * IN_MILLISECONDS);
if (IsHeroic())
{
- events.ScheduleEvent(EVENT_RAGE, 12*IN_MILLISECONDS);
- events.ScheduleEvent(EVENT_CHECK_ENRAGE, 5*IN_MILLISECONDS);
+ events.ScheduleEvent(EVENT_RAGE, 12 * IN_MILLISECONDS);
+ events.ScheduleEvent(EVENT_CHECK_ENRAGE, 5 * IN_MILLISECONDS);
}
}
@@ -151,25 +151,25 @@ class boss_elder_nadox : public CreatureScript
switch (eventId)
{
case EVENT_PLAGUE:
- DoCast(SelectTarget(SELECT_TARGET_RANDOM,0, 100, true),SPELL_BROOD_PLAGUE,true);
- events.ScheduleEvent(EVENT_PLAGUE, 15*IN_MILLISECONDS);
+ DoCast(SelectTarget(SELECT_TARGET_RANDOM, 0, 100, true), SPELL_BROOD_PLAGUE, true);
+ events.ScheduleEvent(EVENT_PLAGUE, 15 * IN_MILLISECONDS);
break;
case EVENT_RAGE:
DoCast(H_SPELL_BROOD_RAGE);
- events.ScheduleEvent(EVENT_RAGE, urand(10*IN_MILLISECONDS, 50*IN_MILLISECONDS));
+ events.ScheduleEvent(EVENT_RAGE, urand(10 * IN_MILLISECONDS, 50 * IN_MILLISECONDS));
break;
case EVENT_SUMMON_SWARMER:
DoCast(me, SPELL_SUMMON_SWARMERS);
if (urand(1, 3) == 3) // 33% chance of dialog
Talk(SAY_EGG_SAC);
- events.ScheduleEvent(EVENT_SUMMON_SWARMER, 10*IN_MILLISECONDS);
+ events.ScheduleEvent(EVENT_SUMMON_SWARMER, 10 * IN_MILLISECONDS);
break;
case EVENT_CHECK_ENRAGE:
if (me->HasAura(SPELL_ENRAGE))
return;
if (me->GetPositionZ() < 24.0f)
DoCast(me, SPELL_ENRAGE, true);
- events.ScheduleEvent(EVENT_CHECK_ENRAGE, 5*IN_MILLISECONDS);
+ events.ScheduleEvent(EVENT_CHECK_ENRAGE, 5 * IN_MILLISECONDS);
break;
default:
break;
@@ -209,7 +209,7 @@ class mob_ahnkahar_nerubian : public CreatureScript
if (me->GetEntry() == NPC_AHNKAHAR_GUARDIAN)
DoCast(me, SPELL_GUARDIAN_AURA, true);
- events.ScheduleEvent(EVENT_SPRINT, 13*IN_MILLISECONDS);
+ events.ScheduleEvent(EVENT_SPRINT, 13 * IN_MILLISECONDS);
}
void JustDied(Unit* /*killer*/)
@@ -234,7 +234,7 @@ class mob_ahnkahar_nerubian : public CreatureScript
{
case EVENT_SPRINT:
DoCast(me, SPELL_SPRINT);
- events.ScheduleEvent(EVENT_SPRINT, 20*IN_MILLISECONDS);
+ events.ScheduleEvent(EVENT_SPRINT, 20 * IN_MILLISECONDS);
break;
}
}
diff --git a/src/server/scripts/Northrend/ChamberOfAspects/RubySanctum/boss_general_zarithrian.cpp b/src/server/scripts/Northrend/ChamberOfAspects/RubySanctum/boss_general_zarithrian.cpp
index ab53351cd6b..94325ca015b 100644
--- a/src/server/scripts/Northrend/ChamberOfAspects/RubySanctum/boss_general_zarithrian.cpp
+++ b/src/server/scripts/Northrend/ChamberOfAspects/RubySanctum/boss_general_zarithrian.cpp
@@ -135,13 +135,18 @@ class boss_general_zarithrian : public CreatureScript
Talk(SAY_KILL);
}
+ bool CanAIAttack(Unit const* /*target*/) const
+ {
+ return (instance->GetBossState(DATA_SAVIANA_RAGEFIRE) == DONE && instance->GetBossState(DATA_BALTHARUS_THE_WARBORN) == DONE);
+ }
+
void UpdateAI(uint32 const diff)
{
if (!UpdateVictim())
return;
// Can't use room boundary here, the gameobject is spawned at the same position as the boss. This is just as good anyway.
- if (me->GetPositionX() > 3060.0f)
+ if (me->GetPositionX() > 3058.0f)
{
EnterEvadeMode();
return;
@@ -159,9 +164,11 @@ class boss_general_zarithrian : public CreatureScript
case EVENT_SUMMON_ADDS:
{
if (Creature* stalker1 = ObjectAccessor::GetCreature(*me, instance->GetData64(DATA_ZARITHRIAN_SPAWN_STALKER_1)))
- stalker1->AI()->DoCast(stalker1, SPELL_SUMMON_FLAMECALLER);
+ stalker1->CastSpell(stalker1, SPELL_SUMMON_FLAMECALLER, false);
+
if (Creature* stalker2 = ObjectAccessor::GetCreature(*me, instance->GetData64(DATA_ZARITHRIAN_SPAWN_STALKER_2)))
- stalker2->AI()->DoCast(stalker2, SPELL_SUMMON_FLAMECALLER);
+ stalker2->CastSpell(stalker2, SPELL_SUMMON_FLAMECALLER, false);
+
Talk(SAY_ADDS);
events.ScheduleEvent(EVENT_SUMMON_ADDS, 42000);
break;
@@ -195,9 +202,8 @@ class npc_onyx_flamecaller : public CreatureScript
struct npc_onyx_flamecallerAI : public npc_escortAI
{
- npc_onyx_flamecallerAI(Creature* creature) : npc_escortAI(creature)
+ npc_onyx_flamecallerAI(Creature* creature) : npc_escortAI(creature), _instance(creature->GetInstanceScript())
{
- _instance = creature->GetInstanceScript();
npc_escortAI::SetDespawnAtEnd(false);
}
@@ -289,7 +295,6 @@ class npc_onyx_flamecaller : public CreatureScript
}
private:
EventMap _events;
- bool _movementComplete;
InstanceScript* _instance;
uint8 _lavaGoutCount;
};
diff --git a/src/server/scripts/Northrend/ChamberOfAspects/RubySanctum/boss_halion.cpp b/src/server/scripts/Northrend/ChamberOfAspects/RubySanctum/boss_halion.cpp
index 781dd86cb86..6039e01b901 100644
--- a/src/server/scripts/Northrend/ChamberOfAspects/RubySanctum/boss_halion.cpp
+++ b/src/server/scripts/Northrend/ChamberOfAspects/RubySanctum/boss_halion.cpp
@@ -226,9 +226,9 @@ struct generic_halionAI : public BossAI
{
generic_halionAI(Creature* creature, uint32 bossId) : BossAI(creature, bossId), _canEvade(false) { }
- void EnterCombat(Unit* who)
+ void EnterCombat(Unit* /*who*/)
{
- BossAI::EnterCombat(who);
+ _EnterCombat();
me->AddAura(SPELL_TWILIGHT_PRECISION, me);
_canEvade = false;
events.ScheduleEvent(EVENT_CLEAVE, urand(8000, 10000));
@@ -239,13 +239,13 @@ struct generic_halionAI : public BossAI
void Reset()
{
_canEvade = false;
- BossAI::Reset();
+ _Reset();
}
- void EnterEvadeMode()
+ void JustReachedHome()
{
- BossAI::EnterEvadeMode();
instance->SendEncounterUnit(ENCOUNTER_FRAME_DISENGAGE, me);
+ _JustReachedHome();
}
void ExecuteEvent(uint32 const eventId)
@@ -349,15 +349,20 @@ class boss_halion : public CreatureScript
controller->AI()->SetData(DATA_FIGHT_PHASE, PHASE_ONE);
}
- void JustDied(Unit* killer)
+ void JustDied(Unit* /*killer*/)
{
- BossAI::JustDied(killer);
+ _JustDied();
Talk(SAY_DEATH);
instance->SendEncounterUnit(ENCOUNTER_FRAME_DISENGAGE, me);
+ if (Creature* twilightHalion = ObjectAccessor::GetCreature(*me, instance->GetData64(DATA_TWILIGHT_HALION)))
+ if (twilightHalion->isAlive())
+ twilightHalion->Kill(twilightHalion);
+
if (Creature* controller = ObjectAccessor::GetCreature(*me, instance->GetData64(DATA_HALION_CONTROLLER)))
- me->Kill(controller);
+ if (controller->isAlive())
+ controller->Kill(controller);
}
Position const* GetMeteorStrikePosition() const { return &_meteorStrikePos; }
@@ -515,7 +520,8 @@ class boss_twilight_halion : public CreatureScript
}
if (Creature* controller = ObjectAccessor::GetCreature(*me, instance->GetData64(DATA_HALION_CONTROLLER)))
- controller->Kill(controller);
+ if (controller->isAlive())
+ controller->Kill(controller);
instance->SendEncounterUnit(ENCOUNTER_FRAME_DISENGAGE, me);
}
@@ -592,7 +598,6 @@ class npc_halion_controller : public CreatureScript
_instance(creature->GetInstanceScript()), _summons(me)
{
me->SetPhaseMask(me->GetPhaseMask() | 0x20, true);
- _events.SetPhase(PHASE_INTRO);
}
void Reset()
diff --git a/src/server/scripts/Northrend/ChamberOfAspects/RubySanctum/instance_ruby_sanctum.cpp b/src/server/scripts/Northrend/ChamberOfAspects/RubySanctum/instance_ruby_sanctum.cpp
index c373c2340ff..abfa2df5c97 100644
--- a/src/server/scripts/Northrend/ChamberOfAspects/RubySanctum/instance_ruby_sanctum.cpp
+++ b/src/server/scripts/Northrend/ChamberOfAspects/RubySanctum/instance_ruby_sanctum.cpp
@@ -60,8 +60,11 @@ class instance_ruby_sanctum : public InstanceMapScript
void OnPlayerEnter(Player* /*player*/)
{
if (!GetData64(DATA_HALION_CONTROLLER) && GetBossState(DATA_HALION) != DONE && GetBossState(DATA_GENERAL_ZARITHRIAN) == DONE)
+ {
+ instance->LoadGrid(HalionControllerSpawnPos.GetPositionX(), HalionControllerSpawnPos.GetPositionY());
if (Creature* halionController = instance->SummonCreature(NPC_HALION_CONTROLLER, HalionControllerSpawnPos))
halionController->AI()->DoAction(ACTION_INTRO_HALION);
+ }
}
void OnCreatureCreate(Creature* creature)
@@ -164,6 +167,20 @@ class instance_ruby_sanctum : public InstanceMapScript
}
}
+ void OnUnitDeath(Unit* unit)
+ {
+ Creature* creature = unit->ToCreature();
+ if (!creature)
+ return;
+
+ if (creature->GetEntry() == NPC_GENERAL_ZARITHRIAN && GetBossState(DATA_HALION) != DONE)
+ {
+ instance->LoadGrid(HalionControllerSpawnPos.GetPositionX(), HalionControllerSpawnPos.GetPositionY());
+ if (Creature* halionController = instance->SummonCreature(NPC_HALION_CONTROLLER, HalionControllerSpawnPos))
+ halionController->AI()->DoAction(ACTION_INTRO_HALION);
+ }
+ }
+
uint64 GetData64(uint32 type) const
{
switch (type)
@@ -238,11 +255,6 @@ class instance_ruby_sanctum : public InstanceMapScript
{
if (GetBossState(DATA_SAVIANA_RAGEFIRE) == DONE && GetBossState(DATA_BALTHARUS_THE_WARBORN) == DONE)
HandleGameObject(FlameWallsGUID, state != IN_PROGRESS);
-
- // Not called at instance loading, no big deal.
- if (state == DONE && GetBossState(DATA_HALION) != DONE)
- if (Creature* halionController = instance->SummonCreature(NPC_HALION_CONTROLLER, HalionControllerSpawnPos))
- halionController->AI()->DoAction(ACTION_INTRO_HALION);
break;
}
case DATA_HALION:
diff --git a/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/boss_lord_jaraxxus.cpp b/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/boss_lord_jaraxxus.cpp
index ac520968db8..82cff5ff01f 100644
--- a/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/boss_lord_jaraxxus.cpp
+++ b/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/boss_lord_jaraxxus.cpp
@@ -182,7 +182,7 @@ class boss_jaraxxus : public CreatureScript
events.ScheduleEvent(EVENT_INCINERATE_FLESH, urand(20*IN_MILLISECONDS, 25*IN_MILLISECONDS));
return;
case EVENT_NETHER_POWER:
- me->CastCustomSpell(SPELL_NETHER_POWER, SPELLVALUE_AURA_STACK, RAID_MODE<uint32>(5, 10, 5,10), me, true);
+ me->CastCustomSpell(SPELL_NETHER_POWER, SPELLVALUE_AURA_STACK, RAID_MODE<uint32>(5, 10, 5, 10), me, true);
events.ScheduleEvent(EVENT_NETHER_POWER, 40*IN_MILLISECONDS);
return;
case EVENT_LEGION_FLAME:
diff --git a/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/trial_of_the_crusader.h b/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/trial_of_the_crusader.h
index 70fe03c5e0f..f42c985d3e5 100644
--- a/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/trial_of_the_crusader.h
+++ b/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/trial_of_the_crusader.h
@@ -136,8 +136,8 @@ const Position AnubarakLoc[]=
const Position EndSpawnLoc[]=
{
{648.9167f, 131.0208f, 141.6161f, 0}, // 0 - Highlord Tirion Fordring
- {649.1614f, 142.0399f, 141.3057f ,0}, // 1 - Argent Mage
- {644.6250f, 149.2743f, 140.6015f ,0} // 2 - Portal to Dalaran
+ {649.1614f, 142.0399f, 141.3057f, 0}, // 1 - Argent Mage
+ {644.6250f, 149.2743f, 140.6015f, 0} // 2 - Portal to Dalaran
};
enum euiWorldStates
diff --git a/src/server/scripts/Northrend/FrozenHalls/ForgeOfSouls/boss_devourer_of_souls.cpp b/src/server/scripts/Northrend/FrozenHalls/ForgeOfSouls/boss_devourer_of_souls.cpp
index caf38fd2418..b3781f89e06 100644
--- a/src/server/scripts/Northrend/FrozenHalls/ForgeOfSouls/boss_devourer_of_souls.cpp
+++ b/src/server/scripts/Northrend/FrozenHalls/ForgeOfSouls/boss_devourer_of_souls.cpp
@@ -109,7 +109,7 @@ struct outroPosition
{ { 0, 0 }, { 0.0f, 0.0f, 0.0f, 0.0f } }
};
-Position const CrucibleSummonPos = {5672.294f,2520.686f, 713.4386f, 0.9599311f};
+Position const CrucibleSummonPos = {5672.294f, 2520.686f, 713.4386f, 0.9599311f};
#define DATA_THREE_FACED 1
diff --git a/src/server/scripts/Northrend/Gundrak/boss_slad_ran.cpp b/src/server/scripts/Northrend/Gundrak/boss_slad_ran.cpp
index 4acabc3b2b3..1b5f6e5d426 100644
--- a/src/server/scripts/Northrend/Gundrak/boss_slad_ran.cpp
+++ b/src/server/scripts/Northrend/Gundrak/boss_slad_ran.cpp
@@ -251,7 +251,7 @@ public:
if (TempSummon* _me = me->ToTempSummon())
if (Creature* sladran = _me->GetSummoner()->ToCreature())
- sladran->AI()->SetGUID(target->GetGUID() ,DATA_SNAKES_WHYD_IT_HAVE_TO_BE_SNAKES);
+ sladran->AI()->SetGUID(target->GetGUID(), DATA_SNAKES_WHYD_IT_HAVE_TO_BE_SNAKES);
me->DespawnOrUnsummon();
}
diff --git a/src/server/scripts/Northrend/Naxxramas/boss_grobbulus.cpp b/src/server/scripts/Northrend/Naxxramas/boss_grobbulus.cpp
index f3f2e587178..536c2af2bd8 100644
--- a/src/server/scripts/Northrend/Naxxramas/boss_grobbulus.cpp
+++ b/src/server/scripts/Northrend/Naxxramas/boss_grobbulus.cpp
@@ -20,25 +20,33 @@
#include "naxxramas.h"
#include "SpellInfo.h"
-#define SPELL_BOMBARD_SLIME 28280
-
-#define SPELL_POISON_CLOUD 28240
-#define SPELL_MUTATING_INJECTION 28169
-#define SPELL_SLIME_SPRAY RAID_MODE(28157, 54364)
-#define SPELL_BERSERK 26662
-#define SPELL_POISON_CLOUD_ADD 59116
+enum Spells
+{
+ SPELL_BOMBARD_SLIME = 28280,
+ SPELL_POISON_CLOUD = 28240,
+ SPELL_MUTATING_INJECTION = 28169,
+ SPELL_SLIME_SPRAY = 28157,
+ H_SPELL_SLIME_SPRAY = 54364,
+ SPELL_BERSERK = 26662,
+ SPELL_POISON_CLOUD_ADD = 59116
+};
-#define EVENT_BERSERK 1
-#define EVENT_CLOUD 2
-#define EVENT_INJECT 3
-#define EVENT_SPRAY 4
+enum Events
+{
+ EVENT_BERSERK = 0,
+ EVENT_CLOUD = 1,
+ EVENT_INJECT = 2,
+ EVENT_SPRAY = 3
+};
-#define MOB_FALLOUT_SLIME 16290
+enum CreatureId
+{
+ MOB_FALLOUT_SLIME = 16290
+};
class boss_grobbulus : public CreatureScript
{
-public:
- boss_grobbulus() : CreatureScript("boss_grobbulus") { }
+public: boss_grobbulus() : CreatureScript("boss_grobbulus") { }
CreatureAI* GetAI(Creature* creature) const
{
diff --git a/src/server/scripts/Northrend/Naxxramas/boss_kelthuzad.cpp b/src/server/scripts/Northrend/Naxxramas/boss_kelthuzad.cpp
index 35f703e0563..1b34e2a93a7 100644
--- a/src/server/scripts/Northrend/Naxxramas/boss_kelthuzad.cpp
+++ b/src/server/scripts/Northrend/Naxxramas/boss_kelthuzad.cpp
@@ -603,7 +603,7 @@ public:
ThreatContainer::StorageType const &threatList = me->getThreatManager().getThreatList();
for (ThreatContainer::StorageType::const_iterator itr = threatList.begin(); itr != threatList.end(); ++itr)
{
- Unit * const target = (*itr)->getTarget();
+ Unit* const target = (*itr)->getTarget();
if (target->GetTypeId() == TYPEID_PLAYER
&& target->getPowerType() == POWER_MANA
diff --git a/src/server/scripts/Northrend/Nexus/EyeOfEternity/boss_malygos.cpp b/src/server/scripts/Northrend/Nexus/EyeOfEternity/boss_malygos.cpp
index 952363c6dcf..1be93dcbaa5 100644
--- a/src/server/scripts/Northrend/Nexus/EyeOfEternity/boss_malygos.cpp
+++ b/src/server/scripts/Northrend/Nexus/EyeOfEternity/boss_malygos.cpp
@@ -167,7 +167,7 @@ enum Texts
#define MAX_HOVER_DISK_WAYPOINTS 18
-// Sniffed data (x, y,z)
+// Sniffed data (x, y, z)
const Position HoverDiskWaypoints[MAX_HOVER_DISK_WAYPOINTS] =
{
{782.9821f, 1296.652f, 282.1114f, 0.0f},
@@ -192,7 +192,7 @@ const Position HoverDiskWaypoints[MAX_HOVER_DISK_WAYPOINTS] =
#define GROUND_Z 268
-// Source: Sniffs (x,y,z)
+// Source: Sniffs (x, y, z)
#define MALYGOS_MAX_WAYPOINTS 16
const Position MalygosPhaseTwoWaypoints[MALYGOS_MAX_WAYPOINTS] =
{
diff --git a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_algalon_the_observer.cpp b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_algalon_the_observer.cpp
index 1f96848fa0a..13a4fe23690 100644
--- a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_algalon_the_observer.cpp
+++ b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_algalon_the_observer.cpp
@@ -343,7 +343,7 @@ class boss_algalon_the_observer : public CreatureScript
DoCast(me, SPELL_RIDE_THE_LIGHTNING, true);
me->GetMotionMaster()->MovePoint(POINT_ALGALON_LAND, AlgalonLandPos);
me->SetHomePosition(AlgalonLandPos);
- Movement::MoveSplineInit init(*me);
+ Movement::MoveSplineInit init(me);
init.MoveTo(AlgalonLandPos.GetPositionX(), AlgalonLandPos.GetPositionY(), AlgalonLandPos.GetPositionZ());
init.SetOrientationFixed(true);
init.Launch();
diff --git a/src/server/scripts/Northrend/Ulduar/Ulduar/instance_ulduar.cpp b/src/server/scripts/Northrend/Ulduar/Ulduar/instance_ulduar.cpp
index 7934f6fba72..d3c174841ee 100644
--- a/src/server/scripts/Northrend/Ulduar/Ulduar/instance_ulduar.cpp
+++ b/src/server/scripts/Northrend/Ulduar/Ulduar/instance_ulduar.cpp
@@ -787,7 +787,7 @@ class instance_ulduar : public InstanceMapScript
return 0;
}
- bool CheckAchievementCriteriaMeet(uint32 criteriaId, Player const* , Unit const* /* = NULL */, uint32 /* = 0 */)
+ bool CheckAchievementCriteriaMeet(uint32 criteriaId, Player const*, Unit const* /* = NULL */, uint32 /* = 0 */)
{
switch (criteriaId)
{
diff --git a/src/server/scripts/Northrend/UtgardeKeep/UtgardeKeep/boss_ingvar_the_plunderer.cpp b/src/server/scripts/Northrend/UtgardeKeep/UtgardeKeep/boss_ingvar_the_plunderer.cpp
index 1fd84b6f6ce..f579fb2b93c 100644
--- a/src/server/scripts/Northrend/UtgardeKeep/UtgardeKeep/boss_ingvar_the_plunderer.cpp
+++ b/src/server/scripts/Northrend/UtgardeKeep/UtgardeKeep/boss_ingvar_the_plunderer.cpp
@@ -124,10 +124,10 @@ public:
events.Reset();
events.SetPhase(PHASE_HUMAN);
- events.ScheduleEvent(EVENT_CLEAVE, urand(6,12)*IN_MILLISECONDS, 0, PHASE_HUMAN);
- events.ScheduleEvent(EVENT_STAGGERING_ROAR, urand(18,21)*IN_MILLISECONDS, 0, PHASE_HUMAN);
- events.ScheduleEvent(EVENT_ENRAGE, urand(7,14)*IN_MILLISECONDS, 0, PHASE_HUMAN);
- events.ScheduleEvent(EVENT_SMASH, urand(12,17)*IN_MILLISECONDS, 0, PHASE_HUMAN);
+ events.ScheduleEvent(EVENT_CLEAVE, urand(6, 12)*IN_MILLISECONDS, 0, PHASE_HUMAN);
+ events.ScheduleEvent(EVENT_STAGGERING_ROAR, urand(18, 21)*IN_MILLISECONDS, 0, PHASE_HUMAN);
+ events.ScheduleEvent(EVENT_ENRAGE, urand(7, 14)*IN_MILLISECONDS, 0, PHASE_HUMAN);
+ events.ScheduleEvent(EVENT_SMASH, urand(12, 17)*IN_MILLISECONDS, 0, PHASE_HUMAN);
if (instance)
instance->SetData(DATA_INGVAR_EVENT, NOT_STARTED);
@@ -193,9 +193,9 @@ public:
void ScheduleSecondPhase()
{
events.SetPhase(PHASE_UNDEAD);
- events.ScheduleEvent(EVENT_DARK_SMASH, urand(14,18)*IN_MILLISECONDS, 0, PHASE_UNDEAD);
- events.ScheduleEvent(EVENT_DREADFUL_ROAR, urand(18,22)*IN_MILLISECONDS, 0, PHASE_UNDEAD);
- events.ScheduleEvent(EVENT_WOE_STRIKE, urand(10,14)*IN_MILLISECONDS, 0, PHASE_UNDEAD);
+ events.ScheduleEvent(EVENT_DARK_SMASH, urand(14, 18)*IN_MILLISECONDS, 0, PHASE_UNDEAD);
+ events.ScheduleEvent(EVENT_DREADFUL_ROAR, urand(18, 22)*IN_MILLISECONDS, 0, PHASE_UNDEAD);
+ events.ScheduleEvent(EVENT_WOE_STRIKE, urand(10, 14)*IN_MILLISECONDS, 0, PHASE_UNDEAD);
events.ScheduleEvent(EVENT_SHADOW_AXE, 30*IN_MILLISECONDS, 0, PHASE_UNDEAD);
}
@@ -221,19 +221,19 @@ public:
// PHASE ONE
case EVENT_CLEAVE:
DoCastVictim(SPELL_CLEAVE);
- events.ScheduleEvent(EVENT_CLEAVE, urand(6,12)*IN_MILLISECONDS, 0, PHASE_HUMAN);
+ events.ScheduleEvent(EVENT_CLEAVE, urand(6, 12)*IN_MILLISECONDS, 0, PHASE_HUMAN);
break;
case EVENT_STAGGERING_ROAR:
DoCast(me, SPELL_STAGGERING_ROAR);
- events.ScheduleEvent(EVENT_STAGGERING_ROAR, urand(18,22)*IN_MILLISECONDS, 0, PHASE_HUMAN);
+ events.ScheduleEvent(EVENT_STAGGERING_ROAR, urand(18, 22)*IN_MILLISECONDS, 0, PHASE_HUMAN);
break;
case EVENT_ENRAGE:
DoCast(me, SPELL_ENRAGE);
- events.ScheduleEvent(EVENT_ENRAGE, urand(7,14)*IN_MILLISECONDS, 0, PHASE_HUMAN);
+ events.ScheduleEvent(EVENT_ENRAGE, urand(7, 14)*IN_MILLISECONDS, 0, PHASE_HUMAN);
break;
case EVENT_SMASH:
DoCastAOE(SPELL_SMASH);
- events.ScheduleEvent(EVENT_SMASH, urand(12,16)*IN_MILLISECONDS, 0, PHASE_HUMAN);
+ events.ScheduleEvent(EVENT_SMASH, urand(12, 16)*IN_MILLISECONDS, 0, PHASE_HUMAN);
break;
case EVENT_JUST_TRANSFORMED:
me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE);
@@ -247,15 +247,15 @@ public:
// PHASE TWO
case EVENT_DARK_SMASH:
DoCastVictim(SPELL_DARK_SMASH);
- events.ScheduleEvent(EVENT_DARK_SMASH, urand(12,16)*IN_MILLISECONDS, 0, PHASE_UNDEAD);
+ events.ScheduleEvent(EVENT_DARK_SMASH, urand(12, 16)*IN_MILLISECONDS, 0, PHASE_UNDEAD);
break;
case EVENT_DREADFUL_ROAR:
DoCast(me, SPELL_DREADFUL_ROAR);
- events.ScheduleEvent(EVENT_DREADFUL_ROAR, urand(18,22)*IN_MILLISECONDS, 0, PHASE_UNDEAD);
+ events.ScheduleEvent(EVENT_DREADFUL_ROAR, urand(18, 22)*IN_MILLISECONDS, 0, PHASE_UNDEAD);
break;
case EVENT_WOE_STRIKE:
DoCastVictim(SPELL_WOE_STRIKE);
- events.ScheduleEvent(EVENT_WOE_STRIKE, urand(10,14)*IN_MILLISECONDS, 0, PHASE_UNDEAD);
+ events.ScheduleEvent(EVENT_WOE_STRIKE, urand(10, 14)*IN_MILLISECONDS, 0, PHASE_UNDEAD);
break;
case EVENT_SHADOW_AXE:
if (Unit* target = SelectTarget(SELECT_TARGET_TOPAGGRO, 1))
diff --git a/src/server/scripts/Northrend/UtgardeKeep/UtgardeKeep/boss_keleseth.cpp b/src/server/scripts/Northrend/UtgardeKeep/UtgardeKeep/boss_keleseth.cpp
index d709182bf04..6e532a88396 100644
--- a/src/server/scripts/Northrend/UtgardeKeep/UtgardeKeep/boss_keleseth.cpp
+++ b/src/server/scripts/Northrend/UtgardeKeep/UtgardeKeep/boss_keleseth.cpp
@@ -129,8 +129,8 @@ public:
instance->SetData(DATA_PRINCEKELESETH_EVENT, NOT_STARTED);
events.Reset();
- events.ScheduleEvent(EVENT_SHADOWBOLT, urand(2,3)*IN_MILLISECONDS);
- events.ScheduleEvent(EVENT_FROST_TOMB, urand(14,19)*IN_MILLISECONDS);
+ events.ScheduleEvent(EVENT_SHADOWBOLT, urand(2, 3)*IN_MILLISECONDS);
+ events.ScheduleEvent(EVENT_FROST_TOMB, urand(14, 19)*IN_MILLISECONDS);
events.ScheduleEvent(EVENT_SUMMON_SKELETONS, 6*IN_MILLISECONDS);
summons.DespawnAll();
@@ -213,7 +213,7 @@ public:
break;
case EVENT_SHADOWBOLT:
DoCastVictim(SPELL_SHADOWBOLT);
- events.ScheduleEvent(EVENT_SHADOWBOLT, urand(2,3)*IN_MILLISECONDS);
+ events.ScheduleEvent(EVENT_SHADOWBOLT, urand(2, 3)*IN_MILLISECONDS);
break;
case EVENT_FROST_TOMB:
if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 100, true, -SPELL_FROST_TOMB))
@@ -225,7 +225,7 @@ public:
// checked from sniffs - the player casts the spell
target->CastSpell(target, SPELL_FROST_TOMB_SUMMON, true);
}
- events.ScheduleEvent(EVENT_FROST_TOMB, urand(14,19)*IN_MILLISECONDS);
+ events.ScheduleEvent(EVENT_FROST_TOMB, urand(14, 19)*IN_MILLISECONDS);
break;
}
}
@@ -261,7 +261,7 @@ public:
void Reset()
{
events.Reset();
- events.ScheduleEvent(EVENT_DECREPIFY, urand(4,6)*IN_MILLISECONDS);
+ events.ScheduleEvent(EVENT_DECREPIFY, urand(4, 6)*IN_MILLISECONDS);
}
@@ -280,7 +280,7 @@ public:
me->SetFlag(UNIT_FIELD_BYTES_1, UNIT_STAND_STATE_DEAD);
events.Reset();
- events.ScheduleEvent(EVENT_RESURRECT, urand(18,22)*IN_MILLISECONDS);
+ events.ScheduleEvent(EVENT_RESURRECT, urand(18, 22)*IN_MILLISECONDS);
me->GetMotionMaster()->MovementExpired(false);
me->GetMotionMaster()->MoveIdle();
@@ -304,7 +304,7 @@ public:
{
case EVENT_DECREPIFY:
DoCast(SelectTarget(SELECT_TARGET_RANDOM, 0, 0.0f, true, -SPELL_DECREPIFY), SPELL_DECREPIFY);
- events.ScheduleEvent(EVENT_DECREPIFY, urand(1,5)*IN_MILLISECONDS);
+ events.ScheduleEvent(EVENT_DECREPIFY, urand(1, 5)*IN_MILLISECONDS);
break;
case EVENT_RESURRECT:
events.ScheduleEvent(EVENT_FULL_HEAL, 1*IN_MILLISECONDS);
@@ -325,7 +325,7 @@ public:
me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
me->RemoveFlag(UNIT_FIELD_BYTES_1, UNIT_STAND_STATE_DEAD);
me->GetMotionMaster()->MoveChase(me->getVictim());
- events.ScheduleEvent(EVENT_DECREPIFY, urand(4,6)*IN_MILLISECONDS);
+ events.ScheduleEvent(EVENT_DECREPIFY, urand(4, 6)*IN_MILLISECONDS);
break;
}
}
diff --git a/src/server/scripts/Northrend/UtgardeKeep/UtgardePinnacle/boss_svala.cpp b/src/server/scripts/Northrend/UtgardeKeep/UtgardePinnacle/boss_svala.cpp
index 9468a111db5..dffdadc5b9c 100644
--- a/src/server/scripts/Northrend/UtgardeKeep/UtgardePinnacle/boss_svala.cpp
+++ b/src/server/scripts/Northrend/UtgardeKeep/UtgardePinnacle/boss_svala.cpp
@@ -101,8 +101,8 @@ enum SvalaPhase
static const float spectatorWP[2][3] =
{
- {296.95f,-312.76f,86.36f},
- {297.69f,-275.81f,86.36f}
+ {296.95f, -312.76f, 86.36f},
+ {297.69f, -275.81f, 86.36f}
};
static Position ArthasPos = { 295.81f, -366.16f, 92.57f, 1.58f };
@@ -517,7 +517,7 @@ public:
if (motionType == POINT_MOTION_TYPE)
{
if (pointId == 1)
- me->GetMotionMaster()->MovePoint(2,spectatorWP[1][0],spectatorWP[1][1],spectatorWP[1][2]);
+ me->GetMotionMaster()->MovePoint(2, spectatorWP[1][0], spectatorWP[1][1], spectatorWP[1][2]);
else if (pointId == 2)
me->DespawnOrUnsummon(1000);
}
diff --git a/src/server/scripts/Northrend/VioletHold/boss_xevozz.cpp b/src/server/scripts/Northrend/VioletHold/boss_xevozz.cpp
index 92ced3c702f..891b91c6dfb 100644
--- a/src/server/scripts/Northrend/VioletHold/boss_xevozz.cpp
+++ b/src/server/scripts/Northrend/VioletHold/boss_xevozz.cpp
@@ -28,7 +28,7 @@ enum Spells
SPELL_ARCANE_BUFFET_H = 59485,
SPELL_SUMMON_ETHEREAL_SPHERE_1 = 54102,
SPELL_SUMMON_ETHEREAL_SPHERE_2 = 54137,
- SPELL_SUMMON_ETHEREAL_SPHERE_3 = 54138,
+ SPELL_SUMMON_ETHEREAL_SPHERE_3 = 54138
};
enum NPCs
@@ -42,7 +42,7 @@ enum CreatureSpells
SPELL_ARCANE_POWER = 54160,
H_SPELL_ARCANE_POWER = 59474,
SPELL_SUMMON_PLAYERS = 54164,
- SPELL_POWER_BALL_VISUAL = 54141,
+ SPELL_POWER_BALL_VISUAL = 54141
};
enum Yells
diff --git a/src/server/scripts/Northrend/zone_icecrown.cpp b/src/server/scripts/Northrend/zone_icecrown.cpp
index 2ec5a3e8164..098fa80a3d3 100644
--- a/src/server/scripts/Northrend/zone_icecrown.cpp
+++ b/src/server/scripts/Northrend/zone_icecrown.cpp
@@ -586,7 +586,7 @@ public:
struct npc_blessed_bannerAI : public Scripted_NoMovementAI
{
- npc_blessed_bannerAI(Creature* creature) : Scripted_NoMovementAI(creature) , Summons(me)
+ npc_blessed_bannerAI(Creature* creature) : Scripted_NoMovementAI(creature), Summons(me)
{
HalofSpawned = false;
PhaseCount = 0;
@@ -611,7 +611,7 @@ public:
me->setRegeneratingHealth(false);
DoCast(SPELL_THREAT_PULSE);
me->AI()->Talk(BANNER_SAY);
- events.ScheduleEvent(EVENT_SPAWN,3000);
+ events.ScheduleEvent(EVENT_SPAWN, 3000);
}
void EnterCombat(Unit* /*who*/) {}
@@ -672,167 +672,167 @@ public:
guidMason[2] = Mason3->GetGUID();
Mason3->GetMotionMaster()->MovePoint(0, Mason3Pos[1]);
}
- events.ScheduleEvent(EVENT_INTRO_1,15000);
+ events.ScheduleEvent(EVENT_INTRO_1, 15000);
}
break;
case EVENT_INTRO_1:
{
- if (Creature* Dalfors = me->GetCreature(*me,guidDalfors))
+ if (Creature* Dalfors = me->GetCreature(*me, guidDalfors))
Dalfors->AI()->Talk(DALFORS_SAY_PRE_1);
- events.ScheduleEvent(EVENT_INTRO_2,5000);
+ events.ScheduleEvent(EVENT_INTRO_2, 5000);
}
break;
case EVENT_INTRO_2:
{
- if (Creature* Dalfors = me->GetCreature(*me,guidDalfors))
+ if (Creature* Dalfors = me->GetCreature(*me, guidDalfors))
{
Dalfors->SetFacingTo(6.215f);
Dalfors->AI()->Talk(DALFORS_SAY_PRE_2);
}
- events.ScheduleEvent(EVENT_INTRO_3,5000);
+ events.ScheduleEvent(EVENT_INTRO_3, 5000);
}
break;
case EVENT_INTRO_3:
{
- if (Creature* Dalfors = me->GetCreature(*me,guidDalfors))
+ if (Creature* Dalfors = me->GetCreature(*me, guidDalfors))
{
Dalfors->GetMotionMaster()->MovePoint(0, DalforsPos[2]);
Dalfors->SetHomePosition(DalforsPos[2]);
}
- if (Creature* Priest1 = me->GetCreature(*me,guidPriest[0]))
+ if (Creature* Priest1 = me->GetCreature(*me, guidPriest[0]))
{
Priest1->SetFacingTo(5.7421f);
Priest1->SetHomePosition(Priest1Pos[1]);
}
- if (Creature* Priest2 = me->GetCreature(*me,guidPriest[1]))
+ if (Creature* Priest2 = me->GetCreature(*me, guidPriest[1]))
{
Priest2->SetFacingTo(5.7421f);
Priest2->SetHomePosition(Priest2Pos[1]);
}
- if (Creature* Priest3 = me->GetCreature(*me,guidPriest[2]))
+ if (Creature* Priest3 = me->GetCreature(*me, guidPriest[2]))
{
Priest3->SetFacingTo(5.7421f);
Priest3->SetHomePosition(Priest3Pos[1]);
}
- if (Creature* Mason1 = me->GetCreature(*me,guidMason[0]))
+ if (Creature* Mason1 = me->GetCreature(*me, guidMason[0]))
{
Mason1->GetMotionMaster()->MovePoint(0, Mason1Pos[2]);
Mason1->SetHomePosition(Mason1Pos[2]);
}
- if (Creature* Mason2 = me->GetCreature(*me,guidMason[1]))
+ if (Creature* Mason2 = me->GetCreature(*me, guidMason[1]))
{
Mason2->GetMotionMaster()->MovePoint(0, Mason2Pos[2]);
Mason2->SetHomePosition(Mason2Pos[2]);
}
- if (Creature* Mason3 = me->GetCreature(*me,guidMason[2]))
+ if (Creature* Mason3 = me->GetCreature(*me, guidMason[2]))
{
Mason3->GetMotionMaster()->MovePoint(0, Mason3Pos[2]);
Mason3->SetHomePosition(Mason3Pos[2]);
}
- events.ScheduleEvent(EVENT_START_FIGHT,5000);
- events.ScheduleEvent(EVENT_MASON_ACTION,15000);
+ events.ScheduleEvent(EVENT_START_FIGHT, 5000);
+ events.ScheduleEvent(EVENT_MASON_ACTION, 15000);
}
break;
case EVENT_MASON_ACTION:
{
- if (Creature* Mason1 = me->GetCreature(*me,guidMason[0]))
+ if (Creature* Mason1 = me->GetCreature(*me, guidMason[0]))
{
Mason1->SetFacingTo(2.8972f);
- Mason1->AI()->SetData(1,1); // triggers SAI actions on npc
+ Mason1->AI()->SetData(1, 1); // triggers SAI actions on npc
}
- if (Creature* Mason2 = me->GetCreature(*me,guidMason[1]))
+ if (Creature* Mason2 = me->GetCreature(*me, guidMason[1]))
{
Mason2->SetFacingTo(3.1241f);
- Mason2->AI()->SetData(1,1); // triggers SAI actions on npc
+ Mason2->AI()->SetData(1, 1); // triggers SAI actions on npc
}
- if (Creature* Mason3 = me->GetCreature(*me,guidMason[2]))
+ if (Creature* Mason3 = me->GetCreature(*me, guidMason[2]))
{
Mason3->SetFacingTo(3.6651f);
- Mason3->AI()->SetData(1,1); // triggers SAI actions on npc
+ Mason3->AI()->SetData(1, 1); // triggers SAI actions on npc
}
}
break;
case EVENT_START_FIGHT:
{
- if(Creature* LK = GetClosestCreatureWithEntry(me,NPC_LK,100))
+ if (Creature* LK = GetClosestCreatureWithEntry(me, NPC_LK, 100))
LK->AI()->Talk(LK_TALK_1);
- if (Creature* Dalfors = me->GetCreature(*me,guidDalfors))
+ if (Creature* Dalfors = me->GetCreature(*me, guidDalfors))
Dalfors->AI()->Talk(DALFORS_SAY_START);
- events.ScheduleEvent(EVENT_WAVE_SPAWN,1000);
+ events.ScheduleEvent(EVENT_WAVE_SPAWN, 1000);
}
break;
case EVENT_WAVE_SPAWN:
{
if (PhaseCount == 3)
{
- if (Creature* LK = GetClosestCreatureWithEntry(me,NPC_LK,100))
+ if (Creature* LK = GetClosestCreatureWithEntry(me, NPC_LK, 100))
LK->AI()->Talk(LK_TALK_2);
}
else if (PhaseCount == 6)
{
- if (Creature* LK = GetClosestCreatureWithEntry(me,NPC_LK,100))
+ if (Creature* LK = GetClosestCreatureWithEntry(me, NPC_LK, 100))
LK->AI()->Talk(LK_TALK_3);
}
- if (Creature* tempsum = DoSummon(NPC_SCOURGE_DRUDGE,Mason3Pos[0]))
+ if (Creature* tempsum = DoSummon(NPC_SCOURGE_DRUDGE, Mason3Pos[0]))
{
tempsum->SetHomePosition(DalforsPos[2]);
- tempsum->AI()->AttackStart(GetClosestCreatureWithEntry(me,NPC_BLESSED_BANNER,100));
+ tempsum->AI()->AttackStart(GetClosestCreatureWithEntry(me, NPC_BLESSED_BANNER, 100));
}
- if (urand(0,1) == 0)
+ if (urand(0, 1) == 0)
{
- if (Creature* tempsum = DoSummon(NPC_HIDEOUS_PLAGEBRINGER,Mason1Pos[0]))
+ if (Creature* tempsum = DoSummon(NPC_HIDEOUS_PLAGEBRINGER, Mason1Pos[0]))
{
tempsum->SetHomePosition(DalforsPos[2]);
- tempsum->AI()->AttackStart(GetClosestCreatureWithEntry(me,NPC_BLESSED_BANNER,100));
+ tempsum->AI()->AttackStart(GetClosestCreatureWithEntry(me, NPC_BLESSED_BANNER, 100));
}
- if (Creature* tempsum = DoSummon(NPC_HIDEOUS_PLAGEBRINGER,Mason2Pos[0]))
+ if (Creature* tempsum = DoSummon(NPC_HIDEOUS_PLAGEBRINGER, Mason2Pos[0]))
{
tempsum->SetHomePosition(DalforsPos[2]);
- tempsum->AI()->AttackStart(GetClosestCreatureWithEntry(me,NPC_BLESSED_BANNER,100));
+ tempsum->AI()->AttackStart(GetClosestCreatureWithEntry(me, NPC_BLESSED_BANNER, 100));
}
}
else
{
- if (Creature* tempsum = DoSummon(NPC_REANIMATED_CAPTAIN,Mason1Pos[0]))
+ if (Creature* tempsum = DoSummon(NPC_REANIMATED_CAPTAIN, Mason1Pos[0]))
{
tempsum->SetHomePosition(DalforsPos[2]);
- tempsum->AI()->AttackStart(GetClosestCreatureWithEntry(me,NPC_BLESSED_BANNER,100));
+ tempsum->AI()->AttackStart(GetClosestCreatureWithEntry(me, NPC_BLESSED_BANNER, 100));
}
- if (Creature* tempsum = DoSummon(NPC_REANIMATED_CAPTAIN,Mason2Pos[0]))
+ if (Creature* tempsum = DoSummon(NPC_REANIMATED_CAPTAIN, Mason2Pos[0]))
{
tempsum->SetHomePosition(DalforsPos[2]);
- tempsum->AI()->AttackStart(GetClosestCreatureWithEntry(me,NPC_BLESSED_BANNER,100));
+ tempsum->AI()->AttackStart(GetClosestCreatureWithEntry(me, NPC_BLESSED_BANNER, 100));
}
}
PhaseCount++;
if (PhaseCount < 8)
- events.ScheduleEvent(EVENT_WAVE_SPAWN,urand(10000,20000));
+ events.ScheduleEvent(EVENT_WAVE_SPAWN, urand(10000, 20000));
else
- events.ScheduleEvent(EVENT_HALOF,urand(10000,20000));
+ events.ScheduleEvent(EVENT_HALOF, urand(10000, 20000));
}
break;
case EVENT_HALOF:
{
- if (Creature* LK = GetClosestCreatureWithEntry(me,NPC_LK,100))
+ if (Creature* LK = GetClosestCreatureWithEntry(me, NPC_LK, 100))
LK->AI()->Talk(LK_TALK_4);
- if (Creature* tempsum = DoSummon(NPC_SCOURGE_DRUDGE,Mason1Pos[0]))
+ if (Creature* tempsum = DoSummon(NPC_SCOURGE_DRUDGE, Mason1Pos[0]))
{
tempsum->SetHomePosition(DalforsPos[2]);
- tempsum->AI()->AttackStart(GetClosestCreatureWithEntry(me,NPC_BLESSED_BANNER,100));
+ tempsum->AI()->AttackStart(GetClosestCreatureWithEntry(me, NPC_BLESSED_BANNER, 100));
}
- if (Creature* tempsum = DoSummon(NPC_SCOURGE_DRUDGE,Mason2Pos[0]))
+ if (Creature* tempsum = DoSummon(NPC_SCOURGE_DRUDGE, Mason2Pos[0]))
{
tempsum->SetHomePosition(DalforsPos[2]);
- tempsum->AI()->AttackStart(GetClosestCreatureWithEntry(me,NPC_BLESSED_BANNER,100));
+ tempsum->AI()->AttackStart(GetClosestCreatureWithEntry(me, NPC_BLESSED_BANNER, 100));
}
- if (Creature* tempsum = DoSummon(NPC_HALOF_THE_DEATHBRINGER,DalforsPos[0]))
+ if (Creature* tempsum = DoSummon(NPC_HALOF_THE_DEATHBRINGER, DalforsPos[0]))
{
HalofSpawned = true;
guidHalof = tempsum->GetGUID();
tempsum->SetHomePosition(DalforsPos[2]);
- tempsum->AI()->AttackStart(GetClosestCreatureWithEntry(me,NPC_BLESSED_BANNER,100));
+ tempsum->AI()->AttackStart(GetClosestCreatureWithEntry(me, NPC_BLESSED_BANNER, 100));
}
}
break;
@@ -845,17 +845,17 @@ public:
}
if (PhaseCount == 8)
- if (Creature* Halof = me->GetCreature(*me,guidHalof))
+ if (Creature* Halof = me->GetCreature(*me, guidHalof))
if (Halof->isDead())
{
- DoCast(me,SPELL_CRUSADERS_SPIRE_VICTORY,true);
+ DoCast(me, SPELL_CRUSADERS_SPIRE_VICTORY, true);
Summons.DespawnEntry(NPC_HIDEOUS_PLAGEBRINGER);
Summons.DespawnEntry(NPC_REANIMATED_CAPTAIN);
Summons.DespawnEntry(NPC_SCOURGE_DRUDGE);
Summons.DespawnEntry(NPC_HALOF_THE_DEATHBRINGER);
- if (Creature* Dalfors = me->GetCreature(*me,guidDalfors))
+ if (Creature* Dalfors = me->GetCreature(*me, guidDalfors))
Dalfors->AI()->Talk(DALFORS_YELL_FINISHED);
- events.ScheduleEvent(EVENT_ENDED,10000);
+ events.ScheduleEvent(EVENT_ENDED, 10000);
}
}
};
diff --git a/src/server/scripts/Northrend/zone_sholazar_basin.cpp b/src/server/scripts/Northrend/zone_sholazar_basin.cpp
index 2aa355084a1..7db2ae4707b 100644
--- a/src/server/scripts/Northrend/zone_sholazar_basin.cpp
+++ b/src/server/scripts/Northrend/zone_sholazar_basin.cpp
@@ -540,13 +540,13 @@ public:
Player* player = Player::GetPlayer(*me, playerGUID);
Creature* orphan = Creature::GetCreature(*me, orphanGUID);
- if(!orphan || !player)
+ if (!orphan || !player)
{
Reset();
return;
}
- switch(phase)
+ switch (phase)
{
case 1:
orphan->GetMotionMaster()->MovePoint(0, me->GetPositionX() + cos(me->GetOrientation()) * 5, me->GetPositionY() + sin(me->GetOrientation()) * 5, me->GetPositionZ());
@@ -1033,6 +1033,102 @@ public:
}
};
+/*######
+## Quest: Reconnaissance Flight (12671)
+######*/
+enum ReconnaissanceFlight
+{
+ NPC_PLANE = 28710, // Vic's Flying Machine
+ NPC_PILOT = 28646,
+
+ VIC_SAY_0 = 0,
+ VIC_SAY_1 = 1,
+ VIC_SAY_2 = 2,
+ VIC_SAY_3 = 3,
+ VIC_SAY_4 = 4,
+ VIC_SAY_5 = 5,
+ VIC_SAY_6 = 6,
+ PLANE_EMOTE = 0,
+
+ AURA_ENGINE = 52255, // Engine on Fire
+
+ SPELL_LAND = 52226, // Land Flying Machine
+ SPELL_CREDIT = 53328 // Land Flying Machine Credit
+};
+
+class npc_vics_flying_machine : public CreatureScript
+{
+public:
+ npc_vics_flying_machine() : CreatureScript("npc_vics_flying_machine") { }
+
+ struct npc_vics_flying_machineAI : public VehicleAI
+ {
+ npc_vics_flying_machineAI(Creature* creature) : VehicleAI(creature) {}
+
+ void PassengerBoarded(Unit* passenger, int8 /*seatId*/, bool apply)
+ {
+ if (apply && passenger->GetTypeId() == TYPEID_PLAYER)
+ me->GetMotionMaster()->MovePath(NPC_PLANE, false);
+ }
+
+ void MovementInform(uint32 type, uint32 id)
+ {
+ if (type != WAYPOINT_MOTION_TYPE)
+ return;
+
+ if (Creature* pilot = GetClosestCreatureWithEntry(me, NPC_PILOT, 10))
+ switch (id)
+ {
+ case 5:
+ pilot->AI()->Talk(VIC_SAY_0);
+ break;
+ case 11:
+ pilot->AI()->Talk(VIC_SAY_1);
+ break;
+ case 12:
+ pilot->AI()->Talk(VIC_SAY_2);
+ break;
+ case 14:
+ pilot->AI()->Talk(VIC_SAY_3);
+ break;
+ case 15:
+ pilot->AI()->Talk(VIC_SAY_4);
+ break;
+ case 17:
+ pilot->AI()->Talk(VIC_SAY_5);
+ break;
+ case 21:
+ pilot->AI()->Talk(VIC_SAY_6);
+ break;
+ case 25:
+ me->AI()->Talk(PLANE_EMOTE);
+ me->AI()->DoCast(AURA_ENGINE);
+ break;
+ }
+ }
+
+ void SpellHit(Unit* /*caster*/, SpellInfo const* spell)
+ {
+ if (spell->Id == SPELL_LAND)
+ {
+ Unit* passenger = me->GetVehicleKit()->GetPassenger(1); // player should be on seat 1
+ if (passenger && passenger->GetTypeId() == TYPEID_PLAYER)
+ {
+ passenger->CastSpell(passenger, SPELL_CREDIT, true);
+ passenger->ExitVehicle();
+ }
+ }
+ }
+
+ void UpdateAI(const uint32 /*diff*/) {}
+ };
+
+ CreatureAI* GetAI(Creature* creature) const
+ {
+ return new npc_vics_flying_machineAI(creature);
+ }
+};
+
void AddSC_sholazar_basin()
{
new npc_injured_rainspeaker_oracle();
@@ -1045,4 +1141,5 @@ void AddSC_sholazar_basin()
new spell_q12620_the_lifewarden_wrath();
new spell_q12589_shoot_rjr();
new npc_haiphoon();
+ new npc_vics_flying_machine();
}
diff --git a/src/server/scripts/Northrend/zone_storm_peaks.cpp b/src/server/scripts/Northrend/zone_storm_peaks.cpp
index 85ab1dc1127..dae2ffd36f6 100644
--- a/src/server/scripts/Northrend/zone_storm_peaks.cpp
+++ b/src/server/scripts/Northrend/zone_storm_peaks.cpp
@@ -423,7 +423,7 @@ public:
Unit* player = me->GetVehicleKit()->GetPassenger(0);
if (player && player->GetTypeId() == TYPEID_PLAYER)
{
- // for each prisoner on drake,give credit
+ // for each prisoner on drake, give credit
for (uint8 i = 1; i < 4; ++i)
if (Unit* prisoner = me->GetVehicleKit()->GetPassenger(i))
{
diff --git a/src/server/scripts/Northrend/zone_wintergrasp.cpp b/src/server/scripts/Northrend/zone_wintergrasp.cpp
index 8935c77b30e..8c255d49d25 100644
--- a/src/server/scripts/Northrend/zone_wintergrasp.cpp
+++ b/src/server/scripts/Northrend/zone_wintergrasp.cpp
@@ -271,7 +271,7 @@ class npc_wg_queue : public CreatureScript
return true;
}
- bool OnGossipSelect(Player* player, Creature* /*creature*/ , uint32 /*sender*/ , uint32 /*action*/)
+ bool OnGossipSelect(Player* player, Creature* /*creature*/, uint32 /*sender*/, uint32 /*action*/)
{
player->CLOSE_GOSSIP_MENU();
diff --git a/src/server/scripts/Outland/BlackTemple/boss_mother_shahraz.cpp b/src/server/scripts/Outland/BlackTemple/boss_mother_shahraz.cpp
index 11569c30f1e..ec85d675225 100644
--- a/src/server/scripts/Outland/BlackTemple/boss_mother_shahraz.cpp
+++ b/src/server/scripts/Outland/BlackTemple/boss_mother_shahraz.cpp
@@ -45,7 +45,7 @@ enum MotherShahraz
SPELL_ATTRACTION = 40871,
SPELL_SILENCING_SHRIEK = 40823,
SPELL_ENRAGE = 23537,
- SPELL_SABER_LASH = 40810,//43267
+ SPELL_SABER_LASH = 40810, //43267
SPELL_SABER_LASH_IMM = 43690,
SPELL_TELEPORT_VISUAL = 40869,
SPELL_BERSERK = 45078
diff --git a/src/server/scripts/Outland/CoilfangReservoir/underbog/boss_hungarfen.cpp b/src/server/scripts/Outland/CoilfangReservoir/underbog/boss_hungarfen.cpp
index 9fa5a1a51f5..c55e93726a9 100644
--- a/src/server/scripts/Outland/CoilfangReservoir/underbog/boss_hungarfen.cpp
+++ b/src/server/scripts/Outland/CoilfangReservoir/underbog/boss_hungarfen.cpp
@@ -26,8 +26,11 @@ EndScriptData */
#include "ScriptMgr.h"
#include "ScriptedCreature.h"
-#define SPELL_FOUL_SPORES 31673
-#define SPELL_ACID_GEYSER 38739
+enum Spells
+{
+ SPELL_FOUL_SPORES = 31673,
+ SPELL_ACID_GEYSER = 38739
+};
class boss_hungarfen : public CreatureScript
{
diff --git a/src/server/scripts/Outland/HellfireCitadel/BloodFurnace/instance_blood_furnace.cpp b/src/server/scripts/Outland/HellfireCitadel/BloodFurnace/instance_blood_furnace.cpp
index 4eaf7cc2d6c..be51b7922a2 100644
--- a/src/server/scripts/Outland/HellfireCitadel/BloodFurnace/instance_blood_furnace.cpp
+++ b/src/server/scripts/Outland/HellfireCitadel/BloodFurnace/instance_blood_furnace.cpp
@@ -375,23 +375,23 @@ class instance_blood_furnace : public InstanceMapScript
switch (id)
{
case DATA_PRISON_CELL5:
- HandleGameObject(PrisonCell5GUID,true);
+ HandleGameObject(PrisonCell5GUID, true);
ActivatePrisoners(PrisonersCell5);
break;
case DATA_PRISON_CELL6:
- HandleGameObject(PrisonCell6GUID,true);
+ HandleGameObject(PrisonCell6GUID, true);
ActivatePrisoners(PrisonersCell6);
break;
case DATA_PRISON_CELL7:
- HandleGameObject(PrisonCell7GUID,true);
+ HandleGameObject(PrisonCell7GUID, true);
ActivatePrisoners(PrisonersCell7);
break;
case DATA_PRISON_CELL8:
- HandleGameObject(PrisonCell8GUID,true);
+ HandleGameObject(PrisonCell8GUID, true);
ActivatePrisoners(PrisonersCell8);
break;
case DATA_DOOR5:
- HandleGameObject(Door5GUID,true);
+ HandleGameObject(Door5GUID, true);
if (Creature* broggok = instance->GetCreature(BroggokGUID))
broggok->AI()->DoAction(ACTION_ACTIVATE_BROGGOK);
break;
diff --git a/src/server/scripts/Outland/TempestKeep/Eye/boss_astromancer.cpp b/src/server/scripts/Outland/TempestKeep/Eye/boss_astromancer.cpp
index 9c4b764fd2a..01ca1636c6b 100644
--- a/src/server/scripts/Outland/TempestKeep/Eye/boss_astromancer.cpp
+++ b/src/server/scripts/Outland/TempestKeep/Eye/boss_astromancer.cpp
@@ -518,7 +518,7 @@ class spell_astromancer_wrath_of_the_astromancer : public SpellScriptLoader
return;
Unit* target = GetUnitOwner();
- target->CastSpell(target, GetSpellInfo()->Effects[EFFECT_1].CalcValue(),false);
+ target->CastSpell(target, GetSpellInfo()->Effects[EFFECT_1].CalcValue(), false);
}
void Register()
diff --git a/src/server/scripts/Outland/TempestKeep/Mechanar/boss_gatewatcher_gyrokill.cpp b/src/server/scripts/Outland/TempestKeep/Mechanar/boss_gatewatcher_gyrokill.cpp
index 796308d68f6..dc9940c87b8 100644
--- a/src/server/scripts/Outland/TempestKeep/Mechanar/boss_gatewatcher_gyrokill.cpp
+++ b/src/server/scripts/Outland/TempestKeep/Mechanar/boss_gatewatcher_gyrokill.cpp
@@ -16,9 +16,9 @@
*/
/* ScriptData
-SDName: Boss_Gatewatcher_Gyrokill
-SD%Complete: 0
-SDComment: Place Holder
+SDName: boss_gatewatcher_gyrokill
+SD%Complete: 99%
+SDComment:
SDCategory: Tempest Keep, The Mechanar
EndScriptData */
@@ -50,37 +50,27 @@ enum Events
EVENT_SHADOW_POWER = 2
};
-class Boss_Gatewatcher_Gyrokill : public CreatureScript
+class boss_gatewatcher_gyrokill : public CreatureScript
{
- public: Boss_Gatewatcher_Gyrokill() : CreatureScript("Boss_Gatewatcher_Gyrokill") { }
+ public:
+ boss_gatewatcher_gyrokill() : CreatureScript("boss_gatewatcher_gyrokill") {}
- struct Boss_Gatewatcher_GyrokillAI : public BossAI
+ struct boss_gatewatcher_gyrokillAI : public BossAI
{
- Boss_Gatewatcher_GyrokillAI(Creature* creature) : BossAI(creature, DATA_GATEWATCHER_GYROKILL) {}
-
- void Reset()
- {
- if (instance)
- instance->SetData(DATA_GATEWATCHER_GYROKILL, NOT_STARTED);
- }
+ boss_gatewatcher_gyrokillAI(Creature* creature) : BossAI(creature, DATA_GATEWATCHER_GYROKILL) {}
void JustDied(Unit* /*killer*/)
{
- if (instance)
- instance->SetData(DATA_GATEWATCHER_GYROKILL, DONE);
-
+ _JustDied();
Talk(SAY_DEATH);
}
void EnterCombat(Unit* /*who*/)
{
- if (instance)
- instance->SetData(DATA_GATEWATCHER_GYROKILL, IN_PROGRESS);
-
+ _EnterCombat();
events.ScheduleEvent(EVENT_STREAM_OF_MACHINE_FLUID, 10000);
events.ScheduleEvent(EVENT_SAW_BLADE, 20000);
events.ScheduleEvent(EVENT_SHADOW_POWER, 25000);
-
Talk(SAY_AGGRO);
}
@@ -108,18 +98,12 @@ class Boss_Gatewatcher_Gyrokill : public CreatureScript
events.ScheduleEvent(EVENT_STREAM_OF_MACHINE_FLUID, urand(13000, 17000));
break;
case EVENT_SAW_BLADE:
- if (IsHeroic())
- DoCast(me, H_SPELL_SAW_BLADE);
- else
- DoCast(me, SPELL_SAW_BLADE);
+ DoCast(me, SPELL_SAW_BLADE);
Talk(SAY_SAW_BLADEs);
events.ScheduleEvent(EVENT_SAW_BLADE, urand(20000, 30000));
break;
case EVENT_SHADOW_POWER:
- if (IsHeroic())
- DoCast(me, H_SPELL_SHADOW_POWER);
- else
- DoCast(me, SPELL_SHADOW_POWER);
+ DoCast(me, SPELL_SHADOW_POWER);
events.ScheduleEvent(EVENT_SAW_BLADE, urand(25000, 35000));
break;
default:
@@ -133,11 +117,11 @@ class Boss_Gatewatcher_Gyrokill : public CreatureScript
CreatureAI* GetAI(Creature* creature) const
{
- return new Boss_Gatewatcher_GyrokillAI (creature);
+ return new boss_gatewatcher_gyrokillAI (creature);
}
};
-void AddSC_Boss_Gatewatcher_Gyrokill()
+void AddSC_boss_gatewatcher_gyrokill()
{
- new Boss_Gatewatcher_Gyrokill();
-} \ No newline at end of file
+ new boss_gatewatcher_gyrokill();
+}
diff --git a/src/server/scripts/Outland/TempestKeep/Mechanar/boss_gatewatcher_ironhand.cpp b/src/server/scripts/Outland/TempestKeep/Mechanar/boss_gatewatcher_ironhand.cpp
index 09ff6cf8e49..440e17a29cf 100644
--- a/src/server/scripts/Outland/TempestKeep/Mechanar/boss_gatewatcher_ironhand.cpp
+++ b/src/server/scripts/Outland/TempestKeep/Mechanar/boss_gatewatcher_ironhand.cpp
@@ -25,8 +25,9 @@ EndScriptData */
#include "ScriptMgr.h"
#include "ScriptedCreature.h"
+#include "mechanar.h"
-enum eSays
+enum Says
{
SAY_AGGRO = 0,
SAY_HAMMER = 1,
@@ -35,111 +36,96 @@ enum eSays
EMOTE_HAMMER = 4
};
-enum eSpells
+enum Spells
{
- // Spells to be casted
SPELL_SHADOW_POWER = 35322,
H_SPELL_SHADOW_POWER = 39193,
SPELL_HAMMER_PUNCH = 35326,
SPELL_JACKHAMMER = 35327,
H_SPELL_JACKHAMMER = 39194,
- SPELL_STREAM_OF_MACHINE_FLUID = 35311,
+ SPELL_STREAM_OF_MACHINE_FLUID = 35311
+};
+
+enum Events
+{
+ EVENT_STREAM_OF_MACHINE_FLUID = 0,
+ EVENT_JACKHAMMER = 1,
+ EVENT_SHADOW_POWER = 2
};
class boss_gatewatcher_iron_hand : public CreatureScript
{
public:
+ boss_gatewatcher_iron_hand(): CreatureScript("boss_gatewatcher_iron_hand") {}
- boss_gatewatcher_iron_hand()
- : CreatureScript("boss_gatewatcher_iron_hand")
+ struct boss_gatewatcher_iron_handAI : public BossAI
{
- }
- // Gatewatcher Iron-Hand AI
- struct boss_gatewatcher_iron_handAI : public ScriptedAI
- {
- boss_gatewatcher_iron_handAI(Creature* creature) : ScriptedAI(creature)
- {
- }
-
- uint32 Shadow_Power_Timer;
- uint32 Jackhammer_Timer;
- uint32 Stream_of_Machine_Fluid_Timer;
-
- void Reset()
- {
- Shadow_Power_Timer = 25000;
- Jackhammer_Timer = 45000;
- Stream_of_Machine_Fluid_Timer = 55000;
+ boss_gatewatcher_iron_handAI(Creature* creature) : BossAI(creature, DATA_GATEWATCHER_IRON_HAND) {}
- }
- void EnterCombat(Unit* /*who*/)
- {
- Talk(SAY_AGGRO);
- }
-
- void KilledUnit(Unit* /*victim*/)
- {
- if (rand()%2)
- return;
+ void EnterCombat(Unit* /*who*/)
+ {
+ _EnterCombat();
+ events.ScheduleEvent(EVENT_STREAM_OF_MACHINE_FLUID, 55000);
+ events.ScheduleEvent(EVENT_JACKHAMMER, 45000);
+ events.ScheduleEvent(EVENT_SHADOW_POWER, 25000);
+ Talk(SAY_AGGRO);
+ }
+ void KilledUnit(Unit* /*victim*/)
+ {
+ if (roll_chance_i(50))
Talk(SAY_SLAY);
- }
-
- void JustDied(Unit* /*killer*/)
- {
- Talk(SAY_DEATH);
- //TODO: Add door check/open code
- }
-
- void UpdateAI(const uint32 diff)
- {
- //Return since we have no target
- if (!UpdateVictim())
- return;
+ }
- //Shadow Power
- if (Shadow_Power_Timer <= diff)
- {
- DoCast(me, SPELL_SHADOW_POWER);
- Shadow_Power_Timer = urand(20000, 28000);
- }
- else
- Shadow_Power_Timer -= diff;
+ void JustDied(Unit* /*killer*/)
+ {
+ _JustDied();
+ Talk(SAY_DEATH);
+ }
- //Jack Hammer
- if (Jackhammer_Timer <= diff)
- {
- //TODO: expect cast this about 5 times in a row (?), announce it by emote only once
- Talk(EMOTE_HAMMER);
- DoCast(me->getVictim(), SPELL_JACKHAMMER);
+ void UpdateAI(uint32 const diff)
+ {
+ if (!UpdateVictim())
+ return;
- //chance to yell, but not same time as emote (after spell in fact casted)
- if (rand()%2)
- return;
+ events.Update(diff);
- Talk(SAY_HAMMER);
- Jackhammer_Timer = 30000;
- }
- else
- Jackhammer_Timer -= diff;
+ if (me->HasUnitState(UNIT_STATE_CASTING))
+ return;
- //Stream of Machine Fluid
- if (Stream_of_Machine_Fluid_Timer <= diff)
+ while (uint32 eventId = events.ExecuteEvent())
+ {
+ switch (eventId)
{
- DoCast(me->getVictim(), SPELL_STREAM_OF_MACHINE_FLUID);
- Stream_of_Machine_Fluid_Timer = urand(35000, 50000);
+ case EVENT_STREAM_OF_MACHINE_FLUID:
+ DoCastVictim(SPELL_STREAM_OF_MACHINE_FLUID, true);
+ events.ScheduleEvent(EVENT_STREAM_OF_MACHINE_FLUID, urand(35000, 50000));
+ break;
+ case EVENT_JACKHAMMER:
+ Talk(EMOTE_HAMMER);
+ //TODO: expect cast this about 5 times in a row (?), announce it by emote only once
+ DoCastVictim(SPELL_JACKHAMMER, true);
+ if (roll_chance_i(50))
+ Talk(SAY_HAMMER);
+ events.ScheduleEvent(EVENT_JACKHAMMER, 30000);
+ break;
+ case EVENT_SHADOW_POWER:
+ DoCast(me, SPELL_SHADOW_POWER);
+ events.ScheduleEvent(EVENT_SHADOW_POWER, urand(20000, 28000));
+ break;
+ default:
+ break;
}
- else
- Stream_of_Machine_Fluid_Timer -= diff;
-
- DoMeleeAttackIfReady();
}
- };
- CreatureAI* GetAI(Creature* creature) const
- {
- return new boss_gatewatcher_iron_handAI(creature);
+ DoMeleeAttackIfReady();
}
+ };
+
+ CreatureAI* GetAI(Creature* creature) const
+ {
+ return new boss_gatewatcher_iron_handAI(creature);
+ }
};
void AddSC_boss_gatewatcher_iron_hand()
diff --git a/src/server/scripts/Outland/TempestKeep/Mechanar/boss_mechano_lord_capacitus.cpp b/src/server/scripts/Outland/TempestKeep/Mechanar/boss_mechano_lord_capacitus.cpp
index f8c44471b30..1a43798ccef 100644
--- a/src/server/scripts/Outland/TempestKeep/Mechanar/boss_mechano_lord_capacitus.cpp
+++ b/src/server/scripts/Outland/TempestKeep/Mechanar/boss_mechano_lord_capacitus.cpp
@@ -15,21 +15,144 @@
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-//! TODO - Boss not scripted, just ported required spellscript from core
-
#include "ScriptMgr.h"
#include "ScriptedCreature.h"
#include "SpellScript.h"
+#include "mechanar.h"
#include "Player.h"
enum Spells
{
+ SPELL_HEADCRACK = 35161,
+ SPELL_REFLECTIVE_MAGIC_SHIELD = 35158,
+ SPELL_REFLECTIVE_DAMAGE_SHIELD = 35159,
+ SPELL_POLARITY_SHIFT = 39096,
+ SPELL_BERSERK = 26662,
+ SPELL_NETHER_CHARGE_TIMER = 37670,
+ SPELL_NETHER_CHARGE_PASSIVE = 37670,
+
SPELL_POSITIVE_POLARITY = 39088,
SPELL_POSITIVE_CHARGE_STACK = 39089,
SPELL_POSITIVE_CHARGE = 39090,
+
SPELL_NEGATIVE_POLARITY = 39091,
SPELL_NEGATIVE_CHARGE_STACK = 39092,
- SPELL_NEGATIVE_CHARGE = 39093,
+ SPELL_NEGATIVE_CHARGE = 39093
+};
+
+enum Yells
+{
+ YELL_AGGRO = 0,
+ YELL_REFLECTIVE_MAGIC_SHIELD = 1,
+ YELL_REFLECTIVE_DAMAGE_SHIELD = 2,
+ YELL_KILL = 3,
+ YELL_DEATH = 4
+};
+
+enum Creatures
+{
+ NPC_NETHER_CHARGE = 20405
+};
+
+enum Events
+{
+ EVENT_NONE = 0,
+
+ EVENT_HEADCRACK = 1,
+ EVENT_REFLECTIVE_DAMAGE_SHIELD = 2,
+ EVENT_REFLECTIVE_MAGIE_SHIELD = 3,
+ EVENT_POSITIVE_SHIFT = 4,
+ EVENT_SUMMON_NETHER_CHARGE = 5,
+ EVENT_BERSERK = 6
+};
+
+class boss_mechano_lord_capacitus : public CreatureScript
+{
+ public:
+ boss_mechano_lord_capacitus() : CreatureScript("boss_mechano_lord_capacitus") { }
+
+ struct boss_mechano_lord_capacitusAI : public BossAI
+ {
+ boss_mechano_lord_capacitusAI(Creature* creature) : BossAI(creature, DATA_MECHANOLORD_CAPACITUS) { }
+
+ void EnterCombat(Unit* /*who*/)
+ {
+ _EnterCombat();
+ Talk(YELL_AGGRO);
+ events.ScheduleEvent(EVENT_HEADCRACK, 10 * IN_MILLISECONDS);
+ events.ScheduleEvent(EVENT_REFLECTIVE_DAMAGE_SHIELD, 15 * IN_MILLISECONDS);
+ events.ScheduleEvent(EVENT_SUMMON_NETHER_CHARGE, 10 * IN_MILLISECONDS);
+ events.ScheduleEvent(EVENT_BERSERK, 3 * MINUTE * IN_MILLISECONDS);
+
+ if (IsHeroic())
+ events.ScheduleEvent(EVENT_POSITIVE_SHIFT, 15 * IN_MILLISECONDS);
+ }
+
+ void KilledUnit(Unit* /*victim*/)
+ {
+ Talk(YELL_KILL);
+ }
+
+ void JustDied(Unit* /*victim*/)
+ {
+ _JustDied();
+ Talk(YELL_DEATH);
+ }
+
+ void UpdateAI(uint32 const diff)
+ {
+ if (!UpdateVictim())
+ return;
+
+ events.Update(diff);
+
+ if (me->HasUnitState(UNIT_STATE_CASTING))
+ return;
+
+ while (uint32 eventId = events.ExecuteEvent())
+ {
+ switch (eventId)
+ {
+ case EVENT_HEADCRACK:
+ DoCastVictim(SPELL_HEADCRACK);
+ events.ScheduleEvent(EVENT_HEADCRACK, 10 * IN_MILLISECONDS);
+ break;
+ case EVENT_REFLECTIVE_DAMAGE_SHIELD:
+ Talk(YELL_REFLECTIVE_DAMAGE_SHIELD);
+ DoCast(me, SPELL_REFLECTIVE_DAMAGE_SHIELD);
+ events.ScheduleEvent(EVENT_REFLECTIVE_MAGIE_SHIELD, 30 * IN_MILLISECONDS);
+ break;
+ case EVENT_REFLECTIVE_MAGIE_SHIELD:
+ Talk(YELL_REFLECTIVE_MAGIC_SHIELD);
+ DoCast(me, SPELL_REFLECTIVE_MAGIC_SHIELD);
+ events.ScheduleEvent(EVENT_REFLECTIVE_DAMAGE_SHIELD, 30 * IN_MILLISECONDS);
+ break;
+ case EVENT_POSITIVE_SHIFT:
+ DoCastAOE(SPELL_POLARITY_SHIFT);
+ events.ScheduleEvent(EVENT_POSITIVE_SHIFT, urand(45, 60) * IN_MILLISECONDS);
+ break;
+ case EVENT_SUMMON_NETHER_CHARGE:
+ Position pos;
+ me->GetRandomNearPosition(pos, 5.0f);
+ me->SummonCreature(NPC_NETHER_CHARGE, pos, TEMPSUMMON_TIMED_DESPAWN, 18000);
+ events.ScheduleEvent(EVENT_SUMMON_NETHER_CHARGE, 10 * IN_MILLISECONDS);
+ break;
+ case EVENT_BERSERK:
+ DoCast(me, SPELL_BERSERK);
+ break;
+ default:
+ break;
+ }
+ }
+
+ DoMeleeAttackIfReady();
+ }
+ };
+
+ CreatureAI* GetAI(Creature* creature) const
+ {
+ return new boss_mechano_lord_capacitusAI(creature);
+ }
};
class spell_capacitus_polarity_charge : public SpellScriptLoader
@@ -138,6 +261,7 @@ class spell_capacitus_polarity_shift : public SpellScriptLoader
void AddSC_boss_mechano_lord_capacitus()
{
+ new boss_mechano_lord_capacitus();
new spell_capacitus_polarity_charge();
new spell_capacitus_polarity_shift();
}
diff --git a/src/server/scripts/Outland/TempestKeep/Mechanar/boss_nethermancer_sepethrea.cpp b/src/server/scripts/Outland/TempestKeep/Mechanar/boss_nethermancer_sepethrea.cpp
index cdfb20b5f0d..9bda618b732 100644
--- a/src/server/scripts/Outland/TempestKeep/Mechanar/boss_nethermancer_sepethrea.cpp
+++ b/src/server/scripts/Outland/TempestKeep/Mechanar/boss_nethermancer_sepethrea.cpp
@@ -65,30 +65,16 @@ class boss_nethermancer_sepethrea : public CreatureScript
struct boss_nethermancer_sepethreaAI : public BossAI
{
- boss_nethermancer_sepethreaAI(Creature* creature) : BossAI(creature,DATA_NETHERMANCER_SEPRETHREA)
- {
- instance = creature->GetInstanceScript();
- }
-
- InstanceScript* instance;
-
- void Reset()
- {
- if (instance)
- instance->SetData(DATA_NETHERMANCER_SEPRETHREA, NOT_STARTED);
- }
+ boss_nethermancer_sepethreaAI(Creature* creature) : BossAI(creature, DATA_NETHERMANCER_SEPRETHREA) {}
void EnterCombat(Unit* who)
{
- if (instance)
- instance->SetData(DATA_NETHERMANCER_SEPRETHREA, IN_PROGRESS);
-
+ _EnterCombat();
events.ScheduleEvent(EVENT_FROST_ATTACK, urand(7000, 10000));
events.ScheduleEvent(EVENT_ARCANE_BLAST, urand(12000, 18000));
events.ScheduleEvent(EVENT_DRAGONS_BREATH, urand(18000, 22000));
events.ScheduleEvent(EVENT_KNOCKBACK, urand(22000, 28000));
events.ScheduleEvent(EVENT_SOLARBURN, 30000);
-
Talk(SAY_AGGRO);
DoCast(who, SPELL_SUMMON_RAGIN_FLAMES);
Talk(SAY_SUMMON);
@@ -101,9 +87,8 @@ class boss_nethermancer_sepethrea : public CreatureScript
void JustDied(Unit* /*killer*/)
{
+ _JustDied();
Talk(SAY_DEATH);
- if (instance)
- instance->SetData(DATA_NETHERMANCER_SEPRETHREA, DONE);
}
void UpdateAI(uint32 const diff)
@@ -160,10 +145,7 @@ class boss_nethermancer_sepethrea : public CreatureScript
class mob_ragin_flames : public CreatureScript
{
public:
- mob_ragin_flames()
- : CreatureScript("mob_ragin_flames")
- {
- }
+ mob_ragin_flames() : CreatureScript("mob_ragin_flames") { }
struct mob_ragin_flamesAI : public ScriptedAI
{
@@ -250,4 +232,3 @@ void AddSC_boss_nethermancer_sepethrea()
new boss_nethermancer_sepethrea();
new mob_ragin_flames();
}
-
diff --git a/src/server/scripts/Outland/TempestKeep/Mechanar/boss_pathaleon_the_calculator.cpp b/src/server/scripts/Outland/TempestKeep/Mechanar/boss_pathaleon_the_calculator.cpp
index b7857239157..9d894e41b60 100644
--- a/src/server/scripts/Outland/TempestKeep/Mechanar/boss_pathaleon_the_calculator.cpp
+++ b/src/server/scripts/Outland/TempestKeep/Mechanar/boss_pathaleon_the_calculator.cpp
@@ -61,40 +61,28 @@ enum Events
EVENT_ARCANE_EXPLOSION = 4
};
+enum Creatures
+{
+ NPC_NETHER_WRAITH = 21062
+};
+
class boss_pathaleon_the_calculator : public CreatureScript
{
- public: boss_pathaleon_the_calculator(): CreatureScript("boss_pathaleon_the_calculator") {}
+ public:
+ boss_pathaleon_the_calculator(): CreatureScript("boss_pathaleon_the_calculator") {}
struct boss_pathaleon_the_calculatorAI : public BossAI
{
- boss_pathaleon_the_calculatorAI(Creature* creature) : BossAI(creature,DATA_PATHALEON_THE_CALCULATOR), summons(me) {}
-
- SummonList summons;
- bool Enraged;
- uint32 Counter;
-
- void Reset()
- {
- if (instance)
- instance->SetData(DATA_PATHALEON_THE_CALCULATOR, NOT_STARTED);
-
- Enraged = false;
- Counter = 0;
- summons.DespawnAll();
- }
+ boss_pathaleon_the_calculatorAI(Creature* creature) : BossAI(creature, DATA_PATHALEON_THE_CALCULATOR) { }
void EnterCombat(Unit* /*who*/)
{
- if (instance)
- instance->SetData(DATA_PATHALEON_THE_CALCULATOR, IN_PROGRESS);
-
+ _EnterCombat();
events.ScheduleEvent(EVENT_SUMMON, 30000);
events.ScheduleEvent(EVENT_MANA_TAP, urand(12000, 20000));
events.ScheduleEvent(EVENT_ARCANE_TORRENT, urand(16000, 25000));
events.ScheduleEvent(EVENT_DOMINATION, urand(25000, 40000));
- if (IsHeroic())
- events.ScheduleEvent(EVENT_ARCANE_EXPLOSION, urand(8000, 13000));
-
+ events.ScheduleEvent(EVENT_ARCANE_EXPLOSION, urand(8000, 13000));
Talk(SAY_AGGRO);
}
@@ -105,22 +93,17 @@ class boss_pathaleon_the_calculator : public CreatureScript
void JustDied(Unit* /*killer*/)
{
+ _JustDied();
Talk(SAY_DEATH);
-
- summons.DespawnAll();
-
- if (instance)
- instance->SetData(DATA_PATHALEON_THE_CALCULATOR, DONE);
- }
-
- void JustSummoned(Creature* summon)
- {
- summons.Summon(summon);
}
- void SummonedCreatureDespawn(Creature* summon)
+ void DamageTaken(Unit* /*attacker*/, uint32& damage)
{
- summons.Despawn(summon);
+ if (me->HealthBelowPctDamaged(20, damage) && !me->HasAura(SPELL_FRENZY))
+ {
+ DoCast(me, SPELL_FRENZY);
+ Talk(SAY_ENRAGE);
+ }
}
void UpdateAI(uint32 const diff)
@@ -130,13 +113,6 @@ class boss_pathaleon_the_calculator : public CreatureScript
events.Update(diff);
- if (!Enraged && HealthBelowPct(21))
- {
- DoCast(me, SPELL_FRENZY);
- Talk(SAY_ENRAGE);
- Enraged = true;
- }
-
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
@@ -147,10 +123,11 @@ class boss_pathaleon_the_calculator : public CreatureScript
case EVENT_SUMMON:
for (uint8 i = 0; i < 3; ++i)
{
- Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0);
- Creature* Wraith = me->SummonCreature(21062, me->GetPositionX(), me->GetPositionY(), me->GetPositionZ(), 0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 25000);
- if (target && Wraith)
- Wraith->AI()->AttackStart(target);
+ if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0))
+ {
+ if (Creature* Wraith = me->SummonCreature(NPC_NETHER_WRAITH, me->GetPositionX(), me->GetPositionY(), me->GetPositionZ(), 0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 25000))
+ Wraith->AI()->AttackStart(target);
+ }
}
Talk(SAY_SUMMON);
events.ScheduleEvent(EVENT_SUMMON, urand(30000, 45000));
@@ -181,20 +158,17 @@ class boss_pathaleon_the_calculator : public CreatureScript
}
};
- CreatureAI* GetAI(Creature* creature) const
- {
- return new boss_pathaleon_the_calculatorAI (creature);
- }
+ CreatureAI* GetAI(Creature* creature) const
+ {
+ return new boss_pathaleon_the_calculatorAI (creature);
+ }
};
class mob_nether_wraith : public CreatureScript
{
public:
- mob_nether_wraith()
- : CreatureScript("mob_nether_wraith")
- {
- }
+ mob_nether_wraith() : CreatureScript("mob_nether_wraith") { }
struct mob_nether_wraithAI : public ScriptedAI
{
diff --git a/src/server/scripts/Outland/TempestKeep/Mechanar/instance_mechanar.cpp b/src/server/scripts/Outland/TempestKeep/Mechanar/instance_mechanar.cpp
index 21fb085b266..146569db284 100644
--- a/src/server/scripts/Outland/TempestKeep/Mechanar/instance_mechanar.cpp
+++ b/src/server/scripts/Outland/TempestKeep/Mechanar/instance_mechanar.cpp
@@ -16,70 +16,122 @@
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-/* ScriptData
-SDName: Instance_Mechanar
-SD%Complete: 100
-SDComment:
-SDCategory: Mechanar
-EndScriptData */
#include "ScriptMgr.h"
#include "InstanceScript.h"
#include "mechanar.h"
+static DoorData const doorData[] =
+{
+ { GO_DOOR_MOARG_1, DATA_GATEWATCHER_IRON_HAND, DOOR_TYPE_PASSAGE, BOUNDARY_NONE },
+ { GO_DOOR_MOARG_2, DATA_GATEWATCHER_GYROKILL, DOOR_TYPE_PASSAGE, BOUNDARY_NONE },
+ { GO_DOOR_NETHERMANCER, DATA_NETHERMANCER_SEPRETHREA, DOOR_TYPE_ROOM, BOUNDARY_NONE },
+ {0, 0, DOOR_TYPE_ROOM, BOUNDARY_NONE }
+};
+
class instance_mechanar : public InstanceMapScript
{
- public: instance_mechanar(): InstanceMapScript("instance_mechanar", 554) {}
+ public:
+ instance_mechanar(): InstanceMapScript("instance_mechanar", 554) { }
struct instance_mechanar_InstanceMapScript : public InstanceScript
{
- instance_mechanar_InstanceMapScript(Map* map) : InstanceScript(map) {}
+ instance_mechanar_InstanceMapScript(Map* map) : InstanceScript(map)
+ {
+ SetBossNumber(EncounterCount);
+ LoadDoorData(doorData);
+ }
- uint32 m_auiEncounter[MAX_ENCOUNTER];
- void Initialize()
+ void OnGameObjectCreate(GameObject* gameObject)
{
- // memset(&m_auiEncounter, 0, sizeof(m_auiEncounter));
+ switch (gameObject->GetEntry())
+ {
+ case GO_DOOR_MOARG_1:
+ case GO_DOOR_MOARG_2:
+ case GO_DOOR_NETHERMANCER:
+ AddDoor(gameObject, true);
+ break;
+ default:
+ break;
+ }
}
- bool IsEncounterInProgress() const
+ void OnGameObjectRemove(GameObject* gameObject)
{
- for (uint8 i = 0; i < MAX_ENCOUNTER; ++i)
- if (m_auiEncounter[i] == IN_PROGRESS)
- return true;
-
- return false;
+ switch (gameObject->GetEntry())
+ {
+ case GO_DOOR_MOARG_1:
+ case GO_DOOR_MOARG_2:
+ case GO_DOOR_NETHERMANCER:
+ AddDoor(gameObject, false);
+ break;
+ default:
+ break;
+ }
}
- uint32 GetData(uint32 type) const
+ bool SetBossState(uint32 type, EncounterState state)
{
+ if (!InstanceScript::SetBossState(type, state))
+ return false;
+
switch (type)
{
- case DATA_GATEWATCHER_GYROKILL: return m_auiEncounter[DATA_GATEWATCHER_GYROKILL];
- case DATA_IRON_HAND: return m_auiEncounter[DATA_IRON_HAND];
- case DATA_MECHANOLORD_CAPACITUS: return m_auiEncounter[DATA_MECHANOLORD_CAPACITUS];
- case DATA_NETHERMANCER_SEPRETHREA: return m_auiEncounter[DATA_NETHERMANCER_SEPRETHREA];
- case DATA_PATHALEON_THE_CALCULATOR: return m_auiEncounter[DATA_PATHALEON_THE_CALCULATOR];
+ case DATA_GATEWATCHER_GYROKILL:
+ case DATA_GATEWATCHER_IRON_HAND:
+ case DATA_MECHANOLORD_CAPACITUS:
+ case DATA_NETHERMANCER_SEPRETHREA:
+ case DATA_PATHALEON_THE_CALCULATOR:
+ break;
+ default:
+ break;
}
- return false;
+ return true;
}
- uint64 GetData64(uint32 /*identifier*/) const
+ std::string GetSaveData()
{
- return 0;
+ OUT_SAVE_INST_DATA;
+
+ std::ostringstream saveStream;
+ saveStream << "M C " << GetBossSaveData();
+
+ OUT_SAVE_INST_DATA_COMPLETE;
+ return saveStream.str();
}
- void SetData(uint32 type, uint32 data)
+ void Load(const char* str)
{
- switch (type)
+ if (!str)
{
- case DATA_GATEWATCHER_GYROKILL: m_auiEncounter[DATA_GATEWATCHER_GYROKILL] = data; break;
- case DATA_IRON_HAND: m_auiEncounter[DATA_IRON_HAND] = data; break;
- case DATA_MECHANOLORD_CAPACITUS: m_auiEncounter[DATA_MECHANOLORD_CAPACITUS] = data; break;
- case DATA_NETHERMANCER_SEPRETHREA: m_auiEncounter[DATA_NETHERMANCER_SEPRETHREA] = data; break;
- case DATA_PATHALEON_THE_CALCULATOR: m_auiEncounter[DATA_PATHALEON_THE_CALCULATOR] = data; break;
+ 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 == 'C')
+ {
+ for (uint32 i = 0; i < EncounterCount; ++i)
+ {
+ uint32 tmpState;
+ loadStream >> tmpState;
+ if (tmpState == IN_PROGRESS || tmpState > SPECIAL)
+ tmpState = NOT_STARTED;
+ SetBossState(i, EncounterState(tmpState));
+ }
+ }
+ else
+ OUT_LOAD_INST_DATA_FAIL;
+
+ OUT_LOAD_INST_DATA_COMPLETE;
}
};
@@ -91,6 +143,5 @@ class instance_mechanar : public InstanceMapScript
void AddSC_instance_mechanar()
{
- new instance_mechanar;
+ new instance_mechanar();
}
-
diff --git a/src/server/scripts/Outland/TempestKeep/Mechanar/mechanar.h b/src/server/scripts/Outland/TempestKeep/Mechanar/mechanar.h
index 7aa4cca7f8e..0d3a4ea241d 100644
--- a/src/server/scripts/Outland/TempestKeep/Mechanar/mechanar.h
+++ b/src/server/scripts/Outland/TempestKeep/Mechanar/mechanar.h
@@ -18,18 +18,22 @@
#ifndef DEF_MECHANAR_H
#define DEF_MECHANAR_H
+uint32 const EncounterCount = 5;
+
enum DataTypes
{
DATA_GATEWATCHER_GYROKILL = 0,
- DATA_IRON_HAND = 1,
+ DATA_GATEWATCHER_IRON_HAND = 1,
DATA_MECHANOLORD_CAPACITUS = 2,
DATA_NETHERMANCER_SEPRETHREA = 3,
DATA_PATHALEON_THE_CALCULATOR = 4
};
-enum Misc
+enum GameobjectIds
{
- MAX_ENCOUNTER = 5
+ GO_DOOR_MOARG_1 = 184632,
+ GO_DOOR_MOARG_2 = 184322,
+ GO_DOOR_NETHERMANCER = 184449
};
#endif
diff --git a/src/server/scripts/Outland/zone_nagrand.cpp b/src/server/scripts/Outland/zone_nagrand.cpp
index 0a92b985c95..edb7a2642d5 100644
--- a/src/server/scripts/Outland/zone_nagrand.cpp
+++ b/src/server/scripts/Outland/zone_nagrand.cpp
@@ -639,7 +639,7 @@ public:
if (ChainLightningTimer <= diff)
{
DoCast(me->getVictim(), SPELL_KUR_CHAIN_LIGHTNING);
- ChainLightningTimer = urand(7000,14000);
+ ChainLightningTimer = urand(7000, 14000);
} else ChainLightningTimer -= diff;
if (HealthBelowPct(30))
@@ -654,7 +654,7 @@ public:
if (FrostShockTimer <= diff)
{
DoCast(me->getVictim(), SPELL_KUR_FROST_SHOCK);
- FrostShockTimer = urand(7500,15000);
+ FrostShockTimer = urand(7500, 15000);
} else FrostShockTimer -= diff;
DoMeleeAttackIfReady();
diff --git a/src/server/scripts/Spells/spell_dk.cpp b/src/server/scripts/Spells/spell_dk.cpp
index afb9a625761..62a0599dab1 100644
--- a/src/server/scripts/Spells/spell_dk.cpp
+++ b/src/server/scripts/Spells/spell_dk.cpp
@@ -37,6 +37,7 @@ enum DeathKnightSpells
SPELL_DK_DEATH_COIL_HEAL = 47633,
SPELL_DK_DEATH_STRIKE_HEAL = 45470,
SPELL_DK_GHOUL_EXPLODE = 47496,
+ SPELL_DK_GLYPH_OF_ICEBOUND_FORTITUDE = 58625,
SPELL_DK_RUNIC_POWER_ENERGIZE = 49088,
SPELL_DK_SCOURGE_STRIKE_TRIGGERED = 70890,
SPELL_DK_WILL_OF_THE_NECROPOLIS_TALENT_R1 = 49189,
@@ -172,7 +173,7 @@ class spell_dk_anti_magic_zone : public SpellScriptLoader
return true;
}
- bool Validate(SpellInfo const* /*spellEntry*/)
+ bool Validate(SpellInfo const* /*spellInfo*/)
{
if (!sSpellMgr->GetSpellInfo(SPELL_DK_ANTI_MAGIC_SHELL_TALENT))
return false;
@@ -584,6 +585,55 @@ class spell_dk_ghoul_explode : public SpellScriptLoader
}
};
+// 48792 - Icebound Fortitude
+class spell_dk_icebound_fortitude : public SpellScriptLoader
+{
+ public:
+ spell_dk_icebound_fortitude() : SpellScriptLoader("spell_dk_icebound_fortitude") { }
+
+ class spell_dk_icebound_fortitude_AuraScript : public AuraScript
+ {
+ PrepareAuraScript(spell_dk_icebound_fortitude_AuraScript);
+
+ bool Load()
+ {
+ Unit* caster = GetCaster();
+ return caster && caster->GetTypeId() == TYPEID_PLAYER;
+ }
+
+ void CalculateAmount(AuraEffect const* /*aurEff*/, int32& amount, bool& /*canBeRecalculated*/)
+ {
+ if (Unit* caster = GetCaster())
+ {
+ int32 value = amount;
+ uint32 defValue = uint32(caster->ToPlayer()->GetSkillValue(SKILL_DEFENSE) + caster->ToPlayer()->GetRatingBonusValue(CR_DEFENSE_SKILL));
+
+ if (defValue > 400)
+ value -= int32((defValue - 400) * 0.15);
+
+ // Glyph of Icebound Fortitude
+ if (AuraEffect const* aurEff = caster->GetAuraEffect(SPELL_DK_GLYPH_OF_ICEBOUND_FORTITUDE, EFFECT_0))
+ {
+ int32 valMax = -aurEff->GetAmount();
+ if (value > valMax)
+ value = valMax;
+ }
+ amount = value;
+ }
+ }
+
+ void Register()
+ {
+ DoEffectCalcAmount += AuraEffectCalcAmountFn(spell_dk_icebound_fortitude_AuraScript::CalculateAmount, EFFECT_2, SPELL_AURA_MOD_DAMAGE_PERCENT_TAKEN);
+ }
+ };
+
+ AuraScript* GetAuraScript() const
+ {
+ return new spell_dk_icebound_fortitude_AuraScript();
+ }
+};
+
// 50365, 50371 - Improved Blood Presence
class spell_dk_improved_blood_presence : public SpellScriptLoader
{
@@ -677,6 +727,33 @@ class spell_dk_improved_unholy_presence : public SpellScriptLoader
}
};
+// 59754 Rune Tap - Party
+class spell_dk_rune_tap_party : public SpellScriptLoader
+{
+ public:
+ spell_dk_rune_tap_party() : SpellScriptLoader("spell_dk_rune_tap_party") { }
+
+ class spell_dk_rune_tap_party_SpellScript : public SpellScript
+ {
+ PrepareSpellScript(spell_dk_rune_tap_party_SpellScript);
+
+ void CheckTargets(std::list<WorldObject*>& targets)
+ {
+ targets.remove(GetCaster());
+ }
+
+ void Register()
+ {
+ OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_dk_rune_tap_party_SpellScript::CheckTargets, EFFECT_0, TARGET_UNIT_CASTER_AREA_PARTY);
+ }
+ };
+
+ SpellScript* GetSpellScript() const
+ {
+ return new spell_dk_rune_tap_party_SpellScript();
+ }
+};
+
// 55090 - Scourge Strike (55265, 55270, 55271)
class spell_dk_scourge_strike : public SpellScriptLoader
{
@@ -784,6 +861,33 @@ class spell_dk_spell_deflection : public SpellScriptLoader
}
};
+// 55233 - Vampiric Blood
+class spell_dk_vampiric_blood : public SpellScriptLoader
+{
+ public:
+ spell_dk_vampiric_blood() : SpellScriptLoader("spell_dk_vampiric_blood") { }
+
+ class spell_dk_vampiric_blood_AuraScript : public AuraScript
+ {
+ PrepareAuraScript(spell_dk_vampiric_blood_AuraScript);
+
+ void CalculateAmount(AuraEffect const* /*aurEff*/, int32& amount, bool& /*canBeRecalculated*/)
+ {
+ amount = GetUnitOwner()->CountPctFromMaxHealth(amount);
+ }
+
+ void Register()
+ {
+ DoEffectCalcAmount += AuraEffectCalcAmountFn(spell_dk_vampiric_blood_AuraScript::CalculateAmount, EFFECT_1, SPELL_AURA_MOD_INCREASE_HEALTH);
+ }
+ };
+
+ AuraScript* GetAuraScript() const
+ {
+ return new spell_dk_vampiric_blood_AuraScript();
+ }
+};
+
// 52284 - Will of the Necropolis
class spell_dk_will_of_the_necropolis : public SpellScriptLoader
{
@@ -861,9 +965,12 @@ void AddSC_deathknight_spell_scripts()
new spell_dk_death_pact();
new spell_dk_death_strike();
new spell_dk_ghoul_explode();
+ new spell_dk_icebound_fortitude();
new spell_dk_improved_blood_presence();
new spell_dk_improved_unholy_presence();
+ new spell_dk_rune_tap_party();
new spell_dk_scourge_strike();
new spell_dk_spell_deflection();
+ new spell_dk_vampiric_blood();
new spell_dk_will_of_the_necropolis();
}
diff --git a/src/server/scripts/Spells/spell_druid.cpp b/src/server/scripts/Spells/spell_druid.cpp
index 6dd453597de..e2b918e1c7f 100644
--- a/src/server/scripts/Spells/spell_druid.cpp
+++ b/src/server/scripts/Spells/spell_druid.cpp
@@ -30,6 +30,9 @@
enum DruidSpells
{
SPELL_DRUID_ENRAGE_MOD_DAMAGE = 51185,
+ SPELL_DRUID_GLYPH_OF_TYPHOON = 62135,
+ SPELL_DRUID_IDOL_OF_FERAL_SHADOWS = 34241,
+ SPELL_DRUID_IDOL_OF_WORSHIP = 60774,
SPELL_DRUID_INCREASED_MOONFIRE_DURATION = 38414,
SPELL_DRUID_KING_OF_THE_JUNGLE = 48492,
SPELL_DRUID_LIFEBLOOM_ENERGIZE = 64372,
@@ -41,6 +44,35 @@ enum DruidSpells
SPELL_DRUID_ITEM_T8_BALANCE_RELIC = 64950,
};
+// -1850 - Dash
+class spell_dru_dash : public SpellScriptLoader
+{
+ public:
+ spell_dru_dash() : SpellScriptLoader("spell_dru_dash") { }
+
+ class spell_dru_dash_AuraScript : public AuraScript
+ {
+ PrepareAuraScript(spell_dru_dash_AuraScript);
+
+ void CalculateAmount(AuraEffect const* /*aurEff*/, int32& amount, bool& /*canBeRecalculated*/)
+ {
+ // do not set speed if not in cat form
+ if (GetUnitOwner()->GetShapeshiftForm() != FORM_CAT)
+ amount = 0;
+ }
+
+ void Register()
+ {
+ DoEffectCalcAmount += AuraEffectCalcAmountFn(spell_dru_dash_AuraScript::CalculateAmount, EFFECT_0, SPELL_AURA_MOD_INCREASE_SPEED);
+ }
+ };
+
+ AuraScript* GetAuraScript() const
+ {
+ return new spell_dru_dash_AuraScript();
+ }
+};
+
// -5229 - Enrage
class spell_dru_enrage : public SpellScriptLoader
{
@@ -121,6 +153,69 @@ class spell_dru_glyph_of_starfire : public SpellScriptLoader
}
};
+// 34246 - Idol of the Emerald Queen
+// 60779 - Idol of Lush Moss
+class spell_dru_idol_lifebloom : public SpellScriptLoader
+{
+ public:
+ spell_dru_idol_lifebloom() : SpellScriptLoader("spell_dru_idol_lifebloom") { }
+
+ class spell_dru_idol_lifebloom_AuraScript : public AuraScript
+ {
+ PrepareAuraScript(spell_dru_idol_lifebloom_AuraScript);
+
+ void HandleEffectCalcSpellMod(AuraEffect const* aurEff, SpellModifier*& spellMod)
+ {
+ if (!spellMod)
+ {
+ spellMod = new SpellModifier(GetAura());
+ spellMod->op = SPELLMOD_DOT;
+ spellMod->type = SPELLMOD_FLAT;
+ spellMod->spellId = GetId();
+ spellMod->mask = GetSpellInfo()->Effects[aurEff->GetEffIndex()].SpellClassMask;
+ }
+ spellMod->value = aurEff->GetAmount() / 7;
+ }
+
+ void Register()
+ {
+ DoEffectCalcSpellMod += AuraEffectCalcSpellModFn(spell_dru_idol_lifebloom_AuraScript::HandleEffectCalcSpellMod, EFFECT_0, SPELL_AURA_DUMMY);
+ }
+ };
+
+ AuraScript* GetAuraScript() const
+ {
+ return new spell_dru_idol_lifebloom_AuraScript();
+ }
+};
+
+// 29166 - Innervate
+class spell_dru_innervate : public SpellScriptLoader
+{
+ public:
+ spell_dru_innervate() : SpellScriptLoader("spell_dru_innervate") { }
+
+ class spell_dru_innervate_AuraScript : public AuraScript
+ {
+ PrepareAuraScript(spell_dru_innervate_AuraScript);
+
+ void CalculateAmount(AuraEffect const* aurEff, int32& amount, bool& /*canBeRecalculated*/)
+ {
+ amount = CalculatePct(int32(GetUnitOwner()->GetCreatePowers(POWER_MANA) / aurEff->GetTotalTicks()), amount);
+ }
+
+ void Register()
+ {
+ DoEffectCalcAmount += AuraEffectCalcAmountFn(spell_dru_innervate_AuraScript::CalculateAmount, EFFECT_0, SPELL_AURA_PERIODIC_ENERGIZE);
+ }
+ };
+
+ AuraScript* GetAuraScript() const
+ {
+ return new spell_dru_innervate_AuraScript();
+ }
+};
+
// -5570 - Insect Swarm
class spell_dru_insect_swarm : public SpellScriptLoader
{
@@ -276,6 +371,33 @@ class spell_dru_moonkin_form_passive : public SpellScriptLoader
}
};
+// 48391 - Owlkin Frenzy
+class spell_dru_owlkin_frenzy : public SpellScriptLoader
+{
+ public:
+ spell_dru_owlkin_frenzy() : SpellScriptLoader("spell_dru_owlkin_frenzy") { }
+
+ class spell_dru_owlkin_frenzy_AuraScript : public AuraScript
+ {
+ PrepareAuraScript(spell_dru_owlkin_frenzy_AuraScript);
+
+ void CalculateAmount(AuraEffect const* /*aurEff*/, int32& amount, bool& /*canBeRecalculated*/)
+ {
+ amount = CalculatePct(GetUnitOwner()->GetCreatePowers(POWER_MANA), amount);
+ }
+
+ void Register()
+ {
+ DoEffectCalcAmount += AuraEffectCalcAmountFn(spell_dru_owlkin_frenzy_AuraScript::CalculateAmount, EFFECT_2, SPELL_AURA_PERIODIC_ENERGIZE);
+ }
+ };
+
+ AuraScript* GetAuraScript() const
+ {
+ return new spell_dru_owlkin_frenzy_AuraScript();
+ }
+};
+
// -16972 - Predatory Strikes
class spell_dru_predatory_strikes : public SpellScriptLoader
{
@@ -349,6 +471,54 @@ class spell_dru_primal_tenacity : public SpellScriptLoader
}
};
+// -1079 - Rip
+class spell_dru_rip : public SpellScriptLoader
+{
+ public:
+ spell_dru_rip() : SpellScriptLoader("spell_dru_rip") { }
+
+ class spell_dru_rip_AuraScript : public AuraScript
+ {
+ PrepareAuraScript(spell_dru_rip_AuraScript);
+
+ bool Load()
+ {
+ Unit* caster = GetCaster();
+ return caster && caster->GetTypeId() == TYPEID_PLAYER;
+ }
+
+ void CalculateAmount(AuraEffect const* /*aurEff*/, int32& amount, bool& canBeRecalculated)
+ {
+ canBeRecalculated = false;
+
+ if (Unit* caster = GetCaster())
+ {
+ // 0.01 * $AP * cp
+ uint8 cp = caster->ToPlayer()->GetComboPoints();
+
+ // Idol of Feral Shadows. Can't be handled as SpellMod due its dependency from CPs
+ if (AuraEffect const* idol = caster->GetAuraEffect(SPELL_DRUID_IDOL_OF_FERAL_SHADOWS, EFFECT_0))
+ amount += cp * idol->GetAmount();
+ // Idol of Worship. Can't be handled as SpellMod due its dependency from CPs
+ else if (AuraEffect const* idol = caster->GetAuraEffect(SPELL_DRUID_IDOL_OF_WORSHIP, EFFECT_0))
+ amount += cp * idol->GetAmount();
+
+ amount += int32(CalculatePct(caster->GetTotalAttackPowerValue(BASE_ATTACK), cp));
+ }
+ }
+
+ void Register()
+ {
+ DoEffectCalcAmount += AuraEffectCalcAmountFn(spell_dru_rip_AuraScript::CalculateAmount, EFFECT_0, SPELL_AURA_PERIODIC_DAMAGE);
+ }
+ };
+
+ AuraScript* GetAuraScript() const
+ {
+ return new spell_dru_rip_AuraScript();
+ }
+};
+
// 62606 - Savage Defense
class spell_dru_savage_defense : public SpellScriptLoader
{
@@ -658,6 +828,35 @@ class spell_dru_tiger_s_fury : public SpellScriptLoader
}
};
+// -61391 - Typhoon
+class spell_dru_typhoon : public SpellScriptLoader
+{
+ public:
+ spell_dru_typhoon() : SpellScriptLoader("spell_dru_typhoon") { }
+
+ class spell_dru_typhoon_SpellScript : public SpellScript
+ {
+ PrepareSpellScript(spell_dru_typhoon_SpellScript);
+
+ void HandleKnockBack(SpellEffIndex effIndex)
+ {
+ // Glyph of Typhoon
+ if (GetCaster()->HasAura(SPELL_DRUID_GLYPH_OF_TYPHOON))
+ PreventHitDefaultEffect(effIndex);
+ }
+
+ void Register()
+ {
+ OnEffectHitTarget += SpellEffectFn(spell_dru_typhoon_SpellScript::HandleKnockBack, EFFECT_0, SPELL_EFFECT_KNOCK_BACK);
+ }
+ };
+
+ SpellScript* GetSpellScript() const
+ {
+ return new spell_dru_typhoon_SpellScript();
+ }
+};
+
// 70691 - Item T10 Restoration 4P Bonus
class spell_dru_t10_restoration_4p_bonus : public SpellScriptLoader
{
@@ -715,13 +914,18 @@ class spell_dru_t10_restoration_4p_bonus : public SpellScriptLoader
void AddSC_druid_spell_scripts()
{
+ new spell_dru_dash();
new spell_dru_enrage();
new spell_dru_glyph_of_starfire();
+ new spell_dru_idol_lifebloom();
+ new spell_dru_innervate();
new spell_dru_insect_swarm();
new spell_dru_lifebloom();
new spell_dru_moonkin_form_passive();
+ new spell_dru_owlkin_frenzy();
new spell_dru_predatory_strikes();
new spell_dru_primal_tenacity();
+ new spell_dru_rip();
new spell_dru_savage_defense();
new spell_dru_savage_roar();
new spell_dru_starfall_aoe();
@@ -729,5 +933,6 @@ void AddSC_druid_spell_scripts()
new spell_dru_survival_instincts();
new spell_dru_swift_flight_passive();
new spell_dru_tiger_s_fury();
+ new spell_dru_typhoon();
new spell_dru_t10_restoration_4p_bonus();
}
diff --git a/src/server/scripts/Spells/spell_generic.cpp b/src/server/scripts/Spells/spell_generic.cpp
index 4a1b0d558ff..6004ec6cf89 100644
--- a/src/server/scripts/Spells/spell_generic.cpp
+++ b/src/server/scripts/Spells/spell_generic.cpp
@@ -226,6 +226,158 @@ class spell_gen_cannibalize : public SpellScriptLoader
}
};
+// 63845 - Create Lance
+enum CreateLanceSpells
+{
+ SPELL_CREATE_LANCE_ALLIANCE = 63914,
+ SPELL_CREATE_LANCE_HORDE = 63919
+};
+
+class spell_gen_create_lance : public SpellScriptLoader
+{
+ public:
+ spell_gen_create_lance() : SpellScriptLoader("spell_gen_create_lance") { }
+
+ class spell_gen_create_lance_SpellScript : public SpellScript
+ {
+ PrepareSpellScript(spell_gen_create_lance_SpellScript);
+
+ bool Validate(SpellInfo const* /*spellInfo*/)
+ {
+ if (!sSpellMgr->GetSpellInfo(SPELL_CREATE_LANCE_ALLIANCE) || !sSpellMgr->GetSpellInfo(SPELL_CREATE_LANCE_HORDE))
+ return false;
+ return true;
+ }
+
+ void HandleScript(SpellEffIndex effIndex)
+ {
+ PreventHitDefaultEffect(effIndex);
+
+ if (Player* target = GetHitPlayer())
+ {
+ if (target->GetTeam() == ALLIANCE)
+ GetCaster()->CastSpell(target, SPELL_CREATE_LANCE_ALLIANCE, true);
+ else
+ GetCaster()->CastSpell(target, SPELL_CREATE_LANCE_HORDE, true);
+ }
+ }
+
+ void Register()
+ {
+ OnEffectHitTarget += SpellEffectFn(spell_gen_create_lance_SpellScript::HandleScript, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT);
+ }
+ };
+
+ SpellScript* GetSpellScript() const
+ {
+ return new spell_gen_create_lance_SpellScript();
+ }
+};
+
+// 28702 - Netherbloom
+enum Netherbloom
+{
+ SPELL_NETHERBLOOM_POLLEN_1 = 28703
+};
+
+class spell_gen_netherbloom : public SpellScriptLoader
+{
+ public:
+ spell_gen_netherbloom() : SpellScriptLoader("spell_gen_netherbloom") { }
+
+ class spell_gen_netherbloom_SpellScript : public SpellScript
+ {
+ PrepareSpellScript(spell_gen_netherbloom_SpellScript);
+
+ bool Validate(SpellInfo const* /*spellInfo*/)
+ {
+ for (uint8 i = 0; i < 5; ++i)
+ if (!sSpellMgr->GetSpellInfo(SPELL_NETHERBLOOM_POLLEN_1 + i))
+ return false;
+ return true;
+ }
+
+ void HandleScript(SpellEffIndex effIndex)
+ {
+ PreventHitDefaultEffect(effIndex);
+
+ if (Unit* target = GetHitUnit())
+ {
+ // 25% chance of casting a random buff
+ if (roll_chance_i(75))
+ return;
+
+ // triggered spells are 28703 to 28707
+ // Note: some sources say, that there was the possibility of
+ // receiving a debuff. However, this seems to be removed by a patch.
+
+ // don't overwrite an existing aura
+ for (uint8 i = 0; i < 5; ++i)
+ if (target->HasAura(SPELL_NETHERBLOOM_POLLEN_1 + i))
+ return;
+
+ target->CastSpell(target, SPELL_NETHERBLOOM_POLLEN_1 + urand(0, 4), true);
+ }
+ }
+
+ void Register()
+ {
+ OnEffectHitTarget += SpellEffectFn(spell_gen_netherbloom_SpellScript::HandleScript, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT);
+ }
+ };
+
+ SpellScript* GetSpellScript() const
+ {
+ return new spell_gen_netherbloom_SpellScript();
+ }
+};
+
+// 28720 - Nightmare Vine
+enum NightmareVine
+{
+ SPELL_NIGHTMARE_POLLEN = 28721
+};
+
+class spell_gen_nightmare_vine : public SpellScriptLoader
+{
+ public:
+ spell_gen_nightmare_vine() : SpellScriptLoader("spell_gen_nightmare_vine") { }
+
+ class spell_gen_nightmare_vine_SpellScript : public SpellScript
+ {
+ PrepareSpellScript(spell_gen_nightmare_vine_SpellScript);
+
+ bool Validate(SpellInfo const* /*spellInfo*/)
+ {
+ if (!sSpellMgr->GetSpellInfo(SPELL_NIGHTMARE_POLLEN))
+ return false;
+ return true;
+ }
+
+ void HandleScript(SpellEffIndex effIndex)
+ {
+ PreventHitDefaultEffect(effIndex);
+
+ if (Unit* target = GetHitUnit())
+ {
+ // 25% chance of casting Nightmare Pollen
+ if (roll_chance_i(25))
+ target->CastSpell(target, SPELL_NIGHTMARE_POLLEN, true);
+ }
+ }
+
+ void Register()
+ {
+ OnEffectHitTarget += SpellEffectFn(spell_gen_nightmare_vine_SpellScript::HandleScript, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT);
+ }
+ };
+
+ SpellScript* GetSpellScript() const
+ {
+ return new spell_gen_nightmare_vine_SpellScript();
+ }
+};
+
// 45472 Parachute
enum ParachuteSpells
{
@@ -1594,7 +1746,7 @@ class spell_gen_gnomish_transporter : public SpellScriptLoader
void HandleDummy(SpellEffIndex /* effIndex */)
{
Unit* caster = GetCaster();
- caster->CastSpell(caster, roll_chance_i(50) ? SPELL_TRANSPORTER_SUCCESS : SPELL_TRANSPORTER_FAILURE , true);
+ caster->CastSpell(caster, roll_chance_i(50) ? SPELL_TRANSPORTER_SUCCESS : SPELL_TRANSPORTER_FAILURE, true);
}
void Register()
@@ -3195,6 +3347,9 @@ void AddSC_generic_spell_scripts()
new spell_gen_av_drekthar_presence();
new spell_gen_burn_brutallus();
new spell_gen_cannibalize();
+ new spell_gen_create_lance();
+ new spell_gen_netherbloom();
+ new spell_gen_nightmare_vine();
new spell_gen_parachute();
new spell_gen_pet_summoned();
new spell_gen_remove_flight_auras();
diff --git a/src/server/scripts/Spells/spell_holiday.cpp b/src/server/scripts/Spells/spell_holiday.cpp
index 83532e7d72d..dbfc2b44501 100644
--- a/src/server/scripts/Spells/spell_holiday.cpp
+++ b/src/server/scripts/Spells/spell_holiday.cpp
@@ -17,6 +17,7 @@
/*
* Spells used in holidays/game events that do not fit any other category.
+ * Ordered alphabetically using scriptname.
* Scriptnames in this file should be prefixed with "spell_#holidayname_".
*/
@@ -133,6 +134,7 @@ class spell_hallow_end_trick : public SpellScriptLoader
class spell_hallow_end_trick_SpellScript : public SpellScript
{
PrepareSpellScript(spell_hallow_end_trick_SpellScript);
+
bool Validate(SpellInfo const* /*spell*/)
{
if (!sSpellMgr->GetSpellInfo(SPELL_PIRATE_COSTUME_MALE) || !sSpellMgr->GetSpellInfo(SPELL_PIRATE_COSTUME_FEMALE) || !sSpellMgr->GetSpellInfo(SPELL_NINJA_COSTUME_MALE)
@@ -170,7 +172,7 @@ class spell_hallow_end_trick : public SpellScriptLoader
break;
}
- caster->CastSpell(target, spellId, true, NULL);
+ caster->CastSpell(target, spellId, true);
}
}
@@ -218,8 +220,8 @@ class spell_hallow_end_trick_or_treat : public SpellScriptLoader
Unit* caster = GetCaster();
if (Player* target = GetHitPlayer())
{
- caster->CastSpell(target, roll_chance_i(50) ? SPELL_TRICK : SPELL_TREAT, true, NULL);
- caster->CastSpell(target, SPELL_TRICKED_OR_TREATED, true, NULL);
+ caster->CastSpell(target, roll_chance_i(50) ? SPELL_TRICK : SPELL_TREAT, true);
+ caster->CastSpell(target, SPELL_TRICKED_OR_TREATED, true);
}
}
@@ -301,27 +303,10 @@ class spell_winter_veil_mistletoe : public SpellScriptLoader
void HandleScript(SpellEffIndex /*effIndex*/)
{
- Unit* caster = GetCaster();
-
if (Player* target = GetHitPlayer())
{
- uint32 spellId = 0;
- switch (urand(0, 2))
- {
- case 0:
- spellId = SPELL_CREATE_MISTLETOE;
- break;
- case 1:
- spellId = SPELL_CREATE_HOLLY;
- break;
- case 2:
- spellId = SPELL_CREATE_SNOWFLAKES;
- break;
- default:
- return;
- }
-
- caster->CastSpell(target, spellId, true);
+ uint32 spellId = RAND(SPELL_CREATE_HOLLY, SPELL_CREATE_MISTLETOE, SPELL_CREATE_SNOWFLAKES);
+ GetCaster()->CastSpell(target, spellId, true);
}
}
@@ -337,6 +322,71 @@ class spell_winter_veil_mistletoe : public SpellScriptLoader
}
};
+// 26275 - PX-238 Winter Wondervolt TRAP
+enum PX238WinterWondervolt
+{
+ SPELL_PX_238_WINTER_WONDERVOLT_TRANSFORM_1 = 26157,
+ SPELL_PX_238_WINTER_WONDERVOLT_TRANSFORM_2 = 26272,
+ SPELL_PX_238_WINTER_WONDERVOLT_TRANSFORM_3 = 26273,
+ SPELL_PX_238_WINTER_WONDERVOLT_TRANSFORM_4 = 26274
+};
+
+class spell_winter_veil_px_238_winter_wondervolt : public SpellScriptLoader
+{
+ public:
+ spell_winter_veil_px_238_winter_wondervolt() : SpellScriptLoader("spell_winter_veil_px_238_winter_wondervolt") { }
+
+ class spell_winter_veil_px_238_winter_wondervolt_SpellScript : public SpellScript
+ {
+ PrepareSpellScript(spell_winter_veil_px_238_winter_wondervolt_SpellScript);
+
+ bool Validate(SpellInfo const* /*spellInfo*/)
+ {
+ if (!sSpellMgr->GetSpellInfo(SPELL_PX_238_WINTER_WONDERVOLT_TRANSFORM_1) ||
+ !sSpellMgr->GetSpellInfo(SPELL_PX_238_WINTER_WONDERVOLT_TRANSFORM_2) ||
+ !sSpellMgr->GetSpellInfo(SPELL_PX_238_WINTER_WONDERVOLT_TRANSFORM_3) ||
+ !sSpellMgr->GetSpellInfo(SPELL_PX_238_WINTER_WONDERVOLT_TRANSFORM_4))
+ return false;
+ return true;
+ }
+
+ void HandleScript(SpellEffIndex effIndex)
+ {
+ PreventHitDefaultEffect(effIndex);
+
+ uint32 const spells[4] =
+ {
+ SPELL_PX_238_WINTER_WONDERVOLT_TRANSFORM_1,
+ SPELL_PX_238_WINTER_WONDERVOLT_TRANSFORM_2,
+ SPELL_PX_238_WINTER_WONDERVOLT_TRANSFORM_3,
+ SPELL_PX_238_WINTER_WONDERVOLT_TRANSFORM_4
+ };
+
+ if (Unit* target = GetHitUnit())
+ {
+ for (uint8 i = 0; i < 4; ++i)
+ if (target->HasAura(spells[i]))
+ return;
+
+ GetCaster()->CastSpell(target, spells[urand(0, 3)], true);
+ }
+ }
+
+ void Register()
+ {
+ OnEffectHitTarget += SpellEffectFn(spell_winter_veil_px_238_winter_wondervolt_SpellScript::HandleScript, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT);
+ }
+
+ private:
+
+ };
+
+ SpellScript* GetSpellScript() const
+ {
+ return new spell_winter_veil_px_238_winter_wondervolt_SpellScript();
+ }
+};
+
void AddSC_holiday_spell_scripts()
{
// Love is in the Air
@@ -347,4 +397,5 @@ void AddSC_holiday_spell_scripts()
new spell_hallow_end_tricky_treat();
// Winter Veil
new spell_winter_veil_mistletoe();
+ new spell_winter_veil_px_238_winter_wondervolt();
}
diff --git a/src/server/scripts/Spells/spell_hunter.cpp b/src/server/scripts/Spells/spell_hunter.cpp
index b3c429d27aa..a9b21807899 100644
--- a/src/server/scripts/Spells/spell_hunter.cpp
+++ b/src/server/scripts/Spells/spell_hunter.cpp
@@ -286,7 +286,6 @@ class spell_hun_last_stand_pet : public SpellScriptLoader
void Register()
{
- // add dummy effect spell handler to pet's Last Stand
OnEffectHitTarget += SpellEffectFn(spell_hun_last_stand_pet_SpellScript::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY);
}
};
@@ -452,7 +451,6 @@ class spell_hun_pet_carrion_feeder : public SpellScriptLoader
void Register()
{
- // add dummy effect spell handler to pet's Last Stand
OnEffectHit += SpellEffectFn(spell_hun_pet_carrion_feeder_SpellScript::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY);
OnCheckCast += SpellCheckCastFn(spell_hun_pet_carrion_feeder_SpellScript::CheckIfCorpseNear);
}
@@ -501,7 +499,6 @@ class spell_hun_pet_heart_of_the_phoenix : public SpellScriptLoader
void Register()
{
- // add dummy effect spell handler to pet's Last Stand
OnEffectHitTarget += SpellEffectFn(spell_hun_pet_heart_of_the_phoenix_SpellScript::HandleScript, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT);
}
};
@@ -551,7 +548,6 @@ class spell_hun_readiness : public SpellScriptLoader
void Register()
{
- // add dummy effect spell handler to Readiness
OnEffectHitTarget += SpellEffectFn(spell_hun_readiness_SpellScript::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY);
}
};
diff --git a/src/server/scripts/Spells/spell_item.cpp b/src/server/scripts/Spells/spell_item.cpp
index ccd8f3f81e7..c38d05bc02a 100644
--- a/src/server/scripts/Spells/spell_item.cpp
+++ b/src/server/scripts/Spells/spell_item.cpp
@@ -73,6 +73,93 @@ class spell_item_trigger_spell : public SpellScriptLoader
}
};
+// 26400 - Arcane Shroud
+class spell_item_arcane_shroud : public SpellScriptLoader
+{
+ public:
+ spell_item_arcane_shroud() : SpellScriptLoader("spell_item_arcane_shroud") { }
+
+ class spell_item_arcane_shroud_AuraScript : public AuraScript
+ {
+ PrepareAuraScript(spell_item_arcane_shroud_AuraScript);
+
+ void CalculateAmount(AuraEffect const* /*aurEff*/, int32& amount, bool& /*canBeRecalculated*/)
+ {
+ int32 diff = GetUnitOwner()->getLevel() - 60;
+ if (diff > 0)
+ amount += 2 * diff;
+ }
+
+ void Register()
+ {
+ DoEffectCalcAmount += AuraEffectCalcAmountFn(spell_item_arcane_shroud_AuraScript::CalculateAmount, EFFECT_0, SPELL_AURA_MOD_THREAT);
+ }
+ };
+
+ AuraScript* GetAuraScript() const
+ {
+ return new spell_item_arcane_shroud_AuraScript();
+ }
+};
+
+// 8342 - Defibrillate (Goblin Jumper Cables) have 33% chance on success
+// 22999 - Defibrillate (Goblin Jumper Cables XL) have 50% chance on success
+// 54732 - Defibrillate (Gnomish Army Knife) have 67% chance on success
+enum Defibrillate
+{
+ SPELL_GOBLIN_JUMPER_CABLES_FAIL = 8338,
+ SPELL_GOBLIN_JUMPER_CABLES_XL_FAIL = 23055
+};
+
+class spell_item_defibrillate : public SpellScriptLoader
+{
+ public:
+ spell_item_defibrillate(char const* name, uint8 chance, uint32 failSpell = 0) : SpellScriptLoader(name), _chance(chance), _failSpell(failSpell) { }
+
+ class spell_item_defibrillate_SpellScript : public SpellScript
+ {
+ PrepareSpellScript(spell_item_defibrillate_SpellScript);
+
+ public:
+ spell_item_defibrillate_SpellScript(uint8 chance, uint32 failSpell) : SpellScript(), _chance(chance), _failSpell(failSpell) { }
+
+ bool Validate(SpellInfo const* /*spellInfo*/)
+ {
+ if (_failSpell && !sSpellMgr->GetSpellInfo(_failSpell))
+ return false;
+ return true;
+ }
+
+ void HandleScript(SpellEffIndex effIndex)
+ {
+ if (roll_chance_i(_chance))
+ {
+ PreventHitDefaultEffect(effIndex);
+ if (_failSpell)
+ GetCaster()->CastSpell(GetCaster(), _failSpell, true, GetCastItem());
+ }
+ }
+
+ void Register()
+ {
+ OnEffectHitTarget += SpellEffectFn(spell_item_defibrillate_SpellScript::HandleScript, EFFECT_0, SPELL_EFFECT_RESURRECT);
+ }
+
+ private:
+ uint8 _chance;
+ uint32 _failSpell;
+ };
+
+ SpellScript* GetSpellScript() const
+ {
+ return new spell_item_defibrillate_SpellScript(_chance, _failSpell);
+ }
+
+ private:
+ uint8 _chance;
+ uint32 _failSpell;
+};
+
// http://www.wowhead.com/item=6522 Deviate Fish
// 8063 Deviate Fish
enum DeviateFishSpells
@@ -464,6 +551,35 @@ class spell_item_noggenfogger_elixir : public SpellScriptLoader
}
};
+// 17512 - Piccolo of the Flaming Fire
+class spell_item_piccolo_of_the_flaming_fire : public SpellScriptLoader
+{
+ public:
+ spell_item_piccolo_of_the_flaming_fire() : SpellScriptLoader("spell_item_piccolo_of_the_flaming_fire") { }
+
+ class spell_item_piccolo_of_the_flaming_fire_SpellScript : public SpellScript
+ {
+ PrepareSpellScript(spell_item_piccolo_of_the_flaming_fire_SpellScript);
+
+ void HandleScript(SpellEffIndex effIndex)
+ {
+ PreventHitDefaultEffect(effIndex);
+ if (Player* target = GetHitPlayer())
+ target->HandleEmoteCommand(EMOTE_STATE_DANCE);
+ }
+
+ void Register()
+ {
+ OnEffectHitTarget += SpellEffectFn(spell_item_piccolo_of_the_flaming_fire_SpellScript::HandleScript, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT);
+ }
+ };
+
+ SpellScript* GetSpellScript() const
+ {
+ return new spell_item_piccolo_of_the_flaming_fire_SpellScript();
+ }
+};
+
// http://www.wowhead.com/item=6657 Savory Deviate Delight
// 8213 Savory Deviate Delight
enum SavoryDeviateDelight
@@ -522,6 +638,79 @@ class spell_item_savory_deviate_delight : public SpellScriptLoader
}
};
+// 48129 - Scroll of Recall
+// 60320 - Scroll of Recall II
+// 60321 - Scroll of Recall III
+enum ScrollOfRecall
+{
+ SPELL_SCROLL_OF_RECALL_I = 48129,
+ SPELL_SCROLL_OF_RECALL_II = 60320,
+ SPELL_SCROLL_OF_RECALL_III = 60321,
+ SPELL_LOST = 60444,
+ SPELL_SCROLL_OF_RECALL_FAIL_ALLIANCE_1 = 60323,
+ SPELL_SCROLL_OF_RECALL_FAIL_HORDE_1 = 60328,
+};
+
+class spell_item_scroll_of_recall : public SpellScriptLoader
+{
+ public:
+ spell_item_scroll_of_recall() : SpellScriptLoader("spell_item_scroll_of_recall") { }
+
+ class spell_item_scroll_of_recall_SpellScript : public SpellScript
+ {
+ PrepareSpellScript(spell_item_scroll_of_recall_SpellScript);
+
+ bool Load()
+ {
+ return GetCaster()->GetTypeId() == TYPEID_PLAYER;
+ }
+
+ void HandleScript(SpellEffIndex effIndex)
+ {
+ Unit* caster = GetCaster();
+ uint8 maxSafeLevel = 0;
+ switch (GetSpellInfo()->Id)
+ {
+ case SPELL_SCROLL_OF_RECALL_I: // Scroll of Recall
+ maxSafeLevel = 40;
+ break;
+ case SPELL_SCROLL_OF_RECALL_II: // Scroll of Recall II
+ maxSafeLevel = 70;
+ break;
+ case SPELL_SCROLL_OF_RECALL_III: // Scroll of Recal III
+ maxSafeLevel = 80;
+ break;
+ default:
+ break;
+ }
+
+ if (caster->getLevel() > maxSafeLevel)
+ {
+ caster->CastSpell(caster, SPELL_LOST, true);
+
+ // ALLIANCE from 60323 to 60330 - HORDE from 60328 to 60335
+ uint32 spellId = SPELL_SCROLL_OF_RECALL_FAIL_ALLIANCE_1;
+ if (GetCaster()->ToPlayer()->GetTeam() == HORDE)
+ spellId = SPELL_SCROLL_OF_RECALL_FAIL_HORDE_1;
+
+ GetCaster()->CastSpell(GetCaster(), spellId + urand(0, 7), true);
+
+ PreventHitDefaultEffect(effIndex);
+ }
+ }
+
+ void Register()
+ {
+ OnEffectHitTarget += SpellEffectFn(spell_item_scroll_of_recall_SpellScript::HandleScript, EFFECT_0, SPELL_EFFECT_TELEPORT_UNITS);
+ }
+ };
+
+ SpellScript* GetSpellScript() const
+ {
+ return new spell_item_scroll_of_recall_SpellScript();
+ }
+};
+
// http://www.wowhead.com/item=7734 Six Demon Bag
// 14537 Six Demon Bag
enum SixDemonBagSpells
@@ -593,6 +782,35 @@ class spell_item_six_demon_bag : public SpellScriptLoader
}
};
+// 28862 - The Eye of Diminution
+class spell_item_the_eye_of_diminution : public SpellScriptLoader
+{
+ public:
+ spell_item_the_eye_of_diminution() : SpellScriptLoader("spell_item_the_eye_of_diminution") { }
+
+ class spell_item_the_eye_of_diminution_AuraScript : public AuraScript
+ {
+ PrepareAuraScript(spell_item_the_eye_of_diminution_AuraScript);
+
+ void CalculateAmount(AuraEffect const* /*aurEff*/, int32& amount, bool& /*canBeRecalculated*/)
+ {
+ int32 diff = GetUnitOwner()->getLevel() - 60;
+ if (diff > 0)
+ amount += diff;
+ }
+
+ void Register()
+ {
+ DoEffectCalcAmount += AuraEffectCalcAmountFn(spell_item_the_eye_of_diminution_AuraScript::CalculateAmount, EFFECT_0, SPELL_AURA_MOD_THREAT);
+ }
+ };
+
+ AuraScript* GetAuraScript() const
+ {
+ return new spell_item_the_eye_of_diminution_AuraScript();
+ }
+};
+
// http://www.wowhead.com/item=44012 Underbelly Elixir
// 59640 Underbelly Elixir
enum UnderbellyElixirSpells
@@ -1350,7 +1568,7 @@ class spell_item_poultryizer : public SpellScriptLoader
void HandleDummy(SpellEffIndex /* effIndex */)
{
if (GetCastItem() && GetHitUnit())
- GetCaster()->CastSpell(GetHitUnit(), roll_chance_i(80) ? SPELL_POULTRYIZER_SUCCESS : SPELL_POULTRYIZER_BACKFIRE , true, GetCastItem());
+ GetCaster()->CastSpell(GetHitUnit(), roll_chance_i(80) ? SPELL_POULTRYIZER_SUCCESS : SPELL_POULTRYIZER_BACKFIRE, true, GetCastItem());
}
void Register()
@@ -1710,7 +1928,7 @@ class spell_item_teach_language : public SpellScriptLoader
Player* caster = GetCaster()->ToPlayer();
if (roll_chance_i(34))
- caster->CastSpell(caster,caster->GetTeam() == ALLIANCE ? SPELL_LEARN_GNOMISH_BINARY : SPELL_LEARN_GOBLIN_BINARY, true);
+ caster->CastSpell(caster, caster->GetTeam() == ALLIANCE ? SPELL_LEARN_GNOMISH_BINARY : SPELL_LEARN_GOBLIN_BINARY, true);
}
void Register()
@@ -2004,7 +2222,7 @@ public:
void HandleDummy(SpellEffIndex /*effIndex*/)
{
if (GetHitUnit())
- GetCaster()->CastSpell(GetCaster(),SPELL_FORCE_CAST_SUMMON_GNOME_SOUL);
+ GetCaster()->CastSpell(GetCaster(), SPELL_FORCE_CAST_SUMMON_GNOME_SOUL);
}
void Register()
@@ -2030,6 +2248,10 @@ void AddSC_item_spell_scripts()
// 23075 Mithril Mechanical Dragonling
new spell_item_trigger_spell("spell_item_mithril_mechanical_dragonling", SPELL_MITHRIL_MECHANICAL_DRAGONLING);
+ new spell_item_arcane_shroud();
+ new spell_item_defibrillate("spell_item_goblin_jumper_cables", 67, SPELL_GOBLIN_JUMPER_CABLES_FAIL);
+ new spell_item_defibrillate("spell_item_goblin_jumper_cables_xl", 50, SPELL_GOBLIN_JUMPER_CABLES_XL_FAIL);
+ new spell_item_defibrillate("spell_item_gnomish_army_knife", 33);
new spell_item_deviate_fish();
new spell_item_flask_of_the_north();
new spell_item_gnomish_death_ray();
@@ -2037,8 +2259,11 @@ void AddSC_item_spell_scripts()
new spell_item_mingos_fortune_generator();
new spell_item_net_o_matic();
new spell_item_noggenfogger_elixir();
+ new spell_item_piccolo_of_the_flaming_fire();
new spell_item_savory_deviate_delight();
+ new spell_item_scroll_of_recall();
new spell_item_six_demon_bag();
+ new spell_item_the_eye_of_diminution();
new spell_item_underbelly_elixir();
new spell_item_shadowmourne();
new spell_item_red_rider_air_rifle();
diff --git a/src/server/scripts/Spells/spell_mage.cpp b/src/server/scripts/Spells/spell_mage.cpp
index 61f0579190f..a673759a49e 100644
--- a/src/server/scripts/Spells/spell_mage.cpp
+++ b/src/server/scripts/Spells/spell_mage.cpp
@@ -29,7 +29,7 @@
enum MageSpells
{
SPELL_MAGE_COLD_SNAP = 11958,
- SPELL_MAGE_FROST_WARDING_R1 = 28332,
+ SPELL_MAGE_FROST_WARDING_R1 = 11189,
SPELL_MAGE_FROST_WARDING_TRIGGERED = 57776,
SPELL_MAGE_INCANTERS_ABSORBTION_R1 = 44394,
SPELL_MAGE_INCANTERS_ABSORBTION_TRIGGERED = 44413,
@@ -45,6 +45,31 @@ enum MageSpells
SPELL_MAGE_GLYPH_OF_BLAST_WAVE = 62126,
};
+// Incanter's Absorbtion
+class spell_mage_incanters_absorbtion_base_AuraScript : public AuraScript
+{
+ public:
+ bool Validate(SpellInfo const* /*spellInfo*/)
+ {
+ if (!sSpellMgr->GetSpellInfo(SPELL_MAGE_INCANTERS_ABSORBTION_TRIGGERED))
+ return false;
+ if (!sSpellMgr->GetSpellInfo(SPELL_MAGE_INCANTERS_ABSORBTION_R1))
+ return false;
+ return true;
+ }
+
+ void Trigger(AuraEffect* aurEff, DamageInfo& /*dmgInfo*/, uint32& absorbAmount)
+ {
+ Unit* target = GetTarget();
+
+ if (AuraEffect* talentAurEff = target->GetAuraEffectOfRankedSpell(SPELL_MAGE_INCANTERS_ABSORBTION_R1, EFFECT_0))
+ {
+ int32 bp = CalculatePct(absorbAmount, talentAurEff->GetAmount());
+ target->CastCustomSpell(target, SPELL_MAGE_INCANTERS_ABSORBTION_TRIGGERED, &bp, NULL, NULL, true, NULL, aurEff);
+ }
+ }
+};
+
// -11113 - Blast Wave
class spell_mage_blast_wave : public SpellScriptLoader
{
@@ -127,29 +152,47 @@ class spell_mage_cold_snap : public SpellScriptLoader
}
};
-// -543, -6143 - Frost Warding
-class spell_mage_frost_warding_trigger : public SpellScriptLoader
+// -543 - Fire Ward
+// -6143 - Frost Ward
+class spell_mage_fire_frost_ward : public SpellScriptLoader
{
public:
- spell_mage_frost_warding_trigger() : SpellScriptLoader("spell_mage_frost_warding_trigger") { }
+ spell_mage_fire_frost_ward() : SpellScriptLoader("spell_mage_fire_frost_ward") { }
- class spell_mage_frost_warding_trigger_AuraScript : public AuraScript
+ class spell_mage_fire_frost_ward_AuraScript : public spell_mage_incanters_absorbtion_base_AuraScript
{
- PrepareAuraScript(spell_mage_frost_warding_trigger_AuraScript);
+ PrepareAuraScript(spell_mage_fire_frost_ward_AuraScript);
bool Validate(SpellInfo const* /*spellInfo*/)
{
- if (!sSpellMgr->GetSpellInfo(SPELL_MAGE_FROST_WARDING_TRIGGERED) || !sSpellMgr->GetSpellInfo(SPELL_MAGE_FROST_WARDING_R1))
+ if (!sSpellMgr->GetSpellInfo(SPELL_MAGE_FROST_WARDING_TRIGGERED))
+ return false;
+ if (!sSpellMgr->GetSpellInfo(SPELL_MAGE_FROST_WARDING_R1))
return false;
return true;
}
- void Absorb(AuraEffect* aurEff, DamageInfo & dmgInfo, uint32 & absorbAmount)
+ void CalculateAmount(AuraEffect const* /*aurEff*/, int32& amount, bool& canBeRecalculated)
+ {
+ canBeRecalculated = false;
+ if (Unit* caster = GetCaster())
+ {
+ // +80.68% from sp bonus
+ float bonus = 0.8068f;
+
+ bonus *= caster->SpellBaseHealingBonusDone(GetSpellInfo()->GetSchoolMask());
+ bonus *= caster->CalculateLevelPenalty(GetSpellInfo());
+
+ amount += int32(bonus);
+ }
+ }
+
+ void Absorb(AuraEffect* aurEff, DamageInfo& dmgInfo, uint32& absorbAmount)
{
Unit* target = GetTarget();
if (AuraEffect* talentAurEff = target->GetAuraEffectOfRankedSpell(SPELL_MAGE_FROST_WARDING_R1, EFFECT_0))
{
- int32 chance = talentAurEff->GetSpellInfo()->Effects[EFFECT_1].CalcValue();
+ int32 chance = talentAurEff->GetSpellInfo()->Effects[EFFECT_1].CalcValue(); // SPELL_EFFECT_DUMMY with NO_TARGET
if (roll_chance_i(chance))
{
@@ -164,79 +207,58 @@ class spell_mage_frost_warding_trigger : public SpellScriptLoader
void Register()
{
- OnEffectAbsorb += AuraEffectAbsorbFn(spell_mage_frost_warding_trigger_AuraScript::Absorb, EFFECT_0);
+ DoEffectCalcAmount += AuraEffectCalcAmountFn(spell_mage_fire_frost_ward_AuraScript::CalculateAmount, EFFECT_0, SPELL_AURA_SCHOOL_ABSORB);
+ OnEffectAbsorb += AuraEffectAbsorbFn(spell_mage_fire_frost_ward_AuraScript::Absorb, EFFECT_0);
+ AfterEffectAbsorb += AuraEffectAbsorbFn(spell_mage_fire_frost_ward_AuraScript::Trigger, EFFECT_0);
}
};
AuraScript* GetAuraScript() const
{
- return new spell_mage_frost_warding_trigger_AuraScript();
+ return new spell_mage_fire_frost_ward_AuraScript();
}
};
-class spell_mage_incanters_absorbtion_base_AuraScript : public AuraScript
+// -11426 - Ice Barrier
+class spell_mage_ice_barrier : public SpellScriptLoader
{
public:
+ spell_mage_ice_barrier() : SpellScriptLoader("spell_mage_ice_barrier") { }
- bool Validate(SpellInfo const* /*spellInfo*/)
- {
- return sSpellMgr->GetSpellInfo(SPELL_MAGE_INCANTERS_ABSORBTION_TRIGGERED)
- && sSpellMgr->GetSpellInfo(SPELL_MAGE_INCANTERS_ABSORBTION_R1);
- }
-
- void Trigger(AuraEffect* aurEff, DamageInfo & /*dmgInfo*/, uint32 & absorbAmount)
+ class spell_mage_ice_barrier_AuraScript : public spell_mage_incanters_absorbtion_base_AuraScript
{
- Unit* target = GetTarget();
+ PrepareAuraScript(spell_mage_ice_barrier_AuraScript);
- if (AuraEffect* talentAurEff = target->GetAuraEffectOfRankedSpell(SPELL_MAGE_INCANTERS_ABSORBTION_R1, EFFECT_0))
+ void CalculateAmount(AuraEffect const* aurEff, int32& amount, bool& canBeRecalculated)
{
- int32 bp = CalculatePct(absorbAmount, talentAurEff->GetAmount());
- target->CastCustomSpell(target, SPELL_MAGE_INCANTERS_ABSORBTION_TRIGGERED, &bp, NULL, NULL, true, NULL, aurEff);
- }
- }
-};
-
-// -543, -6143, -11426 - Incanter's Absorption
-class spell_mage_incanters_absorbtion_absorb : public SpellScriptLoader
-{
- public:
- spell_mage_incanters_absorbtion_absorb() : SpellScriptLoader("spell_mage_incanters_absorbtion_absorb") { }
+ canBeRecalculated = false;
+ if (Unit* caster = GetCaster())
+ {
+ // +80.68% from sp bonus
+ float bonus = 0.8068f;
- class spell_mage_incanters_absorbtion_absorb_AuraScript : public spell_mage_incanters_absorbtion_base_AuraScript
- {
- PrepareAuraScript(spell_mage_incanters_absorbtion_absorb_AuraScript);
+ bonus *= caster->SpellBaseHealingBonusDone(GetSpellInfo()->GetSchoolMask());
- void Register()
- {
- AfterEffectAbsorb += AuraEffectAbsorbFn(spell_mage_incanters_absorbtion_absorb_AuraScript::Trigger, EFFECT_0);
- }
- };
+ // Glyph of Ice Barrier: its weird having a SPELLMOD_ALL_EFFECTS here but its blizzards doing :)
+ // Glyph of Ice Barrier is only applied at the spell damage bonus because it was already applied to the base value in CalculateSpellDamage
+ bonus = caster->ApplyEffectModifiers(GetSpellInfo(), aurEff->GetEffIndex(), bonus);
- AuraScript* GetAuraScript() const
- {
- return new spell_mage_incanters_absorbtion_absorb_AuraScript();
- }
-};
+ bonus *= caster->CalculateLevelPenalty(GetSpellInfo());
-// -1463 - Incanter's Absorption
-class spell_mage_incanters_absorbtion_manashield : public SpellScriptLoader
-{
- public:
- spell_mage_incanters_absorbtion_manashield() : SpellScriptLoader("spell_mage_incanters_absorbtion_manashield") { }
-
- class spell_mage_incanters_absorbtion_manashield_AuraScript : public spell_mage_incanters_absorbtion_base_AuraScript
- {
- PrepareAuraScript(spell_mage_incanters_absorbtion_manashield_AuraScript);
+ amount += int32(bonus);
+ }
+ }
void Register()
{
- AfterEffectManaShield += AuraEffectManaShieldFn(spell_mage_incanters_absorbtion_manashield_AuraScript::Trigger, EFFECT_0);
+ DoEffectCalcAmount += AuraEffectCalcAmountFn(spell_mage_ice_barrier_AuraScript::CalculateAmount, EFFECT_0, SPELL_AURA_SCHOOL_ABSORB);
+ AfterEffectAbsorb += AuraEffectAbsorbFn(spell_mage_ice_barrier_AuraScript::Trigger, EFFECT_0);
}
};
AuraScript* GetAuraScript() const
{
- return new spell_mage_incanters_absorbtion_manashield_AuraScript();
+ return new spell_mage_ice_barrier_AuraScript();
}
};
@@ -279,6 +301,44 @@ class spell_mage_living_bomb : public SpellScriptLoader
}
};
+// -1463 - Mana Shield
+class spell_mage_mana_shield : public SpellScriptLoader
+{
+ public:
+ spell_mage_mana_shield() : SpellScriptLoader("spell_mage_mana_shield") { }
+
+ class spell_mage_mana_shield_AuraScript : public spell_mage_incanters_absorbtion_base_AuraScript
+ {
+ PrepareAuraScript(spell_mage_mana_shield_AuraScript);
+
+ void CalculateAmount(AuraEffect const* /*aurEff*/, int32& amount, bool& canBeRecalculated)
+ {
+ canBeRecalculated = false;
+ if (Unit* caster = GetCaster())
+ {
+ // +80.53% from sp bonus
+ float bonus = 0.8053f;
+
+ bonus *= caster->SpellBaseHealingBonusDone(GetSpellInfo()->GetSchoolMask());
+ bonus *= caster->CalculateLevelPenalty(GetSpellInfo());
+
+ amount += int32(bonus);
+ }
+ }
+
+ void Register()
+ {
+ DoEffectCalcAmount += AuraEffectCalcAmountFn(spell_mage_mana_shield_AuraScript::CalculateAmount, EFFECT_0, SPELL_AURA_MANA_SHIELD);
+ AfterEffectManaShield += AuraEffectManaShieldFn(spell_mage_mana_shield_AuraScript::Trigger, EFFECT_0);
+ }
+ };
+
+ AuraScript* GetAuraScript() const
+ {
+ return new spell_mage_mana_shield_AuraScript();
+ }
+};
+
enum SilvermoonPolymorph
{
NPC_AUROSALIA = 18744,
@@ -378,10 +438,10 @@ void AddSC_mage_spell_scripts()
{
new spell_mage_blast_wave();
new spell_mage_cold_snap();
- new spell_mage_frost_warding_trigger();
- new spell_mage_incanters_absorbtion_absorb();
- new spell_mage_incanters_absorbtion_manashield();
+ new spell_mage_fire_frost_ward();
+ new spell_mage_ice_barrier();
new spell_mage_living_bomb();
+ new spell_mage_mana_shield();
new spell_mage_polymorph_cast_visual();
new spell_mage_summon_water_elemental();
}
diff --git a/src/server/scripts/Spells/spell_paladin.cpp b/src/server/scripts/Spells/spell_paladin.cpp
index d3fc86302e1..2f22de97d77 100644
--- a/src/server/scripts/Spells/spell_paladin.cpp
+++ b/src/server/scripts/Spells/spell_paladin.cpp
@@ -51,6 +51,13 @@ enum PaladinSpells
SPELL_PALADIN_HAND_OF_SACRIFICE = 6940,
SPELL_PALADIN_DIVINE_SACRIFICE = 64205,
+
+ SPELL_PALADIN_GLYPH_OF_SALVATION = 63225,
+
+ SPELL_PALADIN_RIGHTEOUS_DEFENSE_TAUNT = 31790,
+
+ SPELL_GENERIC_ARENA_DAMPENING = 74410,
+ SPELL_GENERIC_BATTLEGROUND_DAMPENING = 74411
};
// 31850 - Ardent Defender
@@ -490,6 +497,39 @@ class spell_pal_hand_of_sacrifice : public SpellScriptLoader
}
};
+// 1038 - Hand of Salvation
+class spell_pal_hand_of_salvation : public SpellScriptLoader
+{
+ public:
+ spell_pal_hand_of_salvation() : SpellScriptLoader("spell_pal_hand_of_salvation") { }
+
+ class spell_pal_hand_of_salvation_AuraScript : public AuraScript
+ {
+ PrepareAuraScript(spell_pal_hand_of_salvation_AuraScript);
+
+ void CalculateAmount(AuraEffect const* /*aurEff*/, int32& amount, bool& /*canBeRecalculated*/)
+ {
+ if (Unit* caster = GetCaster())
+ {
+ // Glyph of Salvation
+ if (caster->GetGUID() == GetUnitOwner()->GetGUID())
+ if (AuraEffect const* aurEff = caster->GetAuraEffect(SPELL_PALADIN_GLYPH_OF_SALVATION, EFFECT_0))
+ amount -= aurEff->GetAmount();
+ }
+ }
+
+ void Register()
+ {
+ DoEffectCalcAmount += AuraEffectCalcAmountFn(spell_pal_hand_of_salvation_AuraScript::CalculateAmount, EFFECT_1, SPELL_AURA_MOD_DAMAGE_PERCENT_TAKEN);
+ }
+ };
+
+ AuraScript* GetAuraScript() const
+ {
+ return new spell_pal_hand_of_salvation_AuraScript();
+ }
+};
+
// -20473 - Holy Shock
class spell_pal_holy_shock : public SpellScriptLoader
{
@@ -656,6 +696,13 @@ class spell_pal_righteous_defense : public SpellScriptLoader
{
PrepareSpellScript(spell_pal_righteous_defense_SpellScript);
+ bool Validate(SpellInfo const* /*spellInfo*/)
+ {
+ if (!sSpellMgr->GetSpellInfo(SPELL_PALADIN_RIGHTEOUS_DEFENSE_TAUNT))
+ return false;
+ return true;
+ }
+
SpellCastResult CheckCast()
{
Unit* caster = GetCaster();
@@ -673,9 +720,27 @@ class spell_pal_righteous_defense : public SpellScriptLoader
return SPELL_CAST_OK;
}
+ void HandleTriggerSpellLaunch(SpellEffIndex effIndex)
+ {
+ PreventHitDefaultEffect(effIndex);
+ }
+
+ void HandleTriggerSpellHit(SpellEffIndex effIndex)
+ {
+ PreventHitDefaultEffect(effIndex);
+ if (Unit* target = GetHitUnit())
+ GetCaster()->CastSpell(target, SPELL_PALADIN_RIGHTEOUS_DEFENSE_TAUNT, true);
+ }
+
void Register()
{
OnCheckCast += SpellCheckCastFn(spell_pal_righteous_defense_SpellScript::CheckCast);
+ //! WORKAROUND
+ //! target select will be executed in hitphase of effect 0
+ //! so we must handle trigger spell also in hit phase (default execution in launch phase)
+ //! see issue #3718
+ OnEffectLaunchTarget += SpellEffectFn(spell_pal_righteous_defense_SpellScript::HandleTriggerSpellLaunch, EFFECT_1, SPELL_EFFECT_TRIGGER_SPELL);
+ OnEffectHitTarget += SpellEffectFn(spell_pal_righteous_defense_SpellScript::HandleTriggerSpellHit, EFFECT_1, SPELL_EFFECT_TRIGGER_SPELL);
}
};
@@ -685,6 +750,50 @@ class spell_pal_righteous_defense : public SpellScriptLoader
}
};
+// 58597 - Sacred Shield
+class spell_pal_sacred_shield : public SpellScriptLoader
+{
+ public:
+ spell_pal_sacred_shield() : SpellScriptLoader("spell_pal_sacred_shield") { }
+
+ class spell_pal_sacred_shield_AuraScript : public AuraScript
+ {
+ PrepareAuraScript(spell_pal_sacred_shield_AuraScript);
+
+ void CalculateAmount(AuraEffect const* aurEff, int32& amount, bool& /*canBeRecalculated*/)
+ {
+ if (Unit* caster = GetCaster())
+ {
+ // +75.00% from sp bonus
+ float bonus = CalculatePct(caster->SpellBaseHealingBonusDone(GetSpellInfo()->GetSchoolMask()), 75.0f);
+
+ // Divine Guardian is only applied at the spell healing bonus because it was already applied to the base value in CalculateSpellDamage
+ bonus = caster->ApplyEffectModifiers(GetSpellInfo(), aurEff->GetEffIndex(), bonus);
+ bonus *= caster->CalculateLevelPenalty(GetSpellInfo());
+
+ amount += int32(bonus);
+
+ // Arena - Dampening
+ if (AuraEffect const* dampening = caster->GetAuraEffect(SPELL_GENERIC_ARENA_DAMPENING, EFFECT_0))
+ AddPct(amount, dampening->GetAmount());
+ // Battleground - Dampening
+ else if (AuraEffect const* dampening = caster->GetAuraEffect(SPELL_GENERIC_BATTLEGROUND_DAMPENING, EFFECT_0))
+ AddPct(amount, dampening->GetAmount());
+ }
+ }
+
+ void Register()
+ {
+ DoEffectCalcAmount += AuraEffectCalcAmountFn(spell_pal_sacred_shield_AuraScript::CalculateAmount, EFFECT_0, SPELL_AURA_SCHOOL_ABSORB);
+ }
+ };
+
+ AuraScript* GetAuraScript() const
+ {
+ return new spell_pal_sacred_shield_AuraScript();
+ }
+};
+
void AddSC_paladin_spell_scripts()
{
new spell_pal_ardent_defender();
@@ -696,8 +805,10 @@ void AddSC_paladin_spell_scripts()
new spell_pal_exorcism_and_holy_wrath_damage();
new spell_pal_guarded_by_the_light();
new spell_pal_hand_of_sacrifice();
+ new spell_pal_hand_of_salvation();
new spell_pal_holy_shock();
new spell_pal_judgement_of_command();
new spell_pal_lay_on_hands();
new spell_pal_righteous_defense();
+ new spell_pal_sacred_shield();
}
diff --git a/src/server/scripts/Spells/spell_pet.cpp b/src/server/scripts/Spells/spell_pet.cpp
index 5198e555ee1..c4d5562ab39 100644
--- a/src/server/scripts/Spells/spell_pet.cpp
+++ b/src/server/scripts/Spells/spell_pet.cpp
@@ -1451,7 +1451,7 @@ public:
if (pet->GetEntry() == ENTRY_ARMY_OF_THE_DEAD_GHOUL)
amount = -90;
// Night of the dead
- else if ( Aura * aur = owner->GetAuraOfRankedSpell(SPELL_NIGHT_OF_THE_DEAD))
+ else if (Aura* aur = owner->GetAuraOfRankedSpell(SPELL_NIGHT_OF_THE_DEAD))
amount = aur->GetSpellInfo()->Effects[EFFECT_2].CalcValue();
}
}
diff --git a/src/server/scripts/Spells/spell_priest.cpp b/src/server/scripts/Spells/spell_priest.cpp
index 1c416a0d28a..23a39819aa2 100644
--- a/src/server/scripts/Spells/spell_priest.cpp
+++ b/src/server/scripts/Spells/spell_priest.cpp
@@ -30,6 +30,7 @@
enum PriestSpells
{
SPELL_PRIEST_EMPOWERED_RENEW = 63544,
+ SPELL_PRIEST_GLYPHE_OF_LIGHTWELL = 55673,
SPELL_PRIEST_GUARDIAN_SPIRIT_HEAL = 48153,
SPELL_PRIEST_PENANCE_R1 = 47540,
SPELL_PRIEST_PENANCE_R1_DAMAGE = 47758,
@@ -43,6 +44,7 @@ enum PriestSpells
enum PriestSpellIcons
{
+ PRIEST_ICON_ID_BORROWED_TIME = 2899,
PRIEST_ICON_ID_EMPOWERED_RENEW_TALENT = 3021,
PRIEST_ICON_ID_PAIN_AND_SUFFERING = 2874,
};
@@ -104,6 +106,38 @@ class spell_pri_guardian_spirit : public SpellScriptLoader
}
};
+// -7001 - Lightwell Renew
+class spell_pri_lightwell_renew : public SpellScriptLoader
+{
+ public:
+ spell_pri_lightwell_renew() : SpellScriptLoader("spell_pri_lightwell_renew") { }
+
+ class spell_pri_lightwell_renew_AuraScript : public AuraScript
+ {
+ PrepareAuraScript(spell_pri_lightwell_renew_AuraScript);
+
+ void CalculateAmount(AuraEffect const* /*aurEff*/, int32& amount, bool& /*canBeRecalculated*/)
+ {
+ if (Unit* caster = GetCaster())
+ {
+ // Bonus from Glyph of Lightwell
+ if (AuraEffect* modHealing = caster->GetAuraEffect(SPELL_PRIEST_GLYPHE_OF_LIGHTWELL, EFFECT_0))
+ AddPct(amount, modHealing->GetAmount());
+ }
+ }
+
+ void Register()
+ {
+ DoEffectCalcAmount += AuraEffectCalcAmountFn(spell_pri_lightwell_renew_AuraScript::CalculateAmount, EFFECT_0, SPELL_AURA_PERIODIC_HEAL);
+ }
+ };
+
+ AuraScript* GetAuraScript() const
+ {
+ return new spell_pri_lightwell_renew_AuraScript();
+ }
+};
+
// -8129 - Mana Burn
class spell_pri_mana_burn : public SpellScriptLoader
{
@@ -260,81 +294,114 @@ class spell_pri_penance : public SpellScriptLoader
}
};
-// 33110 - Prayer of Mending Heal
-class spell_pri_prayer_of_mending_heal : public SpellScriptLoader
+// -17 - Power Word: Shield
+class spell_pri_power_word_shield : public SpellScriptLoader
{
public:
- spell_pri_prayer_of_mending_heal() : SpellScriptLoader("spell_pri_prayer_of_mending_heal") { }
+ spell_pri_power_word_shield() : SpellScriptLoader("spell_pri_power_word_shield") { }
- class spell_pri_prayer_of_mending_heal_SpellScript : public SpellScript
+ class spell_pri_power_word_shield_AuraScript : public AuraScript
{
- PrepareSpellScript(spell_pri_prayer_of_mending_heal_SpellScript);
+ PrepareAuraScript(spell_pri_power_word_shield_AuraScript);
- void HandleHeal(SpellEffIndex /*effIndex*/)
+ bool Validate(SpellInfo const* /*spellInfo*/)
{
- if (Unit* caster = GetOriginalCaster())
+ if (!sSpellMgr->GetSpellInfo(SPELL_PRIEST_REFLECTIVE_SHIELD_TRIGGERED))
+ return false;
+ if (!sSpellMgr->GetSpellInfo(SPELL_PRIEST_REFLECTIVE_SHIELD_R1))
+ return false;
+ return true;
+ }
+
+ void CalculateAmount(AuraEffect const* aurEff, int32& amount, bool& canBeRecalculated)
+ {
+ canBeRecalculated = false;
+ if (Unit* caster = GetCaster())
{
- if (AuraEffect* aurEff = caster->GetAuraEffect(SPELL_PRIEST_T9_HEALING_2P, EFFECT_0))
+ // +80.68% from sp bonus
+ float bonus = 0.8068f;
+
+ // Borrowed Time
+ if (AuraEffect const* borrowedTime = caster->GetDummyAuraEffect(SPELLFAMILY_PRIEST, PRIEST_ICON_ID_BORROWED_TIME, EFFECT_1))
+ bonus += CalculatePct(1.0f, borrowedTime->GetAmount());
+
+ bonus *= caster->SpellBaseHealingBonusDone(GetSpellInfo()->GetSchoolMask());
+
+ // Improved PW: Shield: its weird having a SPELLMOD_ALL_EFFECTS here but its blizzards doing :)
+ // Improved PW: Shield is only applied at the spell healing bonus because it was already applied to the base value in CalculateSpellDamage
+ bonus = caster->ApplyEffectModifiers(GetSpellInfo(), aurEff->GetEffIndex(), bonus);
+ bonus *= caster->CalculateLevelPenalty(GetSpellInfo());
+
+ amount += int32(bonus);
+
+ // Twin Disciplines
+ if (AuraEffect const* twinDisciplines = caster->GetAuraEffect(SPELL_AURA_ADD_PCT_MODIFIER, SPELLFAMILY_PRIEST, 0x400000, 0, 0, GetCasterGUID()))
+ AddPct(amount, twinDisciplines->GetAmount());
+
+ // Focused Power
+ amount *= caster->GetTotalAuraMultiplier(SPELL_AURA_MOD_HEALING_DONE_PERCENT);
+ }
+ }
+
+ void ReflectDamage(AuraEffect* aurEff, DamageInfo& dmgInfo, uint32& absorbAmount)
+ {
+ Unit* target = GetTarget();
+ if (dmgInfo.GetAttacker() == target)
+ return;
+
+ if (Unit* caster = GetCaster())
+ if (AuraEffect* talentAurEff = caster->GetAuraEffectOfRankedSpell(SPELL_PRIEST_REFLECTIVE_SHIELD_R1, EFFECT_0))
{
- int32 heal = GetHitHeal();
- AddPct(heal, aurEff->GetAmount());
- SetHitHeal(heal);
+ int32 bp = CalculatePct(absorbAmount, talentAurEff->GetAmount());
+ target->CastCustomSpell(dmgInfo.GetAttacker(), SPELL_PRIEST_REFLECTIVE_SHIELD_TRIGGERED, &bp, NULL, NULL, true, NULL, aurEff);
}
- }
}
void Register()
{
- OnEffectHitTarget += SpellEffectFn(spell_pri_prayer_of_mending_heal_SpellScript::HandleHeal, EFFECT_0, SPELL_EFFECT_HEAL);
+ DoEffectCalcAmount += AuraEffectCalcAmountFn(spell_pri_power_word_shield_AuraScript::CalculateAmount, EFFECT_0, SPELL_AURA_SCHOOL_ABSORB);
+ AfterEffectAbsorb += AuraEffectAbsorbFn(spell_pri_power_word_shield_AuraScript::ReflectDamage, EFFECT_0);
}
};
- SpellScript* GetSpellScript() const
+ AuraScript* GetAuraScript() const
{
- return new spell_pri_prayer_of_mending_heal_SpellScript();
+ return new spell_pri_power_word_shield_AuraScript();
}
};
-// -17 - Reflective Shield
-class spell_pri_reflective_shield_trigger : public SpellScriptLoader
+// 33110 - Prayer of Mending Heal
+class spell_pri_prayer_of_mending_heal : public SpellScriptLoader
{
public:
- spell_pri_reflective_shield_trigger() : SpellScriptLoader("spell_pri_reflective_shield_trigger") { }
+ spell_pri_prayer_of_mending_heal() : SpellScriptLoader("spell_pri_prayer_of_mending_heal") { }
- class spell_pri_reflective_shield_trigger_AuraScript : public AuraScript
+ class spell_pri_prayer_of_mending_heal_SpellScript : public SpellScript
{
- PrepareAuraScript(spell_pri_reflective_shield_trigger_AuraScript);
-
- bool Validate(SpellInfo const* /*spellInfo*/)
- {
- if (!sSpellMgr->GetSpellInfo(SPELL_PRIEST_REFLECTIVE_SHIELD_TRIGGERED) || !sSpellMgr->GetSpellInfo(SPELL_PRIEST_REFLECTIVE_SHIELD_R1))
- return false;
- return true;
- }
+ PrepareSpellScript(spell_pri_prayer_of_mending_heal_SpellScript);
- void Trigger(AuraEffect* aurEff, DamageInfo & dmgInfo, uint32 & absorbAmount)
+ void HandleHeal(SpellEffIndex /*effIndex*/)
{
- Unit* target = GetTarget();
- if (dmgInfo.GetAttacker() == target)
- return;
-
- if (GetCaster())
- if (AuraEffect* talentAurEff = target->GetAuraEffectOfRankedSpell(SPELL_PRIEST_REFLECTIVE_SHIELD_R1, EFFECT_0))
+ if (Unit* caster = GetOriginalCaster())
+ {
+ if (AuraEffect* aurEff = caster->GetAuraEffect(SPELL_PRIEST_T9_HEALING_2P, EFFECT_0))
{
- int32 bp = CalculatePct(absorbAmount, talentAurEff->GetAmount());
- target->CastCustomSpell(dmgInfo.GetAttacker(), SPELL_PRIEST_REFLECTIVE_SHIELD_TRIGGERED, &bp, NULL, NULL, true, NULL, aurEff);
+ int32 heal = GetHitHeal();
+ AddPct(heal, aurEff->GetAmount());
+ SetHitHeal(heal);
}
+ }
}
void Register()
{
- AfterEffectAbsorb += AuraEffectAbsorbFn(spell_pri_reflective_shield_trigger_AuraScript::Trigger, EFFECT_0);
+ OnEffectHitTarget += SpellEffectFn(spell_pri_prayer_of_mending_heal_SpellScript::HandleHeal, EFFECT_0, SPELL_EFFECT_HEAL);
}
};
- AuraScript* GetAuraScript() const
+ SpellScript* GetSpellScript() const
{
- return new spell_pri_reflective_shield_trigger_AuraScript();
+ return new spell_pri_prayer_of_mending_heal_SpellScript();
}
};
@@ -458,12 +525,13 @@ class spell_pri_vampiric_touch : public SpellScriptLoader
void AddSC_priest_spell_scripts()
{
new spell_pri_guardian_spirit();
+ new spell_pri_lightwell_renew();
new spell_pri_mana_burn();
new spell_pri_mind_sear();
new spell_pri_pain_and_suffering_proc();
new spell_pri_penance();
+ new spell_pri_power_word_shield();
new spell_pri_prayer_of_mending_heal();
- new spell_pri_reflective_shield_trigger();
new spell_pri_renew();
new spell_pri_shadow_word_death();
new spell_pri_vampiric_touch();
diff --git a/src/server/scripts/Spells/spell_quest.cpp b/src/server/scripts/Spells/spell_quest.cpp
index 9cd94e9c7d5..49a47a17392 100644
--- a/src/server/scripts/Spells/spell_quest.cpp
+++ b/src/server/scripts/Spells/spell_quest.cpp
@@ -25,6 +25,7 @@
#include "ScriptMgr.h"
#include "ScriptedCreature.h"
#include "SpellScript.h"
+#include "SpellAuras.h"
#include "Vehicle.h"
#include "GridNotifiers.h"
#include "GridNotifiersImpl.h"
@@ -1517,6 +1518,49 @@ class spell_q11010_q11102_q11023_q11008_check_fly_mount : public SpellScriptLoad
}
};
+enum SpellZuldrakRat
+{
+ SPELL_SUMMON_GORGED_LURKING_BASILISK = 50928
+};
+
+class spell_q12527_zuldrak_rat : public SpellScriptLoader
+{
+ public:
+ spell_q12527_zuldrak_rat() : SpellScriptLoader("spell_q12527_zuldrak_rat") { }
+
+ class spell_q12527_zuldrak_rat_SpellScript : public SpellScript
+ {
+ PrepareSpellScript(spell_q12527_zuldrak_rat_SpellScript);
+
+ bool Validate(SpellInfo const* /*spell*/)
+ {
+ if (!sSpellMgr->GetSpellInfo(SPELL_SUMMON_GORGED_LURKING_BASILISK))
+ return false;
+ return true;
+ }
+
+ void HandleScriptEffect(SpellEffIndex /* effIndex */)
+ {
+ if (GetHitAura() && GetHitAura()->GetStackAmount() >= GetSpellInfo()->StackAmount)
+ {
+ GetHitUnit()->CastSpell((Unit*) NULL, SPELL_SUMMON_GORGED_LURKING_BASILISK, true);
+ if (Creature* basilisk = GetHitUnit()->ToCreature())
+ basilisk->DespawnOrUnsummon();
+ }
+ }
+
+ void Register()
+ {
+ OnEffectHitTarget += SpellEffectFn(spell_q12527_zuldrak_rat_SpellScript::HandleScriptEffect, EFFECT_1, SPELL_EFFECT_SCRIPT_EFFECT);
+ }
+ };
+
+ SpellScript* GetSpellScript() const
+ {
+ return new spell_q12527_zuldrak_rat_SpellScript();
+ }
+};
+
void AddSC_quest_spell_scripts()
{
new spell_q55_sacred_cleansing();
@@ -1554,4 +1598,5 @@ void AddSC_quest_spell_scripts()
new spell_q11010_q11102_q11023_choose_loc();
new spell_q11010_q11102_q11023_q11008_check_fly_mount();
new spell_q12372_azure_on_death_force_whisper();
+ new spell_q12527_zuldrak_rat();
}
diff --git a/src/server/scripts/Spells/spell_rogue.cpp b/src/server/scripts/Spells/spell_rogue.cpp
index 1a2738afdbb..156158b4e36 100644
--- a/src/server/scripts/Spells/spell_rogue.cpp
+++ b/src/server/scripts/Spells/spell_rogue.cpp
@@ -250,7 +250,7 @@ class spell_rog_preparation : public SpellScriptLoader
return GetCaster()->GetTypeId() == TYPEID_PLAYER;
}
- bool Validate(SpellInfo const* /*spellEntry*/)
+ bool Validate(SpellInfo const* /*spellInfo*/)
{
if (!sSpellMgr->GetSpellInfo(SPELL_ROGUE_GLYPH_OF_PREPARATION))
return false;
@@ -292,7 +292,6 @@ class spell_rog_preparation : public SpellScriptLoader
void Register()
{
- // add dummy effect spell handler to Preparation
OnEffectHitTarget += SpellEffectFn(spell_rog_preparation_SpellScript::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY);
}
};
@@ -348,6 +347,58 @@ class spell_rog_prey_on_the_weak : public SpellScriptLoader
}
};
+// -1943 - Rupture
+class spell_rog_rupture : public SpellScriptLoader
+{
+ public:
+ spell_rog_rupture() : SpellScriptLoader("spell_rog_rupture") { }
+
+ class spell_rog_rupture_AuraScript : public AuraScript
+ {
+ PrepareAuraScript(spell_rog_rupture_AuraScript);
+
+ bool Load()
+ {
+ Unit* caster = GetCaster();
+ return caster && caster->GetTypeId() == TYPEID_PLAYER;
+ }
+
+ void CalculateAmount(AuraEffect const* /*aurEff*/, int32& amount, bool& canBeRecalculated)
+ {
+ if (Unit* caster = GetCaster())
+ {
+ canBeRecalculated = false;
+
+ float const attackpowerPerCombo[6] =
+ {
+ 0.0f,
+ 0.015f, // 1 point: ${($m1 + $b1*1 + 0.015 * $AP) * 4} damage over 8 secs
+ 0.024f, // 2 points: ${($m1 + $b1*2 + 0.024 * $AP) * 5} damage over 10 secs
+ 0.03f, // 3 points: ${($m1 + $b1*3 + 0.03 * $AP) * 6} damage over 12 secs
+ 0.03428571f, // 4 points: ${($m1 + $b1*4 + 0.03428571 * $AP) * 7} damage over 14 secs
+ 0.0375f // 5 points: ${($m1 + $b1*5 + 0.0375 * $AP) * 8} damage over 16 secs
+ };
+
+ uint8 cp = caster->ToPlayer()->GetComboPoints();
+ if (cp > 5)
+ cp = 5;
+
+ amount += int32(caster->GetTotalAttackPowerValue(BASE_ATTACK) * attackpowerPerCombo[cp]);
+ }
+ }
+
+ void Register()
+ {
+ DoEffectCalcAmount += AuraEffectCalcAmountFn(spell_rog_rupture_AuraScript::CalculateAmount, EFFECT_0, SPELL_AURA_PERIODIC_DAMAGE);
+ }
+ };
+
+ AuraScript* GetAuraScript() const
+ {
+ return new spell_rog_rupture_AuraScript();
+ }
+};
+
// 5938 - Shiv
class spell_rog_shiv : public SpellScriptLoader
{
@@ -396,5 +447,6 @@ void AddSC_rogue_spell_scripts()
new spell_rog_nerves_of_steel();
new spell_rog_preparation();
new spell_rog_prey_on_the_weak();
+ new spell_rog_rupture();
new spell_rog_shiv();
}
diff --git a/src/server/scripts/Spells/spell_shaman.cpp b/src/server/scripts/Spells/spell_shaman.cpp
index ff7c83b95a5..2994761be6f 100644
--- a/src/server/scripts/Spells/spell_shaman.cpp
+++ b/src/server/scripts/Spells/spell_shaman.cpp
@@ -30,28 +30,70 @@
enum ShamanSpells
{
- SHAMAN_SPELL_GLYPH_OF_MANA_TIDE = 55441,
- SHAMAN_SPELL_MANA_TIDE_TOTEM = 39609,
- SHAMAN_SPELL_FIRE_NOVA_R1 = 1535,
- SHAMAN_SPELL_FIRE_NOVA_TRIGGERED_R1 = 8349,
- SHAMAN_SPELL_SATED = 57724,
- SHAMAN_SPELL_EXHAUSTION = 57723,
+ SPELL_SHAMAN_ANCESTRAL_AWAKENING_PROC = 52752,
+ SPELL_SHAMAN_BIND_SIGHT = 6277,
+ SPELL_SHAMAN_CLEANSING_TOTEM_EFFECT = 52025,
+ SPELL_SHAMAN_EXHAUSTION = 57723,
+ SPELL_SHAMAN_FIRE_NOVA_R1 = 1535,
+ SPELL_SHAMAN_FIRE_NOVA_TRIGGERED_R1 = 8349,
+ SPELL_SHAMAN_GLYPH_OF_HEALING_STREAM_TOTEM = 55456,
+ SPELL_SHAMAN_GLYPH_OF_MANA_TIDE = 55441,
+ SPELL_SHAMAN_GLYPH_OF_THUNDERSTORM = 62132,
+ SPELL_SHAMAN_LAVA_FLOWS_R1 = 51480,
+ SPELL_SHAMAN_LAVA_FLOWS_TRIGGERED_R1 = 64694,
+ SPELL_SHAMAN_MANA_SPRING_TOTEM_ENERGIZE = 52032,
+ SPELL_SHAMAN_MANA_TIDE_TOTEM = 39609,
+ SPELL_SHAMAN_SATED = 57724,
+ SPELL_SHAMAN_STORM_EARTH_AND_FIRE = 51483,
+ SPELL_SHAMAN_TOTEM_EARTHBIND_EARTHGRAB = 64695,
+ SPELL_SHAMAN_TOTEM_EARTHBIND_TOTEM = 6474,
+ SPELL_SHAMAN_TOTEM_EARTHEN_POWER = 59566,
+ SPELL_SHAMAN_TOTEM_HEALING_STREAM_HEAL = 52042
+};
+
+enum ShamanSpellIcons
+{
+ SHAMAN_ICON_ID_RESTORATIVE_TOTEMS = 338,
+ SHAMAN_ICON_ID_SHAMAN_LAVA_FLOW = 3087
+};
+
+// 52759 - Ancestral Awakening (Proc)
+class spell_sha_ancestral_awakening_proc : public SpellScriptLoader
+{
+ public:
+ spell_sha_ancestral_awakening_proc() : SpellScriptLoader("spell_sha_ancestral_awakening_proc") { }
+
+ class spell_sha_ancestral_awakening_proc_SpellScript : public SpellScript
+ {
+ PrepareSpellScript(spell_sha_ancestral_awakening_proc_SpellScript);
- SHAMAN_SPELL_STORM_EARTH_AND_FIRE = 51483,
- EARTHBIND_TOTEM_SPELL_EARTHGRAB = 64695,
+ bool Validate(SpellInfo const* /*spellInfo*/)
+ {
+ if (!sSpellMgr->GetSpellInfo(SPELL_SHAMAN_ANCESTRAL_AWAKENING_PROC))
+ return false;
+ return true;
+ }
- // For Earthen Power
- SHAMAN_TOTEM_SPELL_EARTHBIND_TOTEM = 6474,
- SHAMAN_TOTEM_SPELL_EARTHEN_POWER = 59566,
+ void HandleDummy(SpellEffIndex /*effIndex*/)
+ {
+ int32 damage = GetEffectValue();
+ if (GetHitUnit())
+ GetCaster()->CastCustomSpell(GetHitUnit(), SPELL_SHAMAN_ANCESTRAL_AWAKENING_PROC, &damage, NULL, NULL, true);
+ }
- SHAMAN_BIND_SIGHT = 6277,
+ void Register()
+ {
+ OnEffectHitTarget += SpellEffectFn(spell_sha_ancestral_awakening_proc_SpellScript::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY);
+ }
+ };
- ICON_ID_SHAMAN_LAVA_FLOW = 3087,
- SHAMAN_LAVA_FLOWS_R1 = 51480,
- SHAMAN_LAVA_FLOWS_TRIGGERED_R1 = 64694,
+ SpellScript* GetSpellScript() const
+ {
+ return new spell_sha_ancestral_awakening_proc_SpellScript();
+ }
};
-// 51474 - Astral shift
+// 51474 - Astral Shift
class spell_sha_astral_shift : public SpellScriptLoader
{
public:
@@ -95,115 +137,136 @@ class spell_sha_astral_shift : public SpellScriptLoader
}
};
-// 1535 Fire Nova
-class spell_sha_fire_nova : public SpellScriptLoader
+// 2825 - Bloodlust
+class spell_sha_bloodlust : public SpellScriptLoader
{
public:
- spell_sha_fire_nova() : SpellScriptLoader("spell_sha_fire_nova") { }
+ spell_sha_bloodlust() : SpellScriptLoader("spell_sha_bloodlust") { }
- class spell_sha_fire_nova_SpellScript : public SpellScript
+ class spell_sha_bloodlust_SpellScript : public SpellScript
{
- PrepareSpellScript(spell_sha_fire_nova_SpellScript);
+ PrepareSpellScript(spell_sha_bloodlust_SpellScript);
- bool Validate(SpellInfo const* spellEntry)
+ bool Validate(SpellInfo const* /*spellInfo*/)
{
- if (!sSpellMgr->GetSpellInfo(SHAMAN_SPELL_FIRE_NOVA_R1) || sSpellMgr->GetFirstSpellInChain(SHAMAN_SPELL_FIRE_NOVA_R1) != sSpellMgr->GetFirstSpellInChain(spellEntry->Id))
- return false;
-
- uint8 rank = sSpellMgr->GetSpellRank(spellEntry->Id);
- if (!sSpellMgr->GetSpellWithRank(SHAMAN_SPELL_FIRE_NOVA_TRIGGERED_R1, rank, true))
+ if (!sSpellMgr->GetSpellInfo(SPELL_SHAMAN_SATED))
return false;
return true;
}
- SpellCastResult CheckFireTotem()
+ void RemoveInvalidTargets(std::list<WorldObject*>& targets)
{
- // fire totem
- if (!GetCaster()->m_SummonSlot[1])
- {
- SetCustomCastResultMessage(SPELL_CUSTOM_ERROR_MUST_HAVE_FIRE_TOTEM);
- return SPELL_FAILED_CUSTOM_ERROR;
- }
+ targets.remove_if(Trinity::UnitAuraCheck(true, SPELL_SHAMAN_SATED));
+ }
- return SPELL_CAST_OK;
+ void ApplyDebuff()
+ {
+ if (Unit* target = GetHitUnit())
+ target->CastSpell(target, SPELL_SHAMAN_SATED, true);
}
- void HandleDummy(SpellEffIndex /*effIndex*/)
+ void Register()
{
- if (Unit* caster = GetCaster())
+ OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_sha_bloodlust_SpellScript::RemoveInvalidTargets, EFFECT_0, TARGET_UNIT_CASTER_AREA_RAID);
+ OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_sha_bloodlust_SpellScript::RemoveInvalidTargets, EFFECT_1, TARGET_UNIT_CASTER_AREA_RAID);
+ OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_sha_bloodlust_SpellScript::RemoveInvalidTargets, EFFECT_2, TARGET_UNIT_CASTER_AREA_RAID);
+ AfterHit += SpellHitFn(spell_sha_bloodlust_SpellScript::ApplyDebuff);
+ }
+ };
+
+ SpellScript* GetSpellScript() const
+ {
+ return new spell_sha_bloodlust_SpellScript();
+ }
+};
+
+// -1064 - Chain Heal
+class spell_sha_chain_heal : public SpellScriptLoader
+{
+ public:
+ spell_sha_chain_heal() : SpellScriptLoader("spell_sha_chain_heal") { }
+
+ class spell_sha_chain_heal_SpellScript : public SpellScript
+ {
+ PrepareSpellScript(spell_sha_chain_heal_SpellScript);
+
+ bool Load()
+ {
+ firstHeal = true;
+ riptide = false;
+ return true;
+ }
+
+ void HandleHeal(SpellEffIndex /*effIndex*/)
+ {
+ if (firstHeal)
{
- uint8 rank = sSpellMgr->GetSpellRank(GetSpellInfo()->Id);
- if (uint32 spellId = sSpellMgr->GetSpellWithRank(SHAMAN_SPELL_FIRE_NOVA_TRIGGERED_R1, rank))
+ // Check if the target has Riptide
+ if (AuraEffect* aurEff = GetHitUnit()->GetAuraEffect(SPELL_AURA_PERIODIC_HEAL, SPELLFAMILY_SHAMAN, 0, 0, 0x10, GetCaster()->GetGUID()))
{
- Creature* totem = caster->GetMap()->GetCreature(caster->m_SummonSlot[1]);
- if (totem && totem->isTotem())
- caster->CastSpell(totem, spellId, true);
+ riptide = true;
+ // Consume it
+ GetHitUnit()->RemoveAura(aurEff->GetBase());
}
+ firstHeal = false;
}
+ // Riptide increases the Chain Heal effect by 25%
+ if (riptide)
+ SetHitHeal(GetHitHeal() * 1.25f);
}
void Register()
{
- OnCheckCast += SpellCheckCastFn(spell_sha_fire_nova_SpellScript::CheckFireTotem);
- OnEffectHitTarget += SpellEffectFn(spell_sha_fire_nova_SpellScript::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY);
+ OnEffectHitTarget += SpellEffectFn(spell_sha_chain_heal_SpellScript::HandleHeal, EFFECT_0, SPELL_EFFECT_HEAL);
}
+
+ bool firstHeal;
+ bool riptide;
};
SpellScript* GetSpellScript() const
{
- return new spell_sha_fire_nova_SpellScript();
+ return new spell_sha_chain_heal_SpellScript();
}
};
-// 39610 Mana Tide Totem
-class spell_sha_mana_tide_totem : public SpellScriptLoader
+// 8171 - Cleansing Totem (Pulse)
+class spell_sha_cleansing_totem_pulse : public SpellScriptLoader
{
public:
- spell_sha_mana_tide_totem() : SpellScriptLoader("spell_sha_mana_tide_totem") { }
+ spell_sha_cleansing_totem_pulse() : SpellScriptLoader("spell_sha_cleansing_totem_pulse") { }
- class spell_sha_mana_tide_totem_SpellScript : public SpellScript
+ class spell_sha_cleansing_totem_pulse_SpellScript : public SpellScript
{
- PrepareSpellScript(spell_sha_mana_tide_totem_SpellScript);
+ PrepareSpellScript(spell_sha_cleansing_totem_pulse_SpellScript);
- bool Validate(SpellInfo const* /*spellEntry*/)
+ bool Validate(SpellInfo const* /*spellInfo*/)
{
- if (!sSpellMgr->GetSpellInfo(SHAMAN_SPELL_GLYPH_OF_MANA_TIDE) || !sSpellMgr->GetSpellInfo(SHAMAN_SPELL_MANA_TIDE_TOTEM))
+ if (!sSpellMgr->GetSpellInfo(SPELL_SHAMAN_CLEANSING_TOTEM_EFFECT))
return false;
return true;
}
void HandleDummy(SpellEffIndex /*effIndex*/)
{
- if (Unit* caster = GetCaster())
- if (Unit* unitTarget = GetHitUnit())
- {
- if (unitTarget->getPowerType() == POWER_MANA)
- {
- int32 effValue = GetEffectValue();
- // Glyph of Mana Tide
- if (Unit* owner = caster->GetOwner())
- if (AuraEffect* dummy = owner->GetAuraEffect(SHAMAN_SPELL_GLYPH_OF_MANA_TIDE, 0))
- effValue += dummy->GetAmount();
- // Regenerate 6% of Total Mana Every 3 secs
- int32 effBasePoints0 = int32(CalculatePct(unitTarget->GetMaxPower(POWER_MANA), effValue));
- caster->CastCustomSpell(unitTarget, SHAMAN_SPELL_MANA_TIDE_TOTEM, &effBasePoints0, NULL, NULL, true, NULL, NULL, GetOriginalCaster()->GetGUID());
- }
- }
+ int32 bp = 1;
+ if (GetCaster() && GetHitUnit() && GetOriginalCaster())
+ GetCaster()->CastCustomSpell(GetHitUnit(), SPELL_SHAMAN_CLEANSING_TOTEM_EFFECT, NULL, &bp, NULL, true, NULL, NULL, GetOriginalCaster()->GetGUID());
}
void Register()
{
- OnEffectHitTarget += SpellEffectFn(spell_sha_mana_tide_totem_SpellScript::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY);
+ OnEffectHitTarget += SpellEffectFn(spell_sha_cleansing_totem_pulse_SpellScript::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY);
}
};
SpellScript* GetSpellScript() const
{
- return new spell_sha_mana_tide_totem_SpellScript();
+ return new spell_sha_cleansing_totem_pulse_SpellScript();
}
};
-// 6474 - Earthbind Totem - Fix Talent:Earthen Power
+// 6474 - Earthbind Totem - Fix Talent: Earthen Power
class spell_sha_earthbind_totem : public SpellScriptLoader
{
public:
@@ -213,9 +276,9 @@ class spell_sha_earthbind_totem : public SpellScriptLoader
{
PrepareAuraScript(spell_sha_earthbind_totem_AuraScript);
- bool Validate(SpellInfo const* /*spellEntry*/)
+ bool Validate(SpellInfo const* /*spellInfo*/)
{
- if (!sSpellMgr->GetSpellInfo(SHAMAN_TOTEM_SPELL_EARTHBIND_TOTEM) || !sSpellMgr->GetSpellInfo(SHAMAN_TOTEM_SPELL_EARTHEN_POWER))
+ if (!sSpellMgr->GetSpellInfo(SPELL_SHAMAN_TOTEM_EARTHBIND_TOTEM) || !sSpellMgr->GetSpellInfo(SPELL_SHAMAN_TOTEM_EARTHEN_POWER))
return false;
return true;
}
@@ -227,7 +290,7 @@ class spell_sha_earthbind_totem : public SpellScriptLoader
if (Player* owner = GetCaster()->GetCharmerOrOwnerPlayerOrPlayerItself())
if (AuraEffect* aur = owner->GetDummyAuraEffect(SPELLFAMILY_SHAMAN, 2289, 0))
if (roll_chance_i(aur->GetBaseAmount()))
- GetTarget()->CastSpell((Unit*)NULL, SHAMAN_TOTEM_SPELL_EARTHEN_POWER, true);
+ GetTarget()->CastSpell((Unit*)NULL, SPELL_SHAMAN_TOTEM_EARTHEN_POWER, true);
}
void Apply(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/)
@@ -238,10 +301,10 @@ class spell_sha_earthbind_totem : public SpellScriptLoader
if (!owner)
return;
// Storm, Earth and Fire
- if (AuraEffect* aurEff = owner->GetAuraEffectOfRankedSpell(SHAMAN_SPELL_STORM_EARTH_AND_FIRE, EFFECT_1))
+ if (AuraEffect* aurEff = owner->GetAuraEffectOfRankedSpell(SPELL_SHAMAN_STORM_EARTH_AND_FIRE, EFFECT_1))
{
if (roll_chance_i(aurEff->GetAmount()))
- GetCaster()->CastSpell(GetCaster(), EARTHBIND_TOTEM_SPELL_EARTHGRAB, false);
+ GetCaster()->CastSpell(GetCaster(), SPELL_SHAMAN_TOTEM_EARTHBIND_EARTHGRAB, false);
}
}
@@ -275,6 +338,7 @@ class EarthenPowerTargetSelector
}
};
+// 59566 - Earthen Power
class spell_sha_earthen_power : public SpellScriptLoader
{
public:
@@ -301,177 +365,112 @@ class spell_sha_earthen_power : public SpellScriptLoader
}
};
-class spell_sha_bloodlust : public SpellScriptLoader
+// -1535 - Fire Nova
+class spell_sha_fire_nova : public SpellScriptLoader
{
public:
- spell_sha_bloodlust() : SpellScriptLoader("spell_sha_bloodlust") { }
+ spell_sha_fire_nova() : SpellScriptLoader("spell_sha_fire_nova") { }
- class spell_sha_bloodlust_SpellScript : public SpellScript
+ class spell_sha_fire_nova_SpellScript : public SpellScript
{
- PrepareSpellScript(spell_sha_bloodlust_SpellScript);
+ PrepareSpellScript(spell_sha_fire_nova_SpellScript);
- bool Validate(SpellInfo const* /*spellEntry*/)
+ bool Validate(SpellInfo const* spellInfo)
{
- if (!sSpellMgr->GetSpellInfo(SHAMAN_SPELL_SATED))
+ if (!sSpellMgr->GetSpellInfo(SPELL_SHAMAN_FIRE_NOVA_R1) || sSpellMgr->GetFirstSpellInChain(SPELL_SHAMAN_FIRE_NOVA_R1) != sSpellMgr->GetFirstSpellInChain(spellInfo->Id))
return false;
- return true;
- }
-
- void RemoveInvalidTargets(std::list<WorldObject*>& targets)
- {
- targets.remove_if(Trinity::UnitAuraCheck(true, SHAMAN_SPELL_SATED));
- }
-
- void ApplyDebuff()
- {
- if (Unit* target = GetHitUnit())
- target->CastSpell(target, SHAMAN_SPELL_SATED, true);
- }
-
- void Register()
- {
- OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_sha_bloodlust_SpellScript::RemoveInvalidTargets, EFFECT_0, TARGET_UNIT_CASTER_AREA_RAID);
- OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_sha_bloodlust_SpellScript::RemoveInvalidTargets, EFFECT_1, TARGET_UNIT_CASTER_AREA_RAID);
- OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_sha_bloodlust_SpellScript::RemoveInvalidTargets, EFFECT_2, TARGET_UNIT_CASTER_AREA_RAID);
- AfterHit += SpellHitFn(spell_sha_bloodlust_SpellScript::ApplyDebuff);
- }
- };
-
- SpellScript* GetSpellScript() const
- {
- return new spell_sha_bloodlust_SpellScript();
- }
-};
-
-class spell_sha_heroism : public SpellScriptLoader
-{
- public:
- spell_sha_heroism() : SpellScriptLoader("spell_sha_heroism") { }
-
- class spell_sha_heroism_SpellScript : public SpellScript
- {
- PrepareSpellScript(spell_sha_heroism_SpellScript);
- bool Validate(SpellInfo const* /*spellEntry*/)
- {
- if (!sSpellMgr->GetSpellInfo(SHAMAN_SPELL_EXHAUSTION))
+ uint8 rank = sSpellMgr->GetSpellRank(spellInfo->Id);
+ if (!sSpellMgr->GetSpellWithRank(SPELL_SHAMAN_FIRE_NOVA_TRIGGERED_R1, rank, true))
return false;
return true;
}
- void RemoveInvalidTargets(std::list<WorldObject*>& targets)
- {
- targets.remove_if(Trinity::UnitAuraCheck(true, SHAMAN_SPELL_EXHAUSTION));
- }
-
- void ApplyDebuff()
- {
- if (Unit* target = GetHitUnit())
- target->CastSpell(target, SHAMAN_SPELL_EXHAUSTION, true);
- }
-
- void Register()
+ SpellCastResult CheckFireTotem()
{
- OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_sha_heroism_SpellScript::RemoveInvalidTargets, EFFECT_0, TARGET_UNIT_CASTER_AREA_RAID);
- OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_sha_heroism_SpellScript::RemoveInvalidTargets, EFFECT_1, TARGET_UNIT_CASTER_AREA_RAID);
- OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_sha_heroism_SpellScript::RemoveInvalidTargets, EFFECT_2, TARGET_UNIT_CASTER_AREA_RAID);
- AfterHit += SpellHitFn(spell_sha_heroism_SpellScript::ApplyDebuff);
- }
- };
-
- SpellScript* GetSpellScript() const
- {
- return new spell_sha_heroism_SpellScript();
- }
-};
-
-enum AncestralAwakeningProc
-{
- SPELL_ANCESTRAL_AWAKENING_PROC = 52752,
-};
-
-class spell_sha_ancestral_awakening_proc : public SpellScriptLoader
-{
- public:
- spell_sha_ancestral_awakening_proc() : SpellScriptLoader("spell_sha_ancestral_awakening_proc") { }
-
- class spell_sha_ancestral_awakening_proc_SpellScript : public SpellScript
- {
- PrepareSpellScript(spell_sha_ancestral_awakening_proc_SpellScript);
+ // fire totem
+ if (!GetCaster()->m_SummonSlot[1])
+ {
+ SetCustomCastResultMessage(SPELL_CUSTOM_ERROR_MUST_HAVE_FIRE_TOTEM);
+ return SPELL_FAILED_CUSTOM_ERROR;
+ }
- bool Validate(SpellInfo const* /*SpellEntry*/)
- {
- if (!sSpellMgr->GetSpellInfo(SPELL_ANCESTRAL_AWAKENING_PROC))
- return false;
- return true;
+ return SPELL_CAST_OK;
}
- void HandleDummy(SpellEffIndex /* effIndex */)
+ void HandleDummy(SpellEffIndex /*effIndex*/)
{
- int32 damage = GetEffectValue();
- if (GetCaster() && GetHitUnit())
- GetCaster()->CastCustomSpell(GetHitUnit(), SPELL_ANCESTRAL_AWAKENING_PROC, &damage, NULL, NULL, true);
+ if (Unit* caster = GetCaster())
+ {
+ uint8 rank = sSpellMgr->GetSpellRank(GetSpellInfo()->Id);
+ if (uint32 spellId = sSpellMgr->GetSpellWithRank(SPELL_SHAMAN_FIRE_NOVA_TRIGGERED_R1, rank))
+ {
+ Creature* totem = caster->GetMap()->GetCreature(caster->m_SummonSlot[1]);
+ if (totem && totem->isTotem())
+ caster->CastSpell(totem, spellId, true);
+ }
+ }
}
void Register()
{
- OnEffectHitTarget += SpellEffectFn(spell_sha_ancestral_awakening_proc_SpellScript::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY);
+ OnCheckCast += SpellCheckCastFn(spell_sha_fire_nova_SpellScript::CheckFireTotem);
+ OnEffectHitTarget += SpellEffectFn(spell_sha_fire_nova_SpellScript::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY);
}
};
SpellScript* GetSpellScript() const
{
- return new spell_sha_ancestral_awakening_proc_SpellScript();
+ return new spell_sha_fire_nova_SpellScript();
}
};
-enum CleansingTotemPulse
-{
- SPELL_CLEANSING_TOTEM_EFFECT = 52025,
-};
-
-class spell_sha_cleansing_totem_pulse : public SpellScriptLoader
+// -8050 - Flame Shock
+class spell_sha_flame_shock : public SpellScriptLoader
{
public:
- spell_sha_cleansing_totem_pulse() : SpellScriptLoader("spell_sha_cleansing_totem_pulse") { }
+ spell_sha_flame_shock() : SpellScriptLoader("spell_sha_flame_shock") { }
- class spell_sha_cleansing_totem_pulse_SpellScript : public SpellScript
+ class spell_sha_flame_shock_AuraScript : public AuraScript
{
- PrepareSpellScript(spell_sha_cleansing_totem_pulse_SpellScript);
+ PrepareAuraScript(spell_sha_flame_shock_AuraScript);
- bool Validate(SpellInfo const* /*SpellEntry*/)
+ bool Validate(SpellInfo const* /*spell*/)
{
- if (!sSpellMgr->GetSpellInfo(SPELL_CLEANSING_TOTEM_EFFECT))
+ if (!sSpellMgr->GetSpellInfo(SPELL_SHAMAN_LAVA_FLOWS_R1))
+ return false;
+ if (!sSpellMgr->GetSpellInfo(SPELL_SHAMAN_LAVA_FLOWS_TRIGGERED_R1))
return false;
return true;
}
- void HandleDummy(SpellEffIndex /* effIndex */)
+ void HandleDispel(DispelInfo* /*dispelInfo*/)
{
- int32 bp = 1;
- if (GetCaster() && GetHitUnit() && GetOriginalCaster())
- GetCaster()->CastCustomSpell(GetHitUnit(), SPELL_CLEANSING_TOTEM_EFFECT, NULL, &bp, NULL, true, NULL, NULL, GetOriginalCaster()->GetGUID());
+ if (Unit* caster = GetCaster())
+ // Lava Flows
+ if (AuraEffect const* aurEff = caster->GetDummyAuraEffect(SPELLFAMILY_SHAMAN, SHAMAN_ICON_ID_SHAMAN_LAVA_FLOW, EFFECT_0))
+ {
+ if (sSpellMgr->GetFirstSpellInChain(SPELL_SHAMAN_LAVA_FLOWS_R1) != sSpellMgr->GetFirstSpellInChain(aurEff->GetId()))
+ return;
+
+ uint8 rank = sSpellMgr->GetSpellRank(aurEff->GetId());
+ caster->CastSpell(caster, sSpellMgr->GetSpellWithRank(SPELL_SHAMAN_LAVA_FLOWS_TRIGGERED_R1, rank), true);
+ }
}
void Register()
{
- OnEffectHitTarget += SpellEffectFn(spell_sha_cleansing_totem_pulse_SpellScript::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY);
+ AfterDispel += AuraDispelFn(spell_sha_flame_shock_AuraScript::HandleDispel);
}
};
- SpellScript* GetSpellScript() const
+ AuraScript* GetAuraScript() const
{
- return new spell_sha_cleansing_totem_pulse_SpellScript();
+ return new spell_sha_flame_shock_AuraScript();
}
};
-enum HealingStreamTotem
-{
- SPELL_GLYPH_OF_HEALING_STREAM_TOTEM = 55456,
- ICON_ID_RESTORATIVE_TOTEMS = 338,
- SPELL_HEALING_STREAM_TOTEM_HEAL = 52042,
-};
-
+// 52041, 52046, 52047, 52048, 52049, 52050, 58759, 58760, 58761 - Healing Stream Totem
class spell_sha_healing_stream_totem : public SpellScriptLoader
{
public:
@@ -481,14 +480,14 @@ class spell_sha_healing_stream_totem : public SpellScriptLoader
{
PrepareSpellScript(spell_sha_healing_stream_totem_SpellScript);
- bool Validate(SpellInfo const* /*SpellEntry*/)
+ bool Validate(SpellInfo const* /*spellInfo*/)
{
- if (!sSpellMgr->GetSpellInfo(SPELL_GLYPH_OF_HEALING_STREAM_TOTEM) || !sSpellMgr->GetSpellInfo(SPELL_HEALING_STREAM_TOTEM_HEAL))
+ if (!sSpellMgr->GetSpellInfo(SPELL_SHAMAN_GLYPH_OF_HEALING_STREAM_TOTEM) || !sSpellMgr->GetSpellInfo(SPELL_SHAMAN_TOTEM_HEALING_STREAM_HEAL))
return false;
return true;
}
- void HandleDummy(SpellEffIndex /* effIndex */)
+ void HandleDummy(SpellEffIndex /*effIndex*/)
{
int32 damage = GetEffectValue();
SpellInfo const* triggeringSpell = GetTriggeringSpell();
@@ -501,16 +500,16 @@ class spell_sha_healing_stream_totem : public SpellScriptLoader
damage = int32(owner->SpellHealingBonusDone(target, triggeringSpell, damage, HEAL));
// Restorative Totems
- if (AuraEffect* dummy = owner->GetAuraEffect(SPELL_AURA_DUMMY, SPELLFAMILY_SHAMAN, ICON_ID_RESTORATIVE_TOTEMS, 1))
+ if (AuraEffect* dummy = owner->GetAuraEffect(SPELL_AURA_DUMMY, SPELLFAMILY_SHAMAN, SHAMAN_ICON_ID_RESTORATIVE_TOTEMS, 1))
AddPct(damage, dummy->GetAmount());
// Glyph of Healing Stream Totem
- if (AuraEffect const* aurEff = owner->GetAuraEffect(SPELL_GLYPH_OF_HEALING_STREAM_TOTEM, EFFECT_0))
+ if (AuraEffect const* aurEff = owner->GetAuraEffect(SPELL_SHAMAN_GLYPH_OF_HEALING_STREAM_TOTEM, EFFECT_0))
AddPct(damage, aurEff->GetAmount());
damage = int32(target->SpellHealingBonusTaken(owner, triggeringSpell, damage, HEAL));
}
- caster->CastCustomSpell(target, SPELL_HEALING_STREAM_TOTEM_HEAL, &damage, 0, 0, true, 0, 0, GetOriginalCaster()->GetGUID());
+ caster->CastCustomSpell(target, SPELL_SHAMAN_TOTEM_HEALING_STREAM_HEAL, &damage, 0, 0, true, 0, 0, GetOriginalCaster()->GetGUID());
}
}
@@ -526,49 +525,50 @@ class spell_sha_healing_stream_totem : public SpellScriptLoader
}
};
-enum ManaSpringTotem
-{
- SPELL_MANA_SPRING_TOTEM_ENERGIZE = 52032,
-};
-
-class spell_sha_mana_spring_totem : public SpellScriptLoader
+// 32182 - Heroism
+class spell_sha_heroism : public SpellScriptLoader
{
public:
- spell_sha_mana_spring_totem() : SpellScriptLoader("spell_sha_mana_spring_totem") { }
+ spell_sha_heroism() : SpellScriptLoader("spell_sha_heroism") { }
- class spell_sha_mana_spring_totem_SpellScript : public SpellScript
+ class spell_sha_heroism_SpellScript : public SpellScript
{
- PrepareSpellScript(spell_sha_mana_spring_totem_SpellScript);
+ PrepareSpellScript(spell_sha_heroism_SpellScript);
- bool Validate(SpellInfo const* /*SpellEntry*/)
+ bool Validate(SpellInfo const* /*spellInfo*/)
{
- if (!sSpellMgr->GetSpellInfo(SPELL_MANA_SPRING_TOTEM_ENERGIZE))
+ if (!sSpellMgr->GetSpellInfo(SPELL_SHAMAN_EXHAUSTION))
return false;
return true;
}
- void HandleDummy(SpellEffIndex /* effIndex */)
+ void RemoveInvalidTargets(std::list<WorldObject*>& targets)
+ {
+ targets.remove_if(Trinity::UnitAuraCheck(true, SPELL_SHAMAN_EXHAUSTION));
+ }
+
+ void ApplyDebuff()
{
- int32 damage = GetEffectValue();
if (Unit* target = GetHitUnit())
- if (Unit* caster = GetCaster())
- if (target->getPowerType() == POWER_MANA)
- caster->CastCustomSpell(target, SPELL_MANA_SPRING_TOTEM_ENERGIZE, &damage, 0, 0, true, 0, 0, GetOriginalCaster()->GetGUID());
+ target->CastSpell(target, SPELL_SHAMAN_EXHAUSTION, true);
}
void Register()
{
- OnEffectHitTarget += SpellEffectFn(spell_sha_mana_spring_totem_SpellScript::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY);
+ OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_sha_heroism_SpellScript::RemoveInvalidTargets, EFFECT_0, TARGET_UNIT_CASTER_AREA_RAID);
+ OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_sha_heroism_SpellScript::RemoveInvalidTargets, EFFECT_1, TARGET_UNIT_CASTER_AREA_RAID);
+ OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_sha_heroism_SpellScript::RemoveInvalidTargets, EFFECT_2, TARGET_UNIT_CASTER_AREA_RAID);
+ AfterHit += SpellHitFn(spell_sha_heroism_SpellScript::ApplyDebuff);
}
-
};
SpellScript* GetSpellScript() const
{
- return new spell_sha_mana_spring_totem_SpellScript();
+ return new spell_sha_heroism_SpellScript();
}
};
+// 60103 - Lava Lash
class spell_sha_lava_lash : public SpellScriptLoader
{
public:
@@ -583,7 +583,7 @@ class spell_sha_lava_lash : public SpellScriptLoader
return GetCaster()->GetTypeId() == TYPEID_PLAYER;
}
- void HandleDummy(SpellEffIndex /* effIndex */)
+ void HandleDummy(SpellEffIndex /*effIndex*/)
{
if (Player* caster = GetCaster()->ToPlayer())
{
@@ -612,100 +612,94 @@ class spell_sha_lava_lash : public SpellScriptLoader
}
};
-// 1064 Chain Heal
-class spell_sha_chain_heal : public SpellScriptLoader
+// 52031, 52033, 52034, 52035, 52036, 58778, 58779, 58780 - Mana Spring Totem
+class spell_sha_mana_spring_totem : public SpellScriptLoader
{
public:
- spell_sha_chain_heal() : SpellScriptLoader("spell_sha_chain_heal") { }
+ spell_sha_mana_spring_totem() : SpellScriptLoader("spell_sha_mana_spring_totem") { }
- class spell_sha_chain_heal_SpellScript : public SpellScript
+ class spell_sha_mana_spring_totem_SpellScript : public SpellScript
{
- PrepareSpellScript(spell_sha_chain_heal_SpellScript);
+ PrepareSpellScript(spell_sha_mana_spring_totem_SpellScript);
- bool Load()
+ bool Validate(SpellInfo const* /*spellInfo*/)
{
- firstHeal = true;
- riptide = false;
+ if (!sSpellMgr->GetSpellInfo(SPELL_SHAMAN_MANA_SPRING_TOTEM_ENERGIZE))
+ return false;
return true;
}
- void HandleHeal(SpellEffIndex /*effIndex*/)
+ void HandleDummy(SpellEffIndex /*effIndex*/)
{
- if (firstHeal)
- {
- // Check if the target has Riptide
- if (AuraEffect* aurEff = GetHitUnit()->GetAuraEffect(SPELL_AURA_PERIODIC_HEAL, SPELLFAMILY_SHAMAN, 0, 0, 0x10, GetCaster()->GetGUID()))
- {
- riptide = true;
- // Consume it
- GetHitUnit()->RemoveAura(aurEff->GetBase());
- }
- firstHeal = false;
- }
- // Riptide increases the Chain Heal effect by 25%
- if (riptide)
- SetHitHeal(GetHitHeal() * 1.25f);
+ int32 damage = GetEffectValue();
+ if (Unit* target = GetHitUnit())
+ if (Unit* caster = GetCaster())
+ if (target->getPowerType() == POWER_MANA)
+ caster->CastCustomSpell(target, SPELL_SHAMAN_MANA_SPRING_TOTEM_ENERGIZE, &damage, 0, 0, true, 0, 0, GetOriginalCaster()->GetGUID());
}
void Register()
{
- OnEffectHitTarget += SpellEffectFn(spell_sha_chain_heal_SpellScript::HandleHeal, EFFECT_0, SPELL_EFFECT_HEAL);
+ OnEffectHitTarget += SpellEffectFn(spell_sha_mana_spring_totem_SpellScript::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY);
}
- bool firstHeal;
- bool riptide;
};
SpellScript* GetSpellScript() const
{
- return new spell_sha_chain_heal_SpellScript();
+ return new spell_sha_mana_spring_totem_SpellScript();
}
};
-class spell_sha_flame_shock : public SpellScriptLoader
+// 39610 - Mana Tide Totem
+class spell_sha_mana_tide_totem : public SpellScriptLoader
{
public:
- spell_sha_flame_shock() : SpellScriptLoader("spell_sha_flame_shock") { }
+ spell_sha_mana_tide_totem() : SpellScriptLoader("spell_sha_mana_tide_totem") { }
- class spell_sha_flame_shock_AuraScript : public AuraScript
+ class spell_sha_mana_tide_totem_SpellScript : public SpellScript
{
- PrepareAuraScript(spell_sha_flame_shock_AuraScript);
+ PrepareSpellScript(spell_sha_mana_tide_totem_SpellScript);
- bool Validate(SpellInfo const* /*spell*/)
+ bool Validate(SpellInfo const* /*spellInfo*/)
{
- if (!sSpellMgr->GetSpellInfo(SHAMAN_LAVA_FLOWS_R1))
- return false;
- if (!sSpellMgr->GetSpellInfo(SHAMAN_LAVA_FLOWS_TRIGGERED_R1))
+ if (!sSpellMgr->GetSpellInfo(SPELL_SHAMAN_GLYPH_OF_MANA_TIDE) || !sSpellMgr->GetSpellInfo(SPELL_SHAMAN_MANA_TIDE_TOTEM))
return false;
return true;
}
- void HandleDispel(DispelInfo* /*dispelInfo*/)
+ void HandleDummy(SpellEffIndex /*effIndex*/)
{
if (Unit* caster = GetCaster())
- // Lava Flows
- if (AuraEffect const* aurEff = caster->GetDummyAuraEffect(SPELLFAMILY_SHAMAN, ICON_ID_SHAMAN_LAVA_FLOW, EFFECT_0))
+ if (Unit* unitTarget = GetHitUnit())
{
- if (sSpellMgr->GetFirstSpellInChain(SHAMAN_LAVA_FLOWS_R1) != sSpellMgr->GetFirstSpellInChain(aurEff->GetId()))
- return;
-
- uint8 rank = sSpellMgr->GetSpellRank(aurEff->GetId());
- caster->CastSpell(caster, sSpellMgr->GetSpellWithRank(SHAMAN_LAVA_FLOWS_TRIGGERED_R1, rank), true);
+ if (unitTarget->getPowerType() == POWER_MANA)
+ {
+ int32 effValue = GetEffectValue();
+ // Glyph of Mana Tide
+ if (Unit* owner = caster->GetOwner())
+ if (AuraEffect* dummy = owner->GetAuraEffect(SPELL_SHAMAN_GLYPH_OF_MANA_TIDE, 0))
+ effValue += dummy->GetAmount();
+ // Regenerate 6% of Total Mana Every 3 secs
+ int32 effBasePoints0 = int32(CalculatePct(unitTarget->GetMaxPower(POWER_MANA), effValue));
+ caster->CastCustomSpell(unitTarget, SPELL_SHAMAN_MANA_TIDE_TOTEM, &effBasePoints0, NULL, NULL, true, NULL, NULL, GetOriginalCaster()->GetGUID());
+ }
}
}
void Register()
{
- AfterDispel += AuraDispelFn(spell_sha_flame_shock_AuraScript::HandleDispel);
+ OnEffectHitTarget += SpellEffectFn(spell_sha_mana_tide_totem_SpellScript::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY);
}
};
- AuraScript* GetAuraScript() const
+ SpellScript* GetSpellScript() const
{
- return new spell_sha_flame_shock_AuraScript();
+ return new spell_sha_mana_tide_totem_SpellScript();
}
};
+// 6495 - Sentry Totem
class spell_sha_sentry_totem : public SpellScriptLoader
{
public:
@@ -717,7 +711,7 @@ class spell_sha_sentry_totem : public SpellScriptLoader
bool Validate(SpellInfo const* /*spell*/)
{
- if (!sSpellMgr->GetSpellInfo(SHAMAN_BIND_SIGHT))
+ if (!sSpellMgr->GetSpellInfo(SPELL_SHAMAN_BIND_SIGHT))
return false;
return true;
}
@@ -727,7 +721,7 @@ class spell_sha_sentry_totem : public SpellScriptLoader
if (Unit* caster = GetCaster())
if (Creature* totem = caster->GetMap()->GetCreature(caster->m_SummonSlot[4]))
if (totem->isTotem())
- caster->CastSpell(totem, SHAMAN_BIND_SIGHT, true);
+ caster->CastSpell(totem, SPELL_SHAMAN_BIND_SIGHT, true);
}
void AfterRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/)
@@ -750,21 +744,51 @@ class spell_sha_sentry_totem : public SpellScriptLoader
}
};
+// -51490 - Thunderstorm
+class spell_sha_thunderstorm : public SpellScriptLoader
+{
+ public:
+ spell_sha_thunderstorm() : SpellScriptLoader("spell_sha_thunderstorm") { }
+
+ class spell_sha_thunderstorm_SpellScript : public SpellScript
+ {
+ PrepareSpellScript(spell_sha_thunderstorm_SpellScript);
+
+ void HandleKnockBack(SpellEffIndex effIndex)
+ {
+ // Glyph of Thunderstorm
+ if (GetCaster()->HasAura(SPELL_SHAMAN_GLYPH_OF_THUNDERSTORM))
+ PreventHitDefaultEffect(effIndex);
+ }
+
+ void Register()
+ {
+ OnEffectHitTarget += SpellEffectFn(spell_sha_thunderstorm_SpellScript::HandleKnockBack, EFFECT_2, SPELL_EFFECT_KNOCK_BACK);
+ }
+ };
+
+ SpellScript* GetSpellScript() const
+ {
+ return new spell_sha_thunderstorm_SpellScript();
+ }
+};
+
void AddSC_shaman_spell_scripts()
{
+ new spell_sha_ancestral_awakening_proc();
new spell_sha_astral_shift();
- new spell_sha_fire_nova();
- new spell_sha_mana_tide_totem();
- new spell_sha_earthbind_totem();
- new spell_sha_earthen_power();
new spell_sha_bloodlust();
- new spell_sha_heroism();
- new spell_sha_ancestral_awakening_proc();
+ new spell_sha_chain_heal();
new spell_sha_cleansing_totem_pulse();
+ new spell_sha_earthbind_totem();
+ new spell_sha_earthen_power();
+ new spell_sha_fire_nova();
+ new spell_sha_flame_shock();
new spell_sha_healing_stream_totem();
- new spell_sha_mana_spring_totem();
+ new spell_sha_heroism();
new spell_sha_lava_lash();
- new spell_sha_chain_heal();
- new spell_sha_flame_shock();
+ new spell_sha_mana_spring_totem();
+ new spell_sha_mana_tide_totem();
new spell_sha_sentry_totem();
+ new spell_sha_thunderstorm();
}
diff --git a/src/server/scripts/Spells/spell_warlock.cpp b/src/server/scripts/Spells/spell_warlock.cpp
index 2070933173b..68ad1315dce 100644
--- a/src/server/scripts/Spells/spell_warlock.cpp
+++ b/src/server/scripts/Spells/spell_warlock.cpp
@@ -28,141 +28,88 @@
enum WarlockSpells
{
- WARLOCK_DEMONIC_EMPOWERMENT_SUCCUBUS = 54435,
- WARLOCK_DEMONIC_EMPOWERMENT_VOIDWALKER = 54443,
- WARLOCK_DEMONIC_EMPOWERMENT_FELGUARD = 54508,
- WARLOCK_DEMONIC_EMPOWERMENT_FELHUNTER = 54509,
- WARLOCK_DEMONIC_EMPOWERMENT_IMP = 54444,
- WARLOCK_IMPROVED_HEALTHSTONE_R1 = 18692,
- WARLOCK_IMPROVED_HEALTHSTONE_R2 = 18693,
- WARLOCK_DEMONIC_CIRCLE_SUMMON = 48018,
- WARLOCK_DEMONIC_CIRCLE_TELEPORT = 48020,
- WARLOCK_DEMONIC_CIRCLE_ALLOW_CAST = 62388,
- WARLOCK_HAUNT = 48181,
- WARLOCK_HAUNT_HEAL = 48210,
- WARLOCK_UNSTABLE_AFFLICTION_DISPEL = 31117,
- WARLOCK_CURSE_OF_DOOM_EFFECT = 18662,
- WARLOCK_IMPROVED_HEALTH_FUNNEL_R1 = 18703,
- WARLOCK_IMPROVED_HEALTH_FUNNEL_R2 = 18704,
- WARLOCK_IMPROVED_HEALTH_FUNNEL_BUFF_R1 = 60955,
- WARLOCK_IMPROVED_HEALTH_FUNNEL_BUFF_R2 = 60956,
+ SPELL_WARLOCK_CURSE_OF_DOOM_EFFECT = 18662,
+ SPELL_WARLOCK_DEMONIC_CIRCLE_SUMMON = 48018,
+ SPELL_WARLOCK_DEMONIC_CIRCLE_TELEPORT = 48020,
+ SPELL_WARLOCK_DEMONIC_CIRCLE_ALLOW_CAST = 62388,
+ SPELL_WARLOCK_DEMONIC_EMPOWERMENT_SUCCUBUS = 54435,
+ SPELL_WARLOCK_DEMONIC_EMPOWERMENT_VOIDWALKER = 54443,
+ SPELL_WARLOCK_DEMONIC_EMPOWERMENT_FELGUARD = 54508,
+ SPELL_WARLOCK_DEMONIC_EMPOWERMENT_FELHUNTER = 54509,
+ SPELL_WARLOCK_DEMONIC_EMPOWERMENT_IMP = 54444,
+ SPELL_WARLOCK_IMPROVED_HEALTHSTONE_R1 = 18692,
+ SPELL_WARLOCK_IMPROVED_HEALTHSTONE_R2 = 18693,
+ SPELL_WARLOCK_IMPROVED_HEALTH_FUNNEL_R1 = 18703,
+ SPELL_WARLOCK_IMPROVED_HEALTH_FUNNEL_R2 = 18704,
+ SPELL_WARLOCK_IMPROVED_HEALTH_FUNNEL_BUFF_R1 = 60955,
+ SPELL_WARLOCK_IMPROVED_HEALTH_FUNNEL_BUFF_R2 = 60956,
+ SPELL_WARLOCK_HAUNT = 48181,
+ SPELL_WARLOCK_HAUNT_HEAL = 48210,
+ SPELL_WARLOCK_LIFE_TAP_ENERGIZE = 31818,
+ SPELL_WARLOCK_LIFE_TAP_ENERGIZE_2 = 32553,
+ SPELL_WARLOCK_SOULSHATTER = 32835,
+ SPELL_WARLOCK_UNSTABLE_AFFLICTION_DISPEL = 31117
};
-class spell_warl_banish : public SpellScriptLoader
+enum WarlockSpellIcons
{
-public:
- spell_warl_banish() : SpellScriptLoader("spell_warl_banish") { }
-
- class spell_warl_banish_SpellScript : public SpellScript
- {
- PrepareSpellScript(spell_warl_banish_SpellScript);
-
- bool Load()
- {
- _removed = false;
- return true;
- }
-
- void HandleBanish()
- {
- if (Unit* target = GetHitUnit())
- {
- if (target->GetAuraEffect(SPELL_AURA_SCHOOL_IMMUNITY, SPELLFAMILY_WARLOCK, 0, 0x08000000, 0))
- {
- //No need to remove old aura since its removed due to not stack by current Banish aura
- PreventHitDefaultEffect(EFFECT_0);
- PreventHitDefaultEffect(EFFECT_1);
- PreventHitDefaultEffect(EFFECT_2);
- _removed = true;
- }
- }
- }
-
- void RemoveAura()
- {
- if (_removed)
- PreventHitAura();
- }
-
- void Register()
- {
- BeforeHit += SpellHitFn(spell_warl_banish_SpellScript::HandleBanish);
- AfterHit += SpellHitFn(spell_warl_banish_SpellScript::RemoveAura);
- }
-
- bool _removed;
- };
-
- SpellScript* GetSpellScript() const
- {
- return new spell_warl_banish_SpellScript();
- }
+ WARLOCK_ICON_ID_IMPROVED_LIFE_TAP = 208,
+ WARLOCK_ICON_ID_MANA_FEED = 1982
};
-// 47193 Demonic Empowerment
-class spell_warl_demonic_empowerment : public SpellScriptLoader
+// 710, 18647 - Banish
+class spell_warl_banish : public SpellScriptLoader
{
public:
- spell_warl_demonic_empowerment() : SpellScriptLoader("spell_warl_demonic_empowerment") { }
+ spell_warl_banish() : SpellScriptLoader("spell_warl_banish") { }
- class spell_warl_demonic_empowerment_SpellScript : public SpellScript
+ class spell_warl_banish_SpellScript : public SpellScript
{
- PrepareSpellScript(spell_warl_demonic_empowerment_SpellScript);
+ PrepareSpellScript(spell_warl_banish_SpellScript);
- bool Validate(SpellInfo const* /*spellEntry*/)
+ bool Load()
{
- if (!sSpellMgr->GetSpellInfo(WARLOCK_DEMONIC_EMPOWERMENT_SUCCUBUS) || !sSpellMgr->GetSpellInfo(WARLOCK_DEMONIC_EMPOWERMENT_VOIDWALKER) || !sSpellMgr->GetSpellInfo(WARLOCK_DEMONIC_EMPOWERMENT_FELGUARD) || !sSpellMgr->GetSpellInfo(WARLOCK_DEMONIC_EMPOWERMENT_FELHUNTER) || !sSpellMgr->GetSpellInfo(WARLOCK_DEMONIC_EMPOWERMENT_IMP))
- return false;
+ _removed = false;
return true;
}
- void HandleScriptEffect(SpellEffIndex /*effIndex*/)
+ void HandleBanish()
{
- if (Creature* targetCreature = GetHitCreature())
+ if (Unit* target = GetHitUnit())
{
- if (targetCreature->isPet())
+ if (target->GetAuraEffect(SPELL_AURA_SCHOOL_IMMUNITY, SPELLFAMILY_WARLOCK, 0, 0x08000000, 0))
{
- CreatureTemplate const* ci = sObjectMgr->GetCreatureTemplate(targetCreature->GetEntry());
- switch (ci->family)
- {
- case CREATURE_FAMILY_SUCCUBUS:
- targetCreature->CastSpell(targetCreature, WARLOCK_DEMONIC_EMPOWERMENT_SUCCUBUS, true);
- break;
- case CREATURE_FAMILY_VOIDWALKER:
- {
- SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(WARLOCK_DEMONIC_EMPOWERMENT_VOIDWALKER);
- int32 hp = int32(targetCreature->CountPctFromMaxHealth(GetCaster()->CalculateSpellDamage(targetCreature, spellInfo, 0)));
- targetCreature->CastCustomSpell(targetCreature, WARLOCK_DEMONIC_EMPOWERMENT_VOIDWALKER, &hp, NULL, NULL, true);
- //unitTarget->CastSpell(unitTarget, 54441, true);
- break;
- }
- case CREATURE_FAMILY_FELGUARD:
- targetCreature->CastSpell(targetCreature, WARLOCK_DEMONIC_EMPOWERMENT_FELGUARD, true);
- break;
- case CREATURE_FAMILY_FELHUNTER:
- targetCreature->CastSpell(targetCreature, WARLOCK_DEMONIC_EMPOWERMENT_FELHUNTER, true);
- break;
- case CREATURE_FAMILY_IMP:
- targetCreature->CastSpell(targetCreature, WARLOCK_DEMONIC_EMPOWERMENT_IMP, true);
- break;
- }
+ // No need to remove old aura since its removed due to not stack by current Banish aura
+ PreventHitDefaultEffect(EFFECT_0);
+ PreventHitDefaultEffect(EFFECT_1);
+ PreventHitDefaultEffect(EFFECT_2);
+ _removed = true;
}
}
}
+ void RemoveAura()
+ {
+ if (_removed)
+ PreventHitAura();
+ }
+
void Register()
{
- OnEffectHitTarget += SpellEffectFn(spell_warl_demonic_empowerment_SpellScript::HandleScriptEffect, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT);
+ BeforeHit += SpellHitFn(spell_warl_banish_SpellScript::HandleBanish);
+ AfterHit += SpellHitFn(spell_warl_banish_SpellScript::RemoveAura);
}
+
+ bool _removed;
};
SpellScript* GetSpellScript() const
{
- return new spell_warl_demonic_empowerment_SpellScript();
+ return new spell_warl_banish_SpellScript();
}
};
-// 6201 Create Healthstone (and ranks)
+// 6201 - Create Healthstone (and ranks)
class spell_warl_create_healthstone : public SpellScriptLoader
{
public:
@@ -174,9 +121,9 @@ class spell_warl_create_healthstone : public SpellScriptLoader
static uint32 const iTypes[8][3];
- bool Validate(SpellInfo const* /*spellEntry*/)
+ bool Validate(SpellInfo const* /*spellInfo*/)
{
- if (!sSpellMgr->GetSpellInfo(WARLOCK_IMPROVED_HEALTHSTONE_R1) || !sSpellMgr->GetSpellInfo(WARLOCK_IMPROVED_HEALTHSTONE_R2))
+ if (!sSpellMgr->GetSpellInfo(SPELL_WARLOCK_IMPROVED_HEALTHSTONE_R1) || !sSpellMgr->GetSpellInfo(SPELL_WARLOCK_IMPROVED_HEALTHSTONE_R2))
return false;
return true;
}
@@ -204,8 +151,12 @@ class spell_warl_create_healthstone : public SpellScriptLoader
{
switch (aurEff->GetId())
{
- case WARLOCK_IMPROVED_HEALTHSTONE_R1: rank = 1; break;
- case WARLOCK_IMPROVED_HEALTHSTONE_R2: rank = 2; break;
+ case SPELL_WARLOCK_IMPROVED_HEALTHSTONE_R1:
+ rank = 1;
+ break;
+ case SPELL_WARLOCK_IMPROVED_HEALTHSTONE_R2:
+ rank = 2;
+ break;
default:
sLog->outError(LOG_FILTER_SPELLS_AURAS, "Unknown rank of Improved Healthstone id: %d", aurEff->GetId());
break;
@@ -241,142 +192,336 @@ uint32 const spell_warl_create_healthstone::spell_warl_create_healthstone_SpellS
{36892, 36893, 36894} // Fel Healthstone
};
-// 47422 Everlasting Affliction
-class spell_warl_everlasting_affliction : public SpellScriptLoader
+// -603 - Curse of Doom
+class spell_warl_curse_of_doom : public SpellScriptLoader
{
public:
- spell_warl_everlasting_affliction() : SpellScriptLoader("spell_warl_everlasting_affliction") { }
+ spell_warl_curse_of_doom() : SpellScriptLoader("spell_warl_curse_of_doom") { }
- class spell_warl_everlasting_affliction_SpellScript : public SpellScript
+ class spell_warl_curse_of_doom_AuraScript : public AuraScript
{
- PrepareSpellScript(spell_warl_everlasting_affliction_SpellScript);
+ PrepareAuraScript(spell_warl_curse_of_doom_AuraScript);
- void HandleScriptEffect(SpellEffIndex /*effIndex*/)
+ bool Validate(SpellInfo const* /*spell*/)
{
- if (Unit* unitTarget = GetHitUnit())
- // Refresh corruption on target
- if (AuraEffect* aur = unitTarget->GetAuraEffect(SPELL_AURA_PERIODIC_DAMAGE, SPELLFAMILY_WARLOCK, 0x2, 0, 0, GetCaster()->GetGUID()))
- aur->GetBase()->RefreshDuration();
+ if (!sSpellMgr->GetSpellInfo(SPELL_WARLOCK_CURSE_OF_DOOM_EFFECT))
+ return false;
+ return true;
+ }
+
+ bool Load()
+ {
+ return GetCaster() && GetCaster()->GetTypeId() == TYPEID_PLAYER;
+ }
+
+ void OnRemove(AuraEffect const* aurEff, AuraEffectHandleModes /*mode*/)
+ {
+ if (!GetCaster())
+ return;
+
+ AuraRemoveMode removeMode = GetTargetApplication()->GetRemoveMode();
+ if (removeMode != AURA_REMOVE_BY_DEATH || !IsExpired())
+ return;
+
+ if (GetCaster()->ToPlayer()->isHonorOrXPTarget(GetTarget()))
+ GetCaster()->CastSpell(GetTarget(), SPELL_WARLOCK_CURSE_OF_DOOM_EFFECT, true, NULL, aurEff);
}
void Register()
{
- OnEffectHitTarget += SpellEffectFn(spell_warl_everlasting_affliction_SpellScript::HandleScriptEffect, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT);
+ AfterEffectRemove += AuraEffectRemoveFn(spell_warl_curse_of_doom_AuraScript::OnRemove, EFFECT_0, SPELL_AURA_PERIODIC_DAMAGE, AURA_EFFECT_HANDLE_REAL);
}
};
- SpellScript* GetSpellScript() const
+ AuraScript* GetAuraScript() const
{
- return new spell_warl_everlasting_affliction_SpellScript();
+ return new spell_warl_curse_of_doom_AuraScript();
}
};
-// 18541 Ritual of Doom Effect
-class spell_warl_ritual_of_doom_effect : public SpellScriptLoader
+// 48018 - Demonic Circle Summon
+class spell_warl_demonic_circle_summon : public SpellScriptLoader
{
-public:
- spell_warl_ritual_of_doom_effect() : SpellScriptLoader("spell_warl_ritual_of_doom_effect") { }
+ public:
+ spell_warl_demonic_circle_summon() : SpellScriptLoader("spell_warl_demonic_circle_summon") { }
+
+ class spell_warl_demonic_circle_summon_AuraScript : public AuraScript
+ {
+ PrepareAuraScript(spell_warl_demonic_circle_summon_AuraScript);
+
+ void HandleRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes mode)
+ {
+ // If effect is removed by expire remove the summoned demonic circle too.
+ if (!(mode & AURA_EFFECT_HANDLE_REAPPLY))
+ GetTarget()->RemoveGameObject(GetId(), true);
+
+ GetTarget()->RemoveAura(SPELL_WARLOCK_DEMONIC_CIRCLE_ALLOW_CAST);
+ }
+
+ void HandleDummyTick(AuraEffect const* /*aurEff*/)
+ {
+ if (GameObject* circle = GetTarget()->GetGameObject(GetId()))
+ {
+ // Here we check if player is in demonic circle teleport range, if so add
+ // WARLOCK_DEMONIC_CIRCLE_ALLOW_CAST; allowing him to cast the WARLOCK_DEMONIC_CIRCLE_TELEPORT.
+ // If not in range remove the WARLOCK_DEMONIC_CIRCLE_ALLOW_CAST.
+
+ SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(SPELL_WARLOCK_DEMONIC_CIRCLE_TELEPORT);
+
+ if (GetTarget()->IsWithinDist(circle, spellInfo->GetMaxRange(true)))
+ {
+ if (!GetTarget()->HasAura(SPELL_WARLOCK_DEMONIC_CIRCLE_ALLOW_CAST))
+ GetTarget()->CastSpell(GetTarget(), SPELL_WARLOCK_DEMONIC_CIRCLE_ALLOW_CAST, true);
+ }
+ else
+ GetTarget()->RemoveAura(SPELL_WARLOCK_DEMONIC_CIRCLE_ALLOW_CAST);
+ }
+ }
- class spell_warl_ritual_of_doom_effect_SpellScript : public SpellScript
- {
- PrepareSpellScript(spell_warl_ritual_of_doom_effect_SpellScript);
+ void Register()
+ {
+ OnEffectRemove += AuraEffectApplyFn(spell_warl_demonic_circle_summon_AuraScript::HandleRemove, EFFECT_0, SPELL_AURA_PERIODIC_DUMMY, AURA_EFFECT_HANDLE_REAL_OR_REAPPLY_MASK);
+ OnEffectPeriodic += AuraEffectPeriodicFn(spell_warl_demonic_circle_summon_AuraScript::HandleDummyTick, EFFECT_0, SPELL_AURA_PERIODIC_DUMMY);
+ }
+ };
- void HandleDummy(SpellEffIndex /*effIndex*/)
+ AuraScript* GetAuraScript() const
{
- Unit* caster = GetCaster();
- caster->CastSpell(caster, GetEffectValue(), true);
+ return new spell_warl_demonic_circle_summon_AuraScript();
}
+};
- void Register()
+// 48020 - Demonic Circle Teleport
+class spell_warl_demonic_circle_teleport : public SpellScriptLoader
+{
+ public:
+ spell_warl_demonic_circle_teleport() : SpellScriptLoader("spell_warl_demonic_circle_teleport") { }
+
+ class spell_warl_demonic_circle_teleport_AuraScript : public AuraScript
{
- OnEffectHit += SpellEffectFn(spell_warl_ritual_of_doom_effect_SpellScript::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY);
- }
- };
+ PrepareAuraScript(spell_warl_demonic_circle_teleport_AuraScript);
- SpellScript* GetSpellScript() const
- {
- return new spell_warl_ritual_of_doom_effect_SpellScript();
- }
+ void HandleTeleport(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/)
+ {
+ if (Player* player = GetTarget()->ToPlayer())
+ {
+ if (GameObject* circle = player->GetGameObject(SPELL_WARLOCK_DEMONIC_CIRCLE_SUMMON))
+ {
+ player->NearTeleportTo(circle->GetPositionX(), circle->GetPositionY(), circle->GetPositionZ(), circle->GetOrientation());
+ player->RemoveMovementImpairingAuras();
+ }
+ }
+ }
+
+ void Register()
+ {
+ OnEffectApply += AuraEffectApplyFn(spell_warl_demonic_circle_teleport_AuraScript::HandleTeleport, EFFECT_0, SPELL_AURA_MECHANIC_IMMUNITY, AURA_EFFECT_HANDLE_REAL);
+ }
+ };
+
+ AuraScript* GetAuraScript() const
+ {
+ return new spell_warl_demonic_circle_teleport_AuraScript();
+ }
};
-class spell_warl_seed_of_corruption : public SpellScriptLoader
+// 47193 - Demonic Empowerment
+class spell_warl_demonic_empowerment : public SpellScriptLoader
{
public:
- spell_warl_seed_of_corruption() : SpellScriptLoader("spell_warl_seed_of_corruption") { }
+ spell_warl_demonic_empowerment() : SpellScriptLoader("spell_warl_demonic_empowerment") { }
- class spell_warl_seed_of_corruption_SpellScript : public SpellScript
+ class spell_warl_demonic_empowerment_SpellScript : public SpellScript
{
- PrepareSpellScript(spell_warl_seed_of_corruption_SpellScript);
+ PrepareSpellScript(spell_warl_demonic_empowerment_SpellScript);
- void FilterTargets(std::list<WorldObject*>& targets)
+ bool Validate(SpellInfo const* /*spellInfo*/)
{
- if (GetExplTargetUnit())
- targets.remove(GetExplTargetUnit());
+ if (!sSpellMgr->GetSpellInfo(SPELL_WARLOCK_DEMONIC_EMPOWERMENT_SUCCUBUS) || !sSpellMgr->GetSpellInfo(SPELL_WARLOCK_DEMONIC_EMPOWERMENT_VOIDWALKER) || !sSpellMgr->GetSpellInfo(SPELL_WARLOCK_DEMONIC_EMPOWERMENT_FELGUARD) || !sSpellMgr->GetSpellInfo(SPELL_WARLOCK_DEMONIC_EMPOWERMENT_FELHUNTER) || !sSpellMgr->GetSpellInfo(SPELL_WARLOCK_DEMONIC_EMPOWERMENT_IMP))
+ return false;
+ return true;
+ }
+
+ void HandleScriptEffect(SpellEffIndex /*effIndex*/)
+ {
+ if (Creature* targetCreature = GetHitCreature())
+ {
+ if (targetCreature->isPet())
+ {
+ CreatureTemplate const* ci = sObjectMgr->GetCreatureTemplate(targetCreature->GetEntry());
+ switch (ci->family)
+ {
+ case CREATURE_FAMILY_SUCCUBUS:
+ targetCreature->CastSpell(targetCreature, SPELL_WARLOCK_DEMONIC_EMPOWERMENT_SUCCUBUS, true);
+ break;
+ case CREATURE_FAMILY_VOIDWALKER:
+ {
+ SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(SPELL_WARLOCK_DEMONIC_EMPOWERMENT_VOIDWALKER);
+ int32 hp = int32(targetCreature->CountPctFromMaxHealth(GetCaster()->CalculateSpellDamage(targetCreature, spellInfo, 0)));
+ targetCreature->CastCustomSpell(targetCreature, SPELL_WARLOCK_DEMONIC_EMPOWERMENT_VOIDWALKER, &hp, NULL, NULL, true);
+ //unitTarget->CastSpell(unitTarget, 54441, true);
+ break;
+ }
+ case CREATURE_FAMILY_FELGUARD:
+ targetCreature->CastSpell(targetCreature, SPELL_WARLOCK_DEMONIC_EMPOWERMENT_FELGUARD, true);
+ break;
+ case CREATURE_FAMILY_FELHUNTER:
+ targetCreature->CastSpell(targetCreature, SPELL_WARLOCK_DEMONIC_EMPOWERMENT_FELHUNTER, true);
+ break;
+ case CREATURE_FAMILY_IMP:
+ targetCreature->CastSpell(targetCreature, SPELL_WARLOCK_DEMONIC_EMPOWERMENT_IMP, true);
+ break;
+ }
+ }
+ }
}
void Register()
{
- OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_warl_seed_of_corruption_SpellScript::FilterTargets, EFFECT_0, TARGET_UNIT_DEST_AREA_ENEMY);
+ OnEffectHitTarget += SpellEffectFn(spell_warl_demonic_empowerment_SpellScript::HandleScriptEffect, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT);
}
};
SpellScript* GetSpellScript() const
{
- return new spell_warl_seed_of_corruption_SpellScript();
+ return new spell_warl_demonic_empowerment_SpellScript();
}
};
-enum Soulshatter
+// 47422 - Everlasting Affliction
+class spell_warl_everlasting_affliction : public SpellScriptLoader
{
- SPELL_SOULSHATTER = 32835,
+ public:
+ spell_warl_everlasting_affliction() : SpellScriptLoader("spell_warl_everlasting_affliction") { }
+
+ class spell_warl_everlasting_affliction_SpellScript : public SpellScript
+ {
+ PrepareSpellScript(spell_warl_everlasting_affliction_SpellScript);
+
+ void HandleScriptEffect(SpellEffIndex /*effIndex*/)
+ {
+ if (Unit* unitTarget = GetHitUnit())
+ // Refresh corruption on target
+ if (AuraEffect* aur = unitTarget->GetAuraEffect(SPELL_AURA_PERIODIC_DAMAGE, SPELLFAMILY_WARLOCK, 0x2, 0, 0, GetCaster()->GetGUID()))
+ aur->GetBase()->RefreshDuration();
+ }
+
+ void Register()
+ {
+ OnEffectHitTarget += SpellEffectFn(spell_warl_everlasting_affliction_SpellScript::HandleScriptEffect, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT);
+ }
+ };
+
+ SpellScript* GetSpellScript() const
+ {
+ return new spell_warl_everlasting_affliction_SpellScript();
+ }
};
-class spell_warl_soulshatter : public SpellScriptLoader
+// -48181 - Haunt
+class spell_warl_haunt : public SpellScriptLoader
{
public:
- spell_warl_soulshatter() : SpellScriptLoader("spell_warl_soulshatter") { }
+ spell_warl_haunt() : SpellScriptLoader("spell_warl_haunt") { }
- class spell_warl_soulshatter_SpellScript : public SpellScript
+ class spell_warl_haunt_SpellScript : public SpellScript
{
- PrepareSpellScript(spell_warl_soulshatter_SpellScript);
+ PrepareSpellScript(spell_warl_haunt_SpellScript);
+
+ void HandleOnHit()
+ {
+ if (Aura* aura = GetHitAura())
+ if (AuraEffect* aurEff = aura->GetEffect(EFFECT_1))
+ aurEff->SetAmount(CalculatePct(aurEff->GetAmount(), GetHitDamage()));
+ }
+
+ void Register()
+ {
+ OnHit += SpellHitFn(spell_warl_haunt_SpellScript::HandleOnHit);
+ }
+ };
+
+ class spell_warl_haunt_AuraScript : public AuraScript
+ {
+ PrepareAuraScript(spell_warl_haunt_AuraScript);
bool Validate(SpellInfo const* /*spell*/)
{
- if (!sSpellMgr->GetSpellInfo(SPELL_SOULSHATTER))
+ if (!sSpellMgr->GetSpellInfo(SPELL_WARLOCK_HAUNT_HEAL))
return false;
return true;
}
- void HandleDummy(SpellEffIndex /*effIndex*/)
+ void HandleRemove(AuraEffect const* aurEff, AuraEffectHandleModes /*mode*/)
{
- Unit* caster = GetCaster();
- if (Unit* target = GetHitUnit())
+ if (Unit* caster = GetCaster())
{
- if (target->CanHaveThreatList() && target->getThreatManager().getThreat(caster) > 0.0f)
- caster->CastSpell(target, SPELL_SOULSHATTER, true);
+ int32 amount = aurEff->GetAmount();
+ GetTarget()->CastCustomSpell(caster, SPELL_WARLOCK_HAUNT_HEAL, &amount, NULL, NULL, true, NULL, aurEff, GetCasterGUID());
}
}
void Register()
{
- OnEffectHitTarget += SpellEffectFn(spell_warl_soulshatter_SpellScript::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY);
+ OnEffectRemove += AuraEffectApplyFn(spell_warl_haunt_AuraScript::HandleRemove, EFFECT_1, SPELL_AURA_DUMMY, AURA_EFFECT_HANDLE_REAL_OR_REAPPLY_MASK);
}
};
SpellScript* GetSpellScript() const
{
- return new spell_warl_soulshatter_SpellScript();
+ return new spell_warl_haunt_SpellScript();
+ }
+
+ AuraScript* GetAuraScript() const
+ {
+ return new spell_warl_haunt_AuraScript();
}
};
-enum LifeTap
+// -755 - Health Funnel
+class spell_warl_health_funnel : public SpellScriptLoader
{
- SPELL_LIFE_TAP_ENERGIZE = 31818,
- SPELL_LIFE_TAP_ENERGIZE_2 = 32553,
- ICON_ID_IMPROVED_LIFE_TAP = 208,
- ICON_ID_MANA_FEED = 1982,
+ public:
+ spell_warl_health_funnel() : SpellScriptLoader("spell_warl_health_funnel") { }
+
+ class spell_warl_health_funnel_AuraScript : public AuraScript
+ {
+ PrepareAuraScript(spell_warl_health_funnel_AuraScript);
+
+ void ApplyEffect(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/)
+ {
+ Unit* caster = GetCaster();
+ if (!caster)
+ return;
+
+ Unit* target = GetTarget();
+ if (caster->HasAura(SPELL_WARLOCK_IMPROVED_HEALTH_FUNNEL_R2))
+ target->CastSpell(target, SPELL_WARLOCK_IMPROVED_HEALTH_FUNNEL_BUFF_R2, true);
+ else if (caster->HasAura(SPELL_WARLOCK_IMPROVED_HEALTH_FUNNEL_R1))
+ target->CastSpell(target, SPELL_WARLOCK_IMPROVED_HEALTH_FUNNEL_BUFF_R1, true);
+ }
+
+ void RemoveEffect(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/)
+ {
+ Unit* target = GetTarget();
+ target->RemoveAurasDueToSpell(SPELL_WARLOCK_IMPROVED_HEALTH_FUNNEL_BUFF_R1);
+ target->RemoveAurasDueToSpell(SPELL_WARLOCK_IMPROVED_HEALTH_FUNNEL_BUFF_R2);
+ }
+
+ void Register()
+ {
+ OnEffectRemove += AuraEffectRemoveFn(spell_warl_health_funnel_AuraScript::RemoveEffect, EFFECT_0, SPELL_AURA_PERIODIC_HEAL, AURA_EFFECT_HANDLE_REAL);
+ OnEffectApply += AuraEffectApplyFn(spell_warl_health_funnel_AuraScript::ApplyEffect, EFFECT_0, SPELL_AURA_PERIODIC_HEAL, AURA_EFFECT_HANDLE_REAL);
+ }
+ };
+
+ AuraScript* GetAuraScript() const
+ {
+ return new spell_warl_health_funnel_AuraScript();
+ }
};
+// -1454 - Life Tap
class spell_warl_life_tap : public SpellScriptLoader
{
public:
@@ -393,7 +538,7 @@ class spell_warl_life_tap : public SpellScriptLoader
bool Validate(SpellInfo const* /*spell*/)
{
- if (!sSpellMgr->GetSpellInfo(SPELL_LIFE_TAP_ENERGIZE) || !sSpellMgr->GetSpellInfo(SPELL_LIFE_TAP_ENERGIZE_2))
+ if (!sSpellMgr->GetSpellInfo(SPELL_WARLOCK_LIFE_TAP_ENERGIZE) || !sSpellMgr->GetSpellInfo(SPELL_WARLOCK_LIFE_TAP_ENERGIZE_2))
return false;
return true;
}
@@ -410,20 +555,20 @@ class spell_warl_life_tap : public SpellScriptLoader
target->ModifyHealth(-damage);
// Improved Life Tap mod
- if (AuraEffect const* aurEff = caster->GetDummyAuraEffect(SPELLFAMILY_WARLOCK, ICON_ID_IMPROVED_LIFE_TAP, 0))
+ if (AuraEffect const* aurEff = caster->GetDummyAuraEffect(SPELLFAMILY_WARLOCK, WARLOCK_ICON_ID_IMPROVED_LIFE_TAP, 0))
AddPct(mana, aurEff->GetAmount());
- caster->CastCustomSpell(target, SPELL_LIFE_TAP_ENERGIZE, &mana, NULL, NULL, false);
+ caster->CastCustomSpell(target, SPELL_WARLOCK_LIFE_TAP_ENERGIZE, &mana, NULL, NULL, false);
// Mana Feed
int32 manaFeedVal = 0;
- if (AuraEffect const* aurEff = caster->GetAuraEffect(SPELL_AURA_ADD_FLAT_MODIFIER, SPELLFAMILY_WARLOCK, ICON_ID_MANA_FEED, 0))
+ if (AuraEffect const* aurEff = caster->GetAuraEffect(SPELL_AURA_ADD_FLAT_MODIFIER, SPELLFAMILY_WARLOCK, WARLOCK_ICON_ID_MANA_FEED, 0))
manaFeedVal = aurEff->GetAmount();
if (manaFeedVal > 0)
{
ApplyPct(manaFeedVal, mana);
- caster->CastCustomSpell(caster, SPELL_LIFE_TAP_ENERGIZE_2, &manaFeedVal, NULL, NULL, true, NULL);
+ caster->CastCustomSpell(caster, SPELL_WARLOCK_LIFE_TAP_ENERGIZE_2, &manaFeedVal, NULL, NULL, true, NULL);
}
}
}
@@ -448,149 +593,139 @@ class spell_warl_life_tap : public SpellScriptLoader
}
};
-class spell_warl_demonic_circle_summon : public SpellScriptLoader
+// 18541 - Ritual of Doom Effect
+class spell_warl_ritual_of_doom_effect : public SpellScriptLoader
{
public:
- spell_warl_demonic_circle_summon() : SpellScriptLoader("spell_warl_demonic_circle_summon") { }
+ spell_warl_ritual_of_doom_effect() : SpellScriptLoader("spell_warl_ritual_of_doom_effect") { }
- class spell_warl_demonic_circle_summon_AuraScript : public AuraScript
+ class spell_warl_ritual_of_doom_effect_SpellScript : public SpellScript
{
- PrepareAuraScript(spell_warl_demonic_circle_summon_AuraScript);
-
- void HandleRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes mode)
- {
- // If effect is removed by expire remove the summoned demonic circle too.
- if (!(mode & AURA_EFFECT_HANDLE_REAPPLY))
- GetTarget()->RemoveGameObject(GetId(), true);
-
- GetTarget()->RemoveAura(WARLOCK_DEMONIC_CIRCLE_ALLOW_CAST);
- }
+ PrepareSpellScript(spell_warl_ritual_of_doom_effect_SpellScript);
- void HandleDummyTick(AuraEffect const* /*aurEff*/)
+ void HandleDummy(SpellEffIndex /*effIndex*/)
{
- if (GameObject* circle = GetTarget()->GetGameObject(GetId()))
- {
- // Here we check if player is in demonic circle teleport range, if so add
- // WARLOCK_DEMONIC_CIRCLE_ALLOW_CAST; allowing him to cast the WARLOCK_DEMONIC_CIRCLE_TELEPORT.
- // If not in range remove the WARLOCK_DEMONIC_CIRCLE_ALLOW_CAST.
-
- SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(WARLOCK_DEMONIC_CIRCLE_TELEPORT);
-
- if (GetTarget()->IsWithinDist(circle, spellInfo->GetMaxRange(true)))
- {
- if (!GetTarget()->HasAura(WARLOCK_DEMONIC_CIRCLE_ALLOW_CAST))
- GetTarget()->CastSpell(GetTarget(), WARLOCK_DEMONIC_CIRCLE_ALLOW_CAST, true);
- }
- else
- GetTarget()->RemoveAura(WARLOCK_DEMONIC_CIRCLE_ALLOW_CAST);
- }
+ Unit* caster = GetCaster();
+ caster->CastSpell(caster, GetEffectValue(), true);
}
void Register()
{
- OnEffectRemove += AuraEffectApplyFn(spell_warl_demonic_circle_summon_AuraScript::HandleRemove, EFFECT_0, SPELL_AURA_PERIODIC_DUMMY, AURA_EFFECT_HANDLE_REAL_OR_REAPPLY_MASK);
- OnEffectPeriodic += AuraEffectPeriodicFn(spell_warl_demonic_circle_summon_AuraScript::HandleDummyTick, EFFECT_0, SPELL_AURA_PERIODIC_DUMMY);
+ OnEffectHit += SpellEffectFn(spell_warl_ritual_of_doom_effect_SpellScript::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY);
}
};
- AuraScript* GetAuraScript() const
+ SpellScript* GetSpellScript() const
{
- return new spell_warl_demonic_circle_summon_AuraScript();
+ return new spell_warl_ritual_of_doom_effect_SpellScript();
}
};
-class spell_warl_demonic_circle_teleport : public SpellScriptLoader
+// -27285 - Seed of Corruption
+class spell_warl_seed_of_corruption : public SpellScriptLoader
{
public:
- spell_warl_demonic_circle_teleport() : SpellScriptLoader("spell_warl_demonic_circle_teleport") { }
+ spell_warl_seed_of_corruption() : SpellScriptLoader("spell_warl_seed_of_corruption") { }
- class spell_warl_demonic_circle_teleport_AuraScript : public AuraScript
+ class spell_warl_seed_of_corruption_SpellScript : public SpellScript
{
- PrepareAuraScript(spell_warl_demonic_circle_teleport_AuraScript);
+ PrepareSpellScript(spell_warl_seed_of_corruption_SpellScript);
- void HandleTeleport(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/)
+ void FilterTargets(std::list<WorldObject*>& targets)
{
- if (Player* player = GetTarget()->ToPlayer())
- {
- if (GameObject* circle = player->GetGameObject(WARLOCK_DEMONIC_CIRCLE_SUMMON))
- {
- player->NearTeleportTo(circle->GetPositionX(), circle->GetPositionY(), circle->GetPositionZ(), circle->GetOrientation());
- player->RemoveMovementImpairingAuras();
- }
- }
+ if (GetExplTargetUnit())
+ targets.remove(GetExplTargetUnit());
}
void Register()
{
- OnEffectApply += AuraEffectApplyFn(spell_warl_demonic_circle_teleport_AuraScript::HandleTeleport, EFFECT_0, SPELL_AURA_MECHANIC_IMMUNITY, AURA_EFFECT_HANDLE_REAL);
+ OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_warl_seed_of_corruption_SpellScript::FilterTargets, EFFECT_0, TARGET_UNIT_DEST_AREA_ENEMY);
}
};
- AuraScript* GetAuraScript() const
+ SpellScript* GetSpellScript() const
{
- return new spell_warl_demonic_circle_teleport_AuraScript();
+ return new spell_warl_seed_of_corruption_SpellScript();
}
};
-class spell_warl_haunt : public SpellScriptLoader
+// -7235 - Shadow Ward
+class spell_warl_shadow_ward : public SpellScriptLoader
{
public:
- spell_warl_haunt() : SpellScriptLoader("spell_warl_haunt") { }
+ spell_warl_shadow_ward() : SpellScriptLoader("spell_warl_shadow_ward") { }
- class spell_warl_haunt_SpellScript : public SpellScript
+ class spell_warl_shadow_ward_AuraScript : public AuraScript
{
- PrepareSpellScript(spell_warl_haunt_SpellScript);
+ PrepareAuraScript(spell_warl_shadow_ward_AuraScript);
- void HandleOnHit()
+ void CalculateAmount(AuraEffect const* /*aurEff*/, int32& amount, bool& canBeRecalculated)
{
- if (Aura* aura = GetHitAura())
- if (AuraEffect* aurEff = aura->GetEffect(EFFECT_1))
- aurEff->SetAmount(CalculatePct(aurEff->GetAmount(), GetHitDamage()));
+ canBeRecalculated = false;
+ if (Unit* caster = GetCaster())
+ {
+ // +80.68% from sp bonus
+ float bonus = 0.8068f;
+
+ bonus *= caster->SpellBaseHealingBonusDone(GetSpellInfo()->GetSchoolMask());
+ bonus *= caster->CalculateLevelPenalty(GetSpellInfo());
+
+ amount += int32(bonus);
+ }
}
void Register()
{
- OnHit += SpellHitFn(spell_warl_haunt_SpellScript::HandleOnHit);
+ DoEffectCalcAmount += AuraEffectCalcAmountFn(spell_warl_shadow_ward_AuraScript::CalculateAmount, EFFECT_0, SPELL_AURA_SCHOOL_ABSORB);
}
};
- class spell_warl_haunt_AuraScript : public AuraScript
+ AuraScript* GetAuraScript() const
{
- PrepareAuraScript(spell_warl_haunt_AuraScript);
+ return new spell_warl_shadow_ward_AuraScript();
+ }
+};
+
+// 29858 - Soulshatter
+class spell_warl_soulshatter : public SpellScriptLoader
+{
+ public:
+ spell_warl_soulshatter() : SpellScriptLoader("spell_warl_soulshatter") { }
+
+ class spell_warl_soulshatter_SpellScript : public SpellScript
+ {
+ PrepareSpellScript(spell_warl_soulshatter_SpellScript);
bool Validate(SpellInfo const* /*spell*/)
{
- if (!sSpellMgr->GetSpellInfo(WARLOCK_HAUNT_HEAL))
+ if (!sSpellMgr->GetSpellInfo(SPELL_WARLOCK_SOULSHATTER))
return false;
return true;
}
- void HandleRemove(AuraEffect const* aurEff, AuraEffectHandleModes /*mode*/)
+ void HandleDummy(SpellEffIndex /*effIndex*/)
{
- if (Unit* caster = GetCaster())
+ Unit* caster = GetCaster();
+ if (Unit* target = GetHitUnit())
{
- int32 amount = aurEff->GetAmount();
- GetTarget()->CastCustomSpell(caster, WARLOCK_HAUNT_HEAL, &amount, NULL, NULL, true, NULL, aurEff, GetCasterGUID());
+ if (target->CanHaveThreatList() && target->getThreatManager().getThreat(caster) > 0.0f)
+ caster->CastSpell(target, SPELL_WARLOCK_SOULSHATTER, true);
}
}
void Register()
{
- OnEffectRemove += AuraEffectApplyFn(spell_warl_haunt_AuraScript::HandleRemove, EFFECT_1, SPELL_AURA_DUMMY, AURA_EFFECT_HANDLE_REAL_OR_REAPPLY_MASK);
+ OnEffectHitTarget += SpellEffectFn(spell_warl_soulshatter_SpellScript::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY);
}
};
SpellScript* GetSpellScript() const
{
- return new spell_warl_haunt_SpellScript();
- }
-
- AuraScript* GetAuraScript() const
- {
- return new spell_warl_haunt_AuraScript();
+ return new spell_warl_soulshatter_SpellScript();
}
};
+// -30108 - Unstable Affliction
class spell_warl_unstable_affliction : public SpellScriptLoader
{
public:
@@ -602,7 +737,7 @@ class spell_warl_unstable_affliction : public SpellScriptLoader
bool Validate(SpellInfo const* /*spell*/)
{
- if (!sSpellMgr->GetSpellInfo(WARLOCK_UNSTABLE_AFFLICTION_DISPEL))
+ if (!sSpellMgr->GetSpellInfo(SPELL_WARLOCK_UNSTABLE_AFFLICTION_DISPEL))
return false;
return true;
}
@@ -614,7 +749,7 @@ class spell_warl_unstable_affliction : public SpellScriptLoader
{
int32 damage = aurEff->GetAmount() * 9;
// backfire damage and silence
- caster->CastCustomSpell(dispelInfo->GetDispeller(), WARLOCK_UNSTABLE_AFFLICTION_DISPEL, &damage, NULL, NULL, true, NULL, aurEff);
+ caster->CastCustomSpell(dispelInfo->GetDispeller(), SPELL_WARLOCK_UNSTABLE_AFFLICTION_DISPEL, &damage, NULL, NULL, true, NULL, aurEff);
}
}
@@ -630,108 +765,21 @@ class spell_warl_unstable_affliction : public SpellScriptLoader
}
};
-class spell_warl_curse_of_doom : public SpellScriptLoader
-{
- public:
- spell_warl_curse_of_doom() : SpellScriptLoader("spell_warl_curse_of_doom") { }
-
- class spell_warl_curse_of_doom_AuraScript : public AuraScript
- {
- PrepareAuraScript(spell_warl_curse_of_doom_AuraScript);
-
- bool Validate(SpellInfo const* /*spell*/)
- {
- if (!sSpellMgr->GetSpellInfo(WARLOCK_CURSE_OF_DOOM_EFFECT))
- return false;
- return true;
- }
-
- bool Load()
- {
- return GetCaster() && GetCaster()->GetTypeId() == TYPEID_PLAYER;
- }
-
- void OnRemove(AuraEffect const* aurEff, AuraEffectHandleModes /*mode*/)
- {
- if (!GetCaster())
- return;
-
- AuraRemoveMode removeMode = GetTargetApplication()->GetRemoveMode();
- if (removeMode != AURA_REMOVE_BY_DEATH || !IsExpired())
- return;
-
- if (GetCaster()->ToPlayer()->isHonorOrXPTarget(GetTarget()))
- GetCaster()->CastSpell(GetTarget(), WARLOCK_CURSE_OF_DOOM_EFFECT, true, NULL, aurEff);
- }
-
- void Register()
- {
- AfterEffectRemove += AuraEffectRemoveFn(spell_warl_curse_of_doom_AuraScript::OnRemove, EFFECT_0, SPELL_AURA_PERIODIC_DAMAGE, AURA_EFFECT_HANDLE_REAL);
- }
- };
-
- AuraScript* GetAuraScript() const
- {
- return new spell_warl_curse_of_doom_AuraScript();
- }
-};
-
-class spell_warl_health_funnel : public SpellScriptLoader
-{
-public:
- spell_warl_health_funnel() : SpellScriptLoader("spell_warl_health_funnel") { }
-
- class spell_warl_health_funnel_AuraScript : public AuraScript
- {
- PrepareAuraScript(spell_warl_health_funnel_AuraScript);
-
- void ApplyEffect(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/)
- {
- Unit* caster = GetCaster();
- if (!caster)
- return;
-
- Unit* target = GetTarget();
- if (caster->HasAura(WARLOCK_IMPROVED_HEALTH_FUNNEL_R2))
- target->CastSpell(target, WARLOCK_IMPROVED_HEALTH_FUNNEL_BUFF_R2, true);
- else if (caster->HasAura(WARLOCK_IMPROVED_HEALTH_FUNNEL_R1))
- target->CastSpell(target, WARLOCK_IMPROVED_HEALTH_FUNNEL_BUFF_R1, true);
- }
-
- void RemoveEffect(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/)
- {
- Unit* target = GetTarget();
- target->RemoveAurasDueToSpell(WARLOCK_IMPROVED_HEALTH_FUNNEL_BUFF_R1);
- target->RemoveAurasDueToSpell(WARLOCK_IMPROVED_HEALTH_FUNNEL_BUFF_R2);
- }
-
- void Register()
- {
- OnEffectRemove += AuraEffectRemoveFn(spell_warl_health_funnel_AuraScript::RemoveEffect, EFFECT_0, SPELL_AURA_PERIODIC_HEAL, AURA_EFFECT_HANDLE_REAL);
- OnEffectApply += AuraEffectApplyFn(spell_warl_health_funnel_AuraScript::ApplyEffect, EFFECT_0, SPELL_AURA_PERIODIC_HEAL, AURA_EFFECT_HANDLE_REAL);
- }
- };
-
- AuraScript* GetAuraScript() const
- {
- return new spell_warl_health_funnel_AuraScript();
- }
-};
-
void AddSC_warlock_spell_scripts()
{
new spell_warl_banish();
- new spell_warl_demonic_empowerment();
new spell_warl_create_healthstone();
+ new spell_warl_curse_of_doom();
+ new spell_warl_demonic_circle_summon();
+ new spell_warl_demonic_circle_teleport();
+ new spell_warl_demonic_empowerment();
new spell_warl_everlasting_affliction();
+ new spell_warl_haunt();
+ new spell_warl_health_funnel();
+ new spell_warl_life_tap();
new spell_warl_ritual_of_doom_effect();
new spell_warl_seed_of_corruption();
+ new spell_warl_shadow_ward();
new spell_warl_soulshatter();
- new spell_warl_life_tap();
- new spell_warl_demonic_circle_summon();
- new spell_warl_demonic_circle_teleport();
- new spell_warl_haunt();
new spell_warl_unstable_affliction();
- new spell_warl_curse_of_doom();
- new spell_warl_health_funnel();
}
diff --git a/src/server/scripts/Spells/spell_warrior.cpp b/src/server/scripts/Spells/spell_warrior.cpp
index f84265b89de..822d4b17992 100644
--- a/src/server/scripts/Spells/spell_warrior.cpp
+++ b/src/server/scripts/Spells/spell_warrior.cpp
@@ -41,6 +41,7 @@ enum WarriorSpells
SPELL_WARRIOR_JUGGERNAUT_CRIT_BONUS_TALENT = 64976,
SPELL_WARRIOR_LAST_STAND_TRIGGERED = 12976,
SPELL_WARRIOR_SLAM = 50783,
+ SPELL_WARRIOR_TAUNT = 355,
SPELL_WARRIOR_UNRELENTING_ASSAULT_RANK_1 = 46859,
SPELL_WARRIOR_UNRELENTING_ASSAULT_RANK_2 = 46860,
SPELL_WARRIOR_UNRELENTING_ASSAULT_TRIGGER_1 = 64849,
@@ -333,6 +334,34 @@ class spell_warr_improved_spell_reflection : public SpellScriptLoader
}
};
+// 5246 - Intimidating Shout
+class spell_warr_intimidating_shout : public SpellScriptLoader
+{
+ public:
+ spell_warr_intimidating_shout() : SpellScriptLoader("spell_warr_intimidating_shout") { }
+
+ class spell_warr_intimidating_shout_SpellScript : public SpellScript
+ {
+ PrepareSpellScript(spell_warr_intimidating_shout_SpellScript);
+
+ void FilterTargets(std::list<WorldObject*>& unitList)
+ {
+ unitList.remove(GetExplTargetWorldObject());
+ }
+
+ void Register()
+ {
+ OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_warr_intimidating_shout_SpellScript::FilterTargets, EFFECT_1, TARGET_UNIT_SRC_AREA_ENEMY);
+ OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_warr_intimidating_shout_SpellScript::FilterTargets, EFFECT_2, TARGET_UNIT_SRC_AREA_ENEMY);
+ }
+ };
+
+ SpellScript* GetSpellScript() const
+ {
+ return new spell_warr_intimidating_shout_SpellScript();
+ }
+};
+
// 12975 - Last Stand
class spell_warr_last_stand : public SpellScriptLoader
{
@@ -409,6 +438,83 @@ class spell_warr_overpower : public SpellScriptLoader
}
};
+// -772 - Rend
+class spell_warr_rend : public SpellScriptLoader
+{
+ public:
+ spell_warr_rend() : SpellScriptLoader("spell_warr_rend") { }
+
+ class spell_warr_rend_AuraScript : public AuraScript
+ {
+ PrepareAuraScript(spell_warr_rend_AuraScript);
+
+ void CalculateAmount(AuraEffect const* aurEff, int32& amount, bool& canBeRecalculated)
+ {
+ if (Unit* caster = GetCaster())
+ {
+ canBeRecalculated = false;
+
+ // $0.2 * (($MWB + $mwb) / 2 + $AP / 14 * $MWS) bonus per tick
+ float ap = caster->GetTotalAttackPowerValue(BASE_ATTACK);
+ int32 mws = caster->GetAttackTime(BASE_ATTACK);
+ float mwbMin = caster->GetWeaponDamageRange(BASE_ATTACK, MINDAMAGE);
+ float mwbMax = caster->GetWeaponDamageRange(BASE_ATTACK, MAXDAMAGE);
+ float mwb = ((mwbMin + mwbMax) / 2 + ap * mws / 14000) * 0.2f;
+ amount += int32(caster->ApplyEffectModifiers(GetSpellInfo(), aurEff->GetEffIndex(), mwb));
+
+ // "If used while your target is above 75% health, Rend does 35% more damage."
+ // as for 3.1.3 only ranks above 9 (wrong tooltip?)
+ if (GetSpellInfo()->GetRank() >= 9)
+ {
+ if (GetUnitOwner()->HasAuraState(AURA_STATE_HEALTH_ABOVE_75_PERCENT, GetSpellInfo(), caster))
+ AddPct(amount, GetSpellInfo()->Effects[EFFECT_2].CalcValue(caster));
+ }
+ }
+ }
+
+ void Register()
+ {
+ DoEffectCalcAmount += AuraEffectCalcAmountFn(spell_warr_rend_AuraScript::CalculateAmount, EFFECT_0, SPELL_AURA_PERIODIC_DAMAGE);
+ }
+ };
+
+ AuraScript* GetAuraScript() const
+ {
+ return new spell_warr_rend_AuraScript();
+ }
+};
+
+// 64380, 65941 - Shattering Throw
+class spell_warr_shattering_throw : public SpellScriptLoader
+{
+ public:
+ spell_warr_shattering_throw() : SpellScriptLoader("spell_warr_shattering_throw") { }
+
+ class spell_warr_shattering_throw_SpellScript : public SpellScript
+ {
+ PrepareSpellScript(spell_warr_shattering_throw_SpellScript);
+
+ void HandleScript(SpellEffIndex effIndex)
+ {
+ PreventHitDefaultEffect(effIndex);
+
+ // remove shields, will still display immune to damage part
+ if (Unit* target = GetHitUnit())
+ target->RemoveAurasWithMechanic(1 << MECHANIC_IMMUNE_SHIELD, AURA_REMOVE_BY_ENEMY_SPELL);
+ }
+
+ void Register()
+ {
+ OnEffectHitTarget += SpellEffectFn(spell_warr_shattering_throw_SpellScript::HandleScript, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT);
+ }
+ };
+
+ SpellScript* GetSpellScript() const
+ {
+ return new spell_warr_shattering_throw_SpellScript();
+ }
+};
+
// -1464 - Slam
class spell_warr_slam : public SpellScriptLoader
{
@@ -493,6 +599,37 @@ class spell_warr_vigilance : public SpellScriptLoader
}
};
+// 50725 Vigilance
+class spell_warr_vigilance_trigger : public SpellScriptLoader
+{
+ public:
+ spell_warr_vigilance_trigger() : SpellScriptLoader("spell_warr_vigilance_trigger") { }
+
+ class spell_warr_vigilance_trigger_SpellScript : public SpellScript
+ {
+ PrepareSpellScript(spell_warr_vigilance_trigger_SpellScript);
+
+ void HandleScript(SpellEffIndex effIndex)
+ {
+ PreventHitDefaultEffect(effIndex);
+
+ // Remove Taunt cooldown
+ if (Player* target = GetHitPlayer())
+ target->RemoveSpellCooldown(SPELL_WARRIOR_TAUNT, true);
+ }
+
+ void Register()
+ {
+ OnEffectHitTarget += SpellEffectFn(spell_warr_vigilance_trigger_SpellScript::HandleScript, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT);
+ }
+ };
+
+ SpellScript* GetSpellScript() const
+ {
+ return new spell_warr_vigilance_trigger_SpellScript();
+ }
+};
+
void AddSC_warrior_spell_scripts()
{
new spell_warr_bloodthirst();
@@ -502,8 +639,12 @@ void AddSC_warrior_spell_scripts()
new spell_warr_deep_wounds();
new spell_warr_execute();
new spell_warr_improved_spell_reflection();
+ new spell_warr_intimidating_shout();
new spell_warr_last_stand();
new spell_warr_overpower();
+ new spell_warr_rend();
+ new spell_warr_shattering_throw();
new spell_warr_slam();
new spell_warr_vigilance();
+ new spell_warr_vigilance_trigger();
}
diff --git a/src/server/scripts/World/npcs_special.cpp b/src/server/scripts/World/npcs_special.cpp
index aaac8ee4b83..8a3342cf2de 100644
--- a/src/server/scripts/World/npcs_special.cpp
+++ b/src/server/scripts/World/npcs_special.cpp
@@ -2509,11 +2509,11 @@ public:
break;
case GOSSIP_ACTION_INFO_DEF + 8:
player->CLOSE_GOSSIP_MENU();
- player->AddItem(ITEM_KEY_TO_THE_FOCUSING_IRIS,1);
+ player->AddItem(ITEM_KEY_TO_THE_FOCUSING_IRIS, 1);
break;
case GOSSIP_ACTION_INFO_DEF + 9:
player->CLOSE_GOSSIP_MENU();
- player->AddItem(ITEM_HC_KEY_TO_THE_FOCUSING_IRIS,1);
+ player->AddItem(ITEM_HC_KEY_TO_THE_FOCUSING_IRIS, 1);
break;
}
return true;
@@ -2828,7 +2828,7 @@ public:
{
if (!me->FindNearestCreature(NPC_OMEN, 100.0f, false) && me->GetDistance2d(omenSummonPos.GetPositionX(), omenSummonPos.GetPositionY()) <= 100.0f)
{
- switch (urand(0,9))
+ switch (urand(0, 9))
{
case 0:
case 1:
diff --git a/src/server/shared/CMakeLists.txt b/src/server/shared/CMakeLists.txt
index 86d0cbf613b..01ccf648b31 100644
--- a/src/server/shared/CMakeLists.txt
+++ b/src/server/shared/CMakeLists.txt
@@ -50,6 +50,7 @@ set(shared_STAT_SRCS
include_directories(
${CMAKE_BINARY_DIR}
+ ${CMAKE_SOURCE_DIR}/dep/recastnavigation/Detour
${CMAKE_SOURCE_DIR}/dep/SFMT
${CMAKE_SOURCE_DIR}/dep/sockets/include
${CMAKE_SOURCE_DIR}/dep/utf8cpp
diff --git a/src/server/shared/Cryptography/WardenKeyGeneration.h b/src/server/shared/Cryptography/WardenKeyGeneration.h
index 5c04da38dc9..6d5fd563ba3 100644
--- a/src/server/shared/Cryptography/WardenKeyGeneration.h
+++ b/src/server/shared/Cryptography/WardenKeyGeneration.h
@@ -73,7 +73,7 @@ private:
SHA1Hash sh;
uint32 taken;
- uint8 o0[20],o1[20],o2[20];
+ uint8 o0[20], o1[20], o2[20];
};
#endif
diff --git a/src/server/shared/Database/Implementation/CharacterDatabase.cpp b/src/server/shared/Database/Implementation/CharacterDatabase.cpp
index cfbd4b2bc45..1c2410b8b53 100644
--- a/src/server/shared/Database/Implementation/CharacterDatabase.cpp
+++ b/src/server/shared/Database/Implementation/CharacterDatabase.cpp
@@ -374,7 +374,8 @@ void CharacterDatabaseConnection::DoPrepareStatements()
PrepareStatement(CHAR_UPD_GROUP_DIFFICULTY, "UPDATE groups SET difficulty = ? WHERE guid = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_UPD_GROUP_RAID_DIFFICULTY, "UPDATE groups SET raiddifficulty = ? WHERE guid = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_DEL_ALL_GM_TICKETS, "TRUNCATE TABLE gm_tickets", CONNECTION_ASYNC);
- PrepareStatement(CHAR_DEL_INVALID_SPELL, "DELETE FROM character_talent WHERE spell = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_DEL_INVALID_SPELL_TALENTS, "DELETE FROM character_talent WHERE spell = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_DEL_INVALID_SPELL_SPELLS, "DELETE FROM character_spell WHERE spell = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_UPD_DELETE_INFO, "UPDATE characters SET deleteInfos_Name = name, deleteInfos_Account = account, deleteDate = UNIX_TIMESTAMP(), name = '', account = 0 WHERE guid = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_UDP_RESTORE_DELETE_INFO, "UPDATE characters SET name = ?, account = ?, deleteDate = NULL, deleteInfos_Name = NULL, deleteInfos_Account = NULL WHERE deleteDate IS NOT NULL AND guid = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_UPD_ZONE, "UPDATE characters SET zone = ? WHERE guid = ?", CONNECTION_ASYNC);
diff --git a/src/server/shared/Database/Implementation/CharacterDatabase.h b/src/server/shared/Database/Implementation/CharacterDatabase.h
index bfa7bc48cf5..e4728e19934 100644
--- a/src/server/shared/Database/Implementation/CharacterDatabase.h
+++ b/src/server/shared/Database/Implementation/CharacterDatabase.h
@@ -315,7 +315,8 @@ enum CharacterDatabaseStatements
CHAR_UPD_GROUP_MEMBER_FLAG,
CHAR_UPD_GROUP_DIFFICULTY,
CHAR_UPD_GROUP_RAID_DIFFICULTY,
- CHAR_DEL_INVALID_SPELL,
+ CHAR_DEL_INVALID_SPELL_SPELLS,
+ CHAR_DEL_INVALID_SPELL_TALENTS,
CHAR_UPD_DELETE_INFO,
CHAR_UDP_RESTORE_DELETE_INFO,
CHAR_UPD_ZONE,
diff --git a/src/server/shared/Debugging/Errors.h b/src/server/shared/Debugging/Errors.h
index 3d10740f149..10e94634e9a 100644
--- a/src/server/shared/Debugging/Errors.h
+++ b/src/server/shared/Debugging/Errors.h
@@ -24,7 +24,7 @@
#include <ace/Stack_Trace.h>
#include <ace/OS_NS_unistd.h>
-#define WPAssert(assertion) { if (!(assertion)) { ACE_Stack_Trace st; sLog->outError(LOG_FILTER_GENERAL, "\n%s:%i in %s ASSERTION FAILED:\n %s\n%s\n", __FILE__, __LINE__, __FUNCTION__, #assertion, st.c_str()); *((volatile int*)NULL) = 0; } }
+#define WPAssert(assertion) { if (!(assertion)) { ACE_Stack_Trace st; fprintf(stderr, "\n%s:%i in %s ASSERTION FAILED:\n %s\n%s\n", __FILE__, __LINE__, __FUNCTION__, #assertion, st.c_str()); *((volatile int*)NULL) = 0; } }
#define WPError(assertion, errmsg) { if (!(assertion)) { sLog->outError(LOG_FILTER_GENERAL, "%\n%s:%i in %s ERROR:\n %s\n", __FILE__, __LINE__, __FUNCTION__, (char *)errmsg); *((volatile int*)NULL) = 0; } }
#define WPWarning(assertion, errmsg) { if (!(assertion)) { sLog->outError(LOG_FILTER_GENERAL, "\n%s:%i in %s WARNING:\n %s\n", __FILE__, __LINE__, __FUNCTION__, (char *)errmsg); } }
#define WPFatal(assertion, errmsg) { if (!(assertion)) { sLog->outError(LOG_FILTER_GENERAL, "\n%s:%i in %s FATAL ERROR:\n %s\n", __FILE__, __LINE__, __FUNCTION__, (char *)errmsg); ACE_OS::sleep(10); *((volatile int*)NULL) = 0; } }
diff --git a/src/server/shared/Dynamic/HashNamespace.h b/src/server/shared/Dynamic/HashNamespace.h
index afb55ae0e00..88fa8c6f00e 100644
--- a/src/server/shared/Dynamic/HashNamespace.h
+++ b/src/server/shared/Dynamic/HashNamespace.h
@@ -93,7 +93,7 @@ template<> struct hash<std::string>
{
size_t operator()(const std::string &__x) const
{
- return hash<char const *>()(__x.c_str());
+ return hash<char const*>()(__x.c_str());
}
};
diff --git a/src/server/shared/Logging/Appender.cpp b/src/server/shared/Logging/Appender.cpp
index f2f5da53dae..1d215e1212e 100644
--- a/src/server/shared/Logging/Appender.cpp
+++ b/src/server/shared/Logging/Appender.cpp
@@ -73,10 +73,7 @@ void Appender::setLogLevel(LogLevel _level)
void Appender::write(LogMessage& message)
{
if (!level || level > message.level)
- {
- //fprintf(stderr, "Appender::write: Appender %s, Level %s. Msg %s Level %s Type %s WRONG LEVEL MASK\n", getName().c_str(), getLogLevelString(level), message.text.c_str(), getLogLevelString(message.level), getLogFilterTypeString(message.type)); // DEBUG - RemoveMe
return;
- }
message.prefix.clear();
if (flags & APPENDER_FLAGS_PREFIX_TIMESTAMP)
@@ -222,5 +219,6 @@ char const* Appender::getLogFilterTypeString(LogFilterType type)
default:
break;
}
+
return "???";
}
diff --git a/src/server/shared/Logging/Appender.h b/src/server/shared/Logging/Appender.h
index 6a0f0bdac26..a8854a8abc6 100644
--- a/src/server/shared/Logging/Appender.h
+++ b/src/server/shared/Logging/Appender.h
@@ -143,7 +143,7 @@ class Appender
static const char* getLogFilterTypeString(LogFilterType type);
private:
- virtual void _write(LogMessage& /*message*/) = 0;
+ virtual void _write(LogMessage const& /*message*/) = 0;
uint8 id;
std::string name;
diff --git a/src/server/shared/Logging/AppenderConsole.cpp b/src/server/shared/Logging/AppenderConsole.cpp
index d0af761188c..a1212bd135b 100644
--- a/src/server/shared/Logging/AppenderConsole.cpp
+++ b/src/server/shared/Logging/AppenderConsole.cpp
@@ -154,7 +154,7 @@ void AppenderConsole::ResetColor(bool stdout_stream)
#endif
}
-void AppenderConsole::_write(LogMessage& message)
+void AppenderConsole::_write(LogMessage const& message)
{
bool stdout_stream = message.level == LOG_LEVEL_ERROR || message.level == LOG_LEVEL_FATAL;
diff --git a/src/server/shared/Logging/AppenderConsole.h b/src/server/shared/Logging/AppenderConsole.h
index 3319c84e887..6f3fcca901c 100644
--- a/src/server/shared/Logging/AppenderConsole.h
+++ b/src/server/shared/Logging/AppenderConsole.h
@@ -51,7 +51,7 @@ class AppenderConsole: public Appender
private:
void SetColor(bool stdout_stream, ColorTypes color);
void ResetColor(bool stdout_stream);
- void _write(LogMessage& message);
+ void _write(LogMessage const& message);
bool _colored;
ColorTypes _colors[MaxLogLevels];
};
diff --git a/src/server/shared/Logging/AppenderDB.cpp b/src/server/shared/Logging/AppenderDB.cpp
index 86677eeedd8..ae5fc17de73 100644
--- a/src/server/shared/Logging/AppenderDB.cpp
+++ b/src/server/shared/Logging/AppenderDB.cpp
@@ -18,8 +18,8 @@
#include "AppenderDB.h"
#include "Database/DatabaseEnv.h"
-AppenderDB::AppenderDB(uint8 id, std::string const& name, LogLevel level, uint32 realmId)
- : Appender(id, name, APPENDER_DB, level), realm(realmId), enable(false)
+AppenderDB::AppenderDB(uint8 id, std::string const& name, LogLevel level)
+ : Appender(id, name, APPENDER_DB, level), realmId(0), enabled(false)
{
}
@@ -27,10 +27,11 @@ AppenderDB::~AppenderDB()
{
}
-void AppenderDB::_write(LogMessage& message)
+void AppenderDB::_write(LogMessage const& message)
{
- if (!enable)
+ if (!enabled)
return;
+
switch (message.type)
{
case LOG_FILTER_SQL:
@@ -40,7 +41,7 @@ void AppenderDB::_write(LogMessage& message)
default:
PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_INS_LOG);
stmt->setUInt64(0, message.mtime);
- stmt->setUInt32(1, realm);
+ stmt->setUInt32(1, realmId);
stmt->setUInt8(2, uint8(message.type));
stmt->setUInt8(3, uint8(message.level));
stmt->setString(4, message.text);
@@ -49,7 +50,8 @@ void AppenderDB::_write(LogMessage& message)
}
}
-void AppenderDB::setEnable(bool _enable)
+void AppenderDB::setRealmId(uint32 _realmId)
{
- enable = _enable;
+ enabled = true;
+ realmId = _realmId;
}
diff --git a/src/server/shared/Logging/AppenderDB.h b/src/server/shared/Logging/AppenderDB.h
index 5ab9a1ee423..f9dde0a1e82 100644
--- a/src/server/shared/Logging/AppenderDB.h
+++ b/src/server/shared/Logging/AppenderDB.h
@@ -23,14 +23,15 @@
class AppenderDB: public Appender
{
public:
- AppenderDB(uint8 _id, std::string const& _name, LogLevel level, uint32 realmId);
+ AppenderDB(uint8 _id, std::string const& _name, LogLevel level);
~AppenderDB();
- void setEnable(bool enable);
+
+ void setRealmId(uint32 realmId);
private:
- uint32 realm;
- bool enable;
- void _write(LogMessage& message);
+ uint32 realmId;
+ bool enabled;
+ void _write(LogMessage const& message);
};
#endif
diff --git a/src/server/shared/Logging/AppenderFile.cpp b/src/server/shared/Logging/AppenderFile.cpp
index 7b0bac03d03..8189237bb4e 100644
--- a/src/server/shared/Logging/AppenderFile.cpp
+++ b/src/server/shared/Logging/AppenderFile.cpp
@@ -39,7 +39,7 @@ AppenderFile::~AppenderFile()
}
}
-void AppenderFile::_write(LogMessage& message)
+void AppenderFile::_write(LogMessage const& message)
{
if (dynamicName)
{
@@ -70,5 +70,6 @@ FILE* AppenderFile::OpenFile(std::string const &filename, std::string const &mod
newName.append(LogMessage::getTimeStr(time(NULL)));
rename(filename.c_str(), newName.c_str()); // no error handling... if we couldn't make a backup, just ignore
}
+
return fopen((logDir + filename).c_str(), mode.c_str());
}
diff --git a/src/server/shared/Logging/AppenderFile.h b/src/server/shared/Logging/AppenderFile.h
index 934370d70b4..a3fe285cc7d 100644
--- a/src/server/shared/Logging/AppenderFile.h
+++ b/src/server/shared/Logging/AppenderFile.h
@@ -28,7 +28,7 @@ class AppenderFile: public Appender
FILE* OpenFile(std::string const& _name, std::string const& _mode, bool _backup);
private:
- void _write(LogMessage& message);
+ void _write(LogMessage const& message);
FILE* logfile;
std::string filename;
std::string logDir;
diff --git a/src/server/shared/Logging/Log.cpp b/src/server/shared/Logging/Log.cpp
index 4048f056f46..96c72b5eb74 100644
--- a/src/server/shared/Logging/Log.cpp
+++ b/src/server/shared/Logging/Log.cpp
@@ -31,7 +31,6 @@
Log::Log() : worker(NULL)
{
- SetRealmID(0);
m_logsTimestamp = "_" + GetTimestampStr();
LoadFromConfig();
}
@@ -82,7 +81,7 @@ void Log::CreateAppenderFromConfig(const char* name)
if (!name || *name == '\0')
return;
- // Format=type,level,flags,optional1,optional2
+ // Format=type, level, flags, optional1, optional2
// if type = File. optional1 = file and option2 = mode
// if type = Console. optional1 = Color
std::string options = "Appender.";
@@ -154,7 +153,7 @@ void Log::CreateAppenderFromConfig(const char* name)
case APPENDER_DB:
{
uint8 id = NextAppenderId();
- appenders[id] = new AppenderDB(id, name, level, realm);
+ appenders[id] = new AppenderDB(id, name, level);
break;
}
default:
@@ -265,13 +264,6 @@ void Log::ReadLoggersFromConfig()
loggers[LOG_FILTER_GENERAL].Create("root", LOG_FILTER_GENERAL, LOG_LEVEL_DISABLED);
}
-void Log::EnableDBAppenders()
-{
- for (AppenderMap::iterator it = appenders.begin(); it != appenders.end(); ++it)
- if (it->second && it->second->getType() == APPENDER_DB)
- ((AppenderDB *)it->second)->setEnable(true);
-}
-
void Log::vlog(LogFilterType filter, LogLevel level, char const* str, va_list argptr)
{
char text[MAX_QUERY_LEN];
@@ -281,12 +273,16 @@ void Log::vlog(LogFilterType filter, LogLevel level, char const* str, va_list ar
void Log::write(LogMessage* msg)
{
+ if (loggers.empty())
+ return;
+
+ msg->text.append("\n");
+ Logger* logger = GetLoggerByType(msg->type);
+
if (worker)
- {
- msg->text.append("\n");
- Logger* logger = GetLoggerByType(msg->type);
worker->enqueue(new LogOperation(logger, msg));
- }
+ else
+ logger->write(*msg);
}
std::string Log::GetTimestampStr()
@@ -463,9 +459,11 @@ void Log::outCommand(uint32 account, const char * str, ...)
write(msg);
}
-void Log::SetRealmID(uint32 id)
+void Log::SetRealmId(uint32 id)
{
- realm = id;
+ for (AppenderMap::iterator it = appenders.begin(); it != appenders.end(); ++it)
+ if (it->second && it->second->getType() == APPENDER_DB)
+ ((AppenderDB *)it->second)->setRealmId(id);
}
void Log::Close()
@@ -484,7 +482,10 @@ void Log::Close()
void Log::LoadFromConfig()
{
Close();
- worker = new LogWorker();
+
+ if (ConfigMgr::GetBoolDefault("Log.Async.Enable", false))
+ worker = new LogWorker();
+
AppenderId = 0;
m_logsDir = ConfigMgr::GetStringDefault("LogsDir", "");
if (!m_logsDir.empty())
diff --git a/src/server/shared/Logging/Log.h b/src/server/shared/Logging/Log.h
index 26fcc40c5e0..46aaea4bad1 100644
--- a/src/server/shared/Logging/Log.h
+++ b/src/server/shared/Logging/Log.h
@@ -45,19 +45,18 @@ class Log
bool ShouldLog(LogFilterType type, LogLevel level) const;
bool SetLogLevel(std::string const& name, char const* level, bool isLogger = true);
- void outTrace(LogFilterType f, char const* str, ...) ATTR_PRINTF(3,4);
- void outDebug(LogFilterType f, char const* str, ...) ATTR_PRINTF(3,4);
- void outInfo(LogFilterType f, char const* str, ...) ATTR_PRINTF(3,4);
- void outWarn(LogFilterType f, char const* str, ...) ATTR_PRINTF(3,4);
- void outError(LogFilterType f, char const* str, ...) ATTR_PRINTF(3,4);
- void outFatal(LogFilterType f, char const* str, ...) ATTR_PRINTF(3,4);
-
- void EnableDBAppenders();
+ void outTrace(LogFilterType f, char const* str, ...) ATTR_PRINTF(3, 4);
+ void outDebug(LogFilterType f, char const* str, ...) ATTR_PRINTF(3, 4);
+ void outInfo(LogFilterType f, char const* str, ...) ATTR_PRINTF(3, 4);
+ void outWarn(LogFilterType f, char const* str, ...) ATTR_PRINTF(3, 4);
+ void outError(LogFilterType f, char const* str, ...) ATTR_PRINTF(3, 4);
+ void outFatal(LogFilterType f, char const* str, ...) ATTR_PRINTF(3, 4);
+
void outCommand(uint32 account, const char * str, ...) ATTR_PRINTF(3, 4);
void outCharDump(char const* str, uint32 account_id, uint32 guid, char const* name);
static std::string GetTimestampStr();
- void SetRealmID(uint32 id);
+ void SetRealmId(uint32 id);
private:
void vlog(LogFilterType f, LogLevel level, char const* str, va_list argptr);
@@ -78,7 +77,6 @@ class Log
std::string m_logsDir;
std::string m_logsTimestamp;
- uint32 realm;
LogWorker* worker;
};
diff --git a/src/server/shared/Memory.h b/src/server/shared/Memory.h
new file mode 100644
index 00000000000..25533638915
--- /dev/null
+++ b/src/server/shared/Memory.h
@@ -0,0 +1,19 @@
+
+
+#ifndef _MEMORY_H
+#define _MEMORY_H
+
+#include "DetourAlloc.h"
+
+// memory management
+inline void* dtCustomAlloc(int size, dtAllocHint /*hint*/)
+{
+ return (void*)new unsigned char[size];
+}
+
+inline void dtCustomFree(void* ptr)
+{
+ delete [] (unsigned char*)ptr;
+}
+
+#endif \ No newline at end of file
diff --git a/src/server/shared/Packets/ByteBuffer.h b/src/server/shared/Packets/ByteBuffer.h
index 5553b04f461..22dad8d7dd1 100644
--- a/src/server/shared/Packets/ByteBuffer.h
+++ b/src/server/shared/Packets/ByteBuffer.h
@@ -52,7 +52,7 @@ class ByteBufferPositionException : public ByteBufferException
{
ACE_Stack_Trace trace;
- sLog->outError(LOG_FILTER_NETWORKIO, "Attempted to %s value with size: "SIZEFMTD" in ByteBuffer (pos: " SIZEFMTD " size: "SIZEFMTD")\n[Stacktrace: %s]" ,
+ sLog->outError(LOG_FILTER_NETWORKIO, "Attempted to %s value with size: "SIZEFMTD" in ByteBuffer (pos: " SIZEFMTD " size: "SIZEFMTD")\n[Stacktrace: %s]",
(_add ? "put" : "get"), ValueSize, Pos, Size, trace.c_str());
}
diff --git a/src/server/shared/Utilities/Util.h b/src/server/shared/Utilities/Util.h
index 10c2e866a7d..e7218d7ba72 100644
--- a/src/server/shared/Utilities/Util.h
+++ b/src/server/shared/Utilities/Util.h
@@ -39,7 +39,7 @@ template<typename T, class S> struct Finder
class Tokenizer
{
public:
- typedef std::vector<char const *> StorageType;
+ typedef std::vector<char const*> StorageType;
typedef StorageType::size_type size_type;
diff --git a/src/server/worldserver/CMakeLists.txt b/src/server/worldserver/CMakeLists.txt
index 413d6487480..8c1350a7ca0 100644
--- a/src/server/worldserver/CMakeLists.txt
+++ b/src/server/worldserver/CMakeLists.txt
@@ -39,6 +39,7 @@ endif()
include_directories(
${CMAKE_BINARY_DIR}
${CMAKE_SOURCE_DIR}/dep/g3dlite/include
+ ${CMAKE_SOURCE_DIR}/dep/recastnavigation/Detour
${CMAKE_SOURCE_DIR}/dep/gsoap
${CMAKE_SOURCE_DIR}/dep/sockets/include
${CMAKE_SOURCE_DIR}/dep/SFMT
@@ -163,6 +164,7 @@ target_link_libraries(worldserver
collision
g3dlib
gsoap
+ Detour
${JEMALLOC_LIBRARY}
${READLINE_LIBRARY}
${TERMCAP_LIBRARY}
diff --git a/src/server/worldserver/Master.cpp b/src/server/worldserver/Master.cpp
index fe872c1977f..56dd64bc45a 100644
--- a/src/server/worldserver/Master.cpp
+++ b/src/server/worldserver/Master.cpp
@@ -438,8 +438,6 @@ bool Master::_StartDB()
}
sLog->outInfo(LOG_FILTER_WORLDSERVER, "Realm running as realm ID %d", realmID);
- sLog->SetRealmID(realmID);
-
///- Clean the database before starting
ClearOnlineAccounts();
diff --git a/src/server/worldserver/RemoteAccess/RASocket.cpp b/src/server/worldserver/RemoteAccess/RASocket.cpp
index 75c95d5ea98..e0f4e7f0de6 100644
--- a/src/server/worldserver/RemoteAccess/RASocket.cpp
+++ b/src/server/worldserver/RemoteAccess/RASocket.cpp
@@ -65,7 +65,13 @@ int RASocket::handle_close(ACE_HANDLE, ACE_Reactor_Mask)
int RASocket::send(const std::string& line)
{
- return size_t(peer().send(line.c_str(), line.length())) == line.length() ? 0 : -1;
+#ifdef MSG_NOSIGNAL
+ ssize_t n = peer().send(line.c_str(), line.length(), MSG_NOSIGNAL);
+#else
+ ssize_t n = peer().send(line.c_str(), line.length());
+#endif // MSG_NOSIGNAL
+
+ return n == ssize_t(line.length()) ? 0 : -1;
}
int RASocket::recv_line(ACE_Message_Block& buffer)
@@ -160,7 +166,7 @@ int RASocket::process_command(const std::string& command)
break;
}
- if (size_t(peer().send(mb->rd_ptr(), mb->length())) != mb->length())
+ if (send(std::string(mb->rd_ptr(), mb->length())) == -1)
{
mb->release();
return -1;
@@ -356,8 +362,7 @@ int RASocket::svc(void)
for (;;)
{
// show prompt
- const char* tc_prompt = "TC> ";
- if (size_t(peer().send(tc_prompt, strlen(tc_prompt))) != strlen(tc_prompt))
+ if (send("TC> ") == -1)
return -1;
std::string line;
diff --git a/src/server/worldserver/worldserver.conf.dist b/src/server/worldserver/worldserver.conf.dist
index 68cf3994e3b..51c083a3dbe 100644
--- a/src/server/worldserver/worldserver.conf.dist
+++ b/src/server/worldserver/worldserver.conf.dist
@@ -269,6 +269,14 @@ PlayerSave.Stats.MinLevel = 0
PlayerSave.Stats.SaveOnlyOnLogout = 1
#
+# mmap.enablePathFinding
+# Description: Enable/Disable pathfinding using mmaps - experimental
+# Default: 0 - (Disabled)
+# 1 - (Enabled)
+
+mmap.enablePathFinding = 0
+
+#
# vmap.enableLOS
# vmap.enableHeight
# Description: VMmap support for line of sight and height calculation.
@@ -280,14 +288,6 @@ vmap.enableLOS = 1
vmap.enableHeight = 1
#
-# vmap.petLOS
-# Description: Check line of sight for pets, to avoid them attacking through walls.
-# Default: 1 - (Enabled, each pet attack will be checked for line of sight)
-# 0 - (Disabled, somewhat less CPU usage)
-
-vmap.petLOS = 1
-
-#
# vmap.enableIndoorCheck
# Description: VMap based indoor check to remove outdoor-only auras (mounts etc.).
# Default: 1 - (Enabled)
@@ -2824,4 +2824,12 @@ Logger.Opcodes=41,6,Console Server
Loggers=Root Chat DBErrors GM RA Warden Character Load WorldServer Opcodes
#
+# Log.Async.Enable
+# Description: Enables asyncronous message logging.
+# Default: 0 - (Disabled)
+# 1 - (Enabled)
+
+Log.Async.Enable = 0
+
+#
###################################################################################################
diff --git a/src/tools/CMakeLists.txt b/src/tools/CMakeLists.txt
index 0386f27d3df..49c17913c64 100644
--- a/src/tools/CMakeLists.txt
+++ b/src/tools/CMakeLists.txt
@@ -11,3 +11,5 @@
add_subdirectory(map_extractor)
add_subdirectory(vmap4_assembler)
add_subdirectory(vmap4_extractor)
+add_subdirectory(mmaps_generator)
+add_subdirectory(mesh_extractor)
diff --git a/src/tools/map_extractor/CMakeLists.txt b/src/tools/map_extractor/CMakeLists.txt
index 9fb8d5713d7..ea073456680 100644
--- a/src/tools/map_extractor/CMakeLists.txt
+++ b/src/tools/map_extractor/CMakeLists.txt
@@ -11,23 +11,22 @@
file(GLOB_RECURSE sources *.cpp *.h)
-if( UNIX )
- include_directories (
+set(include_Dirs
${CMAKE_SOURCE_DIR}/src/server/shared
${CMAKE_SOURCE_DIR}/dep/libmpq
${CMAKE_CURRENT_SOURCE_DIR}
${CMAKE_CURRENT_SOURCE_DIR}/loadlib
- )
-elseif( WIN32 )
- include_directories (
- ${CMAKE_SOURCE_DIR}/src/server/shared
- ${CMAKE_SOURCE_DIR}/dep/libmpq
+)
+
+if( WIN32 )
+ set(include_Dirs
+ ${include_Dirs}
${CMAKE_SOURCE_DIR}/dep/libmpq/win
- ${CMAKE_CURRENT_SOURCE_DIR}
- ${CMAKE_CURRENT_SOURCE_DIR}/loadlib
)
endif()
+include_directories(${include_Dirs})
+
add_executable(mapextractor
${sources}
)
@@ -38,8 +37,6 @@ target_link_libraries(mapextractor
${ZLIB_LIBRARIES}
)
-add_dependencies(mapextractor mpq)
-
if( UNIX )
install(TARGETS mapextractor DESTINATION bin)
elseif( WIN32 )
diff --git a/src/tools/map_extractor/System.cpp b/src/tools/map_extractor/System.cpp
index d0342717324..bf88a92c32a 100644
--- a/src/tools/map_extractor/System.cpp
+++ b/src/tools/map_extractor/System.cpp
@@ -222,7 +222,7 @@ uint32 ReadMapDBC()
map_ids[x].id = dbc.getRecord(x).getUInt(0);
strcpy(map_ids[x].name, dbc.getRecord(x).getString(1));
}
- printf("Done! (%zu maps loaded)\n", map_count);
+ printf("Done! (%u maps loaded)\n", (uint32)map_count);
return map_count;
}
@@ -247,7 +247,7 @@ void ReadAreaTableDBC()
maxAreaId = dbc.getMaxId();
- printf("Done! (%zu areas loaded)\n", area_count);
+ printf("Done! (%u areas loaded)\n", (uint32)area_count);
}
void ReadLiquidTypeTableDBC()
@@ -268,7 +268,7 @@ void ReadLiquidTypeTableDBC()
for(uint32 x = 0; x < liqTypeCount; ++x)
LiqType[dbc.getRecord(x).getUInt(0)] = dbc.getRecord(x).getUInt(3);
- printf("Done! (%zu LiqTypes loaded)\n", liqTypeCount);
+ printf("Done! (%u LiqTypes loaded)\n", (uint32)liqTypeCount);
}
//
@@ -277,7 +277,7 @@ void ReadLiquidTypeTableDBC()
// Map file format data
static char const* MAP_MAGIC = "MAPS";
-static char const* MAP_VERSION_MAGIC = "v1.2";
+static char const* MAP_VERSION_MAGIC = "v1.3";
static char const* MAP_AREA_MAGIC = "AREA";
static char const* MAP_HEIGHT_MAGIC = "MHGT";
static char const* MAP_LIQUID_MAGIC = "MLIQ";
@@ -293,6 +293,8 @@ struct map_fileheader
uint32 heightMapSize;
uint32 liquidMapOffset;
uint32 liquidMapSize;
+ uint32 holesOffset;
+ uint32 holesSize;
};
#define MAP_AREA_NO_AREA 0x0001
@@ -827,9 +829,38 @@ bool ConvertADT(char *filename, char *filename2, int /*cell_y*/, int /*cell_x*/,
map.liquidMapSize += sizeof(float)*liquidHeader.width*liquidHeader.height;
}
+ // map hole info
+ uint16 holes[ADT_CELLS_PER_GRID][ADT_CELLS_PER_GRID];
+
+ if (map.liquidMapOffset)
+ map.holesOffset = map.liquidMapOffset + map.liquidMapSize;
+ else
+ map.holesOffset = map.heightMapOffset + map.heightMapSize;
+
+ memset(holes, 0, sizeof(holes));
+ bool hasHoles = false;
+
+ for (int i = 0; i < ADT_CELLS_PER_GRID; ++i)
+ {
+ for (int j = 0; j < ADT_CELLS_PER_GRID; ++j)
+ {
+ adt_MCNK * cell = cells->getMCNK(i,j);
+ if (!cell)
+ continue;
+ holes[i][j] = cell->holes;
+ if (!hasHoles && cell->holes != 0)
+ hasHoles = true;
+ }
+ }
+
+ if (hasHoles)
+ map.holesSize = sizeof(holes);
+ else
+ map.holesSize = 0;
+
// Ok all data prepared - store it
- FILE *output=fopen(filename2, "wb");
- if(!output)
+ FILE* output = fopen(filename2, "wb");
+ if (!output)
{
printf("Can't create the output file '%s'\n", filename2);
return false;
@@ -876,6 +907,11 @@ bool ConvertADT(char *filename, char *filename2, int /*cell_y*/, int /*cell_x*/,
fwrite(&liquid_height[y+liquidHeader.offsetY][liquidHeader.offsetX], sizeof(float), liquidHeader.width, output);
}
}
+
+ // store hole data
+ if (hasHoles)
+ fwrite(holes, map.holesSize, 1, output);
+
fclose(output);
return true;
diff --git a/src/tools/map_extractor/adt.cpp b/src/tools/map_extractor/adt.cpp
index fde70681113..30524dbd640 100644
--- a/src/tools/map_extractor/adt.cpp
+++ b/src/tools/map_extractor/adt.cpp
@@ -6,6 +6,13 @@
int holetab_h[4] = {0x1111, 0x2222, 0x4444, 0x8888};
int holetab_v[4] = {0x000F, 0x00F0, 0x0F00, 0xF000};
+u_map_fcc MHDRMagic = { {'R','D','H','M'} };
+u_map_fcc MCINMagic = { {'N','I','C','M'} };
+u_map_fcc MH2OMagic = { {'O','2','H','M'} };
+u_map_fcc MCNKMagic = { {'K','N','C','M'} };
+u_map_fcc MCVTMagic = { {'T','V','C','M'} };
+u_map_fcc MCLQMagic = { {'Q','L','C','M'} };
+
bool isHole(int holes, int i, int j)
{
int testi = i / 2;
@@ -53,7 +60,7 @@ bool ADT_file::prepareLoadedData()
bool adt_MHDR::prepareLoadedData()
{
- if (fcc != 'MHDR')
+ if (fcc != MHDRMagic.fcc)
return false;
if (size!=sizeof(adt_MHDR)-8)
@@ -72,7 +79,7 @@ bool adt_MHDR::prepareLoadedData()
bool adt_MCIN::prepareLoadedData()
{
- if (fcc != 'MCIN')
+ if (fcc != MCINMagic.fcc)
return false;
// Check cells data
@@ -86,7 +93,7 @@ bool adt_MCIN::prepareLoadedData()
bool adt_MH2O::prepareLoadedData()
{
- if (fcc != 'MH2O')
+ if (fcc != MH2OMagic.fcc)
return false;
// Check liquid data
@@ -98,7 +105,7 @@ bool adt_MH2O::prepareLoadedData()
bool adt_MCNK::prepareLoadedData()
{
- if (fcc != 'MCNK')
+ if (fcc != MCNKMagic.fcc)
return false;
// Check height map
@@ -113,7 +120,7 @@ bool adt_MCNK::prepareLoadedData()
bool adt_MCVT::prepareLoadedData()
{
- if (fcc != 'MCVT')
+ if (fcc != MCVTMagic.fcc)
return false;
if (size != sizeof(adt_MCVT)-8)
@@ -124,8 +131,8 @@ bool adt_MCVT::prepareLoadedData()
bool adt_MCLQ::prepareLoadedData()
{
- if (fcc != 'MCLQ')
+ if (fcc != MCLQMagic.fcc)
return false;
return true;
-} \ No newline at end of file
+}
diff --git a/src/tools/map_extractor/loadlib.cpp b/src/tools/map_extractor/loadlib.cpp
index 465eb04083f..5dcb479a11c 100644
--- a/src/tools/map_extractor/loadlib.cpp
+++ b/src/tools/map_extractor/loadlib.cpp
@@ -6,6 +6,8 @@
class MPQFile;
+u_map_fcc MverMagic = { {'R','E','V','M'} };
+
FileLoader::FileLoader()
{
data = 0;
@@ -49,7 +51,7 @@ bool FileLoader::prepareLoadedData()
{
// Check version
version = (file_MVER *) data;
- if (version->fcc != 'MVER')
+ if (version->fcc != MverMagic.fcc)
return false;
if (version->ver != FILE_FORMAT_VERSION)
return false;
diff --git a/src/tools/map_extractor/loadlib/loadlib.h b/src/tools/map_extractor/loadlib/loadlib.h
index bf6c0706d46..f32f46c63b9 100644
--- a/src/tools/map_extractor/loadlib/loadlib.h
+++ b/src/tools/map_extractor/loadlib/loadlib.h
@@ -29,6 +29,12 @@ typedef uint8_t uint8;
#define FILE_FORMAT_VERSION 18
+union u_map_fcc
+{
+ char fcc_txt[4];
+ uint32 fcc;
+};
+
//
// File version chunk
//
diff --git a/src/tools/map_extractor/mpq_libmpq.cpp b/src/tools/map_extractor/mpq_libmpq.cpp
index 81aa8cc2894..1c1a12e7b85 100644
--- a/src/tools/map_extractor/mpq_libmpq.cpp
+++ b/src/tools/map_extractor/mpq_libmpq.cpp
@@ -79,7 +79,7 @@ size_t MPQFile::read(void* dest, size_t bytes)
if (eof) return 0;
size_t rpos = pointer + bytes;
- if (rpos > size) {
+ if (rpos > size_t(size)) {
bytes = size - pointer;
eof = true;
}
diff --git a/src/tools/map_extractor/mpq_libmpq04.h b/src/tools/map_extractor/mpq_libmpq04.h
index 89f715e9e87..9f0163067c4 100644
--- a/src/tools/map_extractor/mpq_libmpq04.h
+++ b/src/tools/map_extractor/mpq_libmpq04.h
@@ -1,6 +1,3 @@
-#define _CRT_SECURE_NO_DEPRECATE
-#define _CRT_SECURE_NO_WARNINGS
-
#ifndef MPQ_H
#define MPQ_H
diff --git a/src/tools/map_extractor/wdt.cpp b/src/tools/map_extractor/wdt.cpp
index dedefbb64e5..ff255145583 100644
--- a/src/tools/map_extractor/wdt.cpp
+++ b/src/tools/map_extractor/wdt.cpp
@@ -2,23 +2,27 @@
#include "wdt.h"
+u_map_fcc MWMOMagic = { {'O', 'M', 'W', 'M'} };
+u_map_fcc MPHDMagic = { {'D', 'H', 'P', 'M'} };
+u_map_fcc MAINMagic = { {'N', 'I', 'A', 'M'} };
+
bool wdt_MWMO::prepareLoadedData()
{
- if (fcc != 'MWMO')
+ if (fcc != MWMOMagic.fcc)
return false;
return true;
}
bool wdt_MPHD::prepareLoadedData()
{
- if (fcc != 'MPHD')
+ if (fcc != MPHDMagic.fcc)
return false;
return true;
}
bool wdt_MAIN::prepareLoadedData()
{
- if (fcc != 'MAIN')
+ if (fcc != MAINMagic.fcc)
return false;
return true;
}
@@ -59,4 +63,4 @@ bool WDT_file::prepareLoadedData()
if (!wmo->prepareLoadedData())
return false;
return true;
-} \ No newline at end of file
+}
diff --git a/src/tools/mesh_extractor/ADT.cpp b/src/tools/mesh_extractor/ADT.cpp
new file mode 100644
index 00000000000..c2ac19d5be0
--- /dev/null
+++ b/src/tools/mesh_extractor/ADT.cpp
@@ -0,0 +1,53 @@
+#include "ADT.h"
+#include "DoodadHandler.h"
+#include "LiquidHandler.h"
+#include "WorldModelHandler.h"
+
+ADT::ADT( std::string file ) : ObjectData(NULL), Data(NULL), HasObjectData(false),
+ _DoodadHandler(NULL), _WorldModelHandler(NULL), _LiquidHandler(NULL)
+{
+ Data = new ChunkedData(file);
+ ObjectData = new ChunkedData(Utils::Replace(file, ".adt", "_obj0.adt"));
+ if (ObjectData->Stream)
+ HasObjectData = true;
+ else
+ ObjectData = NULL;
+}
+
+ADT::~ADT()
+{
+ delete ObjectData;
+ delete Data;
+
+ for (std::vector<MapChunk*>::iterator itr = MapChunks.begin(); itr != MapChunks.end(); ++itr)
+ delete *itr;
+
+ MapChunks.clear();
+ delete _DoodadHandler;
+ delete _WorldModelHandler;
+ delete _LiquidHandler;
+}
+
+void ADT::Read()
+{
+ Header.Read(Data->GetChunkByName("MHDR")->GetStream());
+ MapChunks.reserve(16 * 16);
+
+ for (std::vector<Chunk*>::iterator itr = Data->Chunks.begin(); itr != Data->Chunks.end(); ++itr)
+ if ((*itr)->Name == "MCNK")
+ MapChunks.push_back(new MapChunk(this, *itr));
+
+ _LiquidHandler = new LiquidHandler(this);
+
+ // do this separate from map chunk initialization to access liquid data
+ for (std::vector<MapChunk*>::iterator itr = MapChunks.begin(); itr != MapChunks.end(); ++itr)
+ (*itr)->GenerateTriangles();
+
+ _DoodadHandler = new DoodadHandler(this);
+ for (std::vector<MapChunk*>::iterator itr = MapChunks.begin(); itr != MapChunks.end(); ++itr)
+ _DoodadHandler->ProcessMapChunk(*itr);
+
+ _WorldModelHandler = new WorldModelHandler(this);
+ for (std::vector<MapChunk*>::iterator itr = MapChunks.begin(); itr != MapChunks.end(); ++itr)
+ _WorldModelHandler->ProcessMapChunk(*itr);
+}
diff --git a/src/tools/mesh_extractor/ADT.h b/src/tools/mesh_extractor/ADT.h
new file mode 100644
index 00000000000..133596eb024
--- /dev/null
+++ b/src/tools/mesh_extractor/ADT.h
@@ -0,0 +1,29 @@
+#ifndef ADT_H
+#define ADT_H
+#include "ChunkedData.h"
+#include "MapChunk.h"
+
+class DoodadHandler;
+class WorldModelHandler;
+class LiquidHandler;
+
+class ADT
+{
+public:
+ ADT(std::string file);
+ ~ADT();
+
+ void Read();
+
+ ChunkedData* ObjectData;
+ ChunkedData* Data;
+ std::vector<MapChunk*> MapChunks;
+ MHDR Header;
+ // Can we dispose of this?
+ bool HasObjectData;
+
+ DoodadHandler* _DoodadHandler;
+ WorldModelHandler* _WorldModelHandler;
+ LiquidHandler* _LiquidHandler;
+};
+#endif \ No newline at end of file
diff --git a/src/tools/mesh_extractor/CMakeLists.txt b/src/tools/mesh_extractor/CMakeLists.txt
new file mode 100644
index 00000000000..9ed8472051d
--- /dev/null
+++ b/src/tools/mesh_extractor/CMakeLists.txt
@@ -0,0 +1,50 @@
+# Copyright (C) 2005-2009 MaNGOS project <http://getmangos.com/>
+# Copyright (C) 2008-2013 TrinityCore <http://www.trinitycore.org/>
+#
+# This file is free software; as a special exception the author gives
+# unlimited permission to copy and/or distribute it, with or without
+# modifications, as long as this notice is preserved.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY, to the extent permitted by law; without even the
+# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+file(GLOB_RECURSE meshExtract_Sources *.cpp *.h)
+
+set(include_Base
+ ${CMAKE_BINARY_DIR}
+ ${CMAKE_SOURCE_DIR}/src/server/shared
+ ${CMAKE_SOURCE_DIR}/dep/recastnavigation/Recast
+ ${CMAKE_SOURCE_DIR}/dep/recastnavigation/Detour
+ ${CMAKE_SOURCE_DIR}/dep/libmpq
+ ${CMAKE_SOURCE_DIR}/dep/g3dlite/include
+ ${ACE_INCLUDE_DIR}
+ ${CMAKE_CURRENT_SOURCE_DIR}
+)
+
+if( WIN32 )
+ set(include_Base
+ ${include_Base}
+ ${CMAKE_SOURCE_DIR}/dep/libmpq/win
+ )
+endif()
+
+include_directories(${include_Base})
+
+add_executable(MeshExtractor ${meshExtract_Sources})
+
+target_link_libraries(MeshExtractor
+ g3dlib
+ mpq
+ Recast
+ Detour
+ ${BZIP2_LIBRARIES}
+ ${ZLIB_LIBRARIES}
+ ${ACE_LIBRARY}
+)
+
+if( UNIX )
+ install(TARGETS MeshExtractor DESTINATION bin)
+elseif( WIN32 )
+ install(TARGETS MeshExtractor DESTINATION "${CMAKE_INSTALL_PREFIX}")
+endif()
diff --git a/src/tools/mesh_extractor/Cache.h b/src/tools/mesh_extractor/Cache.h
new file mode 100644
index 00000000000..60e3d8434cf
--- /dev/null
+++ b/src/tools/mesh_extractor/Cache.h
@@ -0,0 +1,63 @@
+#ifndef CACHE_H
+#define CACHE_H
+#include <string>
+#include <map>
+#include "Define.h"
+#include <ace/Guard_T.h>
+#include <ace/Synch.h>
+
+class WorldModelRoot;
+class Model;
+
+template<class K, class T>
+class GenericCache
+{
+public:
+ GenericCache() {}
+
+ static const uint32 FlushLimit = 1000;
+
+ void Insert(K key, T* val)
+ {
+ ACE_GUARD(ACE_Thread_Mutex, g, mutex);
+
+ if (_items.size() > FlushLimit)
+ Clear();
+ _items[key] = val;
+ }
+
+ T* Get(K key)
+ {
+ ACE_GUARD_RETURN(ACE_Thread_Mutex, g, mutex, NULL);
+ typename std::map<K, T*>::iterator itr = _items.find(key);
+ if (itr != _items.end())
+ return itr->second;
+ return NULL;
+ }
+
+ void Clear()
+ {
+ for (typename std::map<K, T*>::iterator itr = _items.begin(); itr != _items.end(); ++itr)
+ delete itr->second;
+ _items.clear();
+ }
+private:
+ std::map<K, T*> _items;
+ ACE_Thread_Mutex mutex;
+};
+
+class CacheClass
+{
+public:
+ CacheClass() {}
+ GenericCache<std::string, Model> ModelCache;
+ GenericCache<std::string, WorldModelRoot> WorldModelCache;
+
+ void Clear()
+ {
+
+ }
+};
+
+extern CacheClass* Cache;
+#endif
diff --git a/src/tools/mesh_extractor/Chunk.cpp b/src/tools/mesh_extractor/Chunk.cpp
new file mode 100644
index 00000000000..9f2898a46e0
--- /dev/null
+++ b/src/tools/mesh_extractor/Chunk.cpp
@@ -0,0 +1,31 @@
+#include "Chunk.h"
+#include "Utils.h"
+
+int32 Chunk::FindSubChunkOffset(std::string name)
+{
+ // Reverse the name
+ name = std::string(name.rbegin(), name.rend());
+ if (name.size() != 4)
+ return -1;
+
+ FILE* stream = GetStream();
+ uint32 matched = 0;
+ while (uint32(ftell(stream)) < Utils::Size(stream))
+ {
+ char b = 0;
+ if (fread(&b, sizeof(char), 1, stream) != 1 || b != name[matched])
+ matched = 0;
+ else
+ ++matched;
+
+ if (matched == 4)
+ return ftell(stream) - 4;
+ }
+ return -1;
+}
+
+FILE* Chunk::GetStream()
+{
+ fseek(Stream, Offset, SEEK_SET);
+ return Stream;
+}
diff --git a/src/tools/mesh_extractor/Chunk.h b/src/tools/mesh_extractor/Chunk.h
new file mode 100644
index 00000000000..f3681a9f576
--- /dev/null
+++ b/src/tools/mesh_extractor/Chunk.h
@@ -0,0 +1,20 @@
+#ifndef CHUNK_H
+#define CHUNK_H
+#include "Define.h"
+#include <string>
+class ChunkedData;
+
+class Chunk
+{
+public:
+ Chunk(const char* name, uint32 length, uint32 offset, FILE* stream) : Name(name), Length(length), Offset(offset), Stream(stream) {}
+
+ int32 FindSubChunkOffset(std::string name);
+ FILE* GetStream();
+ std::string Name;
+ uint32 Length;
+ uint32 Offset;
+ FILE* Stream;
+};
+
+#endif \ No newline at end of file
diff --git a/src/tools/mesh_extractor/ChunkedData.cpp b/src/tools/mesh_extractor/ChunkedData.cpp
new file mode 100644
index 00000000000..e0db12a6be7
--- /dev/null
+++ b/src/tools/mesh_extractor/ChunkedData.cpp
@@ -0,0 +1,74 @@
+#include "ChunkedData.h"
+#include "MPQManager.h"
+#include "Utils.h"
+
+#include <string>
+
+ChunkedData::ChunkedData( FILE* stream, uint32 maxLength, uint32 chunksHint /*= 300*/ ) :
+Stream(stream)
+{
+ if (!Stream)
+ return;
+ Load(maxLength, chunksHint);
+}
+
+ChunkedData::ChunkedData( std::string file, uint32 chunksHint /*= 300*/ )
+{
+ Stream = MPQHandler->GetFile(file);
+ if (!Stream)
+ return;
+ Load(0, chunksHint);
+}
+
+void ChunkedData::Load( uint32 maxLength, uint32 chunksHint )
+{
+ if (!maxLength)
+ maxLength = Utils::Size(Stream);
+ Chunks.reserve(chunksHint);
+ uint32 baseOffset = ftell(Stream);
+ uint32 calcOffset = 0;
+ while ((calcOffset + baseOffset) < Utils::Size(Stream) && (calcOffset < maxLength))
+ {
+ char nameBytes[5];
+ uint32 read = fread(&nameBytes, sizeof(char), 4, Stream);
+ nameBytes[read] = '\0';
+ std::string name = std::string(nameBytes);
+ // Utils::Reverse(nameBytes);
+ name = std::string(name.rbegin(), name.rend());
+ uint32 length;
+ if (fread(&length, sizeof(uint32), 1, Stream) != 1)
+ continue;
+ calcOffset += 8;
+ Chunks.push_back(new Chunk(name.c_str(), length, calcOffset + baseOffset, Stream));
+ calcOffset += length;
+ // save an extra seek at the end
+ if ((calcOffset + baseOffset) < Utils::Size(Stream) && calcOffset < maxLength)
+ fseek(Stream, length, SEEK_CUR);
+ }
+}
+
+int ChunkedData::GetFirstIndex( std::string name )
+{
+ for (uint32 i = 0; i < Chunks.size(); ++i)
+ if (Chunks[i]->Name == name)
+ return i;
+ return -1;
+}
+
+Chunk* ChunkedData::GetChunkByName( std::string name )
+{
+ for (uint32 i = 0; i < Chunks.size(); ++i)
+ if (Chunks[i]->Name == name)
+ return Chunks[i];
+ return NULL;
+}
+
+ChunkedData::~ChunkedData()
+{
+ for (std::vector<Chunk*>::iterator itr = Chunks.begin(); itr != Chunks.end(); ++itr)
+ delete *itr;
+
+ Chunks.clear();
+ if (Stream)
+ fclose(Stream);
+}
diff --git a/src/tools/mesh_extractor/ChunkedData.h b/src/tools/mesh_extractor/ChunkedData.h
new file mode 100644
index 00000000000..e23648c845e
--- /dev/null
+++ b/src/tools/mesh_extractor/ChunkedData.h
@@ -0,0 +1,21 @@
+#ifndef CHNK_H
+#define CHNK_H
+
+#include <vector>
+#include "Chunk.h"
+
+class ChunkedData
+{
+public:
+ ChunkedData(FILE* stream, uint32 maxLength, uint32 chunksHint = 300);
+ ChunkedData(std::string file, uint32 chunksHint = 300);
+ ~ChunkedData();
+
+ int GetFirstIndex(std::string name);
+ Chunk* GetChunkByName(std::string name);
+
+ void Load(uint32 maxLength, uint32 chunksHint);
+ std::vector<Chunk*> Chunks;
+ FILE* Stream;
+};
+#endif \ No newline at end of file
diff --git a/src/tools/mesh_extractor/Constants.h b/src/tools/mesh_extractor/Constants.h
new file mode 100644
index 00000000000..02e2d25559f
--- /dev/null
+++ b/src/tools/mesh_extractor/Constants.h
@@ -0,0 +1,57 @@
+#ifndef CONSTANTS_H
+#define CONSTANTS_H
+
+class Constants
+{
+public:
+ enum TriangleType
+ {
+ TRIANGLE_TYPE_UNKNOWN,
+ TRIANGLE_TYPE_TERRAIN,
+ TRIANGLE_TYPE_WATER,
+ TRIANGLE_TYPE_DOODAD,
+ TRIANGLE_TYPE_WMO
+ };
+
+ enum PolyArea
+ {
+ POLY_AREA_TERRAIN = 1,
+ POLY_AREA_WATER = 2,
+ POLY_AREA_ROAD = 3,
+ POLY_AREA_DANGER = 4,
+ };
+
+ enum PolyFlag
+ {
+ POLY_FLAG_WALK = 1,
+ POLY_FLAG_SWIM = 2,
+ POLY_FLAG_FLIGHTMASTER = 4
+ };
+
+ enum ExtractFlags
+ {
+ EXTRACT_FLAG_DBC = 0x01,
+ EXTRACT_FLAG_MAPS = 0x02,
+ EXTRACT_FLAG_VMAPS = 0x04,
+ EXTRACT_FLAG_GOB_MODELS = 0x08,
+ EXTRACT_FLAG_MMAPS = 0x10,
+ EXTRACT_FLAG_TEST = 0x20,
+ EXTRACT_FLAG_ALLOWED = EXTRACT_FLAG_DBC | EXTRACT_FLAG_MAPS | EXTRACT_FLAG_VMAPS | EXTRACT_FLAG_GOB_MODELS | EXTRACT_FLAG_MMAPS | EXTRACT_FLAG_TEST
+ };
+
+ static const float TileSize;
+ static const float MaxXY;
+ static const float ChunkSize;
+ static const float UnitSize;
+ static const float Origin[];
+ static const float PI;
+ static const float MaxStandableHeight;
+ static bool ToWoWCoords;
+ static const char* VMAPMagic;
+ static const float BaseUnitDim;
+ static const int VertexPerMap;
+ static const int VertexPerTile;
+ static const int TilesPerMap;
+};
+
+#endif \ No newline at end of file
diff --git a/src/tools/mesh_extractor/ContinentBuilder.cpp b/src/tools/mesh_extractor/ContinentBuilder.cpp
new file mode 100644
index 00000000000..9b5c9f9c77b
--- /dev/null
+++ b/src/tools/mesh_extractor/ContinentBuilder.cpp
@@ -0,0 +1,144 @@
+#include "ContinentBuilder.h"
+#include "TileBuilder.h"
+#include "WDT.h"
+#include "Utils.h"
+#include "DetourNavMesh.h"
+#include "Cache.h"
+#include "ace/Task.h"
+#include "Recast.h"
+
+class BuilderThread : public ACE_Task_Base
+{
+private:
+ int X, Y, MapId;
+ std::string Continent;
+ bool debug;
+ dtNavMeshParams Params;
+ ContinentBuilder* cBuilder;
+public:
+ BuilderThread(ContinentBuilder* _cBuilder, bool deb, dtNavMeshParams& params) : debug(deb), Params(params), cBuilder(_cBuilder), Free(true) {}
+ void SetData(int x, int y, int map, std::string cont) { X = x; Y = y; MapId = map; Continent = cont; }
+
+ int svc()
+ {
+ Free = false;
+ printf("[%02i,%02i] Building tile\n", X, Y);
+ TileBuilder builder(cBuilder, Continent, X, Y, MapId);
+ char buff[100];
+ sprintf(buff, "mmaps/%03u%02u%02u.mmtile", MapId, Y, X);
+ FILE* f = fopen(buff, "r");
+ if (f) // Check if file already exists.
+ {
+ printf("[%02i,%02i] Tile skipped, file already exists\n", X, Y);
+ fclose(f);
+ Free = true;
+ return 0;
+ }
+ uint8* nav = builder.Build(debug, Params);
+ if (nav)
+ {
+ f = fopen(buff, "wb");
+ if (!f)
+ {
+ printf("Could not create file %s. Check that you have write permissions to the destination folder and try again\n", buff);
+ return 0;
+ }
+ MmapTileHeader header;
+ header.size = builder.DataSize;
+ fwrite(&header, sizeof(MmapTileHeader), 1, f);
+ fwrite(nav, sizeof(unsigned char), builder.DataSize, f);
+ fclose(f);
+ }
+ dtFree(nav);
+ printf("[%02u,%02u] Tile Built!\n", X, Y);
+ Free = true;
+ return 0;
+ }
+
+ bool Free;
+};
+
+void ContinentBuilder::getTileBounds(uint32 tileX, uint32 tileY, float* verts, int vertCount, float* bmin, float* bmax)
+{
+ // this is for elevation
+ if (verts && vertCount)
+ rcCalcBounds(verts, vertCount, bmin, bmax);
+ else
+ {
+ bmin[1] = FLT_MIN;
+ bmax[1] = FLT_MAX;
+ }
+
+ // this is for width and depth
+ bmax[0] = (32 - int(tileX)) * Constants::TileSize;
+ bmax[2] = (32 - int(tileY)) * Constants::TileSize;
+ bmin[0] = bmax[0] - Constants::TileSize;
+ bmin[2] = bmax[2] - Constants::TileSize;
+}
+
+void ContinentBuilder::CalculateTileBounds()
+{
+ for (std::vector<TilePos>::iterator itr = TileMap->TileTable.begin(); itr != TileMap->TileTable.end(); ++itr)
+ {
+ tileXMax = std::max(itr->X, tileXMax);
+ tileXMin = std::min(itr->X, tileXMin);
+
+ tileYMax = std::max(itr->Y, tileYMax);
+ tileYMin = std::min(itr->Y, tileYMin);
+ }
+ getTileBounds(tileXMax, tileYMax, NULL, 0, bmin, bmax);
+}
+
+void ContinentBuilder::Build(bool debug)
+{
+ char buff[50];
+ sprintf(buff, "mmaps/%03u.mmap", MapId);
+ FILE* mmap = fopen(buff, "wb");
+ if (!mmap)
+ {
+ printf("Could not create file %s. Check that you have write permissions to the destination folder and try again\n", buff);
+ return;
+ }
+
+ CalculateTileBounds();
+
+ dtNavMeshParams params;
+ params.maxPolys = 1 << STATIC_POLY_BITS;
+ params.maxTiles = TileMap->TileTable.size();
+ rcVcopy(params.orig, bmin);
+ params.tileHeight = Constants::TileSize;
+ params.tileWidth = Constants::TileSize;
+ fwrite(&params, sizeof(dtNavMeshParams), 1, mmap);
+ fclose(mmap);
+ std::vector<BuilderThread*> Threads;
+ for (uint32 i = 0; i < NumberOfThreads; ++i)
+ Threads.push_back(new BuilderThread(this, debug, params));
+ printf("Map %s ( %i ) has %u tiles. Building them with %i threads\n", Continent.c_str(), MapId, uint32(TileMap->TileTable.size()), NumberOfThreads);
+ for (std::vector<TilePos>::iterator itr = TileMap->TileTable.begin(); itr != TileMap->TileTable.end(); ++itr)
+ {
+ bool next = false;
+ while (!next)
+ {
+ for (std::vector<BuilderThread*>::iterator _th = Threads.begin(); _th != Threads.end(); ++_th)
+ {
+ if ((*_th)->Free)
+ {
+ (*_th)->SetData(itr->X, itr->Y, MapId, Continent);
+ (*_th)->activate();
+ next = true;
+ break;
+ }
+ }
+ // Wait for 20 seconds
+ ACE_OS::sleep(ACE_Time_Value (0, 20000));
+ }
+ }
+ Cache->Clear();
+
+ // Free memory
+ for (std::vector<BuilderThread*>::iterator _th = Threads.begin(); _th != Threads.end(); ++_th)
+ {
+ (*_th)->wait();
+ delete *_th;
+ }
+}
diff --git a/src/tools/mesh_extractor/ContinentBuilder.h b/src/tools/mesh_extractor/ContinentBuilder.h
new file mode 100644
index 00000000000..b36ca125b9e
--- /dev/null
+++ b/src/tools/mesh_extractor/ContinentBuilder.h
@@ -0,0 +1,30 @@
+#ifndef CONT_BUILDER_H
+#define CONT_BUILDER_H
+#include <string>
+#include "WDT.h"
+#include "Define.h"
+
+class ContinentBuilder
+{
+public:
+ ContinentBuilder(std::string continent, uint32 mapId, WDT* wdt, uint32 tn) :
+ Continent(continent), TileMap(wdt), MapId(mapId),
+ NumberOfThreads(tn), tileXMin(64), tileYMin(64), tileXMax(0), tileYMax(0)
+ {}
+
+ void Build(bool debug);
+ void getTileBounds(uint32 tileX, uint32 tileY, float* verts, int vertCount, float* bmin, float* bmax);
+ void CalculateTileBounds();
+ float bmin[3];
+ float bmax[3];
+private:
+ std::string Continent;
+ WDT* TileMap;
+ uint32 MapId;
+ uint32 NumberOfThreads;
+ int tileXMin;
+ int tileYMin;
+ int tileXMax;
+ int tileYMax;
+};
+#endif
diff --git a/src/tools/mesh_extractor/DBC.cpp b/src/tools/mesh_extractor/DBC.cpp
new file mode 100644
index 00000000000..9a55ff6d7ed
--- /dev/null
+++ b/src/tools/mesh_extractor/DBC.cpp
@@ -0,0 +1,70 @@
+#include <cstdio>
+#include "DBC.h"
+#include "Define.h"
+
+DBC::DBC( FILE* stream ) : StringBlock(NULL), StringBlockSize(0), IsFaulty(true)
+{
+ char magic[5];
+ uint32 count = 0;
+ count += fread(&magic, sizeof(char), 4, stream);
+ magic[4] = '\0';
+ count += fread(&RecordCount, sizeof(uint32), 1, stream);
+ Records.reserve(RecordCount);
+ count += fread(&Fields, sizeof(uint32), 1, stream);
+ count += fread(&RecordSize, sizeof(uint32), 1, stream);
+ count += fread(&StringBlockSize, sizeof(uint32), 1, stream);
+ if (count != 8)
+ printf("DBC::DBC: Failed to read some data expected 8, read %u\n", count);
+
+ for (int i = 0; i < RecordCount; i++)
+ {
+ Record* rec = new Record(this);
+ Records.push_back(rec);
+ int size = 0;
+ for (int f = 0; f < Fields; f++)
+ {
+ if (size + 4 > RecordSize)
+ {
+ IsFaulty = true;
+ break;
+ }
+ uint32 tmp;
+ if (fread(&tmp, sizeof(uint32), 1, stream) != 1)
+ printf("DBC::DBC: Failed to read some data expected 1, read 0\n");
+ rec->Values.push_back(tmp);
+ size += 4;
+ }
+ }
+ StringBlock = new uint8[StringBlockSize];
+ count = fread(StringBlock, sizeof(uint8), StringBlockSize, stream);
+ if (count != StringBlockSize)
+ printf("DBC::DBC: Failed to read some data expected %u, read %u\n", StringBlockSize, count);
+}
+
+std::string DBC::GetStringByOffset( int offset )
+{
+ int len = 0;
+ for (uint32 i = offset; i < StringBlockSize; i++)
+ {
+ if (!StringBlock[i])
+ {
+ len = (i - offset);
+ break;
+ }
+ }
+ char* d = new char[len+1];
+ strcpy(d, (const char*)(StringBlock + offset));
+ d[len] = '\0';
+ std::string val = std::string(d);
+ delete d;
+ return val;
+}
+
+Record* DBC::GetRecordById( int id )
+{
+ // we assume Id is index 0
+ for (std::vector<Record*>::iterator itr = Records.begin(); itr != Records.end(); ++itr)
+ if ((*itr)->Values[0] == id)
+ return *itr;
+ return NULL;
+}
diff --git a/src/tools/mesh_extractor/DBC.h b/src/tools/mesh_extractor/DBC.h
new file mode 100644
index 00000000000..5ed57754e73
--- /dev/null
+++ b/src/tools/mesh_extractor/DBC.h
@@ -0,0 +1,53 @@
+#ifndef DBC_H
+#define DBC_H
+#include <vector>
+#include <string>
+#include "Define.h"
+
+class Record;
+
+class DBC
+{
+public:
+ DBC(FILE* stream);
+
+ std::string GetStringByOffset(int offset);
+
+ Record* GetRecordById(int id);
+
+ std::string Name;
+ std::vector<Record*> Records;
+ int RecordCount;
+ int Fields;
+ int RecordSize;
+ uint8* StringBlock;
+ uint32 StringBlockSize;
+ bool IsFaulty;
+};
+
+class Record
+{
+public:
+ Record(DBC* dbc) : Source(dbc) {}
+
+ DBC* Source;
+ std::vector<int> Values;
+
+ int operator[](int index)
+ {
+ return Values[index];
+ }
+
+ template <typename T>
+ T GetValue(int index)
+ {
+ return *(T*)(&Values[index]);
+ }
+
+ std::string GetString(int index)
+ {
+ return Source->GetStringByOffset(Values[index]);
+ }
+};
+
+#endif \ No newline at end of file
diff --git a/src/tools/mesh_extractor/DoodadHandler.cpp b/src/tools/mesh_extractor/DoodadHandler.cpp
new file mode 100644
index 00000000000..56c2a7986f8
--- /dev/null
+++ b/src/tools/mesh_extractor/DoodadHandler.cpp
@@ -0,0 +1,109 @@
+#include "DoodadHandler.h"
+#include "Chunk.h"
+#include "Cache.h"
+#include "Model.h"
+#include "G3D/Matrix4.h"
+
+DoodadHandler::DoodadHandler( ADT* adt ) : ObjectDataHandler(adt), _definitions(NULL), _paths(NULL)
+{
+ if (!adt->HasObjectData)
+ return;
+ Chunk* mddf = adt->ObjectData->GetChunkByName("MDDF");
+ if (mddf)
+ ReadDoodadDefinitions(mddf);
+
+ Chunk* mmid = adt->ObjectData->GetChunkByName("MMID");
+ Chunk* mmdx = adt->ObjectData->GetChunkByName("MMDX");
+ if (mmid && mmdx)
+ ReadDoodadPaths(mmid, mmdx);
+}
+
+void DoodadHandler::ProcessInternal( ChunkedData* subChunks )
+{
+ if (!IsSane())
+ return;
+ Chunk* doodadReferencesChunk = subChunks->GetChunkByName("MCRD");
+ if (!doodadReferencesChunk)
+ return;
+ FILE* stream = doodadReferencesChunk->GetStream();
+ uint32 refCount = doodadReferencesChunk->Length / 4;
+ for (uint32 i = 0; i < refCount; i++)
+ {
+ int32 index;
+ if (int count = fread(&index, sizeof(int32), 1, stream) != 1)
+ printf("DoodadHandler::ProcessInternal: Failed to read some data expected 1, read %d\n", count);
+ if (index < 0 || uint32(index) >= _definitions->size())
+ continue;
+ DoodadDefinition doodad = (*_definitions)[index];
+ if (_drawn.find(doodad.UniqueId) != _drawn.end())
+ continue;
+ _drawn.insert(doodad.UniqueId);
+ if (doodad.MmidIndex >= _paths->size())
+ continue;
+
+ std::string path = (*_paths)[doodad.MmidIndex];
+ Model* model = Cache->ModelCache.Get(path);
+ if (!model)
+ {
+ model = new Model(path);
+ Cache->ModelCache.Insert(path, model);
+ }
+ if (!model->IsCollidable)
+ continue;
+
+ Vertices.reserve(refCount * model->Vertices.size() * 0.2);
+ Triangles.reserve(refCount * model->Triangles.size() * 0.2);
+
+ InsertModelGeometry(doodad, model);
+ }
+}
+
+void DoodadHandler::ReadDoodadDefinitions( Chunk* chunk )
+{
+ int32 count = chunk->Length / 36;
+ _definitions = new std::vector<DoodadDefinition>;
+ _definitions->reserve(count);
+ FILE* stream = chunk->GetStream();
+ for (int i = 0; i < count; i++)
+ {
+ DoodadDefinition def;
+ def.Read(stream);
+ _definitions->push_back(def);
+ }
+}
+
+void DoodadHandler::ReadDoodadPaths( Chunk* id, Chunk* data )
+{
+ int paths = id->Length / 4;
+ _paths = new std::vector<std::string>();
+ _paths->reserve(paths);
+ for (int i = 0; i < paths; i++)
+ {
+ FILE* idStream = id->GetStream();
+ fseek(idStream, i * 4, SEEK_CUR);
+ uint32 offset;
+ if (fread(&offset, sizeof(uint32), 1, idStream) != 1)
+ printf("DoodadHandler::ReadDoodadPaths: Failed to read some data expected 1, read 0\n");
+ FILE* dataStream = data->GetStream();
+ fseek(dataStream, offset + data->Offset, SEEK_SET);
+ _paths->push_back(Utils::ReadString(dataStream));
+ }
+}
+
+void DoodadHandler::InsertModelGeometry(const DoodadDefinition& def, Model* model)
+{
+ G3D::Matrix4 transformation = Utils::GetTransformation(def);
+ uint32 vertOffset = Vertices.size();
+
+ for (std::vector<Vector3>::iterator itr = model->Vertices.begin(); itr != model->Vertices.end(); ++itr)
+ Vertices.push_back(Utils::VectorTransform(*itr, transformation));
+
+ for (std::vector<Triangle<uint16> >::iterator itr = model->Triangles.begin(); itr != model->Triangles.end(); ++itr)
+ Triangles.push_back(Triangle<uint32>(Constants::TRIANGLE_TYPE_DOODAD, itr->V0 + vertOffset, itr->V1 + vertOffset, itr->V2 + vertOffset));
+}
+
+DoodadHandler::~DoodadHandler()
+{
+ delete _definitions;
+ delete _paths;
+}
diff --git a/src/tools/mesh_extractor/DoodadHandler.h b/src/tools/mesh_extractor/DoodadHandler.h
new file mode 100644
index 00000000000..981834ec7ac
--- /dev/null
+++ b/src/tools/mesh_extractor/DoodadHandler.h
@@ -0,0 +1,57 @@
+#ifndef DOOADHNDL_H
+#define DOOADHNDL_H
+#include "ObjectDataHandler.h"
+#include "Utils.h"
+#include "Chunk.h"
+#include "Model.h"
+#include <set>
+#include <vector>
+
+class DoodadDefinition : public IDefinition
+{
+public:
+ uint32 MmidIndex;
+ uint32 UniqueId;
+ uint16 DecimalScale;
+ uint16 Flags;
+
+ virtual float Scale() const { return DecimalScale / 1024.0f; }
+
+ void Read(FILE* stream)
+ {
+ int count = 0;
+
+ count += fread(&MmidIndex, sizeof(uint32), 1, stream);
+ count += fread(&UniqueId, sizeof(uint32), 1, stream);
+ Position = Vector3::Read(stream);
+ Rotation = Vector3::Read(stream);
+ count += fread(&DecimalScale, sizeof(uint16), 1, stream);
+ count += fread(&Flags, sizeof(uint16), 1, stream);
+ if (count != 4)
+ printf("DoodadDefinition::Read: Failed to read some data expected 4, read %d\n", count);
+ }
+};
+
+class DoodadHandler : public ObjectDataHandler
+{
+public:
+ DoodadHandler(ADT* adt);
+ ~DoodadHandler();
+
+ std::vector<Vector3> Vertices;
+ std::vector<Triangle<uint32> > Triangles;
+ bool IsSane() { return _definitions && _paths; }
+
+
+protected:
+ void ProcessInternal(ChunkedData* chunk);
+
+private:
+ void ReadDoodadDefinitions(Chunk* chunk);
+ void ReadDoodadPaths(Chunk* id, Chunk* data);
+ void InsertModelGeometry(const DoodadDefinition& def, Model* model);
+ std::set<uint32> _drawn;
+ std::vector<DoodadDefinition>* _definitions;
+ std::vector<std::string>* _paths;
+};
+#endif
diff --git a/src/tools/mesh_extractor/Geometry.cpp b/src/tools/mesh_extractor/Geometry.cpp
new file mode 100644
index 00000000000..2fc470e8e9f
--- /dev/null
+++ b/src/tools/mesh_extractor/Geometry.cpp
@@ -0,0 +1,123 @@
+#include "Geometry.h"
+#include "Constants.h"
+#include "ADT.h"
+#include "WorldModelHandler.h"
+#include "DoodadHandler.h"
+
+Geometry::Geometry() : Transform(false)
+{
+ Vertices.reserve(10000);
+ Triangles.reserve(10000);
+}
+
+void Geometry::CalculateBoundingBox( float*& min, float*& max )
+{
+ min = new float[3];
+ max = new float[3];
+
+ for (std::vector<Vector3>::iterator itr = Vertices.begin(); itr != Vertices.end(); ++itr)
+ {
+ if (itr->x > max[0])
+ max[0] = itr->x;
+ if (itr->x < min[0])
+ min[0] = itr->x;
+
+ if (itr->y > max[1])
+ max[1] = itr->y;
+ if (itr->y < min[1])
+ min[1] = itr->y;
+
+ if (itr->z > max[2])
+ max[2] = itr->z;
+ if (itr->z < min[2])
+ min[2] = itr->z;
+ }
+}
+
+void Geometry::CalculateMinMaxHeight( float& min, float& max )
+{
+ min = 0.0f;
+ max = 0.0f;
+
+ for (std::vector<Vector3>::iterator itr = Vertices.begin(); itr != Vertices.end(); ++itr)
+ {
+ if (Transform)
+ {
+ if (itr->y < min)
+ min = itr->y;
+ if (itr->y > max)
+ max = itr->y;
+ }
+ else
+ {
+ if (itr->z < min)
+ min = itr->z;
+ if (itr->z > max)
+ max = itr->z;
+ }
+ }
+}
+
+void Geometry::AddData( std::vector<Vector3>& verts, std::vector<Triangle<uint32> >& tris )
+{
+ uint32 vertOffset = Vertices.size();
+ for (std::vector<Vector3>::iterator itr = verts.begin(); itr != verts.end(); ++itr)
+ Vertices.push_back(Transform ? Utils::ToRecast(*itr) : *itr);
+
+ for (std::vector<Triangle<uint32> >::iterator itr = tris.begin(); itr != tris.end(); ++itr)
+ Triangles.push_back(Triangle<uint32>(itr->Type, itr->V0 + vertOffset, itr->V1 + vertOffset, itr->V2 + vertOffset));
+}
+
+void Geometry::GetRawData( float*& verts, int*& tris, uint8*& areas )
+{
+ verts = new float[Vertices.size() * 3];
+ for (uint32 i = 0; i < Vertices.size(); ++i)
+ {
+ Vector3& vert = Vertices[i];
+ verts[(i * 3) + 0] = vert.x;
+ verts[(i * 3) + 1] = vert.y;
+ verts[(i * 3) + 2] = vert.z;
+ }
+
+ tris = new int[Triangles.size() * 3];
+ for (uint32 i = 0; i < Triangles.size(); ++i)
+ {
+ Triangle<uint32>& tri = Triangles[i];
+ tris[(i * 3) + 0] = (int)tri.V0;
+ tris[(i * 3) + 1] = (int)tri.V1;
+ tris[(i * 3) + 2] = (int)tri.V2;
+ }
+
+ areas = new uint8[Triangles.size()];
+ for (uint32 i = 0; i < Triangles.size(); i++)
+ {
+ switch (Triangles[i].Type)
+ {
+ case Constants::TRIANGLE_TYPE_WATER:
+ areas[i] = Constants::POLY_AREA_WATER;
+ break;
+ default:
+ areas[i] = Constants::POLY_AREA_TERRAIN;
+ break;
+ }
+ }
+}
+
+void Geometry::AddAdt( ADT* adt )
+{
+ for (std::vector<MapChunk*>::iterator itr = adt->MapChunks.begin(); itr != adt->MapChunks.end(); ++itr)
+ {
+ std::vector<Triangle<uint32> > tmp;
+ tmp.reserve((*itr)->Triangles.size());
+ for (std::vector<Triangle<uint8> >::iterator itr2 = (*itr)->Triangles.begin(); itr2 != (*itr)->Triangles.end(); ++itr2)
+ tmp.push_back(Triangle<uint32>(itr2->Type, itr2->V0, itr2->V1, itr2->V2));
+ AddData((*itr)->Vertices, tmp);
+ }
+
+ if (!adt->_DoodadHandler->Triangles.empty())
+ AddData(adt->_DoodadHandler->Vertices, adt->_DoodadHandler->Triangles);
+
+ if (!adt->_WorldModelHandler->Triangles.empty())
+ AddData(adt->_WorldModelHandler->Vertices, adt->_WorldModelHandler->Triangles);
+}
+
diff --git a/src/tools/mesh_extractor/Geometry.h b/src/tools/mesh_extractor/Geometry.h
new file mode 100644
index 00000000000..e445234dd12
--- /dev/null
+++ b/src/tools/mesh_extractor/Geometry.h
@@ -0,0 +1,23 @@
+#ifndef GEOMETRY_H
+#define GEOMETRY_H
+#include <vector>
+
+#include "Utils.h"
+
+class ADT;
+class Geometry
+{
+public:
+ Geometry();
+
+ void CalculateBoundingBox(float*& min, float*& max);
+ void CalculateMinMaxHeight(float& min, float& max);
+ void AddData(std::vector<Vector3>& verts, std::vector<Triangle<uint32> >& tris);
+ void AddAdt(ADT* adt);
+ void GetRawData(float*& verts, int*& tris, uint8*& areas);
+
+ std::vector<Vector3> Vertices;
+ std::vector<Triangle<uint32> > Triangles;
+ bool Transform;
+};
+#endif \ No newline at end of file
diff --git a/src/tools/mesh_extractor/LiquidHandler.cpp b/src/tools/mesh_extractor/LiquidHandler.cpp
new file mode 100644
index 00000000000..285ea1a5b74
--- /dev/null
+++ b/src/tools/mesh_extractor/LiquidHandler.cpp
@@ -0,0 +1,102 @@
+#include "LiquidHandler.h"
+#include "Utils.h"
+
+LiquidHandler::LiquidHandler( ADT* adt ) : Source(adt)
+{
+ HandleNewLiquid();
+}
+
+void LiquidHandler::HandleNewLiquid()
+{
+ Chunk* chunk = Source->Data->GetChunkByName("MH2O");
+ if (!chunk)
+ return;
+
+ Vertices.reserve(1000);
+ Triangles.reserve(1000);
+
+ FILE* stream = chunk->GetStream();
+ H2OHeader header[256];
+ MCNKData.reserve(256);
+ for (int i = 0; i < 256; i++)
+ header[i] = H2OHeader::Read(stream);
+
+ for (int i = 0; i < 256; i++)
+ {
+ H2OHeader h = header[i];
+ if (h.LayerCount == 0)
+ {
+ // Need to fill in missing data with dummies.
+ MCNKData.push_back(MCNKLiquidData(NULL, H2ORenderMask()));
+ continue;
+ }
+ fseek(stream, chunk->Offset + h.OffsetInformation, SEEK_SET);
+ H2OInformation information = H2OInformation::Read(stream);
+
+ float** heights = new float*[9];
+ for (int j = 0; j < 9; ++i)
+ {
+ heights[j] = new float[9];
+ memset(heights[j], 0, sizeof(float) * 9);
+ }
+
+ H2ORenderMask renderMask;
+ if (information.LiquidType != 2)
+ {
+ fseek(stream, chunk->Offset + h.OffsetRender, SEEK_SET);
+ renderMask = H2ORenderMask::Read(stream);
+ if ((Utils::IsAllZero(renderMask.Mask, 8) || (information.Width == 8 && information.Height == 8)) && information.OffsetMask2)
+ {
+ fseek(stream, chunk->Offset + information.OffsetMask2, SEEK_SET);
+ uint32 size = ceil(information.Width * information.Height / 8.0f);
+ uint8* altMask = new uint8[size];
+ if (fread(altMask, sizeof(uint8), size, stream) == size)
+ for (uint32 mi = 0; mi < size; mi++)
+ renderMask.Mask[mi + information.OffsetY] |= altMask[mi];
+ delete[] altMask;
+ }
+ fseek(stream, chunk->Offset + information.OffsetHeightmap, SEEK_SET);
+
+ for (int y = information.OffsetY; y < (information.OffsetY + information.Height); y++)
+ for (int x = information.OffsetX; x < (information.OffsetX + information.Width); x++)
+ if (fread(&heights[x][y], sizeof(float), 1, stream) != 1)
+ return;
+ }
+ else
+ {
+ // Fill with ocean data
+ for (uint32 i = 0; i < 8; ++i)
+ renderMask.Mask[i] = 0xFF;
+
+ for (uint32 y = 0; y < 9; ++y)
+ for (uint32 x = 0; x < 9; ++x)
+ heights[x][y] = information.HeightLevel1;
+ }
+
+ MCNKData.push_back(MCNKLiquidData(heights, renderMask));
+
+ for (int y = information.OffsetY; y < (information.OffsetY + information.Height); y++)
+ {
+ for (int x = information.OffsetX; x < (information.OffsetX + information.Width); x++)
+ {
+ if (!renderMask.ShouldRender(x, y))
+ continue;
+
+ MapChunk* mapChunk = Source->MapChunks[i];
+ Vector3 location = mapChunk->Header.Position;
+ location.y = location.y - (x * Constants::UnitSize);
+ location.x = location.x - (y * Constants::UnitSize);
+ location.z = heights[x][y];
+
+ uint32 vertOffset = Vertices.size();
+ Vertices.push_back(location);
+ Vertices.push_back(Vector3(location.x - Constants::UnitSize, location.y, location.z));
+ Vertices.push_back(Vector3(location.x, location.y - Constants::UnitSize, location.z));
+ Vertices.push_back(Vector3(location.x - Constants::UnitSize, location.y - Constants::UnitSize, location.z));
+
+ Triangles.push_back(Triangle<uint32>(Constants::TRIANGLE_TYPE_WATER, vertOffset, vertOffset+2, vertOffset + 1));
+ Triangles.push_back(Triangle<uint32>(Constants::TRIANGLE_TYPE_WATER, vertOffset + 2, vertOffset + 3, vertOffset + 1));
+ }
+ }
+ }
+}
diff --git a/src/tools/mesh_extractor/LiquidHandler.h b/src/tools/mesh_extractor/LiquidHandler.h
new file mode 100644
index 00000000000..41e128ba20b
--- /dev/null
+++ b/src/tools/mesh_extractor/LiquidHandler.h
@@ -0,0 +1,21 @@
+#ifndef LIQUID_H
+#define LIQUID_H
+#include "ADT.h"
+#include "Utils.h"
+#include "Define.h"
+
+#include <vector>
+
+class LiquidHandler
+{
+public:
+ LiquidHandler(ADT* adt);
+
+ ADT* Source;
+ std::vector<Vector3> Vertices;
+ std::vector<Triangle<uint32> > Triangles;
+ std::vector<MCNKLiquidData> MCNKData;
+private:
+ void HandleNewLiquid();
+};
+#endif \ No newline at end of file
diff --git a/src/tools/mesh_extractor/MPQ.cpp b/src/tools/mesh_extractor/MPQ.cpp
new file mode 100644
index 00000000000..18a9eb0f0e3
--- /dev/null
+++ b/src/tools/mesh_extractor/MPQ.cpp
@@ -0,0 +1,118 @@
+#include "MPQ.h"
+#include "MPQManager.h"
+#include <deque>
+#include <cstdio>
+
+MPQArchive::MPQArchive(const char* filename)
+{
+ int result = libmpq__archive_open(&mpq_a, filename, -1);
+ printf("Opening %s\n", filename);
+ if (result)
+ {
+ switch (result)
+ {
+ case LIBMPQ_ERROR_OPEN :
+ printf("Error opening archive '%s': Does file really exist?\n", filename);
+ break;
+ case LIBMPQ_ERROR_FORMAT : /* bad file format */
+ printf("Error opening archive '%s': Bad file format\n", filename);
+ break;
+ case LIBMPQ_ERROR_SEEK : /* seeking in file failed */
+ printf("Error opening archive '%s': Seeking in file failed\n", filename);
+ break;
+ case LIBMPQ_ERROR_READ : /* Read error in archive */
+ printf("Error opening archive '%s': Read error in archive\n", filename);
+ break;
+ case LIBMPQ_ERROR_MALLOC : /* maybe not enough memory? :) */
+ printf("Error opening archive '%s': Maybe not enough memory\n", filename);
+ break;
+ default:
+ printf("Error opening archive '%s': Unknown error\n", filename);
+ break;
+ }
+ }
+ GetFileListTo(Files);
+}
+
+void MPQArchive::close()
+{
+ libmpq__archive_close(mpq_a);
+}
+
+MPQFile::MPQFile(const char* filename):
+eof(false), buffer(0), pointer(0), size(0)
+{
+ for (std::deque<MPQArchive*>::iterator i = MPQHandler->Archives.begin(); i != MPQHandler->Archives.end();++i)
+ {
+ mpq_archive* mpq_a = (*i)->mpq_a;
+
+ uint32_t filenum;
+ if(libmpq__file_number(mpq_a, filename, &filenum))
+ continue;
+ libmpq__off_t transferred;
+ libmpq__file_unpacked_size(mpq_a, filenum, &size);
+
+ // HACK: in patch.mpq some files don't want to open and give 1 for filesize
+ if (size<=1) {
+ // printf("warning: file %s has size %d; cannot Read.\n", filename, size);
+ eof = true;
+ buffer = 0;
+ return;
+ }
+ buffer = new char[size];
+
+ //libmpq_file_getdata
+ libmpq__file_read(mpq_a, filenum, (unsigned char*)buffer, size, &transferred);
+ /*libmpq_file_getdata(&mpq_a, hash, fileno, (unsigned char*)buffer);*/
+ return;
+
+ }
+ eof = true;
+ buffer = 0;
+}
+
+size_t MPQFile::Read(void* dest, size_t bytes)
+{
+ if (eof)
+ return 0;
+
+ size_t rpos = pointer + bytes;
+ if (rpos > size_t(size)) {
+ bytes = size - pointer;
+ eof = true;
+ }
+
+ memcpy(dest, &(buffer[pointer]), bytes);
+
+ pointer = rpos;
+
+ return bytes;
+}
+
+void MPQFile::seek(int offset)
+{
+ pointer = offset;
+ eof = (pointer >= size);
+}
+
+void MPQFile::seekRelative(int offset)
+{
+ pointer += offset;
+ eof = (pointer >= size);
+}
+
+void MPQFile::close()
+{
+ if (buffer)
+ delete[] buffer;
+ buffer = 0;
+ eof = true;
+}
+
+FILE* MPQFile::GetFileStream()
+{
+ FILE* file = tmpfile();
+ fwrite(buffer, sizeof(char), size, file);
+ fseek(file, 0, SEEK_SET);
+ return file;
+}
diff --git a/src/tools/mesh_extractor/MPQ.h b/src/tools/mesh_extractor/MPQ.h
new file mode 100644
index 00000000000..2f8b082f526
--- /dev/null
+++ b/src/tools/mesh_extractor/MPQ.h
@@ -0,0 +1,88 @@
+#ifndef MPQ_H
+#define MPQ_H
+
+#include "libmpq/mpq.h"
+#include "Define.h"
+#include <string>
+#include <ctype.h>
+#include <vector>
+#include <iostream>
+#include <deque>
+
+class MPQArchive
+{
+
+public:
+ mpq_archive_s *mpq_a;
+
+ std::vector<std::string> Files;
+
+ MPQArchive(const char* filename);
+ void close();
+
+ void GetFileListTo(std::vector<std::string>& filelist) {
+ uint32_t filenum;
+ if(libmpq__file_number(mpq_a, "(listfile)", &filenum)) return;
+ libmpq__off_t size, transferred;
+ libmpq__file_unpacked_size(mpq_a, filenum, &size);
+
+ char *buffer = new char[size];
+
+ libmpq__file_read(mpq_a, filenum, (unsigned char*)buffer, size, &transferred);
+
+ char seps[] = "\n";
+ char* token;
+
+ token = strtok( buffer, seps );
+ uint32 counter = 0;
+ while ((token != NULL) && (counter < size)) {
+ //cout << token << endl;
+ token[strlen(token) - 1] = 0;
+ std::string s = token;
+ filelist.push_back(s);
+ counter += strlen(token) + 2;
+ token = strtok(NULL, seps);
+ }
+
+ delete[] buffer;
+ }
+};
+
+class MPQFile
+{
+ //MPQHANDLE handle;
+ bool eof;
+ char *buffer;
+ libmpq__off_t pointer,size;
+
+ // disable copying
+ MPQFile(const MPQFile& /*f*/) {}
+ void operator=(const MPQFile& /*f*/) {}
+
+public:
+ MPQFile(const char* filename); // filenames are not case sensitive
+ ~MPQFile() { close(); }
+ size_t Read(void* dest, size_t bytes);
+ FILE* GetFileStream();
+ size_t getSize() { return size; }
+ size_t getPos() { return pointer; }
+ char* getBuffer() { return buffer; }
+ char* getPointer() { return buffer + pointer; }
+ bool isEof() { return eof; }
+ void seek(int offset);
+ void seekRelative(int offset);
+ void close();
+};
+
+inline void flipcc(char *fcc)
+{
+ char t;
+ t=fcc[0];
+ fcc[0]=fcc[3];
+ fcc[3]=t;
+ t=fcc[1];
+ fcc[1]=fcc[2];
+ fcc[2]=t;
+}
+
+#endif
diff --git a/src/tools/mesh_extractor/MPQManager.cpp b/src/tools/mesh_extractor/MPQManager.cpp
new file mode 100644
index 00000000000..91b9c121c89
--- /dev/null
+++ b/src/tools/mesh_extractor/MPQManager.cpp
@@ -0,0 +1,108 @@
+#include "MPQManager.h"
+#include "MPQ.h"
+#include "DBC.h"
+#include "Utils.h"
+#include <ace/Guard_T.h>
+
+char const* MPQManager::Files[] = {
+ "common.MPQ",
+ "common-2.MPQ",
+ "expansion.MPQ",
+ "lichking.MPQ",
+ "patch.MPQ",
+ "patch-2.MPQ",
+ "patch-3.MPQ"
+};
+
+char const* MPQManager::Languages[] = { "enGB", "enUS", "deDE", "esES", "frFR", "koKR", "zhCN", "zhTW", "enCN", "enTW", "esMX", "ruRU" };
+
+void MPQManager::Initialize()
+{
+ InitializeDBC();
+ uint32 size = sizeof(Files) / sizeof(char*);
+ for (uint32 i = 0; i < size; ++i)
+ {
+ MPQArchive* arc = new MPQArchive(std::string("Data/" + std::string(Files[i])).c_str());
+ Archives.push_front(arc);
+ printf("Opened %s\n", Files[i]);
+ }
+}
+
+void MPQManager::InitializeDBC()
+{
+ BaseLocale = -1;
+ std::string fileName;
+ uint32 size = sizeof(Languages) / sizeof(char*);
+ MPQArchive* _baseLocale = NULL;
+ for (uint32 i = 0; i < size; ++i)
+ {
+ std::string _fileName = "Data/" + std::string(Languages[i]) + "/locale-" + std::string(Languages[i]) + ".MPQ";
+ FILE* file = fopen(_fileName.c_str(), "rb");
+ if (file)
+ {
+ if (BaseLocale == -1)
+ {
+ BaseLocale = i;
+ _baseLocale = new MPQArchive(_fileName.c_str());
+ fileName = _fileName;
+ LocaleFiles[i] = _baseLocale;
+ }
+ else
+ LocaleFiles[i] = new MPQArchive(_fileName.c_str());
+
+ AvailableLocales.insert(i);
+ printf("Detected locale: %s\n", Languages[i]);
+ }
+ }
+ Archives.push_front(_baseLocale);
+ if (BaseLocale == -1)
+ {
+ printf("No locale data detected\n");
+ ASSERT(false);
+ }
+ else
+ printf("Using default locale: %s\n", Languages[BaseLocale]);
+}
+
+FILE* MPQManager::GetFile( std::string path )
+{
+ ACE_GUARD_RETURN(ACE_Thread_Mutex, g, mutex, NULL);
+ MPQFile file(path.c_str());
+ if (file.isEof())
+ return NULL;
+ return file.GetFileStream();
+}
+
+DBC* MPQManager::GetDBC( std::string name )
+{
+ std::string path = "DBFilesClient\\" + name + ".dbc";
+ return new DBC(GetFile(path));
+}
+
+FILE* MPQManager::GetFileFrom( std::string path, MPQArchive* file )
+{
+ ACE_GUARD_RETURN(ACE_Thread_Mutex, g, mutex, NULL);
+ mpq_archive* mpq_a = file->mpq_a;
+
+ uint32_t filenum;
+ if(libmpq__file_number(mpq_a, path.c_str(), &filenum))
+ return NULL;
+
+ libmpq__off_t transferred;
+ libmpq__off_t size = 0;
+ libmpq__file_unpacked_size(mpq_a, filenum, &size);
+
+ // HACK: in patch.mpq some files don't want to open and give 1 for filesize
+ if (size <= 1)
+ return NULL;
+
+ uint8* buffer = new uint8[size];
+
+ //libmpq_file_getdata
+ libmpq__file_read(mpq_a, filenum, (unsigned char*)buffer, size, &transferred);
+
+ // Pack the return into a FILE stream
+ FILE* ret = tmpfile();
+ fwrite(buffer, sizeof(uint8), size, ret);
+ return ret;
+}
diff --git a/src/tools/mesh_extractor/MPQManager.h b/src/tools/mesh_extractor/MPQManager.h
new file mode 100644
index 00000000000..2f49ad258a5
--- /dev/null
+++ b/src/tools/mesh_extractor/MPQManager.h
@@ -0,0 +1,36 @@
+#ifndef MPQ_MANAGER_H
+#define MPQ_MANAGER_H
+
+#include "MPQ.h"
+#include <ace/Synch.h>
+#include <set>
+#include <map>
+
+class DBC;
+class MPQManager
+{
+public:
+ MPQManager() {}
+ ~MPQManager() {}
+
+ void Initialize();
+ FILE* GetFile(std::string path);
+ FILE* GetFileFrom(std::string path, MPQArchive* file);
+ DBC* GetDBC(std::string name);
+ std::vector<std::string> GetAllFiles(std::string extension);
+
+ std::deque<MPQArchive*> Archives;
+ int32 BaseLocale;
+ std::set<uint32> AvailableLocales;
+ std::map<uint32, MPQArchive*> LocaleFiles;
+
+ static char const* Files[];
+ static char const* Languages[];
+protected:
+ void InitializeDBC();
+private:
+ ACE_Thread_Mutex mutex;
+};
+
+extern MPQManager* MPQHandler;
+#endif \ No newline at end of file
diff --git a/src/tools/mesh_extractor/MapChunk.cpp b/src/tools/mesh_extractor/MapChunk.cpp
new file mode 100644
index 00000000000..8fe40773d43
--- /dev/null
+++ b/src/tools/mesh_extractor/MapChunk.cpp
@@ -0,0 +1,74 @@
+#include "MapChunk.h"
+#include "ADT.h"
+#include "LiquidHandler.h"
+
+MapChunk::MapChunk( ADT* _adt, Chunk* chunk ) : Adt(_adt), Source(chunk)
+{
+ FILE* stream = chunk->GetStream();
+ Header.Read(stream);
+ fseek(stream, chunk->Offset, SEEK_SET);
+ Index = Header.IndexX + Header.IndexY * 16;
+ GenerateVertices(stream);
+}
+
+void MapChunk::GenerateTriangles()
+{
+ Triangles.reserve(256);
+ for (int y = 0; y < 8; y++)
+ {
+ for (int x = 0; x < 8; x++)
+ {
+ if (HasHole(Header.Holes, x / 2, y / 2))
+ continue;
+
+ uint32 topLeft = (17 * y) + x;
+ uint32 topRight = (17 * y) + x + 1;
+ uint32 bottomLeft = (17 * (y + 1)) + x;
+ uint32 bottomRight = (17 * (y + 1)) + x + 1;
+ uint32 center = (17 * y) + 9 + x;
+
+ Constants::TriangleType triangleType = Constants::TRIANGLE_TYPE_TERRAIN;
+ if (Adt->_LiquidHandler && !Adt->_LiquidHandler->MCNKData.empty())
+ {
+ MCNKLiquidData& data = Adt->_LiquidHandler->MCNKData[Index];
+ float maxHeight = std::max(
+ std::max(
+ std::max(std::max(Vertices[topLeft].z, Vertices[topRight].z), Vertices[bottomLeft].z),
+ Vertices[bottomRight].z), Vertices[center].z);
+ if (data.IsWater(x, y, maxHeight))
+ triangleType = Constants::TRIANGLE_TYPE_WATER;
+ }
+
+ Triangles.push_back(Triangle<uint8>(triangleType, topRight, topLeft, center));
+ Triangles.push_back(Triangle<uint8>(triangleType, topLeft, bottomLeft, center));
+ Triangles.push_back(Triangle<uint8>(triangleType, bottomLeft, bottomRight, center));
+ Triangles.push_back(Triangle<uint8>(triangleType, bottomRight, topRight, center));
+ }
+ }
+}
+
+void MapChunk::GenerateVertices( FILE* stream )
+{
+ fseek(stream, Header.OffsetMCVT, SEEK_CUR);
+ Vertices.reserve(125);
+
+ for (int j = 0; j < 17; j++)
+ {
+ int values = j % 2 ? 8 : 9;
+ for (int i = 0; i < values; i++)
+ {
+ float tmp;
+ if (fread(&tmp, sizeof(float), 1, stream) != 1)
+ printf("MapChunk::GenerateVertices: Failed to read some data expected 1, read 0\n");
+ Vector3 vert(Header.Position.x - (j * (Constants::UnitSize * 0.5f)), Header.Position.y - (i * Constants::UnitSize), Header.Position.z + tmp);
+ if (values == 8)
+ vert.y -= Constants::UnitSize * 0.5f;
+ Vertices.push_back(vert);
+ }
+ }
+}
+
+bool MapChunk::HasHole( uint32 map, int x, int y )
+{
+ return (map & 0x0000FFFF) & ((1 << x) << (y << 2));
+}
diff --git a/src/tools/mesh_extractor/MapChunk.h b/src/tools/mesh_extractor/MapChunk.h
new file mode 100644
index 00000000000..e7d835ae0e3
--- /dev/null
+++ b/src/tools/mesh_extractor/MapChunk.h
@@ -0,0 +1,24 @@
+#ifndef MAPCHUNK_H
+#define MAPCHUNK_H
+#include "Chunk.h"
+#include "Utils.h"
+#include "Constants.h"
+#include <vector>
+class ADT;
+
+class MapChunk
+{
+public:
+ MapChunk(ADT* _adt, Chunk* chunk);
+
+ void GenerateTriangles();
+ void GenerateVertices(FILE* stream);
+ static bool HasHole(uint32 map, int x, int y);
+ ADT* Adt;
+ Chunk* Source;
+ MapChunkHeader Header;
+ std::vector<Vector3> Vertices;
+ std::vector<Triangle<uint8> > Triangles;
+ int32 Index;
+};
+#endif \ No newline at end of file
diff --git a/src/tools/mesh_extractor/MeshExtractor.cpp b/src/tools/mesh_extractor/MeshExtractor.cpp
new file mode 100644
index 00000000000..e06f44c7125
--- /dev/null
+++ b/src/tools/mesh_extractor/MeshExtractor.cpp
@@ -0,0 +1,428 @@
+#include "MPQManager.h"
+#include "WDT.h"
+#include "ContinentBuilder.h"
+#include "Cache.h"
+#include "DBC.h"
+#include "Constants.h"
+#include "Model.h"
+
+#include "Recast.h"
+#include "DetourNavMesh.h"
+#include "DetourNavMeshQuery.h"
+
+#include <set>
+
+MPQManager* MPQHandler;
+CacheClass* Cache;
+
+void ExtractMMaps(std::set<uint32>& mapIds, uint32 threads, bool debug)
+{
+ DBC* dbc = MPQHandler->GetDBC("Map");
+ for (std::vector<Record*>::iterator itr = dbc->Records.begin(); itr != dbc->Records.end(); ++itr)
+ {
+ uint32 mapId = (*itr)->Values[0];
+
+ // Skip this map if a list of specific maps was provided and this one is not contained in it.
+ if (!mapIds.empty() && mapIds.find(mapId) == mapIds.end())
+ continue;
+
+ std::string name = (*itr)->GetString(1);
+ WDT wdt("World\\maps\\" + name + "\\" + name + ".wdt");
+ if (!wdt.IsValid || wdt.IsGlobalModel)
+ continue;
+ printf("Building %s MapId %u\n", name.c_str(), mapId);
+ ContinentBuilder builder(name, mapId, &wdt, threads);
+ builder.Build(debug);
+ }
+}
+
+void ExtractDBCs()
+{
+ printf("Extracting DBCs\n");
+ // Create the filesystem structure
+ std::string baseDBCPath = "dbc/";
+ Utils::CreateDir(baseDBCPath);
+
+ // Populate list of DBC files
+ std::set<std::string> DBCFiles;
+ for (std::vector<std::string>::iterator itr = MPQHandler->LocaleFiles[MPQHandler->BaseLocale]->Files.begin(); itr != MPQHandler->LocaleFiles[MPQHandler->BaseLocale]->Files.end(); ++itr)
+ if (itr->rfind(".dbc") == itr->length() - strlen(".dbc"))
+ DBCFiles.insert(*itr);
+
+ // Iterate over all available locales
+ for (std::set<uint32>::iterator itr = MPQHandler->AvailableLocales.begin(); itr != MPQHandler->AvailableLocales.end(); ++itr)
+ {
+ printf("Extracting DBCs for locale %s\n", MPQManager::Languages[*itr]);
+ std::string path = baseDBCPath;
+ if (*itr != uint32(MPQHandler->BaseLocale))
+ {
+ path += std::string(MPQManager::Languages[*itr]) + "/";
+ Utils::CreateDir(path);
+ }
+
+ std::string component = "component.wow-" + std::string(MPQManager::Languages[*itr]) + ".txt";
+ // Extract the component file
+ Utils::SaveToDisk(MPQHandler->GetFile(component), path + component);
+ // Extract the DBC files for the given locale
+ for (std::set<std::string>::iterator itr2 = DBCFiles.begin(); itr2 != DBCFiles.end(); ++itr2)
+ Utils::SaveToDisk(MPQHandler->GetFileFrom(*itr2, MPQHandler->LocaleFiles[*itr]), path + (itr2->c_str() + strlen("DBFilesClient\\")));
+ }
+ printf("DBC extraction finished!\n");
+}
+
+void ExtractGameobjectModels()
+{
+ Constants::ToWoWCoords = true;
+ printf("Extracting GameObject models\n");
+
+ std::string baseBuildingsPath = "Buildings/";
+ std::string baseVmapsPath = "vmaps/";
+ Utils::CreateDir(baseVmapsPath);
+ Utils::CreateDir(baseBuildingsPath);
+
+ FILE* modelList = fopen((baseVmapsPath + "GameObjectModels.list").c_str(), "wb");
+ if (!modelList)
+ {
+ printf("Could not create file vmaps/GameObjectModels.list, please make sure that you have the write permissions in the folder\n");
+ return;
+ }
+
+ DBC* dbc = MPQHandler->GetDBC("GameObjectDisplayInfo");
+ for (std::vector<Record*>::iterator itr = dbc->Records.begin(); itr != dbc->Records.end(); ++itr)
+ {
+ std::string path = (*itr)->GetString(1);
+ std::string fileName = Utils::GetPlainName(path.c_str());
+ std::string extension = Utils::GetExtension(fileName);
+ // Convert the extension to lowercase
+ std::transform(extension.begin(), extension.end(), extension.begin(), ::tolower);
+ if (extension == "mdx" || extension == "m2")
+ {
+ fileName = Utils::FixModelPath(fileName);
+ Model model(path);
+
+ if (model.IsBad)
+ continue;
+
+ FILE* output = fopen((baseBuildingsPath + fileName).c_str(), "wb");
+ if (!output)
+ {
+ printf("Could not create file %s, please check that you have write permissions\n", (baseBuildingsPath + fileName).c_str());
+ continue;
+ }
+ // Placeholder for 0 values
+ int Nop[12] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+
+ fwrite(Constants::VMAPMagic, 8, 1, output);
+ uint32 numVerts = model.Header.CountBoundingVertices;
+ fwrite(&numVerts, sizeof(uint32), 1, output);
+ uint32 numGroups = 1;
+ fwrite(&numGroups, sizeof(uint32), 1, output);
+ fwrite(Nop, 4 * 3 , 1, output); // rootwmoid, flags, groupid
+ fwrite(Nop, sizeof(float), 3 * 2, output);//bbox, only needed for WMO currently
+ fwrite(Nop, 4, 1, output);// liquidflags
+ fwrite("GRP ", 4, 1, output);
+
+ uint32 branches = 1;
+ uint32 wsize = sizeof(branches) + sizeof(uint32) * branches;
+ fwrite(&wsize, sizeof(uint32), 1, output);
+ fwrite(&branches, sizeof(branches), 1, output);
+ uint32 numTris = model.Header.CountBoundingTriangles;
+ fwrite(&numTris, sizeof(uint32), 1, output);
+ fwrite("INDX", 4, 1, output);
+ wsize = sizeof(uint32) + sizeof(unsigned short) * numTris;
+ fwrite(&wsize, sizeof(int), 1, output);
+ fwrite(&numTris, sizeof(uint32), 1, output);
+ uint16* indices = new uint16[numTris];
+
+ if (numTris > 0)
+ {
+ uint32 i = 0;
+ for (std::vector<Triangle<uint16> >::iterator itr2 = model.Triangles.begin(); itr2 != model.Triangles.end(); ++itr2, ++i)
+ {
+ indices[i * 3 + 0] = itr2->V0;
+ indices[i * 3 + 1] = itr2->V1;
+ indices[i * 3 + 2] = itr2->V2;
+ }
+ fwrite(indices, sizeof(uint16), numTris, output);
+ }
+
+
+ fwrite("VERT", 4, 1, output);
+ wsize = sizeof(int) + sizeof(float) * 3 * numVerts;
+ fwrite(&wsize, sizeof(int), 1, output);
+ fwrite(&numVerts, sizeof(int), 1, output);
+ float* vertices = new float[numVerts*3];
+
+ if (numVerts > 0)
+ {
+ uint32 i = 0;
+ for (std::vector<Vector3>::iterator itr2 = model.Vertices.begin(); itr2 != model.Vertices.end(); ++itr2, ++i)
+ {
+ vertices[i * 3 + 0] = itr2->x;
+ vertices[i * 3 + 1] = itr2->y;
+ vertices[i * 3 + 2] = itr2->z;
+ }
+
+ fwrite(vertices, sizeof(float), numVerts * 3, output);
+ }
+
+ fclose(output);
+ delete[] indices;
+ delete[] vertices;
+
+ uint32 displayId = (*itr)->Values[0];
+ uint32 pathLength = fileName.size();
+ fwrite(&displayId, sizeof(uint32), 1, modelList);
+ fwrite(&pathLength, sizeof(uint32), 1, modelList);
+ fwrite(fileName.c_str(), sizeof(char), pathLength, modelList);
+ }
+ else if (extension == "wmo")
+ {
+ WorldModelRoot model(path);
+
+ FILE* output = fopen((baseBuildingsPath + fileName).c_str(), "wb");
+ if (!output)
+ {
+ printf("Could not create file %s, please check that you have write permissions\n", (baseBuildingsPath + fileName).c_str());
+ continue;
+ }
+
+ fwrite(Constants::VMAPMagic, 1, 8, output);
+ uint32 numVertices = 0;
+ fwrite(&numVertices, sizeof(uint32), 1, output); // will be filled later
+ fwrite(&model.Header.CountGroups, sizeof(uint32), 1, output);
+ fwrite(&model.Header.WmoId, sizeof(uint32), 1, output);
+
+ for (std::vector<WorldModelGroup>::iterator itr2 = model.Groups.begin(); itr2 != model.Groups.end(); ++itr2)
+ {
+ fwrite(&itr2->Header.Flags, sizeof(uint32), 1, output);
+ fwrite(&itr2->Header.WmoId, sizeof(uint32), 1, output);
+ fwrite(&itr2->Header.BoundingBox[0], sizeof(uint32), 1, output);
+ fwrite(&itr2->Header.BoundingBox[1], sizeof(uint32), 1, output);
+ uint32 LiquidFlags = itr2->HasLiquidData ? 1 : 0;
+ fwrite(&LiquidFlags, sizeof(uint32), 1, output);
+
+ fwrite("GRP ", sizeof(char), 4, output);
+ uint32 k = 0;
+ uint32 mobaBatch = itr2->MOBALength / 12;
+ uint32* MobaEx = new uint32[mobaBatch*4];
+
+ for(uint32 i = 8; i < itr2->MOBALength; i += 12)
+ MobaEx[k++] = itr2->MOBA[i];
+
+ int mobaSizeGrp = mobaBatch * 4 + 4;
+ fwrite(&mobaSizeGrp, 4, 1, output);
+ fwrite(&mobaBatch, 4, 1, output);
+ fwrite(MobaEx, 4, k, output);
+ delete[] MobaEx;
+
+ // Note: still not finished
+ }
+
+ fclose(output);
+ }
+ }
+
+ fclose(modelList);
+ printf("GameObject models extraction finished!");
+ Constants::ToWoWCoords = false;
+}
+
+bool HandleArgs(int argc, char** argv, uint32& threads, std::set<uint32>& mapList, bool& debugOutput, uint32& extractFlags)
+{
+ char* param = NULL;
+ extractFlags = 0;
+
+ for (int i = 1; i < argc; ++i)
+ {
+ if (strcmp(argv[i], "--threads") == 0)
+ {
+ param = argv[++i];
+ if (!param)
+ return false;
+
+ threads = atoi(param);
+ printf("Using %i threads\n", threads);
+ }
+ else if (strcmp(argv[i], "--maps") == 0)
+ {
+ param = argv[++i];
+ if (!param)
+ return false;
+
+ char* copy = strdup(param);
+ char* token = strtok(copy, ",");
+ while (token)
+ {
+ mapList.insert(atoi(token));
+ token = strtok(NULL, ",");
+ }
+
+ printf("Extracting only provided list of maps (%u).\n", uint32(mapList.size()));
+ }
+ else if (strcmp(argv[i], "--debug") == 0)
+ {
+ param = argv[++i];
+ if (!param)
+ return false;
+ debugOutput = atoi(param);
+ if (debugOutput)
+ printf("Output will contain debug information (.obj files)\n");
+ }
+ else if (strcmp(argv[i], "--extract") == 0)
+ {
+ param = argv[++i];
+ if (!param)
+ return false;
+
+ extractFlags = atoi(param);
+
+ if (!(extractFlags & Constants::EXTRACT_FLAG_ALLOWED)) // Tried to use an invalid flag
+ return false;
+
+ printf("Detected flags: \n");
+ printf("* Extract DBCs: %s\n", (extractFlags & Constants::EXTRACT_FLAG_DBC) ? "Yes" : "No");
+ printf("* Extract Maps: %s\n", (extractFlags & Constants::EXTRACT_FLAG_MAPS) ? "Yes" : "No");
+ printf("* Extract VMaps: %s\n", (extractFlags & Constants::EXTRACT_FLAG_VMAPS) ? "Yes" : "No");
+ printf("* Extract GameObject Models: %s\n", (extractFlags & Constants::EXTRACT_FLAG_GOB_MODELS) ? "Yes" : "No");
+ printf("* Extract MMaps: %s\n", (extractFlags & Constants::EXTRACT_FLAG_MMAPS) ? "Yes" : "No");
+ }
+ }
+ return true;
+}
+
+void PrintUsage()
+{
+ printf("MeshExtractor help.\n");
+ printf("* Use \"--threads <number>\" to specify <number> threads, default to 4 (Only available when extracting MMaps)\n");
+ printf("* Use \"--maps a,b,c,d,e\" to extract only the maps specified (Do not use spaces)\n");
+ printf("* Use \"--debug 1\" to generate debug information of the tiles (Only available when extracting MMaps)\n");
+ printf("* Use \"--extract X\" to extract the data specified by the flag X (Note: You can combine the flags with the bitwise OR operator |). Available flags are: \n");
+ {
+ printf("- %u to extract DBCs\n", Constants::EXTRACT_FLAG_DBC);
+ printf("- %u to extract Maps (Not yet implemented)\n", Constants::EXTRACT_FLAG_MAPS);
+ printf("- %u to extract VMaps (Not yet implemented)\n", Constants::EXTRACT_FLAG_VMAPS);
+ printf("- %u to extract GameObject models (Not yet finished, you need to run VMapAssembler on the extracted files)\n", Constants::EXTRACT_FLAG_GOB_MODELS);
+ printf("- %u to extract MMaps (Not yet finished)\n", Constants::EXTRACT_FLAG_MMAPS);
+ }
+}
+
+void LoadTile(dtNavMesh*& navMesh, const char* tile)
+{
+ FILE* f = fopen(tile, "rb");
+ MmapTileHeader header;
+
+ if (fread(&header, sizeof(MmapTileHeader), 1, f) != 1)
+ return;
+
+ uint8* nav = new uint8[header.size];
+ if (fread(nav, header.size, 1, f) != 1)
+ return;
+
+ navMesh->addTile(nav, header.size, DT_TILE_FREE_DATA, 0, NULL);
+
+ fclose(f);
+}
+
+int main(int argc, char* argv[])
+{
+ if (!system("pause"))
+ {
+ printf("main: Error in system call to pause\n");
+ return -1;
+ }
+
+ uint32 threads = 4, extractFlags = 0;
+ std::set<uint32> mapIds;
+ bool debug = false;
+
+ if (!HandleArgs(argc, argv, threads, mapIds, debug, extractFlags))
+ {
+ PrintUsage();
+ return -1;
+ }
+
+ Cache = new CacheClass();
+ MPQHandler = new MPQManager();
+ MPQHandler->Initialize();
+
+ if (extractFlags & Constants::EXTRACT_FLAG_DBC)
+ ExtractDBCs();
+
+ if (extractFlags & Constants::EXTRACT_FLAG_MMAPS)
+ ExtractMMaps(mapIds, threads, debug);
+
+ if (extractFlags & Constants::EXTRACT_FLAG_GOB_MODELS)
+ ExtractGameobjectModels();
+
+ if (extractFlags & Constants::EXTRACT_FLAG_TEST)
+ {
+ float start[] = { 0.0f, 0.0f, 0.0f };
+ float end[] = { 0.0f, 0.0f, 0.0f };
+
+ //
+ float m_spos[3];
+ m_spos[0] = -1.0f * start[1];
+ m_spos[1] = start[2];
+ m_spos[2] = -1.0f * start[0];
+
+ //
+ float m_epos[3];
+ m_epos[0] = -1.0f * end[1];
+ m_epos[1] = end[2];
+ m_epos[2] = -1.0f * end[0];
+
+ //
+ dtQueryFilter m_filter;
+ m_filter.setIncludeFlags(0xffff) ;
+ m_filter.setExcludeFlags(0);
+
+ //
+ float m_polyPickExt[3];
+ m_polyPickExt[0] = 2;
+ m_polyPickExt[1] = 4;
+ m_polyPickExt[2] = 2;
+
+ //
+ dtPolyRef m_startRef;
+ dtPolyRef m_endRef;
+
+ FILE* mmap = fopen(".mmap", "rb");
+ dtNavMeshParams params;
+ int count = fread(&params, sizeof(dtNavMeshParams), 1, mmap);
+ fclose(mmap);
+ if (count != 1)
+ {
+ printf("main: Error reading from .mmap\n");
+ return 0;
+ }
+
+ dtNavMesh* navMesh = new dtNavMesh();
+ dtNavMeshQuery* navMeshQuery = new dtNavMeshQuery();
+
+ navMesh->init(&params);
+ LoadTile(navMesh, ".mmtile");
+ LoadTile(navMesh, ".mmtile");
+ LoadTile(navMesh, ".mmtile");
+ LoadTile(navMesh, ".mmtile");
+ LoadTile(navMesh, ".mmtile");
+ LoadTile(navMesh, ".mmtile");
+
+ navMeshQuery->init(navMesh, 2048);
+
+ float nearestPt[3];
+
+ navMeshQuery->findNearestPoly(m_spos, m_polyPickExt, &m_filter, &m_startRef, nearestPt);
+ navMeshQuery->findNearestPoly(m_epos, m_polyPickExt, &m_filter, &m_endRef, nearestPt);
+
+ if ( !m_startRef || !m_endRef )
+ {
+ std::cerr << "Could not find any nearby poly's (" << m_startRef << "," << m_endRef << ")" << std::endl;
+ return 0;
+ }
+
+ printf("Found!");
+ }
+
+ return 0;
+}
diff --git a/src/tools/mesh_extractor/Model.cpp b/src/tools/mesh_extractor/Model.cpp
new file mode 100644
index 00000000000..77b1adbeaa0
--- /dev/null
+++ b/src/tools/mesh_extractor/Model.cpp
@@ -0,0 +1,67 @@
+#include "Model.h"
+#include "MPQManager.h"
+#include "Utils.h"
+
+Model::Model( std::string path ) : IsCollidable(false), IsBad(false)
+{
+ Stream = MPQHandler->GetFile(Utils::FixModelPath(path));
+ if (!Stream)
+ {
+ IsBad = true;
+ return;
+ }
+ Header.Read(Stream);
+ if (Header.OffsetBoundingNormals > 0 && Header.OffsetBoundingVertices > 0 &&
+ Header.OffsetBoundingTriangles > 0 && Header.BoundingRadius > 0.0f)
+ {
+ IsCollidable = true;
+ ReadVertices(Stream);
+ ReadBoundingNormals(Stream);
+ ReadBoundingTriangles(Stream);
+ }
+}
+
+Model::~Model()
+{
+ if (Stream)
+ fclose(Stream);
+}
+
+void Model::ReadVertices( FILE* stream )
+{
+ fseek(stream, Header.OffsetBoundingVertices, SEEK_SET);
+ Vertices.reserve(Header.CountBoundingVertices);
+ for (uint32 i = 0; i < Header.CountBoundingVertices; ++i)
+ {
+ Vertices.push_back(Vector3::Read(stream));
+ if (Constants::ToWoWCoords)
+ Vertices[i] = Utils::ToWoWCoords(Vertices[i]);
+ }
+}
+
+void Model::ReadBoundingTriangles( FILE* stream )
+{
+ fseek(stream, Header.OffsetBoundingTriangles, SEEK_SET);
+ Triangles.reserve(Header.CountBoundingTriangles / 3);
+ for (uint32 i = 0; i < Header.CountBoundingTriangles / 3; i++)
+ {
+ Triangle<uint16> tri;
+ tri.Type = Constants::TRIANGLE_TYPE_DOODAD;
+ int count = 0;
+ count += fread(&tri.V0, sizeof(uint16), 1, stream);
+ count += fread(&tri.V1, sizeof(uint16), 1, stream);
+ count += fread(&tri.V2, sizeof(uint16), 1, stream);
+ if (count != 3)
+ printf("Model::ReadBoundingTriangles: Error reading data, expected 3, read %d\n", count);
+ Triangles.push_back(tri);
+ }
+}
+
+void Model::ReadBoundingNormals( FILE* stream )
+{
+ fseek(stream, Header.OffsetBoundingNormals, SEEK_SET);
+ Normals.reserve(Header.CountBoundingNormals);
+ for (uint32 i = 0; i < Header.CountBoundingNormals; i++)
+ Normals.push_back(Vector3::Read(stream));
+}
+
diff --git a/src/tools/mesh_extractor/Model.h b/src/tools/mesh_extractor/Model.h
new file mode 100644
index 00000000000..ea9331e7c30
--- /dev/null
+++ b/src/tools/mesh_extractor/Model.h
@@ -0,0 +1,23 @@
+#ifndef MODEL_H
+#define MODEL_H
+#include <vector>
+#include "Utils.h"
+
+class Model
+{
+public:
+ Model(std::string path);
+ ~Model();
+
+ void ReadVertices(FILE* stream);
+ void ReadBoundingTriangles(FILE* stream);
+ void ReadBoundingNormals(FILE* stream);
+ ModelHeader Header;
+ std::vector<Vector3> Vertices;
+ std::vector<Vector3> Normals;
+ std::vector<Triangle<uint16> > Triangles;
+ bool IsCollidable;
+ FILE* Stream;
+ bool IsBad;
+};
+#endif \ No newline at end of file
diff --git a/src/tools/mesh_extractor/ObjectDataHandler.cpp b/src/tools/mesh_extractor/ObjectDataHandler.cpp
new file mode 100644
index 00000000000..789efc6d62c
--- /dev/null
+++ b/src/tools/mesh_extractor/ObjectDataHandler.cpp
@@ -0,0 +1,21 @@
+#include "ObjectDataHandler.h"
+#include "Chunk.h"
+#include "ADT.h"
+#include "ChunkedData.h"
+
+void ObjectDataHandler::ProcessMapChunk( MapChunk* chunk )
+{
+ if (!Source->HasObjectData)
+ return;
+ // fuck it blizzard, why is this crap necessary?
+ int32 firstIndex = Source->ObjectData->GetFirstIndex("MCNK");
+ if (firstIndex == -1)
+ return;
+ if (uint32(firstIndex + chunk->Index) > Source->ObjectData->Chunks.size())
+ return;
+ Chunk* ourChunk = Source->ObjectData->Chunks[firstIndex + chunk->Index];
+ if (ourChunk->Length == 0)
+ return;
+ ChunkedData* subChunks = new ChunkedData(ourChunk->GetStream(), ourChunk->Length, 2);
+ ProcessInternal(subChunks);
+}
diff --git a/src/tools/mesh_extractor/ObjectDataHandler.h b/src/tools/mesh_extractor/ObjectDataHandler.h
new file mode 100644
index 00000000000..75b4e45700c
--- /dev/null
+++ b/src/tools/mesh_extractor/ObjectDataHandler.h
@@ -0,0 +1,15 @@
+#ifndef ODATA_HNDL_H
+#define ODATA_HNDL_H
+#include "ADT.h"
+#include "MapChunk.h"
+
+class ObjectDataHandler
+{
+public:
+ ObjectDataHandler(ADT* _adt) : Source(_adt) {}
+
+ void ProcessMapChunk(MapChunk* chunk);
+ virtual void ProcessInternal(ChunkedData* data) = 0;
+ ADT* Source;
+};
+#endif \ No newline at end of file
diff --git a/src/tools/mesh_extractor/TileBuilder.cpp b/src/tools/mesh_extractor/TileBuilder.cpp
new file mode 100644
index 00000000000..9bb9b11619f
--- /dev/null
+++ b/src/tools/mesh_extractor/TileBuilder.cpp
@@ -0,0 +1,311 @@
+#include "ContinentBuilder.h"
+#include "TileBuilder.h"
+#include "Geometry.h"
+#include "Constants.h"
+#include "Utils.h"
+#include "Cache.h"
+#include "ADT.h"
+#include "WDT.h"
+#include "Recast.h"
+#include "RecastAlloc.h"
+#include "DetourNavMeshBuilder.h"
+
+#include <ace/Synch.h>
+
+TileBuilder::TileBuilder(ContinentBuilder* _cBuilder, std::string world, int x, int y, uint32 mapId) :
+ World(world), X(x), Y(y), MapId(mapId), _Geometry(NULL), DataSize(0), cBuilder(_cBuilder)
+{
+ /*
+ Test, non-working values
+ // Cell Size = TileSize / TileVoxelSize
+ // 1800 = TileVoxelSize
+ Config.cs = Constants::TileSize / 1800;
+ // Cell Height
+ Config.ch = 0.4f;
+ // Min Region Area = 20^2
+ Config.minRegionArea = 20*20;
+ // Merge Region Area = 40^2
+ Config.mergeRegionArea = 40*40;
+ Config.tileSize = Constants::TileSize / 4;
+ Config.walkableSlopeAngle = 50.0f;
+ Config.detailSampleDist = 3.0f;
+ Config.detailSampleMaxError = 1.25f;
+ Config.walkableClimb = floorf(1.0f / Config.ch);
+ Config.walkableHeight = ceilf(1.652778f / Config.ch);
+ Config.walkableRadius = ceilf(0.2951389f / Config.cs);
+ Config.maxEdgeLen = Config.walkableRadius * 8;
+ Config.borderSize = Config.walkableRadius + 4;
+ Config.width = 1800 + Config.borderSize * 2;
+ Config.height = 1800 + Config.borderSize * 2;
+ Config.maxVertsPerPoly = 6;
+ Config.maxSimplificationError = 1.3f;
+ */
+
+ // All are in UNIT metrics!
+ memset(&Config, 0, sizeof(rcConfig));
+
+ Config.maxVertsPerPoly = DT_VERTS_PER_POLYGON;
+ Config.cs = Constants::BaseUnitDim;
+ Config.ch = Constants::BaseUnitDim;
+ Config.walkableSlopeAngle = 60.0f;
+ Config.tileSize = Constants::VertexPerTile;
+ Config.walkableRadius = 1;
+ Config.borderSize = Config.walkableRadius + 3;
+ Config.maxEdgeLen = Constants::VertexPerTile + 1; //anything bigger than tileSize
+ Config.walkableHeight = 3;
+ Config.walkableClimb = 2; // keep less than walkableHeight
+ Config.minRegionArea = rcSqr(60);
+ Config.mergeRegionArea = rcSqr(50);
+ Config.maxSimplificationError = 2.0f; // eliminates most jagged edges (tinny polygons)
+ Config.detailSampleDist = Config.cs * 64;
+ Config.detailSampleMaxError = Config.ch * 2;
+
+ Context = new rcContext;
+}
+
+void TileBuilder::CalculateTileBounds( float*& bmin, float*& bmax, dtNavMeshParams& /*navMeshParams*/ )
+{
+ bmin = new float[3];
+ bmax = new float[3];
+ bmin[0] = Constants::Origin[0] /*navMeshParams.orig[0]*/ + (Constants::TileSize * X);
+ bmin[2] = Constants::Origin[2] /*navMeshParams.orig[2]*/ + (Constants::TileSize * Y);
+ bmax[0] = Constants::Origin[0] /*navMeshParams.orig[0]*/ + (Constants::TileSize * (X + 1));
+ bmax[2] = Constants::Origin[2] /*navMeshParams.orig[2]*/ + (Constants::TileSize * (Y + 1));
+}
+
+uint8* TileBuilder::Build(bool dbg, dtNavMeshParams& navMeshParams)
+{
+ _Geometry = new Geometry();
+ _Geometry->Transform = true;
+ ADT* adt = new ADT(Utils::GetAdtPath(World, X, Y));
+ adt->Read();
+ _Geometry->AddAdt(adt);
+ delete adt;
+
+ if (_Geometry->Vertices.empty() && _Geometry->Triangles.empty())
+ return NULL;
+
+ // again, we load everything - wasteful but who cares
+ for (int ty = Y - 2; ty <= Y + 2; ty++)
+ {
+ for (int tx = X - 2; tx <= X + 2; tx++)
+ {
+ // don't load main tile again
+ if (tx == X && ty == Y)
+ continue;
+
+ ADT* _adt = new ADT(Utils::GetAdtPath(World, tx, ty));
+ // If this condition is met, it means that this wdt does not contain the ADT
+ if (!_adt->Data->Stream)
+ {
+ delete _adt;
+ continue;
+ }
+ _adt->Read();
+ _Geometry->AddAdt(_adt);
+ delete _adt;
+ }
+ }
+
+ if (dbg)
+ {
+ char buff[100];
+ sprintf(buff, "mmaps/%s_%02u%02u.obj", World.c_str(), Y, X);
+ FILE* debug = fopen(buff, "wb");
+ for (uint32 i = 0; i < _Geometry->Vertices.size(); ++i)
+ fprintf(debug, "v %f %f %f\n", _Geometry->Vertices[i].x, _Geometry->Vertices[i].y, _Geometry->Vertices[i].z);
+ for (uint32 i = 0; i < _Geometry->Triangles.size(); ++i)
+ fprintf(debug, "f %i %i %i\n", _Geometry->Triangles[i].V0 + 1, _Geometry->Triangles[i].V1 + 1, _Geometry->Triangles[i].V2 + 1);
+ fclose(debug);
+ }
+
+ uint32 numVerts = _Geometry->Vertices.size();
+ uint32 numTris = _Geometry->Triangles.size();
+ float* vertices;
+ int* triangles;
+ uint8* areas;
+ _Geometry->GetRawData(vertices, triangles, areas);
+ _Geometry->Vertices.clear();
+ _Geometry->Triangles.clear();
+
+
+ rcVcopy(Config.bmin, cBuilder->bmin);
+ rcVcopy(Config.bmax, cBuilder->bmax);
+
+ // this sets the dimensions of the heightfield - should maybe happen before border padding
+ rcCalcGridSize(Config.bmin, Config.bmax, Config.cs, &Config.width, &Config.height);
+
+ // Initialize per tile config.
+ rcConfig tileCfg = Config;
+ tileCfg.width = Config.tileSize + Config.borderSize * 2;
+ tileCfg.height = Config.tileSize + Config.borderSize * 2;
+
+ // merge per tile poly and detail meshes
+ rcPolyMesh** pmmerge = new rcPolyMesh*[Constants::TilesPerMap * Constants::TilesPerMap];
+ rcPolyMeshDetail** dmmerge = new rcPolyMeshDetail*[Constants::TilesPerMap * Constants::TilesPerMap];
+
+ int nmerge = 0;
+ for (int y = 0; y < Constants::TilesPerMap; ++y)
+ {
+ for (int x = 0; x < Constants::TilesPerMap; ++x)
+ {
+ // Calculate the per tile bounding box.
+ tileCfg.bmin[0] = Config.bmin[0] + float(x * Config.tileSize - Config.borderSize) * Config.cs;
+ tileCfg.bmin[2] = Config.bmin[2] + float(y * Config.tileSize - Config.borderSize) * Config.cs;
+ tileCfg.bmax[0] = Config.bmin[0] + float((x + 1) * Config.tileSize + Config.borderSize) * Config.cs;
+ tileCfg.bmax[2] = Config.bmin[2] + float((y + 1) * Config.tileSize + Config.borderSize) * Config.cs;
+
+
+ rcHeightfield* hf = rcAllocHeightfield();
+ rcCreateHeightfield(Context, *hf, tileCfg.width, tileCfg.height, tileCfg.bmin, tileCfg.bmax, tileCfg.cs, tileCfg.ch);
+ rcClearUnwalkableTriangles(Context, tileCfg.walkableSlopeAngle, vertices, numVerts, triangles, numTris, areas);
+ rcRasterizeTriangles(Context, vertices, numVerts, triangles, areas, numTris, *hf, Config.walkableClimb);
+
+ // Once all geometry is rasterized, we do initial pass of filtering to
+ // remove unwanted overhangs caused by the conservative rasterization
+ // as well as filter spans where the character cannot possibly stand.
+ rcFilterLowHangingWalkableObstacles(Context, Config.walkableClimb, *hf);
+ rcFilterLedgeSpans(Context, tileCfg.walkableHeight, tileCfg.walkableClimb, *hf);
+ rcFilterWalkableLowHeightSpans(Context, tileCfg.walkableHeight, *hf);
+
+ // Compact the heightfield so that it is faster to handle from now on.
+ // This will result in more cache coherent data as well as the neighbours
+ // between walkable cells will be calculated.
+ rcCompactHeightfield* chf = rcAllocCompactHeightfield();
+ rcBuildCompactHeightfield(Context, tileCfg.walkableHeight, tileCfg.walkableClimb, *hf, *chf);
+
+ rcFreeHeightField(hf);
+
+ // Erode the walkable area by agent radius.
+ rcErodeWalkableArea(Context, Config.walkableRadius, *chf);
+ // Prepare for region partitioning, by calculating distance field along the walkable surface.
+ rcBuildDistanceField(Context, *chf);
+ // Partition the walkable surface into simple regions without holes.
+ rcBuildRegions(Context, *chf, tileCfg.borderSize, tileCfg.minRegionArea, tileCfg.mergeRegionArea);
+
+ // Create contours.
+ rcContourSet* cset = rcAllocContourSet();
+ rcBuildContours(Context, *chf, tileCfg.maxSimplificationError, tileCfg.maxEdgeLen, *cset);
+
+ // Build polygon navmesh from the contours.
+ rcPolyMesh* pmesh = rcAllocPolyMesh();
+ rcBuildPolyMesh(Context, *cset, tileCfg.maxVertsPerPoly, *pmesh);
+
+ // Build detail mesh.
+ rcPolyMeshDetail* dmesh = rcAllocPolyMeshDetail();
+ rcBuildPolyMeshDetail(Context, *pmesh, *chf, tileCfg.detailSampleDist, tileCfg.detailSampleMaxError, *dmesh);
+
+ // Free memory
+ rcFreeCompactHeightfield(chf);
+ rcFreeContourSet(cset);
+
+ pmmerge[nmerge] = pmesh;
+ dmmerge[nmerge] = dmesh;
+ ++nmerge;
+ }
+ }
+
+ rcPolyMesh* pmesh = rcAllocPolyMesh();
+ rcMergePolyMeshes(Context, pmmerge, nmerge, *pmesh);
+
+ rcPolyMeshDetail* dmesh = rcAllocPolyMeshDetail();
+ rcMergePolyMeshDetails(Context, dmmerge, nmerge, *dmesh);
+
+ delete[] pmmerge;
+ delete[] dmmerge;
+
+ printf("[%02i,%02i] Meshes merged!\n", X, Y);
+
+ // Remove padding from the polymesh data. (Remove this odditity)
+ for (int i = 0; i < pmesh->nverts; ++i)
+ {
+ unsigned short* v = &pmesh->verts[i * 3];
+ v[0] -= (unsigned short)Config.borderSize;
+ v[2] -= (unsigned short)Config.borderSize;
+ }
+
+ // Set flags according to area types (e.g. Swim for Water)
+ for (int i = 0; i < pmesh->npolys; i++)
+ {
+ if (pmesh->areas[i] == Constants::POLY_AREA_ROAD || pmesh->areas[i] == Constants::POLY_AREA_TERRAIN)
+ pmesh->flags[i] = Constants::POLY_FLAG_WALK;
+ else if (pmesh->areas[i] == Constants::POLY_AREA_WATER)
+ pmesh->flags[i] = Constants::POLY_FLAG_SWIM;
+ }
+
+ dtNavMeshCreateParams params;
+ memset(&params, 0, sizeof(params));
+ // PolyMesh data
+ params.verts = pmesh->verts;
+ params.vertCount = pmesh->nverts;
+ params.polys = pmesh->polys;
+ params.polyAreas = pmesh->areas;
+ params.polyFlags = pmesh->flags;
+ params.polyCount = pmesh->npolys;
+ params.nvp = pmesh->nvp;
+ // PolyMeshDetail data
+ params.detailMeshes = dmesh->meshes;
+ params.detailVerts = dmesh->verts;
+ params.detailVertsCount = dmesh->nverts;
+ params.detailTris = dmesh->tris;
+ params.detailTriCount = dmesh->ntris;
+ rcVcopy(params.bmin, pmesh->bmin);
+ rcVcopy(params.bmax, pmesh->bmax);
+ // General settings
+ params.ch = Config.ch;
+ params.cs = Config.cs;
+ params.walkableClimb = Constants::BaseUnitDim * Config.walkableClimb;
+ params.walkableHeight = Constants::BaseUnitDim * Config.walkableHeight;
+ params.walkableRadius = Constants::BaseUnitDim * Config.walkableRadius;
+ params.tileX = (((cBuilder->bmin[0] + cBuilder->bmax[0]) / 2) - navMeshParams.orig[0]) / Constants::TileSize;
+ params.tileY = (((cBuilder->bmin[2] + cBuilder->bmax[2]) / 2) - navMeshParams.orig[2]) / Constants::TileSize;
+
+ rcVcopy(params.bmin, cBuilder->bmin);
+ rcVcopy(params.bmax, cBuilder->bmax);
+
+ // Offmesh-connection settings
+ params.offMeshConCount = 0; // none for now
+
+ params.tileSize = Constants::VertexPerMap;
+
+ if (!params.polyCount || !params.polys || Constants::TilesPerMap * Constants::TilesPerMap == params.polyCount)
+ {
+ // we have flat tiles with no actual geometry - don't build those, its useless
+ // keep in mind that we do output those into debug info
+ // drop tiles with only exact count - some tiles may have geometry while having less tiles
+ printf("[%02i,%02i] No polygons to build on tile, skipping.\n", X, Y);
+ rcFreePolyMesh(pmesh);
+ rcFreePolyMeshDetail(dmesh);
+ delete areas;
+ delete triangles;
+ delete vertices;
+ return NULL;
+ }
+
+ int navDataSize;
+ uint8* navData;
+ printf("[%02i,%02i] Creating the navmesh with %i vertices, %i polys, %i triangles!\n", X, Y, pmesh->nverts, pmesh->npolys, dmesh->ntris);
+ bool result = dtCreateNavMeshData(&params, &navData, &navDataSize);
+
+ // Free some memory
+ rcFreePolyMesh(pmesh);
+ rcFreePolyMeshDetail(dmesh);
+ delete areas;
+ delete triangles;
+ delete vertices;
+
+ if (result)
+ {
+ printf("[%02i,%02i] NavMesh created, size %i!\n", X, Y, navDataSize);
+ DataSize = navDataSize;
+ return navData;
+ }
+
+ return NULL;
+}
+
+TileBuilder::~TileBuilder()
+{
+ delete Context;
+ delete _Geometry;
+}
diff --git a/src/tools/mesh_extractor/TileBuilder.h b/src/tools/mesh_extractor/TileBuilder.h
new file mode 100644
index 00000000000..40c96f6ec42
--- /dev/null
+++ b/src/tools/mesh_extractor/TileBuilder.h
@@ -0,0 +1,30 @@
+#ifndef TILE_BUILD_H
+#define TILE_BUILD_H
+#include <string>
+#include "Recast.h"
+
+#include "Geometry.h"
+
+class ContinentBuilder;
+class WDT;
+
+class TileBuilder
+{
+public:
+ TileBuilder(ContinentBuilder* _cBuilder, std::string world, int x, int y, uint32 mapId);
+ ~TileBuilder();
+
+ void CalculateTileBounds(float*& bmin, float*& bmax, dtNavMeshParams& navMeshParams);
+ uint8* Build(bool dbg, dtNavMeshParams& navMeshParams);
+
+ std::string World;
+ int X;
+ int Y;
+ int MapId;
+ rcConfig Config;
+ rcContext* Context;
+ Geometry* _Geometry;
+ uint32 DataSize;
+ ContinentBuilder* cBuilder;
+};
+#endif \ No newline at end of file
diff --git a/src/tools/mesh_extractor/Utils.cpp b/src/tools/mesh_extractor/Utils.cpp
new file mode 100644
index 00000000000..119ac94f94f
--- /dev/null
+++ b/src/tools/mesh_extractor/Utils.cpp
@@ -0,0 +1,568 @@
+#include "Utils.h"
+#include "WorldModelHandler.h"
+#include "Constants.h"
+#include <cstring>
+#include "G3D/Matrix4.h"
+#include "G3D/Quat.h"
+
+#ifdef _WIN32
+ #include "direct.h"
+#else
+ #include <sys/stat.h>
+ #include <unistd.h>
+#endif
+
+const float Constants::TileSize = 533.0f + (1/3.0f);
+const float Constants::MaxXY = 32.0f * Constants::TileSize;
+const float Constants::ChunkSize = Constants::TileSize / 16.0f;
+const float Constants::UnitSize = Constants::ChunkSize / 8.0f;
+const float Constants::Origin[] = { -Constants::MaxXY, 0.0f, -Constants::MaxXY };
+const float Constants::PI = 3.1415926f;
+const float Constants::MaxStandableHeight = 1.5f;
+const char* Constants::VMAPMagic = "VMAP041";
+bool Constants::ToWoWCoords = false;
+const float Constants::BaseUnitDim = 0.533333f;
+const int Constants::VertexPerMap = (Constants::TileSize / Constants::BaseUnitDim) + 0.5f;
+const int Constants::VertexPerTile = 40;
+const int Constants::TilesPerMap = Constants::VertexPerMap / Constants::VertexPerTile;
+
+void Utils::CreateDir( const std::string& Path )
+{
+#ifdef _WIN32
+ _mkdir( Path.c_str());
+#else
+ mkdir( Path.c_str(), 0777 );
+#endif
+}
+
+void Utils::Reverse(char word[])
+{
+ int len = strlen(word);
+ for (int i = 0;i < len / 2; i++)
+ {
+ word[i] ^= word[len-i-1];
+ word[len-i-1] ^= word[i];
+ word[i] ^= word[len-i-1];
+ }
+}
+
+std::string Utils::ReadString( FILE* file )
+{
+ std::string ret;
+ int i = 0;
+ while (true)
+ {
+ char b;
+ if (fread(&b, sizeof(char), 1, file) != 1 || b == 0)
+ break;
+ ret[i++] = b;
+ }
+ return ret;
+}
+
+uint32 Utils::Size( FILE* file )
+{
+ // store the old position
+ uint32 offset = ftell(file);
+ // Get file size
+ fseek(file, 0, SEEK_END);
+ uint32 size = ftell(file);
+ // reset back to the old position
+ fseek(file, offset, SEEK_SET);
+ return size;
+}
+
+Vector3 Utils::ToRecast( Vector3 val )
+{
+ return Vector3(-val.y, val.z, -val.x);
+}
+
+std::string Utils::GetAdtPath( std::string world, int x, int y )
+{
+ return "World\\Maps\\" + world + "\\" + world + "_" + Utils::ToString(x) + "_" + Utils::ToString(y) + ".adt";
+}
+
+std::string Utils::FixModelPath( std::string path )
+{
+ return Utils::GetPathBase(path) + ".M2";
+}
+
+G3D::Matrix4 Utils::RotationX(float angle)
+{
+ float _cos = cos(angle);
+ float _sin = sin(angle);
+ G3D::Matrix4 ret = G3D::Matrix4::identity();
+ ret[2][2] = _cos;
+ ret[2][3] = _sin;
+ ret[3][2] = -_sin;
+ ret[3][3] = _cos;
+ return ret;
+}
+
+G3D::Matrix4 Utils::GetTransformation(IDefinition def)
+{
+ G3D::Matrix4 translation;
+ if (def.Position.x == 0.0f && def.Position.y == 0.0f && def.Position.z == 0.0f)
+ translation = G3D::Matrix4::identity();
+ else
+ translation = G3D::Matrix4::translation(-(def.Position.z - Constants::MaxXY),
+ -(def.Position.x - Constants::MaxXY), def.Position.y);
+
+ G3D::Matrix4 rotation = RotationX(ToRadians(def.Rotation.z)) * RotationY(ToRadians(def.Rotation.x)) * RotationZ(ToRadians(def.Rotation.y + 180));
+ if (def.Scale() < 1.0f || def.Scale() > 1.0f)
+ return G3D::Matrix4::scale(def.Scale()) * rotation * translation;
+ return rotation * translation;
+}
+
+G3D::Matrix4 Utils::RotationY( float angle )
+{
+ float _cos = cos(angle);
+ float _sin = sin(angle);
+ G3D::Matrix4 ret = G3D::Matrix4::identity();
+ ret[1][1] = _cos;
+ ret[1][3] = -_sin;
+ ret[3][1] = _sin;
+ ret[3][3] = _cos;
+ return ret;
+}
+
+G3D::Matrix4 Utils::RotationZ( float angle )
+{
+ float _cos = cos(angle);
+ float _sin = sin(angle);
+ G3D::Matrix4 ret = G3D::Matrix4::identity();
+ ret[1][1] = _cos;
+ ret[1][2] = _sin;
+ ret[2][1] = -_sin;
+ ret[2][2] = _cos;
+ return ret;
+}
+
+float Utils::ToRadians( float degrees )
+{
+ return Constants::PI * degrees / 180.0f;
+}
+
+Vector3 Utils::VectorTransform( Vector3 vec, G3D::Matrix4 matrix )
+{
+ Vector3 ret;
+ ret.x = vec.x * matrix[1][1] + vec.y * matrix[2][1] + vec.z * matrix[3][1] + matrix[4][1];
+ ret.y = vec.x * matrix[1][2] + vec.y * matrix[2][2] + vec.z * matrix[3][2] + matrix[4][2];
+ ret.z = vec.x * matrix[1][3] + vec.y * matrix[2][3] + vec.z * matrix[3][3] + matrix[4][3];
+ return ret;
+}
+
+std::string Utils::GetPathBase( std::string path )
+{
+ size_t lastIndex = path.find_last_of(".");
+ if (lastIndex != std::string::npos)
+ return path.substr(0, lastIndex);
+ return path;
+}
+
+Vector3 Vector3::Read( FILE* file )
+{
+ Vector3 ret;
+ if (fread(&ret, sizeof(Vector3), 1, file) != 1)
+ printf("Vector3::Read: Failed to read some data expected 1, read 0\n");
+ return ret;
+}
+
+Vector3 Utils::GetLiquidVert(G3D::Matrix4 transformation, Vector3 basePosition, float height, int /*x*/, int /*y*/)
+{
+ if (Utils::Distance(height, 0.0f) > 0.5f)
+ basePosition.z = 0.0f;
+ return Utils::VectorTransform(basePosition + Vector3(basePosition.x * Constants::UnitSize, basePosition.y * Constants::UnitSize, height), transformation);
+}
+
+float Utils::Distance( float x, float y )
+{
+ return sqrt(x*x + y*y);
+}
+
+std::string Utils::Replace( std::string str, const std::string& oldStr, const std::string& newStr )
+{
+ size_t pos = 0;
+ while((pos = str.find(oldStr, pos)) != std::string::npos)
+ {
+ str.replace(pos, oldStr.length(), newStr);
+ pos += newStr.length();
+ }
+ return str;
+}
+
+G3D::Matrix4 Utils::GetWmoDoodadTransformation( DoodadInstance inst, WorldModelDefinition root )
+{
+ G3D::Matrix4 rootTransformation = Utils::GetTransformation(root);
+ G3D::Matrix4 translation = G3D::Matrix4::translation(inst.Position.x, inst.Position.y, inst.Position.z);
+ G3D::Matrix4 scale = G3D::Matrix4::scale(inst.Scale);
+ G3D::Matrix4 rotation = Utils::RotationY(Constants::PI);
+ G3D::Quat quat(-inst.QuatY, inst.QuatZ, -inst.QuatX, inst.QuatW);
+ G3D::Matrix4 quatRotation = quat.toRotationMatrix();
+
+ return scale * rotation * quatRotation ** translation * rootTransformation;
+}
+
+void Utils::SaveToDisk( FILE* stream, std::string path )
+{
+ FILE* disk = fopen(path.c_str(), "wb");
+ if (!disk)
+ {
+ printf("SaveToDisk: Could not save file %s to disk, please verify that you have write permissions on that directory\n", path.c_str());
+ return;
+ }
+
+ uint32 size = Utils::Size(stream);
+ uint8* data = new uint8[size];
+ // Read the data to an array
+ if (fread(data, 1, size, stream) != 1)
+ {
+ printf("SaveToDisk: Error reading from Stream while trying to save file %s to disck.\n", path.c_str());
+ return;
+ }
+ // And write it in the file
+ fwrite(data, 1, size, disk);
+
+ // Close the filestream
+ fclose(disk);
+ // Free the used memory
+ delete data;
+}
+
+Vector3 Utils::ToWoWCoords( Vector3 vec )
+{
+ return Vector3(vec.x, -vec.z, vec.y);
+}
+
+std::string Utils::GetExtension( std::string path )
+{
+ std::string::size_type idx = path.rfind('.');
+ std::string extension = "";
+
+ if(idx != std::string::npos)
+ extension = path.substr(idx+1);
+ return extension;
+}
+
+void MapChunkHeader::Read(FILE* stream)
+{
+ int count = 0;
+
+ count += fread(&Flags, sizeof(uint32), 1, stream);
+ count += fread(&IndexX, sizeof(uint32), 1, stream);
+ count += fread(&IndexY, sizeof(uint32), 1, stream);
+ count += fread(&Layers, sizeof(uint32), 1, stream);
+ count += fread(&DoodadRefs, sizeof(uint32), 1, stream);
+ count += fread(&OffsetMCVT, sizeof(uint32), 1, stream);
+ count += fread(&OffsetMCNR, sizeof(uint32), 1, stream);
+ count += fread(&OffsetMCLY, sizeof(uint32), 1, stream);
+ count += fread(&OffsetMCRF, sizeof(uint32), 1, stream);
+ count += fread(&OffsetMCAL, sizeof(uint32), 1, stream);
+ count += fread(&SizeMCAL, sizeof(uint32), 1, stream);
+ count += fread(&OffsetMCSH, sizeof(uint32), 1, stream);
+ count += fread(&SizeMCSH, sizeof(uint32), 1, stream);
+ count += fread(&AreaId, sizeof(uint32), 1, stream);
+ count += fread(&MapObjectRefs, sizeof(uint32), 1, stream);
+ count += fread(&Holes, sizeof(uint32), 1, stream);
+ LowQualityTextureMap = new uint32[4];
+ count += fread(LowQualityTextureMap, sizeof(uint32), 4, stream);
+ count += fread(&PredTex, sizeof(uint32), 1, stream);
+ count += fread(&NumberEffectDoodad, sizeof(uint32), 1, stream);
+ count += fread(&OffsetMCSE, sizeof(uint32), 1, stream);
+ count += fread(&SoundEmitters, sizeof(uint32), 1, stream);
+ count += fread(&OffsetMCLQ, sizeof(uint32), 1, stream);
+ count += fread(&SizeMCLQ, sizeof(uint32), 1, stream);
+ Position = Vector3::Read(stream);
+ count += fread(&OffsetMCCV, sizeof(uint32), 1, stream);
+
+ if (count != 27)
+ printf("MapChunkHeader::Read: Failed to read some data expected 27, read %d\n", count);
+}
+
+void MHDR::Read(FILE* stream)
+{
+ int count = 0;
+
+ count += fread(&Flags, sizeof(uint32), 1, stream);
+ count += fread(&OffsetMCIN, sizeof(uint32), 1, stream);
+ count += fread(&OffsetMTEX, sizeof(uint32), 1, stream);
+ count += fread(&OffsetMMDX, sizeof(uint32), 1, stream);
+ count += fread(&OffsetMMID, sizeof(uint32), 1, stream);
+ count += fread(&OffsetMWMO, sizeof(uint32), 1, stream);
+ count += fread(&OffsetMWID, sizeof(uint32), 1, stream);
+ count += fread(&OffsetMDDF, sizeof(uint32), 1, stream);
+ count += fread(&OffsetMODF, sizeof(uint32), 1, stream);
+ count += fread(&OffsetMFBO, sizeof(uint32), 1, stream);
+ count += fread(&OffsetMH2O, sizeof(uint32), 1, stream);
+ count += fread(&OffsetMTFX, sizeof(uint32), 1, stream);
+
+ if (count != 12)
+ printf("MHDR::Read: Failed to read some data expected 12, read %d\n", count);
+}
+
+void ModelHeader::Read(FILE* stream)
+{
+ int count = 0;
+
+ count += fread(&Magic, sizeof(char), 4, stream);
+ Magic[4] = '\0'; // null-terminate it.
+ count += fread(&Version, sizeof(uint32), 1, stream);
+ count += fread(&LengthModelName, sizeof(uint32), 1, stream);
+ count += fread(&OffsetName, sizeof(uint32), 1, stream);
+ count += fread(&ModelFlags, sizeof(uint32), 1, stream);
+ count += fread(&CountGlobalSequences, sizeof(uint32), 1, stream);
+ count += fread(&OffsetGlobalSequences, sizeof(uint32), 1, stream);
+ count += fread(&CountAnimations, sizeof(uint32), 1, stream);
+ count += fread(&OffsetAnimations, sizeof(uint32), 1, stream);
+ count += fread(&CountAnimationLookup, sizeof(uint32), 1, stream);
+ count += fread(&OffsetAnimationLookup, sizeof(uint32), 1, stream);
+ count += fread(&CountBones, sizeof(uint32), 1, stream);
+ count += fread(&OffsetBones, sizeof(uint32), 1, stream);
+ count += fread(&CountKeyBoneLookup, sizeof(uint32), 1, stream);
+ count += fread(&OffsetKeyBoneLookup, sizeof(uint32), 1, stream);
+ count += fread(&CountVertices, sizeof(uint32), 1, stream);
+ count += fread(&OffsetVertices, sizeof(uint32), 1, stream);
+ count += fread(&CountViews, sizeof(uint32), 1, stream);
+ count += fread(&CountColors, sizeof(uint32), 1, stream);
+ count += fread(&OffsetColors, sizeof(uint32), 1, stream);
+ count += fread(&CountTextures, sizeof(uint32), 1, stream);
+ count += fread(&OffsetTextures, sizeof(uint32), 1, stream);
+ count += fread(&CountTransparency, sizeof(uint32), 1, stream);
+ count += fread(&OffsetTransparency, sizeof(uint32), 1, stream);
+ count += fread(&CountUvAnimation, sizeof(uint32), 1, stream);
+ count += fread(&OffsetUvAnimation, sizeof(uint32), 1, stream);
+ count += fread(&CountTexReplace, sizeof(uint32), 1, stream);
+ count += fread(&OffsetTexReplace, sizeof(uint32), 1, stream);
+ count += fread(&CountRenderFlags, sizeof(uint32), 1, stream);
+ count += fread(&OffsetRenderFlags, sizeof(uint32), 1, stream);
+ count += fread(&CountBoneLookup, sizeof(uint32), 1, stream);
+ count += fread(&OffsetBoneLookup, sizeof(uint32), 1, stream);
+ count += fread(&CountTexLookup, sizeof(uint32), 1, stream);
+ count += fread(&OffsetTexLookup, sizeof(uint32), 1, stream);
+ count += fread(&CountTexUnits, sizeof(uint32), 1, stream);
+ count += fread(&OffsetTexUnits, sizeof(uint32), 1, stream);
+ count += fread(&CountTransLookup, sizeof(uint32), 1, stream);
+ count += fread(&OffsetTransLookup, sizeof(uint32), 1, stream);
+ count += fread(&CountUvAnimLookup, sizeof(uint32), 1, stream);
+ count += fread(&OffsetUvAnimLookup, sizeof(uint32), 1, stream);
+ VertexBox[0] = Vector3::Read(stream);
+ VertexBox[1] = Vector3::Read(stream);
+ count += fread(&VertexRadius, sizeof(float), 1, stream);
+ BoundingBox[0] = Vector3::Read(stream);
+ BoundingBox[1] = Vector3::Read(stream);
+ count += fread(&BoundingRadius, sizeof(float), 1, stream);
+ count += fread(&CountBoundingTriangles, sizeof(uint32), 1, stream);
+ count += fread(&OffsetBoundingTriangles, sizeof(uint32), 1, stream);
+ count += fread(&CountBoundingVertices, sizeof(uint32), 1, stream);
+ count += fread(&OffsetBoundingVertices, sizeof(uint32), 1, stream);
+ count += fread(&CountBoundingNormals, sizeof(uint32), 1, stream);
+ count += fread(&OffsetBoundingNormals, sizeof(uint32), 1, stream);
+
+ if (count != 51)
+ printf("ModelHeader::Read: Failed to read some data expected 51, read %d\n", count);
+
+}
+
+WorldModelHeader WorldModelHeader::Read(FILE* stream)
+{
+ WorldModelHeader ret;
+ int count = 0;
+
+ count += fread(&ret.CountMaterials, sizeof(uint32), 1, stream);
+ count += fread(&ret.CountGroups, sizeof(uint32), 1, stream);
+ count += fread(&ret.CountPortals, sizeof(uint32), 1, stream);
+ count += fread(&ret.CountLights, sizeof(uint32), 1, stream);
+ count += fread(&ret.CountModels, sizeof(uint32), 1, stream);
+ count += fread(&ret.CountDoodads, sizeof(uint32), 1, stream);
+ count += fread(&ret.CountSets, sizeof(uint32), 1, stream);
+ count += fread(&ret.AmbientColorUnk, sizeof(uint32), 1, stream);
+ count += fread(&ret.WmoId, sizeof(uint32), 1, stream);
+ ret.BoundingBox[0] = Vector3::Read(stream);
+ ret.BoundingBox[1] = Vector3::Read(stream);
+ count += fread(&ret.LiquidTypeRelated, sizeof(uint32), 1, stream);
+
+ if (count != 10)
+ printf("WorldModelHeader::Read: Failed to read some data expected 10, read %d\n", count);
+
+ return ret;
+}
+
+DoodadInstance DoodadInstance::Read(FILE* stream)
+{
+ DoodadInstance ret;
+ int count = 0;
+
+ count += fread(&ret.FileOffset, sizeof(uint32), 1, stream);
+ ret.Position = Vector3::Read(stream);
+ count += fread(&ret.QuatW, sizeof(float), 1, stream);
+ count += fread(&ret.QuatX, sizeof(float), 1, stream);
+ count += fread(&ret.QuatY, sizeof(float), 1, stream);
+ count += fread(&ret.QuatZ, sizeof(float), 1, stream);
+ count += fread(&ret.Scale, sizeof(float), 1, stream);
+ count += fread(&ret.LightColor, sizeof(uint32), 1, stream);
+
+ if (count != 7)
+ printf("DoodadInstance::Read: Failed to read some data expected 7, read %d\n", count);
+
+ return ret;
+}
+
+DoodadSet DoodadSet::Read(FILE* stream)
+{
+ DoodadSet ret;
+ char name[21];
+ int count = 0;
+
+ count += fread(&name, sizeof(char), 20, stream);
+ name[20] = '\0';
+ ret.Name = name;
+ count += fread(&ret.FirstInstanceIndex, sizeof(uint32), 1, stream);
+ count += fread(&ret.CountInstances, sizeof(uint32), 1, stream);
+ count += fread(&ret.UnknownZero, sizeof(uint32), 1, stream);
+
+ if (count != 23)
+ printf("DoodadSet::Read: Failed to read some data expected 23, read %d\n", count);
+
+ return ret;
+}
+
+LiquidHeader LiquidHeader::Read(FILE* stream)
+{
+ LiquidHeader ret;
+ int count = 0;
+ count += fread(&ret.CountXVertices, sizeof(uint32), 1, stream);
+ count += fread(&ret.CountYVertices, sizeof(uint32), 1, stream);
+ count += fread(&ret.Width, sizeof(uint32), 1, stream);
+ count += fread(&ret.Height, sizeof(uint32), 1, stream);
+ ret.BaseLocation = Vector3::Read(stream);
+ count += fread(&ret.MaterialId, sizeof(uint16), 1, stream);
+
+ if (count != 5)
+ printf("LiquidHeader::Read: Failed to read some data expected 5, read %d\n", count);
+
+ return ret;
+}
+
+LiquidData LiquidData::Read(FILE* stream, LiquidHeader& header)
+{
+ LiquidData ret;
+ ret.HeightMap = new float*[header.CountXVertices];
+ for (uint32 i = 0; i < header.CountXVertices; ++i)
+ ret.HeightMap[i] = new float[header.CountYVertices];
+
+ ret.RenderFlags = new uint8*[header.Width];
+ for (uint32 i = 0; i < header.Width; ++i)
+ ret.RenderFlags[i] = new uint8[header.Height];
+
+ for (uint32 y = 0; y < header.CountYVertices; y++)
+ {
+ for (uint32 x = 0; x < header.CountXVertices; x++)
+ {
+ uint32 discard;
+ float tmp;
+ if (fread(&discard, sizeof(uint32), 1, stream) == 1 &&
+ fread(&tmp, sizeof(float), 1, stream) == 1)
+ {
+ ret.HeightMap[x][y] = tmp;
+ }
+ }
+ }
+
+ for (uint32 y = 0; y < header.Height; y++)
+ {
+ for (uint32 x = 0; x < header.Width; x++)
+ {
+ uint8 tmp = 0;
+ if (fread(&tmp, sizeof(uint8), 1, stream) == 1)
+ ret.RenderFlags[x][y] = tmp;
+ }
+ }
+
+ return ret;
+}
+
+H2ORenderMask H2ORenderMask::Read(FILE* stream)
+{
+ H2ORenderMask ret;
+ if (int count = fread(&ret.Mask, sizeof(uint8), 8, stream) != 8)
+ printf("H2OHeader::Read: Failed to read some data expected 8, read %d\n", count);
+ return ret;
+}
+
+bool MCNKLiquidData::IsWater(int x, int y, float height)
+{
+ if (!Heights)
+ return false;
+ if (!Mask.ShouldRender(x, y))
+ return false;
+ float diff = Heights[x][y] - height;
+ if (diff > Constants::MaxStandableHeight)
+ return true;
+ return false;
+}
+
+H2OHeader H2OHeader::Read(FILE* stream)
+{
+ H2OHeader ret;
+ int count = 0;
+ count += fread(&ret.OffsetInformation, sizeof(uint32), 1, stream);
+ count += fread(&ret.LayerCount, sizeof(uint32), 1, stream);
+ count += fread(&ret.OffsetRender, sizeof(uint32), 1, stream);
+
+ if (count != 3)
+ printf("H2OHeader::Read: Failed to read some data expected 3, read %d\n", count);
+
+ return ret;
+}
+
+H2OInformation H2OInformation::Read(FILE* stream)
+{
+ H2OInformation ret;
+ int count = 0;
+ count += fread(&ret.LiquidType, sizeof(uint16), 1, stream);
+ count += fread(&ret.Flags, sizeof(uint16), 1, stream);
+ count += fread(&ret.HeightLevel1, sizeof(float), 1, stream);
+ count += fread(&ret.HeightLevel2, sizeof(float), 1, stream);
+ count += fread(&ret.OffsetX, sizeof(uint8), 1, stream);
+ count += fread(&ret.OffsetY, sizeof(uint8), 1, stream);
+ count += fread(&ret.Width, sizeof(uint8), 1, stream);
+ count += fread(&ret.Height, sizeof(uint8), 1, stream);
+ count += fread(&ret.OffsetMask2, sizeof(uint32), 1, stream);
+ count += fread(&ret.OffsetHeightmap, sizeof(uint32), 1, stream);
+
+ if (count != 10)
+ printf("H2OInformation::Read: Failed to read some data expected 10, read %d\n", count);
+
+ return ret;
+}
+
+char* Utils::GetPlainName(const char* FileName)
+{
+ char* temp;
+
+ if((temp = (char*)strrchr(FileName, '\\')) != NULL)
+ FileName = temp + 1;
+ return (char*)FileName;
+}
+
+WMOGroupHeader WMOGroupHeader::Read( FILE* stream )
+{
+ WMOGroupHeader ret;
+ int count = 0;
+ count += fread(&ret.OffsetGroupName, sizeof(uint32), 1, stream);
+ count += fread(&ret.OffsetDescriptiveName, sizeof(uint32), 1, stream);
+ count += fread(&ret.Flags, sizeof(uint32), 1, stream);
+ ret.BoundingBox[0] = Vector3::Read(stream);
+ ret.BoundingBox[1] = Vector3::Read(stream);
+ count += fread(&ret.OffsetPortals, sizeof(uint32), 1, stream);
+ count += fread(&ret.CountPortals, sizeof(uint32), 1, stream);
+ count += fread(&ret.CountBatches, sizeof(uint16), 4, stream);
+ count += fread(&ret.Fogs, sizeof(uint8), 4, stream);
+ count += fread(&ret.LiquidTypeRelated, sizeof(uint32), 1, stream);
+ count += fread(&ret.WmoId, sizeof(uint32), 1, stream);
+
+ if (count != 15)
+ printf("WMOGroupHeader::Read: Failed to read some data expected 15, read %d\n", count);
+
+ return ret;
+}
diff --git a/src/tools/mesh_extractor/Utils.h b/src/tools/mesh_extractor/Utils.h
new file mode 100644
index 00000000000..64fb1bb35ba
--- /dev/null
+++ b/src/tools/mesh_extractor/Utils.h
@@ -0,0 +1,381 @@
+#ifndef UTILS_H
+#define UTILS_H
+#include <cstdio>
+#include <string>
+#include <sstream>
+
+#include "G3D/Matrix4.h"
+#include "DetourNavMesh.h"
+
+#include "Define.h"
+#include "Constants.h"
+
+#include <ace/Stack_Trace.h>
+
+struct WorldModelDefinition;
+class DoodadInstance;
+
+#define ASSERT(assertion) { if (!(assertion)) { ACE_Stack_Trace st; fprintf(stderr, "\n%s:%i in %s ASSERTION FAILED:\n %s\n%s\n", __FILE__, __LINE__, __FUNCTION__, #assertion, st.c_str()); *((volatile int*)NULL) = 0; } }
+
+struct Vector3
+{
+ Vector3() {}
+ Vector3(float X, float Y, float Z) : x(X), y(Y), z(Z) {}
+ float x;
+ float y;
+ float z;
+
+ Vector3 operator +(Vector3 const& other)
+ {
+ return Vector3(x + other.x, y + other.y, z + other.z);
+ }
+
+ static Vector3 Read(FILE* file);
+};
+
+struct TilePos
+{
+ TilePos(int x, int y) : X(x), Y(y) {}
+ int X;
+ int Y;
+};
+
+template<typename T>
+struct Triangle
+{
+ Triangle() {}
+ Triangle(Constants::TriangleType type, T v0, T v1, T v2) : V0(v0), V1(v1), V2(v2), Type(type) {}
+ T V0;
+ T V1;
+ T V2;
+ Constants::TriangleType Type;
+};
+
+class MapChunkHeader
+{
+public:
+ MapChunkHeader() {}
+ uint32 Flags;
+ uint32 IndexX;
+ uint32 IndexY;
+ uint32 Layers;
+ uint32 DoodadRefs;
+ uint32 OffsetMCVT;
+ uint32 OffsetMCNR;
+ uint32 OffsetMCLY;
+ uint32 OffsetMCRF;
+ uint32 OffsetMCAL;
+ uint32 SizeMCAL;
+ uint32 OffsetMCSH;
+ uint32 SizeMCSH;
+ uint32 AreaId;
+ uint32 MapObjectRefs;
+ uint32 Holes;
+ uint32* LowQualityTextureMap;
+ uint32 PredTex;
+ uint32 NumberEffectDoodad;
+ uint32 OffsetMCSE;
+ uint32 SoundEmitters;
+ uint32 OffsetMCLQ;
+ uint32 SizeMCLQ;
+ Vector3 Position;
+ uint32 OffsetMCCV;
+
+ void Read(FILE* stream);
+};
+
+class MHDR
+{
+public:
+ MHDR() {}
+ uint32 Flags;
+ uint32 OffsetMCIN;
+ uint32 OffsetMTEX;
+ uint32 OffsetMMDX;
+ uint32 OffsetMMID;
+ uint32 OffsetMWMO;
+ uint32 OffsetMWID;
+ uint32 OffsetMDDF;
+ uint32 OffsetMODF;
+ uint32 OffsetMFBO;
+ uint32 OffsetMH2O;
+ uint32 OffsetMTFX;
+
+ void Read(FILE* stream);
+};
+
+class ModelHeader
+{
+public:
+ char Magic[5];
+ uint32 Version;
+ uint32 LengthModelName;
+ uint32 OffsetName;
+ uint32 ModelFlags;
+ uint32 CountGlobalSequences;
+ uint32 OffsetGlobalSequences;
+ uint32 CountAnimations;
+ uint32 OffsetAnimations;
+ uint32 CountAnimationLookup;
+ uint32 OffsetAnimationLookup;
+ uint32 CountBones;
+ uint32 OffsetBones;
+ uint32 CountKeyBoneLookup;
+ uint32 OffsetKeyBoneLookup;
+ uint32 CountVertices;
+ uint32 OffsetVertices;
+ uint32 CountViews;
+ uint32 CountColors;
+ uint32 OffsetColors;
+ uint32 CountTextures;
+ uint32 OffsetTextures;
+ uint32 CountTransparency;
+ uint32 OffsetTransparency;
+ uint32 CountUvAnimation;
+ uint32 OffsetUvAnimation;
+ uint32 CountTexReplace;
+ uint32 OffsetTexReplace;
+ uint32 CountRenderFlags;
+ uint32 OffsetRenderFlags;
+ uint32 CountBoneLookup;
+ uint32 OffsetBoneLookup;
+ uint32 CountTexLookup;
+ uint32 OffsetTexLookup;
+ uint32 CountTexUnits;
+ uint32 OffsetTexUnits;
+ uint32 CountTransLookup;
+ uint32 OffsetTransLookup;
+ uint32 CountUvAnimLookup;
+ uint32 OffsetUvAnimLookup;
+ Vector3 VertexBox[2];
+ float VertexRadius;
+ Vector3 BoundingBox[2];
+ float BoundingRadius;
+ uint32 CountBoundingTriangles;
+ uint32 OffsetBoundingTriangles;
+ uint32 CountBoundingVertices;
+ uint32 OffsetBoundingVertices;
+ uint32 CountBoundingNormals;
+ uint32 OffsetBoundingNormals;
+
+ void Read(FILE* stream);
+};
+
+class WorldModelHeader
+{
+public:
+ WorldModelHeader() {}
+ uint32 CountMaterials;
+ uint32 CountGroups;
+ uint32 CountPortals;
+ uint32 CountLights;
+ uint32 CountModels;
+ uint32 CountDoodads;
+ uint32 CountSets;
+ uint32 AmbientColorUnk;
+ uint32 WmoId;
+ Vector3 BoundingBox[2];
+ uint32 LiquidTypeRelated;
+
+ static WorldModelHeader Read(FILE* stream);
+};
+
+class DoodadInstance
+{
+public:
+ DoodadInstance() {}
+ uint32 FileOffset;
+ std::string File;
+ Vector3 Position;
+ float QuatW;
+ float QuatX;
+ float QuatY;
+ float QuatZ;
+ float Scale;
+ uint32 LightColor;
+
+ static DoodadInstance Read(FILE* stream);
+};
+
+class DoodadSet
+{
+public:
+ DoodadSet() {}
+ std::string Name;
+ uint32 FirstInstanceIndex;
+ uint32 CountInstances;
+ uint32 UnknownZero;
+
+ static DoodadSet Read(FILE* stream);
+};
+
+class LiquidHeader
+{
+public:
+ LiquidHeader() {}
+ uint32 CountXVertices;
+ uint32 CountYVertices;
+ uint32 Width;
+ uint32 Height;
+ Vector3 BaseLocation;
+ uint16 MaterialId;
+
+ static LiquidHeader Read(FILE* stream);
+};
+
+class LiquidData
+{
+public:
+ LiquidData() {}
+ float** HeightMap;
+ uint8** RenderFlags;
+
+ bool ShouldRender(int x, int y)
+ {
+ return RenderFlags[x][y] != 0x0F;
+ }
+
+ static LiquidData Read(FILE* stream, LiquidHeader& header);
+};
+
+class H2ORenderMask
+{
+public:
+ H2ORenderMask() {}
+ uint8 Mask[8];
+
+ bool ShouldRender(int x, int y)
+ {
+ return (Mask[y] >> x & 1) != 0;
+ }
+
+ static H2ORenderMask Read(FILE* stream);
+};
+
+class MCNKLiquidData
+{
+public:
+ MCNKLiquidData() {}
+ MCNKLiquidData(float** heights, H2ORenderMask mask) : Heights(heights), Mask(mask) {}
+
+ float** Heights;
+ H2ORenderMask Mask;
+
+ bool IsWater(int x, int y, float height);
+};
+
+class H2OHeader
+{
+public:
+ H2OHeader() {}
+ uint32 OffsetInformation;
+ uint32 LayerCount;
+ uint32 OffsetRender;
+
+ static H2OHeader Read(FILE* stream);
+};
+
+class H2OInformation
+{
+public:
+ H2OInformation() {}
+ uint16 LiquidType;
+ uint16 Flags;
+ float HeightLevel1;
+ float HeightLevel2;
+ uint8 OffsetX;
+ uint8 OffsetY;
+ uint8 Width;
+ uint8 Height;
+ uint32 OffsetMask2;
+ uint32 OffsetHeightmap;
+
+ static H2OInformation Read(FILE* stream);
+};
+
+class WMOGroupHeader
+{
+public:
+ WMOGroupHeader() {}
+
+ uint32 OffsetGroupName;
+ uint32 OffsetDescriptiveName;
+ uint32 Flags;
+ Vector3 BoundingBox[2];
+ uint32 OffsetPortals;
+ uint32 CountPortals;
+ uint16 CountBatches[4];
+ uint8 Fogs[4];
+ uint32 LiquidTypeRelated;
+ uint32 WmoId;
+
+ static WMOGroupHeader Read(FILE* stream);
+};
+
+// Dummy class to act as an interface.
+class IDefinition
+{
+public:
+ Vector3 Position;
+ Vector3 Rotation;
+ virtual float Scale() const { return 1.0f; };
+};
+
+#define MMAP_MAGIC 0x4d4d4150 // 'MMAP'
+#define MMAP_VERSION 3
+
+struct MmapTileHeader
+{
+ uint32 mmapMagic;
+ uint32 dtVersion;
+ uint32 mmapVersion;
+ uint32 size;
+ bool usesLiquids;
+
+ MmapTileHeader() : mmapMagic(MMAP_MAGIC), dtVersion(DT_NAVMESH_VERSION),
+ mmapVersion(MMAP_VERSION), size(0), usesLiquids(true) {}
+};
+
+class Utils
+{
+public:
+ static void Reverse(char word[]);
+ static std::string ReadString(FILE* file);
+ static uint32 Size(FILE* file);
+ static Vector3 ToRecast( Vector3 val );
+ static std::string GetAdtPath(std::string world, int x, int y);
+ static std::string FixModelPath(std::string path);
+ static G3D::Matrix4 GetTransformation(IDefinition def);
+ /// They say its better to declare template functions in the header files.
+ template <typename T>
+ static std::string ToString(T val)
+ {
+ std::stringstream ss;
+ ss << val;
+ return ss.str();
+ }
+ static G3D::Matrix4 RotationX(float angle);
+ static G3D::Matrix4 RotationY(float angle);
+ static G3D::Matrix4 RotationZ(float angle);
+ static float ToRadians(float degrees);
+ static Vector3 VectorTransform(Vector3 vec, G3D::Matrix4 matrix);
+ static std::string GetPathBase(std::string path);
+ static Vector3 GetLiquidVert(G3D::Matrix4 transformation, Vector3 basePosition, float height, int x, int y);
+ static float Distance(float x, float y);
+ template<typename T>
+ static bool IsAllZero(T* arr, uint32 size)
+ {
+ for (uint32 i = 0; i < size; ++i)
+ if (arr[i])
+ return false;
+ return true;
+ }
+ static std::string Replace( std::string str, const std::string& oldStr, const std::string& newStr );
+ static G3D::Matrix4 GetWmoDoodadTransformation( DoodadInstance inst, WorldModelDefinition root );
+ static void CreateDir( const std::string& Path );
+ static void SaveToDisk(FILE* stream, std::string path);
+ static Vector3 ToWoWCoords( Vector3 vec );
+ static std::string GetExtension( std::string path );
+ static char* GetPlainName(const char* FileName);
+};
+#endif
diff --git a/src/tools/mesh_extractor/WDT.cpp b/src/tools/mesh_extractor/WDT.cpp
new file mode 100644
index 00000000000..70d140e79ed
--- /dev/null
+++ b/src/tools/mesh_extractor/WDT.cpp
@@ -0,0 +1,60 @@
+#include "WDT.h"
+#include "Chunk.h"
+#include "ChunkedData.h"
+#include "Utils.h"
+#include "WorldModelHandler.h"
+
+WDT::WDT(std::string file) : IsGlobalModel(false), IsValid(false)
+{
+ Data = new ChunkedData(file, 2);
+ ReadTileTable();
+ ReadGlobalModel();
+}
+
+void WDT::ReadGlobalModel()
+{
+ Chunk* fileChunk = Data->GetChunkByName("MWMO");
+ Chunk* defChunk = Data->GetChunkByName("MODF");
+ if (!fileChunk || !defChunk)
+ return;
+
+ IsGlobalModel = true;
+ ModelDefinition = WorldModelDefinition::Read(defChunk->GetStream());
+ ModelFile = Utils::ReadString(fileChunk->GetStream());
+}
+
+void WDT::ReadTileTable()
+{
+ Chunk* chunk = Data->GetChunkByName("MAIN");
+ if (!chunk)
+ return;
+ IsValid = true;
+ FILE* stream = chunk->GetStream();
+ for (int y = 0; y < 64; ++y)
+ {
+ for (int x = 0; x < 64; ++x)
+ {
+ const uint32 hasTileFlag = 0x1;
+ uint32 flags;
+ uint32 discard;
+ int count = 0;
+ count += fread(&flags, sizeof(uint32), 1, stream);
+ count += fread(&discard, sizeof(uint32), 1, stream);
+
+ if (count != 2)
+ printf("WDT::ReadTileTable: Failed to read some data expected 2, read %d\n", count);
+
+ if (flags & hasTileFlag)
+ TileTable.push_back(TilePos(x, y));
+
+ }
+ }
+}
+
+bool WDT::HasTile( int x, int y )
+{
+ for (std::vector<TilePos>::iterator itr = TileTable.begin(); itr != TileTable.end(); ++itr)
+ if (itr->X == x && itr->Y == y)
+ return true;
+ return false;
+}
diff --git a/src/tools/mesh_extractor/WDT.h b/src/tools/mesh_extractor/WDT.h
new file mode 100644
index 00000000000..a12aa65218b
--- /dev/null
+++ b/src/tools/mesh_extractor/WDT.h
@@ -0,0 +1,27 @@
+#ifndef WDT_H
+#define WDT_H
+#include <string>
+#include <vector>
+
+#include "ChunkedData.h"
+#include "WorldModelHandler.h"
+#include "Utils.h"
+
+class WDT
+{
+public:
+ WDT(std::string file);
+
+ ChunkedData* Data;
+ std::vector<TilePos> TileTable;
+ bool IsGlobalModel;
+ bool IsValid;
+ std::string ModelFile;
+ WorldModelDefinition ModelDefinition;
+ bool HasTile(int x, int y);
+private:
+ void ReadGlobalModel();
+ void ReadTileTable();
+};
+
+#endif \ No newline at end of file
diff --git a/src/tools/mesh_extractor/WorldModelGroup.cpp b/src/tools/mesh_extractor/WorldModelGroup.cpp
new file mode 100644
index 00000000000..21e1c1e63e1
--- /dev/null
+++ b/src/tools/mesh_extractor/WorldModelGroup.cpp
@@ -0,0 +1,143 @@
+#include "WorldModelGroup.h"
+#include "ChunkedData.h"
+#include "Chunk.h"
+#include "Utils.h"
+
+WorldModelGroup::WorldModelGroup( std::string path, int groupIndex ) : GroupIndex(groupIndex), MOBA(NULL), IsBad(false)
+{
+ Data = new ChunkedData(path);
+ if (!Data->Stream)
+ {
+ IsBad = true;
+ return;
+ }
+ Chunk* mainChunk = Data->GetChunkByName("MOGP");
+ int32 firstSub = mainChunk->FindSubChunkOffset("MOPY");
+ if (firstSub == -1)
+ return;
+
+ Name = Utils::GetPlainName(path.c_str());
+
+ FILE* stream = mainChunk->GetStream();
+ fseek(stream, firstSub, SEEK_SET);
+ SubData = new ChunkedData(stream, mainChunk->Length - firstSub);
+
+ ReadHeader();
+ ReadMaterials();
+ ReadTriangles();
+ ReadVertices();
+ ReadNormals();
+ ReadLiquid();
+ ReadBatches();
+}
+
+void WorldModelGroup::ReadNormals()
+{
+ Chunk* chunk = SubData->GetChunkByName("MONR");
+ if (!chunk)
+ return;
+
+ uint32 normalCount = chunk->Length / 12;
+ ASSERT(normalCount == Vertices.size() && "normalCount is different than the Vertices count");
+ Normals.reserve(normalCount);
+ FILE* stream = chunk->GetStream();
+ for (uint32 i = 0; i < normalCount; i++)
+ Normals.push_back(Vector3::Read(stream));
+}
+
+void WorldModelGroup::ReadLiquid()
+{
+ Chunk* chunk = SubData->GetChunkByName("MLIQ");
+ if (!chunk)
+ return;
+
+ HasLiquidData = true;
+ FILE* stream = chunk->GetStream();
+ LiquidDataHeader = LiquidHeader::Read(stream);
+ LiquidDataGeometry = LiquidData::Read(stream, LiquidDataHeader);
+}
+
+void WorldModelGroup::ReadVertices()
+{
+ Chunk* chunk = SubData->GetChunkByName("MOVT");
+ if (!chunk)
+ return;
+
+ uint32 verticeCount = chunk->Length / 12;
+ Vertices.reserve(verticeCount);
+ FILE* stream = chunk->GetStream();
+ for (uint32 i = 0; i < verticeCount; i++)
+ Vertices.push_back(Vector3::Read(stream));
+}
+
+void WorldModelGroup::ReadTriangles()
+{
+ Chunk* chunk = SubData->GetChunkByName("MOVI");
+ if (!chunk)
+ return;
+
+ uint32 triangleCount = chunk->Length / 6;
+ ASSERT(triangleCount == TriangleFlags.size() && "triangleCount != TriangleFlags.size()");
+ FILE* stream = chunk->GetStream();
+ Triangles.reserve(triangleCount);
+ for (uint32 i = 0; i < triangleCount; i++)
+ {
+ uint16 v0;
+ uint16 v1;
+ uint16 v2;
+ int count = 0;
+ count += fread(&v0, sizeof(uint16), 1, stream);
+ count += fread(&v1, sizeof(uint16), 1, stream);
+ count += fread(&v2, sizeof(uint16), 1, stream);
+ if (count != 3)
+ printf("WorldModelGroup::ReadMaterials: Error reading data, expected 3, read %d\n", count);
+
+ Triangles.push_back(Triangle<uint16>(Constants::TRIANGLE_TYPE_WMO, v0, v1, v2));
+ }
+}
+
+void WorldModelGroup::ReadMaterials()
+{
+ Chunk* chunk = SubData->GetChunkByName("MOPY");
+ if (!chunk)
+ return;
+
+ FILE* stream = chunk->GetStream();
+ uint32 triangleCount = chunk->Length / 2;
+ TriangleFlags.reserve(triangleCount);
+ TriangleMaterials.reserve(triangleCount);
+ for (uint32 i = 0; i < triangleCount; i++)
+ {
+ uint8 tmp;
+ if (fread(&tmp, sizeof(uint8), 1, stream) != 1)
+ printf("WorldModelGroup::ReadMaterials: Error reading data, expected 1, read 0\n");
+ TriangleFlags.push_back(tmp);
+ // Read again for material.
+ if (fread(&tmp, sizeof(uint8), 1, stream) != 1)
+ printf("WorldModelGroup::ReadMaterials: Error reading data, expected 1, read 0\n");
+ TriangleMaterials.push_back(tmp);
+ }
+}
+
+void WorldModelGroup::ReadHeader()
+{
+ Chunk* chunk = Data->GetChunkByName("MOGP");
+ if (!chunk)
+ return;
+
+ FILE* stream = chunk->GetStream();
+ Header = WMOGroupHeader::Read(stream);
+}
+
+void WorldModelGroup::ReadBatches()
+{
+ Chunk* chunk = Data->GetChunkByName("MOBA");
+ if (!chunk)
+ return;
+
+ MOBALength = chunk->Length / 2;
+ MOBA = new uint16[MOBALength];
+ uint32 count = (uint32)fread(MOBA, sizeof(uint16), MOBALength, chunk->GetStream());
+ if (count != MOBALength)
+ printf("WorldModelGroup::ReadBatches: Error reading data, expected %u, read %u\n", MOBALength, count);
+}
diff --git a/src/tools/mesh_extractor/WorldModelGroup.h b/src/tools/mesh_extractor/WorldModelGroup.h
new file mode 100644
index 00000000000..e4fe34cbc75
--- /dev/null
+++ b/src/tools/mesh_extractor/WorldModelGroup.h
@@ -0,0 +1,38 @@
+#ifndef WMOGROUP_H
+#define WMOGROUP_H
+#include "ChunkedData.h"
+#include "Utils.h"
+
+class WorldModelGroup
+{
+public:
+ WorldModelGroup(std::string path, int groupIndex);
+ ChunkedData* Data;
+ ChunkedData* SubData;
+ int GroupIndex;
+ std::string Name;
+ WMOGroupHeader Header;
+
+ std::vector<uint8> TriangleFlags;
+ std::vector<uint8> TriangleMaterials;
+ std::vector<Triangle<uint16> > Triangles;
+ std::vector<Vector3> Vertices;
+ std::vector<Vector3> Normals;
+ // @ToDo: Research.
+ uint16* MOBA;
+ uint32 MOBALength;
+
+ bool HasLiquidData;
+ bool IsBad;
+ LiquidHeader LiquidDataHeader;
+ LiquidData LiquidDataGeometry;
+private:
+ void ReadNormals();
+ void ReadLiquid();
+ void ReadVertices();
+ void ReadTriangles();
+ void ReadMaterials();
+ void ReadHeader();
+ void ReadBatches();
+};
+#endif \ No newline at end of file
diff --git a/src/tools/mesh_extractor/WorldModelHandler.cpp b/src/tools/mesh_extractor/WorldModelHandler.cpp
new file mode 100644
index 00000000000..ecfff4e97d4
--- /dev/null
+++ b/src/tools/mesh_extractor/WorldModelHandler.cpp
@@ -0,0 +1,204 @@
+#include "WorldModelHandler.h"
+#include "WorldModelRoot.h"
+#include "Chunk.h"
+#include "Cache.h"
+#include "Model.h"
+#include "Define.h"
+#include "G3D/Matrix4.h"
+#include <cstdio>
+
+WorldModelDefinition WorldModelDefinition::Read( FILE* file )
+{
+ WorldModelDefinition ret;
+ int count = 0;
+ count += fread(&ret.MwidIndex, sizeof(uint32), 1, file);
+ count += fread(&ret.UniqueId, sizeof(uint32), 1, file);
+ ret.Position = Vector3::Read(file);
+ ret.Rotation = Vector3::Read(file);
+ ret.UpperExtents = Vector3::Read(file);
+ ret.LowerExtents = Vector3::Read(file);
+ count += fread(&ret.Flags, sizeof(uint16), 1, file);
+ count += fread(&ret.DoodadSet, sizeof(uint16), 1, file);
+ uint32 discard;
+ count += fread(&discard, sizeof(uint32), 1, file);
+
+ if (count != 5)
+ printf("WorldModelDefinition::Read: Error reading data, expected 5, read %d\n", count);
+ return ret;
+}
+
+
+WorldModelHandler::WorldModelHandler( ADT* adt ) : ObjectDataHandler(adt), _definitions(NULL), _paths(NULL)
+{
+ if (!adt->HasObjectData)
+ return;
+ ReadModelPaths();
+ ReadDefinitions();
+}
+
+void WorldModelHandler::ProcessInternal( ChunkedData* subChunks )
+{
+ if (!IsSane())
+ return;
+ Chunk* wmoReferencesChunk = subChunks->GetChunkByName("MCRW");
+ if (!wmoReferencesChunk)
+ return;
+ FILE* stream = wmoReferencesChunk->GetStream();
+ uint32 refCount = wmoReferencesChunk->Length / 4;
+ for (uint32 i = 0; i < refCount; i++)
+ {
+ int32 index;
+ if (fread(&index, sizeof(int32), 1, stream) != 1)
+ printf("WorldModelDefinition::Read: Error reading data, expected 1, read 0\n");
+
+ if (index < 0 || uint32(index) >= _definitions->size())
+ continue;
+
+ WorldModelDefinition wmo = (*_definitions)[index];
+
+ if (_drawn.find(wmo.UniqueId) != _drawn.end())
+ continue;
+ _drawn.insert(wmo.UniqueId);
+
+ if (wmo.MwidIndex >= _paths->size())
+ continue;
+
+ std::string path = (*_paths)[wmo.MwidIndex];
+ WorldModelRoot* model = Cache->WorldModelCache.Get(path);
+ if (!model)
+ {
+ model = new WorldModelRoot(path);
+ Cache->WorldModelCache.Insert(path, model);
+ }
+
+ Vertices.reserve(1000);
+ Triangles.reserve(1000);
+
+ InsertModelGeometry(Vertices, Triangles, wmo, model);
+ }
+}
+
+void WorldModelHandler::InsertModelGeometry( std::vector<Vector3>& verts, std::vector<Triangle<uint32> >& tris, WorldModelDefinition& def, WorldModelRoot* root )
+{
+ G3D::Matrix4 transformation = Utils::GetTransformation(def);
+ for (std::vector<WorldModelGroup>::iterator group = root->Groups.begin(); group != root->Groups.end(); ++group)
+ {
+ uint32 vertOffset = verts.size();
+ for (std::vector<Vector3>::iterator itr2 = group->Vertices.begin(); itr2 != group->Vertices.end(); ++itr2)
+ verts.push_back(Utils::VectorTransform(*itr2, transformation));
+
+ for (uint32 i = 0; i < group->Triangles.size(); ++i)
+ {
+ // only include collidable tris
+ if ((group->TriangleFlags[i] & 0x04) != 0 && group->TriangleMaterials[i] != 0xFF)
+ continue;
+ Triangle<uint16> tri = group->Triangles[i];
+ tris.push_back(Triangle<uint32>(Constants::TRIANGLE_TYPE_WMO, tri.V0 + vertOffset, tri.V1 + vertOffset, tri.V2 + vertOffset));
+ }
+ }
+
+ if (def.DoodadSet < root->DoodadSets.size())
+ {
+ DoodadSet set = root->DoodadSets[def.DoodadSet];
+ std::vector<DoodadInstance> instances;
+ instances.reserve(set.CountInstances);
+ for (uint32 i = set.FirstInstanceIndex; i < (set.CountInstances + set.FirstInstanceIndex); i++)
+ {
+ if (i >= root->DoodadInstances.size())
+ break;
+ instances.push_back(root->DoodadInstances[i]);
+ }
+
+ for (std::vector<DoodadInstance>::iterator instance = instances.begin(); instance != instances.end(); ++instance)
+ {
+ Model* model = Cache->ModelCache.Get(instance->File);
+ if (!model)
+ {
+ model = new Model(instance->File);
+ Cache->ModelCache.Insert(instance->File, model);
+ }
+
+ if (!model->IsCollidable)
+ continue;
+ G3D::Matrix4 doodadTransformation = Utils::GetWmoDoodadTransformation(*instance, def);
+ int vertOffset = verts.size();
+ for (std::vector<Vector3>::iterator itr2 = model->Vertices.begin(); itr2 != model->Vertices.end(); ++itr2)
+ verts.push_back(Utils::VectorTransform(*itr2, doodadTransformation));
+ for (std::vector<Triangle<uint16> >::iterator itr2 = model->Triangles.begin(); itr2 != model->Triangles.end(); ++itr2)
+ tris.push_back(Triangle<uint32>(Constants::TRIANGLE_TYPE_WMO, itr2->V0 + vertOffset, itr2->V1 + vertOffset, itr2->V2 + vertOffset));
+ }
+
+ for (std::vector<WorldModelGroup>::iterator group = root->Groups.begin(); group != root->Groups.end(); ++group)
+ {
+ if (!group->HasLiquidData)
+ continue;
+
+ for (uint32 y = 0; y < group->LiquidDataHeader.Height; y++)
+ {
+ for (uint32 x = 0; x < group->LiquidDataHeader.Width; x++)
+ {
+ if (!group->LiquidDataGeometry.ShouldRender(x, y))
+ continue;
+
+ uint32 vertOffset = verts.size();
+ verts.push_back(Utils::GetLiquidVert(transformation, group->LiquidDataHeader.BaseLocation,
+ group->LiquidDataGeometry.HeightMap[x][y], x, y));
+ verts.push_back(Utils::GetLiquidVert(transformation, group->LiquidDataHeader.BaseLocation,
+ group->LiquidDataGeometry.HeightMap[x + 1][y], x + 1, y));
+ verts.push_back(Utils::GetLiquidVert(transformation, group->LiquidDataHeader.BaseLocation,
+ group->LiquidDataGeometry.HeightMap[x][y + 1], x, y + 1));
+ verts.push_back(Utils::GetLiquidVert(transformation, group->LiquidDataHeader.BaseLocation,
+ group->LiquidDataGeometry.HeightMap[x + 1][y + 1], x + 1, y + 1));
+
+ tris.push_back(Triangle<uint32>(Constants::TRIANGLE_TYPE_WATER, vertOffset, vertOffset + 2, vertOffset + 1));
+ tris.push_back(Triangle<uint32>(Constants::TRIANGLE_TYPE_WATER, vertOffset + 2, vertOffset + 3, vertOffset + 1));
+
+ }
+ }
+ }
+ }
+}
+
+void WorldModelHandler::ReadDefinitions()
+{
+ Chunk* chunk = Source->ObjectData->GetChunkByName("MODF");
+ if (!chunk)
+ return;
+
+ const int32 definitionSize = 64;
+ uint32 definitionCount = chunk->Length / definitionSize;
+ _definitions = new std::vector<WorldModelDefinition>;
+ _definitions->reserve(definitionCount);
+ FILE* stream = chunk->GetStream();
+ for (uint32 i = 0; i < definitionCount; i++)
+ _definitions->push_back(WorldModelDefinition::Read(stream));
+}
+
+void WorldModelHandler::ReadModelPaths()
+{
+ Chunk* mwid = Source->ObjectData->GetChunkByName("MWID");
+ Chunk* mwmo = Source->ObjectData->GetChunkByName("MWMO");
+ if (!mwid || !mwmo)
+ return;
+
+ uint32 paths = mwid->Length / 4;
+ _paths = new std::vector<std::string>;
+ _paths->reserve(paths);
+ for (uint32 i = 0; i < paths; i++)
+ {
+ FILE* stream = mwid->GetStream();
+ fseek(stream, i * 4, SEEK_CUR);
+ uint32 offset;
+ if (fread(&offset, sizeof(uint32), 1, stream) != 1)
+ printf("WorldModelDefinition::Read: Error reading data, expected 1, read 0\n");
+ FILE* dataStream = mwmo->GetStream();
+ fseek(dataStream, offset + mwmo->Offset, SEEK_SET);
+ _paths->push_back(Utils::ReadString(dataStream));
+ }
+}
+
+WorldModelHandler::~WorldModelHandler()
+{
+ delete _definitions;
+ delete _paths;
+}
diff --git a/src/tools/mesh_extractor/WorldModelHandler.h b/src/tools/mesh_extractor/WorldModelHandler.h
new file mode 100644
index 00000000000..29715ded696
--- /dev/null
+++ b/src/tools/mesh_extractor/WorldModelHandler.h
@@ -0,0 +1,47 @@
+#ifndef WMODEL_HNDL_H
+#define WMODEL_HNDL_H
+#include "Define.h"
+#include "Utils.h"
+#include "WorldModelRoot.h"
+#include "ObjectDataHandler.h"
+
+#include <set>
+#include <vector>
+
+class ADT;
+
+struct WorldModelDefinition : public IDefinition
+{
+public:
+ WorldModelDefinition() {}
+
+ uint32 MwidIndex;
+ uint32 UniqueId;
+ Vector3 UpperExtents;
+ Vector3 LowerExtents;
+ uint16 Flags;
+ uint16 DoodadSet;
+
+ static WorldModelDefinition Read(FILE* file);
+};
+
+class WorldModelHandler : public ObjectDataHandler
+{
+public:
+ WorldModelHandler(ADT* adt);
+ ~WorldModelHandler();
+
+ std::vector<Vector3> Vertices;
+ std::vector<Triangle<uint32> > Triangles;
+ bool IsSane() { return _definitions && _paths; }
+ void InsertModelGeometry(std::vector<Vector3>& verts, std::vector<Triangle<uint32> >& tris, WorldModelDefinition& def, WorldModelRoot* root);
+protected:
+ void ProcessInternal(ChunkedData* data);
+private:
+ void ReadDefinitions();
+ void ReadModelPaths();
+ std::set<uint32> _drawn;
+ std::vector<WorldModelDefinition>* _definitions;
+ std::vector<std::string>* _paths;
+};
+#endif \ No newline at end of file
diff --git a/src/tools/mesh_extractor/WorldModelRoot.cpp b/src/tools/mesh_extractor/WorldModelRoot.cpp
new file mode 100644
index 00000000000..c34a77e4531
--- /dev/null
+++ b/src/tools/mesh_extractor/WorldModelRoot.cpp
@@ -0,0 +1,74 @@
+#include "WorldModelRoot.h"
+#include "ChunkedData.h"
+#include "Utils.h"
+
+WorldModelRoot::WorldModelRoot( std::string path )
+{
+ Data = new ChunkedData(path);
+ Path = path;
+ ReadHeader();
+ ReadGroups();
+ ReadDoodadInstances();
+ ReadDoodadSets();
+}
+
+void WorldModelRoot::ReadGroups()
+{
+ std::string pathBase = Utils::GetPathBase(Path);
+ Groups.reserve(Header.CountGroups);
+ for (uint32 i = 0; i < Header.CountGroups; i++)
+ {
+ char name[200];
+ sprintf(name, "%s_%03u.wmo", pathBase.c_str(), i);
+ WorldModelGroup group(name, i);
+ if (!group.IsBad)
+ Groups.push_back(group);
+ }
+}
+
+void WorldModelRoot::ReadDoodadSets()
+{
+ Chunk* chunk = Data->GetChunkByName("MODS");
+ if (!chunk)
+ return;
+
+ FILE* stream = chunk->GetStream();
+ ASSERT(chunk->Length / 32 == Header.CountSets && "chunk.Length / 32 == Header.CountSets");
+ DoodadSets.reserve(Header.CountSets);
+ for (uint32 i = 0; i < Header.CountSets; i++)
+ DoodadSets.push_back(DoodadSet::Read(stream));
+}
+
+void WorldModelRoot::ReadDoodadInstances()
+{
+ Chunk* chunk = Data->GetChunkByName("MODD");
+ Chunk* nameChunk = Data->GetChunkByName("MODN");
+ if (!chunk || !nameChunk)
+ return;
+
+ const uint32 instanceSize = 40;
+ uint32 countInstances = chunk->Length / instanceSize;
+ DoodadInstances.reserve(countInstances);
+ for (uint32 i = 0; i < countInstances; i++)
+ {
+ FILE* stream = chunk->GetStream();
+ fseek(stream, instanceSize * i, SEEK_CUR);
+ DoodadInstance instance = DoodadInstance::Read(stream);
+ FILE* nameStream = nameChunk->GetStream();
+ if (instance.FileOffset >= nameChunk->Length)
+ continue;
+ fseek(nameStream, instance.FileOffset, SEEK_CUR);
+ instance.File = Utils::ReadString(nameStream);
+ DoodadInstances.push_back(instance);
+ }
+}
+
+void WorldModelRoot::ReadHeader()
+{
+ Chunk* chunk = Data->GetChunkByName("MOHD");
+ if (!chunk)
+ return;
+
+ FILE* stream = chunk->GetStream();
+ Header = WorldModelHeader::Read(stream);
+}
diff --git a/src/tools/mesh_extractor/WorldModelRoot.h b/src/tools/mesh_extractor/WorldModelRoot.h
new file mode 100644
index 00000000000..c06ff3d5d2b
--- /dev/null
+++ b/src/tools/mesh_extractor/WorldModelRoot.h
@@ -0,0 +1,26 @@
+#ifndef WMOROOT_H
+#define WMOROOT_H
+#include <string>
+#include <vector>
+
+#include "ChunkedData.h"
+#include "Utils.h"
+#include "WorldModelGroup.h"
+
+class WorldModelRoot
+{
+public:
+ WorldModelRoot(std::string path);
+ std::string Path;
+ ChunkedData* Data;
+ WorldModelHeader Header;
+ std::vector<DoodadInstance> DoodadInstances;
+ std::vector<DoodadSet> DoodadSets;
+ std::vector<WorldModelGroup> Groups;
+private:
+ void ReadGroups();
+ void ReadDoodadSets();
+ void ReadDoodadInstances();
+ void ReadHeader();
+};
+#endif \ No newline at end of file
diff --git a/src/tools/mesh_extractor/readme b/src/tools/mesh_extractor/readme
new file mode 100644
index 00000000000..85cd7cfc975
--- /dev/null
+++ b/src/tools/mesh_extractor/readme
@@ -0,0 +1,6 @@
+Experimental mesh extractor.
+Original work in C# by stschake
+Thanks to:
+Subv
+~
+For helping in the porting to C++ \ No newline at end of file
diff --git a/src/tools/mmaps_generator/CMakeLists.txt b/src/tools/mmaps_generator/CMakeLists.txt
new file mode 100644
index 00000000000..b168691c994
--- /dev/null
+++ b/src/tools/mmaps_generator/CMakeLists.txt
@@ -0,0 +1,55 @@
+# Copyright (C) 2008-2013 TrinityCore <http://www.trinitycore.org/>
+#
+# This file is free software; as a special exception the author gives
+# unlimited permission to copy and/or distribute it, with or without
+# modifications, as long as this notice is preserved.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY, to the extent permitted by law; without even the
+# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+file(GLOB_RECURSE mmap_gen_sources *.cpp *.h)
+
+set(mmap_gen_Includes
+ ${CMAKE_BINARY_DIR}
+ ${ACE_INCLUDE_DIR}
+ ${CMAKE_SOURCE_DIR}/dep/libmpq
+ ${CMAKE_SOURCE_DIR}/dep/zlib
+ ${CMAKE_SOURCE_DIR}/dep/bzip2
+ ${CMAKE_SOURCE_DIR}/dep/g3dlite/include
+ ${CMAKE_SOURCE_DIR}/dep/recastnavigation/Recast
+ ${CMAKE_SOURCE_DIR}/dep/recastnavigation/Detour
+ ${CMAKE_SOURCE_DIR}/src/server/shared
+ ${CMAKE_SOURCE_DIR}/src/server/game/Conditions
+ ${CMAKE_SOURCE_DIR}/src/server/collision
+ ${CMAKE_SOURCE_DIR}/src/server/collision/Management
+ ${CMAKE_SOURCE_DIR}/src/server/collision/Maps
+ ${CMAKE_SOURCE_DIR}/src/server/collision/Models
+)
+
+if( WIN32 )
+ set(mmap_gen_Includes
+ ${mmap_gen_Includes}
+ ${CMAKE_SOURCE_DIR}/dep/libmpq/win
+ )
+endif()
+
+include_directories(${mmap_gen_Includes})
+
+add_executable(mmaps_generator ${mmap_gen_sources})
+
+target_link_libraries(mmaps_generator
+ collision
+ g3dlib
+ Recast
+ Detour
+ ${ACE_LIBRARY}
+ ${BZIP2_LIBRARIES}
+ ${ZLIB_LIBRARIES}
+)
+
+if( UNIX )
+ install(TARGETS mmaps_generator DESTINATION bin)
+elseif( WIN32 )
+ install(TARGETS mmaps_generator DESTINATION "${CMAKE_INSTALL_PREFIX}")
+endif()
diff --git a/src/tools/mmaps_generator/Info/readme.txt b/src/tools/mmaps_generator/Info/readme.txt
new file mode 100644
index 00000000000..8d7c4f9d2e0
--- /dev/null
+++ b/src/tools/mmaps_generator/Info/readme.txt
@@ -0,0 +1,66 @@
+Generator command line args
+
+--offMeshInput [file.*] Path to file containing off mesh connections data.
+ Format must be: (see offmesh_example.txt)
+ "map_id tile_x,tile_y (start_x start_y start_z) (end_x end_y end_z) size //optional comments"
+ Single mesh connection per line.
+
+--silent Make us script friendly. Do not wait for user input
+ on error or completion.
+
+--bigBaseUnit [true|false] Generate tile/map using bigger basic unit.
+ Use this option only if you have unexpected gaps.
+
+ false: use normal metrics (default)
+
+--maxAngle [#] Max walkable inclination angle
+
+ float between 45 and 90 degrees (default 60)
+
+--skipLiquid liquid data for maps
+
+ false: include liquid data (default)
+
+--skipContinents [true|false] continents are maps 0 (Eastern Kingdoms),
+ 1 (Kalimdor), 530 (Outlands), 571 (Northrend)
+
+ false: build continents (default)
+
+--skipJunkMaps [true|false] junk maps include some unused
+ maps, transport maps, and some other
+
+ true: skip junk maps (default)
+
+--skipBattlegrounds [true|false] does not include PVP arenas
+
+ false: skip battlegrounds (default)
+
+--debugOutput [true|false] create debugging files for use with RecastDemo
+ if you are only creating mmaps for use with MaNGOS,
+ you don't want debugging files
+
+ false: don't create debugging files (default)
+
+--tile [#,#] Build the specified tile
+ seperate number with a comma ','
+ must specify a map number (see below)
+ if this option is not used, all tiles are built
+
+ [#] Build only the map specified by #
+ this command will build the map regardless of --skip* option settings
+ if you do not specify a map number, builds all maps that pass the filters specified by --skip* options
+
+
+examples:
+
+movement_extractor
+builds maps using the default settings (see above for defaults)
+
+movement_extractor --skipContinents true
+builds the default maps, except continents
+
+movement_extractor 0
+builds all tiles of map 0
+
+movement_extractor 0 --tile 34,46
+builds only tile 34,46 of map 0 (this is the southern face of blackrock mountain) \ No newline at end of file
diff --git a/src/tools/mmaps_generator/IntermediateValues.cpp b/src/tools/mmaps_generator/IntermediateValues.cpp
new file mode 100644
index 00000000000..a490273ad80
--- /dev/null
+++ b/src/tools/mmaps_generator/IntermediateValues.cpp
@@ -0,0 +1,277 @@
+/*
+ * Copyright (C) 2008-2013 TrinityCore <http://www.trinitycore.org/>
+ * Copyright (C) 2005-2011 MaNGOS <http://getmangos.com/>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include "IntermediateValues.h"
+
+namespace MMAP
+{
+ IntermediateValues::~IntermediateValues()
+ {
+ rcFreeCompactHeightfield(compactHeightfield);
+ rcFreeHeightField(heightfield);
+ rcFreeContourSet(contours);
+ rcFreePolyMesh(polyMesh);
+ rcFreePolyMeshDetail(polyMeshDetail);
+ }
+
+ void IntermediateValues::writeIV(uint32 mapID, uint32 tileX, uint32 tileY)
+ {
+ char fileName[255];
+ char tileString[25];
+ sprintf(tileString, "[%02u,%02u]: ", tileX, tileY);
+
+ printf("%sWriting debug output... \r", tileString);
+
+ std::string name("meshes/%03u%02i%02i.");
+
+#define DEBUG_WRITE(fileExtension,data) \
+ do { \
+ sprintf(fileName, (name + fileExtension).c_str(), mapID, tileY, tileX); \
+ FILE* file = fopen(fileName, "wb"); \
+ if (!file) \
+ { \
+ char message[1024]; \
+ sprintf(message, "%sFailed to open %s for writing!\n", tileString, fileName); \
+ perror(message); \
+ } \
+ else \
+ debugWrite(file, data); \
+ if (file) fclose(file); \
+ printf("%sWriting debug output... \r", tileString); \
+ } while (false)
+
+ if (heightfield)
+ DEBUG_WRITE("hf", heightfield);
+ if (compactHeightfield)
+ DEBUG_WRITE("chf", compactHeightfield);
+ if (contours)
+ DEBUG_WRITE("cs", contours);
+ if (polyMesh)
+ DEBUG_WRITE("pmesh", polyMesh);
+ if (polyMeshDetail)
+ DEBUG_WRITE("dmesh", polyMeshDetail);
+
+#undef DEBUG_WRITE
+ }
+
+ void IntermediateValues::debugWrite(FILE* file, const rcHeightfield* mesh)
+ {
+ if (!file || !mesh)
+ return;
+
+ fwrite(&(mesh->cs), sizeof(float), 1, file);
+ fwrite(&(mesh->ch), sizeof(float), 1, file);
+ fwrite(&(mesh->width), sizeof(int), 1, file);
+ fwrite(&(mesh->height), sizeof(int), 1, file);
+ fwrite(mesh->bmin, sizeof(float), 3, file);
+ fwrite(mesh->bmax, sizeof(float), 3, file);
+
+ for (int y = 0; y < mesh->height; ++y)
+ for (int x = 0; x < mesh->width; ++x)
+ {
+ rcSpan* span = mesh->spans[x+y*mesh->width];
+
+ // first, count the number of spans
+ int spanCount = 0;
+ while (span)
+ {
+ spanCount++;
+ span = span->next;
+ }
+
+ // write the span count
+ fwrite(&spanCount, sizeof(int), 1, file);
+
+ // write the spans
+ span = mesh->spans[x+y*mesh->width];
+ while (span)
+ {
+ fwrite(span, sizeof(rcSpan), 1, file);
+ span = span->next;
+ }
+ }
+ }
+
+ void IntermediateValues::debugWrite(FILE* file, const rcCompactHeightfield* chf)
+ {
+ if (!file | !chf)
+ return;
+
+ fwrite(&(chf->width), sizeof(chf->width), 1, file);
+ fwrite(&(chf->height), sizeof(chf->height), 1, file);
+ fwrite(&(chf->spanCount), sizeof(chf->spanCount), 1, file);
+
+ fwrite(&(chf->walkableHeight), sizeof(chf->walkableHeight), 1, file);
+ fwrite(&(chf->walkableClimb), sizeof(chf->walkableClimb), 1, file);
+
+ fwrite(&(chf->maxDistance), sizeof(chf->maxDistance), 1, file);
+ fwrite(&(chf->maxRegions), sizeof(chf->maxRegions), 1, file);
+
+ fwrite(chf->bmin, sizeof(chf->bmin), 1, file);
+ fwrite(chf->bmax, sizeof(chf->bmax), 1, file);
+
+ fwrite(&(chf->cs), sizeof(chf->cs), 1, file);
+ fwrite(&(chf->ch), sizeof(chf->ch), 1, file);
+
+ int tmp = 0;
+ if (chf->cells) tmp |= 1;
+ if (chf->spans) tmp |= 2;
+ if (chf->dist) tmp |= 4;
+ if (chf->areas) tmp |= 8;
+
+ fwrite(&tmp, sizeof(tmp), 1, file);
+
+ if (chf->cells)
+ fwrite(chf->cells, sizeof(rcCompactCell), chf->width*chf->height, file);
+ if (chf->spans)
+ fwrite(chf->spans, sizeof(rcCompactSpan), chf->spanCount, file);
+ if (chf->dist)
+ fwrite(chf->dist, sizeof(unsigned short), chf->spanCount, file);
+ if (chf->areas)
+ fwrite(chf->areas, sizeof(unsigned char), chf->spanCount, file);
+ }
+
+ void IntermediateValues::debugWrite(FILE* file, const rcContourSet* cs)
+ {
+ if (!file || !cs)
+ return;
+
+ fwrite(&(cs->cs), sizeof(float), 1, file);
+ fwrite(&(cs->ch), sizeof(float), 1, file);
+ fwrite(cs->bmin, sizeof(float), 3, file);
+ fwrite(cs->bmax, sizeof(float), 3, file);
+ fwrite(&(cs->nconts), sizeof(int), 1, file);
+ for (int i = 0; i < cs->nconts; ++i)
+ {
+ fwrite(&cs->conts[i].area, sizeof(unsigned char), 1, file);
+ fwrite(&cs->conts[i].reg, sizeof(unsigned short), 1, file);
+ fwrite(&cs->conts[i].nverts, sizeof(int), 1, file);
+ fwrite(cs->conts[i].verts, sizeof(int), cs->conts[i].nverts*4, file);
+ fwrite(&cs->conts[i].nrverts, sizeof(int), 1, file);
+ fwrite(cs->conts[i].rverts, sizeof(int), cs->conts[i].nrverts*4, file);
+ }
+ }
+
+ void IntermediateValues::debugWrite(FILE* file, const rcPolyMesh* mesh)
+ {
+ if (!file || !mesh)
+ return;
+
+ fwrite(&(mesh->cs), sizeof(float), 1, file);
+ fwrite(&(mesh->ch), sizeof(float), 1, file);
+ fwrite(&(mesh->nvp), sizeof(int), 1, file);
+ fwrite(mesh->bmin, sizeof(float), 3, file);
+ fwrite(mesh->bmax, sizeof(float), 3, file);
+ fwrite(&(mesh->nverts), sizeof(int), 1, file);
+ fwrite(mesh->verts, sizeof(unsigned short), mesh->nverts*3, file);
+ fwrite(&(mesh->npolys), sizeof(int), 1, file);
+ fwrite(mesh->polys, sizeof(unsigned short), mesh->npolys*mesh->nvp*2, file);
+ fwrite(mesh->flags, sizeof(unsigned short), mesh->npolys, file);
+ fwrite(mesh->areas, sizeof(unsigned char), mesh->npolys, file);
+ fwrite(mesh->regs, sizeof(unsigned short), mesh->npolys, file);
+ }
+
+ void IntermediateValues::debugWrite(FILE* file, const rcPolyMeshDetail* mesh)
+ {
+ if (!file || !mesh)
+ return;
+
+ fwrite(&(mesh->nverts), sizeof(int), 1, file);
+ fwrite(mesh->verts, sizeof(float), mesh->nverts*3, file);
+ fwrite(&(mesh->ntris), sizeof(int), 1, file);
+ fwrite(mesh->tris, sizeof(char), mesh->ntris*4, file);
+ fwrite(&(mesh->nmeshes), sizeof(int), 1, file);
+ fwrite(mesh->meshes, sizeof(int), mesh->nmeshes*4, file);
+ }
+
+ void IntermediateValues::generateObjFile(uint32 mapID, uint32 tileX, uint32 tileY, MeshData &meshData)
+ {
+ char objFileName[255];
+ sprintf(objFileName, "meshes/map%03u%02u%02u.obj", mapID, tileY, tileX);
+
+ FILE* objFile = fopen(objFileName, "wb");
+ if (!objFile)
+ {
+ char message[1024];
+ sprintf(message, "Failed to open %s for writing!\n", objFileName);
+ perror(message);
+ return;
+ }
+
+ G3D::Array<float> allVerts;
+ G3D::Array<int> allTris;
+
+ allTris.append(meshData.liquidTris);
+ allVerts.append(meshData.liquidVerts);
+ TerrainBuilder::copyIndices(meshData.solidTris, allTris, allVerts.size() / 3);
+ allVerts.append(meshData.solidVerts);
+
+ float* verts = allVerts.getCArray();
+ int vertCount = allVerts.size() / 3;
+ int* tris = allTris.getCArray();
+ int triCount = allTris.size() / 3;
+
+ for (int i = 0; i < allVerts.size() / 3; i++)
+ fprintf(objFile, "v %f %f %f\n", verts[i*3], verts[i*3 + 1], verts[i*3 + 2]);
+
+ for (int i = 0; i < allTris.size() / 3; i++)
+ fprintf(objFile, "f %i %i %i\n", tris[i*3] + 1, tris[i*3 + 1] + 1, tris[i*3 + 2] + 1);
+
+ fclose(objFile);
+
+
+ char tileString[25];
+ sprintf(tileString, "[%02u,%02u]: ", tileY, tileX);
+ printf("%sWriting debug output... \r", tileString);
+
+ sprintf(objFileName, "meshes/%03u.map", mapID);
+
+ objFile = fopen(objFileName, "wb");
+ if (!objFile)
+ {
+ char message[1024];
+ sprintf(message, "Failed to open %s for writing!\n", objFileName);
+ perror(message);
+ return;
+ }
+
+ char b = '\0';
+ fwrite(&b, sizeof(char), 1, objFile);
+ fclose(objFile);
+
+ sprintf(objFileName, "meshes/%03u%02u%02u.mesh", mapID, tileY, tileX);
+ objFile = fopen(objFileName, "wb");
+ if (!objFile)
+ {
+ char message[1024];
+ sprintf(message, "Failed to open %s for writing!\n", objFileName);
+ perror(message);
+ return;
+ }
+
+ fwrite(&vertCount, sizeof(int), 1, objFile);
+ fwrite(verts, sizeof(float), vertCount*3, objFile);
+ fflush(objFile);
+
+ fwrite(&triCount, sizeof(int), 1, objFile);
+ fwrite(tris, sizeof(int), triCount*3, objFile);
+ fflush(objFile);
+
+ fclose(objFile);
+ }
+}
diff --git a/src/tools/mmaps_generator/IntermediateValues.h b/src/tools/mmaps_generator/IntermediateValues.h
new file mode 100644
index 00000000000..89a5c3ae4c2
--- /dev/null
+++ b/src/tools/mmaps_generator/IntermediateValues.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2008-2013 TrinityCore <http://www.trinitycore.org/>
+ * Copyright (C) 2005-2011 MaNGOS <http://getmangos.com/>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _INTERMEDIATE_VALUES_H
+#define _INTERMEDIATE_VALUES_H
+
+#include "PathCommon.h"
+#include "TerrainBuilder.h"
+#include "Recast.h"
+#include "DetourNavMesh.h"
+
+namespace MMAP
+{
+ // this class gathers all debug info holding and output
+ struct IntermediateValues
+ {
+ rcHeightfield* heightfield;
+ rcCompactHeightfield* compactHeightfield;
+ rcContourSet* contours;
+ rcPolyMesh* polyMesh;
+ rcPolyMeshDetail* polyMeshDetail;
+
+ IntermediateValues() : heightfield(NULL), compactHeightfield(NULL),
+ contours(NULL), polyMesh(NULL), polyMeshDetail(NULL) {}
+ ~IntermediateValues();
+
+ void writeIV(uint32 mapID, uint32 tileX, uint32 tileY);
+
+ void debugWrite(FILE* file, const rcHeightfield* mesh);
+ void debugWrite(FILE* file, const rcCompactHeightfield* chf);
+ void debugWrite(FILE* file, const rcContourSet* cs);
+ void debugWrite(FILE* file, const rcPolyMesh* mesh);
+ void debugWrite(FILE* file, const rcPolyMeshDetail* mesh);
+
+ void generateObjFile(uint32 mapID, uint32 tileX, uint32 tileY, MeshData &meshData);
+ };
+}
+#endif
diff --git a/src/tools/mmaps_generator/MapBuilder.cpp b/src/tools/mmaps_generator/MapBuilder.cpp
new file mode 100644
index 00000000000..a62011ddfff
--- /dev/null
+++ b/src/tools/mmaps_generator/MapBuilder.cpp
@@ -0,0 +1,982 @@
+/*
+ * Copyright (C) 2008-2013 TrinityCore <http://www.trinitycore.org/>
+ * Copyright (C) 2005-2011 MaNGOS <http://getmangos.com/>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include "PathCommon.h"
+#include "MapBuilder.h"
+
+#include "MapTree.h"
+#include "ModelInstance.h"
+
+#include "DetourNavMeshBuilder.h"
+#include "DetourNavMesh.h"
+#include "DetourCommon.h"
+
+#include "DisableMgr.h"
+#include <ace/OS_NS_unistd.h>
+
+uint32 GetLiquidFlags(uint32 /*liquidType*/) { return 0; }
+namespace DisableMgr
+{
+ bool IsDisabledFor(DisableType /*type*/, uint32 /*entry*/, Unit const* /*unit*/, uint8 /*flags*/ /*= 0*/) { return false; }
+}
+
+#define MMAP_MAGIC 0x4d4d4150 // 'MMAP'
+#define MMAP_VERSION 3
+
+struct MmapTileHeader
+{
+ uint32 mmapMagic;
+ uint32 dtVersion;
+ uint32 mmapVersion;
+ uint32 size;
+ bool usesLiquids : 1;
+
+ MmapTileHeader() : mmapMagic(MMAP_MAGIC), dtVersion(DT_NAVMESH_VERSION),
+ mmapVersion(MMAP_VERSION), size(0), usesLiquids(true) {}
+};
+
+namespace MMAP
+{
+ MapBuilder::MapBuilder(float maxWalkableAngle, bool skipLiquid,
+ bool skipContinents, bool skipJunkMaps, bool skipBattlegrounds,
+ bool debugOutput, bool bigBaseUnit, const char* offMeshFilePath) :
+ m_terrainBuilder (NULL),
+ m_debugOutput (debugOutput),
+ m_offMeshFilePath (offMeshFilePath),
+ m_skipContinents (skipContinents),
+ m_skipJunkMaps (skipJunkMaps),
+ m_skipBattlegrounds (skipBattlegrounds),
+ m_maxWalkableAngle (maxWalkableAngle),
+ m_bigBaseUnit (bigBaseUnit),
+ m_rcContext (NULL)
+ {
+ m_terrainBuilder = new TerrainBuilder(skipLiquid);
+
+ m_rcContext = new rcContext(false);
+
+ discoverTiles();
+ }
+
+ /**************************************************************************/
+ MapBuilder::~MapBuilder()
+ {
+ for (TileList::iterator it = m_tiles.begin(); it != m_tiles.end(); ++it)
+ {
+ (*it).second->clear();
+ delete (*it).second;
+ }
+
+ delete m_terrainBuilder;
+ delete m_rcContext;
+ }
+
+ /**************************************************************************/
+ void MapBuilder::discoverTiles()
+ {
+ std::vector<std::string> files;
+ uint32 mapID, tileX, tileY, tileID, count = 0;
+ char filter[12];
+
+ printf("Discovering maps... ");
+ getDirContents(files, "maps");
+ 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())
+ {
+ m_tiles.insert(std::pair<uint32, std::set<uint32>*>(mapID, new std::set<uint32>));
+ count++;
+ }
+ }
+
+ files.clear();
+ getDirContents(files, "vmaps", "*.vmtree");
+ for (uint32 i = 0; i < files.size(); ++i)
+ {
+ mapID = uint32(atoi(files[i].substr(0,3).c_str()));
+ m_tiles.insert(std::pair<uint32, std::set<uint32>*>(mapID, new std::set<uint32>));
+ count++;
+ }
+ printf("found %u.\n", count);
+
+ count = 0;
+ printf("Discovering tiles... ");
+ for (TileList::iterator itr = m_tiles.begin(); itr != m_tiles.end(); ++itr)
+ {
+ std::set<uint32>* tiles = (*itr).second;
+ mapID = (*itr).first;
+
+ sprintf(filter, "%03u*.vmtile", mapID);
+ files.clear();
+ getDirContents(files, "vmaps", filter);
+ for (uint32 i = 0; i < files.size(); ++i)
+ {
+ tileX = uint32(atoi(files[i].substr(7,2).c_str()));
+ tileY = uint32(atoi(files[i].substr(4,2).c_str()));
+ tileID = StaticMapTree::packTileID(tileY, tileX);
+
+ tiles->insert(tileID);
+ count++;
+ }
+
+ sprintf(filter, "%03u*", mapID);
+ files.clear();
+ getDirContents(files, "maps", filter);
+ for (uint32 i = 0; i < files.size(); ++i)
+ {
+ tileY = uint32(atoi(files[i].substr(3,2).c_str()));
+ tileX = uint32(atoi(files[i].substr(5,2).c_str()));
+ tileID = StaticMapTree::packTileID(tileX, tileY);
+
+ if (tiles->insert(tileID).second)
+ count++;
+ }
+ }
+ printf("found %u.\n\n", count);
+ }
+
+ /**************************************************************************/
+ std::set<uint32>* MapBuilder::getTileList(uint32 mapID)
+ {
+ TileList::iterator itr = m_tiles.find(mapID);
+ if (itr != m_tiles.end())
+ return (*itr).second;
+
+ std::set<uint32>* tiles = new std::set<uint32>();
+ m_tiles.insert(std::pair<uint32, std::set<uint32>*>(mapID, tiles));
+ return tiles;
+ }
+
+ /**************************************************************************/
+ void MapBuilder::buildAllMaps(int threads)
+ {
+ std::vector<BuilderThread*> _threads;
+
+ for (int i = 0; i < threads; ++i)
+ _threads.push_back(new BuilderThread(this));
+
+ for (TileList::iterator it = m_tiles.begin(); it != m_tiles.end(); ++it)
+ {
+ uint32 mapID = it->first;
+ if (!shouldSkipMap(mapID))
+ {
+ if (threads > 1)
+ {
+ bool next = false;
+ while (!next)
+ {
+ for (std::vector<BuilderThread*>::iterator _th = _threads.begin(); _th != _threads.end(); ++_th)
+ {
+ if ((*_th)->Free)
+ {
+ (*_th)->SetMapId(mapID);
+ (*_th)->activate();
+ next = true;
+ break;
+ }
+ }
+ // Wait for 20 seconds
+ ACE_OS::sleep(ACE_Time_Value (0, 20000));
+ }
+ }
+ else
+ buildMap(mapID);
+ }
+ }
+
+ // Free memory
+ for (std::vector<BuilderThread*>::iterator _th = _threads.begin(); _th != _threads.end(); ++_th)
+ {
+ (*_th)->wait();
+ delete *_th;
+ }
+ }
+
+ /**************************************************************************/
+ void MapBuilder::getGridBounds(uint32 mapID, uint32 &minX, uint32 &minY, uint32 &maxX, uint32 &maxY)
+ {
+ maxX = INT_MAX;
+ maxY = INT_MAX;
+ minX = INT_MIN;
+ minY = INT_MIN;
+
+ float bmin[3], bmax[3], lmin[3], lmax[3];
+ MeshData meshData;
+
+ // make sure we process maps which don't have tiles
+ // initialize the static tree, which loads WDT models
+ if (!m_terrainBuilder->loadVMap(mapID, 64, 64, meshData))
+ return;
+
+ // get the coord bounds of the model data
+ if (meshData.solidVerts.size() + meshData.liquidVerts.size() == 0)
+ return;
+
+ // get the coord bounds of the model data
+ if (meshData.solidVerts.size() && meshData.liquidVerts.size())
+ {
+ rcCalcBounds(meshData.solidVerts.getCArray(), meshData.solidVerts.size() / 3, bmin, bmax);
+ rcCalcBounds(meshData.liquidVerts.getCArray(), meshData.liquidVerts.size() / 3, lmin, lmax);
+ rcVmin(bmin, lmin);
+ rcVmax(bmax, lmax);
+ }
+ else if (meshData.solidVerts.size())
+ rcCalcBounds(meshData.solidVerts.getCArray(), meshData.solidVerts.size() / 3, bmin, bmax);
+ else
+ rcCalcBounds(meshData.liquidVerts.getCArray(), meshData.liquidVerts.size() / 3, lmin, lmax);
+
+ // convert coord bounds to grid bounds
+ maxX = 32 - bmin[0] / GRID_SIZE;
+ maxY = 32 - bmin[2] / GRID_SIZE;
+ minX = 32 - bmax[0] / GRID_SIZE;
+ minY = 32 - bmax[2] / GRID_SIZE;
+ }
+
+ void MapBuilder::buildMeshFromFile(char* name)
+ {
+ FILE* file = fopen(name, "rb");
+ if (!file)
+ return;
+
+ printf("Building mesh from file\n");
+ int tileX, tileY, mapId;
+ if (fread(&mapId, sizeof(int), 1, file) != 1)
+ return;
+ if (fread(&tileX, sizeof(int), 1, file) != 1)
+ return;
+ if (fread(&tileY, sizeof(int), 1, file) != 1)
+ return;
+
+ dtNavMesh* navMesh = NULL;
+ buildNavMesh(mapId, navMesh);
+ if (!navMesh)
+ {
+ printf("Failed creating navmesh! \n");
+ fclose(file);
+ return;
+ }
+
+ uint32 verticesCount, indicesCount;
+ if (fread(&verticesCount, sizeof(uint32), 1, file) != 1)
+ return;
+ if (fread(&indicesCount, sizeof(uint32), 1, file) != 1)
+ return;
+
+ float* verts = new float[verticesCount];
+ int* inds = new int[indicesCount];
+
+ if (fread(verts, sizeof(float), verticesCount, file) != verticesCount)
+ return;
+ if (fread(inds, sizeof(int), indicesCount, file) != indicesCount)
+ return;
+
+ MeshData data;
+
+ for (uint32 i = 0; i < verticesCount; ++i)
+ data.solidVerts.append(verts[i]);
+
+ for (uint32 i = 0; i < indicesCount; ++i)
+ data.solidTris.append(inds[i]);
+
+ TerrainBuilder::cleanVertices(data.solidVerts, data.solidTris);
+ // get bounds of current tile
+ float bmin[3], bmax[3];
+ getTileBounds(tileX, tileY, data.solidVerts.getCArray(), data.solidVerts.size() / 3, bmin, bmax);
+
+ // build navmesh tile
+ buildMoveMapTile(mapId, tileX, tileY, data, bmin, bmax, navMesh);
+ fclose(file);
+ }
+
+ /**************************************************************************/
+ void MapBuilder::buildSingleTile(uint32 mapID, uint32 tileX, uint32 tileY)
+ {
+ dtNavMesh* navMesh = NULL;
+ buildNavMesh(mapID, navMesh);
+ if (!navMesh)
+ {
+ printf("Failed creating navmesh! \n");
+ return;
+ }
+
+ buildTile(mapID, tileX, tileY, navMesh);
+ dtFreeNavMesh(navMesh);
+ }
+
+ /**************************************************************************/
+ void MapBuilder::buildMap(uint32 mapID)
+ {
+ printf("[Thread %u] Building map %03u:\n", uint32(ACE_Thread::self()), mapID);
+
+ std::set<uint32>* tiles = getTileList(mapID);
+
+ // make sure we process maps which don't have tiles
+ if (!tiles->size())
+ {
+ // convert coord bounds to grid bounds
+ uint32 minX, minY, maxX, maxY;
+ getGridBounds(mapID, minX, minY, maxX, maxY);
+
+ // add all tiles within bounds to tile list.
+ for (uint32 i = minX; i <= maxX; ++i)
+ for (uint32 j = minY; j <= maxY; ++j)
+ tiles->insert(StaticMapTree::packTileID(i, j));
+ }
+
+ if (!tiles->empty())
+ {
+ // build navMesh
+ dtNavMesh* navMesh = NULL;
+ buildNavMesh(mapID, navMesh);
+ if (!navMesh)
+ {
+ printf("[Map %i] Failed creating navmesh!\n", mapID);
+ return;
+ }
+
+ // now start building mmtiles for each tile
+ printf("[Map %i] We have %u tiles. \n", mapID, (unsigned int)tiles->size());
+ for (std::set<uint32>::iterator it = tiles->begin(); it != tiles->end(); ++it)
+ {
+ uint32 tileX, tileY;
+
+ // unpack tile coords
+ StaticMapTree::unpackTileID((*it), tileX, tileY);
+
+ if (shouldSkipTile(mapID, tileX, tileY))
+ continue;
+
+ buildTile(mapID, tileX, tileY, navMesh);
+ }
+
+ dtFreeNavMesh(navMesh);
+ }
+
+ printf("[Map %i] Complete!\n", mapID);
+ }
+
+ /**************************************************************************/
+ void MapBuilder::buildTile(uint32 mapID, uint32 tileX, uint32 tileY, dtNavMesh* navMesh)
+ {
+ printf("[Map %i] Building tile [%02u,%02u]\n", mapID, tileX, tileY);
+
+ MeshData meshData;
+
+ // get heightmap data
+ m_terrainBuilder->loadMap(mapID, tileX, tileY, meshData);
+
+ // get model data
+ m_terrainBuilder->loadVMap(mapID, tileY, tileX, meshData);
+
+ // if there is no data, give up now
+ if (!meshData.solidVerts.size() && !meshData.liquidVerts.size())
+ return;
+
+ // remove unused vertices
+ TerrainBuilder::cleanVertices(meshData.solidVerts, meshData.solidTris);
+ TerrainBuilder::cleanVertices(meshData.liquidVerts, meshData.liquidTris);
+
+ // gather all mesh data for final data check, and bounds calculation
+ G3D::Array<float> allVerts;
+ allVerts.append(meshData.liquidVerts);
+ allVerts.append(meshData.solidVerts);
+
+ if (!allVerts.size())
+ return;
+
+ // get bounds of current tile
+ float bmin[3], bmax[3];
+ getTileBounds(tileX, tileY, allVerts.getCArray(), allVerts.size() / 3, bmin, bmax);
+
+ m_terrainBuilder->loadOffMeshConnections(mapID, tileX, tileY, meshData, m_offMeshFilePath);
+
+ // build navmesh tile
+ buildMoveMapTile(mapID, tileX, tileY, meshData, bmin, bmax, navMesh);
+ }
+
+ /**************************************************************************/
+ void MapBuilder::buildNavMesh(uint32 mapID, dtNavMesh* &navMesh)
+ {
+ std::set<uint32>* tiles = getTileList(mapID);
+
+ // old code for non-statically assigned bitmask sizes:
+ ///*** calculate number of bits needed to store tiles & polys ***/
+ //int tileBits = dtIlog2(dtNextPow2(tiles->size()));
+ //if (tileBits < 1) tileBits = 1; // need at least one bit!
+ //int polyBits = sizeof(dtPolyRef)*8 - SALT_MIN_BITS - tileBits;
+
+ int polyBits = STATIC_POLY_BITS;
+
+ int maxTiles = tiles->size();
+ int maxPolysPerTile = 1 << polyBits;
+
+ /*** calculate bounds of map ***/
+
+ uint32 tileXMin = 64, tileYMin = 64, tileXMax = 0, tileYMax = 0, tileX, tileY;
+ for (std::set<uint32>::iterator it = tiles->begin(); it != tiles->end(); ++it)
+ {
+ StaticMapTree::unpackTileID(*it, tileX, tileY);
+
+ if (tileX > tileXMax)
+ tileXMax = tileX;
+ else if (tileX < tileXMin)
+ tileXMin = tileX;
+
+ if (tileY > tileYMax)
+ tileYMax = tileY;
+ else if (tileY < tileYMin)
+ tileYMin = tileY;
+ }
+
+ // use Max because '32 - tileX' is negative for values over 32
+ float bmin[3], bmax[3];
+ getTileBounds(tileXMax, tileYMax, NULL, 0, bmin, bmax);
+
+ /*** now create the navmesh ***/
+
+ // navmesh creation params
+ dtNavMeshParams navMeshParams;
+ memset(&navMeshParams, 0, sizeof(dtNavMeshParams));
+ navMeshParams.tileWidth = GRID_SIZE;
+ navMeshParams.tileHeight = GRID_SIZE;
+ rcVcopy(navMeshParams.orig, bmin);
+ navMeshParams.maxTiles = maxTiles;
+ navMeshParams.maxPolys = maxPolysPerTile;
+
+ navMesh = dtAllocNavMesh();
+ printf("[Map %i] Creating navMesh...\n", mapID);
+ if (!navMesh->init(&navMeshParams))
+ {
+ printf("[Map %i] Failed creating navmesh! \n", mapID);
+ return;
+ }
+
+ char fileName[25];
+ sprintf(fileName, "mmaps/%03u.mmap", mapID);
+
+ FILE* file = fopen(fileName, "wb");
+ if (!file)
+ {
+ dtFreeNavMesh(navMesh);
+ char message[1024];
+ sprintf(message, "[Map %i] Failed to open %s for writing!\n", mapID, fileName);
+ perror(message);
+ return;
+ }
+
+ // now that we know navMesh params are valid, we can write them to file
+ fwrite(&navMeshParams, sizeof(dtNavMeshParams), 1, file);
+ fclose(file);
+ }
+
+ /**************************************************************************/
+ void MapBuilder::buildMoveMapTile(uint32 mapID, uint32 tileX, uint32 tileY,
+ MeshData &meshData, float bmin[3], float bmax[3],
+ dtNavMesh* navMesh)
+ {
+ // console output
+ char tileString[20];
+ sprintf(tileString, "[Map %03i] [%02i,%02i]: ", mapID, tileX, tileY);
+ printf("%s Building movemap tiles...\n", tileString);
+
+ IntermediateValues iv;
+
+ float* tVerts = meshData.solidVerts.getCArray();
+ int tVertCount = meshData.solidVerts.size() / 3;
+ int* tTris = meshData.solidTris.getCArray();
+ int tTriCount = meshData.solidTris.size() / 3;
+
+ float* lVerts = meshData.liquidVerts.getCArray();
+ int lVertCount = meshData.liquidVerts.size() / 3;
+ int* lTris = meshData.liquidTris.getCArray();
+ int lTriCount = meshData.liquidTris.size() / 3;
+ uint8* lTriFlags = meshData.liquidType.getCArray();
+
+ // these are WORLD UNIT based metrics
+ // this are basic unit dimentions
+ // value have to divide GRID_SIZE(533.33333f) ( aka: 0.5333, 0.2666, 0.3333, 0.1333, etc )
+ const static float BASE_UNIT_DIM = m_bigBaseUnit ? 0.533333f : 0.266666f;
+
+ // All are in UNIT metrics!
+ const static int VERTEX_PER_MAP = int(GRID_SIZE/BASE_UNIT_DIM + 0.5f);
+ const static int VERTEX_PER_TILE = m_bigBaseUnit ? 40 : 80; // must divide VERTEX_PER_MAP
+ const static int TILES_PER_MAP = VERTEX_PER_MAP/VERTEX_PER_TILE;
+
+ rcConfig config;
+ memset(&config, 0, sizeof(rcConfig));
+
+ rcVcopy(config.bmin, bmin);
+ rcVcopy(config.bmax, bmax);
+
+ config.maxVertsPerPoly = DT_VERTS_PER_POLYGON;
+ config.cs = BASE_UNIT_DIM;
+ config.ch = BASE_UNIT_DIM;
+ config.walkableSlopeAngle = m_maxWalkableAngle;
+ config.tileSize = VERTEX_PER_TILE;
+ config.walkableRadius = m_bigBaseUnit ? 1 : 2;
+ config.borderSize = config.walkableRadius + 3;
+ config.maxEdgeLen = VERTEX_PER_TILE + 1; //anything bigger than tileSize
+ config.walkableHeight = m_bigBaseUnit ? 3 : 6;
+ config.walkableClimb = m_bigBaseUnit ? 2 : 4; // keep less than walkableHeight
+ config.minRegionArea = rcSqr(60);
+ config.mergeRegionArea = rcSqr(50);
+ config.maxSimplificationError = 2.0f; // eliminates most jagged edges (tinny polygons)
+ config.detailSampleDist = config.cs * 64;
+ config.detailSampleMaxError = config.ch * 2;
+
+ // this sets the dimensions of the heightfield - should maybe happen before border padding
+ rcCalcGridSize(config.bmin, config.bmax, config.cs, &config.width, &config.height);
+
+ // allocate subregions : tiles
+ Tile* tiles = new Tile[TILES_PER_MAP * TILES_PER_MAP];
+
+ // Initialize per tile config.
+ rcConfig tileCfg = config;
+ tileCfg.width = config.tileSize + config.borderSize*2;
+ tileCfg.height = config.tileSize + config.borderSize*2;
+
+ // merge per tile poly and detail meshes
+ rcPolyMesh** pmmerge = new rcPolyMesh*[TILES_PER_MAP * TILES_PER_MAP];
+ if (!pmmerge)
+ {
+ printf("%s alloc pmmerge FIALED!\n", tileString);
+ return;
+ }
+
+ rcPolyMeshDetail** dmmerge = new rcPolyMeshDetail*[TILES_PER_MAP * TILES_PER_MAP];
+ if (!dmmerge)
+ {
+ printf("%s alloc dmmerge FIALED!\n", tileString);
+ return;
+ }
+
+ int nmerge = 0;
+ // build all tiles
+ for (int y = 0; y < TILES_PER_MAP; ++y)
+ {
+ for (int x = 0; x < TILES_PER_MAP; ++x)
+ {
+ Tile& tile = tiles[x + y * TILES_PER_MAP];
+
+ // Calculate the per tile bounding box.
+ tileCfg.bmin[0] = config.bmin[0] + float(x*config.tileSize - config.borderSize)*config.cs;
+ tileCfg.bmin[2] = config.bmin[2] + float(y*config.tileSize - config.borderSize)*config.cs;
+ tileCfg.bmax[0] = config.bmin[0] + float((x+1)*config.tileSize + config.borderSize)*config.cs;
+ tileCfg.bmax[2] = config.bmin[2] + float((y+1)*config.tileSize + config.borderSize)*config.cs;
+
+ // build heightfield
+ tile.solid = rcAllocHeightfield();
+ if (!tile.solid || !rcCreateHeightfield(m_rcContext, *tile.solid, tileCfg.width, tileCfg.height, tileCfg.bmin, tileCfg.bmax, tileCfg.cs, tileCfg.ch))
+ {
+ printf("%s Failed building heightfield! \n", tileString);
+ continue;
+ }
+
+ // mark all walkable tiles, both liquids and solids
+ unsigned char* triFlags = new unsigned char[tTriCount];
+ memset(triFlags, NAV_GROUND, tTriCount*sizeof(unsigned char));
+ rcClearUnwalkableTriangles(m_rcContext, tileCfg.walkableSlopeAngle, tVerts, tVertCount, tTris, tTriCount, triFlags);
+ rcRasterizeTriangles(m_rcContext, tVerts, tVertCount, tTris, triFlags, tTriCount, *tile.solid, config.walkableClimb);
+ delete[] triFlags;
+
+ rcFilterLowHangingWalkableObstacles(m_rcContext, config.walkableClimb, *tile.solid);
+ rcFilterLedgeSpans(m_rcContext, tileCfg.walkableHeight, tileCfg.walkableClimb, *tile.solid);
+ rcFilterWalkableLowHeightSpans(m_rcContext, tileCfg.walkableHeight, *tile.solid);
+
+ rcRasterizeTriangles(m_rcContext, lVerts, lVertCount, lTris, lTriFlags, lTriCount, *tile.solid, config.walkableClimb);
+
+ // compact heightfield spans
+ tile.chf = rcAllocCompactHeightfield();
+ if (!tile.chf || !rcBuildCompactHeightfield(m_rcContext, tileCfg.walkableHeight, tileCfg.walkableClimb, *tile.solid, *tile.chf))
+ {
+ printf("%s Failed compacting heightfield! \n", tileString);
+ continue;
+ }
+
+ // build polymesh intermediates
+ if (!rcErodeWalkableArea(m_rcContext, config.walkableRadius, *tile.chf))
+ {
+ printf("%s Failed eroding area! \n", tileString);
+ continue;
+ }
+
+ if (!rcBuildDistanceField(m_rcContext, *tile.chf))
+ {
+ printf("%s Failed building distance field! \n", tileString);
+ continue;
+ }
+
+ if (!rcBuildRegions(m_rcContext, *tile.chf, tileCfg.borderSize, tileCfg.minRegionArea, tileCfg.mergeRegionArea))
+ {
+ printf("%s Failed building regions! \n", tileString);
+ continue;
+ }
+
+ tile.cset = rcAllocContourSet();
+ if (!tile.cset || !rcBuildContours(m_rcContext, *tile.chf, tileCfg.maxSimplificationError, tileCfg.maxEdgeLen, *tile.cset))
+ {
+ printf("%s Failed building contours! \n", tileString);
+ continue;
+ }
+
+ // build polymesh
+ tile.pmesh = rcAllocPolyMesh();
+ if (!tile.pmesh || !rcBuildPolyMesh(m_rcContext, *tile.cset, tileCfg.maxVertsPerPoly, *tile.pmesh))
+ {
+ printf("%s Failed building polymesh! \n", tileString);
+ continue;
+ }
+
+ tile.dmesh = rcAllocPolyMeshDetail();
+ if (!tile.dmesh || !rcBuildPolyMeshDetail(m_rcContext, *tile.pmesh, *tile.chf, tileCfg.detailSampleDist, tileCfg.detailSampleMaxError, *tile.dmesh))
+ {
+ printf("%s Failed building polymesh detail! \n", tileString);
+ continue;
+ }
+
+ // free those up
+ // we may want to keep them in the future for debug
+ // but right now, we don't have the code to merge them
+ rcFreeHeightField(tile.solid);
+ tile.solid = NULL;
+ rcFreeCompactHeightfield(tile.chf);
+ tile.chf = NULL;
+ rcFreeContourSet(tile.cset);
+ tile.cset = NULL;
+
+ if (tile.pmesh)
+ {
+ pmmerge[nmerge] = tile.pmesh;
+ dmmerge[nmerge] = tile.dmesh;
+ nmerge++;
+ }
+ }
+ }
+
+ iv.polyMesh = rcAllocPolyMesh();
+ if (!iv.polyMesh)
+ {
+ printf("%s alloc iv.polyMesh FIALED!\n", tileString);
+ return;
+ }
+ rcMergePolyMeshes(m_rcContext, pmmerge, nmerge, *iv.polyMesh);
+
+ iv.polyMeshDetail = rcAllocPolyMeshDetail();
+ if (!iv.polyMeshDetail)
+ {
+ printf("%s alloc m_dmesh FIALED!\n", tileString);
+ return;
+ }
+ rcMergePolyMeshDetails(m_rcContext, dmmerge, nmerge, *iv.polyMeshDetail);
+
+ // free things up
+ delete[] pmmerge;
+ delete[] dmmerge;
+
+ delete[] tiles;
+
+ // remove padding for extraction
+ for (int i = 0; i < iv.polyMesh->nverts; ++i)
+ {
+ unsigned short* v = &iv.polyMesh->verts[i*3];
+ v[0] -= (unsigned short)config.borderSize;
+ v[2] -= (unsigned short)config.borderSize;
+ }
+
+ // set polygons as walkable
+ // TODO: special flags for DYNAMIC polygons, ie surfaces that can be turned on and off
+ for (int i = 0; i < iv.polyMesh->npolys; ++i)
+ if (iv.polyMesh->areas[i] & RC_WALKABLE_AREA)
+ iv.polyMesh->flags[i] = iv.polyMesh->areas[i];
+
+ // setup mesh parameters
+ dtNavMeshCreateParams params;
+ memset(&params, 0, sizeof(params));
+ params.verts = iv.polyMesh->verts;
+ params.vertCount = iv.polyMesh->nverts;
+ params.polys = iv.polyMesh->polys;
+ params.polyAreas = iv.polyMesh->areas;
+ params.polyFlags = iv.polyMesh->flags;
+ params.polyCount = iv.polyMesh->npolys;
+ params.nvp = iv.polyMesh->nvp;
+ params.detailMeshes = iv.polyMeshDetail->meshes;
+ params.detailVerts = iv.polyMeshDetail->verts;
+ params.detailVertsCount = iv.polyMeshDetail->nverts;
+ params.detailTris = iv.polyMeshDetail->tris;
+ params.detailTriCount = iv.polyMeshDetail->ntris;
+
+ params.offMeshConVerts = meshData.offMeshConnections.getCArray();
+ params.offMeshConCount = meshData.offMeshConnections.size()/6;
+ params.offMeshConRad = meshData.offMeshConnectionRads.getCArray();
+ params.offMeshConDir = meshData.offMeshConnectionDirs.getCArray();
+ params.offMeshConAreas = meshData.offMeshConnectionsAreas.getCArray();
+ params.offMeshConFlags = meshData.offMeshConnectionsFlags.getCArray();
+
+ params.walkableHeight = BASE_UNIT_DIM*config.walkableHeight; // agent height
+ params.walkableRadius = BASE_UNIT_DIM*config.walkableRadius; // agent radius
+ params.walkableClimb = BASE_UNIT_DIM*config.walkableClimb; // keep less that walkableHeight (aka agent height)!
+ params.tileX = (((bmin[0] + bmax[0]) / 2) - navMesh->getParams()->orig[0]) / GRID_SIZE;
+ params.tileY = (((bmin[2] + bmax[2]) / 2) - navMesh->getParams()->orig[2]) / GRID_SIZE;
+ rcVcopy(params.bmin, bmin);
+ rcVcopy(params.bmax, bmax);
+ params.cs = config.cs;
+ params.ch = config.ch;
+ params.tileSize = VERTEX_PER_MAP;
+
+ // will hold final navmesh
+ unsigned char* navData = NULL;
+ int navDataSize = 0;
+
+ do
+ {
+ // these values are checked within dtCreateNavMeshData - handle them here
+ // so we have a clear error message
+ if (params.nvp > DT_VERTS_PER_POLYGON)
+ {
+ printf("%s Invalid verts-per-polygon value! \n", tileString);
+ continue;
+ }
+ if (params.vertCount >= 0xffff)
+ {
+ printf("%s Too many vertices! \n", tileString);
+ continue;
+ }
+ if (!params.vertCount || !params.verts)
+ {
+ // occurs mostly when adjacent tiles have models
+ // loaded but those models don't span into this tile
+
+ // message is an annoyance
+ //printf("%sNo vertices to build tile! \n", tileString);
+ continue;
+ }
+ if (!params.polyCount || !params.polys ||
+ TILES_PER_MAP*TILES_PER_MAP == params.polyCount)
+ {
+ // we have flat tiles with no actual geometry - don't build those, its useless
+ // keep in mind that we do output those into debug info
+ // drop tiles with only exact count - some tiles may have geometry while having less tiles
+ printf("%s No polygons to build on tile! \n", tileString);
+ continue;
+ }
+ if (!params.detailMeshes || !params.detailVerts || !params.detailTris)
+ {
+ printf("%s No detail mesh to build tile! \n", tileString);
+ continue;
+ }
+
+ printf("%s Building navmesh tile...\n", tileString);
+ if (!dtCreateNavMeshData(&params, &navData, &navDataSize))
+ {
+ printf("%s Failed building navmesh tile! \n", tileString);
+ continue;
+ }
+
+ dtTileRef tileRef = 0;
+ printf("%s Adding tile to navmesh...\n", tileString);
+ // DT_TILE_FREE_DATA tells detour to unallocate memory when the tile
+ // is removed via removeTile()
+ dtStatus dtResult = navMesh->addTile(navData, navDataSize, DT_TILE_FREE_DATA, 0, &tileRef);
+ if (!tileRef || dtResult != DT_SUCCESS)
+ {
+ printf("%s Failed adding tile to navmesh! \n", tileString);
+ continue;
+ }
+
+ // file output
+ char fileName[255];
+ sprintf(fileName, "mmaps/%03u%02i%02i.mmtile", mapID, tileY, tileX);
+ FILE* file = fopen(fileName, "wb");
+ if (!file)
+ {
+ char message[1024];
+ sprintf(message, "[Map %i] Failed to open %s for writing!\n", mapID, fileName);
+ perror(message);
+ navMesh->removeTile(tileRef, NULL, NULL);
+ continue;
+ }
+
+ printf("%s Writing to file...\n", tileString);
+
+ // write header
+ MmapTileHeader header;
+ header.usesLiquids = m_terrainBuilder->usesLiquids();
+ header.size = uint32(navDataSize);
+ fwrite(&header, sizeof(MmapTileHeader), 1, file);
+
+ // write data
+ fwrite(navData, sizeof(unsigned char), navDataSize, file);
+ fclose(file);
+
+ // now that tile is written to disk, we can unload it
+ navMesh->removeTile(tileRef, NULL, NULL);
+ }
+ while (0);
+
+ if (m_debugOutput)
+ {
+ // restore padding so that the debug visualization is correct
+ for (int i = 0; i < iv.polyMesh->nverts; ++i)
+ {
+ unsigned short* v = &iv.polyMesh->verts[i*3];
+ v[0] += (unsigned short)config.borderSize;
+ v[2] += (unsigned short)config.borderSize;
+ }
+
+ iv.generateObjFile(mapID, tileX, tileY, meshData);
+ iv.writeIV(mapID, tileX, tileY);
+ }
+ }
+
+ /**************************************************************************/
+ void MapBuilder::getTileBounds(uint32 tileX, uint32 tileY, float* verts, int vertCount, float* bmin, float* bmax)
+ {
+ // this is for elevation
+ if (verts && vertCount)
+ rcCalcBounds(verts, vertCount, bmin, bmax);
+ else
+ {
+ bmin[1] = FLT_MIN;
+ bmax[1] = FLT_MAX;
+ }
+
+ // this is for width and depth
+ bmax[0] = (32 - int(tileX)) * GRID_SIZE;
+ bmax[2] = (32 - int(tileY)) * GRID_SIZE;
+ bmin[0] = bmax[0] - GRID_SIZE;
+ bmin[2] = bmax[2] - GRID_SIZE;
+ }
+
+ /**************************************************************************/
+ bool MapBuilder::shouldSkipMap(uint32 mapID)
+ {
+ if (m_skipContinents)
+ switch (mapID)
+ {
+ case 0:
+ case 1:
+ case 530:
+ case 571:
+ return true;
+ default:
+ break;
+ }
+
+ if (m_skipJunkMaps)
+ switch (mapID)
+ {
+ case 13: // test.wdt
+ case 25: // ScottTest.wdt
+ case 29: // Test.wdt
+ case 42: // Colin.wdt
+ case 169: // EmeraldDream.wdt (unused, and very large)
+ case 451: // development.wdt
+ case 573: // ExteriorTest.wdt
+ case 597: // CraigTest.wdt
+ case 605: // development_nonweighted.wdt
+ case 606: // QA_DVD.wdt
+ return true;
+ default:
+ if (isTransportMap(mapID))
+ return true;
+ break;
+ }
+
+ if (m_skipBattlegrounds)
+ switch (mapID)
+ {
+ case 30: // AV
+ case 37: // ?
+ case 489: // WSG
+ case 529: // AB
+ case 566: // EotS
+ case 607: // SotA
+ case 628: // IoC
+ return true;
+ default:
+ break;
+ }
+
+ return false;
+ }
+
+ /**************************************************************************/
+ bool MapBuilder::isTransportMap(uint32 mapID)
+ {
+ switch (mapID)
+ {
+ // transport maps
+ case 582:
+ case 584:
+ case 586:
+ case 587:
+ case 588:
+ case 589:
+ case 590:
+ case 591:
+ case 592:
+ case 593:
+ case 594:
+ case 596:
+ case 610:
+ case 612:
+ case 613:
+ case 614:
+ case 620:
+ case 621:
+ case 622:
+ case 623:
+ case 641:
+ case 642:
+ case 647:
+ case 672:
+ case 673:
+ case 712:
+ case 713:
+ case 718:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ /**************************************************************************/
+ bool MapBuilder::shouldSkipTile(uint32 mapID, uint32 tileX, uint32 tileY)
+ {
+ char fileName[255];
+ sprintf(fileName, "mmaps/%03u%02i%02i.mmtile", mapID, tileY, tileX);
+ FILE* file = fopen(fileName, "rb");
+ if (!file)
+ return false;
+
+ MmapTileHeader header;
+ int count = fread(&header, sizeof(MmapTileHeader), 1, file);
+ fclose(file);
+ if (count != 1)
+ return false;
+
+ if (header.mmapMagic != MMAP_MAGIC || header.dtVersion != DT_NAVMESH_VERSION)
+ return false;
+
+ if (header.mmapVersion != MMAP_VERSION)
+ return false;
+
+ return true;
+ }
+
+}
diff --git a/src/tools/mmaps_generator/MapBuilder.h b/src/tools/mmaps_generator/MapBuilder.h
new file mode 100644
index 00000000000..ea9636b3cc3
--- /dev/null
+++ b/src/tools/mmaps_generator/MapBuilder.h
@@ -0,0 +1,150 @@
+/*
+ * Copyright (C) 2008-2013 TrinityCore <http://www.trinitycore.org/>
+ * Copyright (C) 2005-2011 MaNGOS <http://getmangos.com/>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _MAP_BUILDER_H
+#define _MAP_BUILDER_H
+
+#include <vector>
+#include <set>
+#include <map>
+
+#include "TerrainBuilder.h"
+#include "IntermediateValues.h"
+
+#include "Recast.h"
+#include "DetourNavMesh.h"
+
+#include <ace/Task.h>
+
+using namespace VMAP;
+
+// G3D namespace typedefs conflicts with ACE typedefs
+
+namespace MMAP
+{
+ typedef std::map<uint32, std::set<uint32>*> TileList;
+ struct Tile
+ {
+ Tile() : chf(NULL), solid(NULL), cset(NULL), pmesh(NULL), dmesh(NULL) {}
+ ~Tile()
+ {
+ rcFreeCompactHeightfield(chf);
+ rcFreeContourSet(cset);
+ rcFreeHeightField(solid);
+ rcFreePolyMesh(pmesh);
+ rcFreePolyMeshDetail(dmesh);
+ }
+ rcCompactHeightfield* chf;
+ rcHeightfield* solid;
+ rcContourSet* cset;
+ rcPolyMesh* pmesh;
+ rcPolyMeshDetail* dmesh;
+ };
+
+ class MapBuilder
+ {
+ public:
+ MapBuilder(float maxWalkableAngle = 60.f,
+ bool skipLiquid = false,
+ bool skipContinents = false,
+ bool skipJunkMaps = true,
+ bool skipBattlegrounds = false,
+ bool debugOutput = false,
+ bool bigBaseUnit = false,
+ const char* offMeshFilePath = NULL);
+
+ ~MapBuilder();
+
+ // builds all mmap tiles for the specified map id (ignores skip settings)
+ void buildMap(uint32 mapID);
+ void buildMeshFromFile(char* name);
+
+ // builds an mmap tile for the specified map and its mesh
+ void buildSingleTile(uint32 mapID, uint32 tileX, uint32 tileY);
+
+ // builds list of maps, then builds all of mmap tiles (based on the skip settings)
+ void buildAllMaps(int threads);
+
+ private:
+ // detect maps and tiles
+ void discoverTiles();
+ std::set<uint32>* getTileList(uint32 mapID);
+
+ void buildNavMesh(uint32 mapID, dtNavMesh* &navMesh);
+
+ void buildTile(uint32 mapID, uint32 tileX, uint32 tileY, dtNavMesh* navMesh);
+
+ // move map building
+ void buildMoveMapTile(uint32 mapID,
+ uint32 tileX,
+ uint32 tileY,
+ MeshData &meshData,
+ float bmin[3],
+ float bmax[3],
+ dtNavMesh* navMesh);
+
+ void getTileBounds(uint32 tileX, uint32 tileY,
+ float* verts, int vertCount,
+ float* bmin, float* bmax);
+ void getGridBounds(uint32 mapID, uint32 &minX, uint32 &minY, uint32 &maxX, uint32 &maxY);
+
+ bool shouldSkipMap(uint32 mapID);
+ bool isTransportMap(uint32 mapID);
+ bool shouldSkipTile(uint32 mapID, uint32 tileX, uint32 tileY);
+
+ TerrainBuilder* m_terrainBuilder;
+ TileList m_tiles;
+
+ bool m_debugOutput;
+
+ const char* m_offMeshFilePath;
+ bool m_skipContinents;
+ bool m_skipJunkMaps;
+ bool m_skipBattlegrounds;
+
+ float m_maxWalkableAngle;
+ bool m_bigBaseUnit;
+
+ // build performance - not really used for now
+ rcContext* m_rcContext;
+ };
+
+ class BuilderThread : public ACE_Task<ACE_MT_SYNCH>
+ {
+ private:
+ MapBuilder* _builder;
+ uint32 _mapId;
+ public:
+ BuilderThread(MapBuilder* builder) : _builder(builder), Free(true) {}
+
+ void SetMapId(uint32 mapId) { _mapId = mapId; }
+
+ int svc()
+ {
+ Free = false;
+ if (_builder)
+ _builder->buildMap(_mapId);
+ Free = true;
+ return 0;
+ }
+
+ bool Free;
+ };
+}
+
+#endif
diff --git a/src/tools/mmaps_generator/PathCommon.h b/src/tools/mmaps_generator/PathCommon.h
new file mode 100644
index 00000000000..3e06ff58410
--- /dev/null
+++ b/src/tools/mmaps_generator/PathCommon.h
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2008-2013 TrinityCore <http://www.trinitycore.org/>
+ * Copyright (C) 2005-2011 MaNGOS <http://getmangos.com/>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _MMAP_COMMON_H
+#define _MMAP_COMMON_H
+
+#include <string>
+#include <vector>
+#include <ace/OS_NS_sys_time.h>
+
+#include "Define.h"
+
+#ifndef _WIN32
+ #include <stddef.h>
+ #include <dirent.h>
+#endif
+
+#ifdef __linux__
+ #include <errno.h>
+#endif
+
+enum NavTerrain
+{
+ NAV_EMPTY = 0x00,
+ NAV_GROUND = 0x01,
+ NAV_MAGMA = 0x02,
+ NAV_SLIME = 0x04,
+ NAV_WATER = 0x08,
+ NAV_UNUSED1 = 0x10,
+ NAV_UNUSED2 = 0x20,
+ NAV_UNUSED3 = 0x40,
+ NAV_UNUSED4 = 0x80
+ // we only have 8 bits
+};
+
+namespace MMAP
+{
+ inline bool matchWildcardFilter(const char* filter, const char* str)
+ {
+ if (!filter || !str)
+ return false;
+
+ // end on null character
+ while (*filter && *str)
+ {
+ if (*filter == '*')
+ {
+ if (*++filter == '\0') // wildcard at end of filter means all remaing chars match
+ return true;
+
+ while (true)
+ {
+ if (*filter == *str)
+ break;
+ if (*str == '\0')
+ return false; // reached end of string without matching next filter character
+ str++;
+ }
+ }
+ else if (*filter != *str)
+ return false; // mismatch
+
+ filter++;
+ str++;
+ }
+
+ return ((*filter == '\0' || (*filter == '*' && *++filter == '\0')) && *str == '\0');
+ }
+
+ enum ListFilesResult
+ {
+ LISTFILE_DIRECTORY_NOT_FOUND = 0,
+ LISTFILE_OK = 1
+ };
+
+ inline ListFilesResult getDirContents(std::vector<std::string> &fileList, std::string dirpath = ".", std::string filter = "*")
+ {
+ #ifdef WIN32
+ HANDLE hFind;
+ WIN32_FIND_DATA findFileInfo;
+ std::string directory;
+
+ directory = dirpath + "/" + filter;
+
+ hFind = FindFirstFile(directory.c_str(), &findFileInfo);
+
+ if (hFind == INVALID_HANDLE_VALUE)
+ return LISTFILE_DIRECTORY_NOT_FOUND;
+ do
+ {
+ if ((findFileInfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0)
+ fileList.push_back(std::string(findFileInfo.cFileName));
+ }
+ while (FindNextFile(hFind, &findFileInfo));
+
+ FindClose(hFind);
+
+ #else
+ const char *p = dirpath.c_str();
+ DIR * dirp = opendir(p);
+ struct dirent * dp;
+ dirp = opendir(p);
+
+ while (dirp)
+ {
+ errno = 0;
+ if ((dp = readdir(dirp)) != NULL)
+ {
+ if (matchWildcardFilter(filter.c_str(), dp->d_name))
+ fileList.push_back(std::string(dp->d_name));
+ }
+ else
+ break;
+ }
+
+ if (dirp)
+ closedir(dirp);
+ else
+ return LISTFILE_DIRECTORY_NOT_FOUND;
+ #endif
+
+ return LISTFILE_OK;
+ }
+
+ inline uint32 getMSTime()
+ {
+ static const ACE_Time_Value ApplicationStartTime = ACE_OS::gettimeofday();
+ return (ACE_OS::gettimeofday() - ApplicationStartTime).msec();
+ }
+
+ inline uint32 getMSTimeDiff(uint32 oldMSTime, uint32 newMSTime)
+ {
+ // getMSTime() have limited data range and this is case when it overflow in this tick
+ if (oldMSTime > newMSTime)
+ return (0xFFFFFFFF - oldMSTime) + newMSTime;
+ else
+ return newMSTime - oldMSTime;
+ }
+
+ inline uint32 GetMSTimeDiffToNow(uint32 oldMSTime)
+ {
+ return getMSTimeDiff(oldMSTime, getMSTime());
+ }
+}
+
+#endif
diff --git a/src/tools/mmaps_generator/PathGenerator.cpp b/src/tools/mmaps_generator/PathGenerator.cpp
new file mode 100644
index 00000000000..01059099f98
--- /dev/null
+++ b/src/tools/mmaps_generator/PathGenerator.cpp
@@ -0,0 +1,296 @@
+/*
+ * Copyright (C) 2005-2011 MaNGOS <http://getmangos.com/>
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "PathCommon.h"
+#include "MapBuilder.h"
+
+using namespace MMAP;
+
+bool checkDirectories(bool debugOutput)
+{
+ std::vector<std::string> dirFiles;
+
+ if (getDirContents(dirFiles, "maps") == LISTFILE_DIRECTORY_NOT_FOUND || dirFiles.empty())
+ {
+ printf("'maps' directory is empty or does not exist\n");
+ return false;
+ }
+
+ dirFiles.clear();
+ if (getDirContents(dirFiles, "vmaps", "*.vmtree") == LISTFILE_DIRECTORY_NOT_FOUND || dirFiles.empty())
+ {
+ printf("'vmaps' directory is empty or does not exist\n");
+ return false;
+ }
+
+ dirFiles.clear();
+ if (getDirContents(dirFiles, "mmaps") == LISTFILE_DIRECTORY_NOT_FOUND)
+ {
+ printf("'mmaps' directory does not exist\n");
+ return false;
+ }
+
+ dirFiles.clear();
+ if (debugOutput)
+ {
+ if (getDirContents(dirFiles, "meshes") == LISTFILE_DIRECTORY_NOT_FOUND)
+ {
+ printf("'meshes' directory does not exist (no place to put debugOutput files)\n");
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool handleArgs(int argc, char** argv,
+ int &mapnum,
+ int &tileX,
+ int &tileY,
+ float &maxAngle,
+ bool &skipLiquid,
+ bool &skipContinents,
+ bool &skipJunkMaps,
+ bool &skipBattlegrounds,
+ bool &debugOutput,
+ bool &silent,
+ bool &bigBaseUnit,
+ char* &offMeshInputPath,
+ char* &file,
+ int& threads)
+{
+ char* param = NULL;
+ for (int i = 1; i < argc; ++i)
+ {
+ if (strcmp(argv[i], "--maxAngle") == 0)
+ {
+ param = argv[++i];
+ if (!param)
+ return false;
+
+ float maxangle = atof(param);
+ if (maxangle <= 90.f && maxangle >= 45.f)
+ maxAngle = maxangle;
+ else
+ printf("invalid option for '--maxAngle', using default\n");
+ }
+ else if (strcmp(argv[i], "--threads") == 0)
+ {
+ param = argv[++i];
+ if (!param)
+ return false;
+ threads = atoi(param);
+ printf("Using %i threads to extract mmaps\n", threads);
+ }
+ else if (strcmp(argv[i], "--file") == 0)
+ {
+ param = argv[++i];
+ if (!param)
+ return false;
+ file = param;
+ }
+ else if (strcmp(argv[i], "--tile") == 0)
+ {
+ param = argv[++i];
+ if (!param)
+ return false;
+
+ char* stileX = strtok(param, ",");
+ char* stileY = strtok(NULL, ",");
+ int tilex = atoi(stileX);
+ int tiley = atoi(stileY);
+
+ if ((tilex > 0 && tilex < 64) || (tilex == 0 && strcmp(stileX, "0") == 0))
+ tileX = tilex;
+ if ((tiley > 0 && tiley < 64) || (tiley == 0 && strcmp(stileY, "0") == 0))
+ tileY = tiley;
+
+ if (tileX < 0 || tileY < 0)
+ {
+ printf("invalid tile coords.\n");
+ return false;
+ }
+ }
+ else if (strcmp(argv[i], "--skipLiquid") == 0)
+ {
+ param = argv[++i];
+ if (!param)
+ return false;
+
+ if (strcmp(param, "true") == 0)
+ skipLiquid = true;
+ else if (strcmp(param, "false") == 0)
+ skipLiquid = false;
+ else
+ printf("invalid option for '--skipLiquid', using default\n");
+ }
+ else if (strcmp(argv[i], "--skipContinents") == 0)
+ {
+ param = argv[++i];
+ if (!param)
+ return false;
+
+ if (strcmp(param, "true") == 0)
+ skipContinents = true;
+ else if (strcmp(param, "false") == 0)
+ skipContinents = false;
+ else
+ printf("invalid option for '--skipContinents', using default\n");
+ }
+ else if (strcmp(argv[i], "--skipJunkMaps") == 0)
+ {
+ param = argv[++i];
+ if (!param)
+ return false;
+
+ if (strcmp(param, "true") == 0)
+ skipJunkMaps = true;
+ else if (strcmp(param, "false") == 0)
+ skipJunkMaps = false;
+ else
+ printf("invalid option for '--skipJunkMaps', using default\n");
+ }
+ else if (strcmp(argv[i], "--skipBattlegrounds") == 0)
+ {
+ param = argv[++i];
+ if (!param)
+ return false;
+
+ if (strcmp(param, "true") == 0)
+ skipBattlegrounds = true;
+ else if (strcmp(param, "false") == 0)
+ skipBattlegrounds = false;
+ else
+ printf("invalid option for '--skipBattlegrounds', using default\n");
+ }
+ else if (strcmp(argv[i], "--debugOutput") == 0)
+ {
+ param = argv[++i];
+ if (!param)
+ return false;
+
+ if (strcmp(param, "true") == 0)
+ debugOutput = true;
+ else if (strcmp(param, "false") == 0)
+ debugOutput = false;
+ else
+ printf("invalid option for '--debugOutput', using default true\n");
+ }
+ else if (strcmp(argv[i], "--silent") == 0)
+ {
+ silent = true;
+ }
+ else if (strcmp(argv[i], "--bigBaseUnit") == 0)
+ {
+ param = argv[++i];
+ if (!param)
+ return false;
+
+ if (strcmp(param, "true") == 0)
+ bigBaseUnit = true;
+ else if (strcmp(param, "false") == 0)
+ bigBaseUnit = false;
+ else
+ printf("invalid option for '--bigBaseUnit', using default false\n");
+ }
+ else if (strcmp(argv[i], "--offMeshInput") == 0)
+ {
+ param = argv[++i];
+ if (!param)
+ return false;
+
+ offMeshInputPath = param;
+ }
+ else
+ {
+ int map = atoi(argv[i]);
+ if (map > 0 || (map == 0 && (strcmp(argv[i], "0") == 0)))
+ mapnum = map;
+ else
+ {
+ printf("invalid map id\n");
+ return false;
+ }
+ }
+ }
+
+ return true;
+}
+
+int finish(const char* message, int returnValue)
+{
+ printf("%s", message);
+ getchar();
+ return returnValue;
+}
+
+int main(int argc, char** argv)
+{
+ int threads = 3, mapnum = -1;
+ float maxAngle = 60.0f;
+ int tileX = -1, tileY = -1;
+ bool skipLiquid = false,
+ skipContinents = false,
+ skipJunkMaps = true,
+ skipBattlegrounds = false,
+ debugOutput = false,
+ silent = false,
+ bigBaseUnit = false;
+ char* offMeshInputPath = NULL;
+ char* file = NULL;
+
+ bool validParam = handleArgs(argc, argv, mapnum,
+ tileX, tileY, maxAngle,
+ skipLiquid, skipContinents, skipJunkMaps, skipBattlegrounds,
+ debugOutput, silent, bigBaseUnit, offMeshInputPath, file, threads);
+
+ if (!validParam)
+ return silent ? -1 : finish("You have specified invalid parameters", -1);
+
+ if (mapnum == -1 && debugOutput)
+ {
+ if (silent)
+ return -2;
+
+ printf("You have specifed debug output, but didn't specify a map to generate.\n");
+ printf("This will generate debug output for ALL maps.\n");
+ printf("Are you sure you want to continue? (y/n) ");
+ if (getchar() != 'y')
+ return 0;
+ }
+
+ if (!checkDirectories(debugOutput))
+ return silent ? -3 : finish("Press any key to close...", -3);
+
+ MapBuilder builder(maxAngle, skipLiquid, skipContinents, skipJunkMaps,
+ skipBattlegrounds, debugOutput, bigBaseUnit, offMeshInputPath);
+
+ uint32 start = getMSTime();
+ if (file)
+ builder.buildMeshFromFile(file);
+ else if (tileX > -1 && tileY > -1 && mapnum >= 0)
+ builder.buildSingleTile(mapnum, tileX, tileY);
+ else if (mapnum >= 0)
+ builder.buildMap(uint32(mapnum));
+ else
+ builder.buildAllMaps(threads);
+
+ if (!silent)
+ printf("Finished. MMAPS were built in %u ms!\n", GetMSTimeDiffToNow(start));
+ return 1;
+}
diff --git a/src/tools/mmaps_generator/TerrainBuilder.cpp b/src/tools/mmaps_generator/TerrainBuilder.cpp
new file mode 100644
index 00000000000..a42cd2b9bc6
--- /dev/null
+++ b/src/tools/mmaps_generator/TerrainBuilder.cpp
@@ -0,0 +1,933 @@
+/*
+ * Copyright (C) 2008-2013 TrinityCore <http://www.trinitycore.org/>
+ * Copyright (C) 2005-2011 MaNGOS <http://getmangos.com/>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include "TerrainBuilder.h"
+
+#include "PathCommon.h"
+#include "MapBuilder.h"
+
+#include "VMapManager2.h"
+#include "MapTree.h"
+#include "ModelInstance.h"
+#include <vector>
+
+// ******************************************
+// Map file format defines
+// ******************************************
+struct map_fileheader
+{
+ uint32 mapMagic;
+ uint32 versionMagic;
+ uint32 buildMagic;
+ uint32 areaMapOffset;
+ uint32 areaMapSize;
+ uint32 heightMapOffset;
+ uint32 heightMapSize;
+ uint32 liquidMapOffset;
+ uint32 liquidMapSize;
+ uint32 holesOffset;
+ uint32 holesSize;
+};
+
+#define MAP_HEIGHT_NO_HEIGHT 0x0001
+#define MAP_HEIGHT_AS_INT16 0x0002
+#define MAP_HEIGHT_AS_INT8 0x0004
+
+struct map_heightHeader
+{
+ uint32 fourcc;
+ uint32 flags;
+ float gridHeight;
+ float gridMaxHeight;
+};
+
+#define MAP_LIQUID_NO_TYPE 0x0001
+#define MAP_LIQUID_NO_HEIGHT 0x0002
+
+struct map_liquidHeader
+{
+ uint32 fourcc;
+ uint16 flags;
+ uint16 liquidType;
+ uint8 offsetX;
+ uint8 offsetY;
+ uint8 width;
+ uint8 height;
+ float liquidLevel;
+};
+
+#define MAP_LIQUID_TYPE_NO_WATER 0x00
+#define MAP_LIQUID_TYPE_WATER 0x01
+#define MAP_LIQUID_TYPE_OCEAN 0x02
+#define MAP_LIQUID_TYPE_MAGMA 0x04
+#define MAP_LIQUID_TYPE_SLIME 0x08
+#define MAP_LIQUID_TYPE_DARK_WATER 0x10
+#define MAP_LIQUID_TYPE_WMO_WATER 0x20
+
+namespace MMAP
+{
+
+ char const* MAP_VERSION_MAGIC = "v1.3";
+
+ TerrainBuilder::TerrainBuilder(bool skipLiquid) : m_skipLiquid (skipLiquid){ }
+ TerrainBuilder::~TerrainBuilder() { }
+
+ /**************************************************************************/
+ void TerrainBuilder::getLoopVars(Spot portion, int &loopStart, int &loopEnd, int &loopInc)
+ {
+ switch (portion)
+ {
+ case ENTIRE:
+ loopStart = 0;
+ loopEnd = V8_SIZE_SQ;
+ loopInc = 1;
+ break;
+ case TOP:
+ loopStart = 0;
+ loopEnd = V8_SIZE;
+ loopInc = 1;
+ break;
+ case LEFT:
+ loopStart = 0;
+ loopEnd = V8_SIZE_SQ - V8_SIZE + 1;
+ loopInc = V8_SIZE;
+ break;
+ case RIGHT:
+ loopStart = V8_SIZE - 1;
+ loopEnd = V8_SIZE_SQ;
+ loopInc = V8_SIZE;
+ break;
+ case BOTTOM:
+ loopStart = V8_SIZE_SQ - V8_SIZE;
+ loopEnd = V8_SIZE_SQ;
+ loopInc = 1;
+ break;
+ }
+ }
+
+ /**************************************************************************/
+ void TerrainBuilder::loadMap(uint32 mapID, uint32 tileX, uint32 tileY, MeshData &meshData)
+ {
+ if (loadMap(mapID, tileX, tileY, meshData, ENTIRE))
+ {
+ loadMap(mapID, tileX+1, tileY, meshData, LEFT);
+ loadMap(mapID, tileX-1, tileY, meshData, RIGHT);
+ loadMap(mapID, tileX, tileY+1, meshData, TOP);
+ loadMap(mapID, tileX, tileY-1, meshData, BOTTOM);
+ }
+ }
+
+ /**************************************************************************/
+ bool TerrainBuilder::loadMap(uint32 mapID, uint32 tileX, uint32 tileY, MeshData &meshData, Spot portion)
+ {
+ char mapFileName[255];
+ sprintf(mapFileName, "maps/%03u%02u%02u.map", mapID, tileY, tileX);
+
+ FILE* mapFile = fopen(mapFileName, "rb");
+ if (!mapFile)
+ return false;
+
+ map_fileheader fheader;
+ if (fread(&fheader, sizeof(map_fileheader), 1, mapFile) != 1 ||
+ fheader.versionMagic != *((uint32 const*)(MAP_VERSION_MAGIC)))
+ {
+ fclose(mapFile);
+ printf("%s is the wrong version, please extract new .map files\n", mapFileName);
+ return false;
+ }
+
+ map_heightHeader hheader;
+ fseek(mapFile, fheader.heightMapOffset, SEEK_SET);
+
+ bool haveTerrain = false;
+ bool haveLiquid = false;
+ if (fread(&hheader, sizeof(map_heightHeader), 1, mapFile) == 1)
+ {
+ haveTerrain = !(hheader.flags & MAP_HEIGHT_NO_HEIGHT);
+ haveLiquid = fheader.liquidMapOffset && !m_skipLiquid;
+ }
+
+ // no data in this map file
+ if (!haveTerrain && !haveLiquid)
+ {
+ fclose(mapFile);
+ return false;
+ }
+
+ // data used later
+ uint16 holes[16][16];
+ memset(holes, 0, sizeof(holes));
+ uint8 liquid_type[16][16];
+ memset(liquid_type, 0, sizeof(liquid_type));
+ G3D::Array<int> ltriangles;
+ G3D::Array<int> ttriangles;
+
+ // terrain data
+ if (haveTerrain)
+ {
+ float heightMultiplier;
+ float V9[V9_SIZE_SQ], V8[V8_SIZE_SQ];
+ int expected = V9_SIZE_SQ + V8_SIZE_SQ;
+
+ if (hheader.flags & MAP_HEIGHT_AS_INT8)
+ {
+ uint8 v9[V9_SIZE_SQ];
+ uint8 v8[V8_SIZE_SQ];
+ int count = 0;
+ count += fread(v9, sizeof(uint8), V9_SIZE_SQ, mapFile);
+ count += fread(v8, sizeof(uint8), V8_SIZE_SQ, mapFile);
+ if (count != expected)
+ printf("TerrainBuilder::loadMap: Failed to read some data expected %d, read %d\n", expected, count);
+
+ heightMultiplier = (hheader.gridMaxHeight - hheader.gridHeight) / 255;
+
+ for (int i = 0; i < V9_SIZE_SQ; ++i)
+ V9[i] = (float)v9[i]*heightMultiplier + hheader.gridHeight;
+
+ for (int i = 0; i < V8_SIZE_SQ; ++i)
+ V8[i] = (float)v8[i]*heightMultiplier + hheader.gridHeight;
+ }
+ else if (hheader.flags & MAP_HEIGHT_AS_INT16)
+ {
+ uint16 v9[V9_SIZE_SQ];
+ uint16 v8[V8_SIZE_SQ];
+ int count = 0;
+ count += fread(v9, sizeof(uint16), V9_SIZE_SQ, mapFile);
+ count += fread(v8, sizeof(uint16), V8_SIZE_SQ, mapFile);
+ if (count != expected)
+ printf("TerrainBuilder::loadMap: Failed to read some data expected %d, read %d\n", expected, count);
+
+ heightMultiplier = (hheader.gridMaxHeight - hheader.gridHeight) / 65535;
+
+ for (int i = 0; i < V9_SIZE_SQ; ++i)
+ V9[i] = (float)v9[i]*heightMultiplier + hheader.gridHeight;
+
+ for (int i = 0; i < V8_SIZE_SQ; ++i)
+ V8[i] = (float)v8[i]*heightMultiplier + hheader.gridHeight;
+ }
+ else
+ {
+ int count = 0;
+ count += fread(V9, sizeof(float), V9_SIZE_SQ, mapFile);
+ count += fread(V8, sizeof(float), V8_SIZE_SQ, mapFile);
+ if (count != expected)
+ printf("TerrainBuilder::loadMap: Failed to read some data expected %d, read %d\n", expected, count);
+ }
+
+ // hole data
+ if (fheader.holesSize != 0)
+ {
+ memset(holes, 0, fheader.holesSize);
+ fseek(mapFile, fheader.holesOffset, SEEK_SET);
+ if (fread(holes, fheader.holesSize, 1, mapFile) != 1)
+ printf("TerrainBuilder::loadMap: Failed to read some data expected 1, read 0\n");
+ }
+
+ int count = meshData.solidVerts.size() / 3;
+ float xoffset = (float(tileX)-32)*GRID_SIZE;
+ float yoffset = (float(tileY)-32)*GRID_SIZE;
+
+ float coord[3];
+
+ for (int i = 0; i < V9_SIZE_SQ; ++i)
+ {
+ getHeightCoord(i, GRID_V9, xoffset, yoffset, coord, V9);
+ meshData.solidVerts.append(coord[0]);
+ meshData.solidVerts.append(coord[2]);
+ meshData.solidVerts.append(coord[1]);
+ }
+
+ for (int i = 0; i < V8_SIZE_SQ; ++i)
+ {
+ getHeightCoord(i, GRID_V8, xoffset, yoffset, coord, V8);
+ meshData.solidVerts.append(coord[0]);
+ meshData.solidVerts.append(coord[2]);
+ meshData.solidVerts.append(coord[1]);
+ }
+
+ int indices[3], loopStart = 0, loopEnd = 0, loopInc = 0;
+ getLoopVars(portion, loopStart, loopEnd, loopInc);
+ for (int i = loopStart; i < loopEnd; i+=loopInc)
+ for (int j = TOP; j <= BOTTOM; j+=1)
+ {
+ getHeightTriangle(i, Spot(j), indices);
+ ttriangles.append(indices[2] + count);
+ ttriangles.append(indices[1] + count);
+ ttriangles.append(indices[0] + count);
+ }
+ }
+
+ // liquid data
+ if (haveLiquid)
+ {
+ map_liquidHeader lheader;
+ fseek(mapFile, fheader.liquidMapOffset, SEEK_SET);
+ if (fread(&lheader, sizeof(map_liquidHeader), 1, mapFile) != 1)
+ printf("TerrainBuilder::loadMap: Failed to read some data expected 1, read 0\n");
+
+
+ float* liquid_map = NULL;
+
+ if (!(lheader.flags & MAP_LIQUID_NO_TYPE))
+ if (fread(liquid_type, sizeof(liquid_type), 1, mapFile) != 1)
+ printf("TerrainBuilder::loadMap: Failed to read some data expected 1, read 0\n");
+
+ if (!(lheader.flags & MAP_LIQUID_NO_HEIGHT))
+ {
+ uint32 toRead = lheader.width * lheader.height;
+ liquid_map = new float [toRead];
+ if (fread(liquid_map, sizeof(float), toRead, mapFile) != toRead)
+ printf("TerrainBuilder::loadMap: Failed to read some data expected 1, read 0\n");
+ }
+
+ if (liquid_map)
+ {
+ int count = meshData.liquidVerts.size() / 3;
+ float xoffset = (float(tileX)-32)*GRID_SIZE;
+ float yoffset = (float(tileY)-32)*GRID_SIZE;
+
+ float coord[3];
+ int row, col;
+
+ // generate coordinates
+ if (!(lheader.flags & MAP_LIQUID_NO_HEIGHT))
+ {
+ int j = 0;
+ for (int i = 0; i < V9_SIZE_SQ; ++i)
+ {
+ row = i / V9_SIZE;
+ col = i % V9_SIZE;
+
+ if (row < lheader.offsetY || row >= lheader.offsetY + lheader.height ||
+ col < lheader.offsetX || col >= lheader.offsetX + lheader.width)
+ {
+ // dummy vert using invalid height
+ meshData.liquidVerts.append((xoffset+col*GRID_PART_SIZE)*-1, INVALID_MAP_LIQ_HEIGHT, (yoffset+row*GRID_PART_SIZE)*-1);
+ continue;
+ }
+
+ getLiquidCoord(i, j, xoffset, yoffset, coord, liquid_map);
+ meshData.liquidVerts.append(coord[0]);
+ meshData.liquidVerts.append(coord[2]);
+ meshData.liquidVerts.append(coord[1]);
+ j++;
+ }
+ }
+ else
+ {
+ for (int i = 0; i < V9_SIZE_SQ; ++i)
+ {
+ row = i / V9_SIZE;
+ col = i % V9_SIZE;
+ meshData.liquidVerts.append((xoffset+col*GRID_PART_SIZE)*-1, lheader.liquidLevel, (yoffset+row*GRID_PART_SIZE)*-1);
+ }
+ }
+
+ delete [] liquid_map;
+
+ int indices[3], loopStart = 0, loopEnd = 0, loopInc = 0, triInc = BOTTOM-TOP;
+ getLoopVars(portion, loopStart, loopEnd, loopInc);
+
+ // generate triangles
+ for (int i = loopStart; i < loopEnd; i+=loopInc)
+ for (int j = TOP; j <= BOTTOM; j+= triInc)
+ {
+ getHeightTriangle(i, Spot(j), indices, true);
+ ltriangles.append(indices[2] + count);
+ ltriangles.append(indices[1] + count);
+ ltriangles.append(indices[0] + count);
+ }
+ }
+ }
+
+ fclose(mapFile);
+
+ // now that we have gathered the data, we can figure out which parts to keep:
+ // liquid above ground, ground above liquid
+ int loopStart = 0, loopEnd = 0, loopInc = 0, tTriCount = 4;
+ bool useTerrain, useLiquid;
+
+ float* lverts = meshData.liquidVerts.getCArray();
+ int* ltris = ltriangles.getCArray();
+
+ float* tverts = meshData.solidVerts.getCArray();
+ int* ttris = ttriangles.getCArray();
+
+ if ((ltriangles.size() + ttriangles.size()) == 0)
+ return false;
+
+ // make a copy of liquid vertices
+ // used to pad right-bottom frame due to lost vertex data at extraction
+ float* lverts_copy = NULL;
+ if (meshData.liquidVerts.size())
+ {
+ lverts_copy = new float[meshData.liquidVerts.size()];
+ memcpy(lverts_copy, lverts, sizeof(float)*meshData.liquidVerts.size());
+ }
+
+ getLoopVars(portion, loopStart, loopEnd, loopInc);
+ for (int i = loopStart; i < loopEnd; i+=loopInc)
+ {
+ for (int j = 0; j < 2; ++j)
+ {
+ // default is true, will change to false if needed
+ useTerrain = true;
+ useLiquid = true;
+ uint8 liquidType = MAP_LIQUID_TYPE_NO_WATER;
+ // FIXME: "warning: the address of ‘liquid_type’ will always evaluate as ‘true’"
+
+ // if there is no liquid, don't use liquid
+ if (!meshData.liquidVerts.size() || !ltriangles.size())
+ useLiquid = false;
+ else
+ {
+ liquidType = getLiquidType(i, liquid_type);
+ switch (liquidType)
+ {
+ default:
+ useLiquid = false;
+ break;
+ case MAP_LIQUID_TYPE_WATER:
+ case MAP_LIQUID_TYPE_OCEAN:
+ // merge different types of water
+ liquidType = NAV_WATER;
+ break;
+ case MAP_LIQUID_TYPE_MAGMA:
+ liquidType = NAV_MAGMA;
+ break;
+ case MAP_LIQUID_TYPE_SLIME:
+ liquidType = NAV_SLIME;
+ break;
+ case MAP_LIQUID_TYPE_DARK_WATER:
+ // players should not be here, so logically neither should creatures
+ useTerrain = false;
+ useLiquid = false;
+ break;
+ }
+ }
+
+ // if there is no terrain, don't use terrain
+ if (!ttriangles.size())
+ useTerrain = false;
+
+ // while extracting ADT data we are losing right-bottom vertices
+ // this code adds fair approximation of lost data
+ if (useLiquid)
+ {
+ float quadHeight = 0;
+ uint32 validCount = 0;
+ for(uint32 idx = 0; idx < 3; idx++)
+ {
+ float h = lverts_copy[ltris[idx]*3 + 1];
+ if (h != INVALID_MAP_LIQ_HEIGHT && h < INVALID_MAP_LIQ_HEIGHT_MAX)
+ {
+ quadHeight += h;
+ validCount++;
+ }
+ }
+
+ // update vertex height data
+ if (validCount > 0 && validCount < 3)
+ {
+ quadHeight /= validCount;
+ for(uint32 idx = 0; idx < 3; idx++)
+ {
+ float h = lverts[ltris[idx]*3 + 1];
+ if (h == INVALID_MAP_LIQ_HEIGHT || h > INVALID_MAP_LIQ_HEIGHT_MAX)
+ lverts[ltris[idx]*3 + 1] = quadHeight;
+ }
+ }
+
+ // no valid vertexes - don't use this poly at all
+ if (validCount == 0)
+ useLiquid = false;
+ }
+
+ // if there is a hole here, don't use the terrain
+ if (useTerrain && fheader.holesSize != 0)
+ useTerrain = !isHole(i, holes);
+
+ // we use only one terrain kind per quad - pick higher one
+ if (useTerrain && useLiquid)
+ {
+ float minLLevel = INVALID_MAP_LIQ_HEIGHT_MAX;
+ float maxLLevel = INVALID_MAP_LIQ_HEIGHT;
+ for(uint32 x = 0; x < 3; x++)
+ {
+ float h = lverts[ltris[x]*3 + 1];
+ if (minLLevel > h)
+ minLLevel = h;
+
+ if (maxLLevel < h)
+ maxLLevel = h;
+ }
+
+ float maxTLevel = INVALID_MAP_LIQ_HEIGHT;
+ float minTLevel = INVALID_MAP_LIQ_HEIGHT_MAX;
+ for(uint32 x = 0; x < 6; x++)
+ {
+ float h = tverts[ttris[x]*3 + 1];
+ if (maxTLevel < h)
+ maxTLevel = h;
+
+ if (minTLevel > h)
+ minTLevel = h;
+ }
+
+ // terrain under the liquid?
+ if (minLLevel > maxTLevel)
+ useTerrain = false;
+
+ //liquid under the terrain?
+ if (minTLevel > maxLLevel)
+ useLiquid = false;
+ }
+
+ // store the result
+ if (useLiquid)
+ {
+ meshData.liquidType.append(liquidType);
+ for (int k = 0; k < 3; ++k)
+ meshData.liquidTris.append(ltris[k]);
+ }
+
+ if (useTerrain)
+ for (int k = 0; k < 3*tTriCount/2; ++k)
+ meshData.solidTris.append(ttris[k]);
+
+ // advance to next set of triangles
+ ltris += 3;
+ ttris += 3*tTriCount/2;
+ }
+ }
+
+ if (lverts_copy)
+ delete [] lverts_copy;
+
+ return meshData.solidTris.size() || meshData.liquidTris.size();
+ }
+
+ /**************************************************************************/
+ void TerrainBuilder::getHeightCoord(int index, Grid grid, float xOffset, float yOffset, float* coord, float* v)
+ {
+ // wow coords: x, y, height
+ // coord is mirroed about the horizontal axes
+ switch (grid)
+ {
+ case GRID_V9:
+ coord[0] = (xOffset + index%(V9_SIZE)*GRID_PART_SIZE) * -1.f;
+ coord[1] = (yOffset + (int)(index/(V9_SIZE))*GRID_PART_SIZE) * -1.f;
+ coord[2] = v[index];
+ break;
+ case GRID_V8:
+ coord[0] = (xOffset + index%(V8_SIZE)*GRID_PART_SIZE + GRID_PART_SIZE/2.f) * -1.f;
+ coord[1] = (yOffset + (int)(index/(V8_SIZE))*GRID_PART_SIZE + GRID_PART_SIZE/2.f) * -1.f;
+ coord[2] = v[index];
+ break;
+ }
+ }
+
+ /**************************************************************************/
+ void TerrainBuilder::getHeightTriangle(int square, Spot triangle, int* indices, bool liquid/* = false*/)
+ {
+ int rowOffset = square/V8_SIZE;
+ if (!liquid)
+ switch (triangle)
+ {
+ case TOP:
+ indices[0] = square+rowOffset; // 0-----1 .... 128
+ indices[1] = square+1+rowOffset; // |\ T /|
+ indices[2] = (V9_SIZE_SQ)+square; // | \ / |
+ break; // |L 0 R| .. 127
+ case LEFT: // | / \ |
+ indices[0] = square+rowOffset; // |/ B \|
+ indices[1] = (V9_SIZE_SQ)+square; // 129---130 ... 386
+ indices[2] = square+V9_SIZE+rowOffset; // |\ /|
+ break; // | \ / |
+ case RIGHT: // | 128 | .. 255
+ indices[0] = square+1+rowOffset; // | / \ |
+ indices[1] = square+V9_SIZE+1+rowOffset; // |/ \|
+ indices[2] = (V9_SIZE_SQ)+square; // 258---259 ... 515
+ break;
+ case BOTTOM:
+ indices[0] = (V9_SIZE_SQ)+square;
+ indices[1] = square+V9_SIZE+1+rowOffset;
+ indices[2] = square+V9_SIZE+rowOffset;
+ break;
+ default: break;
+ }
+ else
+ switch (triangle)
+ { // 0-----1 .... 128
+ case TOP: // |\ |
+ indices[0] = square+rowOffset; // | \ T |
+ indices[1] = square+1+rowOffset; // | \ |
+ indices[2] = square+V9_SIZE+1+rowOffset; // | B \ |
+ break; // | \|
+ case BOTTOM: // 129---130 ... 386
+ indices[0] = square+rowOffset; // |\ |
+ indices[1] = square+V9_SIZE+1+rowOffset; // | \ |
+ indices[2] = square+V9_SIZE+rowOffset; // | \ |
+ break; // | \ |
+ default: break; // | \|
+ } // 258---259 ... 515
+
+ }
+
+ /**************************************************************************/
+ void TerrainBuilder::getLiquidCoord(int index, int index2, float xOffset, float yOffset, float* coord, float* v)
+ {
+ // wow coords: x, y, height
+ // coord is mirroed about the horizontal axes
+ coord[0] = (xOffset + index%(V9_SIZE)*GRID_PART_SIZE) * -1.f;
+ coord[1] = (yOffset + (int)(index/(V9_SIZE))*GRID_PART_SIZE) * -1.f;
+ coord[2] = v[index2];
+ }
+
+ static uint16 holetab_h[4] = {0x1111, 0x2222, 0x4444, 0x8888};
+ static uint16 holetab_v[4] = {0x000F, 0x00F0, 0x0F00, 0xF000};
+
+ /**************************************************************************/
+ bool TerrainBuilder::isHole(int square, const uint16 holes[16][16])
+ {
+ int row = square / 128;
+ int col = square % 128;
+ int cellRow = row / 8; // 8 squares per cell
+ int cellCol = col / 8;
+ int holeRow = row % 8 / 2;
+ int holeCol = (square - (row * 128 + cellCol * 8)) / 2;
+
+ uint16 hole = holes[cellRow][cellCol];
+
+ return (hole & holetab_h[holeCol] & holetab_v[holeRow]) != 0;
+ }
+
+ /**************************************************************************/
+ uint8 TerrainBuilder::getLiquidType(int square, const uint8 liquid_type[16][16])
+ {
+ int row = square / 128;
+ int col = square % 128;
+ int cellRow = row / 8; // 8 squares per cell
+ int cellCol = col / 8;
+
+ return liquid_type[cellRow][cellCol];
+ }
+
+ /**************************************************************************/
+ bool TerrainBuilder::loadVMap(uint32 mapID, uint32 tileX, uint32 tileY, MeshData &meshData)
+ {
+ IVMapManager* vmapManager = new VMapManager2();
+ int result = vmapManager->loadMap("vmaps", mapID, tileX, tileY);
+ bool retval = false;
+
+ do
+ {
+ if (result == VMAP_LOAD_RESULT_ERROR)
+ break;
+
+ InstanceTreeMap instanceTrees;
+ ((VMapManager2*)vmapManager)->getInstanceMapTree(instanceTrees);
+
+ if (!instanceTrees[mapID])
+ break;
+
+ ModelInstance* models = NULL;
+ uint32 count = 0;
+ instanceTrees[mapID]->getModelInstances(models, count);
+
+ if (!models)
+ break;
+
+ for (uint32 i = 0; i < count; ++i)
+ {
+ ModelInstance instance = models[i];
+
+ // model instances exist in tree even though there are instances of that model in this tile
+ WorldModel* worldModel = instance.getWorldModel();
+ if (!worldModel)
+ continue;
+
+ // now we have a model to add to the meshdata
+ retval = true;
+
+ std::vector<GroupModel> groupModels;
+ worldModel->getGroupModels(groupModels);
+
+ // all M2s need to have triangle indices reversed
+ bool isM2 = instance.name.find(".m2") != std::string::npos || instance.name.find(".M2") != std::string::npos;
+
+ // transform data
+ float scale = instance.iScale;
+ G3D::Matrix3 rotation = G3D::Matrix3::fromEulerAnglesXYZ(G3D::pi()*instance.iRot.z/-180.f, G3D::pi()*instance.iRot.x/-180.f, G3D::pi()*instance.iRot.y/-180.f);
+ Vector3 position = instance.iPos;
+ position.x -= 32*GRID_SIZE;
+ position.y -= 32*GRID_SIZE;
+
+ for (std::vector<GroupModel>::iterator it = groupModels.begin(); it != groupModels.end(); ++it)
+ {
+ std::vector<Vector3> tempVertices;
+ std::vector<Vector3> transformedVertices;
+ std::vector<MeshTriangle> tempTriangles;
+ WmoLiquid* liquid = NULL;
+
+ it->getMeshData(tempVertices, tempTriangles, liquid);
+
+ // first handle collision mesh
+ transform(tempVertices, transformedVertices, scale, rotation, position);
+
+ int offset = meshData.solidVerts.size() / 3;
+
+ copyVertices(transformedVertices, meshData.solidVerts);
+ copyIndices(tempTriangles, meshData.solidTris, offset, isM2);
+
+ // now handle liquid data
+ if (liquid)
+ {
+ std::vector<Vector3> liqVerts;
+ std::vector<int> liqTris;
+ uint32 tilesX, tilesY, vertsX, vertsY;
+ Vector3 corner;
+ liquid->getPosInfo(tilesX, tilesY, corner);
+ vertsX = tilesX + 1;
+ vertsY = tilesY + 1;
+ uint8* flags = liquid->GetFlagsStorage();
+ float* data = liquid->GetHeightStorage();
+ uint8 type = NAV_EMPTY;
+
+ // convert liquid type to NavTerrain
+ switch (liquid->GetType())
+ {
+ case 0:
+ case 1:
+ type = NAV_WATER;
+ break;
+ case 2:
+ type = NAV_MAGMA;
+ break;
+ case 3:
+ type = NAV_SLIME;
+ break;
+ }
+
+ // indexing is weird...
+ // after a lot of trial and error, this is what works:
+ // vertex = y*vertsX+x
+ // tile = x*tilesY+y
+ // flag = y*tilesY+x
+
+ Vector3 vert;
+ for (uint32 x = 0; x < vertsX; ++x)
+ for (uint32 y = 0; y < vertsY; ++y)
+ {
+ vert = Vector3(corner.x + x * GRID_PART_SIZE, corner.y + y * GRID_PART_SIZE, data[y*vertsX + x]);
+ vert = vert * rotation * scale + position;
+ vert.x *= -1.f;
+ vert.y *= -1.f;
+ liqVerts.push_back(vert);
+ }
+
+ int idx1, idx2, idx3, idx4;
+ uint32 square;
+ for (uint32 x = 0; x < tilesX; ++x)
+ for (uint32 y = 0; y < tilesY; ++y)
+ if ((flags[x+y*tilesX] & 0x0f) != 0x0f)
+ {
+ square = x * tilesY + y;
+ idx1 = square+x;
+ idx2 = square+1+x;
+ idx3 = square+tilesY+1+1+x;
+ idx4 = square+tilesY+1+x;
+
+ // top triangle
+ liqTris.push_back(idx3);
+ liqTris.push_back(idx2);
+ liqTris.push_back(idx1);
+ // bottom triangle
+ liqTris.push_back(idx4);
+ liqTris.push_back(idx3);
+ liqTris.push_back(idx1);
+ }
+
+ uint32 liqOffset = meshData.liquidVerts.size() / 3;
+ for (uint32 i = 0; i < liqVerts.size(); ++i)
+ meshData.liquidVerts.append(liqVerts[i].y, liqVerts[i].z, liqVerts[i].x);
+
+ for (uint32 i = 0; i < liqTris.size() / 3; ++i)
+ {
+ meshData.liquidTris.append(liqTris[i*3+1] + liqOffset, liqTris[i*3+2] + liqOffset, liqTris[i*3] + liqOffset);
+ meshData.liquidType.append(type);
+ }
+ }
+ }
+ }
+ }
+ while (false);
+
+ vmapManager->unloadMap(mapID, tileX, tileY);
+ delete vmapManager;
+
+ return retval;
+ }
+
+ /**************************************************************************/
+ void TerrainBuilder::transform(std::vector<Vector3> &source, std::vector<Vector3> &transformedVertices, float scale, G3D::Matrix3 &rotation, Vector3 &position)
+ {
+ for (std::vector<Vector3>::iterator it = source.begin(); it != source.end(); ++it)
+ {
+ // apply tranform, then mirror along the horizontal axes
+ Vector3 v((*it) * rotation * scale + position);
+ v.x *= -1.f;
+ v.y *= -1.f;
+ transformedVertices.push_back(v);
+ }
+ }
+
+ /**************************************************************************/
+ void TerrainBuilder::copyVertices(std::vector<Vector3> &source, G3D::Array<float> &dest)
+ {
+ for (std::vector<Vector3>::iterator it = source.begin(); it != source.end(); ++it)
+ {
+ dest.push_back((*it).y);
+ dest.push_back((*it).z);
+ dest.push_back((*it).x);
+ }
+ }
+
+ /**************************************************************************/
+ void TerrainBuilder::copyIndices(std::vector<MeshTriangle> &source, G3D::Array<int> &dest, int offset, bool flip)
+ {
+ if (flip)
+ {
+ for (std::vector<MeshTriangle>::iterator it = source.begin(); it != source.end(); ++it)
+ {
+ dest.push_back((*it).idx2+offset);
+ dest.push_back((*it).idx1+offset);
+ dest.push_back((*it).idx0+offset);
+ }
+ }
+ else
+ {
+ for (std::vector<MeshTriangle>::iterator it = source.begin(); it != source.end(); ++it)
+ {
+ dest.push_back((*it).idx0+offset);
+ dest.push_back((*it).idx1+offset);
+ dest.push_back((*it).idx2+offset);
+ }
+ }
+ }
+
+ /**************************************************************************/
+ void TerrainBuilder::copyIndices(G3D::Array<int> &source, G3D::Array<int> &dest, int offset)
+ {
+ int* src = source.getCArray();
+ for (int32 i = 0; i < source.size(); ++i)
+ dest.append(src[i] + offset);
+ }
+
+ /**************************************************************************/
+ void TerrainBuilder::cleanVertices(G3D::Array<float> &verts, G3D::Array<int> &tris)
+ {
+ std::map<int, int> vertMap;
+
+ int* t = tris.getCArray();
+ float* v = verts.getCArray();
+
+ G3D::Array<float> cleanVerts;
+ int index, count = 0;
+ // collect all the vertex indices from triangle
+ for (int i = 0; i < tris.size(); ++i)
+ {
+ if (vertMap.find(t[i]) != vertMap.end())
+ continue;
+ std::pair<int, int> val;
+ val.first = t[i];
+
+ index = val.first;
+ val.second = count;
+
+ vertMap.insert(val);
+ cleanVerts.append(v[index * 3], v[index * 3 + 1], v[index * 3 + 2]);
+ count++;
+ }
+
+ verts.fastClear();
+ verts.append(cleanVerts);
+ cleanVerts.clear();
+
+ // update triangles to use new indices
+ for (int i = 0; i < tris.size(); ++i)
+ {
+ std::map<int, int>::iterator it;
+ if ((it = vertMap.find(t[i])) == vertMap.end())
+ continue;
+
+ t[i] = (*it).second;
+ }
+
+ vertMap.clear();
+ }
+
+ /**************************************************************************/
+ void TerrainBuilder::loadOffMeshConnections(uint32 mapID, uint32 tileX, uint32 tileY, MeshData &meshData, const char* offMeshFilePath)
+ {
+ // no meshfile input given?
+ if (offMeshFilePath == NULL)
+ return;
+
+ FILE* fp = fopen(offMeshFilePath, "rb");
+ if (!fp)
+ {
+ printf(" loadOffMeshConnections:: input file %s not found!\n", offMeshFilePath);
+ return;
+ }
+
+ // pretty silly thing, as we parse entire file and load only the tile we need
+ // but we don't expect this file to be too large
+ char* buf = new char[512];
+ while(fgets(buf, 512, fp))
+ {
+ float p0[3], p1[3];
+ uint32 mid, tx, ty;
+ float size;
+ if (sscanf(buf, "%d %d,%d (%f %f %f) (%f %f %f) %f", &mid, &tx, &ty,
+ &p0[0], &p0[1], &p0[2], &p1[0], &p1[1], &p1[2], &size) != 10)
+ continue;
+
+ if (mapID == mid && tileX == tx && tileY == ty)
+ {
+ meshData.offMeshConnections.append(p0[1]);
+ meshData.offMeshConnections.append(p0[2]);
+ meshData.offMeshConnections.append(p0[0]);
+
+ meshData.offMeshConnections.append(p1[1]);
+ meshData.offMeshConnections.append(p1[2]);
+ meshData.offMeshConnections.append(p1[0]);
+
+ meshData.offMeshConnectionDirs.append(1); // 1 - both direction, 0 - one sided
+ meshData.offMeshConnectionRads.append(size); // agent size equivalent
+ // can be used same way as polygon flags
+ meshData.offMeshConnectionsAreas.append((unsigned char)0xFF);
+ meshData.offMeshConnectionsFlags.append((unsigned short)0xFF); // all movement masks can make this path
+ }
+
+ }
+
+ delete [] buf;
+ fclose(fp);
+ }
+}
diff --git a/src/tools/mmaps_generator/TerrainBuilder.h b/src/tools/mmaps_generator/TerrainBuilder.h
new file mode 100644
index 00000000000..069a5a94c84
--- /dev/null
+++ b/src/tools/mmaps_generator/TerrainBuilder.h
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2008-2013 TrinityCore <http://www.trinitycore.org/>
+ * Copyright (C) 2005-2011 MaNGOS <http://getmangos.com/>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _MMAP_TERRAIN_BUILDER_H
+#define _MMAP_TERRAIN_BUILDER_H
+
+#include "PathCommon.h"
+#include "WorldModel.h"
+
+#include "G3D/Array.h"
+#include "G3D/Vector3.h"
+#include "G3D/Matrix3.h"
+
+namespace MMAP
+{
+ enum Spot
+ {
+ TOP = 1,
+ RIGHT = 2,
+ LEFT = 3,
+ BOTTOM = 4,
+ ENTIRE = 5
+ };
+
+ enum Grid
+ {
+ GRID_V8,
+ GRID_V9
+ };
+
+ static const int V9_SIZE = 129;
+ static const int V9_SIZE_SQ = V9_SIZE*V9_SIZE;
+ static const int V8_SIZE = 128;
+ static const int V8_SIZE_SQ = V8_SIZE*V8_SIZE;
+ static const float GRID_SIZE = 533.33333f;
+ static const float GRID_PART_SIZE = GRID_SIZE/V8_SIZE;
+
+ // see contrib/extractor/system.cpp, CONF_use_minHeight
+ static const float INVALID_MAP_LIQ_HEIGHT = -500.f;
+ static const float INVALID_MAP_LIQ_HEIGHT_MAX = 5000.0f;
+
+ // see following files:
+ // contrib/extractor/system.cpp
+ // src/game/Map.cpp
+
+ struct MeshData
+ {
+ G3D::Array<float> solidVerts;
+ G3D::Array<int> solidTris;
+
+ G3D::Array<float> liquidVerts;
+ G3D::Array<int> liquidTris;
+ G3D::Array<uint8> liquidType;
+
+ // offmesh connection data
+ G3D::Array<float> offMeshConnections; // [p0y,p0z,p0x,p1y,p1z,p1x] - per connection
+ G3D::Array<float> offMeshConnectionRads;
+ G3D::Array<unsigned char> offMeshConnectionDirs;
+ G3D::Array<unsigned char> offMeshConnectionsAreas;
+ G3D::Array<unsigned short> offMeshConnectionsFlags;
+ };
+
+ class TerrainBuilder
+ {
+ public:
+ TerrainBuilder(bool skipLiquid);
+ ~TerrainBuilder();
+
+ void loadMap(uint32 mapID, uint32 tileX, uint32 tileY, MeshData &meshData);
+ bool loadVMap(uint32 mapID, uint32 tileX, uint32 tileY, MeshData &meshData);
+ void loadOffMeshConnections(uint32 mapID, uint32 tileX, uint32 tileY, MeshData &meshData, const char* offMeshFilePath);
+
+ bool usesLiquids() { return !m_skipLiquid; }
+
+ // vert and triangle methods
+ static void transform(std::vector<G3D::Vector3> &original, std::vector<G3D::Vector3> &transformed,
+ float scale, G3D::Matrix3 &rotation, G3D::Vector3 &position);
+ static void copyVertices(std::vector<G3D::Vector3> &source, G3D::Array<float> &dest);
+ static void copyIndices(std::vector<VMAP::MeshTriangle> &source, G3D::Array<int> &dest, int offest, bool flip);
+ static void copyIndices(G3D::Array<int> &src, G3D::Array<int> &dest, int offset);
+ static void cleanVertices(G3D::Array<float> &verts, G3D::Array<int> &tris);
+ private:
+ /// Loads a portion of a map's terrain
+ bool loadMap(uint32 mapID, uint32 tileX, uint32 tileY, MeshData &meshData, Spot portion);
+
+ /// Sets loop variables for selecting only certain parts of a map's terrain
+ void getLoopVars(Spot portion, int &loopStart, int &loopEnd, int &loopInc);
+
+ /// Controls whether liquids are loaded
+ bool m_skipLiquid;
+
+ /// Load the map terrain from file
+ bool loadHeightMap(uint32 mapID, uint32 tileX, uint32 tileY, G3D::Array<float> &vertices, G3D::Array<int> &triangles, Spot portion);
+
+ /// Get the vector coordinate for a specific position
+ void getHeightCoord(int index, Grid grid, float xOffset, float yOffset, float* coord, float* v);
+
+ /// Get the triangle's vector indices for a specific position
+ void getHeightTriangle(int square, Spot triangle, int* indices, bool liquid = false);
+
+ /// Determines if the specific position's triangles should be rendered
+ bool isHole(int square, const uint16 holes[16][16]);
+
+ /// Get the liquid vector coordinate for a specific position
+ void getLiquidCoord(int index, int index2, float xOffset, float yOffset, float* coord, float* v);
+
+ /// Get the liquid type for a specific position
+ uint8 getLiquidType(int square, const uint8 liquid_type[16][16]);
+
+ // hide parameterless and copy constructor
+ TerrainBuilder();
+ TerrainBuilder(const TerrainBuilder &tb);
+ };
+}
+
+#endif
+
diff --git a/src/tools/mmaps_generator/VMapExtensions.cpp b/src/tools/mmaps_generator/VMapExtensions.cpp
new file mode 100644
index 00000000000..972f222bba6
--- /dev/null
+++ b/src/tools/mmaps_generator/VMapExtensions.cpp
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2008-2013 TrinityCore <http://www.trinitycore.org/>
+ * Copyright (C) 2005-2011 MaNGOS <http://getmangos.com/>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include <vector>
+#include "MapTree.h"
+#include "VMapManager2.h"
+#include "WorldModel.h"
+#include "ModelInstance.h"
+
+namespace VMAP
+{
+ // Need direct access to encapsulated VMAP data, so we add functions for MMAP generator
+ // maybe add MapBuilder as friend to all of the below classes would be better?
+
+ // declared in src/shared/vmap/MapTree.h
+ void StaticMapTree::getModelInstances(ModelInstance* &models, uint32 &count)
+ {
+ models = iTreeValues;
+ count = iNTreeValues;
+ }
+
+ // declared in src/shared/vmap/VMapManager2.h
+ void VMapManager2::getInstanceMapTree(InstanceTreeMap &instanceMapTree)
+ {
+ instanceMapTree = iInstanceMapTrees;
+ }
+
+ // declared in src/shared/vmap/WorldModel.h
+ void WorldModel::getGroupModels(std::vector<GroupModel> &groupModels)
+ {
+ groupModels = this->groupModels;
+ }
+
+ // declared in src/shared/vmap/WorldModel.h
+ void GroupModel::getMeshData(std::vector<Vector3> &vertices, std::vector<MeshTriangle> &triangles, WmoLiquid* &liquid)
+ {
+ vertices = this->vertices;
+ triangles = this->triangles;
+ liquid = iLiquid;
+ }
+
+ // declared in src/shared/vmap/ModelInstance.h
+ WorldModel* ModelInstance::getWorldModel()
+ {
+ return iModel;
+ }
+
+ // declared in src/shared/vmap/WorldModel.h
+ void WmoLiquid::getPosInfo(uint32 &tilesX, uint32 &tilesY, Vector3 &corner) const
+ {
+ tilesX = iTilesX;
+ tilesY = iTilesY;
+ corner = iCorner;
+ }
+}
diff --git a/src/tools/vmap4_assembler/CMakeLists.txt b/src/tools/vmap4_assembler/CMakeLists.txt
index 55b6514d091..8d455eae6ee 100644
--- a/src/tools/vmap4_assembler/CMakeLists.txt
+++ b/src/tools/vmap4_assembler/CMakeLists.txt
@@ -20,9 +20,7 @@ include_directories(
${ZLIB_INCLUDE_DIR}
)
-add_definitions(-DNO_CORE_FUNCS)
add_executable(vmap4assembler VMapAssembler.cpp)
-add_dependencies(vmap4assembler mpq)
if(CMAKE_SYSTEM_NAME MATCHES "Darwin")
set_target_properties(vmap4assembler PROPERTIES LINK_FLAGS "-framework Carbon")
diff --git a/src/tools/vmap4_extractor/CMakeLists.txt b/src/tools/vmap4_extractor/CMakeLists.txt
index 19798e46475..023f5447f85 100644
--- a/src/tools/vmap4_extractor/CMakeLists.txt
+++ b/src/tools/vmap4_extractor/CMakeLists.txt
@@ -11,26 +11,19 @@
file(GLOB_RECURSE sources *.cpp *.h)
-# uncomment next line to disable debug mode
-add_definitions("-DIOMAP_DEBUG")
-
-# build setup currently only supports libmpq 0.4.x
-add_definitions("-DUSE_LIBMPQ04")
-add_definitions("-Wall")
-add_definitions("-ggdb")
-add_definitions("-O3")
-
-if( UNIX )
- include_directories(
- ${CMAKE_SOURCE_DIR}/dep/libmpq
- )
-elseif( WIN32 )
- include_directories(
+set(include_Dirs
${CMAKE_SOURCE_DIR}/dep/libmpq
+)
+
+if( WIN32 )
+ set(include_Dirs
+ ${include_Dirs}
${CMAKE_SOURCE_DIR}/dep/libmpq/win
)
endif()
+include_directories(${include_Dirs})
+
add_executable(vmap4extractor ${sources})
target_link_libraries(vmap4extractor
@@ -39,8 +32,6 @@ target_link_libraries(vmap4extractor
${ZLIB_LIBRARIES}
)
-add_dependencies(vmap4extractor mpq)
-
if( UNIX )
install(TARGETS vmap4extractor DESTINATION bin)
elseif( WIN32 )
diff --git a/src/tools/vmap4_extractor/adtfile.cpp b/src/tools/vmap4_extractor/adtfile.cpp
index a605118eead..56e62d474d1 100644
--- a/src/tools/vmap4_extractor/adtfile.cpp
+++ b/src/tools/vmap4_extractor/adtfile.cpp
@@ -69,8 +69,7 @@ void fixname2(char* name, size_t len)
char* GetExtension(char* FileName)
{
- char* szTemp;
- if (szTemp = strrchr(FileName, '.'))
+ if (char* szTemp = strrchr(FileName, '.'))
return szTemp;
return NULL;
}
diff --git a/src/tools/vmap4_extractor/model.cpp b/src/tools/vmap4_extractor/model.cpp
index c642f73ccae..b950db023fe 100644
--- a/src/tools/vmap4_extractor/model.cpp
+++ b/src/tools/vmap4_extractor/model.cpp
@@ -152,10 +152,10 @@ ModelInstance::ModelInstance(MPQFile& f, char const* ModelInstName, uint32 mapID
fseek(input, 8, SEEK_SET); // get the correct no of vertices
int nVertices;
- fread(&nVertices, sizeof (int), 1, input);
+ int count = fread(&nVertices, sizeof (int), 1, input);
fclose(input);
- if(nVertices == 0)
+ if (count != 1 || nVertices == 0)
return;
uint16 adtId = 0;// not used for models
diff --git a/src/tools/vmap4_extractor/mpq_libmpq.cpp b/src/tools/vmap4_extractor/mpq_libmpq.cpp
index 528b9679a58..ffa097d9a22 100644
--- a/src/tools/vmap4_extractor/mpq_libmpq.cpp
+++ b/src/tools/vmap4_extractor/mpq_libmpq.cpp
@@ -79,7 +79,7 @@ size_t MPQFile::read(void* dest, size_t bytes)
if (eof) return 0;
size_t rpos = pointer + bytes;
- if (rpos > size) {
+ if (rpos > size_t(size)) {
bytes = size - pointer;
eof = true;
}
diff --git a/src/tools/vmap4_extractor/mpq_libmpq04.h b/src/tools/vmap4_extractor/mpq_libmpq04.h
index 89f715e9e87..9f0163067c4 100644
--- a/src/tools/vmap4_extractor/mpq_libmpq04.h
+++ b/src/tools/vmap4_extractor/mpq_libmpq04.h
@@ -1,6 +1,3 @@
-#define _CRT_SECURE_NO_DEPRECATE
-#define _CRT_SECURE_NO_WARNINGS
-
#ifndef MPQ_H
#define MPQ_H
diff --git a/src/tools/vmap4_extractor/vmapexport.cpp b/src/tools/vmap4_extractor/vmapexport.cpp
index 186f9c8606e..89e4b850dac 100644
--- a/src/tools/vmap4_extractor/vmapexport.cpp
+++ b/src/tools/vmap4_extractor/vmapexport.cpp
@@ -461,8 +461,7 @@ int main(int argc, char ** argv)
printf("Your output directory seems to be polluted, please use an empty directory!\n");
printf("<press return to exit>");
char garbage[2];
- scanf("%c", garbage);
- return 1;
+ return scanf("%c", garbage);
}
}
diff --git a/src/tools/vmap4_extractor/wdtfile.cpp b/src/tools/vmap4_extractor/wdtfile.cpp
index 7420edfee2e..d9216fd77eb 100644
--- a/src/tools/vmap4_extractor/wdtfile.cpp
+++ b/src/tools/vmap4_extractor/wdtfile.cpp
@@ -35,7 +35,7 @@ WDTFile::WDTFile(char* file_name, char* file_name1) : WDT(file_name), gWmoInstan
filename.append(file_name1,strlen(file_name1));
}
-bool WDTFile::init(char */*map_id*/, unsigned int mapID)
+bool WDTFile::init(char* /*map_id*/, unsigned int mapID)
{
if (WDT.isEof())
{
diff --git a/src/tools/vmap4_extractor/wmo.cpp b/src/tools/vmap4_extractor/wmo.cpp
index 0bc749b9bd5..7c9f23c5a95 100644
--- a/src/tools/vmap4_extractor/wmo.cpp
+++ b/src/tools/vmap4_extractor/wmo.cpp
@@ -521,10 +521,10 @@ WMOInstance::WMOInstance(MPQFile& f, char const* WmoInstName, uint32 mapID, uint
fseek(input, 8, SEEK_SET); // get the correct no of vertices
int nVertices;
- fread(&nVertices, sizeof (int), 1, input);
+ int count = fread(&nVertices, sizeof (int), 1, input);
fclose(input);
- if(nVertices == 0)
+ if (count != 1 || nVertices == 0)
return;
float x,z;