diff --git a/doc/script_commands.txt b/doc/script_commands.txt index 2ab536e15..a621ec8ce 100644 --- a/doc/script_commands.txt +++ b/doc/script_commands.txt @@ -206,7 +206,8 @@ Where "A -> B" means that the command is executed from A with B as target. 16 SCRIPT_COMMAND_PLAY_SOUND source = any object, target=any/player * datalong = sound_id - * datalong2 (bitmask: 0/1=anyone/target, 0/2=with distance dependent, so 1|2 = 3 is target with distance dependent) + * datalong2 (bitmask: 0/1=target-player, 0/2=with distance dependent, 0/4=map wide, 0/8=zone wide; + so 1|2 = 3 is target with distance dependent) 17 SCRIPT_COMMAND_CREATE_ITEM source or target must be player * datalong = item entry diff --git a/src/game/ScriptMgr.cpp b/src/game/ScriptMgr.cpp index f419e141d..a4f84eab8 100644 --- a/src/game/ScriptMgr.cpp +++ b/src/game/ScriptMgr.cpp @@ -17,7 +17,7 @@ */ #include "ScriptMgr.h" -#include "Policies/SingletonImp.h" +#include "Policies/Singleton.h" #include "Log.h" #include "ProgressBar.h" #include "ObjectMgr.h" @@ -28,6 +28,9 @@ #include "Cell.h" #include "CellImpl.h" #include "SQLStorages.h" +#include "BattleGround/BattleGround.h" +#include "OutdoorPvP/OutdoorPvP.h" +#include "WaypointMovementGenerator.h" #include "revision_nr.h" @@ -38,6 +41,7 @@ ScriptMapMapName sGameObjectScripts; ScriptMapMapName sGameObjectTemplateScripts; ScriptMapMapName sEventScripts; ScriptMapMapName sGossipScripts; +ScriptMapMapName sCreatureDeathScripts; ScriptMapMapName sCreatureMovementScripts; INSTANTIATE_SINGLETON_1(ScriptMgr); @@ -368,7 +372,7 @@ void ScriptMgr::LoadScripts(ScriptMapMapName& scripts, const char* tablename) info->type == GAMEOBJECT_TYPE_BUTTON || info->type == GAMEOBJECT_TYPE_TRAP) { - sLog.outErrorDb("Table `%s` have gameobject type (%u) unsupported by command SCRIPT_COMMAND_RESPAWN_GAMEOBJECT for script id %u", tablename, info->id, tmp.id); + sLog.outErrorDb("Table `%s` have gameobject type (%u) unsupported by command SCRIPT_COMMAND_RESPAWN_GAMEOBJECT for script id %u", tablename, info->type, tmp.id); continue; } break; @@ -458,6 +462,11 @@ void ScriptMgr::LoadScripts(ScriptMapMapName& scripts, const char* tablename) tablename, tmp.playSound.soundId, tmp.id); continue; } + // bitmask: 0/1=target-player, 0/2=with distance dependent, 0/4=map wide, 0/8=zone wide + if (tmp.playSound.flags & ~(1 | 2 | 4 | 8)) + sLog.outErrorDb("Table `%s` using unsupported sound flags (datalong2: %u) in SCRIPT_COMMAND_PLAY_SOUND for script id %u, unsupported flags will be ignored", tablename, tmp.playSound.flags, tmp.id); + if ((tmp.playSound.flags & (1 | 2)) > 0 && (tmp.playSound.flags & (4 | 8)) > 0) + sLog.outErrorDb("Table `%s` uses sound flags (datalong2: %u) in SCRIPT_COMMAND_PLAY_SOUND for script id %u, combining (1|2) with (4|8) makes no sense", tablename, tmp.playSound.flags, tmp.id); break; } case SCRIPT_COMMAND_CREATE_ITEM: // 17 @@ -622,6 +631,17 @@ void ScriptMgr::LoadScripts(ScriptMapMapName& scripts, const char* tablename) } break; } + case SCRIPT_COMMAND_TERMINATE_SCRIPT: // 31 + { + if (tmp.terminateScript.npcEntry && !ObjectMgr::GetCreatureTemplate(tmp.terminateScript.npcEntry)) + { + sLog.outErrorDb("Table `%s` has datalong = %u in SCRIPT_COMMAND_TERMINATE_SCRIPT for script id %u, but this npc entry does not exist.", tablename, tmp.sendTaxiPath.taxiPathId, tmp.id); + continue; + } + break; + } + case SCRIPT_COMMAND_PAUSE_WAYPOINTS: // 32 + break; default: { sLog.outErrorDb("Table `%s` unknown command %u, skipping.", tablename, tmp.command); @@ -648,55 +668,55 @@ void ScriptMgr::LoadScripts(ScriptMapMapName& scripts, const char* tablename) void ScriptMgr::LoadGameObjectScripts() { - LoadScripts(sGameObjectScripts, "gameobject_scripts"); + LoadScripts(sGameObjectScripts, "dbscripts_on_go_use"); // check ids for (ScriptMapMap::const_iterator itr = sGameObjectScripts.second.begin(); itr != sGameObjectScripts.second.end(); ++itr) { if (!sObjectMgr.GetGOData(itr->first)) - sLog.outErrorDb("Table `gameobject_scripts` has not existing gameobject (GUID: %u) as script id", itr->first); + sLog.outErrorDb("Table `dbscripts_on_go_use` has not existing gameobject (GUID: %u) as script id", itr->first); } } void ScriptMgr::LoadGameObjectTemplateScripts() { - LoadScripts(sGameObjectTemplateScripts, "gameobject_template_scripts"); + LoadScripts(sGameObjectTemplateScripts, "dbscripts_on_go_template_use"); // check ids for (ScriptMapMap::const_iterator itr = sGameObjectTemplateScripts.second.begin(); itr != sGameObjectTemplateScripts.second.end(); ++itr) { if (!sObjectMgr.GetGameObjectInfo(itr->first)) - sLog.outErrorDb("Table `gameobject_template_scripts` has not existing gameobject (Entry: %u) as script id", itr->first); + sLog.outErrorDb("Table `dbscripts_on_go_template_use` has not existing gameobject (Entry: %u) as script id", itr->first); } } void ScriptMgr::LoadQuestEndScripts() { - LoadScripts(sQuestEndScripts, "quest_end_scripts"); + LoadScripts(sQuestEndScripts, "dbscripts_on_quest_end"); // check ids for (ScriptMapMap::const_iterator itr = sQuestEndScripts.second.begin(); itr != sQuestEndScripts.second.end(); ++itr) { if (!sObjectMgr.GetQuestTemplate(itr->first)) - sLog.outErrorDb("Table `quest_end_scripts` has not existing quest (Id: %u) as script id", itr->first); + sLog.outErrorDb("Table `dbscripts_on_quest_end` has not existing quest (Id: %u) as script id", itr->first); } } void ScriptMgr::LoadQuestStartScripts() { - LoadScripts(sQuestStartScripts, "quest_start_scripts"); + LoadScripts(sQuestStartScripts, "dbscripts_on_quest_start"); // check ids for (ScriptMapMap::const_iterator itr = sQuestStartScripts.second.begin(); itr != sQuestStartScripts.second.end(); ++itr) { if (!sObjectMgr.GetQuestTemplate(itr->first)) - sLog.outErrorDb("Table `quest_start_scripts` has not existing quest (Id: %u) as script id", itr->first); + sLog.outErrorDb("Table `dbscripts_on_quest_start` has not existing quest (Id: %u) as script id", itr->first); } } void ScriptMgr::LoadSpellScripts() { - LoadScripts(sSpellScripts, "spell_scripts"); + LoadScripts(sSpellScripts, "dbscripts_on_spell"); // check ids for (ScriptMapMap::const_iterator itr = sSpellScripts.second.begin(); itr != sSpellScripts.second.end(); ++itr) @@ -704,7 +724,7 @@ void ScriptMgr::LoadSpellScripts() SpellEntry const* spellInfo = sSpellStore.LookupEntry(itr->first); if (!spellInfo) { - sLog.outErrorDb("Table `spell_scripts` has not existing spell (Id: %u) as script id", itr->first); + sLog.outErrorDb("Table `dbscripts_on_spell` has not existing spell (Id: %u) as script id", itr->first); continue; } @@ -720,97 +740,53 @@ void ScriptMgr::LoadSpellScripts() } if (!found) - sLog.outErrorDb("Table `spell_scripts` has unsupported spell (Id: %u)", itr->first); + sLog.outErrorDb("Table `dbscripts_on_spell` has unsupported spell (Id: %u)", itr->first); } } void ScriptMgr::LoadEventScripts() { - LoadScripts(sEventScripts, "event_scripts"); + LoadScripts(sEventScripts, "dbscripts_on_event"); - std::set evt_scripts; - - // Load all possible script entries from gameobjects - for (uint32 i = 1; i < sGOStorage.MaxEntry; ++i) - { - if (GameObjectInfo const* goInfo = sGOStorage.LookupEntry(i)) - { - if (uint32 eventId = goInfo->GetEventScriptId()) - evt_scripts.insert(eventId); - - if (goInfo->type == GAMEOBJECT_TYPE_CAPTURE_POINT) - { - evt_scripts.insert(goInfo->capturePoint.neutralEventID1); - evt_scripts.insert(goInfo->capturePoint.neutralEventID2); - evt_scripts.insert(goInfo->capturePoint.contestedEventID1); - evt_scripts.insert(goInfo->capturePoint.contestedEventID2); - evt_scripts.insert(goInfo->capturePoint.progressEventID1); - evt_scripts.insert(goInfo->capturePoint.progressEventID2); - evt_scripts.insert(goInfo->capturePoint.winEventID1); - evt_scripts.insert(goInfo->capturePoint.winEventID2); - } - } - } - - // Load all possible script entries from spells - for (uint32 i = 1; i < sSpellStore.GetNumRows(); ++i) - { - SpellEntry const* spell = sSpellStore.LookupEntry(i); - if (spell) - { - for (int j = 0; j < MAX_EFFECT_INDEX; ++j) - { - SpellEffectEntry const* spellEffect = spell->GetSpellEffect(SpellEffectIndex(j)); - if (!spellEffect) - continue; - - if (spellEffect->Effect == SPELL_EFFECT_SEND_EVENT) - { - if (spellEffect->EffectMiscValue) - evt_scripts.insert(spellEffect->EffectMiscValue); - } - } - } - } - - for (size_t path_idx = 0; path_idx < sTaxiPathNodesByPath.size(); ++path_idx) - { - for (size_t node_idx = 0; node_idx < sTaxiPathNodesByPath[path_idx].size(); ++node_idx) - { - TaxiPathNodeEntry const& node = sTaxiPathNodesByPath[path_idx][node_idx]; - - if (node.arrivalEventID) - evt_scripts.insert(node.arrivalEventID); - - if (node.departureEventID) - evt_scripts.insert(node.departureEventID); - } - } + std::set eventIds; // Store possible event ids + CollectPossibleEventIds(eventIds); // Then check if all scripts are in above list of possible script entries for (ScriptMapMap::const_iterator itr = sEventScripts.second.begin(); itr != sEventScripts.second.end(); ++itr) { - std::set::const_iterator itr2 = evt_scripts.find(itr->first); - if (itr2 == evt_scripts.end()) - sLog.outErrorDb("Table `event_scripts` has script (Id: %u) not referring to any gameobject_template type 10 data2 field, type 3 data6 field, type 13 data 2 field, type 29 or any spell effect %u or path taxi node data", + std::set::const_iterator itr2 = eventIds.find(itr->first); + if (itr2 == eventIds.end()) + sLog.outErrorDb("Table `dbscripts_on_event` has script (Id: %u) not referring to any fitting gameobject_template or any spell effect %u or path taxi node data", itr->first, SPELL_EFFECT_SEND_EVENT); } } void ScriptMgr::LoadGossipScripts() { - LoadScripts(sGossipScripts, "gossip_scripts"); + LoadScripts(sGossipScripts, "dbscripts_on_gossip"); // checks are done in LoadGossipMenuItems and LoadGossipMenu } void ScriptMgr::LoadCreatureMovementScripts() { - LoadScripts(sCreatureMovementScripts, "creature_movement_scripts"); + LoadScripts(sCreatureMovementScripts, "dbscripts_on_creature_movement"); // checks are done in WaypointManager::Load } +void ScriptMgr::LoadCreatureDeathScripts() +{ + LoadScripts(sCreatureDeathScripts, "dbscripts_on_creature_death"); + + // check ids + for(ScriptMapMap::const_iterator itr = sCreatureDeathScripts.second.begin(); itr != sCreatureDeathScripts.second.end(); ++itr) + { + if (!sObjectMgr.GetCreatureTemplate(itr->first)) + sLog.outErrorDb("Table `dbscripts_on_creature_death` has not existing creature (Entry: %u) as script id", itr->first); + } +} + void ScriptMgr::LoadDbScriptStrings() { sObjectMgr.LoadMangosStrings(WorldDatabase, "db_script_string", MIN_DB_SCRIPT_STRING_ID, MAX_DB_SCRIPT_STRING_ID); @@ -828,6 +804,7 @@ void ScriptMgr::LoadDbScriptStrings() CheckScriptTexts(sGameObjectTemplateScripts, ids); CheckScriptTexts(sEventScripts, ids); CheckScriptTexts(sGossipScripts, ids); + CheckScriptTexts(sCreatureDeathScripts, ids); CheckScriptTexts(sCreatureMovementScripts, ids); sWaypointMgr.CheckTextsExistance(ids); @@ -1019,23 +996,32 @@ Player* ScriptAction::GetPlayerTargetOrSourceAndLog(WorldObject* pSource, WorldO } /// Handle one Script Step -void ScriptAction::HandleScriptStep() +// Return true if and only if further parts of this script shall be skipped +bool ScriptAction::HandleScriptStep() { - Object* source = NULL; - Object* target = NULL; - if (!GetScriptCommandObject(m_sourceGuid, true, source)) - return; - if (!GetScriptCommandObject(m_targetGuid, false, target)) - return; + WorldObject* pSource; + WorldObject* pTarget; + Object* pSourceOrItem; // Stores a provided pSource (if exists as WorldObject) or source-item - // Give some debug log output for easier use - DEBUG_LOG("DB-SCRIPTS: Process table `%s` id %u, command %u for source %s (%sin world), target %s (%sin world)", m_table, m_script->id, m_script->command, m_sourceGuid.GetString().c_str(), source ? "" : "not ", m_targetGuid.GetString().c_str(), target ? "" : "not "); + { // Add scope for source & target variables so that they are not used below + Object* source = NULL; + Object* target = NULL; + if (!GetScriptCommandObject(m_sourceGuid, true, source)) + return false; + if (!GetScriptCommandObject(m_targetGuid, false, target)) + return false; - // Get expected source and target (if defined with buddy) - WorldObject* pSource = source && source->isType(TYPEMASK_WORLDOBJECT) ? (WorldObject*)source : NULL; - WorldObject* pTarget = target && target->isType(TYPEMASK_WORLDOBJECT) ? (WorldObject*)target : NULL; - if (!GetScriptProcessTargets(pSource, pTarget, pSource, pTarget)) - return; + // Give some debug log output for easier use + DEBUG_LOG("DB-SCRIPTS: Process table `%s` id %u, command %u for source %s (%sin world), target %s (%sin world)", m_table, m_script->id, m_script->command, m_sourceGuid.GetString().c_str(), source ? "" : "not ", m_targetGuid.GetString().c_str(), target ? "" : "not "); + + // Get expected source and target (if defined with buddy) + pSource = source && source->isType(TYPEMASK_WORLDOBJECT) ? (WorldObject*)source : NULL; + pTarget = target && target->isType(TYPEMASK_WORLDOBJECT) ? (WorldObject*)target : NULL; + if (!GetScriptProcessTargets(pSource, pTarget, pSource, pTarget)) + return false; + + pSourceOrItem = pSource ? pSource : (source && source->isType(TYPEMASK_ITEM) ? source : NULL); + } switch (m_script->command) { @@ -1111,20 +1097,18 @@ void ScriptAction::HandleScriptStep() break; } case SCRIPT_COMMAND_FIELD_SET: // 2 - // TODO - if (!source) + if (!pSourceOrItem) { sLog.outError(" DB-SCRIPTS: Process table `%s` id %u, command %u call for NULL object.", m_table, m_script->id, m_script->command); break; } - - if (m_script->setField.fieldId <= OBJECT_FIELD_ENTRY || m_script->setField.fieldId >= source->GetValuesCount()) + if (m_script->setField.fieldId <= OBJECT_FIELD_ENTRY || m_script->setField.fieldId >= pSourceOrItem->GetValuesCount()) { - sLog.outError(" DB-SCRIPTS: Process table `%s` id %u, command %u call for wrong field %u (max count: %u) in object (TypeId: %u).", m_table, m_script->id, m_script->command, m_script->setField.fieldId, source->GetValuesCount(), source->GetTypeId()); + sLog.outError(" DB-SCRIPTS: Process table `%s` id %u, command %u call for wrong field %u (max count: %u) in %s.", + m_table, m_script->id, m_script->command, m_script->setField.fieldId, pSourceOrItem->GetValuesCount(), pSourceOrItem->GetGuidStr().c_str()); break; } - - source->SetUInt32Value(m_script->setField.fieldId, m_script->setField.fieldValue); + pSourceOrItem->SetUInt32Value(m_script->setField.fieldId, m_script->setField.fieldValue); break; case SCRIPT_COMMAND_MOVE_TO: // 3 { @@ -1158,36 +1142,32 @@ void ScriptAction::HandleScriptStep() break; } case SCRIPT_COMMAND_FLAG_SET: // 4 - // TODO - if (!source) + if (!pSourceOrItem) { sLog.outError("SCRIPT_COMMAND_FLAG_SET (script id %u) call for NULL object.", m_script->id); break; } - if (m_script->setFlag.fieldId <= OBJECT_FIELD_ENTRY || m_script->setFlag.fieldId >= source->GetValuesCount()) + if (m_script->setFlag.fieldId <= OBJECT_FIELD_ENTRY || m_script->setFlag.fieldId >= pSourceOrItem->GetValuesCount()) { - sLog.outError("SCRIPT_COMMAND_FLAG_SET (script id %u) call for wrong field %u (max count: %u) in object (TypeId: %u).", - m_script->id, m_script->setFlag.fieldId, source->GetValuesCount(), source->GetTypeId()); + sLog.outError("SCRIPT_COMMAND_FLAG_SET (script id %u) call for wrong field %u (max count: %u) in %s.", + m_script->id, m_script->setFlag.fieldId, pSourceOrItem->GetValuesCount(), pSourceOrItem->GetGuidStr().c_str()); break; } - - source->SetFlag(m_script->setFlag.fieldId, m_script->setFlag.fieldValue); + pSourceOrItem->SetFlag(m_script->setFlag.fieldId, m_script->setFlag.fieldValue); break; case SCRIPT_COMMAND_FLAG_REMOVE: // 5 - // TODO - if (!source) + if (!pSourceOrItem) { sLog.outError("SCRIPT_COMMAND_FLAG_REMOVE (script id %u) call for NULL object.", m_script->id); break; } - if (m_script->removeFlag.fieldId <= OBJECT_FIELD_ENTRY || m_script->removeFlag.fieldId >= source->GetValuesCount()) + if (m_script->removeFlag.fieldId <= OBJECT_FIELD_ENTRY || m_script->removeFlag.fieldId >= pSourceOrItem->GetValuesCount()) { - sLog.outError("SCRIPT_COMMAND_FLAG_REMOVE (script id %u) call for wrong field %u (max count: %u) in object (TypeId: %u).", - m_script->id, m_script->removeFlag.fieldId, source->GetValuesCount(), source->GetTypeId()); + sLog.outError("SCRIPT_COMMAND_FLAG_REMOVE (script id %u) call for wrong field %u (max count: %u) in %s.", + m_script->id, m_script->removeFlag.fieldId, pSourceOrItem->GetValuesCount(), pSourceOrItem->GetGuidStr().c_str()); break; } - - source->RemoveFlag(m_script->removeFlag.fieldId, m_script->removeFlag.fieldValue); + pSourceOrItem->RemoveFlag(m_script->removeFlag.fieldId, m_script->removeFlag.fieldValue); break; case SCRIPT_COMMAND_TELEPORT_TO: // 6 { @@ -1323,7 +1303,7 @@ void ScriptAction::HandleScriptStep() float z = m_script->z; float o = m_script->o; - Creature* pCreature = pSource->SummonCreature(m_script->summonCreature.creatureEntry, x, y, z, o, m_script->summonCreature.despawnDelay ? TEMPSUMMON_TIMED_OR_DEAD_DESPAWN : TEMPSUMMON_DEAD_DESPAWN, m_script->summonCreature.despawnDelay, (m_script->data_flags & SCRIPT_FLAG_COMMAND_ADDITIONAL) ? true : false); + Creature* pCreature = pSource->SummonCreature(m_script->summonCreature.creatureEntry, x, y, z, o, m_script->summonCreature.despawnDelay ? TEMPSUMMON_TIMED_OOC_OR_DEAD_DESPAWN : TEMPSUMMON_DEAD_DESPAWN, m_script->summonCreature.despawnDelay, (m_script->data_flags & SCRIPT_FLAG_COMMAND_ADDITIONAL) ? true : false); if (!pCreature) { sLog.outError(" DB-SCRIPTS: Process table `%s` id %u, command %u failed for creature (entry: %u).", m_table, m_script->id, m_script->command, m_script->summonCreature.creatureEntry); @@ -1374,7 +1354,7 @@ void ScriptAction::HandleScriptStep() pDoor->UseDoorOrButton(time_to_reset); if (pTarget && pTarget->isType(TYPEMASK_GAMEOBJECT) && ((GameObject*)pTarget)->GetGoType() == GAMEOBJECT_TYPE_BUTTON) - ((GameObject*)target)->UseDoorOrButton(time_to_reset); + ((GameObject*)pTarget)->UseDoorOrButton(time_to_reset); break; } @@ -1408,7 +1388,7 @@ void ScriptAction::HandleScriptStep() break; } - case SCRIPT_COMMAND_PLAY_SOUND: // 16 // TODO + case SCRIPT_COMMAND_PLAY_SOUND: // 16 { if (!pSource) { @@ -1416,31 +1396,21 @@ void ScriptAction::HandleScriptStep() break; } - // bitmask: 0/1=anyone/target, 0/2=with distance dependent - Player* pTarget = NULL; - + // bitmask: 0/1=target-player, 0/2=with distance dependent, 0/4=map wide, 0/8=zone wide + Player* pSoundTarget = NULL; if (m_script->playSound.flags & 1) { - if (!target) - { - sLog.outError(" DB-SCRIPTS: Process table `%s` id %u, command %u in targeted mode call for NULL target.", m_table, m_script->id, m_script->command); + pSoundTarget = GetPlayerTargetOrSourceAndLog(pSource, pTarget); + if (!pSoundTarget) break; - } - - if (target->GetTypeId() != TYPEID_PLAYER) - { - sLog.outError(" DB-SCRIPTS: Process table `%s` id %u, command %u in targeted mode call for non-player (TypeId: %u), skipping.", m_table, m_script->id, m_script->command, target->GetTypeId()); - break; - } - - pTarget = (Player*)target; } - // bitmask: 0/1=anyone/target, 0/2=with distance dependent if (m_script->playSound.flags & 2) - pSource->PlayDistanceSound(m_script->playSound.soundId, pTarget); + pSource->PlayDistanceSound(m_script->playSound.soundId, pSoundTarget); + else if (m_script->playSound.flags & (4 | 8)) + m_map->PlayDirectSoundToMap(m_script->playSound.soundId, m_script->playSound.flags & 8 ? pSource->GetZoneId() : 0); else - pSource->PlayDirectSound(m_script->playSound.soundId, pTarget); + pSource->PlayDirectSound(m_script->playSound.soundId, pSoundTarget); break; } @@ -1575,7 +1545,7 @@ void ScriptAction::HandleScriptStep() if (LogIfNotCreature(pSource)) break; - ((Creature*)pSource)->SetWalk(!m_script->run.run); + ((Creature*)pSource)->SetWalk(!m_script->run.run, true); break; } @@ -1587,7 +1557,7 @@ void ScriptAction::HandleScriptStep() break; Creature* pAttacker = static_cast(pSource); - Unit* unitTarget = static_cast(target); + Unit* unitTarget = static_cast(pTarget); if (pAttacker->IsFriendlyTo(unitTarget)) { @@ -1667,10 +1637,64 @@ void ScriptAction::HandleScriptStep() pPlayer->ActivateTaxiPathTo(m_script->sendTaxiPath.taxiPathId); break; } + case SCRIPT_COMMAND_TERMINATE_SCRIPT: // 31 + { + bool result = false; + if (m_script->terminateScript.npcEntry) + { + WorldObject* pSearcher = pSource ? pSource : pTarget; + if (pSearcher->GetTypeId() == TYPEID_PLAYER && pTarget && pTarget->GetTypeId() != TYPEID_PLAYER) + pSearcher = pTarget; + + Creature* pCreatureBuddy = NULL; + MaNGOS::NearestCreatureEntryWithLiveStateInObjectRangeCheck u_check(*pSearcher, m_script->terminateScript.npcEntry, true, false, m_script->terminateScript.searchDist); + MaNGOS::CreatureLastSearcher searcher(pCreatureBuddy, u_check); + Cell::VisitGridObjects(pSearcher, searcher, m_script->terminateScript.searchDist); + + if (!(m_script->data_flags & SCRIPT_FLAG_COMMAND_ADDITIONAL) && !pCreatureBuddy) + { + DEBUG_LOG("DB-SCRIPTS: Process table `%s` id %u, terminate further steps of this script! (as searched npc %u was not found alive)", m_table, m_script->id, m_script->terminateScript.npcEntry); + result = true; + } + else if (m_script->data_flags & SCRIPT_FLAG_COMMAND_ADDITIONAL && pCreatureBuddy) + { + DEBUG_LOG("DB-SCRIPTS: Process table `%s` id %u, terminate further steps of this script! (as searched npc %u was found alive)", m_table, m_script->id, m_script->terminateScript.npcEntry); + result = true; + } + } + else + result = true; + + if (result) // Terminate further steps of this script + { + if (m_script->textId[0] && !LogIfNotCreature(pSource)) + { + Creature* cSource = static_cast(pSource); + if (cSource->GetMotionMaster()->GetCurrentMovementGeneratorType() == WAYPOINT_MOTION_TYPE) + (static_cast* >(cSource->GetMotionMaster()->top()))->AddToWaypointPauseTime(m_script->textId[0]); + } + + return true; + } + + break; + } + case SCRIPT_COMMAND_PAUSE_WAYPOINTS: // 32 + { + if (LogIfNotCreature(pSource)) + return false; + if (m_script->pauseWaypoint.doPause) + ((Creature*)pSource)->addUnitState(UNIT_STAT_WAYPOINT_PAUSED); + else + ((Creature*)pSource)->clearUnitState(UNIT_STAT_WAYPOINT_PAUSED); + break; + } default: sLog.outError(" DB-SCRIPTS: Process table `%s` id %u, command %u unknown command used.", m_table, m_script->id, m_script->command); break; } + + return false; } // ///////////////////////////////////////////////////////// @@ -1741,66 +1765,8 @@ void ScriptMgr::LoadEventIdScripts() BarGoLink bar(result->GetRowCount()); - // TODO: remove duplicate code below, same way to collect event id's used in LoadEventScripts() - std::set evt_scripts; - - // Load all possible event entries from gameobjects - for (uint32 i = 1; i < sGOStorage.MaxEntry; ++i) - { - if (GameObjectInfo const* goInfo = sGOStorage.LookupEntry(i)) - { - if (uint32 eventId = goInfo->GetEventScriptId()) - evt_scripts.insert(eventId); - - if (goInfo->type == GAMEOBJECT_TYPE_CAPTURE_POINT) - { - evt_scripts.insert(goInfo->capturePoint.neutralEventID1); - evt_scripts.insert(goInfo->capturePoint.neutralEventID2); - evt_scripts.insert(goInfo->capturePoint.contestedEventID1); - evt_scripts.insert(goInfo->capturePoint.contestedEventID2); - evt_scripts.insert(goInfo->capturePoint.progressEventID1); - evt_scripts.insert(goInfo->capturePoint.progressEventID2); - evt_scripts.insert(goInfo->capturePoint.winEventID1); - evt_scripts.insert(goInfo->capturePoint.winEventID2); - } - } - } - - // Load all possible event entries from spells - for (uint32 i = 1; i < sSpellStore.GetNumRows(); ++i) - { - SpellEntry const* spell = sSpellStore.LookupEntry(i); - if (spell) - { - for (int j = 0; j < MAX_EFFECT_INDEX; ++j) - { - SpellEffectEntry const* spellEffect = spell->GetSpellEffect(SpellEffectIndex(j)); - if (!spellEffect) - continue; - - if (spellEffect->Effect == SPELL_EFFECT_SEND_EVENT) - { - if (spellEffect->EffectMiscValue) - evt_scripts.insert(spellEffect->EffectMiscValue); - } - } - } - } - - // Load all possible event entries from taxi path nodes - for (size_t path_idx = 0; path_idx < sTaxiPathNodesByPath.size(); ++path_idx) - { - for (size_t node_idx = 0; node_idx < sTaxiPathNodesByPath[path_idx].size(); ++node_idx) - { - TaxiPathNodeEntry const& node = sTaxiPathNodesByPath[path_idx][node_idx]; - - if (node.arrivalEventID) - evt_scripts.insert(node.arrivalEventID); - - if (node.departureEventID) - evt_scripts.insert(node.departureEventID); - } - } + std::set eventIds; // Store possible event ids + CollectPossibleEventIds(eventIds); do { @@ -1812,8 +1778,8 @@ void ScriptMgr::LoadEventIdScripts() uint32 eventId = fields[0].GetUInt32(); const char* scriptName = fields[1].GetString(); - std::set::const_iterator itr = evt_scripts.find(eventId); - if (itr == evt_scripts.end()) + std::set::const_iterator itr = eventIds.find(eventId); + if (itr == eventIds.end()) sLog.outErrorDb("Table `scripted_event_id` has id %u not referring to any gameobject_template type 10 data2 field, type 3 data6 field, type 13 data 2 field, type 29 or any spell effect %u or path taxi node data", eventId, SPELL_EFFECT_SEND_EVENT); @@ -2146,6 +2112,125 @@ void ScriptMgr::UnloadScriptLibrary() m_pOnAuraDummy = NULL; } +void ScriptMgr::CollectPossibleEventIds(std::set& eventIds) +{ + // Load all possible script entries from gameobjects + for (SQLStorageBase::SQLSIterator itr = sGOStorage.getDataBegin(); itr < sGOStorage.getDataEnd(); ++itr) + { + switch (itr->type) + { + case GAMEOBJECT_TYPE_GOOBER: + eventIds.insert(itr->goober.eventId); + break; + case GAMEOBJECT_TYPE_CHEST: + eventIds.insert(itr->chest.eventId); + break; + case GAMEOBJECT_TYPE_CAMERA: + eventIds.insert(itr->camera.eventID); + break; + case GAMEOBJECT_TYPE_CAPTURE_POINT: + eventIds.insert(itr->capturePoint.neutralEventID1); + eventIds.insert(itr->capturePoint.neutralEventID2); + eventIds.insert(itr->capturePoint.contestedEventID1); + eventIds.insert(itr->capturePoint.contestedEventID2); + eventIds.insert(itr->capturePoint.progressEventID1); + eventIds.insert(itr->capturePoint.progressEventID2); + eventIds.insert(itr->capturePoint.winEventID1); + eventIds.insert(itr->capturePoint.winEventID2); + break; + case GAMEOBJECT_TYPE_DESTRUCTIBLE_BUILDING: + eventIds.insert(itr->destructibleBuilding.damagedEvent); + eventIds.insert(itr->destructibleBuilding.destroyedEvent); + eventIds.insert(itr->destructibleBuilding.intactEvent); + eventIds.insert(itr->destructibleBuilding.rebuildingEvent); + break; + default: + break; + } + } + + // Load all possible script entries from spells + for (uint32 i = 1; i < sSpellStore.GetNumRows(); ++i) + { + SpellEntry const* spell = sSpellStore.LookupEntry(i); + if (spell) + { + for (int j = 0; j < MAX_EFFECT_INDEX; ++j) + { + SpellEffectEntry const* spellEffect = spell->GetSpellEffect(SpellEffectIndex(j)); + if (!spellEffect) + continue; + + if (spellEffect->Effect == SPELL_EFFECT_SEND_EVENT) + { + if (spellEffect->EffectMiscValue) + eventIds.insert(spellEffect->EffectMiscValue); + } + } + } + } + + // Load all possible event entries from taxi path nodes + for (size_t path_idx = 0; path_idx < sTaxiPathNodesByPath.size(); ++path_idx) + { + for (size_t node_idx = 0; node_idx < sTaxiPathNodesByPath[path_idx].size(); ++node_idx) + { + TaxiPathNodeEntry const& node = sTaxiPathNodesByPath[path_idx][node_idx]; + + if (node.arrivalEventID) + eventIds.insert(node.arrivalEventID); + + if (node.departureEventID) + eventIds.insert(node.departureEventID); + } + } +} + +// Starters for events +bool StartEvents_Event(Map* map, uint32 id, Object* source, Object* target, bool isStart/*=true*/, Unit* forwardToPvp/*=NULL*/) +{ + MANGOS_ASSERT(source); + + // Handle SD2 script + if (sScriptMgr.OnProcessEvent(id, source, target, isStart)) + return true; + + // Handle PvP Calls + if (forwardToPvp && source->GetTypeId() == TYPEID_GAMEOBJECT) + { + BattleGround* bg = NULL; + OutdoorPvP* opvp = NULL; + if (forwardToPvp->GetTypeId() == TYPEID_PLAYER) + { + bg = ((Player*)forwardToPvp)->GetBattleGround(); + if (!bg) + opvp = sOutdoorPvPMgr.GetScript(((Player*)forwardToPvp)->GetCachedZoneId()); + } + else + { + if (map->IsBattleGroundOrArena()) + bg = ((BattleGroundMap*)map)->GetBG(); + else // Use the go, because GOs don't move + opvp = sOutdoorPvPMgr.GetScript(((GameObject*)source)->GetZoneId()); + } + + if (bg && bg->HandleEvent(id, static_cast(source))) + return true; + + if (opvp && opvp->HandleEvent(id, static_cast(source))) + return true; + } + + Map::ScriptExecutionParam execParam = Map::SCRIPT_EXEC_PARAM_UNIQUE_BY_SOURCE_TARGET; + if (source->isType(TYPEMASK_CREATURE_OR_GAMEOBJECT)) + execParam = Map::SCRIPT_EXEC_PARAM_UNIQUE_BY_SOURCE; + else if (target && target->isType(TYPEMASK_CREATURE_OR_GAMEOBJECT)) + execParam = Map::SCRIPT_EXEC_PARAM_UNIQUE_BY_TARGET; + + return map->ScriptsStart(sEventScripts, id, source, target, execParam); +} + +// Wrappers uint32 GetAreaTriggerScriptId(uint32 triggerId) { return sScriptMgr.GetAreaTriggerScriptId(triggerId); diff --git a/src/game/ScriptMgr.h b/src/game/ScriptMgr.h index 902e8efcb..9871ac636 100644 --- a/src/game/ScriptMgr.h +++ b/src/game/ScriptMgr.h @@ -44,13 +44,13 @@ class WorldObject; enum ScriptCommand // resSource, resTarget are the resulting Source/ Target after buddy search is done { SCRIPT_COMMAND_TALK = 0, // resSource = WorldObject, resTarget = Unit/none - // datalong1 (see enum ChatType for supported CHAT_TYPE_'s), datalong2 = language - // dataint = text entry from db_script_string -table. dataint2-4 optional for random selected texts. + // datalong1 (see enum ChatType for supported CHAT_TYPE_'s), datalong2 = language + // dataint = text entry from db_script_string -table. dataint2-4 optional for random selected texts. SCRIPT_COMMAND_EMOTE = 1, // resSource = Unit, resTarget = Unit/none - // datalong1 = emote_id + // datalong1 = emote_id SCRIPT_COMMAND_FIELD_SET = 2, // source = any, datalong = field_id, datalong2 = value SCRIPT_COMMAND_MOVE_TO = 3, // resSource = Creature, datalong2 = travel_speed*100, x/y/z - // data_flags & SCRIPT_FLAG_COMMAND_ADDITIONAL: teleport unit to position + // data_flags & SCRIPT_FLAG_COMMAND_ADDITIONAL: teleport unit to position SCRIPT_COMMAND_FLAG_SET = 4, // source = any, datalong = field_id, datalong2 = bitmask SCRIPT_COMMAND_FLAG_REMOVE = 5, // source = any, datalong = field_id, datalong2 = bitmask SCRIPT_COMMAND_TELEPORT_TO = 6, // source or target with Player, datalong2 = map_id, x/y/z @@ -58,39 +58,46 @@ enum ScriptCommand // resSource, resTar SCRIPT_COMMAND_KILL_CREDIT = 8, // source or target with Player, datalong = creature entry (or 0 for target-entry), datalong2 = bool (0=personal credit, 1=group credit) SCRIPT_COMMAND_RESPAWN_GAMEOBJECT = 9, // source = any, datalong=db_guid, datalong2=despawn_delay SCRIPT_COMMAND_TEMP_SUMMON_CREATURE = 10, // source = any, datalong=creature entry, datalong2=despawn_delay - // data_flags & SCRIPT_FLAG_COMMAND_ADDITIONAL = summon active + // data_flags & SCRIPT_FLAG_COMMAND_ADDITIONAL = summon active SCRIPT_COMMAND_OPEN_DOOR = 11, // datalong=db_guid (or not provided), datalong2=reset_delay SCRIPT_COMMAND_CLOSE_DOOR = 12, // datalong=db_guid (or not provided), datalong2=reset_delay SCRIPT_COMMAND_ACTIVATE_OBJECT = 13, // source = unit, target=GO SCRIPT_COMMAND_REMOVE_AURA = 14, // resSource = Unit, datalong = spell_id SCRIPT_COMMAND_CAST_SPELL = 15, // resSource = Unit, cast spell at resTarget = Unit - // datalong=spellid - // data_flags & SCRIPT_FLAG_COMMAND_ADDITIONAL = cast triggered - SCRIPT_COMMAND_PLAY_SOUND = 16, // resSource = WorldObject, target=any/player, datalong (sound_id), datalong2 (bitmask: 0/1=anyone/target, 0/2=with distance dependent, so 1|2 = 3 is target with distance dependent) + // datalong=spellid + // data_flags & SCRIPT_FLAG_COMMAND_ADDITIONAL = cast triggered + SCRIPT_COMMAND_PLAY_SOUND = 16, // resSource = WorldObject, target=any/player, datalong (sound_id), datalong2 (bitmask: 0/1=target-player, 0/2=with distance dependent, 0/4=map wide, 0/8=zone wide; so 1|2 = 3 is target with distance dependent) SCRIPT_COMMAND_CREATE_ITEM = 17, // source or target must be player, datalong = item entry, datalong2 = amount SCRIPT_COMMAND_DESPAWN_SELF = 18, // resSource = Creature, datalong = despawn delay SCRIPT_COMMAND_PLAY_MOVIE = 19, // target can only be a player, datalog = movie id SCRIPT_COMMAND_MOVEMENT = 20, // resSource = Creature. datalong = MovementType (0:idle, 1:random or 2:waypoint), datalong2 = wander-distance - // data_flags & SCRIPT_FLAG_COMMAND_ADDITIONAL = Random-movement around current position + // data_flags & SCRIPT_FLAG_COMMAND_ADDITIONAL = Random-movement around current position SCRIPT_COMMAND_SET_ACTIVEOBJECT = 21, // resSource = Creature - // datalong=bool 0=off, 1=on + // datalong=bool 0=off, 1=on SCRIPT_COMMAND_SET_FACTION = 22, // resSource = Creature - // datalong=factionId, datalong2=faction_flags + // datalong=factionId, datalong2=faction_flags SCRIPT_COMMAND_MORPH_TO_ENTRY_OR_MODEL = 23, // resSource = Creature, datalong=creature entry/modelid - // data_flags & SCRIPT_FLAG_COMMAND_ADDITIONAL = use datalong value as modelid explicit + // data_flags & SCRIPT_FLAG_COMMAND_ADDITIONAL = use datalong value as modelid explicit SCRIPT_COMMAND_MOUNT_TO_ENTRY_OR_MODEL = 24, // resSource = Creature, datalong=creature entry/modelid - // data_flags & SCRIPT_FLAG_COMMAND_ADDITIONAL = use datalong value as modelid explicit + // data_flags & SCRIPT_FLAG_COMMAND_ADDITIONAL = use datalong value as modelid explicit SCRIPT_COMMAND_SET_RUN = 25, // resSource = Creature - // datalong= bool 0=off, 1=on + // datalong= bool 0=off, 1=on SCRIPT_COMMAND_ATTACK_START = 26, // resSource = Creature, resTarget = Unit SCRIPT_COMMAND_GO_LOCK_STATE = 27, // resSource = GameObject - // datalong= 1=lock, 2=unlock, 4=set not-interactable, 8=set interactable + // datalong= 1=lock, 2=unlock, 4=set not-interactable, 8=set interactable SCRIPT_COMMAND_STAND_STATE = 28, // resSource = Creature - // datalong = stand state (enum UnitStandStateType) + // datalong = stand state (enum UnitStandStateType) SCRIPT_COMMAND_MODIFY_NPC_FLAGS = 29, // resSource = Creature - // datalong=NPCFlags - // datalong2:0x00=toggle, 0x01=add, 0x02=remove + // datalong=NPCFlags + // datalong2:0x00=toggle, 0x01=add, 0x02=remove SCRIPT_COMMAND_SEND_TAXI_PATH = 30, // datalong = taxi path id (source or target must be player) + SCRIPT_COMMAND_TERMINATE_SCRIPT = 31, // datalong = search for npc entry if provided + // datalong2= search distance + // data_flags & SCRIPT_FLAG_COMMAND_ADDITIONAL: terminate steps of this script if npc found + // ELSE: terminate steps of this script if npc not found + // dataint=diff to change a waittime of current Waypoint Movement + SCRIPT_COMMAND_PAUSE_WAYPOINTS = 32, // resSource = Creature + // datalong = 0: unpause waypoint 1: pause waypoint }; #define MAX_TEXT_ID 4 // used for SCRIPT_COMMAND_TALK @@ -287,12 +294,25 @@ struct ScriptInfo uint32 change_flag; // datalong2 } npcFlag; - struct + struct // SCRIPT_COMMAND_SEND_TAXI_PATH (30) { uint32 taxiPathId; // datalong uint32 empty; } sendTaxiPath; + struct // SCRIPT_COMMAND_TERMINATE_SCRIPT (31) + { + uint32 npcEntry; // datalong + uint32 searchDist; // datalong2 + // changeWaypointWaitTime // dataint + } terminateScript; + + struct // SCRIPT_COMMAND_PAUSE_WAYPOINTS (32) + { + uint32 doPause; // datalong + uint32 empty; + } pauseWaypoint; + struct { uint32 data[2]; @@ -351,6 +371,7 @@ struct ScriptInfo case SCRIPT_COMMAND_MOVEMENT: case SCRIPT_COMMAND_MORPH_TO_ENTRY_OR_MODEL: case SCRIPT_COMMAND_MOUNT_TO_ENTRY_OR_MODEL: + case SCRIPT_COMMAND_TERMINATE_SCRIPT: return true; default: return false; @@ -365,7 +386,21 @@ class ScriptAction m_table(_table), m_map(_map), m_sourceGuid(_sourceGuid), m_targetGuid(_targetGuid), m_ownerGuid(_ownerGuid), m_script(_script) {} - void HandleScriptStep(); + bool HandleScriptStep(); // return true IF AND ONLY IF the script should be terminated + + const char* GetTableName() const { return m_table; } + uint32 GetId() const { return m_script->id; } + ObjectGuid GetSourceGuid() const { return m_sourceGuid; } + ObjectGuid GetTargetGuid() const { return m_targetGuid; } + ObjectGuid GetOwnerGuid() const { return m_ownerGuid; } + + bool IsSameScript(const char* table, uint32 id, ObjectGuid sourceGuid, ObjectGuid targetGuid, ObjectGuid ownerGuid) const + { + return table == m_table && id == GetId() && + (sourceGuid == m_sourceGuid || !sourceGuid) && + (targetGuid == m_targetGuid || !targetGuid) && + (ownerGuid == m_ownerGuid || !ownerGuid); + } private: const char* m_table; // of which table the script was started @@ -395,6 +430,7 @@ extern ScriptMapMapName sGameObjectScripts; extern ScriptMapMapName sGameObjectTemplateScripts; extern ScriptMapMapName sEventScripts; extern ScriptMapMapName sGossipScripts; +extern ScriptMapMapName sCreatureDeathScripts; extern ScriptMapMapName sCreatureMovementScripts; enum ScriptLoadResult @@ -418,6 +454,7 @@ class ScriptMgr void LoadEventScripts(); void LoadSpellScripts(); void LoadGossipScripts(); + void LoadCreatureDeathScripts(); void LoadCreatureMovementScripts(); void LoadDbScriptStrings(); @@ -468,6 +505,7 @@ class ScriptMgr bool OnAuraDummy(Aura const* pAura, bool apply); private: + void CollectPossibleEventIds(std::set& eventIds); void LoadScripts(ScriptMapMapName& scripts, const char* tablename); void CheckScriptTexts(ScriptMapMapName const& scripts, std::set& ids); @@ -520,6 +558,9 @@ class ScriptMgr bool (MANGOS_IMPORT* m_pOnAuraDummy)(Aura const*, bool); }; +// Starters for events +bool StartEvents_Event(Map* map, uint32 id, Object* source, Object* target, bool isStart = true, Unit* forwardToPvp = NULL); + #define sScriptMgr MaNGOS::Singleton::Instance() MANGOS_DLL_SPEC uint32 GetAreaTriggerScriptId(uint32 triggerId); diff --git a/src/shared/revision_nr.h b/src/shared/revision_nr.h index a904aa377..60484454b 100644 --- a/src/shared/revision_nr.h +++ b/src/shared/revision_nr.h @@ -1,4 +1,4 @@ #ifndef __REVISION_NR_H__ #define __REVISION_NR_H__ - #define REVISION_NR "12558" + #define REVISION_NR "12559" #endif // __REVISION_NR_H__