/** * 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); } }
/** * Forget the dungeon map (ala "Thinking of Maud..."). */ void wiz_dark(void) { int y, x; /* Forget every grid */ for (y = 0; y < cave->height; y++) { for (x = 0; x < cave->width; x++) { struct object *obj; /* Process the grid */ sqinfo_off(cave->squares[y][x].info, SQUARE_MARK); sqinfo_off(cave->squares[y][x].info, SQUARE_DTRAP); sqinfo_off(cave->squares[y][x].info, SQUARE_DEDGE); /* Forget all objects */ for (obj = square_object(cave, y, x); obj; obj = obj->next) { /* Skip dead objects */ assert(obj->kind); /* Forget the object */ obj->marked = MARK_UNAWARE; } } } /* Fully update the visuals */ player->upkeep->update |= (PU_FORGET_VIEW | PU_UPDATE_VIEW | PU_MONSTERS); /* Redraw map, monster list */ player->upkeep->redraw |= (PR_MAP | PR_MONLIST | PR_ITEMLIST); }
/** * 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); } }
/** * Light or Darken the town */ void cave_illuminate(struct chunk *c, bool daytime) { int y, x, i; /* Apply light or darkness */ for (y = 0; y < c->height; y++) for (x = 0; x < c->width; x++) { int d; bool light = FALSE; feature_type *f_ptr = &f_info[c->squares[y][x].feat]; /* Skip grids with no surrounding floors or stairs */ for (d = 0; d < 9; d++) { /* Extract adjacent (legal) location */ int yy = y + ddy_ddd[d]; int xx = x + ddx_ddd[d]; /* Paranoia */ if (!square_in_bounds_fully(c, yy, xx)) continue; /* Test */ if (square_isfloor(c, yy, xx) || square_isstairs(c, yy, xx)) light = TRUE; } if (!light) continue; /* Only interesting grids at night */ if (daytime || !tf_has(f_ptr->flags, TF_FLOOR)) { sqinfo_on(c->squares[y][x].info, SQUARE_GLOW); sqinfo_on(c->squares[y][x].info, SQUARE_MARK); } else { sqinfo_off(c->squares[y][x].info, SQUARE_GLOW); sqinfo_off(c->squares[y][x].info, SQUARE_MARK); } } /* Light shop doorways */ for (y = 0; y < c->height; y++) { for (x = 0; x < c->width; x++) { if (!square_isshop(c, y, x)) continue; for (i = 0; i < 8; i++) { int yy = y + ddy_ddd[i]; int xx = x + ddx_ddd[i]; sqinfo_on(c->squares[yy][xx].info, SQUARE_GLOW); sqinfo_on(c->squares[yy][xx].info, SQUARE_MARK); } } } /* Fully update the visuals */ player->upkeep->update |= (PU_FORGET_VIEW | PU_UPDATE_VIEW | PU_MONSTERS); /* Redraw map, monster list */ player->upkeep->redraw |= (PR_MAP | PR_MONLIST | PR_ITEMLIST); }
/** * 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); } }
/** * Determine if a trap actually exists in this square. * * Called with vis = 0 to accept any trap, = 1 to accept only visible * traps, and = -1 to accept only invisible traps. * * Clear the SQUARE_TRAP flag if none exist. */ static bool square_verify_trap(struct chunk *c, int y, int x, int vis) { struct trap *trap = square_trap(c, y, x); bool trap_exists = false; /* Scan the square trap list */ while (trap) { /* Accept any trap */ if (!vis) return true; /* Accept traps that match visibility requirements */ if ((vis == 1) && trf_has(trap->flags, TRF_VISIBLE)) return true; if ((vis == -1) && !trf_has(trap->flags, TRF_VISIBLE)) return true; /* Note that a trap does exist */ trap_exists = true; } /* No traps in this location. */ if (!trap_exists) { /* No traps */ sqinfo_off(c->squares[y][x].info, SQUARE_TRAP); /* Take note */ square_note_spot(c, y, x); } /* Report failure */ return false; }
/** * Determine if a trap actually exists in this square. * * Called with vis = 0 to accept any trap, = 1 to accept only visible * traps, and = -1 to accept only invisible traps. * * Clear the SQUARE_TRAP flag if none exist. */ static bool square_verify_trap(struct chunk *c, int y, int x, int vis) { struct trap *trap = c->squares[y][x].trap; bool trap_exists = FALSE; /* Scan the square trap list */ while (trap) { /* Accept any trap */ if (!vis) return TRUE; /* Accept traps that match visibility requirements */ if ((vis == 1) && trf_has(trap->flags, TRF_VISIBLE)) return TRUE; if ((vis == -1) && !trf_has(trap->flags, TRF_VISIBLE)) return TRUE; /* Note that a trap does exist */ trap_exists = TRUE; } /* No traps in this location. */ if (!trap_exists) { /* No traps */ sqinfo_off(c->squares[y][x].info, SQUARE_TRAP); /* No reason to mark this square, ... */ sqinfo_off(c->squares[y][x].info, SQUARE_MARK); /* ... unless certain conditions apply */ square_note_spot(c, y, x); } /* Report failure */ return FALSE; }
/* Darken the grid */ static void project_feature_handler_DARK_WEAK(project_feature_handler_context_t *context) { const int x = context->x; const int y = context->y; if (player->depth != 0 || !is_daytime()) /* Turn off the light */ sqinfo_off(cave->squares[y][x].info, SQUARE_GLOW); /* Grid is in line of sight */ if (square_isview(cave, y, x)) { /* Observe */ context->obvious = true; /* Fully update the visuals */ player->upkeep->update |= (PU_FORGET_VIEW | PU_UPDATE_VIEW | PU_MONSTERS); } }
/** * Try to break a glyph. */ static bool process_monster_glyph(struct chunk *c, struct monster *m_ptr, int nx, int ny) { assert(square_iswarded(c, ny, nx)); /* Break the ward */ if (randint1(z_info->glyph_hardness) < m_ptr->race->level) { /* Describe observable breakage */ if (square_ismark(c, ny, nx)) msg("The rune of protection is broken!"); /* Forget the rune */ sqinfo_off(c->squares[ny][nx].info, SQUARE_MARK); /* Break the rune */ square_remove_ward(c, ny, nx); return TRUE; } /* Unbroken ward - can't move */ return FALSE; }
/** * Tunnel through wall. Assumes valid location. * * Note that it is impossible to "extend" rooms past their * outer walls (which are actually part of the room). * * Attempting to do so will produce floor grids which are not part * of the room, and whose "illumination" status do not change with * the rest of the room. */ static bool twall(int y, int x) { /* Paranoia -- Require a wall or door or some such */ if (!(square_isdiggable(cave, y, x) || square_iscloseddoor(cave, y, x))) return (FALSE); /* Sound */ sound(MSG_DIG); /* Forget the wall */ sqinfo_off(cave->squares[y][x].info, SQUARE_MARK); /* Remove the feature */ square_tunnel_wall(cave, y, x); /* Update the visuals */ player->upkeep->update |= (PU_UPDATE_VIEW | PU_MONSTERS); /* Fully update the flow */ player->upkeep->update |= (PU_FORGET_FLOW | PU_UPDATE_FLOW); /* Result */ return (TRUE); }
/** * Generate a random level. * * Confusingly, this function also generate the town level (level 0). * \param c is the level we're going to end up with, in practice the global cave * \param p is the current player struct, in practice the global player */ void cave_generate(struct chunk **c, struct player *p) { const char *error = "no generation"; int y, x, tries = 0; struct chunk *chunk; assert(c); /* Generate */ for (tries = 0; tries < 100 && error; tries++) { struct dun_data dun_body; error = NULL; /* Mark the dungeon as being unready (to avoid artifact loss, etc) */ character_dungeon = FALSE; /* Allocate global data (will be freed when we leave the loop) */ dun = &dun_body; dun->cent = mem_zalloc(z_info->level_room_max * sizeof(struct loc)); dun->door = mem_zalloc(z_info->level_door_max * sizeof(struct loc)); dun->wall = mem_zalloc(z_info->wall_pierce_max * sizeof(struct loc)); dun->tunn = mem_zalloc(z_info->tunn_grid_max * sizeof(struct loc)); /* Choose a profile and build the level */ dun->profile = choose_profile(p->depth); chunk = dun->profile->builder(p); if (!chunk) { error = "Failed to find builder"; mem_free(dun->cent); mem_free(dun->door); mem_free(dun->wall); mem_free(dun->tunn); continue; } /* Ensure quest monsters */ if (is_quest(chunk->depth)) { int i; for (i = 1; i < z_info->r_max; i++) { monster_race *r_ptr = &r_info[i]; int y, x; /* The monster must be an unseen quest monster of this depth. */ if (r_ptr->cur_num > 0) continue; if (!rf_has(r_ptr->flags, RF_QUESTOR)) continue; if (r_ptr->level != chunk->depth) continue; /* Pick a location and place the monster */ find_empty(chunk, &y, &x); place_new_monster(chunk, y, x, r_ptr, TRUE, TRUE, ORIGIN_DROP); } } /* Clear generation flags. */ for (y = 0; y < chunk->height; y++) { for (x = 0; x < chunk->width; x++) { sqinfo_off(chunk->squares[y][x].info, SQUARE_WALL_INNER); sqinfo_off(chunk->squares[y][x].info, SQUARE_WALL_OUTER); sqinfo_off(chunk->squares[y][x].info, SQUARE_WALL_SOLID); sqinfo_off(chunk->squares[y][x].info, SQUARE_MON_RESTRICT); } } /* Regenerate levels that overflow their maxima */ if (cave_monster_max(chunk) >= z_info->level_monster_max) error = "too many monsters"; if (error) ROOM_LOG("Generation restarted: %s.", error); mem_free(dun->cent); mem_free(dun->door); mem_free(dun->wall); mem_free(dun->tunn); } if (error) quit_fmt("cave_generate() failed 100 times!"); /* Free the old cave, use the new one */ if (*c) cave_free(*c); *c = chunk; /* Place dungeon squares to trigger feeling (not in town) */ if (player->depth) place_feeling(*c); /* Save the town */ else if (!chunk_find_name("Town")) { struct chunk *town = chunk_write(0, 0, z_info->town_hgt, z_info->town_wid, FALSE, FALSE, FALSE); town->name = string_make("Town"); chunk_list_add(town); } (*c)->feeling = calc_obj_feeling(*c) + calc_mon_feeling(*c); /* Validate the dungeon (we could use more checks here) */ chunk_validate_objects(*c); /* The dungeon is ready */ character_dungeon = TRUE; /* Free old and allocate new known level */ if (cave_k) cave_free(cave_k); cave_k = cave_new(cave->height, cave->width); if (!cave->depth) cave_known(); (*c)->created_at = turn; }
/** * Work out if a monster can move through the grid, if necessary bashing * down doors in the way. * * Returns TRUE if the monster is able to move through the grid. */ static bool process_monster_can_move(struct chunk *c, struct monster *m_ptr, const char *m_name, int nx, int ny, bool *did_something) { monster_lore *l_ptr = get_lore(m_ptr->race); /* Floor is open? */ if (square_ispassable(c, ny, nx)) return TRUE; /* Permanent wall in the way */ if (square_iswall(c, ny, nx) && square_isperm(c, ny, nx)) return FALSE; /* Normal wall, door, or secret door in the way */ /* There's some kind of feature in the way, so learn about * kill-wall and pass-wall now */ if (mflag_has(m_ptr->mflag, MFLAG_VISIBLE)) { rf_on(l_ptr->flags, RF_PASS_WALL); rf_on(l_ptr->flags, RF_KILL_WALL); } /* Monster moves through walls (and doors) */ if (rf_has(m_ptr->race->flags, RF_PASS_WALL)) return TRUE; /* Monster destroys walls (and doors) */ else if (rf_has(m_ptr->race->flags, RF_KILL_WALL)) { /* Forget the wall */ sqinfo_off(c->squares[ny][nx].info, SQUARE_MARK); /* Notice */ square_destroy_wall(c, ny, nx); /* Note changes to viewable region */ if (player_has_los_bold(ny, nx)) player->upkeep->update |= PU_UPDATE_VIEW; /* Update the flow, since walls affect flow */ player->upkeep->update |= PU_UPDATE_FLOW; return TRUE; } /* Handle doors and secret doors */ else if (square_iscloseddoor(c, ny, nx) || square_issecretdoor(c, ny, nx)) { bool may_bash = rf_has(m_ptr->race->flags, RF_BASH_DOOR) && one_in_(2); /* Take a turn */ *did_something = TRUE; /* Learn about door abilities */ if (mflag_has(m_ptr->mflag, MFLAG_VISIBLE)) { rf_on(l_ptr->flags, RF_OPEN_DOOR); rf_on(l_ptr->flags, RF_BASH_DOOR); } /* Creature can open or bash doors */ if (!rf_has(m_ptr->race->flags, RF_OPEN_DOOR) && !rf_has(m_ptr->race->flags, RF_BASH_DOOR)) return FALSE; /* Stuck door -- try to unlock it */ if (square_islockeddoor(c, ny, nx)) { int k = square_door_power(c, ny, nx); if (randint0(m_ptr->hp / 10) > k) { if (may_bash) msg("%s slams against the door.", m_name); else msg("%s fiddles with the lock.", m_name); /* Reduce the power of the door by one */ square_set_door_lock(c, ny, nx, k - 1); } } else { /* Handle viewable doors */ if (player_has_los_bold(ny, nx)) player->upkeep->update |= PU_UPDATE_VIEW; /* Closed or secret door -- open or bash if allowed */ if (may_bash) { square_smash_door(c, ny, nx); msg("You hear a door burst open!"); disturb(player, 0); /* Fall into doorway */ return TRUE; } else if (rf_has(m_ptr->race->flags, RF_OPEN_DOOR)) { square_open_door(c, ny, nx); } } } return FALSE; }
void square_unmark(struct chunk *c, int y, int x) { sqinfo_off(c->squares[y][x].info, SQUARE_MARK); }
/** * Perform the basic "disarm" command * * Assume there is no monster blocking the destination * * Returns TRUE if repeated commands may continue */ static bool do_cmd_disarm_aux(int y, int x) { int i, j, power; struct trap *trap = cave->squares[y][x].trap; bool more = FALSE; /* Verify legality */ if (!do_cmd_disarm_test(y, x)) return (FALSE); /* Choose first player trap */ while (trap) { if (trf_has(trap->flags, TRF_TRAP)) break; trap = trap->next; } if (!trap) return 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; /* XXX XXX XXX Variable power? */ /* Extract trap "power" */ power = 5; /* Extract the difficulty */ j = i - power; /* Always have a small chance of success */ if (j < 2) j = 2; /* Success */ if (randint0(100) < j) { /* Message */ msgt(MSG_DISARM, "You have disarmed the %s.", trap->kind->name); /* Reward */ player_exp_gain(player, power); /* Forget the trap */ sqinfo_off(cave->squares[y][x].info, SQUARE_MARK); /* Remove the trap */ square_destroy_trap(cave, y, x); } else if ((i > 5) && (randint1(i) > 5)) { /* Failure -- Keep trying */ event_signal(EVENT_INPUT_FLUSH); msg("You failed to disarm the %s.", trap->kind->name); more = TRUE; } else { /* Failure -- Set off the trap */ msg("You set off the %s!", trap->kind->name); hit_trap(y, x); } /* Result */ return (more); }