diff options
Diffstat (limited to '')
-rw-r--r-- | src/Mobs/AggressiveMonster.cpp | 12 | ||||
-rw-r--r-- | src/Mobs/Blaze.cpp | 2 | ||||
-rw-r--r-- | src/Mobs/CaveSpider.cpp | 6 | ||||
-rw-r--r-- | src/Mobs/Creeper.cpp | 2 | ||||
-rw-r--r-- | src/Mobs/Enderman.cpp | 2 | ||||
-rw-r--r-- | src/Mobs/Ghast.cpp | 2 | ||||
-rw-r--r-- | src/Mobs/Monster.cpp | 121 | ||||
-rw-r--r-- | src/Mobs/Monster.h | 21 | ||||
-rw-r--r-- | src/Mobs/PassiveAggressiveMonster.cpp | 4 | ||||
-rw-r--r-- | src/Mobs/PassiveMonster.cpp | 36 | ||||
-rw-r--r-- | src/Mobs/PathFinder.h | 2 | ||||
-rw-r--r-- | src/Mobs/Skeleton.cpp | 4 | ||||
-rw-r--r-- | src/Mobs/Wolf.cpp | 33 |
13 files changed, 171 insertions, 76 deletions
diff --git a/src/Mobs/AggressiveMonster.cpp b/src/Mobs/AggressiveMonster.cpp index 512bfb4a1..c67f01b8f 100644 --- a/src/Mobs/AggressiveMonster.cpp +++ b/src/Mobs/AggressiveMonster.cpp @@ -26,9 +26,9 @@ void cAggressiveMonster::InStateChasing(std::chrono::milliseconds a_Dt, cChunk & { super::InStateChasing(a_Dt, a_Chunk); - if (m_Target != nullptr) + if (GetTarget() != nullptr) { - MoveToPosition(m_Target->GetPosition()); + MoveToPosition(GetTarget()->GetPosition()); } } @@ -62,14 +62,14 @@ void cAggressiveMonster::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) CheckEventSeePlayer(a_Chunk); } - if (m_Target == nullptr) + if (GetTarget() == nullptr) { return; } cTracer LineOfSight(GetWorld()); Vector3d MyHeadPosition = GetPosition() + Vector3d(0, GetHeight(), 0); - Vector3d AttackDirection(m_Target->GetPosition() + Vector3d(0, m_Target->GetHeight(), 0) - MyHeadPosition); + Vector3d AttackDirection(GetTarget()->GetPosition() + Vector3d(0, GetTarget()->GetHeight(), 0) - MyHeadPosition); if (TargetIsInRange() && !LineOfSight.Trace(MyHeadPosition, AttackDirection, static_cast<int>(AttackDirection.Length())) && (GetHealth() > 0.0)) @@ -85,14 +85,14 @@ void cAggressiveMonster::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) bool cAggressiveMonster::Attack(std::chrono::milliseconds a_Dt) { - if ((m_Target == nullptr) || (m_AttackCoolDownTicksLeft != 0)) + if ((GetTarget() == nullptr) || (m_AttackCoolDownTicksLeft != 0)) { return false; } // Setting this higher gives us more wiggle room for attackrate ResetAttackCooldown(); - m_Target->TakeDamage(dtMobAttack, this, m_AttackDamage, 0); + GetTarget()->TakeDamage(dtMobAttack, this, m_AttackDamage, 0); return true; } diff --git a/src/Mobs/Blaze.cpp b/src/Mobs/Blaze.cpp index bd3b3f776..d002e14e7 100644 --- a/src/Mobs/Blaze.cpp +++ b/src/Mobs/Blaze.cpp @@ -34,7 +34,7 @@ void cBlaze::GetDrops(cItems & a_Drops, cEntity * a_Killer) bool cBlaze::Attack(std::chrono::milliseconds a_Dt) { - if ((m_Target != nullptr) && (m_AttackCoolDownTicksLeft == 0)) + if ((GetTarget() != nullptr) && (m_AttackCoolDownTicksLeft == 0)) { // Setting this higher gives us more wiggle room for attackrate Vector3d Speed = GetLookVector() * 20; diff --git a/src/Mobs/CaveSpider.cpp b/src/Mobs/CaveSpider.cpp index ee3f4803c..2a4975126 100644 --- a/src/Mobs/CaveSpider.cpp +++ b/src/Mobs/CaveSpider.cpp @@ -33,11 +33,11 @@ bool cCaveSpider::Attack(std::chrono::milliseconds a_Dt) { return false; } - - if (m_Target->IsPawn()) + + if (GetTarget()->IsPawn()) { // TODO: Easy = no poison, Medium = 7 seconds, Hard = 15 seconds - static_cast<cPawn *>(m_Target)->AddEntityEffect(cEntityEffect::effPoison, 7 * 20, 0); + static_cast<cPawn *>(GetTarget())->AddEntityEffect(cEntityEffect::effPoison, 7 * 20, 0); } return true; } diff --git a/src/Mobs/Creeper.cpp b/src/Mobs/Creeper.cpp index d88c99953..47d294a30 100644 --- a/src/Mobs/Creeper.cpp +++ b/src/Mobs/Creeper.cpp @@ -27,7 +27,7 @@ void cCreeper::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) { super::Tick(a_Dt, a_Chunk); - if ((m_Target == nullptr) || (!TargetIsInRange() && !m_BurnedWithFlintAndSteel)) + if ((GetTarget() == nullptr) || (!TargetIsInRange() && !m_BurnedWithFlintAndSteel)) { if (m_bIsBlowing) { diff --git a/src/Mobs/Enderman.cpp b/src/Mobs/Enderman.cpp index 4a30a0acd..ccfd44110 100644 --- a/src/Mobs/Enderman.cpp +++ b/src/Mobs/Enderman.cpp @@ -104,7 +104,7 @@ void cEnderman::GetDrops(cItems & a_Drops, cEntity * a_Killer) void cEnderman::CheckEventSeePlayer(cChunk & a_Chunk) { - if (m_Target != nullptr) + if (GetTarget() != nullptr) { return; } diff --git a/src/Mobs/Ghast.cpp b/src/Mobs/Ghast.cpp index 61813d0fe..0544255df 100644 --- a/src/Mobs/Ghast.cpp +++ b/src/Mobs/Ghast.cpp @@ -34,7 +34,7 @@ void cGhast::GetDrops(cItems & a_Drops, cEntity * a_Killer) bool cGhast::Attack(std::chrono::milliseconds a_Dt) { - if ((m_Target != nullptr) && (m_AttackCoolDownTicksLeft == 0)) + if ((GetTarget() != nullptr) && (m_AttackCoolDownTicksLeft == 0)) { // Setting this higher gives us more wiggle room for attackrate Vector3d Speed = GetLookVector() * 20; diff --git a/src/Mobs/Monster.cpp b/src/Mobs/Monster.cpp index 4a543e400..28cb10238 100644 --- a/src/Mobs/Monster.cpp +++ b/src/Mobs/Monster.cpp @@ -74,7 +74,6 @@ cMonster::cMonster(const AString & a_ConfigName, eMonsterType a_MobType, const A : super(etMonster, a_Width, a_Height) , m_EMState(IDLE) , m_EMPersonality(AGGRESSIVE) - , m_Target(nullptr) , m_PathFinder(a_Width, a_Height) , m_PathfinderActivated(false) , m_JumpCoolDown(0) @@ -101,6 +100,7 @@ cMonster::cMonster(const AString & a_ConfigName, eMonsterType a_MobType, const A , m_RelativeWalkSpeed(1) , m_Age(1) , m_AgingTimer(20 * 60 * 20) // about 20 minutes + , m_Target(nullptr) { if (!a_ConfigName.empty()) { @@ -112,6 +112,25 @@ cMonster::cMonster(const AString & a_ConfigName, eMonsterType a_MobType, const A +cMonster::~cMonster() +{ + ASSERT(GetTarget() == nullptr); +} + + + + + +void cMonster::Destroyed() +{ + SetTarget(nullptr); // Tell them we're no longer targeting them. + super::Destroyed(); +} + + + + + void cMonster::SpawnOn(cClientHandle & a_Client) { a_Client.SendSpawnMob(*this); @@ -214,6 +233,7 @@ void cMonster::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) super::Tick(a_Dt, a_Chunk); GET_AND_VERIFY_CURRENT_CHUNK(Chunk, POSX_TOINT, POSZ_TOINT); + ASSERT((GetTarget() == nullptr) || (GetTarget()->IsPawn() && (GetTarget()->GetWorld() == GetWorld()))); if (m_AttackCoolDownTicksLeft > 0) { m_AttackCoolDownTicksLeft -= 1; @@ -234,17 +254,15 @@ void cMonster::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) { ++m_TicksSinceLastDamaged; } - if ((m_Target != nullptr)) + if ((GetTarget() != nullptr)) { - if (m_Target->IsDestroyed()) - { - m_Target = nullptr; - } - else if (m_Target->IsPlayer()) + ASSERT(!GetTarget()->IsDestroyed()); + + if (GetTarget()->IsPlayer()) { - if (static_cast<cPlayer *>(m_Target)->IsGameModeCreative()) + if (static_cast<cPlayer *>(GetTarget())->IsGameModeCreative()) { - m_Target = nullptr; + SetTarget(nullptr); m_EMState = IDLE; } } @@ -343,11 +361,10 @@ void cMonster::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) void cMonster::SetPitchAndYawFromDestination(bool a_IsFollowingPath) { - /* Todo Buggy */ Vector3d BodyDistance; - if (!a_IsFollowingPath && (m_Target != nullptr)) + if (!a_IsFollowingPath && (GetTarget() != nullptr)) { - BodyDistance = m_Target->GetPosition() - GetPosition(); + BodyDistance = GetTarget()->GetPosition() - GetPosition(); } else { @@ -359,17 +376,16 @@ void cMonster::SetPitchAndYawFromDestination(bool a_IsFollowingPath) SetYaw(BodyRotation); Vector3d HeadDistance; - if (m_Target != nullptr) + if (GetTarget() != nullptr) { - if (m_Target->IsPlayer()) // Look at a player + if (GetTarget()->IsPlayer()) // Look at a player { - HeadDistance = m_Target->GetPosition() - GetPosition(); - // HeadDistance.y = static_cast<cPlayer *>(m_Target)->GetStance() - 1; + HeadDistance = GetTarget()->GetPosition() - GetPosition(); } else // Look at some other entity { - HeadDistance = m_Target->GetPosition() - GetPosition(); - // HeadDistance.y = m_Target->GetPosY() + GetHeight(); + HeadDistance = GetTarget()->GetPosition() - GetPosition(); + // HeadDistance.y = GetTarget()->GetPosY() + GetHeight(); } } else // Look straight @@ -448,9 +464,9 @@ bool cMonster::DoTakeDamage(TakeDamageInfo & a_TDI) m_World->BroadcastSoundEffect(m_SoundHurt, GetPosX(), GetPosY(), GetPosZ(), 1.0f, 0.8f); } - if (a_TDI.Attacker != nullptr) + if ((a_TDI.Attacker != nullptr) && a_TDI.Attacker->IsPawn()) { - m_Target = a_TDI.Attacker; + SetTarget(static_cast<cPawn*>(a_TDI.Attacker)); m_TicksSinceLastDamaged = 0; } return true; @@ -577,9 +593,9 @@ void cMonster::CheckEventSeePlayer(cChunk & a_Chunk) void cMonster::CheckEventLostPlayer(void) { - if (m_Target != nullptr) + if (GetTarget() != nullptr) { - if ((m_Target->GetPosition() - GetPosition()).Length() > m_SightDistance) + if ((GetTarget()->GetPosition() - GetPosition()).Length() > m_SightDistance) { EventLosePlayer(); } @@ -598,7 +614,9 @@ void cMonster::CheckEventLostPlayer(void) // default to change state to chasing void cMonster::EventSeePlayer(cEntity * a_SeenPlayer, cChunk & a_Chunk) { - m_Target = a_SeenPlayer; + UNUSED(a_Chunk); + ASSERT(a_SeenPlayer->IsPlayer()); + SetTarget(static_cast<cPawn*>(a_SeenPlayer)); } @@ -607,7 +625,7 @@ void cMonster::EventSeePlayer(cEntity * a_SeenPlayer, cChunk & a_Chunk) void cMonster::EventLosePlayer(void) { - m_Target = nullptr; + SetTarget(nullptr); m_EMState = IDLE; } @@ -678,11 +696,11 @@ void cMonster::InStateEscaping(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) { UNUSED(a_Dt); - if (m_Target != nullptr) + if (GetTarget() != nullptr) { Vector3d newloc = GetPosition(); - newloc.x = (m_Target->GetPosition().x < newloc.x)? (newloc.x + m_SightDistance): (newloc.x - m_SightDistance); - newloc.z = (m_Target->GetPosition().z < newloc.z)? (newloc.z + m_SightDistance): (newloc.z - m_SightDistance); + newloc.x = (GetTarget()->GetPosition().x < newloc.x)? (newloc.x + m_SightDistance): (newloc.x - m_SightDistance); + newloc.z = (GetTarget()->GetPosition().z < newloc.z)? (newloc.z + m_SightDistance): (newloc.z - m_SightDistance); MoveToPosition(newloc); } else @@ -890,6 +908,55 @@ int cMonster::GetSpawnDelay(cMonster::eFamily a_MobFamily) + +/** Sets the target. */ +void cMonster::SetTarget (cPawn * a_NewTarget) +{ + ASSERT((a_NewTarget == nullptr) || (!IsDestroyed())); + if (m_Target == a_NewTarget) + { + return; + } + cPawn * OldTarget = m_Target; + m_Target = a_NewTarget; + + if (OldTarget != nullptr) + { + // Notify the old target that we are no longer targeting it. + OldTarget->NoLongerTargetingMe(this); + } + + if (a_NewTarget != nullptr) + { + ASSERT(!a_NewTarget->IsDestroyed()); + // Notify the new target that we are now targeting it. + m_Target->TargetingMe(this); + } + +} + + + + + +void cMonster::UnsafeUnsetTarget() +{ + m_Target = nullptr; +} + + + + + +cPawn * cMonster::GetTarget () +{ + return m_Target; +} + + + + + cMonster * cMonster::NewMonsterFromType(eMonsterType a_MobType) { cFastRandom Random; diff --git a/src/Mobs/Monster.h b/src/Mobs/Monster.h index 7c4683942..2155a4a7c 100644 --- a/src/Mobs/Monster.h +++ b/src/Mobs/Monster.h @@ -44,6 +44,10 @@ public: */ cMonster(const AString & a_ConfigName, eMonsterType a_MobType, const AString & a_SoundHurt, const AString & a_SoundDeath, double a_Width, double a_Height); + ~cMonster(); + + virtual void Destroyed() override; + CLASS_PROTODEF(cMonster) virtual void SpawnOn(cClientHandle & a_ClientHandle) override; @@ -156,6 +160,16 @@ public: // tolua_end + /** Sets the target that this mob will chase. Pass a nullptr to unset. */ + void SetTarget (cPawn * a_NewTarget); + + /** Unset the target without notifying the target entity. Do not use this, use SetTarget(nullptr) instead. + This is only used by cPawn internally. */ + void UnsafeUnsetTarget(); + + /** Returns the current target. */ + cPawn * GetTarget (); + /** Creates a new object of the specified mob. a_MobType is the type of the mob to be created Asserts and returns null if mob type is not specified @@ -164,9 +178,6 @@ public: protected: - /** A pointer to the entity this mobile is aiming to reach */ - cEntity * m_Target; - /** The pathfinder instance handles pathfinding for this monster. */ cPathFinder m_PathFinder; @@ -255,4 +266,8 @@ protected: /** Adds weapon that is equipped with the chance saved in m_DropChance[...] (this will be greter than 1 if picked up or 0.085 + (0.01 per LootingLevel) if born with) to the drop */ void AddRandomWeaponDropItem(cItems & a_Drops, unsigned int a_LootingLevel); +private: + /** A pointer to the entity this mobile is aiming to reach */ + cPawn * m_Target; + } ; // tolua_export diff --git a/src/Mobs/PassiveAggressiveMonster.cpp b/src/Mobs/PassiveAggressiveMonster.cpp index 71ac7bd89..a1bb1138f 100644 --- a/src/Mobs/PassiveAggressiveMonster.cpp +++ b/src/Mobs/PassiveAggressiveMonster.cpp @@ -26,9 +26,9 @@ bool cPassiveAggressiveMonster::DoTakeDamage(TakeDamageInfo & a_TDI) return false; } - if ((m_Target != nullptr) && (m_Target->IsPlayer())) + if ((GetTarget() != nullptr) && (GetTarget()->IsPlayer())) { - if (!static_cast<cPlayer *>(m_Target)->IsGameModeCreative()) + if (!static_cast<cPlayer *>(GetTarget())->IsGameModeCreative()) { m_EMState = CHASING; } diff --git a/src/Mobs/PassiveMonster.cpp b/src/Mobs/PassiveMonster.cpp index 30b46500d..53288a54c 100644 --- a/src/Mobs/PassiveMonster.cpp +++ b/src/Mobs/PassiveMonster.cpp @@ -157,19 +157,33 @@ void cPassiveMonster::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) virtual bool Item(cEntity * a_Entity) override { - // if we're the same species as someone around and they don't have a partner, start mating with them - if ((a_Entity->GetEntityType() == m_Me->GetEntityType()) && (a_Entity != m_Me)) + // If the entity is not a monster, don't breed with it + // Also, do not self-breed + if ((a_Entity->GetEntityType() != etMonster) || (a_Entity == m_Me)) { - cPassiveMonster * Me = static_cast<cPassiveMonster*>(m_Me); - cPassiveMonster * Partner = static_cast<cPassiveMonster*>(a_Entity); - if (Partner->IsInLove() && (Partner->GetPartner() == nullptr)) - { - Partner->EngageLoveMode(Me); - Me->EngageLoveMode(Partner); - return true; - } + return false; } - return false; + + cPassiveMonster * Me = static_cast<cPassiveMonster*>(m_Me); + cPassiveMonster * PotentialPartner = static_cast<cPassiveMonster*>(a_Entity); + + // If the potential partner is not of the same species, don't breed with it + if (PotentialPartner->GetMobType() != Me->GetMobType()) + { + return false; + } + + // If the potential partner is not in love + // Or they already have a mate, do not breed with them + if ((!PotentialPartner->IsInLove()) || (PotentialPartner->GetPartner() != nullptr)) + { + return false; + } + + // All conditions met, let's breed! + PotentialPartner->EngageLoveMode(Me); + Me->EngageLoveMode(PotentialPartner); + return true; } } Callback(this); diff --git a/src/Mobs/PathFinder.h b/src/Mobs/PathFinder.h index 1bdc13a32..213530b11 100644 --- a/src/Mobs/PathFinder.h +++ b/src/Mobs/PathFinder.h @@ -14,7 +14,7 @@ class cPathFinder public: /** Creates a cPathFinder instance. Each mob should have one cPathFinder throughout its lifetime. @param a_MobWidth The mob width. - @param a_MobWidth The mob height. + @param a_MobHeight The mob height. */ cPathFinder(double a_MobWidth, double a_MobHeight); diff --git a/src/Mobs/Skeleton.cpp b/src/Mobs/Skeleton.cpp index adad543d2..7697f1279 100644 --- a/src/Mobs/Skeleton.cpp +++ b/src/Mobs/Skeleton.cpp @@ -52,10 +52,10 @@ bool cSkeleton::Attack(std::chrono::milliseconds a_Dt) { StopMovingToPosition(); // Todo handle this in a better way, the skeleton does some uneeded recalcs due to inStateChasing cFastRandom Random; - if ((m_Target != nullptr) && (m_AttackCoolDownTicksLeft == 0)) + if ((GetTarget() != nullptr) && (m_AttackCoolDownTicksLeft == 0)) { Vector3d Inaccuracy = Vector3d(Random.NextFloat(0.5) - 0.25, Random.NextFloat(0.5) - 0.25, Random.NextFloat(0.5) - 0.25); - Vector3d Speed = (m_Target->GetPosition() + Inaccuracy - GetPosition()) * 5; + Vector3d Speed = (GetTarget()->GetPosition() + Inaccuracy - GetPosition()) * 5; Speed.y = Speed.y - 1 + Random.NextInt(3); cArrowEntity * Arrow = new cArrowEntity(this, GetPosX(), GetPosY() + 1, GetPosZ(), Speed); if (Arrow == nullptr) diff --git a/src/Mobs/Wolf.cpp b/src/Mobs/Wolf.cpp index 3be14211b..d188d91eb 100644 --- a/src/Mobs/Wolf.cpp +++ b/src/Mobs/Wolf.cpp @@ -30,7 +30,7 @@ cWolf::cWolf(void) : bool cWolf::DoTakeDamage(TakeDamageInfo & a_TDI) { - cEntity * PreviousTarget = m_Target; + cPawn * PreviousTarget = GetTarget(); if (!super::DoTakeDamage(a_TDI)) { return false; @@ -38,14 +38,13 @@ bool cWolf::DoTakeDamage(TakeDamageInfo & a_TDI) if ((a_TDI.Attacker != nullptr) && a_TDI.Attacker->IsPawn()) { - cPawn * Pawn = static_cast<cPawn*>(m_Target); - if (Pawn->IsPlayer()) + if (GetTarget()->IsPlayer()) { if (m_IsTame) { - if ((static_cast<cPlayer*>(Pawn)->GetUUID() == m_OwnerUUID)) + if ((static_cast<cPlayer*>(GetTarget())->GetUUID() == m_OwnerUUID)) { - m_Target = PreviousTarget; // Do not attack owner + SetTarget(PreviousTarget); // Do not attack owner } else { @@ -100,16 +99,16 @@ bool cWolf::Attack(std::chrono::milliseconds a_Dt) { UNUSED(a_Dt); - if ((m_Target != nullptr) && (m_Target->IsPlayer())) + if ((GetTarget() != nullptr) && (GetTarget()->IsPlayer())) { - if (static_cast<cPlayer *>(m_Target)->GetUUID() == m_OwnerUUID) + if (static_cast<cPlayer *>(GetTarget())->GetUUID() == m_OwnerUUID) { - m_Target = nullptr; + SetTarget(nullptr); return false; } } - NotifyAlliesOfFight(static_cast<cPawn*>(m_Target)); + NotifyAlliesOfFight(static_cast<cPawn*>(GetTarget())); return super::Attack(a_Dt); } @@ -129,7 +128,7 @@ void cWolf::ReceiveNearbyFightInfo(AString a_PlayerID, cPawn * a_Opponent, bool } // If we already have a target - if (m_Target != nullptr) + if (GetTarget() != nullptr) { // If a wolf is asking for help and we already have a target, do nothing if (!a_IsPlayerInvolved) @@ -159,7 +158,7 @@ void cWolf::ReceiveNearbyFightInfo(AString a_PlayerID, cPawn * a_Opponent, bool } } - m_Target = a_Opponent; + SetTarget(a_Opponent); } @@ -264,7 +263,7 @@ void cWolf::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) super::Tick(a_Dt, a_Chunk); } - if (m_Target == nullptr) + if (GetTarget() == nullptr) { cPlayer * a_Closest_Player = m_World->FindClosestPlayer(GetPosition(), static_cast<float>(m_SightDistance)); if (a_Closest_Player != nullptr) @@ -311,11 +310,11 @@ void cWolf::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) { if (IsSitting()) { - m_Target = nullptr; + SetTarget(nullptr); } else { - MoveToPosition(m_Target->GetPosition()); + MoveToPosition(GetTarget()->GetPosition()); if (TargetIsInRange()) { Attack(a_Dt); @@ -359,18 +358,18 @@ void cWolf::TickFollowPlayer() { Callback.OwnerPos.y = FindFirstNonAirBlockPosition(Callback.OwnerPos.x, Callback.OwnerPos.z); TeleportToCoords(Callback.OwnerPos.x, Callback.OwnerPos.y, Callback.OwnerPos.z); - m_Target = nullptr; + SetTarget(nullptr); } if (Distance < 2) { - if (m_Target == nullptr) + if (GetTarget() == nullptr) { StopMovingToPosition(); } } else { - if (m_Target == nullptr) + if (GetTarget() == nullptr) { MoveToPosition(Callback.OwnerPos); } |