Esempio n. 1
0
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 ) );
}
Esempio n. 2
0
File: engine.c Progetto: rdebath/sgt
/*
 * '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;
}