diff options
Diffstat (limited to 'src')
27 files changed, 604 insertions, 340 deletions
diff --git a/src/server/game/Battlefield/Zones/BattlefieldWG.cpp b/src/server/game/Battlefield/Zones/BattlefieldWG.cpp index 5555f2a824c..41539b9cda6 100644 --- a/src/server/game/Battlefield/Zones/BattlefieldWG.cpp +++ b/src/server/game/Battlefield/Zones/BattlefieldWG.cpp @@ -41,14 +41,14 @@ BattlefieldWG::~BattlefieldWG()  bool BattlefieldWG::SetupBattlefield()  { -    InitStalker(BATTLEFIELD_WG_NPC_STALKER, WintergraspStalkerPos[0], WintergraspStalkerPos[1], WintergraspStalkerPos[2], WintergraspStalkerPos[3]); -      m_TypeId = BATTLEFIELD_WG;                              // See enum BattlefieldTypes      m_BattleId = BATTLEFIELD_BATTLEID_WG;      m_ZoneId = BATTLEFIELD_WG_ZONEID;      m_MapId = BATTLEFIELD_WG_MAPID;      m_Map = sMapMgr->FindMap(m_MapId, 0); +    InitStalker(BATTLEFIELD_WG_NPC_STALKER, WintergraspStalkerPos[0], WintergraspStalkerPos[1], WintergraspStalkerPos[2], WintergraspStalkerPos[3]); +      m_MaxPlayer = sWorld->getIntConfig(CONFIG_WINTERGRASP_PLR_MAX);      m_IsEnabled = sWorld->getBoolConfig(CONFIG_WINTERGRASP_ENABLE);      m_MinPlayer = sWorld->getIntConfig(CONFIG_WINTERGRASP_PLR_MIN); diff --git a/src/server/game/Battlegrounds/BattlegroundScore.h b/src/server/game/Battlegrounds/BattlegroundScore.h index ae65721b516..95d1db2c337 100644 --- a/src/server/game/Battlegrounds/BattlegroundScore.h +++ b/src/server/game/Battlegrounds/BattlegroundScore.h @@ -87,7 +87,7 @@ struct BattlegroundScore              }          } -        virtual void AppendToPacket(WorldPacket& data)  +        virtual void AppendToPacket(WorldPacket& data)          {              data << uint64(PlayerGuid); @@ -108,7 +108,7 @@ struct BattlegroundScore          uint64 PlayerGuid; -        // Default score, present in every type  +        // Default score, present in every type          uint32 KillingBlows;          uint32 Deaths;          uint32 HonorableKills; diff --git a/src/server/game/DataStores/DBCEnums.h b/src/server/game/DataStores/DBCEnums.h index 10b7c25bb1f..03180b35cb5 100644 --- a/src/server/game/DataStores/DBCEnums.h +++ b/src/server/game/DataStores/DBCEnums.h @@ -326,8 +326,8 @@ enum MapFlags  enum AbilytyLearnType  { -    ABILITY_LEARNED_ON_GET_PROFESSION_SKILL     = 1, -    ABILITY_LEARNED_ON_GET_RACE_OR_CLASS_SKILL  = 2 +    SKILL_LINE_ABILITY_LEARNED_ON_SKILL_VALUE  = 1, // Spell state will update depending on skill value +    SKILL_LINE_ABILITY_LEARNED_ON_SKILL_LEARN  = 2  // Spell will be learned/removed together with entire skill  };  enum ItemEnchantmentType @@ -356,7 +356,7 @@ enum SkillRaceClassInfoFlags      SKILL_FLAG_UNLEARNABLE              = 0x20,     // Skill can be unlearned      SKILL_FLAG_INCLUDE_IN_SORT          = 0x80,     // Spells belonging to a skill with this flag will additionally compare skill ids when sorting spellbook in client      SKILL_FLAG_NOT_TRAINABLE            = 0x100, -    SKILL_FLAG_MONO_VALUE               = 0x400     // Skill always has value 1 +    SKILL_FLAG_MONO_VALUE               = 0x400     // Skill always has value 1 - clientside display flag, real value can be different  };  enum SpellCategoryFlags diff --git a/src/server/game/DataStores/DBCStores.cpp b/src/server/game/DataStores/DBCStores.cpp index 92d00b20645..e960422cbbc 100644 --- a/src/server/game/DataStores/DBCStores.cpp +++ b/src/server/game/DataStores/DBCStores.cpp @@ -450,7 +450,7 @@ void LoadDBCStores(const std::string& dataPath)                  if (spellInfo->spellLevel)                      continue; -                if (skillLine->learnOnGetSkill != ABILITY_LEARNED_ON_GET_RACE_OR_CLASS_SKILL) +                if (skillLine->AutolearnType != SKILL_LINE_ABILITY_LEARNED_ON_SKILL_LEARN)                      continue;                  sPetFamilySpellsStore[i].insert(spellInfo->Id); diff --git a/src/server/game/DataStores/DBCStructure.h b/src/server/game/DataStores/DBCStructure.h index 2da166fb049..946cbc42f25 100644 --- a/src/server/game/DataStores/DBCStructure.h +++ b/src/server/game/DataStores/DBCStructure.h @@ -1535,23 +1535,6 @@ struct ScalingStatValuesEntry  //    uint32    displayOrder;                               // 19     m_sortIndex  //}; -//struct SkillRaceClassInfoEntry{ -//    uint32    id;                                         // 0      m_ID -//    uint32    skillId;                                    // 1      m_skillID -//    uint32    raceMask;                                   // 2      m_raceMask -//    uint32    classMask;                                  // 3      m_classMask -//    uint32    flags;                                      // 4      m_flags -//    uint32    reqLevel;                                   // 5      m_minLevel -//    uint32    skillTierId;                                // 6      m_skillTierID -//    uint32    skillCostID;                                // 7      m_skillCostIndex -//}; - -//struct SkillTiersEntry{ -//    uint32    id;                                         // 0      m_ID -//    uint32    skillValue[16];                             // 1-17   m_cost -//    uint32    maxSkillValue[16];                          // 18-32  m_valueMax -//}; -  struct SkillLineEntry  {      uint32    id;                                           // 0        m_ID @@ -1578,7 +1561,7 @@ struct SkillLineAbilityEntry      //uint32    classmaskNot;                               // 6        m_excludeClass      uint32    req_skill_value;                              // 7        m_minSkillLineRank      uint32    forward_spellid;                              // 8        m_supercededBySpell -    uint32    learnOnGetSkill;                              // 9        m_acquireMethod +    uint32    AutolearnType;                                // 9        m_acquireMethod      uint32    max_value;                                    // 10       m_trivialSkillLineRankHigh      uint32    min_value;                                    // 11       m_trivialSkillLineRankLow      //uint32    characterPoints[2];                         // 12-13    m_characterPoints[2] diff --git a/src/server/game/Entities/GameObject/GameObject.cpp b/src/server/game/Entities/GameObject/GameObject.cpp index ae08a4251a5..23e40382fa2 100644 --- a/src/server/game/Entities/GameObject/GameObject.cpp +++ b/src/server/game/Entities/GameObject/GameObject.cpp @@ -958,6 +958,17 @@ void GameObject::SaveRespawnTime()          GetMap()->SaveGORespawnTime(m_DBTableGuid, m_respawnTime);  } +bool GameObject::IsNeverVisible() const +{ +    if (WorldObject::IsNeverVisible()) +        return true; + +    if (GetGoType() == GAMEOBJECT_TYPE_SPELL_FOCUS && GetGOInfo()->spellFocus.serverOnly == 1) +        return true; + +    return false; +} +  bool GameObject::IsAlwaysVisibleFor(WorldObject const* seer) const  {      if (WorldObject::IsAlwaysVisibleFor(seer)) diff --git a/src/server/game/Entities/GameObject/GameObject.h b/src/server/game/Entities/GameObject/GameObject.h index 8f70fc0e907..549de28bb12 100644 --- a/src/server/game/Entities/GameObject/GameObject.h +++ b/src/server/game/Entities/GameObject/GameObject.h @@ -778,6 +778,8 @@ class GameObject : public WorldObject, public GridObject<GameObject>, public Map          void TriggeringLinkedGameObject(uint32 trapEntry, Unit* target); +        bool IsNeverVisible() const override; +          bool IsAlwaysVisibleFor(WorldObject const* seer) const;          bool IsInvisibleDueToDespawn() const; diff --git a/src/server/game/Entities/Object/Object.cpp b/src/server/game/Entities/Object/Object.cpp index 4ff0153dea8..08d984e0790 100644 --- a/src/server/game/Entities/Object/Object.cpp +++ b/src/server/game/Entities/Object/Object.cpp @@ -2603,13 +2603,39 @@ void WorldObject::MovePosition(Position &pos, float dist, float angle)      pos.SetOrientation(GetOrientation());  } +// @todo: replace with WorldObject::UpdateAllowedPositionZ +float NormalizeZforCollision(WorldObject* obj, float x, float y, float z) +{ +    float ground = obj->GetMap()->GetHeight(obj->GetPhaseMask(), x, y, MAX_HEIGHT, true); +    float floor = obj->GetMap()->GetHeight(obj->GetPhaseMask(), x, y, z + 2.0f, true); +    float helper = fabs(ground - z) <= fabs(floor - z) ? ground : floor; +    if (z > helper) // must be above ground +    { +        if (Unit* unit = obj->ToUnit()) +        { +            if (unit->CanFly()) +                return z; +        } +        LiquidData liquid_status; +        ZLiquidStatus res = obj->GetMap()->getLiquidStatus(x, y, z, MAP_ALL_LIQUIDS, &liquid_status); +        if (res && liquid_status.level > helper) // water must be above ground +        { +            if (liquid_status.level > z) // z is underwater +                return z; +            else +                return fabs(liquid_status.level - z) <= fabs(helper - z) ? liquid_status.level : helper; +        } +    } +    return helper; +} +  void WorldObject::MovePositionToFirstCollision(Position &pos, float dist, float angle)  {      angle += GetOrientation(); -    float destx, desty, destz, ground, floor; -    pos.m_positionZ += 2.0f; +    float destx, desty, destz;      destx = pos.m_positionX + dist * std::cos(angle);      desty = pos.m_positionY + dist * std::sin(angle); +    destz = NormalizeZforCollision(this, destx, desty, pos.GetPositionZ());      // Prevent invalid coordinates here, position is unchanged      if (!Trinity::IsValidMapCoord(destx, desty)) @@ -2618,11 +2644,7 @@ void WorldObject::MovePositionToFirstCollision(Position &pos, float dist, float          return;      } -    ground = GetMap()->GetHeight(GetPhaseMask(), destx, desty, MAX_HEIGHT, true); -    floor = GetMap()->GetHeight(GetPhaseMask(), destx, desty, pos.m_positionZ, true); -    destz = fabs(ground - pos.m_positionZ) <= fabs(floor - pos.m_positionZ) ? ground : floor; - -    bool col = VMAP::VMapFactory::createOrGetVMapManager()->getObjectHitPos(GetMapId(), pos.m_positionX, pos.m_positionY, pos.m_positionZ+0.5f, destx, desty, destz+0.5f, destx, desty, destz, -0.5f); +    bool col = VMAP::VMapFactory::createOrGetVMapManager()->getObjectHitPos(GetMapId(), pos.m_positionX, pos.m_positionY, pos.m_positionZ + 0.5f, destx, desty, destz + 0.5f, destx, desty, destz, -0.5f);      // collision occured      if (col) @@ -2634,7 +2656,7 @@ void WorldObject::MovePositionToFirstCollision(Position &pos, float dist, float      }      // check dynamic collision -    col = GetMap()->getObjectHitPos(GetPhaseMask(), pos.m_positionX, pos.m_positionY, pos.m_positionZ+0.5f, destx, desty, destz+0.5f, destx, desty, destz, -0.5f); +    col = GetMap()->getObjectHitPos(GetPhaseMask(), pos.m_positionX, pos.m_positionY, pos.m_positionZ + 0.5f, destx, desty, destz + 0.5f, destx, desty, destz, -0.5f);      // Collided with a gameobject      if (col) @@ -2644,18 +2666,16 @@ void WorldObject::MovePositionToFirstCollision(Position &pos, float dist, float          dist = sqrt((pos.m_positionX - destx)*(pos.m_positionX - destx) + (pos.m_positionY - desty)*(pos.m_positionY - desty));      } -    float step = dist/10.0f; +    float step = dist / 10.0f;      for (uint8 j = 0; j < 10; ++j)      {          // do not allow too big z changes -        if (fabs(pos.m_positionZ - destz) > 6) +        if (fabs(pos.m_positionZ - destz) > 6.0f)          {              destx -= step * std::cos(angle);              desty -= step * std::sin(angle); -            ground = GetMap()->GetHeight(GetPhaseMask(), destx, desty, MAX_HEIGHT, true); -            floor = GetMap()->GetHeight(GetPhaseMask(), destx, desty, pos.m_positionZ, true); -            destz = fabs(ground - pos.m_positionZ) <= fabs(floor - pos.m_positionZ) ? ground : floor; +            destz = NormalizeZforCollision(this, destx, desty, pos.GetPositionZ());          }          // we have correct destz now          else @@ -2667,7 +2687,7 @@ void WorldObject::MovePositionToFirstCollision(Position &pos, float dist, float      Trinity::NormalizeMapCoord(pos.m_positionX);      Trinity::NormalizeMapCoord(pos.m_positionY); -    UpdateAllowedPositionZ(pos.m_positionX, pos.m_positionY, pos.m_positionZ); +    pos.m_positionZ = NormalizeZforCollision(this, destx, desty, pos.GetPositionZ());      pos.SetOrientation(GetOrientation());  } diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index 8653106a865..5c96e607355 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -1134,7 +1134,8 @@ bool Player::Create(uint32 guidlow, CharacterCreateInfo* createInfo)      }      // original spells -    learnDefaultSpells(); +    LearnDefaultSkills(); +    LearnCustomSpells();      // original action bar      for (PlayerCreateInfoActions::const_iterator action_itr = info->action.begin(); action_itr != info->action.end(); ++action_itr) @@ -3827,34 +3828,14 @@ bool Player::addSpell(uint32 spellId, bool active, bool learning, bool dependent              if (!pSkill)                  continue; -            if (!Has310Flyer(false) && pSkill->id == SKILL_MOUNTS) +            if (_spell_idx->second->AutolearnType == SKILL_LINE_ABILITY_LEARNED_ON_SKILL_LEARN && !HasSkill(pSkill->id)) +                LearnDefaultSkill(pSkill->id, 0); + +            if (pSkill->id == SKILL_MOUNTS && !Has310Flyer(false))                  for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i)                      if (spellInfo->Effects[i].ApplyAuraName == SPELL_AURA_MOD_INCREASE_MOUNTED_FLIGHT_SPEED &&                          spellInfo->Effects[i].CalcValue() == 310)                          SetHas310Flyer(true); - -            if (HasSkill(pSkill->id)) -                continue; - -            if (_spell_idx->second->learnOnGetSkill == ABILITY_LEARNED_ON_GET_RACE_OR_CLASS_SKILL || -                // lockpicking/runeforging special case, not have ABILITY_LEARNED_ON_GET_RACE_OR_CLASS_SKILL -                ((pSkill->id == SKILL_LOCKPICKING || pSkill->id == SKILL_RUNEFORGING) && _spell_idx->second->max_value == 0)) -            { -                switch (GetSkillRangeType(pSkill, _spell_idx->second->racemask != 0)) -                { -                    case SKILL_RANGE_LANGUAGE: -                        SetSkill(pSkill->id, GetSkillStep(pSkill->id), 300, 300); -                        break; -                    case SKILL_RANGE_LEVEL: -                        SetSkill(pSkill->id, GetSkillStep(pSkill->id), 1, GetMaxSkillValueForLevel()); -                        break; -                    case SKILL_RANGE_MONO: -                        SetSkill(pSkill->id, GetSkillStep(pSkill->id), 1, 1); -                        break; -                    default: -                        break; -                } -            }          }      } @@ -4079,33 +4060,27 @@ void Player::removeSpell(uint32 spell_id, bool disabled, bool learn_low_rank)          // not ranked skills          SkillLineAbilityMapBounds bounds = sSpellMgr->GetSkillLineAbilityMapBounds(spell_id); -        for (SkillLineAbilityMap::const_iterator _spell_idx = bounds.first; _spell_idx != bounds.second; ++_spell_idx) +        // most likely will never be used, haven't heard of cases where players unlearn a mount +        if (Has310Flyer(false) && spellInfo)          { -            SkillLineEntry const* pSkill = sSkillLineStore.LookupEntry(_spell_idx->second->skillId); -            if (!pSkill) -                continue; - -            if ((_spell_idx->second->learnOnGetSkill == ABILITY_LEARNED_ON_GET_RACE_OR_CLASS_SKILL && -                pSkill->categoryId != SKILL_CATEGORY_CLASS) ||// not unlearn class skills (spellbook/talent pages) -                // lockpicking/runeforging special case, not have ABILITY_LEARNED_ON_GET_RACE_OR_CLASS_SKILL -                ((pSkill->id == SKILL_LOCKPICKING || pSkill->id == SKILL_RUNEFORGING) && _spell_idx->second->max_value == 0)) +            for (SkillLineAbilityMap::const_iterator _spell_idx = bounds.first; _spell_idx != bounds.second; ++_spell_idx)              { -                // not reset skills for professions and racial abilities -                if ((pSkill->categoryId == SKILL_CATEGORY_SECONDARY || pSkill->categoryId == SKILL_CATEGORY_PROFESSION) && -                    (IsProfessionSkill(pSkill->id) || _spell_idx->second->racemask != 0)) +                SkillLineEntry const* pSkill = sSkillLineStore.LookupEntry(_spell_idx->second->skillId); +                if (!pSkill)                      continue; -                SetSkill(pSkill->id, GetSkillStep(pSkill->id), 0, 0); -            } - -            // most likely will never be used, haven't heard of cases where players unlearn a mount -            if (Has310Flyer(false) && _spell_idx->second->skillId == SKILL_MOUNTS) -            { -                if (spellInfo) +                if (_spell_idx->second->skillId == SKILL_MOUNTS) +                {                      for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i) +                    {                          if (spellInfo->Effects[i].ApplyAuraName == SPELL_AURA_MOD_INCREASE_MOUNTED_FLIGHT_SPEED &&                              spellInfo->Effects[i].CalcValue() == 310) +                        {                              Has310Flyer(true, spell_id);    // with true as first argument its also used to set/remove the flag +                            break; +                        } +                    } +                }              }          }      } @@ -6080,9 +6055,6 @@ bool Player::UpdateSkill(uint32 skill_id, uint32 step)      if (!skill_id)          return false; -    if (skill_id == SKILL_FIST_WEAPONS) -        skill_id = SKILL_UNARMED; -      SkillStatusMap::iterator itr = mSkillStatus.find(skill_id);      if (itr == mSkillStatus.end() || itr->second.uState == SKILL_DELETED)          return false; @@ -6097,13 +6069,14 @@ bool Player::UpdateSkill(uint32 skill_id, uint32 step)      if (value < max)      { -        uint32 new_value = value+step; +        uint32 new_value = value + step;          if (new_value > max)              new_value = max;          SetUInt32Value(valueIndex, MAKE_SKILL_VALUE(new_value, max));          if (itr->second.uState != SKILL_NEW)              itr->second.uState = SKILL_CHANGED; +          UpdateSkillEnchantments(skill_id, value, new_value);          UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_REACH_SKILL_LEVEL, skill_id);          return true; @@ -6277,9 +6250,25 @@ void Player::UpdateWeaponSkill(WeaponAttackType attType)      Item* tmpitem = GetWeaponForAttack(attType, true);      if (!tmpitem && attType == BASE_ATTACK) +    { +        // Keep unarmed & fist weapon skills in sync          UpdateSkill(SKILL_UNARMED, weapon_skill_gain); +        UpdateSkill(SKILL_FIST_WEAPONS, weapon_skill_gain); +    }      else if (tmpitem && tmpitem->GetTemplate()->SubClass != ITEM_SUBCLASS_WEAPON_FISHING_POLE) -        UpdateSkill(tmpitem->GetSkill(), weapon_skill_gain); +    { +        switch (tmpitem->GetTemplate()->SubClass) +        { +            case ITEM_SUBCLASS_WEAPON_FISHING_POLE: +                break; +            case ITEM_SUBCLASS_WEAPON_FIST: +                UpdateSkill(SKILL_UNARMED, weapon_skill_gain); +                // no break intended +            default: +                UpdateSkill(tmpitem->GetSkill(), weapon_skill_gain); +                break; +        } +    }      UpdateAllCritPercentages();  } @@ -6352,11 +6341,11 @@ void Player::UpdateSkillsForLevel()              continue;          uint32 pskill = itr->first; -        SkillLineEntry const* pSkill = sSkillLineStore.LookupEntry(pskill); -        if (!pSkill) +        SkillRaceClassInfoEntry const* rcEntry = GetSkillRaceClassInfo(pskill, getRace(), getClass()); +        if (!rcEntry)              continue; -        if (GetSkillRangeType(pSkill, false) != SKILL_RANGE_LEVEL) +        if (GetSkillRangeType(rcEntry) != SKILL_RANGE_LEVEL)              continue;          uint32 valueIndex = PLAYER_SKILL_VALUE_INDEX(itr->second.pos); @@ -17704,7 +17693,8 @@ bool Player::LoadFromDB(uint32 guid, SQLQueryHolder *holder)      // after spell and quest load      InitTalentForLevel(); -    learnDefaultSpells(); +    LearnDefaultSkills(); +    LearnCustomSpells();      // must be before inventory (some items required reputation check)      m_reputationMgr->LoadFromDB(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_REPUTATION)); @@ -22071,6 +22061,27 @@ void Player::AddSpellCooldown(uint32 spellid, uint32 itemid, time_t end_time)      m_spellCooldowns[spellid] = sc;  } +void Player::ModifySpellCooldown(uint32 spellId, int32 cooldown) +{ +    SpellCooldowns::iterator itr = m_spellCooldowns.find(spellId); +    if (itr == m_spellCooldowns.end()) +        return; + +    time_t now = time(NULL); +    if (itr->second.end + (cooldown / IN_MILLISECONDS) > now) +        itr->second.end += (cooldown / IN_MILLISECONDS); +    else +        m_spellCooldowns.erase(itr); + +    WorldPacket data(SMSG_MODIFY_COOLDOWN, 4 + 8 + 4); +    data << uint32(spellId);            // Spell ID +    data << uint64(GetGUID());          // Player GUID +    data << int32(cooldown);            // Cooldown mod in milliseconds +    GetSession()->SendPacket(&data); + +    TC_LOG_DEBUG("misc", "ModifySpellCooldown:: Player: %s (GUID: %u) Spell: %u cooldown: %u", GetName().c_str(), GetGUIDLow(), spellId, GetSpellCooldownDelay(spellId)); +} +  void Player::SendCooldownEvent(SpellInfo const* spellInfo, uint32 itemId /*= 0*/, Spell* spell /*= NULL*/, bool setCooldown /*= true*/)  {      // start cooldowns at server side, if any @@ -23078,15 +23089,19 @@ void Player::resetSpells(bool myClassOnly)          for (PlayerSpellMap::const_iterator iter = smap.begin(); iter != smap.end(); ++iter)              removeSpell(iter->first, false, false);           // only iter->first can be accessed, object by iter->second can be deleted already -    learnDefaultSpells(); +    LearnDefaultSkills(); +    LearnCustomSpells();      learnQuestRewardedSpells();  } -void Player::learnDefaultSpells() +void Player::LearnCustomSpells()  { +    if (!sWorld->getBoolConfig(CONFIG_START_ALL_SPELLS)) +        return; +      // learn default race/class spells      PlayerInfo const* info = sObjectMgr->GetPlayerInfo(getRace(), getClass()); -    for (PlayerCreateInfoSpells::const_iterator itr = info->spell.begin(); itr != info->spell.end(); ++itr) +    for (PlayerCreateInfoSpells::const_iterator itr = info->customSpells.begin(); itr != info->customSpells.end(); ++itr)      {          uint32 tspell = *itr;          TC_LOG_DEBUG("entities.player.loading", "PLAYER (Class: %u Race: %u): Adding initial spell, id = %u", uint32(getClass()), uint32(getRace()), tspell); @@ -23097,6 +23112,68 @@ void Player::learnDefaultSpells()      }  } +void Player::LearnDefaultSkills() +{ +    // learn default race/class skills +    PlayerInfo const* info = sObjectMgr->GetPlayerInfo(getRace(), getClass()); +    for (PlayerCreateInfoSkills::const_iterator itr = info->skills.begin(); itr != info->skills.end(); ++itr) +    { +        uint32 skillId = itr->SkillId; +        if (HasSkill(skillId)) +            continue; + +        LearnDefaultSkill(skillId, itr->Rank); +    } +} + +void Player::LearnDefaultSkill(uint32 skillId, uint16 rank) +{ +    SkillRaceClassInfoEntry const* rcInfo = GetSkillRaceClassInfo(skillId, getRace(), getClass()); +    if (!rcInfo) +        return; + +    TC_LOG_DEBUG("entities.player.loading", "PLAYER (Class: %u Race: %u): Adding initial skill, id = %u", uint32(getClass()), uint32(getRace()), skillId); +    switch (GetSkillRangeType(rcInfo)) +    { +        case SKILL_RANGE_LANGUAGE: +            SetSkill(skillId, 0, 300, 300); +            break; +        case SKILL_RANGE_LEVEL: +        { +            uint16 skillValue = 1; +            uint16 maxValue = GetMaxSkillValueForLevel(); +            if (rcInfo->Flags & SKILL_FLAG_ALWAYS_MAX_VALUE) +                skillValue = maxValue; +            else if (getClass() == CLASS_DEATH_KNIGHT) +                skillValue = std::min(std::max<uint16>({ 1, uint16((getLevel() - 1) * 5) }), maxValue); + +            SetSkill(skillId, 0, skillValue, maxValue); +            break; +        } +        case SKILL_RANGE_MONO: +            SetSkill(skillId, 0, 1, 1); +            break; +        case SKILL_RANGE_RANK: +        { +            if (!rank) +                break; + +            SkillTiersEntry const* tier = sSkillTiersStore.LookupEntry(rcInfo->SkillTier); +            uint16 maxValue = tier->MaxSkill[std::max<int32>(rank - 1, 0)]; +            uint16 skillValue = 1; +            if (rcInfo->Flags & SKILL_FLAG_ALWAYS_MAX_VALUE) +                skillValue = maxValue; +            else if (getClass() == CLASS_DEATH_KNIGHT) +                skillValue = std::min(std::max<uint16>({ uint16(1), uint16((getLevel() - 1) * 5) }), maxValue); + +            SetSkill(skillId, rank, skillValue, maxValue); +            break; +        } +        default: +            break; +    } +} +  void Player::learnQuestRewardedSpells(Quest const* quest)  {      int32 spell_id = quest->GetRewSpellCast(); @@ -23194,29 +23271,35 @@ void Player::learnSkillRewardedSpells(uint32 skill_id, uint32 skill_value)  {      uint32 raceMask  = getRaceMask();      uint32 classMask = getClassMask(); -    for (uint32 j=0; j<sSkillLineAbilityStore.GetNumRows(); ++j) +    for (uint32 j = 0; j < sSkillLineAbilityStore.GetNumRows(); ++j)      {          SkillLineAbilityEntry const* pAbility = sSkillLineAbilityStore.LookupEntry(j); -        if (!pAbility || pAbility->skillId != skill_id || pAbility->learnOnGetSkill != ABILITY_LEARNED_ON_GET_PROFESSION_SKILL) +        if (!pAbility || pAbility->skillId != skill_id)              continue; + +        SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(pAbility->spellId); +        if (!spellInfo) +            continue; + +        if (pAbility->AutolearnType != SKILL_LINE_ABILITY_LEARNED_ON_SKILL_VALUE && pAbility->AutolearnType != SKILL_LINE_ABILITY_LEARNED_ON_SKILL_LEARN) +            continue; +          // Check race if set          if (pAbility->racemask && !(pAbility->racemask & raceMask))              continue; +          // Check class if set          if (pAbility->classmask && !(pAbility->classmask & classMask))              continue; -        if (sSpellMgr->GetSpellInfo(pAbility->spellId)) -        { -            // need unlearn spell -            if (skill_value < pAbility->req_skill_value) -                removeSpell(pAbility->spellId); -            // need learn -            else if (!IsInWorld()) -                addSpell(pAbility->spellId, true, true, true, false); -            else -                learnSpell(pAbility->spellId, true); -        } +        // need unlearn spell +        if (skill_value < pAbility->req_skill_value && pAbility->AutolearnType == SKILL_LINE_ABILITY_LEARNED_ON_SKILL_VALUE) +            removeSpell(pAbility->spellId); +        // need learn +        else if (!IsInWorld()) +            addSpell(pAbility->spellId, true, true, true, false); +        else +            learnSpell(pAbility->spellId, true);      }  } @@ -23944,7 +24027,7 @@ bool Player::IsAtRecruitAFriendDistance(WorldObject const* pOther) const      return pOther->GetDistance(player) <= sWorld->getFloatConfig(CONFIG_MAX_RECRUIT_A_FRIEND_DISTANCE);  } -uint32 Player::GetBaseWeaponSkillValue (WeaponAttackType attType) const +uint32 Player::GetBaseWeaponSkillValue(WeaponAttackType attType) const  {      Item* item = GetWeaponForAttack(attType, true); @@ -23952,8 +24035,8 @@ uint32 Player::GetBaseWeaponSkillValue (WeaponAttackType attType) const      if (attType != BASE_ATTACK && !item)          return 0; -    // weapon skill or (unarmed for base attack and for fist weapons) -    uint32  skill = (item && item->GetSkill() != SKILL_FIST_WEAPONS) ? item->GetSkill() : uint32(SKILL_UNARMED); +    // weapon skill or (unarmed for base attack) +    uint32  skill = item ? item->GetSkill() : uint32(SKILL_UNARMED);      return GetBaseSkillValue(skill);  } @@ -24907,15 +24990,15 @@ void Player::_LoadSkills(PreparedQueryResult result)              uint16 value    = fields[1].GetUInt16();              uint16 max      = fields[2].GetUInt16(); -            SkillLineEntry const* pSkill = sSkillLineStore.LookupEntry(skill); -            if (!pSkill) +            SkillRaceClassInfoEntry const* rcEntry = GetSkillRaceClassInfo(skill, getRace(), getClass()); +            if (!rcEntry)              {                  TC_LOG_ERROR("entities.player", "Character %u has skill %u that does not exist.", GetGUIDLow(), skill);                  continue;              }              // set fixed skill ranges -            switch (GetSkillRangeType(pSkill, false)) +            switch (GetSkillRangeType(rcEntry))              {                  case SKILL_RANGE_LANGUAGE:                      // 300..300                      value = max = 300; @@ -24923,9 +25006,12 @@ void Player::_LoadSkills(PreparedQueryResult result)                  case SKILL_RANGE_MONO:                          // 1..1, grey monolite bar                      value = max = 1;                      break; +                case SKILL_RANGE_LEVEL: +                    max = GetMaxSkillValueForLevel();                  default:                      break;              } +              if (value == 0)              {                  TC_LOG_ERROR("entities.player", "Character %u has skill %u with value 0. Will be deleted.", GetGUIDLow(), skill); @@ -24940,11 +25026,20 @@ void Player::_LoadSkills(PreparedQueryResult result)                  continue;              } -            // enable unlearn button for primary professions only -            if (pSkill->categoryId == SKILL_CATEGORY_PROFESSION) -                SetUInt32Value(PLAYER_SKILL_INDEX(count), MAKE_PAIR32(skill, 1)); -            else -                SetUInt32Value(PLAYER_SKILL_INDEX(count), MAKE_PAIR32(skill, 0)); +            uint16 skillStep = 0; +            if (SkillTiersEntry const* skillTier = sSkillTiersStore.LookupEntry(rcEntry->SkillTier)) +            { +                for (uint32 i = 0; i < MAX_SKILL_STEP; ++i) +                { +                    if (skillTier->MaxSkill[skillStep] == max) +                    { +                        skillStep = i + 1; +                        break; +                    } +                } +            } + +            SetUInt32Value(PLAYER_SKILL_INDEX(count), MAKE_PAIR32(skill, skillStep));              SetUInt32Value(PLAYER_SKILL_VALUE_INDEX(count), MAKE_SKILL_VALUE(value, max));              SetUInt32Value(PLAYER_SKILL_BONUS_INDEX(count), 0); @@ -24970,34 +25065,6 @@ void Player::_LoadSkills(PreparedQueryResult result)          SetUInt32Value(PLAYER_SKILL_VALUE_INDEX(count), 0);          SetUInt32Value(PLAYER_SKILL_BONUS_INDEX(count), 0);      } - -    // special settings -    if (getClass() == CLASS_DEATH_KNIGHT) -    { -        uint8 base_level = std::min(getLevel(), uint8(sWorld->getIntConfig(CONFIG_START_HEROIC_PLAYER_LEVEL))); -        if (base_level < 1) -            base_level = 1; -        uint16 base_skill = (base_level-1)*5;               // 270 at starting level 55 -        if (base_skill < 1) -            base_skill = 1;                                 // skill mast be known and then > 0 in any case - -        if (GetPureSkillValue(SKILL_FIRST_AID) < base_skill) -            SetSkill(SKILL_FIRST_AID, 4 /*artisan*/, base_skill, 300); -        if (GetPureSkillValue(SKILL_AXES) < base_skill) -            SetSkill(SKILL_AXES, 0, base_skill, base_skill); -        if (GetPureSkillValue(SKILL_DEFENSE) < base_skill) -            SetSkill(SKILL_DEFENSE, 0, base_skill, base_skill); -        if (GetPureSkillValue(SKILL_POLEARMS) < base_skill) -            SetSkill(SKILL_POLEARMS, 0, base_skill, base_skill); -        if (GetPureSkillValue(SKILL_SWORDS) < base_skill) -            SetSkill(SKILL_SWORDS, 0, base_skill, base_skill); -        if (GetPureSkillValue(SKILL_2H_AXES) < base_skill) -            SetSkill(SKILL_2H_AXES, 0, base_skill, base_skill); -        if (GetPureSkillValue(SKILL_2H_SWORDS) < base_skill) -            SetSkill(SKILL_2H_SWORDS, 0, base_skill, base_skill); -        if (GetPureSkillValue(SKILL_UNARMED) < base_skill) -            SetSkill(SKILL_UNARMED, 0, base_skill, base_skill); -    }  }  uint32 Player::GetPhaseMaskForSpawn() const diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h index fdebbde0ae2..a146b5d672a 100644 --- a/src/server/game/Entities/Player/Player.h +++ b/src/server/game/Entities/Player/Player.h @@ -249,6 +249,14 @@ struct PlayerCreateInfoAction  typedef std::list<PlayerCreateInfoAction> PlayerCreateInfoActions; +struct PlayerCreateInfoSkill +{ +    uint16 SkillId; +    uint16 Rank; +}; + +typedef std::list<PlayerCreateInfoSkill> PlayerCreateInfoSkills; +  struct PlayerInfo  {                                                              // existence checked by displayId != 0 @@ -263,8 +271,9 @@ struct PlayerInfo      uint16 displayId_m;      uint16 displayId_f;      PlayerCreateInfoItems item; -    PlayerCreateInfoSpells spell; +    PlayerCreateInfoSpells customSpells;      PlayerCreateInfoActions action; +    PlayerCreateInfoSkills skills;      PlayerLevelInfo* levelInfo;                             //[level-1] 0..MaxPlayerLevel-1  }; @@ -1573,7 +1582,9 @@ class Player : public Unit, public GridObject<Player>          void learnSpell(uint32 spell_id, bool dependent);          void removeSpell(uint32 spell_id, bool disabled = false, bool learn_low_rank = true);          void resetSpells(bool myClassOnly = false); -        void learnDefaultSpells(); +        void LearnCustomSpells(); +        void LearnDefaultSkills(); +        void LearnDefaultSkill(uint32 skillId, uint16 rank);          void learnQuestRewardedSpells();          void learnQuestRewardedSpells(Quest const* quest);          void learnSpellHighRank(uint32 spellid); @@ -1636,6 +1647,7 @@ class Player : public Unit, public GridObject<Player>          uint32 GetSpellCooldownDelay(uint32 spell_id) const;          void AddSpellAndCategoryCooldowns(SpellInfo const* spellInfo, uint32 itemId, Spell* spell = NULL, bool infinityCooldown = false);          void AddSpellCooldown(uint32 spell_id, uint32 itemid, time_t end_time); +        void ModifySpellCooldown(uint32 spellId, int32 cooldown);          void SendCooldownEvent(SpellInfo const* spellInfo, uint32 itemId = 0, Spell* spell = NULL, bool setCooldown = true);          void ProhibitSpellSchool(SpellSchoolMask idSchoolMask, uint32 unTimeMs);          void RemoveSpellCooldown(uint32 spell_id, bool update = false); diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp index 5eb48160e8a..d6479550ad4 100644 --- a/src/server/game/Entities/Unit/Unit.cpp +++ b/src/server/game/Entities/Unit/Unit.cpp @@ -2834,12 +2834,10 @@ uint32 Unit::GetWeaponSkillValue (WeaponAttackType attType, Unit const* target)          if (IsInFeralForm())              return GetMaxSkillValueForLevel();              // always maximized SKILL_FERAL_COMBAT in fact -        // weapon skill or (unarmed for base attack and fist weapons) -        uint32 skill; -        if (item && item->GetSkill() != SKILL_FIST_WEAPONS) +        // weapon skill or (unarmed for base attack) +        uint32 skill = SKILL_UNARMED; +        if (item)              skill = item->GetSkill(); -        else -            skill = SKILL_UNARMED;          // in PvP use full skill instead current skill value          value = (target && target->IsControlledByPlayer()) @@ -6891,31 +6889,6 @@ bool Unit::HandleDummyAuraProc(Unit* victim, uint32 damage, AuraEffect* triggere                      }                      break;                  } -                // Item - Shaman T10 Elemental 2P Bonus -                case 70811: -                { -                    // Lightning Bolt & Chain Lightning -                    if (procSpell->SpellFamilyFlags[0] & 0x3) -                    { -                        if (ToPlayer()->HasSpellCooldown(16166)) -                        { -                            uint32 newCooldownDelay = ToPlayer()->GetSpellCooldownDelay(16166); -                            if (newCooldownDelay < 3) -                                newCooldownDelay = 0; -                            else -                                newCooldownDelay -= 2; -                            ToPlayer()->AddSpellCooldown(16166, 0, uint32(time(NULL) + newCooldownDelay)); - -                            WorldPacket data(SMSG_MODIFY_COOLDOWN, 4+8+4); -                            data << uint32(16166);                  // Spell ID -                            data << uint64(GetGUID());              // Player GUID -                            data << int32(-2000);                   // Cooldown mod in milliseconds -                            ToPlayer()->GetSession()->SendPacket(&data); -                            return true; -                        } -                    } -                    return false; -                }                  // Item - Shaman T10 Elemental 4P Bonus                  case 70817:                  { @@ -7714,7 +7687,8 @@ bool Unit::HandleProcTriggerSpell(Unit* victim, uint32 damage, AuraEffect* trigg          ? ToPlayer()->GetItemByGuid(triggeredByAura->GetBase()->GetCastItemGUID()) : NULL;      // Try handle unknown trigger spells -    if (sSpellMgr->GetSpellInfo(trigger_spell_id) == NULL) +    // triggered spells exists only in serverside spell_dbc +    /// @todo: reverify and move these spells to spellscripts      {          switch (auraSpellInfo->SpellFamilyName)          { @@ -11157,6 +11131,9 @@ bool Unit::IsImmunedToSpellEffect(SpellInfo const* spellInfo, uint32 index) cons      if (!spellInfo || !spellInfo->Effects[index].IsEffect())          return false; +    if (spellInfo->Attributes & SPELL_ATTR0_UNAFFECTED_BY_INVULNERABILITY) +        return false; +              // If m_immuneToEffect type contain this effect type, IMMUNE effect.      uint32 effect = spellInfo->Effects[index].Effect;      SpellImmuneList const& effectList = m_spellImmune[IMMUNITY_EFFECT]; @@ -15288,7 +15265,7 @@ void Unit::Kill(Unit* victim, bool durabilityLoss)                      group->SendLooter(creature, NULL);                  // Update round robin looter only if the creature had loot -                if (!creature->loot.empty()) +                if (!loot->empty())                      group->UpdateLooterGuid(creature);              }          } diff --git a/src/server/game/Globals/ObjectMgr.cpp b/src/server/game/Globals/ObjectMgr.cpp index 9eb7d5ec1bf..278460e73dd 100644 --- a/src/server/game/Globals/ObjectMgr.cpp +++ b/src/server/game/Globals/ObjectMgr.cpp @@ -3322,17 +3322,91 @@ void ObjectMgr::LoadPlayerInfo()          }      } + +    // Load playercreate skills +    TC_LOG_INFO("server.loading", "Loading Player Create Skill Data..."); +    { +        uint32 oldMSTime = getMSTime(); + +        QueryResult result = WorldDatabase.PQuery("SELECT raceMask, classMask, skill, rank FROM playercreateinfo_skills"); + +        if (!result) +        { +            TC_LOG_ERROR("server.loading", ">> Loaded 0 player create skills. DB table `playercreateinfo_skills` is empty."); +        } +        else +        { +            uint32 count = 0; + +            do +            { +                Field* fields = result->Fetch(); +                uint32 raceMask = fields[0].GetUInt32(); +                uint32 classMask = fields[1].GetUInt32(); +                PlayerCreateInfoSkill skill; +                skill.SkillId = fields[2].GetUInt16(); +                skill.Rank = fields[3].GetUInt16(); + +                if (skill.Rank >= MAX_SKILL_STEP) +                { +                    TC_LOG_ERROR("sql.sql", "Skill rank value %hu set for skill %hu raceMask %u classMask %u is too high, max allowed value is %d", skill.Rank, skill.SkillId, raceMask, classMask, MAX_SKILL_STEP); +                    continue; +                } + +                if (raceMask != 0 && !(raceMask & RACEMASK_ALL_PLAYABLE)) +                { +                    TC_LOG_ERROR("sql.sql", "Wrong race mask %u in `playercreateinfo_skills` table, ignoring.", raceMask); +                    continue; +                } + +                if (classMask != 0 && !(classMask & CLASSMASK_ALL_PLAYABLE)) +                { +                    TC_LOG_ERROR("sql.sql", "Wrong class mask %u in `playercreateinfo_skills` table, ignoring.", classMask); +                    continue; +                } + +                if (!sSkillLineStore.LookupEntry(skill.SkillId)) +                { +                    TC_LOG_ERROR("sql.sql", "Wrong skill id %u in `playercreateinfo_skills` table, ignoring.", skill.SkillId); +                    continue; +                } + +                for (uint32 raceIndex = RACE_HUMAN; raceIndex < MAX_RACES; ++raceIndex) +                { +                    if (raceMask == 0 || ((1 << (raceIndex - 1)) & raceMask)) +                    { +                        for (uint32 classIndex = CLASS_WARRIOR; classIndex < MAX_CLASSES; ++classIndex) +                        { +                            if (classMask == 0 || ((1 << (classIndex - 1)) & classMask)) +                            { +                                if (!GetSkillRaceClassInfo(skill.SkillId, raceIndex, classIndex)) +                                    continue; + +                                if (PlayerInfo* info = _playerInfo[raceIndex][classIndex]) +                                { +                                    info->skills.push_back(skill); +                                    ++count; +                                } +                            } +                        } +                    } +                } +            } while (result->NextRow()); + +            TC_LOG_INFO("server.loading", ">> Loaded %u player create skills in %u ms", count, GetMSTimeDiffToNow(oldMSTime)); +        } +    } +      // Load playercreate spells      TC_LOG_INFO("server.loading", "Loading Player Create Spell Data...");      {          uint32 oldMSTime = getMSTime(); -        std::string tableName = sWorld->getBoolConfig(CONFIG_START_ALL_SPELLS) ? "playercreateinfo_spell_custom" : "playercreateinfo_spell"; -        QueryResult result = WorldDatabase.PQuery("SELECT racemask, classmask, Spell FROM %s", tableName.c_str()); +        QueryResult result = WorldDatabase.PQuery("SELECT racemask, classmask, Spell FROM playercreateinfo_spell_custom");          if (!result)          { -            TC_LOG_ERROR("server.loading", ">> Loaded 0 player create spells. DB table `%s` is empty.", tableName.c_str()); +            TC_LOG_ERROR("server.loading", ">> Loaded 0 player create spells. DB table `playercreateinfo_spell_custom` is empty.");          }          else          { @@ -3347,13 +3421,13 @@ void ObjectMgr::LoadPlayerInfo()                  if (raceMask != 0 && !(raceMask & RACEMASK_ALL_PLAYABLE))                  { -                    TC_LOG_ERROR("sql.sql", "Wrong race mask %u in `%s` table, ignoring.", raceMask, tableName.c_str()); +                    TC_LOG_ERROR("sql.sql", "Wrong race mask %u in `playercreateinfo_spell_custom` table, ignoring.", raceMask);                      continue;                  }                  if (classMask != 0 && !(classMask & CLASSMASK_ALL_PLAYABLE))                  { -                    TC_LOG_ERROR("sql.sql", "Wrong class mask %u in `%s` table, ignoring.", classMask, tableName.c_str()); +                    TC_LOG_ERROR("sql.sql", "Wrong class mask %u in `playercreateinfo_spell_custom` table, ignoring.", classMask);                      continue;                  } @@ -3367,7 +3441,7 @@ void ObjectMgr::LoadPlayerInfo()                              {                                  if (PlayerInfo* info = _playerInfo[raceIndex][classIndex])                                  { -                                    info->spell.push_back(spellId); +                                    info->customSpells.push_back(spellId);                                      ++count;                                  }                                  // We need something better here, the check is not accounting for spells used by multiple races/classes but not all of them. @@ -3381,7 +3455,7 @@ void ObjectMgr::LoadPlayerInfo()              }              while (result->NextRow()); -            TC_LOG_INFO("server.loading", ">> Loaded %u player create spells in %u ms", count, GetMSTimeDiffToNow(oldMSTime)); +            TC_LOG_INFO("server.loading", ">> Loaded %u custom player create spells in %u ms", count, GetMSTimeDiffToNow(oldMSTime));          }      } @@ -7808,36 +7882,27 @@ int32 ObjectMgr::GetBaseReputationOf(FactionEntry const* factionEntry, uint8 rac      return 0;  } -SkillRangeType GetSkillRangeType(SkillLineEntry const* pSkill, bool racial) +SkillRangeType GetSkillRangeType(SkillRaceClassInfoEntry const* rcEntry)  { -    switch (pSkill->categoryId) +    SkillLineEntry const* skill = sSkillLineStore.LookupEntry(rcEntry->SkillId); +    if (!skill) +        return SKILL_RANGE_NONE; + +    if (sSkillTiersStore.LookupEntry(rcEntry->SkillTier)) +        return SKILL_RANGE_RANK; + +    if (rcEntry->SkillId == SKILL_RUNEFORGING) +        return SKILL_RANGE_MONO; + +    switch (skill->categoryId)      { -        case SKILL_CATEGORY_LANGUAGES: return SKILL_RANGE_LANGUAGE; -        case SKILL_CATEGORY_WEAPON: -            if (pSkill->id != SKILL_FIST_WEAPONS) -                return SKILL_RANGE_LEVEL; -            else -                return SKILL_RANGE_MONO;          case SKILL_CATEGORY_ARMOR: -        case SKILL_CATEGORY_CLASS: -            if (pSkill->id != SKILL_LOCKPICKING) -                return SKILL_RANGE_MONO; -            else -                return SKILL_RANGE_LEVEL; -        case SKILL_CATEGORY_SECONDARY: -        case SKILL_CATEGORY_PROFESSION: -            // not set skills for professions and racial abilities -            if (IsProfessionSkill(pSkill->id)) -                return SKILL_RANGE_RANK; -            else if (racial) -                return SKILL_RANGE_NONE; -            else -                return SKILL_RANGE_MONO; -        default: -        case SKILL_CATEGORY_ATTRIBUTES:                     //not found in dbc -        case SKILL_CATEGORY_GENERIC:                        //only GENERIC(DND) -            return SKILL_RANGE_NONE; +            return SKILL_RANGE_MONO; +        case SKILL_CATEGORY_LANGUAGES: +            return SKILL_RANGE_LANGUAGE;      } + +    return SKILL_RANGE_LEVEL;  }  void ObjectMgr::LoadGameTele() diff --git a/src/server/game/Globals/ObjectMgr.h b/src/server/game/Globals/ObjectMgr.h index e5e55b847d3..3f8013bbd78 100644 --- a/src/server/game/Globals/ObjectMgr.h +++ b/src/server/game/Globals/ObjectMgr.h @@ -643,7 +643,7 @@ enum SkillRangeType      SKILL_RANGE_NONE                                        // 0..0 always  }; -SkillRangeType GetSkillRangeType(SkillLineEntry const* pSkill, bool racial); +SkillRangeType GetSkillRangeType(SkillRaceClassInfoEntry const* rcEntry);  #define MAX_PLAYER_NAME          12                         // max allowed by client name length  #define MAX_INTERNAL_PLAYER_NAME 15                         // max server internal player name length (> MAX_PLAYER_NAME for support declined names) diff --git a/src/server/game/Maps/Map.cpp b/src/server/game/Maps/Map.cpp index 01e3af149dc..a49e69ec311 100644 --- a/src/server/game/Maps/Map.cpp +++ b/src/server/game/Maps/Map.cpp @@ -119,6 +119,9 @@ bool Map::ExistVMap(uint32 mapid, int gx, int gy)  void Map::LoadMMap(int gx, int gy)  { +    if (!MMAP::MMapFactory::IsPathfindingEnabled(GetId())) +        return; +      bool mmapLoadResult = MMAP::MMapFactory::createOrGetMMapManager()->loadMap((sWorld->GetDataPath() + "mmaps").c_str(), GetId(), gx, gy);      if (mmapLoadResult) @@ -129,6 +132,8 @@ void Map::LoadMMap(int gx, int gy)  void Map::LoadVMap(int gx, int gy)  { +    if (!VMAP::VMapFactory::createOrGetVMapManager()->isMapLoadingEnabled()) +        return;                                                              // x and y are swapped !!      int vmapLoadResult = VMAP::VMapFactory::createOrGetVMapManager()->loadMap((sWorld->GetDataPath()+ "vmaps").c_str(),  GetId(), gx, gy);      switch (vmapLoadResult) diff --git a/src/server/game/Movement/PathGenerator.cpp b/src/server/game/Movement/PathGenerator.cpp index d6912bac7c8..3140fc23296 100644 --- a/src/server/game/Movement/PathGenerator.cpp +++ b/src/server/game/Movement/PathGenerator.cpp @@ -882,3 +882,41 @@ float PathGenerator::Dist3DSqr(G3D::Vector3 const& p1, G3D::Vector3 const& p2) c  {      return (p1 - p2).squaredLength();  } + +void PathGenerator::ReducePathLenghtByDist(float dist) +{ +    if (GetPathType() == PATHFIND_BLANK) +    { +        TC_LOG_ERROR("maps", "PathGenerator::ReducePathLenghtByDist called before path was built"); +        return; +    } + +    if (_pathPoints.size() < 2) // path building failure +        return; + +    uint32 i = _pathPoints.size(); +    G3D::Vector3 nextVec = _pathPoints[--i]; +    while (i > 0) +    { +        G3D::Vector3 currVec = _pathPoints[--i]; +        G3D::Vector3 diffVec = (nextVec - currVec); +        float len = diffVec.length(); +        if (len > dist) +        { +            float step = dist / len; +            // same as nextVec +            _pathPoints[i + 1] -= diffVec * step; +            _pathPoints.resize(i + 2); +            break; +        } +        else if (i == 0) // at second point +        { +            _pathPoints[1] = _pathPoints[0]; +            _pathPoints.resize(2); +            break; +        } + +        dist -= len; +        nextVec = currVec; // we're going backwards +    } +} diff --git a/src/server/game/Movement/PathGenerator.h b/src/server/game/Movement/PathGenerator.h index 6e0d72ec8da..a9a13c37251 100644 --- a/src/server/game/Movement/PathGenerator.h +++ b/src/server/game/Movement/PathGenerator.h @@ -72,6 +72,8 @@ class PathGenerator          PathType GetPathType() const { return _type; } +        void ReducePathLenghtByDist(float dist); // path must be already built +      private:          dtPolyRef _pathPolyRefs[MAX_PATH_LENGTH];   // array of detour polygon references diff --git a/src/server/game/Spells/Spell.cpp b/src/server/game/Spells/Spell.cpp index 9086a304e1c..d223425a027 100644 --- a/src/server/game/Spells/Spell.cpp +++ b/src/server/game/Spells/Spell.cpp @@ -1305,6 +1305,7 @@ void Spell::SelectImplicitCasterDestTargets(SpellEffIndex effIndex, SpellImplici  void Spell::SelectImplicitTargetDestTargets(SpellEffIndex effIndex, SpellImplicitTargetInfo const& targetType)  { +    ASSERT(m_targets.GetObjectTarget() && "Spell::SelectImplicitTargetDestTargets - no explicit object target available!");      WorldObject* target = m_targets.GetObjectTarget();      SpellDestination dest(*target); @@ -2265,7 +2266,7 @@ void Spell::DoAllEffectOnTarget(TargetInfo* target)      m_spellAura = NULL; // Set aura to null for every target-make sure that pointer is not used for unit without aura applied                              //Spells with this flag cannot trigger if effect is cast on self -    bool canEffectTrigger = !(m_spellInfo->AttributesEx3 & SPELL_ATTR3_CANT_TRIGGER_PROC) && unitTarget->CanProc() && CanExecuteTriggersOnHit(mask); +    bool canEffectTrigger = !(m_spellInfo->AttributesEx3 & SPELL_ATTR3_CANT_TRIGGER_PROC) && unitTarget->CanProc() && (CanExecuteTriggersOnHit(mask) || missInfo == SPELL_MISS_IMMUNE || missInfo == SPELL_MISS_IMMUNE2);      Unit* spellHitTarget = NULL;      if (missInfo == SPELL_MISS_NONE)                          // In case spell hit target, do all effect on that target @@ -2311,15 +2312,8 @@ void Spell::DoAllEffectOnTarget(TargetInfo* target)          if (m_damage > 0)              positive = false;          else if (!m_healing) -        { -            for (uint8 i = 0; i< MAX_SPELL_EFFECTS; ++i) -                // If at least one effect negative spell is negative hit -                if (mask & (1<<i) && !m_spellInfo->IsPositiveEffect(i)) -                { -                    positive = false; -                    break; -                } -        } +            positive = m_spellInfo->IsPositive(); +          switch (m_spellInfo->DmgClass)          {              case SPELL_DAMAGE_CLASS_MAGIC: @@ -2397,7 +2391,6 @@ void Spell::DoAllEffectOnTarget(TargetInfo* target)                  caster->ToPlayer()->CastItemCombatSpell(unitTarget, m_attackType, procVictim, procEx);          } -          m_damage = damageInfo.damage;          caster->DealSpellDamage(&damageInfo, true); @@ -2538,9 +2531,14 @@ SpellMissInfo Spell::DoSpellHitOnUnit(Unit* unit, uint32 effectMask, bool scaleA          }      } +    uint8 aura_effmask = 0; +    for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i) +        if (effectMask & (1 << i) && m_spellInfo->Effects[i].IsUnitOwnedAuraEffect()) +            aura_effmask |= 1 << i; +      // Get Data Needed for Diminishing Returns, some effects may have multiple auras, so this must be done on spell hit, not aura add      m_diminishGroup = GetDiminishingReturnsGroupForSpell(m_spellInfo, m_triggeredByAuraSpell); -    if (m_diminishGroup) +    if (m_diminishGroup && aura_effmask)      {          m_diminishLevel = unit->GetDiminishing(m_diminishGroup);          DiminishingReturnsType type = GetDiminishingReturnsGroupType(m_diminishGroup); @@ -2551,11 +2549,6 @@ SpellMissInfo Spell::DoSpellHitOnUnit(Unit* unit, uint32 effectMask, bool scaleA              unit->IncrDiminishing(m_diminishGroup);      } -    uint8 aura_effmask = 0; -    for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i) -        if (effectMask & (1 << i) && m_spellInfo->Effects[i].IsUnitOwnedAuraEffect()) -            aura_effmask |= 1 << i; -      if (aura_effmask)      {          // Select rank for aura with level requirements only in specific cases @@ -5051,15 +5044,24 @@ SpellCastResult Spell::CheckCast(bool strict)                      if (!target)                          return SPELL_FAILED_DONT_REPORT; -                    //target->GetContactPoint(m_caster, pos.m_positionX, pos.m_positionY, pos.m_positionZ); -                    Position pos = target->GetFirstCollisionPosition(CONTACT_DISTANCE, target->GetRelativeAngle(m_caster)); +                    float objSize = target->GetObjectSize(); +                    float range = m_spellInfo->GetMaxRange(true, m_caster, this) * 1.5f + objSize; // can't be overly strict -                    m_preGeneratedPath.SetPathLengthLimit(m_spellInfo->GetMaxRange(true) * 1.5f); -                    bool result = m_preGeneratedPath.CalculatePath(pos.m_positionX, pos.m_positionY, pos.m_positionZ + target->GetObjectSize(), false, true); +                    m_preGeneratedPath.SetPathLengthLimit(range); +                    // first try with raycast, if it fails fall back to normal path +                    bool result = m_preGeneratedPath.CalculatePath(target->GetPositionX(), target->GetPositionY(), target->GetPositionZ() + target->GetObjectSize(), false, true);                      if (m_preGeneratedPath.GetPathType() & PATHFIND_SHORT)                          return SPELL_FAILED_OUT_OF_RANGE;                      else if (!result || m_preGeneratedPath.GetPathType() & PATHFIND_NOPATH) -                        return SPELL_FAILED_NOPATH; +                    { +                        result = m_preGeneratedPath.CalculatePath(target->GetPositionX(), target->GetPositionY(), target->GetPositionZ() + target->GetObjectSize(), false, false); +                        if (m_preGeneratedPath.GetPathType() & PATHFIND_SHORT) +                            return SPELL_FAILED_OUT_OF_RANGE; +                        else if (!result || m_preGeneratedPath.GetPathType() & PATHFIND_NOPATH) +                            return SPELL_FAILED_NOPATH; +                    } + +                    m_preGeneratedPath.ReducePathLenghtByDist(objSize); // move back                  }                  break;              } diff --git a/src/server/game/Spells/SpellEffects.cpp b/src/server/game/Spells/SpellEffects.cpp index 606851bf8f1..c3ca10905fd 100644 --- a/src/server/game/Spells/SpellEffects.cpp +++ b/src/server/game/Spells/SpellEffects.cpp @@ -654,14 +654,6 @@ void Spell::EffectSchoolDMG(SpellEffIndex effIndex)                  }                  break;              } -            case SPELLFAMILY_MAGE: -            { -                // Deep Freeze should deal damage to permanently stun-immune targets. -                if (m_spellInfo->Id == 71757) -                    if (unitTarget->GetTypeId() != TYPEID_UNIT || !(unitTarget->IsImmunedToSpellEffect(sSpellMgr->GetSpellInfo(44572), 0))) -                        return; -                break; -            }          }          if (m_originalCaster && damage > 0 && apply_direct_bonus) @@ -874,7 +866,10 @@ void Spell::EffectTriggerSpell(SpellEffIndex effIndex)          if (spellInfo->GetExplicitTargetMask() & TARGET_FLAG_DEST_LOCATION)              targets.SetDst(m_targets); -        targets.SetUnitTarget(m_caster); +        if (Unit* target = m_targets.GetUnitTarget()) +            targets.SetUnitTarget(target); +        else +            targets.SetUnitTarget(m_caster);      }      CustomSpellValues values; @@ -4594,7 +4589,7 @@ void Spell::EffectLeap(SpellEffIndex /*effIndex*/)          return;      Position pos = destTarget->GetPosition(); -    pos = unitTarget->GetFirstCollisionPosition(unitTarget->GetDistance(pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ() + 2.0f), 0.0f); +    pos = unitTarget->GetFirstCollisionPosition(unitTarget->GetDistance(pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ()), 0.0f);      unitTarget->NearTeleportTo(pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ(), pos.GetOrientation(), unitTarget == m_caster);  } diff --git a/src/server/game/Spells/SpellInfo.cpp b/src/server/game/Spells/SpellInfo.cpp index 4d97dc97e5b..8034002a470 100644 --- a/src/server/game/Spells/SpellInfo.cpp +++ b/src/server/game/Spells/SpellInfo.cpp @@ -967,7 +967,7 @@ bool SpellInfo::IsAbilityLearnedWithProfession() const      for (SkillLineAbilityMap::const_iterator _spell_idx = bounds.first; _spell_idx != bounds.second; ++_spell_idx)      {          SkillLineAbilityEntry const* pAbility = _spell_idx->second; -        if (!pAbility || pAbility->learnOnGetSkill != ABILITY_LEARNED_ON_GET_PROFESSION_SKILL) +        if (!pAbility || pAbility->AutolearnType != SKILL_LINE_ABILITY_LEARNED_ON_SKILL_VALUE)              continue;          if (pAbility->req_skill_value > 0) diff --git a/src/server/game/Spells/SpellMgr.cpp b/src/server/game/Spells/SpellMgr.cpp index 6f56c0ebc40..77450dc859a 100644 --- a/src/server/game/Spells/SpellMgr.cpp +++ b/src/server/game/Spells/SpellMgr.cpp @@ -2382,7 +2382,7 @@ void SpellMgr::LoadPetLevelupSpellMap()                  if (skillLine->skillId != creatureFamily->skillLine[j])                      continue; -                if (skillLine->learnOnGetSkill != ABILITY_LEARNED_ON_GET_RACE_OR_CLASS_SKILL) +                if (skillLine->AutolearnType != SKILL_LINE_ABILITY_LEARNED_ON_SKILL_LEARN)                      continue;                  SpellInfo const* spell = GetSpellInfo(skillLine->spellId); diff --git a/src/server/scripts/Commands/cs_learn.cpp b/src/server/scripts/Commands/cs_learn.cpp index 12721c61936..03d10149ae5 100644 --- a/src/server/scripts/Commands/cs_learn.cpp +++ b/src/server/scripts/Commands/cs_learn.cpp @@ -332,7 +332,8 @@ public:          if (!handler->extractPlayerTarget((char*)args, &target))              return false; -        target->learnDefaultSpells(); +        target->LearnDefaultSkills(); +        target->LearnCustomSpells();          target->learnQuestRewardedSpells();          handler->PSendSysMessage(LANG_COMMAND_LEARN_ALL_DEFAULT_AND_QUEST, handler->GetNameLink(target).c_str()); diff --git a/src/server/scripts/EasternKingdoms/MagistersTerrace/instance_magisters_terrace.cpp b/src/server/scripts/EasternKingdoms/MagistersTerrace/instance_magisters_terrace.cpp index e0050420a08..1f8ccfbd5d8 100644 --- a/src/server/scripts/EasternKingdoms/MagistersTerrace/instance_magisters_terrace.cpp +++ b/src/server/scripts/EasternKingdoms/MagistersTerrace/instance_magisters_terrace.cpp @@ -159,7 +159,7 @@ class instance_magisters_terrace : public InstanceMapScript                  switch (type)                  {                      case DATA_DELRISSA: -                        if (type == IN_PROGRESS) +                        if (state == IN_PROGRESS)                              DelrissaDeathCount = 0;                          break;                      default: diff --git a/src/server/scripts/EasternKingdoms/ScarletMonastery/boss_arcanist_doan.cpp b/src/server/scripts/EasternKingdoms/ScarletMonastery/boss_arcanist_doan.cpp index 787bf7584e5..f18c0aac8bb 100644 --- a/src/server/scripts/EasternKingdoms/ScarletMonastery/boss_arcanist_doan.cpp +++ b/src/server/scripts/EasternKingdoms/ScarletMonastery/boss_arcanist_doan.cpp @@ -1,6 +1,5 @@  /*   * Copyright (C) 2008-2014 TrinityCore <http://www.trinitycore.org/> - * Copyright (C) 2006-2009 ScriptDev2 <https://scriptdev2.svn.sourceforge.net/>   *   * This program is free software; you can redistribute it and/or modify it   * under the terms of the GNU General Public License as published by the @@ -16,15 +15,9 @@   * with this program. If not, see <http://www.gnu.org/licenses/>.   */ -/* ScriptData -SDName: Boss_Arcanist_Doan -SD%Complete: 100 -SDComment: -SDCategory: Scarlet Monastery -EndScriptData */ -  #include "ScriptMgr.h"  #include "ScriptedCreature.h" +#include "scarlet_monastery.h"  enum Yells  { @@ -34,103 +27,100 @@ enum Yells  enum Spells  { -    SPELL_POLYMORPH             = 13323, -    SPELL_AOESILENCE            = 8988, -    SPELL_ARCANEEXPLOSION       = 9433, -    SPELL_FIREAOE               = 9435, -    SPELL_ARCANEBUBBLE          = 9438 +    SPELL_SILENCE               = 8988, +    SPELL_ARCANE_EXPLOSION      = 9433, +    SPELL_DETONATION            = 9435, +    SPELL_ARCANE_BUBBLE         = 9438, +    SPELL_POLYMORPH             = 13323  }; -class boss_arcanist_doan : public CreatureScript +enum Events  { -public: -    boss_arcanist_doan() : CreatureScript("boss_arcanist_doan") { } - -    CreatureAI* GetAI(Creature* creature) const override -    { -        return new boss_arcanist_doanAI(creature); -    } - -    struct boss_arcanist_doanAI : public ScriptedAI -    { -        boss_arcanist_doanAI(Creature* creature) : ScriptedAI(creature) { } - -        uint32 Polymorph_Timer; -        uint32 AoESilence_Timer; -        uint32 ArcaneExplosion_Timer; -        bool bCanDetonate; -        bool bShielded; - -        void Reset() override -        { -            Polymorph_Timer = 20000; -            AoESilence_Timer = 15000; -            ArcaneExplosion_Timer = 3000; -            bCanDetonate = false; -            bShielded = false; -        } +    EVENT_SILENCE               = 1, +    EVENT_ARCANE_EXPLOSION      = 2, +    EVENT_ARCANE_BUBBLE         = 3, +    EVENT_POLYMORPH             = 4 +}; -        void EnterCombat(Unit* /*who*/) override -        { -            Talk(SAY_AGGRO); -        } +class boss_arcanist_doan : public CreatureScript +{ +    public: +        boss_arcanist_doan() : CreatureScript("boss_arcanist_doan") { } -        void UpdateAI(uint32 diff) override +        struct boss_arcanist_doanAI : public BossAI          { -            if (!UpdateVictim()) -                return; - -            if (bShielded && bCanDetonate) +            boss_arcanist_doanAI(Creature* creature) : BossAI(creature, DATA_ARCANIST_DOAN)              { -                DoCast(me, SPELL_FIREAOE); -                bCanDetonate = false; +                _healthAbove50Pct = true;              } -            if (me->HasAura(SPELL_ARCANEBUBBLE)) -                return; - -            //If we are <50% hp cast Arcane Bubble -            if (!bShielded && !HealthAbovePct(50)) +            void Reset() override              { -                //wait if we already casting -                if (me->IsNonMeleeSpellCast(false)) -                    return; - -                Talk(SAY_SPECIALAE); -                DoCast(me, SPELL_ARCANEBUBBLE); - -                bCanDetonate = true; -                bShielded = true; +                _Reset(); +                _healthAbove50Pct = true;              } -            if (Polymorph_Timer <= diff) +            void EnterCombat(Unit* /*who*/) override              { -                if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 1)) -                    DoCast(target, SPELL_POLYMORPH); +                _EnterCombat(); +                Talk(SAY_AGGRO); -                Polymorph_Timer = 20000; +                events.ScheduleEvent(EVENT_SILENCE,         15 * IN_MILLISECONDS); +                events.ScheduleEvent(EVENT_ARCANE_EXPLOSION, 3 * IN_MILLISECONDS); +                events.ScheduleEvent(EVENT_POLYMORPH,       30 * IN_MILLISECONDS);              } -            else Polymorph_Timer -= diff; -            //AoESilence_Timer -            if (AoESilence_Timer <= diff) +            void UpdateAI(uint32 diff) override              { -                DoCastVictim(SPELL_AOESILENCE); -                AoESilence_Timer = urand(15000, 20000); -            } -            else AoESilence_Timer -= diff; +                if (!UpdateVictim()) +                    return; -            //ArcaneExplosion_Timer -            if (ArcaneExplosion_Timer <= diff) -            { -                DoCastVictim(SPELL_ARCANEEXPLOSION); -                ArcaneExplosion_Timer = 8000; +                events.Update(diff); + +                if (me->HasUnitState(UNIT_STATE_CASTING)) +                    return; + +                while (uint32 eventId = events.ExecuteEvent()) +                { +                    switch (eventId) +                    { +                        case EVENT_SILENCE: +                            DoCastVictim(SPELL_SILENCE); +                            events.ScheduleEvent(EVENT_SILENCE, urand(15, 20) * IN_MILLISECONDS); +                            break; +                        case EVENT_ARCANE_EXPLOSION: +                            DoCastVictim(SPELL_ARCANE_EXPLOSION); +                            events.ScheduleEvent(EVENT_SILENCE, 8 * IN_MILLISECONDS); +                            break; +                        case EVENT_POLYMORPH: +                            if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 1, 30.0f, true)) +                                DoCast(target, SPELL_POLYMORPH); +                            events.ScheduleEvent(EVENT_POLYMORPH, 20 * IN_MILLISECONDS); +                            break; +                        default: +                            break; +                    } +                } + +                if (_healthAbove50Pct && HealthBelowPct(50)) +                { +                    _healthAbove50Pct = false; +                    Talk(SAY_SPECIALAE); +                    DoCast(me, SPELL_ARCANE_BUBBLE); +                    DoCastAOE(SPELL_DETONATION); +                } + +                DoMeleeAttackIfReady();              } -            else ArcaneExplosion_Timer -= diff; -            DoMeleeAttackIfReady(); +        private: +            bool _healthAbove50Pct; +        }; + +        CreatureAI* GetAI(Creature* creature) const override +        { +            return new boss_arcanist_doanAI(creature);          } -    };  };  void AddSC_boss_arcanist_doan() diff --git a/src/server/scripts/EasternKingdoms/ScarletMonastery/scarlet_monastery.h b/src/server/scripts/EasternKingdoms/ScarletMonastery/scarlet_monastery.h index cd5a74ee478..bdac6b089fd 100644 --- a/src/server/scripts/EasternKingdoms/ScarletMonastery/scarlet_monastery.h +++ b/src/server/scripts/EasternKingdoms/ScarletMonastery/scarlet_monastery.h @@ -30,7 +30,8 @@ enum DataTypes      DATA_HORSEMAN_EVENT             = 5,      GAMEOBJECT_PUMPKIN_SHRINE       = 6, -    DATA_VORREL                     = 7 +    DATA_VORREL                     = 7, +    DATA_ARCANIST_DOAN              = 8  };  #endif // SCARLET_M_ diff --git a/src/server/scripts/Spells/spell_item.cpp b/src/server/scripts/Spells/spell_item.cpp index a17d7dce2ea..30f9e914037 100644 --- a/src/server/scripts/Spells/spell_item.cpp +++ b/src/server/scripts/Spells/spell_item.cpp @@ -383,6 +383,45 @@ class spell_item_echoes_of_light : public SpellScriptLoader          }  }; +// 7434 - Fate Rune of Unsurpassed Vigor +enum FateRuneOfUnsurpassedVigor +{ +    SPELL_UNSURPASSED_VIGOR = 25733 +}; + +class spell_item_fate_rune_of_unsurpassed_vigor : public SpellScriptLoader +{ +    public: +        spell_item_fate_rune_of_unsurpassed_vigor() : SpellScriptLoader("spell_item_fate_rune_of_unsurpassed_vigor") { } + +        class spell_item_fate_rune_of_unsurpassed_vigor_AuraScript : public AuraScript +        { +            PrepareAuraScript(spell_item_fate_rune_of_unsurpassed_vigor_AuraScript); + +            bool Validate(SpellInfo const* /*spellInfo*/) override +            { +                if (!sSpellMgr->GetSpellInfo(SPELL_UNSURPASSED_VIGOR)) +                    return false; +                return true; +            } + +            void HandleProc(AuraEffect const* /*aurEff*/, ProcEventInfo& /*eventInfo*/) +            { +                GetTarget()->CastSpell(GetTarget(), SPELL_UNSURPASSED_VIGOR, true); +            } + +            void Register() override +            { +                OnEffectProc += AuraEffectProcFn(spell_item_fate_rune_of_unsurpassed_vigor_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_DUMMY); +            } +        }; + +        AuraScript* GetAuraScript() const override +        { +            return new spell_item_fate_rune_of_unsurpassed_vigor_AuraScript(); +        } +}; +  // http://www.wowhead.com/item=47499 Flask of the North  // 67019 Flask of the North  enum FlaskOfTheNorthSpells @@ -2607,6 +2646,7 @@ void AddSC_item_spell_scripts()      new spell_item_desperate_defense();      new spell_item_deviate_fish();      new spell_item_echoes_of_light(); +    new spell_item_fate_rune_of_unsurpassed_vigor();      new spell_item_flask_of_the_north();      new spell_item_gnomish_death_ray();      new spell_item_make_a_wish(); diff --git a/src/server/scripts/Spells/spell_shaman.cpp b/src/server/scripts/Spells/spell_shaman.cpp index 61ff79c505e..a1c2d0e1d1c 100644 --- a/src/server/scripts/Spells/spell_shaman.cpp +++ b/src/server/scripts/Spells/spell_shaman.cpp @@ -34,6 +34,7 @@ enum ShamanSpells      SPELL_SHAMAN_BIND_SIGHT                     = 6277,      SPELL_SHAMAN_CLEANSING_TOTEM_EFFECT         = 52025,      SPELL_SHAMAN_EARTH_SHIELD_HEAL              = 379, +    SPELL_SHAMAN_ELEMENTAL_MASTERY              = 16166,      SPELL_SHAMAN_EXHAUSTION                     = 57723,      SPELL_SHAMAN_FIRE_NOVA_R1                   = 1535,      SPELL_SHAMAN_FIRE_NOVA_TRIGGERED_R1         = 8349, @@ -767,6 +768,42 @@ class spell_sha_item_mana_surge : public SpellScriptLoader          }  }; +// 70811 - Item - Shaman T10 Elemental 2P Bonus +class spell_sha_item_t10_elemental_2p_bonus : public SpellScriptLoader +{ +    public: +        spell_sha_item_t10_elemental_2p_bonus() : SpellScriptLoader("spell_sha_item_t10_elemental_2p_bonus") { } + +        class spell_sha_item_t10_elemental_2p_bonus_AuraScript : public AuraScript +        { +            PrepareAuraScript(spell_sha_item_t10_elemental_2p_bonus_AuraScript); + +            bool Validate(SpellInfo const* /*spellInfo*/) override +            { +                if (!sSpellMgr->GetSpellInfo(SPELL_SHAMAN_ELEMENTAL_MASTERY)) +                    return false; +                return true; +            } + +            void HandleEffectProc(AuraEffect const* aurEff, ProcEventInfo& /*eventInfo*/) +            { +                PreventDefaultAction(); +                if (Player* target = GetTarget()->ToPlayer()) +                    target->ModifySpellCooldown(SPELL_SHAMAN_ELEMENTAL_MASTERY, -aurEff->GetAmount()); +            } + +            void Register() override +            { +                OnEffectProc += AuraEffectProcFn(spell_sha_item_t10_elemental_2p_bonus_AuraScript::HandleEffectProc, EFFECT_0, SPELL_AURA_DUMMY); +            } +        }; + +        AuraScript* GetAuraScript() const override +        { +            return new spell_sha_item_t10_elemental_2p_bonus_AuraScript(); +        } +}; +  // 60103 - Lava Lash  class spell_sha_lava_lash : public SpellScriptLoader  { @@ -989,6 +1026,7 @@ void AddSC_shaman_spell_scripts()      new spell_sha_item_lightning_shield();      new spell_sha_item_lightning_shield_trigger();      new spell_sha_item_mana_surge(); +    new spell_sha_item_t10_elemental_2p_bonus();      new spell_sha_lava_lash();      new spell_sha_mana_spring_totem();      new spell_sha_mana_tide_totem(); diff --git a/src/tools/vmap4_extractor/model.cpp b/src/tools/vmap4_extractor/model.cpp index 825a2697c16..c527ec32ddb 100644 --- a/src/tools/vmap4_extractor/model.cpp +++ b/src/tools/vmap4_extractor/model.cpp @@ -96,8 +96,19 @@ bool Model::ConvertToVMAPModel(const char * outfilename)      wsize = sizeof(uint32) + sizeof(unsigned short) * nIndexes;      fwrite(&wsize, sizeof(int), 1, output);      fwrite(&nIndexes, sizeof(uint32), 1, output); -    if (nIndexes >0) +    if (nIndexes > 0) +    { +        for (uint32 i = 0; i < nIndexes; ++i) +        { +            if ((i % 3) - 1 == 0 && i + 1 < nIndexes) +            { +                uint16 tmp = indices[i]; +                indices[i] = indices[i + 1]; +                indices[i + 1] = tmp; +            } +        }          fwrite(indices, sizeof(unsigned short), nIndexes, output); +    }      fwrite("VERT", 4, 1, output);      wsize = sizeof(int) + sizeof(float) * 3 * nVertices; @@ -105,8 +116,12 @@ bool Model::ConvertToVMAPModel(const char * outfilename)      fwrite(&nVertices, sizeof(int), 1, output);      if (nVertices >0)      { -        for(uint32 vpos=0; vpos <nVertices; ++vpos) -            std::swap(vertices[vpos].y, vertices[vpos].z); +        for (uint32 vpos = 0; vpos < nVertices; ++vpos) +        { +            float tmp = vertices[vpos].y; +            vertices[vpos].y = -vertices[vpos].z; +            vertices[vpos].z = tmp; +        }          fwrite(vertices, sizeof(float)*3, nVertices, output);      }  | 
