mirror of
https://github.com/mangosfour/server.git
synced 2025-12-27 10:37:02 +00:00
Many, many cmangos Cata commits applied
The following commits were either applied or found not to be applicable:
This commit is contained in:
parent
32a26f44c7
commit
a800f3b1ad
100 changed files with 2385 additions and 1305 deletions
|
|
@ -55,7 +55,7 @@ AggressorAI::MoveInLineOfSight(Unit* u)
|
|||
return;
|
||||
|
||||
if (m_creature->CanInitiateAttack() && u->IsTargetableForAttack() &&
|
||||
m_creature->IsHostileTo(u) && u->isInAccessablePlaceFor(m_creature))
|
||||
m_creature->IsHostileTo(u) && u->IsInAccessablePlaceFor(m_creature))
|
||||
{
|
||||
float attackRadius = m_creature->GetAttackDistance(u);
|
||||
if (m_creature->IsWithinDistInMap(u, attackRadius) && m_creature->IsWithinLOSInMap(u))
|
||||
|
|
|
|||
|
|
@ -208,6 +208,10 @@ void Creature::AddToWorld()
|
|||
{ GetMap()->GetObjectsStore().insert<Creature>(GetObjectGuid(), (Creature*)this); }
|
||||
|
||||
Unit::AddToWorld();
|
||||
|
||||
// Make active if required
|
||||
if (sWorld.isForceLoadMap(GetMapId()) || (GetCreatureInfo()->ExtraFlags & CREATURE_EXTRA_FLAG_ACTIVE))
|
||||
SetActiveObjectState(true);
|
||||
}
|
||||
|
||||
void Creature::RemoveFromWorld()
|
||||
|
|
@ -384,7 +388,14 @@ bool Creature::InitEntry(uint32 Entry, CreatureData const* data /*=NULL*/, GameE
|
|||
UpdateSpeed(MOVE_WALK, false);
|
||||
UpdateSpeed(MOVE_RUN, false);
|
||||
|
||||
SetLevitate(cinfo->InhabitType & INHABIT_AIR);
|
||||
SetLevitate((cinfo->InhabitType & INHABIT_AIR) != 0); // TODO: may not be correct to send opcode at this point (already handled by UPDATE_OBJECT createObject)
|
||||
|
||||
// check if we need to add swimming movement. TODO: i thing movement flags should be computed automatically at each movement of creature so we need a sort of UpdateMovementFlags() method
|
||||
if (cinfo->InhabitType & INHABIT_WATER && // check inhabit type water
|
||||
!(cinfo->ExtraFlags & CREATURE_EXTRA_FLAG_WALK_IN_WATER) && // check if creature is forced to walk (crabs, giant,...)
|
||||
data && // check if there is data to get creature spawn pos
|
||||
GetMap()->GetTerrain()->IsSwimmable(data->posX, data->posY, data->posZ, minfo->bounding_radius)) // check if creature is in water and have enough space to swim
|
||||
m_movementInfo.AddMovementFlag(MOVEFLAG_SWIMMING); // add swimming movement
|
||||
|
||||
// checked at loading
|
||||
m_defaultMovementType = MovementGeneratorType(cinfo->MovementType);
|
||||
|
|
@ -485,8 +496,10 @@ uint32 Creature::ChooseDisplayId(const CreatureInfo* cinfo, const CreatureData*
|
|||
// if mod2 use mod2 unless mod2 has modelid_alt_model (then both by 50%-chance)
|
||||
// if mod1 use mod1
|
||||
|
||||
// model selected here may be replaced with other_gender using own function
|
||||
// The follow decision tree needs to be updated if MAX_CREATURE_MODEL is changed.
|
||||
static_assert(MAX_CREATURE_MODEL == 4, "Need to update model selection code for new or removed model fields");
|
||||
|
||||
// model selected here may be replaced with other_gender using own function
|
||||
if (cinfo->ModelId[3] && cinfo->ModelId[2] && cinfo->ModelId[1] && cinfo->ModelId[0])
|
||||
{
|
||||
display_id = cinfo->ModelId[urand(0, 3)];
|
||||
|
|
@ -1159,6 +1172,9 @@ void Creature::SaveToDB(uint32 mapid, uint8 spawnMask, uint32 phaseMask)
|
|||
CreatureInfo const* cinfo = GetCreatureInfo();
|
||||
if (cinfo)
|
||||
{
|
||||
// The following if-else assumes that there are 4 model fields and needs updating if this is changed.
|
||||
static_assert(MAX_CREATURE_MODEL == 4, "Need to update custom model check for new/removed model fields.");
|
||||
|
||||
if (displayId != cinfo->ModelId[0] && displayId != cinfo->ModelId[1] &&
|
||||
displayId != cinfo->ModelId[2] && displayId != cinfo->ModelId[3])
|
||||
{
|
||||
|
|
@ -1765,6 +1781,35 @@ bool Creature::IsImmuneToSpellEffect(SpellEntry const* spellInfo, SpellEffectInd
|
|||
return Unit::IsImmuneToSpellEffect(spellInfo, index, castOnSelf);
|
||||
}
|
||||
|
||||
// Set loot status. Also handle remove corpse timer
|
||||
void Creature::SetLootStatus(CreatureLootStatus status)
|
||||
{
|
||||
if (status <= m_lootStatus)
|
||||
return;
|
||||
|
||||
m_lootStatus = status;
|
||||
switch (status)
|
||||
{
|
||||
case CREATURE_LOOT_STATUS_LOOTED:
|
||||
if (m_creatureInfo->SkinningLootId)
|
||||
SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_SKINNABLE);
|
||||
else
|
||||
RemoveFlag(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_LOOTABLE);
|
||||
break;
|
||||
case CREATURE_LOOT_STATUS_SKINNED:
|
||||
m_corpseDecayTimer = 0; // remove corpse at next update
|
||||
RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_SKINNABLE);
|
||||
RemoveFlag(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_LOOTABLE);
|
||||
break;
|
||||
case CREATURE_LOOT_STATUS_SKIN_AVAILABLE:
|
||||
SetFlag(UNIT_FIELD_FLAGS, UNIT_DYNFLAG_LOOTABLE);
|
||||
RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_SKINNABLE);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// return true if this creature is tapped by the player or by a member of his group.
|
||||
bool Creature::IsTappedBy(Player const* player) const
|
||||
{
|
||||
|
|
@ -2038,7 +2083,7 @@ bool Creature::IsOutOfThreatArea(Unit* pVictim) const
|
|||
if (!pVictim->IsTargetableForAttack())
|
||||
return true;
|
||||
|
||||
if (!pVictim->isInAccessablePlaceFor(this))
|
||||
if (!pVictim->IsInAccessablePlaceFor(this))
|
||||
return true;
|
||||
|
||||
if (!pVictim->IsVisibleForOrDetect(this, this, false))
|
||||
|
|
@ -2771,6 +2816,54 @@ void Creature::SetLevitate(bool enable)
|
|||
}
|
||||
}
|
||||
|
||||
void Creature::SetSwim(bool enable)
|
||||
{
|
||||
if (enable)
|
||||
m_movementInfo.AddMovementFlag(MOVEFLAG_SWIMMING);
|
||||
else
|
||||
m_movementInfo.RemoveMovementFlag(MOVEFLAG_SWIMMING);
|
||||
|
||||
WorldPacket data(enable ? SMSG_SPLINE_MOVE_START_SWIM : SMSG_SPLINE_MOVE_STOP_SWIM);
|
||||
data << GetPackGUID();
|
||||
SendMessageToSet(&data, true);
|
||||
}
|
||||
|
||||
void Creature::SetCanFly(bool enable)
|
||||
{
|
||||
if (enable)
|
||||
m_movementInfo.AddMovementFlag(MOVEFLAG_CAN_FLY);
|
||||
else
|
||||
m_movementInfo.RemoveMovementFlag(MOVEFLAG_CAN_FLY);
|
||||
|
||||
WorldPacket data(enable ? SMSG_SPLINE_MOVE_SET_FLYING : SMSG_SPLINE_MOVE_UNSET_FLYING, 9);
|
||||
data << GetPackGUID();
|
||||
SendMessageToSet(&data, true);
|
||||
}
|
||||
|
||||
void Creature::SetFeatherFall(bool enable)
|
||||
{
|
||||
if (enable)
|
||||
m_movementInfo.AddMovementFlag(MOVEFLAG_SAFE_FALL);
|
||||
else
|
||||
m_movementInfo.RemoveMovementFlag(MOVEFLAG_SAFE_FALL);
|
||||
|
||||
WorldPacket data(enable ? SMSG_SPLINE_MOVE_FEATHER_FALL : SMSG_SPLINE_MOVE_NORMAL_FALL);
|
||||
data << GetPackGUID();
|
||||
SendMessageToSet(&data, true);
|
||||
}
|
||||
|
||||
void Creature::SetHover(bool enable)
|
||||
{
|
||||
if (enable)
|
||||
m_movementInfo.AddMovementFlag(MOVEFLAG_HOVER);
|
||||
else
|
||||
m_movementInfo.RemoveMovementFlag(MOVEFLAG_HOVER);
|
||||
|
||||
WorldPacket data(enable ? SMSG_SPLINE_MOVE_SET_HOVER : SMSG_SPLINE_MOVE_UNSET_HOVER, 9);
|
||||
data << GetPackGUID();
|
||||
SendMessageToSet(&data, false);
|
||||
}
|
||||
|
||||
void Creature::SetRoot(bool enable)
|
||||
{
|
||||
if (enable)
|
||||
|
|
|
|||
|
|
@ -542,11 +542,11 @@ class Creature : public Unit
|
|||
void SetCorpseDelay(uint32 delay) { m_corpseDelay = delay; }
|
||||
uint32 GetCorpseDelay() const { return m_corpseDelay; }
|
||||
bool IsRacialLeader() const { return GetCreatureInfo()->RacialLeader; }
|
||||
bool IsCivilian() const { return GetCreatureInfo()->ExtraFlags & CREATURE_EXTRA_FLAG_CIVILIAN; }
|
||||
bool IsGuard() const { return GetCreatureInfo()->ExtraFlags & CREATURE_EXTRA_FLAG_GUARD; }
|
||||
bool IsCivilian() const { return (GetCreatureInfo()->ExtraFlags & CREATURE_EXTRA_FLAG_CIVILIAN) != 0; }
|
||||
bool IsGuard() const { return (GetCreatureInfo()->ExtraFlags & CREATURE_EXTRA_FLAG_GUARD) != 0; }
|
||||
|
||||
bool CanWalk() const { return GetCreatureInfo()->InhabitType & INHABIT_GROUND; }
|
||||
bool CanSwim() const { return GetCreatureInfo()->InhabitType & INHABIT_WATER; }
|
||||
bool CanWalk() const { return (GetCreatureInfo()->InhabitType & INHABIT_GROUND) != 0; }
|
||||
bool CanSwim() const { return (GetCreatureInfo()->InhabitType & INHABIT_WATER) != 0; }
|
||||
bool IsSwimming() const { return (m_movementInfo.HasMovementFlag((MovementFlags)(MOVEFLAG_SWIMMING))); }
|
||||
bool CanFly() const { return (GetCreatureInfo()->InhabitType & INHABIT_AIR) || (GetByteValue(UNIT_FIELD_BYTES_1, 3) & UNIT_BYTE1_FLAG_FLY_ANIM) || m_movementInfo.HasMovementFlag((MovementFlags)(MOVEFLAG_LEVITATING | MOVEFLAG_CAN_FLY)); }
|
||||
bool IsFlying() const { return (m_movementInfo.HasMovementFlag((MovementFlags)(MOVEFLAG_FLYING | MOVEFLAG_LEVITATING))); }
|
||||
|
|
@ -560,6 +560,7 @@ class Creature : public Unit
|
|||
|
||||
bool IsImmuneToSpell(SpellEntry const* spellInfo, bool castOnSelf) override;
|
||||
bool IsImmuneToSpellEffect(SpellEntry const* spellInfo, SpellEffectIndex index, bool castOnSelf) const override;
|
||||
void SetLootStatus(CreatureLootStatus status);
|
||||
bool IsTappedBy(Player const* player) const;
|
||||
|
||||
bool IsElite() const
|
||||
|
|
@ -591,6 +592,10 @@ class Creature : public Unit
|
|||
|
||||
void SetWalk(bool enable, bool asDefault = true);
|
||||
void SetLevitate(bool enable);
|
||||
void SetSwim(bool enable) override;
|
||||
void SetCanFly(bool enable) override;
|
||||
void SetFeatherFall(bool enable) override;
|
||||
void SetHover(bool enable) override;
|
||||
void SetRoot(bool enable) override;
|
||||
void SetWaterWalk(bool enable) override;
|
||||
|
||||
|
|
@ -818,6 +823,7 @@ class Creature : public Unit
|
|||
uint32 m_lootMoney;
|
||||
ObjectGuid m_lootRecipientGuid; // player who will have rights for looting if m_lootGroupRecipient==0 or group disbanded
|
||||
uint32 m_lootGroupRecipientId; // group who will have rights for looting if set and exist
|
||||
CreatureLootStatus m_lootStatus; // loot status (used to know when we could loot, pickpocket or skin)
|
||||
|
||||
/// Timers
|
||||
uint32 m_corpseDecayTimer; // (msecs)timer for death or corpse disappearance
|
||||
|
|
|
|||
|
|
@ -182,6 +182,7 @@ inline bool IsTimerBasedEvent(EventAI_Type type)
|
|||
case EVENT_T_MISSING_AURA:
|
||||
case EVENT_T_TARGET_MISSING_AURA:
|
||||
case EVENT_T_RANGE:
|
||||
case EVENT_T_ENERGY:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
|
|
@ -276,6 +277,13 @@ bool CreatureEventAI::ProcessEvent(CreatureEventAIHolder& pHolder, Unit* pAction
|
|||
pHolder.UpdateRepeatTimer(m_creature, event.spell_hit.repeatMin, event.spell_hit.repeatMax);
|
||||
break;
|
||||
case EVENT_T_RANGE:
|
||||
if (!m_creature->IsInCombat() || !m_creature->getVictim() || !m_creature->IsInMap(m_creature->getVictim()))
|
||||
return false;
|
||||
|
||||
// DISCUSS TODO - Likely replace IsInRange check with CombatReach checks (as used rather for such checks)
|
||||
if (!m_creature->IsInRange(m_creature->getVictim(), (float)event.range.minDist, (float)event.range.maxDist))
|
||||
return false;
|
||||
|
||||
// Repeat Timers
|
||||
pHolder.UpdateRepeatTimer(m_creature, event.range.repeatMin, event.range.repeatMax);
|
||||
break;
|
||||
|
|
@ -394,6 +402,9 @@ bool CreatureEventAI::ProcessEvent(CreatureEventAIHolder& pHolder, Unit* pAction
|
|||
break;
|
||||
case EVENT_T_AURA:
|
||||
{
|
||||
if (!m_creature->IsInCombat())
|
||||
return false;
|
||||
|
||||
SpellAuraHolder* holder = m_creature->GetSpellAuraHolder(event.buffed.spellId);
|
||||
if (!holder || holder->GetStackAmount() < event.buffed.amount)
|
||||
return false;
|
||||
|
|
@ -419,6 +430,9 @@ bool CreatureEventAI::ProcessEvent(CreatureEventAIHolder& pHolder, Unit* pAction
|
|||
}
|
||||
case EVENT_T_MISSING_AURA:
|
||||
{
|
||||
if (!m_creature->IsInCombat())
|
||||
return false;
|
||||
|
||||
SpellAuraHolder* holder = m_creature->GetSpellAuraHolder(event.buffed.spellId);
|
||||
if (holder && holder->GetStackAmount() >= event.buffed.amount)
|
||||
return false;
|
||||
|
|
@ -444,6 +458,21 @@ bool CreatureEventAI::ProcessEvent(CreatureEventAIHolder& pHolder, Unit* pAction
|
|||
}
|
||||
case EVENT_T_RECEIVE_AI_EVENT:
|
||||
break;
|
||||
case EVENT_T_ENERGY:
|
||||
{
|
||||
if (!m_creature->IsInCombat() || !m_creature->GetMaxPower(POWER_ENERGY))
|
||||
return false;
|
||||
|
||||
uint32 perc = (m_creature->GetPower(POWER_ENERGY) * 100) / m_creature->GetMaxPower(POWER_ENERGY);
|
||||
|
||||
if (perc > event.percent_range.percentMax || perc < event.percent_range.percentMin)
|
||||
return false;
|
||||
|
||||
LOG_PROCESS_EVENT;
|
||||
// Repeat Timers
|
||||
pHolder.UpdateRepeatTimer(m_creature, event.percent_range.repeatMin, event.percent_range.repeatMax);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
sLog.outErrorEventAI("Creature %u using Event %u has invalid Event Type(%u), missing from ProcessEvent() Switch.", m_creature->GetEntry(), pHolder.Event.event_id, pHolder.Event.event_type);
|
||||
break;
|
||||
|
|
@ -478,7 +507,7 @@ bool CreatureEventAI::ProcessEvent(CreatureEventAIHolder& pHolder, Unit* pAction
|
|||
if (count)
|
||||
{
|
||||
// select action number from found amount
|
||||
uint32 idx = urand(0, count - 1);
|
||||
uint32 idx = rnd % count;
|
||||
|
||||
// find selected action, skipping not used
|
||||
uint32 j = 0;
|
||||
|
|
@ -520,17 +549,17 @@ void CreatureEventAI::ProcessAction(CreatureEventAI_Action const& action, uint32
|
|||
if (action.type == ACTION_T_TEXT)
|
||||
{
|
||||
if (action.text.TextId[1] && action.text.TextId[2])
|
||||
textId = action.text.TextId[urand(0, 2)];
|
||||
else if (action.text.TextId[1] && urand(0, 1))
|
||||
textId = action.text.TextId[rnd % 3];
|
||||
else if (action.text.TextId[1] && (rnd % 2))
|
||||
textId = action.text.TextId[1];
|
||||
else
|
||||
textId = action.text.TextId[0];
|
||||
}
|
||||
// ACTION_T_CHANCED_TEXT, chance hits
|
||||
else if (urand(0, 99) < action.chanced_text.chance)
|
||||
else if ((rnd % 100) < action.chanced_text.chance)
|
||||
{
|
||||
if (action.chanced_text.TextId[0] && action.chanced_text.TextId[1])
|
||||
textId = action.chanced_text.TextId[urand(0, 1)];
|
||||
textId = action.chanced_text.TextId[rnd % 2];
|
||||
else
|
||||
textId = action.chanced_text.TextId[0];
|
||||
}
|
||||
|
|
@ -839,9 +868,9 @@ void CreatureEventAI::ProcessAction(CreatureEventAI_Action const& action, uint32
|
|||
|
||||
Creature* pCreature = NULL;
|
||||
if ((*i).second.SpawnTimeSecs)
|
||||
pCreature = m_creature->SummonCreature(action.summon_id.creatureId, (*i).second.position_x, (*i).second.position_y, (*i).second.position_z, (*i).second.orientation, TEMPSUMMON_TIMED_OOC_OR_DEAD_DESPAWN, (*i).second.SpawnTimeSecs);
|
||||
pCreature = m_creature->SummonCreature(action.summon_id.creatureId, i->second.position_x, i->second.position_y, i->second.position_z, i->second.orientation, TEMPSUMMON_TIMED_OOC_OR_DEAD_DESPAWN, i->second.SpawnTimeSecs);
|
||||
else
|
||||
pCreature = m_creature->SummonCreature(action.summon_id.creatureId, (*i).second.position_x, (*i).second.position_y, (*i).second.position_z, (*i).second.orientation, TEMPSUMMON_TIMED_OOC_DESPAWN, 0);
|
||||
pCreature = m_creature->SummonCreature(action.summon_id.creatureId, i->second.position_x, i->second.position_y, i->second.position_z, i->second.orientation, TEMPSUMMON_TIMED_OOC_DESPAWN, 0);
|
||||
|
||||
if (!pCreature)
|
||||
sLog.outErrorEventAI("failed to spawn creature %u. EventId %d.Creature %d", action.summon_id.creatureId, EventId, m_creature->GetEntry());
|
||||
|
|
@ -1035,29 +1064,26 @@ void CreatureEventAI::Reset()
|
|||
{
|
||||
m_EventUpdateTime = EVENT_UPDATE_TIME;
|
||||
m_EventDiff = 0;
|
||||
|
||||
m_throwAIEventStep = 0;
|
||||
|
||||
// Reset all events to enabled
|
||||
for (CreatureEventAIList::iterator i = m_CreatureEventAIList.begin(); i != m_CreatureEventAIList.end(); ++i)
|
||||
{
|
||||
CreatureEventAI_Event const& event = (*i).Event;
|
||||
CreatureEventAI_Event const& event = i->Event;
|
||||
switch (event.event_type)
|
||||
{
|
||||
// Reset all out of combat timers
|
||||
// Reset all out of combat timers
|
||||
case EVENT_T_TIMER_OOC:
|
||||
{
|
||||
if ((*i).UpdateRepeatTimer(m_creature, event.timer.initialMin, event.timer.initialMax))
|
||||
(*i).Enabled = true;
|
||||
if (i->UpdateRepeatTimer(m_creature, event.timer.initialMin, event.timer.initialMax))
|
||||
i->Enabled = true;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
// TODO: enable below code line / verify this is correct to enable events previously disabled (ex. aggro yell), instead of enable this in void Aggro()
|
||||
//i->Enabled = true;
|
||||
//i->Time = 0;
|
||||
break;
|
||||
// default:
|
||||
// TODO: enable below code line / verify this is correct to enable events previously disabled (ex. aggro yell), instead of enable this in void Aggro()
|
||||
//(*i).Enabled = true;
|
||||
//(*i).Time = 0;
|
||||
// break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1066,7 +1092,7 @@ void CreatureEventAI::JustReachedHome()
|
|||
{
|
||||
for (CreatureEventAIList::iterator i = m_CreatureEventAIList.begin(); i != m_CreatureEventAIList.end(); ++i)
|
||||
{
|
||||
if ((*i).Event.event_type == EVENT_T_REACHED_HOME)
|
||||
if (i->Event.event_type == EVENT_T_REACHED_HOME)
|
||||
ProcessEvent(*i);
|
||||
}
|
||||
|
||||
|
|
@ -1079,18 +1105,16 @@ void CreatureEventAI::EnterEvadeMode()
|
|||
m_creature->DeleteThreatList();
|
||||
m_creature->CombatStop(true);
|
||||
|
||||
if (m_creature->IsAlive())
|
||||
// only alive creatures that are not on transport can return to home position
|
||||
if (m_creature->IsAlive() && !m_creature->IsBoarded())
|
||||
m_creature->GetMotionMaster()->MoveTargetedHome();
|
||||
|
||||
m_creature->SetLootRecipient(NULL);
|
||||
|
||||
if (m_bEmptyList)
|
||||
return;
|
||||
m_creature->SetLootRecipient(nullptr);
|
||||
|
||||
// Handle Evade events
|
||||
for (CreatureEventAIList::iterator i = m_CreatureEventAIList.begin(); i != m_CreatureEventAIList.end(); ++i)
|
||||
{
|
||||
if ((*i).Event.event_type == EVENT_T_EVADE)
|
||||
if (i->Event.event_type == EVENT_T_EVADE)
|
||||
ProcessEvent(*i);
|
||||
}
|
||||
}
|
||||
|
|
@ -1112,7 +1136,7 @@ void CreatureEventAI::JustDied(Unit* killer)
|
|||
// Handle On Death events
|
||||
for (CreatureEventAIList::iterator i = m_CreatureEventAIList.begin(); i != m_CreatureEventAIList.end(); ++i)
|
||||
{
|
||||
if ((*i).Event.event_type == EVENT_T_DEATH)
|
||||
if (i->Event.event_type == EVENT_T_DEATH)
|
||||
ProcessEvent(*i, killer);
|
||||
}
|
||||
|
||||
|
|
@ -1127,7 +1151,7 @@ void CreatureEventAI::KilledUnit(Unit* victim)
|
|||
|
||||
for (CreatureEventAIList::iterator i = m_CreatureEventAIList.begin(); i != m_CreatureEventAIList.end(); ++i)
|
||||
{
|
||||
if ((*i).Event.event_type == EVENT_T_KILL)
|
||||
if (i->Event.event_type == EVENT_T_KILL)
|
||||
ProcessEvent(*i, victim);
|
||||
}
|
||||
}
|
||||
|
|
@ -1136,7 +1160,7 @@ void CreatureEventAI::JustSummoned(Creature* pUnit)
|
|||
{
|
||||
for (CreatureEventAIList::iterator i = m_CreatureEventAIList.begin(); i != m_CreatureEventAIList.end(); ++i)
|
||||
{
|
||||
if ((*i).Event.event_type == EVENT_T_SUMMONED_UNIT)
|
||||
if (i->Event.event_type == EVENT_T_SUMMONED_UNIT)
|
||||
ProcessEvent(*i, pUnit);
|
||||
}
|
||||
}
|
||||
|
|
@ -1145,7 +1169,7 @@ void CreatureEventAI::SummonedCreatureJustDied(Creature* pUnit)
|
|||
{
|
||||
for (CreatureEventAIList::iterator i = m_CreatureEventAIList.begin(); i != m_CreatureEventAIList.end(); ++i)
|
||||
{
|
||||
if ((*i).Event.event_type == EVENT_T_SUMMONED_JUST_DIED)
|
||||
if (i->Event.event_type == EVENT_T_SUMMONED_JUST_DIED)
|
||||
ProcessEvent(*i, pUnit);
|
||||
}
|
||||
}
|
||||
|
|
@ -1154,12 +1178,12 @@ void CreatureEventAI::SummonedCreatureDespawn(Creature* pUnit)
|
|||
{
|
||||
for (CreatureEventAIList::iterator i = m_CreatureEventAIList.begin(); i != m_CreatureEventAIList.end(); ++i)
|
||||
{
|
||||
if ((*i).Event.event_type == EVENT_T_SUMMONED_JUST_DESPAWN)
|
||||
if (i->Event.event_type == EVENT_T_SUMMONED_JUST_DESPAWN)
|
||||
ProcessEvent(*i, pUnit);
|
||||
}
|
||||
}
|
||||
|
||||
void CreatureEventAI::ReceiveAIEvent(AIEventType eventType, Creature* pSender, Unit* pInvoker, uint32 miscValue)
|
||||
void CreatureEventAI::ReceiveAIEvent(AIEventType eventType, Creature* pSender, Unit* pInvoker, uint32 /*miscValue*/)
|
||||
{
|
||||
MANGOS_ASSERT(pSender);
|
||||
|
||||
|
|
@ -1169,29 +1193,29 @@ void CreatureEventAI::ReceiveAIEvent(AIEventType eventType, Creature* pSender, U
|
|||
itr->Event.receiveAIEvent.eventType == eventType && (!itr->Event.receiveAIEvent.senderEntry || itr->Event.receiveAIEvent.senderEntry == pSender->GetEntry()))
|
||||
ProcessEvent(*itr, pInvoker, pSender);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CreatureEventAI::EnterCombat(Unit* enemy)
|
||||
{
|
||||
// Check for on combat start events
|
||||
for (CreatureEventAIList::iterator i = m_CreatureEventAIList.begin(); i != m_CreatureEventAIList.end(); ++i)
|
||||
{
|
||||
CreatureEventAI_Event const& event = (*i).Event;
|
||||
CreatureEventAI_Event const& event = i->Event;
|
||||
switch (event.event_type)
|
||||
{
|
||||
case EVENT_T_AGGRO:
|
||||
(*i).Enabled = true;
|
||||
i->Enabled = true;
|
||||
ProcessEvent(*i, enemy);
|
||||
break;
|
||||
// Reset all in combat timers
|
||||
// Reset all in combat timers
|
||||
case EVENT_T_TIMER_IN_COMBAT:
|
||||
if ((*i).UpdateRepeatTimer(m_creature, event.timer.initialMin, event.timer.initialMax))
|
||||
(*i).Enabled = true;
|
||||
if (i->UpdateRepeatTimer(m_creature, event.timer.initialMin, event.timer.initialMax))
|
||||
i->Enabled = true;
|
||||
break;
|
||||
// All normal events need to be re-enabled and their time set to 0
|
||||
// All normal events need to be re-enabled and their time set to 0
|
||||
default:
|
||||
(*i).Enabled = true;
|
||||
(*i).Time = 0;
|
||||
i->Enabled = true;
|
||||
i->Time = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
@ -1221,21 +1245,21 @@ void CreatureEventAI::MoveInLineOfSight(Unit* who)
|
|||
return;
|
||||
|
||||
// Check for OOC LOS Event
|
||||
if (!m_creature->getVictim())
|
||||
if (m_HasOOCLoSEvent && !m_creature->getVictim())
|
||||
{
|
||||
for (CreatureEventAIList::iterator itr = m_CreatureEventAIList.begin(); itr != m_CreatureEventAIList.end(); ++itr)
|
||||
{
|
||||
if ((*itr).Event.event_type == EVENT_T_OOC_LOS)
|
||||
if (itr->Event.event_type == EVENT_T_OOC_LOS)
|
||||
{
|
||||
// can trigger if closer than fMaxAllowedRange
|
||||
float fMaxAllowedRange = (float)(*itr).Event.ooc_los.maxRange;
|
||||
float fMaxAllowedRange = (float)itr->Event.ooc_los.maxRange;
|
||||
|
||||
// if range is ok and we are actually in LOS
|
||||
if (m_creature->IsWithinDistInMap(who, fMaxAllowedRange) && m_creature->IsWithinLOSInMap(who))
|
||||
// if friendly event && who is not hostile OR hostile event && who is hostile
|
||||
if ((itr->Event.ooc_los.noHostile && !m_creature->IsHostileTo(who)) ||
|
||||
((!itr->Event.ooc_los.noHostile) && m_creature->IsHostileTo(who)))
|
||||
{
|
||||
// if friendly event&&who is not hostile OR hostile event&&who is hostile
|
||||
if (((*itr).Event.ooc_los.noHostile && !m_creature->IsHostileTo(who)) ||
|
||||
((!(*itr).Event.ooc_los.noHostile) && m_creature->IsHostileTo(who)))
|
||||
// if range is ok and we are actually in LOS
|
||||
if (m_creature->IsWithinDistInMap(who, fMaxAllowedRange) && m_creature->IsWithinLOSInMap(who))
|
||||
ProcessEvent(*itr, who);
|
||||
}
|
||||
}
|
||||
|
|
@ -1246,7 +1270,7 @@ void CreatureEventAI::MoveInLineOfSight(Unit* who)
|
|||
return;
|
||||
|
||||
if (m_creature->CanInitiateAttack() && who->IsTargetableForAttack() &&
|
||||
m_creature->IsHostileTo(who) && who->isInAccessablePlaceFor(m_creature))
|
||||
m_creature->IsHostileTo(who) && who->IsInAccessablePlaceFor(m_creature))
|
||||
{
|
||||
if (!m_creature->CanFly() && m_creature->GetDistanceZ(who) > CREATURE_Z_ATTACK_RANGE)
|
||||
return;
|
||||
|
|
@ -1271,10 +1295,10 @@ void CreatureEventAI::MoveInLineOfSight(Unit* who)
|
|||
void CreatureEventAI::SpellHit(Unit* pUnit, const SpellEntry* pSpell)
|
||||
{
|
||||
for (CreatureEventAIList::iterator i = m_CreatureEventAIList.begin(); i != m_CreatureEventAIList.end(); ++i)
|
||||
if ((*i).Event.event_type == EVENT_T_SPELLHIT)
|
||||
if (i->Event.event_type == EVENT_T_SPELLHIT)
|
||||
// If spell id matches (or no spell id) & if spell school matches (or no spell school)
|
||||
if (!(*i).Event.spell_hit.spellId || pSpell->Id == (*i).Event.spell_hit.spellId)
|
||||
if (pSpell->SchoolMask & (*i).Event.spell_hit.schoolMask)
|
||||
if (!i->Event.spell_hit.spellId || pSpell->Id == i->Event.spell_hit.spellId)
|
||||
if (pSpell->SchoolMask & i->Event.spell_hit.schoolMask)
|
||||
ProcessEvent(*i, pUnit);
|
||||
}
|
||||
|
||||
|
|
@ -1292,55 +1316,28 @@ void CreatureEventAI::UpdateAI(const uint32 diff)
|
|||
for (CreatureEventAIList::iterator i = m_CreatureEventAIList.begin(); i != m_CreatureEventAIList.end(); ++i)
|
||||
{
|
||||
// Decrement Timers
|
||||
if ((*i).Time)
|
||||
if (i->Time)
|
||||
{
|
||||
if ((*i).Time > m_EventDiff)
|
||||
if (i->Time > m_EventDiff)
|
||||
{
|
||||
// Do not decrement timers if event cannot trigger in this phase
|
||||
if (!((*i).Event.event_inverse_phase_mask & (1 << m_Phase)))
|
||||
(*i).Time -= m_EventDiff;
|
||||
|
||||
// Skip processing of events that have time remaining
|
||||
continue;
|
||||
if (!(i->Event.event_inverse_phase_mask & (1 << m_Phase)))
|
||||
i->Time -= m_EventDiff;
|
||||
}
|
||||
else (*i).Time = 0;
|
||||
else
|
||||
i->Time = 0;
|
||||
}
|
||||
|
||||
// Events that are updated every EVENT_UPDATE_TIME
|
||||
switch ((*i).Event.event_type)
|
||||
{
|
||||
case EVENT_T_TIMER_OOC:
|
||||
case EVENT_T_TIMER_GENERIC:
|
||||
ProcessEvent(*i);
|
||||
break;
|
||||
case EVENT_T_TIMER_IN_COMBAT:
|
||||
case EVENT_T_MANA:
|
||||
case EVENT_T_HP:
|
||||
case EVENT_T_TARGET_HP:
|
||||
case EVENT_T_TARGET_CASTING:
|
||||
case EVENT_T_FRIENDLY_HP:
|
||||
case EVENT_T_AURA:
|
||||
case EVENT_T_TARGET_AURA:
|
||||
case EVENT_T_MISSING_AURA:
|
||||
case EVENT_T_TARGET_MISSING_AURA:
|
||||
if (Combat)
|
||||
ProcessEvent(*i);
|
||||
break;
|
||||
case EVENT_T_RANGE:
|
||||
if (Combat)
|
||||
{
|
||||
if (m_creature->getVictim() && m_creature->IsInMap(m_creature->getVictim()))
|
||||
if (m_creature->IsInRange(m_creature->getVictim(), (float)(*i).Event.range.minDist, (float)(*i).Event.range.maxDist))
|
||||
ProcessEvent(*i);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
// Skip processing of events that have time remaining or are disabled
|
||||
if (!(i->Enabled) || i->Time)
|
||||
continue;
|
||||
|
||||
m_EventDiff = 0;
|
||||
m_EventUpdateTime = EVENT_UPDATE_TIME;
|
||||
if (IsTimerBasedEvent(i->Event.event_type))
|
||||
ProcessEvent(*i);
|
||||
}
|
||||
|
||||
m_EventDiff = 0;
|
||||
m_EventUpdateTime = EVENT_UPDATE_TIME;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -1348,9 +1345,23 @@ void CreatureEventAI::UpdateAI(const uint32 diff)
|
|||
m_EventUpdateTime -= diff;
|
||||
}
|
||||
|
||||
// Melee Auto-Attack (recheck m_creature->getVictim in case of combat state was changed while processing events)
|
||||
if (Combat && m_MeleeEnabled && m_creature->getVictim())
|
||||
DoMeleeAttackIfReady();
|
||||
// Melee Auto-Attack (getVictim might be nullptr as result of timer based events and actions)
|
||||
if (Combat && m_creature->getVictim())
|
||||
{
|
||||
// Update creature dynamic movement position before doing anything else
|
||||
if (m_DynamicMovement)
|
||||
{
|
||||
if (!m_creature->hasUnitState(UNIT_STAT_CAN_NOT_REACT) && !m_creature->IsNonMeleeSpellCasted(false))
|
||||
{
|
||||
if (m_LastSpellMaxRange && m_creature->IsInRange(m_creature->getVictim(), 0, (m_LastSpellMaxRange / 1.5f)))
|
||||
SetCombatMovement(false, true);
|
||||
else
|
||||
SetCombatMovement(true, true);
|
||||
}
|
||||
}
|
||||
else if (m_MeleeEnabled)
|
||||
DoMeleeAttackIfReady();
|
||||
}
|
||||
}
|
||||
|
||||
bool CreatureEventAI::IsVisible(Unit* pl) const
|
||||
|
|
@ -1478,12 +1489,12 @@ void CreatureEventAI::ReceiveEmote(Player* pPlayer, uint32 text_emote)
|
|||
{
|
||||
for (CreatureEventAIList::iterator itr = m_CreatureEventAIList.begin(); itr != m_CreatureEventAIList.end(); ++itr)
|
||||
{
|
||||
if ((*itr).Event.event_type == EVENT_T_RECEIVE_EMOTE)
|
||||
if (itr->Event.event_type == EVENT_T_RECEIVE_EMOTE)
|
||||
{
|
||||
if ((*itr).Event.receive_emote.emoteId != text_emote)
|
||||
return;
|
||||
if (itr->Event.receive_emote.emoteId != text_emote)
|
||||
continue;
|
||||
|
||||
PlayerCondition pcon(0, (*itr).Event.receive_emote.condition, (*itr).Event.receive_emote.conditionValue1, (*itr).Event.receive_emote.conditionValue2);
|
||||
PlayerCondition pcon(0, itr->Event.receive_emote.condition, itr->Event.receive_emote.conditionValue1, itr->Event.receive_emote.conditionValue2);
|
||||
if (pcon.Meets(pPlayer, m_creature->GetMap(), m_creature, CONDITION_FROM_EVENTAI))
|
||||
{
|
||||
DEBUG_FILTER_LOG(LOG_FILTER_AI_AND_MOVEGENSS, "CreatureEventAI: ReceiveEmote CreatureEventAI: Condition ok, processing");
|
||||
|
|
|
|||
|
|
@ -70,6 +70,7 @@ enum EventAI_Type
|
|||
EVENT_T_TARGET_MISSING_AURA = 28, // Param1 = SpellID, Param2 = Number of time stacked expected, Param3/4 Repeat Min/Max
|
||||
EVENT_T_TIMER_GENERIC = 29, // InitialMin, InitialMax, RepeatMin, RepeatMax
|
||||
EVENT_T_RECEIVE_AI_EVENT = 30, // AIEventType, Sender-Entry, unused, unused
|
||||
EVENT_T_ENERGY = 31, // EnergyMax%, EnergyMin%, RepeatMin, RepeatMax
|
||||
|
||||
EVENT_T_END,
|
||||
};
|
||||
|
|
@ -467,6 +468,7 @@ struct CreatureEventAI_Event
|
|||
// EVENT_T_MANA = 3
|
||||
// EVENT_T_TARGET_HP = 12
|
||||
// EVENT_T_TARGET_MANA = 18
|
||||
// EVENT_T_ENERGY = 31
|
||||
struct
|
||||
{
|
||||
uint32 percentMax;
|
||||
|
|
|
|||
|
|
@ -90,7 +90,7 @@ void CreatureEventAIMgr::CheckUnusedAITexts()
|
|||
sLog.outErrorEventAI("Entry %i in table `creature_ai_texts` but not used in EventAI scripts.", *itr);
|
||||
}
|
||||
|
||||
/// Helper function to check if a target-suite is suitable for the event-type
|
||||
/// Helper function to check if a target-type is suitable for the event-type
|
||||
bool IsValidTargetType(EventAI_Type eventType, EventAI_ActionType actionType, uint32 targetType, uint32 eventId, uint8 action)
|
||||
{
|
||||
switch (targetType)
|
||||
|
|
@ -327,6 +327,7 @@ void CreatureEventAIMgr::LoadCreatureEventAI_Scripts()
|
|||
case EVENT_T_MANA:
|
||||
case EVENT_T_TARGET_HP:
|
||||
case EVENT_T_TARGET_MANA:
|
||||
case EVENT_T_ENERGY:
|
||||
if (temp.percent_range.percentMax > 100)
|
||||
sLog.outErrorEventAI("Creature %u are using percentage event(%u) with param2 (MinPercent) > 100. Event will never trigger! ", temp.creature_id, i);
|
||||
|
||||
|
|
|
|||
|
|
@ -1269,8 +1269,11 @@ void GameObject::Use(Unit* user)
|
|||
player->RewardPlayerAndGroupAtCast(this);
|
||||
}
|
||||
|
||||
if (scriptReturnValue)
|
||||
{ return; }
|
||||
// activate script
|
||||
if (!scriptReturnValue)
|
||||
GetMap()->ScriptsStart(sGameObjectScripts, GetGUIDLow(), spellCaster, this);
|
||||
else
|
||||
return;
|
||||
|
||||
// cast this spell later if provided
|
||||
spellId = info->goober.spellId;
|
||||
|
|
@ -2382,6 +2385,23 @@ void GameObject::ForceGameObjectHealth(int32 diff, Unit* caster)
|
|||
SetGoAnimProgress(GetMaxHealth() ? m_useTimes * 255 / GetMaxHealth() : 255);
|
||||
}
|
||||
|
||||
float GameObject::GetInteractionDistance()
|
||||
{
|
||||
switch (GetGoType())
|
||||
{
|
||||
// TODO: find out how the client calculates the maximal usage distance to spellless working
|
||||
// gameobjects like guildbanks and mailboxes - 10.0 is a just an abitrary chosen number
|
||||
case GAMEOBJECT_TYPE_GUILD_BANK:
|
||||
case GAMEOBJECT_TYPE_MAILBOX:
|
||||
return 10.0f;
|
||||
case GAMEOBJECT_TYPE_FISHINGHOLE:
|
||||
case GAMEOBJECT_TYPE_FISHINGNODE:
|
||||
return 20.0f + CONTACT_DISTANCE; // max spell range
|
||||
default:
|
||||
return INTERACTION_DISTANCE;
|
||||
}
|
||||
}
|
||||
|
||||
uint32 GameObject::GetScriptId()
|
||||
{
|
||||
return sScriptMgr.GetBoundScriptId(SCRIPTED_GAMEOBJECT, -int32(GetGUIDLow())) ? sScriptMgr.GetBoundScriptId(SCRIPTED_GAMEOBJECT, -int32(GetGUIDLow())) : sScriptMgr.GetBoundScriptId(SCRIPTED_GAMEOBJECT, GetEntry());
|
||||
|
|
|
|||
|
|
@ -842,6 +842,11 @@ class GameObject : public WorldObject
|
|||
ObjectGuid m_lootRecipientGuid; // player who will have rights for looting if m_lootGroupRecipient==0 or group disbanded
|
||||
uint32 m_lootGroupRecipientId; // group who will have rights for looting if set and exist
|
||||
|
||||
// Used for chest type
|
||||
bool m_isInUse; // only one player at time are allowed to open chest
|
||||
time_t m_reStockTimer; // timer to refill the chest
|
||||
time_t m_despawnTimer; // timer to despawn the chest if something changed in it
|
||||
|
||||
private:
|
||||
void SwitchDoorOrButton(bool activate, bool alternative = false);
|
||||
void TickCapturePoint();
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@ void GuardAI::MoveInLineOfSight(Unit* u)
|
|||
|
||||
if (!m_creature->getVictim() && u->IsTargetableForAttack() &&
|
||||
(u->IsHostileToPlayers() || m_creature->IsHostileTo(u) /*|| u->getVictim() && m_creature->IsFriendlyTo(u->getVictim())*/) &&
|
||||
u->isInAccessablePlaceFor(m_creature))
|
||||
u->IsInAccessablePlaceFor(m_creature))
|
||||
{
|
||||
float attackRadius = m_creature->GetAttackDistance(u);
|
||||
if (m_creature->IsWithinDistInMap(u, attackRadius))
|
||||
|
|
|
|||
|
|
@ -332,7 +332,7 @@ bool LootStoreItem::IsValid(LootStore const& store, uint32 entry) const
|
|||
return false;
|
||||
}
|
||||
|
||||
if (maxcount < mincountOrRef) // wrong max count
|
||||
if (maxcount < (uint32)mincountOrRef) // wrong max count
|
||||
{
|
||||
sLog.outErrorDb("Table '%s' entry %d item %d: max count (%u) less that min count (%i) - skipped", store.GetName(), entry, itemid, uint32(maxcount), mincountOrRef);
|
||||
return false;
|
||||
|
|
|
|||
|
|
@ -70,7 +70,9 @@ enum LootType
|
|||
|
||||
LOOT_FISHINGHOLE = 20, // unsupported by client, sending LOOT_FISHING instead
|
||||
LOOT_FISHING_FAIL = 21, // unsupported by client, sending LOOT_FISHING instead
|
||||
LOOT_INSIGNIA = 22 // unsupported by client, sending LOOT_CORPSE instead
|
||||
LOOT_INSIGNIA = 22, // unsupported by client, sending LOOT_CORPSE instead
|
||||
LOOT_MAIL = 23,
|
||||
LOOT_SPELL = 24,
|
||||
};
|
||||
|
||||
enum LootSlotType
|
||||
|
|
|
|||
|
|
@ -664,19 +664,37 @@ void Object::BuildValuesUpdate(uint8 updatetype, ByteBuffer* data, UpdateMask* u
|
|||
{
|
||||
*data << (m_uint32Values[index] & ~UNIT_FLAG_NOT_SELECTABLE);
|
||||
}
|
||||
// hide lootable animation for unallowed players
|
||||
/* Hide loot animation for players that aren't permitted to loot the corpse */
|
||||
else if (index == UNIT_DYNAMIC_FLAGS && GetTypeId() == TYPEID_UNIT)
|
||||
{
|
||||
uint32 send_value = m_uint32Values[index];
|
||||
|
||||
/* Initiate pointer to creature so we can check loot */
|
||||
if (Creature* my_creature = (Creature*)this)
|
||||
/* If the creature is NOT fully looted */
|
||||
if (!my_creature->loot.isLooted())
|
||||
/* If the lootable flag is NOT set */
|
||||
if (!(send_value & UNIT_DYNFLAG_LOOTABLE))
|
||||
{
|
||||
/* Update it on the creature */
|
||||
my_creature->SetFlag(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_LOOTABLE);
|
||||
/* Update it in the packet */
|
||||
send_value = send_value | UNIT_DYNFLAG_LOOTABLE;
|
||||
}
|
||||
|
||||
/* If we're not allowed to loot the target, destroy the lootable flag */
|
||||
if (!target->isAllowedToLoot((Creature*)this))
|
||||
*data << (m_uint32Values[index] & ~(UNIT_DYNFLAG_LOOTABLE | UNIT_DYNFLAG_TAPPED_BY_PLAYER));
|
||||
else
|
||||
{
|
||||
// flag only for original loot recipent
|
||||
if (target->GetObjectGuid() == ((Creature*)this)->GetLootRecipientGuid())
|
||||
*data << m_uint32Values[index];
|
||||
else
|
||||
*data << (m_uint32Values[index] & ~(UNIT_DYNFLAG_TAPPED | UNIT_DYNFLAG_TAPPED_BY_PLAYER));
|
||||
}
|
||||
if (send_value & UNIT_DYNFLAG_LOOTABLE)
|
||||
{ send_value = send_value & ~UNIT_DYNFLAG_LOOTABLE; }
|
||||
|
||||
/* If we are allowed to loot it and mob is tapped by us, destroy the tapped flag */
|
||||
bool is_tapped = target->IsTappedByMeOrMyGroup((Creature*)this);
|
||||
|
||||
/* If the creature has tapped flag but is tapped by us, remove the flag */
|
||||
if (send_value & UNIT_DYNFLAG_TAPPED && is_tapped)
|
||||
{ send_value = send_value & ~UNIT_DYNFLAG_TAPPED; }
|
||||
|
||||
*data << send_value;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -1479,8 +1497,11 @@ void WorldObject::UpdateGroundPositionZ(float x, float y, float& z) const
|
|||
{ z = new_z + 0.05f; } // just to be sure that we are not a few pixel under the surface
|
||||
}
|
||||
|
||||
void WorldObject::UpdateAllowedPositionZ(float x, float y, float& z) const
|
||||
void WorldObject::UpdateAllowedPositionZ(float x, float y, float& z, Map* atMap /*=nullptr*/) const
|
||||
{
|
||||
if (!atMap)
|
||||
atMap = GetMap();
|
||||
|
||||
switch (GetTypeId())
|
||||
{
|
||||
case TYPEID_UNIT:
|
||||
|
|
@ -1492,21 +1513,21 @@ void WorldObject::UpdateAllowedPositionZ(float x, float y, float& z) const
|
|||
bool canSwim = ((Creature const*)this)->CanSwim();
|
||||
float ground_z = z;
|
||||
float max_z = canSwim
|
||||
? GetTerrain()->GetWaterOrGroundLevel(x, y, z, &ground_z, !((Unit const*)this)->HasAuraType(SPELL_AURA_WATER_WALK))
|
||||
: ((ground_z = GetMap()->GetHeight(GetPhaseMask(), x, y, z)));
|
||||
? atMap->GetTerrain()->GetWaterOrGroundLevel(x, y, z, &ground_z, !((Unit const*)this)->HasAuraType(SPELL_AURA_WATER_WALK))
|
||||
: ((ground_z = atMap->GetHeight(GetPhaseMask(), x, y, z)));
|
||||
if (max_z > INVALID_HEIGHT)
|
||||
{
|
||||
if (z > max_z)
|
||||
{ z = max_z; }
|
||||
z = max_z;
|
||||
else if (z < ground_z)
|
||||
{ z = ground_z; }
|
||||
z = ground_z;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
float ground_z = GetMap()->GetHeight(GetPhaseMask(), x, y, z);
|
||||
float ground_z = atMap->GetHeight(GetPhaseMask(), x, y, z);
|
||||
if (z < ground_z)
|
||||
{ z = ground_z; }
|
||||
z = ground_z;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
|
@ -1516,18 +1537,18 @@ void WorldObject::UpdateAllowedPositionZ(float x, float y, float& z) const
|
|||
if (!((Player const*)this)->CanFly())
|
||||
{
|
||||
float ground_z = z;
|
||||
float max_z = GetTerrain()->GetWaterOrGroundLevel(x, y, z, &ground_z, !((Unit const*)this)->HasAuraType(SPELL_AURA_WATER_WALK));
|
||||
float max_z = atMap->GetTerrain()->GetWaterOrGroundLevel(x, y, z, &ground_z, !((Unit const*)this)->HasAuraType(SPELL_AURA_WATER_WALK));
|
||||
if (max_z > INVALID_HEIGHT)
|
||||
{
|
||||
if (z > max_z)
|
||||
{ z = max_z; }
|
||||
z = max_z;
|
||||
else if (z < ground_z)
|
||||
{ z = ground_z; }
|
||||
z = ground_z;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
float ground_z = GetMap()->GetHeight(GetPhaseMask(), x, y, z);
|
||||
float ground_z = atMap->GetHeight(GetPhaseMask(), x, y, z);
|
||||
if (z < ground_z)
|
||||
z = ground_z;
|
||||
}
|
||||
|
|
@ -1535,9 +1556,9 @@ void WorldObject::UpdateAllowedPositionZ(float x, float y, float& z) const
|
|||
}
|
||||
default:
|
||||
{
|
||||
float ground_z = GetMap()->GetHeight(GetPhaseMask(), x, y, z);
|
||||
float ground_z = atMap->GetHeight(GetPhaseMask(), x, y, z);
|
||||
if (ground_z > INVALID_HEIGHT)
|
||||
{ z = ground_z; }
|
||||
z = ground_z;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
@ -1914,9 +1935,9 @@ void WorldObject::GetNearPoint(WorldObject const* searcher, float& x, float& y,
|
|||
if (!sWorld.getConfig(CONFIG_BOOL_DETECT_POS_COLLISION))
|
||||
{
|
||||
if (searcher)
|
||||
searcher->UpdateAllowedPositionZ(x, y, z); // update to LOS height if available
|
||||
searcher->UpdateAllowedPositionZ(x, y, z, GetMap()); // update to LOS height if available
|
||||
else
|
||||
{ UpdateGroundPositionZ(x, y, z); }
|
||||
UpdateGroundPositionZ(x, y, z);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -1942,12 +1963,12 @@ void WorldObject::GetNearPoint(WorldObject const* searcher, float& x, float& y,
|
|||
if (selector.CheckOriginalAngle())
|
||||
{
|
||||
if (searcher)
|
||||
searcher->UpdateAllowedPositionZ(x, y, z); // update to LOS height if available
|
||||
searcher->UpdateAllowedPositionZ(x, y, z, GetMap()); // update to LOS height if available
|
||||
else
|
||||
{ UpdateGroundPositionZ(x, y, z); }
|
||||
UpdateGroundPositionZ(x, y, z);
|
||||
|
||||
if (fabs(init_z - z) < dist && IsWithinLOS(x, y, z))
|
||||
{ return; }
|
||||
return;
|
||||
|
||||
first_los_conflict = true; // first point have LOS problems
|
||||
}
|
||||
|
|
@ -1964,12 +1985,12 @@ void WorldObject::GetNearPoint(WorldObject const* searcher, float& x, float& y,
|
|||
z = GetPositionZ();
|
||||
|
||||
if (searcher)
|
||||
searcher->UpdateAllowedPositionZ(x, y, z); // update to LOS height if available
|
||||
searcher->UpdateAllowedPositionZ(x, y, z, GetMap()); // update to LOS height if available
|
||||
else
|
||||
{ UpdateGroundPositionZ(x, y, z); }
|
||||
UpdateGroundPositionZ(x, y, z);
|
||||
|
||||
if (fabs(init_z - z) < dist && IsWithinLOS(x, y, z))
|
||||
{ return; }
|
||||
return;
|
||||
}
|
||||
|
||||
// BAD NEWS: not free pos (or used or have LOS problems)
|
||||
|
|
@ -1980,9 +2001,9 @@ void WorldObject::GetNearPoint(WorldObject const* searcher, float& x, float& y,
|
|||
y = first_y;
|
||||
|
||||
if (searcher)
|
||||
searcher->UpdateAllowedPositionZ(x, y, z); // update to LOS height if available
|
||||
searcher->UpdateAllowedPositionZ(x, y, z, GetMap()); // update to LOS height if available
|
||||
else
|
||||
{ UpdateGroundPositionZ(x, y, z); }
|
||||
UpdateGroundPositionZ(x, y, z);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -1996,12 +2017,12 @@ void WorldObject::GetNearPoint(WorldObject const* searcher, float& x, float& y,
|
|||
z = GetPositionZ();
|
||||
|
||||
if (searcher)
|
||||
searcher->UpdateAllowedPositionZ(x, y, z); // update to LOS height if available
|
||||
searcher->UpdateAllowedPositionZ(x, y, z, GetMap()); // update to LOS height if available
|
||||
else
|
||||
{ UpdateGroundPositionZ(x, y, z); }
|
||||
UpdateGroundPositionZ(x, y, z);
|
||||
|
||||
if (fabs(init_z - z) < dist && IsWithinLOS(x, y, z))
|
||||
{ return; }
|
||||
return;
|
||||
}
|
||||
|
||||
// BAD BAD NEWS: all found pos (free and used) have LOS problem :(
|
||||
|
|
@ -2009,9 +2030,9 @@ void WorldObject::GetNearPoint(WorldObject const* searcher, float& x, float& y,
|
|||
y = first_y;
|
||||
|
||||
if (searcher)
|
||||
searcher->UpdateAllowedPositionZ(x, y, z); // update to LOS height if available
|
||||
searcher->UpdateAllowedPositionZ(x, y, z, GetMap());// update to LOS height if available
|
||||
else
|
||||
{ UpdateGroundPositionZ(x, y, z); }
|
||||
UpdateGroundPositionZ(x, y, z);
|
||||
}
|
||||
|
||||
void WorldObject::SetPhaseMask(uint32 newPhaseMask, bool update)
|
||||
|
|
|
|||
|
|
@ -536,7 +536,7 @@ class WorldObject : public Object
|
|||
|
||||
bool IsPositionValid() const;
|
||||
void UpdateGroundPositionZ(float x, float y, float& z) const;
|
||||
void UpdateAllowedPositionZ(float x, float y, float& z) const;
|
||||
void UpdateAllowedPositionZ(float x, float y, float& z, Map* atMap = nullptr) const;
|
||||
|
||||
void GetRandomPoint(float x, float y, float z, float distance, float& rand_x, float& rand_y, float& rand_z) const;
|
||||
|
||||
|
|
|
|||
|
|
@ -1438,7 +1438,7 @@ void ObjectMgr::LoadCreatures()
|
|||
|
||||
if (cInfo->RegenerateStats & REGEN_FLAG_HEALTH && data.curhealth < cInfo->MinLevelHealth)
|
||||
{
|
||||
sLog.outErrorDb("Table `creature` have creature (GUID: %u Entry: %u) with `creature_template`.`RegenHealth`=1 and low current health (%u), `creature_template`.`MinLevelHealth`=%u.", guid, data.id, data.curhealth, cInfo->MinLevelHealth);
|
||||
sLog.outErrorDb("Table `creature` have creature (GUID: %u Entry: %u) with `creature_template`.`RegenerateStats` & REGEN_FLAG_HEALTH and low current health (%u), `creature_template`.`MinLevelHealth`=%u.", guid, data.id, data.curhealth, cInfo->MinLevelHealth);
|
||||
data.curhealth = cInfo->MinLevelHealth;
|
||||
}
|
||||
|
||||
|
|
@ -1491,8 +1491,13 @@ void ObjectMgr::LoadCreatures()
|
|||
}
|
||||
|
||||
if (gameEvent == 0 && GuidPoolId == 0 && EntryPoolId == 0) // if not this is to be managed by GameEvent System or Pool system
|
||||
{
|
||||
AddCreatureToGrid(guid, &data);
|
||||
|
||||
if (cInfo->ExtraFlags & CREATURE_EXTRA_FLAG_ACTIVE)
|
||||
m_activeCreatures.insert(ActiveCreatureGuidsOnMap::value_type(data.mapid, guid));
|
||||
}
|
||||
|
||||
++count;
|
||||
}
|
||||
while (result->NextRow());
|
||||
|
|
@ -7580,9 +7585,9 @@ void ObjectMgr::LoadCreatureQuestRelations()
|
|||
{
|
||||
CreatureInfo const* cInfo = GetCreatureTemplate(itr->first);
|
||||
if (!cInfo)
|
||||
sLog.outErrorDb("Table `creature_questrelation` have data for nonexistent creature entry (%u) and existing quest %u", itr->first, itr->second);
|
||||
sLog.outErrorDb("Table `creature_involvedrelation` have data for nonexistent creature entry (%u) and existing quest %u", itr->first, itr->second);
|
||||
else if (!(cInfo->NpcFlags & UNIT_NPC_FLAG_QUESTGIVER))
|
||||
sLog.outErrorDb("Table `creature_questrelation` has creature entry (%u) for quest %u, but NpcFlags does not include UNIT_NPC_FLAG_QUESTGIVER", itr->first, itr->second);
|
||||
sLog.outErrorDb("Table `creature_involvedrelation` has creature entry (%u) for quest %u, but NpcFlags does not include UNIT_NPC_FLAG_QUESTGIVER", itr->first, itr->second);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -9400,6 +9405,7 @@ void ObjectMgr::LoadTrainerTemplates()
|
|||
|
||||
// post loading check
|
||||
std::set<uint32> trainer_ids;
|
||||
bool hasErrored = false;
|
||||
|
||||
for (CacheTrainerSpellMap::const_iterator tItr = m_mCacheTrainerTemplateSpellMap.begin(); tItr != m_mCacheTrainerTemplateSpellMap.end(); ++tItr)
|
||||
trainer_ids.insert(tItr->first);
|
||||
|
|
@ -9408,18 +9414,21 @@ void ObjectMgr::LoadTrainerTemplates()
|
|||
{
|
||||
if (CreatureInfo const* cInfo = sCreatureStorage.LookupEntry<CreatureInfo>(i))
|
||||
{
|
||||
if (cInfo->TrainerTemplateId)
|
||||
{
|
||||
if (m_mCacheTrainerTemplateSpellMap.find(cInfo->TrainerTemplateId) != m_mCacheTrainerTemplateSpellMap.end())
|
||||
trainer_ids.erase(cInfo->TrainerTemplateId);
|
||||
else
|
||||
sLog.outErrorDb("Creature (Entry: %u) has trainer_id = %u for nonexistent trainer template", cInfo->Entry, cInfo->TrainerTemplateId);
|
||||
}
|
||||
{
|
||||
sLog.outErrorDb("Creature (Entry: %u) has TrainerTemplateId = %u for nonexistent trainer template", cInfo->Entry, cInfo->TrainerTemplateId);
|
||||
hasErrored = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (std::set<uint32>::const_iterator tItr = trainer_ids.begin(); tItr != trainer_ids.end(); ++tItr)
|
||||
sLog.outErrorDb("Table `npc_trainer_template` has trainer template %u not used by any trainers ", *tItr);
|
||||
|
||||
if (hasErrored || !trainer_ids.empty()) // Append extra line in case of reported errors
|
||||
sLog.outString();
|
||||
}
|
||||
|
||||
void ObjectMgr::LoadVendors(char const* tableName, bool isTemplates)
|
||||
|
|
@ -9497,7 +9506,7 @@ void ObjectMgr::LoadVendorTemplates()
|
|||
if (m_mCacheVendorTemplateItemMap.find(cInfo->VendorTemplateId) != m_mCacheVendorTemplateItemMap.end())
|
||||
vendor_ids.erase(cInfo->VendorTemplateId);
|
||||
else
|
||||
sLog.outErrorDb("Creature (Entry: %u) has vendor_id = %u for nonexistent vendor template", cInfo->Entry, cInfo->VendorTemplateId);
|
||||
sLog.outErrorDb("Creature (Entry: %u) has VendorTemplateId = %u for nonexistent vendor template", cInfo->Entry, cInfo->VendorTemplateId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -9506,6 +9515,56 @@ void ObjectMgr::LoadVendorTemplates()
|
|||
sLog.outErrorDb("Table `npc_vendor_template` has vendor template %u not used by any vendors ", *vItr);
|
||||
}
|
||||
|
||||
/* This function is supposed to take care of three things:
|
||||
* 1) Load Transports on Map or on Continents
|
||||
* 2) Load Active Npcs on Map or Continents
|
||||
* 3) Load Everything dependend on config setting LoadAllGridsOnMaps
|
||||
*
|
||||
* This function is currently WIP, hence parts exist only as draft.
|
||||
*/
|
||||
void ObjectMgr::LoadActiveEntities(Map* _map)
|
||||
{
|
||||
// Special case on startup - load continents
|
||||
if (!_map)
|
||||
{
|
||||
uint32 continents[] = {0, 1, 530, 571};
|
||||
for (int i = 0; i < countof(continents); ++i)
|
||||
{
|
||||
_map = sMapMgr.FindMap(continents[i]);
|
||||
if (!_map)
|
||||
_map = sMapMgr.CreateMap(continents[i], nullptr);
|
||||
|
||||
if (_map)
|
||||
LoadActiveEntities(_map);
|
||||
else
|
||||
sLog.outError("ObjectMgr::LoadActiveEntities - Unable to create Map %u", continents[i]);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Load active objects for _map
|
||||
if (sWorld.isForceLoadMap(_map->GetId()))
|
||||
{
|
||||
for (CreatureDataMap::const_iterator itr = mCreatureDataMap.begin(); itr != mCreatureDataMap.end(); ++itr)
|
||||
{
|
||||
if (itr->second.mapid == _map->GetId())
|
||||
_map->ForceLoadGrid(itr->second.posX, itr->second.posY);
|
||||
}
|
||||
}
|
||||
else // Normal case - Load all npcs that are active
|
||||
{
|
||||
std::pair<ActiveCreatureGuidsOnMap::const_iterator, ActiveCreatureGuidsOnMap::const_iterator> bounds = m_activeCreatures.equal_range(_map->GetId());
|
||||
for (ActiveCreatureGuidsOnMap::const_iterator itr = bounds.first; itr != bounds.second; ++itr)
|
||||
{
|
||||
CreatureData const& data = mCreatureDataMap[itr->second];
|
||||
_map->ForceLoadGrid(data.posX, data.posY);
|
||||
}
|
||||
}
|
||||
|
||||
// Load Transports on Map _map
|
||||
}
|
||||
|
||||
void ObjectMgr::LoadNpcGossips()
|
||||
{
|
||||
m_mCacheNpcTextIdMap.clear();
|
||||
|
|
@ -9568,11 +9627,9 @@ void ObjectMgr::LoadGossipMenu(std::set<uint32>& gossipScriptSet)
|
|||
if (!result)
|
||||
{
|
||||
BarGoLink bar(1);
|
||||
|
||||
bar.step();
|
||||
|
||||
sLog.outString();
|
||||
sLog.outErrorDb(">> Loaded gossip_menu, table is empty!");
|
||||
sLog.outString();
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -9631,20 +9688,25 @@ void ObjectMgr::LoadGossipMenu(std::set<uint32>& gossipScriptSet)
|
|||
|
||||
delete result;
|
||||
|
||||
sLog.outString();
|
||||
sLog.outString(">> Loaded %u gossip_menu entries", count);
|
||||
|
||||
// post loading tests
|
||||
for (uint32 i = 1; i < sCreatureStorage.GetMaxEntry(); ++i)
|
||||
{
|
||||
if (CreatureInfo const* cInfo = sCreatureStorage.LookupEntry<CreatureInfo>(i))
|
||||
if (cInfo->GossipMenuId)
|
||||
if (m_mGossipMenusMap.find(cInfo->GossipMenuId) == m_mGossipMenusMap.end())
|
||||
sLog.outErrorDb("Creature (Entry: %u) has gossip_menu_id = %u for nonexistent menu", cInfo->Entry, cInfo->GossipMenuId);
|
||||
sLog.outErrorDb("Creature (Entry: %u) has GossipMenuId = %u for nonexistent menu", cInfo->Entry, cInfo->GossipMenuId);
|
||||
}
|
||||
|
||||
for (SQLStorageBase::SQLSIterator<GameObjectInfo> itr = sGOStorage.getDataBegin<GameObjectInfo>(); itr < sGOStorage.getDataEnd<GameObjectInfo>(); ++itr)
|
||||
if (uint32 menuid = itr->GetGossipMenuId())
|
||||
if (m_mGossipMenusMap.find(menuid) == m_mGossipMenusMap.end())
|
||||
ERROR_DB_STRICT_LOG("Gameobject (Entry: %u) has gossip_menu_id = %u for nonexistent menu", itr->id, menuid);
|
||||
if (!sLog.HasLogFilter(LOG_FILTER_DB_STRICTED_CHECK))
|
||||
{
|
||||
for (SQLStorageBase::SQLSIterator<GameObjectInfo> itr = sGOStorage.getDataBegin<GameObjectInfo>(); itr < sGOStorage.getDataEnd<GameObjectInfo>(); ++itr)
|
||||
if (uint32 menuid = itr->GetGossipMenuId())
|
||||
if (m_mGossipMenusMap.find(menuid) == m_mGossipMenusMap.end())
|
||||
sLog.outErrorDb("Gameobject (Entry: %u) has gossip_menu_id = %u for nonexistent menu", itr->id, menuid);
|
||||
}
|
||||
|
||||
sLog.outString(">> Loaded %u gossip_menu entries", count);
|
||||
sLog.outString();
|
||||
}
|
||||
|
||||
void ObjectMgr::LoadGossipMenuItems(std::set<uint32>& gossipScriptSet)
|
||||
|
|
@ -9660,11 +9722,9 @@ void ObjectMgr::LoadGossipMenuItems(std::set<uint32>& gossipScriptSet)
|
|||
if (!result)
|
||||
{
|
||||
BarGoLink bar(1);
|
||||
|
||||
bar.step();
|
||||
|
||||
sLog.outString();
|
||||
sLog.outErrorDb(">> Loaded gossip_menu_option, table is empty!");
|
||||
sLog.outString();
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -9770,7 +9830,7 @@ void ObjectMgr::LoadGossipMenuItems(std::set<uint32>& gossipScriptSet)
|
|||
}
|
||||
|
||||
if (found_menu_uses && !found_flags_uses)
|
||||
sLog.outErrorDb("Table gossip_menu_option for menu %u, id %u has `npc_option_NpcFlags` = %u but creatures using this menu does not have corresponding`NpcFlags`. Option will not accessible in game.", gMenuItem.menu_id, gMenuItem.id, gMenuItem.npc_option_NpcFlags);
|
||||
sLog.outErrorDb("Table gossip_menu_option for menu %u, id %u has `npc_option_npcflag` = %u but creatures using this menu does not have corresponding `NpcFlags`. Option will not accessible in game.", gMenuItem.menu_id, gMenuItem.id, gMenuItem.npc_option_NpcFlags);
|
||||
}
|
||||
|
||||
if (gMenuItem.action_poi_id && !GetPointOfInterest(gMenuItem.action_poi_id))
|
||||
|
|
@ -9804,7 +9864,6 @@ void ObjectMgr::LoadGossipMenuItems(std::set<uint32>& gossipScriptSet)
|
|||
m_mGossipMenuItemsMap.insert(GossipMenuItemsMap::value_type(gMenuItem.menu_id, gMenuItem));
|
||||
|
||||
++count;
|
||||
|
||||
}
|
||||
while (result->NextRow());
|
||||
|
||||
|
|
@ -9816,8 +9875,8 @@ void ObjectMgr::LoadGossipMenuItems(std::set<uint32>& gossipScriptSet)
|
|||
sLog.outErrorDb("Table `gossip_menu` contain unused (in creature or GO or menu options) menu id %u.", *itr);
|
||||
}
|
||||
|
||||
sLog.outString();
|
||||
sLog.outString(">> Loaded %u gossip_menu_option entries", count);
|
||||
sLog.outString();
|
||||
}
|
||||
|
||||
void ObjectMgr::LoadGossipMenus()
|
||||
|
|
|
|||
|
|
@ -831,6 +831,9 @@ class ObjectMgr
|
|||
void LoadTrainerTemplates();
|
||||
void LoadTrainers() { LoadTrainers("npc_trainer", false); }
|
||||
|
||||
/// @param _map Map* of the map for which to load active entities. If nullptr active entities on continents are loaded
|
||||
void LoadActiveEntities(Map* _map);
|
||||
|
||||
void LoadVehicleAccessory();
|
||||
|
||||
std::string GeneratePetName(uint32 entry);
|
||||
|
|
@ -1340,10 +1343,13 @@ class ObjectMgr
|
|||
HalfNameMap PetHalfName0;
|
||||
HalfNameMap PetHalfName1;
|
||||
|
||||
typedef std::multimap<uint32 /*mapId*/, uint32 /*guid*/> ActiveCreatureGuidsOnMap;
|
||||
|
||||
// Array to store creature stats, Max creature level + 1 (for data alignement with in game level)
|
||||
CreatureClassLvlStats m_creatureClassLvlStats[DEFAULT_MAX_CREATURE_LEVEL + 1][MAX_CREATURE_CLASS][MAX_EXPANSION + 1];
|
||||
|
||||
MapObjectGuids mMapObjectGuids;
|
||||
ActiveCreatureGuidsOnMap m_activeCreatures;
|
||||
CreatureDataMap mCreatureDataMap;
|
||||
CreatureLocaleMap mCreatureLocaleMap;
|
||||
GameObjectDataMap mGameObjectDataMap;
|
||||
|
|
|
|||
|
|
@ -504,6 +504,7 @@ void Pet::SetDeathState(DeathState s) // overwrite virtual
|
|||
RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_STUNNED);
|
||||
CastPetAuras(true);
|
||||
}
|
||||
CastOwnerTalentAuras();
|
||||
}
|
||||
|
||||
void Pet::Update(uint32 update_diff, uint32 diff)
|
||||
|
|
@ -2091,6 +2092,40 @@ void Pet::SynchronizeLevelWithOwner()
|
|||
}
|
||||
}
|
||||
|
||||
void Pet::SetModeFlags(PetModeFlags mode)
|
||||
{
|
||||
m_petModeFlags = mode;
|
||||
|
||||
Unit* owner = GetOwner();
|
||||
if (!owner || owner->GetTypeId() != TYPEID_PLAYER)
|
||||
return;
|
||||
|
||||
WorldPacket data(SMSG_PET_MODE, 12);
|
||||
data << GetObjectGuid();
|
||||
data << uint32(m_petModeFlags);
|
||||
((Player*)owner)->GetSession()->SendPacket(&data);
|
||||
}
|
||||
|
||||
void Pet::SetStayPosition(bool stay)
|
||||
{
|
||||
if (stay)
|
||||
{
|
||||
m_stayPosX = GetPositionX();
|
||||
m_stayPosY = GetPositionY();
|
||||
m_stayPosZ = GetPositionZ();
|
||||
m_stayPosO = GetOrientation();
|
||||
}
|
||||
else
|
||||
{
|
||||
m_stayPosX = 0;
|
||||
m_stayPosY = 0;
|
||||
m_stayPosZ = 0;
|
||||
m_stayPosO = 0;
|
||||
}
|
||||
|
||||
m_stayPosSet = stay;
|
||||
}
|
||||
|
||||
void Pet::ApplyModeFlags(PetModeFlags mode, bool apply)
|
||||
{
|
||||
if (apply)
|
||||
|
|
|
|||
|
|
@ -206,6 +206,7 @@ class Pet : public Creature
|
|||
bool CanTakeMoreActiveSpells(uint32 SpellIconID);
|
||||
void ToggleAutocast(uint32 spellid, bool apply);
|
||||
|
||||
void SetModeFlags(PetModeFlags mode);
|
||||
void ApplyModeFlags(PetModeFlags mode, bool apply);
|
||||
PetModeFlags GetModeFlags() const { return m_petModeFlags; }
|
||||
|
||||
|
|
@ -231,6 +232,25 @@ class Pet : public Creature
|
|||
bool removeSpell(uint32 spell_id, bool learn_prev, bool clear_ab = true);
|
||||
void CleanupActionBar();
|
||||
|
||||
bool m_retreating;
|
||||
|
||||
void SetIsRetreating(bool retreating = false) { m_retreating = retreating; }
|
||||
bool GetIsRetreating() { return m_retreating; }
|
||||
|
||||
bool m_stayPosSet;
|
||||
float m_stayPosX;
|
||||
float m_stayPosY;
|
||||
float m_stayPosZ;
|
||||
float m_stayPosO;
|
||||
|
||||
void SetStayPosition(bool stay = false);
|
||||
bool IsStayPosSet() { return m_stayPosSet; }
|
||||
|
||||
float GetStayPosX() { return m_stayPosX; }
|
||||
float GetStayPosY() { return m_stayPosY; }
|
||||
float GetStayPosZ() { return m_stayPosZ; }
|
||||
float GetStayPosO() { return m_stayPosO; }
|
||||
|
||||
PetSpellMap m_spells;
|
||||
AutoSpellList m_autospells;
|
||||
|
||||
|
|
|
|||
|
|
@ -60,7 +60,7 @@ void PetAI::MoveInLineOfSight(Unit* u)
|
|||
return;
|
||||
|
||||
if (u->IsTargetableForAttack() && m_creature->IsHostileTo(u) &&
|
||||
u->isInAccessablePlaceFor(m_creature))
|
||||
u->IsInAccessablePlaceFor(m_creature))
|
||||
{
|
||||
float attackRadius = m_creature->GetAttackDistance(u);
|
||||
if (m_creature->IsWithinDistInMap(u, attackRadius) && m_creature->GetDistanceZ(u) <= CREATURE_Z_ATTACK_RANGE)
|
||||
|
|
|
|||
|
|
@ -346,6 +346,12 @@ void TradeData::SetMoney(uint64 money)
|
|||
if (m_money == money)
|
||||
return;
|
||||
|
||||
if (money > m_player->GetMoney())
|
||||
{
|
||||
m_player->GetSession()->SendTradeStatus(TRADE_STATUS_CLOSE_WINDOW);
|
||||
return;
|
||||
}
|
||||
|
||||
m_money = money;
|
||||
|
||||
SetAccepted(false);
|
||||
|
|
@ -1264,19 +1270,17 @@ void Player::Update(uint32 update_diff, uint32 p_time)
|
|||
RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_ENTER_PVP_COMBAT);
|
||||
}
|
||||
}
|
||||
}
|
||||
}// Speed collect rest bonus (section/in hour)
|
||||
|
||||
if (HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_RESTING))
|
||||
{
|
||||
if (roll_chance_i(3) && GetTimeInnEnter() > 0) // Freeze update
|
||||
if (GetTimeInnEnter() > 0) // Freeze update
|
||||
{
|
||||
time_t time_inn = time(NULL) - GetTimeInnEnter();
|
||||
time_t time_inn = now - GetTimeInnEnter();
|
||||
if (time_inn >= 10) // Freeze update
|
||||
{
|
||||
float bubble = 0.125f * sWorld.getConfig(CONFIG_FLOAT_RATE_REST_INGAME);
|
||||
// Speed collect rest bonus (section/in hour)
|
||||
SetRestBonus(float(GetRestBonus() + time_inn * (GetUInt32Value(PLAYER_NEXT_LEVEL_XP) / 72000) * bubble));
|
||||
UpdateInnerTime(time(NULL));
|
||||
SetRestBonus(GetRestBonus() + ComputeRest(time_inn));
|
||||
UpdateInnerTime(now);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2888,7 +2892,7 @@ void Player::InitStatsForLevel(bool reapplyMods)
|
|||
RemoveFlag(PLAYER_FLAGS, PLAYER_FLAGS_AFK | PLAYER_FLAGS_DND | PLAYER_FLAGS_GM | PLAYER_FLAGS_GHOST);
|
||||
|
||||
RemoveStandFlags(UNIT_STAND_FLAGS_ALL); // one form stealth modified bytes
|
||||
RemoveByteFlag(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_FFA_PVP | UNIT_BYTE2_FLAG_SANCTUARY);
|
||||
RemoveByteFlag(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_FFA_PVP | UNIT_BYTE2_FLAG_SUPPORTABLE);
|
||||
|
||||
// restore if need some important flags
|
||||
SetUInt32Value(PLAYER_FIELD_BYTES2, 0); // flags empty by default
|
||||
|
|
@ -4147,14 +4151,14 @@ void Player::BuildCreateUpdateBlockForPlayer(UpdateData* data, Player* target) c
|
|||
{
|
||||
for (int i = 0; i < EQUIPMENT_SLOT_END; ++i)
|
||||
{
|
||||
if (m_items[i] == NULL)
|
||||
if (m_items[i] == nullptr)
|
||||
continue;
|
||||
|
||||
m_items[i]->BuildCreateUpdateBlockForPlayer(data, target);
|
||||
}
|
||||
for (int i = INVENTORY_SLOT_BAG_START; i < BANK_SLOT_BAG_END; ++i)
|
||||
{
|
||||
if (m_items[i] == NULL)
|
||||
if (m_items[i] == nullptr)
|
||||
continue;
|
||||
|
||||
m_items[i]->BuildCreateUpdateBlockForPlayer(data, target);
|
||||
|
|
@ -4555,6 +4559,38 @@ void Player::SetWaterWalk(bool enable)
|
|||
GetSession()->SendPacket(&data);
|
||||
}
|
||||
|
||||
void Player::SetLevitate(bool enable)
|
||||
{
|
||||
WorldPacket data;
|
||||
BuildMoveLevitatePacket(&data, enable, 0);
|
||||
GetSession()->SendPacket(&data);
|
||||
}
|
||||
|
||||
void Player::SetCanFly(bool enable)
|
||||
{
|
||||
WorldPacket data;
|
||||
BuildMoveSetCanFlyPacket(&data, enable, 0);
|
||||
GetSession()->SendPacket(&data);
|
||||
}
|
||||
|
||||
void Player::SetFeatherFall(bool enable)
|
||||
{
|
||||
WorldPacket data;
|
||||
BuildMoveFeatherFallPacket(&data, enable, 0);
|
||||
SendMessageToSet(&data, true);
|
||||
|
||||
// start fall from current height
|
||||
if (!enable)
|
||||
SetFallInformation(0, GetPositionZ());
|
||||
}
|
||||
|
||||
void Player::SetHover(bool enable)
|
||||
{
|
||||
WorldPacket data;
|
||||
BuildMoveHoverPacket(&data, enable, 0);
|
||||
GetSession()->SendPacket(&data);
|
||||
}
|
||||
|
||||
/* Preconditions:
|
||||
- a resurrectable corpse must not be loaded for the player (only bones)
|
||||
- the player must be in world
|
||||
|
|
@ -6184,9 +6220,6 @@ bool Player::SetPosition(float x, float y, float z, float orientation, bool tele
|
|||
// group update
|
||||
if (GetGroup() && (old_x != x || old_y != y))
|
||||
SetGroupUpdateFlag(GROUP_UPDATE_FLAG_POSITION);
|
||||
|
||||
if (GetTrader() && !IsWithinDistInMap(GetTrader(), INTERACTION_DISTANCE))
|
||||
GetSession()->SendCancelTrade(); // will close both side trade windows
|
||||
}
|
||||
|
||||
if (m_positionStatusUpdateTimer) // Update position's state only on interval
|
||||
|
|
@ -6966,13 +6999,13 @@ void Player::UpdateZone(uint32 newZone, uint32 newArea)
|
|||
|
||||
if (zone->flags & AREA_FLAG_SANCTUARY) // in sanctuary
|
||||
{
|
||||
SetByteFlag(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_SANCTUARY);
|
||||
SetByteFlag(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_SUPPORTABLE);
|
||||
if (sWorld.IsFFAPvPRealm())
|
||||
SetFFAPvP(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
RemoveByteFlag(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_SANCTUARY);
|
||||
RemoveByteFlag(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_SUPPORTABLE);
|
||||
}
|
||||
|
||||
if (zone->flags & AREA_FLAG_CAPITAL) // in capital city
|
||||
|
|
@ -7483,9 +7516,9 @@ void Player::ApplyItemEquipSpell(Item* item, bool apply, bool form_change)
|
|||
else
|
||||
{
|
||||
// at un-apply remove all spells (not only at-apply, so any at-use active affects from item and etc)
|
||||
// except with at-use with negative charges, so allow consuming item spells (including with extra flag that prevent consume really)
|
||||
// except on form change and with at-use with negative charges, so allow consuming item spells (including with extra flag that prevent consume really)
|
||||
// applied to player after item remove from equip slot
|
||||
if (spellData.SpellTrigger == ITEM_SPELLTRIGGER_ON_USE && spellData.SpellCharges < 0)
|
||||
if (spellData.SpellTrigger == ITEM_SPELLTRIGGER_ON_USE && (form_change || spellData.SpellCharges < 0))
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
@ -14004,6 +14037,10 @@ bool Player::SatisfyQuestPreviousQuest(Quest const* qInfo, bool msg) const
|
|||
return true;
|
||||
|
||||
// each-from-all exclusive group ( < 0)
|
||||
// given a group with 2+ quests, and one of those has a branch that is not restricted by the group, return true
|
||||
if (qInfo->GetPrevQuestId() != 0 && qPrevInfo->GetNextQuestId() != qInfo->GetPrevQuestId())
|
||||
return true;
|
||||
|
||||
// can be start if only all quests in prev quest exclusive group completed and rewarded
|
||||
ExclusiveQuestGroupsMapBounds bounds = sObjectMgr.GetExclusiveQuestGroupsMapBounds(qPrevInfo->GetExclusiveGroup());
|
||||
|
||||
|
|
@ -14037,6 +14074,11 @@ bool Player::SatisfyQuestPreviousQuest(Quest const* qInfo, bool msg) const
|
|||
if (qPrevInfo->GetExclusiveGroup() >= 0)
|
||||
return true;
|
||||
|
||||
// each-from-all exclusive group ( < 0)
|
||||
// given a group with 2+ quests, and one of those has a branch that is not restricted by the group, return true
|
||||
if (qInfo->GetPrevQuestId() != 0 && qPrevInfo->GetNextQuestId() != abs(qInfo->GetPrevQuestId()))
|
||||
return true;
|
||||
|
||||
// each-from-all exclusive group ( < 0)
|
||||
// can be start if only all quests in prev quest exclusive group active
|
||||
ExclusiveQuestGroupsMapBounds bounds = sObjectMgr.GetExclusiveQuestGroupsMapBounds(qPrevInfo->GetExclusiveGroup());
|
||||
|
|
@ -15494,7 +15536,7 @@ bool Player::LoadFromDB(ObjectGuid guid, SqlQueryHolder* holder)
|
|||
m_bgData.bgTypeID = currentBg->GetTypeID(); // bg data not marked as modified
|
||||
|
||||
// join player to battleground group
|
||||
currentBg->EventPlayerLoggedIn(this, GetObjectGuid());
|
||||
currentBg->EventPlayerLoggedIn(this);
|
||||
currentBg->AddOrSetPlayerToCorrectBgGroup(this, GetObjectGuid(), m_bgData.bgTeam);
|
||||
|
||||
SetInviteForBattleGroundQueueType(bgQueueTypeId, currentBg->GetInstanceID());
|
||||
|
|
@ -15722,17 +15764,7 @@ bool Player::LoadFromDB(ObjectGuid guid, SqlQueryHolder* holder)
|
|||
m_rest_bonus = fields[21].GetFloat();
|
||||
|
||||
if (time_diff > 0)
|
||||
{
|
||||
// speed collect rest bonus in offline, in logout, far from tavern, city (section/in hour)
|
||||
float bubble0 = 0.031f;
|
||||
// speed collect rest bonus in offline, in logout, in tavern, city (section/in hour)
|
||||
float bubble1 = 0.125f;
|
||||
float bubble = fields[23].GetUInt32() > 0
|
||||
? bubble1 * sWorld.getConfig(CONFIG_FLOAT_RATE_REST_OFFLINE_IN_TAVERN_OR_CITY)
|
||||
: bubble0 * sWorld.getConfig(CONFIG_FLOAT_RATE_REST_OFFLINE_IN_WILDERNESS);
|
||||
|
||||
SetRestBonus(GetRestBonus() + time_diff * ((float)GetUInt32Value(PLAYER_NEXT_LEVEL_XP) / 72000)*bubble);
|
||||
}
|
||||
SetRestBonus(GetRestBonus() + ComputeRest(time_diff, true, (fields[23].GetInt32() > 0)));
|
||||
|
||||
// load skills after InitStatsForLevel because it triggering aura apply also
|
||||
_LoadSkills(holder->GetResult(PLAYER_LOGIN_QUERY_LOADSKILLS));
|
||||
|
|
@ -15933,6 +15965,43 @@ bool Player::LoadFromDB(ObjectGuid guid, SqlQueryHolder* holder)
|
|||
return true;
|
||||
}
|
||||
|
||||
bool Player::IsTappedByMeOrMyGroup(Creature* creature)
|
||||
{
|
||||
/* Nobody tapped the monster (solo kill by another NPC) */
|
||||
if (!creature->HasFlag(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_TAPPED))
|
||||
{ return false; }
|
||||
|
||||
/* If there is a loot recipient, assign it to recipient */
|
||||
if (Player* recipient = creature->GetLootRecipient())
|
||||
{
|
||||
/* See if we're in a group */
|
||||
if (Group* plr_group = recipient->GetGroup())
|
||||
{
|
||||
/* Recipient is in a group... but is it ours? */
|
||||
if (Group* my_group = GetGroup())
|
||||
{
|
||||
/* Check groups are the same */
|
||||
if (plr_group != my_group)
|
||||
{ return false; } // Cheater, deny loot
|
||||
}
|
||||
else
|
||||
{ return false; } // We're not in a group, probably cheater
|
||||
|
||||
/* We're in the looters group, so mob is tapped by us */
|
||||
return true;
|
||||
}
|
||||
/* We're not in a group, check to make sure we're the recipient (prevent cheaters) */
|
||||
else if (recipient == this)
|
||||
{ return true; }
|
||||
}
|
||||
else
|
||||
/* Don't know what happened to the recipient, probably disconnected
|
||||
* Either way, it isn't us, so mark as tapped */
|
||||
{ return false; }
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Player::isAllowedToLoot(Creature* creature)
|
||||
{
|
||||
// never tapped by any (mob solo kill)
|
||||
|
|
@ -21716,6 +21785,123 @@ void Player::SetTitle(CharTitlesEntry const* title, bool lost)
|
|||
GetSession()->SendPacket(&data);
|
||||
}
|
||||
|
||||
void Player::UpdateRuneRegen(RuneType rune)
|
||||
{
|
||||
if (rune >= RUNE_DEATH)
|
||||
return;
|
||||
|
||||
RuneType actualRune = rune;
|
||||
float cooldown = RUNE_BASE_COOLDOWN;
|
||||
for (uint8 i = 0; i < MAX_RUNES; i += 2)
|
||||
{
|
||||
if (GetBaseRune(i) != rune)
|
||||
continue;
|
||||
|
||||
uint32 cd = GetRuneCooldown(i);
|
||||
uint32 secondRuneCd = GetRuneCooldown(i + 1);
|
||||
if (!cd && !secondRuneCd)
|
||||
actualRune = GetCurrentRune(i);
|
||||
else if (secondRuneCd && (cd > secondRuneCd || !cd))
|
||||
{
|
||||
cooldown = GetBaseRuneCooldown(i + 1);
|
||||
actualRune = GetCurrentRune(i + 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
cooldown = GetBaseRuneCooldown(i);
|
||||
actualRune = GetCurrentRune(i);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
float auraMod = 1.0f;
|
||||
Unit::AuraList const& regenAuras = GetAurasByType(SPELL_AURA_MOD_POWER_REGEN_PERCENT);
|
||||
for (Unit::AuraList::const_iterator i = regenAuras.begin(); i != regenAuras.end(); ++i)
|
||||
if ((*i)->GetMiscValue() == POWER_RUNE && (*i)->GetSpellEffect()->EffectMiscValueB == rune)
|
||||
auraMod *= (100.0f + (*i)->GetModifier()->m_amount) / 100.0f;
|
||||
|
||||
// Unholy Presence
|
||||
if (Aura* aura = GetAura(48265, EFFECT_INDEX_0))
|
||||
auraMod *= (100.0f + aura->GetModifier()->m_amount) / 100.0f;
|
||||
|
||||
// Runic Corruption
|
||||
if (Aura* aura = GetAura(51460, EFFECT_INDEX_0))
|
||||
auraMod *= (100.0f + aura->GetModifier()->m_amount) / 100.0f;
|
||||
|
||||
float hastePct = (100.0f - GetRatingBonusValue(CR_HASTE_MELEE)) / 100.0f;
|
||||
if (hastePct < 0)
|
||||
hastePct = 1.0f;
|
||||
|
||||
cooldown *= hastePct / auraMod;
|
||||
|
||||
float value = float(1 * IN_MILLISECONDS) / cooldown;
|
||||
SetFloatValue(PLAYER_RUNE_REGEN_1 + uint8(actualRune), value);
|
||||
}
|
||||
|
||||
void Player::UpdateRuneRegen()
|
||||
{
|
||||
for (uint8 i = 0; i < NUM_RUNE_TYPES; ++i)
|
||||
UpdateRuneRegen(RuneType(i));
|
||||
}
|
||||
|
||||
uint8 Player::GetRuneCooldownFraction(uint8 index) const
|
||||
{
|
||||
uint16 baseCd = GetBaseRuneCooldown(index);
|
||||
if (!baseCd || !GetRuneCooldown(index))
|
||||
return 255;
|
||||
else if (baseCd == GetRuneCooldown(index))
|
||||
return 0;
|
||||
|
||||
return uint8(float(baseCd - GetRuneCooldown(index)) / baseCd * 255);
|
||||
}
|
||||
|
||||
void Player::AddRuneByAuraEffect(uint8 index, RuneType newType, Aura const* aura)
|
||||
{
|
||||
// Item - Death Knight T11 DPS 4P Bonus
|
||||
if (newType == RUNE_DEATH && HasAura(90459))
|
||||
CastSpell(this, 90507, true); // Death Eater
|
||||
|
||||
SetRuneConvertAura(index, aura); ConvertRune(index, newType);
|
||||
}
|
||||
|
||||
void Player::RemoveRunesByAuraEffect(Aura const* aura)
|
||||
{
|
||||
for (uint8 i = 0; i < MAX_RUNES; ++i)
|
||||
{
|
||||
if (m_runes->runes[i].ConvertAura == aura)
|
||||
{
|
||||
ConvertRune(i, GetBaseRune(i));
|
||||
SetRuneConvertAura(i, NULL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Player::RestoreBaseRune(uint8 index)
|
||||
{
|
||||
Aura const* aura = m_runes->runes[index].ConvertAura;
|
||||
// If rune was converted by a non-pasive aura that still active we should keep it converted
|
||||
if (aura && !IsPassiveSpell(aura->GetSpellProto()))
|
||||
return;
|
||||
|
||||
// Blood of the North
|
||||
if (aura->GetId() == 54637 && HasAura(54637))
|
||||
return;
|
||||
|
||||
ConvertRune(index, GetBaseRune(index));
|
||||
SetRuneConvertAura(index, NULL);
|
||||
// Don't drop passive talents providing rune convertion
|
||||
if (!aura || aura->GetModifier()->m_auraname != SPELL_AURA_CONVERT_RUNE)
|
||||
return;
|
||||
|
||||
for (uint8 i = 0; i < MAX_RUNES; ++i)
|
||||
if (aura == m_runes->runes[i].ConvertAura)
|
||||
return;
|
||||
|
||||
if (Unit* target = aura->GetTarget())
|
||||
target->RemoveSpellAuraHolder(const_cast<Aura*>(aura)->GetHolder());
|
||||
}
|
||||
|
||||
void Player::ConvertRune(uint8 index, RuneType newType)
|
||||
{
|
||||
SetCurrentRune(index, newType);
|
||||
|
|
@ -21749,7 +21935,7 @@ void Player::ResyncRunes()
|
|||
for (uint32 i = 0; i < MAX_RUNES; ++i)
|
||||
{
|
||||
data << uint8(GetCurrentRune(i)); // rune type
|
||||
data << uint8(255 - (GetRuneCooldown(i) * 51)); // passed cooldown time (0-255)
|
||||
data << uint8(GetRuneCooldownFraction(i));
|
||||
}
|
||||
GetSession()->SendPacket(&data);
|
||||
}
|
||||
|
|
@ -21761,16 +21947,6 @@ void Player::AddRunePower(uint8 index)
|
|||
GetSession()->SendPacket(&data);
|
||||
}
|
||||
|
||||
static RuneType runeSlotTypes[MAX_RUNES] =
|
||||
{
|
||||
/*0*/ RUNE_BLOOD,
|
||||
/*1*/ RUNE_BLOOD,
|
||||
/*2*/ RUNE_UNHOLY,
|
||||
/*3*/ RUNE_UNHOLY,
|
||||
/*4*/ RUNE_FROST,
|
||||
/*5*/ RUNE_FROST
|
||||
};
|
||||
|
||||
void Player::InitRunes()
|
||||
{
|
||||
if (getClass() != CLASS_DEATH_KNIGHT)
|
||||
|
|
@ -24066,6 +24242,77 @@ bool Player::FitArmorSpecializationRules(SpellEntry const * spellProto) const
|
|||
return true;
|
||||
}
|
||||
|
||||
float Player::ComputeRest(time_t timePassed, bool offline /*= false*/, bool inRestPlace /*= false*/)
|
||||
{
|
||||
// Every 8h in resting zone we gain a bubble
|
||||
// A bubble is 5% of the total xp so there is 20 bubbles
|
||||
// So we gain (total XP/20 every 8h) (8h = 288800 sec)
|
||||
// (TotalXP/20)/28800; simplified to (TotalXP/576000) per second
|
||||
// Client automatically double the value sent so we have to divide it by 2
|
||||
// So final formula (TotalXP/1152000)
|
||||
float bonus = timePassed * (GetUInt32Value(PLAYER_NEXT_LEVEL_XP) / 1152000.0f); // Get the gained rest xp for given second
|
||||
if (!offline)
|
||||
bonus *= sWorld.getConfig(CONFIG_FLOAT_RATE_REST_INGAME); // Apply the custom setting
|
||||
else
|
||||
{
|
||||
if (inRestPlace)
|
||||
bonus *= sWorld.getConfig(CONFIG_FLOAT_RATE_REST_OFFLINE_IN_TAVERN_OR_CITY);
|
||||
else
|
||||
bonus *= sWorld.getConfig(CONFIG_FLOAT_RATE_REST_OFFLINE_IN_WILDERNESS) / 4.0f; // bonus is reduced by 4 when not in rest place
|
||||
}
|
||||
return bonus;
|
||||
}
|
||||
|
||||
float Player::GetCollisionHeight(bool mounted) const
|
||||
{
|
||||
if (mounted)
|
||||
{
|
||||
// mounted case
|
||||
CreatureDisplayInfoEntry const* mountDisplayInfo = sCreatureDisplayInfoStore.LookupEntry(GetUInt32Value(UNIT_FIELD_MOUNTDISPLAYID));
|
||||
if (!mountDisplayInfo)
|
||||
return GetCollisionHeight(false);
|
||||
|
||||
CreatureModelDataEntry const* mountModelData = sCreatureModelDataStore.LookupEntry(mountDisplayInfo->ModelId);
|
||||
if (!mountModelData)
|
||||
return GetCollisionHeight(false);
|
||||
|
||||
CreatureDisplayInfoEntry const* displayInfo = sCreatureDisplayInfoStore.LookupEntry(GetNativeDisplayId());
|
||||
if (!displayInfo)
|
||||
{
|
||||
sLog.outError("GetCollisionHeight::Unable to find CreatureDisplayInfoEntry for %u", GetNativeDisplayId());
|
||||
return 0;
|
||||
}
|
||||
CreatureModelDataEntry const* modelData = sCreatureModelDataStore.LookupEntry(displayInfo->ModelId);
|
||||
if (!modelData)
|
||||
{
|
||||
sLog.outError("GetCollisionHeight::Unable to find CreatureModelDataEntry for %u", displayInfo->ModelId);
|
||||
return 0;
|
||||
}
|
||||
|
||||
float scaleMod = GetObjectScale(); // 99% sure about this
|
||||
|
||||
return scaleMod * mountModelData->MountHeight + modelData->CollisionHeight * 0.5f;
|
||||
}
|
||||
else
|
||||
{
|
||||
// use native model collision height in dismounted case
|
||||
CreatureDisplayInfoEntry const* displayInfo = sCreatureDisplayInfoStore.LookupEntry(GetNativeDisplayId());
|
||||
if (!displayInfo)
|
||||
{
|
||||
sLog.outError("GetCollisionHeight::Unable to find CreatureDisplayInfoEntry for %u", GetNativeDisplayId());
|
||||
return 0;
|
||||
}
|
||||
CreatureModelDataEntry const* modelData = sCreatureModelDataStore.LookupEntry(displayInfo->ModelId);
|
||||
if (!modelData)
|
||||
{
|
||||
sLog.outError("GetCollisionHeight::Unable to find CreatureModelDataEntry for %u", displayInfo->ModelId);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return modelData->CollisionHeight;
|
||||
}
|
||||
}
|
||||
|
||||
void Player::SendPetitionSignResult(ObjectGuid petitionGuid, Player* player, uint32 result)
|
||||
{
|
||||
WorldPacket data(SMSG_PETITION_SIGN_RESULTS, 8 + 8 + 4);
|
||||
|
|
|
|||
|
|
@ -353,7 +353,12 @@ struct Areas
|
|||
};
|
||||
|
||||
#define MAX_RUNES 6
|
||||
#define RUNE_COOLDOWN (2*5*IN_MILLISECONDS) // msec
|
||||
|
||||
enum RuneCooldowns
|
||||
{
|
||||
RUNE_BASE_COOLDOWN = 10000,
|
||||
RUNE_MISS_COOLDOWN = 1500 // cooldown applied on runes when the spell misses
|
||||
};
|
||||
|
||||
enum RuneType
|
||||
{
|
||||
|
|
@ -364,17 +369,30 @@ enum RuneType
|
|||
NUM_RUNE_TYPES = 4
|
||||
};
|
||||
|
||||
static RuneType runeSlotTypes[MAX_RUNES] =
|
||||
{
|
||||
/*0*/ RUNE_BLOOD,
|
||||
/*1*/ RUNE_BLOOD,
|
||||
/*2*/ RUNE_UNHOLY,
|
||||
/*3*/ RUNE_UNHOLY,
|
||||
/*4*/ RUNE_FROST,
|
||||
/*5*/ RUNE_FROST
|
||||
};
|
||||
|
||||
struct RuneInfo
|
||||
{
|
||||
uint8 BaseRune;
|
||||
uint8 CurrentRune;
|
||||
uint16 BaseCooldown;
|
||||
uint16 Cooldown; // msec
|
||||
Aura const* ConvertAura;
|
||||
};
|
||||
|
||||
struct Runes
|
||||
{
|
||||
RuneInfo runes[MAX_RUNES];
|
||||
uint8 runeState; // mask of available runes
|
||||
uint32 lastUsedRuneMask;
|
||||
|
||||
void SetRuneState(uint8 index, bool set = true)
|
||||
{
|
||||
|
|
@ -1191,6 +1209,15 @@ class Player : public Unit
|
|||
}
|
||||
void SetRestBonus(float rest_bonus_new);
|
||||
|
||||
/**
|
||||
* \brief: compute rest bonus
|
||||
* \param: time_t timePassed > time from last check
|
||||
* \param: bool offline > is the player was offline?
|
||||
* \param: bool inRestPlace > if it was offline, is the player was in city/tavern/inn?
|
||||
* \returns: float
|
||||
**/
|
||||
float ComputeRest(time_t timePassed, bool offline = false, bool inRestPlace = false);
|
||||
|
||||
RestType GetRestType() const
|
||||
{
|
||||
return rest_type;
|
||||
|
|
@ -1539,6 +1566,9 @@ class Player : public Unit
|
|||
void AddTimedQuest(uint32 quest_id) { m_timedquests.insert(quest_id); }
|
||||
void RemoveTimedQuest(uint32 quest_id) { m_timedquests.erase(quest_id); }
|
||||
|
||||
//! Return collision height sent to client
|
||||
float GetCollisionHeight(bool mounted) const;
|
||||
|
||||
/*********************************************************/
|
||||
/*** LOAD SYSTEM ***/
|
||||
/*********************************************************/
|
||||
|
|
@ -2058,6 +2088,10 @@ class Player : public Unit
|
|||
StopMirrorTimer(FIRE_TIMER);
|
||||
}
|
||||
|
||||
void SetLevitate(bool enable) override;
|
||||
void SetCanFly(bool enable) override;
|
||||
void SetFeatherFall(bool enable) override;
|
||||
void SetHover(bool enable) override;
|
||||
void SetRoot(bool enable) override;
|
||||
void SetWaterWalk(bool enable) override;
|
||||
|
||||
|
|
@ -2371,6 +2405,7 @@ class Player : public Unit
|
|||
bool isMoving() const { return m_movementInfo.HasMovementFlag(movementFlagsMask); }
|
||||
bool isMovingOrTurning() const { return m_movementInfo.HasMovementFlag(movementOrTurningFlagsMask); }
|
||||
|
||||
bool CanSwim() const { return true; }
|
||||
bool CanFly() const { return m_movementInfo.HasMovementFlag(MOVEFLAG_CAN_FLY); }
|
||||
bool IsFlying() const { return m_movementInfo.HasMovementFlag(MOVEFLAG_FLYING); }
|
||||
bool IsFreeFlying() const { return HasAuraType(SPELL_AURA_MOD_FLIGHT_SPEED_MOUNTED) || HasAuraType(SPELL_AURA_FLY); }
|
||||
|
|
@ -2498,7 +2533,8 @@ class Player : public Unit
|
|||
|
||||
GridReference<Player>& GetGridRef() { return m_gridRef; }
|
||||
MapReference& GetMapRef() { return m_mapRef; }
|
||||
|
||||
|
||||
bool IsTappedByMeOrMyGroup(Creature* creature);
|
||||
bool isAllowedToLoot(Creature* creature);
|
||||
|
||||
DeclinedName const* GetDeclinedNames() const { return m_declinedname; }
|
||||
|
|
@ -2508,10 +2544,22 @@ class Player : public Unit
|
|||
RuneType GetBaseRune(uint8 index) const { return RuneType(m_runes->runes[index].BaseRune); }
|
||||
RuneType GetCurrentRune(uint8 index) const { return RuneType(m_runes->runes[index].CurrentRune); }
|
||||
uint16 GetRuneCooldown(uint8 index) const { return m_runes->runes[index].Cooldown; }
|
||||
uint16 GetBaseRuneCooldown(uint8 index) const { return m_runes->runes[index].BaseCooldown; }
|
||||
uint8 GetRuneCooldownFraction(uint8 index) const;
|
||||
void UpdateRuneRegen(RuneType rune);
|
||||
void UpdateRuneRegen();
|
||||
bool IsBaseRuneSlotsOnCooldown(RuneType runeType) const;
|
||||
void ClearLastUsedRuneMask() { m_runes->lastUsedRuneMask = 0; }
|
||||
bool IsLastUsedRune(uint8 index) const { return (m_runes->lastUsedRuneMask & (1 << index)) != 0; }
|
||||
void SetLastUsedRune(RuneType type) { m_runes->lastUsedRuneMask |= 1 << uint32(type); }
|
||||
void SetBaseRune(uint8 index, RuneType baseRune) { m_runes->runes[index].BaseRune = baseRune; }
|
||||
void SetCurrentRune(uint8 index, RuneType currentRune) { m_runes->runes[index].CurrentRune = currentRune; }
|
||||
void SetRuneCooldown(uint8 index, uint16 cooldown) { m_runes->runes[index].Cooldown = cooldown; m_runes->SetRuneState(index, (cooldown == 0) ? true : false); }
|
||||
void SetBaseRuneCooldown(uint8 index, uint16 cooldown) { m_runes->runes[index].BaseCooldown = cooldown; }
|
||||
void SetRuneConvertAura(uint8 index, Aura const* aura) { m_runes->runes[index].ConvertAura = aura; }
|
||||
void AddRuneByAuraEffect(uint8 index, RuneType newType, Aura const* aura);
|
||||
void RemoveRunesByAuraEffect(Aura const* aura);
|
||||
void RestoreBaseRune(uint8 index);
|
||||
void ConvertRune(uint8 index, RuneType newType);
|
||||
bool ActivateRunes(RuneType type, uint32 count);
|
||||
void ResyncRunes();
|
||||
|
|
|
|||
|
|
@ -811,6 +811,7 @@ bool IsPositiveEffect(SpellEntry const* spellproto, SpellEffectIndex effIndex)
|
|||
case 13139: // net-o-matic special effect
|
||||
case 23445: // evil twin
|
||||
case 35679: // Protectorate Demolitionist
|
||||
case 37695: // Stanky
|
||||
case 38637: // Nether Exhaustion (red)
|
||||
case 38638: // Nether Exhaustion (green)
|
||||
case 38639: // Nether Exhaustion (blue)
|
||||
|
|
@ -843,6 +844,7 @@ bool IsPositiveEffect(SpellEntry const* spellproto, SpellEffectIndex effIndex)
|
|||
return false;
|
||||
break;
|
||||
case SPELL_AURA_MOD_DAMAGE_TAKEN: // dependent from bas point sign (positive -> negative)
|
||||
case SPELL_AURA_MOD_DAMAGE_PERCENT_TAKEN:
|
||||
if (spellEffect->CalculateSimpleValue() < 0)
|
||||
return true;
|
||||
// let check by target modes (for Amplify Magic cases/etc)
|
||||
|
|
@ -1406,8 +1408,8 @@ struct DoSpellProcEvent
|
|||
else
|
||||
++count;
|
||||
}
|
||||
|
||||
bool HasEntry(uint32 spellId) { return spe_map.count(spellId) > 0; }
|
||||
|
||||
bool HasEntry(uint32 spellId) { return spe_map.find(spellId) != spe_map.end(); }
|
||||
bool SetStateToEntry(uint32 spellId) { return (state = spe_map.find(spellId)) != spe_map.end(); }
|
||||
SpellProcEventMap& spe_map;
|
||||
SpellProcEventMap::const_iterator state;
|
||||
|
|
@ -1694,7 +1696,7 @@ void SpellMgr::LoadSpellBonuses()
|
|||
break;
|
||||
}
|
||||
}
|
||||
direct_calc = CalculateDefaultCoefficient(spell, SPELL_DIRECT_DAMAGE) * (isHeal ? 1.88f : 1.0f);
|
||||
direct_calc = CalculateDefaultCoefficient(spell, SPELL_DIRECT_DAMAGE) * (isHeal ? SCALE_SPELLPOWER_HEALING : 1.0f);
|
||||
direct_diff = std::abs(sbe.direct_damage - direct_calc);
|
||||
}
|
||||
|
||||
|
|
@ -1716,7 +1718,7 @@ void SpellMgr::LoadSpellBonuses()
|
|||
break;
|
||||
}
|
||||
}
|
||||
dot_calc = CalculateDefaultCoefficient(spell, DOT) * (isHeal ? 1.88f : 1.0f);
|
||||
dot_calc = CalculateDefaultCoefficient(spell, DOT) * (isHeal ? SCALE_SPELLPOWER_HEALING : 1.0f);
|
||||
dot_diff = std::abs(sbe.dot_damage - dot_calc);
|
||||
}
|
||||
|
||||
|
|
@ -2874,7 +2876,7 @@ SpellEntry const* SpellMgr::SelectAuraRankForLevel(SpellEntry const* spellInfo,
|
|||
break;
|
||||
|
||||
// if found appropriate level
|
||||
if (level + 10 >= spellInfo->GetSpellLevel())
|
||||
if (level + 10 >= nextSpellInfo->GetSpellLevel())
|
||||
return nextSpellInfo;
|
||||
|
||||
// one rank less then
|
||||
|
|
@ -3562,7 +3564,7 @@ void SpellMgr::LoadSpellScriptTarget()
|
|||
{
|
||||
if (itr->spellId == 30427 && !cInfo->SkinningLootId)
|
||||
{
|
||||
sLog.outErrorDb("Table `spell_script_target` has creature %u as a target of spellid 30427, but this creature has no skinLootid. Gas extraction will not work!", cInfo->Entry);
|
||||
sLog.outErrorDb("Table `spell_script_target` has creature %u as a target of spellid 30427, but this creature has no SkinningLootId. Gas extraction will not work!", cInfo->Entry);
|
||||
sSpellScriptTargetStorage.EraseEntry(itr->spellId);
|
||||
continue;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -976,8 +976,8 @@ uint32 Unit::DealDamage(Unit* pVictim, uint32 damage, CleanDamage const* cleanDa
|
|||
{
|
||||
SpellEntry const* shareSpell = (*itr)->GetSpellProto();
|
||||
uint32 shareDamage = uint32(damage*(*itr)->GetModifier()->m_amount / 100.0f);
|
||||
DealDamageMods(shareTarget, shareDamage, NULL);
|
||||
DealDamage(shareTarget, shareDamage, 0, damagetype, GetSpellSchoolMask(shareSpell), shareSpell, false);
|
||||
DealDamageMods(shareTarget, shareDamage, nullptr);
|
||||
DealDamage(shareTarget, shareDamage, nullptr, damagetype, GetSpellSchoolMask(shareSpell), shareSpell, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1681,9 +1681,11 @@ void Unit::CalculateSpellDamage(SpellNonMeleeDamage* damageInfo, int32 damage, S
|
|||
{ return; }
|
||||
|
||||
if (!this || !pVictim)
|
||||
{ return; }
|
||||
if (!this->IsAlive() || !pVictim->IsAlive())
|
||||
{ return; }
|
||||
return;
|
||||
|
||||
// units which are not alive cannot deal damage except for dying creatures
|
||||
if ((!this->IsAlive() || !pVictim->IsAlive()) && (this->GetTypeId() != TYPEID_UNIT || this->getDeathState() != DEAD))
|
||||
return;
|
||||
|
||||
// Check spell crit chance
|
||||
bool crit = IsSpellCrit(pVictim, spellInfo, damageSchoolMask, attackType);
|
||||
|
|
@ -3521,7 +3523,7 @@ SpellMissInfo Unit::SpellHitResult(Unit* pVictim, SpellEntry const* spell, bool
|
|||
return SPELL_MISS_EVADE;
|
||||
|
||||
// Check for immune
|
||||
if (pVictim->IsImmuneToSpell(spell, this == pVictim))
|
||||
if (pVictim->IsImmuneToSpell(spell, this == pVictim) && !spell->HasAttribute(SPELL_ATTR_UNAFFECTED_BY_INVULNERABILITY))
|
||||
return SPELL_MISS_IMMUNE;
|
||||
|
||||
// All positive spells can`t miss
|
||||
|
|
@ -3530,7 +3532,7 @@ SpellMissInfo Unit::SpellHitResult(Unit* pVictim, SpellEntry const* spell, bool
|
|||
return SPELL_MISS_NONE;
|
||||
|
||||
// Check for immune
|
||||
if (pVictim->IsImmunedToDamage(GetSpellSchoolMask(spell)))
|
||||
if (pVictim->IsImmunedToDamage(GetSpellSchoolMask(spell)) && !spell->HasAttribute(SPELL_ATTR_UNAFFECTED_BY_INVULNERABILITY))
|
||||
return SPELL_MISS_IMMUNE;
|
||||
|
||||
// Try victim reflect spell
|
||||
|
|
@ -4027,7 +4029,7 @@ void Unit::SetFacingToObject(WorldObject* pObject)
|
|||
SetFacingTo(GetAngle(pObject));
|
||||
}
|
||||
|
||||
bool Unit::isInAccessablePlaceFor(Creature const* c) const
|
||||
bool Unit::IsInAccessablePlaceFor(Creature const* c) const
|
||||
{
|
||||
if (IsInWater())
|
||||
return c->CanSwim();
|
||||
|
|
@ -5875,7 +5877,7 @@ bool Unit::IsHostileTo(Unit const* unit) const
|
|||
return false;
|
||||
|
||||
// Sanctuary
|
||||
if (pTarget->HasByteFlag(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_SANCTUARY) && pTester->HasByteFlag(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_SANCTUARY))
|
||||
if (pTarget->HasByteFlag(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_SUPPORTABLE) && pTester->HasByteFlag(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_SUPPORTABLE))
|
||||
return false;
|
||||
|
||||
// PvP FFA state
|
||||
|
|
@ -5987,7 +5989,7 @@ bool Unit::IsFriendlyTo(Unit const* unit) const
|
|||
return true;
|
||||
|
||||
// Sanctuary
|
||||
if (pTarget->HasByteFlag(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_SANCTUARY) && pTester->HasByteFlag(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_SANCTUARY))
|
||||
if (pTarget->HasByteFlag(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_SUPPORTABLE) && pTester->HasByteFlag(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_SUPPORTABLE))
|
||||
return true;
|
||||
|
||||
// PvP FFA state
|
||||
|
|
@ -6661,6 +6663,17 @@ void Unit::EnergizeBySpell(Unit* pVictim, uint32 SpellID, uint32 Damage, Powers
|
|||
pVictim->ModifyPower(powertype, Damage);
|
||||
}
|
||||
|
||||
/** Calculate spell coefficents and level penalties for spell/melee damage or heal
|
||||
*
|
||||
* this is the caster of the spell/ melee attacker
|
||||
* @param spellProto SpellEntry of the used spell
|
||||
* @param total current value onto which the Bonus and level penalty will be calculated
|
||||
* @param benefit additional benefit from ie spellpower-auras
|
||||
* @param ap_benefit additional melee attackpower benefit from auras
|
||||
* @param damagetype what kind of damage
|
||||
* @param donePart calculate for done or taken
|
||||
* @param defCoeffMod default coefficient for additional scaling (i.e. normal player healing SCALE_SPELLPOWER_HEALING)
|
||||
*/
|
||||
int32 Unit::SpellBonusWithCoeffs(SpellEntry const* spellProto, int32 total, int32 benefit, int32 ap_benefit, DamageEffectType damagetype, bool donePart, float defCoeffMod)
|
||||
{
|
||||
// Distribute Damage over multiple effects, reduce by AoE
|
||||
|
|
@ -7119,7 +7132,7 @@ uint32 Unit::SpellDamageBonusTaken(Unit* pCaster, SpellEntry const* spellProto,
|
|||
int32 TakenAdvertisedBenefit = SpellBaseDamageBonusTaken(GetSpellSchoolMask(spellProto));
|
||||
|
||||
// apply benefit affected by spell power implicit coeffs and spell level penalties
|
||||
TakenTotal = SpellBonusWithCoeffs(spellProto, TakenTotal, TakenAdvertisedBenefit, 0, damagetype, false);
|
||||
TakenTotal = pCaster->SpellBonusWithCoeffs(spellProto, TakenTotal, TakenAdvertisedBenefit, 0, damagetype, false);
|
||||
|
||||
float tmpDamage = (int32(pdamage) + TakenTotal * int32(stack)) * TakenTotalMod;
|
||||
|
||||
|
|
@ -7568,7 +7581,7 @@ uint32 Unit::SpellHealingBonusDone(Unit* pVictim, SpellEntry const* spellProto,
|
|||
int32 DoneAdvertisedBenefit = SpellBaseHealingBonusDone(GetSpellSchoolMask(spellProto));
|
||||
|
||||
// apply ap bonus and benefit affected by spell power implicit coeffs and spell level penalties
|
||||
DoneTotal = SpellBonusWithCoeffs(spellProto, DoneTotal, DoneAdvertisedBenefit, 0, damagetype, true, 1.88f);
|
||||
DoneTotal = SpellBonusWithCoeffs(spellProto, DoneTotal, DoneAdvertisedBenefit, 0, damagetype, true, SCALE_SPELLPOWER_HEALING);
|
||||
|
||||
// use float as more appropriate for negative values and percent applying
|
||||
float heal = (healamount + DoneTotal * int32(stack)) * DoneTotalMod;
|
||||
|
|
@ -7612,7 +7625,7 @@ uint32 Unit::SpellHealingBonusTaken(Unit* pCaster, SpellEntry const* spellProto,
|
|||
int32 TakenAdvertisedBenefit = SpellBaseHealingBonusTaken(GetSpellSchoolMask(spellProto));
|
||||
|
||||
// apply benefit affected by spell power implicit coeffs and spell level penalties
|
||||
TakenTotal = SpellBonusWithCoeffs(spellProto, TakenTotal, TakenAdvertisedBenefit, 0, damagetype, false, 1.88f);
|
||||
TakenTotal = pCaster->SpellBonusWithCoeffs(spellProto, TakenTotal, TakenAdvertisedBenefit, 0, damagetype, false, SCALE_SPELLPOWER_HEALING);
|
||||
|
||||
AuraList const& mHealingGet = GetAurasByType(SPELL_AURA_MOD_HEALING_RECEIVED);
|
||||
for (AuraList::const_iterator i = mHealingGet.begin(); i != mHealingGet.end(); ++i)
|
||||
|
|
@ -8155,7 +8168,7 @@ uint32 Unit::MeleeDamageBonusTaken(Unit* pCaster, uint32 pdamage, WeaponAttackTy
|
|||
if (!isWeaponDamageBasedSpell)
|
||||
{
|
||||
// apply benefit affected by spell power implicit coeffs and spell level penalties
|
||||
TakenFlat = SpellBonusWithCoeffs(spellProto, 0, TakenFlat, 0, damagetype, false);
|
||||
TakenFlat = pCaster->SpellBonusWithCoeffs(spellProto, 0, TakenFlat, 0, damagetype, false);
|
||||
}
|
||||
|
||||
float tmpDamage = float(int32(pdamage) + TakenFlat * int32(stack)) * TakenPercent;
|
||||
|
|
@ -8257,6 +8270,10 @@ void Unit::Mount(uint32 mount, uint32 spellId)
|
|||
pet->ApplyModeFlags(PET_MODE_DISABLE_ACTIONS, true);
|
||||
}
|
||||
}
|
||||
|
||||
float height = ((Player*)this)->GetCollisionHeight(true);
|
||||
if (height)
|
||||
SendCollisionHeightUpdate(height);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -8284,9 +8301,17 @@ void Unit::Unmount(bool from_aura)
|
|||
if (GetTypeId() == TYPEID_PLAYER)
|
||||
{
|
||||
if (Pet* pet = GetPet())
|
||||
pet->ApplyModeFlags(PET_MODE_DISABLE_ACTIONS, false);
|
||||
{
|
||||
// Get reaction state and display appropriately
|
||||
if (CharmInfo* charmInfo = pet->GetCharmInfo())
|
||||
pet->SetModeFlags(PetModeFlags(charmInfo->GetReactState() | charmInfo->GetCommandState() * 0x100));
|
||||
}
|
||||
else
|
||||
{ ((Player*)this)->ResummonPetTemporaryUnSummonedIfAny(); }
|
||||
((Player*)this)->ResummonPetTemporaryUnSummonedIfAny();
|
||||
|
||||
float height = ((Player*)this)->GetCollisionHeight(false);
|
||||
if (height)
|
||||
SendCollisionHeightUpdate(height);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -8687,8 +8712,23 @@ bool Unit::IsVisibleForOrDetect(Unit const* u, WorldObject const* viewPoint, boo
|
|||
if (m_Visibility == VISIBILITY_OFF)
|
||||
return false;
|
||||
|
||||
// grouped players should always see stealthed party members
|
||||
if (GetTypeId() == TYPEID_PLAYER && u->GetTypeId() == TYPEID_PLAYER)
|
||||
if (((Player*)this)->IsGroupVisibleFor(((Player*)u)) && u->IsFriendlyTo(this))
|
||||
return true;
|
||||
|
||||
// raw invisibility
|
||||
bool invisible = (m_invisibilityMask != 0 || u->m_invisibilityMask != 0);
|
||||
if (u->GetTypeId() == TYPEID_PLAYER) // if object is player with mover, use its visibility masks, so that an invisible player MCing a creature can see stuff
|
||||
{
|
||||
if (Player* player = (Player*)u)
|
||||
{
|
||||
if (Unit* mover=player->GetMover())
|
||||
{
|
||||
invisible= (m_invisibilityMask != 0 || mover->m_invisibilityMask != 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// detectable invisibility case
|
||||
if (invisible && (
|
||||
|
|
@ -8705,35 +8745,18 @@ bool Unit::IsVisibleForOrDetect(Unit const* u, WorldObject const* viewPoint, boo
|
|||
// special cases for always overwrite invisibility/stealth
|
||||
if (invisible || m_Visibility == VISIBILITY_GROUP_STEALTH)
|
||||
{
|
||||
// non-hostile case
|
||||
if (!u->IsHostileTo(this))
|
||||
{
|
||||
// player see other player with stealth/invisibility only if he in same group or raid or same team (raid/team case dependent from conf setting)
|
||||
if (GetTypeId() == TYPEID_PLAYER && u->GetTypeId() == TYPEID_PLAYER)
|
||||
{
|
||||
if (((Player*)this)->IsGroupVisibleFor(((Player*)u)))
|
||||
return true;
|
||||
|
||||
// else apply same rules as for hostile case (detecting check for stealth)
|
||||
}
|
||||
}
|
||||
// hostile case
|
||||
else
|
||||
if (u->IsHostileTo(this))
|
||||
{
|
||||
// Hunter mark functionality
|
||||
AuraList const& auras = GetAurasByType(SPELL_AURA_MOD_STALKED);
|
||||
for (AuraList::const_iterator iter = auras.begin(); iter != auras.end(); ++iter)
|
||||
if ((*iter)->GetCasterGuid() == u->GetObjectGuid())
|
||||
return true;
|
||||
|
||||
// else apply detecting check for stealth
|
||||
}
|
||||
|
||||
// none other cases for detect invisibility, so invisible
|
||||
if (invisible)
|
||||
return false;
|
||||
|
||||
// else apply stealth detecting check
|
||||
}
|
||||
|
||||
// unit got in stealth in this moment and must ignore old detected state
|
||||
|
|
@ -9518,7 +9541,7 @@ bool Unit::SelectHostileTarget()
|
|||
for (AuraList::const_reverse_iterator aura = tauntAuras.rbegin(); aura != tauntAuras.rend(); ++aura)
|
||||
{
|
||||
if ((caster = (*aura)->GetCaster()) && caster->IsInMap(this) &&
|
||||
caster->IsTargetableForAttack() && caster->isInAccessablePlaceFor((Creature*)this) &&
|
||||
caster->IsTargetableForAttack() && caster->IsInAccessablePlaceFor((Creature*)this) &&
|
||||
!IsSecondChoiceTarget(caster, true))
|
||||
{
|
||||
target = caster;
|
||||
|
|
@ -9581,7 +9604,7 @@ bool Unit::SelectHostileTarget()
|
|||
{
|
||||
for (AttackerSet::const_iterator itr = m_attackers.begin(); itr != m_attackers.end(); ++itr)
|
||||
{
|
||||
if ((*itr)->IsInMap(this) && (*itr)->IsTargetableForAttack() && (*itr)->isInAccessablePlaceFor((Creature*)this))
|
||||
if ((*itr)->IsInMap(this) && (*itr)->IsTargetableForAttack() && (*itr)->IsInAccessablePlaceFor((Creature*)this))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
@ -12156,3 +12179,59 @@ void Unit::BuildMoveFeatherFallPacket(WorldPacket* data, bool apply, uint32 valu
|
|||
}
|
||||
}
|
||||
|
||||
void Unit::BuildMoveHoverPacket(WorldPacket* data, bool apply, uint32 value)
|
||||
{
|
||||
ObjectGuid guid = GetObjectGuid();
|
||||
|
||||
if (apply)
|
||||
{
|
||||
data->Initialize(SMSG_MOVE_SET_HOVER, 8 + 4 + 1);
|
||||
data->WriteGuidMask<1, 4, 2, 3, 0, 5, 6, 7>(guid);
|
||||
data->WriteGuidBytes<5, 4, 1, 2, 3, 6, 0, 7>(guid);
|
||||
*data << uint32(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
data->Initialize(SMSG_MOVE_UNSET_HOVER, 8 + 4 + 1);
|
||||
data->WriteGuidMask<4, 6, 3, 1, 2, 7, 5, 0>(guid);
|
||||
data->WriteGuidBytes<4, 5, 3, 6, 7, 1, 2, 0>(guid);
|
||||
*data << uint32(0);
|
||||
}
|
||||
}
|
||||
|
||||
void Unit::BuildMoveLevitatePacket(WorldPacket* data, bool apply, uint32 value)
|
||||
{
|
||||
ObjectGuid guid = GetObjectGuid();
|
||||
|
||||
if (apply)
|
||||
{
|
||||
data->Initialize(SMSG_MOVE_GRAVITY_ENABLE);
|
||||
data->WriteGuidMask<1, 4, 7, 5, 2, 0, 3, 6>(GetObjectGuid());
|
||||
data->WriteGuidBytes<3>(GetObjectGuid());
|
||||
*data << uint32(value);
|
||||
data->WriteGuidBytes<7, 6, 4, 0, 1, 5, 2>(GetObjectGuid());
|
||||
}
|
||||
else
|
||||
{
|
||||
data->Initialize(SMSG_MOVE_GRAVITY_DISABLE);
|
||||
data->WriteGuidMask<0, 1, 5, 7, 6, 4, 3, 2>(GetObjectGuid());
|
||||
data->WriteGuidBytes<7, 2, 0>(GetObjectGuid());
|
||||
*data << uint32(value);
|
||||
data->WriteGuidBytes<5, 1, 3, 4, 6>(GetObjectGuid());
|
||||
}
|
||||
}
|
||||
|
||||
void Unit::SendCollisionHeightUpdate(float height)
|
||||
{
|
||||
if (GetTypeId() == TYPEID_PLAYER)
|
||||
{
|
||||
WorldPacket data(SMSG_MOVE_SET_COLLISION_HGT, GetPackGUID().size() + 4 + 4);
|
||||
data.WriteGuidMask<6, 1, 4, 7, 5, 2, 0, 3>(GetObjectGuid());
|
||||
data.WriteGuidBytes<6, 0, 4, 3, 5>(GetObjectGuid());
|
||||
data << uint32(sWorld.GetGameTime()); // Packet counter
|
||||
data.WriteGuidBytes<1, 2, 7>(GetObjectGuid());
|
||||
data << ((Player*)this)->GetCollisionHeight(true);
|
||||
((Player*)this)->GetSession()->SendPacket(&data);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -150,6 +150,8 @@ enum SpellFacingFlags
|
|||
#define BASE_ATTACK_TIME 2000
|
||||
#define BASE_BLOCK_DAMAGE_PERCENT 30
|
||||
|
||||
#define SCALE_SPELLPOWER_HEALING 1.88f
|
||||
|
||||
/**
|
||||
* byte value (UNIT_FIELD_BYTES_1,0).
|
||||
*
|
||||
|
|
@ -225,9 +227,9 @@ enum UnitPVPStateFlags
|
|||
UNIT_BYTE2_FLAG_PVP = 0x01,
|
||||
UNIT_BYTE2_FLAG_UNK1 = 0x02,
|
||||
UNIT_BYTE2_FLAG_FFA_PVP = 0x04,
|
||||
UNIT_BYTE2_FLAG_SANCTUARY = 0x08,
|
||||
UNIT_BYTE2_FLAG_UNK4 = 0x10,
|
||||
UNIT_BYTE2_FLAG_UNK5 = 0x20,
|
||||
UNIT_BYTE2_FLAG_SUPPORTABLE = 0x08, // allows for being targeted for healing/bandaging by friendlies
|
||||
UNIT_BYTE2_FLAG_AURAS = 0x10, // show possitive auras as positive, and allow its dispel
|
||||
UNIT_BYTE2_FLAG_UNK5 = 0x20, // show negative auras as positive, *not* allowing dispel (at least for pets)
|
||||
UNIT_BYTE2_FLAG_UNK6 = 0x40,
|
||||
UNIT_BYTE2_FLAG_UNK7 = 0x80
|
||||
};
|
||||
|
|
@ -596,7 +598,7 @@ enum UnitFlags
|
|||
UNIT_FLAG_PVP = 0x00001000, // changed in 3.0.3
|
||||
UNIT_FLAG_SILENCED = 0x00002000, // silenced, 2.1.1
|
||||
UNIT_FLAG_UNK_14 = 0x00004000, // 2.0.8
|
||||
UNIT_FLAG_UNK_15 = 0x00008000,
|
||||
UNIT_FLAG_UNK_15 = 0x00008000, // related to jerky movement in water?
|
||||
UNIT_FLAG_UNK_16 = 0x00010000, // removes attackable icon
|
||||
UNIT_FLAG_PACIFIED = 0x00020000, // 3.0.3 ok
|
||||
UNIT_FLAG_STUNNED = 0x00040000, // 3.0.3 ok
|
||||
|
|
@ -2399,7 +2401,7 @@ class Unit : public WorldObject
|
|||
|
||||
virtual bool IsInWater() const;
|
||||
virtual bool IsUnderWater() const;
|
||||
bool isInAccessablePlaceFor(Creature const* c) const;
|
||||
bool IsInAccessablePlaceFor(Creature const* c) const;
|
||||
|
||||
void SendHealSpellLog(Unit* pVictim, uint32 SpellID, uint32 Damage, uint32 OverHeal, bool critical = false, uint32 absorb = 0);
|
||||
void SendEnergizeSpellLog(Unit* pVictim, uint32 SpellID, uint32 Damage, Powers powertype);
|
||||
|
|
@ -2656,6 +2658,12 @@ class Unit : public WorldObject
|
|||
bool IsLevitating() const { return m_movementInfo.HasMovementFlag(MOVEFLAG_LEVITATING); }
|
||||
bool IsWalking() const { return m_movementInfo.HasMovementFlag(MOVEFLAG_WALK_MODE); }
|
||||
bool IsRooted() const { return m_movementInfo.HasMovementFlag(MOVEFLAG_ROOT); }
|
||||
|
||||
virtual void SetLevitate(bool /*enabled*/) {}
|
||||
virtual void SetSwim(bool /*enabled*/) {}
|
||||
virtual void SetCanFly(bool /*enabled*/) {}
|
||||
virtual void SetFeatherFall(bool /*enabled*/) {}
|
||||
virtual void SetHover(bool /*enabled*/) {}
|
||||
virtual void SetRoot(bool /*enabled*/) {}
|
||||
/**
|
||||
* Changes this \ref Unit s ability to walk on water.
|
||||
|
|
@ -3327,6 +3335,7 @@ class Unit : public WorldObject
|
|||
|
||||
// at any changes to Scale and/or displayId
|
||||
void UpdateModelData();
|
||||
void SendCollisionHeightUpdate(float height);
|
||||
|
||||
DynamicObject* GetDynObject(uint32 spellId, SpellEffectIndex effIndex);
|
||||
DynamicObject* GetDynObject(uint32 spellId);
|
||||
|
|
@ -3485,6 +3494,9 @@ class Unit : public WorldObject
|
|||
|
||||
bool IsLinkingEventTrigger() const { return m_isCreatureLinkingTrigger; }
|
||||
|
||||
virtual bool CanSwim() const = 0;
|
||||
virtual bool CanFly() const = 0;
|
||||
|
||||
bool IsSplineEnabled() const;
|
||||
|
||||
bool IsInWorgenForm(bool inPermanent = false) const;
|
||||
|
|
@ -3496,6 +3508,8 @@ class Unit : public WorldObject
|
|||
void BuildSendPlayVisualPacket(WorldPacket* data, uint32 value, bool impact);
|
||||
void BuildMoveSetCanFlyPacket(WorldPacket* data, bool apply, uint32 value);
|
||||
void BuildMoveFeatherFallPacket(WorldPacket* data, bool apply, uint32 value);
|
||||
void BuildMoveHoverPacket(WorldPacket* data, bool apply, uint32 value);
|
||||
void BuildMoveLevitatePacket(WorldPacket* data, bool apply, uint32 value);
|
||||
|
||||
protected:
|
||||
explicit Unit();
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue