Merge branch 'master' into 310

Conflicts:
	src/game/Player.cpp
	src/game/QueryHandler.cpp
This commit is contained in:
tomrus88 2009-03-06 08:03:34 +03:00
commit 6aadc16d7d
54 changed files with 1279 additions and 888 deletions

View file

@ -62,7 +62,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)
@ -359,7 +359,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;
@ -416,6 +416,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;
@ -809,7 +811,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;
@ -833,6 +835,9 @@ void Player::StopMirrorTimer(MirrorTimerType Type)
void Player::EnvironmentalDamage(uint64 guid, EnviromentalDamage type, uint32 damage)
{
if(!isAlive() || isGameMaster())
return;
WorldPacket data(SMSG_ENVIRONMENTALDAMAGELOG, (21));
data << (uint64)guid;
data << (uint8)(type!=DAMAGE_FALL_TO_VOID ? type : DAMAGE_FALL);
@ -843,13 +848,18 @@ void Player::EnvironmentalDamage(uint64 guid, EnviromentalDamage type, uint32 da
DealDamage(this, damage, NULL, SELF_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false);
if(type==DAMAGE_FALL && !isAlive()) // DealDamage not apply item durability loss at self damage
if(!isAlive())
{
DEBUG_LOG("We are fall to death, loosing 10 percents durability");
DurabilityLossAll(0.10f,false);
// durability lost message
WorldPacket data(SMSG_DURABILITY_DAMAGE_DEATH, 0);
GetSession()->SendPacket(&data);
if(type==DAMAGE_FALL) // DealDamage not apply item durability loss at self damage
{
DEBUG_LOG("We are fall to death, loosing 10 percents durability");
DurabilityLossAll(0.10f,false);
// durability lost message
WorldPacket data(SMSG_DURABILITY_DAMAGE_DEATH, 0);
GetSession()->SendPacket(&data);
}
GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_DEATHS_FROM, 1, type);
}
}
@ -867,7 +877,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)
@ -879,7 +889,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))
@ -924,7 +934,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
@ -933,11 +943,9 @@ void Player::HandleLava()
uint64 guid = GetGUID();
uint32 damage = urand(600, 700); // TODO: Get more detailed information about lava damage
// if not gamemaster then deal damage
if ( !isGameMaster() )
EnvironmentalDamage(guid, DAMAGE_LAVA, damage);
EnvironmentalDamage(guid, DAMAGE_LAVA, damage);
m_breathTimer = 1000;
m_breathTimer = 1*IN_MILISECONDS;
}
}
else if (!isAlive()) // Disable breath timer and reset underwater flags
@ -1257,7 +1265,7 @@ void Player::Update( uint32 p_time )
{
m_drunkTimer += p_time;
if (m_drunkTimer > 10000)
if (m_drunkTimer > 10*IN_MILISECONDS)
HandleSobering();
}
@ -1320,6 +1328,8 @@ void Player::setDeathState(DeathState s)
if(!ressSpellId)
ressSpellId = GetResurrectionSpellId();
GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_DEATH_AT_MAP, 1);
GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_DEATH, 1);
GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_DEATH_IN_DUNGEON, 1);
}
Unit::setDeathState(s);
@ -1561,6 +1571,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);
@ -2398,6 +2409,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)));
@ -2430,12 +2444,15 @@ void Player::SendInitialSpells()
if(!sEntry)
continue;
// not send infinity cooldown
if(itr->second.end > infTime)
continue;
data << uint32(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
@ -3122,8 +3139,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));
@ -3153,7 +3170,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());
@ -3194,17 +3211,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;
}
}
@ -3319,6 +3339,7 @@ bool Player::resetTalents(bool no_cost)
if(!no_cost)
{
ModifyMoney(-(int32)cost);
GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_FOR_TALENTS, cost);
m_resetTalentsCost = cost;
m_resetTalentsTime = time(NULL);
@ -3327,6 +3348,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;
}
@ -3883,7 +3911,7 @@ void Player::ResurrectPlayer(float restore_percent, bool applySickness)
{
if(Aura* Aur = GetAura(SPELL_ID_PASSIVE_RESURRECTION_SICKNESS,i))
{
Aur->SetAuraDuration(delta*1000);
Aur->SetAuraDuration(delta*IN_MILISECONDS);
Aur->SendAuraUpdate(false);
}
}
@ -3904,7 +3932,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
@ -4228,10 +4256,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))
ClosestGrave = bg->GetClosestGraveYard(GetPositionX(), GetPositionY(), GetPositionZ(), GetTeam());
if( BattleGround *bg = GetBattleGround() )
ClosestGrave = bg->GetClosestGraveYard(this);
else
ClosestGrave = objmgr.GetClosestGraveYard( GetPositionX(), GetPositionY(), GetPositionZ(), GetMapId(), GetTeam() );
@ -5621,7 +5647,8 @@ void Player::SetFactionVisibleForFactionTemplateId(uint32 FactionTemplateId)
if(!factionTemplateEntry)
return;
SetFactionVisibleForFactionId(factionTemplateEntry->faction);
if(factionTemplateEntry->faction)
SetFactionVisibleForFactionId(factionTemplateEntry->faction);
}
void Player::SetFactionVisibleForFactionId(uint32 FactionId)
@ -6389,6 +6416,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 );
@ -9811,6 +9841,12 @@ uint8 Player::CanStoreItems( Item **pItems,int count) const
pBag = (Bag*)GetItemByPos( INVENTORY_SLOT_BAG_0, t );
if( pBag )
{
pBagProto = pBag->GetProto();
// special bag already checked
if( pBagProto && (pBagProto->Class != ITEM_CLASS_CONTAINER || pBagProto->SubClass != ITEM_SUBCLASS_CONTAINER))
continue;
for(uint32 j = 0; j < pBag->GetBagSize(); j++)
{
if( inv_bags[t-INVENTORY_SLOT_BAG_START][j] == 0 )
@ -12600,10 +12636,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
@ -12728,10 +12764,18 @@ void Player::RewardQuest( Quest const *pQuest, uint32 reward, Object* questGiver
if ( getLevel() < sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL) )
GiveXP( XP , NULL );
else
ModifyMoney( int32(pQuest->GetRewMoneyMaxLevel() * sWorld.getRate(RATE_DROP_MONEY)) );
{
int32 money = int32(pQuest->GetRewMoneyMaxLevel() * sWorld.getRate(RATE_DROP_MONEY));
ModifyMoney( money );
GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_MONEY_FROM_QUEST_REWARD, money);
}
// Give player extra money if GetRewOrReqMoney > 0 and get ReqMoney if negative
ModifyMoney( pQuest->GetRewOrReqMoney() );
if(pQuest->GetRewOrReqMoney())
{
ModifyMoney( pQuest->GetRewOrReqMoney() );
GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_MONEY_FROM_QUEST_REWARD, pQuest->GetRewOrReqMoney());
}
// honor reward
if(pQuest->GetRewHonorableKills())
@ -13762,28 +13806,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;
}
}
}
@ -14323,6 +14360,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());
@ -14332,7 +14377,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();
@ -14345,6 +14390,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
@ -15131,7 +15186,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;
@ -15557,9 +15612,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;
}
@ -15818,6 +15875,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
@ -15992,11 +16050,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;
@ -16679,7 +16737,7 @@ void Player::PetSpellInitialize()
time_t cooldown = 0;
if(itr->second > curTime)
cooldown = (itr->second - curTime) * 1000;
cooldown = (itr->second - curTime) * IN_MILISECONDS;
data << uint32(itr->first); // spellid
data << uint16(0); // spell category?
@ -16692,7 +16750,7 @@ void Player::PetSpellInitialize()
time_t cooldown = 0;
if(itr->second > curTime)
cooldown = (itr->second - curTime) * 1000;
cooldown = (itr->second - curTime) * IN_MILISECONDS;
data << uint32(itr->first); // spellid
data << uint16(0); // spell category?
@ -17167,6 +17225,7 @@ bool Player::ActivateTaxiPathTo(std::vector<uint32> const& nodes, uint32 mount_i
//Checks and preparations done, DO FLIGHT
ModifyMoney(-(int32)totalcost);
GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_FOR_TRAVELLING, totalcost);
// prevent stealth flight
RemoveSpellsCausingAura(SPELL_AURA_MOD_STEALTH);
@ -17209,7 +17268,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);
@ -17576,6 +17635,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;
@ -17584,25 +17739,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)
{
@ -18479,7 +18650,7 @@ BGQueueIdBasedOnLevel Player::GetBattleGroundQueueIdFromLevel(BattleGroundTypeId
float Player::GetReputationPriceDiscount( Creature const* pCreature ) const
{
FactionTemplateEntry const* vendor_faction = pCreature->getFactionTemplateEntry();
if(!vendor_faction)
if(!vendor_faction || !vendor_faction->faction)
return 1.0f;
ReputationRank rank = GetReputationRank(vendor_faction->faction);
@ -18626,8 +18797,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;
@ -18639,7 +18810,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);
}
}
@ -19070,7 +19250,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 );
}