int fh_going_bit( dbref target, dbref player, FLAG flag, int fflags, int reset ) { if( Going( target ) && reset && ( Typeof( target ) != TYPE_GARBAGE ) ) { notify( player, "Your object has been spared from destruction." ); return ( fh_any( target, player, flag, fflags, reset ) ); } if( !God( player ) || !destroyable( target ) ) { return 0; } return ( fh_any( target, player, flag, fflags, reset ) ); }
/* * 'key' is h, j, k, l for movement, or x to switch player pieces */ gamestate *make_move (gamestate *state, char key) { gamestate *ret; int xfrom, yfrom, pfrom, xto, yto, pto, pdx, pdy, pdp; int i, j, k; int w, h; typedef struct posn posn; struct posn { posn *next, *prev; int x, y; int vertical; }; posn movehead = { NULL, NULL, -1, -1, 0 }; posn movetail = { NULL, NULL, -1, -1, 0 }; posn nmovhead = { NULL, NULL, -1, -1, 0 }; posn nmovtail = { NULL, NULL, -1, -1, 0 }; posn expshead = { NULL, NULL, -1, -1, 0 }; posn expstail = { NULL, NULL, -1, -1, 0 }; posn *pos; /* * initialise those lists */ movehead.next = &movetail; movetail.prev = &movehead; nmovhead.next = &nmovtail; nmovtail.prev = &nmovhead; expshead.next = &expstail; expstail.prev = &expshead; /* * copy most of the state */ ret = gamestate_copy(state); /* * get the level dimensions */ w = ret->width; h = ret->height; /* * sanity check the state */ if (ret->leveldata[w*ret->player_y+ret->player_x] != '@') { ret->status = BROKEN; return ret; } /* * Handle the special `x' move. When we add this to the stored * sequence, we make the marginal optimisation of removing a * prior x if present rather than adding a new one, so that the * stored move sequence will only ever contain a single x at a * time. */ if (key == 'x') { for (i = 0; i < w; i++) for (j = 0; j < h; j++) if (ret->leveldata[w*j+i] == 'O') { ret->leveldata[w*j+i] = '@'; ret->leveldata[w*ret->player_y+ret->player_x] = 'O'; ret->player_y = j; ret->player_x = i; if (ret->movenum > 0 && ret->sequence[ret->movenum-1] == 'x') { ret->movenum--; } else { ret->movenum++; if (ret->movenum > ret->sequence_size) { ret->sequence_size = ret->movenum + 128; ret->sequence = srealloc(ret->sequence, ret->sequence_size); } ret->sequence[ret->movenum-1] = key; } return ret; } /* * Otherwise, we cannot swap at all, so do nothing. */ return ret; } /* * determine move destination */ xto = xfrom = ret->player_x; yto = yfrom = ret->player_y; switch (key) { case 'h': xto--; break; case 'l': xto++; break; case 'k': yto--; break; case 'j': yto++; break; } pfrom = yfrom * w + xfrom; pto = yto * w + xto; pdx = xto - xfrom; pdy = yto - yfrom; pdp = pto - pfrom; i = ret->leveldata[pto]; /* * process teleporters */ if (i == '#') { int x, y; /* * First find the other teleporter. */ for (x = 0; x < w; x++) { for (y = 0; y < h; y++) if (w*y+x != pto && ret->leveldata[w*y+x] == '#') break; if (y < h) break; } if (x == w) { /* * Couldn't find the other teleporter at all; it must * have been destroyed or something. Disallow move. */ return ret; } /* * Find a suitable emergence point around the other * teleporter. We look right, up, left then down, in that * order. */ if (ret->leveldata[w*y+(x+1)] == ' ') { x++; } else if (ret->leveldata[w*(y-1)+x] == ' ') { y--; } else if (ret->leveldata[w*y+(x-1)] == ' ') { x--; } else if (ret->leveldata[w*(y+1)+x] == ' ') { y++; } else return ret; /* no free square; disallow move */ /* * Continue with the move as usual (we must still do an * expose event on the vacated square, etc). Note that from * here on, xto and xfrom might be wildly different, but * pdx still reflects the logical direction of the move. * Ditto yto/yfrom/pdy, pto/pfrom/pdp. */ xto = x; yto = y; pto = yto * w + xto; i = ret->leveldata[pto]; } /* * disallow the move if the destination isn't movable to */ if (i == '+' || i == '|' || i == '-' || i == '%' || i == 'O' || (i == 'E' && ret->gold_got < ret->gold_total) || (i == '!' && xfrom != xto) || (i == '=' && yfrom != yto)) return ret; /* do nothing */ /* * increment the gold counter if we're moving on to a $ */ if (i == '$') ret->gold_got++; /* * is this a push move? */ if (i == '8' || i == 'o' || i == 'v' || i == '>' || i == '<' || i == '^' || i == 'W' || i == 'X' || i == 'Y' || i == 'Z') { /* * Check the next space along to see if we can push into * it. Disallow the move otherwise. */ j = pto + pdp; k = ret->leveldata[j]; if (k != ' ' && k != '.' && (k != '!' || !pdy || i == '8' || i == 'o') && (k != '=' || !pdx || i == '8' || i == 'o')) return ret; /* do nothing */ /* * Also disallow the move if we're trying to push something * against its direction. */ if (((i == 'v' || i == 'X') && pdy == -1) || ((i == '>' || i == 'W') && pdx == -1) || ((i == '<' || i == 'Y') && pdx == +1) || ((i == '^' || i == 'Z') && pdy == +1)) return ret; /* do nothing */ /* * Now we know we can make the move. Move the pushed piece, * and add it to the moving list if there's a blank in its * direction. The movement of the player itself will be * performed outside this conditional block. */ ret->leveldata[j] = ret->leveldata[pto]; if (((i=='v' || i=='X') && (ret->leveldata[j+w] == ' ' || ret->leveldata[j+w] == '!')) || ((i=='>' || i=='W') && (ret->leveldata[j+1] == ' ' || ret->leveldata[j+1] == '=')) || ((i=='<' || i=='Y') && (ret->leveldata[j-1] == ' ' || ret->leveldata[j-1] == '=')) || ((i=='^' || i=='Z') && (ret->leveldata[j-w] == ' ' || ret->leveldata[j-w] == '!')) || i == 'o') { addlist(pos, move); pos->x = xto + pdx; pos->y = yto + pdy; if (ret->leveldata[j] == 'o') { if (yto-yfrom == -1) ret->leveldata[j] = 'a' | 0x80; /* a is upmoving o */ else if (yto-yfrom == +1) ret->leveldata[j] = 'b' | 0x80; /* b is downmoving o */ else if (xto-xfrom == +1) ret->leveldata[j] = 'c' | 0x80; /* c is rightmoving o */ else if (xto-xfrom == -1) ret->leveldata[j] = 'd' | 0x80; /* d is leftmoving o */ } else ret->leveldata[j] |= 0x80; /* just flag as moving */ } } /* * Move the player and expose the square it has left. Increment * the move count here as well, and add the move to the stored * sequence. */ ret->leveldata[pfrom] = ' '; ret->leveldata[pto] = '@'; ret->player_x = xto; ret->player_y = yto; ret->movenum++; if (ret->movenum > ret->sequence_size) { ret->sequence_size = ret->movenum + 128; ret->sequence = srealloc(ret->sequence, ret->sequence_size); } ret->sequence[ret->movenum-1] = key; addlist(pos, exps); pos->x = xfrom; pos->y = yfrom; pos->vertical = pri(pdy == 0); /* * Kill the player if he walked into a &, or flag the level as * complete if he legitimately walked into the E. In this * situation we still do the movements around him, partly for * looks and mostly because if you walk into the E and a bomb * blows it up in the same move, you _are_ dead. */ if (i == '&') ret->status = DIED; else if (i == 'E' && ret->gold_got >= ret->gold_total) ret->status = COMPLETED; /* * Now repeatedly process exposed cells and moving objects * until none of either remain. */ while (expshead.next->x != -1 || movehead.next->x != -1) { /* * Process exposed cells, which may generate more moving * objects. */ while (expshead.next->x != -1) { posn *p, *q; p = expshead.next; while (p->x != -1) { enum { LEFT, RIGHT, UP, DOWN } directions[4]; int dir; q = p->next; j = p->y * w + p->x; if (p->vertical) { directions[0] = UP; directions[1] = LEFT; directions[2] = RIGHT; directions[3] = DOWN; } else { directions[0] = LEFT; directions[1] = RIGHT; directions[2] = UP; directions[3] = DOWN; } for (dir = 0; dir < 4; dir++) { switch (directions[dir]) { case UP: if ((ret->leveldata[j-w] & 0x7F) == 'v' || (ret->leveldata[j-w] & 0x7F) == 'X') { /* * Falling object above the exposed cell. */ if (!(ret->leveldata[j-w] & 0x80)) { addlist(pos, move); pos->x = p->x; pos->y = p->y-1; ret->leveldata[j-w] |= 0x80;/* flag as moving */ } dir = 4; /* terminate loop */ } break; case LEFT: if ((ret->leveldata[j-1] & 0x7F) == '>' || (ret->leveldata[j-1] & 0x7F) == 'W') { /* * Right-moving object to the left of the * exposed cell. */ if (!(ret->leveldata[j-1] & 0x80)) { addlist(pos, move); pos->x = p->x-1; pos->y = p->y; ret->leveldata[j-1] |= 0x80;/* flag as moving */ } dir = 4; /* terminate loop */ } break; case RIGHT: if ((ret->leveldata[j+1] & 0x7F) == '<' || (ret->leveldata[j+1] & 0x7F) == 'Y') { /* * Left-moving object to the right of the * exposed cell. */ if (!(ret->leveldata[j+1] & 0x80)) { addlist(pos, move); pos->x = p->x+1; pos->y = p->y; ret->leveldata[j+1] |= 0x80;/* flag as moving */ } dir = 4; /* terminate loop */ } break; case DOWN: if ((ret->leveldata[j+w] & 0x7F) == '^' || (ret->leveldata[j+w] & 0x7F) == 'Z') { /* * Up-moving object below the exposed cell. */ if (!(ret->leveldata[j+w] & 0x80)) { addlist(pos, move); pos->x = p->x; pos->y = p->y+1; ret->leveldata[j+w] |= 0x80;/* flag as moving */ } dir = 4; /* terminate loop */ } break; } } remlist(p); p = q; } } /* * Process moving objects, which may generate more exposed * cells. We empty the `move' list and fill up the `nmov' * list with things which are still moving at the end of * the turn. Then we manhandle the new list back into * `move'. */ nmovhead.next = &nmovtail; nmovtail.prev = &nmovhead; while (movehead.next->x != -1) { posn *p, *q; int dx, dy, dp, bomb, sack; p = movehead.next; while (p->x != -1) { q = p->next; j = p->y * w + p->x; k = ret->leveldata[j] & 0x7F; dx = dy = bomb = sack = 0; switch (k) { case 'a': dx = 0; dy = -1; bomb = 0; sack = 1; break; case 'b': dx = 0; dy = +1; bomb = 0; sack = 1; break; case 'c': dx = +1; dy = 0; bomb = 0; sack = 1; break; case 'd': dx = -1; dy = 0; bomb = 0; sack = 1; break; case 'v': dx = 0; dy = +1; bomb = 0; sack = 0; break; case '>': dx = +1; dy = 0; bomb = 0; sack = 0; break; case '<': dx = -1; dy = 0; bomb = 0; sack = 0; break; case '^': dx = 0; dy = -1; bomb = 0; sack = 0; break; case 'X': dx = 0; dy = +1; bomb = 1; sack = 0; break; case 'W': dx = +1; dy = 0; bomb = 1; sack = 0; break; case 'Z': dx = 0; dy = -1; bomb = 1; sack = 0; break; case 'Y': dx = -1; dy = 0; bomb = 1; sack = 0; break; case C_x: dx = +1; dy = 0; bomb = 2; sack = 0; break; case C_w: dx = 0; dy = +1; bomb = 2; sack = 0; break; case C_z: dx = +1; dy = 0; bomb = 2; sack = 0; break; case C_y: dx = 0; dy = +1; bomb = 2; sack = 0; break; } dp = dy*w+dx; if (bomb == 2) { /* special case: exploding thing */ ret->leveldata[j] = ' '; /* remove the bomb */ addlist(pos, exps); /* and expose the vacated cell */ pos->x = p->x; pos->y = p->y; pos->vertical = pri(dy == 0); if (destroyable(p->x-dx, p->y-dy)) { ret->leveldata[j-dp] = ' '; /* and the left/up cell */ addlist(pos, exps); /* expose the vacated cell */ pos->x = p->x - dx; pos->y = p->y - dy; pos->vertical = pri(dy == 0); } if (destroyable(p->x+dx, p->y+dy)) { ret->leveldata[j+dp] = ' '; /* the right/down cell */ addlist(pos, exps); /* expose the vacated cell */ pos->x = p->x + dx; pos->y = p->y + dy; pos->vertical = pri(dy == 0); } } else { if ((!bomb || (ret->flags & LEVEL_FLIMSY_BOMBS)) && !sack && (ret->leveldata[j+dp] == 'X' || ret->leveldata[j+dp] == 'W' || ret->leveldata[j+dp] == 'Y' || ret->leveldata[j+dp] == 'Z')) { ret->leveldata[j] = ' '; /* remove the detonator */ addlist(pos, exps); /* expose the vacated cell */ pos->x = p->x; pos->y = p->y; pos->vertical = pri(dy != 0); ret->leveldata[j+dp] &= 0x1F; /* move to Ctrl-x */ addlist(pos, nmov); /* add exploding bomb to list */ pos->x = p->x + dx; pos->y = p->y + dy; } else { if (ret->leveldata[j+dp] == ' ' || (ret->leveldata[j+dp] == '.' && !sack) || (ret->leveldata[j+dp] == '!' && dy && !sack) || (ret->leveldata[j+dp] == '=' && dx && !sack) || (ret->leveldata[j+dp] == '@' && !sack) || (ret->leveldata[j+dp] == 'O' && !sack)) { ret->leveldata[j+dp] = k | 0x80; /* stay moving */ ret->leveldata[j] = ' '; /* fall */ addlist(pos, exps); /* expose the vacated cell */ pos->x = p->x; pos->y = p->y; pos->vertical = pri(dy != 0); addlist(pos, nmov); /* keep the object moving */ pos->x = p->x + dx; pos->y = p->y + dy; } else { if (sack) ret->leveldata[j] = 'o'; else ret->leveldata[j] &= 0x7F; /* non-moving */ } } } remlist(p); p = q; } } if (nmovhead.next->next) { movehead.next = nmovhead.next; movetail.prev = nmovtail.prev; movehead.next->prev = &movehead; movetail.prev->next = &movetail; } else { /* special case - nmov list empty */ movehead.next = &movetail; movetail.prev = &movehead; } } /* * If we've killed the player at any point, flag him as dead, * unless there is still an `O' on the screen in which case we * transfer to it. */ if (ret->leveldata[w*ret->player_y+ret->player_x] != '@') { for (i = 0; i < w; i++) for (j = 0; j < h; j++) if (ret->leveldata[w*j+i] == 'O') { ret->leveldata[w*j+i] = '@'; ret->player_y = j; ret->player_x = i; return ret; } /* * If we reach here, there was no O, so we really are dead. */ ret->status = DIED; } /* * I think we're done! */ return ret; }