program pacman;
{$R+}
{$I PACMAN0}
{$I PACMAN1}
{$I PACMAN2}

procedure activatepacman;
begin
   xkoordpacman := pacmanstartx;
   ykoordpacman := pacmanstarty;
   generatefigur (1, pacmanstartx, pacmanstarty);
   movepacman (0);
   pacmandir := 0;
end;

procedure generatedots;
begin
   dots[1] := $1800;
   dots[2] := $0003;
   dots[3] := $0060;
   dots[4] := $0C00;
   dots[5] := $8001;
   dots[6] := $0030;
   dots[7] := $0600;
   dots[8] := $C000;
   dots[9] := $0018;
   dots[10] := $0300;
   dots[11] := $6000;
   dots[12] := $000C;
   dots[13] := $0100;
   dots[14] := $3000;
   dots[15] := $0006;
   dots[16] := $00C0;
   dots[17] := $1800;
   dots[18] := $0003;
   dots[19] := $0060;
   dots[20] := $0C00;
   dots[21] := $8001;
   dots[22] := $0030;
   dots[23] := $0600;
   dots[24] := $C000;
   dots[25] := $0018;
   dots[26] := $0300;
   dots[27] := $6000;
   dots[28] := $000C;
   dots[29] := $0100;
end;

procedure insertPowerPill;
begin
   plane3[240*2+32] := $0060;
   plane3[240*2+33] := $00F0;
   plane3[240*1+34] := plane3[240*1+34] or $0100;
   plane3[240*2+34] := plane3[240*2+34] or $00F8;
   plane3[240*1+35] := plane3[240*1+35] or $0100;
   plane3[240*2+35] := plane3[240*2+35] or $00F8;
   plane3[240*2+36] := $00F0;
   plane3[240*2+37] := $0060;
   plane3[240*18+32] := $6000;
   plane3[240*18+33] := $F000;
   plane3[240*18+34] := plane3[240*18+34] or $F801;
   plane3[240*18+35] := plane3[240*18+35] or $F801;
   plane3[240*18+36] := $F000;
   plane3[240*18+37] := $6000;
   plane3[240*0+193] := $1800;
   plane3[240*0+194] := $3C00;
   plane3[240*0+195] := plane3[240*0+195] or $7E00;
   plane3[240*0+196] := plane3[240*0+196] or $7E00;
   plane3[240*0+197] := $3C00;
   plane3[240*0+198] := $1800;
   plane3[240*18+193] := $6000;
   plane3[240*18+194] := $F000;
   plane3[240*18+195] := plane3[240*18+195] or $F801;
   plane3[240*18+196] := plane3[240*18+196] or $F801;
   plane3[240*18+197] := $F000;
   plane3[240*18+198] := $6000;
end;

procedure generatebullets;
var
   i           : integer;
   j           : integer;
   z           : integer;
   distance    : integer;
   line        : integer;
   bullet240   : integer;
   bulletshift : integer;
   bulletarray : array [1..34] of integer;
const
   bulletsplace  : array [1..29] of integer = (
                   0,1,2,2,3,4,4,5,6,6,
                   7,8,8,9,10,11,11,12,13,13,
                   14,15,15,16,17,17,18,19,19);
begin
   bulletsmax := 0;
   for i := 1 to 34 do
   begin
       bulletarray[i] := bulletsplane[i];
       bulletshift := bulletarray[i];
       for j := 1 to 16 do
       begin
           if (bulletshift and $8000) <> 0
           then bulletsmax := bulletsmax+1;
           bulletshift := bulletshift shl 1;
       end;
   end;
   j := 1;
   z := 11;
   distance := 12;
   repeat
       for i := 1 to 29 do
       begin
           if (bulletarray[j] and $8000) <> 0
           then begin
               bullet240 := 240*bulletsplace[i]+z;
               plane3[bullet240] := plane3[bullet240] or dots[i];
               if (i = 13) or (i = 29)
               then begin
                   plane3[bullet240+240] := $0080;
                   plane3[bullet240+241] := $0080;
               end;
               plane3[bullet240+1] := plane3[bullet240];
           end;
           bulletarray[j] := (bulletarray[j] shl 1) or
                               (bulletarray[j+1] shr 15);
           bulletarray[j+1] := bulletarray[j+1] shl 1;
       end;
       j := j+2;
       z := z+distance;
       if distance = 12
       then distance := 11
       else distance := 12;
   until z > 200;
   insertPowerPill;
   for i := 47 to 57 do
       plane3[240*8+2*i] := $0000;  (* Enemy-Gatter *)
   ColorMap[8] := $00;
   ColorMap[24] := $00;
   LoadColorMap(ColorMap);
   Color (switch1);
   Operation (1,8);
   WriteRectangle(1,maxx-54,1,206,plane3);
   Operation (0,15);
   ColorMap[8] := $FF;
   ColorMap[24] := $FF;
   LoadColorMap(ColorMap);
   for i := 47 to 57 do
       plane3[240*8+2*i] := $0008;  (* Enemy-Gatter *)
   fruitstart := random(bulletsmax-60)+30;
   fruitlength := 700-random(400);
   fruitend := 0;
   fruitnr := fruitnr+1;
   initfruit;
end;

procedure generateenemy;
var
   i : integer;
   j : integer;
begin
   fillchar(enemy,720,0);
   enemy[0] := $38;
   enemy[1] := $7C;
   enemy[2] := $FE;
   enemy[3] := $FE;
   enemy[4] := $D6;
   enemy[5] := $FE;
   enemy[6] := $FE;
   enemy[7] := $FE;
   enemy[8] := $FE;
   enemy[9] := $AA;
   for i := 10 to 19 do
       enemy[i] := $00;
   for i := 1 to 17 do
       for j := 0 to 9 do
       begin
           enemy[20*i+j+10] := (enemy[20*i+j-10] shr 1)
                                  or ((enemy[20*i+j-20] shr 1) and $80);
           enemy[20*i+j] := ((enemy[20*i+j-20] and $FEFF) shr 1)
                                  or (enemy[20*i+j-20] shl 15);
       end;
   enemy[360] := $00;
   enemy[361] := $00;
   enemy[362] := $00;
   enemy[363] := $6C;
   enemy[364] := $6C;
   enemy[365] := $6C;
   enemy[366] := $00;
   enemy[367] := $00;
   enemy[368] := $00;
   enemy[369] := $00;
   for i := 370 to 379 do
       enemy[i] := $00;
   for i := 19 to 35 do
       for j := 0 to 9 do
       begin
           enemy[20*i+j+10] := (enemy[20*i+j-10] shr 1)
                                  or ((enemy[20*i+j-20] shr 1) and $80);
           enemy[20*i+j] := ((enemy[20*i+j-20] and $FEFF) shr 1)
                                  or (enemy[20*i+j-20] shl 15);
       end;
end;

procedure generatepacman;
var
   i : integer;
   j : integer;
begin
   fillchar(pacman,640,0);
   pacman[0] := $FE;
   pacman[1] := $82;
   pacman[2] := $AA;
   pacman[3] := $82;
   pacman[4] := $92;
   pacman[5] := $92;
   pacman[6] := $82;
   pacman[7] := $BA;
   pacman[8] := $82;
   pacman[9] := $FE;
   for i := 10 to 19 do
       pacman[i] := $00;
   for i := 1 to 15 do
       for j := 0 to 9 do
       begin
           pacman[20*i+j+10] := (pacman[20*i+j-10] shr 1)
                                  or ((pacman[20*i+j-20] shr 1) and $80);
           pacman[20*i+j] := ((pacman[20*i+j-20] and $FEFF) shr 1)
                                  or (pacman[20*i+j-20] shl 15);
       end;
   pacman[320] := $38;
   pacman[321] := $38;
   pacman[322] := $92;
   pacman[323] := $54;
   pacman[324] := $38;
   pacman[325] := $38;
   pacman[326] := $38;
   pacman[327] := $28;
   pacman[328] := $44;
   pacman[329] := $44;
   for i := 330 to 339 do
       pacman[i] := $00;
   for i := 17 to 31 do
       for j := 0 to 9 do
       begin
           pacman[20*i+j+10] := (pacman[20*i+j-10] shr 1)
                                  or ((pacman[20*i+j-20] shr 1) and $80);
           pacman[20*i+j] := ((pacman[20*i+j-20] and $FEFF) shr 1)
                                  or (pacman[20*i+j-20] shl 15);
       end;
end;

procedure moveenemy (figurnr  : integer);
var
   olddir : integer;
   s1     : integer;
   i      : integer;
   s      : integer;
   p      : integer;
   x      : integer;
   y      : integer;
   zz     : integer;
   r240   : integer;
   r248   : integer;
   r241   : integer;
   r239   : integer;
   ghost  : integer;
   dirok  : boolean;
begin
   movecount := movecount+1;
   if movecount > 10000
   then movecount := 0;
   if (PowerPill > (kraftmax shr 2)) or
      ((PowerPill > 0) and ((movecount and $3F) < 32))
   then color (blue)
   else color (figurnr);
   if enemyeaten[figurnr]
   then ghost := 360
   else ghost := 0;
   case enemydir[figurnr] of
       0 : (* no movement *)
           begin
               x := xkoord[figurnr];
               y := ykoord[figurnr];
               r240 := ((x and $FFF0) shl 4)-(x and $FFF0);
               s := x and $F;
               if s > 8
               then
                   WriteRectangleTwoBytes (x,y,y+9,plane2,mask5[s],mask6[s],r240)
               else WriteRectangleOneByte (x,y,y+9,plane2,mask4[s],r240);
           end;
       1 : (* up *)
           begin
               x := xkoord[figurnr];
               y := ykoord[figurnr];
               r240 := ((x and $FFF0) shl 4)-(x and $FFF0);
               r241 := r240+240;
               s := x and $F;
               q := (s shl 4)+(s shl 2)+ghost;
               if y >= 1
               then begin
                   move(enemy[q],plane2[r240+y-1],20);
                   if s > 8
                   then begin
                       move(enemy[q+10],plane2[r241+y-1],20);
                       WriteRectangleTwoBytes (x,y-1,y+8,plane2,mask5[s],
                                                           mask6[s],r240);
                       Color (white);
                       WriteRectangleTwoBytes (x,y+9,y+9,plane3,mask5[s],
                                                       mask6[s],r240);
                   end
                   else begin
                       WriteRectangleOneByte (x,y-1,y+8,plane2,mask4[s],r240);
                       Color (white);
                       WriteRectangleOneByte (x,y+9,y+9,plane3,mask4[s],r240);
                   end;
                   ykoord[figurnr] := ykoord[figurnr]-1;
               end
               else begin
                   for p := y-1 to -1 do
                   begin
                       plane2[r240+p+208] := enemy[q];
                       q := q+1;
                   end;
                   WriteRectangle(x,x+6,y+207,207,plane2);
                   for p := 0 to y+8 do
                   begin
                       plane2[r240+p] := enemy[q];
                       q := q+1;
                   end;
                   WriteRectangle(x,x+6,0,y+8,plane2);
                   Color (white);
                   WriteRectangle(x,x+6,y+9,y+9,plane3);
                   ykoord[figurnr] := ykoord[figurnr]-1;
                   if ykoord[figurnr] = -10
                   then ykoord[figurnr] := 198;
               end;
               y := ykoord[figurnr];
           end;
      -1 : (* down *)
           begin
               x := xkoord[figurnr];
               y := ykoord[figurnr];
               r240 := ((x and $FFF0) shl 4)-(x and $FFF0);
               r241 := r240+240;
               s := x and $F;
               q := (s shl 4)+(s shl 2)+ghost;
               if y <= 197
               then begin
                   move(enemy[q],plane2[r240+y+1],20);
                   if s > 8
                   then begin
                       move(enemy[q+10],plane2[r241+y+1],20);
                       WriteRectangleTwoBytes (x,y+1,y+10,plane2,mask5[s],
                                                          mask6[s],r240);
                       Color (white);
                       WriteRectangleTwoBytes (x,y,y,plane3,mask5[s],
                                                          mask6[s],r240);
                   end
                   else begin
                       WriteRectangleOneByte (x,y+1,y+10,plane2,mask4[s],r240);
                       Color (white);
                       WriteRectangleOneByte (x,y,y,plane3,mask4[s],r240);
                   end;
                   ykoord[figurnr] := ykoord[figurnr]+1;
               end
               else begin
                   q := q+9;
                   for p := y+10 downto 208 do
                   begin
                       plane2[r240+p-208] := enemy[q];
                       q := q-1;
                   end;
                   WriteRectangle(x,x+6,0,y-198,plane2);
                   for p := 207 downto y+1 do
                   begin
                       plane2[r240+p] := enemy[q];
                       q := q-1;
                   end;
                   WriteRectangle(x,x+6,y+1,207,plane2);
                   Color (white);
                   WriteRectangle(x+1,x+6,y,y,plane3);
                   ykoord[figurnr] := ykoord[figurnr]+1;
                   if ykoord[figurnr] = 208
                   then ykoord[figurnr] := 0;
               end;
               y := ykoord[figurnr];
           end;
       2 : (* left *)
           begin
               xkoord[figurnr]:= xkoord[figurnr]-1;
               x := xkoord[figurnr];
               y := ykoord[figurnr];
               r240 := ((x and $FFF0) shl 4)-(x and $FFF0);
               r241 := r240+240;
               r248 := (((x+7) and $FFF0) shl 4)-((x+7) and $FFF0);
               s := x and $F;
               q := (s shl 4)+(s shl 2)+ghost;
               move(enemy[q],plane2[r240+y],20);
               s1 := (x+7) and 15;
               if s > 8
               then begin
                   move(enemy[q+10],plane2[r241+y],20);
                   WriteRectangleTwoBytes (x,y,y+9,plane2,mask5[s],
                                                             mask6[s],r240);
               end
               else WriteRectangleOneByte (x,y,y+9,plane2,mask4[s],r240);
               Color(switch3);
               WriteRectangleOneByte (x+7,y,y+9,plane3,maskleft[s1],r248);
           end;
      -2 : (* right *)
           begin
               xkoord[figurnr] := xkoord[figurnr]+1;
               x := xkoord[figurnr];
               y := ykoord[figurnr];
               r240 := ((x and $FFF0) shl 4)-(x and $FFF0);
               r241 := r240+240;
               r239 := r240-240;
               s := x and $F;
               q := (s shl 4)+(s shl 2)+ghost;
               if s = 0
               then begin
                   move(enemy[q],plane2[r240+y],20);
                   WriteRectangleOneByte (x,y,y+9,plane2,$01FF,r240);
                   Color(switch3);
                   WriteRectangleOneByte (x-1,y,y+9,plane3,maskright[15],r239);
               end
               else begin
                   move(enemy[q],plane2[r240+y],20);
                   if s > 8
                   then begin
                       move(enemy[q+10],plane2[r241+y],20);
                       WriteRectangleTwoBytes (x,y,y+9,plane2,mask5[s],
                                                              mask6[s],r240);
                   end
                   else WriteRectangleOneByte (x,y,y+9,plane2,mask4[s],r240);
                   s := (s-1) and $F;
                   Color(switch3);
                   WriteRectangleOneByte (x-1,y,y+9,plane3,maskright[s],r240);
               end;
           end;
   end;
   olddir := enemydir[figurnr];
   r := (x+14) mod 22;
   s := (y+16) mod 23;
   if (r = 0) and (s = 0)
   then begin
       r := ((x+14) div 22);
       s := ((y+16) div 23);
       if enemyeaten[figurnr]
       then begin
           if closed
           then enemydir[figurnr] := gethome[((s-1) shl 4)-(s-1)+r]
           else enemydir[figurnr] := gethomefast[((s-1) shl 4)-(s-1)+r];
           if enemydir[figurnr] = 0
           then begin
               enemyeaten[figurnr] := false;
               s := x and $F;
               q := (s shl 4)+(s shl 2);
               move(enemy[q],plane2[r240+y-1],20);
               move(enemy[q+10],plane2[r241+y-1],20);
               if (PowerPill > (kraftmax shr 2)) or
                  ((PowerPill > 0) and ((movecount and $3F) < 32))
               then color (blue)
               else color (figurnr);
               WriteRectangleTwoBytes (x,y,y+9,plane1,mask5[s],mask6[s],r240);
           end;
       end
       else begin
           zz := (s-1)*75+((r-1) shl 2)+r-1;
           p := random(20);
           if p < aggression
           then begin
               if xkoordpacman < x
               then enemydir[figurnr] := 2
               else enemydir[figurnr] := -2;
               dirok := false;
               for i := 2 to layoutenemy[zz+1]+1 do
               begin
                   if layoutenemy[zz+i] = enemydir[figurnr]
                   then dirok := true;
               end;
               if (enemydir[figurnr] = -olddir) or (not dirok)
               then begin
                   if ykoordpacman < y
                   then enemydir[figurnr] := 1
                   else enemydir[figurnr] := -1;
                   dirok := false;
                   for i := 2 to layoutenemy[zz+1]+1 do
                       if layoutenemy[zz+i] = enemydir[figurnr]
                       then dirok := true;
               end;
           end;
           if (not dirok) or
              (p >= aggression) or
              (enemydir[figurnr] = -olddir) or
              (PowerPill > 0)
           then repeat
               enemydir[figurnr] :=
                                  layoutenemy[zz+random(layoutenemy[zz+1])+2];
           until (enemydir[figurnr] <> -olddir) or (random(15) < 2);
       end;
   end;
end;

procedure activateenemies;
var
   i : integer;
begin
   for i := 2 to 5 do
   begin
       xkoord[i] := enemystartx;
       ykoord[i] := enemystarty;
       enemyeaten[i] := false;
       generatefigur (2, xkoord[i], ykoord[i]);
       enemydir[i] := 0;
       moveenemy (i);
   end;
end;

procedure checknewdir;
var
   olddir : integer;
   r      : integer;
   s      : integer;
   i      : integer;
begin
   olddir := pacmandir;
   r := (xkoordpacman+14) mod 22;
   s := (ykoordpacman+16) mod 23;
   if (r = 0) and (s = 0)
   then begin
       r := ((xkoordpacman+14) div 22);
       s := ((ykoordpacman+16) div 23);
       pacmandir := 0;
       for i := 2 to layoutpacman[(s-1)*75+((r-1) shl 2)+r]+1 do
       begin
           if olddir = layoutpacman[(s-1)*75+((r-1) shl 2)+r-1+i]
           then pacmandir := olddir;
       end;
       for i := 2 to layoutpacman[(s-1)*75+((r-1) shl 2)+r]+1 do
       begin
           if nextpacmandir = layoutpacman[(s-1)*75+((r-1) shl 2)+r-1+i]
           then pacmandir := nextpacmandir;
       end;
   end;
end;

procedure dispatchKeystroke;
begin
   case Keystroke.fun_Key of
       _UpArrow    : nextpacmandir := 1;
       _DownArrow  : nextpacmandir := -1;
       _LeftArrow  : nextpacmandir := 2;
       _RightArrow : nextpacmandir := -2;
       _F20        : begin
                         repeat
                             readkbd (Keystroke);
                         until (Keystroke.fun_key = _UpArrow) or
                               (Keystroke.fun_key = _DownArrow) or
                               (Keystroke.fun_key = _LeftArrow) or
                               (Keystroke.fun_key = _RightArrow) or
                               (Keystroke.fun_key = _Exit);
                         dispatchKeystroke;
                     end;
       _Exit       : begin
                         if nextpacmandir <> 0
                         then begin
                             GraphicsOff;
                             Halt;
                         end;
                     end;
   end;
end;

procedure getnextdir;
begin
   readkbd (Keystroke);
   dispatchKeystroke;
   if abs(pacmandir) = abs(nextpacmandir)
   then pacmandir := nextpacmandir;
end;

procedure definespeed;
begin
   case aggression and $3 of
       0 : begin
               speedcounter1 := 2;
               speedcounter2 := 0;
           end;
       1 : begin
               speedcounter1 := 2;
               speedcounter2 := 1;
           end;
       2 : begin
               speedcounter1 := 1;
               speedcounter2 := 1;
           end;
       3 : begin
               speedcounter1 := 1;
               speedcounter2 := 2;
           end;
   end;
   speedloop1 := 0;
   speedloop2 := speedcounter2+1;
end;

begin
   score := 0;
   scoreaddon := 0;
   scorecarry := 0;
   InitGraphics;
   nextpacmandir := 0;
   backgroundcolor (0);
   produceframe;
   initgamelayout;
   fruitnr := 0;
   generatedots;
   generatebullets;
   initializedigits;
   writescoredigit (0, 333, 1);
   writescoredigit (0, 341, 0);
   writescoredigit (0, 349, 1);
   writescoredigit (0, 357, 0);
   writescoredigit (0, 365, 1);
   writescoredigit (0, 373, 0);
   bullets := 0;
   nextpacman := 1;
   movecount := 0;
   generatepacman;
   generateenemy;
   plane2 := plane1;
   fruitdisplay := false;
   PowerPill := 0;
   Lives := 3;
   for i := 1 to Lives do
       putpacmeninplace (i);
   aggression := 1;
   definespeed;
   nextghost := 2;
   erasefigur (pacmenplacex, 208-18*Lives);
   activatepacman;
   activateenemies;
   while Lives > 0 do
   begin
       collision := false;
       for i := 2 to 5 do
           if (abs(xkoordpacman-xkoord[i]) < 5) and
              ((abs(ykoordpacman-ykoord[i]) mod 208) < 5) and
              (not enemyeaten[i])
           then begin
               collision := true;
               if (not enemyeaten[i]) and
                  (PowerPill > 0)
               then begin
                   if hits > 512
                   then begin
                       for j := 1 to hits div 512 do
                       begin
                           score := score+600;
                           scoreaddon := scoreaddon+25;
                       end;
                       addscore(0);
                   end
                   else addscore(50*hits);
                   if hits <= 8192
                   then hits := hits*2;
                   enemyeaten[i] := true;
               end;
           end;
       if collision and (PowerPill = 0)
       then begin
           write(chr(7),chr(7),chr(7),chr(7),chr(7));
           clearinputbuffer;
           Lives := Lives-1;
           for i := 2 to 5 do
           begin
               enemydir[i] := 0;
               moveenemy(i);
           end;
           delay (3000);
           if Lives > 0
           then begin
               erasefigur (xkoordpacman, ykoordpacman);
               for i := 2 to 5 do
                   erasefigur (xkoord[i], ykoord[i]);
               erasetunnel;
               erasefruit;
               fruitstart := random(bulletsmax-60)+30;
               fruitend := 0;
               erasefigur (pacmenplacex, 208-18*Lives);
               activatepacman;
               activateenemies;
           end;
       end
       else begin
           if PowerPill > 0
           then PowerPill := PowerPill-1
           else hits := 1;
           getnextdir;
           checknewdir;
           movepacman (pacmandir);
           if bullets = bulletsmax
           then begin
               bullets := 0;
               if aggression <= 19
               then begin
                   aggression := aggression+1;
                   definespeed;
               end;
               PowerPill := 0;
               displayfruit (1);
               erasefigur (xkoordpacman, ykoordpacman);
               for i := 2 to 5 do
                   erasefigur (xkoord[i], ykoord[i]);
               erasetunnel;
               initgamelayout;
               generatebullets;
               activatepacman;
               activateenemies;
           end
           else if PowerPill > 0
               then speed := (1+(aggression shr 2)) shr 1
               else speed := 1+(aggression shr 2);
           if speedloop2 >= speedcounter2
           then begin
               for i := 1 to speed do
               begin
                   moveenemy (nextghost);
                   nextghost := nextghost+1;
                   if nextghost = 6
                   then nextghost := 2;
               end;
               speedloop1 := speedloop1+1;
               if speedloop1 >= speedcounter1
               then speedloop2 := 0;
           end
           else begin
               for i := 1 to speed+1 do
               begin
                   moveenemy (nextghost);
                   nextghost := nextghost+1;
                   if nextghost = 6
                   then nextghost := 2;
               end;
               speedloop2 := speedloop2+1;
               if speedloop2 >= speedcounter2
               then speedloop1 := 0;
           end;
       end;
   end;

   delay(5000);
   TopTen;
end.