//The ball and men were generated
//with ::/Apps/GrModels/Run.CC.
//They were cut-and-pasted here.


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






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






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





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






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





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





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






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






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






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






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






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






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






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



class Frame
{
    U8 *img[2];
    F64 dt;
};

#define COURT_BORDER    10
#define COLLISION_DAMP  0.8
#define AIR_VISCOSITY   0.1

#define GRAVITY_ACCELERATION    500
#define SHOT_VELOCITY           400
#define DRIBBLE_T               0.25
#define MAN_VELOCITY            150
#define MAN_SQR_RADIUS          (20 * 20)
#define FOUL_VELOCITY_THRESHOLD 50
#define JUMP_VELOCITY           250
#define ROLL_VELOCITY_THRESHOLD 100
#define RANDOM_MAN_ACCELERATION 30

#define HEAD_Z_OFFSET   200
#define HAND_X_OFFSET   30
#define HAND_Y_OFFSET   20
#define HAND_SQR_OFFSET (HAND_X_OFFSET * HAND_X_OFFSET + HAND_Y_OFFSET * HAND_Y_OFFSET)
#define HAND_Z_OFFSET   110

#define FIRST_STANDING      0

#define RUNNING_IMGS_NUM    4
#define FIRST_RUNNING       0
#define LAST_RUNNING        (FIRST_RUNNING + RUNNING_IMGS_NUM - 1)

#define SHOOTING_IMGS_NUM   5
#define FIRST_SHOOTING      (LAST_RUNNING + 1)
#define LAST_SHOOTING       (FIRST_SHOOTING + SHOOTING_IMGS_NUM - 1)

#define DRIBBLING_IMGS_NUM  4
#define FIRST_DRIBBLING     (LAST_SHOOTING + 1)
#define LAST_DRIBBLING      (FIRST_DRIBBLING + DRIBBLING_IMGS_NUM - 1)

#define STOPPED_DRIBBLING_IMGS_NUM  2
#define FIRST_STOPPED_DRIBBLING     (LAST_DRIBBLING + 1)
#define LAST_STOPPED_DRIBBLING      (FIRST_STOPPED_DRIBBLING + STOPPED_DRIBBLING_IMGS_NUM - 1)

Frame imgs[LAST_STOPPED_DRIBBLING+1] = {

    {{<6>, <7>}, 2 * DRIBBLE_T / RUNNING_IMGS_NUM}, 
    {{<2>, <3>}, 2 * DRIBBLE_T / RUNNING_IMGS_NUM}, 
    {{<6>, <7>}, 2 * DRIBBLE_T / RUNNING_IMGS_NUM}, 
    {{<4>, <5>}, 2 * DRIBBLE_T / RUNNING_IMGS_NUM}, 
    {{<8>, <9>}, 0.1}, {{<10>, <11>}, 0.2}, 
    {{<12>, <13>}, 0.2}, {{<12>, <13>}, 0.1}, {{<14>, <15>}, 0.1}, 
    {{<20>, <21>}, 2 * DRIBBLE_T / DRIBBLING_IMGS_NUM}, 
    {{<16>, <17>}, 2 * DRIBBLE_T / DRIBBLING_IMGS_NUM}, 
    {{<20>, <21>}, 2 * DRIBBLE_T / DRIBBLING_IMGS_NUM}, 
    {{<18>, <19>}, 2 * DRIBBLE_T / DRIBBLING_IMGS_NUM}, 
    {{<20>, <21>}, DRIBBLE_T / STOPPED_DRIBBLING_IMGS_NUM}, 
    {{<22>, <23>}, DRIBBLE_T / STOPPED_DRIBBLING_IMGS_NUM}, 
};

RegDefault("ZealOS/KeepAway", "I64 best_score0=0,best_score1=9999;\n");
RegExe("ZealOS/KeepAway");

F64 game_t_end, foul_t_end;
I64 score0, score1;

#define PER_SIDE_NUM    3
#define OBJS_NUM        (PER_SIDE_NUM * 2 + 1)
Bool someone_shooting, someone_has_ball;
F64  shot_land_t;
 
class Obj
{
    I64  team; //-1 is ball
    F64  x, y, z, DxDt, DyDt, DzDt, theta, radius, stolen_t0;
    F64  get_ball_dt, get_ball_theta, nearest_man_dd, last_t0, next_t0, foul_t0;
    I64  last_img, next_img;
    Bool stopped, shooting, has_ball, nearest_ball, pad[4];

} objs[OBJS_NUM], *ball, *human, *last_owner;

/*
Just to be different, Terry didn't use the built-in
DCF_TRANSFORMATION flag in this game.
Instead, he chose a 45 degree angle
between Y and Z as the view point.
If he had used the transform, he would
have to make all the men taller.
This is a little simpler, and faster,
but adds lots of factor 2 vals.

Terry also didn't use the CMathODE feat,
just to be different.
*/

U0 DrawObj(CDC *dc, Obj *o, F64 tt)
{
    U8 *tmps;
    F64 r1 = Max(9 - 0.1 * o->z, 1), r2 = Max(r1 / 4, 1);

    if (o == human)
        dc->color = LTRED;
    else
        dc->color = BLACK;
    GrEllipse(dc, o->x, o->y / 2, r1, r2);
    GrFloodFill(dc, o->x, o->y / 2);

    if (o == ball)
        Sprite3(dc, o->x, (o->y - o->z) / 2, GR_Z_ALL - o->y, <1>);
    else {
        tmps = SpriteInterpolate((tt - o->last_t0) / (o->next_t0 - o->last_t0), 
                    imgs[o->last_img].img[o->team], 
                    imgs[o->next_img].img[o->team]);
        Sprite3YB(dc, o->x, (o->y - o->z) / 2, GR_Z_ALL - o->y, tmps, o->theta);
        Free(tmps);
    }
}

I64 ObjCompare(Obj *o1, Obj *o2)
{
    return o1->y - o2->y;
}

U0 DrawIt(CTask *task, CDC *dc)
{
    F64  tt = tS, d, d_down, d_up;
    I64  i;
    Obj *o_sort[OBJS_NUM], *o;

    DCDepthBufAlloc(dc);
    dc->ls.x = 10000;
    dc->ls.y = 60000;
    dc->ls.z = 10000;
    d = 65535 / D3I32Norm(&dc->ls);
    dc->ls.x *= d;
    dc->ls.y *= d;
    dc->ls.z *= d;

    dc->thick = 2;
    dc->color = RED;
    GrBorder(dc, COURT_BORDER, COURT_BORDER, 
                task->pix_width  - 1 - COURT_BORDER, 
                task->pix_height - 1 - COURT_BORDER);
    for (i = 0; i < OBJS_NUM; i++)
    {
        o = o_sort[i] = &objs[i];
        if (o != ball)
        {
            if (o->has_ball)
            {
                ball->x = o->x + HAND_X_OFFSET * Cos(o->theta - pi / 2) + HAND_Y_OFFSET * Cos(o->theta);
                //The factor 2 is because the man is not transformed.
                ball->y = o->y + HAND_X_OFFSET * Sin(o->theta - pi / 2) / 2 + HAND_Y_OFFSET * Sin(o->theta) / 2;
                if (ball->z + ball->radius * 2 > o->z + HAND_Z_OFFSET)
                    ball->z = o->z + HAND_Z_OFFSET - ball->radius * 2;
            }
            else if (o->shooting)
            {
                ball->x = o->x;
                ball->y = o->y;
                ball->z = o->z + HEAD_Z_OFFSET;
            }
            if (tt > o->next_t0)
            {
                if (o->has_ball && (ball->z + ball->radius * 2 >= o->z + HAND_Z_OFFSET || Abs(ball->DzDt) < 30))
                {
                    //This is an approximation.  Terry's instinct told him the viscosity term
                    //needs an Exp().  However,  we should be syncronized to img frames, 
                    //so we don't have to be perfect.
                    d_down  = 1.0;
                    d_up    = 1.0 / COLLISION_DAMP;
                    //Up bounce takes higher % because speed lost in collision.
                    ball->DzDt = -((d_down + d_up) * (o->z + HAND_Z_OFFSET - ball->radius * 4) / (1.0 - AIR_VISCOSITY) +
                                0.5 * GRAVITY_ACCELERATION * (Sqr(DRIBBLE_T * d_up / (d_down + d_up)) -
                                Sqr(DRIBBLE_T * d_down / (d_down + d_up)))) / DRIBBLE_T;
                }
                o->last_t0 = tt;
                o->last_img = o->next_img++;
                if (o->stopped)
                {
                    if (o->has_ball)
                    {
                        if (!(FIRST_STOPPED_DRIBBLING <= o->next_img <= LAST_STOPPED_DRIBBLING))
                            o->next_img = FIRST_STOPPED_DRIBBLING;
                    }
                    else
                        o->next_img = FIRST_STANDING;
                    o->stopped = FALSE;
                }
                else if (o->shooting)
                {
                    if (!(FIRST_SHOOTING <= o->last_img <= LAST_SHOOTING))
                        o->next_img = FIRST_SHOOTING;
                    if (o->next_img > LAST_SHOOTING)
                    {
                        o->next_img = FIRST_STANDING;
                        someone_has_ball = someone_shooting = o->has_ball = o->shooting = FALSE;
                        ball->DxDt = o->DxDt + SHOT_VELOCITY / sqrt2 * Cos(o->theta - pi / 2);
                        ball->DyDt = o->DyDt + SHOT_VELOCITY / sqrt2 * Sin(o->theta - pi / 2);
                        ball->DzDt = o->DzDt + SHOT_VELOCITY / sqrt2;
                        shot_land_t = tt + (ball->DzDt + Sqrt(Sqr(ball->DzDt) +
                                      2 * GRAVITY_ACCELERATION * ball->z)) / GRAVITY_ACCELERATION;
                    }
                    else
                    {
                        ball->DxDt = 0;
                        ball->DyDt = 0;
                        ball->DzDt = 0;
                    }
                }
                else if (o->has_ball)
                {
                    if (FIRST_RUNNING <= o->next_img <= LAST_RUNNING)
                        o->next_img += FIRST_DRIBBLING - FIRST_RUNNING;
                    if (!(FIRST_DRIBBLING <= o->next_img <= LAST_DRIBBLING))
                        o->next_img = FIRST_DRIBBLING;
                }
                else
                {
                    if (FIRST_DRIBBLING <= o->next_img <= LAST_DRIBBLING)
                        o->next_img += FIRST_RUNNING - FIRST_DRIBBLING;
                    if (!(FIRST_RUNNING <= o->next_img <= LAST_RUNNING))
                        o->next_img = FIRST_RUNNING;
                }
                o->next_t0 += imgs[o->last_img].dt;
                if (o->next_t0 <= tt)
                    o->next_t0 = tt + imgs[o->last_img].dt;
            }
        }
    }

    QuickSortI64(o_sort, OBJS_NUM, &ObjCompare);
    for (i = 0; i < OBJS_NUM; i++)
        DrawObj(dc, o_sort[i], tt);
    tt = (game_t_end - tS) / 60;
    if (tt <= 0)
    {
        dc->color = RED;
        tt = 0;
        if (Blink)
            GrPrint(dc, (task->pix_width  - FONT_WIDTH * 9) >> 1, 
                        (task->pix_height - FONT_HEIGHT)    >> 1, "Game Over");
    }
    else
    {
        if (tS < foul_t_end)
        {
            dc->color = LTRED;
            if (Blink)
                GrPrint(dc, (task->pix_width  - FONT_WIDTH * 4) >> 1, 
                            (task->pix_height - FONT_HEIGHT)    >> 1, "Foul");
        }
        dc->color = BLACK;
    }
    GrPrint(dc, 0, 0, "Time:%d:%04.1f  Score:", ToI64(tt), (tt - ToI64(tt)) * 60);
    GrPrint  (dc, FONT_WIDTH * 27, 0, "Best Score:");

    dc->color = LTCYAN;
    GrPrint(dc, FONT_WIDTH * 20, 0, "%02d", score0);
    dc->color = LTPURPLE;
    GrPrint(dc, FONT_WIDTH * 23, 0, "%02d", score1);

    dc->color = LTCYAN;
    GrPrint(dc, FONT_WIDTH * 39, 0, "%02d", best_score0);
    dc->color = LTPURPLE;
    GrPrint(dc, FONT_WIDTH * 42, 0, "%02d", best_score1);
}

U0 Shoot(Obj *o)
{
    if (!someone_shooting && o->has_ball)
    {
        someone_shooting = o->stopped = o->shooting = TRUE;
        o->has_ball = FALSE;
    }
}

U0 MyNoise(I64 mS, F64 min_ona, F64 max_ona)
{//Make white noise for given number of mS.
// See Noise. On bare-metal, Spawn() hogs CPU.
    CSoundEffectFrame *ns;

    if (mS > 0)
    {
        ns = MAlloc(sizeof(CSoundEffectFrame));
        ns->type = SE_NOISE;
        ns->duration = mS / 1000.0;
        ns->ona1 = min_ona;
        ns->ona2 = max_ona;
        music.mute++;
        SoundEffectTask(ns);
        Sound;
    }

    return;
}

U0 AnimateTask(CTask *parent_task)
{
    F64  d, dx, dy, dt, dx2, dy2, t0 = tS;
    I64  i, j;
    Bool gets_ball;
    Obj *o, *nearest_ball[2];

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

        if (game_t_end && game_t_end < t0)
        {
            game_t_end = 0;
            Beep;
            if (score0 - score1 > best_score0 - best_score1)
            {
                best_score0 = score0;
                best_score1 = score1;
                Sound(86);
                Sleep(100);
                Sound;
                Sleep(100);
                Sound(86);
                Sleep(100);
                Sound;
                Sleep(100);
            }
        }

        if (game_t_end)
        {
            MemSet(&nearest_ball, 0, sizeof(nearest_ball));
            for (i = 0; i < OBJS_NUM; i++) {
                o = &objs[i];
                o->nearest_ball = FALSE;
                if (o != ball)
                {
                    d = 0;
                    for (j = 0; j < 5; j++) //Iterative estimate of how long to get ball.
                        d = Sqrt(Sqr(ball->DxDt * d + ball->x - o->x) + Sqr(ball->DyDt * d + ball->y - o->y)) / MAN_VELOCITY;
                    o->get_ball_dt = d;
                    o->get_ball_theta = Arg(ball->DxDt * d + ball->x - o->x, ball->DyDt * d + ball->y - o->y);
                    if (o != last_owner && !nearest_ball[o->team] || o->get_ball_dt < nearest_ball[o->team]->get_ball_dt)
                        nearest_ball[o->team] = o;
                }
            }
            nearest_ball[0]->nearest_ball = TRUE;
            nearest_ball[1]->nearest_ball = TRUE;

            for (i = 0; i < OBJS_NUM; i++)
            {
                o = &objs[i];
                if (o == ball)
                {
                    o->x += dt * o->DxDt;
                    o->y += dt * o->DyDt;
                    if (!someone_shooting)
                        o->z += dt * (o->DzDt - 0.5 * GRAVITY_ACCELERATION * dt);
                }
                else
                {
                    if (!o->has_ball)
                    {
                        if (t0 - o->stolen_t0 > 2.0 && !someone_shooting)
                        {
                            dx = ball->x - o->x;
                            dy = ball->y - o->y;
                            if (dx * dx + dy * dy < HAND_SQR_OFFSET && ball->z < o->z + HAND_Z_OFFSET)
                            {
                                gets_ball = TRUE;
                                for (j = 0; j < PER_SIDE_NUM * 2; j++)
                                    if (j != i && objs[j].has_ball)
                                    {
                                        if (Rand<2.0 * dt)
                                        {
                                            objs[j].stolen_t0 = t0;
                                            objs[j].has_ball = FALSE;
                                        }
                                        else
                                            gets_ball = FALSE;
                                    }
                                if (gets_ball)
                                {
                                    someone_has_ball = o->has_ball = TRUE;
                                    if (o != last_owner)
                                    {
                                        if (o->team)
                                        {
                                            if (t0 < shot_land_t + 0.1)
                                                score1 += 6;
                                            else
                                                score1 += 2;
                                            MyNoise(250, 74, 74);
                                        }
                                        else
                                        {
                                            if (t0 < shot_land_t + 0.1)
                                                score0 += 6;
                                            else
                                                score0 += 2;
                                            MyNoise(250, 86, 86);
                                        }
                                        last_owner = o;
                                    }
                                }
                            }
                        }
                    }
                    else if (o != human && Rand < 0.25 * dt)
                        Shoot(o);
                    if (!o->shooting)
                    {
                        if (o == human)
                        {
                            dx = (mouse.pos.x - parent_task->pix_left - parent_task->scroll_x) - o->x;
                            dy = (mouse.pos.y - parent_task->pix_top  - parent_task->scroll_y) * 2 - o->y;
                        }
                        else
                        {
                            if (!someone_has_ball && o->nearest_man_dd > 4 * MAN_SQR_RADIUS && o->nearest_ball)
                            {
                                dx = o->DxDt = MAN_VELOCITY * Cos(o->get_ball_theta);
                                dy = o->DyDt = MAN_VELOCITY * Sin(o->get_ball_theta);
                            }
                            else
                            {
                                dx = o->DxDt += RANDOM_MAN_ACCELERATION / sqrt2 * RandI16 / I16_MAX * dt;
                                dy = o->DyDt += RANDOM_MAN_ACCELERATION / sqrt2 * RandI16 / I16_MAX * dt;
                            }
                        }
                        d = Sqrt(dx * dx + dy * dy);
                        if (d >= 1.0)
                        {
                            o->theta = Arg(dx, dy) + pi / 2;
                            dx *= MAN_VELOCITY / sqrt2 * dt / d;
                            dy *= MAN_VELOCITY / sqrt2 * dt / d;
                            o->nearest_man_dd = F64_MAX;
                            for (j = 0; j < PER_SIDE_NUM * 2; j++)
                                if (j != i)
                                {
                                    dx2 = objs[j].x - o->x;
                                    dy2 = objs[j].y - o->y;
                                    d = Sqr(dx2) + Sqr(dy2);
                                    if (d < o->nearest_man_dd)
                                        o->nearest_man_dd = d;
                                    if (d < MAN_SQR_RADIUS)
                                    {
                                        if (d)
                                        {
                                            d = Sqrt(d);
                                            dx2 /= d;
                                            dy2 /= d;
                                        }
                                        if (t0 > o->foul_t0 + 0.15)
                                        {
                                            d = (dx - objs[j].DxDt) * dx2 + (dy - objs[j].DyDt) * dy2;
                                            if (o == human && t0 > o->foul_t0 + 1.0 &&
                                                dt && d / dt>FOUL_VELOCITY_THRESHOLD && objs[j].team)
                                            {
                                                MyNoise(250, 62, 62);
                                                score1 += 1;
                                                foul_t_end = t0 + 1.0;
                                            }
                                            o->foul_t0=t0;
                                        }
                                    }
                                }
                            if (t0 < o->foul_t0 + 0.15)
                            {
                                dx = -dx;
                                dy = -dy;
                            }
                            o->x += dx;
                            o->y += dy;
                            o->stopped = FALSE;
                        }
                        else
                            o->stopped = TRUE;
                    }
                    if (o->DzDt)
                        o->z += dt * (o->DzDt - 0.5 * GRAVITY_ACCELERATION * dt);
                }

                if (o->x + o->radius >= parent_task->pix_width - COURT_BORDER)
                {
                    o->x = parent_task->pix_width - COURT_BORDER - 1 - o->radius;
                    o->DxDt = -COLLISION_DAMP * o->DxDt;
                    if (o == ball)
                        MyNoise(10, 74, 86);
                }
                if (o->x - o->radius < COURT_BORDER)
                {
                    o->x = COURT_BORDER + o->radius;
                    o->DxDt = -COLLISION_DAMP * o->DxDt;
                    if (o == ball)
                        MyNoise(10, 74, 86);
                }

                if (o->y + o->radius * 2 >= (parent_task->pix_height - COURT_BORDER) * 2)
                {
                    o->y = (parent_task->pix_height - COURT_BORDER) * 2 - 1 - o->radius * 2;
                    o->DyDt = -COLLISION_DAMP * o->DyDt;
                    if (o == ball)
                        MyNoise(10, 74, 86);
                }
                if (o->y - o->radius * 2 < 2 * COURT_BORDER)
                {
                    o->y = COURT_BORDER * 2 + o->radius * 2;
                    o->DyDt = -COLLISION_DAMP*  o->DyDt;
                    if (o == ball)
                        MyNoise(10, 74, 86);
                }

                if (o->z - o->radius * 2 < 0)
                {
                    o->z = o->radius * 2;
                    o->DzDt = -COLLISION_DAMP * o->DzDt;
                    if (o->DzDt > ROLL_VELOCITY_THRESHOLD)
                        MyNoise(10, 74, 86);
                    if (o != ball)
                        o->DzDt = 0;
                }
                else if (o->z - o->radius * 2 > 0)
                    o->DzDt -= GRAVITY_ACCELERATION * dt;
                if (o == ball)
                {
                    d = Exp(-AIR_VISCOSITY * dt);
                    o->DxDt *= d;
                    o->DyDt *= d;
                    o->DzDt *= d;
                }
            }
        }
        Refresh;
    }
}

U0 Init()
{
    I64 i;

    someone_shooting = FALSE;
    shot_land_t = 0;
    MemSet(&objs, 0, sizeof(objs));
    for (i = 0; i < PER_SIDE_NUM*2;i++)
    {
        objs[i].team = i & 1;
        objs[i].x = Fs->pix_width / 2 ;
        objs[i].y = 2 * Fs->pix_height / 2;
        objs[i].next_img = objs[i].last_img = FIRST_RUNNING;
    }
    last_owner = NULL;
    human = &objs[0];
    ball =&objs[i];
    ball->team = -1;
    ball->x = 0.5 * Fs->pix_width / 2;
    ball->y = 0.5 * 2 * Fs->pix_height / 2;
    ball->radius = 11;
    ball->z = ball->radius;
    score0 = score1 = 0;
    game_t_end = tS + 3 * 60;
    foul_t_end = 0;
}

U0 KeepAway()
{
    I64 message_code, arg1, arg2;

    PopUpOk(    "Pass or hand-off to your team to score points.$FG$\n\n"
                "\t2 points for successful hand-off.\n"
                "\t6 points for successful pass.\n"
                "\t1 point penalty for foul.\n\n"
                "Left-Click\tto pass.\n\n"
                "Right-Click\tto jump.\n");
    SettingsPush; //See SettingsPush
    Fs->text_attr = BLACK + YELLOW << 4;
    Fs->win_inhibit |= WIG_DBL_CLICK;
    AutoComplete;
    WinBorder;
    WinMax;
    DocCursor;
    DocClear;

    MenuPush(   "File {"
                "  Abort(,CH_SHIFT_ESC);"
                "  Exit(,CH_ESC);"
                "}"
                "Play {"
                "  Restart(,'\n');"
                "  Shoot(,CH_SPACE);"
                "  Jump(,'j');"
                "}"
                );

    Init;
    Fs->draw_it      = &DrawIt;
    Fs->animate_task = Spawn(&AnimateTask, Fs, "Animate",, Fs);

    try
    {
        while (TRUE)
        {
            message_code = MessageGet(&arg1, &arg2, 1 << MESSAGE_MS_L_DOWN | 1 << MESSAGE_MS_R_DOWN | 1 << MESSAGE_KEY_DOWN);
            switch (message_code)
            {
                case MESSAGE_MS_L_DOWN:
ka_shoot:
                    Shoot(human);
                    break;

                case MESSAGE_MS_R_DOWN:
ka_jump:
                    human->DzDt = JUMP_VELOCITY;
                    break;

                case MESSAGE_KEY_DOWN:
                    switch (arg1)
                    {
                        case '\n':
                            Init;
                            break;

                        case 'j':
                            goto ka_jump;

                        case CH_SPACE:
                            goto ka_shoot;

                        case CH_SHIFT_ESC:
                        case CH_ESC:
                            goto ka_done;
                    }
                    break;
            }
        }
ka_done: //Don't goto out of try
        MessageGet(,, 1 << MESSAGE_KEY_UP);
    }
    catch
        PutExcept;
    SettingsPop;
    MenuPop;
    RegWrite("ZealOS/KeepAway", "I64 best_score0=%d,best_score1=%d;\n", best_score0, best_score1);
}