refactor(Core/Creature): Remove Inhabit Type (#9272)

This is in reference to issue: https://github.com/azerothcore/azerothcore-wotlk/issues/4361
This is comprised of a cherry pick and partial tc cherry pick:
592516ae69
dbadb6369c
34cfa69efd
12de860b4a
a22bc236eb
This commit is contained in:
acidmanifesto 2022-01-06 14:33:22 +01:00 committed by GitHub
parent d504a62293
commit 2d4e17fd16
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
26 changed files with 4948 additions and 230 deletions

File diff suppressed because it is too large Load diff

View file

@ -77,7 +77,7 @@ void WorldDatabaseConnection::DoPrepareStatements()
PrepareStatement(WORLD_SEL_WAYPOINT_SCRIPT_ID_BY_GUID, "SELECT id FROM waypoint_scripts WHERE guid = ?", CONNECTION_SYNCH);
PrepareStatement(WORLD_DEL_CREATURE, "DELETE FROM creature WHERE guid = ?", CONNECTION_ASYNC);
PrepareStatement(WORLD_SEL_COMMANDS, "SELECT name, security, help FROM command", CONNECTION_SYNCH);
PrepareStatement(WORLD_SEL_CREATURE_TEMPLATE, "SELECT entry, difficulty_entry_1, difficulty_entry_2, difficulty_entry_3, KillCredit1, KillCredit2, modelid1, modelid2, modelid3, modelid4, name, subname, IconName, gossip_menu_id, minlevel, maxlevel, exp, faction, npcflag, speed_walk, speed_run, detection_range, scale, `rank`, dmgschool, DamageModifier, BaseAttackTime, RangeAttackTime, BaseVariance, RangeVariance, unit_class, unit_flags, unit_flags2, dynamicflags, family, trainer_type, trainer_spell, trainer_class, trainer_race, type, type_flags, lootid, pickpocketloot, skinloot, PetSpellDataId, VehicleId, mingold, maxgold, AIName, MovementType, InhabitType, HoverHeight, HealthModifier, ManaModifier, ArmorModifier, ExperienceModifier, RacialLeader, movementId, RegenHealth, mechanic_immune_mask, spell_school_immune_mask, flags_extra, ScriptName FROM creature_template WHERE entry = ?", CONNECTION_SYNCH);
PrepareStatement(WORLD_SEL_CREATURE_TEMPLATE, "SELECT entry, difficulty_entry_1, difficulty_entry_2, difficulty_entry_3, KillCredit1, KillCredit2, modelid1, modelid2, modelid3, modelid4, name, subname, IconName, gossip_menu_id, minlevel, maxlevel, exp, faction, npcflag, speed_walk, speed_run, detection_range, scale, `rank`, dmgschool, DamageModifier, BaseAttackTime, RangeAttackTime, BaseVariance, RangeVariance, unit_class, unit_flags, unit_flags2, dynamicflags, family, trainer_type, trainer_spell, trainer_class, trainer_race, type, type_flags, lootid, pickpocketloot, skinloot, PetSpellDataId, VehicleId, mingold, maxgold, AIName, MovementType, ctm.Ground, ctm.Swim, ctm.Flight, ctm.Rooted, ctm.Chase, ctm.Random, ctm.InteractionPauseTimer, HoverHeight, HealthModifier, ManaModifier, ArmorModifier, ExperienceModifier, RacialLeader, movementId, RegenHealth, mechanic_immune_mask, spell_school_immune_mask, flags_extra, ScriptName FROM creature_template ct LEFT JOIN creature_template_movement ctm ON ct.entry = ctm.CreatureId WHERE entry = ?", CONNECTION_SYNCH);
PrepareStatement(WORLD_SEL_WAYPOINT_SCRIPT_BY_ID, "SELECT guid, delay, command, datalong, datalong2, dataint, x, y, z, o FROM waypoint_scripts WHERE id = ?", CONNECTION_SYNCH);
PrepareStatement(WORLD_SEL_ITEM_TEMPLATE_BY_NAME, "SELECT entry FROM item_template WHERE name = ?", CONNECTION_SYNCH);
PrepareStatement(WORLD_SEL_CREATURE_BY_ID, "SELECT guid FROM creature WHERE id = ?", CONNECTION_SYNCH);

View file

@ -94,6 +94,19 @@ void HostileRefMgr::setOnlineOfflineState(bool isOnline)
}
}
//=================================================
// The online / offline status is calculated and set
void HostileRefMgr::updateThreatTables()
{
HostileReference* ref = getFirst();
while (ref)
{
ref->updateOnlineStatus();
ref = ref->next();
}
}
//=================================================
// The references are not needed anymore
// tell the source to remove them from the list and free the mem

View file

@ -59,6 +59,8 @@ public:
HostileReference* getFirst() { return ((HostileReference*) RefMgr<Unit, ThreatMgr>::getFirst()); }
void updateThreatTables();
void setOnlineOfflineState(bool isOnline);
// set state for one reference, defined by Unit

View file

@ -55,6 +55,31 @@
// see: https://github.com/azerothcore/azerothcore-wotlk/issues/9766
#include "GridNotifiersImpl.h"
CreatureMovementData::CreatureMovementData() : Ground(CreatureGroundMovementType::Run), Flight(CreatureFlightMovementType::None),
Swim(true), Rooted(false), Chase(CreatureChaseMovementType::Run),
Random(CreatureRandomMovementType::Walk), InteractionPauseTimer(sWorld->getIntConfig(CONFIG_CREATURE_STOP_FOR_PLAYER)) {}
std::string CreatureMovementData::ToString() const
{
constexpr std::array<char const*, 3> GroundStates = {"None", "Run", "Hover"};
constexpr std::array<char const*, 3> FlightStates = {"None", "DisableGravity", "CanFly"};
constexpr std::array<char const*, 3> ChaseStates = {"Run", "CanWalk", "AlwaysWalk"};
constexpr std::array<char const*, 3> RandomStates = {"Walk", "CanRun", "AlwaysRun"};
std::ostringstream str;
str << std::boolalpha
<< "Ground: " << GroundStates[AsUnderlyingType(Ground)]
<< ", Swim: " << Swim
<< ", Flight: " << FlightStates[AsUnderlyingType(Flight)]
<< ", Chase: " << ChaseStates[AsUnderlyingType(Chase)]
<< ", Random: " << RandomStates[AsUnderlyingType(Random)];
if (Rooted)
str << ", Rooted";
str << ", InteractionPauseTimer: " << InteractionPauseTimer;
return str.str();
}
TrainerSpell const* TrainerSpellData::Find(uint32 spell_id) const
{
TrainerSpellMap::const_iterator itr = spellList.find(spell_id);
@ -541,7 +566,7 @@ bool Creature::UpdateEntry(uint32 Entry, const CreatureData* data, bool changele
ApplySpellImmune(0, IMMUNITY_EFFECT, SPELL_EFFECT_ATTACK_ME, true);
}
if (cInfo->InhabitType & INHABIT_ROOT)
if (GetMovementTemplate().IsRooted())
{
SetControlled(true, UNIT_STATE_ROOT);
}
@ -2452,7 +2477,7 @@ bool Creature::CanCreatureAttack(Unit const* victim, bool skipDistCheck) const
else
{
// to prevent creatures in air ignore attacks because distance is already too high...
if (GetCreatureTemplate()->InhabitType & INHABIT_AIR)
if (GetMovementTemplate().IsFlightAllowed())
return victim->IsInDist2d(&m_homePosition, dist);
else
return victim->IsInDist(&m_homePosition, dist);
@ -2500,7 +2525,7 @@ bool Creature::LoadCreaturesAddon(bool reload)
//! Check using InhabitType as movement flags are assigned dynamically
//! basing on whether the creature is in air or not
//! Set MovementFlag_Hover. Otherwise do nothing.
if (GetByteValue(UNIT_FIELD_BYTES_1, 3) & UNIT_BYTE1_FLAG_HOVER /*&& !(GetCreatureTemplate()->InhabitType & INHABIT_AIR)*/)
if (CanHover())
AddUnitMovementFlag(MOVEMENTFLAG_HOVER);
}
@ -2731,6 +2756,14 @@ void Creature::GetRespawnPosition(float& x, float& y, float& z, float* ori, floa
*dist = 0;
}
CreatureMovementData const& Creature::GetMovementTemplate() const
{
if (CreatureMovementData const* movementOverride = sObjectMgr->GetCreatureMovementOverride(m_spawnId))
return *movementOverride;
return GetCreatureTemplate()->Movement;
}
void Creature::AllLootRemovedFromCorpse()
{
if (loot.loot_type != LOOT_SKINNING && !IsPet() && GetCreatureTemplate()->SkinLootId && hasLootRecipient())
@ -2937,8 +2970,6 @@ bool Creature::SetDisableGravity(bool disable, bool packetOnly/*=false*/)
if (!packetOnly && !Unit::SetDisableGravity(disable))
return false;
applyInhabitFlags();
if (m_movedByPlayer)
{
WorldPacket data(disable ? SMSG_MOVE_GRAVITY_DISABLE : SMSG_MOVE_GRAVITY_ENABLE, 12);
@ -2962,23 +2993,6 @@ bool Creature::SetDisableGravity(bool disable, bool packetOnly/*=false*/)
return true;
}
void Creature::applyInhabitFlags()
{
if (IsAlive() && !HasUnitState(UNIT_STATE_ROOT) && !HasUnitMovementFlag(MOVEMENTFLAG_ROOT))
{
if (IsLevitating())
{
SetByteValue(UNIT_FIELD_BYTES_1, UNIT_BYTES_1_OFFSET_ANIM_TIER, UNIT_BYTE1_FLAG_FLY);
}
else if (IsHovering())
{
SetByteValue(UNIT_FIELD_BYTES_1, UNIT_BYTES_1_OFFSET_ANIM_TIER, UNIT_BYTE1_FLAG_HOVER);
}
else
SetByteValue(UNIT_FIELD_BYTES_1, UNIT_BYTES_1_OFFSET_ANIM_TIER, UNIT_BYTE1_FLAG_GROUND);
}
}
bool Creature::SetSwim(bool enable)
{
if (!Unit::SetSwim(enable))
@ -3016,7 +3030,7 @@ bool Creature::CanEnterWater() const
if (CanSwim())
return true;
return GetCreatureTemplate()->InhabitType & INHABIT_WATER;
return GetMovementTemplate().IsSwimAllowed();
}
void Creature::RefreshSwimmingFlag(bool recheck)
@ -3063,14 +3077,6 @@ bool Creature::SetCanFly(bool enable, bool /*packetOnly*/ /* = false */)
return true;
}
bool Creature::CanFly() const
{
if (Unit::IsFlying())
return true;
return GetCreatureTemplate()->InhabitType & INHABIT_AIR;
}
bool Creature::SetWaterWalking(bool enable, bool packetOnly /* = false */)
{
if (!packetOnly && !Unit::SetWaterWalking(enable))
@ -3132,22 +3138,6 @@ bool Creature::SetHover(bool enable, bool packetOnly /*= false*/)
if (!packetOnly && !Unit::SetHover(enable))
return false;
applyInhabitFlags();
if (m_movedByPlayer)
{
WorldPacket data(enable ? SMSG_MOVE_SET_HOVER : SMSG_MOVE_UNSET_HOVER, 12);
data << GetPackGUID();
data << uint32(0); //! movement counter
m_movedByPlayer->ToPlayer()->SendDirectMessage(&data);
data.Initialize(MSG_MOVE_HOVER, 64);
data << GetPackGUID();
BuildMovementPacket(&data);
m_movedByPlayer->ToPlayer()->SendMessageToSet(&data, false);
return true;
}
if (!movespline->Initialized())
return true;
@ -3217,76 +3207,33 @@ void Creature::UpdateMovementFlags()
if (info->flags_extra & CREATURE_FLAG_EXTRA_NO_MOVE_FLAGS_UPDATE)
return;
float z = GetPositionZ();
float ground = GetFloorZ();
bool isInAir = false;
bool Swim = false;
bool canHover = CanHover();
bool isInAir = (G3D::fuzzyGt(GetPositionZ(), ground + (canHover ? GetFloatValue(UNIT_FIELD_HOVERHEIGHT) : 0.0f) + GROUND_HEIGHT_TOLERANCE) || G3D::fuzzyLt(GetPositionZ(), ground - GROUND_HEIGHT_TOLERANCE)); // Can be underground too, prevent the falling
LiquidData const& liquidData = GetLiquidData();
if (liquidData.Status == LIQUID_MAP_NO_WATER)
if (GetMovementTemplate().IsFlightAllowed() && isInAir && !IsFalling())
{
if (ground > INVALID_HEIGHT)
isInAir = G3D::fuzzyGt(z, ground + (canHover ? GetFloatValue(UNIT_FIELD_HOVERHEIGHT) : 0.0f) + GROUND_HEIGHT_TOLERANCE) || G3D::fuzzyLt(z, ground - GROUND_HEIGHT_TOLERANCE); // Can be underground too, prevent the falling
if (GetMovementTemplate().Flight == CreatureFlightMovementType::CanFly)
SetCanFly(true);
else
isInAir = true;
SetDisableGravity(true);
if (!HasAuraType(SPELL_AURA_HOVER))
SetHover(false);
}
else
{
switch (liquidData.Status)
{
case LIQUID_MAP_ABOVE_WATER:
isInAir = true;
break;
case LIQUID_MAP_WATER_WALK:
isInAir = true;
[[fallthrough]];
case LIQUID_MAP_IN_WATER:
Swim = z - liquidData.DepthLevel > GetCollisionHeight() * 0.75f; // Shallow water at ~75% of collision height
break;
case LIQUID_MAP_UNDER_WATER:
Swim = true;
break;
default:
break;
}
SetCanFly(false);
SetDisableGravity(false);
if (IsAlive() && (CanHover() || HasAuraType(SPELL_AURA_HOVER)))
SetHover(true);
}
SetSwim(CanSwim() && Swim);
if (!isInAir)
RemoveUnitMovementFlag(MOVEMENTFLAG_FALLING);
if (info->InhabitType & INHABIT_AIR)
{
if (isInAir && !IsFalling())
{
if (info->InhabitType & INHABIT_GROUND)
{
SetCanFly(true);
SetByteValue(UNIT_FIELD_BYTES_1, UNIT_BYTES_1_OFFSET_ANIM_TIER, UNIT_BYTE1_FLAG_FLY);
}
else
SetDisableGravity(true);
if (!HasAuraType(SPELL_AURA_HOVER))
SetHover(false);
}
else
{
SetCanFly(false);
SetDisableGravity(false);
if (info->InhabitType & INHABIT_GROUND)
{
SetByteValue(UNIT_FIELD_BYTES_1, UNIT_BYTES_1_OFFSET_ANIM_TIER, UNIT_BYTE1_FLAG_GROUND);
}
if (IsAlive() && (CanHover() || HasAuraType(SPELL_AURA_HOVER)))
SetHover(true);
}
}
else if (!HasUnitMovementFlag(MOVEMENTFLAG_CAN_FLY | MOVEMENTFLAG_DISABLE_GRAVITY) && IsAlive() && (CanHover() || HasAuraType(SPELL_AURA_HOVER)))
SetHover(true);
SetSwim(CanSwim() && IsInWater());
}
void Creature::SetObjectScale(float scale)

View file

@ -74,11 +74,15 @@ public:
[[nodiscard]] bool IsCivilian() const { return GetCreatureTemplate()->flags_extra & CREATURE_FLAG_EXTRA_CIVILIAN; }
[[nodiscard]] bool IsTrigger() const { return GetCreatureTemplate()->flags_extra & CREATURE_FLAG_EXTRA_TRIGGER; }
[[nodiscard]] bool IsGuard() const { return GetCreatureTemplate()->flags_extra & CREATURE_FLAG_EXTRA_GUARD; }
[[nodiscard]] bool CanWalk() const { return GetCreatureTemplate()->InhabitType & INHABIT_GROUND; }
CreatureMovementData const& GetMovementTemplate() const;
[[nodiscard]] bool CanWalk() const { return GetMovementTemplate().IsGroundAllowed(); }
[[nodiscard]] bool CanSwim() const override;
[[nodiscard]] bool CanEnterWater() const override;
[[nodiscard]] bool CanFly() const override;
[[nodiscard]] bool CanHover() const { return m_originalAnimTier & UNIT_BYTE1_FLAG_HOVER || IsHovering(); }
[[nodiscard]] bool CanFly() const override { return GetMovementTemplate().IsFlightAllowed() || IsFlying(); }
[[nodiscard]] bool CanHover() const { return GetMovementTemplate().Ground == CreatureGroundMovementType::Hover || IsHovering(); }
MovementGeneratorType GetDefaultMovementType() const override { return m_defaultMovementType; }
void SetDefaultMovementType(MovementGeneratorType mgt) { m_defaultMovementType = mgt; }
void SetReactState(ReactStates st) { m_reactState = st; }
[[nodiscard]] ReactStates GetReactState() const { return m_reactState; }
@ -261,9 +265,6 @@ public:
bool IsMoveInLineOfSightDisabled() { return m_moveInLineOfSightDisabled; }
bool IsMoveInLineOfSightStrictlyDisabled() { return m_moveInLineOfSightStrictlyDisabled; }
[[nodiscard]] MovementGeneratorType GetDefaultMovementType() const { return m_defaultMovementType; }
void SetDefaultMovementType(MovementGeneratorType mgt) { m_defaultMovementType = mgt; }
void RemoveCorpse(bool setSpawnTime = true, bool skipVisibility = false);
void DespawnOrUnsummon(Milliseconds msTimeToDespawn, Seconds forcedRespawnTimer);
@ -462,7 +463,6 @@ private:
uint32 m_assistanceTimer;
void applyInhabitFlags();
};
class AssistDelayEvent : public BasicEvent

View file

@ -86,6 +86,92 @@ enum CreatureFlagsExtra : uint32
CREATURE_FLAG_EXTRA_DB_ALLOWED = (0xFFFFFFFF & ~(CREATURE_FLAG_EXTRA_UNUSED | CREATURE_FLAG_EXTRA_DUNGEON_BOSS)) // SKIP
};
enum class CreatureGroundMovementType : uint8
{
None,
Run,
Hover,
Max
};
enum class CreatureFlightMovementType : uint8
{
None,
DisableGravity,
CanFly,
Max
};
enum class CreatureChaseMovementType : uint8
{
Run,
CanWalk,
AlwaysWalk,
Max
};
enum class CreatureRandomMovementType : uint8
{
Walk,
CanRun,
AlwaysRun,
Max
};
struct CreatureMovementData
{
CreatureMovementData();
CreatureGroundMovementType Ground;
CreatureFlightMovementType Flight;
bool Swim;
bool Rooted;
CreatureChaseMovementType Chase;
CreatureRandomMovementType Random;
uint32 InteractionPauseTimer;
bool IsGroundAllowed() const
{
return Ground != CreatureGroundMovementType::None;
}
bool IsSwimAllowed() const
{
return Swim;
}
bool IsFlightAllowed() const
{
return Flight != CreatureFlightMovementType::None;
}
bool IsRooted() const
{
return Rooted;
}
CreatureChaseMovementType GetChase() const
{
return Chase;
}
CreatureRandomMovementType GetRandom() const
{
return Random;
}
uint32 GetInteractionPauseTimer() const
{
return InteractionPauseTimer;
}
std::string ToString() const;
};
// from `creature_template` table
struct CreatureTemplate
{
@ -138,7 +224,7 @@ struct CreatureTemplate
uint32 maxgold;
std::string AIName;
uint32 MovementType;
uint32 InhabitType;
CreatureMovementData Movement;
float HoverHeight;
float ModHealth;
float ModMana;

View file

@ -234,6 +234,7 @@ Player::Player(WorldSession* session): Unit(true), m_mover(this)
m_MirrorTimerFlags = UNDERWATER_NONE;
m_MirrorTimerFlagsLast = UNDERWATER_NONE;
m_isInWater = false;
m_drunkTimer = 0;
m_deathTimer = 0;
m_deathExpireTime = 0;
@ -2087,6 +2088,24 @@ bool Player::IsFalling() const
return GetPositionZ() < m_lastFallZ && !IsInFlight();
}
void Player::SetInWater(bool apply)
{
if (m_isInWater == apply)
return;
//define player in water by opcodes
//move player's guid into HateOfflineList of those mobs
//which can't swim and move guid back into ThreatList when
//on surface.
//TODO: exist also swimming mobs, and function must be symmetric to enter/leave water
m_isInWater = apply;
// remove auras that need water/land
RemoveAurasWithInterruptFlags(apply ? AURA_INTERRUPT_FLAG_NOT_ABOVEWATER : AURA_INTERRUPT_FLAG_NOT_UNDERWATER);
getHostileRefMgr().updateThreatTables();
}
bool Player::IsInAreaTriggerRadius(const AreaTrigger* trigger) const
{
static const float delta = 5.0f;

View file

@ -1086,6 +1086,9 @@ public:
static bool BuildEnumData(PreparedQueryResult result, WorldPacket* data);
void SetInWater(bool apply);
[[nodiscard]] bool IsInWater() const override { return m_isInWater; }
[[nodiscard]] bool IsFalling() const;
bool IsInAreaTriggerRadius(const AreaTrigger* trigger) const;
@ -2915,6 +2918,7 @@ private:
int32 m_MirrorTimer[MAX_TIMERS];
uint8 m_MirrorTimerFlags;
uint8 m_MirrorTimerFlagsLast;
bool m_isInWater;
// Current teleport data
WorldLocation teleportStore_dest;

View file

@ -39,6 +39,7 @@
#include "InstanceScript.h"
#include "Log.h"
#include "MapMgr.h"
#include "MovementGenerator.h"
#include "MoveSpline.h"
#include "MoveSplineInit.h"
#include "ObjectAccessor.h"
@ -3777,13 +3778,14 @@ bool Unit::isInAccessiblePlaceFor(Creature const* c) const
return false;
}
// In water or jumping in water
if (IsInWater() || (GetLiquidData().Status == LIQUID_MAP_ABOVE_WATER && (IsFalling() || (ToPlayer() && ToPlayer()->IsFalling()))))
if (IsInWater())
{
return IsUnderWater() ? c->CanEnterWater() : (c->CanEnterWater() || c->CanFly());
return c->CanEnterWater();
}
else
{
return c->CanWalk() || c->CanFly();
}
return c->CanWalk() || c->CanFly() || (c->CanSwim() && IsInWater());
}
void Unit::ProcessPositionDataChanged(PositionFullTerrainStatus const& data)
@ -13578,28 +13580,29 @@ void Unit::UpdateSpeed(UnitMoveType mtype, bool forced)
case MOVE_RUN:
case MOVE_SWIM:
case MOVE_FLIGHT:
{
// Set creature speed rate
if (GetTypeId() == TYPEID_UNIT)
speed *= ToCreature()->GetCreatureTemplate()->speed_run; // at this point, MOVE_WALK is never reached
// Normalize speed by 191 aura SPELL_AURA_USE_NORMAL_MOVEMENT_SPEED if need
/// @todo possible affect only on MOVE_RUN
if (int32 normalization = GetMaxPositiveAuraModifier(SPELL_AURA_USE_NORMAL_MOVEMENT_SPEED))
{
if (GetTypeId() == TYPEID_UNIT)
if (Creature* creature = ToCreature())
{
speed *= ToCreature()->GetCreatureTemplate()->speed_run; // at this point, MOVE_WALK is never reached
uint32 immuneMask = creature->GetCreatureTemplate()->MechanicImmuneMask;
if (immuneMask & (1 << (MECHANIC_SNARE - 1)) || immuneMask & (1 << (MECHANIC_DAZE - 1)))
break;
}
// Normalize speed by 191 aura SPELL_AURA_USE_NORMAL_MOVEMENT_SPEED if need
// TODO: possible affect only on MOVE_RUN
if (int32 normalization = GetMaxPositiveAuraModifier(SPELL_AURA_USE_NORMAL_MOVEMENT_SPEED))
{
// Use speed from aura
float max_speed = normalization / (IsControlledByPlayer() ? playerBaseMoveSpeed[mtype] : baseMoveSpeed[mtype]);
// Xinef: normal movement speed - multiply by creature db modifer
if (GetTypeId() == TYPEID_UNIT)
max_speed *= ToCreature()->GetCreatureTemplate()->speed_run;
if (speed > max_speed)
speed = max_speed;
}
break;
// Use speed from aura
float max_speed = normalization / (IsControlledByPlayer() ? playerBaseMoveSpeed[mtype] : baseMoveSpeed[mtype]);
if (speed > max_speed)
speed = max_speed;
}
break;
}
default:
break;
}
@ -16039,6 +16042,11 @@ void Unit::SendPetAIReaction(ObjectGuid guid)
///----------End of Pet responses methods----------
MovementGeneratorType Unit::GetDefaultMovementType() const
{
return IDLE_MOTION_TYPE;
}
void Unit::StopMoving()
{
ClearUnitState(UNIT_STATE_MOVING);
@ -16058,6 +16066,26 @@ void Unit::StopMoving()
init.Stop();
}
void Unit::PauseMovement(uint32 timer /* = 0*/, uint8 slot /* = 0*/)
{
if (slot >= MAX_MOTION_SLOT)
return;
if (MovementGenerator* movementGenerator = GetMotionMaster()->GetMotionSlot(slot))
movementGenerator->Pause(timer);
StopMoving();
}
void Unit::ResumeMovement(uint32 timer /* = 0*/, uint8 slot /* = 0*/)
{
if (slot >= MAX_MOTION_SLOT)
return;
if (MovementGenerator* movementGenerator = GetMotionMaster()->GetMotionSlot(slot))
movementGenerator->Resume(timer);
}
void Unit::StopMovingOnCurrentPos() // pussywizard
{
ClearUnitState(UNIT_STATE_MOVING);
@ -17649,8 +17677,16 @@ bool Unit::SetCharmedBy(Unit* charmer, CharmType type, AuraApplication const* au
if (GetTypeId() == TYPEID_UNIT)
{
if (MovementGenerator* movementGenerator = GetMotionMaster()->GetMotionSlot(MOTION_SLOT_IDLE))
{
movementGenerator->Pause(0);
}
GetMotionMaster()->Clear(MOTION_SLOT_ACTIVE);
StopMoving();
ToCreature()->AI()->OnCharmed(true);
GetMotionMaster()->MoveIdle();
// Xinef: If creature can fly, add normal player flying flag (fixes speed)
if (charmer->GetTypeId() == TYPEID_PLAYER && ToCreature()->CanFly())
@ -19107,7 +19143,7 @@ bool Unit::CanSwim() const
return true;
if (HasFlag(UNIT_FIELD_FLAGS_2, 0x1000000))
return false;
if (IsPet() && HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PET_IN_COMBAT))
if (HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PET_IN_COMBAT))
return true;
return HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_RENAME | UNIT_FLAG_SWIMMING);
}

View file

@ -2345,10 +2345,13 @@ public:
MotionMaster* GetMotionMaster() { return i_motionMaster; }
[[nodiscard]] const MotionMaster* GetMotionMaster() const { return i_motionMaster; }
[[nodiscard]] virtual MovementGeneratorType GetDefaultMovementType() const;
[[nodiscard]] bool IsStopped() const { return !(HasUnitState(UNIT_STATE_MOVING)); }
void StopMoving();
void StopMovingOnCurrentPos();
virtual void PauseMovement(uint32 timer = 0, uint8 slot = 0); // timer in ms
void ResumeMovement(uint32 timer = 0, uint8 slot = 0);
void AddUnitMovementFlag(uint32 f) { m_movementInfo.flags |= f; }
void RemoveUnitMovementFlag(uint32 f) { m_movementInfo.flags &= ~f; }

View file

@ -485,9 +485,9 @@ void ObjectMgr::LoadCreatureTemplates()
"dynamicflags, family, trainer_type, trainer_spell, trainer_class, trainer_race, type, "
// 40 41 42 43 44 45 46 47 48 49
"type_flags, lootid, pickpocketloot, skinloot, PetSpellDataId, VehicleId, mingold, maxgold, AIName, MovementType, "
// 50 51 52 53 54 55 56 57 58 59 60 61 62
"InhabitType, HoverHeight, HealthModifier, ManaModifier, ArmorModifier, ExperienceModifier, RacialLeader, movementId, RegenHealth, mechanic_immune_mask, spell_school_immune_mask, flags_extra, ScriptName "
"FROM creature_template;");
// 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68
"ctm.Ground, ctm.Swim, ctm.Flight, ctm.Rooted, ctm.Chase, ctm.Random, ctm.InteractionPauseTimer, HoverHeight, HealthModifier, ManaModifier, ArmorModifier, ExperienceModifier, RacialLeader, movementId, RegenHealth, mechanic_immune_mask, spell_school_immune_mask, flags_extra, ScriptName "
"FROM creature_template ct LEFT JOIN creature_template_movement ctm ON ct.entry = ctm.CreatureId;");
if (!result)
{
@ -552,44 +552,44 @@ void ObjectMgr::LoadCreatureTemplate(Field* fields)
creatureTemplate.KillCredit[i] = fields[4 + i].GetUInt32();
}
creatureTemplate.Modelid1 = fields[6].GetUInt32();
creatureTemplate.Modelid2 = fields[7].GetUInt32();
creatureTemplate.Modelid3 = fields[8].GetUInt32();
creatureTemplate.Modelid4 = fields[9].GetUInt32();
creatureTemplate.Name = fields[10].GetString();
creatureTemplate.SubName = fields[11].GetString();
creatureTemplate.IconName = fields[12].GetString();
creatureTemplate.GossipMenuId = fields[13].GetUInt32();
creatureTemplate.minlevel = fields[14].GetUInt8();
creatureTemplate.maxlevel = fields[15].GetUInt8();
creatureTemplate.expansion = uint32(fields[16].GetInt16());
creatureTemplate.faction = uint32(fields[17].GetUInt16());
creatureTemplate.npcflag = fields[18].GetUInt32();
creatureTemplate.speed_walk = fields[19].GetFloat();
creatureTemplate.speed_run = fields[20].GetFloat();
creatureTemplate.detection_range = fields[21].GetFloat();
creatureTemplate.scale = fields[22].GetFloat();
creatureTemplate.rank = uint32(fields[23].GetUInt8());
creatureTemplate.dmgschool = uint32(fields[24].GetInt8());
creatureTemplate.DamageModifier = fields[25].GetFloat();
creatureTemplate.BaseAttackTime = fields[26].GetUInt32();
creatureTemplate.RangeAttackTime = fields[27].GetUInt32();
creatureTemplate.BaseVariance = fields[28].GetFloat();
creatureTemplate.RangeVariance = fields[29].GetFloat();
creatureTemplate.unit_class = uint32(fields[30].GetUInt8());
creatureTemplate.unit_flags = fields[31].GetUInt32();
creatureTemplate.unit_flags2 = fields[32].GetUInt32();
creatureTemplate.dynamicflags = fields[33].GetUInt32();
creatureTemplate.family = uint32(fields[34].GetUInt8());
creatureTemplate.trainer_type = uint32(fields[35].GetUInt8());
creatureTemplate.trainer_spell = fields[36].GetUInt32();
creatureTemplate.trainer_class = uint32(fields[37].GetUInt8());
creatureTemplate.trainer_race = uint32(fields[38].GetUInt8());
creatureTemplate.type = uint32(fields[39].GetUInt8());
creatureTemplate.type_flags = fields[40].GetUInt32();
creatureTemplate.lootid = fields[41].GetUInt32();
creatureTemplate.pickpocketLootId = fields[42].GetUInt32();
creatureTemplate.SkinLootId = fields[43].GetUInt32();
creatureTemplate.Modelid1 = fields[6].GetUInt32();
creatureTemplate.Modelid2 = fields[7].GetUInt32();
creatureTemplate.Modelid3 = fields[8].GetUInt32();
creatureTemplate.Modelid4 = fields[9].GetUInt32();
creatureTemplate.Name = fields[10].GetString();
creatureTemplate.SubName = fields[11].GetString();
creatureTemplate.IconName = fields[12].GetString();
creatureTemplate.GossipMenuId = fields[13].GetUInt32();
creatureTemplate.minlevel = fields[14].GetUInt8();
creatureTemplate.maxlevel = fields[15].GetUInt8();
creatureTemplate.expansion = uint32(fields[16].GetInt16());
creatureTemplate.faction = uint32(fields[17].GetUInt16());
creatureTemplate.npcflag = fields[18].GetUInt32();
creatureTemplate.speed_walk = fields[19].GetFloat();
creatureTemplate.speed_run = fields[20].GetFloat();
creatureTemplate.detection_range = fields[21].GetFloat();
creatureTemplate.scale = fields[22].GetFloat();
creatureTemplate.rank = uint32(fields[23].GetUInt8());
creatureTemplate.dmgschool = uint32(fields[24].GetInt8());
creatureTemplate.DamageModifier = fields[25].GetFloat();
creatureTemplate.BaseAttackTime = fields[26].GetUInt32();
creatureTemplate.RangeAttackTime = fields[27].GetUInt32();
creatureTemplate.BaseVariance = fields[28].GetFloat();
creatureTemplate.RangeVariance = fields[29].GetFloat();
creatureTemplate.unit_class = uint32(fields[30].GetUInt8());
creatureTemplate.unit_flags = fields[31].GetUInt32();
creatureTemplate.unit_flags2 = fields[32].GetUInt32();
creatureTemplate.dynamicflags = fields[33].GetUInt32();
creatureTemplate.family = uint32(fields[34].GetUInt8());
creatureTemplate.trainer_type = uint32(fields[35].GetUInt8());
creatureTemplate.trainer_spell = fields[36].GetUInt32();
creatureTemplate.trainer_class = uint32(fields[37].GetUInt8());
creatureTemplate.trainer_race = uint32(fields[38].GetUInt8());
creatureTemplate.type = uint32(fields[39].GetUInt8());
creatureTemplate.type_flags = fields[40].GetUInt32();
creatureTemplate.lootid = fields[41].GetUInt32();
creatureTemplate.pickpocketLootId = fields[42].GetUInt32();
creatureTemplate.SkinLootId = fields[43].GetUInt32();
for (uint8 i = SPELL_SCHOOL_HOLY; i < MAX_SPELL_SCHOOL; ++i)
{
@ -601,25 +601,49 @@ void ObjectMgr::LoadCreatureTemplate(Field* fields)
creatureTemplate.spells[i] = 0;
}
creatureTemplate.PetSpellDataId = fields[44].GetUInt32();
creatureTemplate.VehicleId = fields[45].GetUInt32();
creatureTemplate.mingold = fields[46].GetUInt32();
creatureTemplate.maxgold = fields[47].GetUInt32();
creatureTemplate.AIName = fields[48].GetString();
creatureTemplate.MovementType = uint32(fields[49].GetUInt8());
creatureTemplate.InhabitType = uint32(fields[50].GetUInt8());
creatureTemplate.HoverHeight = fields[51].GetFloat();
creatureTemplate.ModHealth = fields[52].GetFloat();
creatureTemplate.ModMana = fields[53].GetFloat();
creatureTemplate.ModArmor = fields[54].GetFloat();
creatureTemplate.ModExperience = fields[55].GetFloat();
creatureTemplate.RacialLeader = fields[56].GetBool();
creatureTemplate.movementId = fields[57].GetUInt32();
creatureTemplate.RegenHealth = fields[58].GetBool();
creatureTemplate.MechanicImmuneMask = fields[59].GetUInt32();
creatureTemplate.SpellSchoolImmuneMask = fields[60].GetUInt8();
creatureTemplate.flags_extra = fields[61].GetUInt32();
creatureTemplate.ScriptID = GetScriptId(fields[62].GetCString());
creatureTemplate.PetSpellDataId = fields[44].GetUInt32();
creatureTemplate.VehicleId = fields[45].GetUInt32();
creatureTemplate.mingold = fields[46].GetUInt32();
creatureTemplate.maxgold = fields[47].GetUInt32();
creatureTemplate.AIName = fields[48].GetString();
creatureTemplate.MovementType = uint32(fields[49].GetUInt8());
if (!fields[50].IsNull())
{
creatureTemplate.Movement.Ground = static_cast<CreatureGroundMovementType>(fields[50].GetUInt8());
}
creatureTemplate.Movement.Swim = fields[51].GetBool();
if (!fields[52].IsNull())
{
creatureTemplate.Movement.Flight = static_cast<CreatureFlightMovementType>(fields[52].GetUInt8());
}
creatureTemplate.Movement.Rooted = fields[53].GetBool();
if (!fields[54].IsNull())
{
creatureTemplate.Movement.Chase = static_cast<CreatureChaseMovementType>(fields[54].GetUInt8());
}
if (!fields[55].IsNull())
{
creatureTemplate.Movement.Random = static_cast<CreatureRandomMovementType>(fields[55].GetUInt8());
}
if (!fields[56].IsNull())
{
creatureTemplate.Movement.InteractionPauseTimer = fields[56].GetUInt32();
}
creatureTemplate.HoverHeight = fields[57].GetFloat();
creatureTemplate.ModHealth = fields[58].GetFloat();
creatureTemplate.ModMana = fields[59].GetFloat();
creatureTemplate.ModArmor = fields[60].GetFloat();
creatureTemplate.ModExperience = fields[61].GetFloat();
creatureTemplate.RacialLeader = fields[62].GetBool();
creatureTemplate.movementId = fields[63].GetUInt32();
creatureTemplate.RegenHealth = fields[64].GetBool();
creatureTemplate.MechanicImmuneMask = fields[65].GetUInt32();
creatureTemplate.SpellSchoolImmuneMask = fields[66].GetUInt8();
creatureTemplate.flags_extra = fields[67].GetUInt32();
creatureTemplate.ScriptID = GetScriptId(fields[68].GetCString());
}
void ObjectMgr::LoadCreatureTemplateResistances()
@ -1070,17 +1094,7 @@ void ObjectMgr::CheckCreatureTemplate(CreatureTemplate const* cInfo)
const_cast<CreatureTemplate*>(cInfo)->family = 0;
}
if (cInfo->InhabitType <= 0 || cInfo->InhabitType > INHABIT_ANYWHERE)
{
LOG_ERROR("sql.sql", "Creature (Entry: %u) has wrong value (%u) in `InhabitType`, creature will not correctly walk/swim/fly.", cInfo->Entry, cInfo->InhabitType);
const_cast<CreatureTemplate*>(cInfo)->InhabitType = INHABIT_ANYWHERE;
}
if (cInfo->InhabitType == INHABIT_ROOT)
{
LOG_ERROR("sql.sql", "Creature (Entry: %u) only has INHABIT_ROOT(8) as `InhabitType`, creature will not behave correctly.", cInfo->Entry);
const_cast<CreatureTemplate*>(cInfo)->InhabitType = INHABIT_ANYWHERE;
}
CheckCreatureMovement("creature_template_movement", cInfo->Entry, const_cast<CreatureTemplate*>(cInfo)->Movement);
if (cInfo->HoverHeight < 0.0f)
{
@ -1144,6 +1158,33 @@ void ObjectMgr::CheckCreatureTemplate(CreatureTemplate const* cInfo)
const_cast<CreatureTemplate*>(cInfo)->DamageModifier *= Creature::_GetDamageMod(cInfo->rank);
}
void ObjectMgr::CheckCreatureMovement(char const* table, uint64 id, CreatureMovementData& creatureMovement)
{
if (creatureMovement.Ground >= CreatureGroundMovementType::Max)
{
LOG_ERROR("sql.sql", "`%s`.`Ground` wrong value (%u) for Id " UI64FMTD ", setting to Run.", table, uint32(creatureMovement.Ground), id);
creatureMovement.Ground = CreatureGroundMovementType::Run;
}
if (creatureMovement.Flight >= CreatureFlightMovementType::Max)
{
LOG_ERROR("sql.sql", "`%s`.`Flight` wrong value (%u) for Id " UI64FMTD ", setting to None.", table, uint32(creatureMovement.Flight), id);
creatureMovement.Flight = CreatureFlightMovementType::None;
}
if (creatureMovement.Chase >= CreatureChaseMovementType::Max)
{
LOG_ERROR("sql.sql", "`%s`.`Chase` wrong value (%u) for Id " UI64FMTD ", setting to Run.", table, uint32(creatureMovement.Chase), id);
creatureMovement.Chase = CreatureChaseMovementType::Run;
}
if (creatureMovement.Random >= CreatureRandomMovementType::Max)
{
LOG_ERROR("sql.sql", "`%s`.`Random` wrong value (%u) for Id " UI64FMTD ", setting to Walk.", table, uint32(creatureMovement.Random), id);
creatureMovement.Random = CreatureRandomMovementType::Walk;
}
}
void ObjectMgr::LoadCreatureAddons()
{
uint32 oldMSTime = getMSTime();
@ -1315,6 +1356,11 @@ CreatureAddon const* ObjectMgr::GetCreatureTemplateAddon(uint32 entry)
return nullptr;
}
CreatureMovementData const* ObjectMgr::GetCreatureMovementOverride(ObjectGuid::LowType spawnId) const
{
return Acore::Containers::MapGetValuePtr(_creatureMovementOverrides, spawnId);
}
EquipmentInfo const* ObjectMgr::GetEquipmentInfo(uint32 entry, int8& id)
{
EquipmentInfoContainer::const_iterator itr = _equipmentInfoStore.find(entry);
@ -1419,6 +1465,82 @@ void ObjectMgr::LoadEquipmentTemplates()
LOG_INFO("server.loading", " ");
}
void ObjectMgr::LoadCreatureMovementOverrides()
{
uint32 oldMSTime = getMSTime();
_creatureMovementOverrides.clear();
// Load the data from creature_movement_override and if NULL fallback to creature_template_movement
QueryResult result = WorldDatabase.Query("SELECT cmo.SpawnId,"
"COALESCE(cmo.Ground, ctm.Ground),"
"COALESCE(cmo.Swim, ctm.Swim),"
"COALESCE(cmo.Flight, ctm.Flight),"
"COALESCE(cmo.Rooted, ctm.Rooted),"
"COALESCE(cmo.Chase, ctm.Chase),"
"COALESCE(cmo.Random, ctm.Random),"
"COALESCE(cmo.InteractionPauseTimer, ctm.InteractionPauseTimer) "
"FROM creature_movement_override AS cmo "
"LEFT JOIN creature AS c ON c.guid = cmo.SpawnId "
"LEFT JOIN creature_template_movement AS ctm ON ctm.CreatureId = c.id");
if (!result)
{
LOG_INFO("server.loading", ">> Loaded 0 creature movement overrides. DB table `creature_movement_override` is empty!");
return;
}
do
{
Field* fields = result->Fetch();
ObjectGuid::LowType spawnId = fields[0].GetUInt32();
if (!GetCreatureData(spawnId))
{
LOG_ERROR("sql.sql", "Creature (GUID: %u) does not exist but has a record in `creature_movement_override`", spawnId);
continue;
}
CreatureMovementData& movement = _creatureMovementOverrides[spawnId];
if (!fields[1].IsNull())
{
movement.Ground = static_cast<CreatureGroundMovementType>(fields[1].GetUInt8());
}
if (!fields[2].IsNull())
{
movement.Swim = fields[2].GetBool();
}
if (!fields[3].IsNull())
{
movement.Flight = static_cast<CreatureFlightMovementType>(fields[3].GetUInt8());
}
if (!fields[4].IsNull())
{
movement.Rooted = fields[4].GetBool();
}
if (!fields[5].IsNull())
{
movement.Chase = static_cast<CreatureChaseMovementType>(fields[5].GetUInt8());
}
if (!fields[6].IsNull())
{
movement.Random = static_cast<CreatureRandomMovementType>(fields[6].GetUInt8());
}
if (!fields[7].IsNull())
{
movement.InteractionPauseTimer = fields[7].GetUInt32();
}
CheckCreatureMovement("creature_movement_override", spawnId, movement);
} while (result->NextRow());
LOG_INFO("server.loading", ">> Loaded " SZFMTD " movement overrides in %u ms", _creatureMovementOverrides.size(), GetMSTimeDiffToNow(oldMSTime));
}
CreatureModelInfo const* ObjectMgr::GetCreatureModelInfo(uint32 modelId)
{
CreatureModelContainer::const_iterator itr = _creatureModelStore.find(modelId);

View file

@ -749,6 +749,7 @@ public:
GameObjectAddon const* GetGameObjectAddon(ObjectGuid::LowType lowguid);
[[nodiscard]] GameObjectTemplateAddon const* GetGameObjectTemplateAddon(uint32 entry) const;
CreatureAddon const* GetCreatureTemplateAddon(uint32 entry);
CreatureMovementData const* GetCreatureMovementOverride(ObjectGuid::LowType spawnId) const;
ItemTemplate const* GetItemTemplate(uint32 entry);
[[nodiscard]] ItemTemplateContainer const* GetItemTemplateStore() const { return &_itemTemplateStore; }
[[nodiscard]] std::vector<ItemTemplate*> const* GetItemTemplateStoreFast() const { return &_itemTemplateStoreFast; }
@ -989,6 +990,7 @@ public:
void LoadCreatureTemplateResistances();
void LoadCreatureTemplateSpells();
void CheckCreatureTemplate(CreatureTemplate const* cInfo);
void CheckCreatureMovement(char const* table, uint64 id, CreatureMovementData& creatureMovement);
void LoadGameObjectQuestItems();
void LoadCreatureQuestItems();
void LoadTempSummons();
@ -999,6 +1001,7 @@ public:
void LoadGameObjectAddons();
void LoadCreatureModelInfo();
void LoadEquipmentTemplates();
void LoadCreatureMovementOverrides();
void LoadGameObjectLocales();
void LoadGameobjects();
void LoadItemTemplates();
@ -1491,6 +1494,7 @@ private:
CreatureModelContainer _creatureModelStore;
CreatureAddonContainer _creatureAddonStore;
CreatureAddonContainer _creatureTemplateAddonStore;
std::unordered_map<ObjectGuid::LowType, CreatureMovementData> _creatureMovementOverrides;
GameObjectAddonContainer _gameObjectAddonStore;
GameObjectQuestItemMap _gameObjectQuestItemStore;
CreatureQuestItemMap _creatureQuestItemStore;

View file

@ -44,7 +44,9 @@ void WorldSession::HandleBattlemasterHelloOpcode(WorldPacket& recvData)
return;
// Stop the npc if moving
unit->StopMoving();
if (uint32 pause = unit->GetMovementTemplate().GetInteractionPauseTimer())
unit->PauseMovement(pause);
unit->SetHomePosition(unit->GetPosition());
BattlegroundTypeId bgTypeId = sBattlegroundMgr->GetBattleMasterBG(unit->GetEntry());

View file

@ -1001,10 +1001,9 @@ void WorldSession::SendListInventory(ObjectGuid vendorGuid, uint32 vendorEntry)
}
// Stop the npc if moving
if (vendor->HasUnitState(UNIT_STATE_MOVING))
{
vendor->StopMoving();
}
if (uint32 pause = vendor->GetMovementTemplate().GetInteractionPauseTimer())
vendor->PauseMovement(pause);
vendor->SetHomePosition(vendor->GetPosition());
SetCurrentVendor(vendorEntry);

View file

@ -491,6 +491,13 @@ void WorldSession::HandleMovementOpcodes(WorldPacket& recvData)
}
}
if (plrMover && ((movementInfo.flags & MOVEMENTFLAG_SWIMMING) != 0) != plrMover->IsInWater())
{
// now client not include swimming flag in case jumping under water
plrMover->SetInWater(!plrMover->IsInWater() || plrMover->GetMap()->IsUnderWater(plrMover->GetPhaseMask(), movementInfo.pos.GetPositionX(),
movementInfo.pos.GetPositionY(), movementInfo.pos.GetPositionZ(), plrMover->GetCollisionHeight()));
}
bool jumpopcode = false;
if (opcode == MSG_MOVE_JUMP)
{

View file

@ -302,12 +302,10 @@ void WorldSession::HandleGossipHelloOpcode(WorldPacket& recvData)
//if (GetPlayer()->HasUnitState(UNIT_STATE_DIED))
// GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH);
// xinef: and if he has pure gossip or is banker and moves or is tabard designer?
//if (unit->IsArmorer() || unit->IsCivilian() || unit->IsQuestGiver() || unit->IsServiceProvider() || unit->IsGuard())
{
//if (!unit->GetTransport()) // pussywizard: reverted with new spline (old: without this check, npc would stay in place and the transport would continue moving, so the npc falls off. NPCs on transports don't have waypoints, so stopmoving is not needed)
unit->StopMoving();
}
// Stop the npc if moving
if (uint32 pause = unit->GetMovementTemplate().GetInteractionPauseTimer())
unit->PauseMovement(pause);
unit->SetHomePosition(unit->GetPosition());
// If spiritguide, no need for gossip menu, just put player into resurrect queue
if (unit->IsSpiritGuide())

View file

@ -90,9 +90,11 @@ void WorldSession::HandleQuestgiverHelloOpcode(WorldPacket& recvData)
// remove fake death
if (GetPlayer()->HasUnitState(UNIT_STATE_DIED))
GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH);
// Stop the npc if moving
//if (!creature->GetTransport()) // pussywizard: reverted with new spline (old: without this check, npc would stay in place and the transport would continue moving, so the npc falls off. NPCs on transports don't have waypoints, so stopmoving is not needed)
creature->StopMoving();
if (uint32 pause = creature->GetMovementTemplate().GetInteractionPauseTimer())
creature->PauseMovement(pause);
creature->SetHomePosition(creature->GetPosition());
if (sScriptMgr->OnGossipHello(_player, creature))
return;

View file

@ -1395,7 +1395,7 @@ enum AcoreStrings
LANG_BAN_ACCOUNT_YOUBANNEDMESSAGE_WORLD = 11006,
LANG_BAN_ACCOUNT_YOUPERMBANNEDMESSAGE_WORLD = 11007,
LANG_NPCINFO_INHABIT_TYPE = 11008,
LANG_NPCINFO_MOVEMENT_DATA = 11008,
LANG_NPCINFO_FLAGS_EXTRA = 11009,
LANG_INSTANCE_LOGIN_GAMEMASTER_EXCEPTION = 11010,

View file

@ -44,6 +44,11 @@ public:
virtual void unitSpeedChanged() { }
// timer in ms
virtual void Pause(uint32 /* timer = 0*/) {}
// timer in ms
virtual void Resume(uint32 /* overrideTimer = 0*/) {}
// used by Evade code for select point to evade with expected restart default movement
virtual bool GetResetPosition(float& /*x*/, float& /*y*/, float& /*z*/) { return false; }
};

View file

@ -195,18 +195,30 @@ void RandomMovementGenerator<Creature>::_setRandomLocation(Creature* creature)
_currDestPosition.Relocate(finalPoint.x, finalPoint.y, finalPoint.z);
creature->AddUnitState(UNIT_STATE_ROAMING_MOVE);
++_moveCount;
if (roll_chance_i((int32)_moveCount * 25 + 10))
bool walk = true;
switch (creature->GetMovementTemplate().GetRandom())
{
_moveCount = 0;
_nextMoveTime.Reset(urand(4000, 8000));
case CreatureRandomMovementType::CanRun:
walk = creature->IsWalking();
break;
case CreatureRandomMovementType::AlwaysRun:
walk = false;
break;
default:
break;
}
Movement::MoveSplineInit init(creature);
init.MovebyPath(finalPath);
init.SetWalk(true);
init.SetWalk(walk);
init.Launch();
++_moveCount;
if (roll_chance_i((int32) _moveCount * 25 + 10))
{
_moveCount = 0;
_nextMoveTime.Reset(urand(4000, 8000));
}
if (sWorld->getBoolConfig(CONFIG_DONT_CACHE_RANDOM_MOVEMENT_PATHS))
_preComputedPaths.erase(pathIdx);

View file

@ -189,7 +189,18 @@ bool ChaseMovementGenerator<T>::DoUpdate(T* owner, uint32 time_diff)
bool walk = false;
if (cOwner && !cOwner->IsPet())
{
walk = owner->IsWalking();
switch (cOwner->GetMovementTemplate().GetChase())
{
case CreatureChaseMovementType::CanWalk:
if (owner->IsWalking())
walk = true;
break;
case CreatureChaseMovementType::AlwaysWalk:
walk = true;
break;
default:
break;
}
}
owner->AddUnitState(UNIT_STATE_CHASE_MOVE);

View file

@ -217,6 +217,7 @@ enum WorldIntConfigs
CONFIG_CHARACTER_CREATING_DISABLED_CLASSMASK,
CONFIG_CHARACTERS_PER_ACCOUNT,
CONFIG_CHARACTERS_PER_REALM,
CONFIG_CREATURE_STOP_FOR_PLAYER,
CONFIG_HEROIC_CHARACTERS_PER_REALM,
CONFIG_CHARACTER_CREATING_MIN_LEVEL_FOR_HEROIC_CHARACTER,
CONFIG_SKIP_CINEMATICS,

View file

@ -1167,6 +1167,7 @@ void World::LoadConfigSettings(bool reload)
m_bool_configs[CONFIG_ARENA_QUEUE_ANNOUNCER_PLAYERONLY] = sConfigMgr->GetOption<bool> ("Arena.QueueAnnouncer.PlayerOnly", false);
m_bool_configs[CONFIG_OFFHAND_CHECK_AT_SPELL_UNLEARN] = sConfigMgr->GetOption<bool>("OffhandCheckAtSpellUnlearn", true);
m_int_configs[CONFIG_CREATURE_STOP_FOR_PLAYER] = sConfigMgr->GetOption<int32>("Creature.MovingStopTimeForPlayer", 3 * MINUTE * IN_MILLISECONDS);
if (int32 clientCacheId = sConfigMgr->GetOption<int32>("ClientCacheVersion", 0))
{
@ -1688,6 +1689,9 @@ void World::SetInitialWorldSettings()
LOG_INFO("server.loading", "Loading Creature Addon Data...");
sObjectMgr->LoadCreatureAddons(); // must be after LoadCreatureTemplates() and LoadCreatures()
LOG_INFO("server.loading", "Loading Creature Movement Overrides...");
sObjectMgr->LoadCreatureMovementOverrides(); // must be after LoadCreatures()
LOG_INFO("server.loading", "Loading Gameobject Data...");
sObjectMgr->LoadGameobjects();

View file

@ -89,7 +89,8 @@ public:
{ "creature_questender", HandleReloadCreatureQuestEnderCommand, SEC_ADMINISTRATOR, Console::Yes },
{ "creature_linked_respawn", HandleReloadLinkedRespawnCommand, SEC_ADMINISTRATOR, Console::Yes },
{ "creature_loot_template", HandleReloadLootTemplatesCreatureCommand, SEC_ADMINISTRATOR, Console::Yes },
{ "creature_onkill_reputation", HandleReloadOnKillReputationCommand, SEC_ADMINISTRATOR, Console::Yes },
{ "creature_movement_override", HandleReloadCreatureMovementOverrideCommand, SEC_ADMINISTRATOR, Console::Yes},
{ "creature_onkill_reputation", HandleReloadOnKillReputationCommand, SEC_ADMINISTRATOR, Console::Yes },
{ "creature_queststarter", HandleReloadCreatureQuestStarterCommand, SEC_ADMINISTRATOR, Console::Yes },
{ "creature_template", HandleReloadCreatureTemplateCommand, SEC_ADMINISTRATOR, Console::Yes },
{ "disables", HandleReloadDisablesCommand, SEC_ADMINISTRATOR, Console::Yes },
@ -198,6 +199,7 @@ public:
HandleReloadReservedNameCommand(handler);
HandleReloadAcoreStringCommand(handler);
HandleReloadGameTeleCommand(handler);
HandleReloadCreatureMovementOverrideCommand(handler);
HandleReloadVehicleAccessoryCommand(handler);
HandleReloadVehicleTemplateAccessoryCommand(handler);
@ -550,6 +552,14 @@ public:
return true;
}
static bool HandleReloadCreatureMovementOverrideCommand(ChatHandler* handler)
{
LOG_INFO("server.loading", "Re-Loading Creature movement overrides...");
sObjectMgr->LoadCreatureMovementOverrides();
handler->SendGlobalGMSysMessage("DB table `creature_movement_override` reloaded.");
return true;
}
static bool HandleReloadLootTemplatesDisenchantCommand(ChatHandler* handler)
{
LOG_INFO("server.loading", "Re-Loading Loot Tables... (`disenchant_loot_template`)");

View file

@ -1758,6 +1758,14 @@ ListenRange.TextEmote = 40
ListenRange.Yell = 300
#
# Creature.MovingStopTimeForPlayer
# Description: Time (in milliseconds) during which creature will not move after
# interaction with player.
# Default: 180000
Creature.MovingStopTimeForPlayer = 180000
# WaypointMovementStopTimeForPlayer
# Description: Specifies the time (in seconds) that a creature with waypoint
# movement will wait after a player interacts with it.