#define MT_HOSE         1
#define MT_DROPLET      2
class MyMass:CMass
{
  I64 type;
  F64 radius;
};

#define ST_HOSE         1
class MySpring:CSpring
{
  I64 type;
};



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

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

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















/* Graphics Not Rendered in HTML */

#define HOSE_RADIUS     3
#define LINK_SIZE       6
#define NOZZLE_START_Y  (GR_HEIGHT-15*FONT_HEIGHT)
#define NOZZLE_LEN      18
#define FAUCET_X        (5*HOSE_RADIUS)
#define FAUCET_Y        (GR_HEIGHT-12*FONT_HEIGHT)
#define GROUND_Y        (GR_HEIGHT-3*FONT_HEIGHT)
MyMass *faucet,*nozzle;
F64 nozzle_theta;

CMathODE *ode=NULL;
F64 start_up_timeout;

U0 DrawIt(CTask *,CDC *dc)
{
  Bool first;
  F64 dx,dy,d;
  I64  x1b,y1b,x2b,y2b,
        x1a,y1a,x2a,y2a;
  MyMass   *tmpm,*tmpm1;
  MySpring *tmps;
  CD3I32 poly[4];

  Sprite3(dc,0,GROUND_Y,0,<4>);
  if (start_up_timeout>tS) {
    ode->drag_v2=0.01; //Let hose settle during start-up
    ode->drag_v3=0.0001;
    dc->color=RED;
    GrPrint(dc,(GR_WIDTH-FONT_WIDTH*6)>>1,GR_HEIGHT>>1,"Squirt");
    return;
  } else {
    ode->drag_v2=0.0005;
    ode->drag_v3=0.0000025;
  }

  tmpm=faucet;
  dc->color=BLACK;
  GrRect(dc,tmpm->x+8,tmpm->y,8,GROUND_Y-FAUCET_Y);
  Sprite3(dc,tmpm->x,tmpm->y,0,<1>);
  dc->color=BLACK;
  GrCircle(dc,tmpm->x,tmpm->y,tmpm->radius);
  dc->color=GREEN;
  GrFloodFill(dc,tmpm->x,tmpm->y);

  tmpm=nozzle;
  tmpm1=nozzle->last;
  dx=tmpm->x-tmpm1->x;
  dy=tmpm->y-tmpm1->y;
  nozzle_theta=Wrap(Arg(dx,dy));
  Sprite3ZB(dc,tmpm->x,tmpm->y,0,<2>,nozzle_theta);
  dc->color=BLACK;
  GrCircle(dc,tmpm->x,tmpm->y,tmpm->radius);
  dc->color=GREEN;
  GrFloodFill(dc,tmpm->x,tmpm->y);

  first=TRUE;
  tmpm=ode->next_mass;
  while (tmpm!=&ode->next_mass) {
    if (tmpm->type==MT_HOSE) {
      tmpm1=tmpm->last;
      dx=tmpm->x-tmpm1->x;
      dy=tmpm->y-tmpm1->y;
      d=HOSE_RADIUS/Max(Sqrt(dx*dx+dy*dy),0.001);
      dx*=d;
      dy*=d;
      x2a=tmpm->x-dy;
      y2a=tmpm->y+dx;
      x2b=tmpm->x+dy;
      y2b=tmpm->y-dx;

      if (first)
        first=FALSE;
      else {
        dc->color=GREEN;
        poly[0].x=x1a;
        poly[0].y=y1a;
        poly[0].z=0;
        poly[1].x=x2a;
        poly[1].y=y2a;
        poly[1].z=0;
        poly[2].x=x2b;
        poly[2].y=y2b;
        poly[2].z=0;
        poly[3].x=x1b;
        poly[3].y=y1b;
        poly[3].z=0;
        GrFillPoly3(dc,4,poly);
      }

      //Fill gaps
      GrLine(dc,x2a,y2a,x2b,y2b);

      x1a=x2a;
      y1a=y2a;
      x1b=x2b;
      y1b=y2b;
    } else if (tmpm->type==MT_DROPLET)
      Sprite3(dc,tmpm->x,tmpm->y,0,<3>);
    tmpm=tmpm->next;
  }

  tmps=ode->next_spring;
  while (tmps!=&ode->next_spring) {
    if (tmps->type==ST_HOSE) {
      dx=tmps->end1->x-tmps->end2->x;
      dy=tmps->end1->y-tmps->end2->y;
      d=HOSE_RADIUS/Max(Sqrt(dx*dx+dy*dy),0.001);
      dx*=d;
      dy*=d;
      dc->color=BLACK;
      GrLine(dc,tmps->end1->x-dy,tmps->end1->y+dx,
            tmps->end2->x-dy,tmps->end2->y+dx);
      GrLine(dc,tmps->end1->x+dy,tmps->end1->y-dx,
            tmps->end2->x+dy,tmps->end2->y-dx);
    }
    tmps=tmps->next;
  }
}

U0 MyDerivative(CMathODE *ode,F64 t,COrder2D3 *state,COrder2D3 *DstateDt)
{//The forces due to springs and drag are
//automatically handled by the
  //ode code.  We can add new forces
  //here.
  no_warn t,state,DstateDt;
  MyMass *tmpm1=ode->next_mass;
  while (tmpm1!=&ode->next_mass) {
    if (tmpm1->type==MT_HOSE) {
      if (tmpm1->state->y+tmpm1->radius>GROUND_Y)
        tmpm1->DstateDt->DyDt-=Sqr(Sqr(tmpm1->state->y+
              tmpm1->radius-GROUND_Y))*tmpm1->mass;
      else
        tmpm1->DstateDt->DyDt+=500*tmpm1->mass;
      if (tmpm1==nozzle || tmpm1==faucet) {
        tmpm1->DstateDt->DxDt=0;
        tmpm1->DstateDt->DyDt=0;
      }
    } else if (tmpm1->type==MT_DROPLET)
      tmpm1->DstateDt->DyDt=500*tmpm1->mass;
    tmpm1=tmpm1->next;
  }
}

MyMass *PlaceMass(I64 type,I64 x, I64 y,F64 r,
        F64 dx,F64 dy,F64 mass,CTask *mem_task)
{
  MyMass *tmpm=CAlloc(sizeof(MyMass),mem_task);
  tmpm->type=type;
  tmpm->mass=mass;
  tmpm->drag_profile_factor=250.0;
  tmpm->x=x;
  tmpm->y=y;
  tmpm->DxDt=dx;
  tmpm->DyDt=dy;
  tmpm->radius=r;
  QueIns(tmpm,ode->last_mass);
  return tmpm;
}

MySpring PlaceSpring(MyMass *tmpm1,MyMass *tmpm2)
{
  MySpring *tmps=CAlloc(sizeof(MySpring));
  tmps->end1=tmpm1;
  tmps->end2=tmpm2;
  tmps->const=20000;
  QueIns(tmps,ode->last_spring);
  return tmps;
}

U0 HoseNew()
{
  I64 i;
  MyMass *tmpm1=NULL,*tmpm;
  MySpring *tmps;
  for (i=FAUCET_X;i<GR_WIDTH;i+=LINK_SIZE) {
    tmpm=PlaceMass(MT_HOSE,i/2,GROUND_Y-HOSE_RADIUS,HOSE_RADIUS,0,0,1.0,Fs);
    if (tmpm1) {
      tmps=PlaceSpring(tmpm,tmpm1);
      tmps->rest_len=LINK_SIZE;
      tmps->type=ST_HOSE;
      nozzle=tmpm;
    } else
      faucet=tmpm;
    tmpm1=tmpm;
  }
  faucet->y=FAUCET_Y;
  nozzle->y=NOZZLE_START_Y;
  nozzle_theta=0;
}

U0 AnimateTask(I64)
{
  MyMass   *tmpm,*tmpm1;
  F64 dx,dy;
  while (TRUE) {
    dx=Cos(nozzle_theta);
    dy=Sin(nozzle_theta);
    PlaceMass(MT_DROPLET,
          nozzle->x+NOZZLE_LEN*dx,nozzle->y+NOZZLE_LEN*dy,HOSE_RADIUS,
          500*dx,500*dy,100.0,Fs->parent_task);
    if (Rand<0.05) //faucet drip
      PlaceMass(MT_DROPLET,
            faucet->x,faucet->y,HOSE_RADIUS,
            0,0,100.0,Fs->parent_task);

    tmpm=ode->next_mass;
    while (tmpm!=&ode->next_mass) {
      tmpm1=tmpm->next;
      if (tmpm->type==MT_DROPLET && tmpm->y+tmpm->radius>GROUND_Y) {
        QueRem(tmpm);
        Free(tmpm);
      }
      tmpm=tmpm1;
    }
    Refresh;
  }
}

#define NOZZLE_MOVE_STEPS       5
#define NOZZLE_MOVE             15.0
U0 MoveNozzleTaskX(I64 sign)
{
  I64 i;
  for (i=0;i<NOZZLE_MOVE_STEPS;i++) {
    nozzle->x=Clamp(nozzle->x+sign*NOZZLE_MOVE/NOZZLE_MOVE_STEPS,
          HOSE_RADIUS*3,GR_WIDTH-HOSE_RADIUS*3);
    Refresh;
  }
}

U0 MoveNozzleTaskY(I64 sign)
{
  I64 i;
  for (i=0;i<NOZZLE_MOVE_STEPS;i++) {
    nozzle->y=Clamp(nozzle->y+sign*NOZZLE_MOVE/NOZZLE_MOVE_STEPS,
          HOSE_RADIUS*3,GROUND_Y);
    Refresh;
  }
}

U0 Init()
{
  DocClear;
  "$BG,LTCYAN$%h*c",ToI64(GROUND_Y/FONT_HEIGHT),'\n';

  //Allow hose to settle.
  start_up_timeout=tS+0.5;

  ode=ODENew(0,5e-2,ODEF_HAS_MASSES);
  ode->derive=&MyDerivative;
  ode->acceleration_limit=5e3;

  HoseNew;
  QueIns(ode,Fs->last_ode);
}

U0 CleanUp()
{
  Refresh(NOZZLE_MOVE_STEPS); //Let nozzle move tasks die
  QueRem(ode);
  QueDel(&ode->next_mass,TRUE);
  QueDel(&ode->next_spring,TRUE);
  ODEDel(ode);
  DocClear;
}

U0 SongTask(I64)
{
  Fs->task_end_cb=&SndTaskEndCB;
  MusicSettingsRst;
  while (TRUE) {
    Play("5sDCDC4qA5DetDFFeDG4etA5EF4qG5eFC");
    Play("5sDCDC4qA5DetDFFeDG4etA5EF4qG5eFC");
    Play("5DCsG4A5G4AqBeBA5qEE4B5eC4B");
    Play("5DCsG4A5G4AqBeBA5qEE4B5eC4B");
  }
}

U0 Squirt()
{
  I64 sc;
  SettingsPush; //See SettingsPush
  Fs->text_attr=YELLOW<<4+BLUE;
  Fs->song_task=Spawn(&SongTask,NULL,"Song",,Fs);
  AutoComplete;
  WinBorder;
  WinMax;
  DocCursor;

  MenuPush(
        "File {"
        "  Abort(,CH_SHIFT_ESC);"
        "  Exit(,CH_ESC);"
        "}"
        "Play {"
        "  Restart(,'\n');"
        "  Left(,,SC_CURSOR_LEFT);"
        "  Right(,,SC_CURSOR_RIGHT);"
        "  Up(,,SC_CURSOR_UP);"
        "  Down(,,SC_CURSOR_DOWN);"
        "}"
        );

  Init;
  Fs->animate_task=Spawn(&AnimateTask,NULL,"Animate",,Fs);
  Fs->draw_it=&DrawIt;

  try {
    while (TRUE) {
      switch (GetKey(&sc)) {
        case 0:
          switch (sc.u8[0]) {
            case SC_CURSOR_LEFT:
              Spawn(&MoveNozzleTaskX,-1,"Move Nozzle",,Fs);
              break;
            case SC_CURSOR_RIGHT:
              Spawn(&MoveNozzleTaskX,1,"Move Nozzle",,Fs);
              break;
            case SC_CURSOR_UP:
              Spawn(&MoveNozzleTaskY,-1,"Move Nozzle",,Fs);
              break;
            case SC_CURSOR_DOWN:
              Spawn(&MoveNozzleTaskY,1,"Move Nozzle",,Fs);
              break;
          }
          break;
        case '\n':
          CleanUp;
          Init;
          break;
        case CH_SHIFT_ESC:
        case CH_ESC:
          goto sq_done;
      }
    }
sq_done: //Don't goto out of try
  } catch
    PutExcept;
  SettingsPop;
  CleanUp;
  MenuPop;
}

Squirt;