示例#1
0
 void operator()(uint8& mode, bool& cyclic, Movement::PointsArray& points, int& lo, int& hi) const
 {
     mode = Movement::SplineBase::ModeCatmullrom;
     cyclic = false;
     points.assign(_points.begin(), _points.end());
     lo = 1;
     hi = points.size() - 2;
 }
        void InitializeAI()
        {
            ScriptedAI::InitializeAI();

            Movement::PointsArray path;
            path.push_back(G3D::Vector3(me->GetPositionX(), me->GetPositionY(), me->GetPositionZ()));
            for (uint8 i = 0; i < WAYPOINTS_COUNT; ++i)
                path.push_back(G3D::Vector3(startPath[i].GetPositionX(), startPath[i].GetPositionY(), startPath[i].GetPositionZ()));

            me->GetMotionMaster()->MoveSplinePath(&path);
            me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC|UNIT_FLAG_IMMUNE_TO_NPC);
            me->Mount(SKARLOC_MOUNT_MODEL);
        }
示例#3
0
void MotionMaster::MoveSmoothPath(uint32 pointId, Position const* pathPoints, size_t pathSize, bool walk)
{
    Movement::MoveSplineInit init(_owner);
    Movement::PointsArray path;
    path.reserve(pathSize);
    std::transform(pathPoints, pathPoints + pathSize, std::back_inserter(path), [](Position const& point)
    {
        return G3D::Vector3(point.GetPositionX(), point.GetPositionY(), point.GetPositionZ());
    });

    init.MovebyPath(path);
    init.SetSmooth();
    init.SetWalk(walk);

    // This code is not correct
    // GenericMovementGenerator does not affect UNIT_STATE_ROAMING | UNIT_STATE_ROAMING_MOVE
    // need to call PointMovementGenerator with various pointIds
    Mutate(new GenericMovementGenerator(std::move(init), EFFECT_MOTION_TYPE, pointId), MOTION_SLOT_ACTIVE);
}
        void FillPath(Position const& pos, Movement::PointsArray& path)
        {
            G3D::Vector3 point;

            point.x = pos.GetPositionX();
            point.y = pos.GetPositionY();
            point.z = pos.GetPositionZ();

            point.x -= 1.0f;
            path.push_back(point);

            point.x += 1.0f;
            path.push_back(point);

            point.z += 25.0f;
            path.push_back(point);

            path.push_back(point);
        }
uint32 SplineChainMovementGenerator::SendPathSpline(Unit* me, Movement::PointsArray const& wp) const
{
    uint32 numWp = wp.size();
    ASSERT(numWp > 1 && "Every path must have source & destination");
    Movement::MoveSplineInit init(me);
    if (numWp > 2)
        init.MovebyPath(wp);
    else
        init.MoveTo(wp[1], false, true);
    init.SetWalk(_walk);
    return init.Launch();
}
uint32 SplineChainMovementGenerator::SendPathSpline(Unit* owner, Movement::PointsArray const& path) const
{
    uint32 nodeCount = path.size();
    ASSERT(nodeCount > 1, "SplineChainMovementGenerator::SendPathSpline: Every path must have source & destination (size > 1)! (%s)", owner->GetGUID().ToString().c_str());

    Movement::MoveSplineInit init(owner);
    if (nodeCount > 2)
        init.MovebyPath(path);
    else
        init.MoveTo(path[1], false, true);
    init.SetWalk(_walk);
    return init.Launch();
}
示例#7
0
void TransportMgr::GeneratePath(GameObjectTemplate const* goInfo, TransportTemplate* transport)
{
    uint32 pathId = goInfo->moTransport.taxiPathId;
    TaxiPathNodeList const& path = sTaxiPathNodesByPath[pathId];
    std::vector<KeyFrame>& keyFrames = transport->keyFrames;
    Movement::PointsArray splinePath;
    bool mapChange = false;
    bool cyclic = true;
    for (size_t i = 0; i < path.size(); ++i)
    {
        if (!mapChange)
        {
            TaxiPathNodeEntry const& node_i = path[i];
            if (i != path.size() - 1 && (node_i.actionFlag == 1 || node_i.mapid != path[i + 1].mapid))
            {
                cyclic = false;
                keyFrames.back().Teleport = true;
                mapChange = true;
            }
            else
            {
                KeyFrame k(node_i);
                keyFrames.push_back(k);
                splinePath.push_back(G3D::Vector3(node_i.x, node_i.y, node_i.z));
                transport->mapsUsed.insert(k.Node->mapid);
            }
        }
        else
            mapChange = false;
    }

    // Not sure if data8 means the transport can be stopped or that its path in dbc does not contain extra spline points
    if (!goInfo->moTransport.canBeStopped && splinePath.size() >= 2)
    {
        // Remove special catmull-rom spline points
        splinePath.erase(splinePath.begin());
        keyFrames.erase(keyFrames.begin());
        splinePath.pop_back();
        keyFrames.pop_back();
        // Cyclic spline has one more extra point
        if (cyclic && !splinePath.empty())
        {
            splinePath.pop_back();
            keyFrames.pop_back();
        }
    }

    ASSERT(!keyFrames.empty());

    if (transport->mapsUsed.size() > 1)
    {
        for (std::set<uint32>::const_iterator itr = transport->mapsUsed.begin(); itr != transport->mapsUsed.end(); ++itr)
            ASSERT(!sMapStore.LookupEntry(*itr)->Instanceable());

        transport->inInstance = false;
    }
    else
        transport->inInstance = sMapStore.LookupEntry(*transport->mapsUsed.begin())->Instanceable();

    // last to first is always "teleport", even for closed paths
    keyFrames.back().Teleport = true;

    const float speed = float(goInfo->moTransport.moveSpeed);
    const float accel = float(goInfo->moTransport.accelRate);
    const float accel_dist = 0.5f * speed * speed / accel;

    transport->accelTime = speed / accel;
    transport->accelDist = accel_dist;

    int32 firstStop = -1;
    int32 lastStop = -1;

    // first cell is arrived at by teleportation :S
    keyFrames[0].DistFromPrev = 0;
    keyFrames[0].Index = 1;
    if (keyFrames[0].IsStopFrame())
    {
        firstStop = 0;
        lastStop = 0;
    }

    // find the rest of the distances between key points
    // Every path segment has its own spline
    if (cyclic)
    {
        TransportSpline* spline = new TransportSpline();
        spline->init_cyclic_spline(&splinePath[0], splinePath.size(), Movement::SplineBase::ModeCatmullrom, 0);
        spline->initLengths();
        keyFrames[0].DistFromPrev = spline->length(spline->last() - 2, spline->last() - 1);
        keyFrames[0].Spline = spline;
        for (size_t i = 0; i < keyFrames.size(); ++i)
        {
            keyFrames[i].Index = i + 1;
            keyFrames[i].DistFromPrev = spline->length(i, i + 1);
            if (i > 0)
                keyFrames[i - 1].NextDistFromPrev = keyFrames[i].DistFromPrev;
            keyFrames[i].Spline = spline;
            if (keyFrames[i].IsStopFrame())
            {
                // remember first stop frame
                if (firstStop == -1)
                    firstStop = i;
                lastStop = i;
            }
        }
    }
    else
    {
        size_t start = 0;
        for (size_t i = 1; i < keyFrames.size(); ++i)
        {
            if (keyFrames[i - 1].Teleport || i + 1 == keyFrames.size())
            {
                size_t extra = !keyFrames[i - 1].Teleport ? 1 : 0;
                TransportSpline* spline = new TransportSpline();
                spline->init_spline(&splinePath[start], i - start + extra, Movement::SplineBase::ModeCatmullrom);
                spline->initLengths();
                for (size_t j = start; j < i + extra; ++j)
                {
                    keyFrames[j].Index = j - start + 1;
                    keyFrames[j].DistFromPrev = spline->length(j - start, j + 1 - start);
                    if (j > 0)
                        keyFrames[j - 1].NextDistFromPrev = keyFrames[j].DistFromPrev;
                    keyFrames[j].Spline = spline;
                }

                if (keyFrames[i - 1].Teleport)
                {
                    keyFrames[i].Index = i - start + 1;
                    keyFrames[i].DistFromPrev = 0.0f;
                    keyFrames[i - 1].NextDistFromPrev = 0.0f;
                    keyFrames[i].Spline = spline;
                }

                start = i;
            }

            if (keyFrames[i].IsStopFrame())
            {
                // remember first stop frame
                if (firstStop == -1)
                    firstStop = i;
                lastStop = i;
            }
        }
    }

    keyFrames.back().NextDistFromPrev = keyFrames.front().DistFromPrev;

    if (firstStop == -1 || lastStop == -1)
        firstStop = lastStop = 0;

    // at stopping keyframes, we define distSinceStop == 0,
    // and distUntilStop is to the next stopping keyframe.
    // this is required to properly handle cases of two stopping frames in a row (yes they do exist)
    float tmpDist = 0.0f;
    for (size_t i = 0; i < keyFrames.size(); ++i)
    {
        int32 j = (i + lastStop) % keyFrames.size();
        if (keyFrames[j].IsStopFrame() || j == lastStop)
            tmpDist = 0.0f;
        else
            tmpDist += keyFrames[j].DistFromPrev;
        keyFrames[j].DistSinceStop = tmpDist;
    }

    tmpDist = 0.0f;
    for (int32 i = int32(keyFrames.size()) - 1; i >= 0; i--)
    {
        int32 j = (i + firstStop) % keyFrames.size();
        tmpDist += keyFrames[(j + 1) % keyFrames.size()].DistFromPrev;
        keyFrames[j].DistUntilStop = tmpDist;
        if (keyFrames[j].IsStopFrame() || j == firstStop)
            tmpDist = 0.0f;
    }

    for (size_t i = 0; i < keyFrames.size(); ++i)
    {
        float total_dist = keyFrames[i].DistSinceStop + keyFrames[i].DistUntilStop;
        if (total_dist < 2 * accel_dist) // won't reach full speed
        {
            if (keyFrames[i].DistSinceStop < keyFrames[i].DistUntilStop) // is still accelerating
            {
                // calculate accel+brake time for this short segment
                float segment_time = 2.0f * sqrt((keyFrames[i].DistUntilStop + keyFrames[i].DistSinceStop) / accel);
                // substract acceleration time
                keyFrames[i].TimeTo = segment_time - sqrt(2 * keyFrames[i].DistSinceStop / accel);
            }
            else // slowing down
                keyFrames[i].TimeTo = sqrt(2 * keyFrames[i].DistUntilStop / accel);
        }
        else if (keyFrames[i].DistSinceStop < accel_dist) // still accelerating (but will reach full speed)
        {
            // calculate accel + cruise + brake time for this long segment
            float segment_time = (keyFrames[i].DistUntilStop + keyFrames[i].DistSinceStop) / speed + (speed / accel);
            // substract acceleration time
            keyFrames[i].TimeTo = segment_time - sqrt(2 * keyFrames[i].DistSinceStop / accel);
        }
        else if (keyFrames[i].DistUntilStop < accel_dist) // already slowing down (but reached full speed)
            keyFrames[i].TimeTo = sqrt(2 * keyFrames[i].DistUntilStop / accel);
        else // at full speed
            keyFrames[i].TimeTo = (keyFrames[i].DistUntilStop / speed) + (0.5f * speed / accel);
    }

    // calculate tFrom times from tTo times
    float segmentTime = 0.0f;
    for (size_t i = 0; i < keyFrames.size(); ++i)
    {
        int32 j = (i + lastStop) % keyFrames.size();
        if (keyFrames[j].IsStopFrame() || j == lastStop)
            segmentTime = keyFrames[j].TimeTo;
        keyFrames[j].TimeFrom = segmentTime - keyFrames[j].TimeTo;
    }

    // calculate path times
    keyFrames[0].ArriveTime = 0;
    float curPathTime = 0.0f;
    if (keyFrames[0].IsStopFrame())
    {
        curPathTime = float(keyFrames[0].Node->delay);
        keyFrames[0].DepartureTime = uint32(curPathTime * IN_MILLISECONDS);
    }

    for (size_t i = 1; i < keyFrames.size(); ++i)
    {
        curPathTime += keyFrames[i - 1].TimeTo;
        if (keyFrames[i].IsStopFrame())
        {
            keyFrames[i].ArriveTime = uint32(curPathTime * IN_MILLISECONDS);
            keyFrames[i - 1].NextArriveTime = keyFrames[i].ArriveTime;
            curPathTime += float(keyFrames[i].Node->delay);
            keyFrames[i].DepartureTime = uint32(curPathTime * IN_MILLISECONDS);
        }
        else
        {
            curPathTime -= keyFrames[i].TimeTo;
            keyFrames[i].ArriveTime = uint32(curPathTime * IN_MILLISECONDS);
            keyFrames[i - 1].NextArriveTime = keyFrames[i].ArriveTime;
            keyFrames[i].DepartureTime = keyFrames[i].ArriveTime;
        }
    }

    keyFrames.back().NextArriveTime = keyFrames.back().DepartureTime;

    transport->pathTime = keyFrames.back().DepartureTime;
}
bool WaypointMovementGenerator<Creature>::StartMove(Creature* creature)
{
    if (!creature || !creature->IsAlive())
        return false;

    if (!i_path || i_path->nodes.empty())
        return false;

    bool transportPath = creature->GetTransport() != nullptr;

    if (IsArrivalDone)
    {
        if ((i_currentNode == i_path->nodes.size() - 1) && !repeating) // If that's our last waypoint
        {
            WaypointNode const &waypoint = i_path->nodes.at(i_currentNode);

            float x = waypoint.x;
            float y = waypoint.y;
            float z = waypoint.z;
            float o = creature->GetOrientation();

            if (!transportPath)
                creature->SetHomePosition(x, y, z, o);
            else
            {
                if (Transport* trans = creature->GetTransport())
                {
                    o -= trans->GetOrientation();
                    creature->SetTransportHomePosition(x, y, z, o);
                    trans->CalculatePassengerPosition(x, y, z, &o);
                    creature->SetHomePosition(x, y, z, o);
                }
                else
                    transportPath = false;
                // else if (vehicle) - this should never happen, vehicle offsets are const
            }
            return false;
        }

        i_currentNode = (i_currentNode + 1) % i_path->nodes.size();
    }

    float finalOrient = 0.0f;
    uint8 finalMove = WAYPOINT_MOVE_TYPE_WALK;

    Movement::PointsArray pathing;
    pathing.reserve((i_path->nodes.size() - i_currentNode) + 1);

    pathing.push_back(G3D::Vector3(creature->GetPositionX(), creature->GetPositionY(), creature->GetPositionZ()));
    for (uint32 i = i_currentNode; i < i_path->nodes.size(); ++i)
    {
        WaypointNode const &waypoint = i_path->nodes.at(i);

        pathing.push_back(G3D::Vector3(waypoint.x, waypoint.y, waypoint.z));

        finalOrient = waypoint.orientation;
        finalMove = waypoint.moveType;

        if (waypoint.delay)
            break;
    }

    // if we have only 1 point, only current position, we shall return
    if (pathing.size() < 2)
        return false;

    IsArrivalDone = false;
    i_recalculateSpeed = false;

    creature->AddUnitState(UNIT_STATE_ROAMING_MOVE);

    Movement::MoveSplineInit init(creature);

    Movement::Location formationDest(i_path->nodes.at(i_currentNode).x, i_path->nodes.at(i_currentNode).y, i_path->nodes.at(i_currentNode).z, 0.0f);

    //! If creature is on transport, we assume waypoints set in DB are already transport offsets
    if (transportPath)
    {
        init.DisableTransportPathTransformations();
        if (TransportBase* trans = creature->GetDirectTransport())
            trans->CalculatePassengerPosition(formationDest.x, formationDest.y, formationDest.z, &formationDest.orientation);
    }

    init.MovebyPath(pathing, i_currentNode);

    switch (finalMove)
    {
        case WAYPOINT_MOVE_TYPE_LAND:
            init.SetAnimation(Movement::ToGround);
            break;
        case WAYPOINT_MOVE_TYPE_TAKEOFF:
            init.SetAnimation(Movement::ToFly);
            break;
        case WAYPOINT_MOVE_TYPE_RUN:
            init.SetWalk(false);
            break;
        case WAYPOINT_MOVE_TYPE_WALK:
            init.SetWalk(true);
            break;
    }

    if (finalOrient != 0.0f)
        init.SetFacing(finalOrient);

    init.Launch();

    //Call for creature group update
    if (creature->GetFormation() && creature->GetFormation()->getLeader() == creature)
        creature->GetFormation()->LeaderMoveTo(formationDest.x, formationDest.y, formationDest.z);

    return true;
}
        void UpdateAI(uint32 diff)
        {
            if (startPath)
            {
                me->StopMoving();
                startPath = false;
                if (WaypointPath const* i_path = sWaypointMgr->GetPath(me->GetWaypointPath()))
                {
                    Movement::PointsArray pathPoints;
                    pathPoints.push_back(G3D::Vector3(me->GetPositionX(), me->GetPositionY(), me->GetPositionZ()));
                    for (uint8 i = 0; i < i_path->size(); ++i)
                    {
                        WaypointData const* node = i_path->at(i);
                        pathPoints.push_back(G3D::Vector3(node->x, node->y, node->z));
                    }
                    me->GetMotionMaster()->MoveSplinePath(&pathPoints);
                }
            }

            if (!UpdateVictim())
                return;

            events.Update(diff);

            if (me->HasUnitState(UNIT_STATE_CASTING))
                return;

            switch (events.GetEvent())
            {
                case 0:
                    break;
                case EVENT_ENRAGE:
                    me->CastSpell(me, SPELL_BERSERK, true);
                    events.RepeatEvent(600000);
                    break;
                case EVENT_COMMANDER_SAY_AGGRO:
                    if (Creature* commander = ObjectAccessor::GetCreature(*me, CommanderGUID))
                        commander->AI()->Talk(SAY_COMMANDER_AGGRO);
                    events.PopEvent();
                    break;
                case EVENT_EE_SAY_MOVE_OUT:
                    for (uint8 i=0; i<3; ++i)
                        if (Creature* c = ObjectAccessor::GetCreature(*me, ExpeditionEngineerGUIDs[i]))
                        {
                            if (!i)
                                c->MonsterYell(TEXT_EE_MOVE_OUT, LANG_UNIVERSAL, 0);
                            c->AI()->SetData(1, 0); // start repairing
                        }
                    events.PopEvent();
                    break;
                case EVENT_SPELL_FIREBALL:
                    if( Unit *pTarget = SelectTarget(SELECT_TARGET_RANDOM, 0, 200.0f, true) )
                        me->CastSpell(pTarget, SPELL_FIREBALL, false);
                    events.RepeatEvent(4000);
                    break;
                case EVENT_SPELL_DEVOURING_FLAME:
                    if( Unit *pTarget = SelectTarget(SELECT_TARGET_RANDOM, 0, 200.0f, true) )
                        me->CastSpell(pTarget, SPELL_DEVOURINGFLAME, false);
                    events.RepeatEvent(13000);
                    break;
                case EVENT_SUMMON_MOLE_MACHINES:
                    {
                        memset(cords, '\0', sizeof(cords));
                        uint8 num = RAID_MODE( urand(2,3), urand(2,4) );
                        for( int i=0; i<num; ++i )
                        {
                            // X: (550, 625) Y: (-185, -230)
                            cords[i][0] = urand(550, 625);
                            cords[i][1] = -230 + rand()%45;
                            if( GameObject* drill = me->SummonGameObject(GO_DRILL, cords[i][0], cords[i][1], 391.1f, M_PI/4, 0.0f, 0.0f, 0.0f, 0.0f, 8) )
                            {
                                //drill->SetGoAnimProgress(0);
                                //drill->SetLootState(GO_READY);
                                //drill->UseDoorOrButton(8);
                                //drill->SetGoState(GO_STATE_READY);
                                drill->SetGoState(GO_STATE_ACTIVE);
                                drill->SetGoAnimProgress(0);
                            }
                        }
                        events.RepeatEvent(45000);
                        events.RescheduleEvent(EVENT_SUMMON_ADDS, 4000);
                    }
                    break;
                case EVENT_SUMMON_ADDS:
                    for( int i=0; i<4; ++i )
                    {
                        if( !cords[i][0] )
                            break;

                        uint8 opt;
                        uint8 r = urand(1,100);
                        if( r <= 30 ) opt = 1;
                        else if( r <= 65 ) opt = 2;
                        else opt = 3;

                        for( int j=0; j<4; ++j )
                        {
                            float x = cords[i][0] + 4.0f*cos(j*M_PI/2);
                            float y = cords[i][1] + 4.0f*sin(j*M_PI/2);

                            uint32 npc_entry = 0;
                            switch( opt )
                            {
                                case 1: if( j == 1 ) npc_entry = NPC_DARK_RUNE_SENTINEL; break;
                                case 2:
                                    switch( j )
                                    {
                                        case 1: npc_entry = NPC_DARK_RUNE_WATCHER; break;
                                        case 2: npc_entry = NPC_DARK_RUNE_GUARDIAN; break;
                                    }
                                    break;
                                default: // case 3:
                                    switch( j )
                                    {
                                        case 1: npc_entry = NPC_DARK_RUNE_WATCHER; break;
                                        case 2: npc_entry = NPC_DARK_RUNE_GUARDIAN; break;
                                        case 3: npc_entry = NPC_DARK_RUNE_GUARDIAN; break;
                                    }
                                    break;
                            }

                            if( npc_entry )
                                if (Creature* c = me->SummonCreature(npc_entry, x, y, 391.1f, j*M_PI/2, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 5000))
                                    DoZoneInCombat(c);

                        }
                    }
                    events.PopEvent();
                    break;
                case EVENT_WARN_DEEP_BREATH:
                    me->MonsterTextEmote(TEXT_DEEP_BREATH, 0, true);
                    me->RemoveAura(62794);
                    events.PopEvent();
                    events.ScheduleEvent(EVENT_PHASE2_FLAME_BREATH, 2500);
                    break;
                case EVENT_PHASE2_FLAME_BREATH:
                    me->CastSpell(me, S_FLAMEBREATH, true);
                    events.PopEvent();
                    events.ScheduleEvent(EVENT_FLY_UP, 2000);
                    break;
                case EVENT_FLY_UP:
                    me->SetInCombatWithZone(); // just in case
                    if (pInstance)
                        for( int i=0; i<4; ++i )
                            if( uint64 guid = pInstance->GetData64(DATA_HARPOON_FIRE_STATE_1 + i) )
                                if( Creature* hfs = ObjectAccessor::GetCreature(*me, guid) )
                                {
                                    me->SummonCreature(34188, hfs->GetPositionX(), hfs->GetPositionY(), hfs->GetPositionZ(), 0, TEMPSUMMON_TIMED_DESPAWN, 22000);
                                    hfs->AI()->SetData(1, 0);
                                }

                    me->RemoveAura(SPELL_LAUNCH_CHAIN);
                    me->RemoveAura(SPELL_CHAIN_1);
                    me->RemoveAura(SPELL_CHAIN_3);
                    if (RAID_MODE(0,1))
                    {
                        me->RemoveAura(SPELL_CHAIN_2);
                        me->RemoveAura(SPELL_CHAIN_4);
                    }
                    me->CastSpell(me, SPELL_WINGBUFFET, true);
                    
                    if( (me->GetHealth()*100) / me->GetMaxHealth() < 50 ) // start phase 3
                    {
                        me->SetControlled(false, UNIT_STATE_ROOT);
                        me->DisableRotate(false);
                        DoResetThreat();
                        Unit* target = SelectTarget(SELECT_TARGET_NEAREST, 0, 0.0, true);
                        if (!target)
                            target = me->SelectNearestPlayer(200.0f);
                        if (target)
                        {
                            AttackStart(target);
                            me->GetMotionMaster()->MoveChase(target);
                        }
                        bGroundPhase = true;
                        events.PopEvent();
                        events.CancelEvent(EVENT_SPELL_FIREBALL);
                        events.CancelEvent(EVENT_SPELL_DEVOURING_FLAME);
                        events.CancelEvent(EVENT_SUMMON_MOLE_MACHINES);

                        events.ScheduleEvent(EVENT_SPELL_FLAME_BREATH, 20000);
                        events.ScheduleEvent(EVENT_SPELL_DEVOURING_FLAME_GROUND, 5000);
                        events.ScheduleEvent(EVENT_SPELL_FUSE_ARMOR, 10000);
                        events.ScheduleEvent(EVENT_SPELL_FLAME_BUFFET, 3000);

                        break;
                    }
                    else
                    {
                        ++flyTimes;
                        me->SetControlled(false, UNIT_STATE_ROOT);
                        me->DisableRotate(false);
                        me->SendMeleeAttackStop(me->GetVictim());
                        me->GetMotionMaster()->MoveIdle();
                        me->StopMoving();
                        me->SetCanFly(true);
                        me->SetDisableGravity(true);
                        me->SetHover(true);
                        me->SendMovementFlagUpdate();
                        me->GetMotionMaster()->MoveTakeoff(1, CORDS_AIR, 25.0f);
                        events.ScheduleEvent(EVENT_RESUME_FIXING, 22000);
                    }

                    events.PopEvent();
                    break;
                case EVENT_RESUME_FIXING:
                    for (uint8 i=0; i<3; ++i)
                        if (Creature* c = ObjectAccessor::GetCreature(*me, ExpeditionEngineerGUIDs[i]))
                        {
                            if (!i)
                                c->MonsterYell(TEXT_EE_FIRES_OUT, LANG_UNIVERSAL, 0);
                            c->AI()->SetData(1, 0); // start repairing
                        }
                    events.PopEvent();
                    break;
                case EVENT_SPELL_FLAME_BREATH:
                    me->CastSpell(me->GetVictim(), S_FLAMEBREATH, false);
                    events.RepeatEvent(20000);
                    break;
                case EVENT_SPELL_DEVOURING_FLAME_GROUND:
                    me->CastSpell(me->GetVictim(), SPELL_DEVOURINGFLAME, false);
                    events.RepeatEvent(13000);
                    break;
                case EVENT_SPELL_FUSE_ARMOR:
                    if (Unit* victim = me->GetVictim())
                        if (me->IsWithinMeleeRange(victim))
                        {
                            me->CastSpell(victim, SPELL_FUSEARMOR, false);
                            if (Aura* aur = victim->GetAura(SPELL_FUSEARMOR))
                                if (aur->GetStackAmount() == 5)
                                    victim->CastSpell(victim, 64774, true);
                            events.RepeatEvent(10000);
                            break;
                        }
                    events.RepeatEvent(2000);
                    break;
                case EVENT_SPELL_FLAME_BUFFET:
                    me->CastSpell(me->GetVictim(), S_FLAMEBUFFET, false);
                    events.RepeatEvent(7000);
                    break;
            }

            if (bGroundPhase)
                DoMeleeAttackIfReady();
        }