{$mode objfpc}programnatal2015;usescrt,sysutils,strutils,math,graph;constMAXSNOWCOUNT=100;MAXSNOWRADIUS=5;DEFAULTHEADING=-30;FLOORINCREMENT=0.05;DEFAULTJUMP=4.0;PROMPT='Feliz Natal e Prospero Ano Novo!';typeTPointReal=recordx,y:Real;end;TPointApparent=// EInvalidOp was being generated - this solved it, but I don't understand why... o.Orecordx,y:word;end;TSnowFlake=recordposition:recordprevious,current:TPointReal;prev,cur:TPointApparent;end;radius:word;wall,floor:boolean;end;TSnowHeaven=array[1..MAXSNOWCOUNT]ofTSnowFlake;constNILSNOWFLAKE:TSnowFlake=(position:(previous:(x:0.0;y:0.0);current:(x:0.0;y:0.0);prev:(x:0;y:0);cur:(x:0;y:0));radius:0;wall:false;floor:false);varTOP:word;// used as a constantfunctionGenerateSnowFlake(constINIT:boolean;constsh:TSnowFlake):TSnowFlake;beginwithGenerateSnowFlakedobeginwithpositiondobegincurrent.x:=Random(graph.GetMaxX)+1;ifINITthencurrent.y:=RandomRange(TOP+1,graph.GetMaxY)elsecurrent.y:=TOP+1;cur.x:=Trunc(current.x);cur.y:=Trunc(current.y);ifINITthenbeginprevious.x:=current.x;previous.y:=current.y;prev.x:=cur.x;prev.y:=cur.y;endelsebeginprevious.x:=sh.position.current.x;previous.y:=sh.position.current.y;prev.x:=sh.position.cur.x;prev.y:=sh.position.cur.y;end;end;ifINITthenbeginradius:=Random(MAXSNOWRADIUS)+1;wall:=false;floor:=false;endelsebeginradius:=sh.radius;wall:=sh.wall;floor:=sh.floor;end;end;end;procedureInitSnowHeaven(outsh:TSnowHeaven);vari:word;beginRandomize;fori:=Low(sh)toHigh(sh)dosh[i]:=GenerateSnowFlake(true,NILSNOWFLAKE);end;procedurePutSnowHeaven(varsh:TSnowHeaven);vari:word;beginfori:=Low(sh)toHigh(sh)dobeginwithsh[i].positiondobeginif(notsh[i].floor)orsh[i].wallthenbeginSetFillStyle(SolidFill,Black);SetColor(Black);FillEllipse(prev.x,prev.y,sh[i].radius,sh[i].radius);end;SetFillStyle(SolidFill,White);SetColor(White);FillEllipse(cur.x,cur.y,sh[i].radius,sh[i].radius);// After drawing, sets "previous" positionprevious:=current;prev.x:=Trunc(previous.x);prev.y:=Trunc(previous.y);end;end;end;procedurePutFloor(f:Real);beginSetColor(White);SetFillStyle(SolidFill,White);Bar(0,graph.GetMaxY-Trunc(f),graph.GetMaxX,graph.GetMaxY);end;procedureMoveSnowHeaven(varsh:TSnowHeaven;varf:Real;heading:word=DEFAULTHEADING);vardiff:TPointReal;i:word;beginwithdiffdobeginy:=DEFAULTJUMP;x:=sin(DegToRad(heading))*y;end;fori:=Low(sh)toHigh(sh)dobeginwithsh[i].positiondobeginif(previous.x+diff.x<sh[i].radius)or(previous.x+diff.x>graph.GetMaxX-sh[i].radius)or(previous.y+diff.y>graph.GetMaxY-sh[i].radius-f)thenbeginsh[i]:=GenerateSnowFlake(false,sh[i]);if(previous.y+diff.y>graph.GetMaxY-sh[i].radius-f)thenbeginf:=f+FLOORINCREMENT;sh[i].wall:=false;sh[i].floor:=true;endelsebeginsh[i].wall:=true;sh[i].floor:=false;end;endelsebegincurrent.x:=previous.x+diff.x;current.y:=previous.y+diff.y;cur.x:=Trunc(current.x);cur.y:=Trunc(current.y);sh[i].wall:=false;sh[i].floor:=false;end;end;end;end;functionGetBestFit(constTXT:string):Word;vari:word=0;beginwhileTextWidth(TXT)<GetMaxXdobeginInc(i);SetTextStyle(SansSerifFont,HorizDir,i);end;GetBestFit:=i;end;vardriver,modus:SmallInt;snow:TSnowHeaven;floor:Real=0.0;f:Text;begintryDetectGraph(driver,modus);InitGraph(driver,modus,'');trySetTextStyle(SansSerifFont,HorizDir,GetBestFit(PROMPT));TOP:=TextHeight(PROMPT)+10;SetColor(Green);OutTextXY(5,5,PROMPT);InitSnowHeaven(snow);PutSnowHeaven(snow);repeatwhilenotKeyPresseddobeginSleep(1);MoveSnowHeaven(snow,floor);PutSnowHeaven(snow);PutFloor(floor);end;untilReadKey=#13;exceptonex:ExceptiondobeginAssign(f,'error.txt');ReWrite(f);writeln(f,'ERROR ',ex.classname,', ',ex.message);Close(f);end;end;finallyCloseGraph;end;end.