Merge branch 'master' into Playerbot

This commit is contained in:
Yunfan Li
2024-09-17 23:15:28 +08:00
25 changed files with 373 additions and 96 deletions
@@ -1087,7 +1087,7 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui
for (RewardedQuestSet::const_iterator itr = rewQuests.begin(); itr != rewQuests.end(); ++itr)
{
Quest const* quest = sObjectMgr->GetQuestTemplate(*itr);
if (quest && quest->GetZoneOrSort() >= 0 && uint32(quest->GetZoneOrSort()) == achievementCriteria->complete_quests_in_zone.zoneID)
if (quest && quest->GetZoneOrSort() >= 0 && uint32(quest->GetZoneOrSort()) == achievementCriteria->complete_quests_in_zone.zoneID && !(quest->HasSpecialFlag(QUEST_SPECIAL_FLAGS_NO_LOREMASTER_COUNT)))
++counter;
}
SetCriteriaProgress(achievementCriteria, counter);
@@ -2503,7 +2503,7 @@ void AchievementMgr::ProcessOfflineUpdate(AchievementOfflinePlayerUpdate const&
{
AchievementEntry const* achievement = sAchievementStore.LookupEntry(update.arg1);
ASSERT(achievement != NULL, "Not found achievement to complete for offline achievements update. Wrong arg1 ({}) value?", update.arg1);
ASSERT(achievement, "Not found achievement to complete for offline achievements update. Wrong arg1 ({}) value?", update.arg1);
CompletedAchievement(achievement);
break;
@@ -3142,7 +3142,7 @@ void AchievementGlobalMgr::CompletedAchievementForOfflinePlayer(ObjectGuid::LowT
{
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_CHAR_ACHIEVEMENT_OFFLINE_UPDATES);
stmt->SetData(0, playerLowGuid);
stmt->SetData(1, uint32(ACHIEVEMENT_OFFLINE_PLAYER_UPDATE_TYPE_COMPLETE_ACHIEVEMENT));
stmt->SetData(1, ACHIEVEMENT_OFFLINE_PLAYER_UPDATE_TYPE_COMPLETE_ACHIEVEMENT);
stmt->SetData(2, entry->ID);
stmt->SetData(3, 0);
stmt->SetData(4, 0);
@@ -3153,7 +3153,7 @@ void AchievementGlobalMgr::UpdateAchievementCriteriaForOfflinePlayer(ObjectGuid:
{
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_CHAR_ACHIEVEMENT_OFFLINE_UPDATES);
stmt->SetData(0, playerLowGuid);
stmt->SetData(1, uint32(ACHIEVEMENT_OFFLINE_PLAYER_UPDATE_TYPE_UPDATE_CRITERIA));
stmt->SetData(1, ACHIEVEMENT_OFFLINE_PLAYER_UPDATE_TYPE_UPDATE_CRITERIA);
stmt->SetData(2, type);
stmt->SetData(3, miscValue1);
stmt->SetData(4, miscValue2);
@@ -325,6 +325,9 @@ Creature* BattlegroundAV::AddAVCreature(uint16 cinfoid, uint16 type)
if (creature->GetEntry() == BG_AV_CreatureInfo[AV_NPC_A_CAPTAIN] || creature->GetEntry() == BG_AV_CreatureInfo[AV_NPC_H_CAPTAIN])
creature->SetRespawnDelay(RESPAWN_ONE_DAY); /// @todo: look if this can be done by database + also add this for the wingcommanders
if (creature->GetEntry() == BG_AV_CreatureInfo[AV_NPC_A_TOWERDEFENSE] || creature->GetEntry() == BG_AV_CreatureInfo[AV_NPC_H_TOWERDEFENSE])
creature->SetUnitFlag(UNIT_FLAG_DISABLE_MOVE);
if ((isStatic && cinfoid >= 10 && cinfoid <= 14) || (!isStatic && ((cinfoid >= AV_NPC_A_GRAVEDEFENSE0 && cinfoid <= AV_NPC_A_GRAVEDEFENSE3) ||
(cinfoid >= AV_NPC_H_GRAVEDEFENSE0 && cinfoid <= AV_NPC_H_GRAVEDEFENSE3))))
{
@@ -1118,21 +1118,21 @@ const float BG_AV_CreaturePos[AV_CPLACE_MAX][4] =
enum BG_AV_CreatureIds
{
AV_NPC_A_TOWERDEFENSE = 0, // stormpike bowman
AV_NPC_A_GRAVEDEFENSE0 = 1, // stormpike Defender
AV_NPC_A_GRAVEDEFENSE1 = 2, // seasoned defender
AV_NPC_A_GRAVEDEFENSE2 = 3, // veteran defender
AV_NPC_A_GRAVEDEFENSE3 = 4, // champion defender
AV_NPC_A_CAPTAIN = 5, // balinda
AV_NPC_A_BOSS = 6, // vanndar
AV_NPC_A_GRAVEDEFENSE0 = 0, // Stormpike defender
AV_NPC_A_GRAVEDEFENSE1 = 1, // Seasoned defender
AV_NPC_A_GRAVEDEFENSE2 = 2, // Veteran defender
AV_NPC_A_GRAVEDEFENSE3 = 3, // Champion defender
AV_NPC_A_TOWERDEFENSE = 4, // Stormpike bowman
AV_NPC_A_CAPTAIN = 5, // Balinda
AV_NPC_A_BOSS = 6, // Vanndar
AV_NPC_H_TOWERDEFENSE = 7, // frostwolf bowman
AV_NPC_H_GRAVEDEFENSE0 = 8, // frostwolf guardian
AV_NPC_H_GRAVEDEFENSE1 = 9, // seasoned guardian
AV_NPC_H_GRAVEDEFENSE2 = 10, // veteran guardian
AV_NPC_H_GRAVEDEFENSE3 = 11, // champion guardian
AV_NPC_H_CAPTAIN = 12, // galvangar
AV_NPC_H_BOSS = 13, // drek thar
AV_NPC_H_GRAVEDEFENSE0 = 7, // Frostwolf guardian
AV_NPC_H_GRAVEDEFENSE1 = 8, // Seasoned guardian
AV_NPC_H_GRAVEDEFENSE2 = 9, // Veteran guardian
AV_NPC_H_GRAVEDEFENSE3 = 10, // Champion guardian
AV_NPC_H_TOWERDEFENSE = 11, // Frostwolf bowman
AV_NPC_H_CAPTAIN = 12, // Galvangar
AV_NPC_H_BOSS = 13, // Drek thar
AV_NPC_A_MARSHAL_SOUTH = 14,
AV_NPC_MARSHAL_NORTH = 15,
@@ -12437,6 +12437,14 @@ void Player::AutoUnequipOffhandIfNeed(bool force /*= false*/)
if (!CanDualWield() && (offItem->GetTemplate()->InventoryType == INVTYPE_WEAPONOFFHAND || offItem->GetTemplate()->InventoryType == INVTYPE_WEAPON))
force = true;
// unequip offhand weapon if player main hand weapon is a polearm or staff or fishing pole
if (Item* mhWeapon = GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_MAINHAND))
if (ItemTemplate const* mhWeaponProto = mhWeapon->GetTemplate())
if (mhWeaponProto->SubClass == ITEM_SUBCLASS_WEAPON_POLEARM ||
mhWeaponProto->SubClass == ITEM_SUBCLASS_WEAPON_STAFF ||
mhWeaponProto->SubClass == ITEM_SUBCLASS_WEAPON_FISHING_POLE)
force = true;
// need unequip offhand for 2h-weapon without TitanGrip (in any from hands)
if (!force && (CanTitanGrip() || (offItem->GetTemplate()->InventoryType != INVTYPE_2HWEAPON && !IsTwoHandUsed())))
{
@@ -201,28 +201,11 @@ uint8 Player::FindEquipSlot(ItemTemplate const* proto, uint32 slot, bool swap) c
break;
case INVTYPE_2HWEAPON:
slots[0] = EQUIPMENT_SLOT_MAINHAND;
if (Item* mhWeapon = GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_MAINHAND))
{
if (ItemTemplate const* mhWeaponProto = mhWeapon->GetTemplate())
{
if (mhWeaponProto->SubClass == ITEM_SUBCLASS_WEAPON_POLEARM || mhWeaponProto->SubClass == ITEM_SUBCLASS_WEAPON_STAFF)
{
const_cast<Player*>(this)->AutoUnequipOffhandIfNeed(true);
break;
}
}
}
if (GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_OFFHAND))
{
if (proto->SubClass == ITEM_SUBCLASS_WEAPON_POLEARM || proto->SubClass == ITEM_SUBCLASS_WEAPON_STAFF)
{
const_cast<Player*>(this)->AutoUnequipOffhandIfNeed(true);
break;
}
}
if (CanDualWield() && CanTitanGrip() && proto->SubClass != ITEM_SUBCLASS_WEAPON_POLEARM && proto->SubClass != ITEM_SUBCLASS_WEAPON_STAFF && proto->SubClass != ITEM_SUBCLASS_WEAPON_FISHING_POLE)
slots[1] = EQUIPMENT_SLOT_OFFHAND;
if (Item* mhWeapon = GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_MAINHAND))
if (ItemTemplate const* mhWeaponProto = mhWeapon->GetTemplate())
if (mhWeaponProto->SubClass != ITEM_SUBCLASS_WEAPON_POLEARM && mhWeaponProto->SubClass != ITEM_SUBCLASS_WEAPON_STAFF && mhWeaponProto->SubClass != ITEM_SUBCLASS_WEAPON_FISHING_POLE)
slots[1] = EQUIPMENT_SLOT_OFFHAND;
break;
case INVTYPE_TABARD:
slots[0] = EQUIPMENT_SLOT_TABARD;
@@ -1962,6 +1945,14 @@ InventoryResult Player::CanEquipItem(uint8 slot, uint16& dest, Item* pItem, bool
return EQUIP_ERR_CANT_DUAL_WIELD;
}
// Do not allow offhand with main hand polearm, staff or fishing pole
if (Item* mhWeapon = GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_MAINHAND))
if (ItemTemplate const* mhWeaponProto = mhWeapon->GetTemplate())
if (mhWeaponProto->SubClass == ITEM_SUBCLASS_WEAPON_POLEARM ||
mhWeaponProto->SubClass == ITEM_SUBCLASS_WEAPON_STAFF ||
mhWeaponProto->SubClass == ITEM_SUBCLASS_WEAPON_FISHING_POLE)
return EQUIP_ERR_CANT_EQUIP_WITH_TWOHANDED;
if (IsTwoHandUsed())
return EQUIP_ERR_CANT_EQUIP_WITH_TWOHANDED;
}
@@ -1977,7 +1968,7 @@ InventoryResult Player::CanEquipItem(uint8 slot, uint16& dest, Item* pItem, bool
else if (eslot != EQUIPMENT_SLOT_MAINHAND)
return EQUIP_ERR_ITEM_CANT_BE_EQUIPPED;
if (!CanTitanGrip())
if (!CanTitanGrip() || (pProto->SubClass == ITEM_SUBCLASS_WEAPON_POLEARM || pProto->SubClass == ITEM_SUBCLASS_WEAPON_STAFF || pProto->SubClass == ITEM_SUBCLASS_WEAPON_FISHING_POLE))
{
// offhand item must can be stored in inventory for offhand item and it also must be unequipped
Item* offItem = GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_OFFHAND);
+9 -8
View File
@@ -1847,7 +1847,10 @@ void Unit::DealMeleeDamage(CalcDamageInfo* damageInfo, bool durabilityLoss)
if (damageInfo->blocked_amount && damageInfo->TargetState != VICTIMSTATE_BLOCKS)
victim->HandleEmoteCommand(EMOTE_ONESHOT_PARRY_SHIELD);
if (damageInfo->TargetState == VICTIMSTATE_PARRY)
// Parry haste is enabled if it's not a creature or if the creature doesn't have the NO_PARRY_HASTEN flag.
bool isParryHasteEnabled = !victim->IsCreature() ||
!victim->ToCreature()->GetCreatureTemplate()->HasFlagsExtra(CREATURE_FLAG_EXTRA_NO_PARRY_HASTEN);
if (damageInfo->TargetState == VICTIMSTATE_PARRY && isParryHasteEnabled)
{
// Get attack timers
float offtime = float(victim->getAttackTimer(OFF_ATTACK));
@@ -20256,13 +20259,13 @@ void Unit::OutDebugInfo() const
class AuraMunchingQueue : public BasicEvent
{
public:
AuraMunchingQueue(Unit& owner, ObjectGuid targetGUID, int32 basePoints, uint32 spellId, AuraEffect* aurEff) : _owner(owner), _targetGUID(targetGUID), _basePoints(basePoints), _spellId(spellId), _aurEff(aurEff) { }
AuraMunchingQueue(Unit& owner, ObjectGuid targetGUID, int32 basePoints, uint32 spellId) : _owner(owner), _targetGUID(targetGUID), _basePoints(basePoints), _spellId(spellId) { }
bool Execute(uint64 /*eventTime*/, uint32 /*updateTime*/) override
{
if (_owner.IsInWorld() && _owner.FindMap())
if (Unit* target = ObjectAccessor::GetUnit(_owner, _targetGUID))
_owner.CastCustomSpell(_spellId, SPELLVALUE_BASE_POINT0, _basePoints, target, TriggerCastFlags(TRIGGERED_FULL_MASK & ~TRIGGERED_NO_PERIODIC_RESET), nullptr, _aurEff, _owner.GetGUID());
_owner.CastCustomSpell(_spellId, SPELLVALUE_BASE_POINT0, _basePoints, target, TriggerCastFlags(TRIGGERED_FULL_MASK & ~TRIGGERED_NO_PERIODIC_RESET), nullptr, nullptr, _owner.GetGUID());
return true;
}
@@ -20272,15 +20275,13 @@ private:
ObjectGuid _targetGUID;
int32 _basePoints;
uint32 _spellId;
AuraEffect* _aurEff;
};
void Unit::CastDelayedSpellWithPeriodicAmount(Unit* caster, uint32 spellId, AuraType auraType, int32 addAmount, uint8 effectIndex)
{
AuraEffect* aurEff = nullptr;
for (AuraEffectList::iterator i = m_modAuras[auraType].begin(); i != m_modAuras[auraType].end(); ++i)
{
aurEff = *i;
AuraEffect* aurEff = *i;
if (aurEff->GetCasterGUID() != caster->GetGUID() || aurEff->GetId() != spellId || aurEff->GetEffIndex() != effectIndex || !aurEff->GetTotalTicks())
continue;
@@ -20290,9 +20291,9 @@ void Unit::CastDelayedSpellWithPeriodicAmount(Unit* caster, uint32 spellId, Aura
// xinef: delay only for casting on different unit
if (this == caster || !sWorld->getBoolConfig(CONFIG_MUNCHING_BLIZZLIKE))
caster->CastCustomSpell(spellId, SPELLVALUE_BASE_POINT0, addAmount, this, TriggerCastFlags(TRIGGERED_FULL_MASK & ~TRIGGERED_NO_PERIODIC_RESET), nullptr, aurEff, caster->GetGUID());
caster->CastCustomSpell(spellId, SPELLVALUE_BASE_POINT0, addAmount, this, TriggerCastFlags(TRIGGERED_FULL_MASK & ~TRIGGERED_NO_PERIODIC_RESET), nullptr, nullptr, caster->GetGUID());
else
caster->m_Events.AddEvent(new AuraMunchingQueue(*caster, GetGUID(), addAmount, spellId, aurEff), caster->m_Events.CalculateQueueTime(400));
caster->m_Events.AddEvent(new AuraMunchingQueue(*caster, GetGUID(), addAmount, spellId), caster->m_Events.CalculateQueueTime(400));
}
void Unit::SendClearTarget()
@@ -982,8 +982,12 @@ void WorldSession::HandlePlayerLoginFromDB(LoginQueryHolder const& holder)
if (pCurrChar->HasAtLoginFlag(AT_LOGIN_CHECK_ACHIEVS))
{
pCurrChar->RemoveAtLoginFlag(AT_LOGIN_CHECK_ACHIEVS, true);
pCurrChar->CheckAllAchievementCriteria();
// If we process the check while players are loading they won't be notified of the changes.
pCurrChar->m_Events.AddEventAtOffset([pCurrChar]
{
pCurrChar->RemoveAtLoginFlag(AT_LOGIN_CHECK_ACHIEVS, true);
pCurrChar->CheckAllAchievementCriteria();
}, 1s);
}
bool firstLogin = pCurrChar->HasAtLoginFlag(AT_LOGIN_FIRST);
@@ -761,6 +761,24 @@ void InstanceScript::LoadInstanceSavedGameobjectStateData()
}
}
bool InstanceScript::AllBossesDone() const
{
for (auto const& boss : bosses)
if (boss.state != DONE)
return false;
return true;
}
bool InstanceScript::AllBossesDone(std::initializer_list<uint32> bossIds) const
{
for (auto const& bossId : bossIds)
if (!IsBossDone(bossId))
return false;
return true;
}
std::string InstanceScript::GetBossStateName(uint8 state)
{
// See enum EncounterState in InstanceScript.h
@@ -275,6 +275,10 @@ public:
void LoadInstanceSavedGameobjectStateData();
[[nodiscard]] bool IsBossDone(uint32 bossId) const { return GetBossState(bossId) == DONE; };
[[nodiscard]] bool AllBossesDone() const;
[[nodiscard]] bool AllBossesDone(std::initializer_list<uint32> bossIds) const;
TaskScheduler scheduler;
protected:
void SetHeaders(std::string const& dataHeaders);
+1 -1
View File
@@ -187,7 +187,7 @@ void Quest::LoadQuestTemplateAddon(Field* fields)
RequiredMaxRepValue = fields[14].Get<int32>();
StartItemCount = fields[15].Get<uint8>();
RewardMailSenderEntry = fields[16].Get<uint32>();
SpecialFlags = fields[17].Get<uint8>();
SpecialFlags = fields[17].Get<uint32>();
if ((SpecialFlags & QUEST_SPECIAL_FLAGS_AUTO_ACCEPT) && !sWorld->getBoolConfig(CONFIG_QUEST_IGNORE_AUTO_ACCEPT))
{
+7 -6
View File
@@ -165,17 +165,18 @@ enum QuestSpecialFlags
QUEST_SPECIAL_FLAGS_CAST = 0x0020, // Set by 32 in SpecialFlags in DB if the quest requires RequiredOrNpcGo killcredit but NOT kill (a spell cast)
QUEST_SPECIAL_FLAGS_NO_REP_SPILLOVER = 0x0040, // Set by 64 in SpecialFlags in DB if the quest does not share rewarded reputation with other allied factions
QUEST_SPECIAL_FLAGS_CAN_FAIL_IN_ANY_STATE = 0x0080, // Set by 128 in SpecialFlags in DB if the quest is allowed to fail in Player::FailQuest() independant of its current state
QUEST_SPECIAL_FLAGS_NO_LOREMASTER_COUNT = 0x0100, // Set by 256 in SpecialFlags in DB if the quest should not count towards Loremaster Achievement
// room for more custom flags
QUEST_SPECIAL_FLAGS_DB_ALLOWED = QUEST_SPECIAL_FLAGS_REPEATABLE | QUEST_SPECIAL_FLAGS_EXPLORATION_OR_EVENT | QUEST_SPECIAL_FLAGS_AUTO_ACCEPT |
QUEST_SPECIAL_FLAGS_DF_QUEST | QUEST_SPECIAL_FLAGS_MONTHLY | QUEST_SPECIAL_FLAGS_CAST | QUEST_SPECIAL_FLAGS_NO_REP_SPILLOVER |
QUEST_SPECIAL_FLAGS_CAN_FAIL_IN_ANY_STATE,
QUEST_SPECIAL_FLAGS_CAN_FAIL_IN_ANY_STATE | QUEST_SPECIAL_FLAGS_NO_LOREMASTER_COUNT,
QUEST_SPECIAL_FLAGS_DELIVER = 0x0100, // Internal flag computed only
QUEST_SPECIAL_FLAGS_SPEAKTO = 0x0200, // Internal flag computed only
QUEST_SPECIAL_FLAGS_KILL = 0x0400, // Internal flag computed only
QUEST_SPECIAL_FLAGS_TIMED = 0x0800, // Internal flag computed only
QUEST_SPECIAL_FLAGS_PLAYER_KILL = 0x1000 // Internal flag computed only
QUEST_SPECIAL_FLAGS_DELIVER = 0x0200, // Internal flag computed only
QUEST_SPECIAL_FLAGS_SPEAKTO = 0x0400, // Internal flag computed only
QUEST_SPECIAL_FLAGS_KILL = 0x0800, // Internal flag computed only
QUEST_SPECIAL_FLAGS_TIMED = 0x1000, // Internal flag computed only
QUEST_SPECIAL_FLAGS_PLAYER_KILL = 0x2000 // Internal flag computed only
};
struct QuestLocale
+1 -2
View File
@@ -2931,8 +2931,7 @@ void Spell::DoAllEffectOnTarget(TargetInfo* target)
{
if (missInfo != SPELL_MISS_EVADE && !m_caster->IsFriendlyTo(effectUnit) && (!m_spellInfo->IsPositive() || m_spellInfo->HasEffect(SPELL_EFFECT_DISPEL)))
{
if (!m_triggeredByAuraSpell.spellInfo || (!(m_triggeredByAuraSpell.spellInfo->Effects[m_triggeredByAuraSpell.effectIndex].TriggerSpell == m_spellInfo->Id) && !(m_triggeredByAuraSpell.spellInfo->IsAuraEffectEqual(m_spellInfo))))
m_caster->CombatStart(effectUnit, !(m_spellInfo->AttributesEx3 & SPELL_ATTR3_SUPRESS_TARGET_PROCS));
m_caster->CombatStart(effectUnit, !(m_spellInfo->AttributesEx3 & SPELL_ATTR3_SUPRESS_TARGET_PROCS));
// Patch 3.0.8: All player spells which cause a creature to become aggressive to you will now also immediately cause the creature to be tapped.
if (effectUnit->IsInCombatWith(m_caster))
@@ -4831,6 +4831,12 @@ void SpellMgr::LoadSpellInfoCorrections()
spellInfo->Effects[EFFECT_0].RadiusEntry = sSpellRadiusStore.LookupEntry(EFFECT_RADIUS_3_YARDS);
});
// Paralyze
ApplySpellFix({ 41083 }, [](SpellInfo* spellInfo)
{
spellInfo->AttributesEx5 |= SPELL_ATTR5_ALLOW_ACTION_DURING_CHANNEL;
});
for (uint32 i = 0; i < GetSpellInfoStoreSize(); ++i)
{
SpellInfo* spellInfo = mSpellInfoMap[i];