home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 October / usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso / games / volume13 / xsokoban3 / part01 / play.c < prev    next >
C/C++ Source or Header  |  1992-02-11  |  12KB  |  520 lines

  1. #include <stdio.h>
  2. #include "externs.h"
  3. #include "globals.h"
  4.  
  5. /* defining the types of move */
  6. #define MOVE            1
  7. #define PUSH            2
  8. #define SAVE            3
  9. #define UNSAVE          4
  10. #define STOREMOVE       5
  11. #define STOREPUSH       6
  12.  
  13. /* This value merely needs to be greater than the longest possible path length
  14.  * making it the number of squares in the array seems a good bet. 
  15.  */
  16. #define BADMOVE MAXROW*MAXCOL
  17.  
  18. /* some simple checking macros to make sure certain moves are legal when 
  19.  * trying to do mouse based movement.
  20.  */
  21. #define ISPLAYER(x,y) ((map[x][y] == player) || (map[x][y] == playerstore))
  22. #define ISCLEAR(x,y) ((map[x][y] == ground) || (map[x][y] == store))
  23. #define ISPACKET(x,y) ((map[x][y] == packet) || (map[x][y] == save))
  24.  
  25. /* whee, yet more globals */
  26. extern char map[MAXROW+1][MAXCOL+1];
  27. extern short rows, cols, level, moves, pushes, savepack, packets;
  28. extern short scorelevel, scoremoves, scorepushes;
  29. extern POS ppos;
  30. extern Display *dpy;
  31. extern int bit_width, bit_height;
  32.  
  33. static XEvent xev;
  34. static POS tpos1, tpos2, lastppos, lasttpos1, lasttpos2;
  35. static char lppc, ltp1c, ltp2c;
  36. static short action, lastaction;
  37.  
  38. /** For the temporary save **/
  39. static char  tmp_map[MAXROW+1][MAXCOL+1];
  40. static short tmp_pushes, tmp_moves, tmp_savepack;
  41. static POS   tmp_ppos;
  42. static Boolean undolock = _true_;
  43. static Boolean shift = _false_;
  44. static Boolean cntrl = _false_;
  45. static KeySym oldmove;
  46. static int findmap[MAXROW+1][MAXCOL+1];
  47.  
  48. /* play a particular level.
  49.  * All we do here is wait for input, and dispatch to appropriate routines
  50.  * to deal with it nicely.
  51.  */
  52. short Play(void)
  53. {
  54.   short c;
  55.   short ret;
  56.   int bufs = 1;
  57.   char buf[1];
  58.   KeySym sym;
  59.   XComposeStatus compose;
  60.   
  61.   ClearScreen();
  62.   ShowScreen();
  63.   TempSave();
  64.   ret = 0;
  65.   while (ret == 0) {
  66.     XNextEvent(dpy, &xev);
  67.     switch(xev.type) {
  68.       case Expose:
  69.     /* Redisplaying is pretty cheap, so I don't care to much about it */
  70.     RedisplayScreen();
  71.     break;
  72.       case ButtonPress:
  73.     switch(xev.xbutton.button) {
  74.       case Button1:
  75.         /* move the man to where the pointer is. */
  76.         MoveMan(xev.xbutton.x, xev.xbutton.y);
  77.         break;
  78.       case Button2:
  79.         /* redo the last move */
  80.         MakeMove(oldmove);
  81.         break;
  82.       case Button3:
  83.         /* undo last move */
  84.         if(!undolock) {
  85.           UndoMove();
  86.           undolock = _true_;
  87.         }
  88.         break;
  89.       default:
  90.         /* I'm sorry, you win the irritating beep for your efforts. */
  91.         HelpMessage();
  92.         break;
  93.     }
  94.     break;
  95.       case KeyPress:
  96.     buf[0] = '\0';
  97.     (void) XLookupString(&xev.xkey, buf, bufs, &sym, &compose);
  98.     cntrl = (xev.xkey.state & ControlMask) ? _true_ : _false_;
  99.     shift = (xev.xkey.state & ShiftMask) ? _true_ : _false_;
  100.     switch(sym) {
  101.       case XK_q:
  102.         /* q is for quit */
  103.         if(!cntrl)
  104.           ret = E_ENDGAME;
  105.         break;
  106.       case XK_s:
  107.         /* save */
  108.         if(!cntrl) {
  109.           ret = SaveGame();
  110.           if(ret == 0)
  111.         ret = E_SAVED;
  112.         }
  113.         break;
  114.       case XK_question:
  115.         /* help */
  116.         if(!cntrl) {
  117.           ShowHelp();
  118.           RedisplayScreen();
  119.         }
  120.         break;
  121.       case XK_r:
  122.         /* ^R refreshes the screen */
  123.         if(cntrl)
  124.           RedisplayScreen();
  125.         break;
  126.       case XK_c:
  127.         /* make a temp save */
  128.         if(!cntrl)
  129.           TempSave();
  130.         break;
  131.       case XK_U:
  132.         /* Do a full screen reset */
  133.         if(!cntrl) {
  134.           moves = pushes = 0;
  135.           ret = ReadScreen();
  136.           if(ret == 0) {
  137.         ShowScreen();
  138.         undolock = _true_;
  139.           }
  140.         }
  141.         break;
  142.       case XK_u:
  143.         if(cntrl) {
  144.           /* Reset to last temp save */
  145.           TempReset();
  146.           undolock = _true_;
  147.           ShowScreen();
  148.         } else {
  149.           /* undo last move */
  150.           if (!undolock) {
  151.         UndoMove();
  152.         undolock = _true_;
  153.           }
  154.         }
  155.         break;
  156.       case XK_k:
  157.       case XK_K:
  158.       case XK_Up:
  159.       case XK_j:
  160.       case XK_J:
  161.       case XK_Down:
  162.       case XK_l:
  163.       case XK_L:
  164.       case XK_Right:
  165.       case XK_h:
  166.       case XK_H:
  167.       case XK_Left:
  168.         /* A move, A move!! we have a MOVE!! */
  169.         MakeMove(sym);
  170.         break;
  171.       default:
  172.         /* I ONLY want to beep if a key was pressed.  Contrary to what
  173.          * X11 believes, SHIFT and CONTROL are NOT keys
  174.          */
  175.             if(buf[0]) {
  176.           HelpMessage();
  177.         }
  178.         break;
  179.     }
  180.     break;
  181.       default:
  182.     break;
  183.     }
  184.     /* if we solved a level, set it up so we get some score! */
  185.     if((ret == 0) && (packets == savepack)) {
  186.       scorelevel = level;
  187.       scoremoves = moves;
  188.       scorepushes = pushes;
  189.       break;
  190.     }
  191.   }
  192.   return ret;
  193. }
  194.  
  195. /* Well what do you THINK this does? */
  196. void MakeMove(KeySym sym)
  197. {
  198.   do {
  199.     action = TestMove(sym);
  200.     if(action != 0) {
  201.       lastaction = action;
  202.       lastppos.x = ppos.x;
  203.       lastppos.y = ppos.y;
  204.       lppc = map[ppos.x][ppos.y];
  205.       lasttpos1.x = tpos1.x;
  206.       lasttpos1.y = tpos1.y;
  207.       ltp1c = map[tpos1.x][tpos1.y];
  208.       lasttpos2.x = tpos2.x;
  209.       lasttpos2.y = tpos2.y;
  210.       ltp2c = map[tpos2.x][tpos2.y];
  211.       DoMove(lastaction);
  212.       undolock = _false_;
  213.       /* store the current keysym so we can do the repeat. */
  214.       oldmove = sym;
  215.     }
  216.   } while ((action != 0) && (packets != savepack) && (shift || cntrl));
  217. }
  218.  
  219. /* make sure a move is valid and if it is, return type of move */
  220. short TestMove(KeySym action)
  221. {
  222.   short ret;
  223.   char tc;
  224.   Boolean stop_at_object;
  225.  
  226.   stop_at_object = cntrl;
  227.  
  228.   if((action == XK_Up) || (action == XK_k) || (action == XK_K)) {
  229.     tpos1.x = ppos.x-1;
  230.     tpos2.x = ppos.x-2;
  231.     tpos1.y = tpos2.y = ppos.y;
  232.   }
  233.   if((action == XK_Down) || (action == XK_j) || (action == XK_J)) {
  234.     tpos1.x = ppos.x+1;
  235.     tpos2.x = ppos.x+2;
  236.     tpos1.y = tpos2.y = ppos.y;
  237.   }
  238.   if((action == XK_Left) || (action == XK_h) || (action == XK_H)) {
  239.     tpos1.y = ppos.y-1;
  240.     tpos2.y = ppos.y-2;
  241.     tpos1.x = tpos2.x = ppos.x;
  242.   }
  243.   if((action == XK_Right) || (action == XK_l) || (action == XK_L)) {
  244.     tpos1.y = ppos.y+1;
  245.     tpos2.y = ppos.y+2;
  246.     tpos1.x = tpos2.x = ppos.x;
  247.   }
  248.   tc = map[tpos1.x][tpos1.y];
  249.   if((tc == packet) || (tc == save)) {
  250.     if(!stop_at_object) {
  251.       if(map[tpos2.x][tpos2.y] == ground)
  252.     ret = (tc == save) ? UNSAVE : PUSH;
  253.       else if(map[tpos2.x][tpos2.y] == store)
  254.     ret = (tc == save) ? STOREPUSH : SAVE;
  255.       else
  256.     ret = 0;
  257.     } else
  258.       ret = 0;
  259.   } else if(tc == ground)
  260.     ret = MOVE;
  261.   else if(tc == store)
  262.     ret = STOREMOVE;
  263.   else
  264.     ret = 0;
  265.   return ret;
  266. }
  267.  
  268. /* actually update the internal map with the results of the move */
  269. void DoMove(short moveaction)
  270. {
  271.   map[ppos.x][ppos.y] = (map[ppos.x][ppos.y] == player) ? ground : store;
  272.   switch( moveaction) {
  273.     case MOVE:
  274.       map[tpos1.x][tpos1.y] = player;
  275.       break;
  276.     case STOREMOVE:
  277.       map[tpos1.x][tpos1.y] = playerstore;
  278.       break;
  279.     case PUSH:
  280.       map[tpos2.x][tpos2.y] = map[tpos1.x][tpos1.y];
  281.       map[tpos1.x][tpos1.y] = player;
  282.       pushes++;
  283.       break;
  284.     case UNSAVE:
  285.       map[tpos2.x][tpos2.y] = packet;
  286.       map[tpos1.x][tpos1.y] = playerstore;
  287.       pushes++;
  288.       savepack--;
  289.       break;
  290.     case SAVE:
  291.       map[tpos2.x][tpos2.y] = save;
  292.       map[tpos1.x][tpos1.y] = player;
  293.       savepack++;
  294.       pushes++;
  295.       break;
  296.     case STOREPUSH:
  297.       map[tpos2.x][tpos2.y] = save;
  298.       map[tpos1.x][tpos1.y] = playerstore;
  299.       pushes++;
  300.       break;
  301.   }
  302.   moves++;
  303.   DisplayMoves();
  304.   DisplayPushes();
  305.   DisplaySave();
  306.   MapChar(map[ppos.x][ppos.y], ppos.x, ppos.y);
  307.   MapChar(map[tpos1.x][tpos1.y], tpos1.x, tpos1.y);
  308.   MapChar(map[tpos2.x][tpos2.y], tpos2.x, tpos2.y);
  309.   RedisplayScreen();
  310.   ppos.x = tpos1.x;
  311.   ppos.y = tpos1.y;
  312. }
  313.  
  314. /* undo the most recently done move */
  315. void UndoMove(void)
  316. {
  317.   map[lastppos.x][lastppos.y] = lppc;
  318.   map[lasttpos1.x][lasttpos1.y] = ltp1c;
  319.   map[lasttpos2.x][lasttpos2.y] = ltp2c;
  320.   ppos.x = lastppos.x;
  321.   ppos.y = lastppos.y;
  322.   switch( lastaction) {
  323.     case MOVE:
  324.       moves--;
  325.       break;
  326.     case STOREMOVE:
  327.       moves--;
  328.       break;
  329.     case PUSH:
  330.       moves--;
  331.       pushes--;
  332.       break;
  333.     case UNSAVE:
  334.       moves--;
  335.       pushes--;
  336.       savepack++;
  337.       break;
  338.     case SAVE:
  339.       moves--;
  340.       pushes--;
  341.       savepack--;
  342.       break;
  343.     case STOREPUSH:
  344.       moves--;
  345.       pushes--;
  346.       break;
  347.   }
  348.   DisplayMoves();
  349.   DisplayPushes();
  350.   DisplaySave();
  351.   MapChar(map[ppos.x][ppos.y], ppos.x, ppos.y);
  352.   MapChar(map[lasttpos1.x][lasttpos1.y], lasttpos1.x, lasttpos1.y);
  353.   MapChar(map[lasttpos2.x][lasttpos2.y], lasttpos2.x, lasttpos2.y);
  354.   RedisplayScreen();
  355. }
  356.  
  357. /* make a temporary save so we don't screw up too much at once */
  358. void TempSave(void)
  359. {
  360.   short i, j;
  361.  
  362.   for( i = 0; i < rows; i++)
  363.     for( j = 0; j < cols; j++)
  364.        tmp_map[i][j] = map[i][j];
  365.   tmp_pushes = pushes;
  366.   tmp_moves = moves;
  367.   tmp_savepack = savepack;
  368.   tmp_ppos.x = ppos.x;
  369.   tmp_ppos.y = ppos.y;
  370. }
  371.  
  372. /* restore from that little temp save */
  373. void TempReset(void)
  374. {
  375.   short i, j;
  376.  
  377.   for( i = 0; i < rows; i++)
  378.     for( j = 0; j < cols; j++)
  379.       map[i][j] = tmp_map[i][j];
  380.   pushes = tmp_pushes;
  381.   moves = tmp_moves;
  382.   savepack = tmp_savepack;
  383.   ppos.x = tmp_ppos.x;
  384.   ppos.y = tmp_ppos.y;
  385. }
  386.  
  387. /* Function used by the help pager.  We ONLY want to flip pages if a key
  388.  * key is pressed.. We want to exit the help pager if ENTER is pressed.
  389.  * As above, <shift> and <control> and other such fun keys are NOT counted
  390.  * as a keypress.
  391.  */
  392. Boolean WaitForEnter(void)
  393. {
  394.   KeySym keyhit;
  395.   char buf[1];
  396.   int bufs = 1;
  397.   XComposeStatus compose;
  398.  
  399.   while (1) {
  400.     XNextEvent(dpy, &xev);
  401.     switch(xev.type) {
  402.       case Expose:
  403.     RedisplayScreen();
  404.     break;
  405.       case KeyPress:
  406.     buf[0] = '\0';
  407.     XLookupString(&xev.xkey, buf, bufs, &keyhit, &compose);
  408.     if(buf[0]) {
  409.       return (keyhit == XK_Return) ? _true_ : _false_;
  410.     }
  411.     break;
  412.       default:
  413.     break;
  414.     }
  415.   }
  416. }
  417.  
  418. /* find the shortest path to the target via a fill search algorithm */
  419. void FindTarget(int px, int py, int pathlen)
  420. {
  421.   if(!(ISCLEAR(px, py) || ISPLAYER(px, py)))
  422.     return;
  423.   if(findmap[px][py] <= pathlen)
  424.     return;
  425.  
  426.   findmap[px][py] = pathlen++;
  427.  
  428.   if((px == ppos.x) && (py == ppos.y))
  429.     return;
  430.  
  431.   FindTarget(px - 1, py, pathlen);
  432.   FindTarget(px + 1, py, pathlen);
  433.   FindTarget(px, py - 1, pathlen);
  434.   FindTarget(px, py + 1, pathlen);
  435. }
  436.  
  437. /* Do all the fun movement stuff with the mouse */
  438. void MoveMan(int mx, int my)
  439. {
  440.   int i, j, cx, cy, x, y;
  441.  
  442.   shift = cntrl = _false_;
  443.  
  444.   /* reverse the screen coords to get the internal coords (yes, I know this 
  445.    * should be fixed) RSN */
  446.   y = wX(mx);
  447.   x = wY(my);
  448.  
  449.   /* make sure we are within the bounds of the array */
  450.   if((x < 0) || (x > MAXROW) || (y < 0) || (y > MAXCOL)) {
  451.     HelpMessage();
  452.     return;
  453.   }
  454.  
  455.   /* if we are clicking on an object, and are right next to it, we want to
  456.    * move the object.
  457.    */
  458.   if(ISPACKET(x, y)) {
  459.     if(ISPLAYER(x - 1, y))
  460.       MakeMove(XK_Down);
  461.     else if(ISPLAYER(x + 1, y))
  462.       MakeMove(XK_Up);
  463.     else if(ISPLAYER(x, y - 1))
  464.       MakeMove(XK_Right);
  465.     else if(ISPLAYER(x, y + 1))
  466.       MakeMove(XK_Left);
  467.     else {
  468.       /* we weren't right next to the object */
  469.       HelpMessage();
  470.       return;
  471.     }
  472.     return;
  473.   }
  474.  
  475.   /* if we clicked on the player or a wall (or an object but that was already
  476.    * handled) the we don't want to move.
  477.    */
  478.   if(!ISCLEAR(x, y)) {
  479.     HelpMessage();
  480.     return;
  481.   }
  482.   /* okay.. this is a legal place to click, so set it up by filling the
  483.    * trace map with all impossible values
  484.    */
  485.   for(i = 0; i < MAXROW + 1; i++)
  486.     for (j = 0; j < MAXCOL + 1; j++)
  487.       findmap[i][j] = BADMOVE;
  488.   /* flood fill search to find any shortest path. */
  489.   FindTarget(x, y, 0);
  490.  
  491.   /* if we didn't make it back to the players position, there is no valid path
  492.    * to that place.
  493.    */
  494.   if(findmap[ppos.x][ppos.y] == BADMOVE) {
  495.     HelpMessage();
  496.   } else {
  497.     /* we made it back, so let's walk the path we just built up */
  498.     cx = ppos.x;
  499.     cy = ppos.y;
  500.     while(findmap[cx][cy]) {
  501.       if(findmap[cx - 1][cy] == (findmap[cx][cy] - 1)) {
  502.     MakeMove(XK_Up);
  503.     cx--;
  504.       } else if(findmap[cx + 1][cy] == (findmap[cx][cy] - 1)) {
  505.     MakeMove(XK_Down);
  506.     cx++;
  507.       } else if(findmap[cx][cy - 1] == (findmap[cx][cy] - 1)) {
  508.     MakeMove(XK_Left);
  509.     cy--;
  510.       } else if(findmap[cx][cy + 1] == (findmap[cx][cy] - 1)) {
  511.     MakeMove(XK_Right);
  512.     cy++;
  513.       } else {
  514.     /* if we get here, something is SERIOUSLY wrong, so we should abort */
  515.     abort();
  516.       }
  517.     }
  518.   }
  519. }
  520.