RegDefault("ZealOS/BomberGolf", "I64 best_score=99999;\n");
RegExe("ZealOS/BomberGolf");



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




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



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


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


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



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




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




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




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




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


#define MAP_WIDTH           600
#define MAP_HEIGHT          600

#define TREES_NUM           64
class Tree
{
    I64 x, y;

} trees[TREES_NUM];
 

I64 target_count, key_count;


#define TARGETS_NUM         10

#define MDF_TANK            0
#define MDF_BUNKER          1

#define TANK_V                  10.0

class Target
{
    F64  x, y, theta;
    U8  *alive_img, *dead_img1, *dead_img2;
    U8   type;
    Bool dead, pad[6];

} targets[TARGETS_NUM];

#define FALL_TIME           3.00
#define EXPLODE_TIME        0.25
class Bomb
{
    Bomb *next, *last;
    F64   x, y, t;
    Bool  exploding;

} bomb_head;
 
F64 v, x, y, theta, thetaf; //thetaf is the final theta. theta is gradually changed until it reaches thetaf.

U0 DrawIt(CTask *task, CDC *dc)
{
    I64      i, j;
    Bomb    *tmpb;
    Target  *tmpt;
    F64      ts = tS, dx, dy;

    dc->color = ROPF_DITHER|BROWN << 16 | YELLOW;
    GrRect3(dc, 0, 0, 0, dc->width, dc->height);

    dc->x = task->pix_width  >> 1;
    dc->y = task->pix_height >> 1;
    dc->flags |= DCF_TRANSFORMATION;
    Mat4x4TranslationEqu(dc->r, x, y, 0);
    Mat4x4RotZ(dc->r, theta);
    dc->color = BLACK;
    GrBorder(dc, -MAP_WIDTH >> 1, -MAP_HEIGHT >> 1, MAP_WIDTH >> 1, MAP_HEIGHT >> 1);
    for (i = 0; i < TARGETS_NUM; i++)
    {
        tmpt = &targets[i];
        if (tmpt->dead)
        {
            if (i & 1)
            {
                dc->flags |= DCF_SYMMETRY | DCF_JUST_MIRROR;
                DCSymmetry3Set(dc,  tmpt->x, tmpt->y, 0,
                                    tmpt->x, tmpt->y, 1,
                                    tmpt->x + 1024 * Cos(tmpt->theta), tmpt->y + 1024 * Sin(tmpt->theta), 0);
            }
            if (Blink(15))
                Sprite3ZB(dc, tmpt->x, tmpt->y, 0, tmpt->dead_img1, tmpt->theta);
            else
                Sprite3ZB(dc, tmpt->x, tmpt->y, 0, tmpt->dead_img2, tmpt->theta);
            dc->flags &= ~(DCF_SYMMETRY|DCF_JUST_MIRROR);
        }
        else
            Sprite3ZB(dc, tmpt->x, tmpt->y, 0, tmpt->alive_img, tmpt->theta);
    }

    for (i = 0; i < TREES_NUM; i++)
        Sprite3(dc, trees[i].x, trees[i].y, 0, <2>);

    for (i = 0; i < TARGETS_NUM; i++)
    {
        tmpt = &targets[i];
        if (tmpt->dead)
        {
            for (j = 0; j < 40; j++)
            {
                dc->thick = 4;
                if (j & 1)
                    dc->color = ROPF_DITHER | LTGRAY << 16 | BLACK;
                else
                    dc->color = ROPF_DITHER | DKGRAY << 16 | LTGRAY;
                dx = 15 * Sin(ts / 4 + j << 6) + j >> 2;
                dy = 10 * FullTri(ts / 3 + j / 2.0, 20) + j >> 2;
                GrPlot3(dc, tmpt->x + 5 + dx + 0.5 * dy, tmpt->y + 0.5 * dx + dy, 0);
            }
        }
    }

    tmpb = bomb_head.next;
    while (tmpb != &bomb_head)
    {
        if (tmpb->t + FALL_TIME < tS)
        {
            if (Blink(10))
                Sprite3(dc, tmpb->x, tmpb->y, 0, <9>);
            else
                Sprite3(dc, tmpb->x, tmpb->y, 0, <10>);
        }
        tmpb = tmpb->next;
    }

    dc->flags &= ~DCF_TRANSFORMATION;
    Sprite3(dc, task->pix_width >> 1, task->pix_height >> 1, 0, <1>);
    dc->color = RED;
    GrPrint(dc, 0, 0, "Targets:%02d KeyStrokes:%04d Best:%04d", target_count, key_count, best_score);
    if (!target_count && Blink(4))
        GrPrint(dc, (task->pix_width  - FONT_WIDTH * 14) >> 1, 
                    (task->pix_height - FONT_HEIGHT)     >> 1 - 32, "Game Completed");
}
 
U0 BombHit(Bomb *tmpb)
{
    I64 i;

    for (i = 0; i < TARGETS_NUM; i++)
    {
        if (!targets[i].dead && SqrI64(tmpb->x - targets[i].x) + SqrI64(tmpb->y - targets[i].y) < 20 * 20)
        {
            targets[i].dead = TRUE;
            target_count--;
        }
    }
    QueueRemove(tmpb);
    Free(tmpb);
}
 
U0 BombDrop(F64 x, F64 y)
{
    Bomb *tmpb = MAlloc(sizeof(Bomb));

    tmpb->x = x;
    tmpb->y = y;
    tmpb->t = tS;
    tmpb->exploding = FALSE;
    QueueInsert(tmpb, bomb_head.last);
    Sweep(FALL_TIME * 1000, 74, 62);
}
 
I64 AnimateTask(CTask *)
{
    F64      last_t = tS, dt;
    I64      i;
    Bomb    *tmpb, *tmpb1;
    Target  *tmpt;

    while (TRUE)
    {
        dt = tS - last_t;
        last_t = tS;

        if (bomb_head.next == &bomb_head)
        {
            if (target_count)
                Sound(Freq2Ona(100 + 60 * Clamp(0.1 * (1.0 + Abs(Wrap(thetaf - theta, -pi))) ` 4.0, -3, 3)));
            else if (key_count < best_score)
            {
                best_score = key_count;
                Sleep(150);
                Sound(74);
                Sleep(150);
                Sound;
                Sleep(150);
                Sound(74);
                Sleep(150);
                Sound;
            }
            else
                Sound;
        }

        theta += dt * (thetaf - theta);
        x += dt * v * Sin(theta);
        y += dt * v * Cos(theta);

        for (i = 0; i < TARGETS_NUM; i++)
        {
            tmpt = &targets[i];
            if (!tmpt->dead && tmpt->type == MDF_TANK)
            {
                tmpt->x += dt * TANK_V * Cos(tmpt->theta);
                tmpt->y += dt * TANK_V * Sin(tmpt->theta);
                if (i & 1)
                    tmpt->theta += dt * pi / 16;
                else
                    tmpt->theta -= dt * pi / 16;
            }
        }

        tmpb = bomb_head.next;
        while (tmpb != &bomb_head)
        {
            tmpb1 = tmpb->next;
            if (tmpb->t + FALL_TIME + EXPLODE_TIME < tS)
                BombHit(tmpb);
            else if (tmpb->t + FALL_TIME < tS && !tmpb->exploding)
            {
                Noise(EXPLODE_TIME * 1000, 62, 74);
                tmpb->exploding = TRUE;
            }
            tmpb = tmpb1;
        }

        Sleep(10);
    }

    return 0;
}
 
U0 Init()
{
    I64     i;
    Target *tmpt;

    v = 20;
    x = -MAP_WIDTH  >> 1;
    y = -MAP_HEIGHT >> 1;
    thetaf = theta = 1 * pi / 4;

    QueueInit(&bomb_head);

    MemSet(trees, 0, sizeof(trees));
    for (i = 0; i < TREES_NUM; i++)
    {
        trees[i].x = RandU32 % MAP_WIDTH  - MAP_WIDTH  >> 1;
        trees[i].y = RandU32 % MAP_HEIGHT - MAP_HEIGHT >> 1;
    }

    MemSet(targets, 0, sizeof(targets));
    for (i = 0; i < TARGETS_NUM; i++)
    {
        tmpt = &targets[i];
        tmpt->x = RandU32 % MAP_WIDTH  - MAP_WIDTH  >> 1;
        tmpt->y = RandU32 % MAP_HEIGHT - MAP_HEIGHT >> 1;
        if (i < TARGETS_NUM / 3)
        {
            tmpt->type      = MDF_BUNKER;
            tmpt->theta         = (RandU16 & 3) * pi / 2;
            tmpt->alive_img = <6>;
            tmpt->dead_img1 = <7>;
            tmpt->dead_img2 = <8>;
        }
        else
        {
            tmpt->type      = MDF_TANK;
            tmpt->theta         = Rand * 2 * pi;
            tmpt->alive_img = <3>;
            tmpt->dead_img1 = <4>;
            tmpt->dead_img2 = <5>;
        }
    }
    key_count = 0;
    target_count = TARGETS_NUM;
}

U0 CleanUp()
{
    QueueDel(&bomb_head, TRUE);
}

U0 BomberGolf()
{
    I64 sc;

    MenuPush(   "File {"
                "  Abort(,CH_SHIFT_ESC);"
                "  Exit(,CH_ESC);"
                "}"
                "Play {"
                "  Restart(,'\n');"
                "  Faster(,,SC_CURSOR_UP);"
                "  Slower(,,SC_CURSOR_DOWN);"
                "  Left(,,SC_CURSOR_LEFT);"
                "  Right(,,SC_CURSOR_RIGHT);"
                "  Bomb(,CH_SPACE);"
                "}"
                );
    SettingsPush; //See SettingsPush
    AutoComplete;
    WinBorder;
    WinMax;
    DocCursor;
    DocClear;
    PaletteSetLight(FALSE);
    Fs->animate_task = Spawn(&AnimateTask, Fs, "Animate",, Fs);
    Fs->draw_it      = &DrawIt;
    Init;
    try
    {
        while (TRUE)
            switch (KeyGet(&sc))
            {
                case 0:
                    switch (sc.u8[0])
                    {
                        case SC_CURSOR_UP:
                            v += 10;
                            if (v > 300)
                                v = 300;
                            break;

                        case SC_CURSOR_DOWN:
                            v -= 10;
                            if (v < 20)
                                v = 20;
                            break;

                        case SC_CURSOR_LEFT:
                            key_count++;
                            thetaf += 1.0 / (Abs(Wrap(thetaf - theta, -pi)) + 2 * pi);
                            break;

                        case SC_CURSOR_RIGHT:
                            key_count++;
                            thetaf -= 1.0 / (Abs(Wrap(thetaf - theta, -pi)) + 2 * pi);
                            break;

                    }
                    break;

                case CH_SPACE:
                    key_count++;
                    BombDrop(-x - 0.8 * FALL_TIME * v * Sin(theta), -y - 0.8 * FALL_TIME * v * Cos(theta));
                    break;

                case '\n':
                    CleanUp;
                    Init;
                    break;

                case CH_ESC:
                case CH_SHIFT_ESC:
                    goto bm_done;
            }
bm_done:
    }
    catch
        PutExcept;
    SettingsPop;
    CleanUp;
    MenuPop;
    RegWrite("ZealOS/BomberGolf", "I64 best_score=%d;\n", best_score);
}
 
BomberGolf;