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






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




#define BORDER          6
#define EAT_TIME        0.5
#define FLAP_TIME       0.5
#define BAT_BOX 10

Bool flap_down,flap_up;
F64 flap_phase,delta_phase,bat_y,bat_x,eat_timeout,flap_time;
F64 frame_x,game_t0,game_tf;

#define BUGS_NUM        32
I32 bug_cnt,bugs_x[BUGS_NUM],bugs_y[BUGS_NUM];
Bool bugs_dead[BUGS_NUM];

#define GLOW_PERIOD     3.0
F64 bugs_glow_phase[BUGS_NUM];

CDC *limit_flood_fill_dc; //Prevent uncontrolled flood-fill flicker.

U0 DrawIt(CTask *task,CDC *dc)
{
  I64 i,y,x,
        h=MaxI64(1,task->pix_width-2*BORDER),
        v=MaxI64(1,task->pix_height-2*BORDER);
  U8 *tmps,*tmps2,*tmps3;
  F64 tt=flap_phase*flap_phase*flap_phase,ts=tS;

  bat_x=task->pix_width>>3;
  Sprite3(dc,(7*task->pix_width)>>3,20,0,<5>);

  dc->color=DKGRAY;
  GrCircle(dc,bat_x+5+20*Saw(15*tS,2),bat_y,10+20*Saw(15*tS,2),5,-pi/4,pi/2);

  tmps=SpriteInterpolate(tt,<1>,<2>);
  if (eat_timeout && tS<eat_timeout) {
    tmps2=SpriteInterpolate(tt,<3>,<4>);
    tmps3=SpriteInterpolate(1.0-(eat_timeout-tS)/EAT_TIME,tmps2,tmps);
    Free(tmps);
    Free(tmps2);
    tmps=tmps3;
  }

  DCFill(limit_flood_fill_dc);
  Sprite3(limit_flood_fill_dc,16,32,0,tmps);
  if (GrPeek(limit_flood_fill_dc,0,0)!=TRANSPARENT) {//Did FloodFill go crazy?
    limit_flood_fill_dc->color=TRANSPARENT;
    GrFloodFill(limit_flood_fill_dc,0,0);
  }
  GrBlot(dc,bat_x-16,bat_y-32,limit_flood_fill_dc);

  Free(tmps);
  for (i=0;i<BUGS_NUM;i++)
    if (!bugs_dead[i]) {
      x=(bugs_x[i]+frame_x)%h+BORDER;
      y=bugs_y[i]%v+BORDER;
      if (Saw(ts+bugs_glow_phase[i],GLOW_PERIOD)<0.2) {
        if (i&1)
          dc->color=YELLOW;
        else
          dc->color=LTGREEN;
      } else
        dc->color=BLACK;
      GrPlot(dc,x,y);
      GrPlot(dc,x+1,y);
      dc->color=BLACK;
      GrPlot(dc,x,y-1);
    }
  if (game_tf) {
    dc->color=RED;
    GrPrint(dc,(task->pix_width-FONT_WIDTH*14)/2,
          (task->pix_height-FONT_HEIGHT)/2,"Game Completed");
    tt=game_tf;
  } else {
    dc->color=BLACK;
    tt=tS;
  }
  GrPrint(dc,0,0,"Bugs:%3.1f%% Time:%3.2f Best:%3.2f",
        100.0*(BUGS_NUM-bug_cnt)/BUGS_NUM,tt-game_t0,best_score);
}

U0 CheckBugs(CTask *task)
{
  I64 i,x,y,
        h=MaxI64(1,task->pix_width-2*BORDER),
        v=MaxI64(1,task->pix_height-2*BORDER);
  if (eat_timeout && eat_timeout-tS<0.75*EAT_TIME) {
    Suspend(task->song_task,FALSE);
    if (tS>=eat_timeout)
      eat_timeout=0;
  }
  for (i=0;i<BUGS_NUM;i++)
    if (!bugs_dead[i]) {
      x=(bugs_x[i]+frame_x)%h+BORDER;
      y=bugs_y[i]%v+BORDER;
      if (AbsI64(x-bat_x)<BAT_BOX && AbsI64(y-bat_y)<BAT_BOX) {
        bugs_dead[i]=TRUE;
        eat_timeout=tS+EAT_TIME;
        Snd(74);
        Suspend(task->song_task);
        bug_cnt--;
      }
    }
  if (!game_tf && !bug_cnt) {
    game_tf=tS;
    Suspend(task->song_task);
    Snd;
    if (game_tf-game_t0<best_score)
      best_score=game_tf-game_t0;
  }
  frame_x-=0.1;
  if (frame_x<0)
    frame_x+=h;
}

U0 Init()
{
  I64 i;
  limit_flood_fill_dc=DCNew(32,40);
  flap_down=flap_up=FALSE;
  flap_phase=0;
  bat_x=Fs->pix_width>>3;
  bat_y=0;
  frame_x=0;
  bug_cnt=BUGS_NUM;
  for (i=0;i<BUGS_NUM;i++) {
    bugs_dead[i]=FALSE;
    bugs_x[i]=RandU16;
    bugs_y[i]=RandU16;
    bugs_glow_phase[i]=GLOW_PERIOD*Rand;
  }
  Suspend(Fs->song_task,FALSE);
  flap_time=eat_timeout=0;
  delta_phase=game_tf=0;
  game_t0=tS;
}

U0 SongTask(I64)
{//Song by Terry A. Davis
  Fs->task_end_cb=&SndTaskEndCB;
  MusicSettingsRst;
  while (TRUE) {
    Play("4eB5E4B5C4B5EsEFqE4eB5E4B5C4B5EsEF");
    Play("5qE4eA5D4ABA5DsDCqD4eB5E4B5C4B");
    Play("5EsEDqE");
  }
}

U0 AnimateTask(I64)
{
  while (TRUE) {
    if (flap_down) {
      flap_down=FALSE;
      delta_phase=-0.005*Min(1.0,(tS-flap_time)/FLAP_TIME);
      flap_time=tS;
    } else if (flap_up) {
      flap_up=FALSE;
      delta_phase= 0.005;
    }
    if (delta_phase<0) {
      bat_y+=75*delta_phase;
      delta_phase+=0.000015;
    } else
      bat_y+=0.15;
    bat_y=Clamp(bat_y,BORDER,Fs->parent_task->pix_height-BORDER);
    flap_phase=Clamp(flap_phase+delta_phase,0.0,1.0);
    CheckBugs(Fs->parent_task);
    Sleep(1);
  }
}

U0 CleanUp()
{
  DCDel(limit_flood_fill_dc);
}

U0 FlapBat()
{
  Bool rst_space=TRUE;
  I64 arg1,arg2;

  MenuPush(
        "File {"
        "  Abort(,CH_SHIFT_ESC);"
        "  Exit(,CH_ESC);"
        "}"
        "Play {"
        "  Restart(,'\n');"
        "  Flap(,CH_SPACE);"
        "}"
        );

  PopUpOk("Use $GREEN$<SPACE>$FG$ to flap.\nHold down to glide.");

  SettingsPush; //See SettingsPush
  Fs->text_attr=LTGRAY<<4+WHITE;
  WinBorder(ON);
  WinHorz(1,TEXT_COLS/2);
  WinVert(2,TEXT_ROWS/2);
  DocCursor;
  DocClear;
  Fs->song_task=Spawn(&SongTask,NULL,"Song",,Fs);
  Init;
  Fs->animate_task=Spawn(&AnimateTask,NULL,"Animate",,Fs);
  Fs->draw_it=&DrawIt;
  try {
    while (TRUE) {
      switch (GetMsg(&arg1,&arg2,1<<MSG_KEY_DOWN+1<<MSG_KEY_UP)) {
        case MSG_KEY_DOWN:
          switch (arg1) {
            case CH_SPACE:
              if (rst_space) {
                flap_down=TRUE;
                rst_space=FALSE;
              }
              break;
            case '\n':
              CleanUp;
              Init;
              break;
            case CH_SHIFT_ESC:
            case CH_ESC:
              goto bl_done;
          }
          break;
        case MSG_KEY_UP:
          switch (arg1) {
            case CH_SPACE:
              flap_up=TRUE;
              rst_space=TRUE;
              break;
          }
          break;
      }
    }
bl_done:
    GetMsg(,,1<<MSG_KEY_UP);
  } catch
    PutExcept;
  SettingsPop;
  CleanUp;
  MenuPop;
  RegWrite("TempleOS/FlapBat","F64 best_score=%5.4f;\n",best_score);
}

FlapBat;