diff options
author | megamage <none@none> | 2009-07-31 10:48:34 +0800 |
---|---|---|
committer | megamage <none@none> | 2009-07-31 10:48:34 +0800 |
commit | 0a2af88cb455bad8166140e544c829c405055628 (patch) | |
tree | ec7d52e69ac76b332716438acab124c3b6a8c60a | |
parent | b1bd5e7905beea21dab662e15833e5debc7ad05e (diff) |
[8252] Move DB scripting code from global World to Map. Author: Ambal
This one more step to pre-map events proccesing and parallel in future.
[8256] Restore DB script loading broken after [8252]. Author: VladimirMangos
Also propetly decrease scheduled DB script actions at map unloading.
Some code style restore.
--HG--
branch : trunk
-rw-r--r-- | sql/mangos.sql | 54 | ||||
-rw-r--r-- | src/game/GameObject.cpp | 6 | ||||
-rw-r--r-- | src/game/Map.cpp | 865 | ||||
-rw-r--r-- | src/game/Map.h | 9 | ||||
-rw-r--r-- | src/game/Player.cpp | 4 | ||||
-rw-r--r-- | src/game/SpellEffects.cpp | 14 | ||||
-rw-r--r-- | src/game/Transports.cpp | 2 | ||||
-rw-r--r-- | src/game/WaypointMovementGenerator.cpp | 2 | ||||
-rw-r--r-- | src/game/World.cpp | 843 | ||||
-rw-r--r-- | src/game/World.h | 14 |
10 files changed, 921 insertions, 892 deletions
diff --git a/sql/mangos.sql b/sql/mangos.sql index 40877767793..93af8aadee6 100644 --- a/sql/mangos.sql +++ b/sql/mangos.sql @@ -23,7 +23,7 @@ DROP TABLE IF EXISTS `db_version`; CREATE TABLE `db_version` ( `version` varchar(120) default NULL, `creature_ai_version` varchar(120) default NULL, - `required_8251_03_mangos_spell_proc_event` bit(1) default NULL + `required_8254_01_mangos_spell_proc_event` bit(1) default NULL ) ENGINE=MyISAM DEFAULT CHARSET=utf8 ROW_FORMAT=FIXED COMMENT='Used DB version notes'; -- @@ -15654,7 +15654,7 @@ INSERT INTO spell_chain VALUES (27219,11700,689,7,0), (27220,27219,689,8,0), (47857,27220,689,9,0), -/*DrainSoul*/ +/*Drain Soul*/ (1120,0,1120,1,0), (8288,1120,1120,2,0), (8289,8288,1120,3,0), @@ -15670,14 +15670,20 @@ INSERT INTO spell_chain VALUES (59161,48181,48181,2,0), (59163,59161,48181,3,0), (59164,59163,48181,4,0), -/*HowlofTerror*/ +/*Howl of Terror*/ (5484,0,5484,1,0), (17928,5484,5484,2,0), -/*SeedofCorruption*/ +/*Seed of Corruption*/ (27243,0,27243,1,0), (47835,27243,27243,2,0), (47836,47835,27243,3,0), -/*UnstableAffliction*/ +/* Shadow embrace */ +(32385,0,32385,1,0), +(32387,32385,32385,2,0), +(32392,32387,32385,3,0), +(32393,32392,32385,4,0), +(32394,32393,32385,5,0), +/*Unstable Affliction*/ (30108,0,30108,1,0), (30404,30108,30108,2,0), (30405,30404,30108,3,0), @@ -15776,6 +15782,12 @@ INSERT INTO spell_chain VALUES (26372,26371,26364,9,0), (49278,26372,26364,10,0), (49279,49278,26364,11,0), +/*Maelstrom Weapon*/ +(51528,0,51528,1,0), +(51529,51528,51528,2,0), +(51530,51529,51528,3,0), +(51531,51530,51528,4,0), +(51532,51531,51528,5,0), /*Nature Resistance Totem*/ (10595,0,10595,1,0), (10600,10595,10595,2,0), @@ -17233,7 +17245,7 @@ INSERT INTO `spell_proc_event` VALUES (12999, 0x00000000, 0, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 4.000000, 0.000000, 0), (13000, 0x00000000, 0, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 6.000000, 0.000000, 0), (13001, 0x00000000, 0, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 8.000000, 0.000000, 0), -(13002, 0x00000000, 0, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 10.000000, 0.000000, 0), +(13002, 0x00000000, 0, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,10.000000, 0.000000, 0), (13045, 0x00000000, 0, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000002, 0.000000, 0.000000, 0), (13046, 0x00000000, 0, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000002, 0.000000, 0.000000, 0), (13047, 0x00000000, 0, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000002, 0.000000, 0.000000, 0), @@ -17327,8 +17339,8 @@ INSERT INTO `spell_proc_event` VALUES (20131, 0x00000000, 0, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000040, 0.000000, 0.000000, 0), (20132, 0x00000000, 0, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000040, 0.000000, 0.000000, 0), (20164, 0x00000000, 0, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 5.000000, 0.000000, 0), -(20165, 0x00000000, 0, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 20.000000, 0.000000, 0), -(20166, 0x00000000, 0, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 20.000000, 0.000000, 0), +(20165, 0x00000000, 0, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,20.000000, 0.000000, 0), +(20166, 0x00000000, 0, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,15.000000, 0.000000, 0), (20182, 0x00000000, 0, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000040, 0.000000, 0.000000, 0), (20210, 0x00000000, 10, 0xC0000000, 0x00010000, 0x00000000, 0x00000000, 0x00000002, 0.000000, 0.000000, 0), (20212, 0x00000000, 10, 0xC0000000, 0x00010000, 0x00000000, 0x00000000, 0x00000002, 0.000000, 0.000000, 0), @@ -17366,7 +17378,7 @@ INSERT INTO `spell_proc_event` VALUES (24353, 0x00000000, 0, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000002, 0.000000, 0.000000, 0), (24389, 0x00000000, 3, 0x00C00017, 0x00000040, 0x00000000, 0x00000000, 0x00000000, 0.000000, 0.000000, 0), (24658, 0x00000000, 0, 0x00000000, 0x00000000, 0x00000000, 0x00014110, 0x00000000, 0.000000, 0.000000, 0), -(24905, 0x00000000, 0, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000002, 15.000000, 0.000000, 0), +(24905, 0x00000000, 0, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000002,15.000000, 0.000000, 0), (24932, 0x00000000, 0, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000002, 0.000000, 0.000000, 6), (25050, 0x00000004, 0, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0.000000, 0.000000, 0), (25669, 0x00000000, 0, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 1.000000, 0.000000, 0), @@ -17462,7 +17474,7 @@ INSERT INTO `spell_proc_event` VALUES (31570, 0x00000000, 3, 0x00010000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0.000000, 0.000000, 0), (31785, 0x00000000, 0, 0x00000000, 0x00000000, 0x00000000, 0x00008800, 0x00000000, 0.000000, 0.000000, 0), (31794, 0x00000000, 0, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00010000, 0.000000, 0.000000, 0), -(31801, 0x00000000, 0, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 20.000000, 0.000000, 0), +(31801, 0x00000000, 0, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,20.000000, 0.000000, 0), (31833, 0x00000000, 10, 0x80000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0.000000, 0.000000, 0), (31835, 0x00000000, 10, 0x80000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0.000000, 0.000000, 0), (31836, 0x00000000, 10, 0x80000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0.000000, 0.000000, 0), @@ -17472,11 +17484,7 @@ INSERT INTO `spell_proc_event` VALUES (31877, 0x00000000, 10, 0x00800000, 0x00000000, 0x00000008, 0x00004110, 0x00000000, 0.000000, 0.000000, 0), (31878, 0x00000000, 10, 0x00800000, 0x00000000, 0x00000008, 0x00004110, 0x00000000, 0.000000, 0.000000, 0), (31904, 0x00000000, 0, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000040, 0.000000, 0.000000, 0), -(32385, 0x00000000, 5, 0x00000402, 0x00000011, 0x00000000, 0x00000000, 0x00000000, 0.000000, 0.000000, 0), -(32387, 0x00000000, 5, 0x00000402, 0x00000011, 0x00000000, 0x00000000, 0x00000000, 0.000000, 0.000000, 0), -(32392, 0x00000000, 5, 0x00000402, 0x00000011, 0x00000000, 0x00000000, 0x00000000, 0.000000, 0.000000, 0), -(32393, 0x00000000, 5, 0x00000402, 0x00000011, 0x00000000, 0x00000000, 0x00000000, 0.000000, 0.000000, 0), -(32394, 0x00000000, 5, 0x00000402, 0x00000011, 0x00000000, 0x00000000, 0x00000000, 0.000000, 0.000000, 0), +(32385, 0x00000000, 5, 0x00000001, 0x00040000, 0x00000000, 0x00000000, 0x00000000, 0.000000, 0.000000, 0), (32409, 0x00000000, 0, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0.000000, 0.000000, 0), (32587, 0x00000000, 0, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000040, 0.000000, 0.000000, 0), (32642, 0x00000000, 0, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000040, 0.000000, 0.000000, 0), @@ -17752,6 +17760,7 @@ INSERT INTO `spell_proc_event` VALUES (51483, 0x00000001, 11, 0x20000000, 0x00000000, 0x00000000, 0x00004000, 0x00000000, 0.000000, 0.000000, 0), (51485, 0x00000001, 11, 0x20000000, 0x00000000, 0x00000000, 0x00004000, 0x00000000, 0.000000, 0.000000, 0), (51486, 0x00000001, 11, 0x20000000, 0x00000000, 0x00000000, 0x00004000, 0x00000000, 0.000000, 0.000000, 0), +(51528, 0x00000000, 0, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,12.500000, 0.000000, 0), (51556, 0x00000000, 11, 0x000000C0, 0x00000000, 0x00000010, 0x00000000, 0x00000002, 0.000000, 0.000000, 0), (51557, 0x00000000, 11, 0x000000C0, 0x00000000, 0x00000010, 0x00000000, 0x00000002, 0.000000, 0.000000, 0), (51558, 0x00000000, 11, 0x000000C0, 0x00000000, 0x00000010, 0x00000000, 0x00000002, 0.000000, 0.000000, 0), @@ -17781,12 +17790,12 @@ INSERT INTO `spell_proc_event` VALUES (51698, 0x00000000, 0, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000002, 0.000000, 0.000000, 1), (51700, 0x00000000, 0, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000002, 0.000000, 0.000000, 1), (51701, 0x00000000, 0, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000002, 0.000000, 0.000000, 1), -(51940, 0x00000000, 0, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0.000000, 20.000000, 0), -(51989, 0x00000000, 0, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0.000000, 20.000000, 0), -(52004, 0x00000000, 0, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0.000000, 20.000000, 0), -(52005, 0x00000000, 0, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0.000000, 20.000000, 0), -(52007, 0x00000000, 0, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0.000000, 20.000000, 0), -(52008, 0x00000000, 0, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0.000000, 20.000000, 0), +(51940, 0x00000000, 0, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0.000000,20.000000, 0), +(51989, 0x00000000, 0, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0.000000,20.000000, 0), +(52004, 0x00000000, 0, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0.000000,20.000000, 0), +(52005, 0x00000000, 0, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0.000000,20.000000, 0), +(52007, 0x00000000, 0, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0.000000,20.000000, 0), +(52008, 0x00000000, 0, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0.000000,20.000000, 0), (52020, 0x00000000, 7, 0x00008000, 0x00100000, 0x00000000, 0x00000000, 0x00000000, 0.000000, 0.000000, 0), (52127, 0x00000000, 0, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0.000000, 0.000000, 3), (52420, 0x00000000, 0, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0.000000, 0.000000, 30), @@ -17857,6 +17866,7 @@ INSERT INTO `spell_proc_event` VALUES (55677, 0x00000000, 6, 0x00000000, 0x00000001, 0x00000000, 0x00000000, 0x00000000, 0.000000, 0.000000, 0), (55680, 0x00000000, 6, 0x00000200, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0.000000, 0.000000, 0), (55689, 0x00000000, 0, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000002, 0.000000, 0.000000, 0), +(55747, 0x00000000, 0, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0.000000, 0.000000, 45), (55768, 0x00000000, 0, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0.000000, 0.000000, 45), (55776, 0x00000000, 0, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0.000000, 0.000000, 45), (56218, 0x00000000, 5, 0x00000002, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0.000000, 0.000000, 0), @@ -17904,6 +17914,8 @@ INSERT INTO `spell_proc_event` VALUES (60132, 0x00000000, 15, 0x00000000, 0x08020000, 0x00000000, 0x00000000, 0x00000000, 0.000000, 0.000000, 0), (60170, 0x00000000, 5, 0x00000006, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0.000000, 0.000000, 0), (60172, 0x00000000, 5, 0x00040000, 0x00000000, 0x00000000, 0x00000000, 0x00010000, 0.000000, 0.000000, 0), +(60436, 0x00000000, 0, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0.000000, 0.000000, 45), +(60442, 0x00000000, 0, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0.000000, 0.000000, 45), (60493, 0x00000000, 0, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0.000000, 0.000000, 45), (60503, 0x00000000, 4, 0x00000004, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0.000000, 0.000000, 0), (60537, 0x00000000, 0, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000002, 0.000000, 0.000000, 0), diff --git a/src/game/GameObject.cpp b/src/game/GameObject.cpp index 59ec04fc649..b6ac3442c0d 100644 --- a/src/game/GameObject.cpp +++ b/src/game/GameObject.cpp @@ -949,7 +949,7 @@ void GameObject::Use(Unit* user) UseDoorOrButton(); // activate script - sWorld.ScriptsStart(sGameObjectScripts, GetDBTableGUIDLow(), spellCaster, this); + GetMap()->ScriptsStart(sGameObjectScripts, GetDBTableGUIDLow(), spellCaster, this); return; case GAMEOBJECT_TYPE_QUESTGIVER: //2 @@ -1045,7 +1045,7 @@ void GameObject::Use(Unit* user) player->CastedCreatureOrGO(info->id, GetGUID(), 0); if (info->goober.eventId) - sWorld.ScriptsStart(sEventScripts, info->goober.eventId, player, this); + GetMap()->ScriptsStart(sEventScripts, info->goober.eventId, player, this); } // cast this spell later if provided @@ -1068,7 +1068,7 @@ void GameObject::Use(Unit* user) player->SendCinematicStart(info->camera.cinematicId); if (info->camera.eventID) - sWorld.ScriptsStart(sEventScripts, info->camera.eventID, player, this); + GetMap()->ScriptsStart(sEventScripts, info->camera.eventID, player, this); return; } diff --git a/src/game/Map.cpp b/src/game/Map.cpp index 93fd3e21e1b..a0485e72a85 100644 --- a/src/game/Map.cpp +++ b/src/game/Map.cpp @@ -20,6 +20,7 @@ #include "MapManager.h" #include "Player.h" +#include "Vehicle.h" #include "GridNotifiers.h" #include "Log.h" #include "GridStates.h" @@ -36,6 +37,7 @@ #include "Group.h" #include "MapRefManager.h" #include "Vehicle.h" +#include "WaypointManager.h" #include "MapInstanced.h" #include "InstanceSaveMgr.h" @@ -46,9 +48,20 @@ GridState* si_GridStates[MAX_GRID_STATE]; +struct ScriptAction +{ + uint64 sourceGUID; + uint64 targetGUID; + uint64 ownerGUID; // owner of source if source is item + ScriptInfo const* script; // pointer to static script data +}; + Map::~Map() { UnloadAll(); + + if(!m_scriptSchedule.empty()) + sWorld.DecreaseScheduledScriptCount(m_scriptSchedule.size()); } bool Map::ExistMap(uint32 mapid,int gx,int gy) @@ -780,17 +793,21 @@ void Map::Update(const uint32 &t_diff) // Don't unload grids if it's battleground, since we may have manually added GOs,creatures, those doesn't load from DB at grid re-load ! // This isn't really bother us, since as soon as we have instanced BG-s, the whole map unloads as the BG gets ended - if (IsBattleGroundOrArena()) - return; - - for (GridRefManager<NGridType>::iterator i = GridRefManager<NGridType>::begin(); i != GridRefManager<NGridType>::end(); ) + if (!IsBattleGroundOrArena()) { - NGridType *grid = i->getSource(); - GridInfo *info = i->getSource()->getGridInfoRef(); - ++i; // The update might delete the map and we need the next map before the iterator gets invalid - assert(grid->GetGridState() >= 0 && grid->GetGridState() < MAX_GRID_STATE); - si_GridStates[grid->GetGridState()]->Update(*this, *grid, *info, grid->getX(), grid->getY(), t_diff); + for (GridRefManager<NGridType>::iterator i = GridRefManager<NGridType>::begin(); i != GridRefManager<NGridType>::end(); ) + { + NGridType *grid = i->getSource(); + GridInfo *info = i->getSource()->getGridInfoRef(); + ++i; // The update might delete the map and we need the next map before the iterator gets invalid + assert(grid->GetGridState() >= 0 && grid->GetGridState() < MAX_GRID_STATE); + si_GridStates[grid->GetGridState()]->Update(*this, *grid, *info, grid->getX(), grid->getY(), t_diff); + } } + + ///- Process necessary scripts + if (!m_scriptSchedule.empty()) + ScriptsProcess(); } void Map::Remove(Player *player, bool remove) @@ -2745,6 +2762,836 @@ void BattleGroundMap::UnloadAll() Map::UnloadAll(); } +/// Put scripts in the execution queue +void Map::ScriptsStart(ScriptMapMap const& scripts, uint32 id, Object* source, Object* target) +{ + ///- Find the script map + ScriptMapMap::const_iterator s = scripts.find(id); + if (s == scripts.end()) + return; + + // prepare static data + uint64 sourceGUID = source ? source->GetGUID() : (uint64)0; //some script commands doesn't have source + uint64 targetGUID = target ? target->GetGUID() : (uint64)0; + uint64 ownerGUID = (source->GetTypeId()==TYPEID_ITEM) ? ((Item*)source)->GetOwnerGUID() : (uint64)0; + + ///- Schedule script execution for all scripts in the script map + ScriptMap const *s2 = &(s->second); + bool immedScript = false; + for (ScriptMap::const_iterator iter = s2->begin(); iter != s2->end(); ++iter) + { + ScriptAction sa; + sa.sourceGUID = sourceGUID; + sa.targetGUID = targetGUID; + sa.ownerGUID = ownerGUID; + + sa.script = &iter->second; + m_scriptSchedule.insert(std::pair<time_t, ScriptAction>(time_t(sWorld.GetGameTime() + iter->first), sa)); + if (iter->first == 0) + immedScript = true; + + sWorld.IncreaseScheduledScriptsCount(); + } + ///- If one of the effects should be immediate, launch the script execution + if (/*start &&*/ immedScript) + ScriptsProcess(); +} + +void Map::ScriptCommandStart(ScriptInfo const& script, uint32 delay, Object* source, Object* target) +{ + // NOTE: script record _must_ exist until command executed + + // prepare static data + uint64 sourceGUID = source ? source->GetGUID() : (uint64)0; + uint64 targetGUID = target ? target->GetGUID() : (uint64)0; + uint64 ownerGUID = (source->GetTypeId()==TYPEID_ITEM) ? ((Item*)source)->GetOwnerGUID() : (uint64)0; + + ScriptAction sa; + sa.sourceGUID = sourceGUID; + sa.targetGUID = targetGUID; + sa.ownerGUID = ownerGUID; + + sa.script = &script; + m_scriptSchedule.insert(std::pair<time_t, ScriptAction>(time_t(sWorld.GetGameTime() + delay), sa)); + + sWorld.IncreaseScheduledScriptsCount(); + + ///- If effects should be immediate, launch the script execution + if(delay == 0) + ScriptsProcess(); +} + +/// Process queued scripts +void Map::ScriptsProcess() +{ + if (m_scriptSchedule.empty()) + return; + + ///- Process overdue queued scripts + std::multimap<time_t, ScriptAction>::iterator iter = m_scriptSchedule.begin(); + // ok as multimap is a *sorted* associative container + while (!m_scriptSchedule.empty() && (iter->first <= sWorld.GetGameTime())) + { + ScriptAction const& step = iter->second; + + Object* source = NULL; + + if(step.sourceGUID) + { + switch(GUID_HIPART(step.sourceGUID)) + { + case HIGHGUID_ITEM: + // case HIGHGUID_CONTAINER: ==HIGHGUID_ITEM + { + Player* player = HashMapHolder<Player>::Find(step.ownerGUID); + if(player) + source = player->GetItemByGuid(step.sourceGUID); + break; + } + case HIGHGUID_UNIT: + source = HashMapHolder<Creature>::Find(step.sourceGUID); + break; + case HIGHGUID_PET: + source = HashMapHolder<Pet>::Find(step.sourceGUID); + break; + case HIGHGUID_VEHICLE: + source = HashMapHolder<Vehicle>::Find(step.sourceGUID); + break; + case HIGHGUID_PLAYER: + source = HashMapHolder<Player>::Find(step.sourceGUID); + break; + case HIGHGUID_GAMEOBJECT: + source = HashMapHolder<GameObject>::Find(step.sourceGUID); + break; + case HIGHGUID_CORPSE: + source = HashMapHolder<Corpse>::Find(step.sourceGUID); + break; + case HIGHGUID_MO_TRANSPORT: + for (MapManager::TransportSet::iterator iter = MapManager::Instance().m_Transports.begin(); iter != MapManager::Instance().m_Transports.end(); ++iter) + { + if((*iter)->GetGUID() == step.sourceGUID) + { + source = reinterpret_cast<Object*>(*iter); + break; + } + } + break; + default: + sLog.outError("*_script source with unsupported high guid value %u",GUID_HIPART(step.sourceGUID)); + break; + } + } + + //if(source && !source->IsInWorld()) source = NULL; + + Object* target = NULL; + + if(step.targetGUID) + { + switch(GUID_HIPART(step.targetGUID)) + { + case HIGHGUID_UNIT: + target = HashMapHolder<Creature>::Find(step.targetGUID); + break; + case HIGHGUID_PET: + target = HashMapHolder<Pet>::Find(step.targetGUID); + break; + case HIGHGUID_VEHICLE: + target = HashMapHolder<Vehicle>::Find(step.targetGUID); + break; + case HIGHGUID_PLAYER: // empty GUID case also + target = HashMapHolder<Player>::Find(step.targetGUID); + break; + case HIGHGUID_GAMEOBJECT: + target = HashMapHolder<GameObject>::Find(step.targetGUID); + break; + case HIGHGUID_CORPSE: + target = HashMapHolder<Corpse>::Find(step.targetGUID); + break; + default: + sLog.outError("*_script source with unsupported high guid value %u",GUID_HIPART(step.targetGUID)); + break; + } + } + + //if(target && !target->IsInWorld()) target = NULL; + + switch (step.script->command) + { + case SCRIPT_COMMAND_TALK: + { + if(!source) + { + sLog.outError("SCRIPT_COMMAND_TALK call for NULL creature."); + break; + } + + if(source->GetTypeId()!=TYPEID_UNIT) + { + sLog.outError("SCRIPT_COMMAND_TALK call for non-creature (TypeId: %u), skipping.",source->GetTypeId()); + break; + } + if(step.script->datalong > 3) + { + sLog.outError("SCRIPT_COMMAND_TALK invalid chat type (%u), skipping.",step.script->datalong); + break; + } + + uint64 unit_target = target ? target->GetGUID() : 0; + + //datalong 0=normal say, 1=whisper, 2=yell, 3=emote text + switch(step.script->datalong) + { + case 0: // Say + ((Creature *)source)->Say(step.script->dataint, LANG_UNIVERSAL, unit_target); + break; + case 1: // Whisper + if(!unit_target) + { + sLog.outError("SCRIPT_COMMAND_TALK attempt to whisper (%u) NULL, skipping.",step.script->datalong); + break; + } + ((Creature *)source)->Whisper(step.script->dataint,unit_target); + break; + case 2: // Yell + ((Creature *)source)->Yell(step.script->dataint, LANG_UNIVERSAL, unit_target); + break; + case 3: // Emote text + ((Creature *)source)->TextEmote(step.script->dataint, unit_target); + break; + default: + break; // must be already checked at load + } + break; + } + + case SCRIPT_COMMAND_EMOTE: + if(!source) + { + sLog.outError("SCRIPT_COMMAND_EMOTE call for NULL creature."); + break; + } + + if(source->GetTypeId()!=TYPEID_UNIT) + { + sLog.outError("SCRIPT_COMMAND_EMOTE call for non-creature (TypeId: %u), skipping.",source->GetTypeId()); + break; + } + + ((Creature *)source)->HandleEmoteCommand(step.script->datalong); + break; + case SCRIPT_COMMAND_FIELD_SET: + if(!source) + { + sLog.outError("SCRIPT_COMMAND_FIELD_SET call for NULL object."); + break; + } + if(step.script->datalong <= OBJECT_FIELD_ENTRY || step.script->datalong >= source->GetValuesCount()) + { + sLog.outError("SCRIPT_COMMAND_FIELD_SET call for wrong field %u (max count: %u) in object (TypeId: %u).", + step.script->datalong,source->GetValuesCount(),source->GetTypeId()); + break; + } + + source->SetUInt32Value(step.script->datalong, step.script->datalong2); + break; + case SCRIPT_COMMAND_MOVE_TO: + if(!source) + { + sLog.outError("SCRIPT_COMMAND_MOVE_TO call for NULL creature."); + break; + } + + if(source->GetTypeId()!=TYPEID_UNIT) + { + sLog.outError("SCRIPT_COMMAND_MOVE_TO call for non-creature (TypeId: %u), skipping.",source->GetTypeId()); + break; + } + ((Creature*)source)->SendMonsterMoveWithSpeed(step.script->x, step.script->y, step.script->z, step.script->datalong2 ); + ((Creature*)source)->GetMap()->CreatureRelocation(((Creature*)source), step.script->x, step.script->y, step.script->z, 0); + break; + case SCRIPT_COMMAND_FLAG_SET: + if(!source) + { + sLog.outError("SCRIPT_COMMAND_FLAG_SET call for NULL object."); + break; + } + if(step.script->datalong <= OBJECT_FIELD_ENTRY || step.script->datalong >= source->GetValuesCount()) + { + sLog.outError("SCRIPT_COMMAND_FLAG_SET call for wrong field %u (max count: %u) in object (TypeId: %u).", + step.script->datalong,source->GetValuesCount(),source->GetTypeId()); + break; + } + + source->SetFlag(step.script->datalong, step.script->datalong2); + break; + case SCRIPT_COMMAND_FLAG_REMOVE: + if(!source) + { + sLog.outError("SCRIPT_COMMAND_FLAG_REMOVE call for NULL object."); + break; + } + if(step.script->datalong <= OBJECT_FIELD_ENTRY || step.script->datalong >= source->GetValuesCount()) + { + sLog.outError("SCRIPT_COMMAND_FLAG_REMOVE call for wrong field %u (max count: %u) in object (TypeId: %u).", + step.script->datalong,source->GetValuesCount(),source->GetTypeId()); + break; + } + + source->RemoveFlag(step.script->datalong, step.script->datalong2); + break; + + case SCRIPT_COMMAND_TELEPORT_TO: + { + // accept player in any one from target/source arg + if (!target && !source) + { + sLog.outError("SCRIPT_COMMAND_TELEPORT_TO call for NULL object."); + break; + } + + // must be only Player + if((!target || target->GetTypeId() != TYPEID_PLAYER) && (!source || source->GetTypeId() != TYPEID_PLAYER)) + { + sLog.outError("SCRIPT_COMMAND_TELEPORT_TO call for non-player (TypeIdSource: %u)(TypeIdTarget: %u), skipping.", source ? source->GetTypeId() : 0, target ? target->GetTypeId() : 0); + break; + } + + Player* pSource = target && target->GetTypeId() == TYPEID_PLAYER ? (Player*)target : (Player*)source; + + pSource->TeleportTo(step.script->datalong, step.script->x, step.script->y, step.script->z, step.script->o); + break; + } + + case SCRIPT_COMMAND_TEMP_SUMMON_CREATURE: + { + if(!step.script->datalong) // creature not specified + { + sLog.outError("SCRIPT_COMMAND_TEMP_SUMMON_CREATURE call for NULL creature."); + break; + } + + if(!source) + { + sLog.outError("SCRIPT_COMMAND_TEMP_SUMMON_CREATURE call for NULL world object."); + break; + } + + WorldObject* summoner = dynamic_cast<WorldObject*>(source); + + if(!summoner) + { + sLog.outError("SCRIPT_COMMAND_TEMP_SUMMON_CREATURE call for non-WorldObject (TypeId: %u), skipping.",source->GetTypeId()); + break; + } + + float x = step.script->x; + float y = step.script->y; + float z = step.script->z; + float o = step.script->o; + + Creature* pCreature = summoner->SummonCreature(step.script->datalong, x, y, z, o,TEMPSUMMON_TIMED_OR_DEAD_DESPAWN,step.script->datalong2); + if (!pCreature) + { + sLog.outError("SCRIPT_COMMAND_TEMP_SUMMON failed for creature (entry: %u).",step.script->datalong); + break; + } + + break; + } + + case SCRIPT_COMMAND_RESPAWN_GAMEOBJECT: + { + if(!step.script->datalong) // gameobject not specified + { + sLog.outError("SCRIPT_COMMAND_RESPAWN_GAMEOBJECT call for NULL gameobject."); + break; + } + + if(!source) + { + sLog.outError("SCRIPT_COMMAND_RESPAWN_GAMEOBJECT call for NULL world object."); + break; + } + + WorldObject* summoner = dynamic_cast<WorldObject*>(source); + + if(!summoner) + { + sLog.outError("SCRIPT_COMMAND_RESPAWN_GAMEOBJECT call for non-WorldObject (TypeId: %u), skipping.",source->GetTypeId()); + break; + } + + GameObject *go = NULL; + int32 time_to_despawn = step.script->datalong2<5 ? 5 : (int32)step.script->datalong2; + + CellPair p(MaNGOS::ComputeCellPair(summoner->GetPositionX(), summoner->GetPositionY())); + Cell cell(p); + cell.data.Part.reserved = ALL_DISTRICT; + + MaNGOS::GameObjectWithDbGUIDCheck go_check(*summoner,step.script->datalong); + MaNGOS::GameObjectSearcher<MaNGOS::GameObjectWithDbGUIDCheck> checker(summoner, go,go_check); + + TypeContainerVisitor<MaNGOS::GameObjectSearcher<MaNGOS::GameObjectWithDbGUIDCheck>, GridTypeMapContainer > object_checker(checker); + CellLock<GridReadGuard> cell_lock(cell, p); + cell_lock->Visit(cell_lock, object_checker, *summoner->GetMap()); + + if ( !go ) + { + sLog.outError("SCRIPT_COMMAND_RESPAWN_GAMEOBJECT failed for gameobject(guid: %u).", step.script->datalong); + break; + } + + if( go->GetGoType()==GAMEOBJECT_TYPE_FISHINGNODE || + go->GetGoType()==GAMEOBJECT_TYPE_DOOR || + go->GetGoType()==GAMEOBJECT_TYPE_BUTTON || + go->GetGoType()==GAMEOBJECT_TYPE_TRAP ) + { + sLog.outError("SCRIPT_COMMAND_RESPAWN_GAMEOBJECT can not be used with gameobject of type %u (guid: %u).", uint32(go->GetGoType()), step.script->datalong); + break; + } + + if( go->isSpawned() ) + break; //gameobject already spawned + + go->SetLootState(GO_READY); + go->SetRespawnTime(time_to_despawn); //despawn object in ? seconds + + go->GetMap()->Add(go); + break; + } + case SCRIPT_COMMAND_OPEN_DOOR: + { + if(!step.script->datalong) // door not specified + { + sLog.outError("SCRIPT_COMMAND_OPEN_DOOR call for NULL door."); + break; + } + + if(!source) + { + sLog.outError("SCRIPT_COMMAND_OPEN_DOOR call for NULL unit."); + break; + } + + if(!source->isType(TYPEMASK_UNIT)) // must be any Unit (creature or player) + { + sLog.outError("SCRIPT_COMMAND_OPEN_DOOR call for non-unit (TypeId: %u), skipping.",source->GetTypeId()); + break; + } + + Unit* caster = (Unit*)source; + + GameObject *door = NULL; + int32 time_to_close = step.script->datalong2 < 15 ? 15 : (int32)step.script->datalong2; + + CellPair p(MaNGOS::ComputeCellPair(caster->GetPositionX(), caster->GetPositionY())); + Cell cell(p); + cell.data.Part.reserved = ALL_DISTRICT; + + MaNGOS::GameObjectWithDbGUIDCheck go_check(*caster,step.script->datalong); + MaNGOS::GameObjectSearcher<MaNGOS::GameObjectWithDbGUIDCheck> checker(caster,door,go_check); + + TypeContainerVisitor<MaNGOS::GameObjectSearcher<MaNGOS::GameObjectWithDbGUIDCheck>, GridTypeMapContainer > object_checker(checker); + CellLock<GridReadGuard> cell_lock(cell, p); + cell_lock->Visit(cell_lock, object_checker, *caster->GetMap()); + + if (!door) + { + sLog.outError("SCRIPT_COMMAND_OPEN_DOOR failed for gameobject(guid: %u).", step.script->datalong); + break; + } + if (door->GetGoType() != GAMEOBJECT_TYPE_DOOR) + { + sLog.outError("SCRIPT_COMMAND_OPEN_DOOR failed for non-door(GoType: %u).", door->GetGoType()); + break; + } + + if (door->GetGoState() != GO_STATE_READY) + break; //door already open + + door->UseDoorOrButton(time_to_close); + + if(target && target->isType(TYPEMASK_GAMEOBJECT) && ((GameObject*)target)->GetGoType()==GAMEOBJECT_TYPE_BUTTON) + ((GameObject*)target)->UseDoorOrButton(time_to_close); + break; + } + case SCRIPT_COMMAND_CLOSE_DOOR: + { + if(!step.script->datalong) // guid for door not specified + { + sLog.outError("SCRIPT_COMMAND_CLOSE_DOOR call for NULL door."); + break; + } + + if(!source) + { + sLog.outError("SCRIPT_COMMAND_CLOSE_DOOR call for NULL unit."); + break; + } + + if(!source->isType(TYPEMASK_UNIT)) // must be any Unit (creature or player) + { + sLog.outError("SCRIPT_COMMAND_CLOSE_DOOR call for non-unit (TypeId: %u), skipping.",source->GetTypeId()); + break; + } + + Unit* caster = (Unit*)source; + + GameObject *door = NULL; + int32 time_to_open = step.script->datalong2 < 15 ? 15 : (int32)step.script->datalong2; + + CellPair p(MaNGOS::ComputeCellPair(caster->GetPositionX(), caster->GetPositionY())); + Cell cell(p); + cell.data.Part.reserved = ALL_DISTRICT; + + MaNGOS::GameObjectWithDbGUIDCheck go_check(*caster,step.script->datalong); + MaNGOS::GameObjectSearcher<MaNGOS::GameObjectWithDbGUIDCheck> checker(caster,door,go_check); + + TypeContainerVisitor<MaNGOS::GameObjectSearcher<MaNGOS::GameObjectWithDbGUIDCheck>, GridTypeMapContainer > object_checker(checker); + CellLock<GridReadGuard> cell_lock(cell, p); + cell_lock->Visit(cell_lock, object_checker, *caster->GetMap()); + + if ( !door ) + { + sLog.outError("SCRIPT_COMMAND_CLOSE_DOOR failed for gameobject(guid: %u).", step.script->datalong); + break; + } + if ( door->GetGoType() != GAMEOBJECT_TYPE_DOOR ) + { + sLog.outError("SCRIPT_COMMAND_CLOSE_DOOR failed for non-door(GoType: %u).", door->GetGoType()); + break; + } + + if( door->GetGoState() == GO_STATE_READY ) + break; //door already closed + + door->UseDoorOrButton(time_to_open); + + if(target && target->isType(TYPEMASK_GAMEOBJECT) && ((GameObject*)target)->GetGoType()==GAMEOBJECT_TYPE_BUTTON) + ((GameObject*)target)->UseDoorOrButton(time_to_open); + + break; + } + case SCRIPT_COMMAND_QUEST_EXPLORED: + { + if(!source) + { + sLog.outError("SCRIPT_COMMAND_QUEST_EXPLORED call for NULL source."); + break; + } + + if(!target) + { + sLog.outError("SCRIPT_COMMAND_QUEST_EXPLORED call for NULL target."); + break; + } + + // when script called for item spell casting then target == (unit or GO) and source is player + WorldObject* worldObject; + Player* player; + + if(target->GetTypeId()==TYPEID_PLAYER) + { + if(source->GetTypeId()!=TYPEID_UNIT && source->GetTypeId()!=TYPEID_GAMEOBJECT) + { + sLog.outError("SCRIPT_COMMAND_QUEST_EXPLORED call for non-creature and non-gameobject (TypeId: %u), skipping.",source->GetTypeId()); + break; + } + + worldObject = (WorldObject*)source; + player = (Player*)target; + } + else + { + if(target->GetTypeId()!=TYPEID_UNIT && target->GetTypeId()!=TYPEID_GAMEOBJECT) + { + sLog.outError("SCRIPT_COMMAND_QUEST_EXPLORED call for non-creature and non-gameobject (TypeId: %u), skipping.",target->GetTypeId()); + break; + } + + if(source->GetTypeId()!=TYPEID_PLAYER) + { + sLog.outError("SCRIPT_COMMAND_QUEST_EXPLORED call for non-player(TypeId: %u), skipping.",source->GetTypeId()); + break; + } + + worldObject = (WorldObject*)target; + player = (Player*)source; + } + + // quest id and flags checked at script loading + if( (worldObject->GetTypeId()!=TYPEID_UNIT || ((Unit*)worldObject)->isAlive()) && + (step.script->datalong2==0 || worldObject->IsWithinDistInMap(player,float(step.script->datalong2))) ) + player->AreaExploredOrEventHappens(step.script->datalong); + else + player->FailQuest(step.script->datalong); + + break; + } + + case SCRIPT_COMMAND_ACTIVATE_OBJECT: + { + if(!source) + { + sLog.outError("SCRIPT_COMMAND_ACTIVATE_OBJECT must have source caster."); + break; + } + + if(!source->isType(TYPEMASK_UNIT)) + { + sLog.outError("SCRIPT_COMMAND_ACTIVATE_OBJECT source caster isn't unit (TypeId: %u), skipping.",source->GetTypeId()); + break; + } + + if(!target) + { + sLog.outError("SCRIPT_COMMAND_ACTIVATE_OBJECT call for NULL gameobject."); + break; + } + + if(target->GetTypeId()!=TYPEID_GAMEOBJECT) + { + sLog.outError("SCRIPT_COMMAND_ACTIVATE_OBJECT call for non-gameobject (TypeId: %u), skipping.",target->GetTypeId()); + break; + } + + Unit* caster = (Unit*)source; + + GameObject *go = (GameObject*)target; + + go->Use(caster); + break; + } + + case SCRIPT_COMMAND_REMOVE_AURA: + { + Object* cmdTarget = step.script->datalong2 ? source : target; + + if(!cmdTarget) + { + sLog.outError("SCRIPT_COMMAND_REMOVE_AURA call for NULL %s.",step.script->datalong2 ? "source" : "target"); + break; + } + + if(!cmdTarget->isType(TYPEMASK_UNIT)) + { + sLog.outError("SCRIPT_COMMAND_REMOVE_AURA %s isn't unit (TypeId: %u), skipping.",step.script->datalong2 ? "source" : "target",cmdTarget->GetTypeId()); + break; + } + + ((Unit*)cmdTarget)->RemoveAurasDueToSpell(step.script->datalong); + break; + } + + case SCRIPT_COMMAND_CAST_SPELL: + { + if(!source) + { + sLog.outError("SCRIPT_COMMAND_CAST_SPELL must have source caster."); + break; + } + + Object* cmdTarget = step.script->datalong2 & 0x01 ? source : target; + + if(!cmdTarget) + { + sLog.outError("SCRIPT_COMMAND_CAST_SPELL call for NULL %s.",step.script->datalong2 & 0x01 ? "source" : "target"); + break; + } + + if(!cmdTarget->isType(TYPEMASK_UNIT)) + { + sLog.outError("SCRIPT_COMMAND_CAST_SPELL %s isn't unit (TypeId: %u), skipping.",step.script->datalong2 & 0x01 ? "source" : "target",cmdTarget->GetTypeId()); + break; + } + + Unit* spellTarget = (Unit*)cmdTarget; + + Object* cmdSource = step.script->datalong2 & 0x02 ? target : source; + + if(!cmdSource) + { + sLog.outError("SCRIPT_COMMAND_CAST_SPELL call for NULL %s.",step.script->datalong2 & 0x02 ? "target" : "source"); + break; + } + + if(!cmdSource->isType(TYPEMASK_UNIT)) + { + sLog.outError("SCRIPT_COMMAND_CAST_SPELL %s isn't unit (TypeId: %u), skipping.",step.script->datalong2 & 0x02 ? "target" : "source", cmdSource->GetTypeId()); + break; + } + + Unit* spellSource = (Unit*)cmdSource; + + //TODO: when GO cast implemented, code below must be updated accordingly to also allow GO spell cast + spellSource->CastSpell(spellTarget,step.script->datalong,false); + + break; + } + + case SCRIPT_COMMAND_LOAD_PATH: + { + if(!source) + { + sLog.outError("SCRIPT_COMMAND_START_MOVE is tried to apply to NON-existing unit."); + break; + } + + if(!source->isType(TYPEMASK_UNIT)) + { + sLog.outError("SCRIPT_COMMAND_START_MOVE source mover isn't unit (TypeId: %u), skipping.",source->GetTypeId()); + break; + } + + if(!WaypointMgr.GetPath(step.script->datalong)) + { + sLog.outError("SCRIPT_COMMAND_START_MOVE source mover has an invallid path, skipping.", step.script->datalong2); + break; + } + + dynamic_cast<Unit*>(source)->GetMotionMaster()->MovePath(step.script->datalong, step.script->datalong2); + break; + } + + case SCRIPT_COMMAND_CALLSCRIPT_TO_UNIT: + { + if(!step.script->datalong || !step.script->datalong2) + { + sLog.outError("SCRIPT_COMMAND_CALLSCRIPT calls invallid db_script_id or lowguid not present: skipping."); + break; + } + //our target + Creature* target = NULL; + + if(source) //using grid searcher + { + CellPair p(Trinity::ComputeCellPair(((Unit*)source)->GetPositionX(), ((Unit*)source)->GetPositionY())); + Cell cell(p); + cell.data.Part.reserved = ALL_DISTRICT; + + //sLog.outDebug("Attempting to find Creature: Db GUID: %i", step.script->datalong); + Trinity::CreatureWithDbGUIDCheck target_check(((Unit*)source), step.script->datalong); + Trinity::CreatureSearcher<Trinity::CreatureWithDbGUIDCheck> checker(((Unit*)source), target, target_check); + + TypeContainerVisitor<Trinity::CreatureSearcher <Trinity::CreatureWithDbGUIDCheck>, GridTypeMapContainer > unit_checker(checker); + CellLock<GridReadGuard> cell_lock(cell, p); + cell_lock->Visit(cell_lock, unit_checker, *(((Unit*)source)->GetMap())); + } + else //check hashmap holders + { + if(CreatureData const* data = objmgr.GetCreatureData(step.script->datalong)) + target = ObjectAccessor::GetObjectInWorld<Creature>(data->mapid, data->posX, data->posY, MAKE_NEW_GUID(step.script->datalong, data->id, HIGHGUID_UNIT), target); + } + //sLog.outDebug("attempting to pass target..."); + if(!target) + break; + //sLog.outDebug("target passed"); + //Lets choose our ScriptMap map + ScriptMapMap *datamap = NULL; + switch(step.script->dataint) + { + case 1://QUEST END SCRIPTMAP + datamap = &sQuestEndScripts; + break; + case 2://QUEST START SCRIPTMAP + datamap = &sQuestStartScripts; + break; + case 3://SPELLS SCRIPTMAP + datamap = &sSpellScripts; + break; + case 4://GAMEOBJECTS SCRIPTMAP + datamap = &sGameObjectScripts; + break; + case 5://EVENTS SCRIPTMAP + datamap = &sEventScripts; + break; + case 6://WAYPOINTS SCRIPTMAP + datamap = &sWaypointScripts; + break; + default: + sLog.outError("SCRIPT_COMMAND_CALLSCRIPT ERROR: no scriptmap present... ignoring"); + break; + } + //if no scriptmap present... + if(!datamap) + break; + + uint32 script_id = step.script->datalong2; + //insert script into schedule but do not start it + ScriptsStart(*datamap, script_id, target, NULL/*, false*/); + break; + } + + case SCRIPT_COMMAND_KILL: + { + if(!source || ((Creature*)source)->isDead()) + break; + + ((Creature*)source)->DealDamage(((Creature*)source), ((Creature*)source)->GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false); + + switch(step.script->dataint) + { + case 0: break; //return false not remove corpse + case 1: ((Creature*)source)->RemoveCorpse(); break; + } + break; + } + + case SCRIPT_COMMAND_PLAY_SOUND: + { + if(!source) + { + sLog.outError("SCRIPT_COMMAND_PLAY_SOUND call for NULL creature."); + break; + } + + WorldObject* pSource = dynamic_cast<WorldObject*>(source); + if(!pSource) + { + sLog.outError("SCRIPT_COMMAND_PLAY_SOUND call for non-world object (TypeId: %u), skipping.",source->GetTypeId()); + break; + } + + // bitmask: 0/1=anyone/target, 0/2=with distance dependent + Player* pTarget = NULL; + if(step.script->datalong2 & 1) + { + if(!target) + { + sLog.outError("SCRIPT_COMMAND_PLAY_SOUND in targeted mode call for NULL target."); + break; + } + + if(target->GetTypeId()!=TYPEID_PLAYER) + { + sLog.outError("SCRIPT_COMMAND_PLAY_SOUND in targeted mode call for non-player (TypeId: %u), skipping.",target->GetTypeId()); + break; + } + + pTarget = (Player*)target; + } + + // bitmask: 0/1=anyone/target, 0/2=with distance dependent + if(step.script->datalong2 & 2) + pSource->PlayDistanceSound(step.script->datalong,pTarget); + else + pSource->PlayDirectSound(step.script->datalong,pTarget); + break; + } + default: + sLog.outError("Unknown script command %u called.",step.script->command); + break; + } + + m_scriptSchedule.erase(iter); + sWorld.DecreaseScheduledScriptCount(); + + iter = m_scriptSchedule.begin(); + } + return; +} + Creature* Map::GetCreature(uint64 guid) { diff --git a/src/game/Map.h b/src/game/Map.h index ed918125328..e9c303eaa8a 100644 --- a/src/game/Map.h +++ b/src/game/Map.h @@ -43,9 +43,12 @@ class WorldPacket; class InstanceData; class Group; class InstanceSave; +class Object; class WorldObject; class TempSummon; class CreatureGroup; +struct ScriptInfo; +struct ScriptAction; typedef ACE_RW_Thread_Mutex GridRWLock; @@ -398,6 +401,10 @@ class MANGOS_DLL_SPEC Map : public GridRefManager<NGridType>, public MaNGOS::Obj typedef MapRefManager PlayerList; PlayerList const& GetPlayers() const { return m_mapRefManager; } + //per-map script storage + void ScriptsStart(std::map<uint32, std::multimap<uint32, ScriptInfo> > const& scripts, uint32 id, Object* source, Object* target); + void ScriptCommandStart(ScriptInfo const& script, uint32 delay, Object* source, Object* target); + // must called with AddToWorld template<class T> void AddToActive(T* obj) { AddToActiveHelper(obj); } @@ -467,6 +474,7 @@ class MANGOS_DLL_SPEC Map : public GridRefManager<NGridType>, public MaNGOS::Obj void setGridObjectDataLoaded(bool pLoaded, uint32 x, uint32 y) { getNGrid(x,y)->setGridObjectDataLoaded(pLoaded); } void setNGrid(NGridType* grid, uint32 x, uint32 y); + void ScriptsProcess(); void UpdateActiveCells(const float &x, const float &y, const uint32 &t_diff); protected: @@ -508,6 +516,7 @@ class MANGOS_DLL_SPEC Map : public GridRefManager<NGridType>, public MaNGOS::Obj std::vector<Unit*> i_unitsToNotify; std::set<WorldObject *> i_objectsToRemove; std::map<WorldObject*, bool> i_objectsToSwitch; + std::multimap<time_t, ScriptAction> m_scriptSchedule; // Type specific code for add/remove to/from grid template<class T> diff --git a/src/game/Player.cpp b/src/game/Player.cpp index 6c7fb40c3fa..76e109683a9 100644 --- a/src/game/Player.cpp +++ b/src/game/Player.cpp @@ -12882,7 +12882,7 @@ void Player::AddQuest( Quest const *pQuest, Object *questGiver ) //starting initial quest script if(questGiver && pQuest->GetQuestStartScript()!=0) - sWorld.ScriptsStart(sQuestStartScripts, pQuest->GetQuestStartScript(), questGiver, this); + GetMap()->ScriptsStart(sQuestStartScripts, pQuest->GetQuestStartScript(), questGiver, this); // Some spells applied at quest activation SpellAreaForQuestMapBounds saBounds = spellmgr.GetSpellAreaForQuestMapBounds(quest_id,true); @@ -14142,7 +14142,7 @@ void Player::SendQuestReward( Quest const *pQuest, uint32 XP, Object * questGive GetSession()->SendPacket( &data ); if (pQuest->GetQuestCompleteScript() != 0) - sWorld.ScriptsStart(sQuestEndScripts, pQuest->GetQuestCompleteScript(), questGiver, this); + GetMap()->ScriptsStart(sQuestEndScripts, pQuest->GetQuestCompleteScript(), questGiver, this); } void Player::SendQuestFailed( uint32 quest_id ) diff --git a/src/game/SpellEffects.cpp b/src/game/SpellEffects.cpp index 2e683e05122..cf32a96f9f2 100644 --- a/src/game/SpellEffects.cpp +++ b/src/game/SpellEffects.cpp @@ -2483,7 +2483,7 @@ void Spell::EffectSendEvent(uint32 EffectIndex) we do not handle a flag dropping or clicking on flag in battleground by sendevent system */ sLog.outDebug("Spell ScriptStart %u for spellid %u in EffectSendEvent ", m_spellInfo->EffectMiscValue[EffectIndex], m_spellInfo->Id); - sWorld.ScriptsStart(sEventScripts, m_spellInfo->EffectMiscValue[EffectIndex], m_caster, focusObject); + m_caster->GetMap()->ScriptsStart(sEventScripts, m_spellInfo->EffectMiscValue[EffectIndex], m_caster, focusObject); } void Spell::EffectPowerBurn(uint32 i) @@ -2999,7 +2999,7 @@ void Spell::SendLoot(uint64 guid, LootType loottype) case GAMEOBJECT_TYPE_DOOR: case GAMEOBJECT_TYPE_BUTTON: gameObjTarget->UseDoorOrButton(); - sWorld.ScriptsStart(sGameObjectScripts, gameObjTarget->GetDBTableGUIDLow(), player, gameObjTarget); + player->GetMap()->ScriptsStart(sGameObjectScripts, gameObjTarget->GetDBTableGUIDLow(), player, gameObjTarget); return; case GAMEOBJECT_TYPE_QUESTGIVER: @@ -3019,7 +3019,7 @@ void Spell::SendLoot(uint64 guid, LootType loottype) if (gameObjTarget->GetGOInfo()->goober.eventId) { sLog.outDebug("Goober ScriptStart id %u for GO %u", gameObjTarget->GetGOInfo()->goober.eventId,gameObjTarget->GetDBTableGUIDLow()); - sWorld.ScriptsStart(sEventScripts, gameObjTarget->GetGOInfo()->goober.eventId, player, gameObjTarget); + player->GetMap()->ScriptsStart(sEventScripts, gameObjTarget->GetGOInfo()->goober.eventId, player, gameObjTarget); gameObjTarget->EventInform(gameObjTarget->GetGOInfo()->goober.eventId); } @@ -3030,7 +3030,7 @@ void Spell::SendLoot(uint64 guid, LootType loottype) return; Script->GOHello(player, gameObjTarget); - sWorld.ScriptsStart(sGameObjectScripts, gameObjTarget->GetDBTableGUIDLow(), player, gameObjTarget); + gameObjTarget->GetMap()->ScriptsStart(sGameObjectScripts, gameObjTarget->GetDBTableGUIDLow(), player, gameObjTarget); gameObjTarget->AddUniqueUse(player); gameObjTarget->SetLootState(GO_JUST_DEACTIVATED); @@ -3052,7 +3052,7 @@ void Spell::SendLoot(uint64 guid, LootType loottype) if (gameObjTarget->GetGOInfo()->chest.eventId) { sLog.outDebug("Chest ScriptStart id %u for GO %u", gameObjTarget->GetGOInfo()->chest.eventId,gameObjTarget->GetDBTableGUIDLow()); - sWorld.ScriptsStart(sEventScripts, gameObjTarget->GetGOInfo()->chest.eventId, player, gameObjTarget); + player->GetMap()->ScriptsStart(sEventScripts, gameObjTarget->GetGOInfo()->chest.eventId, player, gameObjTarget); } // triggering linked GO @@ -5501,7 +5501,7 @@ void Spell::EffectScriptEffect(uint32 effIndex) // normal DB scripted effect sLog.outDebug("Spell ScriptStart spellid %u in EffectScriptEffect ", m_spellInfo->Id); - sWorld.ScriptsStart(sSpellScripts, m_spellInfo->Id, m_caster, unitTarget); + m_caster->GetMap()->ScriptsStart(sSpellScripts, m_spellInfo->Id, m_caster, unitTarget); } void Spell::EffectSanctuary(uint32 /*i*/) @@ -5703,7 +5703,7 @@ void Spell::EffectActivateObject(uint32 effect_idx) int32 delay_secs = m_spellInfo->EffectMiscValue[effect_idx]; - sWorld.ScriptCommandStart(activateCommand, delay_secs, m_caster, gameObjTarget); + gameObjTarget->GetMap()->ScriptCommandStart(activateCommand, delay_secs, m_caster, gameObjTarget); } void Spell::EffectApplyGlyph(uint32 i) diff --git a/src/game/Transports.cpp b/src/game/Transports.cpp index b0a588509f4..b2a89805ea9 100644 --- a/src/game/Transports.cpp +++ b/src/game/Transports.cpp @@ -487,7 +487,7 @@ void Transport::CheckForEvent(uint32 entry, uint32 wp_id) { uint32 key = entry*100+wp_id; if(objmgr.TransportEventMap.find(key) != objmgr.TransportEventMap.end()) - sWorld.ScriptsStart(sEventScripts, objmgr.TransportEventMap[key], this, NULL); + GetMap()->ScriptsStart(sEventScripts, objmgr.TransportEventMap[key], this, NULL); } void Transport::Update(uint32 /*p_time*/) diff --git a/src/game/WaypointMovementGenerator.cpp b/src/game/WaypointMovementGenerator.cpp index 34be9d3179b..92fd9bca2d7 100644 --- a/src/game/WaypointMovementGenerator.cpp +++ b/src/game/WaypointMovementGenerator.cpp @@ -198,7 +198,7 @@ WaypointMovementGenerator<Creature>::Update(Creature &unit, const uint32 &diff) //note: disable "start" for mtmap if(node->event_id && rand()%100 < node->event_chance) - sWorld.ScriptsStart(sWaypointScripts, node->event_id, &unit, NULL, false); + unit.GetMap()->ScriptsStart(sWaypointScripts, node->event_id, &unit, NULL/*, false*/); MovementInform(unit); unit.UpdateWaypointID(i_currentNode); diff --git a/src/game/World.cpp b/src/game/World.cpp index db59f101389..932eb555f20 100644 --- a/src/game/World.cpp +++ b/src/game/World.cpp @@ -63,7 +63,6 @@ #include "GridNotifiersImpl.h" #include "CellImpl.h" #include "InstanceSaveMgr.h" -#include "WaypointManager.h" #include "Util.h" #include "Language.h" #include "CreatureGroups.h" @@ -83,14 +82,6 @@ float World::m_MaxVisibleDistanceInFlight = DEFAULT_VISIBILITY_DISTANCE; float World::m_VisibleUnitGreyDistance = 0; float World::m_VisibleObjectGreyDistance = 0; -struct ScriptAction -{ - uint64 sourceGUID; - uint64 targetGUID; - uint64 ownerGUID; // owner of source if source is item - ScriptInfo const* script; // pointer to static script data -}; - /// World constructor World::World() { @@ -106,6 +97,7 @@ World::World() m_MaxPlayerCount = 0; m_resultQueue = NULL; m_NextDailyQuestReset = 0; + m_scheduledScripts = 0; m_defaultDbcLocale = LOCALE_enUS; m_availableDbcLocaleMask = 0; @@ -1796,14 +1788,6 @@ void World::Update(uint32 diff) MapManager::Instance().DoDelayedMovesAndRemoves(); }*/ - ///- Process necessary scripts - if (!m_scriptSchedule.empty()) - { - RecordTimeDiff(NULL); - ScriptsProcess(); - RecordTimeDiff("UpdateScriptsProcess"); - } - sBattleGroundMgr.Update(diff); RecordTimeDiff("UpdateBattleGroundMgr"); @@ -1848,831 +1832,6 @@ void World::ForceGameEventUpdate() m_timers[WUPDATE_EVENTS].Reset(); } -/// Put scripts in the execution queue -void World::ScriptsStart(ScriptMapMap const& scripts, uint32 id, Object* source, Object* target, bool start) -{ - ///- Find the script map - ScriptMapMap::const_iterator s = scripts.find(id); - if (s == scripts.end()) - return; - - // prepare static data - uint64 sourceGUID = source ? source->GetGUID() : (uint64)0; //some script commands doesn't have source - uint64 targetGUID = target ? target->GetGUID() : (uint64)0; - uint64 ownerGUID = (source->GetTypeId()==TYPEID_ITEM) ? ((Item*)source)->GetOwnerGUID() : (uint64)0; - - ///- Schedule script execution for all scripts in the script map - ScriptMap const *s2 = &(s->second); - bool immedScript = false; - for (ScriptMap::const_iterator iter = s2->begin(); iter != s2->end(); ++iter) - { - ScriptAction sa; - sa.sourceGUID = sourceGUID; - sa.targetGUID = targetGUID; - sa.ownerGUID = ownerGUID; - - sa.script = &iter->second; - m_scriptSchedule.insert(std::pair<time_t, ScriptAction>(m_gameTime + iter->first, sa)); - if (iter->first == 0) - immedScript = true; - } - ///- If one of the effects should be immediate, launch the script execution - if (start && immedScript) - ScriptsProcess(); -} - -void World::ScriptCommandStart(ScriptInfo const& script, uint32 delay, Object* source, Object* target) -{ - // NOTE: script record _must_ exist until command executed - - // prepare static data - uint64 sourceGUID = source ? source->GetGUID() : (uint64)0; - uint64 targetGUID = target ? target->GetGUID() : (uint64)0; - uint64 ownerGUID = (source->GetTypeId()==TYPEID_ITEM) ? ((Item*)source)->GetOwnerGUID() : (uint64)0; - - ScriptAction sa; - sa.sourceGUID = sourceGUID; - sa.targetGUID = targetGUID; - sa.ownerGUID = ownerGUID; - - sa.script = &script; - m_scriptSchedule.insert(std::pair<time_t, ScriptAction>(m_gameTime + delay, sa)); - - ///- If effects should be immediate, launch the script execution - if(delay == 0) - ScriptsProcess(); -} - -/// Process queued scripts -void World::ScriptsProcess() -{ - if (m_scriptSchedule.empty()) - return; - - ///- Process overdue queued scripts - std::multimap<time_t, ScriptAction>::iterator iter = m_scriptSchedule.begin(); - // ok as multimap is a *sorted* associative container - while (!m_scriptSchedule.empty() && (iter->first <= m_gameTime)) - { - ScriptAction const& step = iter->second; - - Object* source = NULL; - - if(step.sourceGUID) - { - switch(GUID_HIPART(step.sourceGUID)) - { - case HIGHGUID_ITEM: - // case HIGHGUID_CONTAINER: ==HIGHGUID_ITEM - { - Player* player = HashMapHolder<Player>::Find(step.ownerGUID); - if(player) - source = player->GetItemByGuid(step.sourceGUID); - break; - } - case HIGHGUID_UNIT: - source = HashMapHolder<Creature>::Find(step.sourceGUID); - break; - case HIGHGUID_PET: - source = HashMapHolder<Pet>::Find(step.sourceGUID); - break; - case HIGHGUID_VEHICLE: - source = HashMapHolder<Vehicle>::Find(step.sourceGUID); - break; - case HIGHGUID_PLAYER: - source = HashMapHolder<Player>::Find(step.sourceGUID); - break; - case HIGHGUID_GAMEOBJECT: - source = HashMapHolder<GameObject>::Find(step.sourceGUID); - break; - case HIGHGUID_CORPSE: - source = HashMapHolder<Corpse>::Find(step.sourceGUID); - break; - case HIGHGUID_MO_TRANSPORT: - for (MapManager::TransportSet::iterator iter = MapManager::Instance().m_Transports.begin(); iter != MapManager::Instance().m_Transports.end(); ++iter) - { - if((*iter)->GetGUID() == step.sourceGUID) - { - source = reinterpret_cast<Object*>(*iter); - break; - } - } - break; - default: - sLog.outError("*_script source with unsupported high guid value %u",GUID_HIPART(step.sourceGUID)); - break; - } - } - - //if(source && !source->IsInWorld()) source = NULL; - - Object* target = NULL; - - if(step.targetGUID) - { - switch(GUID_HIPART(step.targetGUID)) - { - case HIGHGUID_UNIT: - target = HashMapHolder<Creature>::Find(step.targetGUID); - break; - case HIGHGUID_PET: - target = HashMapHolder<Pet>::Find(step.targetGUID); - break; - case HIGHGUID_VEHICLE: - target = HashMapHolder<Vehicle>::Find(step.targetGUID); - break; - case HIGHGUID_PLAYER: // empty GUID case also - target = HashMapHolder<Player>::Find(step.targetGUID); - break; - case HIGHGUID_GAMEOBJECT: - target = HashMapHolder<GameObject>::Find(step.targetGUID); - break; - case HIGHGUID_CORPSE: - target = HashMapHolder<Corpse>::Find(step.targetGUID); - break; - default: - sLog.outError("*_script source with unsupported high guid value %u",GUID_HIPART(step.targetGUID)); - break; - } - } - - //if(target && !target->IsInWorld()) target = NULL; - - switch (step.script->command) - { - case SCRIPT_COMMAND_TALK: - { - if(!source) - { - sLog.outError("SCRIPT_COMMAND_TALK call for NULL creature."); - break; - } - - if(source->GetTypeId()!=TYPEID_UNIT) - { - sLog.outError("SCRIPT_COMMAND_TALK call for non-creature (TypeId: %u), skipping.",source->GetTypeId()); - break; - } - if(step.script->datalong > 3) - { - sLog.outError("SCRIPT_COMMAND_TALK invalid chat type (%u), skipping.",step.script->datalong); - break; - } - - uint64 unit_target = target ? target->GetGUID() : 0; - - //datalong 0=normal say, 1=whisper, 2=yell, 3=emote text - switch(step.script->datalong) - { - case 0: // Say - ((Creature *)source)->Say(step.script->dataint, LANG_UNIVERSAL, unit_target); - break; - case 1: // Whisper - if(!unit_target) - { - sLog.outError("SCRIPT_COMMAND_TALK attempt to whisper (%u) NULL, skipping.",step.script->datalong); - break; - } - ((Creature *)source)->Whisper(step.script->dataint,unit_target); - break; - case 2: // Yell - ((Creature *)source)->Yell(step.script->dataint, LANG_UNIVERSAL, unit_target); - break; - case 3: // Emote text - ((Creature *)source)->TextEmote(step.script->dataint, unit_target); - break; - default: - break; // must be already checked at load - } - break; - } - - case SCRIPT_COMMAND_EMOTE: - if(!source) - { - sLog.outError("SCRIPT_COMMAND_EMOTE call for NULL creature."); - break; - } - - if(source->GetTypeId()!=TYPEID_UNIT) - { - sLog.outError("SCRIPT_COMMAND_EMOTE call for non-creature (TypeId: %u), skipping.",source->GetTypeId()); - break; - } - - ((Creature *)source)->HandleEmoteCommand(step.script->datalong); - break; - case SCRIPT_COMMAND_FIELD_SET: - if(!source) - { - sLog.outError("SCRIPT_COMMAND_FIELD_SET call for NULL object."); - break; - } - if(step.script->datalong <= OBJECT_FIELD_ENTRY || step.script->datalong >= source->GetValuesCount()) - { - sLog.outError("SCRIPT_COMMAND_FIELD_SET call for wrong field %u (max count: %u) in object (TypeId: %u).", - step.script->datalong,source->GetValuesCount(),source->GetTypeId()); - break; - } - - source->SetUInt32Value(step.script->datalong, step.script->datalong2); - break; - case SCRIPT_COMMAND_MOVE_TO: - if(!source) - { - sLog.outError("SCRIPT_COMMAND_MOVE_TO call for NULL creature."); - break; - } - - if(source->GetTypeId()!=TYPEID_UNIT) - { - sLog.outError("SCRIPT_COMMAND_MOVE_TO call for non-creature (TypeId: %u), skipping.",source->GetTypeId()); - break; - } - ((Creature*)source)->SendMonsterMoveWithSpeed(step.script->x, step.script->y, step.script->z, step.script->datalong2 ); - ((Creature*)source)->GetMap()->CreatureRelocation(((Creature*)source), step.script->x, step.script->y, step.script->z, 0); - break; - case SCRIPT_COMMAND_FLAG_SET: - if(!source) - { - sLog.outError("SCRIPT_COMMAND_FLAG_SET call for NULL object."); - break; - } - if(step.script->datalong <= OBJECT_FIELD_ENTRY || step.script->datalong >= source->GetValuesCount()) - { - sLog.outError("SCRIPT_COMMAND_FLAG_SET call for wrong field %u (max count: %u) in object (TypeId: %u).", - step.script->datalong,source->GetValuesCount(),source->GetTypeId()); - break; - } - - source->SetFlag(step.script->datalong, step.script->datalong2); - break; - case SCRIPT_COMMAND_FLAG_REMOVE: - if(!source) - { - sLog.outError("SCRIPT_COMMAND_FLAG_REMOVE call for NULL object."); - break; - } - if(step.script->datalong <= OBJECT_FIELD_ENTRY || step.script->datalong >= source->GetValuesCount()) - { - sLog.outError("SCRIPT_COMMAND_FLAG_REMOVE call for wrong field %u (max count: %u) in object (TypeId: %u).", - step.script->datalong,source->GetValuesCount(),source->GetTypeId()); - break; - } - - source->RemoveFlag(step.script->datalong, step.script->datalong2); - break; - - case SCRIPT_COMMAND_TELEPORT_TO: - { - // accept player in any one from target/source arg - if (!target && !source) - { - sLog.outError("SCRIPT_COMMAND_TELEPORT_TO call for NULL object."); - break; - } - - // must be only Player - if((!target || target->GetTypeId() != TYPEID_PLAYER) && (!source || source->GetTypeId() != TYPEID_PLAYER)) - { - sLog.outError("SCRIPT_COMMAND_TELEPORT_TO call for non-player (TypeIdSource: %u)(TypeIdTarget: %u), skipping.", source ? source->GetTypeId() : 0, target ? target->GetTypeId() : 0); - break; - } - - Player* pSource = target && target->GetTypeId() == TYPEID_PLAYER ? (Player*)target : (Player*)source; - - pSource->TeleportTo(step.script->datalong, step.script->x, step.script->y, step.script->z, step.script->o); - break; - } - - case SCRIPT_COMMAND_TEMP_SUMMON_CREATURE: - { - if(!step.script->datalong) // creature not specified - { - sLog.outError("SCRIPT_COMMAND_TEMP_SUMMON_CREATURE call for NULL creature."); - break; - } - - if(!source) - { - sLog.outError("SCRIPT_COMMAND_TEMP_SUMMON_CREATURE call for NULL world object."); - break; - } - - WorldObject* summoner = dynamic_cast<WorldObject*>(source); - - if(!summoner) - { - sLog.outError("SCRIPT_COMMAND_TEMP_SUMMON_CREATURE call for non-WorldObject (TypeId: %u), skipping.",source->GetTypeId()); - break; - } - - float x = step.script->x; - float y = step.script->y; - float z = step.script->z; - float o = step.script->o; - - Creature* pCreature = summoner->SummonCreature(step.script->datalong, x, y, z, o,TEMPSUMMON_TIMED_OR_DEAD_DESPAWN,step.script->datalong2); - if (!pCreature) - { - sLog.outError("SCRIPT_COMMAND_TEMP_SUMMON failed for creature (entry: %u).",step.script->datalong); - break; - } - - break; - } - - case SCRIPT_COMMAND_RESPAWN_GAMEOBJECT: - { - if(!step.script->datalong) // gameobject not specified - { - sLog.outError("SCRIPT_COMMAND_RESPAWN_GAMEOBJECT call for NULL gameobject."); - break; - } - - if(!source) - { - sLog.outError("SCRIPT_COMMAND_RESPAWN_GAMEOBJECT call for NULL world object."); - break; - } - - WorldObject* summoner = dynamic_cast<WorldObject*>(source); - - if(!summoner) - { - sLog.outError("SCRIPT_COMMAND_RESPAWN_GAMEOBJECT call for non-WorldObject (TypeId: %u), skipping.",source->GetTypeId()); - break; - } - - GameObject *go = NULL; - int32 time_to_despawn = step.script->datalong2<5 ? 5 : (int32)step.script->datalong2; - - CellPair p(Trinity::ComputeCellPair(summoner->GetPositionX(), summoner->GetPositionY())); - Cell cell(p); - cell.data.Part.reserved = ALL_DISTRICT; - - MaNGOS::GameObjectWithDbGUIDCheck go_check(*summoner,step.script->datalong); - MaNGOS::GameObjectSearcher<MaNGOS::GameObjectWithDbGUIDCheck> checker(summoner, go,go_check); - - TypeContainerVisitor<Trinity::GameObjectSearcher<Trinity::GameObjectWithDbGUIDCheck>, GridTypeMapContainer > object_checker(checker); - CellLock<GridReadGuard> cell_lock(cell, p); - cell_lock->Visit(cell_lock, object_checker, *summoner->GetMap()); - - if ( !go ) - { - sLog.outError("SCRIPT_COMMAND_RESPAWN_GAMEOBJECT failed for gameobject(guid: %u).", step.script->datalong); - break; - } - - if( go->GetGoType()==GAMEOBJECT_TYPE_FISHINGNODE || - go->GetGoType()==GAMEOBJECT_TYPE_DOOR || - go->GetGoType()==GAMEOBJECT_TYPE_BUTTON || - go->GetGoType()==GAMEOBJECT_TYPE_TRAP ) - { - sLog.outError("SCRIPT_COMMAND_RESPAWN_GAMEOBJECT can not be used with gameobject of type %u (guid: %u).", uint32(go->GetGoType()), step.script->datalong); - break; - } - - if( go->isSpawned() ) - break; //gameobject already spawned - - go->SetLootState(GO_READY); - go->SetRespawnTime(time_to_despawn); //despawn object in ? seconds - - go->GetMap()->Add(go); - break; - } - case SCRIPT_COMMAND_OPEN_DOOR: - { - if(!step.script->datalong) // door not specified - { - sLog.outError("SCRIPT_COMMAND_OPEN_DOOR call for NULL door."); - break; - } - - if(!source) - { - sLog.outError("SCRIPT_COMMAND_OPEN_DOOR call for NULL unit."); - break; - } - - if(!source->isType(TYPEMASK_UNIT)) // must be any Unit (creature or player) - { - sLog.outError("SCRIPT_COMMAND_OPEN_DOOR call for non-unit (TypeId: %u), skipping.",source->GetTypeId()); - break; - } - - Unit* caster = (Unit*)source; - - GameObject *door = NULL; - int32 time_to_close = step.script->datalong2 < 15 ? 15 : (int32)step.script->datalong2; - - CellPair p(Trinity::ComputeCellPair(caster->GetPositionX(), caster->GetPositionY())); - Cell cell(p); - cell.data.Part.reserved = ALL_DISTRICT; - - MaNGOS::GameObjectWithDbGUIDCheck go_check(*caster,step.script->datalong); - MaNGOS::GameObjectSearcher<MaNGOS::GameObjectWithDbGUIDCheck> checker(caster,door,go_check); - - TypeContainerVisitor<Trinity::GameObjectSearcher<Trinity::GameObjectWithDbGUIDCheck>, GridTypeMapContainer > object_checker(checker); - CellLock<GridReadGuard> cell_lock(cell, p); - cell_lock->Visit(cell_lock, object_checker, *caster->GetMap()); - - if (!door) - { - sLog.outError("SCRIPT_COMMAND_OPEN_DOOR failed for gameobject(guid: %u).", step.script->datalong); - break; - } - if (door->GetGoType() != GAMEOBJECT_TYPE_DOOR) - { - sLog.outError("SCRIPT_COMMAND_OPEN_DOOR failed for non-door(GoType: %u).", door->GetGoType()); - break; - } - - if (door->GetGoState() != GO_STATE_READY) - break; //door already open - - door->UseDoorOrButton(time_to_close); - - if(target && target->isType(TYPEMASK_GAMEOBJECT) && ((GameObject*)target)->GetGoType()==GAMEOBJECT_TYPE_BUTTON) - ((GameObject*)target)->UseDoorOrButton(time_to_close); - break; - } - case SCRIPT_COMMAND_CLOSE_DOOR: - { - if(!step.script->datalong) // guid for door not specified - { - sLog.outError("SCRIPT_COMMAND_CLOSE_DOOR call for NULL door."); - break; - } - - if(!source) - { - sLog.outError("SCRIPT_COMMAND_CLOSE_DOOR call for NULL unit."); - break; - } - - if(!source->isType(TYPEMASK_UNIT)) // must be any Unit (creature or player) - { - sLog.outError("SCRIPT_COMMAND_CLOSE_DOOR call for non-unit (TypeId: %u), skipping.",source->GetTypeId()); - break; - } - - Unit* caster = (Unit*)source; - - GameObject *door = NULL; - int32 time_to_open = step.script->datalong2 < 15 ? 15 : (int32)step.script->datalong2; - - CellPair p(Trinity::ComputeCellPair(caster->GetPositionX(), caster->GetPositionY())); - Cell cell(p); - cell.data.Part.reserved = ALL_DISTRICT; - - MaNGOS::GameObjectWithDbGUIDCheck go_check(*caster,step.script->datalong); - MaNGOS::GameObjectSearcher<MaNGOS::GameObjectWithDbGUIDCheck> checker(caster,door,go_check); - - TypeContainerVisitor<Trinity::GameObjectSearcher<Trinity::GameObjectWithDbGUIDCheck>, GridTypeMapContainer > object_checker(checker); - CellLock<GridReadGuard> cell_lock(cell, p); - cell_lock->Visit(cell_lock, object_checker, *caster->GetMap()); - - if ( !door ) - { - sLog.outError("SCRIPT_COMMAND_CLOSE_DOOR failed for gameobject(guid: %u).", step.script->datalong); - break; - } - if ( door->GetGoType() != GAMEOBJECT_TYPE_DOOR ) - { - sLog.outError("SCRIPT_COMMAND_CLOSE_DOOR failed for non-door(GoType: %u).", door->GetGoType()); - break; - } - - if( door->GetGoState() == GO_STATE_READY ) - break; //door already closed - - door->UseDoorOrButton(time_to_open); - - if(target && target->isType(TYPEMASK_GAMEOBJECT) && ((GameObject*)target)->GetGoType()==GAMEOBJECT_TYPE_BUTTON) - ((GameObject*)target)->UseDoorOrButton(time_to_open); - - break; - } - case SCRIPT_COMMAND_QUEST_EXPLORED: - { - if(!source) - { - sLog.outError("SCRIPT_COMMAND_QUEST_EXPLORED call for NULL source."); - break; - } - - if(!target) - { - sLog.outError("SCRIPT_COMMAND_QUEST_EXPLORED call for NULL target."); - break; - } - - // when script called for item spell casting then target == (unit or GO) and source is player - WorldObject* worldObject; - Player* player; - - if(target->GetTypeId()==TYPEID_PLAYER) - { - if(source->GetTypeId()!=TYPEID_UNIT && source->GetTypeId()!=TYPEID_GAMEOBJECT) - { - sLog.outError("SCRIPT_COMMAND_QUEST_EXPLORED call for non-creature and non-gameobject (TypeId: %u), skipping.",source->GetTypeId()); - break; - } - - worldObject = (WorldObject*)source; - player = (Player*)target; - } - else - { - if(target->GetTypeId()!=TYPEID_UNIT && target->GetTypeId()!=TYPEID_GAMEOBJECT) - { - sLog.outError("SCRIPT_COMMAND_QUEST_EXPLORED call for non-creature and non-gameobject (TypeId: %u), skipping.",target->GetTypeId()); - break; - } - - if(source->GetTypeId()!=TYPEID_PLAYER) - { - sLog.outError("SCRIPT_COMMAND_QUEST_EXPLORED call for non-player(TypeId: %u), skipping.",source->GetTypeId()); - break; - } - - worldObject = (WorldObject*)target; - player = (Player*)source; - } - - // quest id and flags checked at script loading - if( (worldObject->GetTypeId()!=TYPEID_UNIT || ((Unit*)worldObject)->isAlive()) && - (step.script->datalong2==0 || worldObject->IsWithinDistInMap(player,float(step.script->datalong2))) ) - player->AreaExploredOrEventHappens(step.script->datalong); - else - player->FailQuest(step.script->datalong); - - break; - } - - case SCRIPT_COMMAND_ACTIVATE_OBJECT: - { - if(!source) - { - sLog.outError("SCRIPT_COMMAND_ACTIVATE_OBJECT must have source caster."); - break; - } - - if(!source->isType(TYPEMASK_UNIT)) - { - sLog.outError("SCRIPT_COMMAND_ACTIVATE_OBJECT source caster isn't unit (TypeId: %u), skipping.",source->GetTypeId()); - break; - } - - if(!target) - { - sLog.outError("SCRIPT_COMMAND_ACTIVATE_OBJECT call for NULL gameobject."); - break; - } - - if(target->GetTypeId()!=TYPEID_GAMEOBJECT) - { - sLog.outError("SCRIPT_COMMAND_ACTIVATE_OBJECT call for non-gameobject (TypeId: %u), skipping.",target->GetTypeId()); - break; - } - - Unit* caster = (Unit*)source; - - GameObject *go = (GameObject*)target; - - go->Use(caster); - break; - } - - case SCRIPT_COMMAND_REMOVE_AURA: - { - Object* cmdTarget = step.script->datalong2 ? source : target; - - if(!cmdTarget) - { - sLog.outError("SCRIPT_COMMAND_REMOVE_AURA call for NULL %s.",step.script->datalong2 ? "source" : "target"); - break; - } - - if(!cmdTarget->isType(TYPEMASK_UNIT)) - { - sLog.outError("SCRIPT_COMMAND_REMOVE_AURA %s isn't unit (TypeId: %u), skipping.",step.script->datalong2 ? "source" : "target",cmdTarget->GetTypeId()); - break; - } - - ((Unit*)cmdTarget)->RemoveAurasDueToSpell(step.script->datalong); - break; - } - - case SCRIPT_COMMAND_CAST_SPELL: - { - if(!source) - { - sLog.outError("SCRIPT_COMMAND_CAST_SPELL must have source caster."); - break; - } - - Object* cmdTarget = step.script->datalong2 & 0x01 ? source : target; - - if(!cmdTarget) - { - sLog.outError("SCRIPT_COMMAND_CAST_SPELL call for NULL %s.",step.script->datalong2 & 0x01 ? "source" : "target"); - break; - } - - if(!cmdTarget->isType(TYPEMASK_UNIT)) - { - sLog.outError("SCRIPT_COMMAND_CAST_SPELL %s isn't unit (TypeId: %u), skipping.",step.script->datalong2 & 0x01 ? "source" : "target",cmdTarget->GetTypeId()); - break; - } - - Unit* spellTarget = (Unit*)cmdTarget; - - Object* cmdSource = step.script->datalong2 & 0x02 ? target : source; - - if(!cmdSource) - { - sLog.outError("SCRIPT_COMMAND_CAST_SPELL call for NULL %s.",step.script->datalong2 & 0x02 ? "target" : "source"); - break; - } - - if(!cmdSource->isType(TYPEMASK_UNIT)) - { - sLog.outError("SCRIPT_COMMAND_CAST_SPELL %s isn't unit (TypeId: %u), skipping.",step.script->datalong2 & 0x02 ? "target" : "source", cmdSource->GetTypeId()); - break; - } - - Unit* spellSource = (Unit*)cmdSource; - - //TODO: when GO cast implemented, code below must be updated accordingly to also allow GO spell cast - spellSource->CastSpell(spellTarget,step.script->datalong,false); - - break; - } - - case SCRIPT_COMMAND_LOAD_PATH: - { - if(!source) - { - sLog.outError("SCRIPT_COMMAND_START_MOVE is tried to apply to NON-existing unit."); - break; - } - - if(!source->isType(TYPEMASK_UNIT)) - { - sLog.outError("SCRIPT_COMMAND_START_MOVE source mover isn't unit (TypeId: %u), skipping.",source->GetTypeId()); - break; - } - - if(!WaypointMgr.GetPath(step.script->datalong)) - { - sLog.outError("SCRIPT_COMMAND_START_MOVE source mover has an invallid path, skipping.", step.script->datalong2); - break; - } - - dynamic_cast<Unit*>(source)->GetMotionMaster()->MovePath(step.script->datalong, step.script->datalong2); - break; - } - - case SCRIPT_COMMAND_CALLSCRIPT_TO_UNIT: - { - if(!step.script->datalong || !step.script->datalong2) - { - sLog.outError("SCRIPT_COMMAND_CALLSCRIPT calls invallid db_script_id or lowguid not present: skipping."); - break; - } - //our target - Creature* target = NULL; - - if(source) //using grid searcher - { - CellPair p(Trinity::ComputeCellPair(((Unit*)source)->GetPositionX(), ((Unit*)source)->GetPositionY())); - Cell cell(p); - cell.data.Part.reserved = ALL_DISTRICT; - - //sLog.outDebug("Attempting to find Creature: Db GUID: %i", step.script->datalong); - Trinity::CreatureWithDbGUIDCheck target_check(((Unit*)source), step.script->datalong); - Trinity::CreatureSearcher<Trinity::CreatureWithDbGUIDCheck> checker(((Unit*)source), target, target_check); - - TypeContainerVisitor<Trinity::CreatureSearcher <Trinity::CreatureWithDbGUIDCheck>, GridTypeMapContainer > unit_checker(checker); - CellLock<GridReadGuard> cell_lock(cell, p); - cell_lock->Visit(cell_lock, unit_checker, *(((Unit*)source)->GetMap())); - } - else //check hashmap holders - { - if(CreatureData const* data = objmgr.GetCreatureData(step.script->datalong)) - target = ObjectAccessor::GetObjectInWorld<Creature>(data->mapid, data->posX, data->posY, MAKE_NEW_GUID(step.script->datalong, data->id, HIGHGUID_UNIT), target); - } - //sLog.outDebug("attempting to pass target..."); - if(!target) - break; - //sLog.outDebug("target passed"); - //Lets choose our ScriptMap map - ScriptMapMap *datamap = NULL; - switch(step.script->dataint) - { - case 1://QUEST END SCRIPTMAP - datamap = &sQuestEndScripts; - break; - case 2://QUEST START SCRIPTMAP - datamap = &sQuestStartScripts; - break; - case 3://SPELLS SCRIPTMAP - datamap = &sSpellScripts; - break; - case 4://GAMEOBJECTS SCRIPTMAP - datamap = &sGameObjectScripts; - break; - case 5://EVENTS SCRIPTMAP - datamap = &sEventScripts; - break; - case 6://WAYPOINTS SCRIPTMAP - datamap = &sWaypointScripts; - break; - default: - sLog.outError("SCRIPT_COMMAND_CALLSCRIPT ERROR: no scriptmap present... ignoring"); - break; - } - //if no scriptmap present... - if(!datamap) - break; - - uint32 script_id = step.script->datalong2; - //insert script into schedule but do not start it - ScriptsStart(*datamap, script_id, target, NULL, false); - break; - } - - case SCRIPT_COMMAND_KILL: - { - if(!source || ((Creature*)source)->isDead()) - break; - - ((Creature*)source)->DealDamage(((Creature*)source), ((Creature*)source)->GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false); - - switch(step.script->dataint) - { - case 0: break; //return false not remove corpse - case 1: ((Creature*)source)->RemoveCorpse(); break; - } - break; - } - - case SCRIPT_COMMAND_PLAY_SOUND: - { - if(!source) - { - sLog.outError("SCRIPT_COMMAND_PLAY_SOUND call for NULL creature."); - break; - } - - WorldObject* pSource = dynamic_cast<WorldObject*>(source); - if(!pSource) - { - sLog.outError("SCRIPT_COMMAND_PLAY_SOUND call for non-world object (TypeId: %u), skipping.",source->GetTypeId()); - break; - } - - // bitmask: 0/1=anyone/target, 0/2=with distance dependent - Player* pTarget = NULL; - if(step.script->datalong2 & 1) - { - if(!target) - { - sLog.outError("SCRIPT_COMMAND_PLAY_SOUND in targeted mode call for NULL target."); - break; - } - - if(target->GetTypeId()!=TYPEID_PLAYER) - { - sLog.outError("SCRIPT_COMMAND_PLAY_SOUND in targeted mode call for non-player (TypeId: %u), skipping.",target->GetTypeId()); - break; - } - - pTarget = (Player*)target; - } - - // bitmask: 0/1=anyone/target, 0/2=with distance dependent - if(step.script->datalong2 & 2) - pSource->PlayDistanceSound(step.script->datalong,pTarget); - else - pSource->PlayDirectSound(step.script->datalong,pTarget); - break; - } - default: - sLog.outError("Unknown script command %u called.",step.script->command); - break; - } - - m_scriptSchedule.erase(iter); - - iter = m_scriptSchedule.begin(); - } - return; -} - /// Send a packet to all players (except self if mentioned) void World::SendGlobalMessage(WorldPacket *packet, WorldSession *self, uint32 team) { diff --git a/src/game/World.h b/src/game/World.h index 4864977b029..78d4c181b4a 100644 --- a/src/game/World.h +++ b/src/game/World.h @@ -29,6 +29,7 @@ #include "Timer.h" #include "Policies/Singleton.h" #include "SharedDefines.h" +#include "ace/Atomic_Op.h" #include <map> #include <set> @@ -553,9 +554,10 @@ class World BanReturn BanAccount(BanMode mode, std::string nameOrIP, std::string duration, std::string reason, std::string author); bool RemoveBanAccount(BanMode mode, std::string nameOrIP); - void ScriptsStart(std::map<uint32, std::multimap<uint32, ScriptInfo> > const& scripts, uint32 id, Object* source, Object* target, bool start = true); - void ScriptCommandStart(ScriptInfo const& script, uint32 delay, Object* source, Object* target); - bool IsScriptScheduled() const { return !m_scriptSchedule.empty(); } + uint32 IncreaseScheduledScriptsCount() { return (uint32)++m_scheduledScripts; } + uint32 DecreaseScheduledScriptCount() { return (uint32)--m_scheduledScripts; } + uint32 DecreaseScheduledScriptCount(size_t count) { return (uint32)(m_scheduledScripts -= count); } + bool IsScriptScheduled() const { return m_scheduledScripts > 0; } bool IsAllowedMap(uint32 mapid) { return m_forbiddenMapIds.count(mapid) == 0 ;} @@ -592,7 +594,6 @@ class World void RecordTimeDiff(const char * text, ...); protected: void _UpdateGameTime(); - void ScriptsProcess(); // callback for UpdateRealmCharacters void _UpdateRealmCharCount(QueryResult *resultCharCount, uint32 accountId); @@ -606,6 +607,9 @@ class World bool m_isClosed; + //atomic op counter for active scripts amount + ACE_Atomic_Op<ACE_Thread_Mutex, long> m_scheduledScripts; + time_t m_startTime; time_t m_gameTime; IntervalTimer m_timers[WUPDATE_COUNT]; @@ -626,8 +630,6 @@ class World uint32 m_PlayerCount; uint32 m_MaxPlayerCount; - std::multimap<time_t, ScriptAction> m_scriptSchedule; - std::string m_newCharString; float rate_values[MAX_RATES]; |