//Uses fixed-point.

class MyMass:CMass
{
    Bool collision;
};

#define MAP_WIDTH       2048
#define MAP_HEIGHT      (GR_HEIGHT - 3 * FONT_HEIGHT)

I64  gun_x, gun_y, active_map = 0, gun_recoil;
F64  gun_theta;
CDC *map_dcs[2] = {NULL, NULL};
I16  elevs[MAP_WIDTH];

F64 wind_x;
#define DUST_NUM        512
I64 dust_x[DUST_NUM], dust_y[DUST_NUM];

CMathODE *ode = NULL;

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


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

U0 DrawIt(CTask *task, CDC *dc)
{
    CDC     *map = map_dcs[active_map&1];
    MyMass  *tmpm;
    F64      theta = gun_theta;
    I64      i, x, y, w, 
             h = -task->horz_scroll.pos, 
             v = -task->vert_scroll.pos;

    task->horz_scroll.min = 0;
    task->horz_scroll.max = MAP_WIDTH  - task->pix_width;
    task->vert_scroll.min = 0;
    task->vert_scroll.max = MAP_HEIGHT - task->pix_height;
    map->flags |= DCF_NO_TRANSPARENTS;
    GrBlot(dc, h, v, map);

    Sprite3(dc, gun_x + h, gun_y + v, 0, <2>);

    if (theta < -pi / 2)
    {
        dc->flags |= DCF_SYMMETRY | DCF_JUST_MIRROR;
        DCSymmetrySet(dc, gun_x + h, 0, gun_x + h, 1);
        theta = -pi - theta;
    }
    Sprite3ZB(dc, gun_x + h - gun_recoil * Cos(theta), gun_y + v - gun_recoil * Sin(theta) - 10, 0, <1>, theta);
    dc->flags &= ~(DCF_SYMMETRY | DCF_JUST_MIRROR);

    tmpm = ode->next_mass;
    dc->color = BLACK;
    map->color = ROP_COLLISION;
    map->bkcolor = LTCYAN;
    while (tmpm != &ode->next_mass)
    {
        map->collision_count = 0;
        GrCircle(map, tmpm->x, tmpm->y, 2);
        if (map->collision_count)
            tmpm->collision = TRUE;

        GrCircle(dc, tmpm->x + h, tmpm->y + v, 2);

        tmpm = tmpm->next;
    }

    dc->color = LTGRAY;
    w = tS * wind_x;
    for (i = 0; i < DUST_NUM; i++)
    {
        x = (dust_x[i] + w) % MAP_WIDTH;
        y = dust_y[i];
        if (y < elevs[x])
            GrPlot(dc, x + h, y + v);
    }
}

U0 MyDerivative(CMathODE *ode, F64, COrder2D3 *, COrder2D3 *)
{
    MyMass *tmpm = ode->next_mass;

    while (tmpm != &ode->next_mass)
    {
        tmpm->DstateDt->DyDt += 1000.0 * tmpm->mass;
        tmpm->DstateDt->DxDt += 25.0 * wind_x;
        tmpm = tmpm->next;
    }
}

U0 DrawMap()
{
    CDC *map = map_dcs[(active_map + 1) & 1];
    I64  x;

    map->color = LTCYAN;
    GrRect(map, 0, 0, MAP_WIDTH, MAP_HEIGHT);

    map->color = BLACK;
    for (x = 1; x < MAP_WIDTH; x++)
        GrLine(map, x - 1, elevs[x - 1], x, elevs[x]);

    map->color = BROWN;
    GrFloodFill(map, 0, MAP_HEIGHT - 1, FALSE);

    active_map++;
}

//U0 FireTask(I64)
U0 FireTask()
{
    MyMass *tmpm;
    I64     i;

    if (gun_recoil)
        return;

    tmpm = CAlloc(sizeof(MyMass), Fs->parent_task);
    tmpm->mass                  = 10.0;
    tmpm->drag_profile_factor   = 0.1;
    tmpm->x                     = gun_x + 27 * Cos(gun_theta);
    tmpm->y                     = gun_y - 15 + 27 * Sin(gun_theta);
    tmpm->DxDt                  = 600.0 * Cos(gun_theta);
    tmpm->DyDt                  = 600.0 * Sin(gun_theta);
    tmpm->collision = FALSE;
    while (sys_task_being_screen_updated == Fs->parent_task)
        Yield;
    QueueInsert(tmpm, ode->last_mass);

//  Fs->task_end_cb = &SoundTaskEndCB;
    for (i = 0; i < 60; i++)
    {
        Sound(50 * Rand + 10);
        Sleep(2);
        gun_recoil = i / 12;
    }
    Sound;
    for (i = 0; i <= 60; i++)
    {
        Sleep(1);
        gun_recoil = 5 - i / 12;
    }
}

U0 ManageShots()
{
    I64     i;
    MyMass *tmpm, *tmpm1;
    Bool    chged = FALSE;

    tmpm = ode->next_mass;
    while (tmpm != &ode->next_mass)
    {
        tmpm1 = tmpm->next;
        if (!(0 <= tmpm->x  < MAP_WIDTH) || tmpm->collision)
        {
            QueueRemove(tmpm);
            for (i = tmpm->x - 4; i <= tmpm->x + 4; i++)
                if (0 <= i < MAP_WIDTH)
                    elevs[i] = ClampI64(elevs[i] + 10 - 2 * AbsI64(i - tmpm->x), 0, MAP_HEIGHT - 2);
            Free(tmpm);
            chged = TRUE;
        }
        tmpm = tmpm1;
    }
    if (chged)
        DrawMap;
}

//U0 MoveTask(I64)
U0 MoveTask()
{
//  static F64 quit_time = 0;
    F64 quit_time;

//  if (quit_time)
//      quit_time = tS + 0.1;
//  else
//  {
        Sound(34);
//      Fs->task_end_cb = &SoundTaskEndCB;
        quit_time = tS + 0.1;
        while (quit_time > tS)
            Sleep(1);
//          Yield;
        Sound;
//      quit_time = 0;
//  }
}

U0 Init()
{
    CDC *map;
    I64  i, x, y, dy;

    if (!map_dcs[0])
        map_dcs[0] = DCNew(MAP_WIDTH, MAP_HEIGHT);
    if (!map_dcs[1])
        map_dcs[1] = DCNew(MAP_WIDTH, MAP_HEIGHT);
    map = map_dcs[active_map & 1];
    Fs->horz_scroll.pos = 0;
    Fs->vert_scroll.pos = 0;

    y = ToI64(0.7 * MAP_HEIGHT) << 32;
    dy = 0;
    for (x = 0; x < MAP_WIDTH; x++)
    {
        dy = ClampI64(SignI64(RandI16) << 30 + dy, -3 << 32, 3 << 32);
        y = ClampI64(y + dy, ToI64(0.3 * MAP_HEIGHT) << 32, (MAP_HEIGHT - 2) << 32);
        elevs[x] = y.i32[1];
    }
    gun_x = RandU32 % (MAP_WIDTH - 100) + 50;
    gun_y = elevs[gun_x];
    gun_theta = 0;
    gun_recoil = 0;
    for (x = gun_x - 20; x <= gun_x + 20; x++)
        elevs[x] = gun_y;

    wind_x = RandI16 / 250.0;
    for (i = 0; i < DUST_NUM; i++)
    {
        dust_x[i] = RandU16 % MAP_WIDTH;
        dust_y[i] = RandU16 % MAP_HEIGHT;
    }

    ode = ODENew(0, 1e-4, ODEF_HAS_MASSES);
    ode->derive             = &MyDerivative;
    ode->drag_v2            = 0.002;
    ode->drag_v3            = 0.0001;
    ode->acceleration_limit = 5e5;
    QueueInsert(ode, Fs->last_ode);
    Fs->horz_scroll.min = 0;
    Fs->horz_scroll.max = MAP_WIDTH - Fs->pix_width;
    Fs->horz_scroll.pos = gun_x - Fs->pix_width / 2;
    Fs->vert_scroll.min = 0;
    Fs->vert_scroll.max = MAP_HEIGHT - Fs->pix_height;
    Fs->vert_scroll.pos = 0;
    TaskDerivedValsUpdate;

    DrawMap;
}

U0 CleanUp(CMathODE *ode)
{
    if (ode)
    {
        QueueRemove(ode);
        QueueDel(&ode->next_mass, TRUE);
        ODEDel(ode);
    }
}

U0 BigGuns()
{
    I64 ch, sc;

    PopUpOk(    "Terry refused to rip-off the original\n"
                "so this is intentionally crappy\n"
                "and included for demonstration\n"
                "purposes.\n\n"
                "Write games, don't play them.\n");

    PopUpOk("The map scrolls.\n");

    SettingsPush; //See SettingsPush

    MenuPush(   "File {"
                "  Abort(,CH_SHIFT_ESC);"
                "  Exit(,CH_ESC);"
                "}"
                "Play {"
                "  Restart(,'\n');"
                "  Fire(,CH_SPACE);"
                "  Left(,,SC_CURSOR_LEFT);"
                "  Right(,,SC_CURSOR_RIGHT);"
                "}"
                );

    AutoComplete;
    WinBorder(ON);
    WinMax;
    DocCursor;
    DocClear;
    DocScroll;
    Init;
    Fs->draw_it = &DrawIt;
    try
    {
        while (TRUE)
        {
            while (KeyScan(&ch, &sc))
            {
                switch (ch)
                {
                    case 0:
                        switch (sc.u8[0])
                        {
                            case SC_CURSOR_RIGHT:
                                gun_theta += 5.0 * pi / 180;
                                if (gun_theta > 0)
                                    gun_theta = 0;
                                else
                                    MoveTask;
//                                  Spawn(&MoveTask, NULL, "Move",, Fs);
                                break;
                            case SC_CURSOR_LEFT:
                                gun_theta -= 5.0 * pi / 180;
                                if (gun_theta < -pi)
                                    gun_theta = -pi;
                                else
                                    MoveTask;
//                                  Spawn(&MoveTask, NULL, "Move",, Fs);
                                break;
                        }
                        break;

                    case '\n':
                        CleanUp(ode);
                        Init;
                        break;

                    case CH_SPACE:
//                      Spawn(&FireTask, NULL, "Fire",, Fs);
                        FireTask;
                        break;

                    case CH_SHIFT_ESC:
                    case CH_ESC:
                        goto bg_done;
                }
                ManageShots;
                FlushMessages;
            }
            ManageShots;
            Refresh;
        }
bg_done:
    }
    catch
        PutExcept;
    SettingsPop;
    DCDel(map_dcs[0]);
    map_dcs[0] = NULL;
    DCDel(map_dcs[1]);
    map_dcs[1] = NULL;
    CleanUp(ode);
    MenuPop;
}

BigGuns;