/** * Make a new trap of the given type. Return TRUE if successful. * * We choose a player trap at random if the index is not legal. This means that * things which are not player traps must be picked by passing a valid index. * * This should be the only function that places traps in the dungeon * except the savefile loading code. */ void place_trap(struct chunk *c, int y, int x, int t_idx, int trap_level) { struct trap *new_trap; /* We've been called with an illegal index; choose a random trap */ if ((t_idx <= 0) || (t_idx >= z_info->trap_max)) { /* Require the correct terrain */ if (!square_player_trap_allowed(c, y, x)) return; t_idx = pick_trap(c->squares[y][x].feat, trap_level); } /* Failure */ if (t_idx < 0) return; /* Allocate a new trap for this grid (at the front of the list) */ new_trap = mem_zalloc(sizeof(*new_trap)); new_trap->next = c->squares[y][x].trap; c->squares[y][x].trap = new_trap; /* Set the details */ new_trap->t_idx = t_idx; new_trap->kind = &trap_info[t_idx]; new_trap->fy = y; new_trap->fx = x; trf_copy(new_trap->flags, trap_info[t_idx].flags); /* Toggle on the trap marker */ sqinfo_on(c->squares[y][x].info, SQUARE_TRAP); /* Redraw the grid */ square_light_spot(c, y, x); }
/** * This routine will "darken" all grids in the set passed in. * * In addition, some of these grids will be "unmarked". * * This routine is used (only) by "light_room()" */ static void cave_unlight(struct point_set *ps) { int i; /* Apply flag changes */ for (i = 0; i < ps->n; i++) { int y = ps->pts[i].y; int x = ps->pts[i].x; /* Darken the grid */ sqinfo_off(cave->squares[y][x].info, SQUARE_GLOW); /* Hack -- Forget "boring" grids */ if (square_isfloor(cave, y, x)) sqinfo_off(cave->squares[y][x].info, SQUARE_MARK); } /* Fully update the visuals */ player->upkeep->update |= (PU_FORGET_VIEW | PU_UPDATE_VIEW | PU_MONSTERS); /* Update stuff */ update_stuff(player); /* Process the grids */ for (i = 0; i < ps->n; i++) { int y = ps->pts[i].y; int x = ps->pts[i].x; /* Redraw the grid */ square_light_spot(cave, y, x); } }
/** * Set the terrain type for a square. * * This should be the only function that sets terrain, apart from the savefile * loading code. */ void square_set_feat(struct chunk *c, int y, int x, int feat) { int current_feat = c->squares[y][x].feat; assert(c); assert(y >= 0 && y < c->height); assert(x >= 0 && x < c->width); /* Track changes */ if (current_feat) c->feat_count[current_feat]--; if (feat) c->feat_count[feat]++; /* Make the change */ c->squares[y][x].feat = feat; /* Make the new terrain feel at home */ if (character_dungeon) { /* Remove traps if necessary */ if (!square_player_trap_allowed(c, y, x)) square_destroy_trap(c, y, x); square_note_spot(c, y, x); square_light_spot(c, y, x); } else { /* Make sure no incorrect wall flags set for dungeon generation */ sqinfo_off(c->squares[y][x].info, SQUARE_WALL_INNER); sqinfo_off(c->squares[y][x].info, SQUARE_WALL_OUTER); sqinfo_off(c->squares[y][x].info, SQUARE_WALL_SOLID); } }
/** * This routine will "darken" all grids in the set passed in. * * In addition, some of these grids will be "unmarked". * * This routine is used (only) by "light_room()" */ static void cave_unlight(struct point_set *ps) { int i; /* Apply flag changes */ for (i = 0; i < ps->n; i++) { int y = ps->pts[i].y; int x = ps->pts[i].x; /* Darken the grid */ if (!square_isbright(cave, y, x)) { sqinfo_off(cave->squares[y][x].info, SQUARE_GLOW); } /* Hack -- Forget "boring" grids */ if (square_isfloor(cave, y, x)) square_forget(cave, y, x); } /* Process the grids */ for (i = 0; i < ps->n; i++) { int y = ps->pts[i].y; int x = ps->pts[i].x; /* Redraw the grid */ square_light_spot(cave, y, x); } }
/** * Remove traps. * * If called with t_idx < 0, will remove all traps in the location given. * Otherwise, will remove all traps with the given kind. * * Return TRUE if no traps now exist in this grid. */ bool square_remove_trap(struct chunk *c, int y, int x, bool domsg, int t_idx) { bool trap_exists; struct trap **trap_slot = &c->squares[y][x].trap; struct trap *next_trap; /* Look at the traps in this grid */ while (*trap_slot) { /* Get the next trap (may be NULL) */ next_trap = (*trap_slot)->next; /* If called with a specific index, skip others */ if ((t_idx >= 0) && (t_idx != (*trap_slot)->t_idx)) { if (!next_trap) break; trap_slot = &next_trap; continue; } /* Remove it */ remove_trap_aux(c, *trap_slot, y, x, domsg); /* Replace with the next trap */ *trap_slot = next_trap; } /* Refresh grids that the character can see */ if (square_isseen(c, y, x)) square_light_spot(c, y, x); /* Verify traps (remove marker if appropriate) */ trap_exists = square_verify_trap(c, y, x, 0); /* Report whether any traps exist in this grid */ return (!trap_exists); }
/** * Let the floor carry an object, deleting old ignored items if necessary. * The calling function must deal with the dropped object on failure. * * Optionally put the object at the top or bottom of the pile */ bool floor_carry(struct chunk *c, int y, int x, struct object *drop, bool last) { int n = 0; struct object *obj, *ignore = floor_get_oldest_ignored(y, x); /* Fail if the square can't hold objects */ if (!square_isobjectholding(c, y, x)) return false; /* Scan objects in that grid for combination */ for (obj = square_object(c, y, x); obj; obj = obj->next) { /* Check for combination */ if (object_similar(obj, drop, OSTACK_FLOOR)) { /* Combine the items */ object_absorb(obj, drop); /* Result */ return true; } /* Count objects */ n++; } /* The stack is already too large */ if (n >= z_info->floor_size || (!OPT(player, birth_stacking) && n)) { /* Delete the oldest ignored object */ if (ignore) { square_excise_object(c, y, x, ignore); delist_object(c, ignore); object_delete(&ignore); } else return false; } /* Location */ drop->iy = y; drop->ix = x; /* Forget monster */ drop->held_m_idx = 0; /* Link to the first or last object in the pile */ if (last) pile_insert_end(&c->squares[y][x].obj, drop); else pile_insert(&c->squares[y][x].obj, drop); /* Record in the level list */ list_object(c, drop); /* Redraw */ square_note_spot(c, y, x); square_light_spot(c, y, x); /* Result */ return true; }
/** * Reveal some of the player traps in a square */ bool square_reveal_trap(struct chunk *c, int y, int x, bool always, bool domsg) { int found_trap = 0; struct trap *trap = square_trap(c, y, x); /* Check there is a player trap */ if (!square_isplayertrap(c, y, x)) return false; /* Scan the grid */ while (trap) { /* Skip non-player traps */ if (!trf_has(trap->flags, TRF_TRAP)) { trap = trap->next; continue; } /* Skip traps the player doesn't notice */ if (!always && player->state.skills[SKILL_SEARCH] < trap->power) { trap = trap->next; continue; } /* Trap is invisible */ if (!trf_has(trap->flags, TRF_VISIBLE)) { /* See the trap */ trf_on(trap->flags, TRF_VISIBLE); square_memorize(c, y, x); /* We found a trap */ found_trap++; } trap = trap->next; } /* We found at least one trap */ if (found_trap) { /* We want to talk about it */ if (domsg) { if (found_trap == 1) msg("You have found a trap."); else msg("You have found %d traps.", found_trap); } /* Memorize */ square_memorize(c, y, x); /* Redraw */ square_light_spot(c, y, x); } /* Return true if we found any traps */ return (found_trap != 0); }
/** * Housekeeping after the processing of a player command */ static void process_player_cleanup(void) { int i; /* Significant */ if (player->upkeep->energy_use) { /* Use some energy */ player->energy -= player->upkeep->energy_use; /* Increment the total energy counter */ player->total_energy += player->upkeep->energy_use; /* Do nothing else if player has auto-dropped stuff */ if (!player->upkeep->dropping) { /* Hack -- constant hallucination */ if (player->timed[TMD_IMAGE]) player->upkeep->redraw |= (PR_MAP); /* Shimmer multi-hued monsters */ for (i = 1; i < cave_monster_max(cave); i++) { struct monster *mon = cave_monster(cave, i); if (!mon->race) continue; if (!rf_has(mon->race->flags, RF_ATTR_MULTI)) continue; square_light_spot(cave, mon->fy, mon->fx); } /* Clear NICE flag, and show marked monsters */ for (i = 1; i < cave_monster_max(cave); i++) { struct monster *mon = cave_monster(cave, i); mflag_off(mon->mflag, MFLAG_NICE); if (mflag_has(mon->mflag, MFLAG_MARK)) { if (!mflag_has(mon->mflag, MFLAG_SHOW)) { mflag_off(mon->mflag, MFLAG_MARK); update_mon(mon, cave, false); } } } } } /* Clear SHOW flag and player drop status */ for (i = 1; i < cave_monster_max(cave); i++) { struct monster *mon = cave_monster(cave, i); mflag_off(mon->mflag, MFLAG_SHOW); } player->upkeep->dropping = false; /* Hack - update needed first because inventory may have changed */ update_stuff(player); redraw_stuff(player); }
/** * This routine will Perma-Light all grids in the set passed in. * * This routine is used (only) by "light_room()" * * Dark grids are illuminated. * * Also, process all affected monsters. * * SMART monsters always wake up when illuminated * NORMAL monsters wake up 1/4 the time when illuminated * STUPID monsters wake up 1/10 the time when illuminated */ static void cave_light(struct point_set *ps) { int i; /* Apply flag changes */ for (i = 0; i < ps->n; i++) { int y = ps->pts[i].y; int x = ps->pts[i].x; /* Perma-Light */ sqinfo_on(cave->squares[y][x].info, SQUARE_GLOW); } /* Fully update the visuals */ player->upkeep->update |= (PU_FORGET_VIEW | PU_UPDATE_VIEW | PU_MONSTERS); /* Update stuff */ update_stuff(player); /* Process the grids */ for (i = 0; i < ps->n; i++) { int y = ps->pts[i].y; int x = ps->pts[i].x; /* Redraw the grid */ square_light_spot(cave, y, x); /* Process affected monsters */ if (cave->squares[y][x].mon > 0) { int chance = 25; monster_type *m_ptr = square_monster(cave, y, x); /* Stupid monsters rarely wake up */ if (rf_has(m_ptr->race->flags, RF_STUPID)) chance = 10; /* Smart monsters always wake up */ if (rf_has(m_ptr->race->flags, RF_SMART)) chance = 100; /* Sometimes monsters wake up */ if (m_ptr->m_timed[MON_TMD_SLEEP] && (randint0(100) < chance)) { /* Wake up! */ mon_clear_timed(m_ptr, MON_TMD_SLEEP, MON_TMD_FLG_NOTIFY, FALSE); } } } }
/** * Reveal some of the player traps in a square */ bool square_reveal_trap(struct chunk *c, int y, int x, int chance, bool domsg) { int found_trap = 0; struct trap *trap = c->squares[y][x].trap; /* Check there is a player trap */ if (!square_isplayertrap(c, y, x)) return FALSE; /* Scan the grid */ while (trap) { /* Skip non-player traps */ if (!trf_has(trap->flags, TRF_TRAP)) { trap = trap->next; continue; } /* Trap is invisible */ if (!trf_has(trap->flags, TRF_VISIBLE)) { /* See the trap */ trf_on(trap->flags, TRF_VISIBLE); sqinfo_on(c->squares[y][x].info, SQUARE_MARK); /* We found a trap */ found_trap++; /* If chance is < 100, check for further looking */ if ((chance < 100) && (randint1(100) > chance)) break; } trap = trap->next; } /* We found at least one trap */ if (found_trap) { /* We want to talk about it */ if (domsg) { if (found_trap == 1) msg("You have found a trap."); else msg("You have found %d traps.", found_trap); } /* Memorize */ sqinfo_on(c->squares[y][x].info, SQUARE_MARK); /* Redraw */ square_light_spot(c, y, x); } /* Return TRUE if we found any traps */ return (found_trap != 0); }
/** * Remove traps. * * If called with t_idx < 0, will remove all traps in the location given. * Otherwise, will remove all traps with the given kind. * * Return true if no traps now exist in this grid. */ bool square_set_trap_timeout(struct chunk *c, int y, int x, bool domsg, int t_idx, int time) { bool trap_exists; struct trap *current_trap = NULL; /* Bounds check */ assert(square_in_bounds(c, y, x)); /* Look at the traps in this grid */ current_trap = c->squares[y][x].trap; while (current_trap) { /* Get the next trap (may be NULL) */ struct trap *next_trap = current_trap->next; /* If called with a specific index, skip others */ if ((t_idx >= 0) && (t_idx != current_trap->t_idx)) { if (!next_trap) break; current_trap = next_trap; continue; } /* Set the timer */ current_trap->timeout = time; /* Message if requested */ msg("You have disabled the %s.", current_trap->kind->name); /* Replace with the next trap */ current_trap = next_trap; } /* Refresh grids that the character can see */ if (square_isseen(c, y, x)) square_light_spot(c, y, x); /* Verify traps (remove marker if appropriate) */ trap_exists = square_verify_trap(c, y, x, 0); /* Report whether any traps exist in this grid */ return (!trap_exists); }
/** * This routine will Perma-Light all grids in the set passed in. * * This routine is used (only) by "light_room()" * * Dark grids are illuminated. * * Also, process all affected monsters. * * SMART monsters always wake up when illuminated * NORMAL monsters wake up 1/4 the time when illuminated * STUPID monsters wake up 1/10 the time when illuminated */ static void cave_light(struct point_set *ps) { int i; /* Apply flag changes */ for (i = 0; i < ps->n; i++) { int y = ps->pts[i].y; int x = ps->pts[i].x; /* Perma-Light */ sqinfo_on(cave->squares[y][x].info, SQUARE_GLOW); } /* Process the grids */ for (i = 0; i < ps->n; i++) { int y = ps->pts[i].y; int x = ps->pts[i].x; /* Redraw the grid */ square_light_spot(cave, y, x); /* Process affected monsters */ if (cave->squares[y][x].mon > 0) { int chance = 25; struct monster *mon = square_monster(cave, y, x); /* Stupid monsters rarely wake up */ if (monster_is_stupid(mon)) chance = 10; /* Smart monsters always wake up */ if (monster_is_smart(mon)) chance = 100; /* Sometimes monsters wake up */ if (mon->m_timed[MON_TMD_SLEEP] && (randint0(100) < chance)) { mon_clear_timed(mon, MON_TMD_SLEEP, MON_TMD_FLG_NOTIFY, false); } } } }
/** * Perform the basic "close" command * * Assume there is no monster blocking the destination * * Returns TRUE if repeated commands may continue */ static bool do_cmd_close_aux(int y, int x) { bool more = FALSE; /* Verify legality */ if (!do_cmd_close_test(y, x)) return (FALSE); /* Broken door */ if (square_isbrokendoor(cave, y, x)) { msg("The door appears to be broken."); } else { /* Close door */ square_close_door(cave, y, x); sqinfo_on(cave->squares[y][x].info, SQUARE_MARK); square_light_spot(cave, y, x); player->upkeep->update |= (PU_UPDATE_VIEW | PU_MONSTERS); sound(MSG_SHUTDOOR); } /* Result */ return (more); }
/** * Perform the basic "close" command * * Assume there is no monster blocking the destination * * Returns true if repeated commands may continue */ static bool do_cmd_close_aux(int y, int x) { bool more = false; /* Verify legality */ if (!do_cmd_close_test(y, x)) return (false); /* Broken door */ if (square_isbrokendoor(cave, y, x)) { msg("The door appears to be broken."); } else { /* Close door */ square_close_door(cave, y, x); square_memorize(cave, y, x); square_light_spot(cave, y, x); player->upkeep->update |= (PU_UPDATE_VIEW | PU_MONSTERS); sound(MSG_SHUTDOOR); } /* Result */ return (more); }
/** * Remove traps. * * If called with t_idx < 0, will remove all traps in the location given. * Otherwise, will remove all traps with the given kind. * * Return true if traps were removed. */ bool square_remove_trap(struct chunk *c, int y, int x, int t_idx_remove) { assert(square_in_bounds(c, y, x)); bool removed = false; /* Look at the traps in this grid */ struct trap *prev_trap = NULL; struct trap *trap = c->squares[y][x].trap; while (trap) { struct trap *next_trap = trap->next; if (t_idx_remove == trap->t_idx) { mem_free(trap); removed = true; if (prev_trap) { prev_trap->next = next_trap; } else { c->squares[y][x].trap = next_trap; } break; } prev_trap = trap; trap = next_trap; } /* Refresh grids that the character can see */ if (square_isseen(c, y, x)) square_light_spot(c, y, x); (void)square_verify_trap(c, y, x, 0); return removed; }
/** * Remove all traps from a grid. * * Return true if traps were removed. */ bool square_remove_all_traps(struct chunk *c, int y, int x) { assert(square_in_bounds(c, y, x)); struct trap *trap = c->squares[y][x].trap; bool were_there_traps = trap == NULL ? false : true; while (trap) { struct trap *next_trap = trap->next; mem_free(trap); trap = next_trap; } c->squares[y][x].trap = NULL; /* Refresh grids that the character can see */ if (square_isseen(c, y, x)) { square_light_spot(c, y, x); } (void)square_verify_trap(c, y, x, 0); return were_there_traps; }
/** * Called from project() to affect objects * * Called for projections with the PROJECT_ITEM flag set, which includes * beam, ball and breath effects. * * \param who is the monster list index of the caster * \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 (GF_) type * \param protected_obj is an object that should not be affected by the * projection, typically the object that created it * \return whether the effects were obvious * * Note that this function determines if the player can see anything that * happens by taking into account: blindness, line-of-sight, and illumination. * * Hack -- effects on objects which are memorized but not in view are also seen. */ bool project_o(int who, int r, int y, int x, int dam, int typ, const struct object *protected_obj) { struct object *obj = square_object(cave, y, x); bool obvious = false; /* Scan all objects in the grid */ while (obj) { bool ignore = false; bool do_kill = false; const char *note_kill = NULL; struct object *next = obj->next; project_object_handler_context_t context = { who, r, y, x, dam, typ, obj, obvious, do_kill, ignore, note_kill, }; project_object_handler_f object_handler = object_handlers[typ]; if (object_handler != NULL) object_handler(&context); obvious = context.obvious; do_kill = context.do_kill && (obj != protected_obj); ignore = context.ignore; note_kill = context.note_kill; /* Attempt to destroy the object */ if (do_kill) { char o_name[80]; /* Effect observed */ if (obj->known && !ignore_item_ok(obj) && square_isseen(cave, y, x)) { obvious = true; object_desc(o_name, sizeof(o_name), obj, ODESC_BASE); } /* Artifacts, and other objects, get to resist */ if (obj->artifact || ignore) { /* Observe the resist */ if (obvious && obj->known && !ignore_item_ok(obj)) msg("The %s %s unaffected!", o_name, VERB_AGREEMENT(obj->number, "is", "are")); } else if (obj->mimicking_m_idx) { /* Reveal mimics */ if (obvious) become_aware(cave_monster(cave, obj->mimicking_m_idx)); } else { /* Describe if needed */ if (obvious && obj->known && note_kill && !ignore_item_ok(obj)) msgt(MSG_DESTROY, "The %s %s!", o_name, note_kill); /* Delete the object */ square_excise_object(cave, y, x, obj); delist_object(cave, obj); object_delete(&obj); /* Redraw */ square_note_spot(cave, y, x); square_light_spot(cave, y, x); } } /* Next object */ obj = next; } /* Return "Anything seen?" */ return (obvious); }
/** * Move player in the given direction. * * This routine should only be called when energy has been expended. * * Note that this routine handles monsters in the destination grid, * and also handles attempting to move into walls/doors/rubble/etc. */ void move_player(int dir, bool disarm) { int y = player->py + ddy[dir]; int x = player->px + ddx[dir]; int m_idx = cave->squares[y][x].mon; struct monster *mon = cave_monster(cave, m_idx); bool alterable = (square_isknowntrap(cave, y, x) || square_iscloseddoor(cave, y, x)); /* Attack monsters, alter traps/doors on movement, hit obstacles or move */ if (m_idx > 0) { /* Mimics surprise the player */ if (is_mimicking(mon)) { become_aware(mon); /* Mimic wakes up */ mon_clear_timed(mon, MON_TMD_SLEEP, MON_TMD_FLG_NOMESSAGE, false); } else { py_attack(y, x); } } else if (disarm && square_isknown(cave, y, x) && alterable) { /* Auto-repeat if not already repeating */ if (cmd_get_nrepeats() == 0) cmd_set_repeat(99); do_cmd_alter_aux(dir); } else if (player->upkeep->running && square_isknowntrap(cave, y, x)) { /* Stop running before known traps */ disturb(player, 0); } else if (!square_ispassable(cave, y, x)) { disturb(player, 0); /* Notice unknown obstacles, mention known obstacles */ if (!square_isknown(cave, y, x)) { if (square_isrubble(cave, y, x)) { msgt(MSG_HITWALL, "You feel a pile of rubble blocking your way."); square_memorize(cave, y, x); square_light_spot(cave, y, x); } else if (square_iscloseddoor(cave, y, x)) { msgt(MSG_HITWALL, "You feel a door blocking your way."); square_memorize(cave, y, x); square_light_spot(cave, y, x); } else { msgt(MSG_HITWALL, "You feel a wall blocking your way."); square_memorize(cave, y, x); square_light_spot(cave, y, x); } } else { if (square_isrubble(cave, y, x)) msgt(MSG_HITWALL, "There is a pile of rubble blocking your way."); else if (square_iscloseddoor(cave, y, x)) msgt(MSG_HITWALL, "There is a door blocking your way."); else msgt(MSG_HITWALL, "There is a wall blocking your way."); } } else { /* Move player */ monster_swap(player->py, player->px, y, x); /* Handle store doors, or notice objects */ if (square_isshop(cave, y, x)) { disturb(player, 0); event_signal(EVENT_ENTER_STORE); event_remove_handler_type(EVENT_ENTER_STORE); event_signal(EVENT_USE_STORE); event_remove_handler_type(EVENT_USE_STORE); event_signal(EVENT_LEAVE_STORE); event_remove_handler_type(EVENT_LEAVE_STORE); } else { square_know_pile(cave, y, x); cmdq_push(CMD_AUTOPICKUP); } /* Discover invisible traps, set off visible ones */ if (square_issecrettrap(cave, y, x)) { disturb(player, 0); hit_trap(y, x); } else if (square_isknowntrap(cave, y, x)) { disturb(player, 0); hit_trap(y, x); } /* Update view and search */ update_view(cave, player); search(); } player->upkeep->running_firststep = false; }
/** * Perform the basic "open" command on doors * * Assume there is no monster blocking the destination * * Returns true if repeated commands may continue */ static bool do_cmd_open_aux(int y, int x) { int i, j; bool more = false; /* Verify legality */ if (!do_cmd_open_test(y, x)) return (false); /* Locked door */ if (square_islockeddoor(cave, y, x)) { /* Disarm factor */ i = player->state.skills[SKILL_DISARM_PHYS]; /* Penalize some conditions */ if (player->timed[TMD_BLIND] || no_light()) i = i / 10; if (player->timed[TMD_CONFUSED] || player->timed[TMD_IMAGE]) i = i / 10; /* Extract the lock power */ j = square_door_power(cave, y, x); /* Extract the difficulty XXX XXX XXX */ j = i - (j * 4); /* Always have a small chance of success */ if (j < 2) j = 2; if (randint0(100) < j) { /* Message */ msgt(MSG_LOCKPICK, "You have picked the lock."); /* Open the door */ square_open_door(cave, y, x); /* Update the visuals */ square_memorize(cave, y, x); square_light_spot(cave, y, x); player->upkeep->update |= (PU_UPDATE_VIEW | PU_MONSTERS); /* Experience */ /* Removed to avoid exploit by repeatedly locking and unlocking */ /* player_exp_gain(player, 1); */ } else { event_signal(EVENT_INPUT_FLUSH); /* Message */ msgt(MSG_LOCKPICK_FAIL, "You failed to pick the lock."); /* We may keep trying */ more = true; } } else { /* Closed door */ square_open_door(cave, y, x); square_memorize(cave, y, x); square_light_spot(cave, y, x); player->upkeep->update |= (PU_UPDATE_VIEW | PU_MONSTERS); sound(MSG_OPENDOOR); } /* Result */ return (more); }
/** * Grab all objects from the grid. */ void process_monster_grab_objects(struct chunk *c, struct monster *mon, const char *m_name, int nx, int ny) { struct monster_lore *lore = get_lore(mon->race); struct object *obj; bool visible = mflag_has(mon->mflag, MFLAG_VISIBLE); /* Learn about item pickup behavior */ for (obj = square_object(c, ny, nx); obj; obj = obj->next) { if (!tval_is_money(obj) && visible) { rf_on(lore->flags, RF_TAKE_ITEM); rf_on(lore->flags, RF_KILL_ITEM); break; } } /* Abort if can't pickup/kill */ if (!rf_has(mon->race->flags, RF_TAKE_ITEM) && !rf_has(mon->race->flags, RF_KILL_ITEM)) { return; } /* Take or kill objects on the floor */ obj = square_object(c, ny, nx); while (obj) { char o_name[80]; bool safe = obj->artifact ? true : false; struct object *next = obj->next; /* Skip gold */ if (tval_is_money(obj)) { obj = next; continue; } /* Skip mimicked objects */ if (obj->mimicking_m_idx) { obj = next; continue; } /* Get the object name */ object_desc(o_name, sizeof(o_name), obj, ODESC_PREFIX | ODESC_FULL); /* React to objects that hurt the monster */ if (react_to_slay(obj, mon)) safe = true; /* Try to pick up, or crush */ if (safe) { /* Only give a message for "take_item" */ if (rf_has(mon->race->flags, RF_TAKE_ITEM) && visible && square_isview(c, ny, nx) && !ignore_item_ok(obj)) { /* Dump a message */ msg("%s tries to pick up %s, but fails.", m_name, o_name); } } else if (rf_has(mon->race->flags, RF_TAKE_ITEM)) { /* Describe observable situations */ if (square_isseen(c, ny, nx) && !ignore_item_ok(obj)) msg("%s picks up %s.", m_name, o_name); /* Carry the object */ square_excise_object(c, ny, nx, obj); monster_carry(c, mon, obj); square_note_spot(c, ny, nx); square_light_spot(c, ny, nx); } else { /* Describe observable situations */ if (square_isseen(c, ny, nx) && !ignore_item_ok(obj)) msgt(MSG_DESTROY, "%s crushes %s.", m_name, o_name); /* Delete the object */ square_excise_object(c, ny, nx, obj); delist_object(c, obj); object_delete(&obj); square_note_spot(c, ny, nx); square_light_spot(c, ny, nx); } /* Next object */ obj = next; } }
/** * Attempt to open the given chest at the given location * * Assume there is no monster blocking the destination * * Returns TRUE if repeated commands may continue */ bool do_cmd_open_chest(int y, int x, struct object *obj) { int i, j; bool flag = TRUE; bool more = FALSE; /* Attempt to unlock it */ if (obj->pval > 0) { /* Assume locked, and thus not open */ flag = FALSE; /* Get the "disarm" factor */ i = player->state.skills[SKILL_DISARM]; /* Penalize some conditions */ if (player->timed[TMD_BLIND] || no_light()) i = i / 10; if (player->timed[TMD_CONFUSED] || player->timed[TMD_IMAGE]) i = i / 10; /* Extract the difficulty */ j = i - obj->pval; /* Always have a small chance of success */ if (j < 2) j = 2; /* Success -- May still have traps */ if (randint0(100) < j) { msgt(MSG_LOCKPICK, "You have picked the lock."); player_exp_gain(player, 1); flag = TRUE; } else { /* We may continue repeating */ more = TRUE; event_signal(EVENT_INPUT_FLUSH); msgt(MSG_LOCKPICK_FAIL, "You failed to pick the lock."); } } /* Allowed to open */ if (flag) { /* Apply chest traps, if any */ chest_trap(y, x, obj); /* Let the Chest drop items */ chest_death(y, x, obj); /* Ignore chest if autoignore calls for it */ player->upkeep->notice |= PN_IGNORE; } /* Empty chests were always ignored in ignore_item_okay so we * might as well ignore it here */ if (obj->pval == 0) obj->ignore = TRUE; /* Redraw chest, to be on the safe side (it may have been ignored) */ square_light_spot(cave, y, x); /* Result */ return (more); }
/** * Move player in the given direction. * * This routine should only be called when energy has been expended. * * Note that this routine handles monsters in the destination grid, * and also handles attempting to move into walls/doors/rubble/etc. */ void move_player(int dir, bool disarm) { int py = player->py; int px = player->px; int y = py + ddy[dir]; int x = px + ddx[dir]; int m_idx = cave->squares[y][x].mon; struct monster *mon = cave_monster(cave, m_idx); bool alterable = (square_isknowntrap(cave, y, x) || square_iscloseddoor(cave, y, x)); /* Attack monsters, alter traps/doors on movement, hit obstacles or move */ if (m_idx > 0) { /* Mimics surprise the player */ if (is_mimicking(mon)) { become_aware(mon); /* Mimic wakes up */ mon_clear_timed(mon, MON_TMD_SLEEP, MON_TMD_FLG_NOMESSAGE, false); } else { py_attack(y, x); } } else if (disarm && square_isknown(cave, y, x) && alterable) { /* Auto-repeat if not already repeating */ if (cmd_get_nrepeats() == 0) cmd_set_repeat(99); do_cmd_alter_aux(dir); } else if (player->upkeep->running && square_isknowntrap(cave, y, x)) { /* Stop running before known traps */ disturb(player, 0); } else if (!square_ispassable(cave, y, x)) { /* Disturb the player */ disturb(player, 0); /* Notice unknown obstacles, mention known obstacles */ if (!square_isknown(cave, y, x)) { if (square_isrubble(cave, y, x)) { msgt(MSG_HITWALL, "You feel a pile of rubble blocking your way."); square_memorize(cave, y, x); square_light_spot(cave, y, x); } else if (square_iscloseddoor(cave, y, x)) { msgt(MSG_HITWALL, "You feel a door blocking your way."); square_memorize(cave, y, x); square_light_spot(cave, y, x); } else { msgt(MSG_HITWALL, "You feel a wall blocking your way."); square_memorize(cave, y, x); square_light_spot(cave, y, x); } } else { if (square_isrubble(cave, y, x)) msgt(MSG_HITWALL, "There is a pile of rubble blocking your way."); else if (square_iscloseddoor(cave, y, x)) msgt(MSG_HITWALL, "There is a door blocking your way."); else msgt(MSG_HITWALL, "There is a wall blocking your way."); } } else { /* See if trap detection status will change */ bool old_dtrap = square_isdtrap(cave, py, px); bool new_dtrap = square_isdtrap(cave, y, x); /* Note the change in the detect status */ if (old_dtrap != new_dtrap) player->upkeep->redraw |= (PR_DTRAP); /* Disturb player if the player is about to leave the area */ if (player->upkeep->running && !player->upkeep->running_firststep && old_dtrap && !new_dtrap) { disturb(player, 0); return; } /* Move player */ monster_swap(py, px, y, x); /* New location */ y = py = player->py; x = px = player->px; /* Searching */ if (player->searching || (player->state.skills[SKILL_SEARCH_FREQUENCY] >= 50) || one_in_(50 - player->state.skills[SKILL_SEARCH_FREQUENCY])) search(false); /* Handle store doors, or notice objects */ if (square_isshop(cave, player->py, player->px)) { /* Disturb */ disturb(player, 0); event_signal(EVENT_ENTER_STORE); event_remove_handler_type(EVENT_ENTER_STORE); event_signal(EVENT_USE_STORE); event_remove_handler_type(EVENT_USE_STORE); event_signal(EVENT_LEAVE_STORE); event_remove_handler_type(EVENT_LEAVE_STORE); } else { /* Know objects, queue autopickup */ floor_pile_know(cave, player->py, player->px); cmdq_push(CMD_AUTOPICKUP); } /* Discover invisible traps, set off visible ones */ if (square_issecrettrap(cave, y, x)) { /* Disturb */ disturb(player, 0); /* Hit the trap. */ hit_trap(y, x); } else if (square_isknowntrap(cave, y, x)) { /* Disturb */ disturb(player, 0); /* Hit the trap */ hit_trap(y, x); } } player->upkeep->running_firststep = false; }
/** * Deletes a monster by index. * * When a monster is deleted, all of its objects are deleted. */ void delete_monster_idx(int m_idx) { assert(m_idx > 0); struct monster *mon = cave_monster(cave, m_idx); int y = mon->fy; int x = mon->fx; assert(square_in_bounds(cave, y, x)); /* Hack -- Reduce the racial counter */ mon->race->cur_num--; /* Hack -- count the number of "reproducers" */ if (rf_has(mon->race->flags, RF_MULTIPLY)) num_repro--; /* Hack -- remove target monster */ if (target_get_monster() == mon) target_set_monster(NULL); /* Hack -- remove tracked monster */ if (player->upkeep->health_who == mon) health_track(player->upkeep, NULL); /* Monster is gone */ cave->squares[y][x].mon = 0; /* Delete objects */ struct object *obj = mon->held_obj; while (obj) { struct object *next = obj->next; /* Preserve unseen artifacts (we assume they were created as this * monster's drop) - this will cause unintended behaviour in preserve * off mode if monsters can pick up artifacts */ if (obj->artifact && !(obj->known && obj->known->artifact)) obj->artifact->created = false; /* Delete the object */ delist_object(cave, obj); object_delete(&obj); obj = next; } /* Delete mimicked objects */ if (mon->mimicked_obj) { square_excise_object(cave, y, x, mon->mimicked_obj); delist_object(cave, mon->mimicked_obj); object_delete(&mon->mimicked_obj); } /* Wipe the Monster */ memset(mon, 0, sizeof(struct monster)); /* Count monsters */ cave->mon_cnt--; /* Visual update */ square_light_spot(cave, y, x); }
/** * Handle things that need updating once every 10 game turns */ void process_world(struct chunk *c) { int i, y, x; /* Compact the monster list if we're approaching the limit */ if (cave_monster_count(cave) + 32 > z_info->level_monster_max) compact_monsters(64); /* Too many holes in the monster list - compress */ if (cave_monster_count(cave) + 32 < cave_monster_max(cave)) compact_monsters(0); /*** Check the Time ***/ /* Play an ambient sound at regular intervals. */ if (!(turn % ((10L * z_info->day_length) / 4))) play_ambient_sound(); /*** Handle stores and sunshine ***/ if (!player->depth) { /* Daybreak/Nighfall in town */ if (!(turn % ((10L * z_info->day_length) / 2))) { bool dawn; /* Check for dawn */ dawn = (!(turn % (10L * z_info->day_length))); /* Day breaks */ if (dawn) msg("The sun has risen."); /* Night falls */ else msg("The sun has fallen."); /* Illuminate */ cave_illuminate(c, dawn); } } else { /* Update the stores once a day (while in the dungeon). The changes are not actually made until return to town, to avoid giving details away in the knowledge menu. */ if (!(turn % (10L * z_info->store_turns))) daycount++; } /* Check for creature generation */ if (one_in_(z_info->alloc_monster_chance)) (void)pick_and_place_distant_monster(cave, player, z_info->max_sight + 5, true, player->depth); /*** Damage over Time ***/ /* Take damage from poison */ if (player->timed[TMD_POISONED]) take_hit(player, 1, "poison"); /* Take damage from cuts */ if (player->timed[TMD_CUT]) { /* Mortal wound or Deep Gash */ if (player->timed[TMD_CUT] > TMD_CUT_SEVERE) i = 3; /* Severe cut */ else if (player->timed[TMD_CUT] > TMD_CUT_NASTY) i = 2; /* Other cuts */ else i = 1; /* Take damage */ take_hit(player, i, "a fatal wound"); } /*** Check the Food, and Regenerate ***/ /* Digest normally */ if (!(turn % 100)) { /* Basic digestion rate based on speed */ i = turn_energy(player->state.speed) * 2; /* Regeneration takes more food */ if (player_of_has(player, OF_REGEN)) i += 30; /* Slow digestion takes less food */ if (player_of_has(player, OF_SLOW_DIGEST)) i /= 5; /* Minimal digestion */ if (i < 1) i = 1; /* Digest some food */ player_set_food(player, player->food - i); } /* Getting Faint */ if (player->food < PY_FOOD_FAINT) { /* Faint occasionally */ if (!player->timed[TMD_PARALYZED] && one_in_(10)) { /* Message */ msg("You faint from the lack of food."); disturb(player, 1); /* Faint (bypass free action) */ (void)player_inc_timed(player, TMD_PARALYZED, 1 + randint0(5), true, false); } } /* Starve to death (slowly) */ if (player->food < PY_FOOD_STARVE) { /* Calculate damage */ i = (PY_FOOD_STARVE - player->food) / 10; /* Take damage */ take_hit(player, i, "starvation"); } /* Regenerate Hit Points if needed */ if (player->chp < player->mhp) player_regen_hp(player); /* Regenerate mana if needed */ if (player->csp < player->msp) player_regen_mana(player); /* Timeout various things */ decrease_timeouts(); /* Process light */ player_update_light(player); /*** Process Inventory ***/ /* Handle experience draining */ if (player_of_has(player, OF_DRAIN_EXP)) { if ((player->exp > 0) && one_in_(10)) { s32b d = damroll(10, 6) + (player->exp / 100) * z_info->life_drain_percent; player_exp_lose(player, d / 10, false); } equip_learn_flag(player, OF_DRAIN_EXP); } /* Recharge activatable objects and rods */ recharge_objects(); /* Notice things after time */ if (!(turn % 100)) equip_learn_after_time(player); /* Decrease trap timeouts */ for (y = 0; y < cave->height; y++) { for (x = 0; x < cave->width; x++) { struct trap *trap = cave->squares[y][x].trap; while (trap) { if (trap->timeout) { trap->timeout--; if (!trap->timeout) square_light_spot(cave, y, x); } trap = trap->next; } } } /*** Involuntary Movement ***/ /* Delayed Word-of-Recall */ if (player->word_recall) { /* Count down towards recall */ player->word_recall--; /* Activate the recall */ if (!player->word_recall) { /* Disturbing! */ disturb(player, 0); /* Determine the level */ if (player->depth) { msgt(MSG_TPLEVEL, "You feel yourself yanked upwards!"); dungeon_change_level(player, 0); } else { msgt(MSG_TPLEVEL, "You feel yourself yanked downwards!"); /* Force descent to a lower level if allowed */ if (OPT(player, birth_force_descend) && player->max_depth < z_info->max_depth - 1 && !is_quest(player->max_depth)) { player->max_depth = dungeon_get_next_level(player->max_depth, 1); } /* New depth - back to max depth or 1, whichever is deeper */ dungeon_change_level(player, player->max_depth < 1 ? 1: player->max_depth); } } } /* Delayed Deep Descent */ if (player->deep_descent) { /* Count down towards recall */ player->deep_descent--; /* Activate the recall */ if (player->deep_descent == 0) { int target_increment; int target_depth = player->max_depth; /* Calculate target depth */ target_increment = (4 / z_info->stair_skip) + 1; target_depth = dungeon_get_next_level(player->max_depth, target_increment); disturb(player, 0); /* Determine the level */ if (target_depth > player->depth) { msgt(MSG_TPLEVEL, "The floor opens beneath you!"); dungeon_change_level(player, target_depth); } else { /* Otherwise do something disastrous */ msgt(MSG_TPLEVEL, "You are thrown back in an explosion!"); effect_simple(EF_DESTRUCTION, "0", 0, 5, 0, NULL); } } } }