/** * MaNGOS is a full featured server for World of Warcraft, supporting * the following clients: 1.12.x, 2.4.3, 3.3.5a, 4.3.4a and 5.4.8 * * Copyright (C) 2005-2015 MaNGOS project * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * World of Warcraft, and all World of Warcraft or Warcraft art, images, * and lore are copyrighted by Blizzard Entertainment, Inc. */ #include "ScriptMgr.h" #include "Policies/Singleton.h" #include "Log.h" #include "ProgressBar.h" #include "ObjectMgr.h" #include "WaypointManager.h" #include "World.h" #include "GridNotifiers.h" #include "GridNotifiersImpl.h" #include "Cell.h" #include "CellImpl.h" #include "SQLStorages.h" #include "BattleGround/BattleGround.h" #include "OutdoorPvP/OutdoorPvP.h" #include "WaypointMovementGenerator.h" #include "LFGMgr.h" #ifdef ENABLE_ELUNA #include "LuaEngine.h" #endif /* ENABLE_ELUNA */ #ifdef ENABLE_SD2 #include "system/ScriptDevMgr.h" #endif #include /* std::strcmp */ #include "revision.h" ScriptMapMapName sQuestEndScripts; ScriptMapMapName sQuestStartScripts; ScriptMapMapName sSpellScripts; ScriptMapMapName sGameObjectScripts; ScriptMapMapName sGameObjectTemplateScripts; ScriptMapMapName sEventScripts; ScriptMapMapName sGossipScripts; ScriptMapMapName sCreatureDeathScripts; ScriptMapMapName sCreatureMovementScripts; INSTANTIATE_SINGLETON_1(ScriptMgr); ScriptMgr::ScriptMgr() : m_scheduledScripts(0) { } ScriptMgr::~ScriptMgr() { } // ///////////////////////////////////////////////////////// // DB SCRIPTS (loaders of static data) // ///////////////////////////////////////////////////////// // returns priority (0 == can not start script) uint8 GetSpellStartDBScriptPriority(SpellEntry const* spellinfo, SpellEffectIndex effIdx) { if (spellinfo->Effect[effIdx] == SPELL_EFFECT_SCRIPT_EFFECT) { return 10; } if (spellinfo->Effect[effIdx] == SPELL_EFFECT_DUMMY) { return 9; } // NonExisting triggered spells can also start DB-Spell-Scripts if (spellinfo->Effect[effIdx] == SPELL_EFFECT_TRIGGER_SPELL && !sSpellStore.LookupEntry(spellinfo->EffectTriggerSpell[effIdx])) { return 5; } // NonExisting trigger missile spells can also start DB-Spell-Scripts if (spellinfo->Effect[effIdx] == SPELL_EFFECT_TRIGGER_MISSILE && !sSpellStore.LookupEntry(spellinfo->EffectTriggerSpell[effIdx])) { return 4; } // Can not start script return 0; } // Priorize: SCRIPT_EFFECT before DUMMY before Non-Existing triggered spell, for same priority the first effect with the priority triggers bool ScriptMgr::CanSpellEffectStartDBScript(SpellEntry const* spellinfo, SpellEffectIndex effIdx) { uint8 priority = GetSpellStartDBScriptPriority(spellinfo, effIdx); if (!priority) { return false; } for (int i = 0; i < MAX_EFFECT_INDEX; ++i) { uint8 currentPriority = GetSpellStartDBScriptPriority(spellinfo, SpellEffectIndex(i)); if (currentPriority < priority) // lower priority, continue checking { continue; } if (currentPriority > priority) // take other index with higher priority { return false; } if (i < effIdx) // same priority at lower index { return false; } } return true; } void ScriptMgr::LoadScripts(ScriptMapMapName& scripts, const char* tablename) { if (IsScriptScheduled()) // function don't must be called in time scripts use. { return; } scripts.first = tablename; scripts.second.clear(); // need for reload support // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 QueryResult* result = WorldDatabase.PQuery("SELECT id, delay, command, datalong, datalong2, buddy_entry, search_radius, data_flags, dataint, dataint2, dataint3, dataint4, x, y, z, o FROM %s", tablename); uint32 count = 0; if (!result) { BarGoLink bar(1); bar.step(); sLog.outString(">> Loaded %u script definitions from table %s", count, tablename); sLog.outString(); return; } BarGoLink bar(result->GetRowCount()); do { bar.step(); Field* fields = result->Fetch(); ScriptInfo tmp; tmp.id = fields[0].GetUInt32(); tmp.delay = fields[1].GetUInt32(); tmp.command = fields[2].GetUInt32(); tmp.raw.data[0] = fields[3].GetUInt32(); tmp.raw.data[1] = fields[4].GetUInt32(); tmp.buddyEntry = fields[5].GetUInt32(); tmp.searchRadiusOrGuid = fields[6].GetUInt32(); tmp.data_flags = fields[7].GetUInt8(); tmp.textId[0] = fields[8].GetInt32(); tmp.textId[1] = fields[9].GetInt32(); tmp.textId[2] = fields[10].GetInt32(); tmp.textId[3] = fields[11].GetInt32(); tmp.x = fields[12].GetFloat(); tmp.y = fields[13].GetFloat(); tmp.z = fields[14].GetFloat(); tmp.o = fields[15].GetFloat(); // generic command args check if (tmp.buddyEntry && !(tmp.data_flags & SCRIPT_FLAG_BUDDY_BY_GUID)) { if (tmp.IsCreatureBuddy() && !ObjectMgr::GetCreatureTemplate(tmp.buddyEntry)) { sLog.outErrorDb("Table `%s` has buddyEntry = %u in command %u for script id %u, but this creature_template does not exist, skipping.", tablename, tmp.buddyEntry, tmp.command, tmp.id); continue; } else if (!tmp.IsCreatureBuddy() && !ObjectMgr::GetGameObjectInfo(tmp.buddyEntry)) { sLog.outErrorDb("Table `%s` has buddyEntry = %u in command %u for script id %u, but this gameobject_template does not exist, skipping.", tablename, tmp.buddyEntry, tmp.command, tmp.id); continue; } if (!tmp.searchRadiusOrGuid) { sLog.outErrorDb("Table `%s` has searchRadius = 0 in command %u for script id %u for buddy %u, skipping.", tablename, tmp.command, tmp.id, tmp.buddyEntry); continue; } } if (tmp.data_flags) // Check flags { if (tmp.data_flags & ~MAX_SCRIPT_FLAG_VALID) { sLog.outErrorDb("Table `%s` has invalid data_flags %u in command %u for script id %u, skipping.", tablename, tmp.data_flags, tmp.command, tmp.id); continue; } if (!tmp.HasAdditionalScriptFlag() && tmp.data_flags & SCRIPT_FLAG_COMMAND_ADDITIONAL) { sLog.outErrorDb("Table `%s` has invalid data_flags %u in command %u for script id %u, skipping.", tablename, tmp.data_flags, tmp.command, tmp.id); continue; } if (tmp.data_flags & SCRIPT_FLAG_BUDDY_AS_TARGET && ! tmp.buddyEntry) { sLog.outErrorDb("Table `%s` has buddy required in data_flags %u in command %u for script id %u, but no buddy defined, skipping.", tablename, tmp.data_flags, tmp.command, tmp.id); continue; } if (tmp.data_flags & SCRIPT_FLAG_BUDDY_BY_GUID) // Check guid { if (tmp.IsCreatureBuddy()) { CreatureData const* data = sObjectMgr.GetCreatureData(tmp.searchRadiusOrGuid); if (!data) { sLog.outErrorDb("Table `%s` has buddy defined by guid (SCRIPT_FLAG_BUDDY_BY_GUID %u set) but no npc spawned with guid %u, skipping.", tablename, SCRIPT_FLAG_BUDDY_BY_GUID, tmp.searchRadiusOrGuid); continue; } if (data->id != tmp.buddyEntry) { sLog.outErrorDb("Table `%s` has buddy defined by guid (SCRIPT_FLAG_BUDDY_BY_GUID %u set) but spawned npc with guid %u has entry %u, expected buddy_entry is %u, skipping.", tablename, SCRIPT_FLAG_BUDDY_BY_GUID, tmp.searchRadiusOrGuid, data->id, tmp.buddyEntry); continue; } } else { GameObjectData const* data = sObjectMgr.GetGOData(tmp.searchRadiusOrGuid); if (!data) { sLog.outErrorDb("Table `%s` has go-buddy defined by guid (SCRIPT_FLAG_BUDDY_BY_GUID %u set) but no go spawned with guid %u, skipping.", tablename, SCRIPT_FLAG_BUDDY_BY_GUID, tmp.searchRadiusOrGuid); continue; } if (data->id != tmp.buddyEntry) { sLog.outErrorDb("Table `%s` has go-buddy defined by guid (SCRIPT_FLAG_BUDDY_BY_GUID %u set) but spawned go with guid %u has entry %u, expected buddy_entry is %u, skipping.", tablename, SCRIPT_FLAG_BUDDY_BY_GUID, tmp.searchRadiusOrGuid, data->id, tmp.buddyEntry); continue; } } } } switch (tmp.command) { case SCRIPT_COMMAND_TALK: // 0 { if (tmp.textId[0] == 0) { sLog.outErrorDb("Table `%s` has invalid talk text id (dataint = %i) in SCRIPT_COMMAND_TALK for script id %u", tablename, tmp.textId[0], tmp.id); continue; } for (int i = 0; i < MAX_TEXT_ID; ++i) { if (tmp.textId[i] && (tmp.textId[i] < MIN_DB_SCRIPT_STRING_ID || tmp.textId[i] >= MAX_DB_SCRIPT_STRING_ID)) { sLog.outErrorDb("Table `%s` has out of range text_id%u (dataint = %i expected %u-%u) in SCRIPT_COMMAND_TALK for script id %u", tablename, i + 1, tmp.textId[i], MIN_DB_SCRIPT_STRING_ID, MAX_DB_SCRIPT_STRING_ID, tmp.id); continue; } } // if (!GetMangosStringLocale(tmp.dataint)) will be checked after db_script_string loading break; } case SCRIPT_COMMAND_EMOTE: // 1 { if (!sEmotesStore.LookupEntry(tmp.emote.emoteId)) { sLog.outErrorDb("Table `%s` has invalid emote id (datalong = %u) in SCRIPT_COMMAND_EMOTE for script id %u", tablename, tmp.emote.emoteId, tmp.id); continue; } for (int i = 0; i < MAX_TEXT_ID; ++i) { if (tmp.textId[i] && !sEmotesStore.LookupEntry(tmp.textId[i])) { sLog.outErrorDb("Table `%s` has invalid emote id (text_id%u = %u) in SCRIPT_COMMAND_EMOTE for script id %u", tablename, i + 1, tmp.textId[i], tmp.id); continue; } } break; } case SCRIPT_COMMAND_FIELD_SET: // 2 case SCRIPT_COMMAND_MOVE_TO: // 3 case SCRIPT_COMMAND_FLAG_SET: // 4 case SCRIPT_COMMAND_FLAG_REMOVE: // 5 break; case SCRIPT_COMMAND_TELEPORT_TO: // 6 { if (!sMapStore.LookupEntry(tmp.teleportTo.mapId)) { sLog.outErrorDb("Table `%s` has invalid map (Id: %u) in SCRIPT_COMMAND_TELEPORT_TO for script id %u", tablename, tmp.teleportTo.mapId, tmp.id); continue; } if (!MaNGOS::IsValidMapCoord(tmp.x, tmp.y, tmp.z, tmp.o)) { sLog.outErrorDb("Table `%s` has invalid coordinates (X: %f Y: %f) in SCRIPT_COMMAND_TELEPORT_TO for script id %u", tablename, tmp.x, tmp.y, tmp.id); continue; } break; } case SCRIPT_COMMAND_QUEST_EXPLORED: // 7 { Quest const* quest = sObjectMgr.GetQuestTemplate(tmp.questExplored.questId); if (!quest) { sLog.outErrorDb("Table `%s` has invalid quest (ID: %u) in SCRIPT_COMMAND_QUEST_EXPLORED in `datalong` for script id %u", tablename, tmp.questExplored.questId, tmp.id); continue; } if (!quest->HasSpecialFlag(QUEST_SPECIAL_FLAG_EXPLORATION_OR_EVENT)) { sLog.outErrorDb("Table `%s` has quest (ID: %u) in SCRIPT_COMMAND_QUEST_EXPLORED in `datalong` for script id %u, but quest not have flag QUEST_SPECIAL_FLAG_EXPLORATION_OR_EVENT in quest flags. Script command or quest flags wrong. Quest modified to require objective.", tablename, tmp.questExplored.questId, tmp.id); // this will prevent quest completing without objective const_cast(quest)->SetSpecialFlag(QUEST_SPECIAL_FLAG_EXPLORATION_OR_EVENT); // continue; - quest objective requirement set and command can be allowed } if (float(tmp.questExplored.distance) > DEFAULT_VISIBILITY_DISTANCE) { sLog.outErrorDb("Table `%s` has too large distance (%u) for exploring objective complete in `datalong2` in SCRIPT_COMMAND_QUEST_EXPLORED in `datalong` for script id %u", tablename, tmp.questExplored.distance, tmp.id); continue; } if (tmp.questExplored.distance && float(tmp.questExplored.distance) > DEFAULT_VISIBILITY_DISTANCE) { sLog.outErrorDb("Table `%s` has too large distance (%u) for exploring objective complete in `datalong2` in SCRIPT_COMMAND_QUEST_EXPLORED in `datalong` for script id %u, max distance is %f or 0 for disable distance check", tablename, tmp.questExplored.distance, tmp.id, DEFAULT_VISIBILITY_DISTANCE); continue; } if (tmp.questExplored.distance && float(tmp.questExplored.distance) < INTERACTION_DISTANCE) { sLog.outErrorDb("Table `%s` has too small distance (%u) for exploring objective complete in `datalong2` in SCRIPT_COMMAND_QUEST_EXPLORED in `datalong` for script id %u, min distance is %f or 0 for disable distance check", tablename, tmp.questExplored.distance, tmp.id, INTERACTION_DISTANCE); continue; } break; } case SCRIPT_COMMAND_KILL_CREDIT: // 8 { if (tmp.killCredit.creatureEntry && !ObjectMgr::GetCreatureTemplate(tmp.killCredit.creatureEntry)) { sLog.outErrorDb("Table `%s` has invalid creature (Entry: %u) in SCRIPT_COMMAND_KILL_CREDIT for script id %u", tablename, tmp.killCredit.creatureEntry, tmp.id); continue; } break; } case SCRIPT_COMMAND_RESPAWN_GAMEOBJECT: // 9 { uint32 goEntry = 0; if (!tmp.GetGOGuid()) { if (!tmp.buddyEntry) { sLog.outErrorDb("Table `%s` has no gameobject nor buddy defined in SCRIPT_COMMAND_RESPAWN_GAMEOBJECT for script id %u", tablename, tmp.id); continue; } goEntry = tmp.buddyEntry; } else { GameObjectData const* data = sObjectMgr.GetGOData(tmp.GetGOGuid()); if (!data) { sLog.outErrorDb("Table `%s` has invalid gameobject (GUID: %u) in SCRIPT_COMMAND_RESPAWN_GAMEOBJECT for script id %u", tablename, tmp.GetGOGuid(), tmp.id); continue; } goEntry = data->id; } GameObjectInfo const* info = ObjectMgr::GetGameObjectInfo(goEntry); if (!info) { sLog.outErrorDb("Table `%s` has gameobject with invalid entry (GUID: %u Entry: %u) in SCRIPT_COMMAND_RESPAWN_GAMEOBJECT for script id %u", tablename, tmp.GetGOGuid(), goEntry, tmp.id); continue; } if (info->type == GAMEOBJECT_TYPE_FISHINGNODE || info->type == GAMEOBJECT_TYPE_FISHINGHOLE || info->type == GAMEOBJECT_TYPE_DOOR) { 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; } case SCRIPT_COMMAND_TEMP_SUMMON_CREATURE: // 10 { if (!MaNGOS::IsValidMapCoord(tmp.x, tmp.y, tmp.z, tmp.o)) { sLog.outErrorDb("Table `%s` has invalid coordinates (X: %f Y: %f) in SCRIPT_COMMAND_TEMP_SUMMON_CREATURE for script id %u", tablename, tmp.x, tmp.y, tmp.id); continue; } if (!ObjectMgr::GetCreatureTemplate(tmp.summonCreature.creatureEntry)) { sLog.outErrorDb("Table `%s` has invalid creature (Entry: %u) in SCRIPT_COMMAND_TEMP_SUMMON_CREATURE for script id %u", tablename, tmp.summonCreature.creatureEntry, tmp.id); continue; } break; } case SCRIPT_COMMAND_OPEN_DOOR: // 11 case SCRIPT_COMMAND_CLOSE_DOOR: // 12 { uint32 goEntry = 0; if (!tmp.GetGOGuid()) { if (!tmp.buddyEntry) { sLog.outErrorDb("Table `%s` has no gameobject nor buddy defined in %s for script id %u", tablename, (tmp.command == SCRIPT_COMMAND_OPEN_DOOR ? "SCRIPT_COMMAND_OPEN_DOOR" : "SCRIPT_COMMAND_CLOSE_DOOR"), tmp.id); continue; } goEntry = tmp.buddyEntry; } else { GameObjectData const* data = sObjectMgr.GetGOData(tmp.GetGOGuid()); if (!data) { sLog.outErrorDb("Table `%s` has invalid gameobject (GUID: %u) in %s for script id %u", tablename, tmp.GetGOGuid(), (tmp.command == SCRIPT_COMMAND_OPEN_DOOR ? "SCRIPT_COMMAND_OPEN_DOOR" : "SCRIPT_COMMAND_CLOSE_DOOR"), tmp.id); continue; } goEntry = data->id; } GameObjectInfo const* info = ObjectMgr::GetGameObjectInfo(goEntry); if (!info) { sLog.outErrorDb("Table `%s` has gameobject with invalid entry (GUID: %u Entry: %u) in %s for script id %u", tablename, tmp.GetGOGuid(), goEntry, (tmp.command == SCRIPT_COMMAND_OPEN_DOOR ? "SCRIPT_COMMAND_OPEN_DOOR" : "SCRIPT_COMMAND_CLOSE_DOOR"), tmp.id); continue; } if (info->type != GAMEOBJECT_TYPE_DOOR) { sLog.outErrorDb("Table `%s` has gameobject type (%u) non supported by command %s for script id %u", tablename, info->id, (tmp.command == SCRIPT_COMMAND_OPEN_DOOR ? "SCRIPT_COMMAND_OPEN_DOOR" : "SCRIPT_COMMAND_CLOSE_DOOR"), tmp.id); continue; } break; } case SCRIPT_COMMAND_ACTIVATE_OBJECT: // 13 break; case SCRIPT_COMMAND_REMOVE_AURA: // 14 { if (!sSpellStore.LookupEntry(tmp.removeAura.spellId)) { sLog.outErrorDb("Table `%s` using nonexistent spell (id: %u) in SCRIPT_COMMAND_REMOVE_AURA or SCRIPT_COMMAND_CAST_SPELL for script id %u", tablename, tmp.removeAura.spellId, tmp.id); continue; } break; } case SCRIPT_COMMAND_CAST_SPELL: // 15 { if (!sSpellStore.LookupEntry(tmp.castSpell.spellId)) { sLog.outErrorDb("Table `%s` using nonexistent spell (id: %u) in SCRIPT_COMMAND_REMOVE_AURA or SCRIPT_COMMAND_CAST_SPELL for script id %u", tablename, tmp.castSpell.spellId, tmp.id); continue; } bool hasErrored = false; for (uint8 i = 0; i < MAX_TEXT_ID; ++i) { if (tmp.textId[i] && !sSpellStore.LookupEntry(uint32(tmp.textId[i]))) { sLog.outErrorDb("Table `%s` using nonexistent spell (id: %u) in SCRIPT_COMMAND_CAST_SPELL for script id %u, dataint%u", tablename, uint32(tmp.textId[i]), tmp.id, i + 1); hasErrored = true; } } if (hasErrored) { continue; } break; } case SCRIPT_COMMAND_PLAY_SOUND: // 16 { if (!sSoundEntriesStore.LookupEntry(tmp.playSound.soundId)) { sLog.outErrorDb("Table `%s` using nonexistent sound (id: %u) in SCRIPT_COMMAND_PLAY_SOUND for script id %u", 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 { if (!ObjectMgr::GetItemPrototype(tmp.createItem.itemEntry)) { sLog.outErrorDb("Table `%s` has nonexistent item (entry: %u) in SCRIPT_COMMAND_CREATE_ITEM for script id %u", tablename, tmp.createItem.itemEntry, tmp.id); continue; } if (!tmp.createItem.amount) { sLog.outErrorDb("Table `%s` SCRIPT_COMMAND_CREATE_ITEM but amount is %u for script id %u", tablename, tmp.createItem.amount, tmp.id); continue; } break; } case SCRIPT_COMMAND_DESPAWN_SELF: // 18 { // for later, we might consider despawn by database guid, and define in datalong2 as option to despawn self. break; } case SCRIPT_COMMAND_PLAY_MOVIE: // 19 { sLog.outErrorDb("Table `%s` use unsupported SCRIPT_COMMAND_PLAY_MOVIE for script id %u", tablename, tmp.id); continue; } case SCRIPT_COMMAND_MOVEMENT: // 20 { if (tmp.movement.movementType >= MAX_DB_MOTION_TYPE) { sLog.outErrorDb("Table `%s` SCRIPT_COMMAND_MOVEMENT has invalid MovementType %u for script id %u", tablename, tmp.movement.movementType, tmp.id); continue; } break; } case SCRIPT_COMMAND_SET_ACTIVEOBJECT: // 21 break; case SCRIPT_COMMAND_SET_FACTION: // 22 { if (tmp.faction.factionId && !sFactionTemplateStore.LookupEntry(tmp.faction.factionId)) { sLog.outErrorDb("Table `%s` has datalong = %u in SCRIPT_COMMAND_SET_FACTION for script id %u, but this faction-template does not exist.", tablename, tmp.faction.factionId, tmp.id); continue; } break; } case SCRIPT_COMMAND_MORPH_TO_ENTRY_OR_MODEL: // 23 { if (tmp.data_flags & SCRIPT_FLAG_COMMAND_ADDITIONAL) { if (tmp.morph.creatureOrModelEntry && !sCreatureDisplayInfoStore.LookupEntry(tmp.morph.creatureOrModelEntry)) { sLog.outErrorDb("Table `%s` has datalong2 = %u in SCRIPT_COMMAND_MORPH_TO_ENTRY_OR_MODEL for script id %u, but this model does not exist.", tablename, tmp.morph.creatureOrModelEntry, tmp.id); continue; } } else { if (tmp.morph.creatureOrModelEntry && !ObjectMgr::GetCreatureTemplate(tmp.morph.creatureOrModelEntry)) { sLog.outErrorDb("Table `%s` has datalong2 = %u in SCRIPT_COMMAND_MORPH_TO_ENTRY_OR_MODEL for script id %u, but this creature_template does not exist.", tablename, tmp.morph.creatureOrModelEntry, tmp.id); continue; } } break; } case SCRIPT_COMMAND_MOUNT_TO_ENTRY_OR_MODEL: // 24 { if (tmp.data_flags & SCRIPT_FLAG_COMMAND_ADDITIONAL) { if (tmp.mount.creatureOrModelEntry && !sCreatureDisplayInfoStore.LookupEntry(tmp.mount.creatureOrModelEntry)) { sLog.outErrorDb("Table `%s` has datalong2 = %u in SCRIPT_COMMAND_MOUNT_TO_ENTRY_OR_MODEL for script id %u, but this model does not exist.", tablename, tmp.mount.creatureOrModelEntry, tmp.id); continue; } } else { if (tmp.mount.creatureOrModelEntry && !ObjectMgr::GetCreatureTemplate(tmp.mount.creatureOrModelEntry)) { sLog.outErrorDb("Table `%s` has datalong2 = %u in SCRIPT_COMMAND_MOUNT_TO_ENTRY_OR_MODEL for script id %u, but this creature_template does not exist.", tablename, tmp.mount.creatureOrModelEntry, tmp.id); continue; } } break; } case SCRIPT_COMMAND_SET_RUN: // 25 case SCRIPT_COMMAND_ATTACK_START: // 26 break; case SCRIPT_COMMAND_GO_LOCK_STATE: // 27 { if (// lock(0x01) and unlock(0x02) together ((tmp.goLockState.lockState & 0x01) && (tmp.goLockState.lockState & 0x02)) || // non-interact (0x4) and interact (0x08) together ((tmp.goLockState.lockState & 0x04) && (tmp.goLockState.lockState & 0x08)) || // no setting !tmp.goLockState.lockState || // invalid number tmp.goLockState.lockState >= 0x10) { sLog.outErrorDb("Table `%s` has invalid lock state (datalong = %u) in SCRIPT_COMMAND_GO_LOCK_STATE for script id %u.", tablename, tmp.goLockState.lockState, tmp.id); continue; } break; } case SCRIPT_COMMAND_STAND_STATE: // 28 { if (tmp.standState.stand_state >= MAX_UNIT_STAND_STATE) { sLog.outErrorDb("Table `%s` has invalid stand state (datalong = %u) in SCRIPT_COMMAND_STAND_STATE for script id %u", tablename, tmp.standState.stand_state, tmp.id); continue; } break; } case SCRIPT_COMMAND_MODIFY_NPC_FLAGS: // 29 break; case SCRIPT_COMMAND_SEND_TAXI_PATH: // 30 { if (!sTaxiPathStore.LookupEntry(tmp.sendTaxiPath.taxiPathId)) { sLog.outErrorDb("Table `%s` has datalong = %u in SCRIPT_COMMAND_SEND_TAXI_PATH for script id %u, but this taxi path does not exist.", tablename, tmp.sendTaxiPath.taxiPathId, tmp.id); continue; } // Check if this taxi path can be triggered with a spell if (!sLog.HasLogFilter(LOG_FILTER_DB_STRICTED_CHECK)) { uint32 taxiSpell = 0; for (uint32 i = 1; i < sSpellStore.GetNumRows() && taxiSpell == 0; ++i) { if (SpellEntry const* spell = sSpellStore.LookupEntry(i)) for (int j = 0; j < MAX_EFFECT_INDEX; ++j) { if (spell->Effect[j] == SPELL_EFFECT_SEND_TAXI && spell->EffectMiscValue[j] == int32(tmp.sendTaxiPath.taxiPathId)) { taxiSpell = i; break; } } } if (taxiSpell) { sLog.outErrorDb("Table `%s` has datalong = %u in SCRIPT_COMMAND_SEND_TAXI_PATH for script id %u, but this taxi path can be triggered by spell %u.", tablename, tmp.sendTaxiPath.taxiPathId, tmp.id, taxiSpell); continue; } } 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; case SCRIPT_COMMAND_JOIN_LFG: // 33 break; case SCRIPT_COMMAND_TERMINATE_COND: // 34 { if (!sConditionStorage.LookupEntry(tmp.terminateCond.conditionId)) { sLog.outErrorDb("Table `%s` has datalong = %u in SCRIPT_COMMAND_TERMINATE_COND for script id %u, but this condition_id does not exist.", tablename, tmp.terminateCond.conditionId, tmp.id); continue; } if (tmp.terminateCond.failQuest && !sObjectMgr.GetQuestTemplate(tmp.terminateCond.failQuest)) { sLog.outErrorDb("Table `%s` has datalong2 = %u in SCRIPT_COMMAND_TERMINATE_COND for script id %u, but this questId does not exist.", tablename, tmp.terminateCond.failQuest, tmp.id); continue; } break; } case SCRIPT_COMMAND_TURN_TO: // 35 break; case SCRIPT_COMMAND_SEND_AI_EVENT_AROUND: // 36 { if (tmp.sendAIEvent.eventType >= MAXIMAL_AI_EVENT_EVENTAI) { sLog.outErrorDb("Table `%s` has invalid AI event (datalong = %u) in SCRIPT_COMMAND_SEND_AI_EVENT for script id %u", tablename, tmp.sendAIEvent.eventType, tmp.id); continue; } break; } case SCRIPT_COMMAND_MOVE_DYNAMIC: // 37 { if (tmp.moveDynamic.maxDist < tmp.moveDynamic.minDist) { sLog.outErrorDb("Table `%s` has invalid min-dist (datalong2 = %u) less than max-dist (datalon = %u) in SCRIPT_COMMAND_MOVE_DYNAMIC for script id %u", tablename, tmp.moveDynamic.minDist, tmp.moveDynamic.maxDist, tmp.id); continue; } break; } default: { sLog.outErrorDb("Table `%s` unknown command %u, skipping.", tablename, tmp.command); continue; } } if (scripts.second.find(tmp.id) == scripts.second.end()) { ScriptMap emptyMap; scripts.second[tmp.id] = emptyMap; } scripts.second[tmp.id].insert(ScriptMap::value_type(tmp.delay, tmp)); ++count; } while (result->NextRow()); delete result; sLog.outString(">> Loaded %u script definitions from table %s", count, tablename); sLog.outString(); } void ScriptMgr::LoadGameObjectScripts() { 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 `dbscripts_on_go_use` has not existing gameobject (GUID: %u) as script id", itr->first); } } } void ScriptMgr::LoadGameObjectTemplateScripts() { 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 `dbscripts_on_go_template_use` has not existing gameobject (Entry: %u) as script id", itr->first); } } } void ScriptMgr::LoadQuestEndScripts() { 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 `dbscripts_on_quest_end` has not existing quest (Id: %u) as script id", itr->first); } } } void ScriptMgr::LoadQuestStartScripts() { 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 `dbscripts_on_quest_start` has not existing quest (Id: %u) as script id", itr->first); } } } void ScriptMgr::LoadSpellScripts() { LoadScripts(sSpellScripts, "dbscripts_on_spell"); // check ids for (ScriptMapMap::const_iterator itr = sSpellScripts.second.begin(); itr != sSpellScripts.second.end(); ++itr) { SpellEntry const* spellInfo = sSpellStore.LookupEntry(itr->first); if (!spellInfo) { sLog.outErrorDb("Table `dbscripts_on_spell` has not existing spell (Id: %u) as script id", itr->first); continue; } // check for correct spellEffect bool found = false; for (int i = 0; i < MAX_EFFECT_INDEX; ++i) { if (GetSpellStartDBScriptPriority(spellInfo, SpellEffectIndex(i))) { found = true; break; } } if (!found) { sLog.outErrorDb("Table `dbscripts_on_spell` has unsupported spell (Id: %u)", itr->first); } } } void ScriptMgr::LoadEventScripts() { LoadScripts(sEventScripts, "dbscripts_on_event"); 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 = 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, "dbscripts_on_gossip"); // checks are done in LoadGossipMenuItems and LoadGossipMenu } void ScriptMgr::LoadCreatureMovementScripts() { 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, true); std::set ids; for (int32 i = MIN_DB_SCRIPT_STRING_ID; i < MAX_DB_SCRIPT_STRING_ID; ++i) if (sObjectMgr.GetMangosStringLocale(i)) { ids.insert(i); } CheckScriptTexts(sQuestEndScripts, ids); CheckScriptTexts(sQuestStartScripts, ids); CheckScriptTexts(sSpellScripts, ids); CheckScriptTexts(sGameObjectScripts, ids); CheckScriptTexts(sGameObjectTemplateScripts, ids); CheckScriptTexts(sEventScripts, ids); CheckScriptTexts(sGossipScripts, ids); CheckScriptTexts(sCreatureDeathScripts, ids); CheckScriptTexts(sCreatureMovementScripts, ids); sWaypointMgr.CheckTextsExistance(ids); for (std::set::const_iterator itr = ids.begin(); itr != ids.end(); ++itr) { sLog.outErrorDb("Table `db_script_string` has unused string id %u", *itr); } } void ScriptMgr::CheckScriptTexts(ScriptMapMapName const& scripts, std::set& ids) { for (ScriptMapMap::const_iterator itrMM = scripts.second.begin(); itrMM != scripts.second.end(); ++itrMM) { for (ScriptMap::const_iterator itrM = itrMM->second.begin(); itrM != itrMM->second.end(); ++itrM) { if (itrM->second.command == SCRIPT_COMMAND_TALK) { for (int i = 0; i < MAX_TEXT_ID; ++i) { if (itrM->second.textId[i] && !sObjectMgr.GetMangosStringLocale(itrM->second.textId[i])) { sLog.outErrorDb("Table `db_script_string` is missing string id %u, used in database script table %s id %u.", itrM->second.textId[i], scripts.first, itrMM->first); } if (ids.find(itrM->second.textId[i]) != ids.end()) { ids.erase(itrM->second.textId[i]); } } } } } } // ///////////////////////////////////////////////////////// // DB SCRIPT ENGINE // ///////////////////////////////////////////////////////// /// Helper function to get Object source or target for Script-Command /// returns false iff an error happened bool ScriptAction::GetScriptCommandObject(const ObjectGuid guid, bool includeItem, Object*& resultObject) { resultObject = NULL; if (!guid) { return true; } switch (guid.GetHigh()) { case HIGHGUID_UNIT: resultObject = m_map->GetCreature(guid); break; case HIGHGUID_PET: resultObject = m_map->GetPet(guid); break; case HIGHGUID_PLAYER: resultObject = m_map->GetPlayer(guid); break; case HIGHGUID_GAMEOBJECT: resultObject = m_map->GetGameObject(guid); break; case HIGHGUID_CORPSE: resultObject = HashMapHolder::Find(guid); break; case HIGHGUID_ITEM: // case HIGHGUID_CONTAINER: ==HIGHGUID_ITEM { if (includeItem) { if (Player* player = m_map->GetPlayer(m_ownerGuid)) { resultObject = player->GetItemByGuid(guid); } break; } // else no break, but display error message } default: sLog.outErrorDb(" DB-SCRIPTS: Process table `%s` id %u, command %u with unsupported guid %s, skipping", m_table, m_script->id, m_script->command, guid.GetString().c_str()); return false; } if (resultObject && !resultObject->IsInWorld()) { resultObject = NULL; } return true; } /// Select source and target for a script command /// Returns false iff an error happened bool ScriptAction::GetScriptProcessTargets(WorldObject* pOrigSource, WorldObject* pOrigTarget, WorldObject*& pFinalSource, WorldObject*& pFinalTarget) { WorldObject* pBuddy = NULL; if (m_script->buddyEntry) { if (m_script->data_flags & SCRIPT_FLAG_BUDDY_BY_GUID) { if (m_script->IsCreatureBuddy()) { CreatureInfo const* cinfo = ObjectMgr::GetCreatureTemplate(m_script->buddyEntry); if (cinfo != NULL) { pBuddy = m_map->GetCreature(cinfo->GetObjectGuid(m_script->searchRadiusOrGuid)); if (pBuddy && !((Creature*)pBuddy)->IsAlive()) { sLog.outError(" DB-SCRIPTS: Process table `%s` id %u, command %u has buddy %u by guid %u but buddy is dead, skipping.", m_table, m_script->id, m_script->command, m_script->buddyEntry, m_script->searchRadiusOrGuid); return false; } } else { sLog.outError(" DB-SCRIPTS: Process table `%s` id %u, command %u has no buddy %u by guid %u, skipping.", m_table, m_script->id, m_script->command, m_script->buddyEntry, m_script->searchRadiusOrGuid); return false; } } else { // GameObjectInfo const* ginfo = ObjectMgr::GetGameObjectInfo(m_script->buddyEntry); pBuddy = m_map->GetGameObject(ObjectGuid(HIGHGUID_GAMEOBJECT, m_script->buddyEntry, m_script->searchRadiusOrGuid)); } // TODO Maybe load related grid if not already done? How to handle multi-map case? if (!pBuddy) { sLog.outErrorDb(" DB-SCRIPTS: Process table `%s` id %u, command %u has buddy %u by guid %u not loaded in map %u (data-flags %u), skipping.", m_table, m_script->id, m_script->command, m_script->buddyEntry, m_script->searchRadiusOrGuid, m_map->GetId(), m_script->data_flags); return false; } } else // Buddy by entry { if (!pOrigSource && !pOrigTarget) { sLog.outErrorDb(" DB-SCRIPTS: Process table `%s` id %u, command %u called without buddy %u, but no source for search available, skipping.", m_table, m_script->id, m_script->command, m_script->buddyEntry); return false; } // Prefer non-players as searcher WorldObject* pSearcher = pOrigSource ? pOrigSource : pOrigTarget; if (pSearcher->GetTypeId() == TYPEID_PLAYER && pOrigTarget && pOrigTarget->GetTypeId() != TYPEID_PLAYER) { pSearcher = pOrigTarget; } if (m_script->IsCreatureBuddy()) { Creature* pCreatureBuddy = NULL; MaNGOS::NearestCreatureEntryWithLiveStateInObjectRangeCheck u_check(*pSearcher, m_script->buddyEntry, true, false, m_script->searchRadiusOrGuid, true); MaNGOS::CreatureLastSearcher searcher(pCreatureBuddy, u_check); if (m_script->data_flags & SCRIPT_FLAG_BUDDY_IS_PET) { Cell::VisitWorldObjects(pSearcher, searcher, m_script->searchRadiusOrGuid); } else // Normal Creature { Cell::VisitGridObjects(pSearcher, searcher, m_script->searchRadiusOrGuid); } pBuddy = pCreatureBuddy; // TODO: Remove this extra check output after a while - it might have false effects if (!pBuddy && pSearcher->GetEntry() == m_script->buddyEntry) { sLog.outErrorDb(" DB-SCRIPTS: WARNING: Process table `%s` id %u, command %u has no OTHER buddy %u found - maybe you need to update the script?", m_table, m_script->id, m_script->command, m_script->buddyEntry); pBuddy = pSearcher; } } else { GameObject* pGOBuddy = NULL; MaNGOS::NearestGameObjectEntryInObjectRangeCheck u_check(*pSearcher, m_script->buddyEntry, m_script->searchRadiusOrGuid); MaNGOS::GameObjectLastSearcher searcher(pGOBuddy, u_check); Cell::VisitGridObjects(pSearcher, searcher, m_script->searchRadiusOrGuid); pBuddy = pGOBuddy; } if (!pBuddy) { sLog.outErrorDb(" DB-SCRIPTS: Process table `%s` id %u, command %u has buddy %u not found in range %u of searcher %s (data-flags %u), skipping.", m_table, m_script->id, m_script->command, m_script->buddyEntry, m_script->searchRadiusOrGuid, pSearcher->GetGuidStr().c_str(), m_script->data_flags); return false; } } } if (m_script->data_flags & SCRIPT_FLAG_BUDDY_AS_TARGET) { pFinalSource = pOrigSource; pFinalTarget = pBuddy; } else { pFinalSource = pBuddy ? pBuddy : pOrigSource; pFinalTarget = pOrigTarget; } if (m_script->data_flags & SCRIPT_FLAG_REVERSE_DIRECTION) { std::swap(pFinalSource, pFinalTarget); } if (m_script->data_flags & SCRIPT_FLAG_SOURCE_TARGETS_SELF) { pFinalTarget = pFinalSource; } return true; } /// Helper to log error information bool ScriptAction::LogIfNotCreature(WorldObject* pWorldObject) { if (!pWorldObject || pWorldObject->GetTypeId() != TYPEID_UNIT) { sLog.outErrorDb(" DB-SCRIPTS: Process table `%s` id %u, command %u call for non-creature, skipping.", m_table, m_script->id, m_script->command); return true; } return false; } bool ScriptAction::LogIfNotUnit(WorldObject* pWorldObject) { if (!pWorldObject || !pWorldObject->isType(TYPEMASK_UNIT)) { sLog.outErrorDb(" DB-SCRIPTS: Process table `%s` id %u, command %u call for non-unit, skipping.", m_table, m_script->id, m_script->command); return true; } return false; } bool ScriptAction::LogIfNotGameObject(WorldObject* pWorldObject) { if (!pWorldObject || pWorldObject->GetTypeId() != TYPEID_GAMEOBJECT) { sLog.outErrorDb(" DB-SCRIPTS: Process table `%s` id %u, command %u call for non-gameobject, skipping.", m_table, m_script->id, m_script->command); return true; } return false; } bool ScriptAction::LogIfNotPlayer(WorldObject* pWorldObject) { if (!pWorldObject || pWorldObject->GetTypeId() != TYPEID_PLAYER) { sLog.outErrorDb(" DB-SCRIPTS: Process table `%s` id %u, command %u call for non-player, skipping.", m_table, m_script->id, m_script->command); return true; } return false; } /// Helper to get a player if possible (target preferred) Player* ScriptAction::GetPlayerTargetOrSourceAndLog(WorldObject* pSource, WorldObject* pTarget) { if ((!pTarget || pTarget->GetTypeId() != TYPEID_PLAYER) && (!pSource || pSource->GetTypeId() != TYPEID_PLAYER)) { sLog.outErrorDb(" DB-SCRIPTS: Process table `%s` id %u, command %u call for non player, skipping.", m_table, m_script->id, m_script->command); return NULL; } return pTarget && pTarget->GetTypeId() == TYPEID_PLAYER ? (Player*)pTarget : (Player*)pSource; } /// Handle one Script Step // Return true if and only if further parts of this script shall be skipped bool ScriptAction::HandleScriptStep() { WorldObject* pSource; WorldObject* pTarget; Object* pSourceOrItem; // Stores a provided pSource (if exists as WorldObject) or source-item { // 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; } // 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) { case SCRIPT_COMMAND_TALK: // 0 { if (!pSource) { sLog.outErrorDb(" DB-SCRIPTS: Process table `%s` id %u, command %u found no worldobject as source, skipping.", m_table, m_script->id, m_script->command); break; } Unit* unitTarget = pTarget && pTarget->isType(TYPEMASK_UNIT) ? static_cast(pTarget) : NULL; int32 textId = m_script->textId[0]; // May have text for random if (m_script->textId[1]) { int i = 2; for (; i < MAX_TEXT_ID; ++i) { if (!m_script->textId[i]) { break; } } // Use one random textId = m_script->textId[urand(0, i - 1)]; } if (!DoDisplayText(pSource, textId, unitTarget)) { sLog.outErrorDb(" DB-SCRIPTS: Process table `%s` id %u, could not display text %i properly", m_table, m_script->id, textId); } break; } case SCRIPT_COMMAND_EMOTE: // 1 { if (LogIfNotUnit(pSource)) { break; } std::vector emotes; emotes.push_back(m_script->emote.emoteId); for (int i = 0; i < MAX_TEXT_ID; ++i) { if (!m_script->textId[i]) { break; } emotes.push_back(uint32(m_script->textId[i])); } ((Unit*)pSource)->HandleEmote(emotes[urand(0, emotes.size() - 1)]); break; } case SCRIPT_COMMAND_FIELD_SET: // 2 if (!pSourceOrItem) { sLog.outErrorDb(" 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 >= pSourceOrItem->GetValuesCount()) { sLog.outErrorDb(" 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; } pSourceOrItem->SetUInt32Value(m_script->setField.fieldId, m_script->setField.fieldValue); break; case SCRIPT_COMMAND_MOVE_TO: // 3 { if (LogIfNotUnit(pSource)) { break; } // Just turn around if ((m_script->x == 0.0f && m_script->y == 0.0f && m_script->z == 0.0f) || // Check point-to-point distance, hence revert effect of bounding radius ((Unit*)pSource)->IsWithinDist3d(m_script->x, m_script->y, m_script->z, 0.01f - ((Unit*)pSource)->GetObjectBoundingRadius())) { ((Unit*)pSource)->SetFacingTo(m_script->o); break; } // For command additional teleport the unit if (m_script->data_flags & SCRIPT_FLAG_COMMAND_ADDITIONAL) { ((Unit*)pSource)->NearTeleportTo(m_script->x, m_script->y, m_script->z, m_script->o != 0.0f ? m_script->o : ((Unit*)pSource)->GetOrientation()); break; } // Normal Movement if (m_script->moveTo.travelSpeed) { ((Unit*)pSource)->MonsterMoveWithSpeed(m_script->x, m_script->y, m_script->z, m_script->moveTo.travelSpeed * 0.01f); } else { ((Unit*)pSource)->GetMotionMaster()->Clear(); ((Unit*)pSource)->GetMotionMaster()->MovePoint(0, m_script->x, m_script->y, m_script->z); } break; } case SCRIPT_COMMAND_FLAG_SET: // 4 if (!pSourceOrItem) { sLog.outErrorDb("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 >= pSourceOrItem->GetValuesCount()) { sLog.outErrorDb("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; } pSourceOrItem->SetFlag(m_script->setFlag.fieldId, m_script->setFlag.fieldValue); break; case SCRIPT_COMMAND_FLAG_REMOVE: // 5 if (!pSourceOrItem) { sLog.outErrorDb("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 >= pSourceOrItem->GetValuesCount()) { sLog.outErrorDb("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; } pSourceOrItem->RemoveFlag(m_script->removeFlag.fieldId, m_script->removeFlag.fieldValue); break; case SCRIPT_COMMAND_TELEPORT_TO: // 6 { Player* pPlayer = GetPlayerTargetOrSourceAndLog(pSource, pTarget); if (!pPlayer) { break; } pPlayer->TeleportTo(m_script->teleportTo.mapId, m_script->x, m_script->y, m_script->z, m_script->o); break; } case SCRIPT_COMMAND_QUEST_EXPLORED: // 7 { Player* pPlayer = GetPlayerTargetOrSourceAndLog(pSource, pTarget); if (!pPlayer) { break; } WorldObject* pWorldObject = NULL; if (pSource && pSource->isType(TYPEMASK_CREATURE_OR_GAMEOBJECT)) { pWorldObject = pSource; } else if (pTarget && pTarget->isType(TYPEMASK_CREATURE_OR_GAMEOBJECT)) { pWorldObject = pTarget; } // if we have a distance, we must have a worldobject if (m_script->questExplored.distance != 0 && !pWorldObject) { sLog.outErrorDb(" DB-SCRIPTS: Process table `%s` id %u, command %u called without source worldobject, skipping.", m_table, m_script->id, m_script->command); break; } bool failQuest = false; // Creature must be alive for giving credit if (pWorldObject && pWorldObject->GetTypeId() == TYPEID_UNIT && !((Creature*)pWorldObject)->IsAlive()) { failQuest = true; } else if (m_script->questExplored.distance != 0 && !pWorldObject->IsWithinDistInMap(pPlayer, float(m_script->questExplored.distance))) { failQuest = true; } // quest id and flags checked at script loading if (!failQuest) { pPlayer->AreaExploredOrEventHappens(m_script->questExplored.questId); } else { pPlayer->FailQuest(m_script->questExplored.questId); } break; } case SCRIPT_COMMAND_KILL_CREDIT: // 8 { Player* pPlayer = GetPlayerTargetOrSourceAndLog(pSource, pTarget); if (!pPlayer) { break; } uint32 creatureEntry = m_script->killCredit.creatureEntry; WorldObject* pRewardSource = pSource && pSource->GetTypeId() == TYPEID_UNIT ? pSource : (pTarget && pTarget->GetTypeId() == TYPEID_UNIT ? pTarget : NULL); // dynamic effect, take entry of reward Source if (!creatureEntry) { if (pRewardSource) { creatureEntry = pRewardSource->GetEntry(); } else { sLog.outErrorDb(" DB-SCRIPTS: Process table `%s` id %u, command %u called for dynamic killcredit without creature partner, skipping.", m_table, m_script->id, m_script->command); break; } } if (m_script->killCredit.isGroupCredit) { WorldObject* pSearcher = pRewardSource ? pRewardSource : (pSource ? pSource : pTarget); if (pSearcher != pRewardSource) { sLog.outDebug(" DB-SCRIPTS: Process table `%s` id %u, SCRIPT_COMMAND_KILL_CREDIT called for groupCredit without creature as searcher, script might need adjustment.", m_table, m_script->id); } pPlayer->RewardPlayerAndGroupAtEvent(creatureEntry, pSearcher); } else { pPlayer->KilledMonsterCredit(creatureEntry, pRewardSource ? pRewardSource->GetObjectGuid() : ObjectGuid()); } break; } case SCRIPT_COMMAND_RESPAWN_GAMEOBJECT: // 9 { GameObject* pGo = NULL; uint32 time_to_despawn = m_script->respawnGo.despawnDelay < 5 ? 5 : m_script->respawnGo.despawnDelay; if (m_script->respawnGo.goGuid) { GameObjectData const* goData = sObjectMgr.GetGOData(m_script->respawnGo.goGuid); if (!goData) { break; } // checked at load // TODO - This was a change, was before current map of source pGo = m_map->GetGameObject(ObjectGuid(HIGHGUID_GAMEOBJECT, goData->id, m_script->respawnGo.goGuid)); } else { if (LogIfNotGameObject(pSource)) { break; } pGo = (GameObject*)pSource; } if (!pGo) { sLog.outErrorDb(" DB-SCRIPTS: Process table `%s` id %u, command %u failed for gameobject(guid: %u, buddyEntry: %u).", m_table, m_script->id, m_script->command, m_script->respawnGo.goGuid, m_script->buddyEntry); break; } if (pGo->GetGoType() == GAMEOBJECT_TYPE_FISHINGNODE || pGo->GetGoType() == GAMEOBJECT_TYPE_DOOR) { sLog.outErrorDb(" DB-SCRIPTS: Process table `%s` id %u, command %u can not be used with gameobject of type %u (guid: %u, buddyEntry: %u).", m_table, m_script->id, m_script->command, uint32(pGo->GetGoType()), m_script->respawnGo.goGuid, m_script->buddyEntry); break; } if (pGo->isSpawned()) { break; } // gameobject already spawned pGo->SetLootState(GO_READY); pGo->SetRespawnTime(time_to_despawn); // despawn object in ? seconds pGo->Refresh(); break; } case SCRIPT_COMMAND_TEMP_SUMMON_CREATURE: // 10 { if (!pSource) { sLog.outErrorDb(" DB-SCRIPTS: Process table `%s` id %u, command %u found no worldobject as source, skipping.", m_table, m_script->id, m_script->command); break; } float x = m_script->x; float y = m_script->y; 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_OOC_OR_DEAD_DESPAWN : TEMPSUMMON_DEAD_DESPAWN, m_script->summonCreature.despawnDelay, (m_script->data_flags & SCRIPT_FLAG_COMMAND_ADDITIONAL) ? true : false); if (!pCreature) { sLog.outErrorDb(" 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); break; } break; } case SCRIPT_COMMAND_OPEN_DOOR: // 11 case SCRIPT_COMMAND_CLOSE_DOOR: // 12 { GameObject* pDoor; uint32 time_to_reset = m_script->changeDoor.resetDelay < 15 ? 15 : m_script->changeDoor.resetDelay; if (m_script->changeDoor.goGuid) { GameObjectData const* goData = sObjectMgr.GetGOData(m_script->changeDoor.goGuid); if (!goData) // checked at load { break; } // TODO - Was a change, before random map pDoor = m_map->GetGameObject(ObjectGuid(HIGHGUID_GAMEOBJECT, goData->id, m_script->changeDoor.goGuid)); } else { if (LogIfNotGameObject(pSource)) { break; } pDoor = (GameObject*)pSource; } if (!pDoor) { sLog.outErrorDb(" DB-SCRIPTS: Process table `%s` id %u, command %u failed for gameobject(guid: %u, buddyEntry: %u).", m_table, m_script->id, m_script->command, m_script->changeDoor.goGuid, m_script->buddyEntry); break; } if (pDoor->GetGoType() != GAMEOBJECT_TYPE_DOOR) { sLog.outErrorDb(" DB-SCRIPTS: Process table `%s` id %u, command %u failed for non-door(GoType: %u).", m_table, m_script->id, m_script->command, pDoor->GetGoType()); break; } if ((m_script->command == SCRIPT_COMMAND_OPEN_DOOR && pDoor->GetGoState() != GO_STATE_READY) || (m_script->command == SCRIPT_COMMAND_CLOSE_DOOR && pDoor->GetGoState() == GO_STATE_READY)) { break; } // to be opened door already open, or to be closed door already closed pDoor->UseDoorOrButton(time_to_reset); if (pTarget && pTarget->isType(TYPEMASK_GAMEOBJECT) && ((GameObject*)pTarget)->GetGoType() == GAMEOBJECT_TYPE_BUTTON) { ((GameObject*)pTarget)->UseDoorOrButton(time_to_reset); } break; } case SCRIPT_COMMAND_ACTIVATE_OBJECT: // 13 { if (LogIfNotUnit(pSource)) { break; } if (LogIfNotGameObject(pTarget)) { break; } ((GameObject*)pTarget)->Use((Unit*)pSource); break; } case SCRIPT_COMMAND_REMOVE_AURA: // 14 { if (LogIfNotUnit(pSource)) { break; } ((Unit*)pSource)->RemoveAurasDueToSpell(m_script->removeAura.spellId); break; } case SCRIPT_COMMAND_CAST_SPELL: // 15 { if (LogIfNotUnit(pTarget)) // TODO - Change when support for casting without victim will be supported { break; } // Select Spell uint32 spell = m_script->castSpell.spellId; uint32 filledCount = 0; while (filledCount < MAX_TEXT_ID && m_script->textId[filledCount]) // Count which dataint fields are filled ++filledCount; if (filledCount > 0) if (uint32 randomField = urand(0, filledCount)) // Random selection resulted in one of the dataint fields spell = m_script->textId[randomField - 1]; // TODO: when GO cast implemented, code below must be updated accordingly to also allow GO spell cast if (pSource && pSource->GetTypeId() == TYPEID_GAMEOBJECT) { ((Unit*)pTarget)->CastSpell(((Unit*)pTarget), spell, true, NULL, NULL, pSource->GetObjectGuid()); { break; } } if (LogIfNotUnit(pSource)) { break; } ((Unit*)pSource)->CastSpell(((Unit*)pTarget), spell, (m_script->data_flags & SCRIPT_FLAG_COMMAND_ADDITIONAL) != 0); break; } case SCRIPT_COMMAND_PLAY_SOUND: // 16 { if (!pSource) { sLog.outErrorDb(" DB-SCRIPTS: Process table `%s` id %u, command %u could not find proper source", m_table, m_script->id, m_script->command); break; } // 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) { pSoundTarget = GetPlayerTargetOrSourceAndLog(pSource, pTarget); if (!pSoundTarget) { break; } } if (m_script->playSound.flags & 2) { 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, pSoundTarget); } break; } case SCRIPT_COMMAND_CREATE_ITEM: // 17 { Player* pPlayer = GetPlayerTargetOrSourceAndLog(pSource, pTarget); if (!pPlayer) { break; } if (Item* pItem = pPlayer->StoreNewItemInInventorySlot(m_script->createItem.itemEntry, m_script->createItem.amount)) { pPlayer->SendNewItem(pItem, m_script->createItem.amount, true, false); } break; } case SCRIPT_COMMAND_DESPAWN_SELF: // 18 { // TODO - Remove this check after a while if (pTarget && pTarget->GetTypeId() != TYPEID_UNIT && pSource && pSource->GetTypeId() == TYPEID_UNIT) { sLog.outErrorDb("DB-SCRIPTS: Process table `%s` id %u, command %u target must be creature, but (only) source is, use data_flags to fix", m_table, m_script->id, m_script->command); pTarget = pSource; } if (LogIfNotCreature(pTarget)) { break; } ((Creature*)pTarget)->ForcedDespawn(m_script->despawn.despawnDelay); break; } case SCRIPT_COMMAND_PLAY_MOVIE: // 19 { break; // must be skipped at loading } case SCRIPT_COMMAND_MOVEMENT: // 20 { if (LogIfNotCreature(pSource)) { break; } // Consider add additional checks for cases where creature should not change movementType // (pet? in combat? already using same MMgen as script try to apply?) switch (m_script->movement.movementType) { case IDLE_MOTION_TYPE: ((Creature*)pSource)->GetMotionMaster()->MoveIdle(); break; case RANDOM_MOTION_TYPE: if (m_script->data_flags & SCRIPT_FLAG_COMMAND_ADDITIONAL) { ((Creature*)pSource)->GetMotionMaster()->MoveRandomAroundPoint(pSource->GetPositionX(), pSource->GetPositionY(), pSource->GetPositionZ(), float(m_script->movement.wanderDistance)); } else { float respX, respY, respZ, respO, wander_distance; ((Creature*)pSource)->GetRespawnCoord(respX, respY, respZ, &respO, &wander_distance); wander_distance = m_script->movement.wanderDistance ? m_script->movement.wanderDistance : wander_distance; ((Creature*)pSource)->GetMotionMaster()->MoveRandomAroundPoint(respX, respY, respZ, wander_distance); } break; case WAYPOINT_MOTION_TYPE: ((Creature*)pSource)->GetMotionMaster()->MoveWaypoint(); break; } break; } case SCRIPT_COMMAND_SET_ACTIVEOBJECT: // 21 { if (LogIfNotCreature(pSource)) { break; } ((Creature*)pSource)->SetActiveObjectState(m_script->activeObject.activate); break; } case SCRIPT_COMMAND_SET_FACTION: // 22 { if (LogIfNotCreature(pSource)) { break; } if (m_script->faction.factionId) { ((Creature*)pSource)->SetFactionTemporary(m_script->faction.factionId, m_script->faction.flags); } else { ((Creature*)pSource)->ClearTemporaryFaction(); } break; } case SCRIPT_COMMAND_MORPH_TO_ENTRY_OR_MODEL: // 23 { if (LogIfNotCreature(pSource)) { break; } if (!m_script->morph.creatureOrModelEntry) { ((Creature*)pSource)->DeMorph(); } else if (m_script->data_flags & SCRIPT_FLAG_COMMAND_ADDITIONAL) { ((Creature*)pSource)->SetDisplayId(m_script->morph.creatureOrModelEntry); } else { CreatureInfo const* ci = ObjectMgr::GetCreatureTemplate(m_script->morph.creatureOrModelEntry); uint32 display_id = Creature::ChooseDisplayId(ci); ((Creature*)pSource)->SetDisplayId(display_id); } break; } case SCRIPT_COMMAND_MOUNT_TO_ENTRY_OR_MODEL: // 24 { if (LogIfNotCreature(pSource)) { break; } if (!m_script->mount.creatureOrModelEntry) { ((Creature*)pSource)->Unmount(); } else if (m_script->data_flags & SCRIPT_FLAG_COMMAND_ADDITIONAL) { ((Creature*)pSource)->Mount(m_script->mount.creatureOrModelEntry); } else { CreatureInfo const* ci = ObjectMgr::GetCreatureTemplate(m_script->mount.creatureOrModelEntry); uint32 display_id = Creature::ChooseDisplayId(ci); ((Creature*)pSource)->Mount(display_id); } break; } case SCRIPT_COMMAND_SET_RUN: // 25 { if (LogIfNotCreature(pSource)) { break; } ((Creature*)pSource)->SetWalk(!m_script->run.run, true); break; } case SCRIPT_COMMAND_ATTACK_START: // 26 { if (LogIfNotCreature(pSource)) { break; } if (LogIfNotUnit(pTarget)) { break; } Creature* pAttacker = static_cast(pSource); Unit* unitTarget = static_cast(pTarget); if (pAttacker->IsFriendlyTo(unitTarget)) { sLog.outErrorDb(" DB-SCRIPTS: Process table `%s` id %u, command %u attacker is friendly to target, can not attack (Attacker: %s, Target: %s)", m_table, m_script->id, m_script->command, pAttacker->GetGuidStr().c_str(), unitTarget->GetGuidStr().c_str()); break; } pAttacker->AI()->AttackStart(unitTarget); break; } case SCRIPT_COMMAND_GO_LOCK_STATE: // 27 { if (LogIfNotGameObject(pSource)) { break; } GameObject* pGo = static_cast(pSource); /* flag lockState * go_lock 0x01 * go_unlock 0x02 * go_nonInteract 0x04 * go_Interact 0x08 */ // Lock or Unlock if (m_script->goLockState.lockState & 0x01) { pGo->SetFlag(GAMEOBJECT_FLAGS, GO_FLAG_LOCKED); } else if (m_script->goLockState.lockState & 0x02) { pGo->RemoveFlag(GAMEOBJECT_FLAGS, GO_FLAG_LOCKED); } // Set Non Interactable or Set Interactable if (m_script->goLockState.lockState & 0x04) { pGo->SetFlag(GAMEOBJECT_FLAGS, GO_FLAG_NO_INTERACT); } else if (m_script->goLockState.lockState & 0x08) { pGo->RemoveFlag(GAMEOBJECT_FLAGS, GO_FLAG_NO_INTERACT); } break; } case SCRIPT_COMMAND_STAND_STATE: // 28 { if (LogIfNotCreature(pSource)) { break; } // Must be safe cast to Unit* here ((Unit*)pSource)->SetStandState(m_script->standState.stand_state); break; } case SCRIPT_COMMAND_MODIFY_NPC_FLAGS: // 29 { if (LogIfNotCreature(pSource)) { break; } // Add Flags if (m_script->npcFlag.change_flag & 0x01) { pSource->SetFlag(UNIT_NPC_FLAGS, m_script->npcFlag.flag); } // Remove Flags else if (m_script->npcFlag.change_flag & 0x02) { pSource->RemoveFlag(UNIT_NPC_FLAGS, m_script->npcFlag.flag); } // Toggle Flags else { if (pSource->HasFlag(UNIT_NPC_FLAGS, m_script->npcFlag.flag)) { pSource->RemoveFlag(UNIT_NPC_FLAGS, m_script->npcFlag.flag); } else { pSource->SetFlag(UNIT_NPC_FLAGS, m_script->npcFlag.flag); } } break; } case SCRIPT_COMMAND_SEND_TAXI_PATH: // 30 { // only Player Player* pPlayer = GetPlayerTargetOrSourceAndLog(pSource, pTarget); if (!pPlayer) { break; } 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, true); 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 other 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 other 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; } case SCRIPT_COMMAND_JOIN_LFG: // 33 { Player* pPlayer = GetPlayerTargetOrSourceAndLog(pSource, pTarget); if (!pPlayer) break; sLFGMgr.AddToQueue(pPlayer, m_script->joinLfg.areaId); break; } case SCRIPT_COMMAND_TERMINATE_COND: // 34 { Player* player = NULL; WorldObject* second = pSource; // First case: target is player if (pTarget && pTarget->GetTypeId() == TYPEID_PLAYER) { player = static_cast(pTarget); } // Second case: source is player else if (pSource && pSource->GetTypeId() == TYPEID_PLAYER) { player = static_cast(pSource); second = pTarget; } bool terminateResult; if (m_script->data_flags & SCRIPT_FLAG_COMMAND_ADDITIONAL) { terminateResult = !sObjectMgr.IsPlayerMeetToCondition(m_script->terminateCond.conditionId, player, m_map, second, CONDITION_FROM_DBSCRIPTS); } else { terminateResult = sObjectMgr.IsPlayerMeetToCondition(m_script->terminateCond.conditionId, player, m_map, second, CONDITION_FROM_DBSCRIPTS); } if (terminateResult && m_script->terminateCond.failQuest && player) { if (Group* group = player->GetGroup()) { for (GroupReference* groupRef = group->GetFirstMember(); groupRef != NULL; groupRef = groupRef->next()) { Player* member = groupRef->getSource(); if (member->GetQuestStatus(m_script->terminateCond.failQuest) == QUEST_STATUS_INCOMPLETE) { member->FailQuest(m_script->terminateCond.failQuest); } } } else { if (player->GetQuestStatus(m_script->terminateCond.failQuest) == QUEST_STATUS_INCOMPLETE) { player->FailQuest(m_script->terminateCond.failQuest); } } } return terminateResult; } case SCRIPT_COMMAND_SEND_AI_EVENT_AROUND: // 35 { if (LogIfNotCreature(pSource)) return false; if (LogIfNotUnit(pTarget)) break; ((Creature*)pSource)->AI()->SendAIEventAround(AIEventType(m_script->sendAIEvent.eventType), (Unit*)pTarget, 0, float(m_script->sendAIEvent.radius)); break; } case SCRIPT_COMMAND_TURN_TO: // 36 { if (LogIfNotUnit(pSource)) { break; } ((Unit*)pSource)->SetFacingTo(pSource->GetAngle(pTarget)); break; } case SCRIPT_COMMAND_MOVE_DYNAMIC: // 37 { if (LogIfNotCreature(pSource)) return false; if (LogIfNotUnit(pTarget)) return false; float x, y, z; if (m_script->moveDynamic.maxDist == 0) // Move to pTarget { if (pTarget == pSource) { sLog.outErrorDb(" DB-SCRIPTS: Process table `%s` id %u, _MOVE_DYNAMIC called with maxDist == 0, but resultingSource == resultingTarget (== %s)", m_table, m_script->id, pSource->GetGuidStr().c_str()); break; } pTarget->GetContactPoint(pSource, x, y, z); } else // Calculate position { float orientation; if (m_script->data_flags & SCRIPT_FLAG_COMMAND_ADDITIONAL) orientation = pSource->GetOrientation() + m_script->o + 2 * M_PI_F; else orientation = m_script->o; pSource->GetRandomPoint(pTarget->GetPositionX(), pTarget->GetPositionY(), pTarget->GetPositionZ(), m_script->moveDynamic.maxDist, x, y, z, m_script->moveDynamic.minDist, (orientation == 0.0f ? NULL : &orientation)); z = std::max(z, pTarget->GetPositionZ()); pSource->UpdateAllowedPositionZ(x, y, z); } ((Creature*)pSource)->GetMotionMaster()->MovePoint(1, x, y, z); break; } default: sLog.outErrorDb(" DB-SCRIPTS: Process table `%s` id %u, command %u unknown command used.", m_table, m_script->id, m_script->command); break; } return false; } // ///////////////////////////////////////////////////////// // Scripting Library Hooks // ///////////////////////////////////////////////////////// void ScriptMgr::LoadAreaTriggerScripts() { m_AreaTriggerScripts.clear(); // need for reload case QueryResult* result = WorldDatabase.Query("SELECT entry, ScriptName FROM scripted_areatrigger"); uint32 count = 0; if (!result) { BarGoLink bar(1); bar.step(); sLog.outString(">> Loaded %u scripted areatrigger", count); sLog.outString(); return; } BarGoLink bar(result->GetRowCount()); do { ++count; bar.step(); Field* fields = result->Fetch(); uint32 triggerId = fields[0].GetUInt32(); const char* scriptName = fields[1].GetString(); if (!sAreaTriggerStore.LookupEntry(triggerId)) { sLog.outErrorDb("Table `scripted_areatrigger` has area trigger (ID: %u) not listed in `AreaTrigger.dbc`.", triggerId); continue; } m_AreaTriggerScripts[triggerId] = GetScriptId(scriptName); } while (result->NextRow()); delete result; sLog.outString(">> Loaded %u areatrigger scripts", count); sLog.outString(); } void ScriptMgr::LoadEventIdScripts() { m_EventIdScripts.clear(); // need for reload case QueryResult* result = WorldDatabase.Query("SELECT id, ScriptName FROM scripted_event"); uint32 count = 0; if (!result) { BarGoLink bar(1); bar.step(); sLog.outString(">> Loaded %u scripted event id", count); sLog.outString(); return; } BarGoLink bar(result->GetRowCount()); std::set eventIds; // Store possible event ids CollectPossibleEventIds(eventIds); do { ++count; bar.step(); Field* fields = result->Fetch(); uint32 eventId = fields[0].GetUInt32(); const char* scriptName = fields[1].GetString(); std::set::const_iterator itr = eventIds.find(eventId); if (itr == eventIds.end()) sLog.outErrorDb("Table `scripted_event` 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); m_EventIdScripts[eventId] = GetScriptId(scriptName); } while (result->NextRow()); delete result; sLog.outString(">> Loaded %u scripted event id", count); sLog.outString(); } void ScriptMgr::LoadScriptNames() { m_scriptNames.push_back(""); QueryResult* result = WorldDatabase.Query( "SELECT DISTINCT(ScriptName) FROM creature_template WHERE ScriptName <> '' " "UNION " "SELECT DISTINCT(ScriptName) FROM gameobject_template WHERE ScriptName <> '' " "UNION " "SELECT DISTINCT(ScriptName) FROM item_template WHERE ScriptName <> '' " "UNION " "SELECT DISTINCT(ScriptName) FROM scripted_areatrigger WHERE ScriptName <> '' " "UNION " "SELECT DISTINCT(ScriptName) FROM scripted_event WHERE ScriptName <> '' " "UNION " "SELECT DISTINCT(ScriptName) FROM instance_template WHERE ScriptName <> '' " "UNION " "SELECT DISTINCT(ScriptName) FROM world_template WHERE ScriptName <> ''"); if (!result) { BarGoLink bar(1); bar.step(); sLog.outErrorDb(">> Loaded empty set of Script Names!"); sLog.outString(); return; } BarGoLink bar(result->GetRowCount()); uint32 count = 0; do { bar.step(); m_scriptNames.push_back((*result)[0].GetString()); ++count; } while (result->NextRow()); delete result; std::sort(m_scriptNames.begin(), m_scriptNames.end()); sLog.outString(">> Loaded %d Script Names", count); sLog.outString(); } uint32 ScriptMgr::GetScriptId(const char* name) const { // use binary search to find the script name in the sorted vector // assume "" is the first element if (!name) { return 0; } ScriptNameMap::const_iterator itr = std::lower_bound(m_scriptNames.begin(), m_scriptNames.end(), name); if (itr == m_scriptNames.end() || *itr != name) { return 0; } return uint32(itr - m_scriptNames.begin()); } uint32 ScriptMgr::GetAreaTriggerScriptId(uint32 triggerId) const { AreaTriggerScriptMap::const_iterator itr = m_AreaTriggerScripts.find(triggerId); if (itr != m_AreaTriggerScripts.end()) { return itr->second; } return 0; } uint32 ScriptMgr::GetEventIdScriptId(uint32 eventId) const { EventIdScriptMap::const_iterator itr = m_EventIdScripts.find(eventId); if (itr != m_EventIdScripts.end()) { return itr->second; } return 0; } char const* ScriptMgr::GetScriptLibraryVersion() const { #ifdef ENABLE_SD2 return SD2::GetScriptLibraryVersion(); #else return NULL; #endif } CreatureAI* ScriptMgr::GetCreatureAI(Creature* pCreature) { // Used by Eluna #ifdef ENABLE_ELUNA if (CreatureAI* luaAI = sEluna->GetAI(pCreature)) return luaAI; #endif /* ENABLE_ELUNA */ #ifdef ENABLE_SD2 return SD2::GetCreatureAI(pCreature); #else return NULL; #endif } InstanceData* ScriptMgr::CreateInstanceData(Map* pMap) { #ifdef ENABLE_SD2 return SD2::CreateInstanceData(pMap); #else return NULL; #endif } bool ScriptMgr::OnGossipHello(Player* pPlayer, Creature* pCreature) { // Used by Eluna #ifdef ENABLE_ELUNA if (sEluna->OnGossipHello(pPlayer, pCreature)) return true; #endif /* ENABLE_ELUNA */ #ifdef ENABLE_SD2 return SD2::GossipHello(pPlayer, pCreature); #else return false; #endif } bool ScriptMgr::OnGossipHello(Player* pPlayer, GameObject* pGameObject) { // Used by Eluna #ifdef ENABLE_ELUNA if (sEluna->OnGossipHello(pPlayer, pGameObject)) return true; #endif /* ENABLE_ELUNA */ #ifdef ENABLE_SD2 return SD2::GOGossipHello(pPlayer, pGameObject); #else return false; #endif } bool ScriptMgr::OnGossipSelect(Player* pPlayer, Creature* pCreature, uint32 sender, uint32 action, const char* code) { #ifdef ENABLE_ELUNA if (code) { // Used by Eluna if (sEluna->OnGossipSelectCode(pPlayer, pCreature, sender, action, code)) return true; } else { // Used by Eluna if (sEluna->OnGossipSelect(pPlayer, pCreature, sender, action)) return true; } #endif /* ENABLE_ELUNA */ #ifdef ENABLE_SD2 if (code) { return SD2::GossipSelectWithCode(pPlayer, pCreature, sender, action, code); } else { return SD2::GossipSelect(pPlayer, pCreature, sender, action); } #else return false; #endif } bool ScriptMgr::OnGossipSelect(Player* pPlayer, GameObject* pGameObject, uint32 sender, uint32 action, const char* code) { // Used by Eluna #ifdef ENABLE_ELUNA if (code) { if (sEluna->OnGossipSelectCode(pPlayer, pGameObject, sender, action, code)) return true; } else { if (sEluna->OnGossipSelect(pPlayer, pGameObject, sender, action)) return true; } #endif /* ENABLE_ELUNA */ #ifdef ENABLE_SD2 if (code) { return SD2::GOGossipSelectWithCode(pPlayer, pGameObject, sender, action, code); } else { return SD2::GOGossipSelect(pPlayer, pGameObject, sender, action); } #else return false; #endif } bool ScriptMgr::OnQuestAccept(Player* pPlayer, Creature* pCreature, Quest const* pQuest) { // Used by Eluna #ifdef ENABLE_ELUNA if (sEluna->OnQuestAccept(pPlayer, pCreature, pQuest)) return true; #endif /* ENABLE_ELUNA */ #ifdef ENABLE_SD2 return SD2::QuestAccept(pPlayer, pCreature, pQuest); #else return false; #endif } bool ScriptMgr::OnQuestAccept(Player* pPlayer, GameObject* pGameObject, Quest const* pQuest) { // Used by Eluna #ifdef ENABLE_ELUNA if (sEluna->OnQuestAccept(pPlayer, pGameObject, pQuest)) return true; #endif /* ENABLE_ELUNA */ #ifdef ENABLE_SD2 return SD2::GOQuestAccept(pPlayer, pGameObject, pQuest); #else return false; #endif } bool ScriptMgr::OnQuestAccept(Player* pPlayer, Item* pItem, Quest const* pQuest) { // Used by Eluna #ifdef ENABLE_ELUNA if (sEluna->OnQuestAccept(pPlayer, pItem, pQuest)) return true; #endif /* ENABLE_ELUNA */ #ifdef ENABLE_SD2 return SD2::ItemQuestAccept(pPlayer, pItem, pQuest); #else return false; #endif } bool ScriptMgr::OnQuestRewarded(Player* pPlayer, Creature* pCreature, Quest const* pQuest, uint32 reward) { // Used by Eluna #ifdef ENABLE_ELUNA if (sEluna->OnQuestReward(pPlayer, pCreature, pQuest, reward)) return true; #endif /* ENABLE_ELUNA */ #ifdef ENABLE_SD2 return SD2::QuestRewarded(pPlayer, pCreature, pQuest); #else return false; #endif } bool ScriptMgr::OnQuestRewarded(Player* pPlayer, GameObject* pGameObject, Quest const* pQuest, uint32 reward) { // Used by Eluna #ifdef ENABLE_ELUNA if (sEluna->OnQuestReward(pPlayer, pGameObject, pQuest, reward)) return true; #endif /* ENABLE_ELUNA */ #ifdef ENABLE_SD2 return SD2::GOQuestRewarded(pPlayer, pGameObject, pQuest); #else return false; #endif } uint32 ScriptMgr::GetDialogStatus(Player* pPlayer, Creature* pCreature) { // Used by Eluna #ifdef ENABLE_ELUNA if (uint32 dialogId = sEluna->GetDialogStatus(pPlayer, pCreature)) return dialogId; #endif /* ENABLE_ELUNA */ #ifdef ENABLE_SD2 return SD2::GetNPCDialogStatus(pPlayer, pCreature); #else return DIALOG_STATUS_UNDEFINED; #endif } uint32 ScriptMgr::GetDialogStatus(Player* pPlayer, GameObject* pGameObject) { // Used by Eluna #ifdef ENABLE_ELUNA if (uint32 dialogId = sEluna->GetDialogStatus(pPlayer, pGameObject)) return dialogId; #endif /* ENABLE_ELUNA */ #ifdef ENABLE_SD2 return SD2::GetGODialogStatus(pPlayer, pGameObject); #else return DIALOG_STATUS_UNDEFINED; #endif } bool ScriptMgr::OnGameObjectUse(Player* pPlayer, GameObject* pGameObject) { #ifdef ENABLE_ELUNA if (sEluna->OnGameObjectUse(pPlayer, pGameObject)) return true; #endif /* ENABLE_ELUNA */ #ifdef ENABLE_SD2 return SD2::GOUse(pPlayer, pGameObject); #else return false; #endif } bool ScriptMgr::OnItemUse(Player* pPlayer, Item* pItem, SpellCastTargets const& targets) { // Used by Eluna #ifdef ENABLE_ELUNA if (!sEluna->OnUse(pPlayer, pItem, targets)) return true; #endif /* ENABLE_ELUNA */ #ifdef ENABLE_SD2 return SD2::ItemUse(pPlayer, pItem, targets); #else return false; #endif } bool ScriptMgr::OnAreaTrigger(Player* pPlayer, AreaTriggerEntry const* atEntry) { // Used by Eluna #ifdef ENABLE_ELUNA if (sEluna->OnAreaTrigger(pPlayer, atEntry)) return true; #endif /* ENABLE_ELUNA */ #ifdef ENABLE_SD2 return SD2::AreaTrigger(pPlayer, atEntry); #else return false; #endif } bool ScriptMgr::OnProcessEvent(uint32 eventId, Object* pSource, Object* pTarget, bool isStart) { #ifdef ENABLE_SD2 return SD2::ProcessEvent(eventId, pSource, pTarget, isStart); #else return false; #endif } bool ScriptMgr::OnEffectDummy(Unit* pCaster, uint32 spellId, SpellEffectIndex effIndex, Creature* pTarget, ObjectGuid originalCasterGuid) { // Used by Eluna #ifdef ENABLE_ELUNA if (sEluna->OnDummyEffect(pCaster, spellId, effIndex, pTarget)) return true; #endif /* ENABLE_ELUNA */ #ifdef ENABLE_SD2 return SD2::EffectDummyCreature(pCaster, spellId, effIndex, pTarget, originalCasterGuid); #else return false; #endif } bool ScriptMgr::OnEffectDummy(Unit* pCaster, uint32 spellId, SpellEffectIndex effIndex, GameObject* pTarget, ObjectGuid originalCasterGuid) { // Used by Eluna #ifdef ENABLE_ELUNA if (sEluna->OnDummyEffect(pCaster, spellId, effIndex, pTarget)) return true; #endif /* ENABLE_ELUNA */ #ifdef ENABLE_SD2 return SD2::EffectDummyGameObject(pCaster, spellId, effIndex, pTarget, originalCasterGuid); #else return false; #endif } bool ScriptMgr::OnEffectDummy(Unit* pCaster, uint32 spellId, SpellEffectIndex effIndex, Item* pTarget, ObjectGuid originalCasterGuid) { // Used by Eluna #ifdef ENABLE_ELUNA if (sEluna->OnDummyEffect(pCaster, spellId, effIndex, pTarget)) return true; #endif /* ENABLE_ELUNA */ #ifdef ENABLE_SD2 return SD2::EffectDummyItem(pCaster, spellId, effIndex, pTarget, originalCasterGuid); #else return false; #endif } bool ScriptMgr::OnEffectScriptEffect(Unit* pCaster, uint32 spellId, SpellEffectIndex effIndex, Creature* pTarget, ObjectGuid originalCasterGuid) { #ifdef ENABLE_SD2 return SD2::EffectScriptEffectCreature(pCaster, spellId, effIndex, pTarget, originalCasterGuid); #else return false; #endif } bool ScriptMgr::OnAuraDummy(Aura const* pAura, bool apply) { #ifdef ENABLE_SD2 return SD2::AuraDummy(pAura, apply); #else return false; #endif } ScriptLoadResult ScriptMgr::LoadScriptLibrary(const char* libName) { #ifdef ENABLE_SD2 if (std::strcmp(libName, MANGOS_SCRIPT_NAME) == 0) { SD2::FreeScriptLibrary(); SD2::InitScriptLibrary(); return SCRIPT_LOAD_OK; } #endif return SCRIPT_LOAD_ERR_NOT_FOUND; } void ScriptMgr::UnloadScriptLibrary() { #ifdef ENABLE_SD2 SD2::FreeScriptLibrary(); #else return; #endif } 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; 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) { if (spell->Effect[j] == SPELL_EFFECT_SEND_EVENT) { if (spell->EffectMiscValue[j]) { eventIds.insert(spell->EffectMiscValue[j]); } } } } } } // 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->IsBattleGround()) { 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); } uint32 GetEventIdScriptId(uint32 eventId) { return sScriptMgr.GetEventIdScriptId(eventId); } uint32 GetScriptId(const char* name) { return sScriptMgr.GetScriptId(name); } char const* GetScriptName(uint32 id) { return sScriptMgr.GetScriptName(id); } uint32 GetScriptIdsCount() { return sScriptMgr.GetScriptIdsCount(); } void SetExternalWaypointTable(char const* tableName) { sWaypointMgr.SetExternalWPTable(tableName); } bool AddWaypointFromExternal(uint32 entry, int32 pathId, uint32 pointId, float x, float y, float z, float o, uint32 waittime) { return sWaypointMgr.AddExternalNode(entry, pathId, pointId, x, y, z, o, waittime); }