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