Merge branch 'master' into Playerbot

# Conflicts:
#	src/server/game/Server/WorldSession.cpp
#	src/server/game/Server/WorldSession.h
This commit is contained in:
郑佩茹
2022-12-07 14:46:21 -07:00
196 changed files with 9181 additions and 1080 deletions

View File

@@ -52,6 +52,7 @@ GameObject::GameObject() : WorldObject(false), MovableMapObject(),
m_respawnDelayTime = 300;
m_despawnDelay = 0;
m_despawnRespawnTime = 0s;
m_restockTime = 0s;
m_lootState = GO_NOT_READY;
m_spawnedByDefault = true;
m_allowModifyDestructibleBuilding = true;
@@ -585,6 +586,16 @@ void GameObject::Update(uint32 diff)
spellCaster->CastSpell(spellCaster, spellId, triggered);
return;
}
case GAMEOBJECT_TYPE_CHEST:
if (m_restockTime > GameTime::GetGameTime())
{
return;
}
// If there is no restock timer, or if the restock timer passed, the chest becomes ready to loot
m_restockTime = 0s;
m_lootState = GO_READY;
AddToObjectUpdateIfNeeded();
break;
default:
m_lootState = GO_READY; // for other GOis same switched without delay to GO_READY
break;
@@ -768,6 +779,14 @@ void GameObject::Update(uint32 diff)
m_groupLootTimer -= diff;
}
}
// Non-consumable chest was partially looted and restock time passed, restock all loot now
if (GetGOInfo()->chest.consumable == 0 && GameTime::GetGameTime() >= m_restockTime)
{
m_restockTime = 0s;
m_lootState = GO_READY;
AddToObjectUpdateIfNeeded();
}
break;
case GAMEOBJECT_TYPE_TRAP:
{
@@ -826,21 +845,29 @@ void GameObject::Update(uint32 diff)
loot.clear();
//! If this is summoned by a spell with ie. SPELL_EFFECT_SUMMON_OBJECT_WILD, with or without owner, we check respawn criteria based on spell
//! The GetOwnerGUID() check is mostly for compatibility with hacky scripts - 99% of the time summoning should be done trough spells.
if (GetSpellId() || GetOwnerGUID())
// Do not delete chests or goobers that are not consumed on loot, while still allowing them to despawn when they expire if summoned
bool isSummonedAndExpired = (GetOwner() || GetSpellId()) && m_respawnTime == 0;
if ((GetGoType() == GAMEOBJECT_TYPE_CHEST || GetGoType() == GAMEOBJECT_TYPE_GOOBER) && !GetGOInfo()->IsDespawnAtAction() && !isSummonedAndExpired)
{
//Don't delete spell spawned chests, which are not consumed on loot
if (m_respawnTime > 0 && GetGoType() == GAMEOBJECT_TYPE_CHEST && !GetGOInfo()->IsDespawnAtAction())
if (GetGoType() == GAMEOBJECT_TYPE_CHEST && GetGOInfo()->chest.chestRestockTime > 0)
{
UpdateObjectVisibility();
SetLootState(GO_READY);
// Start restock timer when the chest is fully looted
m_restockTime = GameTime::GetGameTime() + Seconds(GetGOInfo()->chest.chestRestockTime);
SetLootState(GO_NOT_READY);
AddToObjectUpdateIfNeeded();
}
else
{
SetRespawnTime(0);
Delete();
SetLootState(GO_READY);
}
UpdateObjectVisibility();
return;
}
else if (GetOwnerGUID() || GetSpellId())
{
SetRespawnTime(0);
Delete();
return;
}
@@ -2422,6 +2449,13 @@ void GameObject::SetLootState(LootState state, Unit* unit)
AI()->OnStateChanged(state, unit);
sScriptMgr->OnGameObjectLootStateChanged(this, state, unit);
// Start restock timer if the chest is partially looted or not looted at all
if (GetGoType() == GAMEOBJECT_TYPE_CHEST && state == GO_ACTIVATED && GetGOInfo()->chest.chestRestockTime > 0 && m_restockTime == 0s)
{
m_restockTime = GameTime::GetGameTime() + Seconds(GetGOInfo()->chest.chestRestockTime);
}
// pussywizard: lootState has nothing to do with collision, it depends entirely on GOState. Loot state is for timed close/open door and respawning, which then sets GOState
/*if (m_model)
{

View File

@@ -1072,9 +1072,10 @@ protected:
uint32 m_respawnDelayTime; // (secs) if 0 then current GO state no dependent from timer
uint32 m_despawnDelay;
Seconds m_despawnRespawnTime; // override respawn time after delayed despawn
Seconds m_restockTime;
LootState m_lootState;
bool m_spawnedByDefault;
uint32 m_cooldownTime; // used as internal reaction delay time store (not state change reaction).
uint32 m_cooldownTime; // used as internal reaction delay time store (not state change reaction).
// For traps this: spell casting cooldown, for doors/buttons: reset time.
std::unordered_map<ObjectGuid, int32> m_SkillupList;

View File

@@ -1737,7 +1737,7 @@ bool WorldObject::CanSeeOrDetect(WorldObject const* obj, bool ignoreStealth, boo
// Creature scripts
if (Creature const* cObj = obj->ToCreature())
{
if (Player const* player = this->ToPlayer())
if (Player const* player = ToPlayer())
{
if (cObj->IsAIEnabled && !cObj->AI()->CanBeSeen(player))
{
@@ -1745,8 +1745,7 @@ bool WorldObject::CanSeeOrDetect(WorldObject const* obj, bool ignoreStealth, boo
}
ConditionList conditions = sConditionMgr->GetConditionsForNotGroupedEntry(CONDITION_SOURCE_TYPE_CREATURE_VISIBILITY, cObj->GetEntry());
if (!sConditionMgr->IsObjectMeetToConditions((WorldObject*)this, conditions))
if (!sConditionMgr->IsObjectMeetToConditions((WorldObject*)this, (WorldObject*)obj, conditions))
{
return false;
}
@@ -1755,8 +1754,12 @@ bool WorldObject::CanSeeOrDetect(WorldObject const* obj, bool ignoreStealth, boo
// Gameobject scripts
if (GameObject const* goObj = obj->ToGameObject())
if (this->ToPlayer() && !goObj->AI()->CanBeSeen(this->ToPlayer()))
{
if (ToPlayer() && !goObj->AI()->CanBeSeen(ToPlayer()))
{
return false;
}
}
// pussywizard: arena spectator
if (obj->GetTypeId() == TYPEID_PLAYER)

View File

@@ -1316,7 +1316,7 @@ public:
void DestroyZoneLimitedItem(bool update, uint32 new_zone);
void SplitItem(uint16 src, uint16 dst, uint32 count);
void SwapItem(uint16 src, uint16 dst);
void AddItemToBuyBackSlot(Item* pItem);
void AddItemToBuyBackSlot(Item* pItem, uint32 money);
Item* GetItemFromBuyBackSlot(uint32 slot);
void RemoveItemFromBuyBackSlot(uint32 slot, bool del);
[[nodiscard]] uint32 GetMaxKeyringSize() const { return KEYRING_SLOT_END - KEYRING_SLOT_START; }

View File

@@ -3961,7 +3961,7 @@ void Player::SwapItem(uint16 src, uint16 dst)
AutoUnequipOffhandIfNeed();
}
void Player::AddItemToBuyBackSlot(Item* pItem)
void Player::AddItemToBuyBackSlot(Item* pItem, uint32 money)
{
if (pItem)
{
@@ -4003,10 +4003,7 @@ void Player::AddItemToBuyBackSlot(Item* pItem)
uint32 eslot = slot - BUYBACK_SLOT_START;
SetGuidValue(PLAYER_FIELD_VENDORBUYBACK_SLOT_1 + (eslot * 2), pItem->GetGUID());
if (ItemTemplate const* proto = pItem->GetTemplate())
SetUInt32Value(PLAYER_FIELD_BUYBACK_PRICE_1 + eslot, proto->SellPrice * pItem->GetCount());
else
SetUInt32Value(PLAYER_FIELD_BUYBACK_PRICE_1 + eslot, 0);
SetUInt32Value(PLAYER_FIELD_BUYBACK_PRICE_1 + eslot, money);
SetUInt32Value(PLAYER_FIELD_BUYBACK_TIMESTAMP_1 + eslot, (uint32)etime);
// move to next (for non filled list is move most optimized choice)

View File

@@ -1763,28 +1763,33 @@ void Player::UpdateForQuestWorldObjects()
continue;
// check if this unit requires quest specific flags
if (!obj->HasNpcFlag(UNIT_NPC_FLAG_SPELLCLICK))
continue;
SpellClickInfoMapBounds clickPair = sObjectMgr->GetSpellClickInfoMapBounds(obj->GetEntry());
for (SpellClickInfoContainer::const_iterator _itr = clickPair.first; _itr != clickPair.second; ++_itr)
if (obj->HasNpcFlag(UNIT_NPC_FLAG_SPELLCLICK))
{
//! This code doesn't look right, but it was logically converted to condition system to do the exact
//! same thing it did before. It definitely needs to be overlooked for intended functionality.
ConditionList conds = sConditionMgr->GetConditionsForSpellClickEvent(obj->GetEntry(), _itr->second.spellId);
bool buildUpdateBlock = false;
for (ConditionList::const_iterator jtr = conds.begin(); jtr != conds.end() && !buildUpdateBlock; ++jtr)
if ((*jtr)->ConditionType == CONDITION_QUESTREWARDED || (*jtr)->ConditionType == CONDITION_QUESTTAKEN)
buildUpdateBlock = true;
if (buildUpdateBlock)
SpellClickInfoMapBounds clickPair = sObjectMgr->GetSpellClickInfoMapBounds(obj->GetEntry());
for (SpellClickInfoContainer::const_iterator _itr = clickPair.first; _itr != clickPair.second; ++_itr)
{
obj->BuildValuesUpdateBlockForPlayer(&udata, this);
break;
//! This code doesn't look right, but it was logically converted to condition system to do the exact
//! same thing it did before. It definitely needs to be overlooked for intended functionality.
ConditionList conds = sConditionMgr->GetConditionsForSpellClickEvent(obj->GetEntry(), _itr->second.spellId);
bool buildUpdateBlock = false;
for (ConditionList::const_iterator jtr = conds.begin(); jtr != conds.end() && !buildUpdateBlock; ++jtr)
if ((*jtr)->ConditionType == CONDITION_QUESTREWARDED || (*jtr)->ConditionType == CONDITION_QUESTTAKEN)
buildUpdateBlock = true;
if (buildUpdateBlock)
{
obj->BuildValuesUpdateBlockForPlayer(&udata, this);
break;
}
}
}
else if (obj->HasNpcFlag(UNIT_NPC_FLAG_VENDOR_MASK | UNIT_NPC_FLAG_TRAINER))
{
obj->BuildValuesUpdateBlockForPlayer(&udata, this);
}
}
}
udata.BuildPacket(&packet);
GetSession()->SendPacket(&packet);
}

View File

@@ -1031,7 +1031,7 @@ uint32 Unit::DealDamage(Unit* attacker, Unit* victim, uint32 damage, CleanDamage
if (!attacker || attacker->IsControlledByPlayer() || attacker->IsCreatedByPlayer())
{
uint32 unDamage = health < damage ? health : damage;
bool damagedByPlayer = unDamage && attacker && attacker->m_movedByPlayer != nullptr;
bool damagedByPlayer = unDamage && attacker && (attacker->IsPlayer() || attacker->m_movedByPlayer != nullptr);
victim->ToCreature()->LowerPlayerDamageReq(unDamage, damagedByPlayer);
}
}
@@ -18215,6 +18215,11 @@ void Unit::SetControlled(bool apply, UnitState state)
void Unit::SetStunned(bool apply)
{
if (HasUnitState(UNIT_STATE_IN_FLIGHT))
{
return;
}
if (apply)
{
SetTarget();

View File

@@ -2303,7 +2303,7 @@ public:
void SendPetAIReaction(ObjectGuid guid);
///----------End of Pet responses methods----------
void propagateSpeedChange() { GetMotionMaster()->propagateSpeedChange(); }
void propagateSpeedChange() { GetMotionMaster()->PropagateSpeedChange(); }
// reactive attacks
void ClearAllReactives();