aboutsummaryrefslogtreecommitdiff
path: root/src/game/Player.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/game/Player.cpp')
-rw-r--r--src/game/Player.cpp384
1 files changed, 270 insertions, 114 deletions
diff --git a/src/game/Player.cpp b/src/game/Player.cpp
index 850cdfdc0a9..cde74b4a66b 100644
--- a/src/game/Player.cpp
+++ b/src/game/Player.cpp
@@ -68,7 +68,7 @@
#include <cmath>
-#define ZONE_UPDATE_INTERVAL 1000
+#define ZONE_UPDATE_INTERVAL (1*IN_MILISECONDS)
#define PLAYER_SKILL_INDEX(x) (PLAYER_SKILL_INFO_1_1 + ((x)*3))
#define PLAYER_SKILL_VALUE_INDEX(x) (PLAYER_SKILL_INDEX(x)+1)
@@ -367,7 +367,7 @@ Player::Player (WorldSession *session): Unit(), m_achievementMgr(this)
m_swingErrorMsg = 0;
- m_DetectInvTimer = 1000;
+ m_DetectInvTimer = 1*IN_MILISECONDS;
m_bgBattleGroundID = 0;
m_bgTypeID = BATTLEGROUND_TYPE_NONE;
@@ -424,6 +424,8 @@ Player::Player (WorldSession *session): Unit(), m_achievementMgr(this)
m_InstanceValid = true;
m_dungeonDifficulty = DIFFICULTY_NORMAL;
+ m_lastPotionId = 0;
+
for (int i = 0; i < BASEMOD_END; i++)
{
m_auraBaseMod[i][FLAT_MOD] = 0.0f;
@@ -872,7 +874,7 @@ void Player::StartMirrorTimer(MirrorTimerType Type, uint32 MaxValue)
void Player::ModifyMirrorTimer(MirrorTimerType Type, uint32 MaxValue, uint32 CurrentValue, uint32 Regen)
{
if(Type==BREATH_TIMER)
- m_breathTimer = ((MaxValue + 1000) - CurrentValue) / Regen;
+ m_breathTimer = ((MaxValue + 1*IN_MILISECONDS) - CurrentValue) / Regen;
WorldPacket data(SMSG_START_MIRROR_TIMER, (21));
data << (uint32)Type;
@@ -930,7 +932,7 @@ void Player::HandleDrowning()
return;
}
- uint32 UnderWaterTime = 3*MINUTE*1000; // default duration
+ uint32 UnderWaterTime = 3*MINUTE*IN_MILISECONDS; // default duration
AuraList const& mModWaterBreathing = GetAurasByType(SPELL_AURA_MOD_WATER_BREATHING);
for(AuraList::const_iterator i = mModWaterBreathing.begin(); i != mModWaterBreathing.end(); ++i)
@@ -942,7 +944,7 @@ void Player::HandleDrowning()
if (!(m_isunderwater & UNDERWATER_WATER_TRIGGER))
{
m_isunderwater|= UNDERWATER_WATER_TRIGGER;
- m_breathTimer = UnderWaterTime + 1000;
+ m_breathTimer = UnderWaterTime + 1*IN_MILISECONDS;
}
//single trigger "show Breathbar"
if ( m_breathTimer <= UnderWaterTime && !(m_isunderwater & UNDERWATER_WATER_BREATHB))
@@ -987,7 +989,7 @@ void Player::HandleLava()
if (!(m_isunderwater & UNDERWATER_INLAVA))
{
m_isunderwater|= UNDERWATER_WATER_BREATHB;
- m_breathTimer = 1000;
+ m_breathTimer = 1*IN_MILISECONDS;
}
*/
// Reset BreathTimer and still in the lava
@@ -1000,7 +1002,7 @@ void Player::HandleLava()
if ( !isGameMaster() )
EnvironmentalDamage(guid, DAMAGE_LAVA, damage);
- m_breathTimer = 1000;
+ m_breathTimer = 1*IN_MILISECONDS;
}
}
else if (!isAlive()) // Disable breath timer and reset underwater flags
@@ -1313,7 +1315,7 @@ void Player::Update( uint32 p_time )
{
m_drunkTimer += p_time;
- if (m_drunkTimer > 10000)
+ if (m_drunkTimer > 10*IN_MILISECONDS)
HandleSobering();
}
@@ -1643,6 +1645,7 @@ bool Player::TeleportTo(uint32 mapid, float x, float y, float z, float orientati
else
// this will be used instead of the current location in SaveToDB
m_teleport_dest = WorldLocation(mapid, x, y, z, orientation);
+
SetFallInformation(0, z);
//BuildHeartBeatMsg(&data);
@@ -2496,6 +2499,9 @@ void Player::InitStatsForLevel(bool reapplyMods)
void Player::SendInitialSpells()
{
+ time_t curTime = time(NULL);
+ time_t infTime = curTime + MONTH/2;
+
uint16 spellCount = 0;
WorldPacket data(SMSG_INITIAL_SPELLS, (1+2+4*m_spells.size()+2+m_spellCooldowns.size()*(2+2+2+4+4)));
@@ -2528,12 +2534,15 @@ void Player::SendInitialSpells()
if(!sEntry)
continue;
+ // not send infinity cooldown
+ if(itr->second.end > infTime)
+ continue;
+
data << uint16(itr->first);
time_t cooldown = 0;
- time_t curTime = time(NULL);
if(itr->second.end > curTime)
- cooldown = (itr->second.end-curTime)*1000;
+ cooldown = (itr->second.end-curTime)*IN_MILISECONDS;
data << uint16(itr->second.itemid); // cast item id
data << uint16(sEntry->Category); // spell category
@@ -3227,8 +3236,8 @@ void Player::RemoveArenaSpellCooldowns()
SpellEntry const * entry = sSpellStore.LookupEntry(itr->first);
// check if spellentry is present and if the cooldown is less than 15 mins
if( entry &&
- entry->RecoveryTime <= 15 * MINUTE * 1000 &&
- entry->CategoryRecoveryTime <= 15 * MINUTE * 1000 )
+ entry->RecoveryTime <= 15 * MINUTE * IN_MILISECONDS &&
+ entry->CategoryRecoveryTime <= 15 * MINUTE * IN_MILISECONDS )
{
// notify player
WorldPacket data(SMSG_CLEAR_COOLDOWN, (4+8));
@@ -3258,7 +3267,7 @@ void Player::RemoveAllSpellCooldown()
void Player::_LoadSpellCooldowns(QueryResult *result)
{
- m_spellCooldowns.clear();
+ // some cooldowns can be already set at aura loading...
//QueryResult *result = CharacterDatabase.PQuery("SELECT spell,item,time FROM character_spell_cooldown WHERE guid = '%u'",GetGUIDLow());
@@ -3299,17 +3308,20 @@ void Player::_SaveSpellCooldowns()
CharacterDatabase.PExecute("DELETE FROM character_spell_cooldown WHERE guid = '%u'", GetGUIDLow());
time_t curTime = time(NULL);
+ time_t infTime = curTime + MONTH/2;
// remove outdated and save active
for(SpellCooldowns::iterator itr = m_spellCooldowns.begin();itr != m_spellCooldowns.end();)
{
if(itr->second.end <= curTime)
m_spellCooldowns.erase(itr++);
- else
+ else if(itr->second.end <= infTime) // not save locked cooldowns, it will be reset or set at reload
{
CharacterDatabase.PExecute("INSERT INTO character_spell_cooldown (guid,spell,item,time) VALUES ('%u', '%u', '%u', '" I64FMTD "')", GetGUIDLow(), itr->first, itr->second.itemid, uint64(itr->second.end));
++itr;
}
+ else
+ ++itr;
}
}
@@ -3432,6 +3444,13 @@ bool Player::resetTalents(bool no_cost)
//FIXME: remove pet before or after unlearn spells? for now after unlearn to allow removing of talent related, pet affecting auras
RemovePet(NULL,PET_SAVE_NOT_IN_SLOT, true);
+ if(m_canTitanGrip)
+ {
+ m_canTitanGrip = false;
+ if(sWorld.getConfig(CONFIG_OFFHAND_CHECK_AT_TALENTS_RESET))
+ AutoUnequipOffhandIfNeed();
+ }
+
return true;
}
@@ -4003,7 +4022,7 @@ void Player::ResurrectPlayer(float restore_percent, bool applySickness)
{
if(Aura* Aur = GetAura(SPELL_ID_PASSIVE_RESURRECTION_SICKNESS,i))
{
- Aur->SetAuraDurationAndUpdate(delta*1000);
+ Aur->SetAuraDurationAndUpdate(delta*IN_MILISECONDS);
}
}
}
@@ -4023,7 +4042,7 @@ void Player::KillPlayer()
ApplyModFlag(PLAYER_FIELD_BYTES, PLAYER_FIELD_BYTE_RELEASE_TIMER, !sMapStore.LookupEntry(GetMapId())->Instanceable());
// 6 minutes until repop at graveyard
- m_deathTimer = 6*MINUTE*1000;
+ m_deathTimer = 6*MINUTE*IN_MILISECONDS;
UpdateCorpseReclaimDelay(); // dependent at use SetDeathPvP() call before kill
SendCorpseReclaimDelay();
@@ -4348,10 +4367,8 @@ void Player::RepopAtGraveyard()
WorldSafeLocsEntry const *ClosestGrave = NULL;
// Special handle for battleground maps
- BattleGround *bg = sBattleGroundMgr.GetBattleGround(GetBattleGroundId(), m_bgTypeID);
-
- if(bg && (bg->GetTypeID() == BATTLEGROUND_AB || bg->GetTypeID() == BATTLEGROUND_EY || bg->GetTypeID() == BATTLEGROUND_AV))
- ClosestGrave = bg->GetClosestGraveYard(GetPositionX(), GetPositionY(), GetPositionZ(), GetTeam());
+ if( BattleGround *bg = GetBattleGround() )
+ ClosestGrave = bg->GetClosestGraveYard(this);
else
ClosestGrave = objmgr.GetClosestGraveYard( GetPositionX(), GetPositionY(), GetPositionZ(), GetMapId(), GetTeam() );
@@ -6564,6 +6581,9 @@ void Player::UpdateZone(uint32 newZone)
if(isAlive())
DestroyZoneLimitedItem( true, newZone );
+ // check some item equip limitations (in result lost CanTitanGrip at talent reset, for example)
+ AutoUnequipOffhandIfNeed();
+
// recent client version not send leave/join channel packets for built-in local channels
UpdateLocalChannels( newZone );
@@ -6752,7 +6772,7 @@ void Player::_ApplyItemBonuses(ItemPrototype const *proto, uint8 slot, bool appl
if(slot >= INVENTORY_SLOT_BAG_END || !proto)
return;
- for (int i = 0; i < 10; i++)
+ for (int i = 0; i < MAX_ITEM_PROTO_STATS; ++i)
{
uint32 statType = 0;
int32 val = 0;
@@ -7085,7 +7105,7 @@ void Player::ApplyItemEquipSpell(Item *item, bool apply, bool form_change)
if(!proto)
return;
- for (int i = 0; i < 5; i++)
+ for (int i = 0; i < MAX_ITEM_PROTO_SPELLS; ++i)
{
_Spell const& spellData = proto->Spells[i];
@@ -7198,7 +7218,7 @@ void Player::CastItemCombatSpell(Item *item,Unit* Target, WeaponAttackType attTy
if (!Target || Target == this )
return;
- for (int i = 0; i < 5; i++)
+ for (int i = 0; i < MAX_ITEM_PROTO_SPELLS; ++i)
{
_Spell const& spellData = proto->Spells[i];
@@ -7296,7 +7316,7 @@ void Player::CastItemUseSpell(Item *item,SpellCastTargets const& targets,uint8 c
int count = 0;
// item spells casted at use
- for(int i = 0; i < 5; ++i)
+ for(int i = 0; i < MAX_ITEM_PROTO_SPELLS; ++i)
{
_Spell const& spellData = proto->Spells[i];
@@ -12959,10 +12979,10 @@ void Player::AddQuest( Quest const *pQuest, Object *questGiver )
// shared timed quest
if(questGiver && questGiver->GetTypeId()==TYPEID_PLAYER)
- limittime = ((Player*)questGiver)->getQuestStatusMap()[quest_id].m_timer / 1000;
+ limittime = ((Player*)questGiver)->getQuestStatusMap()[quest_id].m_timer / IN_MILISECONDS;
AddTimedQuest( quest_id );
- questStatusData.m_timer = limittime * 1000;
+ questStatusData.m_timer = limittime * IN_MILISECONDS;
qtime = static_cast<uint32>(time(NULL)) + limittime;
}
else
@@ -14122,28 +14142,21 @@ bool Player::HasQuestForItem( uint32 itemid ) const
for (int j = 0; j < QUEST_SOURCE_ITEM_IDS_COUNT; j++)
{
// examined item is a source item
- if (qinfo->ReqSourceId[j] == itemid && qinfo->ReqSourceRef[j] > 0 && qinfo->ReqSourceRef[j] <= QUEST_OBJECTIVES_COUNT)
+ if (qinfo->ReqSourceId[j] == itemid)
{
- uint32 idx = qinfo->ReqSourceRef[j]-1;
+ ItemPrototype const *pProto = objmgr.GetItemPrototype(itemid);
- // total count of created ReqItems and SourceItems is less than ReqItemCount
- if(qinfo->ReqItemId[idx] != 0 &&
- q_status.m_itemcount[idx] * qinfo->ReqSourceCount[j] + GetItemCount(itemid,true) < qinfo->ReqItemCount[idx] * qinfo->ReqSourceCount[j])
+ // 'unique' item
+ if (pProto->MaxCount && GetItemCount(itemid,true) < pProto->MaxCount)
return true;
- // total count of casted ReqCreatureOrGOs and SourceItems is less than ReqCreatureOrGOCount
- if (qinfo->ReqCreatureOrGOId[idx] != 0)
+ // allows custom amount drop when not 0
+ if (qinfo->ReqSourceCount[j])
{
- if(q_status.m_creatureOrGOcount[idx] * qinfo->ReqSourceCount[j] + GetItemCount(itemid,true) < qinfo->ReqCreatureOrGOCount[idx] * qinfo->ReqSourceCount[j])
+ if (GetItemCount(itemid,true) < qinfo->ReqSourceCount[j])
return true;
- }
- // spell with SPELL_EFFECT_QUEST_COMPLETE or SPELL_EFFECT_SEND_EVENT (with script) case
- else if(qinfo->ReqSpell[idx] != 0)
- {
- // not casted and need more reagents/item for use.
- if(!q_status.m_explored && GetItemCount(itemid,true) < qinfo->ReqSourceCount[j])
- return true;
- }
+ } else if (GetItemCount(itemid,true) < pProto->Stackable)
+ return true;
}
}
}
@@ -14684,6 +14697,14 @@ bool Player::LoadFromDB( uint32 guid, SqlQueryHolder *holder )
{
if( (*iter)->GetGUIDLow() == transGUID)
{
+ MapEntry const* transMapEntry = sMapStore.LookupEntry((*iter)->GetMapId());
+ // client without expansion support
+ if(GetSession()->Expansion() < transMapEntry->Expansion())
+ {
+ sLog.outDebug("Player %s using client without required expansion tried login at transport at non accessible map %u", GetName(), (*iter)->GetMapId());
+ break;
+ }
+
m_transport = *iter;
m_transport->AddPassenger(this);
SetMapId(m_transport->GetMapId());
@@ -14693,7 +14714,7 @@ bool Player::LoadFromDB( uint32 guid, SqlQueryHolder *holder )
if(!m_transport)
{
- sLog.outError("ERROR: Player (guidlow %d) have invalid transport guid (%u). Teleport to default race/class locations.",
+ sLog.outError("ERROR: Player (guidlow %d) have problems with transport guid (%u). Teleport to default race/class locations.",
guid,transGUID);
RelocateToHomebind();
@@ -14706,6 +14727,16 @@ bool Player::LoadFromDB( uint32 guid, SqlQueryHolder *holder )
transGUID = 0;
}
}
+ else // not transport case
+ {
+ MapEntry const* mapEntry = sMapStore.LookupEntry(GetMapId());
+ // client without expansion support
+ if(GetSession()->Expansion() < mapEntry->Expansion())
+ {
+ sLog.outDebug("Player %s using client without required expansion tried login at non accessible map %u", GetName(), GetMapId());
+ RelocateToHomebind();
+ }
+ }
// NOW player must have valid map
// load the player's map here if it's not already loaded
@@ -15527,7 +15558,7 @@ void Player::_LoadQuestStatus(QueryResult *result)
if (quest_time <= sWorld.GetGameTime())
questStatusData.m_timer = 1;
else
- questStatusData.m_timer = (quest_time - sWorld.GetGameTime()) * 1000;
+ questStatusData.m_timer = (quest_time - sWorld.GetGameTime()) * IN_MILISECONDS;
}
else
quest_time = 0;
@@ -15974,9 +16005,11 @@ bool Player::_LoadHomeBind(QueryResult *result)
m_homebindZ = fields[4].GetFloat();
delete result;
- // accept saved data only for valid position (and non instanceable)
+ MapEntry const* bindMapEntry = sMapStore.LookupEntry(m_homebindMapId);
+
+ // accept saved data only for valid position (and non instanceable), and accessable
if( MapManager::IsValidMapCoord(m_homebindMapId,m_homebindX,m_homebindY,m_homebindZ) &&
- !sMapStore.LookupEntry(m_homebindMapId)->Instanceable() )
+ !bindMapEntry->Instanceable() && GetSession()->Expansion() >= bindMapEntry->Expansion())
{
ok = true;
}
@@ -16237,6 +16270,7 @@ void Player::_SaveAuras()
AuraMap::const_iterator itr2 = itr;
// save previous spellEffectPair to db
itr2--;
+
SpellEntry const *spellInfo = itr2->second->GetSpellProto();
//skip all auras from spells that are passive or need a shapeshift
@@ -16412,11 +16446,11 @@ void Player::_SaveQuestStatus()
case QUEST_NEW :
CharacterDatabase.PExecute("INSERT INTO character_queststatus (guid,quest,status,rewarded,explored,timer,mobcount1,mobcount2,mobcount3,mobcount4,itemcount1,itemcount2,itemcount3,itemcount4) "
"VALUES ('%u', '%u', '%u', '%u', '%u', '" I64FMTD "', '%u', '%u', '%u', '%u', '%u', '%u', '%u', '%u')",
- GetGUIDLow(), i->first, i->second.m_status, i->second.m_rewarded, i->second.m_explored, uint64(i->second.m_timer / 1000 + sWorld.GetGameTime()), i->second.m_creatureOrGOcount[0], i->second.m_creatureOrGOcount[1], i->second.m_creatureOrGOcount[2], i->second.m_creatureOrGOcount[3], i->second.m_itemcount[0], i->second.m_itemcount[1], i->second.m_itemcount[2], i->second.m_itemcount[3]);
+ GetGUIDLow(), i->first, i->second.m_status, i->second.m_rewarded, i->second.m_explored, uint64(i->second.m_timer / IN_MILISECONDS+ sWorld.GetGameTime()), i->second.m_creatureOrGOcount[0], i->second.m_creatureOrGOcount[1], i->second.m_creatureOrGOcount[2], i->second.m_creatureOrGOcount[3], i->second.m_itemcount[0], i->second.m_itemcount[1], i->second.m_itemcount[2], i->second.m_itemcount[3]);
break;
case QUEST_CHANGED :
CharacterDatabase.PExecute("UPDATE character_queststatus SET status = '%u',rewarded = '%u',explored = '%u',timer = '" I64FMTD "',mobcount1 = '%u',mobcount2 = '%u',mobcount3 = '%u',mobcount4 = '%u',itemcount1 = '%u',itemcount2 = '%u',itemcount3 = '%u',itemcount4 = '%u' WHERE guid = '%u' AND quest = '%u' ",
- i->second.m_status, i->second.m_rewarded, i->second.m_explored, uint64(i->second.m_timer / 1000 + sWorld.GetGameTime()), i->second.m_creatureOrGOcount[0], i->second.m_creatureOrGOcount[1], i->second.m_creatureOrGOcount[2], i->second.m_creatureOrGOcount[3], i->second.m_itemcount[0], i->second.m_itemcount[1], i->second.m_itemcount[2], i->second.m_itemcount[3], GetGUIDLow(), i->first );
+ i->second.m_status, i->second.m_rewarded, i->second.m_explored, uint64(i->second.m_timer / IN_MILISECONDS + sWorld.GetGameTime()), i->second.m_creatureOrGOcount[0], i->second.m_creatureOrGOcount[1], i->second.m_creatureOrGOcount[2], i->second.m_creatureOrGOcount[3], i->second.m_itemcount[0], i->second.m_itemcount[1], i->second.m_itemcount[2], i->second.m_itemcount[3], GetGUIDLow(), i->first );
break;
case QUEST_UNCHANGED:
break;
@@ -17145,7 +17179,7 @@ void Player::PetSpellInitialize()
time_t cooldown = 0;
if(itr->second > curTime)
- cooldown = (itr->second - curTime) * 1000;
+ cooldown = (itr->second - curTime) * IN_MILISECONDS;
data << uint16(itr->first); // spellid
data << uint16(0); // spell category?
@@ -17158,7 +17192,7 @@ void Player::PetSpellInitialize()
time_t cooldown = 0;
if(itr->second > curTime)
- cooldown = (itr->second - curTime) * 1000;
+ cooldown = (itr->second - curTime) * IN_MILISECONDS;
data << uint16(itr->first); // spellid
data << uint16(0); // spell category?
@@ -17662,7 +17696,7 @@ void Player::ProhibitSpellScholl(SpellSchoolMask idSchoolMask, uint32 unTimeMs )
{
data << unSpellId;
data << unTimeMs; // in m.secs
- AddSpellCooldown(unSpellId, 0, curTime + unTimeMs/1000);
+ AddSpellCooldown(unSpellId, 0, curTime + unTimeMs/IN_MILISECONDS);
}
}
GetSession()->SendPacket(&data);
@@ -18029,6 +18063,102 @@ void Player::UpdatePvP(bool state, bool ovrride)
}
}
+void Player::AddSpellAndCategoryCooldowns(SpellEntry const* spellInfo, uint32 itemId, Spell* spell, bool infinityCooldown)
+{
+ // init cooldown values
+ uint32 cat = 0;
+ int32 rec = -1;
+ int32 catrec = -1;
+
+ // some special item spells without correct cooldown in SpellInfo
+ // cooldown information stored in item prototype
+ // This used in same way in WorldSession::HandleItemQuerySingleOpcode data sending to client.
+
+ if(itemId)
+ {
+ if(ItemPrototype const* proto = ObjectMgr::GetItemPrototype(itemId))
+ {
+ for(int idx = 0; idx < 5; ++idx)
+ {
+ if(proto->Spells[idx].SpellId == spellInfo->Id)
+ {
+ cat = proto->Spells[idx].SpellCategory;
+ rec = proto->Spells[idx].SpellCooldown;
+ catrec = proto->Spells[idx].SpellCategoryCooldown;
+ break;
+ }
+ }
+ }
+ }
+
+ // if no cooldown found above then base at DBC data
+ if(rec < 0 && catrec < 0)
+ {
+ cat = spellInfo->Category;
+ rec = spellInfo->RecoveryTime;
+ catrec = spellInfo->CategoryRecoveryTime;
+ }
+
+ time_t curTime = time(NULL);
+
+ time_t catrecTime;
+ time_t recTime;
+
+ // overwrite time for selected category
+ if(infinityCooldown)
+ {
+ // use +MONTH as infinity mark for spell cooldown (will checked as MONTH/2 at save ans skipped)
+ // but not allow ignore until reset or re-login
+ catrecTime = catrec > 0 ? curTime+MONTH : 0;
+ recTime = rec > 0 ? curTime+MONTH : catrecTime;
+ }
+ else
+ {
+ // shoot spells used equipped item cooldown values already assigned in GetAttackTime(RANGED_ATTACK)
+ // prevent 0 cooldowns set by another way
+ if (rec <= 0 && catrec <= 0 && (cat == 76 || IsAutoRepeatRangedSpell(spellInfo) && spellInfo->Id != SPELL_ID_AUTOSHOT))
+ rec = GetAttackTime(RANGED_ATTACK);
+
+ // Now we have cooldown data (if found any), time to apply mods
+ if(rec > 0)
+ ApplySpellMod(spellInfo->Id, SPELLMOD_COOLDOWN, rec, spell);
+
+ if(catrec > 0)
+ ApplySpellMod(spellInfo->Id, SPELLMOD_COOLDOWN, catrec, spell);
+
+ // replace negative cooldowns by 0
+ if (rec < 0) rec = 0;
+ if (catrec < 0) catrec = 0;
+
+ // no cooldown after applying spell mods
+ if( rec == 0 && catrec == 0)
+ return;
+
+ catrecTime = catrec ? curTime+catrec/IN_MILISECONDS : 0;
+ recTime = rec ? curTime+rec/IN_MILISECONDS : catrecTime;
+ }
+
+ // self spell cooldown
+ if(recTime > 0)
+ AddSpellCooldown(spellInfo->Id, itemId, recTime);
+
+ // category spells
+ if (cat && catrec > 0)
+ {
+ SpellCategoryStore::const_iterator i_scstore = sSpellCategoryStore.find(cat);
+ if(i_scstore != sSpellCategoryStore.end())
+ {
+ for(SpellCategorySet::const_iterator i_scset = i_scstore->second.begin(); i_scset != i_scstore->second.end(); ++i_scset)
+ {
+ if(*i_scset == spellInfo->Id) // skip main spell, already handled above
+ continue;
+
+ AddSpellCooldown(*i_scset, itemId, catrecTime);
+ }
+ }
+ }
+}
+
void Player::AddSpellCooldown(uint32 spellid, uint32 itemid, time_t end_time)
{
SpellCooldown sc;
@@ -18037,25 +18167,41 @@ void Player::AddSpellCooldown(uint32 spellid, uint32 itemid, time_t end_time)
m_spellCooldowns[spellid] = sc;
}
-void Player::SendCooldownEvent(SpellEntry const *spellInfo)
+void Player::SendCooldownEvent(SpellEntry const *spellInfo, uint32 itemId, Spell* spell)
{
- if ( !(spellInfo->Attributes & SPELL_ATTR_DISABLED_WHILE_ACTIVE) )
- return;
+ // start cooldowns at server side, if any
+ AddSpellAndCategoryCooldowns(spellInfo,itemId,spell);
- // Get spell cooldown
- int32 cooldown = GetSpellRecoveryTime(spellInfo);
- // Apply spellmods
- ApplySpellMod(spellInfo->Id, SPELLMOD_COOLDOWN, cooldown);
- if (cooldown < 0)
- cooldown = 0;
- // Add cooldown
- AddSpellCooldown(spellInfo->Id, 0, time(NULL) + cooldown / 1000);
- // Send activate
+ // Send activate cooldown timer (possible 0) at client side
WorldPacket data(SMSG_COOLDOWN_EVENT, (4+8));
data << spellInfo->Id;
data << GetGUID();
SendDirectMessage(&data);
}
+
+void Player::UpdatePotionCooldown(Spell* spell)
+{
+ // no potion used i combat or still in combat
+ if(!m_lastPotionId || isInCombat())
+ return;
+
+ // Call not from spell cast, send cooldown event for item spells if no in combat
+ if(!spell)
+ {
+ // spell/item pair let set proper cooldown (except not existed charged spell cooldown spellmods for potions)
+ if(ItemPrototype const* proto = ObjectMgr::GetItemPrototype(m_lastPotionId))
+ for(int idx = 0; idx < 5; ++idx)
+ if(proto->Spells[idx].SpellId && proto->Spells[idx].SpellTrigger == ITEM_SPELLTRIGGER_ON_USE)
+ if(SpellEntry const* spellInfo = sSpellStore.LookupEntry(proto->Spells[idx].SpellId))
+ SendCooldownEvent(spellInfo,m_lastPotionId);
+ }
+ // from spell cases (m_lastPotionId set in Spell::SendSpellCooldown)
+ else
+ SendCooldownEvent(spell->m_spellInfo,m_lastPotionId,spell);
+
+ m_lastPotionId = 0;
+}
+
//slot to be excluded while counting
bool Player::EnchantmentFitsRequirements(uint32 enchantmentcondition, int8 slot)
{
@@ -18797,7 +18943,7 @@ void Player::SendInstanceResetWarning(uint32 mapid, uint32 time)
void Player::ApplyEquipCooldown( Item * pItem )
{
- for(int i = 0; i <5; ++i)
+ for(int i = 0; i < MAX_ITEM_PROTO_SPELLS; ++i)
{
_Spell const& spellData = pItem->GetProto()->Spells[i];
@@ -19242,8 +19388,8 @@ void Player::AutoUnequipOffhandIfNeed()
if(!offItem)
return;
- // need unequip for 2h-weapon without TitanGrip
- if (!IsTwoHandUsed())
+ // need unequip offhand for 2h-weapon without TitanGrip (in any from hands)
+ if (CanTitanGrip() || (offItem->GetProto()->InventoryType != INVTYPE_2HWEAPON && !IsTwoHandUsed()))
return;
ItemPosCountVec off_dest;
@@ -19255,7 +19401,16 @@ void Player::AutoUnequipOffhandIfNeed()
}
else
{
- sLog.outError("Player::EquipItem: Can's store offhand item at 2hand item equip for player (GUID: %u).",GetGUIDLow());
+ MailItemsInfo mi;
+ mi.AddItem(offItem->GetGUIDLow(), offItem->GetEntry(), offItem);
+ MoveItemFromInventory(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_OFFHAND, true);
+ CharacterDatabase.BeginTransaction();
+ offItem->DeleteFromInventoryDB(); // deletes item from character's inventory
+ offItem->SaveToDB(); // recursive and not have transaction guard into self, item not in inventory and can be save standalone
+ CharacterDatabase.CommitTransaction();
+
+ std::string subject = GetSession()->GetMangosString(LANG_NOT_EQUIPPED_ITEM);
+ WorldSession::SendMailTo(this, MAIL_NORMAL, MAIL_STATIONERY_GM, GetGUIDLow(), GetGUIDLow(), subject, 0, &mi, 0, 0, MAIL_CHECK_MASK_NONE);
}
}
@@ -19702,7 +19857,7 @@ void Player::SendCorpseReclaimDelay(bool load)
//! corpse reclaim delay 30 * 1000ms or longer at often deaths
WorldPacket data(SMSG_CORPSE_RECLAIM_DELAY, 4);
- data << uint32(delay*1000);
+ data << uint32(delay*IN_MILISECONDS);
GetSession()->SendPacket( &data );
}
@@ -19796,52 +19951,6 @@ bool ItemPosCount::isContainedIn(ItemPosCountVec const& vec) const
//-------------TRINITY---------------
//***********************************
-void Player::HandleFallDamage(MovementInfo& movementInfo)
-{
- //if(movementInfo.fallTime < 1500)
- // return;
-
- // calculate total z distance of the fall
- float z_diff = m_lastFallZ - movementInfo.z;
- sLog.outDebug("zDiff = %f", z_diff);
-
- //Players with low fall distance, Feather Fall or physical immunity (charges used) are ignored
- // 14.57 can be calculated by resolving damageperc formular below to 0
- if (z_diff >= 14.57f && !isDead() && !isGameMaster() &&
- !HasAuraType(SPELL_AURA_HOVER) && !HasAuraType(SPELL_AURA_FEATHER_FALL) &&
- !HasAuraType(SPELL_AURA_FLY) && !IsImmunedToDamage(SPELL_SCHOOL_MASK_NORMAL) )
- {
- //Safe fall, fall height reduction
- int32 safe_fall = GetTotalAuraModifier(SPELL_AURA_SAFE_FALL);
-
- float damageperc = 0.018f*(z_diff-safe_fall)-0.2426f;
-
- if(damageperc >0 )
- {
- uint32 damage = (uint32)(damageperc * GetMaxHealth()*sWorld.getRate(RATE_DAMAGE_FALL));
-
- float height = movementInfo.z;
- UpdateGroundPositionZ(movementInfo.x,movementInfo.y,height);
-
- if (damage > 0)
- {
- //Prevent fall damage from being more than the player maximum health
- if (damage > GetMaxHealth())
- damage = GetMaxHealth();
-
- // Gust of Wind
- if (GetDummyAura(43621))
- damage = GetMaxHealth()/2;
-
- EnvironmentalDamage(GetGUID(), DAMAGE_FALL, damage);
- }
-
- //Z given by moveinfo, LastZ, FallTime, WaterZ, MapZ, Damage, Safefall reduction
- DEBUG_LOG("FALLDAMAGE z=%f sz=%f pZ=%f FallTime=%d mZ=%f damage=%d SF=%d" , movementInfo.z, height, GetPositionZ(), movementInfo.fallTime, height, damage, safe_fall);
- }
- }
-}
-
void Player::HandleFallUnderMap()
{
if(InBattleGround()
@@ -20400,3 +20509,50 @@ uint8 Player::CanEquipUniqueItem( ItemPrototype const* itemProto, uint8 except_s
return EQUIP_ERR_OK;
}
+
+void Player::HandleFall(MovementInfo const& movementInfo)
+{
+ // calculate total z distance of the fall
+ float z_diff = m_lastFallZ - movementInfo.z;
+ sLog.outDebug("zDiff = %f", z_diff);
+
+ //Players with low fall distance, Feather Fall or physical immunity (charges used) are ignored
+ // 14.57 can be calculated by resolving damageperc formular below to 0
+ if (z_diff >= 14.57f && !isDead() && !isGameMaster() &&
+ !HasAuraType(SPELL_AURA_HOVER) && !HasAuraType(SPELL_AURA_FEATHER_FALL) &&
+ !HasAuraType(SPELL_AURA_FLY) && !IsImmunedToDamage(SPELL_SCHOOL_MASK_NORMAL) )
+ {
+ //Safe fall, fall height reduction
+ int32 safe_fall = GetTotalAuraModifier(SPELL_AURA_SAFE_FALL);
+
+ float damageperc = 0.018f*(z_diff-safe_fall)-0.2426f;
+
+ if(damageperc >0 )
+ {
+ uint32 damage = (uint32)(damageperc * GetMaxHealth()*sWorld.getRate(RATE_DAMAGE_FALL));
+
+ float height = movementInfo.z;
+ UpdateGroundPositionZ(movementInfo.x,movementInfo.y,height);
+
+ if (damage > 0)
+ {
+ //Prevent fall damage from being more than the player maximum health
+ if (damage > GetMaxHealth())
+ damage = GetMaxHealth();
+
+ // Gust of Wind
+ if (GetDummyAura(43621))
+ damage = GetMaxHealth()/2;
+
+ EnvironmentalDamage(GetGUID(), DAMAGE_FALL, damage);
+
+ // recheck alive, might have died of EnvironmentalDamage
+ if (isAlive())
+ GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_FALL_WITHOUT_DYING, uint32(z_diff*100));
+ }
+
+ //Z given by moveinfo, LastZ, FallTime, WaterZ, MapZ, Damage, Safefall reduction
+ DEBUG_LOG("FALLDAMAGE z=%f sz=%f pZ=%f FallTime=%d mZ=%f damage=%d SF=%d" , movementInfo.z, height, GetPositionZ(), movementInfo.fallTime, height, damage, safe_fall);
+ }
+ }
+}