123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607 |
- {
- $Id$
- This program is both available in XTDFPC as in the FPC demoes.
- Copyright (C) 1999 by Marco van de Voort
- SameGame is a standard game in GNOME and KDE. I liked it, and I
- automatically brainstormed how I would implement it.
- It turned out to be really easy, and is basically only 100 lines or so,
- the rest is scorekeeping, helptext, menu etc.
- The game demonstrates some features of the MSMOUSE unit, and some of
- the Crt and Graph units. (depending whether it is compiled with
- UseGraphics or not)
- See the file COPYING.FPC, included in this distribution,
- for details about the copyright.
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- **********************************************************************}
- PROGRAM SameGame;
- {$ifdef UseGraphics}
- {$ifdef Win32}
- {$define Win32Graph}
- {$apptype GUI}
- {$endif}
- {$endif}
- Uses
- {$ifdef Win32}
- Windows,
- {$endif}
- {$ifdef Win32Graph}
- WinCrt,
- {$else}
- Crt,
- {$endif}
- Dos,
- {$IFDEF UseGraphics}
- Graph,
- {$INFO GRAPH}
- {$ENDIF}
- GameUnit;
- CONST
- {$IFDEF UseGraphics}
- GrFieldX = 10; {X topleft of playfield}
- GrFieldY = 70; {Y topleft of playfield}
- ScalerX = 22; {ScalerX x Scaler y dots
- must be approx a square}
- ScalerY = 20;
- {$ENDIF}
- FieldX = 10; {Top left playfield
- coordinates in squares(textmode)}
- FieldY = 3; {Top left playfield coordinates}
- PlayFieldXDimension = 20; {Dimensions of playfield}
- PlayFieldYDimension = 15;
- {$IFDEF UseGraphics}
- RowDispl = 15;
- MenuX = 480;
- MenuY = 120;
- grNewGameLine = 'NEW GAME';
- grHelpLine = 'HELP';
- grEndGame = 'END GAME';
- {$ENDIF}
- {Used colors. Colors[0..2] are the colors used on the playfield, Colors[3]
- is the background and Colors[4] is the color used to mark the pieces}
- Colors : ARRAY [0..4] OF LONGINT = (White,Blue,Red,Black,LightMagenta);
- TYPE PlayFieldType=ARRAY[0..PlayFieldXDimension-1,0..PlayFieldYDimension-1] OF BYTE;
- {$IFDEF UseGraphics}
- PROCEDURE DisplayPlayField(CONST PlayField:PlayFieldType);
- {Screen routine, simply puts the array Playfield on screen.
- Both used for displaying the normal grid as the grid with a certain area marked}
- VAR X,Y : LONGINT;
- LastOne,
- NumbLast : LONGINT;
- BEGIN
- HideMouse;
- FOR Y:=0 TO PlayFieldYDimension-1 DO
- BEGIN
- X:=0;
- REPEAT
- LastOne:=PlayField[X,Y];
- NumbLast:=X;
- WHILE (PlayField[X,Y]=LastOne) AND (X<(PlayFieldXDimension-1))DO
- INC(X);
- SetFillStyle(SolidFill,Colors[LastOne]);
- Bar(GrFieldX+NumbLast*ScalerX,GrFieldY+Y*ScalerY,GrFieldX+X*ScalerX-1,GrFieldY+(Y+1)*ScalerY-1);
- UNTIL X>=(PlayFieldXDimension-1);
- END;
- ShowMouse;
- END;
- {$ELSE}
- PROCEDURE DisplayPlayField(CONST PlayField:PlayFieldType);
- {Screen routine, simply puts the array Playfield on screen.
- Both used for displaying the normal grid as the grid with a certain area marked}
- VAR X,Y : LONGINT;
- BEGIN
- FOR Y:=0 TO PlayFieldYDimension-1 DO
- BEGIN
- GotoXY(FieldX,Y+FieldY);
- FOR X:=0 TO PlayFieldXDimension-1 DO
- BEGIN
- TextColor(Colors[PlayField[X,Y]]);
- Write(#219#219);
- END;
- END;
- END;
- {$ENDIF}
- PROCEDURE ShowHelp;
- {Shows some explanation of the game and waits for a key}
- {$ifndef UseGraphics}
- VAR I : LONGINT;
- {$endif}
- BEGIN
- {$IFDEF UseGraphics}
- HideMouse;
- SetbkColor(black);
- SetViewPort(0,0,getmaxx,getmaxy,clipoff);
- ClearViewPort;
- SetTextStyle(0,Horizdir,2);
- OutTextXY(220,10,'SAMEGAME');
- SetTextStyle(0,Horizdir,1);
- OutTextXY(5,40+1*LineDistY,' is a small game, with a principle copied from some KDE game');
- OutTextXY(5,40+3*LineDistY,'I tried to implement it under Go32V2 to demonstrate the MsMouse unit');
- OutTextXY(5,40+4*LineDistY,'When it worked, I tried to get it running under Linux. I succeeded,');
- OutTextXY(5,40+5*LineDistY,'but the mouse unit of the API doesn'#39't work with GPM 1.17');
- OutTextXY(5,40+7*LineDistY,'If you move over the playfield, aggregates of one color will be marked');
- OutTextXY(5,40+8*LineDistY,'in purple. If you then press the left mouse button, that aggregate will');
- OutTextXY(5,40+9*LineDistY,'disappear, and the playfield will collapse to the bottom-left. Please');
- OutTextXY(5,40+10*LineDistY,'keep in mind that only an aggregate of two blocks or more will disappear.');
- OutTextXY(5,40+12*LineDistY,'For every aggregate you let disappear you get points, but the score is');
- OutTextXY(5,40+13*LineDistY,'quadratic proportional to the number of blocks killed. So 4 times killing');
- OutTextXY(5,40+14*LineDistY,' a 4 block aggregate scores 4 points, and one time 16 blocks will score 64');
- OutTextXY(5,40+15*LineDistY,'blocks. The purpose of the game is obtaining the highscore');
- OutTextXY(5,40+17*LineDistY,'If you manage to empty the entire playfield, you'#39'll get a bonus');
- OutTextXY(5,40+19*LineDistY,'Press any key to get back to the game');
- ShowMouse;
- {$ELSE}
- FOR I:=2 TO 24 DO
- BEGIN
- GotoXY(1,I);
- ClrEol;
- END;
- GotoXY(1,3); TextColor(White);
- Write('SAMEGAME');
- SetDefaultColor;
- WriteLn(' is a small game, with a principle copied from some KDE game');
- WriteLn;
- WriteLn('I tried to implement it under Go32V2 to demonstrate the MsMouse unit');
- Writeln('When it worked, I tried to get it running under Linux. I succeeded,');
- Writeln('but the mouse unit of the API doesn'#39't work with GPM 1.17');
- Writeln;
- WriteLn('If you move over the playfield, aggregates of one color will be marked');
- Writeln('in purple. If you then press the left mouse button, that aggregate will');
- Writeln('disappear, and the playfield will collapse to the bottom-left. Please');
- Writeln('keep in mind that only an aggregate of two blocks or more will disappear.');
- Writeln;
- Writeln('For every aggregate you let disappear you get points, but the score is');
- Writeln('quadratic proportional to the number of blocks killed. So 4 times killing');
- Writeln(' a 4 block aggregate scores 4 points, and one time 16 blocks will score 64');
- Writeln('blocks. The purpose of the game is obtaining the highscore');
- Writeln;
- Writeln('If you manage to empty the entire playfield, you'#39'll get a bonus');
- Writeln;
- WriteLn('Press any key to get back to the game');
- {$ENDIF}
- GetKey;
- END;
- VAR MarkField,PlayField : PlayFieldType; {The playfield, and the marked form}
- CubesMarked : LONGINT; {Cubes currently marked}
- Score : LONGINT; {The current score}
- LastScore : LONGINT;
- PROCEDURE ShowButtons;
- {Shows the clickable buttons}
- BEGIN
- {$IFNDEF UseGraphics}
- TextColor(Yellow); TextBackGround(Blue);
- GotoXY(60,5); Write('NEW game');
- GotoXY(60,6); Write('HELP');
- GotoXY(60,7); Write('END game');
- {$IFDEF Unix}
- GotoXY(60,8); Write('Force IBM charset');
- {$ENDIF}
- SetDefaultColor;
- {$ELSE}
- SetTextStyle(0,Horizdir,1);
- OutTextXY(MenuX,MenuY,grNewGameLine);
- OutTextXY(MenuX,MenuY+RowDispl,grHelpLine);
- OutTextXY(MenuX,MenuY+2*RowDispl,grEndGame);
- {$ENDIF}
- END;
- FUNCTION PlayFieldPiecesLeft:LONGINT;
- {Counts pieces/cubes/blocks left on the playfield}
- VAR I,J,K : LONGINT;
- BEGIN
- K:=0;
- FOR I:=0 TO PlayFieldXDimension-1 DO
- FOR J:=0 TO PlayFieldYDimension-1 DO
- IF PlayField[I,J]<>3 THEN
- INC(K);
- PlayFieldPiecesLeft:=K;
- END;
- PROCEDURE ShowScore;
- {Simply procedure to update the score}
- {$IFDEF UseGraphics}
- VAR S : String;
- {$ENDIF}
- BEGIN
- {$IFDEF UseGraphics}
- Str(Score:5,S);
- SetFillStyle(SolidFill,0);
- Bar(300,440,450,458);
- OutTextXY(300,440,'Score :'+S);
- {$ELSE}
- TextColor(White);
- GotoXY(20,23); Write(' ':20);
- GotoXY(20,23); Write('Score : ',Score);
- SetDefaultColor;
- {$ENDIF}
- END;
- FUNCTION CubesToScore : LONGINT;
- {Function to calculate score from the number of cubes. Should have a higher
- order than linear, or the purpose of the game disappears}
- BEGIN
- CubesToScore:=(CubesMarked*CubesMarked) DIV 4;
- END;
- PROCEDURE MarkAfield(X,Y:LONGINT);
- {Recursively marks the area adjacent to (X,Y);}
- VAR TargetColor : LONGINT;
- PROCEDURE MarkRecur(X1,Y1:LONGINT);
- {Marks X1,Y1, checks if neighbours (horizontally or vertically) are the
- same color}
- BEGIN
- IF (PlayField[X1,Y1]=TargetColor) AND (MarkField[X1,Y1]<>4) THEN
- BEGIN
- MarkField[X1,Y1]:=4;
- INC(CubesMarked);
- IF X1>0 THEN
- MarkRecur(X1-1,Y1);
- IF Y1>0 THEN
- MarkRecur(X1,Y1-1);
- IF X1<(PlayFieldXDimension-1) THEN
- MarkRecur(X1+1,Y1);
- IF Y1<(PlayFieldYDimension-1) THEN
- MarkRecur(X1,Y1+1);
- END;
- END;
- BEGIN
- CubesMarked:=0;
- TargetColor:=PlayField[X,Y];
- IF TargetColor<>3 THEN {Can't mark black space}
- MarkRecur(X,Y);
- END;
- PROCEDURE FillPlayfield;
- {Initial version, probably not nice to play with.
- Some Life'ish algoritm would be better I think. (so that more aggregates exist)}
- VAR X,Y,Last,Now : LONGINT;
- BEGIN
- Last:=0;
- FOR X:=0 TO PlayFieldXDimension-1 DO
- FOR Y:=0 TO PlayFieldYDimension-1 DO
- BEGIN
- Now:=RANDOM(4);
- IF Now=3 THEN
- Now:=Last;
- PlayField[X,Y]:=Now;
- Last:=Now;
- END;
- MarkField:=PlayField;
- END;
- PROCEDURE Colapse;
- {Processes the playfield if the mouse button is used.
- First the procedure deletes the marked area, and let gravity do its work
- Second the procedure uses as if some gravity existed on the left of the
- playfield }
- VAR X, Y,J :LONGINT;
- BEGIN
- {Vertical colapse: All marked pieces are deleted, and let gravity do it's work}
- IF CubesMarked>1 THEN
- BEGIN
- FOR X:=0 TO PlayFieldXDimension-1 DO
- BEGIN
- Y:=PlayFieldYDimension-1; J:=Y;
- REPEAT
- IF MarkField[X,Y]<>4 THEN
- BEGIN
- PlayField[X,J]:=PlayField[X,Y];
- DEC(J);
- END;
- DEC(Y);
- UNTIL Y<0;
- FOR Y:=0 TO J DO
- PlayField[X,Y]:=3;
- END;
- J:=0;
- FOR X:=PlayFieldXDimension-2 DOWNTO 0 DO
- BEGIN
- IF PlayfIeld[X,PlayFieldYDimension-1]=3 THEN
- BEGIN
- Move(PlayfIeld[X+1,0],PlayField[X,0],PlayFieldYDimension*(PlayFieldXDimension-X-1));
- INC(J);
- END;
- END;
- IF J<>0 THEN
- FillChar(PlayField[PlayFieldXDimension-J,0],J*PlayFieldYDimension,#3);
- INC(Score,CubesToScore);
- ShowScore;
- END;
- END;
- PROCEDURE BuildScreen;
- {Some procedures that build the screen}
- BEGIN
- {$IFDEF UseGraphics}
- setbkcolor(black);
- setviewport(0,0,getmaxx,getmaxy,clipoff);
- clearviewport;
- {$ELSE}
- ClrScr;
- {$ENDIF}
- Score:=0;
- ShowScore;
- ShowButtons;
- ShowHighScore;
- ShowMouse;
- {$IFDEF UseGraphics}
- SetTextStyle(0,Horizdir,2);
- OuttextXY(10,10,'SameGame v0.03, (C) by Marco v/d Voort. ');
- SetTextStyle(0,Horizdir,1);
- OuttextXY(50,40,'A demo for the FPC RTL and API units Crt,(MS)Mouse and Graph');
- {$ELSE}
- GotoXY(1,1);
- TextColor(Yellow);
- Write('SameGame v0.02');
- TextColor(White);
- Write(' A demo for the ');
- TextColor(Yellow); Write('FPC');
- TextColor(White); Write(' API or MsMouse unit. By Marco van de Voort');
- SetDefaultColor;
- {$ENDIF}
- IF LastScore<>0 THEN
- BEGIN
- {$Ifdef UseGraphics}
- SetTextStyle(0,Horizdir,1);
- Str(LastScore,S);
- OuttextXY(50,40,'The Score in the last game was :'+S);
- {$else}
- GotoXY(10,20);
- Write('The score in the last game was :',LastScore);
- {$endif}
- END;
- DisplayPlayField(PlayField);
- MarkField:=PlayField;
- END;
- PROCEDURE DoMainLoopMouse;
- {The main game loop. The entire game runs in this procedure, the rest is
- initialisation/finalisation (like loading and saving highscores etc etc)}
- VAR X,Y,
- MX,MY,MState,Dummy : LONGINT;
- EndOfGame : LONGINT;
- S : String;
- BEGIN
- RANDOMIZE;
- REPEAT
- FillPlayField;
- BuildScreen;
- EndOfGame:=0;
- REPEAT
- GetMouseState(MX,MY,MState);
- {$IFDEF UseGraphics}
- X:=2*((MX-GrFieldX) DIV ScalerX) +FieldX;
- Y:=((MY-GrFieldY) DIV ScalerY) +FieldY-1;
- {$ELSE}
- X:=MX SHR 3;
- Y:=MY SHR 3;
- {$ENDIF}
- IF PlayFieldPiecesLeft=0 THEN
- BEGIN
- INC(Score,1000);
- EndOfGame:=1;
- END
- ELSE
- BEGIN
- {$IFDEF UseGraphics}
- IF (MX>=MenuX) AND (MX<(MenuX+16*Length(GrNewGameLine))) THEN
- BEGIN {X in clickable area}
- IF (MY>=MenuY) AND (MY<(MenuY+RowDispl*3+2)) THEN
- BEGIN
- X:=65; {X doesn't matter as long as it is 60..69}
- Y:=((MY-MenuY) DIV RowDispl)+4;
- END;
- END;
- {$ENDIF}
- IF (X>=60) AND (X<=69) THEN
- BEGIN
- IF (MState AND LButton) <>0 THEN {If leftbutton pressed,}
- BEGIN
- IF Y=4 THEN
- EndOfGame:=1;
- IF Y=6 THEN
- EndOfGame:=2;
- IF (EndOfGame>0) AND (PlayFieldPiecesLeft=0) THEN
- INC(Score,1000);
- IF Y=5 THEN
- BEGIN
- ShowHelp;
- BuildScreen;
- END;
- {$IFDEF Unix}
- IF Y=7 THEN
- BEGIN
- write(#27+'(K');
- BuildScreen;
- END;
- {$ENDIF}
- END;
- END;
- IF (X>=(FieldX-2)) AND (Y>=(FieldY-2)) THEN
- BEGIN
- DEC(X,FieldX-1);
- DEC(Y,FieldY-1);
- X:=X SHR 1;
- IF (X<PlayFieldXDimension) AND (Y<PlayFieldYDimension) THEN
- BEGIN
- IF MarkField[X,Y]<>4 THEN
- BEGIN
- MarkField:=PlayField;
- MarkAfield(X,Y);
- DisplayPlayField(MarkField);
- {$ifdef UseGraphics}
- SetFillStyle(SolidFill,black);
- Bar(420,440,540,460);
- SetTextStyle(0,Horizdir,1);
- Str(CubesToScore,S);
- OuttextXY(420,440,'Marked : '+S);
- {$else}
- TextColor(White);
- GotoXY(20,22);
- Write(' ':20);
- GotoXY(20,22);
- Write('Marked :',CubesToScore);
- {$endif}
- END;
- IF (MarkField[X,Y]=4) AND ((MState AND LButton) <>0) THEN
- {If leftbutton pressed,}
- BEGIN
- REPEAT {wait untill it's released.
- The moment of pressing counts}
- GetMouseState(X,Y,Dummy);
- UNTIL (Dummy AND LButton)=0;
- Colapse;
- MarkField:=PlayField;
- DisplayPlayField(MarkField);
- END
- END
- END;
- IF KeyPressed THEN
- BEGIN
- X:=GetKey;
- IF (CHR(X) IN ['X','x','Q','q']) OR (X=27) THEN
- EndOfGame:=2;
- END;
- END;
- UNTIL EndOfGame>0;
- ShowScore;
- X:=SlipInScore(Score);
- IF X<>0 THEN
- BEGIN
- HideMouse;
- ShowHighScore;
- {$IFDEF UseGraphics}
- Str(Score:5,S);
- OutTextXY(HighX+150,HighY+LineDistY*(10-X),S);
- GrInputStr(S,HighX,HighY+LineDistY*(10-X),16,12,10,FALSE,AlfaBeta);
- {$ELSE}
- InputStr(S,HighX,HighY+12-X,10,FALSE,AlfaBeta);
- {$ENDIF}
- HighScore[X-1].Name:=S;
- ShowMouse;
- END;
- LastScore:=Score;
- UNTIL EndOFGame=2;
- END;
- CONST FileName='samegame.scr';
- VAR I : LONGINT;
- {$IFDEF UseGraphics}
- gd,gm : INTEGER;
- Pal : PaletteType;
- {$ENDIF}
- BEGIN
- {$IFDEF UseGraphics}
- {$ifdef Win32}
- ShowWindow(GetActiveWindow,0);
- {$endif}
- gm:=vgahi;
- gd:=vga;
- InitGraph(gd,gm,'');
- if GraphResult <> grOk then
- begin
- Writeln('Graph driver ',gd,' graph mode ',gm,' not supported');
- Halt(1);
- end;
- SetFillStyle(SolidFill,1);
- GetDefaultPalette(Pal);
- SetAllPalette(Pal);
- {$ENDIF}
- IF NOT MousePresent THEN
- BEGIN
- Writeln('No mouse found. A mouse is required!');
- HALT;
- END;
- FOR I:=0 TO 9 DO
- HighScore[I].Score:=I*1500;
- LoadHighScore(FileName);
- InitMouse;
- {$ifndef Win32Graph}
- CursorOff;
- {$endif}
- {$IFDEF UseGraphics}
- HighX:=450; HighY:=220; {the position of the highscore table}
- {$else}
- HighX:=52; HighY:=10; {the position of the highscore table}
- {$endif}
- DoMainLoopMouse;
- HideMouse;
- DoneMouse;
- {$ifndef Win32Graph}
- CursorOn;
- {$endif}
- SaveHighScore;
- {$IFDEF UseGraphics}
- CloseGraph;
- {$ENDIF}
- {$ifndef Win32Graph}
- ClrScr;
- Writeln;
- Writeln('Last games'#39' score was : ',Score);
- {$endif}
- END.
- {
- $Log$
- Revision 1.6 2003-09-06 14:14:12 marco
- * removed unused var reported in bug 2170
- Revision 1.5 2002/09/07 15:06:35 peter
- * old logs removed and tabs fixed
- Revision 1.4 2002/06/02 09:49:17 marco
- * Renamefest
- Revision 1.3 2002/02/22 21:41:22 carl
- * range check error fix
- }
|