]> git.lizzy.rs Git - plan9front.git/blob - sys/src/games/mines/mines.c
db11a8ed9097bbc9f924003b5f726daee00cf219
[plan9front.git] / sys / src / games / mines / mines.c
1 /*
2         Mines ver. 1.0
3
4         Copyright (C) 2001 Antonin Vecera
5
6         email: antonin.vecera@gmail.com
7
8         This program is free software; you can redistribute it and/or modify
9         it under the terms of the GNU General Public License as published by
10         the Free Software Foundation; either version 2 of the License, or
11         (at your option) any later version.
12
13         This program is distributed in the hope that it will be useful,
14         but WITHOUT ANY WARRANTY; without even the implied warranty of
15         MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16         GNU General Public License for more details.
17
18         You should have received a copy of the GNU General Public License
19         along with this program; if not, write to the Free Software
20         Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21 */
22
23 #include <u.h>
24 #include <libc.h>
25 #include <draw.h>
26 #include <event.h>
27 #include "game.h"
28 #include "push.h"
29 #include "move.h"
30 #include "win.h"
31 #include "oops.h"
32 #include "sign.h"
33 #include "digit0.h"
34 #include "digit1.h"
35 #include "digit2.h"
36 #include "digit3.h"
37 #include "digit4.h"
38 #include "digit5.h"
39 #include "digit6.h"
40 #include "digit7.h"
41 #include "digit8.h"
42 #include "digit9.h"
43 #include "unknown.h"
44 #include "empty0.h"
45 #include "empty1.h"
46 #include "empty2.h"
47 #include "empty3.h"
48 #include "empty4.h"
49 #include "empty5.h"
50 #include "empty6.h"
51 #include "empty7.h"
52 #include "empty8.h"
53 #include "query.h"
54 #include "mouse.h"
55 #include "mark.h"
56 #include "mined.h"
57 #include "explosion.h"
58 #include "fault.h"
59
60 enum  {
61         FALSE = 0,
62         TRUE = 1,
63         CellBytes = 15 * 15 * 3,
64         DigitBytes = 11 * 21 * 3,
65         ButtonBytes = 25 * 25 * 3,
66         INT_MAX = 0x7fffffff
67 };
68
69 enum {
70         Beginner,
71         Advanced,
72         Expert,
73         Custom
74 };
75
76 enum {
77         Game,
78         Push,
79         Move,
80         Win,
81         Oops
82 };
83
84 enum {
85         Empty0 = 0,
86         Empty1 = 1,
87         Empty2 = 2,
88         Empty3 = 3,
89         Empty4 = 4,
90         Empty5 = 5,
91         Empty6 = 6,
92         Empty7 = 7,
93         Empty8 = 8,
94         Query,
95         MouseQuery,
96         Mark,
97         Fault,
98         Mined,
99         Explosion,
100         Unknown
101 };
102
103 typedef
104 struct {
105         int Mine, Picture, Neighbours;
106 } FieldCell;
107
108 struct {
109         int MaxX, MaxY, Mines;
110 } Settings[] = { {8, 8, 10}, {16, 16, 40}, {30, 16, 99}, {0, 0, 0} };
111
112 int MaxX, MaxY, Mines, Level, UnknownCell, Playing, MinesRemain, Time, Status, UseQuery = TRUE, UseColor = TRUE;
113
114 Point Origin;
115
116 Image *RGB000000, *RGB0000FF, *RGB007F00, *RGB7F7F7F, *RGBBFBFBF, *RGBFF0000, *RGBFFFF00, *RGBFFFFFF, *ImageButton[5], *ImageSign, *ImageDigit[10], *ImageCell[16];
117
118 FieldCell **MineField;
119
120 void Pack(Point *p, Point p0, Point p1, Point p2, Point p3, Point p4, Point p5) {
121
122         p[0] = p0;
123         p[1] = p1;
124         p[2] = p2;
125         p[3] = p3;
126         p[4] = p4;
127         p[5] = p5;
128 }
129
130 void Button3d(Image *im, Rectangle r, int i, Image *color1, Image *color2, Image *color3, Point sp) {
131
132         Point p[6];
133
134         if(i < 0){
135                 r = insetrect(r, i);
136                 i = -i;
137         }
138
139         draw(im, Rect(r.min.x + i, r.min.y + i, r.max.x - i, r.max.y - i), color1, nil, Pt(sp.x + i, sp.y + i));
140
141         Pack(p, r.min, Pt(r.min.x, r.max.y), Pt(r.min.x + i, r.max.y - i), Pt(r.min.x + i, r.min.y + i), Pt(r.max.x - i, r.min.y + i), Pt(r.max.x, r.min.y));
142         fillpoly(im, p, 6, 0, color2, sp);
143
144         Pack(p, r.max, Pt(r.min.x, r.max.y), Pt(r.min.x + i, r.max.y - i), Pt(r.max.x - i, r.max.y - i), Pt(r.max.x - i, r.min.y + i), Pt(r.max.x, r.min.y));
145         fillpoly(im, p, 6, 0, color3, sp);
146 }
147
148 void DisplayCounter(int x, int n) {
149
150         Image *A, *B, *C;
151
152         Button3d(screen, Rect(x, Origin.y + 16, x + 41, Origin.y + 41), 1, RGB000000, RGB7F7F7F, RGBFFFFFF, ZP);
153
154         if(n < -99) n = -99;
155         if(n > 999) n = 999;
156
157         A = ImageDigit[abs(n / 100 - n / 1000 * 10)];
158         B = ImageDigit[abs(n / 10 - n / 100 * 10)];
159         C = ImageDigit[abs(n / 1 - n / 10 * 10)];
160
161         if(n < 0) A = ImageSign;
162
163         draw(screen, Rect(x + 2, Origin.y + 18, x + 13, Origin.y + 39), A, nil, ZP);
164         draw(screen, Rect(x + 15, Origin.y + 18, x + 26, Origin.y + 39), B, nil, ZP);
165         draw(screen, Rect(x + 28, Origin.y + 18, x + 39, Origin.y + 39), C, nil, ZP);
166 }
167
168 void DrawButton(int Index) {
169
170         draw(screen, Rect(Origin.x +  MaxX * 8 - 1, Origin.y + 16, Origin.x + MaxX * 8 + 24, Origin.y + 41), ImageButton[Index], nil, ZP);
171 }
172
173 void DrawCell(Point Cell) {
174
175         draw(screen, Rect(Origin.x + 12 + Cell.x * 16, Origin.y + 57 + Cell.y * 16, Origin.x + 12 + Cell.x * 16 + Dx(ImageCell[MineField[Cell.x][Cell.y].Picture]->r), Origin.y + 57 + Cell.y * 16 + Dy(ImageCell[MineField[Cell.x][Cell.y].Picture]->r)), ImageCell[MineField[Cell.x][Cell.y].Picture], nil, ZP);
176 }
177
178 void eresized(int New) {
179
180         if(New && getwindow(display, Refmesg) < 0)
181                 fprint(2,"%s: can't reattach to window", argv0);
182
183         Origin.x = screen->r.min.x + (screen->r.max.x - screen->r.min.x - 23 - MaxX * 16) / 2;
184         Origin.y = screen->r.min.y + (screen->r.max.y - screen->r.min.y - 68 - MaxY * 16) / 2;
185
186         draw(screen, screen->r, RGB0000FF, nil, ZP);
187
188         /* main window */
189         Button3d(screen, Rect(Origin.x, Origin.y, Origin.x + 23 + MaxX * 16, Origin.y + 68 + MaxY * 16), 3, RGBBFBFBF, RGBFFFFFF, RGB7F7F7F, ZP);
190
191         /* top small window with button and 2 counters */
192         Button3d(screen, Rect(Origin.x + 9, Origin.y + 9, Origin.x + 14 + MaxX * 16, Origin.y + 48), 2, RGBBFBFBF, RGB7F7F7F, RGBFFFFFF, ZP);
193
194         /* button */
195         DrawButton(Status);
196
197         /* counter on the left side - remaining mines */
198         DisplayCounter(Origin.x + 16, MinesRemain);
199
200         /* counter on the right side - timer */
201         DisplayCounter(Origin.x -34 + MaxX * 16, Time);
202
203         /* bottom window - mine field */
204         Button3d(screen, Rect(Origin.x + 9, Origin.y + 54, Origin.x + 14 + MaxX * 16, Origin.y + 59 + MaxY * 16), 3, RGBBFBFBF, RGB7F7F7F, RGBFFFFFF, ZP);
205
206         {
207                 int x, y;
208
209                 for(x = 1; x < MaxX; x++)
210                         line(screen, Pt(Origin.x + 11 + x * 16, Origin.y + 57), Pt(Origin.x + 11 + x * 16, Origin.y + 55 + MaxY * 16), Endsquare, Endsquare, 0, RGB7F7F7F, ZP);
211
212                 for(y = 1; y < MaxY; y++)
213                         line(screen, Pt(Origin.x + 12, Origin.y + 56 + y * 16), Pt(Origin.x + 10 + MaxX * 16, Origin.y + 56 + y * 16), Endsquare, Endsquare, 0, RGB7F7F7F, ZP);
214
215                 for(y = 0; y < MaxY; y++)
216                         for(x = 0; x < MaxX; x++)
217                                 DrawCell(Pt(x, y));
218         }
219 }
220
221 void MouseCell(Point Cell) {
222
223         int Picture;
224
225         switch(MineField[Cell.x][Cell.y].Picture) {
226                 case Unknown:
227                         Picture = Empty0;
228                         break;
229                 case Query:
230                         Picture = MouseQuery;
231                         break;
232                 default:
233                         return;
234         }
235         draw(screen, Rect(Origin.x + 12 + Cell.x * 16, Origin.y + 57 + Cell.y * 16, Origin.x + 12 + Cell.x * 16 + Dx(ImageCell[Picture]->r), Origin.y + 57 + Cell.y * 16 + Dy(ImageCell[Picture]->r)), ImageCell[Picture], nil, ZP);
236 }
237
238 void RecoverCell(Point Cell) {
239
240         switch(MineField[Cell.x][Cell.y].Picture) {
241                 case Unknown:
242                 case Query:
243                         draw(screen, Rect(Origin.x + 12 + Cell.x * 16, Origin.y + 57 + Cell.y * 16, Origin.x + 12 + Cell.x * 16 + Dx(ImageCell[MineField[Cell.x][Cell.y].Picture]->r), Origin.y + 57 + Cell.y * 16 + Dy(ImageCell[MineField[Cell.x][Cell.y].Picture]->r)), ImageCell[MineField[Cell.x][Cell.y].Picture], nil, ZP);
244         }
245 }
246
247 void *emalloc(ulong size) {
248
249         void *p;
250
251         p = malloc(size);
252         if(p == 0) {
253                 fprint(2, "%s: no memory: %r\n", argv0);
254                 exits("no mem");
255         }
256         return p;
257 }
258
259 void InitMineField(void) {
260
261         /* clean up mine field, make all cells unknown and place new mines */
262         {
263                 int i, x, y;
264
265                 for(y = 0; y < MaxY; y++)
266                         for(x = 0; x < MaxX; x++) {
267                                 MineField[x][y].Mine = FALSE;
268                                 MineField[x][y].Picture = Unknown;
269                         }
270
271                 for(i = 0; i < Mines; ) {
272                         x = nrand(MaxX);
273                         y = nrand(MaxY);
274                         if(MineField[x][y].Mine) continue;
275                         MineField[x][y].Mine = TRUE;
276                         i++;
277                 }
278         }
279
280         /* count mines in neighbourhood */
281         {
282                 int x, y;
283
284                 for(y = 0; y < MaxY; y++)
285                         for(x = 0; x < MaxX; x++) {
286                                 MineField[x][y].Neighbours = 0;
287                                 if(x > 0 && MineField[x - 1][y].Mine) MineField[x][y].Neighbours++;
288                                 if(y > 0 && MineField[x][y - 1].Mine) MineField[x][y].Neighbours++;
289                                 if(x < MaxX -1 && MineField[x + 1][y].Mine) MineField[x][y].Neighbours++;
290                                 if(y < MaxY - 1 && MineField[x][y + 1].Mine) MineField[x][y].Neighbours++;
291                                 if(x > 0 && y > 0 && MineField[x - 1][y - 1].Mine) MineField[x][y].Neighbours++;
292                                 if(x > 0 && y < MaxY - 1 && MineField[x - 1][y + 1].Mine) MineField[x][y].Neighbours++;
293                                 if(x < MaxX - 1 && y > 0 && MineField[x + 1][y - 1].Mine) MineField[x][y].Neighbours++;
294                                 if(x < MaxX - 1 && y < MaxY - 1 && MineField[x + 1][y + 1].Mine) MineField[x][y].Neighbours++;
295                         }
296         }
297
298         Status = Game;
299         Playing = FALSE;
300         MinesRemain = Mines;
301         Time = 0;
302         UnknownCell = MaxX * MaxY - Mines;
303 }
304
305 void NewMineField(int NewLevel) {
306
307         int CurrentMaxX, CurrentMaxY;
308
309         CurrentMaxX = MaxX;
310         CurrentMaxY = MaxY;
311
312         Level = NewLevel;
313
314         /* set size of mine field and number of mines */
315         if(Level == Custom) {
316
317                 /* here should ask a player about custom size of mine field and number of mines; in next release, may be... */
318
319                 if(MaxX < 8) MaxX = 8;
320                 if(MaxY < 8) MaxY = 8;
321                 if(Mines < 10) Mines = 10;
322                 if(MaxX > 30) MaxX = 30;
323                 if(MaxY > 24) MaxY = 24;
324                 if(Mines > (MaxX - 1) * (MaxY - 1)) Mines = (MaxX - 1) * (MaxY - 1);
325         }
326         else {
327                 MaxX = Settings[Level].MaxX;
328                 MaxY = Settings[Level].MaxY;
329                 Mines = Settings[Level].Mines;
330                 Settings[Custom].MaxX = MaxX;
331                 Settings[Custom].MaxY = MaxY;
332                 Settings[Custom].Mines = Mines;
333         }
334
335         if(MaxX != CurrentMaxX || MaxY != CurrentMaxY) {
336
337                 int x;
338
339                 /* if some memory is in use, release it */
340                 if(MineField) {
341                         for(x = 0; x < CurrentMaxX; x++)
342                                 free(MineField[x]);
343                         free(MineField);
344                 }
345
346                 /* allocate new memory */
347                 MineField = (FieldCell **)emalloc(MaxX * sizeof(FieldCell *));
348                 for(x = 0; x < MaxX; x++)
349                         MineField[x] = (FieldCell *)emalloc(MaxY * sizeof(FieldCell));
350         }
351         InitMineField();
352 }
353
354 void GameOver(void) {
355
356         int x, y;
357
358         Playing = FALSE;
359         for(y = 0; y < MaxY; y++)
360                 for(x = 0; x < MaxX; x++)
361                         switch(MineField[x][y].Picture) {
362                                 case Unknown:
363                                 case Query:
364                                         if(MineField[x][y].Mine) {
365                                                 switch(Status) {
366                                                         case Win:
367                                                                 MineField[x][y].Picture = Mark;
368                                                                 DisplayCounter(Origin.x + 16, --MinesRemain);
369                                                                 break;
370                                                         case Oops:
371                                                                 MineField[x][y].Picture = Mined;
372                                                 }
373                                                 DrawCell(Pt(x, y));
374                                         }
375                                         break;
376                                 case Mark:
377                                         if(! MineField[x][y].Mine) {
378                                                 MineField[x][y].Picture = Fault;
379                                                 DrawCell(Pt(x, y));
380                                         }
381                         }
382 }
383
384 void LeftClick(Point Cell) {
385
386         if(! (Status == Game) || Cell.x < 0 || Cell.y < 0) return;
387         Playing = TRUE;
388         switch(MineField[Cell.x][Cell.y].Picture) {
389                 case Query:
390                 case Unknown:
391                         if(MineField[Cell.x][Cell.y].Mine) {
392                                 MineField[Cell.x][Cell.y].Picture = Explosion;
393                                 DrawCell(Cell);
394                                 Status = Oops;
395                                 GameOver();
396                                 return;
397                         }
398                         MineField[Cell.x][Cell.y].Picture = MineField[Cell.x][Cell.y].Neighbours;
399                         DrawCell(Cell);
400                         if(! --UnknownCell) {
401                                 Status = Win;
402                                 GameOver();
403                         }
404                         else if(MineField[Cell.x][Cell.y].Neighbours == 0) {
405                                 if(Cell.x > 0) LeftClick(Pt(Cell.x - 1, Cell.y));
406                                 if(Cell.y > 0) LeftClick(Pt(Cell.x, Cell.y - 1));
407                                 if(Cell.x < MaxX - 1) LeftClick(Pt(Cell.x + 1, Cell.y));
408                                 if(Cell.y < MaxY - 1) LeftClick(Pt(Cell.x, Cell.y + 1));
409                                 if(Cell.x > 0 && Cell.y > 0) LeftClick(Pt(Cell.x - 1, Cell.y - 1));
410                                 if(Cell.x > 0 && Cell.y < MaxY - 1) LeftClick(Pt(Cell.x - 1, Cell.y + 1));
411                                 if(Cell.x < MaxX - 1 && Cell.y > 0) LeftClick(Pt(Cell.x + 1, Cell.y - 1));
412                                 if(Cell.x < MaxX - 1 && Cell.y < MaxY - 1) LeftClick(Pt(Cell.x + 1, Cell.y + 1));
413                         }
414         }
415 }
416
417 void MiddleClick(Point Cell) {
418
419         int Neighbours = 0;
420
421         if(! (Status == Game) || Cell.x < 0 || Cell.y < 0) return;
422         Playing = TRUE;
423         switch(MineField[Cell.x][Cell.y].Picture) {
424                 case Empty1:
425                 case Empty2:
426                 case Empty3:
427                 case Empty4:
428                 case Empty5:
429                 case Empty6:
430                 case Empty7:
431                 case Empty8:
432                         break;
433                 default:
434                          return;
435         }
436
437         if(Cell.x > 0 && MineField[Cell.x - 1][Cell.y].Picture == Mark) Neighbours++;
438         if(Cell.y > 0 && MineField[Cell.x][Cell.y - 1].Picture == Mark) Neighbours++;
439         if(Cell.x < MaxX - 1 && MineField[Cell.x + 1][Cell.y].Picture == Mark) Neighbours++;
440         if(Cell.y < MaxY - 1 && MineField[Cell.x][Cell.y + 1].Picture == Mark) Neighbours++;
441         if(Cell.x > 0 && Cell.y > 0 && MineField[Cell.x - 1][Cell.y - 1].Picture == Mark) Neighbours++;
442         if(Cell.x > 0 && Cell.y < MaxY - 1 && MineField[Cell.x - 1][Cell.y + 1].Picture == Mark) Neighbours++;
443         if(Cell.x < MaxX - 1 && Cell.y > 0 && MineField[Cell.x + 1][Cell.y - 1].Picture == Mark) Neighbours++;
444         if(Cell.x < MaxX - 1 && Cell.y < MaxY - 1 && MineField[Cell.x + 1][Cell.y + 1].Picture == Mark) Neighbours++;
445
446         if(Neighbours == MineField[Cell.x][Cell.y].Neighbours) {
447                 if(Cell.x > 0) LeftClick(Pt(Cell.x - 1, Cell.y));
448                 if(Cell.y > 0) LeftClick(Pt(Cell.x, Cell.y - 1));
449                 if(Cell.x < MaxX - 1) LeftClick(Pt(Cell.x + 1, Cell.y));
450                 if(Cell.y < MaxY - 1) LeftClick(Pt(Cell.x, Cell.y + 1));
451                 if(Cell.x > 0 && Cell.y > 0) LeftClick(Pt(Cell.x - 1, Cell.y - 1));
452                 if(Cell.x > 0 && Cell.y < MaxY - 1) LeftClick(Pt(Cell.x - 1, Cell.y + 1));
453                 if(Cell.x < MaxX - 1 && Cell.y > 0) LeftClick(Pt(Cell.x + 1, Cell.y - 1));
454                 if(Cell.x < MaxX - 1 && Cell.y < MaxY - 1) LeftClick(Pt(Cell.x + 1, Cell.y + 1));
455         }
456 }
457
458 void RightClick(Point Cell) {
459
460         if(! (Status == Game) || Cell.x < 0 || Cell.y < 0) return;
461
462         Playing = TRUE;
463         switch(MineField[Cell.x][Cell.y].Picture) {
464                 case Unknown:
465                         MineField[Cell.x][Cell.y].Picture = Mark;
466                         DrawCell(Cell);
467                         DisplayCounter(Origin.x + 16, --MinesRemain);
468                         break;
469                 case Mark:
470                         MineField[Cell.x][Cell.y].Picture = (UseQuery ? Query : Unknown);
471                         DrawCell(Cell);
472                         DisplayCounter(Origin.x + 16, ++MinesRemain);
473                         break;
474                 case Query:
475                         MineField[Cell.x][Cell.y].Picture = Unknown;
476                         DrawCell(Cell);
477         }
478 }
479
480 void Usage(void) {
481
482         fprint(2, "Usage: %s\n", argv0);
483         exits("usage");
484 }
485
486 void main(int argc, char **argv) {
487
488         ARGBEGIN {
489         default:
490                 Usage();
491         } ARGEND
492
493         if(argc > 0) Usage();
494
495         if(initdraw(nil, nil, "mines") < 0) {
496                 fprint(2, "%s: can't open display: %r\n", argv0);
497                 exits("initdraw");
498         }
499
500         RGB000000 = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, 0x000000ff);
501         RGB0000FF = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, 0x0000ffff);
502         RGB007F00 = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, 0x007f00ff);
503         RGB7F7F7F = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, 0x7f7f7fff);
504         RGBBFBFBF = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, 0xbfbfbfff);
505         RGBFF0000 = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, 0xff0000ff);
506         RGBFFFF00 = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, 0xffff00ff);
507         RGBFFFFFF = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, 0xffffffff);
508
509         ImageButton[Game] = allocimage(display, Rect(0, 0, 25, 25), RGB24, 0, DNofill);
510         loadimage(ImageButton[Game], ImageButton[Game]->r, SrcButtonGame, ButtonBytes);
511
512         ImageButton[Push] = allocimage(display, Rect(0, 0, 25, 25), RGB24, 0, DNofill);
513         loadimage(ImageButton[Push], ImageButton[Push]->r, SrcButtonPush, ButtonBytes);
514
515         ImageButton[Move] = allocimage(display, Rect(0, 0, 25, 25), RGB24, 0, DNofill);
516         loadimage(ImageButton[Move], ImageButton[Move]->r, SrcButtonMove, ButtonBytes);
517
518         ImageButton[Win] = allocimage(display, Rect(0, 0, 25, 25), RGB24, 0, DNofill);
519         loadimage(ImageButton[Win], ImageButton[Win]->r, SrcButtonWin, ButtonBytes);
520
521         ImageButton[Oops] = allocimage(display, Rect(0, 0, 25, 25), RGB24, 0, DNofill);
522         loadimage(ImageButton[Oops], ImageButton[Oops]->r, SrcButtonOops, ButtonBytes);
523
524         ImageSign = allocimage(display, Rect(0, 0, 11, 21), RGB24, 0, DNofill);
525         loadimage(ImageSign, ImageSign->r, SrcSign, DigitBytes);
526
527         ImageDigit[0] = allocimage(display, Rect(0, 0, 11, 21), RGB24, 0, DNofill);
528         loadimage(ImageDigit[0], ImageDigit[0]->r, SrcDigit0, DigitBytes);
529
530         ImageDigit[1] = allocimage(display, Rect(0, 0, 11, 21), RGB24, 0, DNofill);
531         loadimage(ImageDigit[1], ImageDigit[1]->r, SrcDigit1, DigitBytes);
532
533         ImageDigit[2] = allocimage(display, Rect(0, 0, 11, 21), RGB24, 0, DNofill);
534         loadimage(ImageDigit[2], ImageDigit[2]->r, SrcDigit2, DigitBytes);
535
536         ImageDigit[3] = allocimage(display, Rect(0, 0, 11, 21), RGB24, 0, DNofill);
537         loadimage(ImageDigit[3], ImageDigit[3]->r, SrcDigit3, DigitBytes);
538
539         ImageDigit[4] = allocimage(display, Rect(0, 0, 11, 21), RGB24, 0, DNofill);
540         loadimage(ImageDigit[4], ImageDigit[4]->r, SrcDigit4, DigitBytes);
541
542         ImageDigit[5] = allocimage(display, Rect(0, 0, 11, 21), RGB24, 0, DNofill);
543         loadimage(ImageDigit[5], ImageDigit[5]->r, SrcDigit5, DigitBytes);
544
545         ImageDigit[6] = allocimage(display, Rect(0, 0, 11, 21), RGB24, 0, DNofill);
546         loadimage(ImageDigit[6], ImageDigit[6]->r, SrcDigit6, DigitBytes);
547
548         ImageDigit[7] = allocimage(display, Rect(0, 0, 11, 21), RGB24, 0, DNofill);
549         loadimage(ImageDigit[7], ImageDigit[7]->r, SrcDigit7, DigitBytes);
550
551         ImageDigit[8] = allocimage(display, Rect(0, 0, 11, 21), RGB24, 0, DNofill);
552         loadimage(ImageDigit[8], ImageDigit[8]->r, SrcDigit8, DigitBytes);
553
554         ImageDigit[9] = allocimage(display, Rect(0, 0, 11, 21), RGB24, 0, DNofill);
555         loadimage(ImageDigit[9], ImageDigit[9]->r, SrcDigit9, DigitBytes);
556
557         ImageCell[Empty0] = allocimage(display, Rect(0, 0, 15, 15), RGB24, 0, DNofill);
558         loadimage(ImageCell[Empty0], ImageCell[Empty0]->r, SrcEmpty0, CellBytes);
559
560         ImageCell[Empty1] = allocimage(display, Rect(0, 0, 15, 15), RGB24, 0, DNofill);
561         loadimage(ImageCell[Empty1], ImageCell[Empty1]->r, SrcEmpty1, CellBytes);
562
563         ImageCell[Empty2] = allocimage(display, Rect(0, 0, 15, 15), RGB24, 0, DNofill);
564         loadimage(ImageCell[Empty2], ImageCell[Empty2]->r, SrcEmpty2, CellBytes);
565
566         ImageCell[Empty3] = allocimage(display, Rect(0, 0, 15, 15), RGB24, 0, DNofill);
567         loadimage(ImageCell[Empty3], ImageCell[Empty3]->r, SrcEmpty3, CellBytes);
568
569         ImageCell[Empty4] = allocimage(display, Rect(0, 0, 15, 15), RGB24, 0, DNofill);
570         loadimage(ImageCell[Empty4], ImageCell[Empty4]->r, SrcEmpty4, CellBytes);
571
572         ImageCell[Empty5] = allocimage(display, Rect(0, 0, 15, 15), RGB24, 0, DNofill);
573         loadimage(ImageCell[Empty5], ImageCell[Empty5]->r, SrcEmpty5, CellBytes);
574
575         ImageCell[Empty6] = allocimage(display, Rect(0, 0, 15, 15), RGB24, 0, DNofill);
576         loadimage(ImageCell[Empty6], ImageCell[Empty6]->r, SrcEmpty6, CellBytes);
577
578         ImageCell[Empty7] = allocimage(display, Rect(0, 0, 15, 15), RGB24, 0, DNofill);
579         loadimage(ImageCell[Empty7], ImageCell[Empty7]->r, SrcEmpty7, CellBytes);
580
581         ImageCell[Empty8] = allocimage(display, Rect(0, 0, 15, 15), RGB24, 0, DNofill);
582         loadimage(ImageCell[Empty8], ImageCell[Empty8]->r, SrcEmpty8, CellBytes);
583
584         ImageCell[Unknown] = allocimage(display, Rect(0, 0, 15, 15), RGB24, 0, DNofill);
585         loadimage(ImageCell[Unknown], ImageCell[Unknown]->r, SrcUnknown, CellBytes);
586
587         ImageCell[Mark] = allocimage(display, Rect(0, 0, 15, 15), RGB24, 0, DNofill);
588         loadimage(ImageCell[Mark], ImageCell[Mark]->r, SrcMark, CellBytes);
589
590         ImageCell[Query] = allocimage(display, Rect(0, 0, 15, 15), RGB24, 0, DNofill);
591         loadimage(ImageCell[Query], ImageCell[Query]->r, SrcQuery, CellBytes);
592
593         ImageCell[MouseQuery] = allocimage(display, Rect(0, 0, 15, 15), RGB24, 0, DNofill);
594         loadimage(ImageCell[MouseQuery], ImageCell[MouseQuery]->r, SrcMouseQuery, CellBytes);
595
596         ImageCell[Mined] = allocimage(display, Rect(0, 0, 15, 15), RGB24, 0, DNofill);
597         loadimage(ImageCell[Mined], ImageCell[Mined]->r, SrcMined, CellBytes);
598
599         ImageCell[Explosion] = allocimage(display, Rect(0, 0, 15, 15), RGB24, 0, DNofill);
600         loadimage(ImageCell[Explosion], ImageCell[Explosion]->r, SrcExplosion, CellBytes);
601
602         ImageCell[Fault] = allocimage(display, Rect(0, 0, 15, 15), RGB24, 0, DNofill);
603         loadimage(ImageCell[Fault], ImageCell[Fault]->r, SrcFault, CellBytes);
604
605         srand(time(0)); /* initialize generator of random numbers */
606
607         NewMineField(Beginner);
608
609         eresized(0);
610
611         einit(Emouse | Ekeyboard);
612
613         {
614                 int PushButton = FALSE, Button = FALSE, CurrentButton, ChargedButton = FALSE, MiddleButton = FALSE, LastButton = 0;
615                 ulong Key, Etimer;
616                 Event Event;
617                 Point CurrentCell, Cell = Pt(-1, -1);
618
619                 Etimer = etimer(0, 1000);
620
621                 for(;;) {
622                         Key = event(&Event);
623
624                         if(Key == Etimer) {
625
626                                 if(Playing && Time < INT_MAX)
627                                         DisplayCounter(Origin.x -34 + MaxX * 16, ++Time);
628                         }
629
630                         if(Key == Emouse) {
631
632                                 /* mouse over button? */
633                                 CurrentButton = FALSE;
634                                 if(Event.mouse.xy.x > Origin.x + MaxX * 8 && Event.mouse.xy.x < Origin.x + MaxX * 8 + 25 && Event.mouse.xy.y > Origin.y + 17 && Event.mouse.xy.y < Origin.y + 42) CurrentButton = TRUE;
635
636                                 /* mouse over any cell? */
637                                 CurrentCell = Pt(-1, -1);
638                                 if(Event.mouse.xy.x > Origin.x + 12 && Event.mouse.xy.x < Origin.x + 12 + MaxX * 16)
639                                         CurrentCell.x = (Event.mouse.xy.x - Origin.x - 13) / 16;
640                                 if(Event.mouse.xy.y > Origin.y + 57 && Event.mouse.xy.y < Origin.y + 57 + MaxY * 16)
641                                         CurrentCell.y = (Event.mouse.xy.y - Origin.y - 58) / 16;
642
643                                 /* pressed mouse button */
644                                 if(Event.mouse.buttons > LastButton) {
645
646                                         if(CurrentButton)
647                                                 switch(Event.mouse.buttons) {
648                                                         case 1:
649                                                                 PushButton = TRUE;
650                                                                 break;
651                                                         default:
652                                                                 if(PushButton) DrawButton(Status);
653                                                                 PushButton = FALSE;
654                                                 }
655
656                                         if(! (CurrentCell.x < 0) && ! (CurrentCell.y < 0)) {
657                                                 switch(Event.mouse.buttons) {
658                                                         case 1:
659                                                                 ChargedButton = TRUE;
660                                                                 MiddleButton = FALSE;
661                                                                 break;
662                                                         case 2:
663                                                                 if(LastButton == 0) ChargedButton = TRUE;
664                                                                 MiddleButton = TRUE;
665                                                                 break;
666                                                         case 4:
667                                                                 if(LastButton == 0) RightClick(CurrentCell);
668                                                                 else MiddleButton = TRUE;
669                                                                 break;
670                                                         default:
671                                                                 ChargedButton = TRUE;
672                                                                 MiddleButton = TRUE;
673                                                 }
674                                                 if(ChargedButton && Status == Game) DrawButton(Move);
675                                         }
676                                 }
677
678                                 if(PushButton && CurrentButton != Button) {
679                                         if(CurrentButton) DrawButton(Push);
680                                         else DrawButton(Status);
681                                         Button = CurrentButton;
682                                 }
683
684                                 if(ChargedButton && (! eqpt(CurrentCell, Cell) || Event.mouse.buttons != LastButton) && Status == Game) {
685                                         if(! (Cell.x < 0) && ! (Cell.y < 0)) {
686                                                 RecoverCell(Cell);
687                                                 if(Cell.x > 0) RecoverCell(Pt(Cell.x - 1, Cell.y));
688                                                 if(Cell.y > 0) RecoverCell(Pt(Cell.x, Cell.y - 1));
689                                                 if(Cell.x < MaxX - 1) RecoverCell(Pt(Cell.x + 1, Cell.y));
690                                                 if(Cell.y < MaxY - 1) RecoverCell(Pt(Cell.x, Cell.y + 1));
691                                                 if(Cell.x > 0 && Cell.y > 0) RecoverCell(Pt(Cell.x - 1, Cell.y - 1));
692                                                 if(Cell.x > 0 && Cell.y < MaxY - 1) RecoverCell(Pt(Cell.x - 1, Cell.y + 1));
693                                                 if(Cell.x < MaxX - 1 && Cell.y > 0) RecoverCell(Pt(Cell.x + 1, Cell.y - 1));
694                                                 if(Cell.x < MaxX - 1 && Cell.y < MaxY - 1) RecoverCell(Pt(Cell.x + 1, Cell.y + 1));
695                                         }
696                                         Cell = CurrentCell;
697                                         if(! (Cell.x < 0) && ! (Cell.y < 0) && ! (Event.mouse.buttons < LastButton)) {
698                                                 MouseCell(Cell);
699                                                 if(MiddleButton) {
700                                                         if(Cell.x > 0) MouseCell(Pt(Cell.x - 1, Cell.y));
701                                                         if(Cell.y > 0) MouseCell(Pt(Cell.x, Cell.y - 1));
702                                                         if(Cell.x < MaxX - 1) MouseCell(Pt(Cell.x + 1, Cell.y));
703                                                         if(Cell.y < MaxY - 1) MouseCell(Pt(Cell.x, Cell.y + 1));
704                                                         if(Cell.x > 0 && Cell.y > 0) MouseCell(Pt(Cell.x - 1, Cell.y - 1));
705                                                         if(Cell.x > 0 && Cell.y < MaxY - 1) MouseCell(Pt(Cell.x - 1, Cell.y + 1));
706                                                         if(Cell.x < MaxX - 1 && Cell.y > 0) MouseCell(Pt(Cell.x + 1, Cell.y - 1));
707                                                         if(Cell.x < MaxX - 1 && Cell.y < MaxY - 1) MouseCell(Pt(Cell.x + 1, Cell.y + 1));
708                                                 }
709                                         }
710                                 }
711
712                                 /* released mouse button */
713                                 if(Event.mouse.buttons < LastButton) {
714                                         if(PushButton && CurrentButton) {
715                                                 InitMineField();
716                                                 eresized(0);
717                                                 }
718                                         Button = FALSE;
719                                         PushButton = FALSE;
720
721                                         if(ChargedButton) {
722                                                 if(MiddleButton) MiddleClick(Cell);
723                                                 else LeftClick(Cell);
724                                                 DrawButton(Status);
725                                         }
726                                         Cell = Pt(-1, -1);
727                                         ChargedButton = FALSE;
728                                 }
729
730                                 LastButton = Event.mouse.buttons;
731                         }
732
733                         if(Key == Ekeyboard) {
734
735                                 switch(Event.kbdc) {
736                                         case 'n':
737                                         case 'N':
738                                                 InitMineField();
739                                                 eresized(0);
740                                                 break;
741                                         case 'b':
742                                         case 'B':
743                                                 NewMineField(Beginner);
744                                                 eresized(0);
745                                                 break;
746                                         case 'a':
747                                         case 'A':
748                                                 NewMineField(Advanced);
749                                                 eresized(0);
750                                                 break;
751                                         case 'e':
752                                         case 'E':
753                                                 NewMineField(Expert);
754                                                 eresized(0);
755                                                 break;
756                                         case 'q':
757                                         case 'Q':
758                                         case 127:
759                                                 exits(nil);
760                                 }
761                         }
762                 }
763         }
764 }