Trivial Solutions
                    The LoseThos 64-bit PC Operating System
File:/LT/Demo/GameStarters/EagleDive.CPZ
This, and all LoseThos files, are public domain.  Do whatever you like.
God is watching; God is just; God is never out-done in generosity.  He talks!

LoseThos code compiles with the 64-bit LoseThos compiler/assembler I wrote.
Boot the LoseThos CD and you can compile it.  Not in Kansas anymore!

Click here for preliminary compiler information you need.

//This uses fixed-point.

//This program can be static compiled to a .BIZ or JIT.
//If static compiling, override the finename extention warning.
#ifstatic
#include "::/LT/OSMain/StaticAdam.HPZ"
I64 best_score=0; //Doesn't work yet for statics
#else
AcctRegSetDftEntry("LT/EagleDive","I64 best_score=0;\n");
AcctRegExecuteBranch("LT/EagleDive");
#endif


//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 ROCK_ELEVATION          45
#define SNOW_ELEVATION          55

//Too big makes off-screen draws take place.
#define MAX_PANEL_SIZE          16

class Panel
{
  Panel *next;
  I32 n,   //num polygon sides
     num; //panel number
  P3I32 *points;
  I64 color,cnt;
};

class Fish
{
  Fish *next,*last;
  I64 x,y,z;
} fish_root;





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





I64 critical_section_flag;
U0 CriticalSectionStart()
{
  while (LBts(&critical_section_flag,0))
    Yield;
}
U0 CriticalSectionEnd()
{
  LBtr(&critical_section_flag,0);
}

//These are NOT deallocated after the program ends,
//but I did them this way for your benefit
//so 2D array indexing is not done by hand.
//You can type "EagleDive;" at the command line
//to rerun without new allocations.
//Normally, just spawn a popup task,
//run it and kill it using an icon.
#ifstatic
#exe {OptOn(OPTf_GLBLS_ON_DATA_HEAP);};
#endif
I16 elevations[MAP_HEIGHT][MAP_WIDTH];
I32 normals[MAP_HEIGHT][MAP_WIDTH*2];
Panel *panels[MAP_HEIGHT][MAP_WIDTH*2];
#ifstatic
#exe {OptOff(OPTf_GLBLS_ON_DATA_HEAP);};
#endif

I64 num_panels;
Panel *panel_root;
U8 *panels_processed_bitmap;

F64 game_t0,v_grid_size,span_factor,phi,theta1,theta2,speed;

I64 x,y,z,game_score;

BoolI8 game_over;

class MPCtrl {
  I64 mp_cnt;
  U64 do_update;
  F64 update_done_t[MP_MAX_PROCESSORS];
  U64 done_flags;
  BoolI64 app_done;
} mp;

U0 FSTransform(GrDC *dc,I64 *x,I64 *y,I64 *z)
{
  I64 zz;
//I accidentally made the Z coordinate sign backward
//from the fill-poly depth buf scheme, so it is negated at the end.
//Negative Z coordinates are the ones we want plotted.
  GrRot(dc->r,x,y,z);

//We don't want divide by zero or make too monstrous foreground objects.
//The foreground object boundary is kinda crappy looking when
//you point toward the ground.
  *z=-*z;
  zz=*z+50;
  if (zz>0) {
//The divide causes "foreshortening" which makes the distance go to a vanishing point.
    *x = *x * DISPLAY_SCALE/ zz;
    *y = *y * DISPLAY_SCALE/ zz;
  }
  *x+=dc->x;
  *y =dc->y-*y;
}



U0 CalcNormals()
{
/*The idea is that there are two triangles
forming every grid square and we find the normal
vect with a curl.

 i  j  k
 0  1  dz2
 1  0  dz1

Normal:
dz1*i + dz2*j - k

  i   j   k
 -1   0  -dz3
  0  -1  -dz4

Normal:
-dz3*i - dz4*j + k

 
Basically, we want to combine grid
locations with the same normals.  These
are not unit vects for one thing.

What I came-up with is dz1/dz2 and dz3/dz4.

It works pretty well.  It combines
more than it should, but that's good
because it would  result in too many
panels.

*/
  I64 i,j,k,dz1,dz2;
  for (j=0;j<MAP_HEIGHT-1;j++) {
    k=0;
    for (i=0;i<MAP_WIDTH-1;i++) {
      dz1=elevations[j][i+1]-elevations[j][i];
      dz2=elevations[j+1][i]-elevations[j][i];
      if (dz2)
        normals[j][k++]=dz1<<16/dz2;
      else {
        if (!dz1)
          normals[j][k++]=0;
        else if (dz1>0)
          normals[j][k++]=MAX_I32;
        else
          normals[j][k++]=MIN_I32;
      }

      dz1=elevations[j+1][i+1]-elevations[j][i+1];
      dz2=elevations[j+1][i+1]-elevations[j+1][i];
      if (dz2)
        normals[j][k++]=dz1<<16/dz2;
      else {
        if (!dz1)
          normals[j][k++]=0;
        else if (dz1>0)
          normals[j][k++]=MAX_I32;
        else
          normals[j][k++]=MIN_I32;
      }
    }
    normals[j][k++]=0;
    normals[j][k++]=0;
  }
  k=0;
  for (i=0;i<MAP_WIDTH-1;i++) {
    normals[j][k++]=0;
    normals[j][k++]=0;
  }
}

BoolI64 TestSameSlope(I64 x,I64 y,I64 w,I64 h)
{
  I64 k1,k2,xx,yy,s;
  if (!(0<=x && x+w<MAP_WIDTH && 0<=y && y+h<MAP_HEIGHT))
    return FALSE;
    //Works well averaging the two slopes of two triangles.
    //If it is too picky, we get too many panels.
  s=normals[y][x*2]+normals[y][x*2+1];
  for (k2=0;k2<h;k2++)
    for (k1=0;k1<w;k1++) {
      xx=(x+k1)*2;
      yy=y+k2;
      if (normals[yy][xx]+normals[yy][xx+1]!=s)
       return FALSE;
    }
  return TRUE;
}

U0 MPDoPanels(TaskStruct *task)
{
  I64 i,j,k,l,k1,k2,w,h,threshold,lo,hi;
  BoolI8 cont;
  Panel *tempp,*start_ptr=NULL,*end_ptr=NULL;
  P3I32 *poly;
  Fish *tempf;
  lo=Gs->num*(MAP_HEIGHT-1)/mp.mp_cnt;
  hi=(Gs->num+1)*(MAP_HEIGHT-1)/mp.mp_cnt;
  for (threshold=8;threshold>=1;threshold--)
    for (j=lo;j<hi;j++) {
      k=0;
      for (i=0;i<MAP_WIDTH-1;i++) {
        if (!panels[j][k]) {
          w=1;
          h=1;
          do {
            cont=FALSE;
            if (w<MAX_PANEL_SIZE && TestSameSlope(i,j,w+1,h)) {
              w++;
              cont=TRUE;
            }
            if (h<MAX_PANEL_SIZE && TestSameSlope(i,j,w,h+1)) {
              h++;
              cont=TRUE;
            }
          } while (cont);
          if (w>=threshold || h>=threshold) {
            tempp=CAlloc(sizeof(Panel),task);
            lock {tempp->num=num_panels++;}
            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) {
              tempp->color=BLUE;
              if (Rand<0.05) {
                tempf=MAlloc(sizeof(Fish),task);
                tempf->x=(i+w/2)*MAP_SCALE;
                tempf->y=(j+h/2)*MAP_SCALE;
                tempf->z=l;
                CriticalSectionStart;
                InsQue(tempf,fish_root.last);
                CriticalSectionEnd;
              }
            } else {
              if (RandI16&1) {
                if (l<ROCK_ELEVATION*MAP_SCALE)
                  tempp->color=LTGREEN;
                else if (l<SNOW_ELEVATION*MAP_SCALE) {
                  if (!(RandU16&3))
                    tempp->color=LTGRAY;
                  else
                    tempp->color=LTGREEN;
                } else {
                  if (!(RandU16&3))
                    tempp->color=WHITE;
                  else
                    tempp->color=LTGRAY;
                }
              } else {
                if (l<ROCK_ELEVATION*MAP_SCALE)
                  tempp->color=GREEN+LTGREEN<<16+ROPF_DITHER;
                else if (l<SNOW_ELEVATION*MAP_SCALE) {
                  if (!(RandU16&3))
                    tempp->color=DKGRAY+LTGRAY<<16+ROPF_DITHER;
                  else
                    tempp->color=GREEN+LTGREEN<<16+ROPF_DITHER;
                } else {
                  if (!(RandU16&3))
                    tempp->color=LTGRAY+WHITE<<16+ROPF_DITHER;
                  else
                    tempp->color=DKGRAY+LTGRAY<<16+ROPF_DITHER;
                }
              }
            }
            tempp->n=4;
            poly=tempp->points=MAlloc(sizeof(P3I32)*tempp->n,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];
            tempp->next=start_ptr;
            start_ptr=tempp;
            if (!end_ptr)
              end_ptr=tempp;
            for (k2=0;k2<h;k2++)
              for (k1=0;k1<w;k1++) {
                panels[j+k2][(i+k1)*2]=tempp;
                panels[j+k2][(i+k1)*2+1]=tempp;
              }
          }
        }
        k+=2;
      }
    }
  if (end_ptr) {
    CriticalSectionStart;
    if (end_ptr)
      end_ptr->next=panel_root;
    panel_root=start_ptr;
    CriticalSectionEnd;
  }
  LBts(&mp.done_flags,Gs->num);
}


U0 InitMap()
{
//We make a topographic data structure "elevations[][]"
//and convert it to panels.  The conversion to panels leaves
//gaps the way I did it, but it looks good, except for sky leaking through
//There is no wrong way to pick colors if it looks good, unless
//you wanted to do lighting.

//"panels[][]" holds the flat and sloped panels for each spot, while
//"hstep_panels[][]" holds side panels in cases of step-like formations.
//They require a separate array because a single "elevation[][]" spot can
//have a panel on top and a panel on the side like stairs.

  I64 i,j,l,k1,k2,x,y,xx,yy;
  MemSet(elevations,0,sizeof(elevations));
  MemSet(panels,0,sizeof(panels));
  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<MAX_U16/4)
        l=RandU16%(j+1);
      if (l) {
        for (k1=-j;k1<=j;k1++)
          for (k2=-j;k2<=j;k2++) {
            xx=x+k2; yy=y+k1;
            if (0<=xx<MAP_WIDTH &&
                0<=yy<MAP_HEIGHT)
              elevations[yy][xx]+=MAP_SCALE/2;
          }
          l--;
      }
    }
  }

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

  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;


  CalcNormals;

  panel_root=NULL;
  num_panels=0;

  mp.done_flags=0;
  for (i=1;i<mp.mp_cnt;i++)
    MPSpawn(&MPDoPanels,Fs,"Do Panels",1<<i);
  MPDoPanels(Fs);
  while (mp.done_flags!=1<<mp.mp_cnt-1)
    Yield;
  panels_processed_bitmap=MAlloc((num_panels+7)>>3);
}


GrDC *main_dc;

U0 UpdateWin(TaskStruct *task)
{
  GrDC *dc=GrDCAlias(gr_dc,task);
  GrDCBlot(dc,0,0,main_dc);
  GrDCDel(dc);
}


/*/* Graphics Not Rendered in HTML */





















Cores render strips.  The strips are wider toward the horizon by span_factor
and overlap +/- 15%. The cores check the panel map array
and render the panel for each square and mark-it done.

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

*/

U0 MPDrawIt(TaskStruct *task,GrDC *dc)
{
//This is identical to the previous routine but is run by core#1
  I64 i,j,ww,hh,*s2w,x1,y1,z1, xx,yy,
      xh,yh,zh, yh2,xh2, reg x1w, reg y1w, x1h,y1h,
      x3,y3,z3,
      cx=task->win_pixel_width>>1,
      cy=task->win_pixel_height>>1;
  F64 h_grid_size,t0=tNP;
  Panel reg *tempp;

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

  //World to screen coordinates
  GrIdentEqu(dc->r);
  GrRotZEqu(dc->r,theta2);
  GrRotXEqu(dc->r,phi);
  GrRotZEqu(dc->r,theta1);
  GrSetRotMat(dc,dc->r);

  //Screen 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=GrIdent;
  GrRotZEqu(s2w,-theta1);
  GrRotXEqu(s2w,-phi);
  GrRotZEqu(s2w,-theta2);
  xh=0;
  yh=0;
  zh=-256;
  GrRot(s2w,&xh,&yh,&zh);

//The layer for core#1 is not cleared automatically
//it is persistent.  I have carefully syncronized to the update
//cycle initiated by core#0 to prevent flicker.

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

//dc->x and the translation part of dc->r are identical 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;
  GrRot(dc->r,&x1,&y1,&z1);
  GrSetTranslation(dc->r,x1,y1,z1);

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

  h_grid_size=4.0*v_grid_size*(Gs->num+span_factor)/span_factor;
  if (Gs->num&1) { //alternate left-right,right-left
    yh2=-yh;
    xh2=-xh;
  } else {
    yh2=yh;
    xh2=xh;
  }

  ww=h_grid_size;
  hh=1.30*v_grid_size; //Cores overlap 15% on each side of strip.
  //Calc starting point.
  x1h=xx<<8+0.5*yh2*h_grid_size+xh*v_grid_size*(Gs->num+1.15);
  y1h=yy<<8-0.5*xh2*h_grid_size+yh*v_grid_size*(Gs->num+1.15);

  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;
  ww<<=1;
  hh<<=1;

  for (j=0;j<hh;j++) {
    x1w=x1h;
    y1w=y1h;
    for (i=0;i<ww;i++) {
      x1=x1w>>8; y1=y1w>>8;
      if (0<=x1<MAP_WIDTH && 0<=y1<MAP_HEIGHT) {
          if ((tempp=panels[y1][x1*2]) && !LBts(panels_processed_bitmap,tempp->num)) {
            if (tempp->cnt>8*(1.1-Gs->idle_factor)) {
              dc->color=tempp->color;
              tempp->cnt=GrFillPoly3(dc,tempp->n,tempp->points);
            } else
              tempp->cnt++;
          }
      }
      x1w-=yh2;
      y1w+=xh2;
    }
    x1h+=xh;
    y1h+=yh;
  }
  Free(s2w);
  mp.update_done_t[Gs->num]=tNP-t0;
}

U0 APEagleDiveTask(TaskStruct *master_task)
{
  GrDC *dc=GrDCAlias(main_dc,master_task);
  Preempt(ON);
  while (!mp.app_done) {
    LBts(&Fs->task_flags,TASKf_IDLE);
    while (!LBtr(&mp.do_update,Gs->num) && !mp.app_done)
      Yield;
    LBtr(&Fs->task_flags,TASKf_IDLE);
    if (!mp.app_done)
      MPDrawIt(master_task,dc);
  }

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

  GrDCDel(dc);
  LBts(&mp.done_flags,Gs->num);
}

U0 DrawHorizon(GrDC *dc)
{
  I64 x1,y1,z1,x2,y2,z2,xh,yh,zh,*s2w=GrIdent,
      cx=Fs->win_pixel_width>>1,
      cy=Fs->win_pixel_height>>1;
  P3I32 p[4];
  I32 *old_db=dc->depth_buf;
  dc->depth_buf=NULL;

  GrIdentEqu(dc->r);
  GrRotZEqu(dc->r,theta2);
  GrRotXEqu(dc->r,phi);
  GrRotZEqu(dc->r,theta1);
  GrSetRotMat(dc,dc->r);
  dc->flags&=~DCF_TRANSFORMATION;
  dc->transform=&FSTransform;
  dc->x=cx;
  dc->y=cy;

  GrRotZEqu(s2w,-theta1);
  GrRotXEqu(s2w,-phi);
  GrRotZEqu(s2w,-theta2);
  xh=0;
  yh=0;
  zh=-256;
  GrRot(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);
  GrDCClipLine(dc,&x1,&y1,&x2,&y2);

  MemSet(p,0,sizeof(p));
  if (x2<x1) {
    SwapU64(&x1,&x2);
    SwapU64(&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 (-pi/2<=theta1<pi/2 ^^ -pi/2<=Unwrap(phi-pi/2)<pi/2)
      dc->color=LTCYAN;
    else
      dc->color=LTGREEN;
    GrFillPoly3(dc,4,p);
    p[0].y=dc->height-1;
    p[1].y=dc->height-1;
    if (-pi/2<=theta1<pi/2 ^^ -pi/2<=Unwrap(phi-pi/2)<pi/2)
      dc->color=LTGREEN;
    else
      dc->color=LTCYAN;
    GrFillPoly3(dc,4,p);
  } else {
    if (y2<y1) {
      SwapU64(&x1,&x2);
      SwapU64(&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 ^^ -pi/2<=theta1<pi/2 ^^ -pi/2<=Unwrap(phi-pi/2)<pi/2)
        dc->color=LTCYAN;
      else
        dc->color=LTGREEN;
      GrFillPoly3(dc,4,p);
      p[0].x=dc->width-1;
      p[1].x=dc->width-1;
      if (x1<x2 ^^ -pi/2<=theta1<pi/2 ^^ -pi/2<=Unwrap(phi-pi/2)<pi/2)
        dc->color=LTGREEN;
      else
        dc->color=LTCYAN;
      GrFillPoly3(dc,4,p);
    }
  }
  dc->depth_buf=old_db;
}


U0 BSPEagleDive(GrDC *dc)
{
  F64 last_t;
  I64  i,xx,yy,height,bkcolor,
      cx=Fs->win_pixel_width>>1,
      cy=Fs->win_pixel_height>>1;
  Fish *tempf;

  MemSet(panels_processed_bitmap,0,(num_panels+7)>>3);
  MemSet(mp.update_done_t,0,sizeof(F64)*MP_MAX_PROCESSORS);

  xx=x/(MAP_SCALE*COORDINATE_SCALE);
  yy=y/(MAP_SCALE*COORDINATE_SCALE);
  height=z/COORDINATE_SCALE-elevations[yy][xx];
  if (height<0) {
    if (elevations[yy][xx]>WATER_ELEVATION*MAP_SCALE) {
      dc->color=bkcolor=BLACK;
      if (!game_over) {
        GrRect(dc,0,0,dc->width,dc->height);
        music_mute=TRUE;
        Beep;
        game_over=TRUE;
        music_mute=FALSE;
      }
    } else
      bkcolor=BLUE;
  } else
    bkcolor=LTCYAN;

  if (height>=0 && !(-pi/4<=Unwrap(phi-pi)<pi/4)) {
    DrawHorizon(dc);
    GrDCDepthBufRst(dc);

  //Signal all cores to start update
    mp.do_update=1<<mp.mp_cnt-1-1;

    //Wait for all cores to start
    while (mp.do_update)
      Yield;

    MPDrawIt(Fs,dc);

    xx=x/COORDINATE_SCALE;
    yy=y/COORDINATE_SCALE;
    tempf=fish_root.next;
    while (tempf!=&fish_root) {
      if (SqrI64(tempf->x-xx)+SqrI64(tempf->y-yy)<MAP_SCALE*MAP_SCALE*20*20)
        GrSpritePlot3(dc,tempf->x,tempf->y,tempf->z,<1>);
      tempf=tempf->next;
    }

    //Wait for all cores to complete.
    LBts(&Fs->task_flags,TASKf_IDLE);
    for (i=1;i<mp.mp_cnt;i++)
      while (!mp.update_done_t[i])
        Yield;
    LBtr(&Fs->task_flags,TASKf_IDLE);

    last_t=mp.update_done_t[0];
    for (i=1;i<mp.mp_cnt;i++)
      last_t=Max(last_t,mp.update_done_t[i]);

      //Adjust size of updated grid.
    v_grid_size  *=0.90+0.10*(0.65/Fs->win_max_refresh)/last_t;
    v_grid_size=Limit(v_grid_size,20,200);
    //Regulate how much grid expands in the distance.
    if (mp.mp_cnt>1)
      span_factor*=0.90+0.10*mp.update_done_t[mp.mp_cnt-1]/mp.update_done_t[0];
    span_factor=Limit(span_factor,.30,3.0);

  } else {
    dc->color=bkcolor;
    GrRect(dc,0,0,dc->width,dc->height);
  }
  dc->pen_width=2;
  if (bkcolor==BLUE)
    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);
  GrPrintF(dc,0,0,"theta1:%5.1f phi:%5.1f theta2:%5.1f Box:%5.1f",
      theta1*180/pi,phi*180/pi,theta2*180/pi,v_grid_size);
  GrPrintF(dc,0,FONT_HEIGHT,"x:%5.1f y:%5.1f z:%5.1f height:%3d score:%d high:%d",
      ToF64(x)/COORDINATE_SCALE,ToF64(y)/COORDINATE_SCALE,ToF64(z)/COORDINATE_SCALE,
      height,game_score,best_score);

  if (game_over && Blink(,tP)) {
    dc->color=RED;
    GrPutS(dc,(Fs->win_pixel_width-9*FONT_WIDTH)/2,
        (Fs->win_pixel_height-FONT_HEIGHT)/2,"Game Over");
  } else if (tP-game_t0<5.0 && Blink(,tP)) {
    dc->color=WHITE;
    GrPutS(dc,(Fs->win_pixel_width-10*FONT_WIDTH)/2,
        (Fs->win_pixel_height-FONT_HEIGHT)/2,"Catch Fish");
  }

  WinSync;
}

#define ANIMATE_MS      10

U0 AnimateTask(U64 dummy=0)
{
  nounusedwarn dummy;
  //This just steadily moves the airplane forward.
  I64 *s2w,x1,y1,z1;
  F64 t0=tP(Fs->parent_task),mS;
  Fish *tempf,*tempf1;
  while (TRUE) {
    mS=1000*(tP(Fs->parent_task)-t0);
    t0=tP(Fs->parent_task);
    if (!game_over) {
      //Screen to world coordinates
      s2w=GrIdent;
      GrRotZEqu(s2w,-theta1);
      GrRotXEqu(s2w,-phi);
      GrRotZEqu(s2w,-theta2);
      x1=0;y1=0;z1=-speed*mS*COORDINATE_SCALE;
      GrRot(s2w,&x1,&y1,&z1);
      x+=x1;
      y+=y1;
      z+=z1;
      Free(s2w);
      x1=x/COORDINATE_SCALE; y1=y/COORDINATE_SCALE; z1=z/COORDINATE_SCALE;
      if (z1<WATER_ELEVATION*MAP_SCALE) {
        speed=0.5;
        tempf=fish_root.next;
        while (tempf!=&fish_root) {
          tempf1=tempf->next;
          if (SqrI64(tempf->x-x1)+SqrI64(tempf->y-y1)<MAP_SCALE*MAP_SCALE*5*5) {
            game_score++;
            if (game_score>best_score)
              best_score=game_score;
            RemQue(tempf);
            Free(tempf);
            music_mute=TRUE;
            Snd(1000); Sleep(200); Snd(0);
            music_mute=FALSE;
          }
          tempf=tempf1;
        }
      } else {
        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=Limit(speed+(0.0005-0.0002*Abs(phi)/(pi/4)),0.1,5.0);
      }
    }
    WinSync;
  }
}

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

U0 TaskEndCB()
{
  MPEnd;
  Exit;
}

U0 SongTask(U64 dummy=0)
{ //Song by Terry A. Davis
  nounusedwarn dummy;
#ifstatic
  Fs->task_end_cb=ExtFind("SndTaskEndCB");//(Can't take addr of an extern function.)
#else
  Fs->task_end_cb=&SndTaskEndCB;
#endif
  MusicSettingsRst;
  music_stacatto_factor=0.2;
  while (TRUE) {
        Play("W13eBDEDBDEDFEEDFEED");
        Play("BDEDBDEDFEEDFEED");
  }
}

U0 Init(I64 _mp_cnt)
{
  I64 i;
  main_dc=GrDCNew(DCT_COLOR4,GR_WIDTH,GR_HEIGHT);
  critical_section_flag=0;
  game_over=FALSE;
  game_score=0;
  MemSet(&mp,0,sizeof(MPCtrl));
  if (_mp_cnt<=mp_cnt)
    mp.mp_cnt=_mp_cnt;
  else
    mp.mp_cnt=mp_cnt;
  fish_root.next=fish_root.last=&fish_root;
  InitMap;
  GrDCAllocDepthBuf(main_dc);

  Fs->win_max_refresh=30.0;

  v_grid_size=64.0;
  span_factor=1.0;
  phi     =-90.0*pi/180.0;
  theta1=  0.0*pi/180.0;
  theta2=  0.0*pi/180.0;
  speed =  2.5;

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

  for (i=1;i<mp.mp_cnt;i++)
    MPSpawn(&APEagleDiveTask,Fs,"AP EagleDive",1<<i);
  Fs->task_end_cb=&TaskEndCB;
  game_t0=tP;
}

U0 CleanUp()
{
  Fish *tempf=fish_root.next,*tempf1;
  Panel *tempp=panel_root,*tempp1;

  MPEnd;

  while (tempf!=&fish_root) {
    tempf1=tempf->next;
    RemQue(tempf);
    Free(tempf);
    tempf=tempf1;
  }
  while (tempp) {
    tempp1=tempp->next;
    Free(tempp->points);
    Free(tempp);
    tempp=tempp1;
  }
  Free(panels_processed_bitmap);
  GrDCDel(main_dc);
}

U0 EagleDive(I64 _mp_cnt=MP_MAX_PROCESSORS)
{
  I64 ch,sc;
  F64 pp,tt1,tt2;

  SettingsPush; //See SettingsPush
  //We really can't afford to run at 60Hz.      This is how often VGA
  //memory is updated, not related to actual hardware refresh rates.

  WordStat(OFF);
  MenuPush(
  "File {"
  "  Abort(,CH_SHIFT_ESC);"
  "  Exit(,CH_ESC);"
  "}"
  "Play {"
  "  Restart(,CH_CR);"
  "  Down(,,SC_CURSOR_UP);"
  "  Up(,,SC_CURSOR_DOWN);"
  "  Left(,,SC_CURSOR_LEFT);"
  "  Right(,,SC_CURSOR_RIGHT);"
      "}"
      );
  WinMax;
  WinBorder(OFF);
  "Initializing...\n";
  Fs->song_task=Spawn(&SongTask,NULL,"Song",Fs);
  Init(_mp_cnt);
  Fs->animate_task=Spawn(&AnimateTask,NULL,"Animate",Fs);
  Fs->update_win=&UpdateWin;
  try //in case <CTRL-ALT-C> is pressed.
    do {
      if (ScanKey(&ch,&sc)) {
        pp=phi; tt1=theta1; tt2=theta2;
        switch (ch) {
          case 0:
            switch (sc.u8[0]) {
              case SC_CURSOR_DOWN:
                phi       +=-CTRLS_SCALE*Cos(tt1);
                theta2+=-CTRLS_SCALE*Sin(tt1)*Sin(phi);
                break;
              case SC_CURSOR_UP:
                phi       -=-CTRLS_SCALE*Cos(tt1);
                theta2-=-CTRLS_SCALE*Sin(tt1)*Sin(phi);
                break;
              case SC_CURSOR_RIGHT:
                theta1+=CTRLS_SCALE;
                break;
              case SC_CURSOR_LEFT:
                theta1-=CTRLS_SCALE;
                break;
            }
            break;
          case CH_CR:
            Fs->update_win=NULL;
            CleanUp;
            WinSync;
            Init(_mp_cnt);
            Fs->update_win=&UpdateWin;
            break;
          }
        theta1=Unwrap(theta1);
        phi       =Unwrap(phi);
        theta2=Unwrap(theta2);
      } else
        BSPEagleDive(main_dc);
    } while (ch!=CH_SHIFT_ESC && ch!=CH_ESC);
  catch
    CatchAll;
  SettingsPop;
  CleanUp;
#ifnstatic
  AcctRegWriteBranch("LT/EagleDive","I64 best_score=%d;\n",best_score);
#endif
  MenuPop;
}

EagleDive;
Historical capture: 2012-03-09. Home | Power on LoseThos | Sources and changes