//Uses fixed-point.

RegDft("TempleOS/Talons","F64 best_score=9999;\n");
RegExe("TempleOS/Talons");

//Keep these power of two so shift is used instead of multiply
//to index arrays.
#define MAP_WIDTH               1024
#define MAP_HEIGHT              1024

#define MAP_SCALE               150
#define DISPLAY_SCALE           100
#define CTRLS_SCALE             0.05

//I think I did these so the heads-up showed intelligable numbers.
//Scaling is a mess.
#define COORDINATE_SCALE        256
#define COORDINATE_BITS         8

#define WATER_ELEVATION         15
#define BIRD_ELEVATION          10
#define ROCK_ELEVATION          45
#define SNOW_ELEVATION          55

//Too big makes off-scrn draws take place.
#define PANEL_SIZE_MAX          16





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








#define LS_TYPES        3
U8      *landscape_small_imgs[LS_TYPES]={<4>,<5>,<6>},
        *landscape_large_imgs[LS_TYPES];

#define B_NUM   256
class Bird
{
  Bird *next,*last;
  CD3I64 p;
  F64 theta;
} b_head[mp_cnt];

class Obj
{
  Obj *next,*last;
  CD3I64 p;
  U8 *img;
  Bool fish;
};

class Panel
{//Polygon or Obj
  Panel *next;
  CD3I32 *pts;
  I64 cnt;
  U16 update,num_sides;
  CColorROPU32 color;
  Obj *next_obj,*last_obj;
} *panel_head,*panels[MAP_HEIGHT][MAP_WIDTH];

I64 critical_section_flag;
I16 elevations[MAP_HEIGHT][MAP_WIDTH];
CD3 normals[MAP_HEIGHT][MAP_WIDTH];

class MPCtrl {
  I64 init_not_done_flags,update_not_done_flags,app_not_done_flags;
  I64 strip_width[MP_PROCESSORS_NUM];
  Bool app_done;
} mp;

F64 game_t0,game_tf,pitch,roll,heading,phi,speed;
Bool invert_pitch,rolled_over;
I64 strip_height,x,y,z,fish_left;
CD3 v;

U0 WrapAngles()
{
  I64 r[4][4],x,y,z;

  phi=Wrap(phi);
  pitch=Wrap(-phi-pi/2);
  if (Abs(pitch)>pi/2) {
    invert_pitch=TRUE;
    pitch=Wrap(pi-pitch);
  } else
    invert_pitch=FALSE;
  roll=Wrap(roll);
  if (invert_pitch ^^ -pi/2<=roll<pi/2)
    rolled_over=FALSE;
  else
    rolled_over=TRUE;
  heading=Wrap(heading,0);

  //World to scrn coordinates
  Mat4x4IdentEqu(r);
  Mat4x4RotZ(r,heading);
  Mat4x4RotX(r,phi);
  Mat4x4RotZ(r,roll);

  //We use velocity vector for dog-fighting.
  x=0x100000000*speed; y=0; z=0;
  Mat4x4MulXYZ(r,&x,&y,&z);
  v.x=x/ToF64(0x100000000); v.y=y/ToF64(0x100000000); v.z=z/ToF64(0x100000000);
}

U0 EDTransform(CDC *dc,I64 *x,I64 *y,I64 *z)
{
  I64 zz;
  Mat4x4MulXYZ(dc->r,x,y,z);
  *z=zz=-*z;
  if (zz>0) {
    *x = dc->x + *x * DISPLAY_SCALE/zz; //Foreshortening
    *y = dc->y - *y * DISPLAY_SCALE/zz;
  } else {
    *x = dc->x + *x;
    *y = dc->y - *y;
  }
}

U0 CalcNormals()
{/*Find the normal vect with a curl.

i,j and k are the axis unit vectors,
not to be confused with my local index variables.

i  j  k
0  1  dz2
1  0  dz1

Normal:  dz1*i + dz2*j - k
*/
  I64 i,j;
  for (j=0;j<MAP_HEIGHT-1;j++) {
    for (i=0;i<MAP_WIDTH-1;i++) {
      normals[j][i].x=elevations[j][i+1]-elevations[j][i];
      normals[j][i].y=elevations[j+1][i]-elevations[j][i];
      normals[j][i].z=-1;
      D3Unit(&normals[j][i]);
    }
    MemSet(&normals[j][i],0,sizeof(CD3));
  }
  for (i=0;i<MAP_WIDTH-1;i++)
    MemSet(&normals[j][i],0,sizeof(CD3));
}

Bool TestSameSlope(I64 x,I64 y,I64 w,I64 h)
{
  CD3 p,*s;
  I64 k1,k2;
  if (!(0<=x && x+w<MAP_WIDTH && 0<=y && y+h<MAP_HEIGHT))
    return FALSE;
  s=&normals[y][x];
  for (k2=0;k2<h;k2++)
    for (k1=0;k1<w;k1++)
      if (D3NormSqr(D3Sub(&p,&normals[y+k2][x+k1],s))>.10)
        return FALSE;
  return TRUE;
}

U0 MPDoPanels(CTask *task)
{
  I64 i,j,l,k1,k2,w,h,threshold,lo,hi;
  Bool cont;
  Panel *tmpp,*start_ptr=NULL,*end_ptr=NULL;
  CD3I32 *poly;
  Obj *tmpo;
  lo=Gs->num*(MAP_HEIGHT-1)/mp_cnt;
  hi=(Gs->num+1)*(MAP_HEIGHT-1)/mp_cnt;
  for (threshold=8;threshold>=1;threshold--)
    for (j=lo;j<hi;j++) {
      for (i=0;i<MAP_WIDTH-1;i++) {
        if (!panels[j][i]) {
          w=1;
          h=1;
          do {
            cont=FALSE;
            if (w<PANEL_SIZE_MAX && TestSameSlope(i,j,w+1,h)) {
              w++;
              cont=TRUE;
            }
            if (h<PANEL_SIZE_MAX && TestSameSlope(i,j,w,h+1)) {
              h++;
              cont=TRUE;
            }
          } while (cont);
          if (w>=threshold || h>=threshold) {
            tmpp=CAlloc(sizeof(Panel),task);
            QueInit(&tmpp->next_obj);
            l=elevations[j][i];
            if (l<=WATER_ELEVATION*MAP_SCALE &&
                  elevations[j][i+w-1]<=WATER_ELEVATION*MAP_SCALE &&
                  elevations[j+h-1][i]<=WATER_ELEVATION*MAP_SCALE &&
                  elevations[j+h-1][i+w-1]<=WATER_ELEVATION*MAP_SCALE) {
              tmpp->color=BLUE;
              if (Rand<0.05) {
                tmpo=MAlloc(sizeof(Obj),task);
                tmpo->p.x=(i+w/2)*MAP_SCALE;
                tmpo->p.y=(j+h/2)*MAP_SCALE;
                if (Rand<0.1) {
                  tmpo->fish=FALSE;
                  if (RandI16&1)
                    tmpo->img=landscape_large_imgs[0]; //Boat
                  else
                    tmpo->img=landscape_large_imgs[1]; //Boat
                  tmpo->p.z=(WATER_ELEVATION+2)*MAP_SCALE;
                } else {
                  tmpo->fish=TRUE;
                  tmpo->img=<1>; //Fish
                  tmpo->p.z=WATER_ELEVATION*MAP_SCALE;
                }
                QueIns(tmpo,tmpp->last_obj);
              }
            } else {
              if (l<ROCK_ELEVATION*MAP_SCALE) {
                if (RandI16&1)
                  tmpp->color=LTGREEN;
                else
                  tmpp->color=GREEN+LTGREEN<<16+ROPF_DITHER;
                if (Rand<0.03) {
                  tmpo=MAlloc(sizeof(Obj),task);
                  tmpo->p.x=(i+w/2)*MAP_SCALE;
                  tmpo->p.y=(j+h/2)*MAP_SCALE;
                  tmpo->p.z=l;
                  tmpo->img=landscape_large_imgs[2]; //Tree
                  tmpo->fish=FALSE;
                  QueIns(tmpo,tmpp->last_obj);
                }
              } else if (l<SNOW_ELEVATION*MAP_SCALE) {
                if (!(RandU16&3)) {
                  if (RandI16&1)
                    tmpp->color=LTGRAY;
                  else
                    tmpp->color=DKGRAY+LTGRAY<<16+ROPF_DITHER;
                } else {
                  if (RandI16&1)
                    tmpp->color=LTGREEN;
                  else
                    tmpp->color=GREEN+LTGREEN<<16+ROPF_DITHER;
                }
              } else {
                if (!(RandU16&3)) {
                  if (RandI16&1)
                    tmpp->color=WHITE;
                  else
                    tmpp->color=LTGRAY;
                } else {
                  if (RandI16&1)
                    tmpp->color=LTGRAY+WHITE<<16+ROPF_DITHER;
                  else
                    tmpp->color=DKGRAY+LTGRAY<<16+ROPF_DITHER;
                }
              }
            }
            tmpp->num_sides=4;
            poly=tmpp->pts=MAlloc(sizeof(CD3I32)*tmpp->num_sides,task);
            poly[0].x=MAP_SCALE*i;
            poly[0].y=MAP_SCALE*j;
            poly[0].z=elevations[j][i];
            poly[1].x=MAP_SCALE*(i+w);
            poly[1].y=MAP_SCALE*j;
            poly[1].z=elevations[j][i+w];
            poly[2].x=MAP_SCALE*(i+w);
            poly[2].y=MAP_SCALE*(j+h);
            poly[2].z=elevations[j+h][i+w];
            poly[3].x=MAP_SCALE*i;
            poly[3].y=MAP_SCALE*(j+h);
            poly[3].z=elevations[j+h][i];
            tmpp->next=start_ptr;
            start_ptr=tmpp;
            if (!end_ptr)
              end_ptr=tmpp;
            for (k2=0;k2<h;k2++)
              for (k1=0;k1<w;k1++)
                panels[j+k2][i+k1]=tmpp;
          }
        }
      }
    }
  if (end_ptr) {
    while (LBts(&critical_section_flag,0))
      Yield;
    if (end_ptr)
      end_ptr->next=panel_head;
    panel_head=start_ptr;
    LBtr(&critical_section_flag,0);
  }
  LBtr(&mp.init_not_done_flags,Gs->num);
}

U0 InitElevations()
{
  I64 i,j,l,x,y,xx,yy,x1,y1,x2,y2;
  MemSet(elevations,0,sizeof(elevations));
  for (i=0;i<MAP_WIDTH*MAP_HEIGHT/128;i++) {
    x=RandU32%MAP_WIDTH;
    y=RandU32%MAP_HEIGHT;
    j=1<<(RandU32%6);
    l=0;
    while (j--) {
      if (!l && RandU16<U16_MAX/4)
        l=RandU16%(j+1);
      if (l) {
        x1=ClampI64(x-j,0,MAP_WIDTH-1);
        x2=ClampI64(x+j,0,MAP_WIDTH-1);
        y1=ClampI64(y-j,0,MAP_HEIGHT-1);
        y2=ClampI64(y+j,0,MAP_HEIGHT-1);
        for (yy=y1;yy<y2;yy++)
          for (xx=x1;xx<x2;xx++)
            elevations[yy][xx]+=MAP_SCALE/2;
        l--;
      }
    }
  }

  for (j=0;j<MAP_HEIGHT;j++)
    for (i=0;i<MAP_WIDTH;i++)
      if (elevations[j][i]<WATER_ELEVATION*MAP_SCALE)
        elevations[j][i]=WATER_ELEVATION*MAP_SCALE;
}

U0 InitMap()
{/*We make a topographic data structure "elevations[][]"
and convert it to panels. "panels[][]" holds the panels
for each spot.
*/
  I64 i;
  InitElevations;
  MemSet(panels,0,sizeof(panels));
  CalcNormals;
  panel_head=NULL;
  mp.init_not_done_flags=1<<mp_cnt-1;
  for (i=0;i<mp_cnt;i++)
    Spawn(&MPDoPanels,Fs,"Do Panels",i);
  while (mp.init_not_done_flags)
    Sleep(1);
}

F64 claws_down;

#define HAND_X  (1.0-claws_down)*(0.3*w)+claws_down*(0.4*w)
#define HAND_Y  (1.0-claws_down)*(h-125)+claws_down*h




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



U0 ClawDraw(CDC *dc,I64 x1,I64 y1,I64 x2,I64 y2,I64 w,I64 segments,Bool talon)
{
  I64 i,j;
  for (i=0,j=segments;i<segments;i++,j--) {
    dc->thick=w;
    dc->color=BLACK;
    GrLine3(dc,
        j*x1/segments+i*x2/segments,
        j*y1/segments+i*y2/segments,0,
        (j-1)*x1/segments+(i+1)*x2/segments,
        (j-1)*y1/segments+(i+1)*y2/segments,0);
    dc->thick=w-2;
    dc->color=YELLOW;
    GrLine3(dc,
        j*x1/segments+i*x2/segments,
        j*y1/segments+i*y2/segments,0,
        (j-1)*x1/segments+(i+1)*x2/segments,
        (j-1)*y1/segments+(i+1)*y2/segments,0);
  }
  if (talon) {
    if (y1<y2)
      Sprite3B(dc,x1,y1,0,<7>);
    else
      Sprite3B(dc,x1,y1,0,<8>);
  }
}

U0 ClawsDraw(CTask *task,CDC *dc)
{
  F64 claws_up=1.0-claws_down;
  I64 w=task->pix_width,h=task->pix_height;
  dc->flags|=DCF_SYMMETRY;
  DCSymmetrySet(dc,w>>1,0,w>>1,1);

  ClawDraw(dc,HAND_X-30,HAND_Y-50*claws_up,HAND_X-5,HAND_Y,     22,4,TRUE);
  ClawDraw(dc,HAND_X-10,HAND_Y-60*claws_up,HAND_X,HAND_Y,       22,4,TRUE);
  ClawDraw(dc,HAND_X+10,HAND_Y-60*claws_up,HAND_X,HAND_Y,       22,4,TRUE);
  ClawDraw(dc,HAND_X+30,HAND_Y-50*claws_up,HAND_X+5,HAND_Y,     22,4,TRUE);
  ClawDraw(dc,HAND_X+25,HAND_Y+40*claws_up,HAND_X+5,HAND_Y,     22,4,TRUE);

  ClawDraw(dc,HAND_X,HAND_Y,6*w/20,h,38,5,FALSE);
}

CDC *main_dc;
U0 DrawIt(CTask *task,CDC *dc)
{
  main_dc->flags|=DCF_NO_TRANSPARENTS;
  GrBlot(dc,0,0,main_dc);
  if (claws_down)
    ClawsDraw(task,dc);
}

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


















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
































Cores render strips that +/- 15%. The cores check the panel map array
and render the panel for each square, marking-it done.

The depth buf is not locked in the graphic routines
so we get some glitches.
*/

I64 update_jiffy_limit;

U0 MPDrawIt(CTask *task,CDC *dc)
{
  I64 j,update=winmgr.updates&65535,strip_width,*s2w,x1,y1,z1,xx,yy,
        xh,yh,zh,yh2,xh2,x1h,y1h,x1wa,y1wa,x1wb,y1wb,x3,y3,z3,dd,dd_old,
        cx=task->pix_width>>1,cy=task->pix_height>>1,r[16],*elems1,*elems_hard;
  Panel reg *tmpp;
  Bird  *tmpb;
  Obj   *tmpo;
  Bool  w_on_map,h_on_map;

  xx=x/(MAP_SCALE*COORDINATE_SCALE);
  yy=y/(MAP_SCALE*COORDINATE_SCALE);

  //World to scrn coordinates
  Mat4x4IdentEqu(dc->r);
  Mat4x4RotZ(dc->r,heading);
  Mat4x4RotX(dc->r,phi);
  Mat4x4RotZ(dc->r,roll);
  DCMat4x4Set(dc,dc->r);

  //Scrn to world coordinates

  //This gives us the vects for stepping through the grid in
  //the direction the plane is facing. we step horizontally and vertically
  //and use the reciprocal slope principle
  //y=mx+b and y=(-1/m)x+b are perpendicular.

  s2w=Mat4x4IdentNew;
  Mat4x4RotZ(s2w,-roll);
  Mat4x4RotX(s2w,-phi);
  Mat4x4RotZ(s2w,-heading);

  xh=0;
  yh=0;
  zh=-256;
  Mat4x4MulXYZ(s2w,&xh,&yh,&zh);

  //The layer for core1 is not cleared automatically
  //it is persistent.  I have carefully syncronized to the update
  //cycle initiated by Core0 to prevent flicker.

  dc->flags|=DCF_TRANSFORMATION;
  dc->transform=&EDTransform;
  dc->x=cx;
  dc->y=cy;

  //dc->x and the translation part of dc->r are ident in effect,
  //but we use both to show-off.        We could add offsets together and
  //use one or the other.

  x1=-x>>COORDINATE_BITS;
  y1=-y>>COORDINATE_BITS;
  z1=-z>>COORDINATE_BITS;
  Mat4x4MulXYZ(dc->r,&x1,&y1,&z1);
  Mat4x4TranslationEqu(dc->r,x1,y1,z1);

  //This is a refinement.
  if (Abs(phi*180/pi)>90) {
    x3=0;
    y3=-cy;
    z3=0;
    Mat4x4MulXYZ(s2w,&x3,&y3,&z3);
    xx+=x3;
    yy+=y3;
  }

  if (Gs->num&1) {//alt left-right,right-left
    yh2=-yh;
    xh2=-xh;
  } else {
    yh2=yh;
    xh2=xh;
  }

  //Calc starting point.
  x1wa=xx<<8+xh*strip_height>>1/1.3*(Gs->num+1.15);
  y1wa=yy<<8+yh*strip_height>>1/1.3*(Gs->num+1.15);
  x1wb=0;
  y1wb=0;

  xh=-xh; //Back to front to help with depth.
  yh=-yh;

  //Take half steps to cover whole grid.
  xh>>=1;  yh>>=1;
  xh2>>=1; yh2>>=1;
  w_on_map=FALSE;
  dd_old=I64_MAX;
  for (strip_width=0;cnts.jiffies<update_jiffy_limit;strip_width++) {
    x1h=x1wa;
    y1h=y1wa;
    h_on_map=FALSE;
    for (j=0;j<strip_height && cnts.jiffies<update_jiffy_limit;j++) {
      x1=x1h>>8; y1=y1h>>8;
      if (0<=x1<MAP_WIDTH && 0<=y1<MAP_HEIGHT) {
        if ((tmpp=panels[y1][x1]) && tmpp->update!=update) {
          tmpp->update=update;
          if (tmpp->cnt>8*(1.1-Gs->idle_factor)) {
            dc->color=tmpp->color;
            tmpp->cnt=GrFillPoly3(dc,tmpp->num_sides,tmpp->pts);
          } else
            tmpp->cnt++;
          tmpo=tmpp->next_obj;
          while (tmpo!=&tmpp->next_obj) {
            Sprite3(dc,tmpo->p.x,tmpo->p.y,tmpo->p.z,tmpo->img);
            tmpo=tmpo->next;
          }
        }
        h_on_map=TRUE;
      } else if (h_on_map)
        break;
      x1h+=xh;
      y1h+=yh;
    }
    if (h_on_map)
      w_on_map=TRUE;
    else if (w_on_map) {
      strip_width=I64_MAX;
      break;
    }
    x1wb-=yh2;
    y1wb+=xh2;
    if (strip_width&1) {
      x1wa-=x1wb;
      y1wa-=y1wb;
    } else {
      x1wa+=x1wb;
      y1wa+=y1wb;
    }
    if (!w_on_map) {
      dd=SqrI64(x1wa>>8-MAP_WIDTH>>1)+SqrI64(y1wa>>8-MAP_HEIGHT>>1);
      if (dd>dd_old)
        break;
      dd_old=dd;
    }
  }

  tmpb=b_head[Gs->num].next;
  while (tmpb!=&b_head[Gs->num]) {
    elems1=SpriteInterpolate(Tri(tS,0.2),<2>,<3>);

    Mat4x4IdentEqu(r);
    Mat4x4RotZ(r,tmpb->theta);
    elems_hard=SpriteTransform(elems1,r);

    Sprite3(dc,tmpb->p.x,tmpb->p.y,tmpb->p.z,elems_hard);
    Free(elems_hard);
    Free(elems1);

    tmpb=tmpb->next;
  }

  Free(s2w);
  mp.strip_width[Gs->num]=strip_width;
  LBtr(&mp.update_not_done_flags,Gs->num);
}

U0 CoreAPTalonsTask(CTask *master_task)
{
  CDC *dc=DCAlias(main_dc,master_task);
  while (!mp.app_done) {
    while (!Bt(&mp.update_not_done_flags,Gs->num) && !mp.app_done)
      Sleep(1);
    if (!mp.app_done)
      MPDrawIt(master_task,dc);
  }

  //We made an alias of this we don't want freed.
  dc->depth_buf=NULL;

  DCDel(dc);
  LBtr(&mp.app_not_done_flags,Gs->num);
}

U0 DrawHorizon(CDC *dc)
{
  I64 x1,y1,z1,x2,y2,z2,xh,yh,zh,*s2w=Mat4x4IdentNew,
        cx=Fs->pix_width>>1,
        cy=Fs->pix_height>>1;
  CD3I32 p[4];
  I32 *old_db=dc->depth_buf;
  dc->depth_buf=NULL;
  CColorROPU32 ground_color;

  if (game_tf && fish_left)
    DCFill(dc,BLACK);
  else if (-pi/4<=Wrap(phi-pi)<pi/4)
    DCFill(dc,LTCYAN);
  else {
    if (z/COORDINATE_SCALE<(WATER_ELEVATION+3)*MAP_SCALE)
      ground_color=BLUE;
    else
      ground_color=LTGREEN;

    Mat4x4IdentEqu(dc->r);
    Mat4x4RotZ(dc->r,heading);
    Mat4x4RotX(dc->r,phi);
    Mat4x4RotZ(dc->r,roll);

    DCMat4x4Set(dc,dc->r);
    dc->flags&=~DCF_TRANSFORMATION;
    dc->transform=&EDTransform;
    dc->x=cx;
    dc->y=cy;

    Mat4x4RotZ(s2w,-roll);
    Mat4x4RotX(s2w,-phi);
    Mat4x4RotZ(s2w,-heading);

    xh=0;
    yh=0;
    zh=-256;
    Mat4x4MulXYZ(s2w,&xh,&yh,&zh);
    Free(s2w);

    x1=xh+yh*32; y1=yh-xh*32; z1=0;
    (*dc->transform)(dc,&x1,&y1,&z1);
    x2=xh-yh*32; y2=yh+xh*32; z2=0;
    (*dc->transform)(dc,&x2,&y2,&z2);
    DCClipLine(dc,&x1,&y1,&x2,&y2);

    MemSet(p,0,sizeof(p));
    if (x2<x1) {
      SwapI64(&x1,&x2);
      SwapI64(&y1,&y2);
    }
    if (!x1 && x2==dc->width-1) {
      p[0].x=0;
      p[0].y=0;
      p[1].x=dc->width-1;
      p[1].y=0;
      p[2].x=dc->width-1;
      p[2].y=y2;
      p[3].x=0;
      p[3].y=y1;
      if (rolled_over)
        dc->color=ground_color;
      else
        dc->color=LTCYAN;
      GrFillPoly3(dc,4,p);
      p[0].y=dc->height-1;
      p[1].y=dc->height-1;
      if (rolled_over)
        dc->color=LTCYAN;
      else
        dc->color=ground_color;
      GrFillPoly3(dc,4,p);
    } else {
      if (y2<y1) {
        SwapI64(&x1,&x2);
        SwapI64(&y1,&y2);
      }
      if (!y1 && y2==dc->height-1) {
        p[0].x=0;
        p[0].y=0;
        p[1].x=0;
        p[1].y=dc->height-1;
        p[2].x=x2;
        p[2].y=dc->height-1;
        p[3].x=x1;
        p[3].y=0;
        if (x1<x2 ^^ rolled_over)
          dc->color=ground_color;
        else
          dc->color=LTCYAN;
        GrFillPoly3(dc,4,p);
        p[0].x=dc->width-1;
        p[1].x=dc->width-1;
        if (x1<x2 ^^ rolled_over)
          dc->color=LTCYAN;
        else
          dc->color=ground_color;
        GrFillPoly3(dc,4,p);
      } else
        DCFill(dc,LTCYAN); //Not correct.
    }
  }

  dc->depth_buf=old_db;
}

U0 Core0Talons()
{
  CDC *dc=DCAlias(main_dc,Fs);
  I64 i,xx,yy,elev,height,cx=Fs->pix_width>>1,cy=Fs->pix_height>>1;
  F64 min_strip_width,tt;

  update_jiffy_limit=cnts.jiffies+JIFFY_FREQ/40;

  xx=x/(MAP_SCALE*COORDINATE_SCALE);
  yy=y/(MAP_SCALE*COORDINATE_SCALE);
  if (0<=xx<MAP_WIDTH && 0<=yy<MAP_HEIGHT)
    elev=elevations[yy][xx];
  else
    elev=I64_MAX;

  height=z/COORDINATE_SCALE-elev;
  if (height<0 && elev>WATER_ELEVATION*MAP_SCALE && !game_tf) {
    music.mute=TRUE;
    Beep;
    game_tf=tS;
    music.mute=FALSE;
  }

  DrawHorizon(dc);

  if (game_tf) {
    tt=game_tf-game_t0;
    if (Blink) {
      dc->color=RED;
      GrPrint(dc,(Fs->pix_width-9*FONT_WIDTH)/2,
            (Fs->pix_height-FONT_HEIGHT)/2,"Game Over");
    }
  } else {
    DCDepthBufRst(dc);
    mp.update_not_done_flags=1<<mp_cnt-1;
    MPDrawIt(Fs,dc);
    while (mp.update_not_done_flags)
      Sleep(1);

    min_strip_width=F64_MAX;
    for (i=0;i<mp_cnt;i++)
      min_strip_width=Min(min_strip_width,32.0*mp.strip_width[i]/(i+32.0));
    strip_height=ClampI64(
          strip_height*Clamp(0.25*min_strip_width/strip_height,0.9,1.1),
          64,1024);

    tt=tS-game_t0;
    if (tt<5.0 && Blink) {
      dc->color=WHITE;
      GrPrint(dc,(Fs->pix_width-13*FONT_WIDTH)/2,
            (Fs->pix_height-FONT_HEIGHT)/2-4*FONT_HEIGHT,"Catch 10 Fish");
    }
  }

  dc->thick=2;
  if (game_tf && fish_left)
    dc->color=WHITE;
  else
    dc->color=BLACK;
  dc->flags&=~DCF_TRANSFORMATION;
  GrLine3(dc,cx+5,cy,0,cx-5,cy,0);
  GrLine3(dc,cx,cy+5,0,cx,cy-5,0);
  if (invert_pitch)
    GrPrint(dc,0,0,"Pitch:%5.1f Roll:%5.1f Heading:%5.1f "
          "Height:%5d [Core Strip:%3d]",
          pitch*180/pi,Wrap(roll+pi)*180/pi,Wrap(heading+pi,0)*180/pi,
          height,strip_height);
  else
    GrPrint(dc,0,0,"Pitch:%5.1f Roll:%5.1f Heading:%5.1f "
          "Height:%5d [Core Strip:%3d]",
          pitch*180/pi,roll*180/pi,heading*180/pi,height,strip_height);
  GrPrint(dc,0,FONT_HEIGHT,"Fish Remaining:%d Time:%3.2f Best:%3.2f",
        fish_left,tt,best_score);

  //We made an alias of this we don't want freed.
  dc->depth_buf=NULL;
  DCDel(dc);
  Refresh;
}


Obj *FishFind(I64 x1,I64 y1,I64 *_dd)
{
  I64 dd,best_dd=I64_MAX;
  Obj *res=NULL,*tmpo;
  Panel *tmpp=panel_head;
  while (tmpp) {
    tmpo=tmpp->next_obj;
    while (tmpo!=&tmpp->next_obj) {
      if (tmpo->fish) {
        dd=SqrI64(tmpo->p.x-x1)+SqrI64(tmpo->p.y-y1);
        if (dd<best_dd) {
          best_dd=dd;
          res=tmpo;
        }
      }
      tmpo=tmpo->next;
    }
    tmpp=tmpp->next;
  }
  *_dd=best_dd;
  return res;
}

#define ANIMATE_MS      10

U0 AnimateTask(I64)
{//Steadily moves the airplane fwd.
  I64 i,*s2w,x1,y1,z1,dx,dy,dz,dd;
  F64 t0=tS,ms,theta,d;
  Obj *tmpo;
  Bird  *tmpb;
  while (TRUE) {
    ms=1000*(tS-t0);
    t0=tS;
    if (!game_tf) {
//Scrn to world coordinates
      s2w=Mat4x4IdentNew;
      Mat4x4RotZ(s2w,-roll);
      Mat4x4RotX(s2w,-phi);
      Mat4x4RotZ(s2w,-heading);

      dx=0;dy=0;dz=1<<16;
      Mat4x4MulXYZ(s2w,&dx,&dy,&dz);
      x-=speed*ms*COORDINATE_SCALE*dx/1<<16;
      y-=speed*ms*COORDINATE_SCALE*dy/1<<16;
      z-=speed*ms*COORDINATE_SCALE*dz/1<<16;
      Free(s2w);
      x1=x/COORDINATE_SCALE; y1=y/COORDINATE_SCALE; z1=z/COORDINATE_SCALE;
      if (z1<(WATER_ELEVATION+3)*MAP_SCALE) {
        if (z1<WATER_ELEVATION*MAP_SCALE) {
          z=WATER_ELEVATION*MAP_SCALE*COORDINATE_SCALE;
          if (invert_pitch) {
            if (pitch<-pi/8)
              phi+=ms/1000.0*Sign(3*pi/8-phi);
          } else if (pitch<-pi/8)
            phi+=ms/1000.0*Sign(-3*pi/8-phi);
          WrapAngles;
        }
        speed=1.0;
        if (rolled_over || !(tmpo=FishFind(x1,y1,&dd)))
          claws_down=0;
        else {
          d=Sqrt(dd);
          x1-=tmpo->p.x;
          y1-=tmpo->p.y;
          theta=ACos((dx*x1+dy*y1)/(d*1<<16));
          if (theta>0 && d<MAP_SCALE*4)
            claws_down=Saw(d,MAP_SCALE*4);
          else
            claws_down=0.01;
          if (d<MAP_SCALE*2) {
            if (!--fish_left) {
              game_tf=tS;
              if (game_tf-game_t0<best_score)
                best_score=game_tf-game_t0;
            }
            QueRem(tmpo);
            Free(tmpo);
            music.mute=TRUE;
            Snd(74); Sleep(200); Snd;
            music.mute=FALSE;
          }
        }
      } else {
        claws_down=0;
        if (-pi/4<=phi<=pi/4)
          speed+=0.0005;
        else if (-3*pi/4<=phi<=3*pi/4)
          speed+=0.0001;
        else
          speed-=0.0001;
        speed=Clamp(speed+(0.0005-0.0002*Abs(phi)/(pi/4)),0.1,5.0);
      }

      for (i=0;i<mp_cnt;i++) {
        tmpb=b_head[i].next;
        while (tmpb!=&b_head[i]) {
          tmpb->p.x+=10*ms/1000*MAP_SCALE*Cos(tmpb->theta);
          tmpb->p.y+=10*ms/1000*MAP_SCALE*Sin(tmpb->theta);
          tmpb->p.z=BIRD_ELEVATION*MAP_SCALE+
                elevations[tmpb->p.y/MAP_SCALE][tmpb->p.x/MAP_SCALE];
          tmpb->theta+=2*pi*ms/1000/10;
          tmpb=tmpb->next;
        }
      }
    }
    Refresh;
  }
}

U0 MPEnd()
{
  update_jiffy_limit=0;
  mp.app_not_done_flags=1<<mp_cnt-1-1;
  mp.app_done=TRUE;
//Wait for all cores to exit
  while (mp.app_not_done_flags)
    Sleep(1);
}

U0 TaskEndCB()
{
  MPEnd;
  Exit;
}

U0 SongTask(I64)
{
  Fs->task_end_cb=&SndTaskEndCB;
  MusicSettingsRst;
  while (TRUE) {
    Play("5eCGFsD4A5e.C4sG5eGDqCDeGsGG4qG");
    Play("5eCGFsD4A5e.C4sG5eGDqCDeGsGG4qG");
    Play("5eGECGC4A5FCsC4B5C4B5e.GsG4qGB");
    Play("5eGECGC4A5FCsC4B5C4B5e.GsG4qGB");
  }
}

U0 PreInit()
{
  I64 i,*r=Mat4x4IdentNew;
  Mat4x4Scale(r,10.0);
  for (i=0;i<LS_TYPES;i++)
    landscape_large_imgs[i]=SpriteTransform(landscape_small_imgs[i],r);
  Free(r);
}

U0 PostCleanUp()
{
  I64 i;
  for (i=0;i<LS_TYPES;i++)
    Free(landscape_large_imgs[i]);
}

U0 Init()
{
  I64 i,xx,yy;
  Bird *tmpb;

  main_dc=DCNew(GR_WIDTH,GR_HEIGHT);
  critical_section_flag=0;
  game_tf=0;
  fish_left=10;

  MemSet(&mp,0,sizeof(MPCtrl));
  InitMap;
  DCDepthBufAlloc(main_dc);

  strip_height=128;

  phi   =-90.0*pi/180.0;
  roll  =0;
  heading=0;
  speed =2.5;
  claws_down=0;
  WrapAngles;

  x=MAP_WIDTH>>1 *COORDINATE_SCALE*MAP_SCALE;
  y=MAP_HEIGHT>>1*COORDINATE_SCALE*MAP_SCALE;
  z=64           *COORDINATE_SCALE*MAP_SCALE;

  xx=x/(MAP_SCALE*COORDINATE_SCALE);
  yy=y/(MAP_SCALE*COORDINATE_SCALE);
  z+=elevations[yy][xx]*COORDINATE_SCALE;

  for (i=0;i<mp_cnt;i++)
    QueInit(&b_head[i]);

  for (i=0;i<B_NUM;i++) {
    tmpb=MAlloc(sizeof(Bird));
    tmpb->p.x=Rand*MAP_WIDTH*MAP_SCALE;
    tmpb->p.y=Rand*MAP_HEIGHT*MAP_SCALE;
    tmpb->p.z=BIRD_ELEVATION*MAP_SCALE+
          elevations[tmpb->p.y/MAP_SCALE][tmpb->p.x/MAP_SCALE];
    tmpb->theta=2*pi*Rand;
    QueIns(tmpb,b_head[i%mp_cnt].last);
  }

  for (i=1;i<mp_cnt;i++)
    Spawn(&CoreAPTalonsTask,Fs,"AP Talons",i);
  Fs->task_end_cb=&TaskEndCB;
  game_t0=tS;
}

U0 CleanUp()
{
  I64 i;
  Panel *tmpp=panel_head,*tmpp1;
  MPEnd;
  while (tmpp) {
    tmpp1=tmpp->next;
    QueDel(&tmpp->next_obj);
    Free(tmpp->pts);
    Free(tmpp);
    tmpp=tmpp1;
  }
  for (i=0;i<mp_cnt;i++) {
    QueDel(&b_head[i]);
    QueInit(&b_head[i]);
  }
  DCDel(main_dc);
}

U0 Talons()
{
  I64 ch,sc;
  SettingsPush; //See SettingsPush
  MenuPush(
        "File {"
        "  Abort(,CH_SHIFT_ESC);"
        "  Exit(,CH_ESC);"
        "}"
        "Play {"
        "  Restart(,'\n');"
        "  Down(,,SC_CURSOR_UP);"
        "  Up(,,SC_CURSOR_DOWN);"
        "  Left(,,SC_CURSOR_LEFT);"
        "  Right(,,SC_CURSOR_RIGHT);"
        "}"
        );
  AutoComplete;
  WinBorder;
  WinMax;
  DocCursor;
  DocClear;
  "Initializing...\n";
  Fs->song_task=Spawn(&SongTask,NULL,"Song",,Fs);
  PreInit;
  Init;
  Fs->animate_task=Spawn(&AnimateTask,NULL,"Animate",,Fs);
  Fs->draw_it=&DrawIt;
  try //in case <CTRL-ALT-c> is pressed.
    do {
      if (ScanKey(&ch,&sc)) {
        switch (ch) {
          case 0:
            switch (sc.u8[0]) {
              start:
                case SC_CURSOR_DOWN:
                  phi-=CTRLS_SCALE*Cos(roll);
                  heading-=CTRLS_SCALE*Sin(roll)*Sin(phi);
                  break;
                case SC_CURSOR_UP:
                  phi+=CTRLS_SCALE*Cos(roll);
                  heading+=CTRLS_SCALE*Sin(roll)*Sin(phi);
                  break;
                case SC_CURSOR_RIGHT:
                  roll+=CTRLS_SCALE;
                  break;
                case SC_CURSOR_LEFT:
                  roll-=CTRLS_SCALE;
                  break;
              end:
                WrapAngles;
            }
            break;
          case '\n':
            Fs->draw_it=NULL;
            CleanUp;
            Refresh;
            Init;
            Fs->draw_it=&DrawIt;
            break;
        }
      } else
        Core0Talons;
    } while (ch!=CH_SHIFT_ESC && ch!=CH_ESC);
  catch
    PutExcept;
  SettingsPop;
  CleanUp;
  PostCleanUp;
  MenuPop;
  RegWrite("TempleOS/Talons","F64 best_score=%5.4f;\n",best_score);
}

Talons;