//Uses fixed-point.

class MyMass:CMass
{
  Bool collision;
};

#define MAP_WIDTH       2048
#define MAP_HEIGHT      (GR_HEIGHT-3*FONT_HEIGHT)

I64 gun_x,gun_y,active_map=0,gun_recoil;
F64 gun_theta;
CDC *map_dcs[2]={NULL,NULL};
I16 elevs[MAP_WIDTH];

F64 wind_x;
#define DUST_NUM        512
I64 dust_x[DUST_NUM],dust_y[DUST_NUM];

CMathODE *ode=NULL;

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


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

U0 DrawIt(CTask *task,CDC *dc)
{
  CDC *map=map_dcs[active_map&1];
  MyMass *tmpm;
  F64 theta=gun_theta;
  I64 i,x,y,w,
        h=-task->horz_scroll.pos,
        v=-task->vert_scroll.pos;
  task->horz_scroll.min=0;
  task->horz_scroll.max=MAP_WIDTH-task->pix_width;
  task->vert_scroll.min=0;
  task->vert_scroll.max=MAP_HEIGHT-task->pix_height;
  map->flags|=DCF_NO_TRANSPARENTS;
  GrBlot(dc,h,v,map);

  Sprite3(dc,gun_x+h,gun_y+v,0,<2>);

  if (theta<-pi/2) {
    dc->flags|=DCF_SYMMETRY|DCF_JUST_MIRROR;
    DCSymmetrySet(dc,gun_x+h,0,gun_x+h,1);
    theta=-pi-theta;
  }
  Sprite3ZB(dc,
        gun_x+h-gun_recoil*Cos(theta),
        gun_y+v-gun_recoil*Sin(theta)-10,0,<1>,theta);
  dc->flags&=~(DCF_SYMMETRY|DCF_JUST_MIRROR);

  tmpm=ode->next_mass;
  dc->color=BLACK;
  map->color=ROP_COLLISION;
  map->bkcolor=LTCYAN;
  while (tmpm!=&ode->next_mass) {
    map->collision_cnt=0;
    GrCircle(map,tmpm->x,tmpm->y,2);
    if (map->collision_cnt)
      tmpm->collision=TRUE;

    GrCircle(dc,tmpm->x+h,tmpm->y+v,2);

    tmpm=tmpm->next;
  }

  dc->color=LTGRAY;
  w=tS*wind_x;
  for (i=0;i<DUST_NUM;i++) {
    x=(dust_x[i]+w)%MAP_WIDTH;
    y=dust_y[i];
    if (y<elevs[x])
      GrPlot(dc,x+h,y+v);
  }
}

U0 MyDerivative(CMathODE *ode,F64,COrder2D3 *,COrder2D3 *)
{
  MyMass *tmpm=ode->next_mass;
  while (tmpm!=&ode->next_mass) {
    tmpm->DstateDt->DyDt+=1000.0*tmpm->mass;
    tmpm->DstateDt->DxDt+=25.0*wind_x;
    tmpm=tmpm->next;
  }
}

U0 DrawMap()
{
  CDC *map=map_dcs[(active_map+1)&1];
  I64 x;

  map->color=LTCYAN;
  GrRect(map,0,0,MAP_WIDTH,MAP_HEIGHT);

  map->color=BLACK;
  for (x=1;x<MAP_WIDTH;x++)
    GrLine(map,x-1,elevs[x-1],x,elevs[x]);

  map->color=BROWN;
  GrFloodFill(map,0,MAP_HEIGHT-1,FALSE);

  active_map++;
}

U0 FireTask(I64)
{
  MyMass *tmpm;
  I64 i;
  if (gun_recoil) return;

  tmpm=CAlloc(sizeof(MyMass),Fs->parent_task);
  tmpm->mass=10.0;
  tmpm->drag_profile_factor=0.1;
  tmpm->x=gun_x+27*Cos(gun_theta);
  tmpm->y=gun_y-15+27*Sin(gun_theta);
  tmpm->DxDt=600.0*Cos(gun_theta);
  tmpm->DyDt=600.0*Sin(gun_theta);
  tmpm->collision=FALSE;
  while (sys_task_being_scrn_updated==Fs->parent_task)
    Yield;
  QueIns(tmpm,ode->last_mass);

  Fs->task_end_cb=&SndTaskEndCB;
  for (i=0;i<60;i++) {
    Snd(50*Rand+10);
    Sleep(2);
    gun_recoil=i/12;
  }
  for (i=0;i<=60;i++) {
    Sleep(1);
    gun_recoil=5-i/12;
  }
}

U0 ManageShots()
{
  I64 i;
  MyMass *tmpm,*tmpm1;
  Bool chged=FALSE;
  tmpm=ode->next_mass;
  while (tmpm!=&ode->next_mass) {
    tmpm1=tmpm->next;
    if (!(0<=tmpm->x<MAP_WIDTH) ||
          tmpm->collision) {
      QueRem(tmpm);
      for (i=tmpm->x-4;i<=tmpm->x+4;i++)
        if (0<=i<MAP_WIDTH)
          elevs[i]=ClampI64(elevs[i]+10-2*AbsI64(i-tmpm->x),0,MAP_HEIGHT-2);
      Free(tmpm);
      chged=TRUE;
    }
    tmpm=tmpm1;
  }
  if (chged)
    DrawMap;
}

U0 MoveTask(I64)
{
  static F64 quit_time=0;
  if (quit_time)
    quit_time=tS+0.1;
  else {
    Snd(34);
    Fs->task_end_cb=&SndTaskEndCB;
    quit_time=tS+0.1;
    while (quit_time>tS)
      Yield;
    quit_time=0;
  }
}

U0 Init()
{
  CDC *map;
  I64 i,x,y,dy;
  if (!map_dcs[0])
    map_dcs[0]=DCNew(MAP_WIDTH,MAP_HEIGHT);
  if (!map_dcs[1])
    map_dcs[1]=DCNew(MAP_WIDTH,MAP_HEIGHT);
  map=map_dcs[active_map&1];
  Fs->horz_scroll.pos=0;
  Fs->vert_scroll.pos=0;

  y=ToI64(0.7*MAP_HEIGHT)<<32;
  dy=0;
  for (x=0;x<MAP_WIDTH;x++) {
    dy=ClampI64(SignI64(RandI16)<<30+dy,-3<<32,3<<32);
    y=ClampI64(y+dy,ToI64(0.3*MAP_HEIGHT)<<32,(MAP_HEIGHT-2)<<32);
    elevs[x]=y.i32[1];
  }
  gun_x=RandU32%(MAP_WIDTH-100)+50;
  gun_y=elevs[gun_x];
  gun_theta=0;
  gun_recoil=0;
  for (x=gun_x-20;x<=gun_x+20;x++)
    elevs[x]=gun_y;

  wind_x=RandI16/250.0;
  for (i=0;i<DUST_NUM;i++) {
    dust_x[i]=RandU16%MAP_WIDTH;
    dust_y[i]=RandU16%MAP_HEIGHT;
  }

  ode=ODENew(0,1e-4,ODEF_HAS_MASSES);
  ode->derive=&MyDerivative;
  ode->drag_v2=0.002;
  ode->drag_v3=0.0001;
  ode->acceleration_limit=5e5;
  QueIns(ode,Fs->last_ode);
  Fs->horz_scroll.min=0;
  Fs->horz_scroll.max=MAP_WIDTH-Fs->pix_width;
  Fs->horz_scroll.pos=gun_x-Fs->pix_width/2;
  Fs->vert_scroll.min=0;
  Fs->vert_scroll.max=MAP_HEIGHT-Fs->pix_height;
  Fs->vert_scroll.pos=0;
  TaskDerivedValsUpdate;

  DrawMap;
}

U0 CleanUp(CMathODE *ode)
{
  if (ode) {
    QueRem(ode);
    QueDel(&ode->next_mass,TRUE);
    ODEDel(ode);
  }
}

U0 BigGuns()
{
  I64 ch,sc;

  PopUpOk(
        "I refuse to rip-off the original\n"
        "so this is intentionally crappy\n"
        "and included for demonstration\n"
        "purposes.\n\n"
        "Write games, don't play them.\n");

  PopUpOk("The map scrolls.\n");

  SettingsPush; //See SettingsPush

  MenuPush(
        "File {"
        "  Abort(,CH_SHIFT_ESC);"
        "  Exit(,CH_ESC);"
        "}"
        "Play {"
        "  Restart(,'\n');"
        "  Fire(,CH_SPACE);"
        "  Left(,,SC_CURSOR_LEFT);"
        "  Right(,,SC_CURSOR_RIGHT);"
        "}"
        );

  AutoComplete;
  WinBorder(ON);
  WinMax;
  DocCursor;
  DocClear;
  DocScroll;
  Init;
  Fs->draw_it=&DrawIt;
  try {
    while (TRUE) {
      while (ScanKey(&ch,&sc))
        switch (ch) {
          case 0:
            switch (sc.u8[0]) {
              case SC_CURSOR_RIGHT:
                gun_theta+=2.0*pi/180;
                if (gun_theta>0)
                  gun_theta=0;
                else
                  Spawn(&MoveTask,NULL,"Move",,Fs);
                break;
              case SC_CURSOR_LEFT:
                gun_theta-=2.0*pi/180;
                if (gun_theta<-pi)
                  gun_theta=-pi;
                else
                  Spawn(&MoveTask,NULL,"Move",,Fs);
                break;
            }
            break;
          case '\n':
            CleanUp(ode);
            Init;
            break;
          case CH_SPACE:
            Spawn(&FireTask,NULL,"Fire",,Fs);
            break;
          case CH_SHIFT_ESC:
          case CH_ESC:
            goto bg_done;
        }
      ManageShots;
      Refresh;
    }
bg_done:
  } catch
    PutExcept;
  SettingsPop;
  DCDel(map_dcs[0]); map_dcs[0]=NULL;
  DCDel(map_dcs[1]); map_dcs[1]=NULL;
  CleanUp(ode);
  MenuPop;
}

BigGuns;