home *** CD-ROM | disk | FTP | other *** search
/ Ian & Stuart's Australian Mac 1993 September / September 93.iso / Archives / Games / Strategy / Puzzle / GameMaster / GM Dev Kit / Rulebook Sources / Chess / Chess.p next >
Encoding:
Text File  |  1991-12-05  |  22.1 KB  |  809 lines  |  [TEXT/PJMM]

  1. unit Chess;
  2.  
  3. { ©1991 [RHS] and Quinn "The Eskimo" }
  4.  
  5. interface
  6.  
  7.     uses
  8.         Sound, {}
  9.         QuickDrawRules, {}
  10.         GameTypes, {}
  11.         NumSubs, Debugs, Failure, DialogSubs, {}
  12.         ChessTypes, GraphSubs, ChessSubs, ChessMoves, ChessBoardSubs, ChessDrawing;
  13.  
  14.     procedure Main (var gameevent: gameEventRecord);
  15.  
  16. implementation
  17.  
  18.     procedure Main (var gameevent: gameEventRecord);
  19.  
  20.         procedure InitRuleBook;
  21.  
  22.             function GetPict (id: integer): handle;
  23.                 var
  24.                     tmp: handle;
  25.             begin
  26.                 tmp := GetResource('PICT', id);
  27.                 FailResError('PICT');
  28.                 FailNil(tmp, 'PICT nil');
  29.                 GetPict := tmp;
  30.             end; { GetPict }
  31.  
  32.             function GetSnd (name: str255): handle;
  33.                 var
  34.                     tmp: handle;
  35.             begin
  36.                 SetResLoad(false);
  37.                 tmp := GetNamedResource('snd ', name);
  38.                 SetResLoad(true);
  39.                 FailResError('snd');
  40.                 FailNil(tmp, 'snd nil');
  41.                 GetSnd := tmp;
  42.             end; { GetSnd }
  43.  
  44.             var
  45.                 col: cellColour;
  46.                 piece: pieceType;
  47.                 h: Handle;
  48.                 gp: globalsPeek;
  49.                 dlog: DialogTHndl;
  50.         begin
  51.             gameevent.globals := NewHandle(sizeof(globalsRecord));
  52.             HLock(gameevent.globals);
  53.             gp := globalsPeek(gameevent.globals);
  54.             for col := Cwhite to Cblack do begin
  55.                 for piece := Oempty to OqueenW do begin
  56.                     h := GetResource('ICN#', rIconBase + 100 * ord(col) + ord(piece));
  57.                     gp^^.icons[col, piece] := h;
  58.                     FailResError(concat('ICN# col=', DecStr(ord(col)), ' piece=', DecStr(ord(piece))));
  59.                     FailNil(h, 'ICN# is nil');
  60.                 end; { for }
  61.             end; { for }
  62.             gp^^.pawnpieces[kPawningX] := OqueenW;
  63.             gp^^.pawnpieces[kPawningX + 1] := ObishopW;
  64.             gp^^.pawnpieces[kPawningX + 2] := OknightW;
  65.             gp^^.pawnpieces[kPawningX + 3] := OrookW;
  66.             gp^^.waitpicts[Pwhite] := GetPict(rWaitForWhite);
  67.             gp^^.waitpicts[Pblack] := GetPict(rWaitForBlack);
  68.             gp^^.movepicts[Pwhite] := GetPict(rMoveWhite);
  69.             gp^^.movepicts[Pblack] := GetPict(rMoveBlack);
  70.             gp^^.resignpicts[Pwhite] := GetPict(rResignWhite);
  71.             gp^^.resignpicts[Pblack] := GetPict(rResignBlack);
  72.             gp^^.winpicts[Pwhite] := GetPict(rWinWhite);
  73.             gp^^.winpicts[Pblack] := GetPict(rWinBlack);
  74.             gp^^.pawnpicts[Pwhite] := GetPict(rPawnWhite);
  75.             gp^^.pawnpicts[Pblack] := GetPict(rPawnBlack);
  76.             gp^^.deadicons := GetResource('SICN', rDeadIcons);
  77.             gp^^.checksnd := GetSnd('Check');
  78.             gp^^.matesnd := GetSnd('Mate');
  79.             FailResError('SICN 1');
  80.             FailNil(gp^^.deadicons, 'SICN nil 1');
  81.             gp^^.chessmenu := GetMenu(rChessMenu);
  82.             FailNil(gp^^.chessmenu, 'Couldnt get menu');
  83.             dlog := DialogTHndl(GetResource('DLOG', rGameDialog));
  84.             FailResError('Game dialog');
  85.             FailNil(dlog, 'Game dialog handle');
  86.             gameevent.int1 := dlog^^.boundsRect.right - dlog^^.boundsRect.left;
  87.             gameevent.int2 := dlog^^.boundsRect.bottom - dlog^^.boundsRect.top;
  88.             ReleaseResource(Handle(dlog));
  89.             HUnlock(gameevent.globals);
  90.         end; { InitRuleBook }
  91.  
  92.         procedure FinishRuleBook;
  93.         begin
  94.             DisposeHandle(gameevent.globals);
  95.             gameevent.globals := nil;
  96. { Don't bother releasing resources cause GameMaster closes the game resource fork }
  97.         end; { FinishRuleBook }
  98.  
  99.         procedure PseudoMain (var game: gameRecord; var globals: globalsRecord);
  100.  
  101.             procedure Resign;
  102.             begin
  103.                 game.playstate := ps_GameOver;
  104.                 game.resigned := true;
  105.             end; { Resign }
  106.  
  107.             procedure MovePiece (from, toc: boardCoord);
  108.  
  109.                 procedure AddToMorgue (piece: pieceType);
  110.                     var
  111.                         p: playerType;
  112.                         r: Rect;
  113.                         itm: integer;
  114.                         body: morgueNdx;
  115.                 begin
  116.                     if piece <> Oempty then begin
  117.                         p := PieceToPlayer(piece);
  118.                         if p = Pwhite then begin
  119.                             itm := dit_wht_pieces
  120.                         end
  121.                         else begin
  122.                             itm := dit_blk_pieces;
  123.                         end; { if }
  124.                         GetDRect(game.dlg, itm, r);
  125.                         InvalRect(r);
  126.                         for body := 1 to kMorgueMax do begin
  127.                             if game.deaths[p][body] = Oempty then begin
  128.                                 game.deaths[p][body] := piece;
  129.                                 Exit(AddToMorgue);
  130.                             end; { if }
  131.                         end; { for }
  132.                     end; { if }
  133.                 end; { AddToMorgue }
  134.  
  135.                 procedure FlashAndUpdatePair (from, toc: boardCoord);
  136.                     var
  137.                         i: integer;
  138.                         junk: longint;
  139.                         fromr, tor: Rect;
  140.                 begin
  141.                     game.board[toc.x, toc.y].occupant := game.board[from.x, from.y].occupant;
  142.                     game.board[from.x, from.y].occupant := Oempty;
  143.                     GetRect(game, from, fromr);
  144.                     GetRect(game, toc, tor);
  145.                     for i := 1 to GetMenuFlash do begin
  146.                         InvertRect(fromr);
  147.                         InvertRect(tor);
  148.                         Delay(6, junk);
  149.                         InvertRect(fromr);
  150.                         InvertRect(tor);
  151.                         Delay(6, junk);
  152.                     end; { for }
  153.                     PlotIcon(tor, globals.icons[game.board[toc.x, toc.y].colour, game.board[toc.x, toc.y].occupant]);
  154.                     PlotIcon(fromr, globals.icons[game.board[from.x, from.y].colour, game.board[from.x, from.y].occupant]);
  155.                 end; { FlashPair }
  156.  
  157.                 procedure CompleteCastle;
  158.                     var
  159.                         rooksrc, rookdest: boardCoord;
  160.                 begin
  161.                     rooksrc.y := toc.y;
  162.                     rookdest.y := toc.y;
  163.                     case toc.x of
  164.                         2:  begin
  165.                             rooksrc.x := 0;
  166.                             rookdest.x := 3;
  167.                         end;
  168.                         6:  begin
  169.                             rooksrc.x := 7;
  170.                             rookdest.x := 5;
  171.                         end;
  172.                         otherwise
  173.                             Failure('king castled to illegal location');
  174.                     end; { case }
  175.                     FlashAndUpdatePair(rooksrc, rookdest);
  176.                 end; { CompleteCastle }
  177.  
  178.                 var
  179.                     tmpfrom: boardCoord;
  180.                     r: Rect;
  181.                     srcrect, dstrect: Rect;
  182.             begin
  183.                 UpdateState(game.specialstate, game.board, from, toc);
  184.                 if (game.board[from.x, from.y].occupant in [OpawnB, OpawnW]) and (abs(from.x - toc.x) = abs(from.y - toc.y)) and (game.board[toc.x, toc.y].occupant = Oempty) then begin
  185. { Move evil pawn back so it can be taken }
  186.                     tmpfrom.x := toc.x;
  187.                     tmpfrom.y := from.y;
  188.                     FlashAndUpdatePair(tmpfrom, toc);
  189.                 end; { if }
  190.                 AddToMorgue(game.board[toc.x, toc.y].occupant);
  191.                 FlashAndUpdatePair(from, toc);
  192.                 if (game.board[toc.x, toc.y].occupant in [OkingW, OkingB]) and (abs(from.x - toc.x) > 1) then begin
  193. { Move rook to complete castle }
  194.                     CompleteCastle;
  195.                 end; { if }
  196.                 if (game.board[toc.x, toc.y].occupant in [OpawnW, OpawnB]) and (toc.y = BaseRow(Opposite(game.playertomove))) then begin
  197. { Pawn at last line }
  198.                     game.playstate := ps_Pawning;
  199.                     game.pawnpos := toc;
  200.                     GetPawningRect(game, dstrect);
  201.                     GetRect(game, toc, srcrect);
  202.                     ZoomRect(srcrect, dstrect, QDGlobals^.grey);
  203.                     InvalRect(dstrect);
  204.                 end;{ if }
  205.                 gameevent.modified := true;
  206.             end; { MovePiece }
  207.  
  208.             procedure MoveComplete;
  209.  
  210.                 procedure PlaySnd (snd: Handle);
  211.                     var
  212.                         state: SignedByte;
  213.                         err: OSErr;
  214.                         volume: integer;
  215.                 begin
  216.                     if game.sounds then begin
  217.                         GetSoundVol(volume);
  218.                         if volume = 0 then begin
  219.                             SysBeep(10);    { Flashes the menu bar }
  220.                         end
  221.                         else begin
  222.                             LoadResource(snd);
  223.                             if ResError = noErr then begin
  224.                                 FailResError('Could not load sound');
  225.                                 state := HGetState(snd);
  226.                                 HLock(snd);
  227.                                 err := SndPlay(nil, snd, true);
  228.                                 FailOSError(err, 'snd play failed');
  229.                                 HSetState(snd, state);
  230.                             end
  231.                             else begin
  232.                                 SysBeep(10);
  233.                             end; { if }
  234.                         end; { if}
  235.                     end; { if }
  236.                 end; { PlaySnd }
  237.  
  238.                 var
  239.                     king: boardCoord;
  240.                     count: integer;
  241.                     valid: boardSet;
  242.             begin
  243.                 if game.playstate = ps_Playing then begin
  244. { Only do checking if were not still waiting for PawnChoice }
  245.                     game.playertomove := Opposite(game.playertomove);
  246.                     if not FindPiece(PlayerToKing(game.playertomove), game.board, king) then begin
  247.                         Failure('Cant find king');
  248.                     end; { if }
  249.                     if CheckCheck(game.board, king) then begin
  250.                         CalculateValidSet(game.board, game.specialstate, king, valid, count);
  251.                         if count = 0 then begin
  252.                             if NoValidMoves(game.playertomove, game.specialstate, game.board) then begin
  253.                                 game.playstate := ps_GameOver;
  254.                             end; { if }
  255.                         end; { if }
  256.                         if game.playstate = ps_GameOver then begin
  257.                             PlaySnd(globals.matesnd);
  258.                         end
  259.                         else begin
  260.                             PlaySnd(globals.checksnd);
  261.                         end; { if }
  262.                     end; { if }
  263.                 end; { if }
  264.             end; { MoveComplete }
  265.  
  266.             procedure PawnChoice (choice: pieceType; where: boardCoord);
  267.                 var
  268.                     srcrect, dstrect: Rect;
  269.                     junk: longint;
  270.                     i: integer;
  271.             begin
  272.                 game.board[where.x, where.y].occupant := choice;
  273.                 game.playstate := ps_Playing;
  274.                 GetPawningRect(game, srcrect);
  275.                 GetRect(game, where, dstrect);
  276.                 ZoomRect(srcrect, dstrect, QDGlobals^.grey);
  277.                 InvalRect(srcrect);
  278.                 for i := 1 to GetMenuFlash do begin
  279.                     InvertRect(dstrect);
  280.                     Delay(6, junk);
  281.                     InvertRect(dstrect);
  282.                     Delay(6, junk);
  283.                 end; { for }
  284.                 PlotIcon(dstrect, globals.icons[game.board[where.x, where.y].colour, game.board[where.x, where.y].occupant]);
  285.             end; { PawnChoice }
  286.  
  287.             procedure UpdateMyTurn;
  288.  
  289.                 procedure SetStatus;
  290.                     var
  291.                         oldpic, pic: Handle;
  292.                         k: integer;
  293.                         r: Rect;
  294.                 begin
  295.                     GetDKind(game.dlg, dit_status, k);
  296.                     GetDHandle(game.dlg, dit_status, oldpic);
  297.                     case game.playState of
  298.                         ps_Pawning: 
  299.                             case game.connectionstate of
  300.                                 cs_Local: 
  301.                                     pic := globals.pawnpicts[game.playertomove];
  302.                                 cs_Remote: 
  303.                                     if gameevent.myturn then begin
  304.                                         pic := globals.pawnpicts[game.playertomove];
  305.                                     end
  306.                                     else begin
  307.                                         pic := globals.waitpicts[game.playertomove];
  308.                                     end; { if }
  309.                             end; { case }
  310.                         ps_Playing: 
  311.                             case game.connectionstate of
  312.                                 cs_Local: 
  313.                                     pic := globals.movepicts[game.playertomove];
  314.                                 cs_Remote: 
  315.                                     if gameevent.myturn then begin
  316.                                         pic := globals.movepicts[game.playertomove];
  317.                                     end
  318.                                     else begin
  319.                                         pic := globals.waitpicts[game.playertomove];
  320.                                     end; { if }
  321.                             end; { case }
  322.                         ps_GameOver: 
  323.                             if game.resigned then begin
  324.                                 pic := globals.resignpicts[game.playertomove];
  325.                             end
  326.                             else begin
  327.                                 pic := globals.winpicts[Opposite(game.playertomove)];
  328.                             end; { if }
  329.                     end; { case }
  330.                     if pic <> oldpic then begin
  331.                         SetDHandle(game.dlg, dit_status, pic);
  332.                         GetDRect(game.dlg, dit_status, r);
  333.                         InvalRect(r);
  334.                     end; { if }
  335.                 end; { SetStatus }
  336.  
  337.             begin
  338.                 case game.connectionstate of { Not efficient but clear }
  339.                     cs_Local: 
  340.                         gameevent.myturn := true;
  341.                     cs_Remote: 
  342.                         gameevent.myturn := (game.playertomove = game.localplayer);
  343.                 end; { case }
  344.                 SetStatus;
  345.             end; { UpdateMyTurn }
  346.  
  347.             procedure CommonInit;
  348.                 var
  349.                     r: rect;
  350.             begin
  351.                 game.globals := globalsPeek(gameevent.globals);
  352.                 game.connectionstate := cs_Local;
  353.                 GetPort(game.dlg);
  354.                 WindowPeek(game.dlg)^.refcon := longint(gameevent.game);
  355.                 GetDRect(game.dlg, dit_board, r);
  356.                 game.boardXOrg := r.left;
  357.                 game.boardYOrg := r.top;
  358.                 SetDHandle(game.dlg, dit_line, Handle(@LineUserItem));
  359.                 SetDHandle(game.dlg, dit_board, Handle(@BoardUserItem));
  360.                 SetDHandle(game.dlg, dit_blk_pieces, Handle(@MorgueUserItem));
  361.                 SetDHandle(game.dlg, dit_wht_pieces, Handle(@MorgueUserItem));
  362.             end; { CommonInit }
  363.  
  364.             procedure InitGameState;
  365.                 var
  366.                     p: playerType;
  367.                     m: morgueNdx;
  368.             begin
  369.                 InitBoard(game.board);
  370.                 game.playertomove := Pwhite;
  371.                 game.playstate := ps_Playing;
  372.                 InitState(game.specialstate);
  373.                 for p := Pblack to Pwhite do begin
  374.                     for m := 1 to kMorgueMax do begin
  375.                         game.deaths[p, m] := Oempty;
  376.                     end; { for }
  377.                 end; { for }
  378.                 game.resigned := false;
  379.             end; { InitGameState }
  380.  
  381.             procedure NewGame;
  382.             begin
  383.                 game.showthemoves := false;
  384.                 game.sounds := true;
  385.                 game.localplayer := Pwhite;
  386.                 InitGameState;
  387.                 CommonInit;
  388.                 UpdateMyTurn;
  389.             end; { NewGame }
  390.  
  391.             procedure OldGame;
  392.             begin
  393.                 CommonInit;
  394.                 UpdateMyTurn;
  395.             end; { OldGame }
  396.  
  397.             procedure Swap;
  398.             begin
  399.                 game.localplayer := Opposite(game.localplayer);
  400.                 UpdateMyTurn;
  401.             end;
  402.  
  403.             procedure Restart;
  404.             begin
  405.                 InitGameState;
  406.                 InvalRect(game.dlg^.portRect);
  407.                 UpdateMyTurn;
  408.             end; { Restart }
  409.  
  410.             procedure Activate;
  411.             begin
  412.                 InsertMenu(globals.chessmenu, 0);
  413.                 DrawMenuBar;
  414.             end; { Activate }
  415.  
  416.             procedure Deactivate;
  417.             begin
  418.                 DeleteMenu(mid_chess);
  419.                 DrawMenuBar;
  420.             end; { Deactivate }
  421.  
  422.             procedure UpdateMenus;
  423.  
  424.                 procedure Greymenu (menu: MenuHandle; itm: integer; greyit: boolean);
  425.                 begin
  426.                     if greyit then begin
  427.                         DisableItem(menu, itm);
  428.                     end
  429.                     else begin
  430.                         EnableItem(menu, itm);
  431.                     end; { if }
  432.                 end; { GreyMenu }
  433.  
  434.             begin
  435.                 GreyMenu(globals.chessmenu, mit_show_moves, false);
  436.                 CheckItem(globals.chessmenu, mit_show_moves, game.showthemoves);
  437.                 CheckItem(globals.chessmenu, mit_sounds, game.sounds);
  438.                 GreyMenu(globals.chessmenu, mit_resign, not gameevent.myturn);
  439.             end; { UpdateMenus }
  440.  
  441.             procedure SendResign;
  442.             forward;
  443.  
  444.             procedure Menu;
  445.             begin
  446.                 if gameevent.int1 <> mid_chess then begin
  447.                     Failure('Menu select not on my menu');
  448.                 end; { if }
  449.                 case gameevent.int2 of
  450.                     mit_sounds: 
  451.                         game.sounds := not game.sounds;
  452.                     mit_show_moves: 
  453.                         game.showthemoves := not game.showthemoves;
  454.                     mit_resign:  begin
  455.                         Resign;
  456.                         if game.connectionstate = cs_Remote then begin
  457.                             SendResign;
  458.                         end; { if }
  459.                         UpdateMyTurn;
  460.                     end; { if }
  461.                 end; { case }
  462.                 gameevent.modified := true;
  463.             end; { Menu }
  464.  
  465.             procedure ConnectionLost;
  466.             begin
  467.                 game.connectionstate := cs_Local;
  468.                 UpdateMyTurn;
  469.             end; { ConnectionLost }
  470.  
  471.             procedure ConnectionMade;
  472.             begin
  473.                 game.connectionstate := cs_Remote;
  474.                 UpdateMyTurn;
  475.             end; { ConnectionMade }
  476.  
  477.             procedure MessageReceived;
  478.  
  479.                 procedure DoRemoteMove;
  480.                     var
  481.                         toc, from: boardCoord;
  482.                 begin
  483.                     if game.playertomove = game.localplayer then begin
  484.                         Failure('The other player is trying to move');
  485.                     end
  486.                     else begin
  487.                         if Length(gameevent.message) = 5 then begin
  488.                             from.x := ord(gameevent.message[2]) - ord('0');
  489.                             from.y := ord(gameevent.message[3]) - ord('0');
  490.                             toc.x := ord(gameevent.message[4]) - ord('0');
  491.                             toc.y := ord(gameevent.message[5]) - ord('0');
  492.                             MovePiece(from, toc);
  493.                             MoveComplete;
  494.                             UpdateMyTurn;
  495.                         end
  496.                         else begin
  497.                             Failure(concat('Message not right length', gameevent.message));
  498.                         end; { if }
  499.                     end; { if }
  500.                 end; { DoRemoteMove }
  501.  
  502.                 procedure DoRemoteResign;
  503.                 begin
  504.                     if game.playertomove = game.localplayer then begin
  505.                         Failure('The other player is trying to resign');
  506.                     end
  507.                     else begin
  508.                         if Length(gameevent.message) = 1 then begin
  509.                             Resign;
  510.                             UpdateMyTurn;
  511.                         end
  512.                         else begin
  513.                             Failure(concat('Message not right length', gameevent.message));
  514.                         end; { if }
  515.                     end; { if }
  516.                 end; { DoRemoteResign }
  517.  
  518.                 procedure DoRemotePawnChoice;
  519.                     var
  520.                         where: boardCoord;
  521.                         choice: pieceType;
  522.                 begin
  523.                     if game.playertomove = game.localplayer then begin
  524.                         Failure('The other player is trying to choice a pawn');
  525.                     end
  526.                     else if game.playstate <> ps_Pawning then begin
  527.                         Failure('Pawning with ps_Pawning');
  528.                     end
  529.                     else if Length(gameevent.message) <> 4 then begin
  530.                         Failure(concat('Message not right length', gameevent.message));
  531.                     end
  532.                     else begin
  533.                         choice := pieceType(ord(gameevent.message[2]) - ord('A'));
  534.                         where.x := ord(gameevent.message[3]) - ord('0');
  535.                         where.y := ord(gameevent.message[4]) - ord('0');
  536.                         PawnChoice(choice, where);
  537.                         MoveComplete;
  538.                         UpdateMyTurn;
  539.                     end; { if }
  540.                 end; { DoRemotePawnChoice }
  541.  
  542.                 var
  543.                     ch: char;
  544.             begin
  545.                 if game.connectionstate <> cs_Remote then begin
  546.                     Failure('Message from God:  Stop playing with TCP');
  547.                 end
  548.                 else begin
  549.                     ch := copy(gameevent.message, 1, 1);
  550.                     case ch of
  551.                         m_Move: 
  552.                             DoRemoteMove;
  553.                         m_Resign: 
  554.                             DoRemoteResign;
  555.                         m_PawnChoice: 
  556.                             DoRemotePawnChoice;
  557.                         otherwise
  558.                             Failure(concat('Message not understood ', gameevent.message));
  559.                     end; { case }
  560.                 end;
  561.             end; { MessageReceived }
  562.  
  563.             procedure SetupSendMessage (message: str255);
  564.             begin
  565.                 gameevent.message := message;
  566.                 gameevent.event := ge_SendMessage;
  567.             end; { SetupSendMessage }
  568.  
  569.             procedure SendRemoteMove (c1, c2: boardCoord);
  570.             begin
  571.                 SetupSendMessage(concat(m_Move, chr(c1.x + ord('0')), chr(c1.y + ord('0')), chr(c2.x + ord('0')), chr(c2.y + ord('0'))));
  572.             end; { SendRemoteMove }
  573.  
  574.             procedure SendResign;
  575.             begin
  576.                 SetupSendmessage(m_Resign);
  577.             end; { SendResign }
  578.  
  579.             procedure SendPawnChoice (choice: pieceType; c1: boardCoord);
  580.             begin
  581.                 SetupSendMessage(concat(m_PawnChoice, chr(ord('A') + ord(choice)), chr(c1.x + ord('0')), chr(c1.y + ord('0'))));
  582.             end; { SendPawnChoice }
  583.  
  584.             procedure MouseDown;
  585.  
  586.                 function DragPiece (start: boardCoord; var finish: boardCoord): boolean;
  587.  
  588.                     procedure InvertValidSet (var valid: boardSet);
  589.                         var
  590.                             x: boardXNdx;
  591.                             y: boardYNdx;
  592.                             r: rect;
  593.                             where: boardCoord;
  594.                     begin
  595.                         for x := 0 to kBoardXMax do begin
  596.                             for y := 0 to kBoardYMax do begin
  597.                                 if valid[x, y] then begin
  598.                                     where.x := x;
  599.                                     where.y := y;
  600.                                     GetRect(game, where, r);
  601.                                     InvertOval(r);
  602.                                 end; { if }
  603.                             end; { for}
  604.                         end; { for }
  605.                     end; { InvertValidSet }
  606.  
  607.                     const
  608.                         shift_key = 56;
  609.                     var
  610.                         valid: boardSet;
  611.                         ps: PenState;
  612.                         old: boardCoord;
  613.                         hirect, dragrect: Rect;
  614.                         oldmouse, newmouse: Point;
  615.                         onboard: boolean;
  616.                         debugging: boolean;
  617.                         km: KeyMap;
  618.                         count: integer;
  619.                 begin
  620.                     CalculateValidSet(game.board, game.specialstate, start, valid, count);
  621.                     if count = 0 then begin
  622.                         DragPiece := false;
  623.                     end
  624.                     else begin
  625.                         GetKeys(km);
  626.                         debugging := km[shift_key] or game.showthemoves;
  627.                         GetPenState(ps);
  628.                         PenMode(patXor);
  629.                         PenPat(QDGlobals^.grey);
  630.                         PenSize(2, 2);
  631.                         if debugging then begin
  632.                             InvertValidSet(valid);
  633.                         end; { if }
  634.                         GetRect(game, start, hirect);
  635.                         dragrect := hirect;
  636.                         FrameRect(dragrect);
  637.                         InvertRect(hirect);
  638.                         oldmouse := gameevent.where;
  639.                         old := start;
  640.                         finish := start;
  641.                         while Button do begin
  642.                             GetMouse(newmouse);
  643.                             if (oldmouse.h <> newmouse.h) or (oldmouse.v <> newmouse.v) then begin
  644.                                 FrameRect(dragrect);
  645.                                 OffsetRect(dragrect, newmouse.h - oldmouse.h, newmouse.v - oldmouse.v);
  646.                                 FrameRect(dragrect);
  647.                                 oldmouse := newmouse;
  648.                                 onboard := FindCell(game, newmouse, finish);
  649.                                 if not onboard or not valid[finish.x, finish.y] then begin
  650.                                     finish := old;
  651.                                 end; { if }
  652.                                 if (finish.x <> old.x) or (finish.y <> old.y) then begin
  653.                                     InvertRect(hirect);
  654.                                     GetRect(game, finish, hirect);
  655.                                     InvertRect(hirect);
  656.                                     old := finish;
  657.                                 end; { if }
  658.                             end; { if }
  659.                         end; { while }
  660.                         FrameRect(dragrect);
  661.                         InvertRect(hirect);
  662.                         if debugging then begin
  663.                             InvertValidSet(valid);
  664.                         end; { if }
  665.                         SetPenState(ps);
  666.                         DragPiece := (finish.x <> start.x) or (finish.y <> start.y);
  667.                     end; { if }
  668.                 end; { DragPiece }
  669.  
  670.                 function DragPawnButtons (start: boardCoord; var choice: pieceType): boolean;
  671.                     var
  672.                         r: Rect;
  673.                         inverted: boolean;
  674.                         inacell: boolean;
  675.                         loc: boardCoord;
  676.                         mouse: Point;
  677.                         junk: longint;
  678.                         i: integer;
  679.                 begin
  680.                     GetRect(game, start, r);
  681.                     inverted := false;
  682.                     while button do begin
  683.                         GetMouse(mouse);
  684.                         inacell := FindCell(game, mouse, loc);
  685.                         inacell := inacell and (loc.x = start.x) and (loc.y = start.y);
  686.                         if (inacell and not inverted) or (inverted and not inacell) then begin
  687.                             InvertRect(r);
  688.                             inverted := not inverted;
  689.                         end; { if }
  690.                     end; { while }
  691.                     if inverted then begin
  692.                         for i := 1 to GetMenuFlash do begin
  693.                             InvertRect(r);
  694.                             Delay(6, junk);
  695.                             InvertRect(r);
  696.                             Delay(6, junk);
  697.                         end; { for }
  698.                         InvertRect(r);
  699.                         choice := globals.pawnpieces[loc.x];
  700.                     end; { if }
  701.                     DragPawnButtons := inverted;
  702.                 end; { DragPawnButtons }
  703.  
  704.                 var
  705.                     click, dest: boardCoord;
  706.                     inacell: boolean;
  707.                     choice: pieceType;
  708.             begin
  709.                 click.x := 0;
  710.                 click.y := 0;
  711.                 dest.x := 0;
  712.                 dest.y := 0;
  713.                 case game.playState of
  714.                     ps_Pawning:  begin
  715.                         inacell := FindCell(game, gameevent.where, click);
  716.                         if inacell and (click.y = kPawningY) and (click.x in [kPawningX..kPawningXMax]) then begin
  717.                             if DragPawnButtons(click, choice) then begin
  718.                                 PawnChoice(PlayerPiece(game.playertomove, choice), game.pawnpos);
  719.                                 if game.connectionstate = cs_Remote then begin
  720.                                     SendPawnChoice(PlayerPiece(game.playertomove, choice), game.pawnpos);
  721.                                 end; { if }
  722.                                 MoveComplete;
  723.                                 UpdateMyTurn;
  724.                             end; { if }
  725.                         end; { if }
  726.                     end;
  727.                     ps_Playing:  begin
  728.                         inacell := FindCell(game, gameevent.where, click);
  729.                         if inacell and PieceInMyTeam(game.playertomove, game.board[click.x, click.y].occupant) then begin
  730.                             if DragPiece(click, dest) then begin
  731.                                 MovePiece(click, dest);
  732.                                 MoveComplete;
  733.                                 if game.connectionstate = cs_Remote then begin
  734.                                     SendRemoteMove(click, dest);
  735.                                 end; { if }
  736.                                 UpdateMyTurn;
  737.                             end; { if }
  738.                         end; { if }
  739.                     end;
  740.                     ps_GameOver:  begin
  741.                         SysBeep(10);
  742.                     end;
  743.                 end; { case }
  744.             end; { MouseDown }
  745.  
  746.         begin { PseudoMain }
  747.             case gameevent.event of
  748.                 ge_NewGame: 
  749.                     NewGame;
  750.                 ge_OldGame: 
  751.                     OldGame;
  752.                 ge_Swap: 
  753.                     Swap;
  754.                 ge_Restart: 
  755.                     Restart;
  756.                 ge_Activate: 
  757.                     Activate;
  758.                 ge_Deactivate: 
  759.                     Deactivate;
  760.                 ge_UpdateMenus: 
  761.                     UpdateMenus;
  762.                 ge_Menu: 
  763.                     Menu;
  764.                 ge_ConnectionLost: 
  765.                     ConnectionLost;
  766.                 ge_ConnectionMade: 
  767.                     ConnectionMade;
  768.                 ge_MessageReceived: 
  769.                     MessageReceived;
  770.                 ge_MouseDown: 
  771.                     MouseDown;
  772.                 otherwise
  773.             end; { case }
  774.         end; { PseudoMain }
  775.  
  776.         var
  777.             s1, s2: signedByte;
  778.             s1valid, s2valid: boolean;
  779.     begin { Main }
  780.         case gameevent.event of
  781.             ge_InitRuleBook: 
  782.                 InitRuleBook;
  783.             ge_FinishRuleBook: 
  784.                 FinishRuleBook;
  785.             otherwise begin
  786.                 if gameevent.event = ge_NewGame then begin
  787.                     SetHandleSize(gameevent.game, sizeof(gameRecord));
  788.                 end; (* if *)
  789.                 s1valid := (gameevent.game <> nil);
  790.                 if s1valid then begin
  791.                     s1 := HGetState(gameevent.game);
  792.                     HLock(gameevent.game);
  793.                 end; (* if *)
  794.                 s2valid := (gameevent.globals <> nil);
  795.                 if s2valid then begin
  796.                     s2 := HGetState(gameevent.globals);
  797.                     HLock(gameevent.globals);
  798.                 end; (* if *)
  799.                 PseudoMain(gamePeek(gameevent.game)^^, globalsPeek(gameevent.globals)^^);
  800.                 if s1valid then begin
  801.                     HSetState(gameevent.game, s1);
  802.                 end; (* if *)
  803.                 if s2valid then begin
  804.                     HSetState(gameevent.globals, s2);
  805.                 end; (* if *)
  806.             end; (* otherwise *)
  807.         end; (* case *)
  808.     end; { Main }
  809. end. { Chess }