home *** CD-ROM | disk | FTP | other *** search
/ Game Killer / Game_Killer.bin / 512.WGRAPH.C < prev    next >
C/C++ Source or Header  |  1992-10-15  |  34KB  |  1,287 lines

  1. /*
  2.  
  3. ---------------------------------------------------------------------------
  4. | Graph class                  |    (c) Oct 1992 Sysma Automatisering     |
  5. ---------------------------------------------------------------------------
  6. | Version 1.0         12/10/92 | First implementation.                    |
  7. |                              | J.P. Dijkstra, M.Sc.                     |
  8. | Version 1.01        15/10/92 | Updated the editor so that when the      |
  9. |                              | mouse cursor is moved out and back in    |
  10. |                              | the map area during drawing (ie. button  |
  11. |                              | pressed), the drawing state remains      |
  12. |                              | active.                                  |
  13. |                              | J.P. Dijkstra, M.Sc.                     |
  14. |--------------------------------------------------------------------------
  15. | The Graph class contains the entire graphical map viewer/editor. The    |
  16. | display look is based upon the MapEdit program, written by Bill Kirby.  |
  17. | The implementation, however, is entirely revamped to take full          |
  18. | of the other classes already implemented in the WolfMap program. This   |
  19. | class, however, still uses the same format for the definition files as  |
  20. | the MapEdit program does.
  21. ---------------------------------------------------------------------------
  22.  
  23. */
  24.  
  25. #include "wolfmap.h"
  26. #include "stdio.h"
  27. #include "alloc.h"
  28. #include "string.h"
  29. #include "graphics.h"
  30. #include "bios.h"
  31.  
  32. /*
  33.  
  34. ---------------------------------------------------------------------------
  35. | Local data structures and constants                                     |
  36. ---------------------------------------------------------------------------
  37. | Struct / Class name         | Description.                              |
  38. |   Field name                | Description.                              |
  39. ---------------------------------------------------------------------------
  40. | Struct / Class name         | Description.                              |
  41. |   Field name                | Description.                              |
  42. ---------------------------------------------------------------------------
  43.  
  44. */
  45.  
  46. //
  47. //   General purpose local definitions.
  48. //
  49. const int cSolid      = 0;
  50. const int cCheckered  = 1;
  51. const int cHorizontal = cTrue;
  52. const int cVertical   = cFalse;
  53.  
  54. //
  55. //   Coordinates of various display areas during editing.
  56. //
  57. const int Offset   = 5;
  58. const int CellSize = 7;
  59.  
  60. const int StartMazeX   = 0;
  61. const int StartMazeY   = 0;
  62. const int EndMazeX     = StartMazeX + 64*CellSize + 2*Offset - 1;
  63. const int EndMazeY     = StartMazeY + 64*CellSize + 2*Offset - 1;
  64. const int StartLegendX = 462;
  65. const int StartLegendY = 0;
  66. const int EndLegendX   = 639;
  67. const int EndLegendY   = 408;
  68. const int StartMapX    = StartLegendX + 2;
  69. const int EndMapX      = StartLegendX + 44;
  70. const int StartObjX    = StartLegendX + 47;
  71. const int EndObjX      = StartLegendX + 84;
  72. const int StartUpX     = StartLegendX + 87;
  73. const int EndUpX       = StartLegendX + 114;
  74. const int StartDownX   = StartLegendX + 117;
  75. const int EndDownX     = EndLegendX   - 2;
  76. const int StartChoiseY = 2;
  77. const int EndChoiseY   = 25;
  78. const int StartChoiseX = StartLegendX + 2;
  79. const int EndChoiseX   = EndLegendX   - 2;
  80. const int StartCommY   = EndLegendY   - 25;
  81. const int EndCommY     = EndLegendY   - 2;
  82. const int StartLevelX  = StartLegendX + 2;
  83. const int EndLevelX    = EndLegendX   - 2;
  84. const int StartLevelY  = EndLegendY   + 1;
  85. const int EndLevelY    = EndMazeY     - 2;
  86.  
  87. /*
  88.  
  89. ---------------------------------------------------------------------------
  90. | Local functions to implement elementary operations.                     |
  91. ---------------------------------------------------------------------------
  92. | Function name               | Description.                              |
  93. ---------------------------------------------------------------------------
  94.  
  95. */
  96.  
  97. static int MouseReset ()
  98. {
  99.   //
  100.   //   Reset the mouse and return the number of buttons. After the reset,
  101.   //   the mouse cursor is hidden.
  102.   //
  103.   asm xor ax, ax;
  104.   asm int 0x33;
  105.  
  106.   return _AX != 0 ? _BX : 0;
  107. }
  108.  
  109. static void MouseShow ()
  110. {
  111.   //
  112.   //   Show the mouse cursor.
  113.   //
  114.   asm mov ax, 1;
  115.   asm int 0x33;
  116. }
  117.  
  118. static void MouseHide ()
  119. {
  120.   //
  121.   //   Hide the mouse cursor.
  122.   //
  123.   asm mov ax, 2;
  124.   asm int 0x33;
  125. }
  126.  
  127. static void MousePos (int *PosX, int *PosY, unsigned *Buttons)
  128. {
  129.   //
  130.   //   Determine and return the current mouse position and button states.
  131.   //
  132.   int      StoreX;
  133.   int      StoreY;
  134.   unsigned StoreButtons;
  135.  
  136.   asm mov ax, 3;
  137.   asm int 0x33;
  138.   asm mov StoreX, cx;
  139.   asm mov StoreY, dx;
  140.   asm mov StoreButtons, bx;
  141.  
  142.   *PosX    = StoreX;
  143.   *PosY    = StoreY;
  144.   *Buttons = StoreButtons;
  145. }
  146.  
  147. static int HexValue (char Value)
  148. {
  149.   //
  150.   //   Convert the character to it's hex value equivalent.
  151.   //
  152.   if (Value >= '0' && Value <= '9') return Value - '0';
  153.   if (Value >= 'a' && Value <= 'f') return Value - 'a' + 10;
  154.   if (Value >= 'A' && Value <= 'F') return Value - 'A' + 10;
  155.  
  156.   return 0;
  157. }
  158.  
  159. static int InRange (int x, int y, int x1, int y1, int x2, int y2)
  160. {
  161.   //
  162.   //   Return true if the given (x,y) coordinates are within the specified
  163.   //   area, false otherwise.
  164.   //
  165.   if (x < x1 || x > x2) return cFalse;
  166.   if (y < y1 || y > y2) return cFalse;
  167.  
  168.   return cTrue;
  169. }
  170.  
  171. static unsigned Min (unsigned First, unsigned Second)
  172. {
  173.   return First <= Second ? First : Second;
  174. }
  175.  
  176. static void Dot (int DotSize, int x, int y, int Color)
  177. {
  178.   x = x * CellSize + StartMazeX + Offset;
  179.   y = y * CellSize + StartMazeY + Offset;
  180.   setfillstyle (SOLID_FILL, Color);
  181.  
  182.   //
  183.   //   Place a dot of the requested size.
  184.   //
  185.   switch (DotSize)
  186.   {
  187.     case 1:
  188.       putpixel (x + 3, y + 3, Color);
  189.       break;
  190.     case 2:
  191.       bar (x + 2, y + 2, x + 4, y + 4);
  192.       break;
  193.     case 3:
  194.       bar (x + 1, y + 1, x + 5, y + 5);
  195.       break;
  196.     case 4:
  197.       bar (x, y, x + 6, y + 6);
  198.       break;
  199.   }
  200. }
  201.  
  202. static void Box (int FillType, int x, int y, int Color)
  203. {
  204.   x = x * CellSize + StartMazeX + Offset;
  205.   y = y * CellSize + StartMazeY + Offset;
  206.  
  207.   //
  208.   //   Draw a box in the cell, using the specified style.
  209.   //
  210.   setfillstyle (FillType == cSolid ? SOLID_FILL : INTERLEAVE_FILL, Color);
  211.   bar (x, y, x + 6, y + 6);
  212. }
  213.  
  214. static void SplitBox (int x, int y, int UpperColor, int LowerColor)
  215. {
  216.   x = x * CellSize + StartMazeX + Offset;
  217.   y = y * CellSize + StartMazeY + Offset;
  218.  
  219.   //
  220.   //   Draw the upper part of the box in the cell.
  221.   //
  222.   setfillstyle (SOLID_FILL, UpperColor);
  223.   bar (x, y, x + 6, y + 3);
  224.  
  225.   //
  226.   //   Now draw the lower part of the box in the cell.
  227.   //
  228.   setfillstyle (SOLID_FILL, LowerColor);
  229.   bar (x, y + 4, x + 6, y + 6);
  230. }
  231.  
  232. static void Circle (int x, int y, int OuterColor, int InnerColor)
  233. {
  234.   x = x * CellSize + StartMazeX + Offset;
  235.   y = y * CellSize + StartMazeY + Offset;
  236.   setcolor (OuterColor);
  237.   setfillstyle (SOLID_FILL, OuterColor);
  238.  
  239.   //
  240.   //   Draw a solid circle. Since it is a small cell we are talking about,
  241.   //   we draw the circle as a series of lines in the cell.
  242.   //
  243.   line (x + 2, y,     x + 4, y);
  244.   line (x + 1, y + 1, x + 5, y + 1);
  245.   bar  (x    , y + 2, x + 6, y + 4);
  246.   line (x + 1, y + 5, x + 5, y + 5);
  247.   line (x + 2, y + 6, x + 4, y + 6);
  248.  
  249.   //
  250.   //   If we need an inner circle, draw it also as a series of lines.
  251.   //
  252.   if (OuterColor != InnerColor)
  253.   {
  254.     setcolor (InnerColor);
  255.     putpixel (x + 3, y + 2, InnerColor);
  256.     line     (x + 2, y + 3, x + 4, y + 3);
  257.     putpixel (x + 3, y + 4, InnerColor);
  258.   }
  259. }
  260.  
  261. static void Door (int Horizontal, int x, int y, int Color)
  262. {
  263.   x = x * CellSize + StartMazeX + Offset;
  264.   y = y * CellSize + StartMazeY + Offset;
  265.   setfillstyle (SOLID_FILL, Color);
  266.  
  267.   //
  268.   //   Draw a horizontal or vertical door.
  269.   //
  270.   if (Horizontal)
  271.   {
  272.     bar (x, y + 2, x + 6, y + 4);
  273.   }
  274.   else
  275.   {
  276.     bar (x + 2, y, x + 4, y + 6);
  277.   }
  278. }
  279.  
  280. static void Cross (int x, int y, int Color)
  281. {
  282.   x = x * CellSize + StartMazeX + Offset;
  283.   y = y * CellSize + StartMazeY + Offset;
  284.   setcolor (Color);
  285.  
  286.   //
  287.   //   Draw two crossed lines in the given color.
  288.   //
  289.   line (x + 1, y + 1, x + 5, y + 5);
  290.   line (x + 5, y + 1, x + 1, y + 5);
  291. }
  292.  
  293. static void Rectangle (int x, int y, int Color)
  294. {
  295.   x = x * CellSize + StartMazeX + Offset;
  296.   y = y * CellSize + StartMazeY + Offset;
  297.   setcolor (Color);
  298.  
  299.   //
  300.   //   Draw a rectangle around the edges of the cell in the given color.
  301.   //
  302.   rectangle (x, y, x + 6, y + 6);
  303. }
  304.  
  305. static void Arrow (int Direction, int x, int y, int Color)
  306. {
  307.   x = x * CellSize + StartMazeX + Offset;
  308.   y = y * CellSize + StartMazeY + Offset;
  309.   setcolor (Color);
  310.  
  311.   //
  312.   //   Draw the arrow as a series of three lines.
  313.   //
  314.   switch (Direction)
  315.   {
  316.     case 0:
  317.       line (x + 3, y + 6, x + 3, y);
  318.       line (x + 3, y,     x,     y + 3);
  319.       line (x + 3, y,     x + 6, y + 3);
  320.       break;
  321.     case 1:
  322.       line (x,     y + 6, x + 6, y);
  323.       line (x + 6, y,     x + 3, y);
  324.       line (x + 6, y,     x + 6, y + 3);
  325.       break;
  326.     case 2:
  327.       line (x,     y + 3, x + 6, y + 3);
  328.       line (x + 6, y + 3, x + 3, y);
  329.       line (x + 6, y + 3, x + 3, y + 6);
  330.       break;
  331.     case 3:
  332.       line (x,     y,     x + 6, y + 6);
  333.       line (x + 6, y + 6, x + 3, y + 6);
  334.       line (x + 6, y + 6, x + 6, y + 3);
  335.       break;
  336.     case 4:
  337.       line (x + 3, y,     x + 3, y + 6);
  338.       line (x + 3, y + 6, x,     y + 3);
  339.       line (x + 3, y + 6, x + 6, y + 3);
  340.       break;
  341.     case 5:
  342.       line (x + 6, y,     x,     y + 6);
  343.       line (x,     y + 6, x + 3, y + 6);
  344.       line (x,     y + 6, x,     y + 3);
  345.       break;
  346.     case 6:
  347.       line (x + 6, y + 3, x,     y + 3);
  348.       line (x,     y + 3, x + 3, y);
  349.       line (x,     y + 3, x + 3, y + 6);
  350.       break;
  351.     case 7:
  352.       line (x + 6, y + 6, x,     y);
  353.       line (x,     y,     x + 3, y);
  354.       line (x,     y,     x,     y + 3);
  355.       break;
  356.   }
  357. }
  358.  
  359. static void Text (int x, int y, int Color, char *Text)
  360. {
  361.   x = x * CellSize + StartMazeX + Offset;
  362.   y = y * CellSize + StartMazeY + Offset;
  363.   setcolor (Color);
  364.  
  365.   //
  366.   //   Display the text in the cell.
  367.   //
  368.   outtextxy (x, y, Text);
  369. }
  370.  
  371. static void Cell (int x, int y, LegendRec *Legend)
  372. {
  373.   //
  374.   //   If no legend entry is specified, output a black box.
  375.   //
  376.   if (Legend == NULL)
  377.   {
  378.     Box (cSolid, x, y, 0);
  379.     return;
  380.   }
  381.  
  382.   //
  383.   //   At this point a representation is specified. Now figure out how to
  384.   //   draw the cell and do it.
  385.   //
  386.   int OuterColor = HexValue (Legend->Representation [0]);
  387.   int InnerColor = HexValue (Legend->Representation [1]);
  388.  
  389.   switch (Legend->Representation [2])
  390.   {
  391.     case '0':
  392.       Text (x, y, OuterColor, Legend->Representation+3);
  393.       break;
  394.     case '1':
  395.       Box (cSolid, x, y, OuterColor);
  396.       break;
  397.     case '2':
  398.       Box (cCheckered, x, y, OuterColor);
  399.       break;
  400.     case '3':
  401.       Box (cSolid, x, y, OuterColor);
  402.       Dot (2, x, y, InnerColor);
  403.       break;
  404.     case '4':
  405.       Box (cCheckered, x, y, OuterColor);
  406.       Dot (2, x, y, InnerColor);
  407.       break;
  408.     case '5':
  409.       Circle (x, y, OuterColor, InnerColor);
  410.       break;
  411.     case '6':
  412.       Door (cHorizontal, x, y, OuterColor);
  413.       break;
  414.     case '7':
  415.       Door (cVertical, x, y, OuterColor);
  416.       break;
  417.     case '8':
  418.       SplitBox (x, y, OuterColor, InnerColor);
  419.       break;
  420.     case '9':
  421.       Dot (1, x, y, OuterColor);
  422.       break;
  423.     case 'a':
  424.       Dot (2, x, y, OuterColor);
  425.       break;
  426.     case 'b':
  427.       Dot (3, x, y, OuterColor);
  428.       break;
  429.     case 'c':
  430.       Dot (4, x, y, OuterColor);
  431.       break;
  432.     case 'd':
  433.       Cross (x, y, OuterColor);
  434.       break;
  435.     case 'e':
  436.       Rectangle (x, y, OuterColor);
  437.       break;
  438.     case 'f':
  439. //      Arrow (HexValue (Legend->Representation [3]), x, y, OuterColor);
  440.       Arrow (InnerColor, x, y, OuterColor);
  441.       break;
  442.   }
  443. }
  444.  
  445. static void ClearLine (int Line)
  446. {
  447.   setfillstyle (SOLID_FILL, 0);
  448.   bar (StartMazeX + Offset, Line, EndMazeX - Offset, Line + 7);
  449. }
  450.  
  451. static void LegendLine (int y, LegendRec **LegendList, unsigned Value)
  452. {
  453.   char Buffer [30];
  454.  
  455.   //
  456.   //   Place the legend and determine the legend text. When a NULL pointer
  457.   //   has been supplied, make a default text.
  458.   //
  459.   if (LegendList [Value] == NULL)
  460.   {
  461.     sprintf (Buffer, " - Unknown %04X", Value);
  462.   }
  463.   else
  464.   {
  465.     sprintf (Buffer, " - %s", LegendList [Value]->Name);
  466.     Cell (66, y, LegendList [Value]);
  467.   }
  468.  
  469.   //
  470.   //   Place the legend text.
  471.   //
  472.   Text (67, y, 15, Buffer);
  473. }
  474.  
  475. static void DisplayLegend (LegendRec **LegendList, unsigned First, unsigned Last)
  476. {
  477.   //
  478.   //   Erase the legend list.
  479.   //
  480.   setfillstyle (SOLID_FILL, 0);
  481.   bar (StartLegendX + 2, EndChoiseY + 3, EndLegendX - 2, StartCommY - 3);
  482.  
  483.   //
  484.   //   Display the legend, one line at a time.
  485.   //
  486.   unsigned Line = 4;
  487.  
  488.   while (First <= Last)
  489.   {
  490.     LegendLine (Line, LegendList, First);
  491.     First++;
  492.     Line += 2;
  493.   }
  494. }
  495.  
  496. static void DisplayChoise (LegendRec **LegendList, unsigned Value)
  497. {
  498.   //
  499.   //   Erase any previous legend.
  500.   //
  501.   setfillstyle (SOLID_FILL, 0);
  502.   bar (StartChoiseX, StartChoiseY, EndChoiseX, EndChoiseY);
  503.  
  504.   //
  505.   //   Place the legend in the choise area.
  506.   //
  507.   LegendLine (1, LegendList, Value);
  508. }
  509.  
  510. static void CellPosition (int Line, int x, int y)
  511. {
  512.   //
  513.   //   Erase the old position information.
  514.   //
  515.   setfillstyle (SOLID_FILL, 0);
  516.   bar (StartMazeX + Offset, Line, StartMazeX + Offset + 49, Line + 7);
  517.  
  518.   //
  519.   //   Write the new position information.
  520.   //
  521.   char Buffer [30];
  522.   setcolor (15);
  523.   sprintf (Buffer, "(%02d,%02d)", x, y);
  524.   outtextxy (StartMazeX + Offset, Line, Buffer);
  525. }
  526.  
  527. static void LegendInfo (int Line, unsigned Value, LegendRec *Legend, char *Leader)
  528. {
  529.   //
  530.   //   Erase the old legend information.
  531.   //
  532.   setfillstyle (SOLID_FILL, 0);
  533.   bar (StartMazeX + Offset + 80, Line, EndMazeX - Offset, Line + 7);
  534.  
  535.   //
  536.   //   Write the new legend information.
  537.   //
  538.   char Buffer [50];
  539.   setcolor (15);
  540.   sprintf (Buffer, "%s %04X - %s", Leader, Value, Legend == NULL ? "Unknown" : Legend->Name);
  541.   outtextxy (StartMazeX + Offset + 80, Line, Buffer);
  542. }
  543.  
  544. static int LoadLegend (char *DefFile, unsigned MaxLegends, LegendRec **Legends)
  545. {
  546.   //
  547.   //   Try opening the specified definition file. Return an error
  548.   //   immediately upon failure.
  549.   //
  550.   FILE *Stream = fopen (DefFile, "r");
  551.   if (Stream == NULL) return errNoDefFile;
  552.  
  553.   char     Name [25];
  554.   char     Repr [5];
  555.   unsigned Value;
  556.   int      Error  = errOk;
  557.  
  558.   //
  559.   //   Read every line from the file, one line at a time.
  560.   //
  561.   while (Error == errOk && !feof (Stream) )
  562.   {
  563.     char Buffer [80];
  564.  
  565.     //
  566.     //   Read and process the line if the line is not empty.
  567.     //
  568.     if (fgets (Buffer, 79, Stream) != NULL && *Buffer != '\0')
  569.     {
  570.       int Result = sscanf (Buffer, "%4x %4s %24[ -9A-Za-z]", &Value, &Repr, &Name);
  571.  
  572.       //
  573.       //   Store the result if the line contained the correct components.
  574.       //
  575.       if (Result == 3 && Value < MaxLegends)
  576.       {
  577.         Legends [Value] = (LegendRec *) malloc ( sizeof (LegendRec) );
  578.         if (Legends [Value] != NULL)
  579.         {
  580.           strcpy (Legends [Value]->Representation, Repr);
  581.           strcpy (Legends [Value]->Name, Name);
  582.         }
  583.         else
  584.         {
  585.           Error = errNoMemory;
  586.         }
  587.       }
  588.       else
  589.       {
  590.         Error = errIllegalLegend;
  591.       }
  592.     }
  593.   }
  594.  
  595.   //
  596.   //   Close the definition file and return the error state.
  597.   //
  598.   fclose (Stream);
  599.   return Error;
  600. }
  601.  
  602. /*
  603.  
  604. ---------------------------------------------------------------------------
  605. | Public functions to implement the desired API.                          |
  606. ---------------------------------------------------------------------------
  607. | Function name               | Description.                              |
  608. ---------------------------------------------------------------------------
  609.  
  610. */
  611.  
  612. GraphRec::GraphRec ()
  613. {
  614.   //
  615.   //   Initialize the private fields.
  616.   //
  617.   Maze         = NULL;
  618.   Objects      = NULL;
  619.   MazeChoise   = 0;
  620.   ObjectChoise = 0;
  621.   Legend       = cMazeLegend;
  622.   ChoiseLegend = cMazeLegend;
  623.   MazeFirst    = 0;
  624.   ObjectsFirst = 0;
  625.   ShowFloors   = cFalse;
  626.   ShowObjects  = cTrue;
  627.   SizeX        = 0;
  628.   SizeY        = 0;
  629.   StartTextY   = 0;
  630. }
  631.  
  632. GraphRec::~GraphRec ()
  633. {
  634.   Close ();
  635. }
  636.  
  637. int GraphRec::Open ()
  638. {
  639.   //
  640.   //   Allocate memory for the pointers to the individual legend records.
  641.   //
  642.   Maze    = (LegendRec **) calloc (cMaxMazeLegends, sizeof (LegendRec) );
  643.   Objects = (LegendRec **) calloc (cMaxObjectLegends, sizeof (LegendRec) );
  644.  
  645.   int Error = Maze != NULL && Objects != NULL ? errOk : errNoMemory;
  646.  
  647.   //
  648.   //   Try loading the legends from the definition files.
  649.   //
  650.   if (Error == errOk) Error = LoadLegend ("mapdata.def", cMaxMazeLegends, Maze);
  651.   if (Error == errOk) Error = LoadLegend ("objdata.def", cMaxObjectLegends, Objects);
  652.  
  653.   //
  654.   //   For display or editing we use the VGA 640x480 screen mode. Normally,
  655.   //   this should not cause any problems, since Wolf3D itself also requires
  656.   //   a VGA system.
  657.   //
  658.   if (Error == errOk)
  659.   {
  660.     int GraphDevice = VGA;
  661.     int GraphMode   = VGAHI;
  662.  
  663.     initgraph (&GraphDevice, &GraphMode, "");
  664.     if (GraphDevice != VGA) Error = errInitializeGraph;
  665.   }
  666.  
  667.   //
  668.   //   If the graphics device has been opened, draw the legend on the
  669.   //   screen.
  670.   //
  671.   if (Error == errOk)
  672.   {
  673.     //
  674.     //   Initialize the mouse and place the button texts.
  675.     //
  676.     MouseReset ();
  677.     setcolor (15);
  678.     settextstyle (DEFAULT_FONT, HORIZ_DIR, 1);
  679.     outtextxy (StartMapX + 12, StartCommY + 9, "MAP  OBJ  UP  DOWN");
  680.  
  681.     //
  682.     //   Draw the borders around the legend and the buttons.
  683.     //
  684.     setcolor (7);
  685.     rectangle (StartLegendX, StartLegendY, EndLegendX, EndLegendY);
  686.     rectangle (StartLegendX + 1, StartLegendY + 1, EndLegendX - 1, EndLegendY - 1);
  687.     line (StartLegendX + 2, StartCommY - 2, EndLegendX - 2, StartCommY - 2);
  688.     line (StartLegendX + 2, StartCommY - 1, EndLegendX - 2, StartCommY - 1);
  689.     line (StartLegendX + 2, EndChoiseY + 1, EndLegendX - 2, EndChoiseY + 1);
  690.     line (StartLegendX + 2, EndChoiseY + 2, EndLegendX - 2, EndChoiseY + 2);
  691.  
  692.     line (EndMapX    + 1, StartCommY, EndMapX    + 1, EndCommY);
  693.     line (StartObjX  - 1, StartCommY, StartObjX  - 1, EndCommY);
  694.     line (EndObjX    + 1, StartCommY, EndObjX    + 1, EndCommY);
  695.     line (StartUpX   - 1, StartCommY, StartUpX   - 1, EndCommY);
  696.     line (EndUpX     + 1, StartCommY, EndUpX     + 1, EndCommY);
  697.     line (StartDownX - 1, StartCommY, StartDownX - 1, EndCommY);
  698.  
  699.     //
  700.     //   Now place the initial legend list and choise in the proper boxes.
  701.     //
  702.     DisplayMazeLegend ();
  703.     DisplayChoise (Maze, MazeChoise);
  704.   }
  705.  
  706.   //
  707.   //   Return the error state.
  708.   //
  709.   return Error;
  710. }
  711.  
  712. int GraphRec::Close ()
  713. {
  714.   //
  715.   //   Close the graphics screen and restore the origional screen mode.
  716.   //
  717.   closegraph ();
  718.  
  719.   //
  720.   //   Deallocate any maze legends.
  721.   //
  722.   if (Maze != NULL)
  723.   {
  724.     int Count = 0;
  725.     while (Count < cMaxMazeLegends)
  726.     {
  727.       if (Maze [Count] != NULL) free (Maze [Count]);
  728.       Count++;
  729.     }
  730.  
  731.     free (Maze);
  732.     Maze = NULL;
  733.   }
  734.  
  735.   //
  736.   //   Deallocate any object legends.
  737.   //
  738.   if (Objects != NULL)
  739.   {
  740.     int Count = 0;
  741.     while (Count < cMaxObjectLegends)
  742.     {
  743.       if (Objects [Count] != NULL) free (Objects [Count]);
  744.       Count++;
  745.     }
  746.  
  747.     free (Objects);
  748.     Objects = NULL;
  749.   }
  750.  
  751.   //
  752.   //   Return Ok to indicate success.
  753.   //
  754.   return errOk;
  755. }
  756.  
  757. void GraphRec::DisplayCell (int x, int y, unsigned MazeValue, unsigned ObjectValue)
  758. {
  759.   //
  760.   //   Output the maze representation of this cell.
  761.   //
  762.   if (MazeValue != 0 && (ShowFloors || DetermineMaze (MazeValue) != mFloor))
  763.   {
  764.     Cell (x, y, Maze [MazeValue]);
  765.   }
  766.  
  767.   //
  768.   //   Output the object representation of this cell.
  769.   //
  770.   if (ObjectValue != 0 && (ShowObjects || DetermineObject (ObjectValue) == oSecretDoor))
  771.   {
  772.     Cell (x, y, Objects [ObjectValue]);
  773.   }
  774. }
  775.  
  776. void GraphRec::DisplayMap (GameMapRec *Map)
  777. {
  778.   //
  779.   //   Erase the old map.
  780.   //
  781.   int InnerStartX = StartMazeX + 2;
  782.   int InnerStartY = StartMazeY + 2;
  783.   int InnerEndX   = StartMazeX + SizeX*CellSize + 2*Offset - 3;
  784.   int InnerEndY   = StartMazeY + SizeY*CellSize + 2*Offset - 3;
  785.  
  786.   setfillstyle (SOLID_FILL, 0);
  787.   bar (InnerStartX, InnerStartY, InnerEndX, InnerEndY);
  788.  
  789.   //
  790.   //   Initialize the pointers.
  791.   //
  792.   unsigned *MazePtr   = Map->MazeBlock ();
  793.   unsigned *ObjectPtr = Map->ObjectsBlock ();
  794.   unsigned  PosY      = 0;
  795.  
  796.   //
  797.   //   Walk the entire maze, one row at a time.
  798.   //
  799.   while (PosY < SizeY)
  800.   {
  801.     //
  802.     //   Walk every cell in a row, one cell at a time.
  803.     //
  804.     unsigned PosX = 0;
  805.  
  806.     while (PosX < SizeX)
  807.     {
  808.       DisplayCell (PosX++, PosY, *(MazePtr++), *(ObjectPtr++));
  809.     }
  810.  
  811.     //
  812.     //   Advance to the next row.
  813.     //
  814.     PosY++;
  815.   }
  816. }
  817.  
  818. void GraphRec::DisplayMazeLegend ()
  819. {
  820.   DisplayLegend (Maze, MazeFirst, Min (MazeFirst+cMaxDisplayLegend, cMaxMazeLegends)-1);
  821. }
  822.  
  823. void GraphRec::DisplayObjectLegend ()
  824. {
  825.   DisplayLegend (Objects, ObjectsFirst, Min (ObjectsFirst+cMaxDisplayLegend, cMaxObjectLegends)-1);
  826. }
  827.  
  828. int GraphRec::DetermineArea (int x, int y, int *ResultX, int *ResultY)
  829. {
  830.   //
  831.   //   Determine if the area is one of the four buttons.
  832.   //
  833.   if ( InRange (x, y, StartMapX,  StartCommY, EndMapX,  EndCommY) ) return aMapButton;
  834.   if ( InRange (x, y, StartObjX,  StartCommY, EndObjX,  EndCommY) ) return aObjectButton;
  835.   if ( InRange (x, y, StartUpX,   StartCommY, EndUpX,   EndCommY) ) return aUpButton;
  836.   if ( InRange (x, y, StartDownX, StartCommY, EndDownX, EndCommY) ) return aDownButton;
  837.  
  838.   //
  839.   //   Determine if the area is the map itself. If so, calculate the cell
  840.   //   coordinates.
  841.   //
  842.   int x1 = StartMazeX + Offset;
  843.   int y1 = StartMazeY + Offset;
  844.   int x2 = x1 + SizeX * CellSize - 1;
  845.   int y2 = y1 + SizeY * CellSize - 1;
  846.  
  847.   if ( InRange (x, y, x1, y1, x2, y2) )
  848.   {
  849.     *ResultX = (x - x1) / CellSize;
  850.     *ResultY = (y - y1) / CellSize;
  851.     return aMap;
  852.   }
  853.  
  854.   //
  855.   //   Determine if the area is the legend list. If so, calculate the line
  856.   //   number.
  857.   //
  858.   x1 = StartLegendX + 2;
  859.   y1 = EndChoiseY   + 3;
  860.   x2 = EndLegendX   - 2;
  861.   y2 = StartCommY   - 3;
  862.  
  863.   if ( InRange (x, y, x1, y1, x2, y2) )
  864.   {
  865.     int Line = (y - (StartMazeY+Offset)) / CellSize;
  866.  
  867.     if (Line >= 4 && (Line & 1) == 0)
  868.     {
  869.       *ResultY = (Line - 4) >> 1;
  870.       return aLegend;
  871.     }
  872.   }
  873.  
  874.   //
  875.   //  At this point no legal area has been recognized, so return None.
  876.   //
  877.   return aNone;
  878. }
  879.  
  880. int GraphRec::Display (GameMapRec *Map, int EpisodeNr, int MapNr)
  881. {
  882.   //
  883.   //   If the sizes are different from the stored sizes, we have to (re)draw
  884.   //   our map border.
  885.   //
  886.   if (Map->MazeSizeX () != SizeX || Map->MazeSizeY () == SizeY)
  887.   {
  888.     setfillstyle (SOLID_FILL, 0);
  889.     setcolor (7);
  890.  
  891.     //
  892.     //   Calculate the new map area and use these figures to draw the
  893.     //   rectangle and to erase any surplus areas.
  894.     //
  895.     int InnerStartX = StartMazeX + 2;
  896.     int InnerStartY = StartMazeY + 2;
  897.     int InnerEndX   = StartMazeX + Map->MazeSizeX ()*CellSize + 2*Offset - 3;
  898.     int InnerEndY   = StartMazeY + Map->MazeSizeY ()*CellSize + 2*Offset - 3;
  899.  
  900.     //
  901.     //   First draw the new border.
  902.     //
  903.     rectangle (InnerStartX - 2, InnerStartY - 2, InnerEndX + 2, InnerEndY + 2);
  904.     rectangle (InnerStartX - 1, InnerStartY - 1, InnerEndX + 1, InnerEndY + 1);
  905.  
  906.     //
  907.     //   Next, check if we have some erasing to do. This is the case if the
  908.     //   old sizes were larger than the new ones.
  909.     //
  910.     if (SizeX < Map->MazeSizeX () )
  911.     {
  912.       bar (InnerEndX + 3, StartMazeY, EndMazeX, EndMazeY);
  913.     }
  914.  
  915.     if (SizeY < Map->MazeSizeY () )
  916.     {
  917.       bar (StartMazeX, InnerEndY + 3, EndMazeX, EndMazeY);
  918.     }
  919.  
  920.     //
  921.     //   Finally, store the new sizes for future use.
  922.     //
  923.     SizeX      = Map->MazeSizeX ();
  924.     SizeY      = Map->MazeSizeY ();
  925.     StartTextY = StartMazeY + SizeY*CellSize + 2*Offset + 2;
  926.   }
  927.  
  928.   //
  929.   //   Display the current episode and map numbers, and the map title.
  930.   //
  931.   char Buffer [30];
  932.   setfillstyle (SOLID_FILL, 0);
  933.   bar (StartLevelX, StartLevelY, EndLevelX, EndLevelY);
  934.  
  935.   sprintf (Buffer, "Episode: %d", EpisodeNr+1);
  936.   Text (66, 59, 15, Buffer);
  937.  
  938.   sprintf (Buffer, "Level:   %d", MapNr);
  939.   Text (66, 61, 15, Buffer);
  940.  
  941.   Text (66, 63, 15, Map->Title () );
  942.  
  943.   //
  944.   //   Now display the map itself.
  945.   //
  946.   DisplayMap (Map);
  947.  
  948.   //
  949.   //   Return Ok to indicate success.
  950.   //
  951.   return errOk;
  952. }
  953.  
  954. int GraphRec::Modify (GameMapRec *Map, int *Changed, int *Command)
  955. {
  956.   //
  957.   //   Initialize tracking information.
  958.   //
  959.   unsigned OldObject = 0;
  960.   unsigned OldMaze   = 0;
  961.   int      OldX      = 0;
  962.   int      OldY      = 0;
  963.   int      OldArea   = aNone;
  964.   int      Drawing   = cFalse;
  965.  
  966.   //
  967.   //   Reset the change indicator.
  968.   //
  969.   *Changed = cFalse;
  970.   *Command = cNone;
  971.  
  972.   //
  973.   //   Process mouse actions and key strokes until the editing of this map
  974.   //   is finished.
  975.   //
  976.   MouseShow ();
  977.   while (*Command == cNone)
  978.   {
  979.     //
  980.     //   Process mouse actions.
  981.     //
  982.     unsigned Buttons;
  983.     int      PosX;
  984.     int      PosY;
  985.     MousePos (&PosX, &PosY, &Buttons);
  986.  
  987.     //
  988.     //   Determine the current area the mouse is in and calculate the
  989.     //   logical coordinates within that area, if needed.
  990.     //
  991.     int ResultX;
  992.     int ResultY;
  993.     int Area = DetermineArea (PosX, PosY, &ResultX, &ResultY);
  994.  
  995.     //
  996.     //   Display cell information, if the mouse is inside the maze display.
  997.     //   Otherwise, remove the cell information.
  998.     //
  999.     if (Area == aMap)
  1000.     {
  1001.       //
  1002.       //   Retrieve the maze/object values for the current cell.
  1003.       //
  1004.       unsigned MazeValue   = Map->MazeBlock ()    [ResultY*SizeX + ResultX];
  1005.       unsigned ObjectValue = Map->ObjectsBlock () [ResultY*SizeX + ResultX];
  1006.  
  1007.       //
  1008.       //   Only process if there are changes.
  1009.       //
  1010.       if (Area != OldArea || ResultX != OldX || ResultY != OldY || MazeValue != OldMaze || ObjectValue != OldObject)
  1011.       {
  1012.         MouseHide ();
  1013.  
  1014.         //
  1015.         //   Show current position.
  1016.         //
  1017.         if (Area != OldArea || ResultX != OldX || ResultY != OldY)
  1018.         {
  1019.           CellPosition (StartTextY, ResultX, ResultY);
  1020.           OldX = ResultX;
  1021.           OldY = ResultY;
  1022.         }
  1023.  
  1024.         //
  1025.         //   Show maze legend of current cell.
  1026.         //
  1027.         if (Area != OldArea || OldMaze != MazeValue)
  1028.         {
  1029.           LegendInfo (StartTextY, MazeValue, Maze [MazeValue], "Map:   ");
  1030.           OldMaze = MazeValue;
  1031.         }
  1032.  
  1033.         //
  1034.         //   Show object legend of current cell.
  1035.         //
  1036.         if (Area != OldArea || OldObject != ObjectValue)
  1037.         {
  1038.           LegendInfo (StartTextY + 10, ObjectValue, Objects [ObjectValue], "Object:");
  1039.           OldObject = ObjectValue;
  1040.         }
  1041.  
  1042.         MouseShow ();
  1043.       }
  1044.     }
  1045.     else
  1046.     {
  1047.       //
  1048.       //   Since the mouse is outside the maze area, hide the information.
  1049.       //
  1050.       if (OldArea == aMap)
  1051.       {
  1052.         MouseHide ();
  1053.         ClearLine (StartTextY);
  1054.         ClearLine (StartTextY + 10);
  1055.         MouseShow ();
  1056.       }
  1057.     }
  1058.     OldArea = Area;
  1059.  
  1060.     //
  1061.     //   Process the area if a mouse button has been pressed.
  1062.     //
  1063.     if (Buttons != 0 && !Drawing)
  1064.     {
  1065.       //
  1066.       //   First wait the buttons to get released, but only if we don't have
  1067.       //   a button press inside the map area. This enables drag and fill.
  1068.       //
  1069.       if (Area != aMap && !Drawing)
  1070.       {
  1071.         while (Buttons != 0) MousePos (&PosX, &PosY, &Buttons);
  1072.       }
  1073.  
  1074.       //
  1075.       //   Now process the area, which can be interpreted as a command.
  1076.       //
  1077.       if (Area != aNone)
  1078.       {
  1079.         switch (Area)
  1080.         {
  1081.           case aMapButton:
  1082.             if (Legend != cMazeLegend)
  1083.             {
  1084.               MouseHide ();
  1085.               DisplayMazeLegend ();
  1086.               Legend = cMazeLegend;
  1087.               MouseShow ();
  1088.             }
  1089.             break;
  1090.  
  1091.           case aObjectButton:
  1092.             if (Legend != cObjectLegend)
  1093.             {
  1094.               MouseHide ();
  1095.               DisplayObjectLegend ();
  1096.               Legend = cObjectLegend;
  1097.               MouseShow ();
  1098.             }
  1099.             break;
  1100.  
  1101.           case aUpButton:
  1102.             MouseHide ();
  1103.             if (Legend == cMazeLegend)
  1104.             {
  1105.               if (MazeFirst > 0)
  1106.               {
  1107.                 MazeFirst -= cMaxDisplayLegend;
  1108.                 DisplayMazeLegend ();
  1109.               }
  1110.             }
  1111.             else
  1112.             {
  1113.               if (ObjectsFirst > 0)
  1114.               {
  1115.                 ObjectsFirst -= cMaxDisplayLegend;
  1116.                 DisplayObjectLegend ();
  1117.               }
  1118.             }
  1119.             MouseShow ();
  1120.             break;
  1121.  
  1122.           case aDownButton:
  1123.             MouseHide ();
  1124.             if (Legend == cMazeLegend)
  1125.             {
  1126.               if (MazeFirst+cMaxDisplayLegend < cMaxMazeLegends)
  1127.               {
  1128.                 MazeFirst += cMaxDisplayLegend;
  1129.                 DisplayMazeLegend ();
  1130.               }
  1131.             }
  1132.             else
  1133.             {
  1134.               if (ObjectsFirst+cMaxDisplayLegend < cMaxObjectLegends)
  1135.               {
  1136.                 ObjectsFirst += cMaxDisplayLegend;
  1137.                 DisplayObjectLegend ();
  1138.               }
  1139.             }
  1140.             MouseShow ();
  1141.             break;
  1142.  
  1143.           case aLegend:
  1144.             MouseHide ();
  1145.             if (Legend == cMazeLegend)
  1146.             {
  1147.               MazeChoise   = ResultY + MazeFirst;
  1148.               ChoiseLegend = cMazeLegend;
  1149.               DisplayChoise (Maze, MazeChoise);
  1150.             }
  1151.             else
  1152.             {
  1153.               ObjectChoise = ResultY + ObjectsFirst;
  1154.               ChoiseLegend = cObjectLegend;
  1155.               DisplayChoise (Objects, ObjectChoise);
  1156.             }
  1157.             MouseShow ();
  1158.             break;
  1159.  
  1160.           case aMap:
  1161.             Drawing = cTrue;
  1162.             break;
  1163.         }
  1164.       }
  1165.     }
  1166.  
  1167.     //
  1168.     //   Cancel the drawing state when the mouse buttons are released.
  1169.     //
  1170.     if (Buttons == 0)
  1171.     {
  1172.       Drawing = cFalse;
  1173.     }
  1174.  
  1175.     //
  1176.     //   If we are drawing inside the map area, update the current cell to
  1177.     //   the selected legend value.
  1178.     //
  1179.     if (Drawing && Area == aMap)
  1180.     {
  1181.       //
  1182.       //   Determine the pointers to the current maze / object value in the
  1183.       //   current cell.
  1184.       //
  1185.       unsigned *MazePtr = Map->MazeBlock ()    + ResultY*SizeX + ResultX;
  1186.       unsigned *ObjPtr  = Map->ObjectsBlock () + ResultY*SizeX + ResultX;
  1187.  
  1188.       //
  1189.       //   If a maze legend value is selected, update the maze. Otherwise
  1190.       //   update the object value of the current cell.
  1191.       //
  1192.       if (ChoiseLegend == cMazeLegend)
  1193.       {
  1194.         if (*MazePtr != MazeChoise)
  1195.         {
  1196.           MouseHide ();
  1197.           *MazePtr = MazeChoise;
  1198.           Box (cSolid, ResultX, ResultY, 0);
  1199.           DisplayCell (ResultX, ResultY, *MazePtr, *ObjPtr);
  1200.           *Changed = cTrue;
  1201.           MouseShow ();
  1202.         }
  1203.       }
  1204.       else
  1205.       {
  1206.         if (*ObjPtr != ObjectChoise)
  1207.         {
  1208.           MouseHide ();
  1209.           *ObjPtr = ObjectChoise;
  1210.           Box (cSolid, ResultX, ResultY, 0);
  1211.           DisplayCell (ResultX, ResultY, *MazePtr, *ObjPtr);
  1212.           *Changed = cTrue;
  1213.           MouseShow ();
  1214.         }
  1215.       }
  1216.     }
  1217.  
  1218.     //
  1219.     //   Process key strokes.
  1220.     //
  1221.     int Key = bioskey (1);
  1222.     if (Key != 0 && Key != -1)
  1223.     {
  1224.       Key = bioskey (0);
  1225.       switch (Key & 0x00FF)
  1226.       {
  1227.         case '0': *Command = cMap0; break;
  1228.         case '1': *Command = cMap1; break;
  1229.         case '2': *Command = cMap2; break;
  1230.         case '3': *Command = cMap3; break;
  1231.         case '4': *Command = cMap4; break;
  1232.         case '5': *Command = cMap5; break;
  1233.         case '6': *Command = cMap6; break;
  1234.         case '7': *Command = cMap7; break;
  1235.         case '8': *Command = cMap8; break;
  1236.         case '9': *Command = cMap9; break;
  1237.         case 'D': *Command = cDiscard; break;
  1238.         case 'S': *Command = cStore; break;
  1239.         case 'e': *Command = cExit; break;
  1240.         case 'q': *Command = cBreak; break;
  1241.         case 27:  *Command = cEscape; break;
  1242.         case 'f':
  1243.           ShowFloors = !ShowFloors;
  1244.           MouseHide ();
  1245.           DisplayMap (Map);
  1246.           MouseShow ();
  1247.           break;
  1248.         case 'o':
  1249.           ShowObjects = !ShowObjects;
  1250.           MouseHide ();
  1251.           DisplayMap (Map);
  1252.           MouseShow ();
  1253.           break;
  1254.         case 'C':
  1255.           Map->Clear ();
  1256.           *Changed = cTrue;
  1257.           MouseHide ();
  1258.           DisplayMap (Map);
  1259.           MouseShow ();
  1260.           break;
  1261.         case 0:
  1262.           switch (Key >> 8)
  1263.           {
  1264.             case 77: *Command = cNextMap; break;
  1265.             case 75: *Command = cPrevMap; break;
  1266.             case 73: *Command = cNextEpisode; break;
  1267.             case 81: *Command = cPrevEpisode; break;
  1268.           }
  1269.           break;
  1270.       }
  1271.     }
  1272.     else
  1273.     {
  1274.       if (Key == -1) *Command = cBreak;
  1275.     }
  1276.   }
  1277.   MouseHide ();
  1278.   ClearLine (StartTextY);
  1279.   ClearLine (StartTextY + 10);
  1280.  
  1281.   //
  1282.   //   Return Ok to indicate success.
  1283.   //
  1284.   return errOk;
  1285. }
  1286.  
  1287.