#define GAME_SPEED_SCALE                0.1

F64      game_speed, 
         launch_unit_x1, launch_unit_y1, launch_unit_x2, launch_unit_y2, launch_t, 
         return_unit_x1, return_unit_y1, return_unit_x2, return_unit_y2, return_t, 
         set_theta_unit_x1, set_theta_unit_y1, set_theta_unit_x2, set_theta_unit_y2, set_theta_t, 
         next_noise;
CTask   *main_task;

#define OT_CARRIER          0
#define OT_CRUISER          1
#define OT_FIGHTER          2
#define OT_TYPES_NUM        3

#define OF_SHIP             1
#define OF_ACTIVE           2
#define OF_RETURNING        4
#define OF_SHOOTING         8

#define FIRE_WIDTH          56
#define FIRE_HEIGHT         16
#define FIRE_X_SCALE        0.5
#define FIRE_Y_SCALE        0.5

#define FIRE_COLORS         16
U8 fire_colors[FIRE_COLORS] = {YELLOW, YELLOW, LTRED, YELLOW,  BLACK, YELLOW, RED, YELLOW, 
                               YELLOW, BLACK, YELLOW, LTRED,  RED, YELLOW, DKGRAY, YELLOW};

class Obj
{
    Obj *next, *last;
    Obj *next_in_squadron, *last_in_squadron;
    Obj *host;
    U8   player, type;
    U16  flags;
    I16  squadron, member_num;
    F64  x, y, theta, dtheta, 
         speed, turn_rate, 
         life_percent, 
         target_x, target_y, 
         ship_guns, ship_guns_range, 
         air_guns, air_guns_range, 
         fuel, max_fuel, fuel_burn_rate, 
         death_time, next_action_time;
    I32  torpedos, max_torpedos;
    F64  torpedos_range;
    U8   fire[(FIRE_WIDTH * FIRE_HEIGHT + 7) / 8];

} obj_head;

class Torpedo
{
    Torpedo *next, *last;
    Obj     *target;
    F64      x, y, theta, speed, timeout;

} torpedo_head;

#define SA_PARKED           0
#define SA_LAUNCHING        1
#define SA_FLYING           2
#define SA_SET_theta            3
#define SA_RETURNING        4
#define SA_DEAD             5

class Squadron : Obj
{
    I64 action, dead_mask, total_mask;

} *squadrons;

U0 SquadronIns(Obj *o, Obj *pred)
{
    Obj *succ = pred->next_in_squadron;

    o->next_in_squadron = succ;
    o->last_in_squadron = pred;
    pred->next_in_squadron = o;
    succ->last_in_squadron = o;
}

U0 SquadronRemove(Obj *o)
{
    Obj *pred = o->last_in_squadron, *succ = o->next_in_squadron;

    pred->next_in_squadron = succ;
    succ->last_in_squadron = pred;
}

#define PLAYERS_NUM         2

I64 num_carriers[PLAYERS_NUM] = {2, 3}, 
    num_cruisers[PLAYERS_NUM] = {2, 3}, 
    num_planes_per_squadron[PLAYERS_NUM] = {6, 5}, 
    num_squadrons_per_carrier[PLAYERS_NUM] = {2, 3}, 
    num_alive[PLAYERS_NUM], 
    num_squadrons;

Obj *ObjFind(F64 x, F64 y,
             I64 flag_mask=OF_ACTIVE|OF_RETURNING, I64 flag_val=OF_ACTIVE,
             I64 type_mask=-1, I64 player_mask=-1, F64 *_d=NULL)
{
    Obj *tmpo = obj_head.next, *best = NULL;
    F64  dd, best_dd = F64_MAX;

    while (tmpo != &obj_head)
    {
        if (tmpo->flags & flag_mask == flag_val && Bt(&type_mask, tmpo->type) && Bt(&player_mask, tmpo->player))
        {
            dd = Sqr(tmpo->x - x) + Sqr(tmpo->y - y);
            if (dd < best_dd)
            {
                best = tmpo;
                best_dd = dd;
            }
        }
        tmpo = tmpo->next;
    }
    if (_d)
        *_d = Sqrt(best_dd);

    return best;
}

U0 ObjDel(Obj *tmpo)
{
    if (tmpo)
    {
        if (tS < tmpo->death_time)
            tmpo->flags &= ~OF_ACTIVE;
        else
        {
            if (tmpo->squadron >= 0)
                SquadronRemove(tmpo);
            QueueRemove(tmpo);
            if (tmpo->squadron >= 0)
                LBts(&squadrons[tmpo->squadron].dead_mask, tmpo->member_num);
            num_alive[tmpo->player]--;
            Free(tmpo);
        }
    }
}



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

U8 *imgs[PLAYERS_NUM][OT_TYPES_NUM] = {{<1>, <2>, <3>}, {<4>, <5>, <6>}};

U0 DrawIt(CTask *task, CDC *dc)
{
    I64      i, j, k;
    F64      d, cur_time = tS;
    Obj     *tmpo;
    Torpedo *tmpt;

    tmpt = torpedo_head.next;
    while (tmpt != &torpedo_head)
    {
        dc->color = WHITE;
        GrPlot(dc, tmpt->x, tmpt->y);
        tmpt = tmpt->next;
    }

    tmpo = obj_head.next;
    while (tmpo != &obj_head)
    {
        if (tmpo->flags & OF_ACTIVE && tmpo->flags & OF_SHIP)
        {
            Sprite3ZB(dc, tmpo->x, tmpo->y, 0, imgs[tmpo->player][tmpo->type], tmpo->theta + pi / 2);

            k = 0;
            for (j = 0; j < FIRE_HEIGHT; j++)
                for (i = 0; i < FIRE_WIDTH; i++)
                    if (Bt(tmpo->fire, k++))
                    {
                        dc->color = fire_colors[ToI64(k + 10 * tS) & (FIRE_COLORS - 1)];
                        GrPlot(dc,  tmpo->x + FIRE_X_SCALE * (i - FIRE_WIDTH / 2 + (11 * tS + i) % 1.7) *
                                    Cos(tmpo->theta) - FIRE_Y_SCALE * (j - FIRE_HEIGHT / 2 + (12 * tS + j) % 1.7) * Sin(tmpo->theta), 
                                    tmpo->y + FIRE_Y_SCALE * (j - FIRE_HEIGHT / 2 + (19 * tS + j) % 1.7) *
                                    Cos(tmpo->theta) + FIRE_X_SCALE * (i - FIRE_WIDTH / 2 + (13 * tS + i) % 1.7) * Sin(tmpo->theta));
                    }
            if (Blink)
            {
                dc->color = BLACK;
                GrLine(dc, tmpo->x + 5, tmpo->y, tmpo->x + 5 + 10, tmpo->y);

                if (tmpo->life_percent > 0)
                {
                    if (tmpo->life_percent < 33)
                        dc->color = RED;
                    else if (tmpo->life_percent < 66)
                        dc->color = YELLOW;
                    else
                        dc->color = GREEN;
                    GrLine(dc, tmpo->x + 5, tmpo->y, tmpo->x + 5 + 10 * tmpo->life_percent / 100, tmpo->y);
                }

                dc->color = BLACK;
                GrLine(dc, tmpo->x + 5, tmpo->y + 2, tmpo->x + 5 + 10, tmpo->y + 2);
                d = tmpo->fuel * 100 / tmpo->max_fuel;
                if (d > 0)
                {
                    if (d < 33)
                        dc->color = RED;
                    else if (d < 66)
                        dc->color = YELLOW;
                    else
                        dc->color = GREEN;
                    GrLine(dc, tmpo->x + 5, tmpo->y + 2, tmpo->x + 5 + 10 * d / 100, tmpo->y + 2);
                }
            }
        }
        tmpo = tmpo->next;
    }

    tmpo = obj_head.next;
    while (tmpo != &obj_head)
    {
        if (tmpo->flags & OF_ACTIVE && !(tmpo->flags & OF_SHIP))
            Sprite3ZB(dc, tmpo->x, tmpo->y, 0, imgs[tmpo->player][tmpo->type], tmpo->theta + pi / 2);
        if (tmpo->flags & OF_SHOOTING)
        {
            dc->color = LTRED;
            GrLine(dc, tmpo->x, tmpo->y, tmpo->target_x, tmpo->target_y, 3);
        }
        tmpo = tmpo->next;
    }

    dc->flags |= DCF_TRANSFORMATION;
    if (cur_time < launch_t)
    {
        dc->color = LTGREEN;
        GrArrow3(dc, launch_unit_x1, launch_unit_y1, 0, launch_unit_x2, launch_unit_y2, 0);
    }
    if (cur_time < return_t)
    {
        dc->color = LTRED;
        GrArrow3(dc, return_unit_x1, return_unit_y1, 0, return_unit_x2, return_unit_y2, 0);
    }
    if (cur_time<set_theta_t)
    {
        dc->color = YELLOW;
        GrArrow3(dc, set_theta_unit_x1, set_theta_unit_y1, 0, set_theta_unit_x2, set_theta_unit_y2, 0);
    }

    dc->color = YELLOW;
    GrPrint(dc, -task->scroll_x, -task->scroll_y, "Game Speed: %5.2f", game_speed);
    dc->color = LTCYAN;
    GrPrint(dc, -task->scroll_x, -task->scroll_y + FONT_HEIGHT, "Player 1: %d", num_alive[0]);
    dc->color = LTPURPLE;
    GrPrint(dc, -task->scroll_x, -task->scroll_y + 2 * FONT_HEIGHT, "Player 2: %d", num_alive[1]);

    if ((!num_alive[0] || !num_alive[1]) && Blink)
    {
        if (!num_alive[1])
        {
            dc->color = LTGREEN;
            GrPrint(dc, task->pix_width  >> 1 - (FONT_WIDTH * 14) / 2 - task->scroll_x, 
                        task->pix_height >> 1 - FONT_HEIGHT / 2       - task->scroll_y, "Game Completed");
        }
        else
        {
            dc->color = LTRED;
            GrPrint(dc, task->pix_width  >> 1 - (FONT_WIDTH * 9) / 2 - task->scroll_x, 
                        task->pix_height >> 1 - FONT_HEIGHT / 2      - task->scroll_y, "Game Over");
        }
    }
}

U0 Init()
{
    I64          i, fighter, ship, player, squadron, 
                w = Fs->pix_width, h = Fs->pix_height;
    Squadron    *tmps;
    Obj         *tmpo, *tmpo1;

    QueueInit(&obj_head);
    QueueInit(&torpedo_head);

    next_noise = tS;
    Fs->scroll_x = 0;
    Fs->scroll_y = 0;
    game_speed = 1.0;
    launch_t = return_t = set_theta_t = 0;
    main_task = Fs;
    num_squadrons = 0;
    for (player = 0; player < PLAYERS_NUM; player++)
    {
        num_alive[player] = 0;
        for (ship = 0; ship < num_cruisers[player]; ship++)
        {
            tmpo1 = CAlloc(sizeof(Obj));
            num_alive[player]++;
            tmpo1->type             = OT_CRUISER;
            tmpo1->player           = player;
            tmpo1->squadron         = -1;
            tmpo1->member_num       = ship;
            tmpo1->flags            = OF_ACTIVE|OF_SHIP;
            tmpo1->x                = 0.8 * w * (Rand - 0.5) + w >> 1;
            tmpo1->y                = 0.8 * h * (Rand - 0.5) + h >> 1;
            tmpo1->host             = NULL;
            tmpo1->speed            = 35.0;
            tmpo1->turn_rate        = 2.5;
            tmpo1->theta                = 2 * pi * (Rand - 0.5);
            tmpo1->dtheta               = 0;
            tmpo1->life_percent     = 100.0;
            tmpo1->fuel = tmpo1->max_fuel = 100000;
            tmpo1->fuel_burn_rate   = 100.0;
            tmpo1->air_guns         = 5000;
            tmpo1->air_guns_range   = 30.0;
            tmpo1->ship_guns        = 10000;
            tmpo1->ship_guns_range  = 30.0;
            tmpo1->torpedos = tmpo1->max_torpedos = 0;
            tmpo1->torpedos_range   = 0.0;
            tmpo1->next_action_time = 0;
            QueueInsert(tmpo1, obj_head.last);
        }

        for (ship = 0; ship < num_carriers[player]; ship++)
        {
            tmpo1 = CAlloc(sizeof(Obj));
            num_alive[player]++;
            tmpo1->type             = OT_CARRIER;
            tmpo1->player           = player;
            tmpo1->squadron         = -1;
            tmpo1->member_num       = ship;
            tmpo1->flags            = OF_ACTIVE|OF_SHIP;
            tmpo1->x                = 0.8 * w * (Rand - 0.5) + w >> 1;
            tmpo1->y                = 0.8 * h * (Rand - 0.5) + h >> 1;
            tmpo1->host             = NULL;
            tmpo1->speed            = 28.0;
            tmpo1->turn_rate        = 1.0;
            tmpo1->theta                = 2 * pi * (Rand - 0.5);
            tmpo1->dtheta               = 0;
            tmpo1->life_percent     = 100.0;
            tmpo1->fuel = tmpo1->max_fuel = 750000;
            tmpo1->fuel_burn_rate   = 500.0;
            tmpo1->air_guns         = 5000;
            tmpo1->air_guns_range   = 20.0;
            tmpo1->ship_guns        = 2000;
            tmpo1->ship_guns_range  = 30.0;
            tmpo1->torpedos = tmpo1->max_torpedos = 0;
            tmpo1->torpedos_range   = 0.0;
            tmpo1->next_action_time = 0;
            QueueInsert(tmpo1, obj_head.last);

            for (squadron = 0; squadron < num_squadrons_per_carrier[player]; squadron++, num_squadrons++)
            {
                for (fighter = 0; fighter < num_planes_per_squadron[player]; fighter++)
                {
                    tmpo = CAlloc(sizeof(Obj));
                    num_alive[player]++;
                    tmpo->type              = OT_FIGHTER;
                    tmpo->player            = player;
                    tmpo->squadron          = num_squadrons;
                    tmpo->member_num        = fighter;
                    tmpo->flags             = 0;
                    tmpo->host              = tmpo1;
                    tmpo->speed             = 300.0;
                    tmpo->turn_rate         = 25.0;
                    tmpo->life_percent      = 100.0;
                    tmpo->fuel = tmpo->max_fuel = 1000;
                    tmpo->fuel_burn_rate    = 1.0;
                    tmpo->air_guns          = 35000;
                    tmpo->air_guns_range    = 8.0;
                    tmpo->ship_guns         = 0;
                    tmpo->ship_guns_range   = 0.0;
                    tmpo->torpedos = tmpo->max_torpedos = 1;
                    tmpo->torpedos_range    = 20.0;
                    QueueInsert(tmpo, obj_head.last);
                }
            }
        }
    }
    squadrons = CAlloc(num_squadrons * sizeof(Squadron));
    for (i = 0, tmps = squadrons; i < num_squadrons; i++, tmps++)
    {
        tmps->next_in_squadron = tmps->last_in_squadron = tmps;
        tmps->squadron = i;
    }
    tmpo = obj_head.next;
    while (tmpo != &obj_head)
    {
        if (tmpo->squadron >= 0)
        {
            tmps = &squadrons[tmpo->squadron];
            tmps->host       = tmpo->host;
            tmps->player     = tmpo->player;
            tmps->total_mask = 1 << num_planes_per_squadron[tmpo->player] - 1;
            SquadronIns(tmpo, tmps->last_in_squadron);
        }
        tmpo = tmpo->next;
    }
}

U0 CleanUp()
{
    QueueDel(&obj_head, TRUE);
    QueueDel(&torpedo_head, TRUE);
    Free(squadrons);
}

Obj *ObjLaunch(I64 player, I64 squadron=-1, Obj *host=NULL, F64 x=F64_MAX, F64 y=F64_MAX, F64 theta=F64_MAX)
{
    Obj *tmpo;
    F64  cur_time = tS;

    if (!host)
        host = ObjFind(x, y,,, 1 << OT_CARRIER, 1 << player);
    if (host && cur_time>host->next_action_time)
    {
        if (theta == F64_MAX)
            theta = Arg(x - host->x, y - host->y);
        tmpo = obj_head.next;
        while (tmpo != &obj_head)
        {
            if (tmpo->host == host && (squadron < 0 || tmpo->squadron == squadron) &&
                !(tmpo->flags & OF_ACTIVE) &&
                (tmpo->squadron < 0 || squadrons[tmpo->squadron].action == SA_PARKED || 
                 squadrons[tmpo->squadron].action == SA_LAUNCHING))
            {
                if (tmpo->fuel <= 0.0)
//When low on fuel,  not zero,  gets launched and captured.
                    LBts(&squadrons[tmpo->squadron].dead_mask, tmpo->member_num);
                else
                {
                    tmpo->flags = tmpo->flags & ~OF_RETURNING | OF_ACTIVE;
                    tmpo->theta = host->theta;
                    if (x == F64_MAX || y == F64_MAX || Sqr(x - host->x) + Sqr(y - host->y) > 3 * 3)
                        tmpo->dtheta = Wrap(theta - tmpo->theta, -pi);
                    else
                        tmpo->dtheta = 0;
                    tmpo->x = host->x;
                    tmpo->y = host->y;
                    host->next_action_time = cur_time + 0.25 / game_speed;
                    return tmpo;
                }
            }
            tmpo = tmpo->next;
        }
    }

    return NULL;
}

Squadron *SquadronLaunch(I64 player, F64 x=F64_MAX, F64 y=F64_MAX)
{
    Squadron *tmps;
    Obj      *tmpo;

    if (tmpo = ObjLaunch(player,,, x, y))
    {
        if (player == 0)
        {
            launch_unit_x1 = tmpo->x;
            launch_unit_y1 = tmpo->y;
            launch_unit_x2 = mouse.pos.x - main_task->pix_left - main_task->scroll_x;
            launch_unit_y2 = mouse.pos.y - main_task->pix_top  - main_task->scroll_y;
            launch_t = tS + 0.5;
        }
        if (tmpo->squadron >= 0)
        {
            tmps = &squadrons[tmpo->squadron];
            if (tmps->action == SA_PARKED)
            {
                tmps->action = SA_LAUNCHING;
                tmps->theta      = tmpo->theta + tmpo->dtheta;
            }
            return tmps;
        }
    }

    return NULL;
}

Obj *ObjReturn(I64 player, F64 x, F64 y)
{
    Obj *tmpo;

    if (tmpo = ObjFind(x, y, OF_ACTIVE, OF_ACTIVE, 1 << OT_FIGHTER, 1 << player))
        tmpo->flags |= OF_RETURNING;

    return tmpo;
}

Squadron *SquadronReturn(I64 player, F64 x, F64 y)
{
    Squadron *tmps;
    Obj      *tmpo;

    if (tmpo = ObjReturn(player, x, y))
    {
        if (player == 0)
        {
            return_unit_x1 = tmpo->x;
            return_unit_y1 = tmpo->y;
            if (tmpo->host)
            {
                return_unit_x2 = tmpo->host->x;
                return_unit_y2 = tmpo->host->y;
                return_t = tS + 0.5;
            }
        }
        if (tmpo->squadron >= 0)
        {
            tmps = &squadrons[tmpo->squadron];
            if (tmps->action == SA_FLYING)
                tmps->action = SA_RETURNING;
            return tmps;
        }
    }

    return NULL;
}

Obj *ObjSettheta(I64 player, F64 x=F64_MAX, F64 y=F64_MAX, F64 theta=F64_MAX)
{
    Obj *tmpo;

    if ((tmpo = ObjFind(x, y,,,, 1 << player)) && tmpo->flags & OF_ACTIVE && !(tmpo->flags & OF_RETURNING))
    {
        if (theta == F64_MAX)
            theta = Arg(x - tmpo->x, y - tmpo->y);
        tmpo->dtheta += Wrap(theta - (tmpo->theta + tmpo->dtheta), -pi);
        return tmpo;
    }

    return NULL;
}

Squadron *SquadronSettheta(I64 player, F64 x=F64_MAX, F64 y=F64_MAX, F64 theta=F64_MAX)
{
    Squadron *tmps;
    Obj      *tmpo;

    if (tmpo = ObjSettheta(player, x, y, theta))
    {
        if (player == 0)
        {
            set_theta_unit_x1 = tmpo->x;
            set_theta_unit_y1 = tmpo->y;
            set_theta_unit_x2 = mouse.pos.x - main_task->pix_left - main_task->scroll_x;
            set_theta_unit_y2 = mouse.pos.y - main_task->pix_top  - main_task->scroll_y;
            set_theta_t = tS + 0.5;
        }
        if (tmpo->squadron >= 0)
        {
            tmps = &squadrons[tmpo->squadron];
            if (tmps->action == SA_FLYING)
            {
                tmps->action = SA_SET_theta;
                tmps->theta      = tmpo->theta + tmpo->dtheta;
            }
            return tmps;
        }
    }

    return NULL;
}

U0 SquadronActions()
{
    I64       i, completed_mask;
    Obj      *tmpo;
    Squadron *tmps;

    for (i = 0, tmps = squadrons; i < num_squadrons; i++, tmps++)
    {
        completed_mask = 0;
        switch (tmps->action)
        {
            case SA_LAUNCHING:
                ObjLaunch(tmps->player, i, tmps->host,,, tmps->theta);
                tmpo = tmps->next_in_squadron;
                while (tmpo != tmps)
                {
                    LBEqual(&completed_mask, tmpo->member_num, tmpo->flags & OF_ACTIVE);
                    tmpo = tmpo->next_in_squadron;
                }
                if (completed_mask | tmps->dead_mask == tmps->total_mask)
                    tmps->action = SA_FLYING;
                break;

            case SA_FLYING:
                tmpo = tmps->next_in_squadron;
                while (tmpo != tmps)
                {
                    LBEqual(&completed_mask, tmpo->member_num, !(tmpo->flags & OF_ACTIVE));
                    tmpo = tmpo->next_in_squadron;
                }
                if (completed_mask | tmps->dead_mask == tmps->total_mask)
                    tmps->action = SA_PARKED;
                break;

            case SA_SET_theta:
                tmpo = tmps->next_in_squadron;
                while (tmpo != tmps)
                {
                    tmpo->dtheta += Wrap(tmps->theta - (tmpo->theta + tmpo->dtheta), -pi);
                    tmpo = tmpo->next_in_squadron;
                }
                tmps->action = SA_FLYING;
                break;

            case SA_RETURNING:
                tmpo = tmps->next_in_squadron;
                while (tmpo != tmps)
                {
                    tmpo->flags |= OF_RETURNING;
                    LBEqual(&completed_mask, tmpo->member_num, !(tmpo->flags & OF_ACTIVE));
                    tmpo = tmpo->next_in_squadron;
                }
                if (completed_mask | tmps->dead_mask == tmps->total_mask)
                    tmps->action = SA_PARKED;
                break;
        }
        if (tmps->dead_mask == tmps->total_mask)
            tmps->action=SA_DEAD;
    }
}

U0 AI(I64 player, F64 period)
{
    Obj *tmpo;

    tmpo = obj_head.next;
    while (tmpo != &obj_head)
    {
        if (tmpo->player == player)
        {
            if (tmpo->type == OT_CARRIER && Rand < 5 * period)
                SquadronLaunch(player, tmpo->x, tmpo->y);
            if (tmpo->flags&OF_ACTIVE && !(tmpo->flags&OF_RETURNING) && Rand < 10.0 * period)
                SquadronSettheta(player, tmpo->x, tmpo->y, tmpo->theta + pi / 2 * (Rand - 0.5));
        }
        tmpo = tmpo->next;
    }
}

U0 ShipDamage(Obj *tmpo, F64 d)
{
    I64 i, x = Rand * FIRE_WIDTH, y = Rand * FIRE_HEIGHT;

    tmpo->life_percent -= d;
    while (d > 0)
    {
        if (!Bts(tmpo->fire, y * FIRE_WIDTH + x))
            d -= 500.0 / (FIRE_WIDTH * FIRE_HEIGHT);
        else
            d -= 25.0 / (FIRE_WIDTH * FIRE_HEIGHT);
        i = RandI16 & 7;
        x += gr_x_offsets[i];
        y += gr_y_offsets[i];
        while (x >= FIRE_WIDTH)
            x -= FIRE_WIDTH;

        while (x < 0)
            x += FIRE_WIDTH;

        while (y >= FIRE_HEIGHT)
            y -= FIRE_HEIGHT;

        while (y < 0)
            y += FIRE_HEIGHT;
    }
}

U0 ShipFix(Obj *tmpo, F64 d)
{
    tmpo->life_percent += d;
    if (tmpo->life_percent >= 100.0)
    {
        tmpo->life_percent = 100.0;
        MemSet(tmpo->fire, 0, sizeof(Obj.fire));
        return;
    }
    while (d > 0)
        if (Btr(tmpo->fire, FIRE_WIDTH * FIRE_HEIGHT * Rand))
            d -= 400.0 / (FIRE_WIDTH * FIRE_HEIGHT);
        else
            d -= 20.0 / (FIRE_WIDTH * FIRE_HEIGHT);
}

U0 Combat(F64 period)
{
    F64      d;
    Obj     *tmpo, *tmpo1;
    Torpedo *tmpt;

    tmpo = obj_head.next;
    while (tmpo != &obj_head)
    {
        tmpo->flags &= ~OF_SHOOTING;
        if (tmpo->flags & OF_ACTIVE &&
            (tmpo1 = ObjFind(tmpo->x, tmpo->y, OF_ACTIVE, OF_ACTIVE,, 1 << (tmpo->player ^ 1), &d)))
        {
            tmpo->target_x = tmpo1->x;
            tmpo->target_y = tmpo1->y;
            if (tmpo1->flags & OF_SHIP)
            {
                if (tmpo->torpedos && d < tmpo->torpedos_range && Rand < 125 * period)
                {
                    tmpo->torpedos--;
                    tmpt = CAlloc(sizeof(Torpedo));
                    tmpt->x         = tmpo->x;
                    tmpt->y         = tmpo->y;
                    tmpt->speed     = 100;
                    d /= tmpt->speed * (GAME_SPEED_SCALE * game_speed);
                    tmpo1->death_time = tmpt->timeout = tS + d;
                    tmpt->target    = tmpo1;
                    tmpt->theta         = Arg(tmpo1->x - tmpo->x, tmpo1->y - tmpo->y);
                    QueueInsert(tmpt, torpedo_head.last);
                    Sweep(2000, 86, 53);
                }
                else if (tmpo->ship_guns > 0 && d < tmpo->ship_guns_range)
                {
                    tmpo->flags |= OF_SHOOTING;
                    if (Rand < 125.0 * period)
                    {
                        ShipDamage(tmpo1, tmpo->ship_guns * Rand * period);
                        if (Rand < 10.0 * period)
                            tmpo1->fuel *= 0.75 * Rand + 0.25;
                    }
                    if (tS > next_noise)
                    {
                        Noise(100, 29, 46);
                        next_noise = tS + 0.1;
                    }
                }
            }
            else
            {
                if (tmpo->air_guns > 0 && d < tmpo->air_guns_range)
                {
                    tmpo->flags |= OF_SHOOTING;
                    if (Rand<125.0 * period)
                    {
                        tmpo1->life_percent -= tmpo->air_guns * Rand * period;
                        if (Rand < 10.0 * period)
                            tmpo1->fuel *= 0.75 * Rand + 0.25;
                    }
                    if (tS > next_noise)
                    {
                        Noise(25, 62, 86);
                        next_noise = tS + 0.025;
                    }
                }
            }
        }
        tmpo = tmpo->next;
    }
    tmpo = obj_head.next;
    while (tmpo != &obj_head)
    {
        tmpo1 = tmpo->next;
        if (tmpo->type == OT_FIGHTER &&
                (tmpo->life_percent <= 0.0 || tmpo->flags & OF_ACTIVE && tmpo->fuel <= 0.0 || tmpo->host &&
                !(tmpo->flags & OF_ACTIVE) && tmpo->host->life_percent <= 0.0))
            ObjDel(tmpo);
        tmpo = tmpo1;
    }
    tmpo = obj_head.next;
    while (tmpo != &obj_head)
    {
        tmpo1 = tmpo->next;
        if (tmpo->life_percent <= 0.0)
            ObjDel(tmpo);
        tmpo = tmpo1;
    }
}

#define ANIMATE_FREQ        50
U0 AnimateTask(I64)
{
    Obj     *tmpo;
    Torpedo *tmpt, *tmpt1;
    F64      d, period;

    while (TRUE)
    {
        period = GAME_SPEED_SCALE * game_speed / ANIMATE_FREQ;
        SquadronActions;

        tmpo = obj_head.next;
        while (tmpo != &obj_head)
        {
            if (tmpo->flags & OF_ACTIVE && tmpo->fuel > 0)
            {
                if (tmpo->dtheta)
                {
                    d = tmpo->dtheta;
                    if (d > tmpo->turn_rate * period)
                        d = tmpo->turn_rate * period;
                    if (d < -tmpo->turn_rate * period)
                        d = -tmpo->turn_rate * period;
                    tmpo->theta  += d;
                    tmpo->dtheta -= d;
                }
                tmpo->x     += tmpo->speed * Cos(tmpo->theta) * period * tmpo->life_percent / 100.0;
                tmpo->y     += tmpo->speed * Sin(tmpo->theta) * period * tmpo->life_percent / 100.0;
                tmpo->fuel  -= tmpo->speed * tmpo->fuel_burn_rate * period;
            }
            tmpo = tmpo->next;
        }

        tmpt = torpedo_head.next;
        while (tmpt != &torpedo_head)
        {
            tmpt1 = tmpt->next;
            if (tS > tmpt->timeout)
            {
                tmpo = tmpt->target;
                if (Rand < 0.333333)
                {
                    ShipDamage(tmpo, 150 * Rand * Rand);
                    if (Rand < 0.333333)
                        tmpo->fuel *= 0.75 * Rand + 0.25;
                }
                QueueRemove(tmpt);
                Free(tmpt);
            }
            else
            {
                tmpt->x += tmpt->speed * Cos(tmpt->theta) * period;
                tmpt->y += tmpt->speed * Sin(tmpt->theta) * period;
            }
            tmpt = tmpt1;
        }

        tmpo = obj_head.next;
        while (tmpo != &obj_head)
        {
            if (tmpo->host && !(tmpo->flags & OF_ACTIVE))
            {
                tmpo->x = tmpo->host->x;
                tmpo->y = tmpo->host->y;
            }
            tmpo = tmpo->next;
        }

        tmpo = obj_head.next;
        while (tmpo != &obj_head)
        {
            if (tmpo->flags & OF_ACTIVE)
            {
                if (tmpo->host)
                {
                    d = Sqrt(Sqr(tmpo->x - tmpo->host->x) + Sqr(tmpo->y - tmpo->host->y));
                    if (d < 8 && tmpo->max_fuel - tmpo->fuel > 30)
                    {
                        tmpo->life_percent = 100.0;
                        if (tmpo->host->fuel > 0)
                        {
                            d = tmpo->max_fuel - tmpo->fuel;
                            if (d > tmpo->host->fuel)
                                d = tmpo->host->fuel;
                            tmpo->host->fuel -= d;
                            tmpo->fuel += d;
                        }
                        tmpo->torpedos  = tmpo->max_torpedos;
                        tmpo->x         = tmpo->host->x;
                        tmpo->y         = tmpo->host->y;
                        tmpo->flags     &= ~OF_ACTIVE;
                    }
                    else if (d > tmpo->fuel - 250)
                        tmpo->flags |= OF_RETURNING;
                    if (tmpo->flags & OF_RETURNING)
                        tmpo->dtheta += Wrap(Arg(tmpo->host->x - tmpo->x, tmpo->host->y - tmpo->y) - (tmpo->theta + tmpo->dtheta), -pi);
                }
                else if (tmpo->type == OT_CARRIER)
                    ShipFix(tmpo, 2.5 * period);
            }
            tmpo = tmpo->next;
        }

        AI(1, period);
        Combat(period);
        Sleep(1000 / ANIMATE_FREQ);
    }
}

U0 FlatTops()
{
    I64 arg1, arg2;

    SettingsPush; //See SettingsPush
    Fs->text_attr = BLUE << 4 + WHITE;
    AutoComplete;
    WinBorder;
    WinMax;
    DocCursor;
    DocClear;
    PopUpOk("$GREEN${Left-Mouse}$FG$\t\tChange Course\n"
            "$GREEN${Right-Mouse}$FG$\t\tLaunch Squadron\n"
            "$GREEN${Right-Double-Mouse}$FG$\tReturn Squadron\n"
            "$GREEN${Ctrl-Left Grab}$FG$\tScroll Screen\n");

    MenuPush(   "File {"
                "  Abort(,CH_SHIFT_ESC);"
                "  Exit(,CH_ESC);"
                "}"
                "Play {"
                "  Restart(,'\n');"
                "  Faster(,'+');"
                "  Slower(,'-');"
                "}"
                );
    Fs->win_inhibit |= WIF_SELF_MS_L | WIF_SELF_MS_R;
    Init;
    Fs->draw_it = &DrawIt;
    Fs->animate_task = Spawn(&AnimateTask, NULL, "Animate",, Fs);
    try
    {
        while (TRUE)
            switch (MessageGet(&arg1, &arg2, 
                                1 << MESSAGE_KEY_DOWN | 1 << MESSAGE_MS_L_UP | 1 << MESSAGE_MS_R_UP | 1 << MESSAGE_MS_R_D_UP))
            {
                case MESSAGE_KEY_DOWN:
                    switch (arg1)
                    {
                        case '\n':
                            CleanUp;
                            Init;
                            break;

                        case CH_ESC:
                        case CH_SHIFT_ESC:
                            goto nv_done;

                        case '+':
                            game_speed *= 1.5;
                            break;

                        case '-':
                            game_speed /= 1.5;
                            break;
                    }
                    break;

                case MESSAGE_MS_L_UP:
                    SquadronSettheta(0, arg1, arg2);
                    break;

                case MESSAGE_MS_R_UP:
                    SquadronLaunch(0, arg1, arg2);
                    break;

                case MESSAGE_MS_R_D_UP:
                    SquadronReturn(0, arg1, arg2);
                    break;
            }
nv_done:
        MessageGet(,, 1 << MESSAGE_KEY_UP);
    }
    catch
        PutExcept;
    SettingsPop;
    CleanUp;
    MenuPop;
}

FlatTops;

















//Maybe use this in the future  <7>/* Graphics Not Rendered in HTML */