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