1
/* This file is a part of gtkboard, a board games system.
2
Copyright (C) 2003, Arvind Narayanan <arvindn@users.sourceforge.net>
4
This program is free software; you can redistribute it and/or modify
5
it under the terms of the GNU General Public License as published by
6
the Free Software Foundation; either version 2 of the License, or
7
(at your option) any later version.
9
This program is distributed in the hope that it will be useful,
10
but WITHOUT ANY WARRANTY; without even the implied warranty of
11
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
GNU General Public License for more details.
14
You should have received a copy of the GNU General Public License
15
along with this program; if not, write to the Free Software
16
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111 USA
24
#include <gdk/gdkkeysyms.h>
29
#define PACMAN_CELL_SIZE 25
30
#define PACMAN_NUM_PIECES 48
32
#define PACMAN_BOARD_WID 26
33
#define PACMAN_BOARD_HEIT 25
35
char pacman_colors[6] = {100, 150, 200, 100, 150, 200};
37
int * pacman_init_pos = NULL;
39
//#define PACMAN_WALL 1
41
#define PACMAN_POWER 2
42
#define PACMAN_EMPTY 3
43
#define PACMAN_IS_EDIBLE(x) (x>=1 && x<=3)
44
#define PACMAN_PAC_MIN 28
45
#define PACMAN_PAC_MAX 32
46
#define PACMAN_PAC_UP 28
47
#define PACMAN_PAC_DOWN 29
48
#define PACMAN_PAC_RIGHT 30
49
#define PACMAN_PAC_LEFT 31
50
#define PACMAN_IS_PAC(x) ((x) >= PACMAN_PAC_MIN && (x) < PACMAN_PAC_MAX)
51
//#define PACMAN_GHOST_MASK 4
52
#define PACMAN_IS_GHOST(x) ((x) >= 4 && x < 20)
53
#define PACMAN_GET_GHOST(x) ((x)-(x)%4)
54
#define PACMAN_GET_GHOST_NUM(x) (PACMAN_GET_GHOST(x)/4-1)
55
/*#define PACMAN_GHOST_INKY 5
56
#define PACMAN_GHOST_PINKY 6
57
#define PACMAN_GHOST_BLINKY 7
58
#define PACMAN_GHOST_SUE 8*/
59
//#define PACMAN_IS_GHOST(x) ((x) >= PACMAN_GHOST_INKY && (x) <= PACMAN_GHOST_SUE)
61
#define PACMAN_WALL_MASK 32
62
#define PACMAN_WALL_UP 8
63
#define PACMAN_WALL_DOWN 4
64
#define PACMAN_WALL_LEFT 2
65
#define PACMAN_WALL_RIGHT 1
67
int pacman_maze[PACMAN_BOARD_HEIT][PACMAN_BOARD_WID] =
69
{32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32},
70
{32, 2, 1, 1, 1, 1, 1,32, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,32, 1, 1, 1, 1, 1, 2,32},
71
{32, 1,32,32,32,32, 1,32, 1,32,32,32,32,32,32,32,32, 1,32, 1,32,32,32,32, 1,32},
72
{32, 1,32,32,32,32, 1,32, 1,32,32,32,32,32,32,32,32, 1,32, 1,32,32,32,32, 1,32},
73
{32, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,32},
74
{32,32,32, 1,32,32, 1,32,32,32,32, 1,32,32, 1,32,32,32,32, 1,32,32, 1,32,32,32},
75
{32,32,32, 1,32,32, 1, 1, 1, 1, 1, 1,32,32, 1, 1, 1, 1, 1, 1,32,32, 1,32,32,32},
76
{32, 1, 1, 1,32,32,32,32, 1,32,32,32,32,32,32,32,32, 1,32,32,32,32, 1, 1, 1,32},
77
{32, 1, 1, 1,32,32,32,32, 1,32,32,32,32,32,32,32,32, 1,32,32,32,32, 1, 1, 1,32},
78
{32,32,32, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,32,32,32},
79
{32,32,32, 1,32,32,32,32, 1,32,32,32,32,32,32, 1,32, 1,32,32,32,32, 1,32,32,32},
80
{32,32,32, 1,32,32,32,32, 1,32, 7, 3,15, 3, 3, 3,32, 1,32,32,32,32, 1,32,32,32},
81
{32,32,32, 1,32,32, 1, 1, 1,32, 3,11, 3,19, 3, 3,32, 1, 1, 1,32,32, 1,32,32,32},
82
{32,32,32, 1,32,32, 1,32, 1,32,32,32,32,32,32,32,32, 1,32, 1,32,32, 1,32,32,32},
83
{32, 1, 1, 1, 1, 1, 1,32, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,32, 1, 1, 1, 1, 1, 1,32},
84
{32,32,32, 1,32,32,32,32,32,32,32, 1,32,32, 1,32,32,32,32,32,32,32, 1,32,32,32},
85
{32,32,32, 1, 1, 1, 1, 1, 1, 1, 1, 1,32,32, 1, 1, 1, 1, 1, 1, 1, 1, 1,32,32,32},
86
{32,32,32, 1,32,32,32,32, 1,32,32,32,32,32,32,32,32, 1,32,32,32,32, 1,32,32,32},
87
{32,32,32, 1,32,32,32,32, 1,32,32,32,32,32,32,32,32, 1,32,32,32,32, 1,32,32,32},
88
{32, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,28, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,32},
89
{32, 1,32,32,32, 1,32,32,32,32,32, 1,32,32, 1,32,32,32,32,32, 1,32,32,32, 1,32},
90
{32, 1,32,32,32, 1,32,32, 1, 1, 1, 1,32,32, 1, 1, 1, 1,32,32, 1,32,32,32, 1,32},
91
{32, 1,32,32,32, 1,32,32, 1,32,32,32,32,32,32,32,32, 1,32,32, 1,32,32,32, 1,32},
92
{32, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2,32},
93
{32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32},
96
static char * brown_square_25_xpm[]=
101
".........................",
102
".........................",
103
".........................",
104
".........................",
105
".........................",
106
".........................",
107
".........................",
108
".........................",
109
".........................",
110
".........................",
111
".........................",
112
".........................",
113
".........................",
114
".........................",
115
".........................",
116
".........................",
117
".........................",
118
".........................",
119
".........................",
120
".........................",
121
".........................",
122
".........................",
123
".........................",
124
".........................",
125
".........................",
132
Game Pacman = { PACMAN_CELL_SIZE, PACMAN_BOARD_WID, PACMAN_BOARD_HEIT,
134
pacman_colors, NULL, NULL, "Pacman", "Arcade", pacman_init};
137
static void pacman_set_init_pos (Pos *pos);
138
static char ** pacman_get_pixmap (int idx, int color);
139
static int pacman_getmove_kb (Pos *cur_pos, int key, byte **move, int **);
140
static int pacman_animate (Pos *pos, byte **movp);
145
game_single_player = TRUE;
146
game_set_init_pos = pacman_set_init_pos;
147
game_get_pixmap = pacman_get_pixmap;
148
game_getmove_kb = pacman_getmove_kb;
149
game_animation_time = 100;
150
game_animate = pacman_animate;
151
game_doc_about_status = STATUS_UNPLAYABLE;
154
"Single player game\n"
155
"Status: Partially implemented (currently unplayable)\n"
156
"URL: "GAME_DEFAULT_URL ("pacman");
159
void pacman_get_cur_pos (byte *pos, int *x, int *y)
162
for (i=0; i<board_wid; i++)
163
for (j=0; j<board_heit; j++)
164
if (PACMAN_IS_PAC(pos [j * board_wid + i]))
165
{ *x = i; *y = j; return;
169
static short dist[PACMAN_BOARD_WID][PACMAN_BOARD_HEIT];
171
// FIXME: avoid recursion
172
void pacman_recursive_dist (byte *pos, int x, int y, int val)
174
if (x < 0 || y < 0 || x >= board_wid || y >= board_heit) return;
175
if (pos[y * board_wid + x] & PACMAN_WALL_MASK) return;
176
if (dist[x][y] >= 0 && dist[x][y] < val) return;
178
pacman_recursive_dist (pos, x+1, y, val+1);
179
pacman_recursive_dist (pos, x-1, y, val+1);
180
pacman_recursive_dist (pos, x, y+1, val+1);
181
pacman_recursive_dist (pos, x, y-1, val+1);
184
void pacman_set_dist (byte *pos)
188
pacman_get_cur_pos (pos, &curx, &cury);
189
for (i=0; i<board_wid; i++)
190
for (j=0; j<board_heit; j++)
192
dist[curx][cury] = 1;
193
pacman_recursive_dist (pos, curx, cury, 0);
196
int pacman_animate (Pos *pos, byte **movp)
198
static int count = 0;
199
static byte move[32];
201
int curx = -1, cury = -1;
202
int incx = 0, incy = 0;
205
// FIXME: do this using stateful
206
static int prevx[4] = {0, 0, 0, 0}, prevy[4] = {0, 0, 0, 0};
207
// make sure the ghosts dont step on each others toes
208
int taken[4] = {-1, -1, -1, -1}, t=0, gid;
209
byte *board = pos->board;
210
pacman_set_dist (board);
211
for (i=0; i<board_wid; i++)
212
for (j=0; j<board_heit; j++)
214
if (!PACMAN_IS_GHOST(board[j * board_wid + i]))
216
gid = PACMAN_GET_GHOST_NUM(board [j * board_wid + i]);
219
int rnd = random()%4;
222
case 0: incx = 0; incy = 1; break;
223
case 1: incx = 0; incy = -1; break;
224
case 2: incx = 1; incy = 0; break;
225
case 3: incx = -1; incy = 0; break;
228
// make the ghosts move straight as long as boardsible
229
if (k < 5 && (incx != prevx[gid] || incy != prevy[gid])) continue;
231
x = i+incx, y = j+incy;
232
// make the ghosts head straight for pacman
233
if (k < 5 && dist[x][y] > dist[i][j]) continue;
234
if (!PACMAN_IS_EDIBLE(board [y * board_wid + x])) continue;
236
for (s=0; s<t; s++) if (y * board_wid + x == taken[s])
240
prevx[gid] = incx; prevy[gid] = incy;
241
taken[t++] = y * board_wid + x;
242
*mp++ = i; *mp++ = j;
243
*mp++ = board [j * board_wid + i] -
244
PACMAN_GET_GHOST (board [j * board_wid + i]);
245
*mp++ = x; *mp++ = y;
246
*mp++ = board [y * board_wid + x] +
247
PACMAN_GET_GHOST (board [j * board_wid + i]);
252
pacman_get_cur_pos (board, &curx, &cury);
253
g_assert (curx >= 0 && cury >= 0);
254
switch ((oldboard = board [cury * board_wid + curx]))
256
case PACMAN_PAC_UP: incx = 0; incy = 1; break;
257
case PACMAN_PAC_DOWN: incx = 0; incy = -1; break;
258
case PACMAN_PAC_RIGHT: incx = 1; incy = 0; break;
259
case PACMAN_PAC_LEFT: incx = -1; incy = 0; break;
261
if (incx == 0 && incy == 0) return -1;
262
x = curx + incx; y = cury + incy;
263
if (x >= 0 && y >= 0 && x < board_wid && y < board_heit &&
264
!(board [y * board_wid + x] & PACMAN_WALL_MASK))
266
*mp++ = curx; *mp++ = cury; *mp++ = PACMAN_EMPTY;
267
*mp++ = x; *mp++ = y; *mp++ = oldboard; *mp++ = -1;
271
return mp > move ? 1 : -1;
278
int pacman_getmove_kb (Pos *pos, int key, byte **movp, int **rmovp)
280
static byte move[10];
281
int curx = -1, cury = -1;
283
pacman_get_cur_pos (pos->board, &curx, &cury);
284
g_assert (curx >= 0 && cury >= 0);
287
case GDK_Up: newpos = PACMAN_PAC_UP; break;
288
case GDK_Down: newpos = PACMAN_PAC_DOWN; break;
289
case GDK_Right: newpos = PACMAN_PAC_RIGHT; break;
290
case GDK_Left: newpos = PACMAN_PAC_LEFT; break;
293
move[0] = curx; move[1] = cury;
300
void pacman_set_init_pos (Pos *pos)
303
int incx[4] = {0, 0, 1, -1};
304
int incy[4] = {1, -1, 0, 0};
305
int wallmask[4] = {PACMAN_WALL_UP, PACMAN_WALL_DOWN,
306
PACMAN_WALL_RIGHT, PACMAN_WALL_LEFT};
307
byte *board = pos->board;
308
for (i=0; i<board_wid; i++)
309
for (j=0; j<board_heit; j++)
310
board [j * board_wid + i] = pacman_maze[board_heit - 1 - j][i];
311
for (i=0; i<board_wid; i++)
312
for (j=0; j<board_heit; j++)
314
if (board [j * board_wid + i] & PACMAN_WALL_MASK)
320
if (x < 0 || y < 0 || x >= board_wid || y>= board_heit)
322
if (board [y * board_wid + x] & PACMAN_WALL_MASK)
323
board [j * board_wid + i] |= wallmask[k];
329
static float get_angle (float x, float y)
332
if (x >= 0 && y == 0) return 0;
333
if (x < 0 && y == 0) return M_PI;
334
if (x == 0 && y > 0) return M_PI/2;
335
if (x == 0 && y < 0) return 3 * M_PI/2;
336
if (x > 0) { ang = atan(y/x); if (ang < 0) ang += 2 * M_PI; return ang;}
337
return M_PI + atan(y/x);
340
static char **pacman_pixmap_ghost_gen(char *pixbuf)
341
// FIXME: ghosts look like flowers :(
344
static char *pixmap[PACMAN_CELL_SIZE+3];
345
pixmap[0] = "25 25 2 1";
346
pixmap[1] = " c none";
347
pixmap[2] = ". c #dddddd";
348
for (i=0; i<PACMAN_CELL_SIZE; i++)
350
pixmap[i+3] = pixbuf + i * (PACMAN_CELL_SIZE+1);
351
pixmap[i+3][PACMAN_CELL_SIZE] = 0;
353
for (i=0; i<PACMAN_CELL_SIZE; i++)
354
for (j=0; j<PACMAN_CELL_SIZE; j++)
356
int x = i - PACMAN_CELL_SIZE/2, y = j - PACMAN_CELL_SIZE/2;
357
float rad = x * x + y * y;
358
float val = cos (3 * get_angle (x, y));
359
val = val * val * PACMAN_CELL_SIZE * PACMAN_CELL_SIZE/6;
360
if (rad - val < PACMAN_CELL_SIZE*PACMAN_CELL_SIZE/10)
361
pixmap[i+3][j] = '.';
363
pixmap[i+3][j] = ' ';
368
char ** pacman_pixmap_wall_gen(int idx, char *pixbuf)
374
if (!(idx & PACMAN_WALL_MASK))
376
colors = pacman_colors;
377
fg = (200 << 16) + 200;
378
for(i=0, bg=0;i<3;i++)
379
{ int col = colors[i]; if (col<0) col += 256; bg += col * (1 << (16-8*i));}
380
pixmap = g_new(char *, PACMAN_CELL_SIZE + 3);
382
pixmap[i] = brown_square_25_xpm[i];
383
for (i=0; i<PACMAN_CELL_SIZE; i++)
385
//pixmap[i+3] = g_new(char, PACMAN_CELL_SIZE+1);
386
pixmap[i+3] = pixbuf + i * (PACMAN_CELL_SIZE+1);
387
pixmap[i+3][PACMAN_CELL_SIZE] = 0;
389
for (i=0; i<PACMAN_CELL_SIZE; i++)
390
for (j=0; j<PACMAN_CELL_SIZE; j++)
392
pixmap[i+3][j] = ' ';
393
if ((i >= PACMAN_CELL_SIZE/3 && i < 2*PACMAN_CELL_SIZE/3)
394
&& (j >= PACMAN_CELL_SIZE/3 && j < 2*PACMAN_CELL_SIZE/3))
395
pixmap[i+3][j] = '.';
396
if ((i >= PACMAN_CELL_SIZE*2/3)
397
&& (j >= PACMAN_CELL_SIZE/3 && j < 2*PACMAN_CELL_SIZE/3)
398
&& idx & PACMAN_WALL_DOWN)
399
pixmap[i+3][j] = '.';
400
if ((i < PACMAN_CELL_SIZE/3)
401
&& (j >= PACMAN_CELL_SIZE/3 && j < 2*PACMAN_CELL_SIZE/3)
402
&& idx & PACMAN_WALL_UP)
403
pixmap[i+3][j] = '.';
404
if ((i >= PACMAN_CELL_SIZE/3 && i < 2*PACMAN_CELL_SIZE/3)
405
&& (j >= PACMAN_CELL_SIZE*2/3)
406
&& idx & PACMAN_WALL_RIGHT)
407
pixmap[i+3][j] = '.';
408
if ((i >= PACMAN_CELL_SIZE/3 && i < 2*PACMAN_CELL_SIZE/3)
409
&& (j < PACMAN_CELL_SIZE/3)
410
&& idx & PACMAN_WALL_LEFT)
411
pixmap[i+3][j] = '.';
416
char ** pacman_pixmap_pac_gen(float dir, float gap, char *pixbuf)
422
pixmap = g_new(char *, 5 + PACMAN_CELL_SIZE);
423
pixmap[0] = "25 25 4 1";
424
pixmap[1] = " c none";
425
pixmap[2] = "b c black";
426
pixmap[3] = "y c yellow";
427
pixmap[4] = "r c red";
428
ang = dir * M_PI / 2;
431
for (i=0; i<PACMAN_CELL_SIZE; i++)
433
pixmap[i+5] = pixbuf + i * (PACMAN_CELL_SIZE+1);
434
pixmap[i+5][PACMAN_CELL_SIZE] = 0;
435
for (j=0; j<PACMAN_CELL_SIZE; j++)
438
pixmap[i+5][j] = ' ';
439
x = i - PACMAN_CELL_SIZE/2, y = j - PACMAN_CELL_SIZE/2;
440
if (x * x + y * y > PACMAN_CELL_SIZE * PACMAN_CELL_SIZE/6)
443
if (tmp > 0) { x /= tmp; y /= tmp; }
444
if (x0 * x + y0 * y < cos (gap * M_PI / 180))
445
pixmap[i+5][j] = 'y';
447
pixmap[i+5][j] = 'y';
453
char ** pacman_get_pixmap (int idx, int color)
457
static char pixbuf[PACMAN_CELL_SIZE*(PACMAN_CELL_SIZE+1)];
458
if (idx & PACMAN_WALL_MASK)
459
return pacman_pixmap_wall_gen(idx, pixbuf);
460
colors = pacman_colors;
461
for(i=0, bg=0;i<3;i++)
462
{ int col = colors[i]; if (col<0) col += 256; bg += col * (1 << (16-8*i));}
463
if (idx == PACMAN_FOOD)
466
return pixmap_ball_gen(PACMAN_CELL_SIZE, pixbuf, fg, bg, 4.0, 30.0);
468
else if (idx == PACMAN_EMPTY)
471
return pixmap_ball_gen(PACMAN_CELL_SIZE, pixbuf, fg, bg, 0, 1);
473
else if (idx == PACMAN_POWER)
476
return pixmap_ball_gen(PACMAN_CELL_SIZE, pixbuf, fg, bg, 6.0, 30.0);
478
else if (idx == PACMAN_PAC_UP)
480
return pacman_pixmap_pac_gen(2, 45, pixbuf);
482
else if (idx == PACMAN_PAC_DOWN)
484
return pacman_pixmap_pac_gen(0, 45, pixbuf);
486
else if (idx == PACMAN_PAC_RIGHT)
488
return pacman_pixmap_pac_gen(1, 45, pixbuf);
490
else if (idx == PACMAN_PAC_LEFT)
492
return pacman_pixmap_pac_gen(3, 45, pixbuf);
494
else if (PACMAN_IS_GHOST(idx))
497
return pacman_pixmap_ghost_gen (pixbuf);
498
//pixmap_ball_gen(PACMAN_CELL_SIZE, pixbuf, fg, bg, 6.0, 24.0);