diff options
Diffstat (limited to 'src')
40 files changed, 338 insertions, 390 deletions
diff --git a/src/server/collision/Models/GameObjectModel.cpp b/src/server/collision/Models/GameObjectModel.cpp index e166a860cd2..9db3d706fca 100644 --- a/src/server/collision/Models/GameObjectModel.cpp +++ b/src/server/collision/Models/GameObjectModel.cpp @@ -139,7 +139,7 @@ bool GameObjectModel::initialize(const GameObject& go, const GameObjectDisplayIn if (Creature* c = const_cast<GameObject&>(go).SummonCreature(24440, pos.x, pos.y, pos.z, 0, TEMPSUMMON_MANUAL_DESPAWN)) { c->setFaction(35); - c->SetFloatValue(OBJECT_FIELD_SCALE_X, 0.1f); + c->SetObjectScale(0.1f); } } #endif diff --git a/src/server/game/AuctionHouse/AuctionHouseMgr.cpp b/src/server/game/AuctionHouse/AuctionHouseMgr.cpp index 8f0eadbe88d..6186e616928 100644 --- a/src/server/game/AuctionHouse/AuctionHouseMgr.cpp +++ b/src/server/game/AuctionHouse/AuctionHouseMgr.cpp @@ -256,6 +256,15 @@ void AuctionHouseMgr::LoadAuctionItems() { uint32 oldMSTime = getMSTime(); + // need to clear in case we are reloading + if (!mAitems.empty()) + { + for (ItemMap::iterator itr = mAitems.begin(); itr != mAitems.end(); ++itr) + delete itr->second; + + mAitems.clear(); + } + // data needs to be at first place for Item::LoadFromDB PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_AUCTION_ITEMS); PreparedQueryResult result = CharacterDatabase.Query(stmt); diff --git a/src/server/game/AuctionHouse/AuctionHouseMgr.h b/src/server/game/AuctionHouse/AuctionHouseMgr.h index 7c870c20b65..5b5b172b2d4 100644 --- a/src/server/game/AuctionHouse/AuctionHouseMgr.h +++ b/src/server/game/AuctionHouse/AuctionHouseMgr.h @@ -42,14 +42,14 @@ enum AuctionError ERR_AUCTION_HIGHER_BID = 5, ERR_AUCTION_BID_INCREMENT = 7, ERR_AUCTION_BID_OWN = 10, - ERR_RESTRICTED_ACCOUNT = 13, + ERR_AUCTION_RESTRICTED_ACCOUNT = 13 }; enum AuctionAction { - AUCTION_SELL_ITEM = 0, - AUCTION_CANCEL = 1, - AUCTION_PLACE_BID = 2 + AUCTION_SELL_ITEM = 0, + AUCTION_CANCEL = 1, + AUCTION_PLACE_BID = 2 }; enum MailAuctionAnswers diff --git a/src/server/game/Conditions/ConditionMgr.cpp b/src/server/game/Conditions/ConditionMgr.cpp index a2c95c30cab..d6fb6905985 100644 --- a/src/server/game/Conditions/ConditionMgr.cpp +++ b/src/server/game/Conditions/ConditionMgr.cpp @@ -1674,7 +1674,8 @@ bool ConditionMgr::isConditionTypeValid(Condition* cond) { if (!sObjectMgr->GetQuestTemplate(cond->ConditionValue1)) { - TC_LOG_ERROR(LOG_FILTER_SQL, "Quest condition specifies non-existing quest (%u), skipped", cond->ConditionValue1); + TC_LOG_ERROR(LOG_FILTER_SQL, "Quest condition (Type: %u) points to non-existing quest (%u) for Source Entry %u. SourceGroup: %u, SourceTypeOrReferenceId: %u", + cond->ConditionType, cond->ConditionValue1, cond->SourceEntry, cond->SourceGroup, cond->SourceType); return false; } diff --git a/src/server/game/Entities/Creature/Creature.cpp b/src/server/game/Entities/Creature/Creature.cpp index 79beab4f7e6..f0b21a29bd2 100644 --- a/src/server/game/Entities/Creature/Creature.cpp +++ b/src/server/game/Entities/Creature/Creature.cpp @@ -330,9 +330,6 @@ bool Creature::InitEntry(uint32 entry, uint32 /*team*/, const CreatureData* data SetName(normalInfo->Name); // at normal entry always - SetFloatValue(UNIT_FIELD_BOUNDINGRADIUS, minfo->bounding_radius); - SetFloatValue(UNIT_FIELD_COMBATREACH, minfo->combat_reach); - SetFloatValue(UNIT_MOD_CAST_SPEED, 1.0f); SetFloatValue(UNIT_MOD_CAST_HASTE, 1.0f); @@ -341,6 +338,7 @@ bool Creature::InitEntry(uint32 entry, uint32 /*team*/, const CreatureData* data SetSpeed(MOVE_SWIM, 1.0f); // using 1.0 rate SetSpeed(MOVE_FLIGHT, 1.0f); // using 1.0 rate + // Will set UNIT_FIELD_BOUNDINGRADIUS and UNIT_FIELD_COMBATREACH SetObjectScale(cinfo->scale); SetFloatValue(UNIT_FIELD_HOVERHEIGHT, cinfo->HoverHeight); @@ -439,28 +437,7 @@ bool Creature::UpdateEntry(uint32 Entry, uint32 team, const CreatureData* data) ApplySpellImmune(0, IMMUNITY_EFFECT, SPELL_EFFECT_ATTACK_ME, true); } - // Set the movement flags if the creature is in that mode. (Only fly if actually in air, only swim if in water, etc) - float ground = GetPositionZ(); - GetMap()->GetWaterOrGroundLevel(GetPositionX(), GetPositionY(), GetPositionZ(), &ground); - - bool isInAir = G3D::fuzzyGt(GetPositionZ(), ground + 0.05f) || G3D::fuzzyLt(GetPositionZ(), ground - 0.05f); // Can be underground too, prevent the falling - - if (cInfo->InhabitType & INHABIT_AIR && cInfo->InhabitType & INHABIT_GROUND && isInAir) - SetCanFly(true); - else if (cInfo->InhabitType & INHABIT_AIR && isInAir) - SetDisableGravity(true); - else - { - SetCanFly(false); - SetDisableGravity(false); - } - - // TODO: Shouldn't we check whether or not the creature is in water first? - if (cInfo->InhabitType & INHABIT_WATER && IsInWater()) - AddUnitMovementFlag(MOVEMENTFLAG_SWIMMING); - else - RemoveUnitMovementFlag(MOVEMENTFLAG_SWIMMING); - + UpdateMovementFlags(); return true; } @@ -474,33 +451,7 @@ void Creature::Update(uint32 diff) m_vehicleKit->Reset(); } - if (IsInWater()) - { - if (canSwim()) - AddUnitMovementFlag(MOVEMENTFLAG_SWIMMING); - } - else - { - if (canWalk()) - RemoveUnitMovementFlag(MOVEMENTFLAG_SWIMMING); - } - - // Set the movement flags if the creature is in that mode. (Only fly if actually in air, only swim if in water, etc) - float ground = GetPositionZ(); - GetMap()->GetWaterOrGroundLevel(GetPositionX(), GetPositionY(), GetPositionZ(), &ground); - - bool isInAir = G3D::fuzzyGt(GetPositionZ(), ground + 0.05f) || G3D::fuzzyLt(GetPositionZ(), ground - 0.05f); // Can be underground too, prevent the falling - CreatureTemplate const* cinfo = GetCreatureTemplate(); - - if (cinfo->InhabitType & INHABIT_AIR && cinfo->InhabitType & INHABIT_GROUND && isInAir) - SetCanFly(true); - else if (cinfo->InhabitType & INHABIT_AIR && isInAir) - SetDisableGravity(true); - else - { - SetCanFly(false); - SetDisableGravity(false); - } + UpdateMovementFlags(); switch (m_deathState) { @@ -870,84 +821,6 @@ void Creature::InitializeReactState() SetReactState(REACT_DEFENSIVE);*/; } -bool Creature::isCanTrainingOf(Player* player, bool msg) const -{ - if (!isTrainer()) - return false; - - TrainerSpellData const* trainer_spells = GetTrainerSpells(); - - if ((!trainer_spells || trainer_spells->spellList.empty()) && GetCreatureTemplate()->trainer_type != TRAINER_TYPE_PETS) - { - TC_LOG_ERROR(LOG_FILTER_SQL, "Creature %u (Entry: %u) have UNIT_NPC_FLAG_TRAINER but have empty trainer spell list.", - GetGUIDLow(), GetEntry()); - return false; - } - - switch (GetCreatureTemplate()->trainer_type) - { - case TRAINER_TYPE_CLASS: - if (player->getClass() != GetCreatureTemplate()->trainer_class) - { - if (msg) - { - player->PlayerTalkClass->ClearMenus(); - switch (GetCreatureTemplate()->trainer_class) - { - case CLASS_DRUID: player->PlayerTalkClass->SendGossipMenu(4913, GetGUID()); break; - case CLASS_HUNTER: player->PlayerTalkClass->SendGossipMenu(10090, GetGUID()); break; - case CLASS_MAGE: player->PlayerTalkClass->SendGossipMenu(328, GetGUID()); break; - case CLASS_PALADIN:player->PlayerTalkClass->SendGossipMenu(1635, GetGUID()); break; - case CLASS_PRIEST: player->PlayerTalkClass->SendGossipMenu(4436, GetGUID()); break; - case CLASS_ROGUE: player->PlayerTalkClass->SendGossipMenu(4797, GetGUID()); break; - case CLASS_SHAMAN: player->PlayerTalkClass->SendGossipMenu(5003, GetGUID()); break; - case CLASS_WARLOCK:player->PlayerTalkClass->SendGossipMenu(5836, GetGUID()); break; - case CLASS_WARRIOR:player->PlayerTalkClass->SendGossipMenu(4985, GetGUID()); break; - } - } - return false; - } - break; - case TRAINER_TYPE_PETS: - if (player->getClass() != CLASS_HUNTER) - { - player->PlayerTalkClass->ClearMenus(); - player->PlayerTalkClass->SendGossipMenu(3620, GetGUID()); - return false; - } - break; - case TRAINER_TYPE_MOUNTS: - if (GetCreatureTemplate()->trainer_race && player->getRace() != GetCreatureTemplate()->trainer_race) - { - if (msg) - { - player->PlayerTalkClass->ClearMenus(); - switch (GetCreatureTemplate()->trainer_race) - { - case RACE_DWARF: player->PlayerTalkClass->SendGossipMenu(5865, GetGUID()); break; - case RACE_GNOME: player->PlayerTalkClass->SendGossipMenu(4881, GetGUID()); break; - case RACE_HUMAN: player->PlayerTalkClass->SendGossipMenu(5861, GetGUID()); break; - case RACE_NIGHTELF: player->PlayerTalkClass->SendGossipMenu(5862, GetGUID()); break; - case RACE_ORC: player->PlayerTalkClass->SendGossipMenu(5863, GetGUID()); break; - case RACE_TAUREN: player->PlayerTalkClass->SendGossipMenu(5864, GetGUID()); break; - case RACE_TROLL: player->PlayerTalkClass->SendGossipMenu(5816, GetGUID()); break; - case RACE_UNDEAD_PLAYER:player->PlayerTalkClass->SendGossipMenu(624, GetGUID()); break; - case RACE_BLOODELF: player->PlayerTalkClass->SendGossipMenu(5862, GetGUID()); break; - case RACE_DRAENEI: player->PlayerTalkClass->SendGossipMenu(5864, GetGUID()); break; - } - } - return false; - } - break; - case TRAINER_TYPE_TRADESKILLS: - // There's no Blacksmith specialization on Cataclysm, conditions are not required for tradeskills - break; - default: - return false; // checked and error output at creature_template loading - } - return true; -} - bool Creature::isCanInteractWithBattleMaster(Player* player, bool msg) const { if (!isBattleMaster()) @@ -1588,30 +1461,10 @@ void Creature::setDeathState(DeathState s) SetFullHealth(); SetLootRecipient(NULL); ResetPlayerDamageReq(); - CreatureTemplate const* cinfo = GetCreatureTemplate(); - SetWalk(true); - - // Set the movement flags if the creature is in that mode. (Only fly if actually in air, only swim if in water, etc) - float ground = GetPositionZ(); - GetMap()->GetWaterOrGroundLevel(GetPositionX(), GetPositionY(), GetPositionZ(), &ground); - bool isInAir = G3D::fuzzyGt(GetPositionZ(), ground + 0.05f) || G3D::fuzzyLt(GetPositionZ(), ground - 0.05f); // Can be underground too, prevent the falling - - if (cinfo->InhabitType & INHABIT_AIR && cinfo->InhabitType & INHABIT_GROUND && isInAir) - SetCanFly(true); - else if (cinfo->InhabitType & INHABIT_AIR && isInAir) - SetDisableGravity(true); - else - { - SetCanFly(false); - SetDisableGravity(false); - } - - if (cinfo->InhabitType & INHABIT_WATER && IsInWater()) - AddUnitMovementFlag(MOVEMENTFLAG_SWIMMING); - else - RemoveUnitMovementFlag(MOVEMENTFLAG_SWIMMING); + UpdateMovementFlags(); + CreatureTemplate const* cinfo = GetCreatureTemplate(); SetUInt32Value(UNIT_NPC_FLAGS, cinfo->npcflag); ClearUnitState(uint32(UNIT_STATE_ALL_STATE)); SetMeleeDamageSchool(SpellSchools(cinfo->dmgschool)); @@ -2675,3 +2528,51 @@ Unit* Creature::SelectNearestHostileUnitInAggroRange(bool useLOS) const return target; } + +void Creature::UpdateMovementFlags() +{ + // Set the movement flags if the creature is in that mode. (Only fly if actually in air, only swim if in water, etc) + float ground = GetMap()->GetHeight(GetPositionX(), GetPositionY(), GetPositionZMinusOffset()); + + bool isInAir = !IsFalling() && (G3D::fuzzyGt(GetPositionZMinusOffset(), ground + 0.05f) || G3D::fuzzyLt(GetPositionZMinusOffset(), ground - 0.05f)); // Can be underground too, prevent the falling + + if (GetCreatureTemplate()->InhabitType & INHABIT_AIR && isInAir) + { + if (GetCreatureTemplate()->InhabitType & INHABIT_GROUND) + SetCanFly(true); + else + SetDisableGravity(true); + } + else + { + SetCanFly(false); + SetDisableGravity(false); + } + + if (GetCreatureTemplate()->InhabitType & INHABIT_WATER && IsInWater()) + AddUnitMovementFlag(MOVEMENTFLAG_SWIMMING); + else + RemoveUnitMovementFlag(MOVEMENTFLAG_SWIMMING); +} + +void Creature::SetObjectScale(float scale) +{ + Unit::SetObjectScale(scale); + + if (CreatureModelInfo const* minfo = sObjectMgr->GetCreatureModelInfo(GetDisplayId())) + { + SetFloatValue(UNIT_FIELD_BOUNDINGRADIUS, minfo->bounding_radius * scale); + SetFloatValue(UNIT_FIELD_COMBATREACH, minfo->combat_reach * scale); + } +} + +void Creature::SetDisplayId(uint32 modelId) +{ + Unit::SetDisplayId(modelId); + + if (CreatureModelInfo const* minfo = sObjectMgr->GetCreatureModelInfo(modelId)) + { + SetFloatValue(UNIT_FIELD_BOUNDINGRADIUS, minfo->bounding_radius * GetFloatValue(OBJECT_FIELD_SCALE_X)); + SetFloatValue(UNIT_FIELD_COMBATREACH, minfo->combat_reach * GetFloatValue(OBJECT_FIELD_SCALE_X)); + } +} diff --git a/src/server/game/Entities/Creature/Creature.h b/src/server/game/Entities/Creature/Creature.h index 4d55b97732d..df4031f521f 100644 --- a/src/server/game/Entities/Creature/Creature.h +++ b/src/server/game/Entities/Creature/Creature.h @@ -453,6 +453,9 @@ class Creature : public Unit, public GridObject<Creature>, public MapCreature void AddToWorld(); void RemoveFromWorld(); + void SetObjectScale(float scale); + void SetDisplayId(uint32 modelId); + void DisappearAndDie(); bool Create(uint32 guidlow, Map* map, uint32 phaseMask, uint32 Entry, uint32 vehId, uint32 team, float x, float y, float z, float ang, const CreatureData* data = NULL); @@ -481,7 +484,6 @@ class Creature : public Unit, public GridObject<Creature>, public MapCreature void InitializeReactState(); /// @todo Rename these properly - bool isCanTrainingOf(Player* player, bool msg) const; bool isCanInteractWithBattleMaster(Player* player, bool msg) const; bool isCanTrainingAndResetTalentsOf(Player* player) const; bool canCreatureAttack(Unit const* victim, bool force = true) const; @@ -519,6 +521,9 @@ class Creature : public Unit, public GridObject<Creature>, public MapCreature bool HasSpell(uint32 spellID) const; bool UpdateEntry(uint32 entry, uint32 team=ALLIANCE, const CreatureData* data=NULL); + + void UpdateMovementFlags(); + bool UpdateStats(Stats stat); bool UpdateAllStats(); void UpdateResistances(uint32 school); diff --git a/src/server/game/Entities/Object/Object.cpp b/src/server/game/Entities/Object/Object.cpp index ed0ac21f6cb..96248f067de 100644 --- a/src/server/game/Entities/Object/Object.cpp +++ b/src/server/game/Entities/Object/Object.cpp @@ -761,12 +761,6 @@ void Object::_BuildValuesUpdate(uint8 updateType, ByteBuffer* data, UpdateMask* { if (!target->canSeeSpellClickOn(this->ToCreature())) appendValue &= ~UNIT_NPC_FLAG_SPELLCLICK; - - if (appendValue & UNIT_NPC_FLAG_TRAINER) - { - if (!creature->isCanTrainingOf(target, false)) - appendValue &= ~(UNIT_NPC_FLAG_TRAINER | UNIT_NPC_FLAG_TRAINER_CLASS | UNIT_NPC_FLAG_TRAINER_PROFESSION); - } } *data << uint32(appendValue); diff --git a/src/server/game/Entities/Object/Object.h b/src/server/game/Entities/Object/Object.h index 854978b921f..a72f4fcf3e4 100644 --- a/src/server/game/Entities/Object/Object.h +++ b/src/server/game/Entities/Object/Object.h @@ -518,6 +518,15 @@ struct MovementInfo void SetFallTime(uint32 time) { fallTime = time; } + void ClearTransport() + { + t_guid = 0; + t_pos.Relocate(0.0f, 0.0f, 0.0f, 0.0f); + t_seat = -1; + t_time = 0; + t_time2 = 0; + } + void OutDebug(); }; diff --git a/src/server/game/Entities/Pet/Pet.cpp b/src/server/game/Entities/Pet/Pet.cpp index 1612b45bc89..ce564bd9090 100644 --- a/src/server/game/Entities/Pet/Pet.cpp +++ b/src/server/game/Entities/Pet/Pet.cpp @@ -2038,3 +2038,16 @@ Player* Pet::GetOwner() const { return Minion::GetOwner()->ToPlayer(); } + +void Pet::SetDisplayId(uint32 modelId) +{ + Guardian::SetDisplayId(modelId); + + if (!isControlled()) + return; + + if (Unit* owner = GetOwner()) + if (Player* player = owner->ToPlayer()) + if (owner->ToPlayer()->GetGroup()) + owner->ToPlayer()->SetGroupUpdateFlag(GROUP_UPDATE_FLAG_PET_MODEL_ID); +} diff --git a/src/server/game/Entities/Pet/Pet.h b/src/server/game/Entities/Pet/Pet.h index 975b97adce3..ac274e3035d 100644 --- a/src/server/game/Entities/Pet/Pet.h +++ b/src/server/game/Entities/Pet/Pet.h @@ -46,6 +46,8 @@ class Pet : public Guardian void AddToWorld(); void RemoveFromWorld(); + void SetDisplayId(uint32 modelId); + PetType getPetType() const { return m_petType; } void setPetType(PetType type) { m_petType = type; } bool isControlled() const { return getPetType() == SUMMON_PET || getPetType() == HUNTER_PET; } diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index e35e4b7d505..642da3e8d95 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -978,8 +978,7 @@ bool Player::Create(uint32 guidlow, CharacterCreateInfo* createInfo) uint8 powertype = cEntry->powerType; - SetFloatValue(UNIT_FIELD_BOUNDINGRADIUS, DEFAULT_WORLD_OBJECT_SIZE); - SetFloatValue(UNIT_FIELD_COMBATREACH, 1.5f); + SetObjectScale(1.0f); setFactionForRace(createInfo->Race); @@ -2136,9 +2135,7 @@ bool Player::TeleportTo(uint32 mapid, float x, float y, float z, float orientati { m_transport->RemovePassenger(this); m_transport = NULL; - m_movementInfo.t_pos.Relocate(0.0f, 0.0f, 0.0f, 0.0f); - m_movementInfo.t_time = 0; - m_movementInfo.t_seat = -1; + m_movementInfo.ClearTransport(); RepopAtGraveyard(); // teleport to near graveyard if on transport, looks blizz like :) } @@ -2162,10 +2159,7 @@ bool Player::TeleportTo(uint32 mapid, float x, float y, float z, float orientati { m_transport->RemovePassenger(this); m_transport = NULL; - m_movementInfo.t_pos.Relocate(0.0f, 0.0f, 0.0f, 0.0f); - m_movementInfo.t_time = 0; - m_movementInfo.t_seat = -1; - m_movementInfo.t_guid = 0; + m_movementInfo.ClearTransport(); } } @@ -4588,16 +4582,16 @@ Mail* Player::GetMail(uint32 id) void Player::BuildCreateUpdateBlockForPlayer(UpdateData* data, Player* target) const { - for (uint8 i = 0; i < EQUIPMENT_SLOT_END; ++i) + if (target == this) { - if (m_items[i] == NULL) - continue; + for (uint8 i = 0; i < EQUIPMENT_SLOT_END; ++i) + { + if (m_items[i] == NULL) + continue; - m_items[i]->BuildCreateUpdateBlockForPlayer(data, target); - } + m_items[i]->BuildCreateUpdateBlockForPlayer(data, target); + } - if (target == this) - { for (uint8 i = INVENTORY_SLOT_BAG_START; i < BANK_SLOT_BAG_END; ++i) { if (m_items[i] == NULL) @@ -5158,8 +5152,6 @@ void Player::BuildPlayerRepop() StopMirrorTimers(); //disable timers(bars) - SetFloatValue(UNIT_FIELD_BOUNDINGRADIUS, float(1.0f)); //see radius of death player? - // set and clear other SetByteValue(UNIT_FIELD_BYTES_1, 3, UNIT_BYTE1_FLAG_ALWAYS_STAND); } @@ -14419,10 +14411,6 @@ void Player::PrepareGossipMenu(WorldObject* source, uint32 menuId /*= 0*/, bool } break; } - case GOSSIP_OPTION_TRAINER: - if (!creature->isCanTrainingOf(this, false)) - canTalk = false; - break; case GOSSIP_OPTION_LEARNDUALSPEC: if (!(GetSpecsCount() == 1 && creature->isCanTrainingAndResetTalentsOf(this) && !(getLevel() < sWorld->getIntConfig(CONFIG_MIN_DUALSPEC_LEVEL)))) canTalk = false; @@ -14450,6 +14438,7 @@ void Player::PrepareGossipMenu(WorldObject* source, uint32 menuId /*= 0*/, bool case GOSSIP_OPTION_QUESTGIVER: canTalk = false; break; + case GOSSIP_OPTION_TRAINER: case GOSSIP_OPTION_GOSSIP: case GOSSIP_OPTION_SPIRITGUIDE: case GOSSIP_OPTION_INNKEEPER: @@ -14463,7 +14452,7 @@ void Player::PrepareGossipMenu(WorldObject* source, uint32 menuId /*= 0*/, bool canTalk = false; break; default: - TC_LOG_ERROR(LOG_FILTER_SQL, "Creature entry %u have unknown gossip option %u for menu %u", creature->GetEntry(), itr->second.OptionType, itr->second.MenuId); + TC_LOG_ERROR(LOG_FILTER_SQL, "Creature entry %u has unknown gossip option %u for menu %u", creature->GetEntry(), itr->second.OptionType, itr->second.MenuId); canTalk = false; break; } @@ -17088,8 +17077,7 @@ bool Player::LoadFromDB(uint32 guid, SQLQueryHolder *holder) _LoadIntoDataField(fields[55].GetCString(), PLAYER_EXPLORED_ZONES_1, PLAYER_EXPLORED_ZONES_SIZE); _LoadIntoDataField(fields[57].GetCString(), PLAYER__FIELD_KNOWN_TITLES, KNOWN_TITLES_SIZE*2); - SetFloatValue(UNIT_FIELD_BOUNDINGRADIUS, DEFAULT_WORLD_OBJECT_SIZE); - SetFloatValue(UNIT_FIELD_COMBATREACH, 1.5f); + SetObjectScale(1.0f); SetFloatValue(UNIT_FIELD_HOVERHEIGHT, 1.0f); // load achievements before anything else to prevent multiple gains for the same achievement/criteria on every loading (as loading does call UpdateAchievementCriteria) @@ -18926,7 +18914,9 @@ void Player::ConvertInstancesToGroup(Player* player, Group* group, bool switchLe { for (BoundInstancesMap::iterator itr = player->m_boundInstances[i].begin(); itr != player->m_boundInstances[i].end();) { - group->BindToInstance(itr->second.save, itr->second.perm, false); + if (!switchLeader || !group->GetBoundInstance(itr->second.save->GetDifficulty(), itr->first)) + group->BindToInstance(itr->second.save, itr->second.perm, false); + // permanent binds are not removed if (switchLeader && !itr->second.perm) { diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h index 1dba62f1f3b..bbda2a75fba 100644 --- a/src/server/game/Entities/Player/Player.h +++ b/src/server/game/Entities/Player/Player.h @@ -1214,7 +1214,7 @@ class Player : public Unit, public GridObject<Player> friend void Item::AddToUpdateQueueOf(Player* player); friend void Item::RemoveFromUpdateQueueOf(Player* player); public: - explicit Player (WorldSession* session); + explicit Player(WorldSession* session); ~Player(); void CleanupsBeforeDelete(bool finalCleanup = true); @@ -1222,6 +1222,13 @@ class Player : public Unit, public GridObject<Player> void AddToWorld(); void RemoveFromWorld(); + void SetObjectScale(float scale) + { + Unit::SetObjectScale(scale); + SetFloatValue(UNIT_FIELD_BOUNDINGRADIUS, scale * DEFAULT_WORLD_OBJECT_SIZE); + SetFloatValue(UNIT_FIELD_COMBATREACH, scale * DEFAULT_COMBAT_REACH); + } + bool TeleportTo(uint32 mapid, float x, float y, float z, float orientation, uint32 options = 0); bool TeleportTo(WorldLocation const &loc, uint32 options = 0); bool TeleportToBGEntryPoint(); diff --git a/src/server/game/Entities/Unit/StatSystem.cpp b/src/server/game/Entities/Unit/StatSystem.cpp index 1f4ab95517d..ac771004096 100644 --- a/src/server/game/Entities/Unit/StatSystem.cpp +++ b/src/server/game/Entities/Unit/StatSystem.cpp @@ -24,6 +24,7 @@ #include "SpellAuras.h" #include "SpellAuraEffects.h" #include "SpellMgr.h" +#include "World.h" inline bool _ModifyUInt32(bool apply, uint32& baseValue, int32& amount) { @@ -437,6 +438,10 @@ void Player::UpdateBlockPercentage() value += GetTotalAuraModifier(SPELL_AURA_MOD_BLOCK_PERCENT); // Increase from rating value += GetRatingBonusValue(CR_BLOCK); + + if (sWorld->getBoolConfig(CONFIG_STATS_LIMITS_ENABLE)) + value = value > sWorld->getFloatConfig(CONFIG_STATS_LIMITS_BLOCK) ? sWorld->getFloatConfig(CONFIG_STATS_LIMITS_BLOCK) : value; + value = value < 0.0f ? 0.0f : value; } SetStatFloatValue(PLAYER_BLOCK_PERCENTAGE, value); @@ -471,6 +476,10 @@ void Player::UpdateCritPercentage(WeaponAttackType attType) float value = GetTotalPercentageModValue(modGroup) + GetRatingBonusValue(cr); // Modify crit from weapon skill and maximized defense skill of same level victim difference value += (int32(GetMaxSkillValueForLevel()) - int32(GetMaxSkillValueForLevel())) * 0.04f; + + if (sWorld->getBoolConfig(CONFIG_STATS_LIMITS_ENABLE)) + value = value > sWorld->getFloatConfig(CONFIG_STATS_LIMITS_CRIT) ? sWorld->getFloatConfig(CONFIG_STATS_LIMITS_CRIT) : value; + value = value < 0.0f ? 0.0f : value; SetStatFloatValue(index, value); } @@ -570,6 +579,10 @@ void Player::UpdateParryPercentage() nondiminishing += GetTotalAuraModifier(SPELL_AURA_MOD_PARRY_PERCENT); // apply diminishing formula to diminishing parry chance value = nondiminishing + diminishing * parry_cap[pclass] / (diminishing + parry_cap[pclass] * m_diminishing_k[pclass]); + + if (sWorld->getBoolConfig(CONFIG_STATS_LIMITS_ENABLE)) + value = value > sWorld->getFloatConfig(CONFIG_STATS_LIMITS_PARRY) ? sWorld->getFloatConfig(CONFIG_STATS_LIMITS_PARRY) : value; + value = value < 0.0f ? 0.0f : value; } SetStatFloatValue(PLAYER_PARRY_PERCENTAGE, value); @@ -602,6 +615,9 @@ void Player::UpdateDodgePercentage() uint32 pclass = getClass()-1; float value = nondiminishing + (diminishing * dodge_cap[pclass] / (diminishing + dodge_cap[pclass] * m_diminishing_k[pclass])); + if (sWorld->getBoolConfig(CONFIG_STATS_LIMITS_ENABLE)) + value = value > sWorld->getFloatConfig(CONFIG_STATS_LIMITS_DODGE) ? sWorld->getFloatConfig(CONFIG_STATS_LIMITS_DODGE) : value; + value = value < 0.0f ? 0.0f : value; SetStatFloatValue(PLAYER_DODGE_PERCENTAGE, value); } diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp index 6c226693871..bda77af54ed 100644 --- a/src/server/game/Entities/Unit/Unit.cpp +++ b/src/server/game/Entities/Unit/Unit.cpp @@ -13317,16 +13317,6 @@ bool Unit::IsPolymorphed() const void Unit::SetDisplayId(uint32 modelId) { SetUInt32Value(UNIT_FIELD_DISPLAYID, modelId); - - if (GetTypeId() == TYPEID_UNIT && ToCreature()->isPet()) - { - Pet* pet = ToPet(); - if (!pet->isControlled()) - return; - Unit* owner = GetOwner(); - if (owner && (owner->GetTypeId() == TYPEID_PLAYER) && owner->ToPlayer()->GetGroup()) - owner->ToPlayer()->SetGroupUpdateFlag(GROUP_UPDATE_FLAG_PET_MODEL_ID); - } } void Unit::RestoreDisplayId() @@ -15862,29 +15852,23 @@ void Unit::EnterVehicle(Unit* base, int8 seatId) void Unit::_EnterVehicle(Vehicle* vehicle, int8 seatId, AuraApplication const* aurApp) { // Must be called only from aura handler + ASSERT(aurApp); + if (!isAlive() || GetVehicleKit() == vehicle || vehicle->GetBase()->IsOnVehicle(this)) return; if (m_vehicle) { - if (m_vehicle == vehicle) - { - if (seatId >= 0 && seatId != GetTransSeat()) - { - TC_LOG_DEBUG(LOG_FILTER_VEHICLES, "EnterVehicle: %u leave vehicle %u seat %d and enter %d.", GetEntry(), m_vehicle->GetBase()->GetEntry(), GetTransSeat(), seatId); - ChangeSeat(seatId); - } - - return; - } - else + if (m_vehicle != vehicle) { TC_LOG_DEBUG(LOG_FILTER_VEHICLES, "EnterVehicle: %u exit %u and enter %u.", GetEntry(), m_vehicle->GetBase()->GetEntry(), vehicle->GetBase()->GetEntry()); ExitVehicle(); } + else if (seatId >= 0 && seatId == GetTransSeat()) + return; } - if (!aurApp || aurApp->GetRemoveMode()) + if (aurApp->GetRemoveMode()) return; if (Player* player = ToPlayer()) @@ -15914,16 +15898,22 @@ void Unit::ChangeSeat(int8 seatId, bool next) if (seat == m_vehicle->Seats.end() || seat->second.Passenger) return; - // Todo: the functions below could be consolidated and refactored to take - // SeatMap::const_iterator as parameter, to save redundant map lookups. - m_vehicle->RemovePassenger(this); + AuraEffect* rideVehicleEffect = NULL; + AuraEffectList const& vehicleAuras = m_vehicle->GetBase()->GetAuraEffectsByType(SPELL_AURA_CONTROL_VEHICLE); + for (AuraEffectList::const_iterator itr = vehicleAuras.begin(); itr != vehicleAuras.end(); ++itr) + { + if ((*itr)->GetCasterGUID() != GetGUID()) + continue; + + // Make sure there is only one ride vehicle aura on target cast by the unit changing seat + ASSERT(!rideVehicleEffect); + rideVehicleEffect = *itr; + } + + // Unit riding a vehicle must always have control vehicle aura on target + ASSERT(rideVehicleEffect); - // Set m_vehicle to NULL before adding passenger as adding new passengers is handled asynchronously - // and someone may call ExitVehicle again before passenger is added to new seat - Vehicle* veh = m_vehicle; - m_vehicle = NULL; - if (!veh->AddPassenger(this, seatId)) - ASSERT(false); + rideVehicleEffect->ChangeAmount(seat->first + 1); } void Unit::ExitVehicle(Position const* /*exitPosition*/) @@ -15953,7 +15943,8 @@ void Unit::_ExitVehicle(Position const* exitPosition) if (!m_vehicle) return; - m_vehicle->RemovePassenger(this); + // This should be done before dismiss, because there may be some aura removal + Vehicle* vehicle = m_vehicle->RemovePassenger(this); Player* player = ToPlayer(); @@ -15961,10 +15952,6 @@ void Unit::_ExitVehicle(Position const* exitPosition) if (player && player->duel && player->duel->isMounted) player->DuelComplete(DUEL_FLED); - // This should be done before dismiss, because there may be some aura removal - Vehicle* vehicle = m_vehicle; - m_vehicle = NULL; - SetControlled(false, UNIT_STATE_ROOT); // SMSG_MOVE_FORCE_UNROOT, ~MOVEMENTFLAG_ROOT Position pos; @@ -16016,6 +16003,11 @@ void Unit::_ExitVehicle(Position const* exitPosition) } } +bool Unit::IsFalling() const +{ + return m_movementInfo.HasMovementFlag(MOVEMENTFLAG_FALLING | MOVEMENTFLAG_FALLING_FAR) || movespline->isFalling(); +} + void Unit::SetCanFly(bool apply) { if (apply) diff --git a/src/server/game/Entities/Unit/Unit.h b/src/server/game/Entities/Unit/Unit.h index 7d149332694..f164b04644b 100644 --- a/src/server/game/Entities/Unit/Unit.h +++ b/src/server/game/Entities/Unit/Unit.h @@ -1931,7 +1931,7 @@ class Unit : public WorldObject void UpdateInterruptMask(); uint32 GetDisplayId() const { return GetUInt32Value(UNIT_FIELD_DISPLAYID); } - void SetDisplayId(uint32 modelId); + virtual void SetDisplayId(uint32 modelId); uint32 GetNativeDisplayId() const { return GetUInt32Value(UNIT_FIELD_NATIVEDISPLAYID); } void RestoreDisplayId(); void SetNativeDisplayId(uint32 modelId) { SetUInt32Value(UNIT_FIELD_NATIVEDISPLAYID, modelId); } @@ -2090,6 +2090,7 @@ class Unit : public WorldObject void RemoveVehicleKit(); Vehicle* GetVehicleKit()const { return m_vehicleKit; } Vehicle* GetVehicle() const { return m_vehicle; } + void SetVehicle(Vehicle* vehicle) { m_vehicle = vehicle; } bool IsOnVehicle(const Unit* vehicle) const; Unit* GetVehicleBase() const; Creature* GetVehicleCreatureBase() const; @@ -2120,6 +2121,7 @@ class Unit : public WorldObject bool isTurning() const { return m_movementInfo.HasMovementFlag(MOVEMENTFLAG_MASK_TURNING); } virtual bool CanFly() const = 0; bool IsFlying() const { return m_movementInfo.HasMovementFlag(MOVEMENTFLAG_FLYING | MOVEMENTFLAG_DISABLE_GRAVITY); } + bool IsFalling() const; void SetCanFly(bool apply); void RewardRage(uint32 baseRage, bool attacker); diff --git a/src/server/game/Entities/Vehicle/Vehicle.cpp b/src/server/game/Entities/Vehicle/Vehicle.cpp index 503d89a8e82..797430683f4 100644 --- a/src/server/game/Entities/Vehicle/Vehicle.cpp +++ b/src/server/game/Entities/Vehicle/Vehicle.cpp @@ -496,10 +496,10 @@ bool Vehicle::AddPassenger(Unit* unit, int8 seatId) * @param [in, out] unit The passenger to remove. */ -void Vehicle::RemovePassenger(Unit* unit) +Vehicle* Vehicle::RemovePassenger(Unit* unit) { if (unit->GetVehicle() != this) - return; + return NULL; SeatMap::iterator seat = GetSeatIteratorForPassenger(unit); ASSERT(seat != Seats.end()); @@ -517,11 +517,7 @@ void Vehicle::RemovePassenger(Unit* unit) _me->RemoveCharmedBy(unit); if (_me->IsInWorld()) - { - unit->m_movementInfo.t_pos.Relocate(0.0f, 0.0f, 0.0f, 0.0f); - unit->m_movementInfo.t_time = 0; - unit->m_movementInfo.t_seat = 0; - } + unit->m_movementInfo.ClearTransport(); // only for flyable vehicles if (unit->IsFlying()) @@ -532,6 +528,9 @@ void Vehicle::RemovePassenger(Unit* unit) if (GetBase()->GetTypeId() == TYPEID_UNIT) sScriptMgr->OnRemovePassenger(this, unit); + + unit->SetVehicle(NULL); + return this; } /** @@ -803,7 +802,7 @@ bool VehicleJoinEvent::Execute(uint64, uint32) Target->RemovePendingEventsForSeat(Seat->first); - Passenger->m_vehicle = Target; + Passenger->SetVehicle(Target); Seat->second.Passenger = Passenger->GetGUID(); if (Seat->second.SeatInfo->CanEnterOrExit()) { diff --git a/src/server/game/Entities/Vehicle/Vehicle.h b/src/server/game/Entities/Vehicle/Vehicle.h index c83a9fa5f33..9fb1b6614b5 100644 --- a/src/server/game/Entities/Vehicle/Vehicle.h +++ b/src/server/game/Entities/Vehicle/Vehicle.h @@ -59,7 +59,7 @@ class Vehicle : public TransportBase bool AddPassenger(Unit* passenger, int8 seatId = -1); void EjectPassenger(Unit* passenger, Unit* controller); - void RemovePassenger(Unit* passenger); + Vehicle* RemovePassenger(Unit* passenger); void RelocatePassengers(); void RemoveAllPassengers(); bool IsVehicleInUse() const; diff --git a/src/server/game/Events/GameEventMgr.cpp b/src/server/game/Events/GameEventMgr.cpp index 6f6080c8d91..54e1ca6a5d6 100644 --- a/src/server/game/Events/GameEventMgr.cpp +++ b/src/server/game/Events/GameEventMgr.cpp @@ -1332,13 +1332,10 @@ void GameEventMgr::ChangeEquipOrModel(int16 event_id, bool activate) creature->LoadEquipment(itr->second.equipment_id, true); if (itr->second.modelid > 0 && itr->second.modelid_prev != itr->second.modelid) { - CreatureModelInfo const* minfo = sObjectMgr->GetCreatureModelInfo(itr->second.modelid); - if (minfo) + if (CreatureModelInfo const* minfo = sObjectMgr->GetCreatureModelInfo(itr->second.modelid)) { creature->SetDisplayId(itr->second.modelid); creature->SetNativeDisplayId(itr->second.modelid); - creature->SetFloatValue(UNIT_FIELD_BOUNDINGRADIUS, minfo->bounding_radius); - creature->SetFloatValue(UNIT_FIELD_COMBATREACH, minfo->combat_reach); } } } @@ -1347,13 +1344,10 @@ void GameEventMgr::ChangeEquipOrModel(int16 event_id, bool activate) creature->LoadEquipment(itr->second.equipement_id_prev, true); if (itr->second.modelid_prev > 0 && itr->second.modelid_prev != itr->second.modelid) { - CreatureModelInfo const* minfo = sObjectMgr->GetCreatureModelInfo(itr->second.modelid_prev); - if (minfo) + if (CreatureModelInfo const* minfo = sObjectMgr->GetCreatureModelInfo(itr->second.modelid_prev)) { creature->SetDisplayId(itr->second.modelid_prev); creature->SetNativeDisplayId(itr->second.modelid_prev); - creature->SetFloatValue(UNIT_FIELD_BOUNDINGRADIUS, minfo->bounding_radius); - creature->SetFloatValue(UNIT_FIELD_COMBATREACH, minfo->combat_reach); } } } diff --git a/src/server/game/Groups/Group.cpp b/src/server/game/Groups/Group.cpp index bfb0e005eda..9d27ca61511 100644 --- a/src/server/game/Groups/Group.cpp +++ b/src/server/game/Groups/Group.cpp @@ -646,30 +646,39 @@ bool Group::RemoveMember(uint64 guid, const RemoveMethod& method /*= GROUP_REMOV } } -void Group::ChangeLeader(uint64 guid) +void Group::ChangeLeader(uint64 newLeaderGuid) { - member_witerator slot = _getMemberWSlot(guid); + member_witerator slot = _getMemberWSlot(newLeaderGuid); if (slot == m_memberSlots.end()) return; - Player* player = ObjectAccessor::FindPlayer(slot->guid); + Player* newLeader = ObjectAccessor::FindPlayer(slot->guid); // Don't allow switching leader to offline players - if (!player) + if (!newLeader) return; - sScriptMgr->OnGroupChangeLeader(this, guid, m_leaderGuid); + sScriptMgr->OnGroupChangeLeader(this, newLeaderGuid, m_leaderGuid); if (!isBGGroup() && !isBFGroup()) { + SQLTransaction trans = CharacterDatabase.BeginTransaction(); + // Remove the groups permanent instance bindings for (uint8 i = 0; i < MAX_DIFFICULTY; ++i) { for (BoundInstancesMap::iterator itr = m_boundInstances[i].begin(); itr != m_boundInstances[i].end();) { - if (itr->second.perm) + // Do not unbind saves of instances that already had map created (a newLeader entered) + // forcing a new instance with another leader requires group disbanding (confirmed on retail) + if (itr->second.perm && !sMapMgr->FindMap(itr->first, itr->second.save->GetInstanceId())) { + PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_GROUP_INSTANCE_PERM_BINDING); + stmt->setUInt32(0, m_dbStoreId); + stmt->setUInt32(1, itr->second.save->GetInstanceId()); + trans->Append(stmt); + itr->second.save->RemoveGroup(this); m_boundInstances[i].erase(itr++); } @@ -678,29 +687,22 @@ void Group::ChangeLeader(uint64 guid) } } - // Same in the database - - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_GROUP_INSTANCE_PERM_BINDING); - - stmt->setUInt32(0, m_dbStoreId); - stmt->setUInt32(1, player->GetGUIDLow()); - - CharacterDatabase.Execute(stmt); - // Copy the permanent binds from the new leader to the group - Player::ConvertInstancesToGroup(player, this, true); + Player::ConvertInstancesToGroup(newLeader, this, true); // Update the group leader - stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_GROUP_LEADER); + PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_GROUP_LEADER); - stmt->setUInt32(0, player->GetGUIDLow()); + stmt->setUInt32(0, newLeader->GetGUIDLow()); stmt->setUInt32(1, m_dbStoreId); - CharacterDatabase.Execute(stmt); + trans->Append(stmt); + + CharacterDatabase.CommitTransaction(trans); } - m_leaderGuid = player->GetGUID(); - m_leaderName = player->GetName(); + m_leaderGuid = newLeader->GetGUID(); + m_leaderName = newLeader->GetName(); ToggleGroupMemberFlag(slot, MEMBER_FLAG_ASSISTANT, false); WorldPacket data(SMSG_GROUP_SET_LEADER, m_leaderName.size()+1); diff --git a/src/server/game/Handlers/AuctionHouseHandler.cpp b/src/server/game/Handlers/AuctionHouseHandler.cpp index cc3b065dbe4..4ebe9a2b90d 100644 --- a/src/server/game/Handlers/AuctionHouseHandler.cpp +++ b/src/server/game/Handlers/AuctionHouseHandler.cpp @@ -30,9 +30,6 @@ #include "Util.h" #include "AccountMgr.h" -//please DO NOT use iterator++, because it is slower than ++iterator!!! -//post-incrementation is always slower than pre-incrementation ! - //void called when player click on auctioneer npc void WorldSession::HandleAuctionHelloOpcode(WorldPacket& recvData) { diff --git a/src/server/game/Handlers/MovementHandler.cpp b/src/server/game/Handlers/MovementHandler.cpp index 2adc1fd2d89..2739e53b818 100644 --- a/src/server/game/Handlers/MovementHandler.cpp +++ b/src/server/game/Handlers/MovementHandler.cpp @@ -361,9 +361,7 @@ void WorldSession::HandleMovementOpcodes(WorldPacket& recvPacket) { plrMover->m_transport->RemovePassenger(plrMover); plrMover->m_transport = NULL; - movementInfo.t_pos.Relocate(0.0f, 0.0f, 0.0f, 0.0f); - movementInfo.t_time = 0; - movementInfo.t_seat = -1; + movementInfo.ClearTransport(); } // fall damage generation (ignore in flight case that can be triggered also at lags in moment teleportation to another map). diff --git a/src/server/game/Handlers/NPCHandler.cpp b/src/server/game/Handlers/NPCHandler.cpp index aebd1baa901..51533ad8a9f 100644 --- a/src/server/game/Handlers/NPCHandler.cpp +++ b/src/server/game/Handlers/NPCHandler.cpp @@ -132,10 +132,6 @@ void WorldSession::SendTrainerList(uint64 guid, const std::string& strTitle) if (GetPlayer()->HasUnitState(UNIT_STATE_DIED)) GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH); - // trainer list loaded at check; - if (!unit->isCanTrainingOf(_player, true)) - return; - CreatureTemplate const* ci = unit->GetCreatureTemplate(); if (!ci) @@ -257,12 +253,6 @@ void WorldSession::HandleTrainerBuySpellOpcode(WorldPacket& recvData) if (GetPlayer()->HasUnitState(UNIT_STATE_DIED)) GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH); - if (!unit->isCanTrainingOf(_player, true)) - { - SendTrainerBuyFailed(guid, spellId, 0); - return; - } - // check present spell in trainer spell list TrainerSpellData const* trainer_spells = unit->GetTrainerSpells(); if (!trainer_spells) diff --git a/src/server/game/Handlers/QuestHandler.cpp b/src/server/game/Handlers/QuestHandler.cpp index 45be13ae776..279c1392e17 100644 --- a/src/server/game/Handlers/QuestHandler.cpp +++ b/src/server/game/Handlers/QuestHandler.cpp @@ -499,12 +499,12 @@ void WorldSession::HandleQuestgiverCompleteQuest(WorldPacket& recvData) TC_LOG_DEBUG(LOG_FILTER_NETWORKIO, "WORLD: Received CMSG_QUESTGIVER_COMPLETE_QUEST npc = %u, questId = %u", uint32(GUID_LOPART(playerGuid)), questId); + Object* object = ObjectAccessor::GetObjectByTypeMask(*_player, playerGuid, TYPEMASK_UNIT|TYPEMASK_GAMEOBJECT); + if (!object || !object->hasInvolvedQuest(questId)) + return; + if (autoCompleteMode == 0) { - Object* object = ObjectAccessor::GetObjectByTypeMask(*_player, playerGuid, TYPEMASK_UNIT|TYPEMASK_GAMEOBJECT); - if (!object || !object->hasInvolvedQuest(questId)) - return; - // some kind of WPE protection if (!_player->CanInteractWithQuestGiver(object)) return; @@ -545,6 +545,9 @@ void WorldSession::HandleQuestgiverCompleteQuest(WorldPacket& recvData) else // no items required _player->PlayerTalkClass->SendQuestGiverOfferReward(quest, playerGuid, !autoCompleteMode); } + + if (Creature* creature = object->ToCreature()) + sScriptMgr->OnQuestComplete(_player, creature, quest); } } diff --git a/src/server/game/Maps/Map.cpp b/src/server/game/Maps/Map.cpp index 54a9fdea264..90e072825de 100644 --- a/src/server/game/Maps/Map.cpp +++ b/src/server/game/Maps/Map.cpp @@ -1934,25 +1934,14 @@ float Map::GetHeight(uint32 phasemask, float x, float y, float z, bool vmap/*=tr bool Map::IsInWater(float x, float y, float pZ, LiquidData* data) const { - // Check surface in x, y point for liquid - if (const_cast<Map*>(this)->GetGrid(x, y)) - { - LiquidData liquid_status; - LiquidData* liquid_ptr = data ? data : &liquid_status; - if (getLiquidStatus(x, y, pZ, MAP_ALL_LIQUIDS, liquid_ptr)) - return true; - } - return false; + LiquidData liquid_status; + LiquidData* liquid_ptr = data ? data : &liquid_status; + return getLiquidStatus(x, y, pZ, MAP_ALL_LIQUIDS, liquid_ptr) & (LIQUID_MAP_IN_WATER | LIQUID_MAP_UNDER_WATER); } bool Map::IsUnderWater(float x, float y, float z) const { - if (const_cast<Map*>(this)->GetGrid(x, y)) - { - if (getLiquidStatus(x, y, z, MAP_LIQUID_TYPE_WATER|MAP_LIQUID_TYPE_OCEAN)&LIQUID_MAP_UNDER_WATER) - return true; - } - return false; + return getLiquidStatus(x, y, z, MAP_LIQUID_TYPE_WATER|MAP_LIQUID_TYPE_OCEAN) & LIQUID_MAP_UNDER_WATER; } bool Map::CheckGridIntegrity(Creature* c, bool moved) const diff --git a/src/server/game/Movement/MovementGenerators/PointMovementGenerator.cpp b/src/server/game/Movement/MovementGenerators/PointMovementGenerator.cpp index 227780daa1f..186723a1c43 100755 --- a/src/server/game/Movement/MovementGenerators/PointMovementGenerator.cpp +++ b/src/server/game/Movement/MovementGenerators/PointMovementGenerator.cpp @@ -43,8 +43,8 @@ void PointMovementGenerator<T>::DoInitialize(T* unit) if (speed > 0.0f) init.SetVelocity(speed); init.Launch(); - - //Call for creature group update + + // Call for creature group update if (Creature* creature = unit->ToCreature()) if (creature->GetFormation() && creature->GetFormation()->getLeader() == creature) creature->GetFormation()->LeaderMoveTo(i_x, i_y, i_z); @@ -72,8 +72,8 @@ bool PointMovementGenerator<T>::DoUpdate(T* unit, uint32 /*diff*/) if (speed > 0.0f) // Default value for point motion type is 0.0, if 0.0 spline will use GetSpeed on unit init.SetVelocity(speed); init.Launch(); - - //Call for creature group update + + // Call for creature group update if (Creature* creature = unit->ToCreature()) if (creature->GetFormation() && creature->GetFormation()->getLeader() == creature) creature->GetFormation()->LeaderMoveTo(i_x, i_y, i_z); diff --git a/src/server/game/Movement/Spline/MoveSpline.h b/src/server/game/Movement/Spline/MoveSpline.h index b18166ea615..a76c552079b 100644 --- a/src/server/game/Movement/Spline/MoveSpline.h +++ b/src/server/game/Movement/Spline/MoveSpline.h @@ -115,6 +115,7 @@ namespace Movement uint32 GetId() const { return m_Id; } bool Finalized() const { return splineflags.done; } bool isCyclic() const { return splineflags.cyclic; } + bool isFalling() const { return splineflags.falling; } const Vector3 FinalDestination() const { return Initialized() ? spline.getPoint(spline.last()) : Vector3(); } const Vector3 CurrentDestination() const { return Initialized() ? spline.getPoint(point_Idx+1) : Vector3(); } int32 currentPathIdx() const; diff --git a/src/server/game/Scripting/ScriptMgr.h b/src/server/game/Scripting/ScriptMgr.h index 62a53ef67f7..85797ac351b 100644 --- a/src/server/game/Scripting/ScriptMgr.h +++ b/src/server/game/Scripting/ScriptMgr.h @@ -687,8 +687,8 @@ class PlayerScript : public UnitScript // Called when a player is killed by a creature virtual void OnPlayerKilledByCreature(Creature* /*killer*/, Player* /*killed*/) { } - // Called when a player's level changes (right before the level is applied) - virtual void OnLevelChanged(Player* /*player*/, uint8 /*newLevel*/) { } + // Called when a player's level changes (after the level is applied) + virtual void OnLevelChanged(Player* /*player*/, uint8 /*oldLevel*/) { } // Called when a player's free talent points change (right before the change is applied) virtual void OnFreeTalentPointsChanged(Player* /*player*/, uint32 /*points*/) { } diff --git a/src/server/game/Spells/Auras/SpellAuraEffects.cpp b/src/server/game/Spells/Auras/SpellAuraEffects.cpp index 6b14e5a8b53..6b2e67df095 100644 --- a/src/server/game/Spells/Auras/SpellAuraEffects.cpp +++ b/src/server/game/Spells/Auras/SpellAuraEffects.cpp @@ -2153,7 +2153,9 @@ void AuraEffect::HandleAuraModScale(AuraApplication const* aurApp, uint8 mode, b Unit* target = aurApp->GetTarget(); - target->ApplyPercentModFloatValue(OBJECT_FIELD_SCALE_X, (float)GetAmount(), apply); + float scale = target->GetFloatValue(OBJECT_FIELD_SCALE_X); + ApplyPercentModFloatVar(scale, float(GetAmount()), apply); + target->SetObjectScale(scale); } void AuraEffect::HandleAuraCloneCaster(AuraApplication const* aurApp, uint8 mode, bool apply) const @@ -3017,7 +3019,12 @@ void AuraEffect::HandleAuraControlVehicle(AuraApplication const* aurApp, uint8 m if (caster->GetTypeId() == TYPEID_UNIT) caster->ToCreature()->RemoveCorpse(); } - caster->_ExitVehicle(); + + if (!(mode & AURA_EFFECT_HANDLE_CHANGE_AMOUNT)) + caster->_ExitVehicle(); + else + target->GetVehicleKit()->RemovePassenger(caster); // Only remove passenger from vehicle without launching exit movement or despawning the vehicle + // some SPELL_AURA_CONTROL_VEHICLE auras have a dummy effect on the player - remove them caster->RemoveAurasDueToSpell(GetId()); } diff --git a/src/server/game/Spells/Spell.cpp b/src/server/game/Spells/Spell.cpp index 4cbb9a28603..4e0c0e0f831 100644 --- a/src/server/game/Spells/Spell.cpp +++ b/src/server/game/Spells/Spell.cpp @@ -1397,7 +1397,26 @@ void Spell::SelectImplicitCasterDestTargets(SpellEffIndex effIndex, SpellImplici float x, y, z, angle; angle = (float)rand_norm() * static_cast<float>(M_PI * 35.0f / 180.0f) - static_cast<float>(M_PI * 17.5f / 180.0f); m_caster->GetClosePoint(x, y, z, DEFAULT_WORLD_OBJECT_SIZE, dis, angle); - m_targets.SetDst(x, y, z, m_caster->GetOrientation()); + + float ground = z; + float liquidLevel = m_caster->GetMap()->GetWaterOrGroundLevel(x, y, z, &ground); + if (liquidLevel <= ground) // When there is no liquid Map::GetWaterOrGroundLevel returns ground level + { + SendCastResult(SPELL_FAILED_NOT_HERE); + SendChannelUpdate(0); + finish(false); + return; + } + + if (ground + 0.75 > liquidLevel) + { + SendCastResult(SPELL_FAILED_TOO_SHALLOW); + SendChannelUpdate(0); + finish(false); + return; + } + + m_targets.SetDst(x, y, liquidLevel, m_caster->GetOrientation()); return; } default: diff --git a/src/server/game/Spells/SpellEffects.cpp b/src/server/game/Spells/SpellEffects.cpp index 44b23d7b774..97a06415a9d 100644 --- a/src/server/game/Spells/SpellEffects.cpp +++ b/src/server/game/Spells/SpellEffects.cpp @@ -5013,25 +5013,9 @@ void Spell::EffectTransmitted(SpellEffIndex effIndex) } Map* cMap = m_caster->GetMap(); - if (goinfo->type == GAMEOBJECT_TYPE_FISHINGNODE || goinfo->type == GAMEOBJECT_TYPE_FISHINGHOLE) - { - LiquidData liqData; - if (!cMap->IsInWater(fx, fy, fz + 1.f/* -0.5f */, &liqData)) // Hack to prevent fishing bobber from failing to land on fishing hole - { // but this is not proper, we really need to ignore not materialized objects - SendCastResult(SPELL_FAILED_NOT_HERE); - SendChannelUpdate(0); - return; - } - - // replace by water level in this case - //fz = cMap->GetWaterLevel(fx, fy); - fz = liqData.level; - } // if gameobject is summoning object, it should be spawned right on caster's position - else if (goinfo->type == GAMEOBJECT_TYPE_SUMMONING_RITUAL) - { + if (goinfo->type == GAMEOBJECT_TYPE_SUMMONING_RITUAL) m_caster->GetPosition(fx, fy, fz); - } GameObject* pGameObj = new GameObject; diff --git a/src/server/game/World/World.cpp b/src/server/game/World/World.cpp index 700039983d3..bde496a9c5d 100644 --- a/src/server/game/World/World.cpp +++ b/src/server/game/World/World.cpp @@ -1266,6 +1266,13 @@ void World::LoadConfigSettings(bool reload) m_int_configs[CONFIG_WINTERGRASP_NOBATTLETIME] = ConfigMgr::GetIntDefault("Wintergrasp.NoBattleTimer", 150); m_int_configs[CONFIG_WINTERGRASP_RESTART_AFTER_CRASH] = ConfigMgr::GetIntDefault("Wintergrasp.CrashRestartTimer", 10); + // Stats limits + m_bool_configs[CONFIG_STATS_LIMITS_ENABLE] = ConfigMgr::GetBoolDefault("Stats.Limits.Enable", false); + m_float_configs[CONFIG_STATS_LIMITS_DODGE] = ConfigMgr::GetFloatDefault("Stats.Limits.Dodge", 95.0f); + m_float_configs[CONFIG_STATS_LIMITS_PARRY] = ConfigMgr::GetFloatDefault("Stats.Limits.Parry", 95.0f); + m_float_configs[CONFIG_STATS_LIMITS_BLOCK] = ConfigMgr::GetFloatDefault("Stats.Limits.Block", 95.0f); + m_float_configs[CONFIG_STATS_LIMITS_CRIT] = ConfigMgr::GetFloatDefault("Stats.Limits.Crit", 95.0f); + // call ScriptMgr if we're reloading the configuration if (reload) sScriptMgr->OnConfigLoad(reload); diff --git a/src/server/game/World/World.h b/src/server/game/World/World.h index ca76bef435d..cf57a178a2c 100644 --- a/src/server/game/World/World.h +++ b/src/server/game/World/World.h @@ -167,6 +167,7 @@ enum WorldBoolConfigs CONFIG_GUILD_LEVELING_ENABLED, CONFIG_UI_QUESTLEVELS_IN_DIALOGS, // Should we add quest levels to the title in the NPC dialogs? CONFIG_EVENT_ANNOUNCE, + CONFIG_STATS_LIMITS_ENABLE, BOOL_CONFIG_VALUE_COUNT }; @@ -183,6 +184,10 @@ enum WorldFloatConfigs CONFIG_CREATURE_FAMILY_ASSISTANCE_RADIUS, CONFIG_THREAT_RADIUS, CONFIG_CHANCE_OF_GM_SURVEY, + CONFIG_STATS_LIMITS_DODGE, + CONFIG_STATS_LIMITS_PARRY, + CONFIG_STATS_LIMITS_BLOCK, + CONFIG_STATS_LIMITS_CRIT, FLOAT_CONFIG_VALUE_COUNT }; diff --git a/src/server/scripts/Commands/cs_reset.cpp b/src/server/scripts/Commands/cs_reset.cpp index 461220faa8e..8e45aa6a855 100644 --- a/src/server/scripts/Commands/cs_reset.cpp +++ b/src/server/scripts/Commands/cs_reset.cpp @@ -99,9 +99,6 @@ public: if (!player->HasAuraType(SPELL_AURA_MOD_SHAPESHIFT)) player->SetShapeshiftForm(FORM_NONE); - player->SetFloatValue(UNIT_FIELD_BOUNDINGRADIUS, DEFAULT_WORLD_OBJECT_SIZE); - player->SetFloatValue(UNIT_FIELD_COMBATREACH, DEFAULT_COMBAT_REACH); - player->setFactionForRace(player->getRace()); player->SetUInt32Value(UNIT_FIELD_BYTES_0, ((player->getRace()) | (player->getClass() << 8) | (player->getGender() << 16) | (powerType << 24))); @@ -116,8 +113,6 @@ public: //-1 is default value player->SetUInt32Value(PLAYER_FIELD_WATCHED_FACTION_INDEX, uint32(-1)); - - //player->SetUInt32Value(PLAYER_FIELD_BYTES, 0xEEE00000); return true; } diff --git a/src/server/scripts/Northrend/Gundrak/gundrak.h b/src/server/scripts/Northrend/Gundrak/gundrak.h index a43edb4fcd7..f47b71cd48e 100644 --- a/src/server/scripts/Northrend/Gundrak/gundrak.h +++ b/src/server/scripts/Northrend/Gundrak/gundrak.h @@ -51,7 +51,8 @@ enum mainCreatures CREATURE_ECK = 29932 }; -enum Gameobjects { +enum Gameobjects +{ GO_SLADRAN_ALTAR = 192518, GO_MOORABI_ALTAR = 192519, @@ -67,7 +68,6 @@ enum Gameobjects { GO_GALDARAH_DOOR3 = 192568, GO_BRIDGE = 193188, GO_COLLISION = 192633 - }; #endif diff --git a/src/server/scripts/Northrend/zone_borean_tundra.cpp b/src/server/scripts/Northrend/zone_borean_tundra.cpp index 147d5eb1370..b1091013270 100644 --- a/src/server/scripts/Northrend/zone_borean_tundra.cpp +++ b/src/server/scripts/Northrend/zone_borean_tundra.cpp @@ -820,7 +820,6 @@ public: void Reset() { WithRedDragonBlood = false; - HarpoonerGUID = 0; } void EnterCombat(Unit* who) @@ -879,6 +878,12 @@ public: } } + if ((me->getFaction() == 35) && (!me->HasAura(SPELL_SUBDUED))) + { + HarpoonerGUID = 0; + me->DisappearAndDie(); + } + if (!UpdateVictim()) return; diff --git a/src/server/scripts/Northrend/zone_howling_fjord.cpp b/src/server/scripts/Northrend/zone_howling_fjord.cpp index 65d69e368b8..268e904e292 100644 --- a/src/server/scripts/Northrend/zone_howling_fjord.cpp +++ b/src/server/scripts/Northrend/zone_howling_fjord.cpp @@ -276,56 +276,6 @@ public: }; /*###### -## npc_mcgoyver -######*/ - -#define GOSSIP_ITEM_MG_I "Walt sent me to pick up some dark iron ingots." -#define GOSSIP_ITEM_MG_II "Yarp." - -enum eMcGoyver -{ - QUEST_WE_CAN_REBUILD_IT = 11483, - - SPELL_CREATURE_DARK_IRON_INGOTS = 44512, - SPELL_TAXI_EXPLORERS_LEAGUE = 44280, - - GOSSIP_TEXTID_MCGOYVER = 12193 -}; - -class npc_mcgoyver : public CreatureScript -{ -public: - npc_mcgoyver() : CreatureScript("npc_mcgoyver") { } - - bool OnGossipHello(Player* player, Creature* creature) - { - if (player->GetQuestStatus(QUEST_WE_CAN_REBUILD_IT) == QUEST_STATUS_INCOMPLETE) - player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_ITEM_MG_I, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+1); - - player->SEND_GOSSIP_MENU(player->GetGossipTextId(creature), creature->GetGUID()); - return true; - } - - bool OnGossipSelect(Player* player, Creature* creature, uint32 /*sender*/, uint32 action) - { - player->PlayerTalkClass->ClearMenus(); - switch (action) - { - case GOSSIP_ACTION_INFO_DEF+1: - player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_ITEM_MG_II, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+2); - player->SEND_GOSSIP_MENU(GOSSIP_TEXTID_MCGOYVER, creature->GetGUID()); - player->CastSpell(player, SPELL_CREATURE_DARK_IRON_INGOTS, true); - break; - case GOSSIP_ACTION_INFO_DEF+2: - player->CastSpell(player, SPELL_TAXI_EXPLORERS_LEAGUE, true); - player->CLOSE_GOSSIP_MENU(); - break; - } - return true; - } -}; - -/*###### ## npc_daegarn ######*/ @@ -435,6 +385,5 @@ void AddSC_howling_fjord() new npc_apothecary_hanes; new npc_plaguehound_tracker; new npc_razael_and_lyana; - new npc_mcgoyver; new npc_daegarn; } diff --git a/src/server/scripts/Spells/spell_generic.cpp b/src/server/scripts/Spells/spell_generic.cpp index a84f299167e..cb010f44f9a 100644 --- a/src/server/scripts/Spells/spell_generic.cpp +++ b/src/server/scripts/Spells/spell_generic.cpp @@ -3634,6 +3634,45 @@ class spell_gen_orc_disguise : public SpellScriptLoader } }; +enum WhisperGulchYoggSaronWhisper +{ + SPELL_YOGG_SARON_WHISPER_DUMMY = 29072 +}; + +class spell_gen_whisper_gulch_yogg_saron_whisper : public SpellScriptLoader +{ + public: + spell_gen_whisper_gulch_yogg_saron_whisper() : SpellScriptLoader("spell_gen_whisper_gulch_yogg_saron_whisper") { } + + class spell_gen_whisper_gulch_yogg_saron_whisper_AuraScript : public AuraScript + { + PrepareAuraScript(spell_gen_whisper_gulch_yogg_saron_whisper_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) + { + if (!sSpellMgr->GetSpellInfo(SPELL_YOGG_SARON_WHISPER_DUMMY)) + return false; + return true; + } + + void HandleEffectPeriodic(AuraEffect const* /*aurEff*/) + { + PreventDefaultAction(); + GetTarget()->CastSpell((Unit*)NULL, SPELL_YOGG_SARON_WHISPER_DUMMY, true); + } + + void Register() + { + OnEffectPeriodic += AuraEffectPeriodicFn(spell_gen_whisper_gulch_yogg_saron_whisper_AuraScript::HandleEffectPeriodic, EFFECT_0, SPELL_AURA_PERIODIC_DUMMY); + } + }; + + AuraScript* GetAuraScript() const + { + return new spell_gen_whisper_gulch_yogg_saron_whisper_AuraScript(); + } +}; + void AddSC_generic_spell_scripts() { new spell_gen_absorb0_hitlimit1(); @@ -3722,4 +3761,5 @@ void AddSC_generic_spell_scripts() new spell_gen_two_forms(); new spell_gen_darkflight(); new spell_gen_orc_disguise(); + new spell_gen_whisper_gulch_yogg_saron_whisper(); } diff --git a/src/server/scripts/World/npcs_special.cpp b/src/server/scripts/World/npcs_special.cpp index a5058cbf1d7..960a3fce7cd 100644 --- a/src/server/scripts/World/npcs_special.cpp +++ b/src/server/scripts/World/npcs_special.cpp @@ -1257,10 +1257,10 @@ public: if (creature->isTrainer()) player->ADD_GOSSIP_ITEM(GOSSIP_ICON_TRAINER, GOSSIP_TEXT_TRAIN, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_TRAIN); - if (creature->isCanTrainingAndResetTalentsOf(player)) + if (player->getClass() == CLASS_ROGUE) player->ADD_GOSSIP_ITEM(GOSSIP_ICON_TRAINER, GOSSIP_HELLO_ROGUE1, GOSSIP_SENDER_MAIN, GOSSIP_OPTION_UNLEARNTALENTS); - if (player->GetSpecsCount() == 1 && creature->isCanTrainingAndResetTalentsOf(player) && player->getLevel() >= sWorld->getIntConfig(CONFIG_MIN_DUALSPEC_LEVEL)) + if (player->GetSpecsCount() == 1 && player->getLevel() >= sWorld->getIntConfig(CONFIG_MIN_DUALSPEC_LEVEL)) player->ADD_GOSSIP_ITEM(GOSSIP_ICON_TRAINER, GOSSIP_HELLO_ROGUE3, GOSSIP_SENDER_MAIN, GOSSIP_OPTION_LEARNDUALSPEC); if (player->getClass() == CLASS_ROGUE && player->getLevel() >= 24 && !player->HasItemCount(17126) && !player->GetQuestRewardStatus(6681)) diff --git a/src/server/shared/Database/Implementation/CharacterDatabase.cpp b/src/server/shared/Database/Implementation/CharacterDatabase.cpp index cc1a7f3fe32..23e9d09eb1d 100644 --- a/src/server/shared/Database/Implementation/CharacterDatabase.cpp +++ b/src/server/shared/Database/Implementation/CharacterDatabase.cpp @@ -395,7 +395,7 @@ void CharacterDatabaseConnection::DoPrepareStatements() PrepareStatement(CHAR_INS_GROUP, "INSERT INTO groups (guid, leaderGuid, lootMethod, looterGuid, lootThreshold, icon1, icon2, icon3, icon4, icon5, icon6, icon7, icon8, groupType, difficulty, raiddifficulty) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC); PrepareStatement(CHAR_INS_GROUP_MEMBER, "INSERT INTO group_member (guid, memberGuid, memberFlags, subgroup, roles) VALUES(?, ?, ?, ?, ?)", CONNECTION_ASYNC); PrepareStatement(CHAR_DEL_GROUP_MEMBER, "DELETE FROM group_member WHERE memberGuid = ?", CONNECTION_ASYNC); - PrepareStatement(CHAR_DEL_GROUP_INSTANCE_PERM_BINDING, "DELETE FROM group_instance WHERE guid = ? AND (permanent = 1 OR instance IN (SELECT instance FROM character_instance WHERE guid = ?))", CONNECTION_ASYNC); + PrepareStatement(CHAR_DEL_GROUP_INSTANCE_PERM_BINDING, "DELETE FROM group_instance WHERE guid = ? AND instance = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_UPD_GROUP_LEADER, "UPDATE groups SET leaderGuid = ? WHERE guid = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_UPD_GROUP_TYPE, "UPDATE groups SET groupType = ? WHERE guid = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_UPD_GROUP_MEMBER_SUBGROUP, "UPDATE group_member SET subgroup = ? WHERE memberGuid = ?", CONNECTION_ASYNC); diff --git a/src/server/worldserver/worldserver.conf.dist b/src/server/worldserver/worldserver.conf.dist index 622070c121d..ac52c95202e 100644 --- a/src/server/worldserver/worldserver.conf.dist +++ b/src/server/worldserver/worldserver.conf.dist @@ -18,6 +18,7 @@ # GAME MASTER SETTINGS # VISIBILITY AND DISTANCES # SERVER RATES +# STATS LIMITS # AUTO BROADCAST # BATTLEGROUND CONFIG # BATTLEFIELD CONFIG @@ -1972,6 +1973,28 @@ Die.Command.Mode = 1 ################################################################################################### ################################################################################################### +# STATS LIMITS +# +# Stats.Limits.Enable +# Description: Enable or disable stats system +# Default: 0 - Disabled + +Stats.Limits.Enable = 0 + +# +# Stats.Limit.[STAT] +# Description: Set percentage limit for dodge, parry, block and crit rating +# Default: 95.0 (95%) + +Stats.Limits.Dodge = 95.0 +Stats.Limits.Parry = 95.0 +Stats.Limits.Block = 95.0 +Stats.Limits.Crit = 95.0 + +# +################################################################################################### + +################################################################################################### # AUTO BROADCAST # # AutoBroadcast.On |