home *** CD-ROM | disk | FTP | other *** search
/ The World of Computer Software / World_Of_Computer_Software-02-387-Vol-3of3.iso / v / vol1n7.zip / HEXES.C < prev    next >
Text File  |  1992-10-10  |  15KB  |  417 lines

  1. /* HEXES.C - Hex map routines
  2.  
  3. Copyright (c) 1992 Timur Tabi                                                     
  4. Copyright (c) 1992 Fasa Corporation                                               
  5.                                                                                   
  6. The following trademarks are the property of Fasa Corporation:                    
  7. BattleTech, CityTech, AeroTech, MechWarrior, BattleMech, and 'Mech.        
  8. The use of these trademarks should not be construed as a challenge to these marks.
  9.                                                                                   
  10. This module contains all the code pertaining to the hexagonal grid of the 
  11. combat map.  This includes drawing and interpreting mouse input.  Hexes are
  12. identified by a column/row index passed as two integers.  X,Y coordinates
  13. are identified with a POINTL structure.
  14. */
  15.  
  16. #define HEXES_C
  17. #define INCL_DOSPROCESS
  18. #define INCL_GPILOGCOLORTABLE
  19. #define INCL_GPIPRIMITIVES
  20. #include <os2.h>
  21. #include <string.h>
  22. #include <stdlib.h>
  23. #include <math.h>
  24. #include "header.h"
  25. #include "game.h"
  26. #include "target.h"
  27.  
  28. // Array of each vertex in a hexagon, ending with the lower-left corner at relative position (0,0)
  29. POINTL aptlHex[]={ {HEX_SIDE,0},
  30.                    {HEX_SIDE+HEX_EXT,HEX_HEIGHT/2},
  31.                    {HEX_SIDE,HEX_HEIGHT},
  32.                    {0,HEX_HEIGHT},
  33.                    {-HEX_EXT,HEX_HEIGHT/2},
  34.                    {0,0}                         };
  35.  
  36. POINTL HexCoord(HEXINDEX hi) {
  37. /* This function returns the X,Y coordinate of the lower-left vertex for a given hex
  38.    index.
  39. */
  40.    POINTL ptl;
  41.  
  42. // Odd numbered columns are drawn a little to the right of even columns
  43. // HEX_SIDE+HEX_EXT is the X-coordinate of column #1
  44.    if (hi.c & 1)                      
  45.       ptl.x=(LONG) HEX_EXT+(HEX_SIDE+HEX_EXT)+(HEX_SIDE+HEX_DIAM)*(hi.c-1)/2;
  46.    else
  47.       ptl.x=(LONG) HEX_EXT+(HEX_SIDE+HEX_DIAM)*hi.c/2;
  48.    ptl.y=(LONG) hi.r*(HEX_HEIGHT/2);
  49.    return ptl;
  50. }
  51.  
  52. POINTL HexMidpoint(HEXINDEX hi) {
  53. /* This function is identical to HexCoord(), except that it returns the coordinates of
  54.    the midpoint (centerpoint) of the hexagon.
  55. */
  56.    POINTL ptl;
  57.  
  58.    if (hi.c & 1)
  59.       ptl.x=(LONG) (HEX_SIDE/2)+HEX_EXT+(HEX_SIDE+HEX_EXT)+(HEX_SIDE+HEX_DIAM)*(hi.c-1)/2;
  60.    else
  61.       ptl.x=(LONG) (HEX_SIDE/2)+HEX_EXT+(HEX_SIDE+HEX_DIAM)*hi.c/2;
  62.    ptl.y=(LONG) (HEX_HEIGHT/2)+hi.r*(HEX_HEIGHT/2);
  63.    return ptl;
  64. }
  65.  
  66. void HexDraw(HPS hps, HEXINDEX hi) {
  67. /* This function draws the hexagon at index 'hi'.
  68.    Future enhancement: instead of calculating all six x,y coordinates, just relocate origin.
  69. */
  70.   int i;
  71.   POINTL aptl[6];
  72.  
  73.   aptl[5]=HexCoord(hi);                       // Move to the last vertex
  74.   GpiMove(hps,&aptl[5]);
  75.   for (i=0; i<5; i++) {
  76.     aptl[i].x=aptl[5].x+aptlHex[i].x;
  77.     aptl[i].y=aptl[5].y+aptlHex[i].y;
  78.   }
  79.   GpiPolyLine(hps,6L,aptl);                   // Draw all six lines at once
  80. }
  81.  
  82. void HexFillDraw(HEXINDEX hi) {
  83. /* This function is identical to HexDraw() except that it draws the terrain inside the
  84.    hexagon and always uses hpsHex.
  85. */
  86.   GpiSetColor(hpsHex,HexTerrainColor(amap[hi.c][hi.r].iTerrain));
  87.   GpiSetPattern(hpsHex,HexTerrainPattern(amap[hi.c][hi.r].iTerrain));
  88.   GpiBeginArea(hpsHex,BA_NOBOUNDARY);
  89.   HexDraw(hpsHex,hi);
  90.   GpiEndArea(hpsHex);
  91.   GpiSetColor(hpsHex,HEX_COLOR);
  92.   HexDraw(hpsHex,hi);
  93. }
  94.  
  95. BYTE HexTerrainColor(int iTerrain) {
  96. /* This routine returns the color for a given terrain type.  The value returned is 
  97.    a parameter to the GpiSetColor() function.
  98. */
  99.   switch (iTerrain) {
  100.     case IDM_TER_CLEAR_GROUND:  return CLR_BROWN;
  101.     case IDM_TER_ROUGH_GROUND:  return CLR_DARKGRAY;
  102.     case IDM_TER_WATER:         return CLR_DARKBLUE;
  103.     case IDM_TER_LIGHT_WOODS:   return CLR_GREEN;
  104.     case IDM_TER_HEAVY_WOODS:   return CLR_DARKGREEN;
  105.     case IDM_TER_PAVEMENT:      return CLR_PALEGRAY;
  106.     case IDM_TER_BRIDGE:        return CLR_BROWN;
  107.     case IDM_TER_LIGHT_BLDG:
  108.     case IDM_TER_MEDIUM_BLDG:
  109.     case IDM_TER_HEAVY_BLDG:
  110.     case IDM_TER_HARD_BLDG:     return CLR_DARKPINK;
  111.   }
  112.   return 0;
  113. }
  114.  
  115. BYTE HexTerrainPattern(int iTerrain) {
  116. /* This routine returns the pattern for a given terrain type.  The value returned is
  117.    a parameter to the GpiSetPattern() function.
  118. */
  119.   switch (iTerrain) {
  120.     case IDM_TER_CLEAR_GROUND:  return PATSYM_DENSE5;
  121.     case IDM_TER_ROUGH_GROUND:  return PATSYM_DENSE6;
  122.     case IDM_TER_WATER:         return PATSYM_DEFAULT;
  123.     case IDM_TER_HEAVY_WOODS:   return PATSYM_DENSE3;
  124.     case IDM_TER_LIGHT_WOODS:   return PATSYM_DENSE5;
  125.     case IDM_TER_PAVEMENT:      return PATSYM_DEFAULT;
  126.     case IDM_TER_BRIDGE:        return PATSYM_DIAG4;
  127.     case IDM_TER_LIGHT_BLDG:    return PATSYM_DENSE8;
  128.     case IDM_TER_MEDIUM_BLDG:   return PATSYM_DENSE7;
  129.     case IDM_TER_HEAVY_BLDG:    return PATSYM_DENSE6;
  130.     case IDM_TER_HARD_BLDG:     return PATSYM_DENSE5;
  131.   }
  132.   return 0;
  133. }
  134.  
  135. BYTE HexTerrainVisibility(int iTerrain) {
  136. /* This routine returns the visibility for a given terrain type. 
  137.    This is a rudimentary initial version.  The true calculations for visibility are far 
  138.    more complex than this.
  139. */
  140.   switch (iTerrain) {
  141.     case IDM_TER_CLEAR_GROUND:  return 0;
  142.     case IDM_TER_ROUGH_GROUND:  return 0;
  143.     case IDM_TER_WATER:         return 0;
  144.     case IDM_TER_HEAVY_WOODS:   return 3;
  145.     case IDM_TER_LIGHT_WOODS:   return 2;
  146.     case IDM_TER_PAVEMENT:      return 0;
  147.     case IDM_TER_BRIDGE:        return 0;
  148.     case IDM_TER_LIGHT_BLDG:    return 1;
  149.     case IDM_TER_MEDIUM_BLDG:   return 1;
  150.     case IDM_TER_HEAVY_BLDG:    return 1;
  151.     case IDM_TER_HARD_BLDG:     return 1;
  152.   }
  153.   return 0;
  154. }
  155.  
  156. void HexInitMap(void) {
  157. /* This routine sets the default terrain type, configures the map presentation space
  158.    paramters, and initializes the 'amap' array.  Assumes that hpsHex has already been
  159.    initialized.
  160. */
  161.   int c,r;
  162.  
  163.   mapDefault.iTerrain=IDM_TER_CLEAR_GROUND;
  164.   GpiSetBackMix(hpsHex,BM_OVERPAINT);
  165.   for (c=0;c<NUM_COLUMNS;c++)
  166.     for (r=c & 1;r<NUM_ROWS-(c & 1);r+=2)
  167.        amap[c][r]=mapDefault;
  168. }
  169.  
  170. void HexDrawMap(HWND hwnd) {
  171. /* Draws the combat map.
  172.    Future enhancement: draw hex outline grid in one call to GpiPolyLine()
  173. */
  174.   RECTL rcl;
  175.   POINTL ptl={0,0};
  176.   HPS hps;
  177.   HEXINDEX hi;
  178.  
  179.   target.fActive=FALSE;                            // Cancel any targetting
  180.   hps=WinBeginPaint(hwnd,0UL,NULL);
  181.   WinQueryWindowRect(hwnd,&rcl);
  182.   GpiSetBackMix(hps,BM_OVERPAINT);
  183.   GpiSetPattern(hps,HexTerrainPattern(mapDefault.iTerrain));
  184.   GpiSetColor(hps,HexTerrainColor(mapDefault.iTerrain));
  185.   GpiMove(hps,&ptl);
  186.   ptl.x=rcl.xRight;
  187.   ptl.y=rcl.yTop;
  188.   GpiBox(hps,DRO_OUTLINEFILL,&ptl,0,0);            // WinFillRect() doesn't
  189.   WinEndPaint(hps);                                //  support patterns
  190.  
  191.   for (hi.c=0;hi.c<NUM_COLUMNS;hi.c++)
  192.     for (hi.r=hi.c & 1;hi.r<NUM_ROWS-(hi.c & 1);hi.r+=2)
  193.       if (amap[hi.c][hi.r].iTerrain==mapDefault.iTerrain)
  194.         HexDraw(hpsHex,hi);
  195.       else
  196.         HexFillDraw(hi);
  197. }
  198.  
  199. // -----------------------------------------------------------------------
  200. //   Hex Locator routines
  201. // -----------------------------------------------------------------------
  202.  
  203. unsigned int * InitHexLimit(unsigned int uiHeight) {
  204. /* Contributed by: BCL
  205.    This functions initializes the integer array of hexagonal y-deltas.
  206. */
  207.   unsigned int * HexLimit = (unsigned int *) malloc(uiHeight * sizeof(int));
  208.   unsigned int HalfWay = uiHeight / 2;
  209.   unsigned int ndx;
  210.  
  211.   for (ndx=0;ndx <= HalfWay; ndx++) {
  212.     HexLimit[ndx] = ((4 * ndx) + 2) / 7;
  213.     HexLimit[uiHeight - 1 - ndx] = HexLimit[ndx];
  214.   }
  215.   return HexLimit;
  216. }
  217.  
  218. BOOL HexInPoint(POINTL ptl, HEXINDEX hi) {
  219. /* Contributed by: BCL
  220.    This function returns TRUE if the point 'ptl' is inside hex 'hi'.
  221. */
  222.    static unsigned int * Limits = NULL;
  223.    POINTL ptlHex=HexCoord(hi);
  224.    int dy;
  225.  
  226. // Initialize the limit array the first time this function is called
  227.    if (Limits == NULL) Limits = InitHexLimit(HEX_HEIGHT);
  228.  
  229.    if (ptl.y < ptlHex.y) return FALSE;
  230.    if (ptl.y > ptlHex.y+HEX_HEIGHT) return FALSE;
  231. // The point is definitely not within the hexagon's inner rectangle.  
  232. //  Let's try the side triangles.
  233.    dy = ptl.y - ptlHex.y;
  234.    if (ptl.x < ptlHex.x - Limits[dy]) return FALSE;
  235.    if (ptl.x > ptlHex.x+HEX_SIDE + Limits[dy]) return FALSE;
  236.    return TRUE;
  237. }
  238.  
  239. BOOL HexLocate(POINTL ptl, HEXINDEX *phi) {
  240. /* Contributed by: BCL
  241.    This routine identifies the hexagon underneath point ptl.  It returns 
  242.    TRUE if it found one, FALSE otherwise.  *phi is modified only if the 
  243.    function returns TRUE.
  244. */
  245.   HEXINDEX hi;
  246.   int GuessC, GuessR, c, r;
  247.  
  248.   if (ptl.x < HEX_SIDE)
  249.     GuessC = 0;
  250.   else
  251.     GuessC = 2 * (ptl.x - HEX_SIDE) / (3 * HEX_SIDE) + 1;
  252.   GuessR = ptl.y / (HEX_HEIGHT/2);
  253.  
  254.   for (c=-1;c<1;c++)
  255.     for (r=-1;r<1;r++) {
  256.       hi.r = GuessR + r;
  257.       hi.c = GuessC + c;
  258.       if (((hi.r % 2) == (hi.c % 2)) && (hi.r >= 0) && (hi.r < NUM_ROWS) && (hi.c >= 0) && (hi.c < NUM_COLUMNS))
  259.         if (HexInPoint(ptl,hi)) {
  260.           phi->c=hi.c;
  261.           phi->r=hi.r;
  262.           return TRUE;
  263.         }
  264.     }
  265.   return FALSE;
  266. }
  267.  
  268. VOID APIENTRY HexHighlight(ULONG ulThreadArg) {
  269. /* This function changes the color of the origin hex during targetting.  It 
  270.    is started as a background thread and continues until target.fActive
  271.    becomes FALSE.  If there are more than 16 colors, then a routine which cycles
  272.    through 256 shades of red is chosen.  Otherwise, the hex simply blinks red.
  273.    At termination, the color is set back and the hex is redrawn.
  274.  
  275.    For the color cycling, 'i' is a byte because the "i++" statement will
  276.    automatically cycle from 0-255.
  277.  
  278.    At this writing the code for color-cycling has NOT been tested on a
  279.    monitor with 256-colors.  It has been tested on a 16-color monitor and
  280.    looks stupid.
  281.  
  282.    This function belongs in module TARGET.  It will eventually be moved.
  283. */
  284.   BYTE i;        
  285.  
  286.   if (lNumColors>16) {
  287.     GpiCreateLogColorTable(target.hpsHighlight,0,LCOLF_RGB,0,0,NULL);
  288.  
  289.     for (i=0; target.fActive; i++) {
  290.       GpiSetColor(target.hpsHighlight,(LONG) i<<16);
  291.       HexDraw(target.hpsHighlight,target.hiStart);
  292.     }
  293.     GpiCreateLogColorTable(target.hpsHighlight,LCOL_RESET,0,0,0,NULL);
  294.   } else
  295.     while (target.fActive) {
  296.       GpiSetColor(target.hpsHighlight,CLR_BLACK);
  297.       HexDraw(target.hpsHighlight,target.hiStart);
  298.       DosSleep(300L);
  299.       if (!target.fActive) break;
  300.         GpiSetColor(target.hpsHighlight,CLR_RED);
  301.       HexDraw(target.hpsHighlight,target.hiStart);
  302.       DosSleep(300L);
  303.     }
  304.  
  305. // Redraw the starting hex before exiting
  306.   GpiSetColor(target.hpsHighlight,HEX_COLOR);
  307.   HexDraw(target.hpsHighlight,target.hiStart);
  308. }
  309.  
  310. HEXINDEX HexNextShortest(HEXINDEX hiFrom, HEXINDEX hiTo) {
  311. /* Contributed by: BCL
  312.    This routine, given a starting and finishing hex, will calculate the next
  313.    hex along the shortest (in count of hexes) path between them. 
  314. */
  315.   int i;                                              // The change in column
  316.  
  317.   if (hiFrom.c < hiTo.c)
  318.     i = 1;                                            // Move from E to W.
  319.   else
  320.     i = -1;                                           // Move from W to E.
  321.  
  322.   if (hiFrom.c != hiTo.c) {
  323.       hiFrom.c += i;                                  // Advance one col in the appropriate dir.
  324.       if (hiFrom.r < hiTo.r)
  325.         hiFrom.r++;                                   // Target lies N; go N in next column.
  326.       else if (hiFrom.r > hiTo.r)
  327.         hiFrom.r--;                                   // Target lies S; go S in next column.
  328.       else if (hiFrom.r > 0)
  329.         hiFrom.r--;                                   // Target lies E or W; go S in next column
  330.                                                       // since we can (row > 0).
  331.       else
  332.         hiFrom.r++;                                   // Target lies E or W; go N in next column
  333.                                                       // since we can't go S (row = 0).
  334.   } else {
  335.  
  336. // Now that we have the right column, lets match the rows. 
  337.  
  338.     if (hiFrom.r < hiTo.r)
  339.       hiFrom.r += 2;                                  // Move from S to N.
  340.     if (hiFrom.r > hiTo.r)
  341.       hiFrom.r -= 2;                                  // Move from N to S.
  342.   }
  343.  
  344.   return hiFrom;
  345. }
  346.  
  347. // -----------------------------------------------------------------------
  348. //   Targetting-line path routines
  349. // -----------------------------------------------------------------------
  350.  
  351. /* Algorithms for the targetting-line path contributed by CM
  352.  
  353.    The problem with this algorithm is that it is _too_ good.  It catches hexagons
  354.    that, although are theoretically under the trajectory, would not normally be
  355.    counted in a real Battletech game.  Try it, and you'll see what I mean.  I'm
  356.    open to suggestions on this one.
  357. */
  358.  
  359. HEXINDEX HexFromSide(HEXINDEX hi, int iSide) {
  360. /* This function returns the hex index of the hexagon that is bordering on
  361.    side iSide of hexagon hi.
  362. */
  363.   switch (iSide) {
  364.     case 0: hi.c++;         // S.E.
  365.             hi.r--;
  366.             break;
  367.     case 1: hi.c++;         // N.E.
  368.             hi.r++;
  369.             break;
  370.     case 2: hi.r+=2;        // North
  371.             break;
  372.     case 3: hi.c--;         // N.W.
  373.             hi.r++;
  374.             break;
  375.     case 4: hi.c--;         // S.W.
  376.             hi.r--;
  377.             break;
  378.     default:hi.r-=2;        // South
  379.   }
  380.   return hi;
  381. }
  382.  
  383. int HexFirstSide(HEXINDEX hiFrom, HEXINDEX hiTo) {
  384. /* This function returns the side to the first hexagon that follows the trajectory 
  385.    from hiFrom to hiTo.  If the targetting line passes through a vertex, this function
  386.    returns a -1
  387. */
  388.   int dx,dy=hiTo.r - hiFrom.r;
  389.   float m;
  390.  
  391.   if (dy == 0) return -1;
  392.  
  393.   dx=hiTo.c - hiFrom.c;
  394.   if (dx == 0) return (dy>0) ? 2 : 5;           // Vertical line?
  395.  
  396.   m=(float) dy/dx;
  397.   if (fabs(m) == 3.0) return -1;
  398.  
  399.   if (fabs(m)>3) return (dy>0) ? 2 : 5;               // Almost a vertical line?
  400.  
  401.   if (m>0) return (dx>0) ? 1 : 4;
  402.  
  403.   return (dx>0) ? 0 : 3;
  404. }
  405.  
  406. void HexPointFromSide(HEXINDEX hi, int iSide, PPOINTL pptl1, PPOINTL pptl2) {
  407. /* This function returns the x,y coordinates of the two endpoints of side
  408.    iSide of hexagon hi.  Two two points are returned in pptl1 and pptl2
  409. */
  410.    POINTL ptl=HexCoord(hi);      // All coordinates are offsets from this point
  411.  
  412.    pptl1->x=ptl.x+aptlHex[iSide].x;             // Calculate the two endpoints of that side
  413.    pptl1->y=ptl.y+aptlHex[iSide].y;
  414.    pptl2->x=ptl.x+aptlHex[(iSide+1) % 6].x;
  415.    pptl2->y=ptl.y+aptlHex[(iSide+1) % 6].y;
  416. }
  417.