diff --git a/src/game/Object/Object.cpp b/src/game/Object/Object.cpp index 4d8a91738..3c96cc206 100644 --- a/src/game/Object/Object.cpp +++ b/src/game/Object/Object.cpp @@ -45,6 +45,7 @@ #include "CreatureLinkingMgr.h" #include "Chat.h" #include "GameTime.h" + #ifdef ENABLE_ELUNA #include "LuaEngine.h" #include "ElunaEventMgr.h" @@ -206,6 +207,7 @@ void Object::BuildCreateUpdateBlockForPlayer(UpdateData* data, Player* target) c buf << uint8(m_objectTypeId); BuildMovementUpdate(&buf, updateFlags); + UpdateMask updateMask; updateMask.SetCount(m_valuesCount); _SetCreateBits(&updateMask, target); @@ -458,6 +460,11 @@ void Object::BuildValuesUpdate(uint8 updatetype, ByteBuffer* data, UpdateMask* u if (GetTypeId() == TYPEID_UNIT) { + if (!target->canSeeSpellClickOn((Creature*)this)) + { + appendValue &= ~UNIT_NPC_FLAG_SPELLCLICK; + } + if (appendValue & UNIT_NPC_FLAG_TRAINER) { if (!((Creature*)this)->IsTrainerOf(target, false)) @@ -524,8 +531,10 @@ void Object::BuildValuesUpdate(uint8 updatetype, ByteBuffer* data, UpdateMask* u /* Initiate pointer to creature so we can check loot */ if (Creature* my_creature = (Creature*)this) + { /* If the creature is NOT fully looted */ if (!my_creature->loot.isLooted()) + { /* If the lootable flag is NOT set */ if (!(send_value & UNIT_DYNFLAG_LOOTABLE)) { @@ -534,13 +543,16 @@ void Object::BuildValuesUpdate(uint8 updatetype, ByteBuffer* data, UpdateMask* u /* Update it in the packet */ send_value = send_value | UNIT_DYNFLAG_LOOTABLE; } - + } + } /* If we're not allowed to loot the target, destroy the lootable flag */ if (!target->isAllowedToLoot((Creature*)this)) + { if (send_value & UNIT_DYNFLAG_LOOTABLE) { send_value = send_value & ~UNIT_DYNFLAG_LOOTABLE; } + } /* If we are allowed to loot it and mob is tapped by us, destroy the tapped flag */ bool is_tapped = target->IsTappedByMeOrMyGroup((Creature*)this); @@ -553,7 +565,7 @@ void Object::BuildValuesUpdate(uint8 updatetype, ByteBuffer* data, UpdateMask* u *data << send_value; } - else + else // Unhandled index, just send { // send in current format (float as float, uint32 as uint32) *data << m_uint32Values[index]; @@ -595,7 +607,8 @@ void Object::BuildValuesUpdate(uint8 updatetype, ByteBuffer* data, UpdateMask* u } else { - *data << uint32(0); // disable quest object + // disable quest object + *data << uint32(0); } } else @@ -943,7 +956,6 @@ bool Object::PrintEntryError(char const* descr) const return false; } - void Object::BuildUpdateDataForPlayer(Player* pl, UpdateDataMapType& update_players) { UpdateDataMapType::iterator iter = update_players.find(pl); @@ -1431,6 +1443,10 @@ void WorldObject::UpdateAllowedPositionZ(float x, float y, float& z, Map* atMap { z = max_z; } + else if (z < ground_z) + { + z = ground_z; + } } } else @@ -1456,6 +1472,10 @@ void WorldObject::UpdateAllowedPositionZ(float x, float y, float& z, Map* atMap { z = max_z; } + else if (z < ground_z) + { + z = ground_z; + } } } else @@ -1716,7 +1736,7 @@ Creature* WorldObject::SummonCreature(uint32 id, float x, float y, float z, floa pCreature->SetRespawnCoord(pos); - //Set run/walk mode + // Set run or walk before any other movement starts pCreature->SetWalk(!setRun); // Active state set before added to map diff --git a/src/game/Object/Object.h b/src/game/Object/Object.h index 1346ef7d7..ca0c9489e 100644 --- a/src/game/Object/Object.h +++ b/src/game/Object/Object.h @@ -38,6 +38,7 @@ #define CONTACT_DISTANCE 0.5f #define INTERACTION_DISTANCE 5.0f #define ATTACK_DISTANCE 5.0f +#define TRADE_DISTANCE 11.11f // max distance for trading #define MAX_VISIBILITY_DISTANCE 333.0f // max distance for visible object show, limited in 333 yards #define DEFAULT_VISIBILITY_DISTANCE 90.0f // default visible distance, 90 yards on continents #define DEFAULT_VISIBILITY_INSTANCE 120.0f // default visible distance in instances, 120 yards diff --git a/src/game/Object/ObjectMgr.cpp b/src/game/Object/ObjectMgr.cpp index 489cd73cb..950f33cdb 100644 --- a/src/game/Object/ObjectMgr.cpp +++ b/src/game/Object/ObjectMgr.cpp @@ -123,6 +123,34 @@ LanguageDesc const* GetLanguageDescByID(uint32 lang) return NULL; } +bool SpellClickInfo::IsFitToRequirements(Player const* player, Creature const* clickedCreature) const +{ + if (conditionId) + { + return sObjectMgr.IsPlayerMeetToCondition(conditionId, player, player->GetMap(), clickedCreature, CONDITION_FROM_SPELLCLICK); + } + + if (questStart) + { + // not in expected required quest state + if (!player || ((!questStartCanActive || !player->IsActiveQuest(questStart)) && !player->GetQuestRewardStatus(questStart))) + { + return false; + } + } + + if (questEnd) + { + // not in expected forbidden quest state + if (!player || player->GetQuestRewardStatus(questEnd)) + { + return false; + } + } + + return true; +} + template T IdGenerator::Generate() { diff --git a/src/game/Object/ObjectMgr.h b/src/game/Object/ObjectMgr.h index 6ef5da840..bae09a891 100644 --- a/src/game/Object/ObjectMgr.h +++ b/src/game/Object/ObjectMgr.h @@ -62,6 +62,22 @@ struct GameTele typedef UNORDERED_MAP GameTeleMap; +struct SpellClickInfo +{ + uint32 spellId; + uint32 questStart; // quest start (quest must be active or rewarded for spell apply) + uint32 questEnd; // quest end (quest don't must be rewarded for spell apply) + bool questStartCanActive; // if true then quest start can be active (not only rewarded) + uint8 castFlags; + uint16 conditionId; // intends to replace questStart, questEnd, questStartCanActive + + // helpers + bool IsFitToRequirements(Player const* player, Creature const* clickedCreature) const; +}; + +typedef std::multimap SpellClickInfoMap; +typedef std::pair SpellClickInfoMapBounds; + struct AreaTrigger { uint8 requiredLevel; @@ -358,7 +374,7 @@ enum ConditionSource // From where was th CONDITION_FROM_HARDCODED = 5, // Used to check a hardcoded event - not actually a condition CONDITION_FROM_VENDOR = 6, // Used to check a condition from a vendor CONDITION_FROM_SPELL_AREA = 7, // Used to check a condition from spell_area table - CONDITION_FROM_RESERVED_1 = 8, // reserved for 3.x and later + CONDITION_FROM_SPELLCLICK = 8, // Used to check a condition from npc_spellclick_spells table CONDITION_FROM_DBSCRIPTS = 9, // Used to check a condition from DB Scripts Engine CONDITION_AREA_TRIGGER = 10, // Used to check a condition from CMSG_AREATRIGGER }; @@ -1146,6 +1162,11 @@ class ObjectMgr int GetOrNewIndexForLocale(LocaleConstant loc); + SpellClickInfoMapBounds GetSpellClickInfoMapBounds(uint32 creature_id) const + { + return mSpellClickInfoMap.equal_range(creature_id); + } + ItemRequiredTargetMapBounds GetItemRequiredTargetMapBounds(uint32 uiItemEntry) const { return m_ItemRequiredTarget.equal_range(uiItemEntry); @@ -1264,6 +1285,8 @@ class ObjectMgr GameTeleMap m_GameTeleMap; + SpellClickInfoMap mSpellClickInfoMap; + ItemRequiredTargetMap m_ItemRequiredTarget; typedef std::vector LocalForIndex; diff --git a/src/game/Object/Player.cpp b/src/game/Object/Player.cpp index c955852da..88f9ce89f 100644 --- a/src/game/Object/Player.cpp +++ b/src/game/Object/Player.cpp @@ -417,13 +417,15 @@ void TradeData::SetAccepted(bool state, bool crosssend /*= false*/) if (!state) { + TradeStatusInfo info; + info.Status = TRADE_STATUS_BACK_TO_TRADE; if (crosssend) { - m_trader->GetSession()->SendTradeStatus(TRADE_STATUS_BACK_TO_TRADE); + m_trader->GetSession()->SendTradeStatus(info); } else { - m_player->GetSession()->SendTradeStatus(TRADE_STATUS_BACK_TO_TRADE); + m_player->GetSession()->SendTradeStatus(info); } } } @@ -676,11 +678,12 @@ Player::~Player() // clean up player-instance binds, may unload some instance saves for (uint8 i = 0; i < MAX_DIFFICULTY; ++i) + { for (BoundInstancesMap::iterator itr = m_boundInstances[i].begin(); itr != m_boundInstances[i].end(); ++itr) { itr->second.state->RemovePlayer(this); } - + } delete m_declinedname; } @@ -1099,6 +1102,7 @@ int32 Player::getMaxTimer(MirrorTimerType timer) default: return 0; } + return 0; } void Player::UpdateMirrorTimers() @@ -1390,7 +1394,7 @@ void Player::Update(uint32 update_diff, uint32 p_time) RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_ENTER_PVP_COMBAT); } } - } + }// Speed collect rest bonus (section/in hour) if (HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_RESTING)) { @@ -6956,6 +6960,15 @@ void Player::SendCinematicStart(uint32 CinematicSequenceId) SendDirectMessage(&data); } +#if defined (WOTLK) || defined (CATA) || defined (MISTS) +void Player::SendMovieStart(uint32 MovieId) +{ + WorldPacket data(SMSG_TRIGGER_MOVIE, 4); + data << uint32(MovieId); + SendDirectMessage(&data); +} +#endif + void Player::CheckAreaExploreAndOutdoor() { if (!IsAlive()) @@ -24148,6 +24161,23 @@ void Player::ResummonPetTemporaryUnSummonedIfAny() m_temporaryUnsummonedPetNumber = 0; } +bool Player::canSeeSpellClickOn(Creature const* c) const +{ + if (!c->HasFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_SPELLCLICK)) + { + return false; + } + + SpellClickInfoMapBounds clickPair = sObjectMgr.GetSpellClickInfoMapBounds(c->GetEntry()); + for (SpellClickInfoMap::const_iterator itr = clickPair.first; itr != clickPair.second; ++itr) + if (itr->second.IsFitToRequirements(this, c)) + { + return true; + } + + return false; +} + void Player::_SaveBGData() { // nothing save diff --git a/src/game/Object/Player.h b/src/game/Object/Player.h index e954cf6d5..d4f92cef1 100644 --- a/src/game/Object/Player.h +++ b/src/game/Object/Player.h @@ -429,6 +429,7 @@ enum PlayerFlags PLAYER_FLAGS_SANCTUARY = 0x00010000, // player entered sanctuary PLAYER_FLAGS_TAXI_BENCHMARK = 0x00020000, // taxi benchmark mode (on/off) (2.0.1) PLAYER_FLAGS_PVP_TIMER = 0x00040000, // 3.0.2, pvp timer active (after you disable pvp manually) + PLAYER_FLAGS_XP_USER_DISABLED = 0x02000000, }; // used for PLAYER__FIELD_KNOWN_TITLES field (uint64), (1< const& nodes, Creature* npc = NULL, uint32 spellid = 0); bool ActivateTaxiPathTo(uint32 taxi_path_id, uint32 spellid = 0); // mount_id can be used in scripting calls @@ -1138,12 +1155,12 @@ class Player : public Unit void SetRestBonus(float rest_bonus_new); /** - * \brief: compute rest bonus - * \param: time_t timePassed > time from last check - * \param: bool offline > is the player was offline? - * \param: bool inRestPlace > if it was offline, is the player was in city/tavern/inn? - * \returns: float - **/ + * \brief: compute rest bonus + * \param: time_t timePassed > time from last check + * \param: bool offline > is the player was offline? + * \param: bool inRestPlace > if it was offline, is the player was in city/tavern/inn? + * \returns: float + **/ float ComputeRest(time_t timePassed, bool offline = false, bool inRestPlace = false); RestType GetRestType() const @@ -1275,6 +1292,7 @@ class Player : public Unit { return m_ammoDPS; } + bool CheckAmmoCompatibility(const ItemPrototype* ammo_proto) const; void QuickEquipItem(uint16 pos, Item* pItem); void VisualizeItem(uint8 slot, Item* pItem); @@ -2367,6 +2385,9 @@ class Player : public Unit bool IsPetNeedBeTemporaryUnsummoned() const { return !IsInWorld() || !IsAlive() || IsMounted() /*+in flight*/; } void SendCinematicStart(uint32 CinematicSequenceId); +#if defined(WOTLK) || defined(CATA) || defined(MISTS) + void SendMovieStart(uint32 MovieId); +#endif /*********************************************************/ /*** INSTANCE SYSTEM ***/ @@ -2429,6 +2450,8 @@ class Player : public Unit bool HasTitle(CharTitlesEntry const* title) const { return HasTitle(title->bit_index); } void SetTitle(CharTitlesEntry const* title, bool lost = false); + bool canSeeSpellClickOn(Creature const* creature) const; + #ifdef ENABLE_PLAYERBOTS //EquipmentSets& GetEquipmentSets() { return m_EquipmentSets; } void SetPlayerbotAI(PlayerbotAI* ai) { assert(!m_playerbotAI && !m_playerbotMgr); m_playerbotAI = ai; } diff --git a/src/game/Server/DBCStores.cpp b/src/game/Server/DBCStores.cpp index 115693bad..4fd449b77 100644 --- a/src/game/Server/DBCStores.cpp +++ b/src/game/Server/DBCStores.cpp @@ -113,6 +113,8 @@ DBCStorage sMailTemplateStore(MailTemplateEntryfmt); DBCStorage sMapStore(MapEntryfmt); +DBCStorage sMovieStore(MovieEntryfmt); + DBCStorage sQuestSortStore(QuestSortEntryfmt); DBCStorage sRandomPropertiesPointsStore(RandomPropertiesPointsfmt); diff --git a/src/game/Server/DBCStores.h b/src/game/Server/DBCStores.h index 4bd45b936..6e6ba8887 100644 --- a/src/game/Server/DBCStores.h +++ b/src/game/Server/DBCStores.h @@ -118,6 +118,7 @@ extern DBCStorage sLiquidTypeStore; extern DBCStorage sLockStore; extern DBCStorage sMailTemplateStore; extern DBCStorage sMapStore; +extern DBCStorage sMovieStore; extern DBCStorage sQuestSortStore; extern DBCStorage sRandomPropertiesPointsStore; extern DBCStorage sSkillLineStore; diff --git a/src/game/Server/DBCStructure.h b/src/game/Server/DBCStructure.h index b74e1d549..656575cbd 100644 --- a/src/game/Server/DBCStructure.h +++ b/src/game/Server/DBCStructure.h @@ -777,6 +777,13 @@ struct MapEntry } }; + +struct MovieEntry +{ + uint32 Id; // 0 m_ID + // char* filename; // 1 m_filename + // uint32 unk2; // 2 m_volume +}; /** * \struct QuestSortEntry * \brief Entry representing the type of quest within the game. diff --git a/src/game/Server/DBCfmt.h b/src/game/Server/DBCfmt.h index 5f3f21575..adb8c07d1 100644 --- a/src/game/Server/DBCfmt.h +++ b/src/game/Server/DBCfmt.h @@ -68,6 +68,7 @@ const char LiquidTypefmt[] = "niii"; const char LockEntryfmt[] = "niiiiiiiiiiiiiiiiiiiiiiiixxxxxxxx"; const char MailTemplateEntryfmt[] = "nxxxxxxxxxxxxxxxxxssssssssssssssssx"; const char MapEntryfmt[] = "nxixssssssssssssssssxxxxxxxixxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxixxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxiffiixxi"; +const char MovieEntryfmt[] = "nxx"; const char QuestSortEntryfmt[] = "nxxxxxxxxxxxxxxxxx"; const char RandomPropertiesPointsfmt[] = "niiiiiiiiiiiiiii"; const char SkillLinefmt[] = "nixssssssssssssssssxxxxxxxxxxxxxxxxxxi"; diff --git a/src/game/Server/WorldSession.h b/src/game/Server/WorldSession.h index 6e8757954..1d70d9c0b 100644 --- a/src/game/Server/WorldSession.h +++ b/src/game/Server/WorldSession.h @@ -40,6 +40,7 @@ struct ItemPrototype; struct AuctionEntry; struct AuctionHouseEntry; struct DeclinedName; +struct TradeStatusInfo; class ObjectGuid; class Creature; @@ -277,7 +278,7 @@ class WorldSession void SendBattlegGroundList(ObjectGuid guid, BattleGroundTypeId bgTypeId); - void SendTradeStatus(TradeStatus status); + void SendTradeStatus(const TradeStatusInfo& status); void SendUpdateTrade(bool trader_state = true); void SendCancelTrade(); @@ -858,5 +859,7 @@ class WorldSession int32 m_clientTimeDelay; ACE_Based::LockedQueue _recvQueue; }; + #endif + /// @} diff --git a/src/game/WorldHandlers/ScriptMgr.cpp b/src/game/WorldHandlers/ScriptMgr.cpp index ce3f295e3..d477aab59 100644 --- a/src/game/WorldHandlers/ScriptMgr.cpp +++ b/src/game/WorldHandlers/ScriptMgr.cpp @@ -28,6 +28,8 @@ #include "ProgressBar.h" #include "ObjectMgr.h" #include "WaypointManager.h" +#include "World.h" +#include #include "GridNotifiers.h" #include "GridNotifiersImpl.h" #include "Cell.h" @@ -88,24 +90,47 @@ ScriptChainMap const* ScriptMgr::GetScriptChainMap(DBScriptType type) // returns priority (0 == can not start script) uint8 GetSpellStartDBScriptPriority(SpellEntry const* spellinfo, SpellEffectIndex effIdx) { +#if defined (CATA) + SpellEffectEntry const* spellEffect = spellinfo->GetSpellEffect(effIdx); + if (!spellEffect) + { + return 0; + } +#endif +#if defined (CATA) + if (spellEffect->Effect == SPELL_EFFECT_SCRIPT_EFFECT) +#else if (spellinfo->Effect[effIdx] == SPELL_EFFECT_SCRIPT_EFFECT) +#endif { return 10; } +#if defined (CATA) + if (spellEffect->Effect == SPELL_EFFECT_DUMMY) +#else if (spellinfo->Effect[effIdx] == SPELL_EFFECT_DUMMY) +#endif { return 9; } // NonExisting triggered spells can also start DB-Spell-Scripts +#if defined (CATA) + if (spellEffect->Effect == SPELL_EFFECT_TRIGGER_SPELL && !sSpellStore.LookupEntry(spellEffect->EffectTriggerSpell)) +#else if (spellinfo->Effect[effIdx] == SPELL_EFFECT_TRIGGER_SPELL && !sSpellStore.LookupEntry(spellinfo->EffectTriggerSpell[effIdx])) +#endif { return 5; } // NonExisting trigger missile spells can also start DB-Spell-Scripts +#if defined (CATA) + if (spellEffect->Effect == SPELL_EFFECT_TRIGGER_MISSILE && !sSpellStore.LookupEntry(spellEffect->EffectTriggerSpell)) +#else if (spellinfo->Effect[effIdx] == SPELL_EFFECT_TRIGGER_MISSILE && !sSpellStore.LookupEntry(spellinfo->EffectTriggerSpell[effIdx])) +#endif { return 4; } @@ -372,15 +397,14 @@ void ScriptMgr::LoadScripts(DBScriptType type) } break; } - case SCRIPT_COMMAND_RESPAWN_GAMEOBJECT: // 9 + case SCRIPT_COMMAND_RESPAWN_GO: // 9 { - uint32 goEntry = 0; - + uint32 goEntry; if (!tmp.GetGOGuid()) { if (!tmp.buddyEntry) { - sLog.outErrorDb("Table `db_scripts [type = %d]` has no gameobject nor buddy defined in SCRIPT_COMMAND_RESPAWN_GAMEOBJECT for script id %u", type, tmp.id); + sLog.outErrorDb("Table `db_scripts [type = %d]` has no gameobject nor buddy defined in SCRIPT_COMMAND_RESPAWN_GO for script id %u", type, tmp.id); continue; } goEntry = tmp.buddyEntry; @@ -390,7 +414,7 @@ void ScriptMgr::LoadScripts(DBScriptType type) GameObjectData const* data = sObjectMgr.GetGOData(tmp.GetGOGuid()); if (!data) { - sLog.outErrorDb("Table `db_scripts [type = %d]` has invalid gameobject (GUID: %u) in SCRIPT_COMMAND_RESPAWN_GAMEOBJECT for script id %u", type, tmp.GetGOGuid(), tmp.id); + sLog.outErrorDb("Table `db_scripts [type = %d]` has invalid gameobject (GUID: %u) in SCRIPT_COMMAND_RESPAWN_GO for script id %u", type, tmp.GetGOGuid(), tmp.id); continue; } goEntry = data->id; @@ -399,7 +423,7 @@ void ScriptMgr::LoadScripts(DBScriptType type) GameObjectInfo const* info = ObjectMgr::GetGameObjectInfo(goEntry); if (!info) { - sLog.outErrorDb("Table `db_scripts [type = %d]` has gameobject with invalid entry (GUID: %u Entry: %u) in SCRIPT_COMMAND_RESPAWN_GAMEOBJECT for script id %u", type, tmp.GetGOGuid(), goEntry, tmp.id); + sLog.outErrorDb("Table `db_scripts [type = %d]` has gameobject with invalid entry (GUID: %u Entry: %u) in SCRIPT_COMMAND_RESPAWN_GO for script id %u", type, tmp.GetGOGuid(), goEntry, tmp.id); continue; } @@ -407,7 +431,7 @@ void ScriptMgr::LoadScripts(DBScriptType type) info->type == GAMEOBJECT_TYPE_FISHINGHOLE || info->type == GAMEOBJECT_TYPE_DOOR) { - sLog.outErrorDb("Table `db_scripts [type = %d]` have gameobject type (%u) unsupported by command SCRIPT_COMMAND_RESPAWN_GAMEOBJECT for script id %u", type, info->type, tmp.id); + sLog.outErrorDb("Table `db_scripts [type = %d]` have gameobject type (%u) unsupported by command SCRIPT_COMMAND_RESPAWN_GO for script id %u", type, info->type, tmp.id); continue; } break; @@ -430,7 +454,7 @@ void ScriptMgr::LoadScripts(DBScriptType type) case SCRIPT_COMMAND_OPEN_DOOR: // 11 case SCRIPT_COMMAND_CLOSE_DOOR: // 12 { - uint32 goEntry = 0; + uint32 goEntry; if (!tmp.GetGOGuid()) { if (!tmp.buddyEntry) @@ -544,9 +568,19 @@ void ScriptMgr::LoadScripts(DBScriptType type) } case SCRIPT_COMMAND_PLAY_MOVIE: // 19 { +#if defined(CLASSIC) sLog.outErrorDb("Table `db_scripts [type = %d]` use unsupported SCRIPT_COMMAND_PLAY_MOVIE for script id %u", type, tmp.id); continue; +#else + if (!sMovieStore.LookupEntry(tmp.playMovie.movieId)) + { + sLog.outErrorDb("Table `db_scripts [type = %d]` use non-existing movie_id (id: %u) in SCRIPT_COMMAND_PLAY_MOVIE for script id %u", + type, tmp.playMovie.movieId, tmp.id); + continue; + } + break; +#endif } case SCRIPT_COMMAND_MOVEMENT: // 20 { @@ -577,7 +611,7 @@ void ScriptMgr::LoadScripts(DBScriptType type) { if (tmp.morph.creatureOrModelEntry && !sCreatureDisplayInfoStore.LookupEntry(tmp.morph.creatureOrModelEntry)) { - sLog.outErrorDb("Table `db_scripts [type = %d]` has datalong2 = %u in SCRIPT_COMMAND_MORPH_TO_ENTRY_OR_MODEL for script id %u, but this model does not exist.", type, tmp.morph.creatureOrModelEntry, tmp.id); + sLog.outErrorDb("Table `db_scripts [type = %d]` has datalong = %u in SCRIPT_COMMAND_MORPH_TO_ENTRY_OR_MODEL for script id %u, but this model does not exist.", type, tmp.morph.creatureOrModelEntry, tmp.id); continue; } } @@ -585,7 +619,7 @@ void ScriptMgr::LoadScripts(DBScriptType type) { if (tmp.morph.creatureOrModelEntry && !ObjectMgr::GetCreatureTemplate(tmp.morph.creatureOrModelEntry)) { - sLog.outErrorDb("Table `db_scripts [type = %d]` has datalong2 = %u in SCRIPT_COMMAND_MORPH_TO_ENTRY_OR_MODEL for script id %u, but this creature_template does not exist.", type, tmp.morph.creatureOrModelEntry, tmp.id); + sLog.outErrorDb("Table `db_scripts [type = %d]` has datalong = %u in SCRIPT_COMMAND_MORPH_TO_ENTRY_OR_MODEL for script id %u, but this creature_template does not exist.", type, tmp.morph.creatureOrModelEntry, tmp.id); continue; } } @@ -598,7 +632,7 @@ void ScriptMgr::LoadScripts(DBScriptType type) { if (tmp.mount.creatureOrModelEntry && !sCreatureDisplayInfoStore.LookupEntry(tmp.mount.creatureOrModelEntry)) { - sLog.outErrorDb("Table `db_scripts [type = %d]` has datalong2 = %u in SCRIPT_COMMAND_MOUNT_TO_ENTRY_OR_MODEL for script id %u, but this model does not exist.", type, tmp.mount.creatureOrModelEntry, tmp.id); + sLog.outErrorDb("Table `db_scripts [type = %d]` has datalong = %u in SCRIPT_COMMAND_MOUNT_TO_ENTRY_OR_MODEL for script id %u, but this model does not exist.", type, tmp.mount.creatureOrModelEntry, tmp.id); continue; } } @@ -606,7 +640,7 @@ void ScriptMgr::LoadScripts(DBScriptType type) { if (tmp.mount.creatureOrModelEntry && !ObjectMgr::GetCreatureTemplate(tmp.mount.creatureOrModelEntry)) { - sLog.outErrorDb("Table `db_scripts [type = %d]` has datalong2 = %u in SCRIPT_COMMAND_MOUNT_TO_ENTRY_OR_MODEL for script id %u, but this creature_template does not exist.", type, tmp.mount.creatureOrModelEntry, tmp.id); + sLog.outErrorDb("Table `db_scripts [type = %d]` has datalong = %u in SCRIPT_COMMAND_MOUNT_TO_ENTRY_OR_MODEL for script id %u, but this creature_template does not exist.", type, tmp.mount.creatureOrModelEntry, tmp.id); continue; } } @@ -659,7 +693,17 @@ void ScriptMgr::LoadScripts(DBScriptType type) if (SpellEntry const* spell = sSpellStore.LookupEntry(i)) for (int j = 0; j < MAX_EFFECT_INDEX; ++j) { +#if defined (CATA) + SpellEffectEntry const* spellEffect = spell->GetSpellEffect(SpellEffectIndex(j)); + if (!spellEffect) + { + continue; + } + + if (spellEffect->Effect == SPELL_EFFECT_SEND_TAXI && spellEffect->EffectMiscValue == tmp.sendTaxiPath.taxiPathId) +#else if (spell->Effect[j] == SPELL_EFFECT_SEND_TAXI && spell->EffectMiscValue[j] == int32(tmp.sendTaxiPath.taxiPathId)) +#endif { taxiSpell = i; break; @@ -763,6 +807,49 @@ void ScriptMgr::LoadScripts(DBScriptType type) break; } + case SCRIPT_COMMAND_RESPAWN: // 41 + break; + case SCRIPT_COMMAND_SET_EQUIPMENT_SLOTS: // 42 + { + if (tmp.textId[0] < 0 || tmp.textId[1] < 0 || tmp.textId[2] < 0) + { + sLog.outErrorDb("Table `db_scripts [type = %d]` has invalid equipment slot (dataint = %u, dataint2 = %u dataint3 = %u) in SCRIPT_COMMAND_SET_EQUIPMENT_SLOTS for script id %u", type, tmp.textId[0], tmp.textId[1], tmp.textId[2], tmp.id); + continue; + } + break; + } + case SCRIPT_COMMAND_RESET_GO: // 43 + break; + case SCRIPT_COMMAND_UPDATE_TEMPLATE: // 44 + { +#if defined(CLASSIC) || defined(TBC) || defined(WOTLK) + if (tmp.updateTemplate.entry && !ObjectMgr::GetCreatureTemplate(tmp.updateTemplate.entry)) +#else + if (!sCreatureStorage.LookupEntry(tmp.updateTemplate.entry)) +#endif + { + sLog.outErrorDb("Table `db_scripts [type = %d]` has datalong = %u in SCRIPT_COMMAND_UPDATE_TEMPLATE for script id %u, but this creature_template does not exist.", type, tmp.updateTemplate.entry, tmp.id); + continue; + } +#if defined(CLASSIC) || defined(TBC) || defined(WOTLK) + if (tmp.updateTemplate.faction > 1) +#else + if (tmp.updateTemplate.faction != 0 && tmp.updateTemplate.faction != 1) +#endif + { + sLog.outErrorDb("Table `db_scripts [type = %d]` uses invalid faction team (datalong2 = %u, must be 0 or 1) in SCRIPT_COMMAND_UPDATE_TEMPLATE for script id %u", type, tmp.updateTemplate.faction, tmp.id); + continue; + } + break; + } + case SCRIPT_COMMAND_XP_USER: // 53 + { + break; + } + case SCRIPT_COMMAND_SET_FLY: // 59 + { + break; + } default: { sLog.outErrorDb("Table `db_scripts [type = %d]` uses unknown command %u, skipping.", type, tmp.command); @@ -944,6 +1031,9 @@ bool ScriptAction::GetScriptCommandObject(const ObjectGuid guid, bool includeIte switch (guid.GetHigh()) { case HIGHGUID_UNIT: +#if defined(WOTLK) || defined(CATA) || defined(MISTS) + case HIGHGUID_VEHICLE: +#endif resultObject = m_map->GetCreature(guid); break; case HIGHGUID_PET: @@ -1045,16 +1135,25 @@ bool ScriptAction::GetScriptProcessTargets(WorldObject* pOrigSource, WorldObject { 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) + if (m_script->data_flags & SCRIPT_FLAG_BUDDY_IS_DESPAWNED) { - Cell::VisitWorldObjects(pSearcher, searcher, m_script->searchRadiusOrGuid); + MaNGOS::AllCreaturesOfEntryInRangeCheck u_check(pSearcher, m_script->buddyEntry, m_script->searchRadiusOrGuid); + MaNGOS::CreatureLastSearcher searcher(pCreatureBuddy, u_check); + Cell::VisitGridObjects(pSearcher, searcher, m_script->searchRadiusOrGuid); } - else // Normal Creature + else { - Cell::VisitGridObjects(pSearcher, searcher, m_script->searchRadiusOrGuid); + 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; @@ -1427,11 +1526,9 @@ bool ScriptAction::HandleScriptStep() break; } - case SCRIPT_COMMAND_RESPAWN_GAMEOBJECT: // 9 + case SCRIPT_COMMAND_RESPAWN_GO: // 9 { - GameObject* pGo = NULL; - uint32 time_to_despawn = m_script->respawnGo.despawnDelay < 5 ? 5 : m_script->respawnGo.despawnDelay; - + GameObject* pGo; if (m_script->respawnGo.goGuid) { GameObjectData const* goData = sObjectMgr.GetGOData(m_script->respawnGo.goGuid); @@ -1471,6 +1568,8 @@ bool ScriptAction::HandleScriptStep() break; // gameobject already spawned } + uint32 time_to_despawn = m_script->respawnGo.despawnDelay < 5 ? 5 : m_script->respawnGo.despawnDelay; + pGo->SetLootState(GO_READY); pGo->SetRespawnTime(time_to_despawn); // despawn object in ? seconds pGo->Refresh(); @@ -1489,7 +1588,7 @@ bool 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 ? TEMPSPAWN_TIMED_OOC_OR_DEAD_DESPAWN : TEMPSPAWN_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 ? TEMPSPAWN_TIMED_OOC_OR_DEAD_DESPAWN : TEMPSPAWN_DEAD_DESPAWN, m_script->summonCreature.despawnDelay, (m_script->data_flags & SCRIPT_FLAG_COMMAND_ADDITIONAL) ? true : false, m_script->textId[0] != 0); if (!pCreature) { sLog.outErrorDb(" DB-SCRIPTS: Process table `db_scripts [type = %d]` id %u, command %u failed for creature (entry: %u).", m_type, m_script->id, m_script->command, m_script->summonCreature.creatureEntry); @@ -1686,6 +1785,15 @@ bool ScriptAction::HandleScriptStep() } case SCRIPT_COMMAND_PLAY_MOVIE: // 19 { +#if defined(WOTLK) || defined (CATA) || defined (MISTS) + Player* pPlayer = GetPlayerTargetOrSourceAndLog(pSource, pTarget); + if (!pPlayer) + { + break; + } + + pPlayer->SendMovieStart(m_script->playMovie.movieId); +#endif break; // must be skipped at loading } case SCRIPT_COMMAND_MOVEMENT: // 20 @@ -1753,18 +1861,14 @@ bool ScriptAction::HandleScriptStep() } case SCRIPT_COMMAND_MORPH_TO_ENTRY_OR_MODEL: // 23 { - if (LogIfNotUnit(pSource)) - { - break; - } - if (pSource->GetTypeId() == TYPEID_PLAYER && m_script->morph.creatureOrModelEntry) // only demorph to players + if (LogIfNotCreature(pSource)) { break; } if (!m_script->morph.creatureOrModelEntry) { - ((Unit*)pSource)->DeMorph(); + ((Creature*)pSource)->DeMorph(); } else if (m_script->data_flags & SCRIPT_FLAG_COMMAND_ADDITIONAL) { @@ -2061,7 +2165,20 @@ bool ScriptAction::HandleScriptStep() break; } +#if defined(CLASSIC) || defined(TBC) || defined(WOTLK) ((Creature*)pSource)->AI()->SendAIEventAround(AIEventType(m_script->sendAIEvent.eventType), (Unit*)pTarget, 0, float(m_script->sendAIEvent.radius)); +#else + // if radius is provided send AI event around + if (m_script->sendAIEvent.radius) + { + ((Creature*)pSource)->AI()->SendAIEventAround(AIEventType(m_script->sendAIEvent.eventType), (Unit*)pTarget, 0, float(m_script->sendAIEvent.radius)); + } + // else if no radius and target is creature send AI event to target + else if (pTarget->GetTypeId() == TYPEID_UNIT) + { + ((Creature*)pSource)->AI()->SendAIEvent(AIEventType(m_script->sendAIEvent.eventType), NULL, (Creature*)pTarget); + } +#endif break; } case SCRIPT_COMMAND_TURN_TO: // 36 @@ -2175,7 +2292,140 @@ bool ScriptAction::HandleScriptStep() break; } + case SCRIPT_COMMAND_RESPAWN: // 41 + { + if (LogIfNotCreature(pTarget)) + { + break; + } + ((Creature*)pTarget)->Respawn(); + break; + } + case SCRIPT_COMMAND_SET_EQUIPMENT_SLOTS: // 42 + { + if (LogIfNotCreature(pSource)) + { + break; + } + Creature* pCSource = static_cast(pSource); + + // reset default + if (m_script->setEquipment.resetDefault) + { + pCSource->LoadEquipment(pCSource->GetCreatureInfo()->EquipmentTemplateId, true); + break; + } + + // main hand + if (m_script->textId[0] >= 0) + { + pCSource->SetVirtualItem(VIRTUAL_ITEM_SLOT_0, m_script->textId[0]); + } + + // off hand + if (m_script->textId[1] >= 0) + { + pCSource->SetVirtualItem(VIRTUAL_ITEM_SLOT_1, m_script->textId[1]); + } + + // ranged + if (m_script->textId[2] >= 0) + { + pCSource->SetVirtualItem(VIRTUAL_ITEM_SLOT_2, m_script->textId[2]); + } + break; + } + case SCRIPT_COMMAND_RESET_GO: // 43 + { + if (LogIfNotGameObject(pTarget)) + { + break; + } + + GameObject* pGoTarget = static_cast(pTarget); + + switch (pGoTarget->GetGoType()) + { + case GAMEOBJECT_TYPE_DOOR: + case GAMEOBJECT_TYPE_BUTTON: + pGoTarget->ResetDoorOrButton(); + break; + default: + sLog.outErrorDb(" DB-SCRIPTS: Process table `db_scripts [type = %d]` id %u, command %u failed for gameobject(buddyEntry: %u). Gameobject is not a door or button", m_type, m_script->id, m_script->command, m_script->buddyEntry); + break; + } + break; + } + case SCRIPT_COMMAND_UPDATE_TEMPLATE: // 44 + { + if (LogIfNotCreature(pSource)) + { + break; + } + + Creature* pCre = static_cast(pSource); + + if (pCre->GetEntry() != m_script->updateTemplate.entry) + { + pCre->UpdateEntry(m_script->updateTemplate.entry, m_script->updateTemplate.faction ? HORDE : ALLIANCE); + } + else + { + sLog.outErrorDb(" DB-SCRIPTS: Process table `db_scripts [type = %d]` id %u, command %u: source creature already has the specified creature entry %u", m_type, m_script->id, m_script->command, m_script->updateTemplate.entry); + } + break; + } + case SCRIPT_COMMAND_XP_USER: // 53 + { + Player* pPlayer = GetPlayerTargetOrSourceAndLog(pSource, pTarget); + if (!pPlayer) + { + break; + } + + if (m_script->xpDisabled.flags) + { + pPlayer->SetFlag(PLAYER_FLAGS, PLAYER_FLAGS_XP_USER_DISABLED); + } + else + { + pPlayer->RemoveFlag(PLAYER_FLAGS, PLAYER_FLAGS_XP_USER_DISABLED); + } + break; + } + + case SCRIPT_COMMAND_SET_FLY: // 59 + { + if (LogIfNotCreature(pSource)) + { + break; + } + + // enable / disable the fly anim flag + if (m_script->data_flags & SCRIPT_FLAG_COMMAND_ADDITIONAL) + { + if (m_script->fly.enable) + { +#if defined(CLASSIC) || defined(TBC) || defined(WOTLK) + pSource->SetByteFlag(UNIT_FIELD_BYTES_1, 3, UNIT_BYTE1_FLAG_ALWAYS_STAND); +#else + pSource->SetByteFlag(UNIT_FIELD_BYTES_1, 3, UNIT_BYTE1_FLAG_FLY_ANIM); +#endif + } + else + { +#if defined(CLASSIC) || defined(TBC) || defined(WOTLK) + pSource->RemoveByteFlag(UNIT_FIELD_BYTES_1, 3, UNIT_BYTE1_FLAG_ALWAYS_STAND); +#else + pSource->RemoveByteFlag(UNIT_FIELD_BYTES_1, 3, UNIT_BYTE1_FLAG_FLY_ANIM); +#endif + } + } + + ((Creature*)pSource)->SetLevitate((m_script->fly.enable == 0) ? false : true); + break; + } default: sLog.outErrorDb(" DB-SCRIPTS: Process table `db_scripts [type = %d]` id %u, command %u unknown command used.", m_type, m_script->id, m_script->command); break; @@ -2758,6 +3008,15 @@ bool ScriptMgr::OnAreaTrigger(Player* pPlayer, AreaTriggerEntry const* atEntry) #endif } +bool ScriptMgr::OnNpcSpellClick(Player* pPlayer, Creature* pClickedCreature, uint32 spellId) +{ +#ifdef ENABLE_SD3 + return SD3::NpcSpellClick(pPlayer, pClickedCreature, spellId); +#else + return false; +#endif +} + bool ScriptMgr::OnProcessEvent(uint32 eventId, Object* pSource, Object* pTarget, bool isStart) { #ifdef ENABLE_SD3 @@ -2880,6 +3139,14 @@ void ScriptMgr::CollectPossibleEventIds(std::set& eventIds) eventIds.insert(itr->capturePoint.winEventID1); eventIds.insert(itr->capturePoint.winEventID2); break; +#if defined(WOTLK) || defined (CATA) || defined (MISTS) + 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; +#endif default: break; } @@ -2893,6 +3160,21 @@ void ScriptMgr::CollectPossibleEventIds(std::set& eventIds) { for (int j = 0; j < MAX_EFFECT_INDEX; ++j) { +#if defined (CATA) + SpellEffectEntry const* spellEffect = spell->GetSpellEffect(SpellEffectIndex(j)); + if (!spellEffect) + { + continue; + } + + if (spellEffect->Effect == SPELL_EFFECT_SEND_EVENT) + { + if (spellEffect->EffectMiscValue) + { + eventIds.insert(spellEffect->EffectMiscValue); + } + } +#else if (spell->Effect[j] == SPELL_EFFECT_SEND_EVENT) { if (spell->EffectMiscValue[j]) @@ -2900,10 +3182,11 @@ void ScriptMgr::CollectPossibleEventIds(std::set& eventIds) eventIds.insert(spell->EffectMiscValue[j]); } } +#endif } } } -#if defined(TBC) || defined (WOTLK) +#if defined(TBC) || defined (WOTLK) || defined (CATA) // Load all possible event entries from taxi path nodes for (size_t path_idx = 0; path_idx < sTaxiPathNodesByPath.size(); ++path_idx) { @@ -2989,6 +3272,7 @@ bool StartEvents_Event(Map* map, uint32 id, Object* source, Object* target, bool return map->ScriptsStart(DBS_ON_EVENT, id, source, target, execParam); } +// Wrappers uint32 GetScriptId(const char* name) { return sScriptMgr.GetScriptId(name); diff --git a/src/game/WorldHandlers/ScriptMgr.h b/src/game/WorldHandlers/ScriptMgr.h index c935089b4..3a903f05b 100644 --- a/src/game/WorldHandlers/ScriptMgr.h +++ b/src/game/WorldHandlers/ScriptMgr.h @@ -61,7 +61,8 @@ enum DBScriptType DBS_ON_GO_USE = 6, DBS_ON_GOT_USE = 7, DBS_ON_EVENT = 8, - DBS_END = 9, + DBS_ON_CREATURE_SPELL = 9, + DBS_END = 10, }; #define DBS_START DBS_ON_QUEST_START @@ -104,7 +105,7 @@ enum DBScriptCommand // resSource, resTar SCRIPT_COMMAND_TELEPORT_TO = 6, // source or target with Player, datalong2 = map_id, x/y/z SCRIPT_COMMAND_QUEST_EXPLORED = 7, // one from source or target must be Player, another GO/Creature, datalong=quest_id, datalong2=distance or 0 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_RESPAWN_GO = 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 // dataint = (bool) setRun; 0 = off (default), 1 = on @@ -148,7 +149,7 @@ enum DBScriptCommand // resSource, resTar // dataint=diff to change a waittime of current Waypoint Movement SCRIPT_COMMAND_PAUSE_WAYPOINTS = 32, // resSource = Creature // datalong = 0: unpause waypoint 1: pause waypoint - SCRIPT_COMMAND_JOIN_LFG = 33, // datalong = zoneId; // Currently only implemented in Zero + SCRIPT_COMMAND_JOIN_LFG = 33, // datalong = zoneId; SCRIPT_COMMAND_TERMINATE_COND = 34, // datalong = condition_id, datalong2 = if != 0 then quest_id of quest that will be failed for player's group if the script is terminated // data_flags & SCRIPT_FLAG_COMMAND_ADDITIONAL terminate when condition is false ELSE terminate when condition is true SCRIPT_COMMAND_SEND_AI_EVENT_AROUND = 35, // resSource = Creature, resTarget = Unit @@ -158,7 +159,7 @@ enum DBScriptCommand // resSource, resTar SCRIPT_COMMAND_MOVE_DYNAMIC = 37, // resSource = Creature, resTarget Worldobject. // datalong = 0: Move resSource towards resTarget // datalong != 0: Move resSource to a random point between datalong2..datalong around resTarget. - // orientation != 0: Obtain a random point around resTarget in direction of orientation + // orientation != 0: Obtain a random point around resTarget in direction of orientation // data_flags & SCRIPT_FLAG_COMMAND_ADDITIONAL Obtain a random point around resTarget in direction of resTarget->GetOrientation + orientation // for resTarget == resSource and orientation == 0 this will mean resSource moving forward SCRIPT_COMMAND_SEND_MAIL = 38, // resSource WorldObject, can be NULL, resTarget Player @@ -168,6 +169,18 @@ enum DBScriptCommand // resSource, resTar SCRIPT_COMMAND_CHANGE_ENTRY = 39, // resSource = Creature, datalong=creature entry // dataint1 = entry SCRIPT_COMMAND_DESPAWN_GO = 40, // resTarget = GameObject + SCRIPT_COMMAND_RESPAWN = 41, // resSource = Creature. Requires SCRIPT_FLAG_BUDDY_IS_DESPAWNED to find dead or despawned targets + SCRIPT_COMMAND_SET_EQUIPMENT_SLOTS = 42, // resSource = Creature, datalong = reset default 0(false) | 1(true) + // dataint = main hand slot, dataint2 = offhand slot, dataint3 = ranged slot + SCRIPT_COMMAND_RESET_GO = 43, // resTarget = GameObject + SCRIPT_COMMAND_UPDATE_TEMPLATE = 44, // resSource = Creature + // datalong = new Creature entry + // datalong2 = Alliance(0) Horde(1), other values throw error + SCRIPT_COMMAND_XP_USER = 53, // source or target with Player, datalong = bool (0=off, 1=on) + SCRIPT_COMMAND_SET_FLY = 59, // resSource = Creature + // datalong = bool 0=off, 1=on + // data_flags & SCRIPT_FLAG_COMMAND_ADDITIONAL set/unset byte flag UNIT_BYTE1_FLAG_FLY_ANIM + // dataint1: Delay (>= 0) in Seconds }; #define MAX_TEXT_ID 4 // used for SCRIPT_COMMAND_TALK, SCRIPT_COMMAND_EMOTE, SCRIPT_COMMAND_CAST_SPELL, SCRIPT_COMMAND_TERMINATE_SCRIPT @@ -181,8 +194,9 @@ enum ScriptInfoDataFlags SCRIPT_FLAG_COMMAND_ADDITIONAL = 0x08, // command dependend SCRIPT_FLAG_BUDDY_BY_GUID = 0x10, // take the buddy by guid SCRIPT_FLAG_BUDDY_IS_PET = 0x20, // buddy is a pet + SCRIPT_FLAG_BUDDY_IS_DESPAWNED = 0x40, // buddy is dead or despawned }; -#define MAX_SCRIPT_FLAG_VALID (2 * SCRIPT_FLAG_BUDDY_IS_PET - 1) +#define MAX_SCRIPT_FLAG_VALID (2 * SCRIPT_FLAG_BUDDY_IS_DESPAWNED - 1) struct ScriptInfo { @@ -414,7 +428,7 @@ struct ScriptInfo uint32 altSender; // datalong2; } sendMail; - struct // SCRIPT_COMMAND_MORPH_TO_ENTRY_OR_MODEL (23) + struct // SCRIPT_COMMAND_MORPH_TO_ENTRY_OR_MODEL (39) { uint32 creatureEntry; // datalong uint32 empty1; // datalong2 @@ -425,6 +439,33 @@ struct ScriptInfo uint32 goGuid; //datalong uint32 respawnTime; //datalong2 } despawnGo; + // datalong unused // SCRIPT_COMMAND_RESPAWN (41) + + struct // SCRIPT_COMMAND_SET_EQUIPMENT_SLOTS (42) + { + uint32 resetDefault; // datalong + uint32 empty; // datalong2 + } setEquipment; + + // datalong unused // SCRIPT_COMMAND_RESET_GO (43) + + struct // SCRIPT_COMMAND_UPDATE_TEMPLATE (44) + { + uint32 entry; // datalong + uint32 faction; // datalong2 + } updateTemplate; + + struct // SCRIPT_COMMAND_XP_USER (53) + { + uint32 flags; // datalong + uint32 empty; // datalong2 + } xpDisabled; + + struct // SCRIPT_COMMAND_SET_FLY (59) + { + uint32 enable; // datalong + uint32 empty; // datalong2 + } fly; struct { @@ -449,13 +490,13 @@ struct ScriptInfo { switch (command) { - case SCRIPT_COMMAND_RESPAWN_GAMEOBJECT: + case SCRIPT_COMMAND_RESPAWN_GO: return respawnGo.goGuid; + case SCRIPT_COMMAND_DESPAWN_GO: + return despawnGo.goGuid; case SCRIPT_COMMAND_OPEN_DOOR: case SCRIPT_COMMAND_CLOSE_DOOR: return changeDoor.goGuid; - case SCRIPT_COMMAND_DESPAWN_GO: - return despawnGo.goGuid; default: return 0; } @@ -465,11 +506,13 @@ struct ScriptInfo { switch (command) { - case SCRIPT_COMMAND_RESPAWN_GAMEOBJECT: + case SCRIPT_COMMAND_RESPAWN_GO: case SCRIPT_COMMAND_OPEN_DOOR: case SCRIPT_COMMAND_CLOSE_DOOR: case SCRIPT_COMMAND_ACTIVATE_OBJECT: case SCRIPT_COMMAND_GO_LOCK_STATE: + case SCRIPT_COMMAND_DESPAWN_GO: + case SCRIPT_COMMAND_RESET_GO: return false; default: return true; @@ -491,6 +534,7 @@ struct ScriptInfo case SCRIPT_COMMAND_TERMINATE_COND: case SCRIPT_COMMAND_TURN_TO: case SCRIPT_COMMAND_MOVE_DYNAMIC: + case SCRIPT_COMMAND_SET_FLY: return true; default: return false; @@ -584,6 +628,9 @@ class ScriptMgr void LoadEventIdScripts(); void LoadSpellIdScripts(); + uint32 GetAreaTriggerScriptId(uint32 triggerId) const; + uint32 GetEventIdScriptId(uint32 eventId) const; + bool ReloadScriptBinding(); ScriptChainMap const* GetScriptChainMap(DBScriptType type); @@ -655,6 +702,7 @@ class ScriptMgr bool OnGameObjectUse(Unit* pUnit, GameObject* pGameObject); bool OnItemUse(Player* pPlayer, Item* pItem, SpellCastTargets const& targets); bool OnAreaTrigger(Player* pPlayer, AreaTriggerEntry const* atEntry); + bool OnNpcSpellClick(Player* pPlayer, Creature* pClickedCreature, uint32 spellId); bool OnProcessEvent(uint32 eventId, Object* pSource, Object* pTarget, bool isStart); bool OnEffectDummy(Unit* pCaster, uint32 spellId, SpellEffectIndex effIndex, Unit* pTarget, ObjectGuid originalCasterGuid); bool OnEffectDummy(Unit* pCaster, uint32 spellId, SpellEffectIndex effIndex, GameObject* pTarget, ObjectGuid originalCasterGuid); @@ -692,6 +740,8 @@ bool StartEvents_Event(Map* map, uint32 id, Object* source, Object* target, bool uint32 GetScriptId(const char* name); char const* GetScriptName(uint32 id); uint32 GetScriptIdsCount(); +uint32 GetAreaTriggerScriptId(uint32 triggerId); +uint32 GetEventIdScriptId(uint32 eventId); void SetExternalWaypointTable(char const* tableName); bool AddWaypointFromExternal(uint32 entry, int32 pathId, uint32 pointId, float x, float y, float z, float o, uint32 waittime); diff --git a/src/game/WorldHandlers/TradeHandler.cpp b/src/game/WorldHandlers/TradeHandler.cpp index 0b2ad6798..46ef797db 100644 --- a/src/game/WorldHandlers/TradeHandler.cpp +++ b/src/game/WorldHandlers/TradeHandler.cpp @@ -36,37 +36,37 @@ #include "DBCStores.h" #include "Language.h" -void WorldSession::SendTradeStatus(TradeStatus status) +void WorldSession::SendTradeStatus(const TradeStatusInfo& info) { WorldPacket data; - switch (status) + switch (info.Status) { case TRADE_STATUS_BEGIN_TRADE: data.Initialize(SMSG_TRADE_STATUS, 4 + 8); - data << uint32(status); + data << uint32(info.Status); data << uint64(0); break; case TRADE_STATUS_OPEN_WINDOW: data.Initialize(SMSG_TRADE_STATUS, 4 + 4); - data << uint32(status); + data << uint32(info.Status); data << uint32(0); // added in 2.4.0 break; case TRADE_STATUS_CLOSE_WINDOW: data.Initialize(SMSG_TRADE_STATUS, 4 + 4 + 1 + 4); - data << uint32(status); + data << uint32(info.Status); data << uint32(0); data << uint8(0); data << uint32(0); break; case TRADE_STATUS_ONLY_CONJURED: data.Initialize(SMSG_TRADE_STATUS, 4 + 1); - data << uint32(status); + data << uint32(info.Status); data << uint8(0); break; default: data.Initialize(SMSG_TRADE_STATUS, 4); - data << uint32(status); + data << uint32(info.Status); break; } @@ -304,10 +304,21 @@ void WorldSession::HandleAcceptTradeOpcode(WorldPacket& recvPacket) // set before checks to properly undo at problems (it already set in to client) my_trade->SetAccepted(true); + TradeStatusInfo info; + if (!_player->IsWithinDistInMap(trader, TRADE_DISTANCE, false)) + { + info.Status = TRADE_STATUS_TARGET_TO_FAR; + SendTradeStatus(info); + my_trade->SetAccepted(false); + return; + } + // not accept case incorrect money amount if (my_trade->GetMoney() > _player->GetMoney()) { - SendNotification(LANG_NOT_ENOUGH_GOLD); + info.Status = TRADE_STATUS_CLOSE_WINDOW; + info.Result = EQUIP_ERR_NOT_ENOUGH_MONEY; + SendTradeStatus(info); my_trade->SetAccepted(false, true); return; } @@ -315,7 +326,9 @@ void WorldSession::HandleAcceptTradeOpcode(WorldPacket& recvPacket) // not accept case incorrect money amount if (his_trade->GetMoney() > trader->GetMoney()) { - trader->GetSession()->SendNotification(LANG_NOT_ENOUGH_GOLD); + info.Status = TRADE_STATUS_CLOSE_WINDOW; + info.Result = EQUIP_ERR_NOT_ENOUGH_MONEY; + trader->GetSession()->SendTradeStatus(info); his_trade->SetAccepted(false, true); return; } @@ -327,7 +340,8 @@ void WorldSession::HandleAcceptTradeOpcode(WorldPacket& recvPacket) { if (!item->CanBeTraded()) { - SendTradeStatus(TRADE_STATUS_TRADE_CANCELED); + info.Status = TRADE_STATUS_TRADE_CANCELED; + SendTradeStatus(info); return; } } @@ -336,7 +350,8 @@ void WorldSession::HandleAcceptTradeOpcode(WorldPacket& recvPacket) { if (!item->CanBeTraded()) { - SendTradeStatus(TRADE_STATUS_TRADE_CANCELED); + info.Status = TRADE_STATUS_TRADE_CANCELED; + SendTradeStatus(info); return; } } @@ -426,31 +441,37 @@ void WorldSession::HandleAcceptTradeOpcode(WorldPacket& recvPacket) } // inform partner client - trader->GetSession()->SendTradeStatus(TRADE_STATUS_TRADE_ACCEPT); + info.Status = TRADE_STATUS_TRADE_ACCEPT; + trader->GetSession()->SendTradeStatus(info); // test if item will fit in each inventory - bool hisCanCompleteTrade = (trader->CanStoreItems(myItems, TRADE_SLOT_TRADED_COUNT) == EQUIP_ERR_OK); - bool myCanCompleteTrade = (_player->CanStoreItems(hisItems, TRADE_SLOT_TRADED_COUNT) == EQUIP_ERR_OK); + TradeStatusInfo myCanCompleteInfo, hisCanCompleteInfo; + hisCanCompleteInfo.Result = trader->CanStoreItems(myItems, TRADE_SLOT_TRADED_COUNT); + myCanCompleteInfo.Result = _player->CanStoreItems(hisItems, TRADE_SLOT_TRADED_COUNT); clearAcceptTradeMode(myItems, hisItems); // in case of missing space report error - if (!myCanCompleteTrade) + if (myCanCompleteInfo.Result != EQUIP_ERR_OK) { clearAcceptTradeMode(my_trade, his_trade); - SendNotification(LANG_NOT_FREE_TRADE_SLOTS); - trader->GetSession()->SendNotification(LANG_NOT_PARTNER_FREE_TRADE_SLOTS); + myCanCompleteInfo.Status = TRADE_STATUS_CLOSE_WINDOW; + trader->GetSession()->SendTradeStatus(myCanCompleteInfo); + myCanCompleteInfo.IsTargetResult = true; + SendTradeStatus(myCanCompleteInfo); my_trade->SetAccepted(false); his_trade->SetAccepted(false); return; } - else if (!hisCanCompleteTrade) + else if (hisCanCompleteInfo.Result != EQUIP_ERR_OK) { clearAcceptTradeMode(my_trade, his_trade); - SendNotification(LANG_NOT_PARTNER_FREE_TRADE_SLOTS); - trader->GetSession()->SendNotification(LANG_NOT_FREE_TRADE_SLOTS); + hisCanCompleteInfo.Status = TRADE_STATUS_CLOSE_WINDOW; + SendTradeStatus(hisCanCompleteInfo); + hisCanCompleteInfo.IsTargetResult = true; + trader->GetSession()->SendTradeStatus(hisCanCompleteInfo); my_trade->SetAccepted(false); his_trade->SetAccepted(false); return; @@ -522,12 +543,14 @@ void WorldSession::HandleAcceptTradeOpcode(WorldPacket& recvPacket) trader->SaveInventoryAndGoldToDB(); CharacterDatabase.CommitTransaction(); - trader->GetSession()->SendTradeStatus(TRADE_STATUS_TRADE_COMPLETE); - SendTradeStatus(TRADE_STATUS_TRADE_COMPLETE); + info.Status = TRADE_STATUS_TRADE_COMPLETE; + trader->GetSession()->SendTradeStatus(info); + SendTradeStatus(info); } else { - trader->GetSession()->SendTradeStatus(TRADE_STATUS_TRADE_ACCEPT); + info.Status = TRADE_STATUS_TRADE_ACCEPT; + trader->GetSession()->SendTradeStatus(info); } } @@ -550,8 +573,10 @@ void WorldSession::HandleBeginTradeOpcode(WorldPacket& /*recvPacket*/) return; } - my_trade->GetTrader()->GetSession()->SendTradeStatus(TRADE_STATUS_OPEN_WINDOW); - SendTradeStatus(TRADE_STATUS_OPEN_WINDOW); + TradeStatusInfo info; + info.Status = TRADE_STATUS_OPEN_WINDOW; + my_trade->GetTrader()->GetSession()->SendTradeStatus(info); + SendTradeStatus(info); } void WorldSession::SendCancelTrade() @@ -561,7 +586,9 @@ void WorldSession::SendCancelTrade() return; } - SendTradeStatus(TRADE_STATUS_TRADE_CANCELED); + TradeStatusInfo info; + info.Status = TRADE_STATUS_TRADE_CANCELED; + SendTradeStatus(info); } void WorldSession::HandleCancelTradeOpcode(WorldPacket& /*recvPacket*/) @@ -583,27 +610,32 @@ void WorldSession::HandleInitiateTradeOpcode(WorldPacket& recvPacket) return; } + TradeStatusInfo info; if (!GetPlayer()->IsAlive()) { - SendTradeStatus(TRADE_STATUS_YOU_DEAD); + info.Status = TRADE_STATUS_YOU_DEAD; + SendTradeStatus(info); return; } if (GetPlayer()->hasUnitState(UNIT_STAT_STUNNED)) { - SendTradeStatus(TRADE_STATUS_YOU_STUNNED); + info.Status = TRADE_STATUS_YOU_STUNNED; + SendTradeStatus(info); return; } if (isLogingOut()) { - SendTradeStatus(TRADE_STATUS_YOU_LOGOUT); + info.Status = TRADE_STATUS_YOU_LOGOUT; + SendTradeStatus(info); return; } if (GetPlayer()->IsTaxiFlying()) { - SendTradeStatus(TRADE_STATUS_TARGET_TO_FAR); + info.Status = TRADE_STATUS_TARGET_TO_FAR; + SendTradeStatus(info); return; } @@ -611,43 +643,50 @@ void WorldSession::HandleInitiateTradeOpcode(WorldPacket& recvPacket) if (!pOther) { - SendTradeStatus(TRADE_STATUS_NO_TARGET); + info.Status = TRADE_STATUS_NO_TARGET; + SendTradeStatus(info); return; } if (pOther == GetPlayer() || pOther->m_trade) { - SendTradeStatus(TRADE_STATUS_BUSY); + info.Status = TRADE_STATUS_BUSY; + SendTradeStatus(info); return; } if (!pOther->IsAlive()) { - SendTradeStatus(TRADE_STATUS_TARGET_DEAD); + info.Status = TRADE_STATUS_TARGET_DEAD; + SendTradeStatus(info); return; } if (pOther->IsTaxiFlying()) { - SendTradeStatus(TRADE_STATUS_TARGET_TO_FAR); + info.Status = TRADE_STATUS_TARGET_TO_FAR; + SendTradeStatus(info); return; } if (pOther->hasUnitState(UNIT_STAT_STUNNED)) { - SendTradeStatus(TRADE_STATUS_TARGET_STUNNED); + info.Status = TRADE_STATUS_TARGET_STUNNED; + SendTradeStatus(info); return; } if (pOther->GetSession()->isLogingOut()) { - SendTradeStatus(TRADE_STATUS_TARGET_LOGOUT); + info.Status = TRADE_STATUS_TARGET_LOGOUT; + SendTradeStatus(info); return; } if (pOther->GetSocial()->HasIgnore(GetPlayer()->GetObjectGuid())) { - SendTradeStatus(TRADE_STATUS_IGNORE_YOU); + info.Status = TRADE_STATUS_IGNORE_YOU; + SendTradeStatus(info); return; } @@ -655,13 +694,15 @@ void WorldSession::HandleInitiateTradeOpcode(WorldPacket& recvPacket) // TOD : Still missing sWorld.getConfig(CONFIG_BOOL_ALLOW_TWO_SIDE_INTERACTION_TRADE) : HAS TO BE PORTED FROM M0 ! if (pOther->GetTeam() != _player->GetTeam() && GetSecurity() == SEC_PLAYER) { - SendTradeStatus(TRADE_STATUS_WRONG_FACTION); + info.Status = TRADE_STATUS_WRONG_FACTION; + SendTradeStatus(info); return; } - if (!pOther->IsWithinDistInMap(_player, 10.0f, false)) + if (!pOther->IsWithinDistInMap(GetPlayer(), TRADE_DISTANCE, false)) { - SendTradeStatus(TRADE_STATUS_TARGET_TO_FAR); + info.Status = TRADE_STATUS_TARGET_TO_FAR; + SendTradeStatus(info); return; } @@ -672,7 +713,8 @@ void WorldSession::HandleInitiateTradeOpcode(WorldPacket& recvPacket) ) ) { - SendTradeStatus(TRADE_STATUS_TRADE_CANCELED); + info.Status = TRADE_STATUS_TRADE_CANCELED; + SendTradeStatus(info); return; } @@ -719,10 +761,12 @@ void WorldSession::HandleSetTradeItemOpcode(WorldPacket& recvPacket) return; } + TradeStatusInfo info; // invalid slot number if (tradeSlot >= TRADE_SLOT_COUNT) { - SendTradeStatus(TRADE_STATUS_TRADE_CANCELED); + info.Status = TRADE_STATUS_TRADE_CANCELED; + SendTradeStatus(info); return; } @@ -730,7 +774,8 @@ void WorldSession::HandleSetTradeItemOpcode(WorldPacket& recvPacket) Item* item = _player->GetItemByPos(bag, slot); if (!item || (tradeSlot != TRADE_SLOT_NONTRADED && !item->CanBeTraded())) { - SendTradeStatus(TRADE_STATUS_TRADE_CANCELED); + info.Status = TRADE_STATUS_TRADE_CANCELED; + SendTradeStatus(info); return; } @@ -738,7 +783,8 @@ void WorldSession::HandleSetTradeItemOpcode(WorldPacket& recvPacket) if (my_trade->HasItem(item->GetObjectGuid())) { // cheating attempt - SendTradeStatus(TRADE_STATUS_TRADE_CANCELED); + info.Status = TRADE_STATUS_TRADE_CANCELED; + SendTradeStatus(info); return; } diff --git a/src/modules/SD3 b/src/modules/SD3 index e7e308611..951ed4f45 160000 --- a/src/modules/SD3 +++ b/src/modules/SD3 @@ -1 +1 @@ -Subproject commit e7e3086118f7c9dd2084e22099e2beb4aee1cf10 +Subproject commit 951ed4f456cbe2ad06ef9a432af14398ba78f62b