#define THRUST  100

Bool    blast_off;
CMass   m1, //Bottom of rocket
                m2; //Top of rocket
CSpring s;

#define ROCKET_HEIGHT   40
#define GROUND_Y                (GR_HEIGHT - 3 * FONT_HEIGHT)


        <1>/* Graphics Not Rendered in HTML */
















/* Graphics Not Rendered in HTML */

CDC *dc2;

U0 DrawIt(CTask *task, CDC *dc)
{
        I64  i, x, y, cx = GR_WIDTH / 2, cy = GROUND_Y;
        Bool engine_on;
        F64  nozzle_angle, theta = Arg(m2.x - m1.x, m2.y - m1.y);

        Sprite3(dc, 0, GROUND_Y, 0, <2>);

        if (Bt(kbd.down_bitmap, SC_CURSOR_UP))
        {
                nozzle_angle = 0;
                engine_on = TRUE;
        }
        else if (Bt(kbd.down_bitmap, SC_CURSOR_LEFT))
        {
                nozzle_angle = pi / 8;
                engine_on = TRUE;
        }
        else if (Bt(kbd.down_bitmap, SC_CURSOR_RIGHT))
        {
                nozzle_angle = -pi / 8;
                engine_on = TRUE;
        }
        else
                engine_on = FALSE;

        if (engine_on)
        {
                x = m1.x - 10 * Cos(theta+nozzle_angle);
                y = m1.y - 10 * Sin(theta+nozzle_angle);
                for (i = 0; i < 6; i++)
                {
                        if ((i ^ winmgr.updates) & 1)
                                dc->color = YELLOW;
                        else
                                dc->color = RED;
                        GrLine(dc, cx + (m1.x + i * Cos(theta - pi / 2)), cy - (m1.y + i * Sin(theta - pi / 2)), cx + x, cy - y);
                        GrLine(dc, cx + (m1.x + i * Cos(theta + pi / 2)), cy - (m1.y + i * Sin(theta + pi / 2)), cx + x, cy - y);
                }

                for (i = 0; i < 10; i++)
                {
                        switch (RandU16 & 3)
                        {
                                case 0: dc2->color = WHITE;     break;
                                case 1: dc2->color = LTGRAY;    break;
                                case 2: dc2->color = DKGRAY;    break;
                                case 3: dc2->color = BLACK;     break;
                        }
                        GrPlot(dc2, cx + (x + RandU16 % 12 - 6), cy - (y + RandU16 % 12 - 6));
                }
                Sound(22);
        }
        else
                Sound;
        Sprite3ZB(dc, cx + (m1.x + m2.x) / 2, cy - (m1.y + m2.y) / 2, 0, <1>, -theta);
}

U0 MyDerivative(CMathODE *, F64, COrder2D3 *, COrder2D3 *)
{
        Bool engine_on;
        F64  nozzle_angle, theta = Arg(m2.state->x - m1.state->x, m2.state->y - m1.state->y);

        if (Bt(kbd.down_bitmap, SC_CURSOR_UP))
        {
                nozzle_angle = 0;
                engine_on = TRUE;
        }
        else if (Bt(kbd.down_bitmap, SC_CURSOR_LEFT))
        {
                nozzle_angle = pi / 8;
                engine_on = TRUE;
        }
        else if (Bt(kbd.down_bitmap, SC_CURSOR_RIGHT))
        {
                nozzle_angle = -pi / 8;
                engine_on = TRUE;
        }
        else
                engine_on = FALSE;

        if (engine_on)
        {
                m1.DstateDt->DxDt += THRUST * Cos(theta + nozzle_angle);
                m1.DstateDt->DyDt += THRUST * Sin(theta + nozzle_angle);
        }
        if (blast_off)
        {
                m1.DstateDt->DyDt -= 25; //Gravity
                m2.DstateDt->DyDt -= 25;
        }
}

U0 Init()
{
        DocClear;
        "$BG,LTCYAN$$GREEN$Up, Left, Right$FG$%h*c", ToI64(GROUND_Y / FONT_HEIGHT), '\n';

        blast_off = FALSE;

        //We don't clear que links.
        MemSet(&m1.start, 0, offset(CMass.end) - offset(CMass.start));
        m1.y = 0;

        MemSet(&m2.start, 0, offset(CMass.end) - offset(CMass.start));
        m2.y = ROCKET_HEIGHT;

        MemSet(&s.start, 0, offset(CSpring.end) - offset(CSpring.start));
        s.end1          = &m1;
        s.end2          = &m2;
        s.rest_len      = ROCKET_HEIGHT;
        s.const         = 10000;

        DCFill;
}

U0 TaskEndCB()
{
        DCFill;
        SoundTaskEndCB;
}

U0 Rocket()
{
        CMathODE *ode = ODENew(0, 1e-2, ODEF_HAS_MASSES);

        SettingsPush; //See SettingsPush
        Fs->text_attr = YELLOW << 4 + BLUE;
        MenuPush(       "File {"
                                "  Abort(,CH_SHIFT_ESC);"
                                "  Exit(,CH_ESC);"
                                "}"
                                "Play {"
                                "  Restart(,'\n');"
                                "  Up(,,SC_CURSOR_UP);"
                                "  UpLeft(,,SC_CURSOR_LEFT);"
                                "  UpRight(,,SC_CURSOR_RIGHT);"
                                "}"
                                );

        AutoComplete;
        WinBorder;
        WinMax;
        DocCursor;
        DocClear;
        dc2 = DCAlias;
        Fs->task_end_cb = &TaskEndCB;

        ode->derive                             = &MyDerivative;
        ode->drag_v2                    = 0.002;
        ode->drag_v3                    = 0.00001;
        ode->acceleration_limit = 5e3;

        Init;
        QueueInsert(&m1, ode->last_mass);
        QueueInsert(&m2, ode->last_mass);
        QueueInsert(&s, ode->last_spring);

        QueueInsert(ode, Fs->last_ode);

        Fs->draw_it = &DrawIt;

        try
        {
                KeyGet;
                blast_off = TRUE;
                while (TRUE)
                {
                        switch (CharGet(, FALSE))
                        {
                                case '\n':
                                        Init;
                                        KeyGet;
                                        blast_off = TRUE;
                                        break;

                                case CH_ESC:
                                case CH_SHIFT_ESC:
                                        goto rk_done;
                        }
                }
rk_done:
        }
        catch
                PutExcept;
        QueueRemove(ode);
        ODEDel(ode);
        DocClear;
        SettingsPop;
        DCFill;
        DCDel(dc2);
        MenuPop;
}

Rocket;