#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;