/** * Called from project() to affect the player * * Called for projections with the PROJECT_PLAY flag set, which includes * bolt, beam, ball and breath effects. * * \param src is the origin of the effect * \param r is the distance from the centre of the effect * \param y the coordinates of the grid being handled * \param x the coordinates of the grid being handled * \param dam is the "damage" from the effect at distance r from the centre * \param typ is the projection (PROJ_) type * \return whether the effects were obvious * * If "r" is non-zero, then the blast was centered elsewhere; the damage * is reduced in project() before being passed in here. This can happen if a * monster breathes at the player and hits a wall instead. * * We assume the player is aware of some effect, and always return "true". */ bool project_p(struct source origin, int r, int y, int x, int dam, int typ) { bool blind = (player->timed[TMD_BLIND] ? true : false); bool seen = !blind; bool obvious = true; /* Monster or trap name (for damage) */ char killer[80]; project_player_handler_f player_handler = player_handlers[typ]; project_player_handler_context_t context = { origin, r, y, x, dam, typ, obvious, }; /* No player here */ if (!square_isplayer(cave, y, x)) { return false; } switch (origin.what) { case SRC_PLAYER: /* Never affect projector */ return false; case SRC_MONSTER: { struct monster *mon = cave_monster(cave, origin.which.monster); /* Check it is visible */ if (!monster_is_visible(mon)) seen = false; /* Get the monster's real name */ monster_desc(killer, sizeof(killer), mon, MDESC_DIED_FROM); break; } case SRC_TRAP: { struct trap *trap = origin.which.trap; /* Get the trap name */ strnfmt(killer, sizeof(killer), "a %s", trap->kind->desc); break; } case SRC_OBJECT: { struct object *obj = origin.which.object; object_desc(killer, sizeof(killer), obj, ODESC_PREFIX | ODESC_BASE); break; } case SRC_NONE: { /* Assume the caller has set the killer variable */ break; } } /* Let player know what is going on */ if (!seen) { msg("You are hit by %s!", projections[typ].blind_desc); } /* Adjust damage for resistance, immunity or vulnerability, and apply it */ dam = adjust_dam(player, typ, dam, RANDOMISE, player->state.el_info[typ].res_level, true); if (dam) { take_hit(player, dam, killer); } /* Handle side effects */ if (player_handler != NULL && player->is_dead == false) { player_handler(&context); } /* Disturb */ disturb(player, 1); /* Return "Anything seen?" */ return context.obvious; }
/** * Process a monster * * In several cases, we directly update the monster lore * * Note that a monster is only allowed to "reproduce" if there * are a limited number of "reproducing" monsters on the current * level. This should prevent the level from being "swamped" by * reproducing monsters. It also allows a large mass of mice to * prevent a louse from multiplying, but this is a small price to * pay for a simple multiplication method. * * XXX Monster fear is slightly odd, in particular, monsters will * fixate on opening a door even if they cannot open it. Actually, * the same thing happens to normal monsters when they hit a door * * In addition, monsters which *cannot* open or bash down a door * will still stand there trying to open it... XXX XXX XXX * * Technically, need to check for monster in the way combined * with that monster being in a wall (or door?) XXX */ static void process_monster(struct chunk *c, struct monster *mon) { struct monster_lore *lore = get_lore(mon->race); bool did_something = false; int i; int dir = 0; bool stagger = false; char m_name[80]; /* Get the monster name */ monster_desc(m_name, sizeof(m_name), mon, MDESC_CAPITAL | MDESC_IND_HID); /* Try to multiply - this can use up a turn */ if (process_monster_multiply(c, mon)) return; /* Attempt to cast a spell */ if (make_attack_spell(mon)) return; /* Work out what kind of movement to use - AI or staggered movement */ if (!process_monster_should_stagger(mon)) { if (!get_moves(c, mon, &dir)) return; } else { stagger = true; } /* Process moves */ for (i = 0; i < 5 && !did_something; i++) { int oy = mon->fy; int ox = mon->fx; /* Get the direction (or stagger) */ int d = (stagger ? ddd[randint0(8)] : side_dirs[dir][i]); /* Get the destination */ int ny = oy + ddy[d]; int nx = ox + ddx[d]; /* Check if we can move */ if (!process_monster_can_move(c, mon, m_name, nx, ny, &did_something)) continue; /* Try to break the glyph if there is one. This can happen multiple * times per turn because failure does not break the loop */ if (square_iswarded(c, ny, nx) && !process_monster_glyph(c, mon, nx, ny)) continue; /* The player is in the way. */ if (square_isplayer(c, ny, nx)) { /* Learn about if the monster attacks */ if (mflag_has(mon->mflag, MFLAG_VISIBLE)) rf_on(lore->flags, RF_NEVER_BLOW); /* Some monsters never attack */ if (rf_has(mon->race->flags, RF_NEVER_BLOW)) continue; /* Otherwise, attack the player */ make_attack_normal(mon, player); did_something = true; break; } else { /* Some monsters never move */ if (rf_has(mon->race->flags, RF_NEVER_MOVE)) { /* Learn about lack of movement */ if (mflag_has(mon->mflag, MFLAG_VISIBLE)) rf_on(lore->flags, RF_NEVER_MOVE); return; } } /* A monster is in the way, try to push past/kill */ if (square_monster(c, ny, nx)) { did_something = process_monster_try_push(c, mon, m_name, nx, ny); } else { /* Otherwise we can just move */ monster_swap(oy, ox, ny, nx); did_something = true; } /* Scan all objects in the grid, if we reached it */ if (mon == square_monster(c, ny, nx)) { monster_desc(m_name, sizeof(m_name), mon, MDESC_CAPITAL | MDESC_IND_HID); process_monster_grab_objects(c, mon, m_name, nx, ny); } } if (did_something) { /* Learn about no lack of movement */ if (mflag_has(mon->mflag, MFLAG_VISIBLE)) rf_on(lore->flags, RF_NEVER_MOVE); /* Possible disturb */ if (mflag_has(mon->mflag, MFLAG_VISIBLE) && mflag_has(mon->mflag, MFLAG_VIEW) && OPT(player, disturb_near)) disturb(player, 0); } /* Hack -- get "bold" if out of options */ if (!did_something && mon->m_timed[MON_TMD_FEAR]) mon_clear_timed(mon, MON_TMD_FEAR, MON_TMD_FLG_NOTIFY, false); /* If we see an unaware monster do something, become aware of it */ if (did_something && mflag_has(mon->mflag, MFLAG_UNAWARE)) become_aware(mon); }