/* * This file is part of the TrinityCore Project. See AUTHORS file for Copyright information * * 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 2 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 . */ #include "MoveSplineInit.h" #include "Creature.h" #include "MoveSpline.h" #include "MovementPackets.h" #include "PathGenerator.h" #include "Unit.h" #include "VehicleDefines.h" namespace Movement { // Transforms coordinates from global to transport offsets class TransportPathTransform { public: TransportPathTransform(Unit const* owner, bool transformForTransport) : _transport(transformForTransport ? owner->GetDirectTransport() : nullptr) { } Vector3 operator()(Vector3 input) const { if (_transport) _transport->CalculatePassengerOffset(input.x, input.y, input.z); return input; } float operator()(float input) const { if (_transport) input -= _transport->GetTransportOrientation(); return input; } private: TransportBase* _transport; }; UnitMoveType SelectSpeedType(uint32 moveFlags) { if (moveFlags & MOVEMENTFLAG_FLYING) { if (moveFlags & MOVEMENTFLAG_BACKWARD /*&& speed_obj.flight >= speed_obj.flight_back*/) return MOVE_FLIGHT_BACK; else return MOVE_FLIGHT; } else if (moveFlags & MOVEMENTFLAG_SWIMMING) { if (moveFlags & MOVEMENTFLAG_BACKWARD /*&& speed_obj.swim >= speed_obj.swim_back*/) return MOVE_SWIM_BACK; else return MOVE_SWIM; } else if (moveFlags & MOVEMENTFLAG_WALKING) { //if (speed_obj.run > speed_obj.walk) return MOVE_WALK; } else if (moveFlags & MOVEMENTFLAG_BACKWARD /*&& speed_obj.run >= speed_obj.run_back*/) return MOVE_RUN_BACK; // Flying creatures use MOVEMENTFLAG_CAN_FLY or MOVEMENTFLAG_DISABLE_GRAVITY // Run speed is their default flight speed. return MOVE_RUN; } int32 MoveSplineInit::Launch() { MoveSpline& move_spline = *unit->movespline; bool transport = !unit->GetTransGUID().IsEmpty(); Location real_position; // there is a big chance that current position is unknown if current state is not finalized, need compute it // this also allows calculate spline position and update map position in much greater intervals // Don't compute for transport movement if the unit is in a motion between two transports if (!move_spline.Finalized() && move_spline.onTransport == transport) real_position = move_spline.ComputePosition(); else { Position const* pos; if (!transport) pos = unit; else pos = &unit->m_movementInfo.transport.pos; real_position.x = pos->GetPositionX(); real_position.y = pos->GetPositionY(); real_position.z = pos->GetPositionZ(); real_position.orientation = unit->GetOrientation(); } // should i do the things that user should do? - no. if (args.path.empty()) return 0; // correct first vertex args.path[0] = real_position; args.initialOrientation = real_position.orientation; args.flags.Enter_Cycle = args.flags.Cyclic; move_spline.onTransport = transport; uint32 moveFlags = unit->m_movementInfo.GetMovementFlags(); if (!args.flags.Backward) moveFlags = (moveFlags & ~MOVEMENTFLAG_BACKWARD) | MOVEMENTFLAG_FORWARD; else moveFlags = (moveFlags & ~MOVEMENTFLAG_FORWARD) | MOVEMENTFLAG_BACKWARD; if (moveFlags & MOVEMENTFLAG_ROOT) moveFlags &= ~MOVEMENTFLAG_MASK_MOVING; if (!args.HasVelocity) { // If spline is initialized with SetWalk method it only means we need to select // walk move speed for it but not add walk flag to unit uint32 moveFlagsForSpeed = moveFlags; if (args.walk) moveFlagsForSpeed |= MOVEMENTFLAG_WALKING; else moveFlagsForSpeed &= ~MOVEMENTFLAG_WALKING; args.velocity = unit->GetSpeed(SelectSpeedType(moveFlagsForSpeed)); if (Creature* creature = unit->ToCreature()) if (creature->HasSearchedAssistance()) args.velocity *= 0.66f; } // limit the speed in the same way the client does float speedLimit = [&]() { if (args.flags.UnlimitedSpeed) return std::numeric_limits::max(); if (args.flags.Falling || args.flags.Catmullrom || args.flags.Flying || args.flags.Parabolic) return 50.0f; return std::max(28.0f, unit->GetSpeed(MOVE_RUN) * 4.0f); }(); args.velocity = std::min(args.velocity, speedLimit); if (!args.Validate(unit)) return 0; unit->m_movementInfo.SetMovementFlags(moveFlags); move_spline.Initialize(args); WorldPackets::Movement::MonsterMove packet; packet.MoverGUID = unit->GetGUID(); packet.Pos = Position(real_position.x, real_position.y, real_position.z, real_position.orientation); packet.InitializeSplineData(move_spline); if (transport) { packet.SplineData.Move.TransportGUID = unit->GetTransGUID(); packet.SplineData.Move.VehicleSeat = unit->GetTransSeat(); } unit->SendMessageToSet(packet.Write(), true); return move_spline.Duration(); } void MoveSplineInit::Stop() { MoveSpline& move_spline = *unit->movespline; // No need to stop if we are not moving if (move_spline.Finalized()) return; bool transport = !unit->GetTransGUID().IsEmpty(); Location loc; if (move_spline.onTransport == transport) loc = move_spline.ComputePosition(); else { Position const* pos; if (!transport) pos = unit; else pos = &unit->m_movementInfo.transport.pos; loc.x = pos->GetPositionX(); loc.y = pos->GetPositionY(); loc.z = pos->GetPositionZ(); loc.orientation = unit->GetOrientation(); } if (move_spline.isTurning()) SetFacing(loc.orientation); args.flags = MoveSplineFlagEnum::Done; unit->m_movementInfo.RemoveMovementFlag(MOVEMENTFLAG_FORWARD); move_spline.onTransport = transport; move_spline.Initialize(args); WorldPackets::Movement::MonsterMove packet; packet.MoverGUID = unit->GetGUID(); packet.Pos = Position(loc.x, loc.y, loc.z, loc.orientation); packet.SplineData.StopSplineStyle = 2; packet.SplineData.ID = move_spline.GetId(); packet.SplineData.StopUseFaceDirection = args.facing.type == MONSTER_MOVE_FACING_ANGLE; packet.SplineData.Move.Face = args.facing.type; packet.SplineData.Move.FaceDirection = args.facing.angle; if (transport) { packet.SplineData.Move.TransportGUID = unit->GetTransGUID(); packet.SplineData.Move.VehicleSeat = unit->GetTransSeat(); } unit->SendMessageToSet(packet.Write(), true); } MoveSplineInit::MoveSplineInit(Unit* m) : unit(m) { args.splineId = splineIdGen.NewId(); // Elevators also use MOVEMENTFLAG_ONTRANSPORT but we do not keep track of their position changes args.TransformForTransport = !unit->GetTransGUID().IsEmpty(); // mix existing state into new args.walk = unit->HasUnitMovementFlag(MOVEMENTFLAG_WALKING); args.flags.CanSwim = unit->CanSwim(); args.flags.Flying = unit->HasUnitMovementFlag(MovementFlags(MOVEMENTFLAG_CAN_FLY | MOVEMENTFLAG_DISABLE_GRAVITY)); args.flags.FastSteering = true; args.flags.Steering = unit->HasNpcFlag2(UNIT_NPC_FLAG_2_STEERING) || !unit->IsInCombat(); } MoveSplineInit::~MoveSplineInit() = default; void MoveSplineInit::SetFacing(Vector3 const& spot) { TransportPathTransform transform(unit, args.TransformForTransport); Vector3 finalSpot = transform(spot); args.facing.f.x = finalSpot.x; args.facing.f.y = finalSpot.y; args.facing.f.z = finalSpot.z; args.facing.type = MONSTER_MOVE_FACING_SPOT; } void MoveSplineInit::SetFacing(float x, float y, float z) { SetFacing({ x, y, z }); } void MoveSplineInit::SetFacing(Unit const* target) { args.facing.angle = unit->GetAbsoluteAngle(target); args.facing.target = target->GetGUID(); args.facing.type = MONSTER_MOVE_FACING_TARGET; } void MoveSplineInit::SetFacing(float angle) { TransportPathTransform transform(unit, args.TransformForTransport); args.facing.angle = Position::NormalizeOrientation(transform(angle)); args.facing.type = MONSTER_MOVE_FACING_ANGLE; } void MoveSplineInit::MovebyPath(PointsArray const& path, int32 pointId) { args.path_Idx_offset = pointId; args.path.resize(path.size()); std::ranges::transform(path, args.path.begin(), TransportPathTransform(unit, args.TransformForTransport)); } void MoveSplineInit::MoveTo(float x, float y, float z, bool generatePath, bool forceDestination) { MoveTo(G3D::Vector3(x, y, z), generatePath, forceDestination); } void MoveSplineInit::MoveTo(Vector3 const& dest, bool generatePath, bool forceDestination) { if (generatePath) { PathGenerator path(unit); bool result = path.CalculatePath(dest.x, dest.y, dest.z, forceDestination); if (result && !(path.GetPathType() & PATHFIND_NOPATH)) { MovebyPath(path.GetPath()); return; } } args.path_Idx_offset = 0; args.path.resize(2); TransportPathTransform transform(unit, args.TransformForTransport); args.path[1] = transform(dest); } void MoveSplineInit::SetFall() { args.flags.Falling = true; args.flags.FallingSlow = unit->HasUnitMovementFlag(MOVEMENTFLAG_FALLING_SLOW); } void MoveSplineInitFacingVisitor::operator()(Position const& point) const { _init.SetFacing(point.GetPositionX(), point.GetPositionY(), point.GetPositionZ()); } }