1 /* Copyright (C) 1993, 1992 Nathan Sidwell */ 2 /* RCS $Id: garden.c,v 4.6 1994/01/06 10:20:24 nathan Stable $ */ 3 /*{{{ includes*/ 4 #include "xmred.h" 5 #include <X11/IntrinsicP.h> 6 #include <X11/StringDefs.h> 7 #include <X11/Xaw/SimpleP.h> 8 /*}}}*/ 9 /*{{{ defines*/ 10 #define WIDTH_EXPAND (GAP_WIDTH / 2) 11 #define HEIGHT_EXPAND (GAP_HEIGHT / 2) 12 /*}}}*/ 13 /*{{{ structs*/ 14 /*{{{ typedef struct _IconClass*/ 15 typedef struct _GardenClass 16 { 17 int ansi_compliance; /* not used */ 18 } GardenClassPart; 19 /*}}}*/ 20 /*{{{ typedef struct _GardenClassRec*/ 21 typedef struct _GardenClassRec 22 { 23 CoreClassPart core_class; 24 SimpleClassPart simple_class; 25 GardenClassPart garden_class; 26 } GardenClassRec; 27 /*}}}*/ 28 /*{{{ typedef struct _GardenPart*/ 29 typedef struct 30 { 31 /* private state */ 32 Position x; /* corner of pixmap */ 33 Position y; /* corner of pixmap */ 34 COORD cell; /* last known cell */ 35 unsigned place; /* place within cell 36 * 0-3 edges */ 37 COORD dir; /* tracking direction */ 38 unsigned onoff; /* adding or removing? */ 39 int option; /* button currently pressed */ 40 } GardenPart; 41 /*}}}*/ 42 /*{{{ typedef struct _GardenRec*/ 43 typedef struct _GardenRec 44 { 45 CorePart core; 46 SimplePart simple; 47 GardenPart garden; 48 } GardenRec; 49 /*}}}*/ 50 /*}}}*/ 51 /*{{{ statics*/ 52 static Widget garden; 53 /*}}}*/ 54 /*{{{ prototypes*/ 55 static VOIDFUNC Move PROTOARG((Widget, XEvent *, String *, Cardinal *)); 56 static VOIDFUNC Press PROTOARG((Widget, XEvent *, String *, Cardinal *)); 57 static VOIDFUNC Release PROTOARG((Widget, XEvent *, String *, Cardinal *)); 58 59 static VOIDFUNC Destroy PROTOARG((Widget)); 60 static VOIDFUNC Initialize PROTOARG((Widget, Widget, ArgList, Cardinal *)); 61 static XtGeometryResult QueryGeometry 62 PROTOARG((Widget, XtWidgetGeometry *, XtWidgetGeometry *)); 63 static VOIDFUNC Redisplay PROTOARG((Widget, XEvent *, Region)); 64 static VOIDFUNC Resize PROTOARG((Widget)); 65 static Boolean SetValues 66 PROTOARG((Widget, Widget, Widget, ArgList, Cardinal *)); 67 68 static VOIDFUNC RedrawRect 69 PROTOARG((Widget, Position, Position, Dimension, Dimension)); 70 71 static unsigned do_option 72 PROTOARG((GardenWidget, unsigned, unsigned, char *, unsigned)); 73 static VOIDFUNC draw_board_icon 74 PROTOARG((DESCRIPTOR CONST *, int, int, unsigned, unsigned)); 75 static unsigned get_cell PROTOARG((GardenWidget, XEvent *, COORD *)); 76 static unsigned get_paths PROTOARG((unsigned, unsigned, char **)); 77 static unsigned long change_path 78 PROTOARG((unsigned, unsigned, unsigned, unsigned, char *, unsigned)); 79 static VOIDFUNC cut_back 80 PROTOARG((int, int, unsigned, unsigned, int, int, SPRITE CONST *)); 81 static VOIDFUNC cut_sprite PROTOARG((unsigned, int, int)); 82 static unsigned long force_blank PROTOARG((unsigned, unsigned, char *)); 83 static VOIDFUNC paint_cell PROTOARG((unsigned, unsigned, unsigned, unsigned)); 84 static unsigned long set_random PROTOARG((unsigned, unsigned, char *)); 85 /*}}}*/ 86 /*{{{ translations*/ 87 static char translations[] = "\ 88 <BtnDown>:press() move()\n\ 89 <BtnUp>:release()\n\ 90 <BtnMotion>:move()\n\ 91 "; 92 /*}}}*/ 93 /*{{{ actions*/ 94 static XtActionsRec actions[] = 95 { 96 {"move", Move}, 97 {"press", Press}, 98 {"release", Release}, 99 }; 100 /*}}}*/ 101 #define SuperClass (WidgetClass)&simpleClassRec 102 /*{{{ GardenClassRec gardenClassRec =*/ 103 GardenClassRec gardenClassRec = 104 { 105 /*{{{ core class part*/ 106 { 107 SuperClass, /* superclass */ 108 "Garden", /* class_name */ 109 sizeof(GardenRec), /* size */ 110 NULL, /* class_initialize */ 111 NULL, /* class_part_initialize */ 112 False, /* class_inited */ 113 Initialize, /* initialize */ 114 NULL, /* initialize_hook */ 115 XtInheritRealize, /* realize */ 116 actions, /* actions */ 117 XtNumber(actions), /* num_actions */ 118 NULL, /* resources */ 119 0, /* num_resources */ 120 NULLQUARK, /* xrm_class */ 121 True, /* compress_motion */ 122 XtExposeCompressMultiple, /* compress_exposure */ 123 True, /* compress_enterleave */ 124 False, /* visible_interest */ 125 Destroy, /* destroy */ 126 Resize, /* resize */ 127 Redisplay, /* expose */ 128 SetValues, /* set_values */ 129 NULL, /* set_values_hook */ 130 XtInheritSetValuesAlmost, /* set_values_almost */ 131 NULL, /* get_values_hook */ 132 NULL, /* accept_focus */ 133 XtVersion, /* version */ 134 NULL, /* callback_private */ 135 translations, /* default_translations */ 136 QueryGeometry, /* query_geometry */ 137 XtInheritDisplayAccelerator, /* display_accelerator */ 138 NULL, /* extension */ 139 }, 140 /*}}}*/ 141 /*{{{ simple class part*/ 142 { 143 XtInheritChangeSensitive /* change_sensitive */ 144 }, 145 /*}}}*/ 146 /*{{{ garden class part*/ 147 { 148 0, /* dummy */ 149 }, 150 /*}}}*/ 151 }; 152 /*}}}*/ 153 WidgetClass gardenWidgetClass = (WidgetClass)&gardenClassRec; 154 /* actions */ 155 /*{{{ void Move(widget, event, params, num_params)*/ 156 static VOIDFUNC Move 157 FUNCARG((widget, event, params, num_params), 158 Widget widget 159 ARGSEP XEvent *event 160 ARGSEP String *params 161 ARGSEP Cardinal *num_params 162 ) 163 /* deal with pointer pressed motion events 164 */ 165 { 166 GardenWidget gw; 167 168 gw = (GardenWidget)widget; 169 if(event->type == MotionNotify && gw->garden.option >= 0) 170 { 171 unsigned place; 172 COORD cell; 173 COORD dir; 174 unsigned cont; 175 unsigned icon; 176 177 place = get_cell(gw, event, &cell); 178 dir.x = cell.x < gw->garden.cell.x ? -1 : 179 cell.x > gw->garden.cell.x ? 1 : 0; 180 dir.y = cell.y < gw->garden.cell.y ? -1 : 181 cell.y > gw->garden.cell.y ? 1 : 0; 182 cont = ((dir.x != 0) << 1) | (dir.y != 0); 183 icon = 0; 184 if(cont) 185 /*{{{ moved to new cell*/ 186 { 187 unsigned first; 188 189 first = 1; 190 if(dir.x * gw->garden.dir.x < 0) 191 { 192 gw->garden.dir.x = 0; 193 gw->garden.dir.y = dir.y; 194 } 195 else if(dir.y * gw->garden.dir.y < 0) 196 { 197 gw->garden.dir.x = dir.x; 198 gw->garden.dir.y = 0; 199 } 200 if(!gw->garden.dir.y && !gw->garden.dir.x) 201 { 202 gw->garden.dir.y = dir.x ? 0 : dir.y; 203 gw->garden.dir.x = dir.x; 204 } 205 while(cont) 206 { 207 unsigned mask; 208 209 /*{{{ arrived @ x?*/ 210 if(cell.x == gw->garden.cell.x) 211 { 212 cont &= 1; 213 if(dir.y) 214 { 215 gw->garden.dir.x = 0; 216 gw->garden.dir.y = dir.y; 217 } 218 } 219 /*}}}*/ 220 /*{{{ arrived @ y?*/ 221 if(cell.y == gw->garden.cell.y) 222 { 223 cont &= 2; 224 if(dir.x) 225 { 226 gw->garden.dir.x = dir.x; 227 gw->garden.dir.y = 0; 228 } 229 } 230 /*}}}*/ 231 mask = gw->garden.dir.x < 0 ? 0x8 : gw->garden.dir.x > 0 ? 0x4 : 232 gw->garden.dir.y < 0 ? 0x2 : gw->garden.dir.y > 0 ? 0x1 : 0; 233 assert(mask); 234 if(first) 235 mask = (mask ^ 0xF) & gw->garden.place ? 0 : 0x10; 236 else if(cont) 237 mask |= 0x10; 238 else if((mask & place) != place) 239 mask |= 0x10 | place; 240 if(mask) 241 icon |= do_option(gw, 0, mask, NULL, 0); 242 first = 0; 243 if(cont) 244 { 245 gw->garden.cell.x += gw->garden.dir.x; 246 gw->garden.cell.y += gw->garden.dir.y; 247 } 248 } 249 } 250 /*}}}*/ 251 else if(place != gw->garden.place) 252 icon = do_option(gw, 0, place | 0x10, NULL, 0); 253 gw->garden.place = place; 254 if(icon) 255 repaint_garden_icon(); 256 } 257 return; 258 } 259 /*}}}*/ 260 /*{{{ void Press(widget, event, params, num_params)*/ 261 static VOIDFUNC Press 262 FUNCARG((widget, event, params, num_params), 263 Widget widget 264 ARGSEP XEvent *event 265 ARGSEP String *params 266 ARGSEP Cardinal *num_params 267 ) 268 /* deal with button press event, 269 * override current button press, fetch option 270 * and determine whether adding or removing 271 */ 272 { 273 GardenWidget gw; 274 275 gw = (GardenWidget)widget; 276 if(event->type != ButtonPress) 277 /* EMPTY */; 278 else if((gw->garden.option = state.button[event->xbutton.button - 1]) < 0) 279 /* EMPTY */; 280 else 281 { 282 unsigned place; 283 COORD cell; 284 285 place = get_cell(gw, event, &cell); 286 if(place) 287 { 288 char *cptr; 289 unsigned current; 290 291 save_garden(); 292 gw->garden.place = place; 293 gw->garden.cell.x = cell.x; 294 gw->garden.cell.y = cell.y; 295 current = get_paths(cell.x, cell.y, &cptr); 296 /*{{{ set or release?*/ 297 switch(gw->garden.option) 298 { 299 /*{{{ case OPTION_APPLES:*/ 300 case OPTION_APPLES: 301 gw->garden.onoff = !ISAPPLE(*cptr) || 302 !(GARDENAPPLE(*cptr) & (1 << state.apple)); 303 break; 304 /*}}}*/ 305 /*{{{ case OPTION_RANDOM:*/ 306 case OPTION_RANDOM: 307 gw->garden.onoff = ISPATH(*cptr) || *cptr == GARDEN_NOAPPLE || 308 *cptr == GARDEN_CHERRY; 309 break; 310 /*}}}*/ 311 /*{{{ case OPTION_CHERRY:*/ 312 case OPTION_CHERRY: 313 gw->garden.onoff = *cptr != GARDEN_CHERRY && 314 !ISPATHCHERRY(*cptr); 315 break; 316 /*}}}*/ 317 /*{{{ case OPTION_PATH:*/ 318 case OPTION_PATH: 319 gw->garden.onoff = current & place; 320 break; 321 /*}}}*/ 322 /*{{{ case OPTION_DEN:*/ 323 case OPTION_DEN: 324 gw->garden.onoff = !ISPATHDEN(*cptr); 325 break; 326 /*}}}*/ 327 /*{{{ case OPTION_PLAYER:*/ 328 case OPTION_PLAYER: 329 gw->garden.onoff = !ISPATHPLAYER(*cptr); 330 break; 331 /*}}}*/ 332 } 333 /*}}}*/ 334 if(do_option(gw, 1, place, cptr, current)) 335 repaint_garden_icon(); 336 } 337 } 338 return; 339 } 340 /*}}}*/ 341 /*{{{ void Release(widget, event, params, num_params)*/ 342 static VOIDFUNC Release 343 FUNCARG((widget, event, params, num_params), 344 Widget widget 345 ARGSEP XEvent *event 346 ARGSEP String *params 347 ARGSEP Cardinal *num_params 348 ) 349 /* deal with button release event 350 * if its the current button being released, then 351 * set current to none 352 */ 353 { 354 GardenWidget gw; 355 356 gw = (GardenWidget)widget; 357 if(event->type == ButtonRelease && 358 state.button[event->xbutton.button - 1] == gw->garden.option) 359 gw->garden.option = -1; 360 return; 361 } 362 /*}}}*/ 363 /* methods */ 364 /*{{{ void Destroy(widget)*/ 365 static VOIDFUNC Destroy 366 FUNCARG((widget), 367 Widget widget 368 ) 369 /* free the GC and remove the flash timeout 370 */ 371 { 372 return; 373 } 374 /*}}}*/ 375 /*{{{ void Initialize(treq, tnew, args, num_args)*/ 376 static VOIDFUNC Initialize 377 FUNCARG((treq, tnew, args, num_args), 378 Widget treq 379 ARGSEP Widget tnew 380 ARGSEP ArgList args 381 ARGSEP Cardinal *num_args 382 ) 383 /* set out default size 384 */ 385 { 386 GardenWidget ngw; 387 388 ngw = (GardenWidget)tnew; 389 ngw->garden.x = ngw->garden.y = 0; 390 ngw->garden.place = 0; 391 ngw->garden.dir.x = ngw->garden.dir.y = 0; 392 ngw->garden.option = -1; 393 if(!ngw->core.width) 394 ngw->core.width = WINDOW_WIDTH; 395 if(!ngw->core.height) 396 ngw->core.height = WINDOW_HEIGHT; 397 return; 398 } 399 /*}}}*/ 400 /*{{{ XtGeometryResult QueryGeometry(widget, proposed, answer)*/ 401 static XtGeometryResult QueryGeometry 402 FUNCARG((widget, proposed, answer), 403 Widget widget 404 ARGSEP XtWidgetGeometry *proposed 405 ARGSEP XtWidgetGeometry *answer 406 ) 407 /* tell our parent what size we'd like to be 408 */ 409 { 410 GardenWidget gw; 411 412 gw = (GardenWidget)widget; 413 answer->request_mode = CWWidth | CWHeight; 414 answer->width = WINDOW_WIDTH; 415 answer->height = WINDOW_HEIGHT; 416 if((proposed->request_mode & (CWWidth | CWHeight)) == (CWWidth | CWHeight) && 417 proposed->width == answer->width && proposed->height == answer->height) 418 return XtGeometryYes; 419 else if(answer->width == gw->core.width && answer->height == gw->core.height) 420 return XtGeometryNo; 421 else 422 return XtGeometryAlmost; 423 } 424 /*}}}*/ 425 /*{{{ void Redisplay(widget, event, region)*/ 426 static VOIDFUNC Redisplay 427 FUNCARG((widget, event, region), 428 Widget widget 429 ARGSEP XEvent *event 430 ARGSEP Region region 431 ) 432 /* repaint ourselves 433 */ 434 { 435 GardenWidget gw; 436 XRectangle rect; 437 438 if(!XtIsRealized(widget)) 439 return; 440 gw = (GardenWidget)widget; 441 XClipBox(region, &rect); 442 XCopyArea(display.display, display.copy, XtWindow((Widget)gw), GCN(GC_COPY), 443 rect.x - gw->garden.x, rect.y - gw->garden.y, rect.width, rect.height, 444 rect.x, rect.y); 445 return; 446 } 447 /*}}}*/ 448 /*{{{ void Resize(widget)*/ 449 static VOIDFUNC Resize 450 FUNCARG((widget), 451 Widget widget 452 ) 453 /* recenter the pixmap, when we get resized 454 */ 455 { 456 GardenWidget gw; 457 458 gw = (GardenWidget)widget; 459 gw->garden.x = (gw->core.width - WINDOW_WIDTH) / 2; 460 gw->garden.y = (gw->core.height - WINDOW_HEIGHT) / 2; 461 return; 462 } 463 /*}}}*/ 464 /*{{{ Boolean SetValues(cw, rw, nw, args, num_args)*/ 465 static Boolean SetValues 466 FUNCARG((cw, rw, nw, args, num_args), 467 Widget cw 468 ARGSEP Widget rw 469 ARGSEP Widget nw 470 ARGSEP ArgList args 471 ARGSEP Cardinal *num_args 472 ) 473 { 474 return False; 475 } 476 /*}}}*/ 477 /* public routines */ 478 /*{{{ void RedrawRect(widget, x, y, w, h)*/ 479 static VOIDFUNC RedrawRect 480 FUNCARG((widget, x, y, w, h), 481 Widget widget 482 ARGSEP Position x 483 ARGSEP Position y 484 ARGSEP Dimension w 485 ARGSEP Dimension h 486 ) 487 { 488 GardenWidget gw; 489 490 if(!XtIsRealized(widget)) 491 return; 492 gw = (GardenWidget)widget; 493 XCopyArea(display.display, display.copy, XtWindow((Widget)gw), GCN(GC_COPY), 494 x, y, w, h, gw->garden.x + x, gw->garden.y + y); 495 return; 496 } 497 /*}}}*/ 498 /* non-widget routines */ 499 /*{{{ unsigned long change_path(x, y, mask, set, cptr, current)*/ 500 static unsigned long change_path 501 FUNCARG((x, y, mask, set, cptr, current), 502 unsigned x /* cell to changed */ 503 ARGSEP unsigned y 504 ARGSEP unsigned mask /* mask to change 505 * 0..3 edges, 506 * 4 center, 507 * 5 place random 508 * 6 clear cherry */ 509 ARGSEP unsigned set /* set or clear those specified */ 510 ARGSEP char *cptr /* map pointer */ 511 ARGSEP unsigned current /* current paths */ 512 ) 513 /* sets or clears the specified parts of a cell. 514 * forces the center to blank 515 * clears the center of adjacent cells if wall is cleared (by recursive call) 516 * returns a mask specifying which bits should be redrawn. 517 * 0..3 edges 518 * 4..7 adjacent centers 519 * 8 center 520 * 9..11 above, left and right tops edges 521 * 13..15 above, left and right above centers 522 * 16..19 adjacent centers changed 523 */ 524 { 525 unsigned long redraw; 526 unsigned random; 527 unsigned cherry; 528 unsigned cleared; 529 530 random = mask & 0x20; 531 redraw = cherry = 0; 532 cleared = mask & 0x40; 533 if(cleared) 534 redraw = force_blank(x, y, cptr); 535 /*{{{ clip*/ 536 { 537 if(!x) 538 mask &= ~4; 539 if(!y) 540 mask &= ~1; 541 if(x == CELLS_ACROSS - 1) 542 mask &= ~8; 543 if(y == CELLS_DOWN - 1) 544 mask &= ~2; 545 } 546 /*}}}*/ 547 if(set) 548 { 549 if(mask & 0x10) 550 mask |= 0xF; 551 mask &= current; 552 } 553 else 554 { 555 if(mask & 0x0F) 556 mask |= 0x10; 557 mask &= ~current; 558 } 559 if(mask & 0x10) 560 { 561 cherry = *cptr == GARDEN_CHERRY || ISPATHCHERRY(*cptr); 562 if(cherry) 563 random = 0; 564 else if(!cleared) 565 redraw = force_blank(x, y, cptr); 566 } 567 if(mask & 0x10 && !y && INRANGE(x, 4, 8)) 568 redraw |= 0x1; 569 if(mask) 570 changed_flag |= state.change; 571 /*{{{ clear center?*/ 572 if(!set && mask & 0x10) 573 { 574 redraw |= 0x100; 575 if(y && (cptr[-1 - CELLS_ACROSS] == GARDEN_RANDOM || 576 ISAPPLE(cptr[-1 - CELLS_ACROSS]))) 577 { 578 adjust_count(COUNT_FALL, 1); 579 redraw |= 0x10; 580 } 581 *cptr = cherry ? GARDEN_PATH + GARDEN_PATH_CHERRY : GARDEN_PATH; 582 } 583 /*}}}*/ 584 /*{{{ change above?*/ 585 if(mask & 0x01) 586 { 587 if(!set && !ISPATH(cptr[-1 - CELLS_ACROSS])) 588 { 589 redraw |= (change_path(x, y - 1, 0x10, 0, 590 cptr - CELLS_ACROSS - 1, get_paths(x, y - 1, NULL)) & 0x11) << 9; 591 redraw |= 0x10010; 592 } 593 if(ISPATHDEN(cptr[-1 - CELLS_ACROSS])) 594 redraw |= 0x10; 595 assert(ISPATH(*cptr) && y); 596 redraw |= 0x01; 597 cptr[-1 - CELLS_ACROSS] = 598 (GARDENPATH(cptr[-1 - CELLS_ACROSS]) ^ 1) + GARDEN_PATH; 599 if(!(GARDENPATH(*cptr) & 2) || !x || !ISPATH(cptr[-1]) || 600 !(GARDENPATH(cptr[-1]) & 2)) 601 redraw |= 0x100; 602 if(!(GARDENPATH(cptr[-1 - CELLS_ACROSS]) & 2) || !x || 603 !ISPATH(cptr[-2 - CELLS_ACROSS]) || 604 !(GARDENPATH(cptr[-2 - CELLS_ACROSS]) & 2)) 605 redraw |= 0x10; 606 } 607 /*}}}*/ 608 /*{{{ change below?*/ 609 if(mask & 0x02) 610 { 611 if(!set && !ISPATH(cptr[1 + CELLS_ACROSS])) 612 { 613 change_path(x, y + 1, 0x10, 0, 614 cptr + CELLS_ACROSS + 1, get_paths(x, y + 1, NULL)); 615 redraw |= 0x20020; 616 } 617 if(ISPATHDEN(cptr[1 + CELLS_ACROSS])) 618 redraw |= 0x20; 619 assert(ISPATH(*cptr) && y != CELLS_DOWN - 1); 620 redraw |= 0x02; 621 *cptr = (GARDENPATH(*cptr) ^ 1) + GARDEN_PATH; 622 if(!(GARDENPATH(*cptr) & 2) || !x || !ISPATH(cptr[-1]) || 623 !(GARDENPATH(cptr[-1]) & 2)) 624 redraw |= 0x100; 625 if(!(GARDENPATH(cptr[1 + CELLS_ACROSS]) & 2) || !x || 626 !ISPATH(cptr[CELLS_ACROSS]) || 627 !(GARDENPATH(cptr[CELLS_ACROSS]) & 2)) 628 redraw |= 0x20; 629 } 630 /*}}}*/ 631 /*{{{ change left?*/ 632 if(mask & 0x04) 633 { 634 if(!set && !ISPATH(cptr[-1])) 635 { 636 redraw |= (change_path(x - 1, y, 0x10, 0, 637 cptr - 1, get_paths(x - 1, y, NULL)) & 0x11) << 10; 638 redraw |= 0x40040; 639 } 640 if(ISPATHDEN(cptr[-1])) 641 redraw |= 0x40; 642 assert(x && ISPATH(*cptr)); 643 redraw |= 0x04; 644 cptr[-1] = (GARDENPATH(cptr[-1]) ^ 2) + GARDEN_PATH; 645 if(!(GARDENPATH(*cptr) & 1) || !y || !ISPATH(cptr[-1 - CELLS_ACROSS]) || 646 !(GARDENPATH(cptr[-1 - CELLS_ACROSS]) & 1)) 647 redraw |= 0x100; 648 if(!(GARDENPATH(cptr[-1]) & 1) || !y || 649 !ISPATH(cptr[-2 - CELLS_ACROSS]) || 650 !(GARDENPATH(cptr[-2 - CELLS_ACROSS]) & 1)) 651 redraw |= 0x40; 652 } 653 /*}}}*/ 654 /*{{{ change right?*/ 655 if(mask & 0x08) 656 { 657 if(!set && !ISPATH(cptr[1])) 658 { 659 redraw |= (change_path(x + 1, y, 0x10, 0, 660 cptr + 1, get_paths(x + 1, y, NULL)) & 0x011) << 11; 661 redraw |= 0x80080; 662 } 663 if(ISPATHDEN(cptr[1])) 664 redraw |= 0x80; 665 assert(ISPATH(*cptr) && x != CELLS_ACROSS - 1); 666 redraw |= 0x08; 667 *cptr = (GARDENPATH(*cptr) ^ 2) + GARDEN_PATH; 668 if(!(GARDENPATH(*cptr) & 1) || !y || !ISPATH(cptr[-1 - CELLS_ACROSS]) || 669 !(GARDENPATH(cptr[-1 - CELLS_ACROSS]) & 1)) 670 redraw |= 0x100; 671 if(!(GARDENPATH(cptr[1]) & 1) || !y || !ISPATH(cptr[-CELLS_ACROSS]) || 672 !(GARDENPATH(cptr[-CELLS_ACROSS]) & 1)) 673 redraw |= 0x80; 674 } 675 /*}}}*/ 676 /*{{{ set center?*/ 677 if(set && mask & 0x10) 678 { 679 redraw |= 0x100; 680 assert((ISPATHBLANK(*cptr) || ISPATHCHERRY(*cptr)) && 681 !(GARDENPATH(*cptr) & 3)); 682 *cptr = cherry ? GARDEN_CHERRY : GARDEN_NOAPPLE; 683 if(random) 684 redraw |= set_random(x, y, cptr); 685 if(y && (cptr[-1 - CELLS_ACROSS] == GARDEN_RANDOM || 686 ISAPPLE(cptr[-1 - CELLS_ACROSS]))) 687 { 688 adjust_count(COUNT_FALL, -1); 689 redraw |= 0x10; 690 } 691 } 692 /*}}}*/ 693 return redraw; 694 } 695 /*}}}*/ 696 /*{{{ void cut_back(sx, sy, width, height, dx, dy, sprite)*/ 697 static VOIDFUNC cut_back 698 FUNCARG((sx, sy, width, height, dx, dy, sprite), 699 int sx /* offset within source sprite */ 700 ARGSEP int sy 701 ARGSEP unsigned width /* size of blat */ 702 ARGSEP unsigned height 703 ARGSEP int dx /* destination address */ 704 ARGSEP int dy 705 ARGSEP SPRITE CONST *sprite /* sprite to blat on */ 706 ) 707 /* 708 * munches the background image with the specified sprite 709 */ 710 { 711 if(display.background != COLOUR_ONE) 712 XCopyArea(display.display, sprite->mask, display.copy, GCN(GC_MASK), 713 sx, sy, width, height, dx, dy); 714 if(display.background != COLOUR_ZERO) 715 XCopyArea(display.display, sprite->image, display.copy, GCN(GC_OR), 716 sx, sy, width, height, dx, dy); 717 return; 718 } 719 /*}}}*/ 720 /*{{{ void cut_sprite(ix, flag, x, y)*/ 721 static VOIDFUNC cut_sprite 722 FUNCARG((ix, x, y), 723 unsigned ix 724 ARGSEP int x 725 ARGSEP int y 726 ) 727 /* 728 * copy a sprite onto the background 729 */ 730 { 731 SPRITE CONST *sptr; 732 733 sptr = &sprites[ix]; 734 if(!INRANGE(ix, SPRITE_CENTER_BASE, 4) || display.background != COLOUR_ZERO) 735 XCopyArea(display.display, sptr->mask, display.copy, GCN(GC_MASK), 736 0, 0, sptr->size.x, sptr->size.y, x, y); 737 XCopyArea(display.display, sptr->image, display.copy, GCN(GC_OR), 738 0, 0, sptr->size.x, sptr->size.y, x, y); 739 return; 740 } 741 /*}}}*/ 742 /*{{{ unsigned do_option(gw, start, place, cptr, current)*/ 743 static unsigned do_option 744 FUNCARG((gw, start, place, cptr, current), 745 GardenWidget gw 746 ARGSEP unsigned start /* first action of sequence */ 747 ARGSEP unsigned place /* place mask on cell */ 748 ARGSEP char *cptr /* char pointer to map (calculated if NULL) */ 749 ARGSEP unsigned current /* current walls (calculated if cptr is NULL) */ 750 ) 751 /* perform an option on the current garden cell, 752 * using the specified place mask. 753 * the caller must deal with tracking 754 * returns flag which specifies if the icon needs refreshing 755 */ 756 { 757 unsigned long redraw; 758 759 redraw = 0; 760 assert(gw->garden.option >= 0); 761 if(!cptr) 762 current = get_paths(gw->garden.cell.x, gw->garden.cell.y, &cptr); 763 /*{{{ perform the option*/ 764 switch(gw->garden.option) 765 { 766 /*{{{ case OPTION_APPLES:*/ 767 case OPTION_APPLES: 768 { 769 if(!(start || place & 0x10)) 770 /* EMPTY */; 771 else if(gw->garden.onoff) 772 { 773 redraw = change_path(gw->garden.cell.x, gw->garden.cell.y, 0x10, 774 1, cptr, current); 775 if(*cptr == GARDEN_CHERRY) 776 force_blank(gw->garden.cell.x, gw->garden.cell.y, cptr); 777 redraw |= set_random(gw->garden.cell.x, gw->garden.cell.y, cptr); 778 if(!ISAPPLE(*cptr)) 779 *cptr = GARDEN_APPLE; 780 if(!(GARDENAPPLE(*cptr) & (1 << state.apple))) 781 { 782 *cptr += 1 << state.apple; 783 adjust_count(COUNT_APPLES + state.apple, 1); 784 } 785 redraw |= 0x100; 786 } 787 else if(ISAPPLE(*cptr) && GARDENAPPLE(*cptr) & (1 << state.apple)) 788 { 789 *cptr -= 1 << state.apple; 790 adjust_count(COUNT_APPLES + state.apple, -1); 791 if(*cptr == GARDEN_APPLE) 792 *cptr = GARDEN_RANDOM; 793 changed_flag |= state.change; 794 redraw = 0x100; 795 } 796 break; 797 } 798 /*}}}*/ 799 /*{{{ case OPTION_RANDOM;*/ 800 case OPTION_RANDOM: 801 { 802 if(!(start || place & 0x10)) 803 /* EMPTY */; 804 else if(gw->garden.onoff) 805 { 806 redraw = change_path(gw->garden.cell.x, gw->garden.cell.y, 807 0x5F, 1, cptr, current); 808 redraw |= set_random(gw->garden.cell.x, gw->garden.cell.y, cptr); 809 } 810 else if(ISAPPLE(*cptr) || *cptr == GARDEN_RANDOM) 811 redraw = force_blank(gw->garden.cell.x, gw->garden.cell.y, cptr); 812 break; 813 } 814 /*}}}*/ 815 /*{{{ case OPTION_CHERRY:*/ 816 case OPTION_CHERRY: 817 { 818 if(!(start || place & 0x10)) 819 /* EMPTY */; 820 else if(gw->garden.onoff) 821 { 822 if(*cptr == GARDEN_CHERRY || ISPATHCHERRY(*cptr)) 823 /* EMPTY */; 824 else 825 { 826 redraw = force_blank(gw->garden.cell.x, gw->garden.cell.y, cptr); 827 if(ISPATH(*cptr)) 828 *cptr += GARDEN_PATH_CHERRY - GARDEN_PATH_BLANK; 829 else 830 *cptr = GARDEN_CHERRY; 831 adjust_count(COUNT_CHERRY, 1); 832 redraw |= 0x100; 833 changed_flag |= state.change; 834 } 835 } 836 else if(*cptr == GARDEN_CHERRY || ISPATHCHERRY(*cptr)) 837 { 838 redraw = force_blank(gw->garden.cell.x, gw->garden.cell.y, cptr); 839 redraw |= set_random(gw->garden.cell.x, gw->garden.cell.y, cptr); 840 } 841 break; 842 } 843 /*}}}*/ 844 /*{{{ case OPTION_PATH:*/ 845 case OPTION_PATH: 846 { 847 redraw = change_path(gw->garden.cell.x, gw->garden.cell.y, 848 place | 0x20, gw->garden.onoff, cptr, current); 849 break; 850 } 851 /*}}}*/ 852 /*{{{ case OPTION_DEN:*/ 853 case OPTION_DEN: 854 { 855 if(!(start || place & 0x10)) 856 /* EMPTY */; 857 else if(gw->garden.onoff) 858 { 859 if(ISPATHDEN(*cptr)) 860 /* EMPTY */; 861 else 862 { 863 redraw = change_path(gw->garden.cell.x, gw->garden.cell.y, 864 0x50, 0, cptr, current); 865 assert(ISPATHBLANK(*cptr)); 866 *cptr += GARDEN_PATH_DEN - GARDEN_PATH_BLANK; 867 adjust_count(COUNT_DEN, 1); 868 redraw |= 0x100; 869 changed_flag |= state.change; 870 } 871 } 872 else if(ISPATHDEN(*cptr)) 873 redraw = force_blank(gw->garden.cell.x, gw->garden.cell.y, cptr); 874 break; 875 } 876 /*}}}*/ 877 /*{{{ case OPTION_PLAYER:*/ 878 case OPTION_PLAYER: 879 { 880 if(!(start || place & 0x10)) 881 /* EMPTY */; 882 else if(gw->garden.onoff) 883 { 884 if(ISPATHPLAYER(*cptr)) 885 /* EMPTY */; 886 else 887 { 888 redraw = change_path(gw->garden.cell.x, gw->garden.cell.y, 889 0x50, 0, cptr, current); 890 assert(ISPATHBLANK(*cptr)); 891 *cptr += GARDEN_PATH_PLAYER - GARDEN_PATH_BLANK; 892 adjust_count(COUNT_PLAYER, 1); 893 redraw |= 0x100; 894 changed_flag |= state.change; 895 } 896 } 897 else if(ISPATHPLAYER(*cptr)) 898 redraw = force_blank(gw->garden.cell.x, gw->garden.cell.y, cptr); 899 break; 900 } 901 /*}}}*/ 902 default: 903 assert(0); 904 } 905 /*}}}*/ 906 /*{{{ update display.copy*/ 907 { 908 /*{{{ typedef struct Entry*/ 909 typedef struct Entry 910 { 911 COORD offset; /* cell offset */ 912 unsigned mask; /* top edge mask */ 913 } ENTRY; 914 /*}}}*/ 915 /*{{{ static ENTRY CONST table[] =*/ 916 static ENTRY CONST table[] = 917 { 918 {{0, -1}, 0x200}, 919 {{0, 1}, 0x000}, 920 {{-1, 0}, 0x400}, 921 {{1, 0}, 0x800}, 922 }; 923 /*}}}*/ 924 /*{{{ static COORD CONST offsets[] =*/ 925 static COORD CONST offsets[] = 926 { 927 { 0, -2}, 928 {-1, -1}, 929 { 1, -1}, 930 }; 931 /*}}}*/ 932 unsigned ix; 933 ENTRY CONST *tptr; 934 COORD CONST *optr; 935 936 if(redraw & 0x10F) 937 paint_cell(gw->garden.cell.x, gw->garden.cell.y, 938 (redraw & 0xF) | ((redraw >> 4) & 0x10), 1); 939 for(tptr = &table[XtNumber(table) - 1], ix = XtNumber(table); ix--; tptr--) 940 if(redraw & (0x10 << ix)) 941 paint_cell(gw->garden.cell.x + tptr->offset.x, 942 gw->garden.cell.y + tptr->offset.y, 943 redraw & tptr->mask ? 0x11 : 0x10, 1); 944 for(optr = &offsets[XtNumber(offsets) - 1], ix = XtNumber(offsets); 945 ix--; optr--) 946 if(redraw & (0x2000 << ix)) 947 paint_cell(gw->garden.cell.x + optr->x, gw->garden.cell.y + optr->y, 948 0x10, 1); 949 draw_board_icon(state.edit, gw->garden.cell.x - ((redraw & 0x44000) != 0), 950 gw->garden.cell.y - ((redraw & 0x10000) != 0), 951 1 + ((redraw & 0x44000) != 0) + ((redraw & 0x88000) != 0), 952 1 + ((redraw & 0x10000) != 0) + ((redraw & 0x20000) != 0)); 953 } 954 /*}}}*/ 955 return redraw; 956 } 957 /*}}}*/ 958 /*{{{ void draw_board_icon(dptr, x, y, w, h)*/ 959 static VOIDFUNC draw_board_icon 960 FUNCARG((dptr, x, y, w, h), 961 DESCRIPTOR CONST *dptr 962 ARGSEP int x 963 ARGSEP int y 964 ARGSEP unsigned w 965 ARGSEP unsigned h 966 ) 967 /* draws the specified board icon cells and their edges 968 */ 969 { 970 BOARD *board; 971 Pixmap pixmap; 972 char *cptr; 973 unsigned row; 974 unsigned column; 975 unsigned ix; 976 XGCValues gcv; 977 978 assert(dptr && dptr->type == DESCRIPTOR_GARDEN && dptr->board); 979 board = dptr->board; 980 pixmap = dptr->pixmap; 981 gcv.fill_style = FillSolid; 982 gcv.foreground = data.mono != False ? display.black : 983 colors[backgrounds[board->colors][1]].pixel; 984 XChangeGC(display.display, GCN(GC_BOARD), GCFillStyle | GCForeground, &gcv); 985 if(x) 986 { 987 x--; 988 w++; 989 } 990 if(y) 991 { 992 y--; 993 h++; 994 } 995 XFillRectangle(display.display, pixmap, GCN(GC_BOARD), 996 x * 3, y * 3 + 3, w * 3, h * 3); 997 for(row = y; h--; row++) 998 for(cptr = &board->map[row][x], column = x, ix = w; ix--; column++, cptr++) 999 /*{{{ do a cell*/ 1000 { 1001 XRectangle rectangles[3]; 1002 unsigned n; 1003 unsigned mask; 1004 char c; 1005 1006 c = *cptr; 1007 if(ISPATH(c)) 1008 mask = 4 | (GARDENPATH(c) & 3); 1009 else 1010 mask = 0; 1011 if(mask == 0x7 && ISPATH(cptr[1]) && GARDENPATH(cptr[1]) & 1 && 1012 ISPATH(cptr[CELLS_ACROSS + 1]) && 1013 GARDENPATH(cptr[CELLS_ACROSS + 1]) & 2) 1014 mask |= 8; 1015 n = 0; 1016 /*{{{ all blank?*/ 1017 if(mask == 0xF) 1018 { 1019 rectangles[n].x = column * 3; 1020 rectangles[n].y = row * 3 + 3; 1021 rectangles[n].width = 3; 1022 rectangles[n].height = 4; 1023 n++; 1024 mask = 0; 1025 } 1026 /*}}}*/ 1027 /*{{{ visit and right?*/ 1028 if((mask & 0x6) == 0x6) 1029 { 1030 rectangles[n].x = column * 3; 1031 rectangles[n].y = row * 3 + 3; 1032 rectangles[n].width = 3; 1033 rectangles[n].height = 2; 1034 n++; 1035 mask ^= 0x6; 1036 } 1037 /*}}}*/ 1038 /*{{{ visit and down?*/ 1039 if((mask & 0x5) == 0x5) 1040 { 1041 rectangles[n].x = column * 3; 1042 rectangles[n].y = row * 3 + 3; 1043 rectangles[n].width = 2; 1044 rectangles[n].height = 3; 1045 n++; 1046 mask ^= 0x5; 1047 } 1048 /*}}}*/ 1049 /*{{{ visit?*/ 1050 if(mask & 0x4) 1051 { 1052 rectangles[n].x = column * 3; 1053 rectangles[n].y = row * 3 + 3; 1054 rectangles[n].width = 2; 1055 rectangles[n].height = 2; 1056 n++; 1057 mask ^= 0x4; 1058 } 1059 /*}}}*/ 1060 /*{{{ down?*/ 1061 if(mask & 0x1) 1062 { 1063 rectangles[n].x = column * 3; 1064 rectangles[n].y = row * 3 + 3 + 2; 1065 rectangles[n].width = 2; 1066 rectangles[n].height = 1; 1067 n++; 1068 mask ^= 0x1; 1069 } 1070 /*}}}*/ 1071 XFillRectangles(display.display, pixmap, GCN(GC_CLEAR), 1072 rectangles, n); 1073 assert(!mask); 1074 /*{{{ cherry?*/ 1075 if(c == GARDEN_CHERRY || ISPATHCHERRY(c)) 1076 { 1077 XPoint points[2]; 1078 1079 points[0].x = column * 3; 1080 points[0].y = row * 3 + 3 + 1; 1081 points[1].x = 1; 1082 points[1].y = -1; 1083 XDrawPoints(display.display, pixmap, 1084 GCN(ISPATH(c) ? GC_SET : GC_CLEAR), points, 2, 1085 CoordModePrevious); 1086 } 1087 /*}}}*/ 1088 } 1089 /*}}}*/ 1090 return; 1091 } 1092 /*}}}*/ 1093 /*{{{ unsigned long force_blank(x, y, cptr*/ 1094 static unsigned long force_blank 1095 FUNCARG((x, y, cptr), 1096 unsigned x 1097 ARGSEP unsigned y 1098 ARGSEP char *cptr 1099 ) 1100 /* sets the cell to blank path or noapple hedge 1101 * adjusts the counts appropriately 1102 * returns mask of parts to redraw, a la change_path 1103 */ 1104 { 1105 unsigned long redraw; 1106 1107 redraw = 0; 1108 if(ISPATH(*cptr) && !ISPATHBLANK(*cptr)) 1109 /*{{{ set to blank path*/ 1110 { 1111 unsigned count; 1112 unsigned delta; 1113 1114 if(ISPATHCHERRY(*cptr)) 1115 { 1116 count = COUNT_CHERRY; 1117 delta = GARDEN_PATH_CHERRY - GARDEN_PATH_BLANK; 1118 redraw = 0x100; 1119 } 1120 else if(ISPATHDEN(*cptr)) 1121 { 1122 count = COUNT_DEN; 1123 delta = GARDEN_PATH_DEN - GARDEN_PATH_BLANK; 1124 redraw = 0x10F; 1125 if(y && ISPATHDEN(cptr[-1 - CELLS_ACROSS])) 1126 redraw |= 0x10; 1127 else if(y != CELLS_DOWN - 1 && ISPATHDEN(cptr[1 + CELLS_ACROSS])) 1128 redraw |= 0x20; 1129 if(x && ISPATHDEN(cptr[-1])) 1130 redraw |= 0x40; 1131 else if(x != CELLS_ACROSS - 1 && ISPATHDEN(cptr[1])) 1132 redraw |= 0x80; 1133 } 1134 else if(ISPATHPLAYER(*cptr)) 1135 { 1136 count = COUNT_PLAYER; 1137 delta = GARDEN_PATH_PLAYER - GARDEN_PATH_BLANK; 1138 redraw = 0x100; 1139 } 1140 else 1141 { 1142 assert(0); 1143 delta = count = 0; /* keep compiler happy */ 1144 } 1145 adjust_count(count, -1); 1146 *cptr -= delta; 1147 changed_flag |= state.change; 1148 } 1149 /*}}}*/ 1150 else if(*cptr == GARDEN_CHERRY) 1151 /*{{{ set to noapple hedge*/ 1152 { 1153 *cptr = GARDEN_NOAPPLE; 1154 adjust_count(COUNT_CHERRY, -1); 1155 changed_flag |= state.change; 1156 redraw = 0x100; 1157 } 1158 /*}}}*/ 1159 else if(ISAPPLE(*cptr)) 1160 /*{{{ set to random hedge*/ 1161 { 1162 unsigned ix; 1163 unsigned mask; 1164 1165 mask = GARDENAPPLE(*cptr); 1166 for(ix = 4; ix--;) 1167 if(mask & (1 << ix)) 1168 adjust_count(COUNT_APPLES + ix, -1); 1169 *cptr = GARDEN_RANDOM; 1170 changed_flag |= state.change; 1171 redraw = 0x100; 1172 } 1173 /*}}}*/ 1174 if(*cptr == GARDEN_RANDOM) 1175 /*{{{ set to noapple hedge*/ 1176 { 1177 *cptr = GARDEN_NOAPPLE; 1178 adjust_count(COUNT_SPACES, -1); 1179 if(y != CELLS_DOWN && ISPATH(cptr[CELLS_ACROSS + 1])) 1180 adjust_count(COUNT_FALL, -1); 1181 changed_flag |= state.change; 1182 redraw = 0x100; 1183 } 1184 /*}}}*/ 1185 return redraw; 1186 } 1187 /*}}}*/ 1188 /*{{{ unsigned get_cell(gw, event, cell_ptr)*/ 1189 static unsigned get_cell 1190 FUNCARG((gw, event, cptr), 1191 GardenWidget gw 1192 ARGSEP XEvent *event 1193 ARGSEP COORD *cptr 1194 ) 1195 /* get the cell and offset from window coordinate 1196 * returns mask of edges we're on and 0x10 for center 1197 */ 1198 { 1199 COORD coord; 1200 unsigned place; 1201 1202 /*{{{ get coordinate*/ 1203 switch(event->type) 1204 { 1205 case ButtonPress: 1206 case ButtonRelease: 1207 coord.x = event->xbutton.x; 1208 coord.y = event->xbutton.y; 1209 break; 1210 case MotionNotify: 1211 coord.x = event->xmotion.x; 1212 coord.y = event->xmotion.y; 1213 break; 1214 default: 1215 assert(0); 1216 } 1217 /*}}}*/ 1218 coord.x -= gw->garden.x + BORDER_LEFT + GAP_WIDTH / 2; 1219 coord.y -= gw->garden.y + BORDER_TOP + GAP_HEIGHT / 2; 1220 place = 0; 1221 /*{{{ clip x*/ 1222 if(coord.x < 0) 1223 { 1224 coord.x = 0; 1225 place = 0x10; 1226 } 1227 else if(coord.x >= (CELL_WIDTH + GAP_WIDTH) * CELLS_ACROSS) 1228 { 1229 coord.x = (CELL_WIDTH + GAP_WIDTH) * CELLS_ACROSS - 1; 1230 place = 0x10; 1231 } 1232 /*}}}*/ 1233 /*{{{ clip y*/ 1234 if(coord.y < 0) 1235 { 1236 coord.y = 0; 1237 place = 0x10; 1238 } 1239 else if(coord.y >= (CELL_HEIGHT + GAP_HEIGHT) * CELLS_DOWN) 1240 { 1241 coord.y = (CELL_HEIGHT + GAP_HEIGHT) * CELLS_DOWN - 1; 1242 place = 0x10; 1243 } 1244 /*}}}*/ 1245 cptr->x = coord.x / (CELL_WIDTH + GAP_WIDTH); 1246 cptr->y = coord.y / (CELL_HEIGHT + GAP_HEIGHT); 1247 coord.x %= (CELL_WIDTH + GAP_WIDTH); 1248 coord.y %= (CELL_HEIGHT + GAP_HEIGHT); 1249 if(coord.x < GAP_WIDTH / 2 + WIDTH_EXPAND) 1250 place |= 0x04; 1251 else if(coord.x >= GAP_WIDTH / 2 + CELL_WIDTH - WIDTH_EXPAND) 1252 place |= 0x08; 1253 if(coord.y < GAP_HEIGHT / 2 + HEIGHT_EXPAND) 1254 place |= 0x01; 1255 else if(coord.y >= GAP_HEIGHT / 2 + CELL_HEIGHT - HEIGHT_EXPAND) 1256 place |= 0x02; 1257 if(place & 0x10) 1258 place = 0; 1259 else if(!place) 1260 place = 0x10; 1261 return place; 1262 } 1263 /*}}}*/ 1264 /*{{{ unsigned get_paths(x, y, cptrptr)*/ 1265 static unsigned get_paths 1266 FUNCARG((x, y, cptrptr), 1267 unsigned x /* the cell coordinate */ 1268 ARGSEP unsigned y 1269 ARGSEP char **cptrptr /* return for cell mapp pointer */ 1270 ) 1271 /* return the current cell walls and pointer to cell map character 1272 */ 1273 { 1274 unsigned current; 1275 char *cptr; 1276 1277 cptr = &state.edit->board->map[y][x]; 1278 if(cptrptr) 1279 *cptrptr = cptr; 1280 current = 0; 1281 /*{{{ set current paths*/ 1282 if(ISPATH(*cptr)) 1283 { 1284 current |= 0x10; 1285 current |= (GARDENPATH(*cptr) & 1) << 1; 1286 current |= (GARDENPATH(*cptr) & 2) << 2; 1287 if(y && ISPATH(cptr[-1 - CELLS_ACROSS])) 1288 current |= GARDENPATH(cptr[-1 - CELLS_ACROSS]) & 1; 1289 if(x && ISPATH(cptr[-1])) 1290 current |= (GARDENPATH(cptr[-1]) & 2) << 1; 1291 } 1292 /*}}}*/ 1293 return current; 1294 } 1295 /*}}}*/ 1296 /*{{{ void install_garden(root)*/ 1297 extern VOIDFUNC install_garden 1298 FUNCARG((root), 1299 Widget root 1300 ) 1301 { 1302 garden = root; 1303 return; 1304 } 1305 /*}}}*/ 1306 /*{{{ void paint_cell(x, y, mask, copy)*/ 1307 static VOIDFUNC paint_cell 1308 FUNCARG((x, y, mask, copy), 1309 unsigned x /* cell coordinates */ 1310 ARGSEP unsigned y 1311 ARGSEP unsigned mask /* parts to paint 1312 * 0-3 edges, 4 center 1313 */ 1314 ARGSEP unsigned copy /* copy to window */ 1315 ) 1316 /* paints the specified cell from scratch, 1317 * updates display.copy 1318 * this will have to be copied to display.window 1319 */ 1320 { 1321 char CONST *cptr; 1322 COORD pixel; 1323 unsigned walls; 1324 1325 assert(x < CELLS_ACROSS && y < CELLS_DOWN); 1326 if(!mask) 1327 return; 1328 pixel.x = PIXELX(x, 0); 1329 pixel.y = PIXELY(y, 0); 1330 cptr = &state.edit->board->map[y][x]; 1331 /*{{{ set board GC*/ 1332 { 1333 XGCValues gcv; 1334 1335 gcv.fill_style = FillOpaqueStippled; 1336 gcv.background = data.mono != False ? display.white : 1337 colors[backgrounds[state.edit->board->colors][0]].pixel; 1338 gcv.foreground = data.mono != False ? display.black : 1339 colors[backgrounds[state.edit->board->colors][1]].pixel; 1340 gcv.stipple = fills[state.edit->board->fill].mask; 1341 XChangeGC(display.display, GCN(GC_BOARD), 1342 GCFillStyle | GCForeground | GCBackground | GCStipple, &gcv); 1343 } 1344 /*}}}*/ 1345 /*{{{ set walls*/ 1346 { 1347 unsigned temp; 1348 unsigned paths; 1349 1350 paths = 0; 1351 if(ISPATH(cptr[0])) 1352 { 1353 temp = GARDENPATH(cptr[0]) & 3; 1354 paths |= (temp & 2) << 2 | (temp & 1) << 1; 1355 } 1356 if(x && ISPATH(cptr[-1])) 1357 { 1358 temp = GARDENPATH(cptr[-1]) & 3; 1359 paths |= (temp & 2) << 1 | (temp & 1) << 10; 1360 } 1361 if(y && ISPATH(cptr[-1 - CELLS_ACROSS])) 1362 { 1363 temp = GARDENPATH(cptr[-1 - CELLS_ACROSS]) & 3; 1364 paths |= (temp & 1) | (temp & 2) << 7; 1365 } 1366 if(y && x && ISPATH(cptr[-2 - CELLS_ACROSS])) 1367 { 1368 temp = GARDENPATH(cptr[-2 - CELLS_ACROSS]) & 3; 1369 paths |= (temp & 1) << 6 | (temp & 2) << 3; 1370 } 1371 if(x != CELLS_ACROSS - 1 && ISPATH(cptr[1])) 1372 { 1373 temp = GARDENPATH(cptr[1]) & 3; 1374 paths |= (temp & 1) << 11; 1375 } 1376 if(y != CELLS_DOWN - 1 && ISPATH(cptr[CELLS_ACROSS + 1])) 1377 { 1378 temp = GARDENPATH(cptr[CELLS_ACROSS + 1]) & 3; 1379 paths |= (temp & 2) << 8; 1380 } 1381 if(x && y != CELLS_DOWN - 1 && ISPATH(cptr[CELLS_ACROSS])) 1382 { 1383 temp = GARDENPATH(cptr[CELLS_ACROSS]) & 3; 1384 paths |= (temp & 2) << 4; 1385 } 1386 if(y && x != CELLS_ACROSS - 1 && ISPATH(cptr[-CELLS_ACROSS])) 1387 { 1388 temp = GARDENPATH(cptr[-CELLS_ACROSS]) & 3; 1389 paths |= (temp & 1) << 7; 1390 } 1391 if(!y && INRANGE(x, 4, 8) && ISPATH(*cptr)) 1392 { 1393 paths |= 0x1; 1394 if(x < 7 && paths & 0x8) 1395 { 1396 paths |= 0x100; 1397 if(ISPATH(cptr[1])) 1398 paths |= 0x080; 1399 } 1400 if(x > 4 && paths & 0x4) 1401 { 1402 paths |= 0x010; 1403 if(ISPATH(cptr[-1])) 1404 paths |= 0x040; 1405 } 1406 } 1407 walls = paths ^ 0xFFF; 1408 } 1409 /*}}}*/ 1410 /*{{{ paint center?*/ 1411 if(mask & 0x10) 1412 { 1413 XFillRectangle(display.display, display.copy, GCN(GC_BOARD), 1414 pixel.x, pixel.y, CELL_WIDTH, CELL_HEIGHT); 1415 if(ISPATH(*cptr)) 1416 { 1417 /*{{{ typedef struct Entry*/ 1418 typedef struct Entry 1419 { 1420 COORD offset; 1421 unsigned mask; 1422 } ENTRY; 1423 /*}}}*/ 1424 /*{{{ static CONST ENTRY table[] =*/ 1425 static CONST ENTRY table[] = 1426 { 1427 {{0, 0}, 0x5}, 1428 {{CELL_WIDTH >> 1, 0}, 0x9}, 1429 {{CELL_WIDTH >> 1, CELL_HEIGHT >> 1}, 0xA}, 1430 {{0, CELL_HEIGHT >> 1}, 0x6}, 1431 }; 1432 /*}}}*/ 1433 SPRITE *sptr; 1434 unsigned ix; 1435 ENTRY CONST *tptr; 1436 1437 sptr = &sprites[SPRITE_CENTER_BASE]; 1438 for(tptr = table, ix = 4; ix--; tptr++) 1439 cut_back(tptr->offset.x, tptr->offset.y, 1440 CELL_WIDTH >> 1, CELL_HEIGHT >> 1, 1441 pixel.x + tptr->offset.x, pixel.y + tptr->offset.y, 1442 &sptr[(walls & tptr->mask) != tptr->mask]); 1443 } 1444 } 1445 /*}}}*/ 1446 /*{{{ set edges*/ 1447 { 1448 /*{{{ typedef struct Entry*/ 1449 typedef struct Entry 1450 { 1451 COORD start; 1452 COORD size; 1453 } ENTRY; 1454 /*}}}*/ 1455 /*{{{ static CONST ENTRY table[] =*/ 1456 static CONST ENTRY table[] = 1457 { 1458 {{-GAP_WIDTH, -GAP_HEIGHT}, {GAP_WIDTH * 2 + CELL_WIDTH, GAP_HEIGHT}}, 1459 {{-GAP_WIDTH, CELL_HEIGHT}, {GAP_WIDTH * 2 + CELL_WIDTH, GAP_HEIGHT}}, 1460 {{-GAP_WIDTH, -GAP_HEIGHT}, {GAP_WIDTH, GAP_HEIGHT * 2 + CELL_HEIGHT}}, 1461 {{CELL_WIDTH, -GAP_HEIGHT}, {GAP_WIDTH, GAP_HEIGHT * 2 + CELL_HEIGHT}}, 1462 }; 1463 /*}}}*/ 1464 unsigned ix; 1465 ENTRY CONST *tptr; 1466 1467 for(tptr = &table[3], ix = 4; ix--; tptr--) 1468 if(mask & (1 << ix)) 1469 XFillRectangle(display.display, display.copy, GCN(GC_BOARD), 1470 pixel.x + tptr->start.x, pixel.y + tptr->start.y, 1471 tptr->size.x, tptr->size.y); 1472 } 1473 /*}}}*/ 1474 if(!(mask & 0x10)) 1475 /* EMPTY */; 1476 else if(*cptr == GARDEN_CHERRY || ISPATH(*cptr)) 1477 /*{{{ draw sprite*/ 1478 { 1479 if(*cptr == GARDEN_CHERRY || ISPATHCHERRY(*cptr)) 1480 cut_sprite(SPRITE_CHERRY, pixel.x, pixel.y); 1481 else if(ISPATHDEN(*cptr)) 1482 { 1483 XFillRectangle(display.display, display.copy, GCN(GC_CLEAR), 1484 pixel.x - DEN_WIDTH, pixel.y - DEN_HEIGHT, 1485 CELL_WIDTH + DEN_HEIGHT * 2, CELL_HEIGHT + DEN_HEIGHT * 2); 1486 cut_sprite(SPRITE_DEN, pixel.x, pixel.y); 1487 } 1488 else if(ISPATHPLAYER(*cptr)) 1489 cut_sprite(SPRITE_PLAYER, pixel.x, pixel.y); 1490 } 1491 /*}}}*/ 1492 else if(*cptr == GARDEN_RANDOM || ISAPPLE(*cptr)) 1493 /*{{{ draw apples*/ 1494 { 1495 unsigned value; 1496 unsigned fall; 1497 1498 value = state.mode == MODE_RANDOM ? 0 : 1499 *cptr == GARDEN_RANDOM ? 0 : GARDENAPPLE(*cptr); 1500 fall = y != CELLS_DOWN - 1 && ISPATH(cptr[CELLS_ACROSS + 1]); 1501 if(state.mode == MODE_SEPARATE || !value) 1502 { 1503 unsigned solid; 1504 1505 solid = value & (1 << state.apple); 1506 cut_sprite(SPRITE_BIG_APPLE + !solid, pixel.x, pixel.y); 1507 if(fall) 1508 cut_sprite(SPRITE_BIG_ARROW + !solid, pixel.x, pixel.y); 1509 } 1510 else 1511 { 1512 unsigned ix; 1513 1514 for(ix = 4; ix--;) 1515 { 1516 COORD offset; 1517 unsigned solid; 1518 1519 solid = !(value & 1 << ix); 1520 offset.x = pixel.x + CELL_WIDTH / 2 * (ix & 1); 1521 offset.y = pixel.y + CELL_HEIGHT / 4 * (ix & 2); 1522 cut_sprite(SPRITE_SMALL_APPLE + solid, offset.x, offset.y); 1523 if(fall) 1524 cut_sprite(SPRITE_SMALL_ARROW + solid, offset.x, offset.y); 1525 } 1526 } 1527 } 1528 /*}}}*/ 1529 /*{{{ cut edges*/ 1530 { 1531 mask &= 0xF; 1532 mask |= mask << 4; 1533 /*{{{ set extra bits to cut*/ 1534 { 1535 /*{{{ typedef struct Entry*/ 1536 typedef struct Entry 1537 { 1538 unsigned mask; 1539 unsigned value; 1540 unsigned current; 1541 unsigned extra; 1542 } ENTRY; 1543 /*}}}*/ 1544 /*{{{ static CONST ENTRY table[] =*/ 1545 static CONST ENTRY table[] = 1546 { 1547 {0x055, 0x004, 0x01, 0x04}, 1548 {0x055, 0x010, 0x01, 0x04}, 1549 {0x055, 0x001, 0x04, 0x01}, 1550 {0x055, 0x040, 0x04, 0x01}, 1551 {0x189, 0x008, 0x10, 0x08}, 1552 {0x189, 0x100, 0x10, 0x08}, 1553 {0x189, 0x001, 0x08, 0x10}, 1554 {0x189, 0x080, 0x08, 0x10}, 1555 {0x426, 0x004, 0x02, 0x40}, 1556 {0x426, 0x020, 0x02, 0x40}, 1557 {0x426, 0x002, 0x40, 0x02}, 1558 {0x426, 0x400, 0x40, 0x02}, 1559 {0xA0A, 0x008, 0x20, 0x80}, 1560 {0xA0A, 0x200, 0x20, 0x80}, 1561 {0xA0A, 0x002, 0x80, 0x20}, 1562 {0xA0A, 0x800, 0x80, 0x20}, 1563 }; 1564 /*}}}*/ 1565 unsigned ix; 1566 ENTRY CONST *tptr; 1567 1568 for(tptr = table, ix = XtNumber(table); ix--; tptr++) 1569 if(mask & tptr->current && (walls & tptr->mask) == tptr->value) 1570 mask |= tptr->extra; 1571 } 1572 /*}}}*/ 1573 /*{{{ cut all the edges*/ 1574 { 1575 /*{{{ typedef struct Entry*/ 1576 typedef struct Entry 1577 { 1578 COORD offset; /* place on copy pixmap */ 1579 unsigned mask; /* bits we're interested in */ 1580 char base[16]; /* base of edge mask */ 1581 } ENTRY; 1582 /*}}}*/ 1583 /*{{{ static CONST ENTRY table[8] =*/ 1584 static CONST ENTRY table[8] = 1585 { 1586 /*{{{ top left horizontal*/ 1587 { 1588 {-GAP_WIDTH, -GAP_HEIGHT}, 1589 0x055, 1590 { 1591 4, 7, 0, 5, 1592 0, 6, 0, 8, 1593 3, 8, 1, 8, 1594 2, 8, 0, 8, 1595 } 1596 }, 1597 /*}}}*/ 1598 /*{{{ bottom left horizontal*/ 1599 { 1600 {-GAP_WIDTH, CELL_HEIGHT}, 1601 0x426, 1602 { 1603 4, 7, 0, 6, 1604 0, 5, 0, 8, 1605 3, 8, 2, 8, 1606 1, 8, 0, 8, 1607 } 1608 }, 1609 /*}}}*/ 1610 /*{{{ top left vertical*/ 1611 { 1612 {-GAP_WIDTH, -GAP_HEIGHT}, 1613 0x055, 1614 { 1615 4, 0, 7, 5, 1616 3, 1, 8, 8, 1617 0, 0, 6, 8, 1618 2, 0, 8, 8, 1619 } 1620 }, 1621 /*}}}*/ 1622 /*{{{ top right vertical*/ 1623 { 1624 {CELL_WIDTH, -GAP_HEIGHT}, 1625 0x189, 1626 { 1627 4, 0, 7, 6, 1628 0, 0, 5, 8, 1629 3, 2, 8, 8, 1630 1, 0, 8, 8, 1631 } 1632 }, 1633 /*}}}*/ 1634 /*{{{ top right horizontal*/ 1635 { 1636 {CELL_WIDTH / 2, -GAP_HEIGHT}, 1637 0x189, 1638 { 1639 4, 7, 0, 5, 1640 3, 8, 1, 8, 1641 0, 6, 0, 8, 1642 2, 8, 0, 8, 1643 } 1644 }, 1645 /*}}}*/ 1646 /*{{{ bottom right horizontal*/ 1647 { 1648 {CELL_WIDTH / 2, CELL_HEIGHT}, 1649 0xA0A, 1650 { 1651 4, 7, 0, 6, 1652 0, 5, 0, 8, 1653 3, 8, 2, 8, 1654 1, 8, 0, 8, 1655 } 1656 }, 1657 /*}}}*/ 1658 /*{{{ bottom left vertical*/ 1659 { 1660 {-GAP_WIDTH, CELL_HEIGHT / 2}, 1661 0x426, 1662 { 1663 4, 0, 7, 5, 1664 3, 1, 8, 8, 1665 0, 0, 6, 8, 1666 2, 0, 8, 8, 1667 } 1668 }, 1669 /*}}}*/ 1670 /*{{{ bottom right vertical*/ 1671 { 1672 {CELL_WIDTH, CELL_HEIGHT / 2}, 1673 0xA0A, 1674 { 1675 4, 0, 7, 6, 1676 3, 2, 8, 8, 1677 0, 0, 5, 8, 1678 1, 0, 8, 8, 1679 } 1680 }, 1681 /*}}}*/ 1682 }; 1683 /*}}}*/ 1684 unsigned ix; 1685 ENTRY CONST *tptr; 1686 1687 for(tptr = &table[XtNumber(table) - 1], ix = XtNumber(table); 1688 ix--; tptr--) 1689 if(mask & (1 << ix)) 1690 { 1691 unsigned mask; 1692 unsigned bits; 1693 unsigned bit; 1694 1695 mask = 0; 1696 /*{{{ compact the interesting bits*/ 1697 for(bits = tptr->mask, bit = 0; bit != 4; bit++) 1698 { 1699 unsigned bitmask; 1700 1701 assert(bits); 1702 bitmask = bits & -bits; 1703 if(walls & bitmask) 1704 mask |= 1 << bit; 1705 bits ^= bitmask; 1706 } 1707 /*}}}*/ 1708 /*{{{ top edge specials*/ 1709 if(!y && INRANGE(x, 3, 9) && mask == 0xC) 1710 { 1711 if(ix == 0 || ix == 4) 1712 mask |= 0x2; 1713 else if(ix == 2 || ix == 3) 1714 mask |= 0x1; 1715 } 1716 /*}}}*/ 1717 if(tptr->base[mask] == 8) 1718 /* EMPTY */; 1719 else if(ix & 2) 1720 cut_back(tptr->base[mask] * GAP_WIDTH, 1721 ix & 4 ? GAP_HEIGHT + CELL_HEIGHT / 2 : 0, 1722 GAP_WIDTH, GAP_HEIGHT + CELL_HEIGHT / 2, 1723 pixel.x + tptr->offset.x, pixel.y + tptr->offset.y, 1724 &sprites[SPRITE_EDGE_BASE + 0]); 1725 else 1726 cut_back(ix & 4 ? GAP_WIDTH + CELL_WIDTH / 2 : 0, 1727 tptr->base[mask] * GAP_HEIGHT, 1728 GAP_WIDTH + CELL_WIDTH / 2, GAP_HEIGHT, 1729 pixel.x + tptr->offset.x, pixel.y + tptr->offset.y, 1730 &sprites[SPRITE_EDGE_BASE + 1]); 1731 } 1732 } 1733 /*}}}*/ 1734 } 1735 /*}}}*/ 1736 if(!y && mask & 0xD) 1737 XDrawLine(display.display, display.copy, GCN(GC_BORDER), 1738 pixel.x - GAP_WIDTH, pixel.y - GAP_HEIGHT, 1739 pixel.x + CELL_WIDTH + GAP_WIDTH, pixel.y - GAP_HEIGHT); 1740 if(copy) 1741 RedrawRect(garden, pixel.x - GAP_WIDTH, pixel.y - GAP_HEIGHT, 1742 CELL_WIDTH + 2 * GAP_HEIGHT, CELL_HEIGHT + 2 * GAP_HEIGHT); 1743 return; 1744 } 1745 /*}}}*/ 1746 /*{{{ void paint_garden_icon(dptr)*/ 1747 extern VOIDFUNC paint_garden_icon 1748 FUNCARG((dptr), 1749 DESCRIPTOR *dptr 1750 ) 1751 { 1752 XFillRectangle(display.display, dptr->pixmap, GCN(GC_CLEAR), 0, 0, 1753 ICON_WIDTH, 3); 1754 XDrawLine(display.display, dptr->pixmap, GCN(GC_BORDER), 0, 2, 1755 ICON_WIDTH, 2); 1756 XDrawLine(display.display, dptr->pixmap, GCN(GC_BORDER), 4 * 3 - 1, 0, 1757 4 * 3 - 1, 2); 1758 XDrawLine(display.display, dptr->pixmap, GCN(GC_BORDER), 8 * 3 - 1, 0, 1759 8 * 3 - 1, 2); 1760 draw_board_icon(dptr, 0, 0, CELLS_ACROSS, CELLS_DOWN); 1761 return; 1762 } 1763 /*}}}*/ 1764 /*{{{ void paint_garden_image()*/ 1765 extern VOIDFUNC paint_garden_image FUNCARGVOID 1766 { 1767 unsigned ix; 1768 unsigned iy; 1769 1770 XFillRectangle(display.display, display.copy, GCN(GC_CLEAR), 1771 0, 0, WINDOW_WIDTH, PIXELY(0, 0)); 1772 XDrawLine(display.display, display.copy, GCN(GC_BORDER), 1773 BORDER_LEFT, BORDER_TOP, BORDER_LEFT + BOARD_WIDTH, BORDER_TOP); 1774 XDrawLine(display.display, display.copy, GCN(GC_BORDER), 1775 PIXELX(4, -1), PIXELY(-1, 0), PIXELX(4, -1), PIXELY(-1, CELL_HEIGHT)); 1776 XDrawLine(display.display, display.copy, GCN(GC_BORDER), 1777 PIXELX(4, XTRA_SPACING * 4 + CELL_WIDTH), PIXELY(-1, 0), 1778 PIXELX(4, XTRA_SPACING * 4 + CELL_WIDTH), PIXELY(-1, CELL_HEIGHT)); 1779 /*{{{ blat on the extra*/ 1780 { 1781 unsigned ix; 1782 1783 for(ix = 5; ix--;) 1784 { 1785 int x; 1786 SPRITE *lptr; 1787 1788 lptr = &sprites[SPRITE_EXTRA]; 1789 x = PIXELX(4, ix * XTRA_SPACING); 1790 XCopyArea(display.display, lptr->mask, 1791 display.copy, GCN(GC_MASK), (int)ix * (CELL_WIDTH / 2), 0, 1792 CELL_WIDTH / 2, CELL_HEIGHT / 2, 1793 x + XTRA_LETTER_X, PIXELY(-1, XTRA_LETTER_Y)); 1794 XCopyArea(display.display, lptr->image, 1795 display.copy, GCN(GC_OR), (int)ix * (CELL_WIDTH / 2), 0, 1796 CELL_WIDTH / 2, CELL_HEIGHT / 2, 1797 x + XTRA_LETTER_X, PIXELY(-1, XTRA_LETTER_Y)); 1798 } 1799 } 1800 /*}}}*/ 1801 XDrawImageString(display.display, display.copy, GCN(GC_TEXT), 1802 PIXELX(4, -GAP_WIDTH) - 3 * font.width, 1803 PIXELY(-1, CELL_HEIGHT / 2) + font.center, "0", (int)1); 1804 for(iy = CELLS_DOWN; iy--;) 1805 for(ix = CELLS_ACROSS; ix--;) 1806 paint_cell(ix, iy, 0x1A | (iy ? 0x0 : 0x1) | (ix ? 0x0 : 0x4), 0); 1807 paint_garden_source(); 1808 RedrawRect(garden, 0, 0, WINDOW_WIDTH, WINDOW_HEIGHT); 1809 return; 1810 } 1811 /*}}}*/ 1812 /*{{{ void paint_garden_source()*/ 1813 extern VOIDFUNC paint_garden_source FUNCARGVOID 1814 { 1815 char CONST *text; 1816 1817 switch(state.source) 1818 { 1819 /*{{{ case SOURCE_UNIQUE:*/ 1820 case SOURCE_UNIQUE: 1821 { 1822 text = "Unique"; 1823 break; 1824 } 1825 /*}}}*/ 1826 /*{{{ case SOURCE_BUFFER:*/ 1827 case SOURCE_BUFFER: 1828 { 1829 text = "Buffer"; 1830 break; 1831 } 1832 /*}}}*/ 1833 /*{{{ default:*/ 1834 default: 1835 { 1836 static char string[] = "Garden 000"; 1837 1838 itoa(string + 7, (unsigned long)state.source, 0); 1839 text = string; 1840 break; 1841 } 1842 /*}}}*/ 1843 } 1844 XFillRectangle(display.display, display.copy, GCN(GC_CLEAR), 1845 PIXELX(8, 0), PIXELY(-1, 0), 1846 (CELLS_ACROSS - 8) * (CELL_WIDTH + GAP_WIDTH), CELL_HEIGHT); 1847 XDrawImageString(display.display, display.copy, GCN(GC_TEXT), 1848 PIXELX(8, (int)font.width * 2), 1849 PIXELY(-1, CELL_HEIGHT / 2 + font.center), text, strlen(text)); 1850 RedrawRect(garden, PIXELX(8, 0), PIXELY(-1, 0), 1851 (CELLS_ACROSS - 8) * (CELL_WIDTH + GAP_WIDTH), CELL_HEIGHT); 1852 menu_garden(text); 1853 return; 1854 } 1855 /*}}}*/ 1856 /*{{{ unsigned long set_random(x, y, cptr)*/ 1857 static unsigned long set_random 1858 FUNCARG((x, y, cptr), 1859 unsigned x 1860 ARGSEP unsigned y 1861 ARGSEP char *cptr 1862 ) 1863 /* sets a random apple possiblilty 1864 * if currently noapple 1865 * returns mask of bits to redraw a la change_path 1866 */ 1867 { 1868 unsigned long redraw; 1869 1870 redraw = 0; 1871 if(*cptr == GARDEN_NOAPPLE) 1872 { 1873 *cptr = GARDEN_RANDOM; 1874 adjust_count(COUNT_SPACES, 1); 1875 if(y != CELLS_DOWN && ISPATH(cptr[CELLS_ACROSS + 1])) 1876 adjust_count(COUNT_FALL, 1); 1877 changed_flag |= state.change; 1878 redraw = 0x100; 1879 } 1880 return redraw; 1881 } 1882 /*}}}*/ 1883