#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 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 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)
                                        {
                                                MyNoise(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)
                                        {
                                                MyNoise(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 */