home *** CD-ROM | disk | FTP | other *** search
/ Collection of Education / collectionofeducationcarat1997.iso / GAMES / CAROM10.ZIP / CAROMSRC.ZIP / CAROM.C < prev    next >
C/C++ Source or Header  |  1994-08-14  |  82KB  |  2,570 lines

  1. /*
  2.    Copyright (c) 1994 Csaba Mßrkus. All rights reserved.
  3.    E-mail: ethcms@duna.ericsson.se
  4.    Addr.:  H-9600 Sßrvßr, Szatmßr u. 4, Hungary
  5.  
  6.    Permission to use, copy, modify, and distribute this software and its
  7.    documentation for any purpose, without fee, and without written agreement
  8.    is hereby granted, provided that the above copyright notice and the
  9.    following two paragraphs appear in all copies of this software.
  10.  
  11.    IN NO EVENT SHALL THE AUTHOR BE LIABLE TO ANY PARTY FOR
  12.    DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
  13.    OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE AUTHOR HAS
  14.    BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  15.  
  16.    THE AUTHOR DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  17.    IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
  18.    THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE AUTHOR HAS
  19.    NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS
  20.    OR MODIFICATIONS.
  21. */
  22.  
  23. /* File: CAROM.C
  24.  
  25.    Purpose: Windows program to implement the standard "carom"
  26.    billiard game
  27. */
  28.  
  29. #include <windows.h>
  30. #include <stdio.h>
  31. #include <string.h>
  32. #include <math.h>
  33. #include <time.h>
  34.  
  35. #include "carom.h"
  36. #include "ballfull.h"
  37. #include "trigo.h"
  38.  
  39. #define SPIN_MIN                  -10
  40. #define SPIN_MAX            10
  41. #define POWER_MIN          1
  42. #define POWER_MAX                100
  43. #define POWER_LOW_VALUE        20
  44. #define POWER_MEDIUM_VALUE    50
  45. #define POWER_HIGH_VALUE    80
  46.  
  47. /* Number of restorable views:
  48. */
  49. #define VIEW_STACK_SIZE        10
  50.  
  51. /* Size of the shot history buffer:
  52. */
  53. #define SHOT_BUFFER_SIZE    (128L * 1024L)
  54.  
  55. /* There is a limit of magnification:
  56. */
  57. #define MAX_MAGNIFY 2.0
  58.  
  59. /* Max. length of the help file name:
  60. */
  61. #define EXE_NAME_MAX_SIZE 80
  62.  
  63. /* Macro to clamp a number into a given range */
  64. #define clamp(x, l, h) ((x) < (l) ? (l) : ((x) > (h) ? (h) : (x)))
  65.  
  66. /* This type contains coordinates in millimeter units:
  67. */
  68. typedef struct tagFPOINT {
  69.     float x;            
  70.     float y;
  71. } FPOINT;
  72.  
  73. // data initialized by first instance
  74. typedef struct tagSETUPDATA {
  75.     char   szAppName[20];        // name of application
  76.     char   szParWinName[20];        // name of parameter window
  77. } SETUPDATA;
  78.  
  79. SETUPDATA SetUpData;
  80.  
  81. /* The various states of the game are listed below. At any time, one
  82.    of hese values is stored in the GameState variable.
  83. */
  84. enum game_states {
  85.     BEFORE_GAME = 0,
  86.     AFTER_GAME,
  87.     SETTING_SHOT,
  88.     ZOOMING_IN,
  89.     BALLS_MOVING
  90. };
  91.  
  92. // Data that can be referenced throughout the
  93. // program but not passed to other instances
  94.  
  95. HINSTANCE hInst;                        // hInstance of application
  96. HWND      hwnd;                         // hWnd of main window
  97.  
  98. DLGPROC   lpDlgProc;                    // ptr to proc for dialog box
  99. DLGPROC   lpNewGameDlgProc;        // ptr to proc for new game settings
  100. DLGPROC   lpParDlgProc;                 // ptr to proc for shot parameters
  101. HWND      hParDlg;                      // Handle of Parameters window
  102.  
  103. COLORREF  ScreenColor[10];              // color array
  104.  
  105. #define TITLE_LENGTH 30
  106.  
  107. /* Place global variables here...
  108. */
  109. int prev_horizontal_spin = 0;
  110. int horizontal_spin = 0, horizontal_spin_save;
  111. int prev_vertical_spin = 0;
  112. int vertical_spin = 0, vertical_spin_save;
  113. int shot_power = POWER_MEDIUM_VALUE, shot_power_save;
  114.  
  115. int spin_being_set = FALSE;    /* True if the ball-spin-setting has the capture */
  116. HWND old_capture_ball;        /* Storage of previous capture */
  117. HCURSOR old_cursor_ball;    /* Handle of active cursor when changing to spin-setting */
  118.  
  119. int sight_line_being_set = FALSE;    /* True if sight-line setting is in progress */
  120. HWND old_capture_sight;            /* Storage of previous capture */
  121. HCURSOR old_cursor_sight;        /* Handle of active cursor when changing to zoom cursor */
  122.  
  123. int zoom_being_set;        /* True if zoom-rectangle is being set */
  124. HWND old_capture_zoom;      /* Storage of previous capture at zooming */
  125. HCURSOR old_cursor_zoom;    /* Handle of active cursor when changing to zoom cursor */
  126. POINT zoom_upper_left;        /* Upper left corner of zoom rectangle */
  127. POINT zoom_lower_right;        /* Lower right corner of zoom rectangle */
  128. int zoom_rectangle_present;    /* True if rectangle is already drawn */
  129. int zoom_rectangle_new;        /* True if UpdateZoom should draw new rectangle */
  130.  
  131. int GameState;            /* Current state of the game */
  132.  
  133. int client_width;        /* Width of drawable client area */
  134. int client_height;        /* Height of drawable client area */
  135.  
  136. int first_shot;            /* True if this is going to be the first shot */
  137. int first_shot_save;        /* Storage for undo purposes */
  138.  
  139. /* The following variables contain the currently valid coordinates of the end of
  140.    the sight-line.
  141. */ 
  142. FPOINT sight_line_end;        /* Other end of sight-line */
  143. FPOINT sight_line_end_save;    /* Storage for undo purposes */
  144.  
  145. FPOINT sight_extra[2][2];
  146. FPOINT sight_extra_save[2][2];
  147. FPOINT old_sight_extra[2][2];
  148. int sight_extra_drawn[2] = { 0, 0 };
  149. int sight_extra_drawn_save[2];
  150. int sight_extra_enabled[2] = { 1, 1 };
  151. int sight_extra_present;
  152.  
  153. /* This variable contains the physical coordinates (millimeter units) of the point that
  154.    is located in the upper left corner of the main windows's client area when displayed.
  155. */
  156. FPOINT upper_left;
  157.  
  158. /* Together with the previous upper_left variable, the magnification defines the visible
  159.    part of the carom-table:
  160.    The physical coordinates (p.x; p.y)  are translated to client window coordinates
  161.    (c.x; c;y) with the following formulas:
  162.     c.x = floor((p.x - upper_left.x) * magnify + 0.5);
  163.     c.y = (upper_left.y - p.y) * magnify;
  164.    The other direction of the conversions, according to these formulas, is the following:
  165.     p.x = (c.x / magnify) + upper_left.x;
  166.     p.y = upper_left.y - (c.y / magnify);  
  167. */
  168. float magnify;
  169.  
  170.  
  171. /* How many pixel the radius of the ball is depends only on the current magnification
  172.    value. Therefore, the radius is always computed when the magnification changes;
  173. */
  174. int RADIUS;
  175.  
  176. /* Client window coordinates of spots:
  177. */
  178. int SPOT_X1, SPOT_X2, SPOT_X3;
  179. int SPOT_Y1, SPOT_Y2, SPOT_Y3;
  180.  
  181. /* This variable contains which player is to play:
  182.    0 : First player
  183.    1 : Second player
  184. */
  185. int cueball;
  186. int cueball_save;            /* Storage for undo purposes */
  187.  
  188. /* Stack storage for the view parameters:
  189. */
  190. struct {
  191.     float ulx;
  192.     float uly;
  193.     float mgn;
  194. } view_stack[VIEW_STACK_SIZE];        /* Array to contain numbers */
  195. int vs_next_free = 0;            /* Index of next available place in the array */ 
  196. int view_stack_elements = 0;        /* Number of items already placed in the array */
  197.  
  198. /* Variables needed for displaying, animating the shot:
  199. */
  200. /* Handle of global memory object which stores the history of the last
  201.    shot.
  202. */
  203. HGLOBAL hglbShotHistory;
  204.  
  205. DWORD dwTakeShotTime;            /* System time when shot is taken */
  206. unsigned huge *shot_history;        /* Pointer to shot-history buffer */
  207. unsigned last_displayed_frame;        /* Index of frame last drawn */
  208. UINT globalidTimer1 = 0;        /* Identifier of timer used for shot animation */
  209. UINT globalidTimer2 = 0;        /* ID of other timer used for shot animation */
  210.  
  211. /* Global variables that contain handles of graphics device objects:
  212. */
  213. HRGN     my_client_region;        /* Always contains the (0,0)-(cli_w, cli_h) region */
  214. HRGN    my_inside_table_region;        /* Region where balls can be located */ 
  215. HPEN     my_wood_pen;            /* Pen to draw wooden frame of table */
  216. HBRUSH    my_wood_brush;            /* Brush to draw wooden frame of table */
  217. HPEN    my_cush_pen;            /* Pen to draw cushions */
  218. HBRUSH    my_cush_brush;            /* Brush to draw cushions */
  219. HPEN    my_cloth_pen;            /* Pen to draw a chuck of the cloth beneath balls */
  220. HBRUSH    my_cloth_brush;            /* Brush to draw the cloth */
  221. HPEN    my_rectangle_pen;        /* Pen to draw zoom-rectangle */
  222. HPEN    my_sight_line_pen;        /* Pen to draw the sight line */
  223. HPEN    my_sight_line_extra_pen;    /* Pen to draw the sight line */
  224. HPEN    my_ball_pen[N];            /* Different pens to draw outline of diff. balls */
  225. HBRUSH    my_ball_brush[N];        /* Brushes to draw the interior of balls */
  226. int pens_brushes_present = FALSE;    /* True if pens are already created */
  227.  
  228. /* State variable to indicate whether the path of the balls should be
  229.    kept displayed:
  230. */
  231. int draw_path = CM_PATH_NONE; 
  232.  
  233. /* Storage of information on all the balls, for undo purposes:
  234. */
  235. ball_data ball_save[N];
  236.  
  237. /* Variables that are set at the beginning of a new game:
  238. */
  239. char player1_name[40];
  240. char player2_name[40];
  241. int GameStyle = DL_NG_GAME0;
  242. int WinningScore = 15;
  243. int CushionPoints = TRUE;
  244. int score[2] = { 0, 0};
  245. int score_save[2];
  246.  
  247. int WhoStarts = DL_NG_STARTR;
  248. int WhoStarted;
  249. int EnableUndo = TRUE;
  250. int EnableBreak = TRUE;
  251.  
  252. HBITMAP hbmpTemp = NULL;            /* Ball-sized bitmap for temporary usage */
  253. HBITMAP hbmpBallMask = NULL;            /* Outside white, inside black */
  254. HBITMAP hbmpClothBall = NULL;            /* Outside black, inside cloth colour */
  255. HBITMAP hbmpBall[N] = { NULL, NULL, NULL };    /* Outside black, inside ball[k] colour */
  256.  
  257. /* Help system variables:
  258. */
  259. char szHelpFileName[EXE_NAME_MAX_SIZE+1];    /* Help file name*/
  260.  
  261. /* Flag to indicate that the system's timer should be used
  262.    to animate the shots:
  263. */
  264. int TimeredShotDisplay = TRUE;
  265.  
  266. /* Storage of address of timer procedure:
  267. */
  268. FARPROC lpShotTimerProc;            
  269.  
  270. /* Macro to convert a fixed-point unsigned number to float. Inverse of FIXED(x) in
  271.    the ballfull module.
  272. */
  273. #define UNFIXED(x) (((float) (x)) / FIXED_MULT)
  274.  
  275. /* Here are the conversion macros:
  276. */
  277. #define X_PHYTOCLI(px) floor(((px) - upper_left.x) * magnify + 0.5)
  278. #define Y_PHYTOCLI(py) floor((upper_left.y - (py)) * magnify + 0.5)
  279. #define X_CLITOPHY(cx) (((cx) / magnify) + upper_left.x)
  280. #define Y_CLITOPHY(cy) (upper_left.y - ((cy) / magnify))
  281.  
  282. // function prototypes
  283.  
  284. int PASCAL      WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
  285.             LPSTR lpszCmdLine, int cmdShow);
  286.  
  287. void            Init(HINSTANCE hInstance, HINSTANCE hPrevInstance,
  288.             LPSTR lpszCmdLine, int cmdShow);
  289.  
  290. void InitScrollBar(HWND hDlg, WORD scroll, WORD edit,
  291.            int mi, int ma, int value);
  292.  
  293. LRESULT CALLBACK WndProc(HWND hWnd, UINT message,
  294.              WPARAM wParam, LPARAM lParam);
  295.  
  296. BOOL CALLBACK  DlgBoxProc(HWND hDlg, UINT message,
  297.               WPARAM wParam, LPARAM lParam);
  298.  
  299. BOOL CALLBACK ParDlgBoxProc(HWND hDlg, UINT message,
  300.                 WPARAM wParam, LPARAM lParam);
  301.  
  302. void CALLBACK tprcShotTimerProc(HWND hWnd, UINT msg, UINT idTimer, DWORD dwTime);
  303.  
  304. void wmCreate(void);
  305. void UnCreate(void);
  306. void cmAbout(void);
  307. int cmNewGame(void);
  308. void DoPaint(void);
  309. void DoPaintDlg(HWND hDlg, BOOL fDrawEntire);
  310.  
  311. //*******************************************************************
  312. // WinMain - Carom main
  313. //
  314. // parameters:
  315. //             hInstance     - The instance of this instance of this
  316. //                             application.
  317. //             hPrevInstance - The instance of the previous instance
  318. //                             of this application. This will be 0
  319. //                             if this is the first instance.
  320. //             lpszCmdLine   - A long pointer to the command line that
  321. //                             started this application.
  322. //             cmdShow       - Indicates how the window is to be shown
  323. //                             initially. ie. SW_SHOWNORMAL, SW_HIDE,
  324. //                             SW_MIMIMIZE.
  325. //
  326. // returns:
  327. //             wParam from last message.
  328. //
  329. //*******************************************************************
  330. int PASCAL WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
  331.            LPSTR lpszCmdLine, int cmdShow)
  332. {
  333.     MSG msg;
  334.  
  335.     // Go init this application. Do not allow multiple instances...
  336.     if (hPrevInstance != 0) {
  337.     char msg[100], ttl[50];
  338.  
  339.     LoadString(hPrevInstance, IDS_ONLYONE_TTL, ttl, 50);
  340.     LoadString(hPrevInstance, IDS_ONLYONE_MSG, msg, 100);
  341.     MessageBox(NULL, msg, ttl, MB_OK | MB_ICONEXCLAMATION);
  342.     return 0;
  343.     }
  344.     Init(hInstance, hPrevInstance, lpszCmdLine, cmdShow);
  345.  
  346.     // Get and dispatch messages for this applicaton.
  347.     while (GetMessage(&msg, NULL, 0, 0)) {
  348.     TranslateMessage(&msg);
  349.     DispatchMessage(&msg);
  350.     }  // while
  351.  
  352.     return(msg.wParam);
  353. }  // end of WinMain()
  354.  
  355. /* Re-computes which part of the client area contains the "inside" of the table,
  356.    that is, where balls can be located:
  357. */
  358. void UpdateInsideTableRegion(void)
  359. {
  360.     DeleteObject(my_inside_table_region);
  361.     my_inside_table_region = CreateRectRgn(
  362.     max(0, X_PHYTOCLI(LEFT) + 1),
  363.     max(0, Y_PHYTOCLI(TOP) + 1),
  364.     min(client_width, X_PHYTOCLI(RIGHT) - 1),
  365.     min(client_height, Y_PHYTOCLI(BOTTOM) - 1));
  366. }
  367.  
  368. /* This function updates the radius in pixels according to the current magnification:
  369. */
  370. void UpdateRadius(void)
  371. {
  372.     int D, i;
  373.     HDC hDC;
  374.  
  375.     RADIUS = floor(BALL_RADIUS * magnify + 0.5);
  376.     D = 2 * RADIUS + 1;
  377.  
  378.     /* Update bitmaps associated with the balls.
  379.        Delete them, and then re-create them:
  380.     */
  381.     if (hbmpTemp) {
  382.     DeleteObject(hbmpTemp);
  383.     }
  384.     if (hbmpBallMask) {
  385.     DeleteObject(hbmpBallMask);
  386.     }
  387.     if (hbmpClothBall) {
  388.     DeleteObject(hbmpClothBall);
  389.     }
  390.     for (i = 0; i < N; i++) {
  391.     if (hbmpBall[i]) {
  392.         DeleteObject(hbmpBall[i]);
  393.     }
  394.     }
  395.     hDC = GetDC(hwnd);
  396.     hbmpTemp = CreateCompatibleBitmap(hDC, D, D);
  397.     hbmpBallMask = CreateCompatibleBitmap(hDC, D, D);
  398.     hbmpClothBall = CreateCompatibleBitmap(hDC, D, D);
  399.     for (i = 0; i < N; i++) {
  400.     hbmpBall[i] = CreateCompatibleBitmap(hDC, D, D);
  401.     }
  402.     ReleaseDC(hwnd, hDC);
  403.     hDC = CreateCompatibleDC(NULL);
  404.     SelectObject(hDC, hbmpBallMask);
  405.     SelectObject(hDC, GetStockObject(WHITE_PEN));
  406.     SelectObject(hDC, GetStockObject(WHITE_BRUSH));
  407.     Rectangle(hDC, 0, 0, D, D);
  408.     SelectObject(hDC, GetStockObject(BLACK_PEN));
  409.     SelectObject(hDC, GetStockObject(BLACK_BRUSH));
  410.     Ellipse(hDC, 0, 0, D, D);
  411.     SelectObject(hDC, hbmpClothBall);
  412.     Rectangle(hDC, 0, 0, D, D);
  413.     for (i = 0; i < N; i++) {
  414.     SelectObject(hDC, hbmpBall[i]);
  415.     Rectangle(hDC, 0, 0, D, D);
  416.     }
  417.     SelectObject(hDC, hbmpClothBall);
  418.     SelectObject(hDC, my_cloth_pen);
  419.     SelectObject(hDC, my_cloth_brush);
  420.     Ellipse(hDC, 0, 0, D, D);
  421.     for (i = 0; i < N; i++) {
  422.         SelectObject(hDC, hbmpBall[i]);
  423.     SelectObject(hDC, my_ball_pen[i]);
  424.     SelectObject(hDC, my_ball_brush[i]);
  425.     Ellipse(hDC, 0, 0, D, D);
  426.     }
  427.     DeleteDC(hDC);
  428.  
  429.     /* Re-calculate the spot locations:
  430.     */
  431.     SPOT_X1 = X_PHYTOCLI(BOTTOMM_SPOT_X);
  432.     SPOT_X2 = X_PHYTOCLI(MIDDLE_SPOT_X);
  433.     SPOT_X3 = X_PHYTOCLI(TOP_SPOT_X);
  434.     SPOT_Y1 = Y_PHYTOCLI(BOTTOMR_SPOT_Y);
  435.     SPOT_Y2 = Y_PHYTOCLI(BOTTOMM_SPOT_Y);
  436.     SPOT_Y3 = Y_PHYTOCLI(BOTTOML_SPOT_Y);
  437. }
  438.  
  439. /* The following functions set the viewing parameters.
  440. */
  441.  
  442. /* This function saves the current view-parameters into the view stack.
  443. */
  444. void PushView(void)
  445. {
  446.     view_stack[vs_next_free].ulx = upper_left.x;
  447.     view_stack[vs_next_free].uly = upper_left.y;
  448.     view_stack[vs_next_free].mgn = magnify;
  449.     vs_next_free = (vs_next_free + 1) % VIEW_STACK_SIZE;
  450.     if (view_stack_elements < VIEW_STACK_SIZE) {
  451.     view_stack_elements++;
  452.     }
  453. }
  454.  
  455. /* Inverse of the previous function: Restores the last saved view from the view stack.
  456.    Returns TRUE if successful, FALSE if the view stack is empty.
  457. */
  458. int PopView(void)
  459. {
  460.     if (view_stack_elements == 0) {
  461.     return FALSE;
  462.     }
  463.     vs_next_free = (vs_next_free == 0 ? VIEW_STACK_SIZE - 1 : vs_next_free - 1);
  464.     upper_left.x = view_stack[vs_next_free].ulx;
  465.     upper_left.y = view_stack[vs_next_free].uly;
  466.     magnify = view_stack[vs_next_free].mgn;
  467.     view_stack_elements--;
  468.     UpdateRadius();
  469.     return TRUE;
  470. }
  471.  
  472. void ComputeFullTableParams(void)
  473. {
  474.     float magn_hor, magn_ver;
  475.  
  476.     magn_hor = client_width / (TABLE_LENGTH + 2 * (WOOD_WIDTH + CUSHION_WIDTH));
  477.     magn_ver = client_height / (TABLE_WIDTH + 2 * (WOOD_WIDTH + CUSHION_WIDTH));
  478.     magnify = min(magn_hor, magn_ver);
  479.     upper_left.x = MIDDLE_SPOT_X - (client_width / 2.0) / magnify;
  480.     upper_left.y = MIDDLE_SPOT_Y + (client_height / 2.0) / magnify;
  481.     UpdateRadius();
  482. }
  483.  
  484. void ComputeLeftTableParams(void)
  485. {
  486.     magnify = client_height / TABLE_WIDTH;
  487.     upper_left.x = min(LEFT, MIDDLE_SPOT_X - (client_width / 2.0) / magnify);
  488.     upper_left.y = TOP;
  489.     UpdateRadius();
  490. }
  491.                        
  492. void ComputeRightTableParams(void)
  493. {
  494.     magnify = client_height / TABLE_WIDTH;
  495.     upper_left.x = max(RIGHT - client_width / magnify,
  496.                        MIDDLE_SPOT_X - (client_width / 2.0) / magnify);
  497.     upper_left.y = TOP;
  498.     UpdateRadius();
  499. }
  500.  
  501. void ComputeZoomParams(void)
  502. {
  503.     float magn_hor, magn_ver, new_magnify;
  504.  
  505.     magn_hor = magnify * (client_width / fabs(zoom_lower_right.x - zoom_upper_left.x));
  506.     magn_ver = magnify * (client_height / fabs(zoom_lower_right.y - zoom_upper_left.y));
  507.     new_magnify = min(magn_hor, magn_ver);
  508.     new_magnify = min(new_magnify, MAX_MAGNIFY);
  509.     upper_left.x = X_CLITOPHY((zoom_upper_left.x + zoom_lower_right.x) / 2.0) -
  510.                    (client_width / 2.0) / new_magnify;
  511.     upper_left.y = Y_CLITOPHY((zoom_upper_left.y + zoom_lower_right.y) / 2.0) +
  512.            (client_height / 2.0) / new_magnify;
  513.     magnify = new_magnify;
  514.     UpdateRadius();
  515. }
  516.  
  517. void UpdateZoom(HWND hwnd, POINT FAR *pcurpos)
  518. {
  519.     HDC hDC;                /* Device context of main window */
  520.     HRGN hrgn;                /* Handle of region to be created */
  521.     HPEN old_pen;            /* Handle of old pen */
  522.  
  523.     /* Get device context, only client area is needed:
  524.     */
  525.     hDC = GetDC(hwnd);
  526.  
  527.     SelectObject(hDC, my_client_region);
  528.  
  529.     /* Clear previous rectangle if needed and draw the new one if needed. 
  530.        Drawing mode: white dotted pen, XOR logic between background and pen colour.
  531.     */
  532.     SetROP2(hDC, R2_XORPEN);
  533.     SetBkMode(hDC, TRANSPARENT);
  534.     old_pen = SelectObject(hDC, my_rectangle_pen);
  535.     SelectObject(hDC, GetStockObject(NULL_BRUSH));
  536.  
  537.     /* Clear old rectangle:
  538.     */
  539.     if (zoom_rectangle_present) {
  540.         Rectangle(hDC, zoom_upper_left.x, zoom_upper_left.y,
  541.                zoom_lower_right.x, zoom_lower_right.y);
  542.     }
  543.  
  544.     /* Update lower right coordinates:
  545.     */
  546.     zoom_lower_right.x = pcurpos->x;
  547.     zoom_lower_right.y = pcurpos->y;
  548.  
  549.     /* Draw new rectangle:
  550.     */
  551.     if (zoom_rectangle_new) {
  552.     Rectangle(hDC, zoom_upper_left.x, zoom_upper_left.y,
  553.                zoom_lower_right.x, zoom_lower_right.y);
  554.         zoom_rectangle_present = TRUE;
  555.     }
  556.     else {
  557.     zoom_rectangle_present = FALSE;
  558.     }
  559.  
  560.     /* Deactivate the new pen:
  561.     */
  562.     SelectObject(hDC, old_pen);
  563.  
  564.     /* Release device context:
  565.     */
  566.     ReleaseDC(hwnd, hDC);
  567. }
  568.  
  569. /****************************************************************************
  570.  
  571.    FUNCTION:   MakeHelpPathName
  572.  
  573.    PURPOSE:    HelpEx assumes that the .HLP help file is in the same
  574.                directory as the HelpEx executable.This function derives
  575.                the full path name of the help file from the path of the
  576.                executable.
  577.  
  578. ****************************************************************************/
  579.  
  580. void MakeHelpPathName(char* szFileName)
  581. {
  582.    char *pcFileName;
  583.    int  nFileNameLen;
  584.  
  585.    nFileNameLen = GetModuleFileName(hInst, szFileName, EXE_NAME_MAX_SIZE);
  586.    pcFileName = szFileName + nFileNameLen;
  587.  
  588.    while (pcFileName > szFileName) {
  589.        if (*pcFileName == '\\' || *pcFileName == ':') {
  590.            *(++pcFileName) = '\0';
  591.            break;
  592.        }
  593.        nFileNameLen--;
  594.        pcFileName--;
  595.    }
  596.  
  597.    if ((nFileNameLen + 13) < EXE_NAME_MAX_SIZE) {
  598.        char hfn[20];
  599.  
  600.        LoadString(hInst, IDS_HELPFILE, hfn, 20);
  601.        lstrcat(szFileName, hfn);
  602.    }
  603.  
  604.    else {
  605.        lstrcat(szFileName, "?");
  606.    }
  607.  
  608.    return;
  609. }
  610.  
  611. /* The following functions are used to update the scoreboard and the
  612.    game status:
  613. */
  614. void UpdateNames(void)
  615. {
  616.     char buf[100];
  617.  
  618.     sprintf(buf, "%s : %s", player1_name, player2_name);
  619.     SetDlgItemText(hParDlg, DL_NAMES, buf);
  620. }
  621.  
  622. void UpdateScore(void)
  623. {
  624.     char buf[20];
  625.  
  626.     sprintf(buf, "%d : %d", score[0], score[1]);
  627.     SetDlgItemText(hParDlg, DL_SCORE, buf);
  628. }
  629.  
  630. void UpdateGameType(void)
  631. {
  632.     char buf[100];
  633.  
  634.     LoadString(hInst, GameStyle == DL_NG_GAME0 ? IDS_GAME0_NAME :
  635.               GameStyle == DL_NG_GAME1 ? IDS_GAME1_NAME :
  636.                          IDS_GAME3_NAME,
  637.            buf, 100);               
  638.     SetDlgItemText(hParDlg, DL_TYPE, buf);
  639. }
  640.  
  641. void UpdateWinningScore(void)
  642. {
  643.     char buf[30];
  644.  
  645.     LoadString(hInst, IDS_WIN_SCORE, buf, 20);
  646.     sprintf(buf + strlen(buf), " %d", WinningScore);       
  647.     SetDlgItemText(hParDlg, DL_WIN_SCORE, buf);
  648. }
  649.  
  650. /* Callback function of initialization of trigonometric functions.
  651.    The code parameter is
  652.     positive if this is the first call: number of intermediate calls;
  653.     0 if this is an intermediate call; and
  654.     -1 if this is the very last call of this function.
  655. */
  656. void DrawBar(int code)
  657. {
  658.     static RECT box;
  659.     static int status;
  660.     char buf[60];
  661.     HDC hDC;
  662.     DWORD extent;
  663.     RECT lr;
  664.  
  665.     switch(code) {
  666.     case 0:
  667.         /* Intermediate call.
  668.         */
  669.         hDC = GetDC(hwnd);
  670.             SelectObject(hDC, GetStockObject(NULL_PEN));
  671.         SelectObject(hDC, GetStockObject(LTGRAY_BRUSH));
  672.         lr.left = box.left + ++status;
  673.         lr.right = lr.left + 2;
  674.         lr.top = box.top + 1;
  675.         lr.bottom = box.bottom;
  676.         Rectangle(hDC, lr.left, lr.top, lr.right, lr.bottom);
  677.             ReleaseDC(hwnd, hDC);
  678.         break;
  679.     case -1:
  680.         /* Last call.
  681.             */
  682.         box.left = box.top = 0;
  683.         box.right = client_width;
  684.             box.bottom = client_height;
  685.         InvalidateRect(hwnd, &box, FALSE);
  686.         ReleaseCapture();
  687.         break;
  688.     default:
  689.         /* First call. Draw the border of the box and write text: 
  690.         */
  691.         SetCapture(hwnd);
  692.             SetCursor(LoadCursor(NULL, IDC_WAIT));
  693.             hDC = GetDC(hwnd);
  694.         LoadString(hInst, IDS_PLEASEWAIT, buf, 60);
  695.             extent = GetTextExtent(hDC, buf, strlen(buf));
  696.         box.left = (client_width - code - 2) / 2;
  697.         box.right = box.left + code + 2;
  698.         box.top = (client_height - 16 - 2 - HIWORD(extent)) / 3 +
  699.               HIWORD(extent) + 2;
  700.         box.bottom = box.top + 16;
  701.         SetTextColor(hDC, RGB(255, 255, 255));
  702.             SetBkMode(hDC, TRANSPARENT);
  703.         TextOut(hDC, (client_width - LOWORD(extent)) / 2,
  704.              (client_height - 16 - 2 - HIWORD(extent)) / 3,
  705.                          buf, strlen(buf));
  706.         SelectObject(hDC, GetStockObject(BLACK_PEN));
  707.         SelectObject(hDC, GetStockObject(WHITE_BRUSH));
  708.         Rectangle(hDC, box.left, box.top, box.right, box.bottom);
  709.         ReleaseDC(hwnd, hDC);
  710.         status = 0;  
  711.             break;
  712.     }
  713. }
  714.  
  715. //*******************************************************************
  716. // Init - init the Carom Biliard application
  717. //
  718. // parameters:
  719. //             hInstance     - The instance of this instance of this
  720. //                             application.
  721. //             hPrevInstance - The instance of the previous instance
  722. //                             of this application. This will be 0
  723. //                             if this is the first instance.
  724. //             lpszCmdLine   - A long pointer to the command line that
  725. //                             started this application.
  726. //             cmdShow       - Indicates how the window is to be shown
  727. //                             initially. ie. SW_SHOWNORMAL, SW_HIDE,
  728. //                             SW_MIMIMIZE.
  729. //
  730. //*******************************************************************
  731. #pragma argsused
  732. void Init(HINSTANCE hInstance, HINSTANCE hPrevInstance,
  733.          LPSTR lpszCmdLine, int cmdShow)
  734. {
  735.     WNDCLASS wcCaromClass, wcShotClass;
  736.     char title[TITLE_LENGTH+1];
  737.     RECT wrect;
  738.  
  739.  
  740.     hInst = hInstance;       // save for use by window procs
  741.  
  742.     // Get string from resource with application name.
  743.     LoadString(hInstance, IDS_NAME, (LPSTR) SetUpData.szAppName, 20);
  744.  
  745.     // Define the window class for this application.
  746.     wcCaromClass.style         = CS_HREDRAW | CS_VREDRAW;
  747.     wcCaromClass.lpfnWndProc   = WndProc;
  748.     wcCaromClass.cbClsExtra    = 0;
  749.     wcCaromClass.cbWndExtra    = 0;
  750.     wcCaromClass.hInstance     = hInstance;
  751.     wcCaromClass.hIcon         = LoadIcon(hInstance, SetUpData.szAppName);
  752.     wcCaromClass.hCursor       = LoadCursor(hInst, "Carom");
  753.     wcCaromClass.hbrBackground = (HBRUSH)GetStockObject(GRAY_BRUSH);
  754.     wcCaromClass.lpszClassName = SetUpData.szAppName;
  755.     wcCaromClass.lpszMenuName  = SetUpData.szAppName;
  756.  
  757.     // Register the class
  758.     RegisterClass(&wcCaromClass);
  759.  
  760.     // fetch in the window title from the stringtable resource
  761.     LoadString(hInstance, IDS_TITLE, (LPSTR)title, TITLE_LENGTH);
  762.  
  763.     // Create applications main window.
  764.     hwnd = CreateWindow(SetUpData.szAppName,    // window class name
  765.             (LPSTR)title,           // window title
  766.             WS_OVERLAPPEDWINDOW,    // window style
  767.             10,                     // x
  768.             16,                     // y
  769.             600,                    // cx
  770.             480,            // cy
  771.             NULL,                   // no parent for this window
  772.             NULL,                   // use the class menu
  773.             hInstance,              // who created this window
  774.             NULL                    // no parms to pass on
  775.             );
  776.  
  777.     // Get string from resource with application name.
  778.     LoadString(hInstance, IDS_PARWINNAME, (LPSTR) SetUpData.szParWinName, 20);
  779.  
  780.     // Create a thunk for the parameters dialog box proc function
  781.     lpParDlgProc = MakeProcInstance((FARPROC)ParDlgBoxProc, hInst);
  782.     hParDlg = CreateDialog(hInst, SetUpData.szParWinName /*"Parameters"*/, hwnd, lpParDlgProc);
  783.  
  784.     GetClientRect(hwnd, &wrect);
  785.     client_width = wrect.right;
  786.     client_height = wrect.bottom - DL_HEIGHT * 2 + 1;
  787.  
  788.     /* We are going to start with the view of the full table.
  789.        The parameters of this view will be computed with this:
  790.     */
  791.     ComputeFullTableParams();
  792.  
  793.     /* Create region that exactly covers the free area of the main window,
  794.        and a region that contains the inside of the table.
  795.     */
  796.     my_client_region = CreateRectRgn(0, 0, client_width, client_height);
  797.     my_inside_table_region = CreateRectRgn(
  798.     max(0, X_PHYTOCLI(LEFT) + 1),
  799.     max(0, Y_PHYTOCLI(TOP) + 1),
  800.     min(client_width, X_PHYTOCLI(RIGHT) - 1),
  801.     min(client_height, Y_PHYTOCLI(BOTTOM) - 1));
  802.  
  803.     sight_line_end.x = RIGHT - BALL_RADIUS;
  804.     sight_line_end.y = WOOD_WIDTH + CUSHION_WIDTH + (TABLE_WIDTH / 2.0);
  805.  
  806.     cueball = 0;            /* First player to play */
  807.     GameState = BEFORE_GAME;
  808.  
  809.     MoveWindow(hParDlg, -1, client_height,
  810.             client_width + 2, DL_HEIGHT * 2, TRUE);
  811.  
  812.     /* Set up initial state of scroll bars */
  813.     InitScrollBar(hParDlg, DL_HOR_SCROLL, DL_HOR_EDIT,
  814.           SPIN_MIN, SPIN_MAX, horizontal_spin);
  815.     InitScrollBar(hParDlg, DL_VER_SCROLL, DL_VER_EDIT,
  816.           SPIN_MIN, SPIN_MAX, vertical_spin);
  817.     InitScrollBar(hParDlg, DL_POWER_SCROLL, DL_POWER_EDIT,
  818.           POWER_MIN, POWER_MAX, shot_power);
  819.  
  820.     /* Load default name of players:
  821.     */
  822.     LoadString(hInst, IDS_PLAYER1_NAME, player1_name, sizeof(player1_name));
  823.     LoadString(hInst, IDS_PLAYER2_NAME, player2_name, sizeof(player2_name));
  824.  
  825.     /* Set up initial state of scoreboard and game status:
  826.     */
  827.     UpdateNames();
  828.     UpdateScore();
  829.     UpdateGameType();
  830.     UpdateWinningScore();
  831.  
  832.     InvalidateRect(hwnd, &wrect, TRUE);
  833.     ShowWindow(hwnd, SW_SHOWMAXIMIZED);
  834.     UpdateWindow(hwnd);
  835.  
  836.     /* Check the appropriate items in the menu:
  837.     */
  838.     CheckMenuItem(GetMenu(hwnd),
  839.           draw_path, MF_BYCOMMAND | MF_CHECKED);
  840.     EnableMenuItem(GetMenu(hwnd),
  841.            CM_UNDO_SHOT, MF_BYCOMMAND | MF_GRAYED);
  842.     if (sight_extra_enabled[1]) {
  843.         CheckMenuItem(GetMenu(hwnd), CM_BROKEN_LINE, MF_BYCOMMAND | MF_CHECKED);
  844.     }
  845.  
  846.     /* Init random number generator:
  847.     */
  848.     randomize();
  849.  
  850.     /* Init Help:
  851.     */
  852.     MakeHelpPathName(szHelpFileName);
  853.  
  854.     /* Create prolog address for timer procedure used for shot animation:
  855.     */
  856.     lpShotTimerProc = MakeProcInstance((FARPROC) tprcShotTimerProc, hInst);
  857.  
  858.     /* Init sine, cosine and arctan tables:
  859.     */
  860.     init_trigo(DrawBar);
  861.  
  862.     /* Start with dialog box so that the users type in their name,
  863.        the required game type, winning score and so on.
  864.     */
  865.     PostMessage(hwnd, WM_COMMAND, CM_NEW_GAME, NULL);
  866. } // end of Init()
  867.  
  868.  
  869. //*******************************************************************
  870. // InitScrollBar - Initializes one scroll bar and its edit box with
  871. //           a range and a starting value
  872. // parameters:
  873. //             hDlg          - The dialog handle (in which dlg is the SB)
  874. //             scroll        - The identifier (not handle) of the SB
  875. //             edit          - The identifier (not handle) of the edit box
  876. //           mi         - The minimum value of the scroll bar
  877. //           ma         - The maximum value of the scroll bar
  878. //            value         - The starting value of the scroll bar
  879. //
  880. // returns:
  881. //             nothing
  882. //
  883. //*******************************************************************
  884. void InitScrollBar(HWND hDlg, WORD scroll, WORD edit,
  885.            int mi, int ma, int value)
  886. {
  887.     WORD hwndscr;
  888.  
  889.     SetScrollRange(hwndscr = GetDlgItem(hDlg, scroll), SB_CTL, mi, ma, FALSE);
  890.     SetScrollPos(hwndscr, SB_CTL, value, TRUE);
  891.     SetDlgItemInt(hDlg, edit, value, TRUE);
  892. }
  893.  
  894. void FindEndPoint(FPOINT *start, FPOINT *on, FPOINT *end)
  895. {
  896.     int end_point_found = FALSE;
  897.     FPOINT cueball;
  898.     FPOINT phycurpos;
  899.     FPOINT cross;
  900.  
  901.     cueball.x = start->x;
  902.     cueball.y = start->y;
  903.     phycurpos.x = on->x;
  904.     phycurpos.y = on->y;
  905.  
  906.     /* Test bottom cushion
  907.     */
  908.     if (phycurpos.y < cueball.y) {
  909.     cross.x = cueball.x + (cueball.y - C_BOTTOM) *
  910.         (phycurpos.x - cueball.x) / (cueball.y - phycurpos.y);
  911.     if (cross.x >= C_LEFT && cross.x <= C_RIGHT) {
  912.         cross.y = C_BOTTOM;
  913.         end_point_found = TRUE;
  914.     }
  915.     }
  916.  
  917.     /* Test top cushion
  918.     */
  919.     if (!end_point_found && phycurpos.y > cueball.y) {
  920.     cross.x = cueball.x + (C_TOP - cueball.y) *
  921.         (phycurpos.x - cueball.x) / (phycurpos.y - cueball.y);
  922.     if (cross.x >= C_LEFT && cross.x <= C_RIGHT) {
  923.         cross.y = C_TOP;
  924.         end_point_found = TRUE;
  925.     }
  926.     }
  927.  
  928.     /* Test for left hand border
  929.     */
  930.     if (!end_point_found && phycurpos.x < cueball.x) {
  931.     cross.y = cueball.y + (cueball.x - C_LEFT) *
  932.             (phycurpos.y - cueball.y) / (cueball.x - phycurpos.x);
  933.     if (cross.y >= C_BOTTOM && cross.y <= C_TOP) {
  934.         cross.x = C_LEFT;
  935.         end_point_found = TRUE;
  936.     }
  937.     }
  938.  
  939.     /* Test for right hand border
  940.     */
  941.     if (!end_point_found && phycurpos.x > cueball.x) {
  942.     cross.y = cueball.y + (C_RIGHT - cueball.x) *
  943.             (phycurpos.y - cueball.y) / (phycurpos.x - cueball.x);
  944.     if (cross.y >= C_BOTTOM && cross.y <= C_TOP) {
  945.         cross.x = C_RIGHT;
  946.         end_point_found = TRUE;
  947.     }
  948.     }
  949.  
  950.     if (!end_point_found) {
  951.     /* Nearly impossible branch... Set horizontal direction:
  952.     */
  953.     cross.x = C_RIGHT;
  954.     cross.y = C_TOP;
  955.     }
  956.     end->x = cross.x;
  957.     end->y = cross.y;
  958. }
  959.  
  960. void DrawSightLines(HDC hDC, FPOINT *extra, int *drawn)
  961. {
  962.     int i;
  963.  
  964.     SetROP2(hDC, R2_XORPEN);
  965.     SetBkMode(hDC, TRANSPARENT);
  966.     SelectObject(hDC, my_sight_line_pen);
  967.     MoveTo(hDC, X_PHYTOCLI(ball[cueball].x), Y_PHYTOCLI(ball[cueball].y));
  968.     LineTo(hDC, X_PHYTOCLI(sight_line_end.x), Y_PHYTOCLI(sight_line_end.y));
  969.     for (i = 0; i < 2; i++) {
  970.     if (drawn[i]) {
  971.             SelectObject(hDC, i == 0 ? my_sight_line_extra_pen : my_sight_line_pen);
  972.         MoveTo(hDC, X_PHYTOCLI(extra[2 * i].x),
  973.                     Y_PHYTOCLI(extra[2 * i].y));
  974.         LineTo(hDC, X_PHYTOCLI(extra[2 * i + 1].x),
  975.             Y_PHYTOCLI(extra[2 * i + 1].y));
  976.         }
  977.     }
  978. }
  979.  
  980. void UpdateSightLine(HWND hwnd, POINT *pcurpos, int erase_old)
  981. {
  982.     FPOINT phycurpos;            /* Position of cursor */
  983.     FPOINT cross;            /* Position of new end-of-line */
  984.     HDC hDC;                /* Device context of main window */
  985.     HRGN hrgn;                /* Handle of region to be created */
  986.     FPOINT temp;
  987.     int j;
  988.  
  989.     /* Make local copy of cursor position (client coords). Translate it to physical
  990.        coordinates:
  991.     */
  992.     phycurpos.x = X_CLITOPHY(pcurpos->x);
  993.     phycurpos.y = Y_CLITOPHY(pcurpos->y);
  994.  
  995.     /* Avoid the case that the cursor points exactly to the centre of the cue ball:
  996.     */
  997.     if (phycurpos.x == ball[cueball].x && phycurpos.y == ball[cueball].y) {
  998.     phycurpos.x += 1.0;
  999.     }
  1000.  
  1001.     /* Find the place where the sight-line crosses the window frame:
  1002.     */
  1003.     temp.x = ball[cueball].x;
  1004.     temp.y = ball[cueball].y;
  1005.     FindEndPoint(&temp, &phycurpos, &cross);
  1006.  
  1007.     /* Check first collision:
  1008.     */
  1009.     {
  1010.         int i = cueball, j, jj;
  1011.     float r = ball[i].r;
  1012.     float a, ca;
  1013.     float ox = ball[i].x;
  1014.     float oy = ball[i].y;
  1015.     float cosa;
  1016.     float sina;
  1017.     float t, t1;
  1018.         float dx, dy, q, nx, ny, discr;
  1019.  
  1020.     a = atan2(cross.y - oy, cross.x - ox);
  1021.     if (a < 0) a += 2 * M_PI;
  1022.     sina = sin(a);
  1023.     cosa = cos(a);
  1024.  
  1025.     memcpy(old_sight_extra, sight_extra, sizeof(old_sight_extra));
  1026.     sight_extra_present = FALSE;
  1027.     /* Check collision with other ball
  1028.     */
  1029.     for (t = RIGHT - LEFT, jj = -1, j = 0; j < N; j++) {
  1030.         if (j != i) {
  1031.         dx = ball[j].x - ox;
  1032.         dy = ball[j].y - oy;
  1033.         q = dx * cosa + dy * sina;
  1034.         discr = (q * q) - ((dx * dx) + (dy * dy) -
  1035.                           (r + ball[j].r) * (r + ball[j].r));
  1036.         if (discr > 0 && (t1 = q - sqrt(discr)) > 0 && t1 < t) {
  1037.             t = t1;
  1038.             jj = j;
  1039.         }
  1040.         }
  1041.     }
  1042.     if (jj >= 0) {
  1043.         j = jj;
  1044.         /* Compute exact place of collision (nx,ny). This position, however,
  1045.            may be outside of the valid area:
  1046.         */
  1047.         nx = ox + (t * cosa);
  1048.         ny = oy + (t * sina);
  1049.         if (nx - r >= LEFT && nx + r <= RIGHT &&
  1050.         ny - r >= BOTTOM && ny + r <= TOP) {
  1051.         /* Place of collision is inside:
  1052.            Yes, there is a collision:
  1053.         */
  1054.         /* Distances:
  1055.         */
  1056.         dx = ball[j].x - nx;
  1057.         dy = ball[j].y - ny;
  1058.         /* Angle of collision line:
  1059.         */
  1060.         ca = atan2(dy, dx);        
  1061.         sight_extra[0][0].x = nx;
  1062.         sight_extra[0][0].y = ny;
  1063.         if (sight_extra[0][0].x - r >= LEFT &&
  1064.             sight_extra[0][0].x + r <= RIGHT &&
  1065.             sight_extra[0][0].y - r >= BOTTOM &&
  1066.             sight_extra[0][0].y + r <= TOP) {
  1067.             sight_extra[0][1].x = cross.x;
  1068.             sight_extra[0][1].y = cross.y;
  1069.         }
  1070.         else {
  1071.             sight_extra[0][0].x =
  1072.             sight_extra[0][0].y =
  1073.             sight_extra[0][1].x =
  1074.                     sight_extra[0][1].y = 0;
  1075.                 }
  1076.         ca -= M_PI_2;
  1077.         if (!(fabs(ca - a) < M_PI_2 ||
  1078.             (fabs(ca - a) > M_PI && fabs(fabs(ca - a) - 2 * M_PI) < M_PI_2))) { 
  1079.             ca += M_PI;
  1080.         }
  1081.         sight_extra[1][0].x = nx;
  1082.         sight_extra[1][0].y = ny;
  1083.         temp.x = nx + 10 * r * cos(ca);
  1084.         temp.y = ny + 10 * r * sin(ca);
  1085.         FindEndPoint(sight_extra[1] + 0, &temp, sight_extra[1] + 1);
  1086.         sight_extra_present = TRUE;
  1087.         if (sight_extra_enabled[0]) {
  1088.             cross.x = nx;
  1089.             cross.y = ny;
  1090.                 }
  1091.         }
  1092.         }
  1093.     }
  1094.  
  1095.     /* Get device context, only client area is needed:
  1096.     */
  1097.     hDC = GetDC(hwnd);
  1098.  
  1099.     SelectObject(hDC, my_client_region);
  1100.  
  1101.     /* Clear previous sight-line and draw the new one.
  1102.     */
  1103.     if (erase_old) {
  1104.     DrawSightLines(hDC, (FPOINT *) old_sight_extra, sight_extra_drawn);
  1105.     }
  1106.     for (j = 0; j < 2; j ++) {
  1107.     sight_extra_drawn[j] = sight_extra_enabled[j] && sight_extra_present;
  1108.     }
  1109.     sight_line_end.x = cross.x;
  1110.     sight_line_end.y = cross.y;
  1111.     DrawSightLines(hDC, (FPOINT *) sight_extra, sight_extra_drawn);
  1112.  
  1113.     /* Release device context:
  1114.     */
  1115.     ReleaseDC(hwnd, hDC);
  1116. }
  1117.  
  1118. //*******************************************************************
  1119. // WndProc - handles messages for this application
  1120. //
  1121. // parameters:
  1122. //             hWnd          - The window handle for this message
  1123. //             message       - The message number
  1124. //             wParam        - The WPARAM parameter for this message
  1125. //             lParam        - The LPARAM parameter for this message
  1126. //
  1127. // returns:
  1128. //             depends on message.
  1129. //
  1130. //*******************************************************************
  1131. LRESULT CALLBACK WndProc(HWND hWnd, UINT message,
  1132.              WPARAM wParam, LPARAM lParam)
  1133. {
  1134.     RECT wrect;
  1135.     static int first_size_message = TRUE;
  1136.  
  1137.     switch (message) {
  1138.     case WM_CREATE:
  1139.         wmCreate();
  1140.         break;
  1141.  
  1142.     case WM_SIZE:
  1143.         client_width = LOWORD(lParam);
  1144.         client_height = HIWORD(lParam) - DL_HEIGHT * 2 + 1;
  1145.  
  1146.         if (first_size_message) {
  1147.         first_size_message = FALSE;
  1148.         ComputeFullTableParams();
  1149.         }
  1150.  
  1151.         DeleteObject(my_client_region);
  1152.         my_client_region = CreateRectRgn(0, 0, client_width, client_height);
  1153.         UpdateInsideTableRegion();
  1154.  
  1155.         if (IsWindow(hParDlg)) {
  1156.         MoveWindow(hParDlg,
  1157.                -1, client_height,
  1158.                client_width + 2, DL_HEIGHT * 2, TRUE);
  1159.         }
  1160.         break;
  1161.  
  1162.     case WM_LBUTTONDOWN:
  1163.         if (GameState == SETTING_SHOT) {
  1164.         /* Muose button pressed inside of the table-image area.
  1165.            Start moving the destination point - changing the sight_line_end
  1166.            global variable.
  1167.         */
  1168.         sight_line_being_set = TRUE;
  1169.         old_capture_sight = SetCapture(hWnd);
  1170.         old_cursor_sight = SetCursor(LoadCursor(hInst, "Set_Dir"));
  1171.         UpdateSightLine(hWnd, &MAKEPOINT(lParam), TRUE);
  1172.         }
  1173.         else if (GameState == ZOOMING_IN) {
  1174.         /* User pressed the mouse button in order to start selecting the area
  1175.            into which she wants to zoom in.
  1176.         */
  1177.         zoom_being_set = TRUE;
  1178.         zoom_upper_left.x = MAKEPOINT(lParam).x;
  1179.         zoom_upper_left.y = MAKEPOINT(lParam).y;
  1180.         zoom_rectangle_present = FALSE;
  1181.                 zoom_rectangle_new = TRUE;
  1182.         }
  1183.         break;
  1184.  
  1185.     case WM_MOUSEMOVE:
  1186.         if (sight_line_being_set) {
  1187.         UpdateSightLine(hWnd, &MAKEPOINT(lParam), TRUE);
  1188.         }
  1189.         else if (zoom_being_set) {
  1190.                 UpdateZoom(hWnd, &MAKEPOINT(lParam));
  1191.             }
  1192.         break;
  1193.  
  1194.     case WM_LBUTTONUP:
  1195.         if (sight_line_being_set) {
  1196.         if (old_capture_sight) {
  1197.             SetCapture(old_capture_sight);
  1198.         }
  1199.         else { 
  1200.             ReleaseCapture();
  1201.         }
  1202.                 SetCursor(old_cursor_sight);
  1203.         sight_line_being_set = FALSE;
  1204.         }
  1205.         else if (zoom_being_set) {
  1206.         zoom_rectangle_new = FALSE;
  1207.         UpdateZoom(hWnd, &MAKEPOINT(lParam));    /* Be nice, clear rectangle */
  1208.         if (old_capture_zoom) {
  1209.             SetCapture(old_capture_zoom);
  1210.         }
  1211.         else {
  1212.             ReleaseCapture();
  1213.         }
  1214.                 SetCursor(old_cursor_zoom);
  1215.         zoom_being_set = FALSE;
  1216.         GameState = SETTING_SHOT;
  1217.         if (abs(zoom_lower_right.x - zoom_upper_left.x) > 2 &&
  1218.             abs(zoom_lower_right.y - zoom_upper_left.y) > 2 &&
  1219.             max(zoom_upper_left.x, zoom_lower_right.x) >= X_PHYTOCLI(LEFT) &&
  1220.             min(zoom_upper_left.x, zoom_lower_right.x) <= X_PHYTOCLI(RIGHT) &&
  1221.             min(zoom_upper_left.y, zoom_lower_right.y) <= Y_PHYTOCLI(BOTTOM) &&
  1222.             max(zoom_upper_left.y, zoom_lower_right.y) >= Y_PHYTOCLI(TOP)) {
  1223.                     RECT wrect;
  1224.  
  1225.                     PushView();
  1226.             ComputeZoomParams();
  1227.             wrect.left = wrect.top = 0;
  1228.             wrect.right = client_width;
  1229.                     wrect.bottom = client_height;
  1230.             InvalidateRect(hWnd, &wrect, TRUE);
  1231.             UpdateInsideTableRegion();
  1232.         }
  1233.             }
  1234.             break;
  1235.  
  1236.     case WM_COMMAND:
  1237.         switch (wParam) {
  1238.         case CM_ABOUT:
  1239.             cmAbout();
  1240.             break;
  1241.  
  1242.         case CM_EXIT:
  1243.             // Tell windows to destroy our window.
  1244.             DestroyWindow(hWnd);
  1245.             break;
  1246.  
  1247.         case CM_PATH_NONE:
  1248.         case CM_PATH_LAST:
  1249.         case CM_PATH_ALL:
  1250.             CheckMenuItem(GetMenu(hwnd),
  1251.                                   draw_path, MF_BYCOMMAND | MF_UNCHECKED);
  1252.             draw_path = wParam;
  1253.             CheckMenuItem(GetMenu(hwnd),
  1254.                   wParam, MF_BYCOMMAND | MF_CHECKED);
  1255.             if (draw_path == CM_PATH_NONE) {
  1256.             RECT wrect;
  1257.  
  1258.             wrect.left = wrect.top = 0;
  1259.             wrect.right = client_width;
  1260.                         wrect.bottom = client_height;
  1261.                         InvalidateRect(hwnd, &wrect, TRUE); 
  1262.             }
  1263.             break;
  1264.  
  1265.         case CM_BROKEN_LINE: {
  1266.             POINT p;
  1267.  
  1268.             sight_extra_enabled[0] ^= 1;
  1269.             sight_extra_enabled[1] ^= 1;
  1270.             CheckMenuItem(GetMenu(hwnd), CM_BROKEN_LINE,
  1271.                   MF_BYCOMMAND | sight_extra_enabled[1] ?
  1272.                              MF_CHECKED : MF_UNCHECKED);
  1273.             p.x = X_PHYTOCLI(sight_line_end.x);
  1274.             p.y = Y_PHYTOCLI(sight_line_end.y);
  1275.             UpdateSightLine(hwnd, &p, TRUE);               
  1276.             break;
  1277.         }
  1278.  
  1279.         case CM_NEW_GAME:
  1280.             /* Replace balls, Reset point counters,
  1281.                gray undo menu item.
  1282.             */
  1283.             if (GameState != BALLS_MOVING) {
  1284.             if (GameState != BEFORE_GAME &&
  1285.                  GameState != AFTER_GAME) {
  1286.                 char ttl[40], msg[200];
  1287.  
  1288.                 LoadString(hInst, IDS_IN_GAME_TTL, ttl, 40);
  1289.                 LoadString(hInst, IDS_IN_GAME_MSG, msg, 200);
  1290.                             MessageBeep(MB_ICONINFORMATION);
  1291.                 MessageBox(hwnd, msg, ttl, MB_ICONINFORMATION | MB_OK);
  1292.             }
  1293.             if (cmNewGame()) {
  1294.                             POINT p;
  1295.  
  1296.                 /* Replace balls into starting position:
  1297.                 */
  1298.                 ball[cueball].x = BOTTOMR_SPOT_X;
  1299.                 ball[cueball].y = BOTTOMR_SPOT_Y;
  1300.                 ball[1-cueball].x = BOTTOMM_SPOT_X;
  1301.                 ball[1-cueball].y = BOTTOMM_SPOT_Y;
  1302.                 ball[2].x = TOP_SPOT_X;
  1303.                 ball[2].y = TOP_SPOT_Y;
  1304.                 /* Reset point counters:
  1305.                 */
  1306.                 score[0] = score[1] = 0;
  1307.                 /* Indicate that this will be the first shot:
  1308.                 */
  1309.                             first_shot = TRUE;
  1310.                 /* Disable undo menu item:
  1311.                 */
  1312.                 EnableMenuItem(GetMenu(hwnd),
  1313.                        CM_UNDO_SHOT,
  1314.                        MF_BYCOMMAND | MF_GRAYED);
  1315.                 EnableMenuItem(GetMenu(hwnd),
  1316.                        CM_BROKEN_LINE,
  1317.                        MF_BYCOMMAND | EnableBreak ? MF_ENABLED : MF_GRAYED);
  1318.                 if (!EnableBreak) {
  1319.                 sight_extra_enabled[0] = sight_extra_enabled[1] = FALSE;
  1320.                 CheckMenuItem(GetMenu(hwnd), CM_BROKEN_LINE,
  1321.                                                      MF_BYCOMMAND | MF_UNCHECKED); 
  1322.                 }
  1323.                                 
  1324.                 InvalidateRgn(hwnd, my_inside_table_region, TRUE);
  1325.                 /* Update scoreboard and game status:
  1326.                 */
  1327.                 UpdateNames();
  1328.                 UpdateScore();
  1329.                 UpdateGameType();
  1330.                 UpdateWinningScore();
  1331.                 /* Store the id of the player who begins the game:
  1332.                 */
  1333.                 WhoStarted = cueball;
  1334.                 /* Change game state to "playing":
  1335.                 */
  1336.                 GameState = SETTING_SHOT;
  1337.                 /* Redraw cue ball colour:
  1338.                 */
  1339.                 p.x = X_PHYTOCLI(sight_line_end.x);
  1340.                 p.y = Y_PHYTOCLI(sight_line_end.y);
  1341.                 UpdateSightLine(hwnd, &p, TRUE);
  1342.                 PostMessage(hParDlg, WM_COMMAND, DL_NO_SPIN, NULL);
  1343.             }
  1344.                     }
  1345.             break;
  1346.  
  1347.         case CM_UNDO_SHOT:
  1348.             /* User wants to undo the last shot. The balls may be moving when
  1349.                this command arrives!
  1350.                If shot timer is still active, remove timer!
  1351.             */
  1352.             if (globalidTimer1) {
  1353.             KillTimer(NULL, globalidTimer1);
  1354.                         KillTimer(NULL, globalidTimer2);
  1355.             globalidTimer1 = globalidTimer2 = 0;
  1356.             }
  1357.             GameState = SETTING_SHOT;
  1358.             memcpy(ball, ball_save, sizeof(ball_save));
  1359.             memcpy(&sight_line_end, &sight_line_end_save, sizeof(sight_line_end));
  1360.             memcpy(sight_extra, sight_extra_save, sizeof(sight_extra));
  1361.             memcpy(sight_extra_drawn, sight_extra_drawn_save, sizeof(sight_extra_drawn));
  1362.             cueball = cueball_save;
  1363.             horizontal_spin = horizontal_spin_save;
  1364.             vertical_spin = vertical_spin_save;
  1365.             shot_power = shot_power_save;
  1366.             memcpy(score, score_save, sizeof(score));
  1367.                     first_shot = first_shot_save;
  1368.             InvalidateRgn(hwnd, my_inside_table_region, TRUE);
  1369.             SetDlgItemInt(hParDlg, DL_HOR_EDIT, horizontal_spin, TRUE);
  1370.             SetDlgItemInt(hParDlg, DL_VER_EDIT, -vertical_spin, TRUE);
  1371.             SetDlgItemInt(hParDlg, DL_POWER_EDIT, shot_power, TRUE);
  1372.             SetScrollPos(GetDlgItem(hParDlg, DL_HOR_SCROLL), SB_CTL,
  1373.                  horizontal_spin, TRUE);
  1374.             SetScrollPos(GetDlgItem(hParDlg, DL_VER_SCROLL), SB_CTL,
  1375.                  vertical_spin, TRUE);
  1376.             DoPaintDlg(hParDlg, TRUE);
  1377.                     UpdateScore();
  1378.             if (EnableBreak) {
  1379.             EnableMenuItem(GetMenu(hwnd), CM_BROKEN_LINE,
  1380.                               MF_BYCOMMAND | MF_ENABLED);
  1381.             }
  1382.             EnableMenuItem(GetMenu(hwnd), DL_ZOOM_IN,
  1383.                           MF_BYCOMMAND | MF_ENABLED);
  1384.             EnableMenuItem(GetMenu(hwnd), DL_TAKE_SHOT,
  1385.                           MF_BYCOMMAND | MF_ENABLED);
  1386.             EnableMenuItem(GetMenu(hwnd), CM_NEW_GAME,
  1387.                           MF_BYCOMMAND | MF_ENABLED);
  1388.             break;
  1389.  
  1390.         case DL_VIEW_LEFT:
  1391.         case DL_VIEW_FULL:
  1392.         case DL_VIEW_RIGHT:
  1393.         case DL_ZOOM_IN:
  1394.         case DL_ZOOM_OUT:
  1395.                 case DL_TAKE_SHOT:
  1396.             ParDlgBoxProc(hParDlg, message, wParam, lParam);
  1397.             break;
  1398.  
  1399.         case CM_HELP_CONTENTS:
  1400.             WinHelp(hWnd, szHelpFileName, HELP_INDEX, 0L);
  1401.             break;
  1402.  
  1403.         default:
  1404.             break;
  1405.         } // switch wParam
  1406.         break;
  1407.  
  1408.     case WM_PAINT:
  1409.         DoPaint();
  1410.         break;
  1411.  
  1412.     case WM_GETMINMAXINFO:
  1413.         // Limit the minimum size of the window to 300x250
  1414.         ((POINT far *)lParam)[3].x = 300;
  1415.         ((POINT far *)lParam)[3].y = 250;
  1416.         break;
  1417.  
  1418.     case WM_DESTROY:
  1419.         // This is the end if we were closed by a DestroyWindow call.
  1420.         /* Clean up:
  1421.         */
  1422.             UnCreate();
  1423.         PostQuitMessage(0);
  1424.         break;
  1425.  
  1426.     default:
  1427.         return(DefWindowProc(hWnd, message, wParam, lParam));
  1428.     }  // switch message
  1429.  
  1430.     return(0L);
  1431. }  // end of WndProc()
  1432.  
  1433.  
  1434. //*******************************************************************
  1435. // DlgBoxProc - handle dialog messages
  1436. //
  1437. // parameters:
  1438. //             hDlg          - The window handle for this message
  1439. //             message       - The message number
  1440. //             wParam        - The WPARAM parameter for this message
  1441. //             lParam        - The LPARAM parameter for this message
  1442. //
  1443. //*******************************************************************
  1444. #pragma argsused
  1445. BOOL CALLBACK DlgBoxProc(HWND hDlg, UINT message,
  1446.                WPARAM wParam, LPARAM lParam)
  1447. {
  1448.  
  1449.     switch (message)
  1450.     {
  1451.     case WM_COMMAND:
  1452.         // regardless of the command (only ID_OK should arrive)
  1453.         // we want to exit the dialog
  1454.         EndDialog(hDlg, TRUE);
  1455.         break;
  1456.  
  1457.     default:
  1458.         return FALSE;
  1459.  
  1460.     }  // switch
  1461.  
  1462.     return(TRUE);
  1463. }  // end of DlgBoxProc()
  1464.  
  1465.  
  1466. //*******************************************************************
  1467. // NewGameDlgBoxProc - handle dialog messages
  1468. //
  1469. // parameters:
  1470. //             hDlg          - The window handle for this message
  1471. //             message       - The message number
  1472. //             wParam        - The WPARAM parameter for this message
  1473. //             lParam        - The LPARAM parameter for this message
  1474. //
  1475. //*******************************************************************
  1476. #pragma argsused
  1477. BOOL CALLBACK NewGameDlgBoxProc(HWND hDlg, UINT message,
  1478.                     WPARAM wParam, LPARAM lParam)
  1479. {
  1480.  
  1481.     switch (message)
  1482.     {
  1483.     case WM_INITDIALOG:
  1484.         /* Set starting values:
  1485.         */
  1486.         SetDlgItemText(hDlg, DL_NG_NAME1, player1_name);
  1487.         SetDlgItemText(hDlg, DL_NG_NAME2, player2_name);
  1488.         CheckRadioButton(hDlg, DL_NG_GAME0, DL_NG_GAME3, GameStyle);
  1489.         SetDlgItemInt(hDlg, DL_NG_WIN_SCORE, WinningScore, TRUE);
  1490.         CheckDlgButton(hDlg, DL_NG_CUSHION_P, CushionPoints);
  1491.         CheckRadioButton(hDlg, DL_NG_START1, DL_NG_STARTR, WhoStarts);
  1492.         CheckDlgButton(hDlg, DL_NG_UNDO, EnableUndo);
  1493.             CheckDlgButton(hDlg, DL_NG_BREAK, EnableBreak);
  1494.         break;
  1495.  
  1496.     case WM_COMMAND:
  1497.         switch (wParam) {
  1498.         case DL_NG_CANCEL:
  1499.             /* Exit dialog with no modifications
  1500.                     */
  1501.             EndDialog(hDlg, wParam);
  1502.             break;
  1503.         case DL_NG_OK: {
  1504.             char name1[40], name2[40];
  1505.             int ws, trans;
  1506.                     char ttl[40], msg[100];
  1507.  
  1508.             /* User wants to exit the dialog with the
  1509.                parameters that are currently set.
  1510.                        We check the validity of these parameters:
  1511.             */
  1512.             GetDlgItemText(hDlg, DL_NG_NAME1, name1, 40);
  1513.             GetDlgItemText(hDlg, DL_NG_NAME2, name2, 40);
  1514.             if (strlen(name1) == 0 || strlen(name2) == 0) {
  1515.             LoadString(hInst, IDS_EMPTY_NAME_TTL, ttl, 40);
  1516.             LoadString(hInst, IDS_EMPTY_NAME_MSG, msg, 100);
  1517.             MessageBox(hDlg, msg, ttl, MB_OK | MB_ICONEXCLAMATION);
  1518.                         break;
  1519.             }
  1520.                     ws = GetDlgItemInt(hDlg, DL_NG_WIN_SCORE, &trans, TRUE); 
  1521.             if (ws < 5 || ws > 1000) {
  1522.             LoadString(hInst, IDS_BAD_WS_TTL, ttl, 40);
  1523.             LoadString(hInst, IDS_BAD_WS_MSG, msg, 100);
  1524.             MessageBox(hDlg, msg, ttl, MB_OK | MB_ICONEXCLAMATION);
  1525.                         break;
  1526.             }
  1527.             /* Everything is OK, copy parameters back to global
  1528.                variables:
  1529.             */
  1530.             strcpy(player1_name, name1);
  1531.             strcpy(player2_name, name2);
  1532.             WinningScore = ws;
  1533.             CushionPoints = IsDlgButtonChecked(hDlg, DL_NG_CUSHION_P);
  1534.             if (IsDlgButtonChecked(hDlg, DL_NG_GAME0)) {
  1535.             GameStyle = DL_NG_GAME0;
  1536.             }
  1537.             else if (IsDlgButtonChecked(hDlg, DL_NG_GAME1)) {
  1538.             GameStyle = DL_NG_GAME1;
  1539.             }
  1540.             else {
  1541.             GameStyle = DL_NG_GAME3;
  1542.                     }
  1543.             if (IsDlgButtonChecked(hDlg, DL_NG_START1)) {
  1544.             WhoStarts = DL_NG_START1;
  1545.                         cueball = 0;
  1546.             }
  1547.             else if (IsDlgButtonChecked(hDlg, DL_NG_START2)) {
  1548.             WhoStarts = DL_NG_START2;
  1549.                         cueball = 1;
  1550.             }
  1551.             else {
  1552.                         WhoStarts = DL_NG_STARTR;
  1553.             cueball = random(2);
  1554.             }
  1555.             EnableUndo = IsDlgButtonChecked(hDlg, DL_NG_UNDO);
  1556.                     EnableBreak = IsDlgButtonChecked(hDlg, DL_NG_BREAK);
  1557.             /* End this dialog box:
  1558.                     */
  1559.             EndDialog(hDlg, wParam);
  1560.             break;
  1561.         }
  1562.             }
  1563.  
  1564.     default:
  1565.         return FALSE;
  1566.  
  1567.     }  // switch
  1568.  
  1569.     return(TRUE);
  1570. }  // end of DlgBoxProc()
  1571.  
  1572.  
  1573.  
  1574. void DoPaintDlgAction(HDC hDC, BOOL fDrawEntire)
  1575. {
  1576.     if (fDrawEntire) {
  1577.         SelectObject(hDC, GetStockObject(BLACK_PEN));
  1578.     SelectObject(hDC, GetStockObject(NULL_BRUSH));
  1579.         Rectangle(hDC, 0, 0, 52, 52);
  1580.     SelectObject(hDC, GetStockObject(WHITE_BRUSH));
  1581.     Ellipse(hDC, 8, 8, 45, 45);
  1582.     }
  1583.     else {
  1584.         SelectObject(hDC, GetStockObject(WHITE_PEN));
  1585.     MoveTo(hDC, 26 - 3 + prev_horizontal_spin, 26 + prev_vertical_spin);
  1586.     LineTo(hDC, 26 + 4 + prev_horizontal_spin, 26 + prev_vertical_spin);
  1587.     MoveTo(hDC, 26 + prev_horizontal_spin, 26 - 3 + prev_vertical_spin);
  1588.     LineTo(hDC, 26 + prev_horizontal_spin, 26 + 4 + prev_vertical_spin);
  1589.         SelectObject(hDC, GetStockObject(BLACK_PEN));
  1590.     }
  1591.     MoveTo(hDC, 26 - 3 + horizontal_spin, 26 + vertical_spin);
  1592.     LineTo(hDC, 26 + 4 + horizontal_spin, 26 + vertical_spin);
  1593.     MoveTo(hDC, 26 + horizontal_spin, 26 - 3 + vertical_spin);
  1594.     LineTo(hDC, 26 + horizontal_spin, 26 + 4 + vertical_spin);
  1595.  
  1596.     prev_horizontal_spin = horizontal_spin;
  1597.     prev_vertical_spin = vertical_spin;
  1598. }
  1599.  
  1600. void DoPaintDlg(HWND hDlg, BOOL fDrawEntire)
  1601. {
  1602.     HWND hwndch = GetDlgItem(hDlg, DL_BALL_IMAGE);
  1603.     HDC hDC;
  1604.  
  1605.     hDC = GetWindowDC(hwndch);
  1606.     DoPaintDlgAction(hDC, fDrawEntire);
  1607.     ReleaseDC(hwndch, hDC);
  1608. }
  1609.  
  1610. void UpdateSpin(HWND hDlg, HWND hwndch)
  1611. {
  1612.     POINT curpos;    /* Position of cursor */
  1613.     RECT wrect;        /* Position of ball-image window */
  1614.     int new_hor_spin;    /* temporary storage for new spin values */
  1615.     int new_ver_spin;    /*            -||-             */
  1616.  
  1617.     GetCursorPos(&curpos);
  1618.     GetWindowRect(hwndch, &wrect);
  1619.     new_hor_spin = clamp(curpos.x - (wrect.left + 26), SPIN_MIN, SPIN_MAX);
  1620.     new_ver_spin = clamp(curpos.y - (wrect.top + 26), SPIN_MIN, SPIN_MAX);
  1621.     SetDlgItemInt(hDlg, DL_HOR_EDIT, new_hor_spin, TRUE);
  1622.     SetDlgItemInt(hDlg, DL_VER_EDIT, - new_ver_spin, TRUE);
  1623.     DoPaintDlg(hDlg, FALSE);
  1624. }
  1625.  
  1626. //*******************************************************************
  1627. // ParDlgBoxProc - handle shot parameter window messages
  1628. //
  1629. // parameters:
  1630. //             hDlg          - The window handle for this message
  1631. //             message       - The message number
  1632. //             wParam        - The WPARAM parameter for this message
  1633. //             lParam        - The LPARAM parameter for this message
  1634. //
  1635. //*******************************************************************
  1636. #pragma argsused
  1637. BOOL CALLBACK ParDlgBoxProc(HWND hDlg, UINT message,
  1638.                WPARAM wParam, LPARAM lParam)
  1639. {
  1640.     RECT wrect;
  1641.     int x, y, w, h;
  1642.  
  1643.     switch (message)
  1644.     {
  1645.     case WM_DRAWITEM: {
  1646.         /* This message is processed to redraw the ball-image in the
  1647.            bottom left corner. This message arrivers whenever the
  1648.            button assigned to that area needs redrawing. It is like
  1649.            a WM_PAINT message...
  1650.         */
  1651.         DRAWITEMSTRUCT FAR *dis = (DRAWITEMSTRUCT FAR*) lParam;
  1652.  
  1653.         switch(dis->itemAction) {
  1654.                 case ODA_DRAWENTIRE:
  1655.             if (dis->CtlID == DL_BALL_IMAGE) {
  1656.             DoPaintDlgAction(dis->hDC, TRUE);
  1657.             }
  1658.                     break;
  1659.         case ODA_SELECT:
  1660.             if (dis->itemState & ODS_SELECTED) {
  1661.             HWND hwndch;
  1662.  
  1663.             hwndch = GetDlgItem(hDlg, DL_BALL_IMAGE);
  1664.             /* Muose button pressed inside of the ball-image area.
  1665.                Start moving the hit point - changing the horizontal_spin
  1666.                and vertical_spin global variables.
  1667.             */
  1668.             spin_being_set = TRUE;
  1669.             old_capture_ball = SetCapture(hDlg);
  1670.             old_cursor_ball = SetCursor(LoadCursor(hInst, "Set_Spin"));
  1671.             UpdateSpin(hDlg, hwndch);
  1672.             }
  1673.             break;
  1674.             }
  1675.         return TRUE;
  1676.     }
  1677.  
  1678.     case WM_MOUSEMOVE:
  1679.         if (spin_being_set) {
  1680.         UpdateSpin(hDlg, GetDlgItem(hDlg, DL_BALL_IMAGE));
  1681.         }
  1682.             break;
  1683.  
  1684.     case WM_LBUTTONUP:
  1685.         if (spin_being_set) {
  1686.         if (old_capture_ball) {
  1687.             SetCapture(old_capture_ball);
  1688.         }
  1689.         else { 
  1690.             ReleaseCapture();
  1691.         }
  1692.         SetCursor(old_cursor_ball);
  1693.         spin_being_set = FALSE;
  1694.         PostMessage(GetDlgItem(hDlg, DL_BALL_IMAGE),
  1695.                             message, wParam, lParam);
  1696.         }
  1697.             break;
  1698.                 
  1699.     case WM_COMMAND:
  1700.         /* The buttons of the dialog box send this message to their
  1701.            parents when they are activated (pressed and released).
  1702.            The wParam contains the control identifier of the button
  1703.                that sent this message.
  1704.             */
  1705.         switch (wParam) {
  1706.         case DL_ZOOM_IN:
  1707.             if (GameState == SETTING_SHOT) {
  1708.             /* User wants to zoom in a visible area.
  1709.             */
  1710.             GameState = ZOOMING_IN;
  1711.             old_capture_zoom = SetCapture(hwnd);
  1712.             old_cursor_zoom = SetCursor(LoadCursor(hInst, "Zoom_In"));
  1713.                     }
  1714.             break;
  1715.         case DL_ZOOM_OUT:
  1716.             /* User wants to go back to the previous sight.
  1717.             */
  1718.             if (PopView() == TRUE) {
  1719.             RECT wrect;
  1720.  
  1721.             wrect.left = wrect.top = 0;
  1722.             wrect.right = client_width;
  1723.             wrect.bottom = client_height;
  1724.             InvalidateRect(hwnd, &wrect, TRUE);
  1725.             UpdateInsideTableRegion();
  1726.             }
  1727.             else {
  1728.             char msg[100], ttl[50];
  1729.  
  1730.             LoadString(hInst, IDS_NOBACKZ_MSG, msg, 100);
  1731.             LoadString(hInst, IDS_NOBACKZ_TTL, ttl, 50);
  1732.             MessageBox(hwnd, msg, ttl, MB_OK | MB_ICONEXCLAMATION);
  1733.                     }
  1734.             break;
  1735.         case DL_VIEW_LEFT: {
  1736.             /* User wants to see the left-hand part of the
  1737.                carom-table.
  1738.                     */
  1739.             RECT wrect;
  1740.  
  1741.                     PushView();
  1742.             ComputeLeftTableParams();
  1743.             wrect.left = wrect.top = 0;
  1744.             wrect.right = client_width;
  1745.                     wrect.bottom = client_height;
  1746.             InvalidateRect(hwnd, &wrect, TRUE);
  1747.             UpdateInsideTableRegion();
  1748.             break;
  1749.                 }
  1750.         case DL_VIEW_RIGHT: {
  1751.             /* User wants to see the right-hand part of the
  1752.                carom-table.
  1753.                     */
  1754.             RECT wrect;
  1755.  
  1756.                     PushView();
  1757.             ComputeRightTableParams();
  1758.             wrect.left = wrect.top = 0;
  1759.             wrect.right = client_width;
  1760.                     wrect.bottom = client_height;
  1761.             InvalidateRect(hwnd, &wrect, TRUE);
  1762.             UpdateInsideTableRegion();
  1763.             break;
  1764.                 }
  1765.         case DL_VIEW_FULL: {
  1766.             /* User wants to see the whole of the carom-table.
  1767.                     */
  1768.             RECT wrect;
  1769.  
  1770.                     PushView();
  1771.             ComputeFullTableParams();
  1772.             wrect.left = wrect.top = 0;
  1773.             wrect.right = client_width;
  1774.                     wrect.bottom = client_height;
  1775.             InvalidateRect(hwnd, &wrect, TRUE);
  1776.             UpdateInsideTableRegion();
  1777.             break;
  1778.                 }
  1779.         case DL_POWER_EDIT:
  1780.             if (GameState == SETTING_SHOT) {
  1781.             /* User is modifying the text-window that contains
  1782.                the actual shot-power. We read the new value that
  1783.                she typed in. We also store the new value if it
  1784.                is valid (it is a number, not letters).
  1785.             */
  1786.             int value, ok;
  1787.  
  1788.             value = GetDlgItemInt(hDlg, DL_POWER_EDIT, &ok, TRUE);
  1789.             if (!ok) {
  1790.                 return 1L;
  1791.             }
  1792.             value = clamp(value, POWER_MIN, POWER_MAX);
  1793.             SetScrollPos(GetDlgItem(hDlg, DL_POWER_SCROLL), SB_CTL,
  1794.                      value, TRUE);
  1795.             shot_power = value;
  1796.             }
  1797.             break;
  1798.         case DL_POWER_LOW:
  1799.         case DL_POWER_MEDIUM:
  1800.         case DL_POWER_HIGH:
  1801.             if (GameState == SETTING_SHOT) {
  1802.             /* User clicked on one of the set-exact-power buttons.
  1803.                We store the value corresponding to that button.
  1804.             */
  1805.             int value;
  1806.  
  1807.             value = wParam == DL_POWER_LOW ? POWER_LOW_VALUE :
  1808.                 (wParam == DL_POWER_MEDIUM ? POWER_MEDIUM_VALUE :
  1809.                 POWER_HIGH_VALUE);
  1810.             SetDlgItemInt(hDlg, DL_POWER_EDIT, value, TRUE);
  1811.                     }
  1812.             break;
  1813.         case DL_HOR_EDIT:
  1814.             if (GameState == SETTING_SHOT) {
  1815.             /* User is modifying the text window that contains the
  1816.                actual value of the horizontal component of the spin.
  1817.                We read and store the new value if it is valid.
  1818.             */
  1819.             int value, ok;
  1820.  
  1821.             value = GetDlgItemInt(hDlg, DL_HOR_EDIT, &ok, TRUE);
  1822.             if (!ok) {
  1823.                 return 1L;
  1824.             }
  1825.             value = clamp(value, SPIN_MIN, SPIN_MAX);
  1826.             SetScrollPos(GetDlgItem(hDlg, DL_HOR_SCROLL), SB_CTL,
  1827.                      value, TRUE);
  1828.             horizontal_spin = value;
  1829.             /* Redraw the ball-image as well...
  1830.             */
  1831.             DoPaintDlg(hDlg, FALSE);
  1832.                     }
  1833.             break;
  1834.         case DL_VER_EDIT:
  1835.             if (GameState == SETTING_SHOT) {
  1836.             /* Same as at the horizontal component above, but we
  1837.                use the inverted value of the number visible in the
  1838.                text window to set the scroll bar position.
  1839.                This is because it looks better if positive numbers
  1840.                refer to top spin instead of back spin.
  1841.             */
  1842.             int value, ok;
  1843.  
  1844.             value = GetDlgItemInt(hDlg, DL_VER_EDIT, &ok, TRUE);
  1845.             if (!ok) {
  1846.                 return 1L;
  1847.             }
  1848.             SetScrollPos(GetDlgItem(hDlg, DL_VER_SCROLL), SB_CTL,
  1849.                      -value, TRUE);
  1850.             vertical_spin = -value;
  1851.             /* Redraw the ball-image as well...
  1852.             */
  1853.             DoPaintDlg(hDlg, FALSE);
  1854.                     }
  1855.             break;
  1856.         case DL_NO_SPIN:
  1857.                     if (GameState == SETTING_SHOT) {
  1858.             /* User clicked on the "NO SPIN" button which is meant
  1859.                to set the cue to hit the centre of the cue ball.
  1860.                We set the edit boxes (which set the scroll bars)
  1861.                and redraw the image of the cue ball.
  1862.             */
  1863.             SetDlgItemInt(hDlg, DL_HOR_EDIT, 0, TRUE);
  1864.             SetDlgItemInt(hDlg, DL_VER_EDIT, 0, TRUE);
  1865.             DoPaintDlg(hDlg, FALSE);
  1866.                     }
  1867.             break;
  1868.         case DL_TAKE_SHOT:
  1869.             if (GameState == BEFORE_GAME) {
  1870.             char ttl[40], msg[200];
  1871.  
  1872.                         LoadString(hInst, IDS_BEGIN_FIRST_TTL, ttl, 40);
  1873.             LoadString(hInst, IDS_BEGIN_FIRST_MSG, msg, 200);
  1874.             MessageBox(hwnd, msg, ttl, MB_ICONEXCLAMATION | MB_OK);
  1875.             }
  1876.             else if (GameState == AFTER_GAME) {
  1877.             char ttl[40], msg[200];
  1878.  
  1879.                         LoadString(hInst, IDS_BEGIN_NEW_TTL, ttl, 40);
  1880.             LoadString(hInst, IDS_BEGIN_NEW_MSG, msg, 200);
  1881.             MessageBox(hwnd, msg, ttl, MB_ICONEXCLAMATION | MB_OK);
  1882.             }
  1883.             else if (GameState == SETTING_SHOT) {
  1884.             /* User pressed the "TAKE SHOT" button. According to
  1885.                the current settings, we must compute and display all
  1886.                the states of the balls after the shot is taken.
  1887.                Meanwhile the cursor is changed to a sand-watch shape.
  1888.             */
  1889.             float direction;
  1890.             int good_shot, extra_points, first_ball, i;
  1891.             HDC hDC;
  1892.  
  1893.             /* First of all, save current parameters so that undo will
  1894.                be possible:
  1895.             */
  1896.             memcpy(ball_save, ball, sizeof(ball_save));
  1897.             memcpy(&sight_line_end_save, &sight_line_end, sizeof(sight_line_end));
  1898.             memcpy(sight_extra_save, sight_extra, sizeof(sight_extra_save));
  1899.                         memcpy(sight_extra_drawn_save, sight_extra_drawn, sizeof(sight_extra_drawn_save));
  1900.             cueball_save = cueball;
  1901.             horizontal_spin_save = horizontal_spin;
  1902.             vertical_spin_save = vertical_spin;
  1903.             shot_power_save = shot_power;
  1904.             memcpy(score_save, score, sizeof(score_save));
  1905.                         first_shot_save = first_shot;
  1906.  
  1907.             /* Capture mouse and change icon to sand-watch:
  1908.             */
  1909.             SetCapture(hwnd);
  1910.             SetCursor(LoadCursor(NULL, IDC_WAIT));
  1911.  
  1912.             /* Clear table if path for only last shot is
  1913.                required:
  1914.             */
  1915.             if (draw_path == CM_PATH_LAST) {
  1916.                 InvalidateRgn(hwnd,
  1917.                       my_inside_table_region,
  1918.                       TRUE);
  1919.                 SendMessage(hwnd, WM_PAINT, NULL, NULL);
  1920.                         }
  1921.             hDC = GetDC(hwnd);
  1922.             SelectObject(hDC, my_client_region);
  1923.             DrawSightLines(hDC, (FPOINT *) sight_extra, sight_extra_drawn);
  1924.             ReleaseDC(hwnd, hDC);
  1925.  
  1926.             /* Compute this shot:
  1927.             */
  1928.             direction = atan2(sight_line_end.y - ball[cueball].y,
  1929.                       sight_line_end.x - ball[cueball].x);
  1930.             if (direction < 0) {
  1931.                 direction += 2 * M_PI;
  1932.             }
  1933.             compute_all_phases(vertical_spin, horizontal_spin, shot_power,
  1934.                        direction, cueball, &good_shot, &extra_points,
  1935.                        &first_ball);
  1936.             ReleaseCapture();
  1937.  
  1938.             switch (GameStyle) {
  1939.                 case DL_NG_GAME0:
  1940.                 if (good_shot &&
  1941.                     ((first_shot == FALSE) || (first_ball == 2))) {
  1942.                     score[cueball] += 1 + CushionPoints * extra_points;
  1943.                 }
  1944.                 else {
  1945.                     cueball = 1 - cueball;
  1946.                 }
  1947.                 break;
  1948.                 case DL_NG_GAME1:
  1949.                 if (good_shot && (extra_points >= 1) &&
  1950.                     ((first_shot == FALSE) || (first_ball == 2))) {
  1951.                     score[cueball] += 1 + CushionPoints * (extra_points - 1);
  1952.                 }
  1953.                 else {
  1954.                     cueball = 1 - cueball;
  1955.                 }
  1956.                                 break;
  1957.                 case DL_NG_GAME3:
  1958.                 if (good_shot && (extra_points >= 3) &&
  1959.                     ((first_shot == FALSE) || (first_ball == 2))) {
  1960.                     score[cueball] += 1 + CushionPoints * (extra_points - 3);
  1961.                 }
  1962.                 else {
  1963.                     cueball = 1 - cueball;
  1964.                 }
  1965.                                 break;
  1966.             }
  1967.                         first_shot = FALSE;
  1968.  
  1969.             if (EnableUndo) {
  1970.                 EnableMenuItem(GetMenu(hwnd),
  1971.                        CM_UNDO_SHOT,
  1972.                        MF_BYCOMMAND | MF_ENABLED);
  1973.                         }
  1974.  
  1975.             EnableMenuItem(GetMenu(hwnd), CM_BROKEN_LINE,
  1976.                               MF_BYCOMMAND | MF_GRAYED);
  1977.             EnableMenuItem(GetMenu(hwnd), DL_ZOOM_IN,
  1978.                               MF_BYCOMMAND | MF_GRAYED);
  1979.             EnableMenuItem(GetMenu(hwnd), DL_TAKE_SHOT,
  1980.                               MF_BYCOMMAND | MF_GRAYED);
  1981.             EnableMenuItem(GetMenu(hwnd), CM_NEW_GAME,
  1982.                               MF_BYCOMMAND | MF_GRAYED);
  1983.  
  1984.             shot_history = (unsigned huge *) GlobalLock(hglbShotHistory);
  1985.             GameState = BALLS_MOVING;
  1986.             last_displayed_frame = 0;
  1987.             dwTakeShotTime = GetTickCount();
  1988.             for (i = 0; i < 2; i++) {
  1989.                 MessageBeep(-1);
  1990.                         }
  1991.                         if (TimeredShotDisplay) {
  1992.                 globalidTimer1 = SetTimer(NULL, 1994, 50,
  1993.                                                       (TIMERPROC) lpShotTimerProc); 
  1994.                 globalidTimer2 = SetTimer(NULL, 1995, 50,
  1995.                                               (TIMERPROC) lpShotTimerProc);    
  1996.             }
  1997.                         else {
  1998.                 while (GameState == BALLS_MOVING) {
  1999.                 tprcShotTimerProc(NULL, NULL, NULL, GetTickCount());
  2000.                             }
  2001.             }
  2002.                     }
  2003.             break;
  2004.                 default:
  2005.                      /* MessageBeep(MB_ICONEXCLAMATION); */
  2006.                      break;
  2007.             }
  2008.             break;
  2009.  
  2010.     case WM_VSCROLL:
  2011.     case WM_HSCROLL:
  2012.         if (GameState == SETTING_SHOT) {
  2013.         /* User performed some actions with one of the three scroll
  2014.            bars located in this dialog box.
  2015.            We compute the new value, store it, set the scroll bar to
  2016.            the new value, set the appropriate text box to the new
  2017.            value and redraw the image of the cue ball.
  2018.         */
  2019.         WORD wScrollCode = wParam;      /* scroll bar code */
  2020.         int nPos = LOWORD(lParam);     /* current position of scroll box */
  2021.         HWND hwndCtl = (HWND) HIWORD(lParam); /* handle of the control */
  2022.         int mi, ma, delta, invertflag, ballredraw;
  2023.         int *storage;
  2024.         WORD edit;
  2025.  
  2026.         switch (wScrollCode) {
  2027.             case SB_LINEDOWN:        /* Scroll one line down. */
  2028.             delta = 1;
  2029.             break;
  2030.             case SB_LINEUP:            /* Scroll one line up. */
  2031.             delta = -1;
  2032.             break;
  2033.             case SB_PAGEDOWN:        /* Scroll one page down. */
  2034.             delta = 10;
  2035.             break;
  2036.             case SB_PAGEUP:            /* Scroll one page up. */
  2037.             delta = -10;
  2038.             break;
  2039.             case SB_THUMBPOSITION:        /* Scroll to abs. position. */
  2040.             case SB_THUMBTRACK:        /* Scroll to abs. Position. */
  2041.             /* No calibration action is needed! */
  2042.             break;
  2043.             default:
  2044.             return 1L;
  2045.         }
  2046.         if (hwndCtl == GetDlgItem(hDlg, DL_HOR_SCROLL)) {
  2047.             mi = SPIN_MIN;
  2048.             ma = SPIN_MAX;
  2049.             storage = &horizontal_spin;
  2050.             edit = DL_HOR_EDIT;
  2051.             invertflag = 1;
  2052.             ballredraw = 1;
  2053.         }
  2054.         else if (hwndCtl == GetDlgItem(hDlg, DL_VER_SCROLL)) {
  2055.             mi = SPIN_MIN;
  2056.             ma = SPIN_MAX;
  2057.             storage = &vertical_spin;
  2058.             edit = DL_VER_EDIT;
  2059.             invertflag = -1;
  2060.             ballredraw = 1;
  2061.         }
  2062.         else if (hwndCtl == GetDlgItem(hDlg, DL_POWER_SCROLL)) {
  2063.             mi = POWER_MIN;
  2064.             ma = POWER_MAX;
  2065.             storage = &shot_power;
  2066.             edit = DL_POWER_EDIT;
  2067.             invertflag = 1;
  2068.             ballredraw = 0;
  2069.         }
  2070.         else {
  2071.             /* Unknown Scroll Bar. Do nothing: */
  2072.             return 1L;
  2073.         }
  2074.  
  2075.         /* Set new value to both scroll bar and edit window */
  2076.         if (wScrollCode != SB_THUMBPOSITION &&
  2077.             wScrollCode != SB_THUMBTRACK) {
  2078.             nPos = *storage + delta;
  2079.         }
  2080.         nPos = clamp(nPos, mi, ma);
  2081.         *storage = nPos;
  2082.         SetScrollPos(hwndCtl, SB_CTL, nPos, TRUE);
  2083.         SetDlgItemInt(hDlg, edit, nPos * invertflag, TRUE);
  2084.         if (ballredraw) {
  2085.             DoPaintDlg(hDlg, FALSE);
  2086.         }
  2087.             }
  2088.         break;
  2089.         default:
  2090.             return 0L;
  2091.     }  // switch
  2092.  
  2093.     return 1L;
  2094. }  // end of ParDlgBoxProc()
  2095.  
  2096.  
  2097. /*************************************************************************
  2098.   DoPaint -- this is the WM_PAINT action routine for the main window.
  2099.   The visible part of the carom-table and the visible balls are redrawn
  2100.   according to the current settings.
  2101.  
  2102.  *************************************************************************/
  2103. void DoPaint()
  2104. {
  2105.     PAINTSTRUCT    PaintInfo;
  2106.     HDC        hDC;
  2107.     int     i;
  2108.  
  2109.     BeginPaint(hwnd, &PaintInfo);
  2110.  
  2111.     hDC = PaintInfo.hdc;
  2112.  
  2113.     // save device context; easiest way to preserve current state
  2114.     SaveDC(hDC);
  2115.  
  2116.     /* Create rectangular region for the drawable area:
  2117.     */
  2118.     SelectObject(hDC, my_client_region);
  2119.  
  2120.     /* Set drawing mode to COPY:
  2121.     */
  2122.     SetROP2(hDC, R2_COPYPEN);
  2123.  
  2124.     /* Draw the table:
  2125.     */
  2126.     SelectObject(hDC, my_wood_pen);
  2127.     SelectObject(hDC, my_wood_brush);
  2128.     Rectangle(hDC, X_PHYTOCLI(0), Y_PHYTOCLI(TOP + CUSHION_WIDTH + WOOD_WIDTH),
  2129.            X_PHYTOCLI(RIGHT + CUSHION_WIDTH + WOOD_WIDTH), Y_PHYTOCLI(0));
  2130.     SelectObject(hDC, my_cush_pen);
  2131.     SelectObject(hDC, my_cush_brush);
  2132.     Rectangle(hDC, X_PHYTOCLI(WOOD_WIDTH), Y_PHYTOCLI(TOP + CUSHION_WIDTH),
  2133.            X_PHYTOCLI(RIGHT + CUSHION_WIDTH), Y_PHYTOCLI(WOOD_WIDTH));
  2134.     SelectObject(hDC, my_cloth_brush);
  2135.     Rectangle(hDC, X_PHYTOCLI(LEFT), Y_PHYTOCLI(TOP),
  2136.                    X_PHYTOCLI(RIGHT), Y_PHYTOCLI(BOTTOM));
  2137.  
  2138.     /* Draw the spots:
  2139.     */
  2140.     SetPixel(hDC, SPOT_X1, SPOT_Y1, RGB(255, 255, 255));
  2141.     SetPixel(hDC, SPOT_X1, SPOT_Y2, RGB(255, 255, 255));
  2142.     SetPixel(hDC, SPOT_X1, SPOT_Y3, RGB(255, 255, 255));
  2143.     SetPixel(hDC, SPOT_X2, SPOT_Y2, RGB(255, 255, 255));
  2144.     SetPixel(hDC, SPOT_X3, SPOT_Y2, RGB(255, 255, 255));
  2145.  
  2146.     if (GameState != BALLS_MOVING) {
  2147.     /* Draw balls and sight_line to the inside of the table:
  2148.     */
  2149.     SelectObject(hDC, my_inside_table_region);
  2150.  
  2151.     /*   Then get a "black pen - white brush" for drawing the first ball.
  2152.     */
  2153.     for (i = 0; i < N; i++) {
  2154.         SelectObject(hDC, my_ball_pen[i]);
  2155.         SelectObject(hDC, my_ball_brush[i]);
  2156.         Ellipse(hDC, X_PHYTOCLI(ball[i].x) - RADIUS,
  2157.              Y_PHYTOCLI(ball[i].y) - RADIUS,
  2158.              X_PHYTOCLI(ball[i].x) + RADIUS + 1,
  2159.              Y_PHYTOCLI(ball[i].y) + RADIUS + 1);
  2160.     }
  2161.  
  2162.         DrawSightLines(hDC, (FPOINT *) sight_extra, sight_extra_drawn);
  2163.     }
  2164.  
  2165.     RestoreDC(hDC, -1);
  2166.     EndPaint(hwnd, &PaintInfo);
  2167. } // end of DoPaint()
  2168.  
  2169.  
  2170. /*************************************************************************
  2171.   wmCreate -- action routine for the WM_CREATE message.  This routine
  2172.   sets up colors for use in the painting routines.
  2173.  *************************************************************************/
  2174. void wmCreate(void)
  2175. {
  2176.     LOGBRUSH logbrush;
  2177.     static COLORREF ball_colours[N] = {
  2178.     RGB(255, 255, 255),
  2179.     RGB(255, 255, 0),
  2180.     RGB(255, 0, 0)
  2181.     };
  2182.     int i;
  2183.  
  2184.     /* Allocate global memory for shot buffer:
  2185.     */
  2186.     hglbShotHistory = GlobalAlloc(GMEM_MOVEABLE, SHOT_BUFFER_SIZE);
  2187.     if (hglbShotHistory == NULL) {
  2188.     char msg[100], ttl[50];
  2189.  
  2190.     LoadString(hInst, IDS_MEMORY_MSG, msg, 100);
  2191.     LoadString(hInst, IDS_MEMORY_TTL, ttl, 50);
  2192.     MessageBox(hwnd, msg, ttl, MB_OK | MB_ICONEXCLAMATION);
  2193.     DestroyWindow(hwnd);
  2194.         return;
  2195.     }
  2196.  
  2197.     /* All the brushes will be SOLID:
  2198.     */
  2199.     logbrush.lbStyle = BS_SOLID;
  2200.  
  2201.     my_wood_pen = CreatePen(PS_SOLID, 1, RGB(0, 0, 0));
  2202.     logbrush.lbColor = RGB(128, 0, 0);
  2203.     my_wood_brush = CreateBrushIndirect(&logbrush);
  2204.  
  2205.     my_cush_pen = CreatePen(PS_SOLID, 1, RGB(0, 32, 0));
  2206.     logbrush.lbColor = RGB(0, 73, 0);
  2207.     my_cush_brush = CreateBrushIndirect(&logbrush);
  2208.  
  2209.     my_cloth_pen = CreatePen(PS_SOLID, 1, RGB(0, 128, 0));
  2210.     logbrush.lbColor = RGB(0, 128, 0);
  2211.     my_cloth_brush = CreateBrushIndirect(&logbrush);
  2212.  
  2213.     my_rectangle_pen = CreatePen(PS_DOT, 1, RGB(255, 255, 255));
  2214.     my_sight_line_pen = CreatePen(PS_SOLID, 1, RGB(0, 128, 0));
  2215.     my_sight_line_extra_pen = CreatePen(PS_DOT, 1, RGB(255, 255, 255));
  2216.  
  2217.     for (i = 0; i < N; i++) {
  2218.     my_ball_pen[i] = CreatePen(PS_SOLID, 1, RGB(0, 0, 0));
  2219.     logbrush.lbColor = ball_colours[i];
  2220.     my_ball_brush[i] = CreateBrushIndirect(&logbrush);
  2221.     }
  2222.  
  2223.     pens_brushes_present = TRUE;
  2224. } // end of wmCreate()
  2225.  
  2226. void UnCreate(void)
  2227. {
  2228.     int i;
  2229.  
  2230.     /* If shot timer is still active, remove timer!
  2231.     */
  2232.     if (globalidTimer1) {
  2233.     KillTimer(NULL, globalidTimer1);
  2234.     KillTimer(NULL, globalidTimer2);
  2235.     globalidTimer1 = globalidTimer2 = 0;
  2236.     }
  2237.  
  2238.     if (pens_brushes_present) {
  2239.     DeleteObject(my_wood_pen);
  2240.     DeleteObject(my_wood_brush);
  2241.     DeleteObject(my_cush_pen);
  2242.     DeleteObject(my_cush_brush);
  2243.     DeleteObject(my_cloth_pen);
  2244.     DeleteObject(my_cloth_brush);
  2245.     DeleteObject(my_rectangle_pen);
  2246.     DeleteObject(my_sight_line_pen);
  2247.     DeleteObject(my_sight_line_extra_pen);
  2248.     for (i = 0; i < N; i++) {
  2249.         DeleteObject(my_ball_pen[i]);
  2250.         DeleteObject(my_ball_brush[i]);
  2251.     }
  2252.     pens_brushes_present = FALSE;
  2253.     }
  2254.     /* Delete ball bitmaps:
  2255.     */
  2256.     if (hbmpTemp) {
  2257.     DeleteObject(hbmpTemp);
  2258.     }
  2259.     if (hbmpBallMask) {
  2260.     DeleteObject(hbmpBallMask);
  2261.     }
  2262.     if (hbmpClothBall) {
  2263.     DeleteObject(hbmpClothBall);
  2264.     }
  2265.     for (i = 0; i < N; i++) {
  2266.     if (hbmpBall[i]) {
  2267.         DeleteObject(hbmpBall[i]);
  2268.     }
  2269.     }
  2270.     /* Free shot history buffer:
  2271.     */
  2272.     if (hglbShotHistory) {
  2273.     GlobalFree(hglbShotHistory);
  2274.     hglbShotHistory = NULL;
  2275.     }
  2276.  
  2277.     /* Free sine, cosine and arctan tables:
  2278.     */
  2279.     uninit_trigo();
  2280.  
  2281.     /* Close Help window:
  2282.     */
  2283.     WinHelp(hwnd, szHelpFileName, HELP_QUIT, 0L);
  2284. }
  2285.  
  2286. /*********************************************************************
  2287.  cmXxxx routines -- these are action routines for menu commands
  2288. *********************************************************************/
  2289.  
  2290. // *************************************************************************
  2291. // cmAbout -- invoke the "About" dialog.  Its return code is ignored, since
  2292. // the About dialog doesn't return anything to the program.
  2293. // *************************************************************************
  2294. void cmAbout(void)
  2295. {
  2296.     lpDlgProc = (DLGPROC) MakeProcInstance( (FARPROC) DlgBoxProc, hInst);
  2297.     DialogBox(hInst, "About", hwnd, lpDlgProc);
  2298.     FreeProcInstance ((FARPROC) lpDlgProc);
  2299. }  // end of cmAbout()
  2300.  
  2301. // *************************************************************************
  2302. // cmNewGame -- invoke the "New game" dialog.  
  2303. // *************************************************************************
  2304. int cmNewGame(void)
  2305. {
  2306.     int result;
  2307.  
  2308.     lpNewGameDlgProc = (DLGPROC) MakeProcInstance( (FARPROC) NewGameDlgBoxProc, hInst);
  2309.     result = DialogBox(hInst, "NewGame", hwnd, lpNewGameDlgProc);
  2310.     FreeProcInstance ((FARPROC) lpNewGameDlgProc);
  2311.     return result == DL_NG_OK;
  2312. }  // end of cmAbout()
  2313.  
  2314. /* Timer procedure: When the user takes the shot, this callback function is initialised and
  2315.    it will be called once every 1/25 seconds.
  2316. */
  2317.  
  2318. #pragma argsused
  2319. void CALLBACK tprcShotTimerProc(HWND hWnd, UINT msg, UINT idTimer, DWORD dwTime)
  2320. {
  2321.     unsigned i, j, k;
  2322.     int beep_cush = 0;
  2323.     int beep_ball = 0;
  2324.     unsigned frame_size = shot_history[0] * 2 + 1;
  2325.     unsigned huge *prev_frame;
  2326.     unsigned huge *this_frame;
  2327.     unsigned utmp;
  2328.     int end_animation = FALSE;
  2329.     HDC hDC, hdctmp, hdctmp2;
  2330.     POINT prev, curr;
  2331.  
  2332.     /* Compute frame number to be displayed:
  2333.     */
  2334.     i = (dwTime - dwTakeShotTime) / (TIME_SLOT * 1000);
  2335. //!!!i = last_displayed_frame + 1;
  2336.     if (i >= shot_history[1] - 1) {
  2337.     /* End of shot. Go display the very last frame, which may not have been displayed
  2338.        yet.
  2339.     */
  2340.     i = shot_history[1] - 1;
  2341.     end_animation = TRUE; 
  2342.     }
  2343.  
  2344.     /* Examine whether there were collisions since the the last drawn frame:
  2345.     */
  2346.     j = last_displayed_frame;
  2347.     for (k = j + 1; k <= i; k++) {
  2348.     utmp = shot_history[2 + k * frame_size + frame_size - 1];
  2349.     beep_cush += LOBYTE(utmp);
  2350.         beep_ball += HIBYTE(utmp);
  2351.     }
  2352.  
  2353.     /* Beep if there have been some collisions:
  2354.     */
  2355.     if (beep_ball > 0) {
  2356.     MessageBeep(-1);
  2357.     }
  2358.  
  2359.     /* Locate the frame to be drawn and the previous frame:
  2360.     */
  2361.     prev_frame = shot_history + (2 + j * frame_size);
  2362.     this_frame = shot_history + (2 + i * frame_size);
  2363.  
  2364.     /* Get device context:
  2365.     */
  2366.     hDC = GetDC(hwnd);
  2367.  
  2368.     /* Select rectangular region:
  2369.     */
  2370.     SelectObject(hDC, my_inside_table_region);
  2371.  
  2372.     /* Process all the balls: Clear one from previous position and draw it to new position.
  2373.        Then go to next ball.
  2374.     */
  2375.     hdctmp = CreateCompatibleDC(hDC);
  2376.     hdctmp2 = CreateCompatibleDC(hDC);
  2377.     SelectObject(hdctmp, hbmpTemp);
  2378.  
  2379.     for (k = 0; k < shot_history[0]; k++) {
  2380.     /* Process kth ball:
  2381.     */
  2382.     /* Determine client coordinates for both previous and current position:
  2383.         */
  2384.     prev.x = X_PHYTOCLI(UNFIXED(*prev_frame++));
  2385.     prev.y = Y_PHYTOCLI(UNFIXED(*prev_frame++));
  2386.     curr.x = X_PHYTOCLI(UNFIXED(*this_frame++));
  2387.     curr.y = Y_PHYTOCLI(UNFIXED(*this_frame++));
  2388.     if (draw_path == CM_PATH_NONE) {
  2389.         /* Delete ball from previous location:
  2390.         */
  2391.         /* Copy area of previous position from screen:
  2392.         */
  2393.         BitBlt(hdctmp, 0, 0, 2 * RADIUS + 1, 2 * RADIUS + 1,
  2394.            hDC, prev.x - RADIUS, prev.y - RADIUS, SRCCOPY);
  2395.         /* Fill the previous ball-shape with cloth colour:
  2396.         */
  2397.         SelectObject(hdctmp2, hbmpBallMask);
  2398.         BitBlt(hdctmp, 0, 0, 2 * RADIUS + 1, 2 * RADIUS + 1,
  2399.            hdctmp2, 0, 0, SRCAND);
  2400.         SelectObject(hdctmp2, hbmpClothBall);
  2401.         BitBlt(hdctmp, 0, 0, 2 * RADIUS + 1, 2 * RADIUS + 1,
  2402.            hdctmp2, 0, 0, SRCPAINT);
  2403.         /* Draw the ball to new position:
  2404.         */
  2405.         SelectObject(hdctmp2, hbmpBallMask);
  2406.         BitBlt(hdctmp, curr.x - prev.x, curr.y - prev.y,
  2407.                2 * RADIUS + 1, 2 * RADIUS + 1,
  2408.            hdctmp2, 0, 0, SRCAND);
  2409.         SelectObject(hdctmp2, hbmpBall[k]);
  2410.         BitBlt(hdctmp, curr.x - prev.x, curr.y - prev.y,
  2411.                2 * RADIUS + 1, 2 * RADIUS + 1,
  2412.            hdctmp2, 0, 0, SRCPAINT);
  2413.         /* Put result back to the screen (no flash!)
  2414.         */
  2415.         BitBlt(hDC, prev.x - RADIUS, prev.y - RADIUS,
  2416.             2 * RADIUS + 1, 2 * RADIUS + 1,
  2417.            hdctmp, 0, 0, SRCCOPY);
  2418.         }
  2419.     /* Get area of new position of the ball:
  2420.     */
  2421.     BitBlt(hdctmp, 0, 0, 2 * RADIUS + 1, 2 * RADIUS + 1,
  2422.            hDC, curr.x - RADIUS, curr.y - RADIUS, SRCCOPY);
  2423.     /* Draw the ball to new position:
  2424.         */
  2425.     SelectObject(hdctmp2, hbmpBallMask);
  2426.     BitBlt(hdctmp, 0, 0, 2 * RADIUS + 1, 2 * RADIUS + 1,
  2427.            hdctmp2, 0, 0, SRCAND);
  2428.     SelectObject(hdctmp2, hbmpBall[k]);
  2429.     BitBlt(hdctmp, 0, 0, 2 * RADIUS + 1, 2 * RADIUS + 1,
  2430.            hdctmp2, 0, 0, SRCPAINT);
  2431.     /* Put result back to the screen (no flash!)
  2432.     */
  2433.     BitBlt(hDC, curr.x - RADIUS, curr.y - RADIUS,
  2434.                 2 * RADIUS + 1, 2 * RADIUS + 1,
  2435.            hdctmp, 0, 0, SRCCOPY);
  2436.     }
  2437.  
  2438.     /* Draw the sposts:
  2439.     */
  2440.     if (draw_path == CM_PATH_NONE) {
  2441.         COLORREF colour;
  2442.  
  2443.     colour = GetPixel(hDC, SPOT_X1, SPOT_Y1);
  2444.     if (GetGValue(colour) && GetRValue(colour) + GetBValue(colour) == 0) {
  2445.         SetPixel(hDC, SPOT_X1, SPOT_Y1, RGB(255, 255, 255));
  2446.     }
  2447.     colour = GetPixel(hDC, SPOT_X1, SPOT_Y2);
  2448.     if (GetGValue(colour) && GetRValue(colour) + GetBValue(colour) == 0) {
  2449.         SetPixel(hDC, SPOT_X1, SPOT_Y2, RGB(255, 255, 255));
  2450.     }
  2451.     colour = GetPixel(hDC, SPOT_X1, SPOT_Y3);
  2452.     if (GetGValue(colour) && GetRValue(colour) + GetBValue(colour) == 0) {
  2453.         SetPixel(hDC, SPOT_X1, SPOT_Y3, RGB(255, 255, 255));
  2454.     }
  2455.     colour = GetPixel(hDC, SPOT_X2, SPOT_Y2);
  2456.     if (GetGValue(colour) && GetRValue(colour) + GetBValue(colour) == 0) {
  2457.         SetPixel(hDC, SPOT_X2, SPOT_Y2, RGB(255, 255, 255));
  2458.     }
  2459.     colour = GetPixel(hDC, SPOT_X3, SPOT_Y2);
  2460.     if (GetGValue(colour) && GetRValue(colour) + GetBValue(colour) == 0) {
  2461.         SetPixel(hDC, SPOT_X3, SPOT_Y2, RGB(255, 255, 255));
  2462.     }
  2463.     }
  2464.  
  2465.     DeleteDC(hdctmp);
  2466.     DeleteDC(hdctmp2);
  2467.  
  2468.     /* Note that the ith frame is the one last displayed:
  2469.     */
  2470.     last_displayed_frame = i;
  2471.  
  2472.     /* Every ball is now redrawn.
  2473.        Terminate timer procedure if this has been the last displayed frame:
  2474.        Kill timer, unlock memory object, draw sight line and update state of game:
  2475.     */
  2476.     if (end_animation) {
  2477.     POINT p;
  2478.         int erase_old_sight = FALSE;
  2479.  
  2480.     KillTimer(NULL, globalidTimer1);
  2481.     KillTimer(NULL, globalidTimer2);
  2482.     globalidTimer1 = globalidTimer2 = 0;
  2483.  
  2484.         GlobalUnlock(hglbShotHistory);
  2485.  
  2486.     UpdateScore();
  2487.  
  2488.         if (EnableBreak) {
  2489.         EnableMenuItem(GetMenu(hwnd), CM_BROKEN_LINE,
  2490.                       MF_BYCOMMAND | MF_ENABLED);
  2491.     }
  2492.     EnableMenuItem(GetMenu(hwnd), DL_ZOOM_IN,
  2493.                       MF_BYCOMMAND | MF_ENABLED);
  2494.     EnableMenuItem(GetMenu(hwnd), DL_TAKE_SHOT,
  2495.                       MF_BYCOMMAND | MF_ENABLED);
  2496.     EnableMenuItem(GetMenu(hwnd), CM_NEW_GAME,
  2497.                       MF_BYCOMMAND | MF_ENABLED);
  2498.  
  2499.     GameState = SETTING_SHOT;
  2500.  
  2501.     /* Has somebody won the game?
  2502.     */
  2503.     if ((cueball == cueball_save && cueball != WhoStarted &&
  2504.          score[cueball] >= WinningScore &&
  2505.          score[1 - cueball] < WinningScore) ||
  2506.         (cueball != cueball_save && cueball_save != WhoStarted &&
  2507.          score[cueball] >= WinningScore) ||
  2508.         (score[0] >= WinningScore && score[1] >= WinningScore))  {
  2509.         char ttl[40], msg[200];
  2510.  
  2511.         LoadString(hInst, IDS_WINNER_TTL, ttl, 40);
  2512.         if (score[0] >= WinningScore && score[1] >= WinningScore) {
  2513.                 LoadString(hInst, IDS_DRAW_GAME_MSG, msg, 200);
  2514.         }
  2515.             else {
  2516.         strcpy(msg, score[0] >= WinningScore ? player1_name :
  2517.                                player2_name);
  2518.         strcat(msg, ", ");
  2519.         LoadString(hInst, IDS_WINNER_MSG,
  2520.                msg + strlen(msg), 200 - strlen(msg));
  2521.         }
  2522.         MessageBox(hwnd, msg, ttl, MB_ICONINFORMATION | MB_OK);
  2523.             GameState = AFTER_GAME;
  2524.     }
  2525.     else if (cueball == cueball_save && cueball == WhoStarted &&
  2526.          score[cueball] >= WinningScore) {
  2527.         /* The player who started the game has reached the number of points
  2528.            to win. But, he/she has not won yet, because the other player
  2529.            has had one less visit than this player and he/she will have the
  2530.            final visit. If he/she also succeeds to reach enough points in
  2531.            this visit, the game will be drawn. If he/she remains under the
  2532.            number of points required to win the game, the player who started
  2533.            will be the winner.
  2534.            So we are going to start the final visit now:
  2535.         */
  2536.         char ttl[40], msg[200];
  2537.  
  2538.         p.x = X_PHYTOCLI(sight_line_end.x);
  2539.         p.y = Y_PHYTOCLI(sight_line_end.y);
  2540.         UpdateSightLine(hwnd, &p, FALSE);
  2541.             erase_old_sight = TRUE;
  2542.  
  2543.         LoadString(hInst, IDS_LAST_VISIT_TTL, ttl, 40);
  2544.         strcpy(msg, cueball == 1 ? player1_name : player2_name);
  2545.         strcat(msg, ", ");
  2546.         LoadString(hInst, IDS_LAST_VISIT_MSG,
  2547.                msg + strlen(msg), 200 - strlen(msg));
  2548.         MessageBox(hwnd, msg, ttl, MB_ICONINFORMATION | MB_OK);
  2549.         /* Switch to the other player and replace the balls
  2550.            into starting position as if this were the first shot:
  2551.         */
  2552.         cueball = 1 - cueball;
  2553.         ball[cueball].x = BOTTOMR_SPOT_X;
  2554.         ball[cueball].y = BOTTOMR_SPOT_Y;
  2555.         ball[1-cueball].x = BOTTOMM_SPOT_X;
  2556.         ball[1-cueball].y = BOTTOMM_SPOT_Y;
  2557.         ball[2].x = TOP_SPOT_X;
  2558.         ball[2].y = TOP_SPOT_Y;
  2559.         first_shot = TRUE;
  2560.         InvalidateRgn(hwnd, my_inside_table_region, TRUE);
  2561.     }
  2562.  
  2563.     p.x = X_PHYTOCLI(sight_line_end.x);
  2564.         p.y = Y_PHYTOCLI(sight_line_end.y); 
  2565.     UpdateSightLine(hwnd, &p, erase_old_sight);
  2566.     PostMessage(hParDlg, WM_COMMAND, DL_NO_SPIN, NULL);
  2567.     }
  2568.     ReleaseDC(hWnd,hDC);
  2569. }
  2570.