Core/Movement: Replace old TargetedMovementGenerator into ChaseMovementGenerator and FollowMovementGenerator, full rewrite for both.
Port From (https://github.com/TrinityCore/TrinityCore/commit/14939204955d1a24fe465cdfcbf307daf3ce4f09)
This commit is contained in:
@@ -208,7 +208,7 @@ namespace Framework.Constants
|
||||
/// </summary>
|
||||
public const int MaxPetStables = 4;
|
||||
public const float PetFollowDist = 1.0f;
|
||||
public const float PetFollowAngle = MathFunctions.PiOver2;
|
||||
public const float PetFollowAngle = MathF.PI;
|
||||
public const int MaxSpellCharm = 4;
|
||||
public const int ActionBarIndexStart = 0;
|
||||
public const byte ActionBarIndexPetSpellStart = 3;
|
||||
|
||||
@@ -474,7 +474,7 @@ namespace Game.AI
|
||||
ClearCharmInfoFlags();
|
||||
me.GetCharmInfo().SetIsCommandAttack(oldCmdAttack); // For passive pets commanded to attack so they will use spells
|
||||
me.GetMotionMaster().Clear();
|
||||
me.GetMotionMaster().MoveChase(target, me.GetPetChaseDistance());
|
||||
me.GetMotionMaster().MoveChase(target, me.GetPetChaseDistance(), MathF.PI);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -1245,11 +1245,7 @@ namespace Game.Chat
|
||||
break;
|
||||
case MovementGeneratorType.Chase:
|
||||
{
|
||||
Unit target;
|
||||
if (unit.IsTypeId(TypeId.Player))
|
||||
target = ((ChaseMovementGenerator<Player>)movementGenerator).GetTarget();
|
||||
else
|
||||
target = ((ChaseMovementGenerator<Creature>)movementGenerator).GetTarget();
|
||||
Unit target = ((ChaseMovementGenerator)movementGenerator).GetTarget();
|
||||
|
||||
if (!target)
|
||||
handler.SendSysMessage(CypherStrings.MovegensChaseNull);
|
||||
@@ -1261,11 +1257,7 @@ namespace Game.Chat
|
||||
}
|
||||
case MovementGeneratorType.Follow:
|
||||
{
|
||||
Unit target;
|
||||
if (unit.IsTypeId(TypeId.Player))
|
||||
target = ((FollowMovementGenerator<Player>)movementGenerator).GetTarget();
|
||||
else
|
||||
target = ((FollowMovementGenerator<Creature>)movementGenerator).GetTarget();
|
||||
Unit target = ((FollowMovementGenerator)movementGenerator).GetTarget();
|
||||
|
||||
if (!target)
|
||||
handler.SendSysMessage(CypherStrings.MovegensFollowNull);
|
||||
|
||||
@@ -982,8 +982,7 @@ namespace Game.Chat
|
||||
return false;
|
||||
}
|
||||
|
||||
FollowMovementGenerator<Creature> mgen = (FollowMovementGenerator<Creature>)creature.GetMotionMaster().Top();
|
||||
|
||||
FollowMovementGenerator mgen = (FollowMovementGenerator)creature.GetMotionMaster().Top();
|
||||
if (mgen.GetTarget() != player)
|
||||
{
|
||||
handler.SendSysMessage(CypherStrings.CreatureNotFollowYou, creature.GetName());
|
||||
|
||||
@@ -235,7 +235,7 @@ namespace Game.Entities
|
||||
return GetAbsoluteAngle(pos.GetPositionX(), pos.GetPositionY());
|
||||
}
|
||||
|
||||
float ToAbsoluteAngle(float relAngle)
|
||||
public float ToAbsoluteAngle(float relAngle)
|
||||
{
|
||||
return NormalizeOrientation(relAngle + Orientation);
|
||||
}
|
||||
|
||||
@@ -1471,14 +1471,16 @@ namespace Game.Entities
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsWithinMeleeRange(Unit obj)
|
||||
public bool IsWithinMeleeRange(Unit obj) { return IsWithinMeleeRangeAt(GetPosition(), obj); }
|
||||
|
||||
public bool IsWithinMeleeRangeAt(Position pos, Unit obj)
|
||||
{
|
||||
if (!obj || !IsInMap(obj) || !IsInPhase(obj))
|
||||
return false;
|
||||
|
||||
float dx = GetPositionX() - obj.GetPositionX();
|
||||
float dy = GetPositionY() - obj.GetPositionY();
|
||||
float dz = GetPositionZ() - obj.GetPositionZ();
|
||||
float dx = pos.GetPositionX() - obj.GetPositionX();
|
||||
float dy = pos.GetPositionY() - obj.GetPositionY();
|
||||
float dz = pos.GetPositionZ() - obj.GetPositionZ();
|
||||
float distsq = (dx * dx) + (dy * dy) + (dz * dz);
|
||||
|
||||
float maxdist = GetMeleeRange(obj) + GetTotalAuraModifier(AuraType.ModAutoAttackRange);
|
||||
|
||||
@@ -41,7 +41,7 @@ namespace Game.Entities
|
||||
|
||||
//Movement
|
||||
protected float[] m_speed_rate = new float[(int)UnitMoveType.Max];
|
||||
RefManager<Unit, ITargetedMovementGeneratorBase> m_FollowingRefManager;
|
||||
List<AbstractFollower> m_followingMe = new();
|
||||
public MoveSpline MoveSpline { get; set; }
|
||||
MotionMaster i_motionMaster;
|
||||
public uint m_movementCounter; //< Incrementing counter used in movement packets
|
||||
|
||||
@@ -406,57 +406,57 @@ namespace Game.Entities
|
||||
case UnitMoveType.Walk:
|
||||
return;
|
||||
case UnitMoveType.Run:
|
||||
{
|
||||
if (IsMounted()) // Use on mount auras
|
||||
{
|
||||
if (IsMounted()) // Use on mount auras
|
||||
{
|
||||
main_speed_mod = GetMaxPositiveAuraModifier(AuraType.ModIncreaseMountedSpeed);
|
||||
stack_bonus = GetTotalAuraMultiplier(AuraType.ModMountedSpeedAlways);
|
||||
non_stack_bonus += GetMaxPositiveAuraModifier(AuraType.ModMountedSpeedNotStack) / 100.0f;
|
||||
}
|
||||
else
|
||||
{
|
||||
main_speed_mod = GetMaxPositiveAuraModifier(AuraType.ModIncreaseSpeed);
|
||||
stack_bonus = GetTotalAuraMultiplier(AuraType.ModSpeedAlways);
|
||||
non_stack_bonus += GetMaxPositiveAuraModifier(AuraType.ModSpeedNotStack) / 100.0f;
|
||||
}
|
||||
break;
|
||||
main_speed_mod = GetMaxPositiveAuraModifier(AuraType.ModIncreaseMountedSpeed);
|
||||
stack_bonus = GetTotalAuraMultiplier(AuraType.ModMountedSpeedAlways);
|
||||
non_stack_bonus += GetMaxPositiveAuraModifier(AuraType.ModMountedSpeedNotStack) / 100.0f;
|
||||
}
|
||||
else
|
||||
{
|
||||
main_speed_mod = GetMaxPositiveAuraModifier(AuraType.ModIncreaseSpeed);
|
||||
stack_bonus = GetTotalAuraMultiplier(AuraType.ModSpeedAlways);
|
||||
non_stack_bonus += GetMaxPositiveAuraModifier(AuraType.ModSpeedNotStack) / 100.0f;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case UnitMoveType.Swim:
|
||||
{
|
||||
main_speed_mod = GetMaxPositiveAuraModifier(AuraType.ModIncreaseSwimSpeed);
|
||||
break;
|
||||
}
|
||||
{
|
||||
main_speed_mod = GetMaxPositiveAuraModifier(AuraType.ModIncreaseSwimSpeed);
|
||||
break;
|
||||
}
|
||||
case UnitMoveType.Flight:
|
||||
{
|
||||
if (IsTypeId(TypeId.Unit) && IsControlledByPlayer()) // not sure if good for pet
|
||||
{
|
||||
if (IsTypeId(TypeId.Unit) && IsControlledByPlayer()) // not sure if good for pet
|
||||
{
|
||||
main_speed_mod = GetMaxPositiveAuraModifier(AuraType.ModIncreaseVehicleFlightSpeed);
|
||||
stack_bonus = GetTotalAuraMultiplier(AuraType.ModVehicleSpeedAlways);
|
||||
main_speed_mod = GetMaxPositiveAuraModifier(AuraType.ModIncreaseVehicleFlightSpeed);
|
||||
stack_bonus = GetTotalAuraMultiplier(AuraType.ModVehicleSpeedAlways);
|
||||
|
||||
// for some spells this mod is applied on vehicle owner
|
||||
int owner_speed_mod = 0;
|
||||
// for some spells this mod is applied on vehicle owner
|
||||
int owner_speed_mod = 0;
|
||||
|
||||
Unit owner = GetCharmer();
|
||||
if (owner != null)
|
||||
owner_speed_mod = owner.GetMaxPositiveAuraModifier(AuraType.ModIncreaseVehicleFlightSpeed);
|
||||
Unit owner = GetCharmer();
|
||||
if (owner != null)
|
||||
owner_speed_mod = owner.GetMaxPositiveAuraModifier(AuraType.ModIncreaseVehicleFlightSpeed);
|
||||
|
||||
main_speed_mod = Math.Max(main_speed_mod, owner_speed_mod);
|
||||
}
|
||||
else if (IsMounted())
|
||||
{
|
||||
main_speed_mod = GetMaxPositiveAuraModifier(AuraType.ModIncreaseMountedFlightSpeed);
|
||||
stack_bonus = GetTotalAuraMultiplier(AuraType.ModMountedFlightSpeedAlways);
|
||||
}
|
||||
else // Use not mount (shapeshift for example) auras (should stack)
|
||||
main_speed_mod = GetTotalAuraModifier(AuraType.ModIncreaseFlightSpeed) + GetTotalAuraModifier(AuraType.ModIncreaseVehicleFlightSpeed);
|
||||
|
||||
non_stack_bonus += GetMaxPositiveAuraModifier(AuraType.ModFlightSpeedNotStack) / 100.0f;
|
||||
|
||||
// Update speed for vehicle if available
|
||||
if (IsTypeId(TypeId.Player) && GetVehicle() != null)
|
||||
GetVehicleBase().UpdateSpeed(UnitMoveType.Flight);
|
||||
break;
|
||||
main_speed_mod = Math.Max(main_speed_mod, owner_speed_mod);
|
||||
}
|
||||
else if (IsMounted())
|
||||
{
|
||||
main_speed_mod = GetMaxPositiveAuraModifier(AuraType.ModIncreaseMountedFlightSpeed);
|
||||
stack_bonus = GetTotalAuraMultiplier(AuraType.ModMountedFlightSpeedAlways);
|
||||
}
|
||||
else // Use not mount (shapeshift for example) auras (should stack)
|
||||
main_speed_mod = GetTotalAuraModifier(AuraType.ModIncreaseFlightSpeed) + GetTotalAuraModifier(AuraType.ModIncreaseVehicleFlightSpeed);
|
||||
|
||||
non_stack_bonus += GetMaxPositiveAuraModifier(AuraType.ModFlightSpeedNotStack) / 100.0f;
|
||||
|
||||
// Update speed for vehicle if available
|
||||
if (IsTypeId(TypeId.Player) && GetVehicle() != null)
|
||||
GetVehicleBase().UpdateSpeed(UnitMoveType.Flight);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
Log.outError(LogFilter.Unit, "Unit.UpdateSpeed: Unsupported move type ({0})", mtype);
|
||||
return;
|
||||
@@ -472,71 +472,70 @@ namespace Game.Entities
|
||||
case UnitMoveType.Run:
|
||||
case UnitMoveType.Swim:
|
||||
case UnitMoveType.Flight:
|
||||
{
|
||||
// Set creature speed rate
|
||||
if (IsTypeId(TypeId.Unit))
|
||||
speed *= ToCreature().GetCreatureTemplate().SpeedRun; // 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
|
||||
int normalization = GetMaxPositiveAuraModifier(AuraType.UseNormalMovementSpeed);
|
||||
if (normalization != 0)
|
||||
{
|
||||
// Set creature speed rate
|
||||
if (IsTypeId(TypeId.Unit))
|
||||
Creature creature1 = ToCreature();
|
||||
if (creature1)
|
||||
{
|
||||
Unit pOwner = GetCharmerOrOwner();
|
||||
if ((IsPet() || IsGuardian()) && !IsInCombat() && pOwner != null) // Must check for owner or crash on "Tame Beast"
|
||||
{
|
||||
// For every yard over 5, increase speed by 0.01
|
||||
// to help prevent pet from lagging behind and despawning
|
||||
float dist = GetDistance(pOwner);
|
||||
float base_rate = 1.00f; // base speed is 100% of owner speed
|
||||
|
||||
if (dist < 5)
|
||||
dist = 5;
|
||||
|
||||
float mult = base_rate + ((dist - 5) * 0.01f);
|
||||
|
||||
speed *= pOwner.GetSpeedRate(mtype) * mult; // pets derive speed from owner when not in combat
|
||||
}
|
||||
else
|
||||
speed *= ToCreature().GetCreatureTemplate().SpeedRun; // at this point, MOVE_WALK is never reached
|
||||
uint immuneMask = creature1.GetCreatureTemplate().MechanicImmuneMask;
|
||||
if (Convert.ToBoolean(immuneMask & (1 << ((int)Mechanics.Snare - 1))) || Convert.ToBoolean(immuneMask & (1 << ((int)Mechanics.Daze - 1))))
|
||||
break;
|
||||
}
|
||||
|
||||
// Normalize speed by 191 aura SPELL_AURA_USE_NORMAL_MOVEMENT_SPEED if need
|
||||
// @todo possible affect only on MOVE_RUN
|
||||
int normalization = GetMaxPositiveAuraModifier(AuraType.UseNormalMovementSpeed);
|
||||
if (normalization != 0)
|
||||
{
|
||||
Creature creature = ToCreature();
|
||||
if (creature)
|
||||
{
|
||||
uint immuneMask = creature.GetCreatureTemplate().MechanicImmuneMask;
|
||||
if (Convert.ToBoolean(immuneMask & (1 << ((int)Mechanics.Snare - 1))) || Convert.ToBoolean(immuneMask & (1 << ((int)Mechanics.Daze - 1))))
|
||||
break;
|
||||
}
|
||||
|
||||
// Use speed from aura
|
||||
float max_speed = normalization / (IsControlledByPlayer() ? SharedConst.playerBaseMoveSpeed[(int)mtype] : SharedConst.baseMoveSpeed[(int)mtype]);
|
||||
if (speed > max_speed)
|
||||
speed = max_speed;
|
||||
}
|
||||
|
||||
if (mtype == UnitMoveType.Run)
|
||||
{
|
||||
// force minimum speed rate @ aura 437 SPELL_AURA_MOD_MINIMUM_SPEED_RATE
|
||||
int minSpeedMod1 = GetMaxPositiveAuraModifier(AuraType.ModMinimumSpeedRate);
|
||||
if (minSpeedMod1 != 0)
|
||||
{
|
||||
float minSpeed = minSpeedMod1 / (IsControlledByPlayer() ? SharedConst.playerBaseMoveSpeed[(int)mtype] : SharedConst.baseMoveSpeed[(int)mtype]);
|
||||
if (speed < minSpeed)
|
||||
speed = minSpeed;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
// Use speed from aura
|
||||
float max_speed = normalization / (IsControlledByPlayer() ? SharedConst.playerBaseMoveSpeed[(int)mtype] : SharedConst.baseMoveSpeed[(int)mtype]);
|
||||
if (speed > max_speed)
|
||||
speed = max_speed;
|
||||
}
|
||||
|
||||
if (mtype == UnitMoveType.Run)
|
||||
{
|
||||
// force minimum speed rate @ aura 437 SPELL_AURA_MOD_MINIMUM_SPEED_RATE
|
||||
int minSpeedMod1 = GetMaxPositiveAuraModifier(AuraType.ModMinimumSpeedRate);
|
||||
if (minSpeedMod1 != 0)
|
||||
{
|
||||
float minSpeed = minSpeedMod1 / (IsControlledByPlayer() ? SharedConst.playerBaseMoveSpeed[(int)mtype] : SharedConst.baseMoveSpeed[(int)mtype]);
|
||||
if (speed < minSpeed)
|
||||
speed = minSpeed;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// for creature case, we check explicit if mob searched for assistance
|
||||
if (IsTypeId(TypeId.Unit))
|
||||
Creature creature = ToCreature();
|
||||
if (creature != null)
|
||||
{
|
||||
// for creature case, we check explicit if mob searched for assistance
|
||||
if (ToCreature().HasSearchedAssistance())
|
||||
speed *= 0.66f; // best guessed value, so this will be 33% reduction. Based off initial speed, mob can then "run", "walk fast" or "walk".
|
||||
|
||||
if (creature.HasUnitTypeMask(UnitTypeMask.Minion) && !creature.IsInCombat())
|
||||
{
|
||||
var top = creature.GetMotionMaster().Top();
|
||||
if (top != null && top.GetMovementGeneratorType() == MovementGeneratorType.Follow)
|
||||
{
|
||||
Unit followed = ((AbstractFollower)top).GetTarget();
|
||||
if (followed != null && followed.GetGUID() == GetOwnerGUID() && !followed.IsInCombat())
|
||||
{
|
||||
float ownerSpeed = followed.GetSpeedRate(mtype);
|
||||
if (speed < ownerSpeed || creature.IsWithinDist3d(followed, 10.0f))
|
||||
speed = ownerSpeed;
|
||||
speed *= Math.Min(Math.Max(1.0f, 0.75f + (GetDistance(followed) - SharedConst.PetFollowDist) * 0.05f), 1.3f);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Apply strongest slow aura mod to speed
|
||||
|
||||
@@ -44,7 +44,6 @@ namespace Game.Entities
|
||||
m_combatManager = new CombatManager(this);
|
||||
m_threatManager = new ThreatManager(this);
|
||||
_spellHistory = new SpellHistory(this);
|
||||
m_FollowingRefManager = new RefManager<Unit, ITargetedMovementGeneratorBase>();
|
||||
|
||||
ObjectTypeId = TypeId.Unit;
|
||||
ObjectTypeMask |= TypeMask.Unit;
|
||||
@@ -454,6 +453,8 @@ namespace Game.Entities
|
||||
|
||||
RemoveAreaAurasDueToLeaveWorld();
|
||||
|
||||
RemoveAllFollowers();
|
||||
|
||||
if (!GetCharmerGUID().IsEmpty())
|
||||
{
|
||||
Log.outFatal(LogFilter.Unit, "Unit {0} has charmer guid when removed from world", GetEntry());
|
||||
@@ -1312,6 +1313,13 @@ namespace Game.Entities
|
||||
|
||||
public Totem ToTotem() { return IsTotem() ? (this as Totem) : null; }
|
||||
public TempSummon ToTempSummon() { return IsSummon() ? (this as TempSummon) : null; }
|
||||
|
||||
void RemoveAllFollowers()
|
||||
{
|
||||
while (!m_followingMe.Empty())
|
||||
m_followingMe[0].SetTarget(null);
|
||||
}
|
||||
|
||||
public virtual void SetDeathState(DeathState s)
|
||||
{
|
||||
// Death state needs to be updated before RemoveAllAurasOnDeath() is called, to prevent entering combat
|
||||
@@ -1563,11 +1571,8 @@ namespace Game.Entities
|
||||
|
||||
public bool IsCharmedOwnedByPlayerOrPlayer() { return GetCharmerOrOwnerOrOwnGUID().IsPlayer(); }
|
||||
|
||||
public void AddFollower(FollowerReference pRef)
|
||||
{
|
||||
m_FollowingRefManager.InsertFirst(pRef);
|
||||
}
|
||||
public void RemoveFollower(FollowerReference pRef) { } //nothing to do yet
|
||||
public void FollowerAdded(AbstractFollower f) { m_followingMe.Add(f); }
|
||||
public void FollowerRemoved(AbstractFollower f) { m_followingMe.Remove(f); }
|
||||
|
||||
public uint GetCreatureTypeMask()
|
||||
{
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright (C) 2012-2020 CypherCore <http://github.com/CypherCore>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Game.Entities;
|
||||
|
||||
namespace Game.Movement
|
||||
{
|
||||
public class AbstractFollower
|
||||
{
|
||||
Unit _target;
|
||||
|
||||
public AbstractFollower(Unit target = null) { SetTarget(target); }
|
||||
|
||||
public void SetTarget(Unit unit)
|
||||
{
|
||||
if (unit == _target)
|
||||
return;
|
||||
|
||||
if (_target)
|
||||
_target.FollowerRemoved(this);
|
||||
|
||||
_target = unit;
|
||||
|
||||
if (_target)
|
||||
_target.FollowerAdded(this);
|
||||
}
|
||||
|
||||
public Unit GetTarget() { return _target; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,214 @@
|
||||
/*
|
||||
* Copyright (C) 2012-2020 CypherCore <http://github.com/CypherCore>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
using Framework.Constants;
|
||||
using Game.Entities;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Framework.GameMath;
|
||||
|
||||
namespace Game.Movement
|
||||
{
|
||||
class ChaseMovementGenerator : AbstractFollower, IMovementGenerator
|
||||
{
|
||||
static uint RANGE_CHECK_INTERVAL = 100; // time (ms) until we attempt to recalculate
|
||||
|
||||
ChaseRange? _range;
|
||||
ChaseAngle? _angle;
|
||||
|
||||
PathGenerator _path;
|
||||
Position _lastTargetPosition;
|
||||
uint _rangeCheckTimer = RANGE_CHECK_INTERVAL;
|
||||
bool _movingTowards = true;
|
||||
bool _mutualChase = true;
|
||||
|
||||
public ChaseMovementGenerator(Unit target, ChaseRange? range, ChaseAngle? angle) : base(target)
|
||||
{
|
||||
_range = range;
|
||||
_angle = angle;
|
||||
}
|
||||
|
||||
public void Initialize(Unit owner)
|
||||
{
|
||||
owner.AddUnitState(UnitState.Chase);
|
||||
owner.SetWalk(false);
|
||||
}
|
||||
|
||||
public bool Update(Unit owner, uint diff)
|
||||
{
|
||||
// owner might be dead or gone (can we even get nullptr here?)
|
||||
if (!owner || !owner.IsAlive())
|
||||
return false;
|
||||
|
||||
// our target might have gone away
|
||||
Unit target = GetTarget();
|
||||
if (!target)
|
||||
return false;
|
||||
|
||||
// the owner might've selected a different target (feels like we shouldn't check this here...)
|
||||
if (owner.GetVictim() != target)
|
||||
return false;
|
||||
|
||||
// the owner might be unable to move (rooted or casting), pause movement
|
||||
if (owner.HasUnitState(UnitState.NotMove) || owner.IsMovementPreventedByCasting())
|
||||
{
|
||||
owner.StopMoving();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool mutualChase = IsMutualChase(owner, target);
|
||||
float hitboxSum = owner.GetCombatReach() + target.GetCombatReach();
|
||||
float minRange = _range.HasValue ? _range.Value.MinRange + hitboxSum : SharedConst.ContactDistance;
|
||||
float minTarget = (_range.HasValue ? _range.Value.MinTolerance : 0.0f) + hitboxSum;
|
||||
float maxRange = _range.HasValue ? _range.Value.MaxRange + hitboxSum : owner.GetMeleeRange(target); // melee range already includes hitboxes
|
||||
float maxTarget = _range.HasValue ? _range.Value.MaxTolerance + hitboxSum : SharedConst.ContactDistance + hitboxSum;
|
||||
ChaseAngle? angle = mutualChase ? null : _angle;
|
||||
|
||||
// if we're already moving, periodically check if we're already in the expected range...
|
||||
if (owner.HasUnitState(UnitState.ChaseMove))
|
||||
{
|
||||
if (_rangeCheckTimer > diff)
|
||||
_rangeCheckTimer -= diff;
|
||||
else
|
||||
{
|
||||
_rangeCheckTimer = RANGE_CHECK_INTERVAL;
|
||||
if (PositionOkay(owner, target, _movingTowards ? null : minTarget, _movingTowards ? maxTarget : null, angle))
|
||||
{
|
||||
_path = null;
|
||||
owner.StopMoving();
|
||||
owner.SetInFront(target);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if we're done moving, we want to clean up
|
||||
if (owner.HasUnitState(UnitState.ChaseMove) && owner.MoveSpline.Finalized())
|
||||
{
|
||||
_path = null;
|
||||
owner.ClearUnitState(UnitState.ChaseMove);
|
||||
owner.SetInFront(target);
|
||||
}
|
||||
|
||||
// if the target moved, we have to consider whether to adjust
|
||||
if (_lastTargetPosition != target.GetPosition() || mutualChase != _mutualChase)
|
||||
{
|
||||
_lastTargetPosition = target.GetPosition();
|
||||
_mutualChase = mutualChase;
|
||||
if (owner.HasUnitState(UnitState.ChaseMove) || !PositionOkay(owner, target, minRange, maxRange, angle))
|
||||
{
|
||||
Creature cOwner = owner.ToCreature();
|
||||
// can we get to the target?
|
||||
if (cOwner != null && !target.IsInAccessiblePlaceFor(cOwner))
|
||||
{
|
||||
cOwner.SetCannotReachTarget(true);
|
||||
cOwner.StopMoving();
|
||||
_path = null;
|
||||
return true;
|
||||
}
|
||||
|
||||
// figure out which way we want to move
|
||||
bool moveToward = !owner.IsInDist(target, maxRange);
|
||||
|
||||
// make a new path if we have to...
|
||||
if (_path == null || moveToward != _movingTowards)
|
||||
_path = new PathGenerator(owner);
|
||||
|
||||
float x, y, z;
|
||||
bool shortenPath;
|
||||
// if we want to move toward the target and there's no fixed angle...
|
||||
if (moveToward && !angle.HasValue)
|
||||
{
|
||||
// ...we'll pathfind to the center, then shorten the path
|
||||
target.GetPosition(out x, out y, out z);
|
||||
shortenPath = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// otherwise, we fall back to nearpoint finding
|
||||
target.GetNearPoint(owner, out x, out y, out z, (moveToward ? maxTarget : minTarget) - hitboxSum, angle.HasValue ? target.ToAbsoluteAngle(angle.Value.RelativeAngle) : target.GetAbsoluteAngle(owner));
|
||||
shortenPath = false;
|
||||
}
|
||||
|
||||
if (owner.IsHovering())
|
||||
owner.UpdateAllowedPositionZ(x, y, ref z);
|
||||
|
||||
bool success = _path.CalculatePath(x, y, z);
|
||||
if (!success || _path.GetPathType().HasFlag(PathType.NoPath))
|
||||
{
|
||||
if (cOwner)
|
||||
cOwner.SetCannotReachTarget(true);
|
||||
owner.StopMoving();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (shortenPath)
|
||||
_path.ShortenPathUntilDist(target, maxTarget);
|
||||
|
||||
if (cOwner)
|
||||
cOwner.SetCannotReachTarget(false);
|
||||
owner.AddUnitState(UnitState.ChaseMove);
|
||||
|
||||
MoveSplineInit init = new(owner);
|
||||
init.MovebyPath(_path.GetPath());
|
||||
init.SetWalk(false);
|
||||
init.SetFacing(target);
|
||||
|
||||
init.Launch();
|
||||
}
|
||||
}
|
||||
|
||||
// and then, finally, we're done for the tick
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Finalize(Unit owner)
|
||||
{
|
||||
owner.ClearUnitState(UnitState.Chase | UnitState.ChaseMove);
|
||||
Creature cOwner = owner.ToCreature();
|
||||
if (cOwner != null)
|
||||
cOwner.SetCannotReachTarget(false);
|
||||
}
|
||||
|
||||
public MovementGeneratorType GetMovementGeneratorType() { return MovementGeneratorType.Chase; }
|
||||
|
||||
public void Reset(Unit owner) { Initialize(owner); }
|
||||
|
||||
public void UnitSpeedChanged() { _lastTargetPosition.Relocate(0.0f, 0.0f, 0.0f); }
|
||||
|
||||
static bool IsMutualChase(Unit owner, Unit target)
|
||||
{
|
||||
IMovementGenerator gen = target.GetMotionMaster().Top();
|
||||
if (gen == null || gen.GetMovementGeneratorType() != MovementGeneratorType.Chase)
|
||||
return false;
|
||||
return ((ChaseMovementGenerator)gen).GetTarget() == owner;
|
||||
}
|
||||
|
||||
static bool PositionOkay(Unit owner, Unit target, float? minDistance, float? maxDistance, ChaseAngle? angle)
|
||||
{
|
||||
float distSq = owner.GetExactDistSq(target);
|
||||
if (minDistance.HasValue && distSq < MathF.Sqrt(minDistance.Value))
|
||||
return false;
|
||||
if (maxDistance.HasValue && distSq > MathF.Sqrt(maxDistance.Value))
|
||||
return false;
|
||||
return !angle.HasValue || angle.Value.IsAngleOkay(target.GetRelativeAngle(owner));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,178 @@
|
||||
/*
|
||||
* Copyright (C) 2012-2020 CypherCore <http://github.com/CypherCore>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
using Framework.Constants;
|
||||
using Framework.GameMath;
|
||||
using Game.Entities;
|
||||
using System;
|
||||
|
||||
namespace Game.Movement
|
||||
{
|
||||
public class FollowMovementGenerator : AbstractFollower, IMovementGenerator
|
||||
{
|
||||
static uint CHECK_INTERVAL = 500;
|
||||
static float FOLLOW_RANGE_TOLERANCE = 1.0f;
|
||||
|
||||
float _range;
|
||||
ChaseAngle _angle;
|
||||
|
||||
uint _checkTimer = CHECK_INTERVAL;
|
||||
PathGenerator _path;
|
||||
Position _lastTargetPosition;
|
||||
|
||||
public FollowMovementGenerator(Unit target, float range, ChaseAngle angle) : base(target)
|
||||
{
|
||||
_range = range;
|
||||
_angle = angle;
|
||||
}
|
||||
|
||||
public void Initialize(Unit owner)
|
||||
{
|
||||
owner.AddUnitState(UnitState.Follow);
|
||||
UpdatePetSpeed(owner);
|
||||
}
|
||||
|
||||
public bool Update(Unit owner, uint diff)
|
||||
{
|
||||
// owner might be dead or gone
|
||||
if (!owner.IsAlive())
|
||||
return false;
|
||||
|
||||
// our target might have gone away
|
||||
Unit target = GetTarget();
|
||||
if (target == null)
|
||||
return false;
|
||||
|
||||
if (owner.HasUnitState(UnitState.NotMove) || owner.IsMovementPreventedByCasting())
|
||||
{
|
||||
owner.StopMoving();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (owner.HasUnitState(UnitState.FollowMove))
|
||||
{
|
||||
if (_checkTimer > diff)
|
||||
_checkTimer -= diff;
|
||||
else
|
||||
{
|
||||
_checkTimer = CHECK_INTERVAL;
|
||||
if (PositionOkay(owner, target, _range, _angle))
|
||||
{
|
||||
_path = null;
|
||||
owner.StopMoving();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (owner.HasUnitState(UnitState.FollowMove) && owner.MoveSpline.Finalized())
|
||||
{
|
||||
_path = null;
|
||||
owner.ClearUnitState(UnitState.FollowMove);
|
||||
}
|
||||
|
||||
if (_lastTargetPosition.GetExactDistSq(target.GetPosition()) > 0.0f)
|
||||
{
|
||||
_lastTargetPosition = target.GetPosition();
|
||||
if (owner.HasUnitState(UnitState.FollowMove) || !PositionOkay(owner, target, _range + FOLLOW_RANGE_TOLERANCE))
|
||||
{
|
||||
if (_path == null)
|
||||
_path = new PathGenerator(owner);
|
||||
|
||||
float x, y, z;
|
||||
|
||||
// select angle
|
||||
float tAngle;
|
||||
float curAngle = target.GetRelativeAngle(owner);
|
||||
if (_angle.IsAngleOkay(curAngle))
|
||||
tAngle = curAngle;
|
||||
else
|
||||
{
|
||||
float diffUpper = Position.NormalizeOrientation(curAngle - _angle.UpperBound());
|
||||
float diffLower = Position.NormalizeOrientation(_angle.LowerBound() - curAngle);
|
||||
if (diffUpper < diffLower)
|
||||
tAngle = _angle.UpperBound();
|
||||
else
|
||||
tAngle = _angle.LowerBound();
|
||||
}
|
||||
|
||||
target.GetNearPoint(owner, out x, out y, out z, _range, target.ToAbsoluteAngle(tAngle));
|
||||
|
||||
if (owner.IsHovering())
|
||||
owner.UpdateAllowedPositionZ(x, y, ref z);
|
||||
|
||||
// pets are allowed to "cheat" on pathfinding when following their master
|
||||
bool allowShortcut = false;
|
||||
Pet oPet = owner.ToPet();
|
||||
if (oPet != null)
|
||||
if (target.GetGUID() == oPet.GetOwnerGUID())
|
||||
allowShortcut = true;
|
||||
|
||||
bool success = _path.CalculatePath(x, y, z, allowShortcut);
|
||||
if (!success || _path.GetPathType().HasFlag(PathType.NoPath))
|
||||
{
|
||||
owner.StopMoving();
|
||||
return true;
|
||||
}
|
||||
|
||||
owner.AddUnitState(UnitState.FollowMove);
|
||||
|
||||
MoveSplineInit init = new(owner);
|
||||
init.MovebyPath(_path.GetPath());
|
||||
init.SetWalk(target.IsWalking());
|
||||
init.SetFacing(target.GetOrientation());
|
||||
init.Launch();
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Finalize(Unit owner)
|
||||
{
|
||||
owner.ClearUnitState(UnitState.Follow | UnitState.FollowMove);
|
||||
UpdatePetSpeed(owner);
|
||||
}
|
||||
|
||||
void UpdatePetSpeed(Unit owner)
|
||||
{
|
||||
Pet oPet = owner.ToPet();
|
||||
if (oPet != null)
|
||||
{
|
||||
if (!GetTarget() || GetTarget().GetGUID() == owner.GetOwnerGUID())
|
||||
{
|
||||
oPet.UpdateSpeed(UnitMoveType.Run);
|
||||
oPet.UpdateSpeed(UnitMoveType.Walk);
|
||||
oPet.UpdateSpeed(UnitMoveType.Swim);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Reset(Unit owner) { }
|
||||
|
||||
public MovementGeneratorType GetMovementGeneratorType() { return MovementGeneratorType.Follow; }
|
||||
|
||||
public void UnitSpeedChanged() { _lastTargetPosition.Relocate(0.0f, 0.0f, 0.0f); }
|
||||
|
||||
static bool PositionOkay(Unit owner, Unit target, float range, ChaseAngle? angle = null)
|
||||
{
|
||||
if (owner.GetExactDistSq(target) > MathF.Sqrt(owner.GetCombatReach() + target.GetCombatReach() + range))
|
||||
return false;
|
||||
|
||||
return !angle.HasValue || angle.Value.IsAngleOkay(target.GetRelativeAngle(owner));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -133,7 +133,7 @@ namespace Game.Movement
|
||||
MovementInform(owner);
|
||||
}
|
||||
|
||||
public override void UnitSpeedChanged()
|
||||
public void UnitSpeedChanged()
|
||||
{
|
||||
_recalculateSpeed = true;
|
||||
}
|
||||
|
||||
@@ -22,27 +22,27 @@ namespace Game.Movement
|
||||
{
|
||||
public class IdleMovementGenerator : IMovementGenerator
|
||||
{
|
||||
public override void Initialize(Unit owner)
|
||||
public void Initialize(Unit owner)
|
||||
{
|
||||
Reset(owner);
|
||||
}
|
||||
|
||||
public override void Reset(Unit owner)
|
||||
public void Reset(Unit owner)
|
||||
{
|
||||
if (!owner.IsStopped())
|
||||
owner.StopMoving();
|
||||
}
|
||||
|
||||
public override bool Update(Unit owner, uint diff)
|
||||
public bool Update(Unit owner, uint diff)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public override void Finalize(Unit owner)
|
||||
public void Finalize(Unit owner)
|
||||
{
|
||||
}
|
||||
|
||||
public override MovementGeneratorType GetMovementGeneratorType()
|
||||
public MovementGeneratorType GetMovementGeneratorType()
|
||||
{
|
||||
return MovementGeneratorType.Idle;
|
||||
}
|
||||
@@ -59,7 +59,7 @@ namespace Game.Movement
|
||||
_direction = direction;
|
||||
}
|
||||
|
||||
public override void Initialize(Unit owner)
|
||||
public void Initialize(Unit owner)
|
||||
{
|
||||
if (!owner.IsStopped())
|
||||
owner.StopMoving();
|
||||
@@ -71,9 +71,9 @@ namespace Game.Movement
|
||||
owner.AttackStop();
|
||||
}
|
||||
|
||||
public override void Reset(Unit owner) { }
|
||||
public void Reset(Unit owner) { }
|
||||
|
||||
public override bool Update(Unit owner, uint diff)
|
||||
public bool Update(Unit owner, uint diff)
|
||||
{
|
||||
float angle = owner.GetOrientation();
|
||||
angle += diff * MathFunctions.TwoPi / _maxDuration * (_direction == RotateDirection.Left ? 1.0f : -1.0f);
|
||||
@@ -90,14 +90,14 @@ namespace Game.Movement
|
||||
return true;
|
||||
}
|
||||
|
||||
public override void Finalize(Unit owner)
|
||||
public void Finalize(Unit owner)
|
||||
{
|
||||
owner.ClearUnitState(UnitState.Rotating);
|
||||
if (owner.IsTypeId(TypeId.Unit))
|
||||
owner.ToCreature().GetAI().MovementInform(MovementGeneratorType.Rotate, 0);
|
||||
}
|
||||
|
||||
public override MovementGeneratorType GetMovementGeneratorType() { return MovementGeneratorType.Rotate; }
|
||||
public MovementGeneratorType GetMovementGeneratorType() { return MovementGeneratorType.Rotate; }
|
||||
|
||||
uint _duration;
|
||||
uint _maxDuration;
|
||||
@@ -111,7 +111,7 @@ namespace Game.Movement
|
||||
_timer = timer;
|
||||
}
|
||||
|
||||
public override void Initialize(Unit owner)
|
||||
public void Initialize(Unit owner)
|
||||
{
|
||||
// Distracted creatures stand up if not standing
|
||||
if (!owner.IsStandState())
|
||||
@@ -120,9 +120,9 @@ namespace Game.Movement
|
||||
owner.AddUnitState(UnitState.Distracted);
|
||||
}
|
||||
|
||||
public override void Reset(Unit owner) { }
|
||||
public void Reset(Unit owner) { }
|
||||
|
||||
public override bool Update(Unit owner, uint diff)
|
||||
public bool Update(Unit owner, uint diff)
|
||||
{
|
||||
if (diff > _timer)
|
||||
return false;
|
||||
@@ -131,7 +131,7 @@ namespace Game.Movement
|
||||
return true;
|
||||
}
|
||||
|
||||
public override void Finalize(Unit owner)
|
||||
public virtual void Finalize(Unit owner)
|
||||
{
|
||||
owner.ClearUnitState(UnitState.Distracted);
|
||||
|
||||
@@ -143,7 +143,7 @@ namespace Game.Movement
|
||||
}
|
||||
}
|
||||
|
||||
public override MovementGeneratorType GetMovementGeneratorType() { return MovementGeneratorType.Distract; }
|
||||
public virtual MovementGeneratorType GetMovementGeneratorType() { return MovementGeneratorType.Distract; }
|
||||
|
||||
uint _timer;
|
||||
}
|
||||
|
||||
@@ -18,51 +18,52 @@
|
||||
using Framework.Constants;
|
||||
using Framework.Dynamic;
|
||||
using Game.Entities;
|
||||
using Framework.GameMath;
|
||||
|
||||
namespace Game.Movement
|
||||
{
|
||||
public abstract class IMovementGenerator
|
||||
public interface IMovementGenerator
|
||||
{
|
||||
public abstract void Finalize(Unit owner);
|
||||
public void Finalize(Unit owner);
|
||||
|
||||
public abstract void Initialize(Unit owner);
|
||||
public void Initialize(Unit owner);
|
||||
|
||||
public abstract void Reset(Unit owner);
|
||||
public void Reset(Unit owner);
|
||||
|
||||
public abstract bool Update(Unit owner, uint time_diff);
|
||||
public bool Update(Unit owner, uint time_diff);
|
||||
|
||||
public abstract MovementGeneratorType GetMovementGeneratorType();
|
||||
public MovementGeneratorType GetMovementGeneratorType();
|
||||
|
||||
public virtual void UnitSpeedChanged() { }
|
||||
public void UnitSpeedChanged() { }
|
||||
|
||||
public virtual void Pause(uint timer = 0) { }
|
||||
public void Pause(uint timer = 0) { }
|
||||
|
||||
public virtual void Resume(uint overrideTimer = 0) { }
|
||||
public void Resume(uint overrideTimer = 0) { }
|
||||
|
||||
// used by Evade code for select point to evade with expected restart default movement
|
||||
public virtual bool GetResetPosition(Unit u, out float x, out float y, out float z)
|
||||
public bool GetResetPosition(Unit u, out float x, out float y, out float z)
|
||||
{
|
||||
x = y = z = 0.0f;
|
||||
x = y = z = 0.0f;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public abstract class MovementGeneratorMedium<T> : IMovementGenerator where T : Unit
|
||||
{
|
||||
public override void Initialize(Unit owner)
|
||||
public virtual void Initialize(Unit owner)
|
||||
{
|
||||
DoInitialize((T)owner);
|
||||
IsActive = true;
|
||||
}
|
||||
public override void Finalize(Unit owner)
|
||||
public virtual void Finalize(Unit owner)
|
||||
{
|
||||
DoFinalize((T)owner);
|
||||
}
|
||||
public override void Reset(Unit owner)
|
||||
public virtual void Reset(Unit owner)
|
||||
{
|
||||
DoReset((T)owner);
|
||||
}
|
||||
public override bool Update(Unit owner, uint diff)
|
||||
public virtual bool Update(Unit owner, uint diff)
|
||||
{
|
||||
return DoUpdate((T)owner, diff);
|
||||
}
|
||||
@@ -73,23 +74,7 @@ namespace Game.Movement
|
||||
public abstract void DoFinalize(T owner);
|
||||
public abstract void DoReset(T owner);
|
||||
public abstract bool DoUpdate(T owner, uint diff);
|
||||
}
|
||||
|
||||
public class FollowerReference : Reference<Unit, ITargetedMovementGeneratorBase>
|
||||
{
|
||||
public override void TargetObjectBuildLink()
|
||||
{
|
||||
GetTarget().AddFollower(this);
|
||||
}
|
||||
|
||||
public override void TargetObjectDestroyLink()
|
||||
{
|
||||
GetTarget().RemoveFollower(this);
|
||||
}
|
||||
|
||||
public override void SourceObjectDestroyLink()
|
||||
{
|
||||
GetSource().StopFollowing();
|
||||
}
|
||||
public virtual MovementGeneratorType GetMovementGeneratorType() { return MovementGeneratorType.Max; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -864,43 +864,52 @@ namespace Game.Movement
|
||||
return (p1 - p2).GetLengthSquared();
|
||||
}
|
||||
|
||||
public void ReducePathLenghtByDist(float dist)
|
||||
public void ShortenPathUntilDist(Position pos, float dist) { ShortenPathUntilDist(new Vector3(pos.posX, pos.posY, pos.posZ), dist); }
|
||||
|
||||
public void ShortenPathUntilDist(Vector3 target, float dist)
|
||||
{
|
||||
if (GetPathType() == PathType.Blank)
|
||||
if (GetPathType() == PathType.Blank || _pathPoints.Length < 2)
|
||||
{
|
||||
Log.outError(LogFilter.Maps, "PathGenerator.ReducePathLenghtByDist called before path was built");
|
||||
Log.outError(LogFilter.Maps, "PathGenerator.ReducePathLengthByDist called before path was successfully built");
|
||||
return;
|
||||
}
|
||||
|
||||
if (_pathPoints.Length < 2) // path building failure
|
||||
float distSq = dist * dist;
|
||||
|
||||
// the first point of the path must be outside the specified range
|
||||
// (this should have really been checked by the caller...)
|
||||
if ((_pathPoints[0] - target).GetLengthSquared() < distSq)
|
||||
return;
|
||||
|
||||
int i = _pathPoints.Length;
|
||||
Vector3 nextVec = _pathPoints[--i];
|
||||
while (i > 0)
|
||||
// check if we even need to do anything
|
||||
if ((_pathPoints[0] - target).GetLengthSquared() >= distSq)
|
||||
return;
|
||||
|
||||
int i = _pathPoints.Length - 1;
|
||||
// find the first i s.t.:
|
||||
// - _pathPoints[i] is still too close
|
||||
// - _pathPoints[i-1] is too far away
|
||||
// => the end point is somewhere on the line between the two
|
||||
while (true)
|
||||
{
|
||||
Vector3 currVec = _pathPoints[--i];
|
||||
Vector3 diffVec = (nextVec - currVec);
|
||||
float len = diffVec.GetLength();
|
||||
if (len > dist)
|
||||
// we know that pathPoints[i] is too close already (from the previous iteration)
|
||||
if ((_pathPoints[i - 1] - target).GetLengthSquared() >= distSq)
|
||||
break; // bingo!
|
||||
|
||||
if (--i == 0)
|
||||
{
|
||||
float step = dist / len;
|
||||
// same as nextVec
|
||||
_pathPoints[i + 1] -= diffVec * step;
|
||||
_sourceUnit.UpdateAllowedPositionZ(_pathPoints[i + 1].X, _pathPoints[i + 1].Y, ref _pathPoints[i + 1].Z);
|
||||
Array.Resize(ref _pathPoints, i + 2);
|
||||
break;
|
||||
}
|
||||
else if (i == 0) // at second point
|
||||
{
|
||||
_pathPoints[1] = _pathPoints[0];
|
||||
// no point found that fulfills the condition
|
||||
_pathPoints[0] = _pathPoints[1];
|
||||
Array.Resize(ref _pathPoints, 2);
|
||||
break;
|
||||
return;
|
||||
}
|
||||
|
||||
dist -= len;
|
||||
nextVec = currVec; // we're going backwards
|
||||
}
|
||||
|
||||
// ok, _pathPoints[i] is too close, _pathPoints[i-1] is not, so our target point is somewhere between the two...
|
||||
// ... settle for a guesstimate since i'm not confident in doing trig on every chase motion tick...
|
||||
// (@todo review this)
|
||||
_pathPoints[i] += (_pathPoints[i - 1] - _pathPoints[i]).directionOrZero() * (dist - (_pathPoints[i] - target).GetLength());
|
||||
Array.Resize(ref _pathPoints, i + 1);
|
||||
}
|
||||
|
||||
public bool IsInvalidDestinationZ(Unit target)
|
||||
|
||||
@@ -135,7 +135,7 @@ namespace Game.Movement
|
||||
}
|
||||
}
|
||||
|
||||
public override void UnitSpeedChanged()
|
||||
public void UnitSpeedChanged()
|
||||
{
|
||||
_recalculateSpeed = true;
|
||||
}
|
||||
@@ -183,16 +183,16 @@ namespace Game.Movement
|
||||
_arrivalSpellTargetGuid = arrivalSpellTargetGuid;
|
||||
}
|
||||
|
||||
public override void Initialize(Unit owner) { }
|
||||
public void Initialize(Unit owner) { }
|
||||
|
||||
public override void Reset(Unit owner) { }
|
||||
public void Reset(Unit owner) { }
|
||||
|
||||
public override bool Update(Unit owner, uint diff)
|
||||
public bool Update(Unit owner, uint diff)
|
||||
{
|
||||
return !owner.MoveSpline.Finalized();
|
||||
}
|
||||
|
||||
public override void Finalize(Unit owner)
|
||||
public void Finalize(Unit owner)
|
||||
{
|
||||
MovementInform(owner);
|
||||
}
|
||||
@@ -206,7 +206,7 @@ namespace Game.Movement
|
||||
owner.ToCreature().GetAI().MovementInform(MovementGeneratorType.Effect, _pointId);
|
||||
}
|
||||
|
||||
public override MovementGeneratorType GetMovementGeneratorType() { return MovementGeneratorType.Effect; }
|
||||
public MovementGeneratorType GetMovementGeneratorType() { return MovementGeneratorType.Effect; }
|
||||
|
||||
uint _pointId;
|
||||
uint _arrivalSpellId;
|
||||
|
||||
@@ -75,7 +75,7 @@ namespace Game.Movement
|
||||
}
|
||||
}
|
||||
|
||||
public override void Initialize(Unit me)
|
||||
public void Initialize(Unit me)
|
||||
{
|
||||
if (_chainSize != 0)
|
||||
{
|
||||
@@ -114,7 +114,7 @@ namespace Game.Movement
|
||||
}
|
||||
}
|
||||
|
||||
public override void Finalize(Unit me)
|
||||
public void Finalize(Unit me)
|
||||
{
|
||||
if (!finished)
|
||||
return;
|
||||
@@ -124,7 +124,7 @@ namespace Game.Movement
|
||||
cMe.GetAI().MovementInform(MovementGeneratorType.SplineChain, _id);
|
||||
}
|
||||
|
||||
public override bool Update(Unit me, uint diff)
|
||||
public bool Update(Unit me, uint diff)
|
||||
{
|
||||
if (finished)
|
||||
return false;
|
||||
@@ -169,9 +169,9 @@ namespace Game.Movement
|
||||
return new SplineChainResumeInfo(_id, _chain, _walk, (byte)(_nextIndex - 1), (byte)(me.MoveSpline.CurrentSplineIdx()), _msToNext);
|
||||
}
|
||||
|
||||
public override void Reset(Unit owner) { }
|
||||
public void Reset(Unit owner) { }
|
||||
|
||||
public override MovementGeneratorType GetMovementGeneratorType() { return MovementGeneratorType.SplineChain; }
|
||||
public MovementGeneratorType GetMovementGeneratorType() { return MovementGeneratorType.SplineChain; }
|
||||
|
||||
uint _id;
|
||||
List<SplineChainLink> _chain = new();
|
||||
|
||||
@@ -1,406 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2012-2020 CypherCore <http://github.com/CypherCore>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
using Framework.Constants;
|
||||
using Framework.GameMath;
|
||||
using Game.Entities;
|
||||
using System;
|
||||
|
||||
namespace Game.Movement
|
||||
{
|
||||
public interface ITargetedMovementGeneratorBase
|
||||
{
|
||||
bool IsTargetValid();
|
||||
Unit GetTarget();
|
||||
void StopFollowing();
|
||||
}
|
||||
|
||||
public abstract class TargetedMovementGenerator<T> : MovementGeneratorMedium<T>, ITargetedMovementGeneratorBase where T : Unit
|
||||
{
|
||||
protected static float MOVE_FOLLOW_REPOSITIONING_DISTANCE = 1.5f;
|
||||
|
||||
protected TargetedMovementGenerator(Unit target, float offset = 0, float angle = 0)
|
||||
{
|
||||
_target = new FollowerReference();
|
||||
_target.Link(target, this);
|
||||
_timer = new TimeTracker();
|
||||
_offset = offset;
|
||||
_angle = angle;
|
||||
_recalculateTravel = false;
|
||||
_targetReached = false;
|
||||
}
|
||||
|
||||
public override bool DoUpdate(T owner, uint diff)
|
||||
{
|
||||
if (!IsTargetValid() || !GetTarget().IsInWorld)
|
||||
return false;
|
||||
|
||||
if (owner == null || !owner.IsAlive())
|
||||
return false;
|
||||
|
||||
if (owner.HasUnitState(UnitState.NotMove) || owner.IsMovementPreventedByCasting() || HasLostTarget(owner))
|
||||
{
|
||||
_interrupt = true;
|
||||
owner.StopMoving();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (_interrupt || _recalculateTravel)
|
||||
{
|
||||
_interrupt = false;
|
||||
SetTargetLocation(owner, true);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool targetMoved = false;
|
||||
_timer.Update((int)diff);
|
||||
if (_timer.Passed())
|
||||
{
|
||||
_timer.Reset(100);
|
||||
|
||||
// the allowed distance between target and mover before the mover needs to reposition in order to keep attacking
|
||||
float distance = GetMaxDistanceBeforeRepositioning(owner);
|
||||
if (owner.IsPet() && (owner.GetCharmerOrOwnerGUID() == GetTarget().GetGUID()))
|
||||
distance = 1.0f + GetTarget().GetCombatReach(); // pet following owner
|
||||
|
||||
Vector3 destination = owner.MoveSpline.FinalDestination();
|
||||
if (owner.MoveSpline.onTransport)
|
||||
{
|
||||
float o = 0;
|
||||
ITransport transport = owner.GetDirectTransport();
|
||||
if (transport != null)
|
||||
transport.CalculatePassengerPosition(ref destination.X, ref destination.Y, ref destination.Z, ref o);
|
||||
}
|
||||
|
||||
// First check distance
|
||||
targetMoved = !GetTarget().IsInDist(destination.X, destination.Y, destination.Z, distance);
|
||||
|
||||
// then, if the target is in range, check also Line of Sight.
|
||||
if (!targetMoved)
|
||||
targetMoved = !GetTarget().IsWithinLOSInMap(owner);
|
||||
}
|
||||
|
||||
if (targetMoved)
|
||||
SetTargetLocation(owner, true);
|
||||
else if (_speedChanged)
|
||||
SetTargetLocation(owner, false);
|
||||
|
||||
if (!_targetReached && owner.MoveSpline.Finalized())
|
||||
{
|
||||
MovementInform(owner);
|
||||
if (_angle == 0.0f && !owner.HasInArc(0.01f, GetTarget()))
|
||||
owner.SetInFront(GetTarget());
|
||||
|
||||
if (!_targetReached)
|
||||
{
|
||||
_targetReached = true;
|
||||
ReachTarget(owner);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void SetTargetLocation(T owner, bool updateDestination)
|
||||
{
|
||||
if (!IsTargetValid() || !GetTarget().IsInWorld)
|
||||
return;
|
||||
|
||||
if (!owner || !owner.IsAlive())
|
||||
return;
|
||||
|
||||
|
||||
if (owner.HasUnitState(UnitState.NotMove) || owner.IsMovementPreventedByCasting() || HasLostTarget(owner))
|
||||
{
|
||||
_interrupt = true;
|
||||
owner.StopMoving();
|
||||
return;
|
||||
}
|
||||
|
||||
if (owner.IsTypeId(TypeId.Unit) && !GetTarget().IsInAccessiblePlaceFor(owner.ToCreature()))
|
||||
{
|
||||
owner.ToCreature().SetCannotReachTarget(true);
|
||||
return;
|
||||
}
|
||||
|
||||
float x, y, z;
|
||||
if (updateDestination || _path == null)
|
||||
{
|
||||
if (_offset == 0)
|
||||
{
|
||||
if (GetTarget().IsWithinDistInMap(owner, SharedConst.ContactDistance))
|
||||
return;
|
||||
|
||||
GetTarget().GetNearPoint(owner, out x, out y, out z, SharedConst.ContactDistance, GetTarget().GetAbsoluteAngle(owner));
|
||||
}
|
||||
else
|
||||
{
|
||||
float distance = _offset + 1.0f;
|
||||
|
||||
if (owner.IsPet() && GetTarget().GetTypeId() == TypeId.Player)
|
||||
distance = 1.0f;
|
||||
|
||||
if (GetTarget().IsWithinDistInMap(owner, distance))
|
||||
return;
|
||||
|
||||
GetTarget().GetClosePoint(out x, out y, out z, _offset, _angle);
|
||||
}
|
||||
|
||||
if (owner.IsHovering())
|
||||
owner.UpdateAllowedPositionZ(x, y, ref z);
|
||||
}
|
||||
else
|
||||
{
|
||||
// the destination has not changed, we just need to refresh the path (usually speed change)
|
||||
var end = _path.GetEndPosition();
|
||||
x = end.X;
|
||||
y = end.Y;
|
||||
z = end.Z;
|
||||
}
|
||||
|
||||
if (_path == null)
|
||||
_path = new PathGenerator(owner);
|
||||
|
||||
// allow pets to use shortcut if no path found when following their master
|
||||
bool forceDest = owner.IsTypeId(TypeId.Unit) && owner.IsPet() && owner.HasUnitState(UnitState.Follow);
|
||||
|
||||
bool result = _path.CalculatePath(x, y, z, forceDest);
|
||||
if (!result && Convert.ToBoolean(_path.GetPathType() & PathType.NoPath))
|
||||
{
|
||||
// Can't reach target
|
||||
_recalculateTravel = true;
|
||||
if (owner.IsTypeId(TypeId.Unit))
|
||||
owner.ToCreature().SetCannotReachTarget(true);
|
||||
return;
|
||||
}
|
||||
|
||||
_targetReached = false;
|
||||
_recalculateTravel = false;
|
||||
_speedChanged = false;
|
||||
|
||||
AddUnitStateMove(owner);
|
||||
|
||||
if (owner.IsTypeId(TypeId.Unit))
|
||||
owner.ToCreature().SetCannotReachTarget(false);
|
||||
|
||||
MoveSplineInit init = new(owner);
|
||||
init.MovebyPath(_path.GetPath());
|
||||
init.SetWalk(EnableWalking());
|
||||
// Using the same condition for facing target as the one that is used for SetInFront on movement end
|
||||
// - applies to ChaseMovementGenerator mostly
|
||||
if (_angle == 0.0f)
|
||||
init.SetFacing(GetTarget());
|
||||
|
||||
init.Launch();
|
||||
}
|
||||
|
||||
bool IsReachable()
|
||||
{
|
||||
return _path != null ? _path.GetPathType().HasAnyFlag(PathType.Normal) : true;
|
||||
}
|
||||
|
||||
public override void UnitSpeedChanged()
|
||||
{
|
||||
_speedChanged = true;
|
||||
}
|
||||
|
||||
public abstract void ClearUnitStateMove(T owner);
|
||||
public abstract void AddUnitStateMove(T owner);
|
||||
public virtual bool HasLostTarget(T owner) { return false; }
|
||||
public abstract void ReachTarget(T owner);
|
||||
public virtual bool EnableWalking() { return false; }
|
||||
public abstract void MovementInform(T owner);
|
||||
public virtual float GetMaxDistanceBeforeRepositioning(T owner) { return 0.0f; }
|
||||
|
||||
public void StopFollowing() { }
|
||||
|
||||
public bool IsTargetValid() { return _target.IsValid(); }
|
||||
public Unit GetTarget() { return _target.GetTarget(); }
|
||||
|
||||
FollowerReference _target;
|
||||
PathGenerator _path;
|
||||
TimeTracker _timer;
|
||||
float _offset;
|
||||
float _angle;
|
||||
bool _recalculateTravel;
|
||||
bool _speedChanged;
|
||||
bool _targetReached;
|
||||
bool _interrupt;
|
||||
}
|
||||
|
||||
public class ChaseMovementGenerator<T> : TargetedMovementGenerator<T> where T : Unit
|
||||
{
|
||||
public ChaseMovementGenerator(Unit target)
|
||||
: base(target)
|
||||
{
|
||||
}
|
||||
|
||||
public ChaseMovementGenerator(Unit target, float offset, float angle)
|
||||
: base(target, offset, angle)
|
||||
{
|
||||
}
|
||||
|
||||
public override void DoInitialize(T owner)
|
||||
{
|
||||
if (owner.IsTypeId(TypeId.Unit))
|
||||
owner.SetWalk(false);
|
||||
|
||||
owner.AddUnitState(UnitState.Chase);
|
||||
SetTargetLocation(owner, true);
|
||||
}
|
||||
|
||||
public override void DoReset(T owner)
|
||||
{
|
||||
DoInitialize(owner);
|
||||
}
|
||||
|
||||
public override void DoFinalize(T owner)
|
||||
{
|
||||
owner.ClearUnitState(UnitState.Chase | UnitState.ChaseMove);
|
||||
}
|
||||
|
||||
public override void ClearUnitStateMove(T owner)
|
||||
{
|
||||
owner.ClearUnitState(UnitState.ChaseMove);
|
||||
}
|
||||
|
||||
public override void AddUnitStateMove(T owner)
|
||||
{
|
||||
owner.AddUnitState(UnitState.ChaseMove);
|
||||
}
|
||||
|
||||
public override bool HasLostTarget(T owner)
|
||||
{
|
||||
return owner.GetVictim() != GetTarget();
|
||||
}
|
||||
|
||||
public override void ReachTarget(T owner)
|
||||
{
|
||||
ClearUnitStateMove(owner);
|
||||
|
||||
if (owner.IsWithinMeleeRange(GetTarget()))
|
||||
owner.Attack(GetTarget(), true);
|
||||
|
||||
if (owner.IsTypeId(TypeId.Unit))
|
||||
owner.ToCreature().SetCannotReachTarget(false);
|
||||
}
|
||||
|
||||
public override float GetMaxDistanceBeforeRepositioning(T owner)
|
||||
{
|
||||
// the notion of melee range and melee attack is clearly separated from the notion of the movement.
|
||||
// As a seperation of concern refactoring, this value should be passed into parameter by the caller (during creation of ChaseMoveGen) instead of defining it
|
||||
// here (or a callback should be used since this value depends on dynamic fields (combat reach).
|
||||
return owner.GetMeleeRange(base.GetTarget());
|
||||
}
|
||||
|
||||
public override void MovementInform(T owner)
|
||||
{
|
||||
if (owner.IsTypeId(TypeId.Unit))
|
||||
{
|
||||
// Pass back the GUIDLow of the target. If it is pet's owner then PetAI will handle
|
||||
if (owner.ToCreature().GetAI() != null)
|
||||
owner.ToCreature().GetAI().MovementInform(MovementGeneratorType.Chase, (uint)GetTarget().GetGUID().GetCounter());
|
||||
}
|
||||
}
|
||||
|
||||
public override MovementGeneratorType GetMovementGeneratorType() { return MovementGeneratorType.Chase; }
|
||||
}
|
||||
|
||||
public class FollowMovementGenerator<T> : TargetedMovementGenerator<T> where T : Unit
|
||||
{
|
||||
public FollowMovementGenerator(Unit target) : base(target) { }
|
||||
|
||||
public FollowMovementGenerator(Unit target, float offset, float angle) : base(target, offset, angle) { }
|
||||
|
||||
public override void DoInitialize(T owner)
|
||||
{
|
||||
owner.AddUnitState(UnitState.Follow);
|
||||
UpdateSpeed(owner);
|
||||
SetTargetLocation(owner, true);
|
||||
}
|
||||
|
||||
public override void DoReset(T owner)
|
||||
{
|
||||
DoInitialize(owner);
|
||||
}
|
||||
|
||||
public override void DoFinalize(T owner)
|
||||
{
|
||||
owner.ClearUnitState(UnitState.Follow | UnitState.FollowMove);
|
||||
UpdateSpeed(owner);
|
||||
}
|
||||
|
||||
public override void ClearUnitStateMove(T owner)
|
||||
{
|
||||
owner.ClearUnitState(UnitState.FollowMove);
|
||||
}
|
||||
|
||||
public override void AddUnitStateMove(T owner)
|
||||
{
|
||||
owner.AddUnitState(UnitState.FollowMove);
|
||||
}
|
||||
|
||||
public override void ReachTarget(T owner)
|
||||
{
|
||||
ClearUnitStateMove(owner);
|
||||
}
|
||||
|
||||
public override bool EnableWalking()
|
||||
{
|
||||
if (typeof(T) == typeof(Player))
|
||||
return false;
|
||||
else
|
||||
return IsTargetValid() && GetTarget().IsWalking();
|
||||
}
|
||||
|
||||
public override float GetMaxDistanceBeforeRepositioning(T owner)
|
||||
{
|
||||
return owner.GetCombatReach() + base.GetTarget().GetCombatReach() + MOVE_FOLLOW_REPOSITIONING_DISTANCE;
|
||||
}
|
||||
|
||||
public override void MovementInform(T owner)
|
||||
{
|
||||
if (owner.IsTypeId(TypeId.Player))
|
||||
return;
|
||||
|
||||
// Pass back the GUIDLow of the target. If it is pet's owner then PetAI will handle
|
||||
if (owner.ToCreature().GetAI() != null)
|
||||
owner.ToCreature().GetAI().MovementInform(MovementGeneratorType.Follow, (uint)GetTarget().GetGUID().GetCounter());
|
||||
}
|
||||
|
||||
public override bool HasLostTarget(T u) { return false; }
|
||||
|
||||
public override MovementGeneratorType GetMovementGeneratorType()
|
||||
{
|
||||
return MovementGeneratorType.Follow;
|
||||
}
|
||||
|
||||
void UpdateSpeed(T owner)
|
||||
{
|
||||
if (owner.IsTypeId(TypeId.Player))
|
||||
return;
|
||||
|
||||
if (!owner.IsPet() || !owner.IsInWorld || !IsTargetValid() && GetTarget().GetGUID() != owner.GetOwnerGUID())
|
||||
return;
|
||||
|
||||
owner.UpdateSpeed(UnitMoveType.Run);
|
||||
owner.UpdateSpeed(UnitMoveType.Walk);
|
||||
owner.UpdateSpeed(UnitMoveType.Swim);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -284,7 +284,7 @@ namespace Game.Movement
|
||||
creature.GetAI().MovementInform(MovementGeneratorType.Waypoint, (uint)_currentNode);
|
||||
}
|
||||
|
||||
public override bool GetResetPosition(Unit u, out float x, out float y, out float z)
|
||||
public bool GetResetPosition(Unit u, out float x, out float y, out float z)
|
||||
{
|
||||
x = y = z = 0;
|
||||
// prevent a crash at empty waypoint path.
|
||||
@@ -301,13 +301,13 @@ namespace Game.Movement
|
||||
return true;
|
||||
}
|
||||
|
||||
public override void Pause(uint timer = 0)
|
||||
public void Pause(uint timer = 0)
|
||||
{
|
||||
_stalled = timer == 0;
|
||||
_nextMoveTime.Reset(timer != 0 ? (int)timer : 1);
|
||||
}
|
||||
|
||||
public override void Resume(uint overrideTimer = 0)
|
||||
public void Resume(uint overrideTimer = 0)
|
||||
{
|
||||
_stalled = false;
|
||||
if (overrideTimer != 0)
|
||||
@@ -321,7 +321,7 @@ namespace Game.Movement
|
||||
|
||||
public override MovementGeneratorType GetMovementGeneratorType() { return MovementGeneratorType.Waypoint; }
|
||||
|
||||
public override void UnitSpeedChanged() { _recalculateSpeed = true; }
|
||||
public void UnitSpeedChanged() { _recalculateSpeed = true; }
|
||||
|
||||
TimeTrackerSmall _nextMoveTime;
|
||||
bool _recalculateSpeed;
|
||||
|
||||
@@ -28,7 +28,7 @@ namespace Game.Movement
|
||||
public class MotionMaster
|
||||
{
|
||||
public const double gravity = 19.29110527038574;
|
||||
public const float SPEED_CHARGE = 42.0f;
|
||||
public const float SPEED_CHARGE = 42.0f;
|
||||
IdleMovementGenerator staticIdleMovement = new();
|
||||
|
||||
public MotionMaster(Unit me)
|
||||
@@ -139,7 +139,7 @@ namespace Game.Movement
|
||||
{
|
||||
if (Empty() || slot >= MovementSlot.Max || _slot[(int)slot] == null)
|
||||
return MovementGeneratorType.Max;
|
||||
|
||||
|
||||
return _slot[(int)slot].GetMovementGeneratorType();
|
||||
}
|
||||
|
||||
@@ -202,7 +202,7 @@ namespace Game.Movement
|
||||
{
|
||||
Unit target = _owner.ToCreature().GetCharmerOrOwner();
|
||||
if (target)
|
||||
StartMovement(new FollowMovementGenerator<Creature>(target, SharedConst.PetFollowDist, SharedConst.PetFollowAngle), MovementSlot.Active);
|
||||
StartMovement(new FollowMovementGenerator(target, SharedConst.PetFollowDist, new ChaseAngle(SharedConst.PetFollowAngle)), MovementSlot.Active);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -212,27 +212,29 @@ namespace Game.Movement
|
||||
StartMovement(new RandomMovementGenerator(spawndist), MovementSlot.Idle);
|
||||
}
|
||||
|
||||
public void MoveFollow(Unit target, float dist = 0.0f, float angle = 0.0f, MovementSlot slot = MovementSlot.Idle)
|
||||
public void MoveFollow(Unit target, float dist, float angle = 0.0f, MovementSlot slot = MovementSlot.Idle) { MoveFollow(target, dist, new ChaseAngle(angle), slot); }
|
||||
|
||||
public void MoveFollow(Unit target, float dist, ChaseAngle angle, MovementSlot slot = MovementSlot.Idle)
|
||||
{
|
||||
// ignore movement request if target not exist
|
||||
if (!target || target == _owner)
|
||||
return;
|
||||
|
||||
if (_owner.IsTypeId(TypeId.Player))
|
||||
StartMovement(new FollowMovementGenerator<Player>(target, dist, angle), slot);
|
||||
else
|
||||
StartMovement(new FollowMovementGenerator<Creature>(target, dist, angle), slot);
|
||||
Log.outDebug(LogFilter.Misc, $"{(_owner.IsPlayer() ? "Player" : "Creature")} (Entry: {_owner.GetEntry()}, {_owner.GetGUID()}) follows {(target.IsPlayer() ? "player" : "creature")} (Entry {target.GetEntry()}, {target.GetGUID()}).");
|
||||
|
||||
StartMovement(new FollowMovementGenerator(target, dist, angle), slot);
|
||||
}
|
||||
|
||||
public void MoveChase(Unit target, float dist = 0.0f, float angle = 0.0f)
|
||||
public void MoveChase(Unit target, float dist, float angle = 0.0f) { MoveChase(target, new ChaseRange(dist), new ChaseAngle(angle)); }
|
||||
|
||||
public void MoveChase(Unit target, ChaseRange? dist = null, ChaseAngle? angle = null)
|
||||
{
|
||||
if (!target || target == _owner)
|
||||
return;
|
||||
|
||||
if (_owner.IsTypeId(TypeId.Player))
|
||||
StartMovement(new ChaseMovementGenerator<Player>(target, dist, angle), MovementSlot.Active);
|
||||
else
|
||||
StartMovement(new ChaseMovementGenerator<Creature>(target, dist, angle), MovementSlot.Active);
|
||||
Log.outDebug(LogFilter.Misc, $"{(_owner.IsPlayer() ? "Player" : "Creature")} (Entry: {_owner.GetEntry()}, {_owner.GetGUID()}) chase to {(target.IsPlayer() ? "player" : "creature")} (Entry: {target.GetEntry()}, {target.GetGUID()})");
|
||||
|
||||
StartMovement(new ChaseMovementGenerator(target, dist, angle), MovementSlot.Active);
|
||||
}
|
||||
|
||||
public void MoveConfused()
|
||||
@@ -840,4 +842,59 @@ namespace Game.Movement
|
||||
Update = 1, // Clear or Expire called from update
|
||||
Reset = 2 // Flag if need top().Reset()
|
||||
}
|
||||
|
||||
public struct ChaseRange
|
||||
{
|
||||
// this contains info that informs how we should path!
|
||||
public float MinRange; // we have to move if we are within this range... (min. attack range)
|
||||
public float MinTolerance; // ...and if we are, we will move this far away
|
||||
public float MaxRange; // we have to move if we are outside this range... (max. attack range)
|
||||
public float MaxTolerance; // ...and if we are, we will move into this range
|
||||
|
||||
public ChaseRange(float range)
|
||||
{
|
||||
MinRange = range > SharedConst.ContactDistance ? 0 : range - SharedConst.ContactDistance;
|
||||
MinTolerance = range;
|
||||
MaxRange = range + SharedConst.ContactDistance;
|
||||
MaxTolerance = range;
|
||||
}
|
||||
|
||||
public ChaseRange(float min, float max)
|
||||
{
|
||||
MinRange = min;
|
||||
MinTolerance = Math.Min(min + SharedConst.ContactDistance, (min + max) / 2);
|
||||
MaxRange = max;
|
||||
MaxTolerance = Math.Max(max - SharedConst.ContactDistance, MinTolerance);
|
||||
}
|
||||
|
||||
public ChaseRange(float min, float tMin, float tMax, float max)
|
||||
{
|
||||
MinRange = min;
|
||||
MinTolerance = tMin;
|
||||
MaxRange = max;
|
||||
MaxTolerance = tMax;
|
||||
}
|
||||
}
|
||||
|
||||
public struct ChaseAngle
|
||||
{
|
||||
public float RelativeAngle; // we want to be at this angle relative to the target (0 = front, M_PI = back)
|
||||
public float Tolerance; // but we'll tolerate anything within +- this much
|
||||
|
||||
public ChaseAngle(float angle, float tol = MathFunctions.PiOver4)
|
||||
{
|
||||
RelativeAngle = Position.NormalizeOrientation(angle);
|
||||
Tolerance = tol;
|
||||
}
|
||||
|
||||
public float UpperBound() { return Position.NormalizeOrientation(RelativeAngle + Tolerance); }
|
||||
|
||||
public float LowerBound() { return Position.NormalizeOrientation(RelativeAngle - Tolerance); }
|
||||
|
||||
public bool IsAngleOkay(float relAngle)
|
||||
{
|
||||
float diff = Math.Abs(relAngle - RelativeAngle);
|
||||
return (Math.Min(diff, (2 * MathF.PI) - diff) <= Tolerance);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4887,7 +4887,7 @@ namespace Game.Spells
|
||||
else if (m_preGeneratedPath.IsInvalidDestinationZ(target)) // Check position z, if in a straight line
|
||||
return SpellCastResult.NoPath;
|
||||
|
||||
m_preGeneratedPath.ReducePathLenghtByDist(objSize); //move back
|
||||
m_preGeneratedPath.ShortenPathUntilDist(target, objSize); //move back
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user