home *** CD-ROM | disk | FTP | other *** search
/ The Complete Encyclopedia of Games 1 2 / Microforum-Over1000GamesVol2-Win31-CD2of2.iso / sports / 2503 / src / graphics.c < prev    next >
C/C++ Source or Header  |  1995-05-26  |  33KB  |  937 lines

  1. /* This is "graphics.c", a part of the pool (billiards)-program
  2.                    
  3.                      "ANOTHER POOL".
  4.  
  5.    "graphics.c" uses CBGRX 2.0 and includes most of the graphic-functions.
  6.  
  7.    Copyright (C) 1995 by Gerrit Jahn (email: ub1g@rz.uni-karlsruhe.de)
  8.  
  9.    "ANOTHER POOL" is free software; you can redistribute it 
  10.    and/or modify it under the terms of the GNU General Public License 
  11.    as published by the Free Software Foundation; either version 2 of 
  12.    the License, or (at your option) any later version.
  13.  
  14.    This program is distributed in the hope that it will be useful,
  15.    but WITHOUT ANY WARRANTY; without even the implied warranty of
  16.    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  17.    GNU General Public License for more details.
  18.  
  19.    You should have received a copy of the GNU General Public License
  20.    along with GNU CC; see the file COPYING.  If not, write to
  21.    the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
  22.  
  23. /* ------------------------------ graphics.c ----------------------------- */
  24.  
  25. #include "c:/djgpp/contrib.20/libgrx/include/grx20.h"
  26. #include <pc.h>
  27. #include <math.h>
  28. #include <stdio.h>
  29. #include <stdlib.h>
  30. #include <time.h>
  31. #include <go32.h>
  32. #include "apool.h"
  33. /* unten: für die Kürzung der BitBlt-Routine */
  34. #include "c:/djgpp/contrib.20/libgrx/src/include/libgrx.h"
  35. #include "c:/djgpp/contrib.20/libgrx/src/include/clipping.h"
  36.  
  37. #ifdef S3
  38. #define BitBlt_S3( dx, dy, x1)\
  39.  bitblt( (x1)-Radius+64, 480+Radius ,(dx)-Radius, (dy)-Radius,\
  40.   (Radius*2)+1, (Radius*2)+1 )
  41. #else
  42. #define BitBlt_S3( dx, dy, x1) (*(CURC->gc_driver->bltr2v))\
  43.    ( curc_gc_frame, (dx) + curc_xoffset, (dy) + curc_yoffset,\
  44.      cont_gc_frame, (x1) + cont_xoffset, cont_yoffset,\
  45.      RADIUS + RADIUS + 1, RADIUS + RADIUS + 1, GrXOR )
  46. #endif
  47.  
  48. #define CRTC_ADR 0x3d4
  49. #define CRTC_DATA 0x3d5
  50.  
  51. #define CRT_REG_LOCK1 0x38
  52. #define CRT_REG_LOCK2 0x39
  53. #define CRT_SYS_CNFG 0x40
  54. #define GP_STAT 0x9ae8
  55.  
  56. #define FRGD_MIX 0xbae8
  57. #define FRGD_COLOR 0xa6e8
  58. #define MULTIFUNC_CNTL 0xbee8
  59. #define CUR_X 0x86e8
  60. #define CUR_Y 0x82e8
  61. #define MAJ_AXIS_PCNT 0x96e8
  62. #define MIN_AXIS_PCNT 0xbee8
  63. #define DESTX_DIASTP 0x8ee8
  64. #define DESTY_AXSTP 0x8ae8
  65. #define ERR_TERM 0x92e8
  66. #define CMD 0x9ae8
  67.      
  68. double oldalph=0.0, alph=0.0, spd=0.75, RADIUSL, e_winkel, old_min;
  69. int counter=0, oldx[BALLS], oldy[BALLS], olds[2] = {SPINPOSX,SPINPOSY};
  70. int old_ball, old_hole, old_paint=5;
  71. struct hole posl[6]; /* 6 Löcher */
  72. struct bande ban[18];
  73. GrCursor *cursor[5];
  74. GrMouseEvent me;
  75. GrFont *fontb, *fontm, *fonts;
  76. GrTextOption textopt = { NULL, {6}, {0}, GR_TEXT_DEFAULT, GR_BYTE_TEXT,
  77.  GR_ALIGN_CENTER, GR_ALIGN_TOP };
  78. GrTextOption textleft = { NULL, {6}, {0}, GR_TEXT_DEFAULT, GR_BYTE_TEXT,
  79.  GR_ALIGN_LEFT, GR_ALIGN_TOP };
  80. GrTextOption textcenter = { NULL, {6}, {0}, GR_TEXT_DEFAULT, GR_BYTE_TEXT,
  81.  GR_ALIGN_CENTER, GR_ALIGN_TOP };
  82. #ifndef S3
  83.  GrContext *context;
  84.  GrFrame *cont_gc_frame, *curc_gc_frame;
  85.  int curc_xoffset, curc_yoffset, cont_xoffset, cont_yoffset;
  86. #endif
  87.  
  88. void init(void)
  89.  {
  90. #ifdef S3
  91.  outportb(CRTC_ADR,CRT_REG_LOCK1);
  92.  outportb(CRTC_DATA,0x48);
  93.  outportb(CRTC_ADR,CRT_REG_LOCK2);
  94.  outportb(CRTC_DATA,0xa0);
  95.  outportb(CRTC_ADR,CRT_SYS_CNFG);
  96.  outportb(CRTC_ADR,CRT_SYS_CNFG);
  97.  outportb(CRTC_DATA,inportb(CRTC_DATA)|1);
  98. #endif
  99.  }
  100.  
  101. void init_graphics( int a, int b, int c)
  102.  { /* Öffnet Grafikschirm der Größe a*b, mit c Farben */
  103.  if( !GrMouseDetect() ) { MSG; printf("Mouse required...\a\n\n"); exit( 0 ); }
  104.  GrSetMode( GR_width_height_color_graphics, a, b, c );
  105.  GrSetColor( 0, 0, 0, 0 );
  106.  GrSetColor( 1, 0, 128, 0 );
  107.  GrSetColor( 2, 0, 96, 0 );
  108.  GrSetColor( 3, 96, 64, 32 );
  109.  GrSetColor( 4, 255, 0, 0 );
  110.  GrSetColor( 5, 255, 255, 255 );
  111.  GrSetColor( 6, 118, 138, 168 );
  112.  GrSetColor( 7, 192, 150, 64 );
  113.  GrSetColor( 8, 128, 80, 20 );
  114.  GrSetColor( 9, 54, 58, 62 );
  115.  GrSetColor( 10, 0, 128, 255 );
  116.  GrSetColor( 11, 255, 255, 0 );
  117.  GrSetColor( 12, 0, 110, 150 );
  118.  GrSetColor( 13, 255, 192, 192 );
  119.  GrSetColor( 14, 0, 0, 127 );
  120.  GrSetColor( 15, 0, 0, 255 );
  121.  GrMouseEventMode( 0 ); /* Maus im "poll"-mode */
  122.  GrMouseInit();
  123.  GrMouseSetAccel( 50, 2 ); /* unten: Mausbeweg. auf Tischgröße reduzieren ! */
  124.  GrMouseSetLimits(LEFT+RADIUS+1, UP+RADIUS+1, RIGHT-RADIUS-1, DOWN-RADIUS-1);
  125.  init();
  126.  make_mouse_cursor();
  127.  fontb = GrLoadFont("ncen29b.fnt");
  128.  textopt.txo_font = fontb;
  129.  fontm = GrLoadFont("xm8x16.fnt");
  130.  textleft.txo_font = fontm;
  131.  fonts = GrLoadFont("xm7x14.fnt");
  132.  textcenter.txo_font = fonts;
  133.  }
  134.  
  135. #ifdef S3 
  136. void
  137. bitblt(int sx1,int sy1,int dx1,int dy1,int sx,int sy)
  138.  {
  139.  while(inportw(GP_STAT)&0xc0); /* wait for 2 FIFOs */
  140.  outportw(MULTIFUNC_CNTL,0xa000);
  141.  outportw(FRGD_MIX,0x65); /* use foreground color set in next line and
  142.   just write (no and or or so) */
  143.  while(inportw(GP_STAT)&0xfe); /* wait for 7 FIFOs */
  144.  outportw(CUR_X,sx1);
  145.  outportw(CUR_Y,sy1);
  146.  outportw(DESTX_DIASTP,dx1);
  147.  outportw(DESTY_AXSTP,dy1);
  148.  outportw(MAJ_AXIS_PCNT,sx-1);
  149.  outportw(MIN_AXIS_PCNT,sy-1);
  150.  outportw(CMD,0xc0b3);
  151.  }
  152. #endif
  153.  
  154. void close_graphics( void )
  155.  {
  156.   GrMouseUnInit();
  157.   GrSetMode(GR_default_text,0,0,0);
  158.  }
  159.  
  160. void msg( char *out )
  161.  { /* Gibt einige Dinge unterhalb des Tisches aus */
  162.  GrFilledBox( 0, DOWN+25, 480 + 60*(fonts==NULL), DOWN+41, 0 );
  163.  GrDrawString( out, strlen(out), 240+30*(fonts==NULL), DOWN+25, &textcenter );
  164.  }
  165.  
  166. void plot_standings( void )
  167.  {
  168.  char out[15];
  169.  sprintf(out,"%d : %d",(int)(ply[0].points)%10000+(int)(ply[1].points)/10000,
  170.   (int)(ply[1].points)%10000 + (int)(ply[0].points)/10000 );
  171.  GrDrawString(out, strlen(out), GrMaxX() - (15+strlen(out))*8, 10, &textleft);
  172.  }
  173.  
  174. void plot_table( void )
  175. /* Hier wird der Tisch neu initialisiert und gemalt, die Banden und Löcher
  176.    werden aus der Datei "table.dat" ausgelesen und entsprechend gemalt. 
  177.    Gleichzeitig werden diese Daten für das Spiel aufbereitet... */
  178.  {
  179.  FILE *dat;
  180.  int i, j, poly[4][2], counter = 0;
  181.  double dummy, dummy2[4];
  182.  struct vect n;
  183.  char out[20];
  184.  GrClearScreen( 0 );
  185.  GrFilledBox( LEFT-10, UP-10, RIGHT+10, DOWN+10, 1 ); /* "Tuch" */ 
  186.  if( !(dat = fopen("table.dat","r")) ) 
  187.   {
  188.   GrSetMode(GR_default_text,0,0,0);
  189.   MSG;  
  190.   printf("error: can't find the file 'table.dat'. \n");
  191.   printf("create this file using 'creatabl.e.xe' ...\n\n");
  192.   exit(0);
  193.   }
  194.  fscanf(dat,"%lg",&RADIUSL );
  195.  for(i=0;i<6;i++) /* Teilkreise der Taschen malen */
  196.   {
  197.    for(j=0;j<2;j++) fscanf(dat,"%lg",&dummy2[j]);
  198.    GrFilledCircle( LEFT+dummy2[0], UP+dummy2[1], RADIUSL, 0 );
  199.    posl[i].p.x = dummy2[0] / DIFFX;
  200.    posl[i].p.y = dummy2[1] / DIFFX;
  201.   }
  202.  for(i=0;i<6;i++)  /* Banden initialisieren und malen */
  203.   {
  204.   for(j=0;j<4;j++)
  205.    { 
  206.     fscanf(dat,"%lg",&dummy); poly[j][0] = LEFT + (int)dummy; 
  207.     fscanf(dat,"%lg",&dummy); poly[j][1] = UP + (int)dummy; 
  208.    }
  209.   for( j=1;j<4;j++) /* Punkte den Banden zuweisen und Norm.vektoren berech. */
  210.    {
  211.    ban[counter].p0.x = poly[j-1][0]-LEFT;
  212.    ban[counter].p0.y = poly[j-1][1]-UP;
  213.    ban[counter].p1.x = poly[j][0]-LEFT;
  214.    ban[counter].p1.y = poly[j][1]-UP;
  215.    /* "Den" zum "Richtungs-Vektor" der Bande senkrechten Vektor bestimmen */
  216.    n.y = ban[counter].p1.x-ban[counter].p0.x;   
  217.    n.x =  - ( ban[counter].p1.y-ban[counter].p0.y );   
  218.    /* Normieren */
  219.    ban[counter].n.x = n.x / ( dummy = BETR( n ) );
  220.    ban[counter].n.y = n.y / dummy;
  221.    counter++;
  222.    }
  223.   GrFilledConvexPolygon( 4, poly, 2); 
  224.   }
  225.  fclose(dat);
  226.  /* dicke Banden wieder schmaler machen (optisch) */
  227.  GrFilledBox( 0,0, GrMaxX(), UP-11, 0 );
  228.  GrFilledBox( 0,DOWN+11, GrMaxX(), GrMaxY(), 0 );
  229.  GrFilledBox( 0,0, LEFT-11, GrMaxY(), 0 );
  230.  GrFilledBox( RIGHT+11,0, GrMaxX(), GrMaxY(), 0 );
  231.  GrDrawString("ANOTHER POOL", 12, GrMaxX()/2, 0, &textopt );
  232.  textcenter.txo_fgcolor.v = 9;
  233.  sprintf(out,"V %s",VERSION);
  234.  GrDrawString( out, strlen(out), 615, 0, &textcenter);
  235.  textcenter.txo_fgcolor.v = 6;
  236.  GrFilledCircle( SPINPOSX, SPINPOSY, 39, 5 );
  237.  GrCircle( SPINPOSX, SPINPOSY, 39, 9 );
  238.  calc_hot_spot();
  239.  calc_e_winkel();
  240.  plot_standings();
  241.  }
  242.  
  243. void speed( double p )
  244.  { /* Gibt den Geschwindigkeitsbalken am unteren Ende des Screens aus */
  245.  int mx = GrMaxX(), my = GrMaxY();
  246.  GrFilledBox( 0, my - 9, mx, my, 14 );
  247.  GrFilledBox( 0, my - 9, (int)(mx*p), my, 15 );
  248.  }
  249.  
  250. void plot_balls( void )
  251.  {  /* Zeichnet alle Bälle neu und löscht die alten */
  252.  int i;
  253.  for(i=0;i<BALLS;i++)
  254.   if( !k[i].stat && !k[i].nopaint )
  255.    {
  256.     BitBlt_S3( oldx[i], oldy[i], (k[i].col)<<5 );
  257.     oldx[i]=(LEFT+DIFFX*k[i].p.x+0.5);
  258.     oldy[i]=(UP+DIFFX*k[i].p.y)+0.5;
  259.     BitBlt_S3( oldx[i], oldy[i], (k[i].col)<<5 );
  260.    }
  261.  }
  262.  
  263. void plot_one_ball( int i )
  264.  { /* Malt im Gegensatz zu plot_balls() nur eine einzige Kugel neu */
  265.  oldx[i]=(LEFT+DIFFX*k[i].p.x)+0.5; 
  266.  oldy[i]=(UP+DIFFX*k[i].p.y)+0.5;
  267.  BitBlt_S3( oldx[i], oldy[i], (k[i].col)<<5 );
  268.  }
  269.  
  270. void set_spin( double sx, double sy, double sz )
  271.  { /* hier wird der (lokale) Spin der Weißen am Anfang gesetzt und gemalt */
  272.   GrFilledCircle( olds[0], olds[1], 7, 5 );
  273.   k[WHITE].ez = sz; k[WHITE].e.y = sy; k[WHITE].e.x = sx;
  274.   olds[0] = SPINPOSX + (31.0*k[WHITE].ez + 0.5);
  275.   olds[1] = SPINPOSY + (31.0*k[WHITE].e.y + 0.5);
  276.   GrFilledCircle( olds[0], olds[1], 7, 12 );
  277.  } 
  278.  
  279. void test_spin( int x, int y ) 
  280.  { /* Mouse-Abfrage für das Setzten des lokalen Spins... */
  281.  int oldx=x, oldy=y;
  282.  double sz, sp, dummy;
  283.  do
  284.   {
  285.   GrMouseGetEvent( GR_M_KEYPRESS | GR_M_MOTION | GR_M_BUTTON_CHANGE, &me );
  286.   if( me.flags & GR_M_MOTION )
  287.    {
  288.     /* sollte vielleicht umgerechnet werden in einen Winkel !!! !!! !!! ??? */
  289.     sz = (me.x - oldx)/31.0; sp = (me.y - oldy)/31.0; 
  290.     GrMouseWarp( oldx, oldy );
  291.     if( (k[WHITE].ez+sz)*(k[WHITE].ez+sz)+
  292.       (k[WHITE].e.y+sp)*(k[WHITE].e.y+sp) < 1.0 )
  293.       set_spin( 0.0, k[WHITE].e.y+sp, k[WHITE].ez+sz );
  294.     else if( (k[WHITE].e.y+sp) || (k[WHITE].ez+sz) )
  295.      {
  296.       dummy = sqrt( (k[WHITE].ez+sz)*(k[WHITE].ez+sz) +
  297.        (k[WHITE].e.y+sp)*(k[WHITE].e.y+sp) );
  298.       set_spin( 0.0, (k[WHITE].e.y+sp)/dummy, (k[WHITE].ez+sz)/dummy );
  299.      }
  300.    }
  301.   }
  302.  while( !(me.flags & GR_M_KEYPRESS) && !(me.flags & GR_M_MIDDLE_UP) && 
  303.   !(me.flags & GR_M_RIGHT_DOWN) );
  304. }
  305.  
  306. void wait_for_click( void )
  307. /* wartet auf Mouse-Click oder Tasten-Druck */
  308.  {
  309.  GrMouseEvent mev;
  310.  msg("press any key or mouse-button");
  311.  do
  312.   { GrMouseGetEvent( GR_M_BUTTON_CHANGE | GR_M_KEYPRESS, &mev ); }
  313.  while( !(mev.flags & ( GR_M_BUTTON_DOWN | GR_M_KEYPRESS )) ); 
  314.  if( (mev.flags & GR_M_KEYPRESS) && (mev.key == 27) ) stop_it();
  315.  }
  316.  
  317. void wink( double a ) /* Winkel zwischen Queue und Tisch darstellen */
  318.  {  /* Könnte mal einen dickeren Queue bekommen !!! !!! !!!*/
  319.  double b = a*M_PI/180.0;
  320.  GrFilledCircleArc( 480, 465, 4*RADIUS, 0, 900, GR_ARC_STYLE_CLOSE2, 5 );
  321.  GrCircleArc( 480, 465, 4*RADIUS+1, 0, 900, GR_ARC_STYLE_OPEN, 9 );
  322.  GrLine(480+4*(RADIUS+1)*cos( oldalph ),465-4*(RADIUS+1)*sin( oldalph ),
  323.   480+8*(RADIUS+1)*cos( oldalph ),465-8*(RADIUS+1)*sin( oldalph ), 0 );
  324.  GrLine(480+4*(RADIUS+1)*cos( b ),465-4*(RADIUS+1)*sin( b ),
  325.   480+8*(RADIUS+1)*cos( b ),465-8*(RADIUS+1)*sin( oldalph = b ), 8 );
  326.  }
  327.  
  328. void err( char *out )
  329.  { /* Ausgabe der normalen Texte während des Spiels */
  330.  GrFilledBox( 0, 450, 400, 469, 0 );
  331.  GrDrawString( out, strlen( out ), 0, 451, &textleft );
  332.  }
  333.  
  334. void err2( char *out, int col )
  335.  { /* Ausgabe von z.B. Free- oder Extra-Ball ... */
  336.  GrFilledBox( 0, 430, 440, 449, 0 );
  337.  textleft.txo_fgcolor.v = col;
  338.  GrDrawString(out, strlen(out), 0, 431, &textleft );
  339.  textleft.txo_fgcolor.v = 6;
  340.  }
  341.  
  342. void debug( char *out )
  343.  { /* Ausgabe der Kommentare beim Computer-Spieler */
  344.  char o[80];
  345.  sprintf(o,"%s",out);
  346.  GrFilledBox( 0, 35, 540, 55, 0 );
  347.  GrDrawString(out, strlen(out), 0, 35, &textleft );
  348.  }
  349.  
  350. void make_mouse_cursor( void )
  351.  { /* erstellt die Bälle, die als Mouse-Cursor dienen */
  352.  int i,j;
  353.  long c_table[5][4] = 
  354.   {{3,6,4,4},{3,6,11,11},{3,6,4,11},{3,6,0,0},{3,5,5,5}};
  355.  char curs[(int)((2*RADIUS+1)*(2*RADIUS+1))];
  356. #ifndef S3
  357.  context = GrCreateContext( 256, 4*RADIUS, NULL, NULL );
  358.  cont_gc_frame = &context->gc_frame;
  359.  curc_gc_frame = &CURC->gc_frame;
  360.  curc_xoffset = CURC->gc_xoffset - RADIUS;
  361.  curc_yoffset = CURC->gc_yoffset - RADIUS;
  362.  cont_xoffset = context->gc_xoffset - RADIUS + 64;
  363.  cont_yoffset = context->gc_yoffset + RADIUS;
  364. #endif
  365.  GrFilledCircle( 32, 2*RADIUS, RADIUS, 1 );  /* f. Mauszeiger */
  366.  GrFilledCircleArc( 32, 2*RADIUS, 3, 2700, 900, GR_ARC_STYLE_CLOSE2, 2 );
  367.  GrFilledCircleArc( 32, 2*RADIUS, 3, 900, 2700, GR_ARC_STYLE_CLOSE2, 3 );
  368.  GrFilledCircle( 64, 2*RADIUS, RADIUS, 4 ); /* f. WEISS;     KUGELN ... */
  369.  GrFilledCircle( 96, 2*RADIUS, RADIUS, 5 ); /* f. ROT   */
  370.  GrFilledCircle( 128, 2*RADIUS, RADIUS, 10 ); /* f. GELB  */
  371.  GrFilledCircle( 160, 2*RADIUS, RADIUS, 1 ); /* f. SCHWARZ */
  372.  GrFilledCircleArc(192, 2*RADIUS, RADIUS, 2700, 900, GR_ARC_STYLE_CLOSE2, 5);
  373.  GrFilledCircleArc(192, 2*RADIUS, RADIUS, 900, 2700, GR_ARC_STYLE_CLOSE2, 10);
  374. #ifdef S3
  375.  bitblt( 0, 0, 0, GrSizeY(), 256, 4*RADIUS );
  376. #else
  377.  GrBitBltNC( context, 0, 0, NULL, 0, 0, 255, 4*RADIUS, GrWRITE);
  378. #endif
  379.  for(j=0;j<2*RADIUS+1;j++) /* fünf neue Maus-"Zeiger" werden erstellt */
  380.   for(i=0;i<2*RADIUS+1;i++)
  381.    curs[(int)(j*(2*RADIUS+1)+i)] = 
  382.     GrPixel( 32-RADIUS+i, 2*RADIUS-RADIUS+j );
  383.  for( i=0;i<5;i++ ) cursor[i] = GrBuildCursor( curs, 2*RADIUS+1, 2*RADIUS+1, 
  384.   2*RADIUS+1, RADIUS, RADIUS, c_table[i] );
  385.  }
  386.  
  387. void mouse_on( void )
  388.  { GrMouseDisplayCursor(); }
  389.  
  390. void mouse_off( void )
  391.  { GrMouseEraseCursor(); }
  392.  
  393. void plot_act_player( int act )
  394.  { /* gibt die kleine Kugel oben links und den akt. Spieler aus */
  395.  char whois[20];
  396.  GrFilledBox( 0, 0, 2*RADIUS+3, 2*RADIUS+3, 1 );
  397.  if( !ply[act].col ) BitBlt_S3( 1 + RADIUS, 1 + RADIUS, 128);
  398.  else BitBlt_S3( 1 + RADIUS, 1 + RADIUS, (ply[act].col)<<5);
  399.  sprintf(whois,"player no: %d",act+1);
  400.  GrDrawString( whois, strlen(whois), 3*RADIUS+1, 1, &textleft );
  401.  }
  402.  
  403. void set_white_ball( void )
  404.  { /* Weiße Kugel am Anfang oder nach Foul neu positionieren */
  405.  int i, j, ok;
  406.  double old, old2;
  407.  if( (c_player == -1) || (act != c_player ) )
  408.   {
  409.   msg("place cue-ball");
  410.   set_spin( 0.0, 0.0, 0.0 );
  411.   old2 = 0.25;
  412.   i = 250;
  413.   do
  414.    {
  415.     old = ((double)i) / 1000.0;
  416.     ok = 1;
  417.     for(j=0;j<WHITE;j++) /* Suche nach freiem Platz auf dem Tisch */
  418.      if( DIFF2( old-k[j].p.x, old2-k[j].p.y ) < 5.0*RADIUS*RADIUS )
  419.       {ok=0; break;}
  420.    }
  421.   while( !ok && (--i>50) );
  422.   k[WHITE].p.x = old; k[WHITE].p.y = old2;
  423.   GrMouseWarp( LEFT + old*DIFFX, UP + old2*DIFFX );
  424.   GrMouseSetLimits( LEFT+RADIUS+1, UP+RADIUS+1, LEFT+DIFFX/4, DOWN-RADIUS-1 );
  425.   do
  426.    {
  427.     GrMouseSetCursor( cursor[4] ); /* weißer Mouse-Cursor */
  428.     GrMouseGetEvent( GR_M_KEYPRESS | GR_M_MOTION | GR_M_BUTTON_CHANGE, &me );
  429.     ok = 1;
  430.     if( me.flags & GR_M_MOTION )
  431.      { /* Verschieben des Spielballs, aber nicht "auf" andere Kugeln */
  432.       for(j=0;j<WHITE;j++)
  433.        if((!(k[j].stat)) && (DIFF2( (me.x-LEFT)/DIFFX - k[j].p.x,
  434.         (me.y-UP)/DIFFX - k[j].p.y ) < 5.0*RADIUS*RADIUS) )
  435.           { ok = 0; break; }
  436.       if( ok )
  437.        {
  438.         old = (double)(me.x-RIGHT+DIFFX)/DIFFX;
  439.         old2 = (double)(me.y-DOWN+DIFFX/2)/DIFFX;
  440.         k[WHITE].p.x = old; k[WHITE].p.y = old2;
  441.         }
  442.       else GrMouseWarp((double)LEFT + old * DIFFX, (double)UP + old2*DIFFX);
  443.      }
  444.    }
  445.   while( !(me.flags & GR_M_KEYPRESS ) && (!(me.flags & GR_M_BUTTON_DOWN)) );
  446.   if( (me.flags & GR_M_KEYPRESS) &&  (me.key == 27) ) stop_it();
  447.   }
  448.  else /* !!! !!! !!! !!! !!! !!! VERBESSERN! */
  449.   {
  450.   /* Computer kann Weiße legen ... */
  451.   set_spin( 0.0, 0.0, 0.0 );
  452.   old2 = 0.25;
  453.   i = 250;
  454.   do
  455.    {
  456.     old = ((double)i) / 1000.0;
  457.     ok = 1;
  458.     for(j=0;j<WHITE;j++) /* Suche nach freiem Platz auf dem Tisch */
  459.      if( DIFF2( old-k[j].p.x, old2-k[j].p.y ) < 5.0*RADIUS*RADIUS )
  460.       {ok=0; break;}
  461.    }
  462.   while( !ok && (--i>50) );
  463.   k[WHITE].p.x = old; k[WHITE].p.y = old2;
  464.   }
  465.  k[WHITE].stat = 0; /* Weiße wieder auf dem Tisch */
  466.  plot_one_ball( WHITE );
  467. }
  468.  
  469. void calc_player_v( void )
  470.  { /* brechnet die Richtung (zum Maus-Zeiger) der Geschwindigkeit und 
  471.       multipliziert diese mit dem "Power"-Faktor spd */
  472.   struct vect v;
  473.   double dummy;
  474.   v.x = (double)(me.x - LEFT) - k[WHITE].p.x*DIFFX;
  475.   v.y = (double)(me.y - UP) - k[WHITE].p.y*DIFFX;
  476.   if( v.x || v.y )
  477.    {
  478.    v.x /= (dummy = sqrt(v.x*v.x+v.y*v.y));
  479.    v.y /= dummy;
  480.   }
  481.   /* v multiplizieren mit Geschwindigkeitsfaktor (0 <= spd <= 1)  */
  482.   k[WHITE].v.x = spd * v.x / DIFFX * cos( alph * M_PI/180.0 );
  483.   k[WHITE].v.y = spd * v.y / DIFFX * cos( alph * M_PI/180.0 );
  484.   set_spin( k[WHITE].e.x*spd, k[WHITE].e.y*spd, k[WHITE].ez/* *spd!!! ??*/);
  485.   err(" "); debug(" ");
  486.  }
  487.  
  488. void set_player_power( void )
  489.  { /* zum Einstellen der Stoßstärke; Balken ganz unten im Bildschirm */
  490.   int old, old2;
  491.   msg("move mouse to increase / decrease power");
  492.   old=me.y; old2=me.x;
  493.   do
  494.    {
  495.     GrMouseGetEvent( GR_M_KEYPRESS | GR_M_MOTION | GR_M_BUTTON_CHANGE, &me);
  496.     if( (me.flags & GR_M_MOTION))
  497.      {
  498.       spd += 1.0/GrSizeX() * (old-me.y - old2 + me.x);
  499.       GrMouseWarp( old2, old );
  500.       if( spd > 1.0 ) spd = 1.0;
  501.       if( spd < 0.0 ) spd = 0.0;
  502.       speed( spd );
  503.      }
  504.    }
  505.   while( !(me.flags & GR_M_KEYPRESS) && !(me.flags & GR_M_RIGHT_UP) ); 
  506.   MSG2;
  507.   }
  508.  
  509. void set_player_spin( void )
  510.  { /* zum Einstellen des Spins; dicke Kugel rechts unten */
  511.   int old, old2;
  512.   msg("move mouse to change spin, press right button to change angle");
  513.   test_spin(me.x, me.y); 
  514.   if( me.flags & (GR_M_MIDDLE_DOWN | GR_M_RIGHT_DOWN) ) 
  515.    {
  516.     old = me.x; old2 = me.y;
  517.     msg("move mouse to change angle between queue an table");
  518.      do
  519.       {
  520.        GrMouseGetEvent( GR_M_KEYPRESS|GR_M_MOTION|GR_M_BUTTON_CHANGE, &me);
  521.        if( me.flags & GR_M_MOTION)
  522.         {
  523.          alph += 0.1 * (old -me.y +old2 -me.x);
  524.          GrMouseWarp( old, old2 );
  525.          if( alph > 85.0 ) alph = 85.0;
  526.          if( alph < 0.0 ) alph = 0.0;
  527.          wink( alph );
  528.         }
  529.       }
  530.      while( !(me.flags & GR_M_KEYPRESS) && 
  531.       (!( me.flags & (GR_M_MIDDLE_UP | GR_M_RIGHT_UP) )) );
  532.    }
  533.   MSG2;
  534.  }
  535.  
  536. void set_test_power( void )
  537.  { /* Test-Prozedur, berechnet Geschw., die die Weisse haben muß, um zur akt.
  538.       Maus-Position zu rollen */
  539.   struct vect v;
  540.   debug("press button, to play white to act.pos. !");
  541.   do 
  542.    { GrMouseGetEvent( GR_M_KEYPRESS|GR_M_MOTION|GR_M_BUTTON_CHANGE, &me); }
  543.   while( !(me.flags & GR_M_BUTTON_DOWN) );
  544.   v.x = (me.x-LEFT)/DIFFX - k[WHITE].p.x;
  545.   v.y = (me.y-UP)/DIFFX  - k[WHITE].p.y;
  546.   set_c_speed( SET_V(BETR( v ), 0), v );
  547.  }
  548.  
  549. int set_test_power2( void )
  550.  { /* Test-Prozedur, berechnet die Geschw., die die Weisse benötigt, um ein
  551.       best. Kugel in ein best. Loch zu schießen; ohne jeden Test, ob andere
  552.       Kugeln im Weg liegen ... */
  553.   int i, puffer_ball;
  554.   struct vect v1, v2, v3, v4;
  555.   double dum1, dummy; 
  556.   int dum2;
  557.   char outtext[80];
  558.   debug("ball ?");
  559.   do 
  560.    { GrMouseGetEvent( GR_M_KEYPRESS|GR_M_MOTION|GR_M_BUTTON_CHANGE, &me); }
  561.   while( !(me.flags & GR_M_BUTTON_DOWN) );
  562.   v1.x = (me.x-LEFT) / DIFFX; 
  563.   v1.y = (me.y-UP) / DIFFX; 
  564.   dum1 = dum2 = 4;
  565.   for( i=0;i<WHITE;i++ )
  566.    if( DIFF( v1.x-k[i].p.x, v1.y-k[i].p.y ) < dum1 )
  567.     { dum1 = DIFF( v1.x-k[i].p.x, v1.y-k[i].p.y ); dum2 = i; }  
  568.   v1.x = k[dum2].p.x; v1.y = k[dum2].p.y;
  569.   puffer_ball = dum2;
  570.   sprintf(outtext,"ball no: %d, hole ?", dum2 );
  571.   debug(outtext);
  572.   do 
  573.    { GrMouseGetEvent( GR_M_KEYPRESS|GR_M_MOTION|GR_M_BUTTON_CHANGE, &me); }
  574.   while( !(me.flags & GR_M_BUTTON_DOWN) );
  575.   v2.x = (me.x-LEFT) / DIFFX; 
  576.   v2.y = (me.y-UP) / DIFFX; 
  577.   dum1 = 1000.0; dum2 = 0;
  578.   for( i=0;i<6;i++ )
  579.    if( DIFF( v2.x-posl[i].m.x, v2.y-posl[i].m.y ) < dum1 )
  580.     { dum1 = DIFF( v2.x-posl[i].m.x, v2.y-posl[i].m.y ); dum2 = i; } 
  581.   sprintf(outtext,"hole: %d",dum2);
  582.   debug(outtext);
  583.   v2.x = posl[dum2].m.x; v2.y = posl[dum2].m.y;
  584.   v3 = calc_tp( v1, v2 );
  585.   dummy = sqrt( DIFF( k[WHITE].p.x-v3.x, k[WHITE].p.y-v3.y ));
  586.   /* geht so natürlich noch nicht wieder !!! */
  587.   v4.x = v2.x - v3.x; v4.y = v2.y - v3.y;
  588.   v1.x = v3.x - k[WHITE].p.x; v1.y = v3.y - k[WHITE].p.y;
  589.   if( COSV( v1, v4 ) > 0.0 )
  590.    {
  591.     v2.x = posl[dum2].p.x - posl[dum2].m.x;
  592.     v2.y = posl[dum2].p.y - posl[dum2].m.y;
  593.     v3.x = posl[dum2].m.x - k[puffer_ball].p.x;
  594.     v3.y = posl[dum2].m.y - k[puffer_ball].p.y;
  595.     if( ((dum2 == 2 || dum2 == 5) && COSV( v2, v3 ) > COS(e_winkel)) 
  596.      || (dum2 != 2 && dum2 != 5) )
  597.       {
  598.        if( (dum1=COSV( v1, v4 )) ) dummy += BETR(v4) / (dum1 * dum1);
  599.        else dummy = 1000000.0; /* =infinity */
  600.        set_c_speed( SET_V(dummy*sqrt(1.0/ROLL), 0), v1 );  
  601.        return 1;
  602.       }
  603.      else 
  604.       {
  605.        char out[80];
  606.        sprintf(out,"can't play this..(ew:%g/ pw:%g)", e_winkel, 
  607.         180.0/M_PI * acos(COSV( v2, v3 )) );
  608.        debug(out);
  609.        return 0;
  610.       }
  611.    }
  612.   debug("can't play this...");
  613.   return 0;
  614. }
  615.  
  616.          /* steht nur wegen den Grafik-Befehlen und #defines in graphics.c */
  617. int menu( void ) 
  618. /* hier wird einiges geregelt, Mouse-Abfragen, die zum Stoßen, Spin u.
  619.    Geschw. einstellen dienen und Tastaturabfragen, wie z.B. Computer-
  620.    Gegner anschalten, Porgramm beenden ... */
  621.  { 
  622.   int i, ok=0, ret_wert=0;
  623.   plot_act_player( act );
  624.   wink( alph = 0.0 );
  625.   set_spin( 0.0, 0.0, 0.0 );
  626.   for( i=0;i<BALLS;i++) /* alle Kugeln neu initialisieren */
  627.    {  k[i].v.x = k[i].v.y = k[i].e.x = k[i].e.y = k[i].ez = 0.0; }
  628.   if( k[WHITE].stat ) set_white_ball(); /* Weiße neu setzen */
  629.   if( ply[act].col == COL_WHITE ) GrMouseSetCursor( cursor[2] );
  630.   else if( ply[act].col == COL_RED ) GrMouseSetCursor( cursor[0] );
  631.   else if( ply[act].col == COL_YELLOW ) GrMouseSetCursor( cursor[1] );
  632.   else GrMouseSetCursor( cursor[3] ); /* if ... COL_BLACK */
  633.   if( act == c_player ) { computer_stoss(); return ret_wert; }
  634.   GrMouseSetLimits(LEFT+RADIUS+1, UP+RADIUS+1, RIGHT-RADIUS-1, DOWN-RADIUS-1);
  635.   ok = 0;
  636.   MSG2;
  637.   while( kbhit() ) getkey();
  638.   do
  639.    {
  640.     mouse_on();
  641.     do 
  642.      { GrMouseGetEvent( GR_M_KEYPRESS | GR_M_BUTTON_CHANGE, &me ); }
  643.     while( !(me.flags & (GR_M_KEYPRESS | GR_M_BUTTON_CHANGE)) ); 
  644.     if( (me.flags & GR_M_BUTTON_DOWN) )
  645.      {
  646.      GrMouseSetLimits( 0, 0, GrMaxX(), GrMaxY() );
  647.      switch(me.buttons)
  648.       {
  649.        case GR_M_LEFT: /* Stoß: Geschw. berechnen */
  650.     calc_player_v(); ok = 1; break;
  651.        case GR_M_RIGHT:  /* Geschwindigkeits (Power)-Faktor verstellen */
  652.     set_player_power(); break;
  653.        case GR_M_MIDDLE: /* Spin einstellen */
  654.     set_player_spin(); break;
  655.        default: {}
  656.       }
  657.       GrMouseSetLimits( LEFT+RADIUS+1, UP+RADIUS+1, RIGHT-RADIUS-1,
  658.        DOWN-RADIUS-1);
  659.      }
  660.     else if( me.flags & GR_M_KEYPRESS )
  661.      switch( me.key )
  662.       {
  663.        case 13: case ' ': calc_player_v(); ok = 1; break;
  664.        case 'q': plot_statistics( 0, 0 ); 
  665.        case 27: ok = ret_wert = 1; break;
  666.        case 'n':        /* new game */
  667.         mouse_off();
  668.     ply[1-act].points += 1;
  669.     stats[act].losses += 1;
  670.     act = 1 - act;
  671.         init_table(); 
  672.         ok = 1; 
  673.         break;
  674.        case 'r':        /* 'instant replay'-mode on/off */
  675.         i = STEP; 
  676.     STEP = old_paint; 
  677.     old_paint = i; 
  678.     if( STEP < 6 ) debug("replay-mode on");
  679.     else debug("replay-mode off");
  680.     break;
  681.        case 'u':        /* undo last shot */
  682.         undo(); debug("undo..."); break; /* akt ??? !!! !!! !!! */
  683.        case 'd': demo = 1; 
  684.         msg("press any key to stop demo");
  685.     c_player = act;
  686.     computer_stoss();
  687.     ok = 1;
  688.     break;
  689.        case 'c':        /* computer plays every shot*/
  690.         if( c_player == -1 ) c_player = act; 
  691.     else { c_player = -1; break; }
  692.        case 'x':        /* let computer play one shot */
  693.     computer_stoss();
  694.         ok = 1;
  695.         break;
  696. #ifdef BANDEN
  697.        case 'b':        /* show shots 'through' 1 or 2 sides */
  698.         mouse_off();
  699.         banden_stoss();
  700.         mouse_on();
  701.         ok = 0;
  702.         break;
  703. #endif
  704.        case 'D': 
  705.         for( i=BLACK+1;i<WHITE;i++ ) delete_ball( i );
  706.         for( i=0;i<BLACK;i++ ) delete_ball( i ); 
  707.     break;
  708.        case 'w':        /* White --> akt-Mouse-Pos */
  709.        /* computer soll Weiße bis zur akt. Mouse-Pos. rollen lassen !!! */
  710.         set_test_power();  ok = 1; break;
  711.        case 'W':        /* shot ball (1) in hole (2) */
  712.         ok = set_test_power2(); break;
  713.        case 's': plot_statistics( 0, 1 ); break;
  714.        case 315: help(); break;            /* F1 */
  715.        case 316: credits(); break;        /* F2 */
  716.        default: {}
  717.       } 
  718.    }
  719.   while( !ok );
  720.   mouse_off();
  721.   return ret_wert;
  722.  }
  723.   
  724. void ctrl_break_off( void ) /* stellt CTRL-BREAK AB ! */
  725.  { _go32_want_ctrl_break( 1 ); }
  726.  
  727. void plot_statistics( double time, int grx )
  728.  {
  729.  int add = 84;
  730.  time_t oldt = clock();
  731.  char out[80];
  732.  GrContext *cont = NULL;
  733.  mouse_off();
  734.  if( grx ) cont = GrCreateContext( GrSizeX(), GrSizeY(), NULL, NULL );
  735.  if( cont || !grx )
  736.   {
  737.   if( grx ) GrBitBltNC(cont, 0, 0, NULL, 0, 0, GrSizeX(), GrSizeY(), GrWRITE);
  738.   GrClearScreen( 0 );
  739.   GrDrawString( "STATISTICS", 10, GrMaxX()/2, 30, &textopt );
  740.   sprintf(out,"                          Player     %5d   %5d", 1, 2);
  741.   GrDrawString( out, strlen(out), 1, add, &textleft );
  742.   add += 32;
  743.   sprintf(out,"standings                            %5d   %5d",
  744.    stats[0].wins+stats[1].losses, stats[1].wins+stats[0].losses);
  745.   GrDrawString( out, strlen(out), 1, (add+=16), &textleft );
  746.   sprintf(out,"-games won (correct play on black)   %5d   %5d",
  747.    stats[0].wins, stats[1].wins);
  748.   GrDrawString( out, strlen(out), 1, (add+=24), &textleft );
  749.   sprintf(out,"-games lost ('direct' foul on black) %5d   %5d",
  750.    stats[0].losses, stats[1].losses  );
  751.   GrDrawString( out, strlen(out), 1, (add+=16), &textleft );
  752.   sprintf(out,"number of pocketed balls             %5d   %5d",
  753.    stats[0].pots, stats[1].pots  );
  754.   GrDrawString( out, strlen(out), 1, (add+=20), &textleft );
  755.   sprintf(out,"no. of attempts without success      %5d   %5d",
  756.    stats[0].nopots, stats[1].nopots  );
  757.   GrDrawString( out, strlen(out), 1, (add+=16), &textleft );
  758.   sprintf(out,"fouls: wrong color touched           %5d   %5d",
  759.    stats[0].fouls.wrongct, stats[1].fouls.wrongct  );
  760.   GrDrawString( out, strlen(out), 1, (add+=16), &textleft );
  761.   sprintf(out,"fouls: wrong color pocketed          %5d   %5d",
  762.    stats[0].fouls.wrongcp, stats[1].fouls.wrongcp  );
  763.   GrDrawString( out, strlen(out), 1, (add+=16), &textleft );
  764.   sprintf(out,"fouls: white ball disappeared        %5d   %5d",
  765.    stats[0].fouls.whited, stats[1].fouls.whited  );
  766.   GrDrawString( out, strlen(out), 1, (add+=16), &textleft );
  767.   sprintf(out,"fouls: no ball or side touched       %5d   %5d",
  768.    stats[0].fouls.notouch, stats[1].fouls.notouch  );
  769.   GrDrawString( out, strlen(out), 1, (add+=16), &textleft );
  770.   if( time > 0 ) while( (double)(clock() - oldt)/CLOCKS_PER_SEC < time );
  771.   else { wait_for_click(); mouse_off(); }
  772.   if( grx )
  773.    {
  774.     GrBitBltNC( NULL, 0, 0, cont, 0, 0, GrSizeX(), GrSizeY(), GrWRITE);
  775.     GrDestroyContext( cont );
  776.    }
  777.   }
  778.  mouse_on();
  779.  }
  780.  
  781. void delete_ball( int n )
  782. /* Kugel "n" fällt in Tasche und wird gelöscht; außerdem wird geprüft, ob
  783.    ein Foul durch das Versenken begangen wurde... Hatte der Spieler vor dem
  784.    Versenken noch keine Farbe (Anfang des Spiels) so bekommt er hier diese */
  785.  {
  786.   k[n].stat = 1;
  787.   k[n].p.x = k[n].p.y = 10000.0; /* eigentlich unnötig ! */
  788.   BitBlt_S3( oldx[n], oldy[n], (k[n].col)<<5 );
  789.   if( k[n].col == COL_WHITE ) ply[act].stat |= FOUL_WHITE_POCKETED;
  790.   else if( (k[n].col == COL_BLACK) && (ply[act].col != COL_BLACK) )
  791.    ply[act].stat |= FOUL_BLACK_ILLEGALY_POCKETED;
  792.   else if( !ply[act].col && (k[n].col != COL_WHITE) ) 
  793.    { /* falls noch kein Spieler-Farbe festgelegt ist */
  794.    if( (k[n].col != COL_BLACK) && (ply[1-act].col != k[n].col) )
  795.    ply[act].col = k[n].col;
  796.    ply[1-act].col = 3 - k[n].col;
  797.    ply[act].stat |= FOUL_CORRECT_POT;
  798.    col_in = k[n].col;
  799.    new_col |= NEW_COL_NEW;
  800.    }
  801.   else if( !freeball && (k[n].col != ply[act].col) ) /* falsche Farbe */
  802.    {
  803.    if( new_col & NEW_COL_NEW ) new_col |= NEW_COL_DOUBLE;
  804.    else ply[act].stat |= FOUL_WRONG_COLOR_POCKETED;
  805.    }
  806.   else if( freeball || (k[n].col == ply[act].col))  /* OK */
  807.    ply[act].stat |= FOUL_CORRECT_POT;
  808.   bande_hit = 1;
  809.   last_pocketed_balls++;
  810.  }
  811.  
  812. void print( char *out, int align, int x, int y )
  813.  {
  814.  int old_align = textleft.txo_xalign;
  815.  textleft.txo_xalign = align;
  816.  GrDrawString( out, strlen(out), x, y, &textleft );
  817.  textleft.txo_xalign = old_align;
  818.  }
  819.  
  820. void print2( char *out, int align, int x, int y )
  821.  {
  822.  int old_align = textcenter.txo_xalign;
  823.  textcenter.txo_xalign = align;
  824.  GrDrawString( out, strlen(out), x, y, &textcenter );
  825.  textcenter.txo_xalign = old_align;
  826.  }
  827.  
  828. void help( void )
  829.  {
  830.  GrContext *cont = NULL;
  831.  int a=100;
  832.  mouse_off();
  833.  cont = GrCreateContext( GrSizeX(), GrSizeY(), NULL, NULL );
  834.  if( cont )
  835.   {
  836.   GrBitBltNC(cont, 0, 0, NULL, 0, 0, GrSizeX(), GrSizeY(), GrWRITE);
  837.   GrClearScreen( 0 );
  838.   GrDrawString("HELP-Screen", 11, GrMaxX()/2, 30, &textopt );
  839.   print("KEYS:",GR_ALIGN_LEFT, 30, 65);
  840.   print("  'c': activate/deactivate computer opponent", 
  841.    GR_ALIGN_LEFT, 30, a+=16);
  842.   print("  'x': computer plays only one shot", GR_ALIGN_LEFT, 30, a+=16 );
  843.   print("  'n': new game, actual game is lost", GR_ALIGN_LEFT, 30, a+=16 );
  844.   print("  'd': demo-mode on; any key to stop demo", 
  845.    GR_ALIGN_LEFT, 30, a+=16 );
  846.   print("  'w': ball rolls to actual mouse-position (test-procedure)",
  847.    GR_ALIGN_LEFT, 30 , a+=16 );
  848.   print("  'W': some sort of computer help (test it)", 
  849.    GR_ALIGN_LEFT, 30, a+=16 ); 
  850.   print("  'u': undo last shot (no redo implemented)", 
  851.    GR_ALIGN_LEFT, 30 , a+=16 );
  852.   print("  'r': slow-motion-mode on/off", GR_ALIGN_LEFT, 30, a+=16 );
  853.   print("  's': show statistics", GR_ALIGN_LEFT, 30, a+=16 );
  854.   print(" 'F1': this screen", GR_ALIGN_LEFT, 30, a+=16 );
  855.   print(" 'F2': credits", GR_ALIGN_LEFT, 30, a+=16 );
  856.   print("ENTER: or SPACE: same as left mouse-button",
  857.    GR_ALIGN_LEFT, 30, a+=16 );
  858.   print("  'q': show statistics before quitting the game",
  859.    GR_ALIGN_LEFT, 30, a+=16);
  860.   print("  ESC: 'fast' quit game (works im allmost every situation)",
  861.    GR_ALIGN_LEFT, 30, a+=16 );
  862.   wait_for_click();
  863.   GrBitBltNC( NULL, 0, 0, cont, 0, 0, GrSizeX(), GrSizeY(), GrWRITE);
  864.   GrDestroyContext( cont );
  865.   }
  866.  else msg("no help available");
  867.  mouse_on();
  868. }
  869.  
  870. void credits( void )
  871.  {
  872. /*#ifndef S3*/
  873.  GrContext *cont = NULL;
  874. /*#endif*/
  875.  int a=70;
  876.  char out[90];
  877.  mouse_off();
  878. /*#ifndef S3*/
  879.  cont = GrCreateContext( GrSizeX(), GrSizeY(), NULL, NULL );
  880.  if( cont )
  881.   {
  882.   GrBitBltNC(cont, 0, 0, NULL, 0, 0, GrSizeX(), GrSizeY(), GrWRITE);
  883. /*#else*/
  884. /* bitblt( 0, 0, 0, 700, 640, 480 );*/
  885. /*#endif*/
  886.   GrClearScreen( 0 );
  887.   sprintf(out,"Another Pool V %s, %s, copyright (c) by Gerrit Jahn",
  888.    VERSION, DATE);
  889.   print2(out, GR_ALIGN_CENTER, GrMaxX()/2, 20 );
  890.   GrDrawString("CREDITS", 8, GrMaxX()/2, 40, &textopt );
  891.   print2("'ANOTHER POOL' is free software;   you can redistribute it",
  892.    GR_ALIGN_LEFT, 90, a+=15);
  893.   print2("and/or modify it under the terms of the GNU General Public License",
  894.    GR_ALIGN_LEFT, 90, a+=15);
  895.   print2("as published by the Free Software Foundation; either version 2 of",
  896.    GR_ALIGN_LEFT, 90, a+=15);
  897.   print2("the License, or (at your option) any later version.",
  898.    GR_ALIGN_LEFT, 90, a+=15);a+=15;
  899.   print2("Another Pool is distributed in the hope that it will be useful,",
  900.    GR_ALIGN_LEFT, 90, a+=15);
  901.   print2("but WITHOUT ANY WARRANTY; without even the implied warranty of",
  902.    GR_ALIGN_LEFT, 90, a+=15);
  903.   print2("MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the",
  904.    GR_ALIGN_LEFT, 90, a+=15);
  905.   print2("GNU General Public License (see file COPYING) for more details.",
  906.    GR_ALIGN_LEFT, 90, a+=15);a+=5;
  907.   print2("------------------------------------------------------------------",
  908.    GR_ALIGN_LEFT, 90, a+=15);a+=5;
  909.   print("Special thanks to Achim Stremplat for helping me in",
  910.    GR_ALIGN_LEFT, 90, a+=15);
  911.   print("coding and talking about the physics of the game.",
  912.    GR_ALIGN_LEFT, 90, a+=15);
  913.   print("Also thanks to Jens Willibald, Martin Schmidt and Frank",
  914.    GR_ALIGN_LEFT, 90, a+=15);
  915.   print2("Schmithuesen for testing; Boris Postler for some corrections",
  916.    GR_ALIGN_LEFT, 90, a+=15);
  917.   print2("on the 'apool.doc'-file.", GR_ALIGN_LEFT, 90, a+=15);
  918.   print2("... and the developers of GNU-C ...", GR_ALIGN_LEFT, 90, a+=15);
  919.   a+=5;
  920.   print2("------------------------------------------------------------------",
  921.    GR_ALIGN_LEFT, 90, a+=15);a+=5;
  922.   print2("If you have any problems, questions or suggestions, email to:",
  923.    GR_ALIGN_LEFT, 90, a+=15);
  924.   print2("ub1g@rz.uni-karlsruhe.de                             Gerrit",
  925.    GR_ALIGN_LEFT, 90, a+=15);a+=15;
  926.   wait_for_click();
  927. /* #ifndef S3*/
  928.   GrBitBltNC( NULL, 0, 0, cont, 0, 0, GrSizeX(), GrSizeY(), GrWRITE);
  929.   GrDestroyContext( cont );
  930.   }
  931.  else msg("no credits available");
  932. /*#else*/
  933. /* bitblt( 0, 700, 0, 0, 640, 480 );*/
  934. /*#endif*/
  935.  mouse_on();
  936.  }
  937.