Пример #1
0
int
doconsult(struct monst *oracl)
{
    int umoney = money_cnt(invent);
    int u_pay, minor_cost = 50, major_cost = 500 + 50 * u.ulevel;
    int add_xpts;
    const char *qbuf;

    /* TODO: Do we want this? The purpose seems to be specifically to prevent
       repeating an Oracle donation. */
    action_completed();

    if (!oracl) {
        pline("There is no one here to consult.");
        return 0;
    } else if (!oracl->mpeaceful) {
        pline("%s is not in the mood for conversation (believe it or not...)", Monnam(oracl));
        return 0;
    } else if (!umoney) {
        pline("You have no money.  There's no free lunch in wireless... and in being an oracle!");
        return 0;
    }

    qbuf = msgprintf("\"Would you mind talking for a little bit?\" (%d %s)",
                     minor_cost, currency(minor_cost));
    switch (ynq(qbuf)) {
    default:
    case 'q':
        return 0;
    case 'y':
        if (umoney < minor_cost) {
            pline("You don't even have enough money for that! There's no free lunch in wireless... and in being an oracle!");
            return 0;
        }
        u_pay = minor_cost;
        break;
    case 'n':
        if (umoney <= minor_cost ||     /* don't even ask */
            (oracle_cnt == 1 || oracle_flg < 0))
            return 0;
        qbuf = msgprintf("\"Oh! You'd like to sit and talk for a _long_ while?\" (%d %s)",
                         major_cost, currency(major_cost));
        if (yn(qbuf) != 'y')
            return 0;
        u_pay = (umoney < major_cost ? umoney : major_cost);

        break;
    }

    money2mon(oracl, u_pay);
    add_xpts = 0;       /* first oracle of each type gives experience points */
    if (u_pay == minor_cost) {
        outrumor(1, BY_ORACLE);
        if (!u.uevent.minor_oracle)
            add_xpts = u_pay / (u.uevent.major_oracle ? 25 : 10);
        /* 5 pts if very 1st, or 2 pts if major already done */
        u.uevent.minor_oracle = TRUE;
    } else {
        boolean cheapskate = u_pay < major_cost;

        outoracle(cheapskate, TRUE);
        if (!cheapskate && !u.uevent.major_oracle)
            add_xpts = u_pay / (u.uevent.minor_oracle ? 25 : 10);
        /* ~100 pts if very 1st, ~40 pts if minor already done */
        u.uevent.major_oracle = TRUE;
        historic_event(FALSE, "received advice from The Oracle.");
        exercise(A_WIS, !cheapskate);
    }
    if (add_xpts) {
        more_experienced(add_xpts, u_pay / 50);
        newexplevel();
    }
    return 1;
}
Пример #2
0
/* return TRUE if the caller needs to place the ball and chain down again
 *
 *  Should not be called while swallowed.  Should be called before movement,
 *  because we might want to move the ball or chain to the hero's old position.
 *
 * It is called if we are moving.  It is also called if we are teleporting
 * *if* the ball doesn't move and we thus must drag the chain.  It is not
 * called for ordinary teleportation.
 *
 * allow_drag is only used in the ugly special case where teleporting must
 * drag the chain, while an identical-looking movement must drag both the ball
 * and chain.
 */
boolean
drag_ball(xchar x, xchar y, int *bc_control, xchar * ballx, xchar * bally,
          xchar * chainx, xchar * chainy, boolean * cause_delay,
          boolean allow_drag)
{
    struct trap *t = NULL;
    boolean already_in_rock;

    *ballx = uball->ox;
    *bally = uball->oy;
    *chainx = uchain->ox;
    *chainy = uchain->oy;
    *bc_control = 0;
    *cause_delay = FALSE;

    if (dist2(x, y, uchain->ox, uchain->oy) <= 2) {     /* nothing moved */
        move_bc(1, *bc_control, *ballx, *bally, *chainx, *chainy);
        return TRUE;
    }

    /* only need to move the chain? */
    if (carried(uball) || distmin(x, y, uball->ox, uball->oy) <= 2) {
        xchar oldchainx = uchain->ox, oldchainy = uchain->oy;

        *bc_control = BC_CHAIN;
        move_bc(1, *bc_control, *ballx, *bally, *chainx, *chainy);
        if (carried(uball)) {
            /* move chain only if necessary */
            if (distmin(x, y, uchain->ox, uchain->oy) > 1) {
                *chainx = u.ux;
                *chainy = u.uy;
            }
            return TRUE;
        }
#define CHAIN_IN_MIDDLE(chx, chy) \
    (distmin(x, y, chx, chy) <= 1 && \
     distmin(chx, chy, uball->ox, uball->oy) <= 1)
#define IS_CHAIN_ROCK(x,y) \
    (IS_ROCK(level->locations[x][y].typ) || \
     (IS_DOOR(level->locations[x][y].typ) && \
      (level->locations[x][y].doormask & (D_CLOSED|D_LOCKED))))
/* Don't ever move the chain into solid rock.  If we have to, then instead
 * undo the move_bc() and jump to the drag ball code.  Note that this also
 * means the "cannot carry and drag" message will not appear, since unless we
 * moved at least two squares there is no possibility of the chain position
 * being in solid rock.
 */
#define SKIP_TO_DRAG { *chainx = oldchainx; *chainy = oldchainy; \
    move_bc(0, *bc_control, *ballx, *bally, *chainx, *chainy); \
    goto drag; }
        if (IS_CHAIN_ROCK(u.ux, u.uy) || IS_CHAIN_ROCK(*chainx, *chainy)
            || IS_CHAIN_ROCK(uball->ox, uball->oy))
            already_in_rock = TRUE;
        else
            already_in_rock = FALSE;

        switch (dist2(x, y, uball->ox, uball->oy)) {
            /* two spaces diagonal from ball, move chain inbetween */
        case 8:
            *chainx = (uball->ox + x) / 2;
            *chainy = (uball->oy + y) / 2;
            if (IS_CHAIN_ROCK(*chainx, *chainy) && !already_in_rock)
                SKIP_TO_DRAG;
            break;

            /* player is distance 2/1 from ball; move chain to one of the two
               spaces between @ __ 0 */
        case 5:{
                xchar tempx, tempy, tempx2, tempy2;

                /* find position closest to current position of chain */
                /* no effect if current position is already OK */
                if (abs(x - uball->ox) == 1) {
                    tempx = x;
                    tempx2 = uball->ox;
                    tempy = tempy2 = (uball->oy + y) / 2;
                } else {
                    tempx = tempx2 = (uball->ox + x) / 2;
                    tempy = y;
                    tempy2 = uball->oy;
                }
                if (IS_CHAIN_ROCK(tempx, tempy) &&
                    !IS_CHAIN_ROCK(tempx2, tempy2) && !already_in_rock) {
                    if (allow_drag) {
                        /* Avoid pathological case *if* not teleporting: 0 0_
                           _X move northeast -----> X@ @ */
                        if (dist2(u.ux, u.uy, uball->ox, uball->oy) == 5 &&
                            dist2(x, y, tempx, tempy) == 1)
                            SKIP_TO_DRAG;
                        /* Avoid pathological case *if* not teleporting: 0 0 _X 
                           move east -----> X_ @ @ */
                        if (dist2(u.ux, u.uy, uball->ox, uball->oy) == 4 &&
                            dist2(x, y, tempx, tempy) == 2)
                            SKIP_TO_DRAG;
                    }
                    *chainx = tempx2;
                    *chainy = tempy2;
                } else if (!IS_CHAIN_ROCK(tempx, tempy) &&
                           IS_CHAIN_ROCK(tempx2, tempy2) && !already_in_rock) {
                    if (allow_drag) {
                        if (dist2(u.ux, u.uy, uball->ox, uball->oy) == 5 &&
                            dist2(x, y, tempx2, tempy2) == 1)
                            SKIP_TO_DRAG;
                        if (dist2(u.ux, u.uy, uball->ox, uball->oy) == 4 &&
                            dist2(x, y, tempx2, tempy2) == 2)
                            SKIP_TO_DRAG;
                    }
                    *chainx = tempx;
                    *chainy = tempy;
                } else if (IS_CHAIN_ROCK(tempx, tempy) &&
                           IS_CHAIN_ROCK(tempx2, tempy2) && !already_in_rock) {
                    SKIP_TO_DRAG;
                } else if (dist2(tempx, tempy, uchain->ox, uchain->oy) <
                           dist2(tempx2, tempy2, uchain->ox, uchain->oy) ||
                           ((dist2(tempx, tempy, uchain->ox, uchain->oy) ==
                             dist2(tempx2, tempy2, uchain->ox, uchain->oy)) &&
                            rn2(2))) {
                    *chainx = tempx;
                    *chainy = tempy;
                } else {
                    *chainx = tempx2;
                    *chainy = tempy2;
                }
                break;
            }

            /* ball is two spaces horizontal or vertical from player; move */
            /* chain inbetween *unless* current chain position is OK */
        case 4:
            if (CHAIN_IN_MIDDLE(uchain->ox, uchain->oy))
                break;
            *chainx = (x + uball->ox) / 2;
            *chainy = (y + uball->oy) / 2;
            if (IS_CHAIN_ROCK(*chainx, *chainy) && !already_in_rock)
                SKIP_TO_DRAG;
            break;

            /* ball is one space diagonal from player.  Check for the following 
               special case: @ _ moving southwest becomes @_ 0 0 (This will
               also catch teleporting that happens to resemble this case, but
               oh well.) Otherwise fall through. */
        case 2:
            if (dist2(x, y, uball->ox, uball->oy) == 2 &&
                dist2(x, y, uchain->ox, uchain->oy) == 4) {
                if (uchain->oy == y)
                    *chainx = uball->ox;
                else
                    *chainy = uball->oy;
                if (IS_CHAIN_ROCK(*chainx, *chainy) && !already_in_rock)
                    SKIP_TO_DRAG;
                break;
            }
            /* fall through */
        case 1:
        case 0:
            /* do nothing if possible */
            if (CHAIN_IN_MIDDLE(uchain->ox, uchain->oy))
                break;
            /* otherwise try to drag chain to player's old position */
            if (CHAIN_IN_MIDDLE(u.ux, u.uy)) {
                *chainx = u.ux;
                *chainy = u.uy;
                break;
            }
            /* otherwise use player's new position (they must have teleported,
               for this to happen) */
            *chainx = x;
            *chainy = y;
            break;

        default:
            impossible("bad chain movement");
            break;
        }
#undef SKIP_TO_DRAG
#undef IS_CHAIN_ROCK
#undef CHAIN_IN_MIDDLE
        return TRUE;
    }

drag:

    if (near_capacity() > SLT_ENCUMBER && dist2(x, y, u.ux, u.uy) <= 2) {
        pline("You cannot %sdrag the heavy iron ball.",
              invent ? "carry all that and also " : "");
        action_completed();
        return FALSE;
    }

    if ((is_pool(level, uchain->ox, uchain->oy) &&
         /* water not mere continuation of previous water */
         (level->locations[uchain->ox][uchain->oy].typ == POOL ||
          !is_pool(level, uball->ox, uball->oy) ||
          level->locations[uball->ox][uball->oy].typ == POOL))
        || ((t = t_at(level, uchain->ox, uchain->oy)) &&
            (t->ttyp == PIT || t->ttyp == SPIKED_PIT || t->ttyp == HOLE ||
             t->ttyp == TRAPDOOR))) {

        if (Levitation) {
            pline("You feel a tug from the iron ball.");
            if (t)
                t->tseen = 1;
        } else {
            struct monst *victim;

            pline("You are jerked back by the iron ball!");
            if ((victim = m_at(level, uchain->ox, uchain->oy)) != 0) {
                int tmp;

                tmp = -2 + Luck + find_mac(victim);
                tmp += omon_adj(victim, uball, TRUE);
                if (tmp >= rnd(20))
                    hmon(victim, uball, 1);
                else
                    miss(xname(uball), victim);

            }   /* now check again in case mon died */
            if (!m_at(level, uchain->ox, uchain->oy)) {
                u.ux = uchain->ox;
                u.uy = uchain->oy;
                newsym(u.ux0, u.uy0);
            }
            action_interrupted();

            *bc_control = BC_BALL;
            move_bc(1, *bc_control, *ballx, *bally, *chainx, *chainy);
            *ballx = uchain->ox;
            *bally = uchain->oy;
            move_bc(0, *bc_control, *ballx, *bally, *chainx, *chainy);
            spoteffects(TRUE);
            return FALSE;
        }
    }

    *bc_control = BC_BALL | BC_CHAIN;

    move_bc(1, *bc_control, *ballx, *bally, *chainx, *chainy);
    if (dist2(x, y, u.ux, u.uy) > 2) {
        /* Awful case: we're still in range of the ball, so we thought we could 
           only move the chain, but it turned out that the target square for
           the chain was rock, so we had to drag it instead. But we can't drag
           it either, because we teleported and are more than one square from
           our old position.  Revert to the teleport behavior. */
        *ballx = *chainx = x;
        *bally = *chainy = y;
    } else {
        *ballx = uchain->ox;
        *bally = uchain->oy;
        *chainx = u.ux;
        *chainy = u.uy;
    }
    *cause_delay = TRUE;
    return TRUE;
}
Пример #3
0
static void wait_for_action_complete(struct cpu_action_queue *q,
					struct cpu_action *a)
{
	while (!action_completed(q, a))
		wfe();
}