diff --git a/sql/mangos.sql b/sql/mangos.sql index 7c0a3bea7..48bf76334 100644 --- a/sql/mangos.sql +++ b/sql/mangos.sql @@ -23,7 +23,8 @@ DROP TABLE IF EXISTS `db_version`; CREATE TABLE `db_version` ( `version` varchar(120) default NULL, `creature_ai_version` varchar(120) default NULL, - `required_8342_01_mangos_spell_proc_event` bit(1) default NULL + `cache_id` int(10) default '0', + `required_8364_01_mangos_db_version` bit(1) default NULL ) ENGINE=MyISAM DEFAULT CHARSET=utf8 ROW_FORMAT=FIXED COMMENT='Used DB version notes'; -- @@ -13591,6 +13592,7 @@ CREATE TABLE `spell_bonus_data` ( LOCK TABLES `spell_bonus_data` WRITE; /*!40000 ALTER TABLE `spell_bonus_data` DISABLE KEYS */; INSERT INTO `spell_bonus_data` VALUES +/* Death Knight */ ('48721', '0', '0', '0.04', 'Death Knight - Blood Boil'), ('55078', '0', '0', '0.055', 'Death Knight - Blood Plague Dummy Proc'), ('50444', '0', '0', '0.105', 'Death Knight - Corpse Explosion Triggered'), @@ -13605,6 +13607,7 @@ INSERT INTO `spell_bonus_data` VALUES ('50536', '0', '0', '0.013', 'Death Knight - Unholy Blight Triggered'), ('50401', '0', '0', '0', 'Death Knight - Razor Frost'), ('56903', '0', '0', '0', 'Death Knight - Lichflame'), +/* Druid */ ('5185', '1.6104', '0', '0', 'Druid - Healing Touch'), ('33763', '0', '0.09518', '0', 'Druid - Lifebloom'), ('774', '0', '0.37604', '0', 'Druid - Rejuvenation'), @@ -13621,6 +13624,7 @@ INSERT INTO `spell_bonus_data` VALUES ('8921', '0.1515', '0.13', '0', 'Druid - Moonfire'), ('2912', '1', '0', '0', 'Druid - Starfire'), ('5176', '0.5714', '0', '0', 'Druid - Wrath'), +/* Mage */ ('30451', '0.7143', '0', '0', 'Mage - Arcane Blast'), ('1449', '0.2128', '0', '0', 'Mage - Arcane Explosion'), ('7268', '0.2857', '0', '0', 'Mage - Arcane Missiles Triggered Spell'), @@ -13642,6 +13646,7 @@ INSERT INTO `spell_bonus_data` VALUES ('11426', '0.8053', '0', '0', 'Mage - Ice Barrier'), ('30455', '0.1429', '0', '0', 'Mage - Ice Lance'), ('34913','0', '0', '0', 'Mage - Molten Armor Triggered'), +/* Paladin */ ('19750','0.4286', '0', '0', 'Paladin - Flash of Light'), ('635', '0.7143', '0', '0', 'Paladin - Holy Light'), ('25912', '0.4286', '0', '0', 'Paladin - Holy Shock Triggered Hurt'), @@ -13662,7 +13667,7 @@ INSERT INTO `spell_bonus_data` VALUES ('25742', '0.07', '0', '0.039', 'Paladin - Seal of Righteousness Dummy Proc'), ('53595', '0', '0', '0','Paladin - Hammer of the Righteous'), ('31803', '0', '0.013', '0.15', 'Paladin - Holy Vengeance'), -('52042', '0.045', '0', '0', 'Shaman - Healing Stream Totem Triggered Heal'), +/* Priest */ ('32546', '0.8068', '0', '0', 'Priest - Binding Heal'), ('34861', '0.402', '0', '0', 'Priest - Circle of Healing'), ('19236', '0.8068', '0', '0', 'Priest - Desperate Prayer'), @@ -13685,9 +13690,11 @@ INSERT INTO `spell_bonus_data` VALUES ('589', '0', '0.1829', '0', 'Priest - Shadow Word: Pain'), ('585', '0.714', '0', '0', 'Priest - Smite'), ('34914', '0', '0.4', '0', 'Priest - Vampiric Touch'), +/* Shaman */ ('974', '0.4762', '0', '0', 'Shaman - Earth Shield'), ('1064', '1.34', '0', '0', 'Shaman - Chain Heal'), ('331', '1.6106', '0', '0', 'Shaman - Healing Wave'), +('52042', '0.045', '0', '0', 'Shaman - Healing Stream Totem Triggered Heal'), ('8004', '0.8082', '0', '0', 'Shaman - Lesser Healing Wave'), ('61295', '0.4', '0.18', '0', 'Shaman - Riptide'), ('421', '0.57', '0', '0', 'Shaman - Chain Lightning'), @@ -13702,6 +13709,7 @@ INSERT INTO `spell_bonus_data` VALUES ('26364', '0.33', '0', '0', 'Shaman - Lightning Shield Proc'), ('8188', '0.1', '0', '0', 'Shaman - Magma Totam Passive'), ('3606', '0.1667', '0', '0', 'Shaman - Searing Totem Attack'), +/* Warlock */ ('980', '0', '0.1', '0', 'Warlock - Curse of Agony'), ('603', '0', '2', '0', 'Warlock - Curse of Doom'), ('172', '0', '0.3', '0', 'Warlock - Corruption'), @@ -13728,7 +13736,9 @@ INSERT INTO `spell_bonus_data` VALUES ('42223', '0.952', '0', '0', 'Warlock - Rain of Fire Triggered'), ('18220', '0.96', '0', '0', 'Warlock - Dark Pact'), ('6229', '0.3', '0', '0', 'Warlock - Shadow Ward'), -('63106', '0', '0', '0', 'Warlock - Siphon Life Triggered'); +('63106', '0', '0', '0', 'Warlock - Siphon Life Triggered'), +/* Item */ +(40293, 0, 0, 0, 'Item - Siphon Essence'); /*!40000 ALTER TABLE `spell_bonus_data` ENABLE KEYS */; UNLOCK TABLES; diff --git a/sql/updates/8361_01_mangos_spell_bonus_data.sql b/sql/updates/8361_01_mangos_spell_bonus_data.sql new file mode 100644 index 000000000..5c6a0452c --- /dev/null +++ b/sql/updates/8361_01_mangos_spell_bonus_data.sql @@ -0,0 +1,6 @@ +ALTER TABLE db_version CHANGE COLUMN required_8342_01_mangos_spell_proc_event required_8361_01_mangos_spell_bonus_data bit; + +DELETE FROM `spell_bonus_data` where entry in (40293); + +INSERT INTO `spell_bonus_data` VALUES +(40293, 0, 0, 0, 'Item - Siphon Essence'); diff --git a/sql/updates/8364_01_mangos_db_version.sql b/sql/updates/8364_01_mangos_db_version.sql new file mode 100644 index 000000000..97f661316 --- /dev/null +++ b/sql/updates/8364_01_mangos_db_version.sql @@ -0,0 +1,4 @@ +ALTER TABLE db_version CHANGE COLUMN required_8361_01_mangos_spell_bonus_data required_8364_01_mangos_db_version bit; + +ALTER TABLE db_version + ADD COLUMN cache_id int(10) default '0' AFTER creature_ai_version; diff --git a/sql/updates/Makefile.am b/sql/updates/Makefile.am index 9a9a36a56..a55e7b9b6 100644 --- a/sql/updates/Makefile.am +++ b/sql/updates/Makefile.am @@ -80,6 +80,8 @@ pkgdata_DATA = \ 8339_01_characters_characters.sql \ 8339_02_characters_character_battleground_data.sql \ 8342_01_mangos_spell_proc_event.sql \ + 8361_01_mangos_spell_bonus_data.sql \ + 8364_01_mangos_db_version.sql \ README ## Additional files to include when running 'make dist' @@ -140,4 +142,6 @@ EXTRA_DIST = \ 8339_01_characters_characters.sql \ 8339_02_characters_character_battleground_data.sql \ 8342_01_mangos_spell_proc_event.sql \ + 8361_01_mangos_spell_bonus_data.sql \ + 8364_01_mangos_db_version.sql \ README diff --git a/src/game/BattleGround.cpp b/src/game/BattleGround.cpp index 446b2aac2..9c70da28e 100644 --- a/src/game/BattleGround.cpp +++ b/src/game/BattleGround.cpp @@ -855,7 +855,7 @@ void BattleGround::RewardItem(Player *plr, uint32 item_id, uint32 count) if( count != 0 && !dest.empty()) // can add some if (Item* item = plr->StoreNewItem( dest, item_id, true, 0)) - plr->SendNewItem(item,count,false,true); + plr->SendNewItem(item,count,true,false); if (no_space_count > 0) SendRewardMarkByMail(plr,item_id,no_space_count); diff --git a/src/game/DBCEnums.h b/src/game/DBCEnums.h index 20b28c1d4..c6f57559c 100644 --- a/src/game/DBCEnums.h +++ b/src/game/DBCEnums.h @@ -19,6 +19,11 @@ #ifndef DBCENUMS_H #define DBCENUMS_H +// Client expected level limitation, like as used in DBC item max levels for "until max player level" +// use as default max player level, must be fit max level for used client +// also see MAX_LEVEL and STRONG_MAX_LEVEL define +#define DEFAULT_MAX_LEVEL 80 + // client supported max level for player/pets/etc. Avoid overflow or client stability affected. // also see GT_MAX_LEVEL define #define MAX_LEVEL 100 diff --git a/src/game/Player.cpp b/src/game/Player.cpp index 404be4dd5..9fbd3deee 100644 --- a/src/game/Player.cpp +++ b/src/game/Player.cpp @@ -1094,8 +1094,8 @@ void Player::Update( uint32 p_time ) if( q_status.m_timer <= p_time ) { uint32 quest_id = *iter; - ++iter; // current iter will be removed in FailTimedQuest - FailTimedQuest( quest_id ); + ++iter; // current iter will be removed in FailQuest + FailQuest(quest_id); } else { @@ -6510,13 +6510,20 @@ void Player::_ApplyItemMods(Item *item, uint8 slot,bool apply) void Player::_ApplyItemBonuses(ItemPrototype const *proto, uint8 slot, bool apply, bool only_level_scale /*= false*/) { - if(slot >= INVENTORY_SLOT_BAG_END || !proto) + if (slot >= INVENTORY_SLOT_BAG_END || !proto) return; ScalingStatDistributionEntry const *ssd = proto->ScalingStatDistribution ? sScalingStatDistributionStore.LookupEntry(proto->ScalingStatDistribution) : NULL; - ScalingStatValuesEntry const *ssv = proto->ScalingStatValue ? sScalingStatValuesStore.LookupEntry(getLevel()) : NULL; + if (only_level_scale && !ssd) + return; - if(only_level_scale && !(ssd && ssv)) + // req. check at equip, but allow use for extended range if range limit max level, set proper level + uint32 ssd_level = getLevel(); + if (ssd && ssd_level > ssd->MaxLevel) + ssd_level = ssd->MaxLevel; + + ScalingStatValuesEntry const *ssv = proto->ScalingStatValue ? sScalingStatValuesStore.LookupEntry(ssd_level) : NULL; + if (only_level_scale && !ssv) return; for (int i = 0; i < MAX_ITEM_PROTO_STATS; ++i) @@ -9786,7 +9793,8 @@ uint8 Player::CanEquipItem( uint8 slot, uint16 &dest, Item *pItem, bool swap, bo } ScalingStatDistributionEntry const *ssd = pProto->ScalingStatDistribution ? sScalingStatDistributionStore.LookupEntry(pProto->ScalingStatDistribution) : 0; - if (ssd && ssd->MaxLevel < getLevel()) + // check allowed level (extend range to upper values if MaxLevel more or equal max player level, this let GM set high level with 1...max range items) + if (ssd && ssd->MaxLevel < DEFAULT_MAX_LEVEL && ssd->MaxLevel < getLevel()) return EQUIP_ERR_ITEM_CANT_BE_EQUIPPED; uint8 eslot = FindEquipSlot( pProto, slot, swap ); @@ -12784,41 +12792,30 @@ void Player::RewardQuest( Quest const *pQuest, uint32 reward, Object* questGiver SetCanDelayTeleport(false); } -void Player::FailQuest( uint32 quest_id ) +void Player::FailQuest(uint32 questId) { - if( quest_id ) + if (Quest const* pQuest = objmgr.GetQuestTemplate(questId)) { - IncompleteQuest( quest_id ); + SetQuestStatus(questId, QUEST_STATUS_FAILED); - uint16 log_slot = FindQuestSlot( quest_id ); - if( log_slot < MAX_QUEST_LOG_SIZE) + uint16 log_slot = FindQuestSlot(questId); + + if (log_slot < MAX_QUEST_LOG_SIZE) { - SetQuestSlotTimer(log_slot, 1 ); - SetQuestSlotState(log_slot,QUEST_STATE_FAIL); + SetQuestSlotTimer(log_slot, 1); + SetQuestSlotState(log_slot, QUEST_STATE_FAIL); } - SendQuestFailed( quest_id ); - } -} -void Player::FailTimedQuest( uint32 quest_id ) -{ - if( quest_id ) - { - QuestStatusData& q_status = mQuestStatus[quest_id]; - - q_status.m_timer = 0; - if (q_status.uState != QUEST_NEW) - q_status.uState = QUEST_CHANGED; - - IncompleteQuest( quest_id ); - - uint16 log_slot = FindQuestSlot( quest_id ); - if( log_slot < MAX_QUEST_LOG_SIZE) + if (pQuest->HasFlag(QUEST_MANGOS_FLAGS_TIMED)) { - SetQuestSlotTimer(log_slot, 1 ); - SetQuestSlotState(log_slot,QUEST_STATE_FAIL); + QuestStatusData& q_status = mQuestStatus[questId]; + + q_status.m_timer = 0; + + SendQuestTimerFailed(questId); } - SendQuestTimerFailed( quest_id ); + else + SendQuestFailed(questId); } } @@ -13267,21 +13264,22 @@ bool Player::CanShareQuest(uint32 quest_id) const return false; } -void Player::SetQuestStatus( uint32 quest_id, QuestStatus status ) +void Player::SetQuestStatus(uint32 quest_id, QuestStatus status) { - Quest const* qInfo = objmgr.GetQuestTemplate(quest_id); - if( qInfo ) + if (Quest const* qInfo = objmgr.GetQuestTemplate(quest_id)) { - if( status == QUEST_STATUS_NONE || status == QUEST_STATUS_INCOMPLETE || status == QUEST_STATUS_COMPLETE ) + if (status == QUEST_STATUS_NONE || status == QUEST_STATUS_INCOMPLETE || status == QUEST_STATUS_COMPLETE || status == QUEST_STATUS_FAILED) { - if( qInfo->HasFlag( QUEST_MANGOS_FLAGS_TIMED ) ) + if (qInfo->HasFlag(QUEST_MANGOS_FLAGS_TIMED)) m_timedquests.erase(qInfo->GetQuestId()); } QuestStatusData& q_status = mQuestStatus[quest_id]; q_status.m_status = status; - if (q_status.uState != QUEST_NEW) q_status.uState = QUEST_CHANGED; + + if (q_status.uState != QUEST_NEW) + q_status.uState = QUEST_CHANGED; } UpdateForQuestWorldObjects(); @@ -15124,16 +15122,20 @@ void Player::_LoadQuestStatus(QueryResult *result) questStatusData.uState = QUEST_UNCHANGED; // add to quest log - if( slot < MAX_QUEST_LOG_SIZE && - ( questStatusData.m_status == QUEST_STATUS_INCOMPLETE || - questStatusData.m_status == QUEST_STATUS_COMPLETE && - (!questStatusData.m_rewarded || pQuest->IsDaily()) ) ) + if (slot < MAX_QUEST_LOG_SIZE && + ((questStatusData.m_status == QUEST_STATUS_INCOMPLETE || + questStatusData.m_status == QUEST_STATUS_COMPLETE || + questStatusData.m_status == QUEST_STATUS_FAILED) && + (!questStatusData.m_rewarded || pQuest->IsDaily()))) { SetQuestSlot(slot, quest_id, quest_time); - if(questStatusData.m_status == QUEST_STATUS_COMPLETE) + if (questStatusData.m_status == QUEST_STATUS_COMPLETE) SetQuestSlotState(slot, QUEST_STATE_COMPLETE); + if (questStatusData.m_status == QUEST_STATUS_FAILED) + SetQuestSlotState(slot, QUEST_STATE_FAIL); + for(uint8 idx = 0; idx < QUEST_OBJECTIVES_COUNT; ++idx) if(questStatusData.m_creatureOrGOcount[idx]) SetQuestSlotCounter(slot, idx, questStatusData.m_creatureOrGOcount[idx]); diff --git a/src/game/Player.h b/src/game/Player.h index 0812d534e..ceaa0ba45 100644 --- a/src/game/Player.h +++ b/src/game/Player.h @@ -1296,7 +1296,6 @@ class MANGOS_DLL_SPEC Player : public Unit void IncompleteQuest( uint32 quest_id ); void RewardQuest( Quest const *pQuest, uint32 reward, Object* questGiver, bool announce = true ); void FailQuest( uint32 quest_id ); - void FailTimedQuest( uint32 quest_id ); bool SatisfyQuestSkillOrClass( Quest const* qInfo, bool msg ); bool SatisfyQuestLevel( Quest const* qInfo, bool msg ); bool SatisfyQuestLog( bool msg ); diff --git a/src/game/QuestDef.h b/src/game/QuestDef.h index 6a751c8bc..999e8de7a 100644 --- a/src/game/QuestDef.h +++ b/src/game/QuestDef.h @@ -97,6 +97,7 @@ enum QuestStatus QUEST_STATUS_UNAVAILABLE = 2, QUEST_STATUS_INCOMPLETE = 3, QUEST_STATUS_AVAILABLE = 4, + QUEST_STATUS_FAILED = 5, MAX_QUEST_STATUS }; diff --git a/src/game/SharedDefines.h b/src/game/SharedDefines.h index 11b311bcb..caa07bb30 100644 --- a/src/game/SharedDefines.h +++ b/src/game/SharedDefines.h @@ -1045,6 +1045,14 @@ enum Targets TARGET_BEHIND_VICTIM = 65, // uses in teleport behind spells, caster/target dependent from spell effect TARGET_DYNAMIC_OBJECT_COORDINATES = 76, TARGET_SINGLE_ENEMY = 77, + TARGET_POINT_AT_NORTH = 78, // 78-85 possible _COORDINATES at radius with pi/4 step around target in unknown order, N? + TARGET_POINT_AT_SOUTH = 79, // S? + TARGET_POINT_AT_EAST = 80, // 80/81 must be symmetric from line caster->target, E (base at 82/83, 84/85 order) ? + TARGET_POINT_AT_WEST = 81, // 80/81 must be symmetric from line caster->target, W (base at 82/83, 84/85 order) ? + TARGET_POINT_AT_NE = 82, // from spell desc: "(NE)" + TARGET_POINT_AT_NW = 83, // from spell desc: "(NW)" + TARGET_POINT_AT_SE = 84, // from spell desc: "(SE)" + TARGET_POINT_AT_SW = 85, // from spell desc: "(SW)" TARGET_SELF2 = 87, TARGET_DIRECTLY_FORWARD = 89, TARGET_NONCOMBAT_PET = 90, @@ -1833,11 +1841,26 @@ enum CreatureFamily enum CreatureTypeFlags { - CREATURE_TYPEFLAGS_TAMEABLE = 0x00001, - CREATURE_TYPEFLAGS_HERBLOOT = 0x00100, - CREATURE_TYPEFLAGS_MININGLOOT = 0x00200, - CREATURE_TYPEFLAGS_ENGINEERLOOT = 0x08000, - CREATURE_TYPEFLAGS_EXOTIC = 0x10000 + CREATURE_TYPEFLAGS_TAMEABLE = 0x00001, //tameable by any hunter + CREATURE_TYPEFLAGS_UNK2 = 0x00002, //? Related to spirits/ghosts in any form? Allow gossip interaction if player is also ghost? Visibility? + CREATURE_TYPEFLAGS_UNK3 = 0x00004, + CREATURE_TYPEFLAGS_UNK4 = 0x00008, + CREATURE_TYPEFLAGS_UNK5 = 0x00010, + CREATURE_TYPEFLAGS_UNK6 = 0x00020, + CREATURE_TYPEFLAGS_UNK7 = 0x00040, + CREATURE_TYPEFLAGS_UNK8 = 0x00080, + CREATURE_TYPEFLAGS_HERBLOOT = 0x00100, //can be looted by herbalist + CREATURE_TYPEFLAGS_MININGLOOT = 0x00200, //can be looted by miner + CREATURE_TYPEFLAGS_UNK11 = 0x00400, + CREATURE_TYPEFLAGS_UNK12 = 0x00800, //? Related to mounts in some way. If mounted, fight mounted, mount appear as independant when rider dies? + CREATURE_TYPEFLAGS_UNK13 = 0x01000, //? Can aid any player in combat if in range? + CREATURE_TYPEFLAGS_UNK14 = 0x02000, + CREATURE_TYPEFLAGS_UNK15 = 0x04000, //? Possibly not in use + CREATURE_TYPEFLAGS_ENGINEERLOOT = 0x08000, //can be looted by engineer + CREATURE_TYPEFLAGS_EXOTIC = 0x10000, //can be tamed by hunter as exotic pet + CREATURE_TYPEFLAGS_UNK18 = 0x20000, //? Related to veichles/pvp? + CREATURE_TYPEFLAGS_UNK19 = 0x40000, //? Related to veichle/siege weapons? + CREATURE_TYPEFLAGS_UNK20 = 0x80000 }; enum CreatureEliteType diff --git a/src/game/Spell.cpp b/src/game/Spell.cpp index 7e92f2d24..6d350265f 100644 --- a/src/game/Spell.cpp +++ b/src/game/Spell.cpp @@ -1205,7 +1205,7 @@ void Spell::DoSpellHitOnUnit(Unit *unit, const uint32 effectMask) unit->RemoveSpellsCausingAura(SPELL_AURA_MOD_STEALTH); // can cause back attack (if detected) - if (!(m_spellInfo->AttributesEx & SPELL_ATTR_EX_NO_INITIAL_AGGRO) && + if (!(m_spellInfo->AttributesEx & SPELL_ATTR_EX_NO_INITIAL_AGGRO) && !IsPositiveSpell(m_spellInfo->Id) && m_caster->isVisibleForOrDetect(unit,false)) // stealth removed at Spell::cast if spell break it { // use speedup check to avoid re-remove after above lines @@ -2110,6 +2110,39 @@ void Spell::SetTargetMap(uint32 i,uint32 cur,UnitList& TagUnitMap) if(DynamicObject* dynObj = m_caster->GetDynObject(m_triggeredByAuraSpell ? m_triggeredByAuraSpell->Id : m_spellInfo->Id)) m_targets.setDestination(dynObj->GetPositionX(), dynObj->GetPositionY(), dynObj->GetPositionZ()); break; + case TARGET_POINT_AT_NORTH: + case TARGET_POINT_AT_SOUTH: + case TARGET_POINT_AT_EAST: + case TARGET_POINT_AT_WEST: + case TARGET_POINT_AT_NE: + case TARGET_POINT_AT_NW: + case TARGET_POINT_AT_SE: + case TARGET_POINT_AT_SW: + { + + if (!(m_targets.m_targetMask & TARGET_FLAG_DEST_LOCATION)) + { + Unit* currentTarget = m_targets.getUnitTarget() ? m_targets.getUnitTarget() : m_caster; + float angle = currentTarget != m_caster ? currentTarget->GetAngle(m_caster) : m_caster->GetOrientation(); + + switch(cur) + { + case TARGET_POINT_AT_NORTH: break; + case TARGET_POINT_AT_SOUTH: angle += M_PI; break; + case TARGET_POINT_AT_EAST: angle -= M_PI/2; break; + case TARGET_POINT_AT_WEST: angle += M_PI/2; break; + case TARGET_POINT_AT_NE: angle -= M_PI/4; break; + case TARGET_POINT_AT_NW: angle += M_PI/4; break; + case TARGET_POINT_AT_SE: angle -= 3*M_PI/4; break; + case TARGET_POINT_AT_SW: angle += 3*M_PI/4; break; + } + + float x,y; + currentTarget->GetNearPoint2D(x,y,radius,angle); + m_targets.setDestination(x,y,currentTarget->GetPositionZ()); + } + break; + } case TARGET_DIRECTLY_FORWARD: { if (!(m_targets.m_targetMask & TARGET_FLAG_DEST_LOCATION)) @@ -2345,6 +2378,9 @@ void Spell::cast(bool skipCheck) if (m_spellInfo->Mechanic == MECHANIC_SHIELD && (m_spellInfo->SpellFamilyFlags & UI64LIT(0x0000000000000001))) AddPrecastSpell(6788); // Weakened Soul + // Prayer of Mending (jump animation), we need formal caster instead original for correct animation + else if (m_spellInfo->SpellFamilyFlags & UI64LIT(0x0000002000000000)) + AddTriggeredSpell(41637); switch(m_spellInfo->Id) { @@ -2362,6 +2398,14 @@ void Spell::cast(bool skipCheck) } break; } + case SPELLFAMILY_ROGUE: + // Fan of Knives (main hand) + if (m_spellInfo->Id == 51723 && m_caster->GetTypeId() == TYPEID_PLAYER && + ((Player*)m_caster)->haveOffhandWeapon()) + { + AddTriggeredSpell(52874); // Fan of Knives (offhand) + } + break; case SPELLFAMILY_PALADIN: { // Divine Shield, Divine Protection or Hand of Protection @@ -2783,12 +2827,16 @@ void Spell::finish(bool ok) // Not drop combopoints if negative spell and if any miss on enemy exist bool needDrop = true; if (!IsPositiveSpell(m_spellInfo->Id)) - for(std::list::const_iterator ihit= m_UniqueTargetInfo.begin();ihit != m_UniqueTargetInfo.end();++ihit) - if (ihit->missCondition != SPELL_MISS_NONE && ihit->targetGUID!=m_caster->GetGUID()) + { + for(std::list::const_iterator ihit= m_UniqueTargetInfo.begin();ihit != m_UniqueTargetInfo.end();++ihit) { - needDrop = false; - break; + if (ihit->missCondition != SPELL_MISS_NONE && ihit->targetGUID!=m_caster->GetGUID()) + { + needDrop = false; + break; + } } + } if (needDrop) ((Player*)m_caster)->ClearComboPoints(); } @@ -3796,11 +3844,21 @@ SpellCastResult Spell::CheckCast(bool strict) // auto selection spell rank implemented in WorldSession::HandleCastSpellOpcode // this case can be triggered if rank not found (too low-level target for first rank) - if(m_caster->GetTypeId() == TYPEID_PLAYER && !IsPassiveSpell(m_spellInfo->Id) && !m_CastItem) + if (m_caster->GetTypeId() == TYPEID_PLAYER && !IsPassiveSpell(m_spellInfo->Id) && !m_CastItem) + { for(int i=0;i<3;++i) - if(IsPositiveEffect(m_spellInfo->Id, i) && m_spellInfo->Effect[i] == SPELL_EFFECT_APPLY_AURA) - if(target->getLevel() + 10 < m_spellInfo->spellLevel) - return SPELL_FAILED_LOWLEVEL; + { + // check only spell that apply positive auras + if (IsPositiveEffect(m_spellInfo->Id, i) && m_spellInfo->Effect[i] == SPELL_EFFECT_APPLY_AURA && + // at not self target + !IsCasterSourceTarget(m_spellInfo->EffectImplicitTargetA[i]) && + // and target low level + target->getLevel() + 10 < m_spellInfo->spellLevel) + { + return SPELL_FAILED_LOWLEVEL; + } + } + } } else if (m_caster->GetTypeId() == TYPEID_PLAYER) // Target - is player caster { diff --git a/src/game/SpellAuras.cpp b/src/game/SpellAuras.cpp index 1fb84ca44..ef6dd642c 100644 --- a/src/game/SpellAuras.cpp +++ b/src/game/SpellAuras.cpp @@ -2198,6 +2198,21 @@ void Aura::HandleAuraDummy(bool apply, bool Real) bg->RemovePlayerFromResurrectQueue(m_target->GetGUID()); return; } + case 36730: // Flame Strike + { + m_target->CastSpell(m_target, 36731, true, NULL, this); + return; + } + case 44191: // Flame Strike + { + if (m_target->GetMap()->IsDungeon()) + { + uint32 spellId = m_target->GetMap()->IsHeroic() ? 46163 : 44190; + + m_target->CastSpell(m_target, spellId, true, NULL, this); + } + return; + } case 45934: // Dark Fiend { // Kill target if dispelled @@ -2389,6 +2404,19 @@ void Aura::HandleAuraDummy(bool apply, bool Real) ((Player*)m_target)->AddSpellMod(m_spellmod, apply); return; } + case 52610: // Savage Roar + { + if(apply) + { + if(m_target->m_form != FORM_CAT) + return; + + m_target->CastSpell(m_target, 62071, true); + } + else + m_target-> RemoveAurasDueToSpell(62071); + return; + } case 61336: // Survival Instincts { if(apply) @@ -2450,6 +2478,32 @@ void Aura::HandleAuraDummy(bool apply, bool Real) ((Player*)m_target)->UpdateAttackPowerAndDamage(); return; } + + // Improved Moonkin Form + if(GetSpellProto()->SpellIconID == 2855) + { + uint32 spell_id; + switch(GetId()) + { + case 48384: spell_id = 50170; //Rank 1 + case 48395: spell_id = 50171; //Rank 2 + case 48396: spell_id = 50172; //Rank 3 + default: + sLog.outError("HandleAuraDummy: Not handled rank of IMF (Spell: %u)",GetId()); + return; + } + + if(apply) + { + if(m_target->m_form != FORM_MOONKIN) + return; + + m_target->CastSpell(m_target, spell_id, true); + } + else + m_target-> RemoveAurasDueToSpell(spell_id); + return; + } break; } case SPELLFAMILY_HUNTER: @@ -2721,10 +2775,12 @@ void Aura::HandleAuraModShapeshift(bool apply, bool Real) { SpellEntry const* aurSpellInfo = (*iter)->GetSpellProto(); + uint32 aurMechMask = GetAllSpellMechanicMask(aurSpellInfo); + // If spell that caused this aura has Croud Control or Daze effect - if((GetAllSpellMechanicMask(aurSpellInfo) & MECHANIC_NOT_REMOVED_BY_SHAPESHIFT) || - // some Daze spells have these parameters instead of MECHANIC_DAZE - (aurSpellInfo->SpellIconID == 15 && aurSpellInfo->Dispel == 0)) + if((aurMechMask & MECHANIC_NOT_REMOVED_BY_SHAPESHIFT) || + // some Daze spells have these parameters instead of MECHANIC_DAZE (skip snare spells) + aurSpellInfo->SpellIconID == 15 && aurSpellInfo->Dispel == 0 && (aurMechMask & (1 << MECHANIC_SNARE))==0) { ++iter; continue; @@ -5366,7 +5422,9 @@ void Aura::HandleShapeshiftBoosts(bool apply) uint32 HotWSpellId = 0; uint32 MasterShaperSpellId = 0; - switch(GetModifier()->m_miscvalue) + uint32 form = GetModifier()->m_miscvalue; + + switch(form) { case FORM_CAT: spellId = 3025; @@ -5438,8 +5496,6 @@ void Aura::HandleShapeshiftBoosts(bool apply) break; } - uint32 form = GetModifier()->m_miscvalue-1; - if(apply) { if (spellId) m_target->CastSpell(m_target, spellId, true, NULL, this ); @@ -5455,7 +5511,7 @@ void Aura::HandleShapeshiftBoosts(bool apply) SpellEntry const *spellInfo = sSpellStore.LookupEntry(itr->first); if (!spellInfo || !(spellInfo->Attributes & (SPELL_ATTR_PASSIVE | (1<<7)))) continue; - if (spellInfo->Stances & (1<Stances & (1<<(form-1))) m_target->CastSpell(m_target, itr->first, true, NULL, this); } @@ -5478,10 +5534,41 @@ void Aura::HandleShapeshiftBoosts(bool apply) if (((Player*)m_target)->HasSpell(17007)) { SpellEntry const *spellInfo = sSpellStore.LookupEntry(24932); - if (spellInfo && spellInfo->Stances & (1<Stances & (1<<(form-1))) m_target->CastSpell(m_target, 24932, true, NULL, this); } + // Savage Roar + if (form == FORM_CAT && ((Player*)m_target)->HasAura(52610)) + m_target->CastSpell(m_target, 62071, true); + + // Improved Moonkin Form + if (form == FORM_MOONKIN) + { + Unit::AuraList const& dummyAuras = m_target->GetAurasByType(SPELL_AURA_DUMMY); + for(Unit::AuraList::const_iterator i = dummyAuras.begin(); i != dummyAuras.end(); i++) + { + if ((*i)->GetSpellProto()->SpellFamilyName==SPELLFAMILY_DRUID && + (*i)->GetSpellProto()->SpellIconID == 2855) + { + uint32 spell_id = 0; + switch((*i)->GetId()) + { + case 48384:spell_id=50170;break;//Rank 1 + case 48395:spell_id=50171;break;//Rank 2 + case 48396:spell_id=50172;break;//Rank 3 + default: + sLog.outError("Aura::HandleShapeshiftBoosts: Not handled rank of IMF (Spell: %u)",(*i)->GetId()); + break; + } + + if(spell_id) + m_target->CastSpell(m_target, spell_id, true, NULL, this); + break; + } + } + } + // Heart of the Wild if (HotWSpellId) { @@ -5519,9 +5606,6 @@ void Aura::HandleShapeshiftBoosts(bool apply) ++itr; } } - - /*double healthPercentage = (double)m_target->GetHealth() / (double)m_target->GetMaxHealth(); - m_target->SetHealth(uint32(ceil((double)m_target->GetMaxHealth() * healthPercentage)));*/ } void Aura::HandleSpellSpecificBoosts(bool apply) @@ -5610,15 +5694,18 @@ void Aura::HandleSpellSpecificBoosts(bool apply) return; } + // prevent aura deletion, specially in multi-boost case + SetInUse(true); + if (apply) { if (spellId1) m_target->CastSpell(m_target, spellId1, true, NULL, this); - if (spellId2) + if (spellId2 && !IsDeleted()) m_target->CastSpell(m_target, spellId2, true, NULL, this); - if (spellId3) + if (spellId3 && !IsDeleted()) m_target->CastSpell(m_target, spellId3, true, NULL, this); - if (spellId4) + if (spellId4 && !IsDeleted()) m_target->CastSpell(m_target, spellId4, true, NULL, this); } else @@ -5632,6 +5719,8 @@ void Aura::HandleSpellSpecificBoosts(bool apply) if (spellId4) m_target->RemoveAurasByCasterSpell(spellId4, GetCasterGUID()); } + + SetInUse(false); } void Aura::HandleAuraEmpathy(bool apply, bool /*Real*/) diff --git a/src/game/SpellEffects.cpp b/src/game/SpellEffects.cpp index 39fb1558e..db4f1b277 100644 --- a/src/game/SpellEffects.cpp +++ b/src/game/SpellEffects.cpp @@ -1957,9 +1957,17 @@ void Spell::EffectForceCast(uint32 i) unitTarget->CastSpell(unitTarget, spellInfo, true, NULL, NULL, m_originalCasterGUID); } -void Spell::EffectTriggerSpell(uint32 i) +void Spell::EffectTriggerSpell(uint32 effIndex) { - uint32 triggered_spell_id = m_spellInfo->EffectTriggerSpell[i]; + // only unit case known + if (!unitTarget) + { + if(gameObjTarget || itemTarget) + sLog.outError("Spell::EffectTriggerSpell (Spell: %u): Unsupported non-unit case!",m_spellInfo->Id); + return; + } + + uint32 triggered_spell_id = m_spellInfo->EffectTriggerSpell[effIndex]; // special cases switch(triggered_spell_id) @@ -1967,21 +1975,21 @@ void Spell::EffectTriggerSpell(uint32 i) // Vanish (not exist) case 18461: { - m_caster->RemoveSpellsCausingAura(SPELL_AURA_MOD_ROOT); - m_caster->RemoveSpellsCausingAura(SPELL_AURA_MOD_DECREASE_SPEED); - m_caster->RemoveSpellsCausingAura(SPELL_AURA_MOD_STALKED); + unitTarget->RemoveSpellsCausingAura(SPELL_AURA_MOD_ROOT); + unitTarget->RemoveSpellsCausingAura(SPELL_AURA_MOD_DECREASE_SPEED); + unitTarget->RemoveSpellsCausingAura(SPELL_AURA_MOD_STALKED); // if this spell is given to NPC it must handle rest by it's own AI - if ( m_caster->GetTypeId() != TYPEID_PLAYER ) + if (unitTarget->GetTypeId() != TYPEID_PLAYER) return; // get highest rank of the Stealth spell uint32 spellId = 0; - const PlayerSpellMap& sp_list = ((Player*)m_caster)->GetSpellMap(); + const PlayerSpellMap& sp_list = ((Player*)unitTarget)->GetSpellMap(); for (PlayerSpellMap::const_iterator itr = sp_list.begin(); itr != sp_list.end(); ++itr) { // only highest rank is shown in spell book, so simply check if shown in spell book - if(!itr->second->active || itr->second->disabled || itr->second->state == PLAYERSPELL_REMOVED) + if (!itr->second->active || itr->second->disabled || itr->second->state == PLAYERSPELL_REMOVED) continue; SpellEntry const *spellInfo = sSpellStore.LookupEntry(itr->first); @@ -2000,10 +2008,10 @@ void Spell::EffectTriggerSpell(uint32 i) return; // reset cooldown on it if needed - if(((Player*)m_caster)->HasSpellCooldown(spellId)) - ((Player*)m_caster)->RemoveSpellCooldown(spellId); + if (((Player*)unitTarget)->HasSpellCooldown(spellId)) + ((Player*)unitTarget)->RemoveSpellCooldown(spellId); - m_caster->CastSpell(m_caster, spellId, true); + m_caster->CastSpell(unitTarget, spellId, true); return; } // just skip @@ -2043,7 +2051,7 @@ void Spell::EffectTriggerSpell(uint32 i) // Cloak of Shadows case 35729: { - Unit::AuraMap& Auras = m_caster->GetAuras(); + Unit::AuraMap& Auras = unitTarget->GetAuras(); for(Unit::AuraMap::iterator iter = Auras.begin(); iter != Auras.end(); ++iter) { // remove all harmful spells on you... @@ -2061,7 +2069,7 @@ void Spell::EffectTriggerSpell(uint32 i) // Priest Shadowfiend (34433) need apply mana gain trigger aura on pet case 41967: { - if (Unit *pet = m_caster->GetPet()) + if (Unit *pet = unitTarget->GetPet()) pet->CastSpell(pet, 28305, true); return; } @@ -2069,63 +2077,54 @@ void Spell::EffectTriggerSpell(uint32 i) // normal case SpellEntry const *spellInfo = sSpellStore.LookupEntry( triggered_spell_id ); - - if(!spellInfo) + if (!spellInfo) { sLog.outError("EffectTriggerSpell of spell %u: triggering unknown spell id %i", m_spellInfo->Id,triggered_spell_id); return; } + // select formal caster for triggered spell + Unit* caster = m_caster; + // some triggered spells require specific equipment - if(spellInfo->EquippedItemClass >=0 && m_caster->GetTypeId()==TYPEID_PLAYER) + if (spellInfo->EquippedItemClass >=0 && m_caster->GetTypeId()==TYPEID_PLAYER) { // main hand weapon required - if(spellInfo->AttributesEx3 & SPELL_ATTR_EX3_MAIN_HAND) + if (spellInfo->AttributesEx3 & SPELL_ATTR_EX3_MAIN_HAND) { Item* item = ((Player*)m_caster)->GetWeaponForAttack(BASE_ATTACK); // skip spell if no weapon in slot or broken - if(!item || item->IsBroken() ) + if (!item || item->IsBroken() ) return; // skip spell if weapon not fit to triggered spell - if(!item->IsFitToSpellRequirements(spellInfo)) + if (!item->IsFitToSpellRequirements(spellInfo)) return; } // offhand hand weapon required - if(spellInfo->AttributesEx3 & SPELL_ATTR_EX3_REQ_OFFHAND) + if (spellInfo->AttributesEx3 & SPELL_ATTR_EX3_REQ_OFFHAND) { Item* item = ((Player*)m_caster)->GetWeaponForAttack(OFF_ATTACK); // skip spell if no weapon in slot or broken - if(!item || item->IsBroken() ) + if (!item || item->IsBroken() ) return; // skip spell if weapon not fit to triggered spell - if(!item->IsFitToSpellRequirements(spellInfo)) + if (!item->IsFitToSpellRequirements(spellInfo)) return; } } - - // some triggered spells must be casted instantly (for example, if next effect case instant kill caster) - bool instant = false; - for(uint32 j = i+1; j < 3; ++j) - { - if(m_spellInfo->Effect[j]==SPELL_EFFECT_INSTAKILL && m_spellInfo->EffectImplicitTargetA[j]==TARGET_SELF) - { - instant = true; - break; - } - } - - if(instant) - { - if (unitTarget) - m_caster->CastSpell(unitTarget,spellInfo,true,m_CastItem,NULL,m_originalCasterGUID); - } else - AddTriggeredSpell(spellInfo); + { + // Note: not exist spells with weapon req. and IsSpellHaveCasterSourceTargets == true + // so this just for speedup places in else + caster = IsSpellWithCasterSourceTargetsOnly(spellInfo) ? unitTarget : m_caster; + } + + caster->CastSpell(unitTarget,spellInfo,true,NULL,NULL,m_originalCasterGUID); } void Spell::EffectTriggerMissileSpell(uint32 effect_idx) @@ -2403,15 +2402,7 @@ void Spell::EffectApplyAura(uint32 i) Aur->SetAuraDuration(duration); } - bool added = unitTarget->AddAura(Aur); - - // Aura not added and deleted in AddAura call; - if (!added) - return; - - // Prayer of Mending (jump animation), we need formal caster instead original for correct animation - if( m_spellInfo->SpellFamilyName == SPELLFAMILY_PRIEST && (m_spellInfo->SpellFamilyFlags & UI64LIT(0x0000002000000000))) - m_caster->CastSpell(unitTarget, 41637, true, NULL, Aur, m_originalCasterGUID); + unitTarget->AddAura(Aur); } void Spell::EffectUnlearnSpecialization( uint32 i ) @@ -2686,6 +2677,26 @@ void Spell::DoCreateItem(uint32 i, uint32 itemtype) return; } + // bg reward have some special in code work + uint32 bgType = 0; + switch(m_spellInfo->Id) + { + case SPELL_AV_MARK_WINNER: + case SPELL_AV_MARK_LOSER: + bgType = BATTLEGROUND_AV; + break; + case SPELL_WS_MARK_WINNER: + case SPELL_WS_MARK_LOSER: + bgType = BATTLEGROUND_WS; + break; + case SPELL_AB_MARK_WINNER: + case SPELL_AB_MARK_LOSER: + bgType = BATTLEGROUND_AB; + break; + default: + break; + } + uint32 num_to_add; // TODO: maybe all this can be replaced by using correct calculated `damage` value @@ -2766,35 +2777,17 @@ void Spell::DoCreateItem(uint32 i, uint32 itemtype) // send info to the client if(pItem) - player->SendNewItem(pItem, num_to_add, true, true); + player->SendNewItem(pItem, num_to_add, true, bgType == 0); // we succeeded in creating at least one item, so a levelup is possible - player->UpdateCraftSkill(m_spellInfo->Id); + if(bgType == 0) + player->UpdateCraftSkill(m_spellInfo->Id); } // for battleground marks send by mail if not add all expected - if(no_space > 0 ) + if(no_space > 0 && bgType) { - BattleGroundTypeId bgType; - switch(m_spellInfo->Id) - { - case SPELL_AV_MARK_WINNER: - case SPELL_AV_MARK_LOSER: - bgType = BATTLEGROUND_AV; - break; - case SPELL_WS_MARK_WINNER: - case SPELL_WS_MARK_LOSER: - bgType = BATTLEGROUND_WS; - break; - case SPELL_AB_MARK_WINNER: - case SPELL_AB_MARK_LOSER: - bgType = BATTLEGROUND_AB; - break; - default: - return; - } - - if(BattleGround* bg = sBattleGroundMgr.GetBattleGroundTemplate(bgType)) + if(BattleGround* bg = sBattleGroundMgr.GetBattleGroundTemplate(BattleGroundTypeId(bgType))) bg->SendRewardMarkByMail(player, newitemid, no_space); } } @@ -4485,6 +4478,13 @@ void Spell::EffectWeaponDmg(uint32 i) if(found) totalDamagePercentMod *= 1.2f; // 120% if poisoned } + // Fan of Knives + else if (m_caster->GetTypeId()==TYPEID_PLAYER && (m_spellInfo->SpellFamilyFlags & UI64LIT(0x0004000000000000))) + { + Item* weapon = ((Player*)m_caster)->GetWeaponForAttack(m_attackType,true); + if (weapon && weapon->GetProto()->SubClass == ITEM_SUBCLASS_WEAPON_DAGGER) + totalDamagePercentMod *= 1.5f; // 150% to daggers + } break; } case SPELLFAMILY_PALADIN: @@ -5054,6 +5054,11 @@ void Spell::EffectScriptEffect(uint32 effIndex) } return; } + case 55693: // Remove Collapsing Cave Aura + if(unitTarget) + return; + unitTarget->RemoveAurasDueToSpell(m_spellInfo->CalculateSimpleValue(effIndex)); + break; case 58418: // Portal to Orgrimmar case 58420: // Portal to Stormwind { @@ -6061,8 +6066,11 @@ void Spell::EffectCharge(uint32 /*i*/) if (!unitTarget) return; + //TODO: research more ContactPoint/attack distance. + //3.666666 instead of ATTACK_DISTANCE(5.0f) in below seem to give more accurate result. float x, y, z; - unitTarget->GetContactPoint(m_caster, x, y, z); + unitTarget->GetContactPoint(m_caster, x, y, z, 3.666666f); + if (unitTarget->GetTypeId() != TYPEID_PLAYER) ((Creature *)unitTarget)->StopMoving(); @@ -6090,7 +6098,7 @@ void Spell::EffectCharge2(uint32 /*i*/) ((Creature *)unitTarget)->StopMoving(); } else if (unitTarget && unitTarget != m_caster) - unitTarget->GetContactPoint(m_caster, x, y, z); + unitTarget->GetContactPoint(m_caster, x, y, z, 3.666666f); else return; diff --git a/src/game/SpellMgr.cpp b/src/game/SpellMgr.cpp index dbb15f434..36db6af7d 100644 --- a/src/game/SpellMgr.cpp +++ b/src/game/SpellMgr.cpp @@ -301,15 +301,17 @@ bool IsPositiveEffect(uint32 spellId, uint32 effIndex) SpellEntry const *spellproto = sSpellStore.LookupEntry(spellId); if (!spellproto) return false; - switch(spellId) - { - case 28441: // not positive dummy spell - case 37675: // Chaos Blast - return false; - } - switch(spellproto->Effect[effIndex]) { + case SPELL_EFFECT_DUMMY: + // some explicitly required dummy effect sets + switch(spellId) + { + case 28441: return false; // AB Effect 000 + default: + break; + } + break; // always positive effects (check before target checks that provided non-positive result in some case for positive effects) case SPELL_EFFECT_HEAL: case SPELL_EFFECT_LEARN_SPELL: @@ -349,15 +351,21 @@ bool IsPositiveEffect(uint32 spellId, uint32 effIndex) break; } } break; - case SPELL_AURA_MOD_STAT: case SPELL_AURA_MOD_DAMAGE_DONE: // dependent from bas point sign (negative -> negative) + case SPELL_AURA_MOD_STAT: + case SPELL_AURA_MOD_SKILL: + case SPELL_AURA_MOD_HEALING_PCT: case SPELL_AURA_MOD_HEALING_DONE: if(spellproto->CalculateSimpleValue(effIndex) < 0) return false; break; + case SPELL_AURA_MOD_DAMAGE_TAKEN: // dependent from bas point sign (positive -> negative) + if(spellproto->CalculateSimpleValue(effIndex) > 0) + return false; + break; case SPELL_AURA_MOD_SPELL_CRIT_CHANCE: if(spellproto->CalculateSimpleValue(effIndex) > 0) - return true; // some expected possitive spells have SPELL_ATTR_EX_NEGATIVE + return true; // some expected positive spells have SPELL_ATTR_EX_NEGATIVE break; case SPELL_AURA_ADD_TARGET_TRIGGER: return true; @@ -391,11 +399,14 @@ bool IsPositiveEffect(uint32 spellId, uint32 effIndex) if(spellproto->Id == 17624) return false; break; + case SPELL_AURA_MOD_PACIFY_SILENCE: + if(spellproto->Id == 24740) // Wisp Costume + return true; + return false; case SPELL_AURA_MOD_ROOT: case SPELL_AURA_MOD_SILENCE: case SPELL_AURA_GHOST: case SPELL_AURA_PERIODIC_LEECH: - case SPELL_AURA_MOD_PACIFY_SILENCE: case SPELL_AURA_MOD_STALKED: case SPELL_AURA_PERIODIC_DAMAGE_PERCENT: return false; @@ -460,14 +471,6 @@ bool IsPositiveEffect(uint32 spellId, uint32 effIndex) break; } } break; - case SPELL_AURA_MOD_HEALING_PCT: - if(spellproto->CalculateSimpleValue(effIndex) < 0) - return false; - break; - case SPELL_AURA_MOD_SKILL: - if(spellproto->CalculateSimpleValue(effIndex) < 0) - return false; - break; case SPELL_AURA_FORCE_REACTION: if(spellproto->Id==42792) // Recently Dropped Flag (prevent cancel) return false; @@ -1411,6 +1414,10 @@ bool SpellMgr::IsNoStackSpellDueToSpell(uint32 spellId_1, uint32 spellId_2) cons // Survival Instincts and Survival Instincts if( spellInfo_1->Id == 61336 && spellInfo_2->Id == 50322 || spellInfo_2->Id == 61336 && spellInfo_1->Id == 50322 ) return false; + + // Savage Roar and Savage Roar (triggered) + if (spellInfo_1->SpellIconID == 2865 && spellInfo_2->SpellIconID == 2865) + return false; } // Leader of the Pack and Scroll of Stamina (multi-family check) @@ -2490,17 +2497,17 @@ void SpellMgr::LoadSpellAreas() SpellAreaMapBounds sa_bounds = GetSpellAreaMapBounds(spellArea.spellId); for(SpellAreaMap::const_iterator itr = sa_bounds.first; itr != sa_bounds.second; ++itr) { - if(spellArea.spellId && itr->second.spellId && spellArea.spellId != itr->second.spellId) + if (spellArea.spellId != itr->second.spellId) continue; - if(spellArea.areaId && itr->second.areaId && spellArea.areaId!= itr->second.areaId) + if (spellArea.areaId != itr->second.areaId) continue; - if(spellArea.questStart && itr->second.questStart && spellArea.questStart!= itr->second.questStart) + if (spellArea.questStart != itr->second.questStart) continue; - if(spellArea.auraSpell && itr->second.auraSpell && spellArea.auraSpell!= itr->second.auraSpell) + if (spellArea.auraSpell != itr->second.auraSpell) continue; - if(spellArea.raceMask && itr->second.raceMask && (spellArea.raceMask & itr->second.raceMask)==0) + if ((spellArea.raceMask & itr->second.raceMask) == 0) continue; - if(spellArea.gender != GENDER_NONE && itr->second.gender != GENDER_NONE && spellArea.gender!= itr->second.gender) + if (spellArea.gender != itr->second.gender) continue; // duplicate by requirements diff --git a/src/game/SpellMgr.h b/src/game/SpellMgr.h index f1eb31ef3..e225aa1c3 100644 --- a/src/game/SpellMgr.h +++ b/src/game/SpellMgr.h @@ -240,9 +240,18 @@ inline bool IsCasterSourceTarget(uint32 target) inline bool IsSpellWithCasterSourceTargetsOnly(SpellEntry const* spellInfo) { for(int i = 0; i < 3; ++i) - if(uint32 target = spellInfo->EffectImplicitTargetA[i]) - if(!IsCasterSourceTarget(target)) - return false; + { + uint32 targetA = spellInfo->EffectImplicitTargetA[i]; + if(targetA && !IsCasterSourceTarget(targetA)) + return false; + + uint32 targetB = spellInfo->EffectImplicitTargetB[i]; + if(targetB && !IsCasterSourceTarget(targetB)) + return false; + + if(!targetA && !targetB) + return false; + } return true; } @@ -257,6 +266,14 @@ inline bool IsPointEffectTarget( Targets target ) case TARGET_CURRENT_ENEMY_COORDINATES: case TARGET_DUELVSPLAYER_COORDINATES: case TARGET_DYNAMIC_OBJECT_COORDINATES: + case TARGET_POINT_AT_NORTH: + case TARGET_POINT_AT_SOUTH: + case TARGET_POINT_AT_EAST: + case TARGET_POINT_AT_WEST: + case TARGET_POINT_AT_NE: + case TARGET_POINT_AT_NW: + case TARGET_POINT_AT_SE: + case TARGET_POINT_AT_SW: return true; default: break; diff --git a/src/game/Unit.cpp b/src/game/Unit.cpp index 49c3b1a69..a118ead6b 100644 --- a/src/game/Unit.cpp +++ b/src/game/Unit.cpp @@ -452,9 +452,10 @@ uint32 Unit::DealDamage(Unit *pVictim, uint32 damage, CleanDamage const* cleanDa if(cInfo && cInfo->lootid) pVictim->SetUInt32Value(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_LOOTABLE); - // some critters required for quests + // some critters required for quests (need normal entry instead possible heroic in any cases) if(GetTypeId() == TYPEID_PLAYER) - ((Player*)this)->KilledMonster(cInfo ,pVictim->GetGUID()); + if(CreatureInfo const* normalInfo = objmgr.GetCreatureTemplate(pVictim->GetEntry())) + ((Player*)this)->KilledMonster(normalInfo,pVictim->GetGUID()); return damage; } @@ -3602,9 +3603,14 @@ bool Unit::RemoveNoStackAurasDueToAura(Aura *Aur) if(i_spellId == spellId) continue; bool is_triggered_by_spell = false; - // prevent triggered aura of removing aura that triggered it + // prevent triggering aura of removing aura that triggered it for(int j = 0; j < 3; ++j) - if (i_spellProto->EffectTriggerSpell[j] == spellProto->Id) + if (i_spellProto->EffectTriggerSpell[j] == spellId) + is_triggered_by_spell = true; + + // prevent triggered aura of removing aura that triggering it (triggered effect early some aura of parent spell + for(int j = 0; j < 3; ++j) + if (spellProto->EffectTriggerSpell[j] == i_spellId) is_triggered_by_spell = true; if (is_triggered_by_spell) @@ -5567,6 +5573,10 @@ bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, Aura* triggeredByAu case 31876: case 31877: case 31878: + // triggered only at casted Judgement spells, not at additional Judgement effects + if(!procSpell || procSpell->Category != 1210) + return false; + target = this; triggered_spell_id = 31930; @@ -11036,7 +11046,7 @@ void Unit::ProcDamageAndSpellFor( bool isVictim, Unit * pTarget, uint32 procFlag sLog.outDebug("ProcDamageAndSpell: casting mending (triggered by %s dummy aura of spell %u)", (isVictim?"a victim's":"an attacker's"),triggeredByAura->GetId()); - HandleMeandingAuraProc(triggeredByAura); + HandleMendingAuraProc(triggeredByAura); break; } case SPELL_AURA_PROC_TRIGGER_SPELL_WITH_VALUE: @@ -11816,7 +11826,7 @@ bool Unit::IsTriggeredAtSpellProcEvent(Unit *pVictim, Aura* aura, SpellEntry con return roll_chance_f(chance); } -bool Unit::HandleMeandingAuraProc( Aura* triggeredByAura ) +bool Unit::HandleMendingAuraProc( Aura* triggeredByAura ) { // aura can be deleted at casts SpellEntry const* spellProto = triggeredByAura->GetSpellProto(); @@ -11854,9 +11864,14 @@ bool Unit::HandleMeandingAuraProc( Aura* triggeredByAura ) mod->mask = spellProto->SpellFamilyFlags; mod->mask2 = spellProto->SpellFamilyFlags2; + // remove before apply next (locked against deleted) + triggeredByAura->SetInUse(true); + RemoveAurasByCasterSpell(spellProto->Id,caster->GetGUID()); + caster->AddSpellMod(mod, true); CastCustomSpell(target,spellProto->Id,&heal,NULL,NULL,true,NULL,triggeredByAura,caster->GetGUID()); caster->AddSpellMod(mod, false); + triggeredByAura->SetInUse(false); } } } diff --git a/src/game/Unit.h b/src/game/Unit.h index 608d60cf9..d4ca820a5 100644 --- a/src/game/Unit.h +++ b/src/game/Unit.h @@ -1551,7 +1551,7 @@ class MANGOS_DLL_SPEC Unit : public WorldObject bool HandleHasteAuraProc( Unit *pVictim, uint32 damage, Aura* triggredByAura, SpellEntry const *procSpell, uint32 procFlag, uint32 procEx, uint32 cooldown); bool HandleProcTriggerSpell(Unit *pVictim, uint32 damage, Aura* triggredByAura, SpellEntry const *procSpell, uint32 procFlag, uint32 procEx, uint32 cooldown); bool HandleOverrideClassScriptAuraProc(Unit *pVictim, uint32 damage, Aura* triggredByAura, SpellEntry const *procSpell, uint32 cooldown); - bool HandleMeandingAuraProc(Aura* triggeredByAura); + bool HandleMendingAuraProc(Aura* triggeredByAura); uint32 m_state; // Even derived shouldn't modify uint32 m_CombatTimer; diff --git a/src/game/WaypointMovementGenerator.cpp b/src/game/WaypointMovementGenerator.cpp index 903ced7ca..acf3fb398 100644 --- a/src/game/WaypointMovementGenerator.cpp +++ b/src/game/WaypointMovementGenerator.cpp @@ -160,10 +160,10 @@ bool WaypointMovementGenerator::Update(Creature &creature, const uint3 else creature.Say(behavior->textid[0], 0, 0); } - - i_hasDone[idx] = true; - MovementInform(creature); } // wpBehaviour found + + i_hasDone[idx] = true; + MovementInform(creature); } // HasDone == false } // i_creature.IsStopped() diff --git a/src/game/World.cpp b/src/game/World.cpp index d8a5ab67d..a6c294b77 100644 --- a/src/game/World.cpp +++ b/src/game/World.cpp @@ -237,6 +237,11 @@ World::AddSession_ (WorldSession* s) s->SendPacket (&packet); s->SendAddonsInfo(); + + WorldPacket pkt(SMSG_CLIENTCACHE_VERSION, 4); + pkt << uint32(sWorld.getConfig(CONFIG_CLIENTCACHE_VERSION)); + s->SendPacket(&pkt); + s->SendTutorialsData(); UpdateMaxSessionCounters (); @@ -673,12 +678,12 @@ void World::LoadConfigSettings(bool reload) if(reload) { - uint32 val = sConfig.GetIntDefault("MaxPlayerLevel", 80); + uint32 val = sConfig.GetIntDefault("MaxPlayerLevel", DEFAULT_MAX_LEVEL); if(val!=m_configs[CONFIG_MAX_PLAYER_LEVEL]) sLog.outError("MaxPlayerLevel option can't be changed at mangosd.conf reload, using current value (%u).",m_configs[CONFIG_MAX_PLAYER_LEVEL]); } else - m_configs[CONFIG_MAX_PLAYER_LEVEL] = sConfig.GetIntDefault("MaxPlayerLevel", 80); + m_configs[CONFIG_MAX_PLAYER_LEVEL] = sConfig.GetIntDefault("MaxPlayerLevel", DEFAULT_MAX_LEVEL); if(m_configs[CONFIG_MAX_PLAYER_LEVEL] > MAX_LEVEL) { @@ -867,7 +872,7 @@ void World::LoadConfigSettings(bool reload) m_configs[CONFIG_MAX_OVERSPEED_PINGS] = sConfig.GetIntDefault("MaxOverspeedPings",2); if(m_configs[CONFIG_MAX_OVERSPEED_PINGS] != 0 && m_configs[CONFIG_MAX_OVERSPEED_PINGS] < 2) { - sLog.outError("MaxOverspeedPings (%i) must be in range 2..infinity (or 0 to disable check. Set to 2.",m_configs[CONFIG_MAX_OVERSPEED_PINGS]); + sLog.outError("MaxOverspeedPings (%i) must be in range 2..infinity (or 0 to disable check). Set to 2.",m_configs[CONFIG_MAX_OVERSPEED_PINGS]); m_configs[CONFIG_MAX_OVERSPEED_PINGS] = 2; } @@ -954,6 +959,18 @@ void World::LoadConfigSettings(bool reload) m_configs[CONFIG_OFFHAND_CHECK_AT_TALENTS_RESET] = sConfig.GetBoolDefault("OffhandCheckAtTalentsReset", false); + if(int clientCacheId = sConfig.GetIntDefault("ClientCacheVersion", 0)) + { + // overwrite DB/old value + if(clientCacheId > 0) + { + m_configs[CONFIG_CLIENTCACHE_VERSION] = clientCacheId; + sLog.outString("Client cache version set to: %u", clientCacheId); + } + else + sLog.outError("ClientCacheVersion can't be negative %d, ignored.", clientCacheId); + } + m_configs[CONFIG_INSTANT_LOGOUT] = sConfig.GetIntDefault("InstantLogout", SEC_MODERATOR); m_VisibleUnitGreyDistance = sConfig.GetFloatDefault("Visibility.Distance.Grey.Unit", 1); @@ -2115,13 +2132,16 @@ void World::UpdateMaxSessionCounters() void World::LoadDBVersion() { - QueryResult* result = WorldDatabase.Query("SELECT version, creature_ai_version FROM db_version LIMIT 1"); + QueryResult* result = WorldDatabase.Query("SELECT version, creature_ai_version, cache_id FROM db_version LIMIT 1"); if(result) { Field* fields = result->Fetch(); m_DBVersion = fields[0].GetCppString(); m_CreatureEventAIVersion = fields[1].GetCppString(); + + // will be overwrite by config values if different and non-0 + m_configs[CONFIG_CLIENTCACHE_VERSION] = fields[2].GetUInt32(); delete result; } diff --git a/src/game/World.h b/src/game/World.h index e4ff977f5..ab3dbc77c 100644 --- a/src/game/World.h +++ b/src/game/World.h @@ -212,6 +212,7 @@ enum WorldConfigs CONFIG_ARENA_SEASON_ID, CONFIG_ARENA_SEASON_IN_PROGRESS, CONFIG_OFFHAND_CHECK_AT_TALENTS_RESET, + CONFIG_CLIENTCACHE_VERSION, CONFIG_VALUE_COUNT }; diff --git a/src/mangosd/mangosd.conf.dist.in b/src/mangosd/mangosd.conf.dist.in index ae0ac39ca..75396690d 100644 --- a/src/mangosd/mangosd.conf.dist.in +++ b/src/mangosd/mangosd.conf.dist.in @@ -569,6 +569,10 @@ LogColors = "" # Default: 0 - recheck offhand slot weapon only at zone update # 1 - recheck offhand slot weapon at talent reset also # +# ClientCacheVersion +# Client cache version for client cache data reset. Use any different from DB value and not recently used for triggering reset. +# Default: 0 (use DB value from world DB db_version.cache_id field) +# # Event.Announce # Default: 0 (false) # 1 (true) @@ -627,6 +631,7 @@ MailDeliveryDelay = 3600 SkillChance.Prospecting = 0 SkillChance.Milling = 0 OffhandCheckAtTalentsReset = 0 +ClientCacheVersion = 0 Event.Announce = 0 BeepAtStart = 1 Motd = "Welcome to the Massive Network Game Object Server." diff --git a/src/realmd/AuthCodes.h b/src/realmd/AuthCodes.h index e091b61de..af2e38b4e 100644 --- a/src/realmd/AuthCodes.h +++ b/src/realmd/AuthCodes.h @@ -70,4 +70,8 @@ enum LoginResult #define EXPECTED_MANGOS_CLIENT_BUILD {10257, 0} +// At update excepted builds please update if need define DEFAULT_MAX_LEVEL +// in DBCEnum.h to default max player level expected by build +// and also in mangosd.conf.in + #endif diff --git a/src/shared/revision_nr.h b/src/shared/revision_nr.h index 03bda0f9b..fb4c3393c 100644 --- a/src/shared/revision_nr.h +++ b/src/shared/revision_nr.h @@ -1,4 +1,4 @@ #ifndef __REVISION_NR_H__ #define __REVISION_NR_H__ - #define REVISION_NR "8342" + #define REVISION_NR "8372" #endif // __REVISION_NR_H__