/** * \returns whether the object's pval is known to the player */ bool object_pval_is_visible(const object_type *o_ptr) { bitflag f[OF_SIZE]; if (o_ptr->ident & IDENT_STORE) return TRUE; /* Aware jewelry with non-variable pvals */ if (object_is_jewelry(o_ptr) && object_flavor_is_aware(o_ptr)) { const object_kind *k_ptr = &k_info[o_ptr->k_idx]; if (!randcalc_varies(k_ptr->pval)) return TRUE; } if (object_was_worn(o_ptr)) { object_flags_known(o_ptr, f); if (flags_test(f, OF_SIZE, OF_PVAL_MASK, FLAG_END)) return TRUE; } return FALSE; }
static size_t obj_desc_inscrip(const object_type *o_ptr, char *buf, size_t max, size_t end) { const char *u[4] = { 0, 0, 0, 0 }; int n = 0; int feel = object_pseudo(o_ptr); bitflag flags_known[OF_SIZE]; object_flags_known(o_ptr, flags_known); /* Get inscription */ if (o_ptr->note) u[n++] = quark_str(o_ptr->note); /* Use special inscription, if any */ if (!object_is_known(o_ptr) && feel) { /* cannot tell excellent vs strange vs splendid until wield */ if (!object_was_worn(o_ptr) && ego_item_p(o_ptr)) u[n++] = "ego"; else u[n++] = inscrip_text[feel]; } else if ((o_ptr->ident & IDENT_EMPTY) && !object_is_known(o_ptr)) u[n++] = "empty"; else if (!object_is_known(o_ptr) && object_was_worn(o_ptr)) { if (wield_slot(o_ptr) == INVEN_WIELD || wield_slot(o_ptr) == INVEN_BOW) u[n++] = "wielded"; else u[n++] = "worn"; } else if (!object_is_known(o_ptr) && object_was_fired(o_ptr)) u[n++] = "fired"; else if (!object_flavor_is_aware(o_ptr) && object_flavor_was_tried(o_ptr)) u[n++] = "tried"; /* Note curses */ if (flags_test(flags_known, OF_SIZE, OF_CURSE_MASK, FLAG_END)) u[n++] = "cursed"; /* Note squelch */ if (squelch_item_ok(o_ptr)) u[n++] = "squelch"; if (n) { int i; for (i = 0; i < n; i++) { if (i == 0) strnfcat(buf, max, &end, " {"); strnfcat(buf, max, &end, "%s", u[i]); if (i < n-1) strnfcat(buf, max, &end, ", "); } strnfcat(buf, max, &end, "}"); } return end; }
/* * Choose the best direction for "flowing". * * Note that ghosts and rock-eaters generally don't flow because they can move * through obstacles. * * Monsters first try to use up-to-date distance information ('sound') as * saved in cave->squares[y][x].cost. Failing that, they'll try using scent * ('when') which is just old cost information. * * Tracking by 'scent' means that monsters end up near enough the player to * switch to 'sound' (cost), or they end up somewhere the player left via * teleport. Teleporting away from a location will cause the monsters who * were chasing the player to converge on that location as long as the player * is still near enough to "annoy" them without being close enough to chase * directly. */ static bool get_moves_flow(struct chunk *c, struct monster *m_ptr) { int i; int best_when = 0; int best_cost = 999; int best_direction = 0; int py = player->py, px = player->px; int my = m_ptr->fy, mx = m_ptr->fx; /* Only use this algorithm for passwall monsters if near permanent walls, * to avoid getting snagged */ if (flags_test(m_ptr->race->flags, RF_SIZE, RF_PASS_WALL, RF_KILL_WALL, FLAG_END) && !near_permwall(m_ptr, c)) return (FALSE); /* If the player has never been near this grid, abort */ if (c->squares[my][mx].when == 0) return FALSE; /* Monster is too far away to notice the player */ if (c->squares[my][mx].cost > z_info->max_flow_depth) return FALSE; if (c->squares[my][mx].cost > m_ptr->race->aaf) return FALSE; /* If the player can see monster, run towards them */ if (player_has_los_bold(my, mx)) return FALSE; /* Check nearby grids, diagonals first */ /* This gives preference to the cardinal directions */ for (i = 7; i >= 0; i--) { /* Get the location */ int y = my + ddy_ddd[i]; int x = mx + ddx_ddd[i]; /* Ignore unvisited/unpassable locations */ if (c->squares[y][x].when == 0) continue; /* Ignore locations whose data is more stale */ if (c->squares[y][x].when < best_when) continue; /* Ignore locations which are farther away */ if (c->squares[y][x].cost > best_cost) continue; /* Save the cost and time */ best_when = c->squares[y][x].when; best_cost = c->squares[y][x].cost; best_direction = i; } /* Save the location to flow toward */ /* We multiply by 16 to angle slightly toward the player's actual location */ if (best_direction) { m_ptr->ty = py + 16 * ddy_ddd[best_direction]; m_ptr->tx = px + 16 * ddx_ddd[best_direction]; return TRUE; } return FALSE; }
static size_t obj_desc_pval(const object_type *o_ptr, char *buf, size_t max, size_t end) { bitflag f[OF_SIZE]; object_flags(o_ptr, f); if (!flags_test(f, OF_SIZE, OF_PVAL_MASK, FLAG_END)) return end; strnfcat(buf, max, &end, " (%+d", o_ptr->pval); if (!of_has(f, OF_HIDE_TYPE)) { if (of_has(f, OF_STEALTH)) strnfcat(buf, max, &end, " stealth"); else if (of_has(f, OF_SEARCH)) strnfcat(buf, max, &end, " searching"); else if (of_has(f, OF_INFRA)) strnfcat(buf, max, &end, " infravision"); else if (of_has(f, OF_SPEED)) strnfcat(buf, max, &end, " speed"); else if (of_has(f, OF_BLOWS)) strnfcat(buf, max, &end, " attack%s", PLURAL(o_ptr->pval)); } strnfcat(buf, max, &end, ")"); return end; }
void search_cycle(CA *ca, char *graph, UINT starting_state) { UINT actual_state = starting_state; int is_new_cycle = 0; while (!flags_test(graph, actual_state, STATE_HAS_BEEN_VISITED)) { flags_set(graph, actual_state, STATE_HAS_BEEN_VISITED | STATE_IS_BEING_VISITED); actual_state = next_state(ca, actual_state); } is_new_cycle = flags_test(graph, actual_state, STATE_IS_BEING_VISITED); if (is_new_cycle) { UINT cycle_len = 0; while (flags_test(graph, actual_state, STATE_IS_BEING_VISITED)) { flags_unset(graph, actual_state, STATE_IS_BEING_VISITED); actual_state = next_state(ca, actual_state); cycle_len ++; } /* printf("len %u seed %u start %u ", cycle_len, actual_state, starting_state); */ printf("%" UFM "\n", cycle_len); fflush(stdout); } for (actual_state = starting_state; flags_test(graph, actual_state, STATE_IS_BEING_VISITED); actual_state = next_state(ca, actual_state)) { flags_unset(graph, actual_state, STATE_IS_BEING_VISITED); } }
/* * \returns whether it is possible an object has a high resist given the * player's current knowledge */ bool object_high_resist_is_possible(const object_type *o_ptr) { bitflag flags[OF_SIZE]; /* Actual object flags */ object_flags(o_ptr, flags); /* Add player's uncertainty */ of_comp_union(flags, o_ptr->known_flags); /* Check for possible high resist */ if (flags_test(flags, OF_SIZE, OF_HIGH_RESIST_MASK, FLAG_END)) return TRUE; else return FALSE; }
int main(int argc, char *argv[]) { UINT i; unsigned int N; UINT NSTATES; unsigned int rule; CA *ca; char *graph; if (argc != 3) { fprintf(stderr, "usage: %s rule ca-size\n", PROGRAM_NAME); exit(EXIT_FAILURE); } sscanf(argv[1], "%u", &rule); sscanf(argv[2], "%u", &N); if (DEBUG) fprintf(stderr, "rule %u\nN=%u\n", rule, N); ca = ca_create(N, rule); NSTATES = 2; NSTATES <<= (N - 1); graph = create_graph(NSTATES); for (i = 0; i < NSTATES; ++i) flags_unset(graph, i, STATE_ALL_SET_MASK); for (i = 0; i < NSTATES; ++i) if (!flags_test(graph, i, STATE_HAS_BEEN_VISITED)) search_cycle(ca, graph, i); free_graph(graph); ca_destroy(ca); return EXIT_SUCCESS; }
/** * Calculate minimum and desired combat ranges. -BR- */ static void find_range(struct monster *mon) { u16b p_lev, m_lev; u16b p_chp, p_mhp; u16b m_chp, m_mhp; u32b p_val, m_val; /* Monsters will run up to z_info->flee_range grids out of sight */ int flee_range = z_info->max_sight + z_info->flee_range; /* All "afraid" monsters will run away */ if (mon->m_timed[MON_TMD_FEAR]) mon->min_range = flee_range; else { /* Minimum distance - stay at least this far if possible */ mon->min_range = 1; /* Examine player power (level) */ p_lev = player->lev; /* Hack - increase p_lev based on specialty abilities */ /* Examine monster power (level plus morale) */ m_lev = mon->race->level + (mon->midx & 0x08) + 25; /* Simple cases first */ if (m_lev + 3 < p_lev) mon->min_range = flee_range; else if (m_lev - 5 < p_lev) { /* Examine player health */ p_chp = player->chp; p_mhp = player->mhp; /* Examine monster health */ m_chp = mon->hp; m_mhp = mon->maxhp; /* Prepare to optimize the calculation */ p_val = (p_lev * p_mhp) + (p_chp << 2); /* div p_mhp */ m_val = (m_lev * m_mhp) + (m_chp << 2); /* div m_mhp */ /* Strong players scare strong monsters */ if (p_val * m_mhp > m_val * p_mhp) mon->min_range = flee_range; } } if (mon->min_range < flee_range) { /* Creatures that don't move never like to get too close */ if (rf_has(mon->race->flags, RF_NEVER_MOVE)) mon->min_range += 3; /* Spellcasters that don't strike never like to get too close */ if (rf_has(mon->race->flags, RF_NEVER_BLOW)) mon->min_range += 3; } /* Maximum range to flee to */ if (!(mon->min_range < flee_range)) mon->min_range = flee_range; /* Nearby monsters won't run away */ else if (mon->cdis < z_info->turn_range) mon->min_range = 1; /* Now find preferred range */ mon->best_range = mon->min_range; /* Archers are quite happy at a good distance */ //if (rf_has(mon->race->flags, RF_ARCHER)) // mon->best_range += 3; if (mon->race->freq_spell > 24) { /* Breathers like point blank range */ if (flags_test(mon->race->spell_flags, RSF_SIZE, RSF_BREATH_MASK, FLAG_END) && (mon->best_range < 6) && (mon->hp > mon->maxhp / 2)) mon->best_range = 6; /* Other spell casters will sit back and cast */ else mon->best_range += 3; } }
/** * Choose "logical" directions for monster movement */ static bool get_moves(struct chunk *c, struct monster *mon, int *dir) { int py = player->py; int px = player->px; int y, x; /* Monsters will run up to z_info->flee_range grids out of sight */ int flee_range = z_info->max_sight + z_info->flee_range; bool done = false; /* Calculate range */ find_range(mon); /* Flow towards the player */ if (get_moves_flow(c, mon)) { /* Extract the "pseudo-direction" */ y = mon->ty - mon->fy; x = mon->tx - mon->fx; } else { /* Head straight for the player */ y = player->py - mon->fy; x = player->px - mon->fx; } /* Normal animal packs try to get the player out of corridors. */ if (rf_has(mon->race->flags, RF_GROUP_AI) && !flags_test(mon->race->flags, RF_SIZE, RF_PASS_WALL, RF_KILL_WALL, FLAG_END)) { int i, open = 0; /* Count empty grids next to player */ for (i = 0; i < 8; i++) { int ry = py + ddy_ddd[i]; int rx = px + ddx_ddd[i]; /* Check grid around the player for room interior (room walls count) * or other empty space */ if (square_ispassable(c, ry, rx) || square_isroom(c, ry, rx)) { /* One more open grid */ open++; } } /* Not in an empty space and strong player */ if ((open < 7) && (player->chp > player->mhp / 2)) { /* Find hiding place */ if (find_hiding(c, mon)) { done = true; y = mon->ty - mon->fy; x = mon->tx - mon->fx; } } } /* Apply fear */ if (!done && (mon->min_range == flee_range)) { /* Try to find safe place */ if (!find_safety(c, mon)) { /* Just leg it away from the player */ y = (-y); x = (-x); } else { /* Set a course for the safe place */ get_moves_fear(c, mon); y = mon->ty - mon->fy; x = mon->tx - mon->fx; } done = true; } /* Monster groups try to surround the player */ if (!done && rf_has(mon->race->flags, RF_GROUP_AI)) { int i, yy = mon->ty, xx = mon->tx; /* If we are not already adjacent */ if (mon->cdis > 1) { /* Find an empty square near the player to fill */ int tmp = randint0(8); for (i = 0; i < 8; i++) { /* Pick squares near player (pseudo-randomly) */ yy = py + ddy_ddd[(tmp + i) & 7]; xx = px + ddx_ddd[(tmp + i) & 7]; /* Ignore filled grids */ if (!square_isempty(cave, yy, xx)) continue; /* Try to fill this hole */ break; } } /* Extract the new "pseudo-direction" */ y = yy - mon->fy; x = xx - mon->fx; } /* Check for no move */ if (!x && !y) return (false); /* Pick the correct direction */ *dir = choose_direction(y, x); /* Want to move */ return (true); }
/** * Choose the best direction for "flowing". * * Note that ghosts and rock-eaters generally don't flow because they can move * through obstacles. * * Monsters first try to use up-to-date distance information ('sound') as * saved in cave->squares[y][x].noise. Failing that, they'll try using scent * ('scent') which is just old noise information. * * Tracking by 'scent' means that monsters end up near enough the player to * switch to 'sound' (noise), or they end up somewhere the player left via * teleport. Teleporting away from a location will cause the monsters who * were chasing the player to converge on that location as long as the player * is still near enough to "annoy" them without being close enough to chase * directly. */ static bool get_moves_flow(struct chunk *c, struct monster *mon) { int i; int best_scent = 0; int best_noise = 999; int best_direction = 0; bool found_direction = false; int py = player->py, px = player->px; int my = mon->fy, mx = mon->fx; /* Only use this algorithm for passwall monsters if near permanent walls, * to avoid getting snagged */ if (flags_test(mon->race->flags, RF_SIZE, RF_PASS_WALL, RF_KILL_WALL, FLAG_END) && !near_permwall(mon, c)) return (false); /* The player is not currently near the monster grid */ if (c->squares[my][mx].scent < c->squares[py][px].scent) /* If the player has never been near this grid, abort */ if (c->squares[my][mx].scent == 0) return false; /* Monster is too far away to notice the player */ if (c->squares[my][mx].noise > z_info->max_flow_depth) return false; if (c->squares[my][mx].noise > mon->race->aaf) return false; /* If the player can see monster, set target and run towards them */ if (square_isview(c, my, mx)) { mon->ty = player->py; mon->tx = player->px; return false; } /* Check nearby grids, diagonals first */ /* This gives preference to the cardinal directions */ for (i = 7; i >= 0; i--) { /* Get the location */ int y = my + ddy_ddd[i]; int x = mx + ddx_ddd[i]; /* Bounds check */ if (!square_in_bounds(c, y, x)) continue; /* Ignore unvisited/unpassable locations */ if (c->squares[y][x].scent == 0) continue; /* Ignore locations whose data is more stale */ if (c->squares[y][x].scent < best_scent) continue; /* Ignore locations which are farther away */ if (c->squares[y][x].noise > best_noise) continue; /* Ignore lava if they can't handle the heat */ if (square_isfiery(c, y, x) && !rf_has(mon->race->flags, RF_IM_FIRE)) continue; /* Save the noise and time */ best_scent = c->squares[y][x].scent; best_noise = c->squares[y][x].noise; best_direction = i; found_direction = true; } /* Save the location to flow toward */ /* Multiply by 16 to angle slightly toward the player's actual location */ if (found_direction) { int dy = 0, dx = 0; /* Ridiculous - actually multiply by whatever doesn't underflow the * byte for ty and tx. Really should do a better solution - NRM */ for (i = 0; i < 16; i++) if ((py + dy > 0) && (px + dx > 0)) { dy += ddy_ddd[best_direction]; dx += ddx_ddd[best_direction]; } mon->ty = py + dy; mon->tx = px + dx; return true; } return false; }
/** * This function takes a pointer to a grid info struct describing the * contents of a grid location (as obtained through the function map_info) * and fills in the character and attr pairs for display. * * ap and cp are filled with the attr/char pair for the monster, object or * floor tile that is at the "top" of the grid (monsters covering objects, * which cover floor, assuming all are present). * * tap and tcp are filled with the attr/char pair for the floor, regardless * of what is on it. This can be used by graphical displays with * transparency to place an object onto a floor tile, is desired. * * Any lighting effects are also applied to these pairs, clear monsters allow * the underlying colour or feature to show through (ATTR_CLEAR and * CHAR_CLEAR), multi-hued colour-changing (ATTR_MULTI) is applied, and so on. * Technically, the flag "CHAR_MULTI" is supposed to indicate that a monster * looks strange when examined, but this flag is currently ignored. * * NOTES: * This is called pretty frequently, whenever a grid on the map display * needs updating, so don't overcomplicate it. * * The "zero" entry in the feature/object/monster arrays are * used to provide "special" attr/char codes, with "monster zero" being * used for the player attr/char, "object zero" being used for the "pile" * attr/char, and "feature zero" being used for the "darkness" attr/char. * * TODO: * The transformations for tile colors, or brightness for the 16x16 * tiles should be handled differently. One possibility would be to * extend feature_type with attr/char definitions for the different states. * This will probably be done outside of the current text->graphics mappings * though. */ void grid_data_as_text(struct grid_data *g, int *ap, wchar_t *cp, int *tap, wchar_t *tcp) { struct feature *feat = &f_info[g->f_idx]; int a = feat_x_attr[g->lighting][feat->fidx]; wchar_t c = feat_x_char[g->lighting][feat->fidx]; bool skip_objects = false; /* Get the colour for ASCII */ if (use_graphics == GRAPHICS_NONE) grid_get_attr(g, &a); /* Save the terrain info for the transparency effects */ (*tap) = a; (*tcp) = c; /* There is a trap in this grid, and we are not hallucinating */ if (g->trap && (!g->hallucinate)) { /* Change graphics to indicate visible traps, skip objects if a web */ skip_objects = get_trap_graphics(cave, g, &a, &c); } if (!skip_objects) { /* If there's an object, deal with that. */ if (g->unseen_money) { /* $$$ gets an orange star*/ a = object_kind_attr(unknown_gold_kind); c = object_kind_char(unknown_gold_kind); } else if (g->unseen_object) { /* Everything else gets a red star */ a = object_kind_attr(unknown_item_kind); c = object_kind_char(unknown_item_kind); } else if (g->first_kind) { if (g->hallucinate) { /* Just pick a random object to display. */ hallucinatory_object(&a, &c); } else if (g->multiple_objects) { /* Get the "pile" feature instead */ a = object_kind_attr(pile_kind); c = object_kind_char(pile_kind); } else { /* Normal attr and char */ a = object_kind_attr(g->first_kind); c = object_kind_char(g->first_kind); } } } /* Handle monsters, the player and trap borders */ if (g->m_idx > 0) { if (g->hallucinate) { /* Just pick a random monster to display. */ hallucinatory_monster(&a, &c); } else if (!monster_is_mimicking(cave_monster(cave, g->m_idx))) { struct monster *mon = cave_monster(cave, g->m_idx); byte da; wchar_t dc; /* Desired attr & char */ da = monster_x_attr[mon->race->ridx]; dc = monster_x_char[mon->race->ridx]; /* Special handling of attrs and/or chars */ if (da & 0x80) { /* Special attr/char codes */ a = da; c = dc; } else if (OPT(player, purple_uniques) && rf_has(mon->race->flags, RF_UNIQUE)) { /* Turn uniques purple if desired (violet, actually) */ a = COLOUR_VIOLET; c = dc; } else if (rf_has(mon->race->flags, RF_ATTR_MULTI) || rf_has(mon->race->flags, RF_ATTR_FLICKER) || rf_has(mon->race->flags, RF_ATTR_RAND)) { /* Multi-hued monster */ a = mon->attr ? mon->attr : da; c = dc; } else if (!flags_test(mon->race->flags, RF_SIZE, RF_ATTR_CLEAR, RF_CHAR_CLEAR, FLAG_END)) { /* Normal monster (not "clear" in any way) */ a = da; /* Desired attr & char. da is not used, should a be set to it?*/ /*da = monster_x_attr[mon->race->ridx];*/ dc = monster_x_char[mon->race->ridx]; c = dc; } else if (a & 0x80) { /* Hack -- Bizarre grid under monster */ a = da; c = dc; } else if (!rf_has(mon->race->flags, RF_CHAR_CLEAR)) { /* Normal char, Clear attr, monster */ c = dc; } else if (!rf_has(mon->race->flags, RF_ATTR_CLEAR)) { /* Normal attr, Clear char, monster */ a = da; } /* Store the drawing attr so we can use it elsewhere */ mon->attr = a; } } else if (g->is_player) { struct monster_race *race = &r_info[0]; /* Get the "player" attr */ a = monster_x_attr[race->ridx]; if ((OPT(player, hp_changes_color)) && !(a & 0x80)) { switch(player->chp * 10 / player->mhp) { case 10: case 9: { a = COLOUR_WHITE; break; } case 8: case 7: { a = COLOUR_YELLOW; break; } case 6: case 5: { a = COLOUR_ORANGE; break; } case 4: case 3: { a = COLOUR_L_RED; break; } case 2: case 1: case 0: { a = COLOUR_RED; break; } default: { a = COLOUR_WHITE; break; } } } /* Get the "player" char */ c = monster_x_char[race->ridx]; } /* Result */ (*ap) = a; (*cp) = c; }
/* * Given an object, return a short identifier which gives some idea of what * the item is. */ obj_pseudo_t object_pseudo(const object_type *o_ptr) { object_kind *k_ptr = &k_info[o_ptr->k_idx]; bitflag flags[OF_SIZE]; /* Get the known and obvious flags on the object, * not including curses or properties of the kind */ object_flags_known(o_ptr, flags); /* MEGA-hack : there needs to be a table of what is obvious in each slot perhaps for each class */ /* FA on gloves is obvious to mage casters */ if (object_FA_would_be_obvious(o_ptr)) flags_mask(flags, OF_SIZE, OF_OBVIOUS_MASK, OF_FREE_ACT, FLAG_END); else flags_mask(flags, OF_SIZE, OF_OBVIOUS_MASK, FLAG_END); flags_clear(flags, OF_SIZE, OF_CURSE_MASK, FLAG_END); of_diff(flags, k_ptr->flags); if (o_ptr->ident & IDENT_INDESTRUCT) return INSCRIP_SPECIAL; if ((object_was_sensed(o_ptr) || object_was_worn(o_ptr)) && artifact_p(o_ptr)) return INSCRIP_SPECIAL; /* jewelry does not pseudo */ if (object_is_jewelry(o_ptr)) return INSCRIP_NULL; /* XXX Eddie should also check for flags with pvals where the pval exceeds * the base pval for things like picks of digging, though for now acid brand gets those */ if (!of_is_empty(flags)) return INSCRIP_SPLENDID; if (!object_is_known(o_ptr) && !object_was_sensed(o_ptr)) return INSCRIP_NULL; if (ego_item_p(o_ptr)) { /* uncursed bad egos are not excellent */ if (flags_test(e_info[o_ptr->name2].flags, OF_SIZE, OF_CURSE_MASK, FLAG_END)) return INSCRIP_STRANGE; /* XXX Eddie need something worse */ else return INSCRIP_EXCELLENT; } if (o_ptr->to_a == randcalc(k_ptr->to_a, 0, MINIMISE) && o_ptr->to_h == randcalc(k_ptr->to_h, 0, MINIMISE) && o_ptr->to_d == randcalc(k_ptr->to_d, 0, MINIMISE)) return INSCRIP_AVERAGE; if (o_ptr->to_a >= randcalc(k_ptr->to_a, 0, MINIMISE) && o_ptr->to_h >= randcalc(k_ptr->to_h, 0, MINIMISE) && o_ptr->to_d >= randcalc(k_ptr->to_d, 0, MINIMISE)) return INSCRIP_MAGICAL; if (o_ptr->to_a <= randcalc(k_ptr->to_a, 0, MINIMISE) && o_ptr->to_h <= randcalc(k_ptr->to_h, 0, MINIMISE) && o_ptr->to_d <= randcalc(k_ptr->to_d, 0, MINIMISE)) return INSCRIP_MAGICAL; return INSCRIP_STRANGE; }
/** * Evaluate the whole monster list and write a new one. power and scaled_power * are always adjusted, level, rarity and mexp only if requested. */ errr eval_monster_power(struct monster_race *racelist) { int i, j, iteration; byte lvl; struct monster_race *race = NULL; ang_file *mon_fp; char buf[1024]; bool dump = FALSE; bool wrote = TRUE; /* Allocate arrays */ power = mem_zalloc(z_info->r_max * sizeof(long)); scaled_power = mem_zalloc(z_info->r_max * sizeof(long)); final_hp = mem_zalloc(z_info->r_max * sizeof(long)); final_melee_dam = mem_zalloc(z_info->r_max * sizeof(long)); final_spell_dam = mem_zalloc(z_info->r_max * sizeof(long)); highest_threat = mem_zalloc(z_info->r_max * sizeof(int)); for (iteration = 0; iteration < 3; iteration ++) { long hp, av_hp, dam, av_dam; long *tot_hp = mem_zalloc(z_info->max_depth * sizeof(long)); long *tot_dam = mem_zalloc(z_info->max_depth * sizeof(long)); long *mon_count = mem_zalloc(z_info->max_depth * sizeof(long)); /* Reset the sum of all monster power values */ tot_mon_power = 0; /* Go through r_info and evaluate power ratings & flows. */ for (i = 0; i < z_info->r_max; i++) { /* Point at the "info" */ race = &racelist[i]; /* Set the current level */ lvl = race->level; /* Maximum damage this monster can do in 10 game turns */ dam = eval_max_dam(race, i); /* Adjust hit points based on resistances */ hp = eval_hp_adjust(race); /* Hack -- set exp */ if (lvl == 0) race->mexp = 0L; else { /* Compute depths of non-unique monsters */ if (!rf_has(race->flags, RF_UNIQUE)) { long mexp = (hp * dam) / 25; long threat = highest_threat[i]; /* Compute level algorithmically */ for (j = 1; (mexp > j + 4) || (threat > j + 5); mexp -= j * j, threat -= (j + 4), j++); /* Set level */ lvl = MIN(( j > 250 ? 90 + (j - 250) / 20 : /* Level 90+ */ (j > 130 ? 70 + (j - 130) / 6 : /* Level 70+ */ (j > 40 ? 40 + (j - 40) / 3 : /* Level 40+ */ j))), 99); /* Set level */ if (arg_rebalance) race->level = lvl; } if (arg_rebalance) { /* Hack -- for Ungoliant */ if (hp > 10000) race->mexp = (hp / 25) * (dam / lvl); else race->mexp = (hp * dam) / (lvl * 25); /* Round to 2 significant figures */ if (race->mexp > 100) { if (race->mexp < 1000) { race->mexp = (race->mexp + 5) / 10; race->mexp *= 10; } else if (race->mexp < 10000) { race->mexp = (race->mexp + 50) / 100; race->mexp *= 100; } else if (race->mexp < 100000) { race->mexp = (race->mexp + 500) / 1000; race->mexp *= 1000; } else if (race->mexp < 1000000) { race->mexp = (race->mexp + 5000) / 10000; race->mexp *= 10000; } else if (race->mexp < 10000000) { race->mexp = (race->mexp + 50000) / 100000; race->mexp *= 100000; } } } } /* If we're rebalancing, this is a nop, if not, we restore the * orig value */ lvl = race->level; if ((lvl) && (race->mexp < 1L)) race->mexp = 1L; /* * Hack - We have to use an adjustment factor to prevent overflow. * Try to scale evenly across all levels instead of scaling by level */ hp /= 2; if(hp < 1) hp = 1; final_hp[i] = hp; /* Define the power rating */ power[i] = hp * dam; /* Adjust for group monsters, using somewhat arbitrary * multipliers for now */ if (!rf_has(race->flags, RF_UNIQUE)) { if (race->friends) power[i] *= 3; } /* Adjust for escorts */ if (race->friends_base) power[i] *= 2; /* Adjust for multiplying monsters. This is modified by the speed, * as fast multipliers are much worse than slow ones. We also * adjust for ability to bypass walls or doors. */ if (rf_has(race->flags, RF_MULTIPLY)) { int adj_power; if (flags_test(race->flags, RF_SIZE, RF_KILL_WALL, RF_PASS_WALL, FLAG_END)) adj_power = power[i] * adj_energy(race); else if (flags_test(race->flags, RF_SIZE, RF_OPEN_DOOR, RF_BASH_DOOR, FLAG_END)) adj_power = power[i] * adj_energy(race) * 3 / 2; else adj_power = power[i] * adj_energy(race) / 2; power[i] = MAX(power[i], adj_power); } /* Update the running totals - these will be used as divisors later * Total HP / dam / count for everything up to the current level */ for (j = lvl; j < (lvl == 0 ? lvl + 1: z_info->max_depth); j++) { int count = 10; /* Uniques don't count towards monster power on the level. */ if (rf_has(race->flags, RF_UNIQUE)) continue; /* Specifically placed monsters don't count towards monster * power on the level. */ if (!(race->rarity)) continue; /* Hack -- provide adjustment factor to prevent overflow */ if ((j == 90) && (race->level < 90)) { hp /= 10; dam /= 10; } if ((j == 65) && (race->level < 65)) { hp /= 10; dam /= 10; } if ((j == 40) && (race->level < 40)) { hp /= 10; dam /= 10; } /* Hack - if it's a group monster or multiplying monster, add * several to the count so the averages don't get thrown off */ if (race->friends || race->friends_base) count = 15; if (rf_has(race->flags, RF_MULTIPLY)) { int adj_energy_amt; if (flags_test(race->flags, RF_SIZE, RF_KILL_WALL, RF_PASS_WALL, FLAG_END)) adj_energy_amt = adj_energy(race); else if (flags_test(race->flags, RF_SIZE, RF_OPEN_DOOR, RF_BASH_DOOR, FLAG_END)) adj_energy_amt = adj_energy(race) * 3 / 2; else adj_energy_amt = adj_energy(race) / 2; count = MAX(1, adj_energy_amt) * count; } /* Very rare monsters count less towards total monster power * on the level. */ if (race->rarity > count) { hp = hp * count / race->rarity; dam = dam * count / race->rarity; count = race->rarity; } tot_hp[j] += hp; tot_dam[j] += dam; mon_count[j] += count / race->rarity; } } /* Apply divisors now */ for (i = 0; i < z_info->r_max; i++) { int new_power; /* Point at the "info" */ race = &racelist[i]; /* Extract level */ lvl = race->level; /* Paranoia */ if (tot_hp[lvl] != 0 && tot_dam[lvl] != 0) { scaled_power[i] = power[i]; /* Divide by av HP and av damage for all in-level monsters */ /* Note we have factored in the above 'adjustment factor' */ av_hp = tot_hp[lvl] * 10 / mon_count[lvl]; av_dam = tot_dam[lvl] * 10 / mon_count[lvl]; /* Justifiable paranoia - avoid divide by zero errors */ if (av_hp > 0) scaled_power[i] = scaled_power[i] / av_hp; if (av_dam > 0) scaled_power[i] = scaled_power[i] / av_dam; /* Never less than 1 */ if (power[i] < 1) power[i] = 1; /* Set powers */ if (arg_rebalance) { race->power = power[i]; race->scaled_power = scaled_power[i]; } /* Get power */ new_power = power[i]; /* Compute rarity algorithmically */ for (j = 1; new_power > j; new_power -= j * j, j++); /* Set rarity */ if (arg_rebalance) race->rarity = j; } } mem_free(mon_count); mem_free(tot_dam); mem_free(tot_hp); } /* Determine total monster power */ for (i = 0; i < z_info->r_max; i++) tot_mon_power += r_info[i].scaled_power; if (dump) { /* Dump the power details */ path_build(buf, sizeof(buf), ANGBAND_DIR_USER, "mon_power.txt"); mon_fp = file_open(buf, MODE_WRITE, FTYPE_TEXT); file_putf(mon_fp, "ridx|level|rarity|d_char|name|pwr|scaled|melee|spell|hp\n"); for (i = 0; i < z_info->r_max; i++) { char mbstr[MB_LEN_MAX + 1] = { 0 }; race = &r_info[i]; /* Don't print anything for nonexistent monsters */ if (!race->name) continue; wctomb(mbstr, race->d_char); file_putf(mon_fp, "%d|%d|%d|%s|%s|%d|%d|%d|%d|%d\n", race->ridx, race->level, race->rarity, mbstr, race->name, power[i], scaled_power[i], final_melee_dam[i], final_spell_dam[i], final_hp[i]); } file_close(mon_fp); } /* Write to the user directory */ path_build(buf, sizeof(buf), ANGBAND_DIR_USER, "new_monster.txt"); if (text_lines_to_file(buf, write_monster_entries)) { msg("Failed to create file %s.new", buf); wrote = FALSE; } /* Free power arrays */ mem_free(highest_threat); mem_free(final_spell_dam); mem_free(final_melee_dam); mem_free(final_hp); mem_free(scaled_power); mem_free(power); /* Success */ return wrote ? 0 : -1; }
/* * Read an object * * This function attempts to "repair" old savefiles, and to extract * the most up to date values for various object fields. */ static int rd_item(object_type *o_ptr) { byte old_dd; byte old_ds; byte tmp8u; size_t i; object_kind *k_ptr; char buf[128]; /* Kind */ rd_s16b(&o_ptr->k_idx); /* Paranoia */ if ((o_ptr->k_idx < 0) || (o_ptr->k_idx >= z_info->k_max)) return (-1); /* Location */ rd_byte(&o_ptr->iy); rd_byte(&o_ptr->ix); /* Type/Subtype */ rd_byte(&o_ptr->tval); rd_byte(&o_ptr->sval); rd_s16b(&o_ptr->pval); /* Pseudo-ID bit */ rd_byte(&tmp8u); rd_byte(&o_ptr->number); rd_s16b(&o_ptr->weight); rd_byte(&o_ptr->name1); rd_byte(&o_ptr->name2); rd_s16b(&o_ptr->timeout); rd_s16b(&o_ptr->to_h); rd_s16b(&o_ptr->to_d); rd_s16b(&o_ptr->to_a); rd_s16b(&o_ptr->ac); rd_byte(&old_dd); rd_byte(&old_ds); rd_byte(&tmp8u); rd_byte(&o_ptr->marked); rd_byte(&o_ptr->origin); rd_byte(&o_ptr->origin_depth); rd_u16b(&o_ptr->origin_xtra); /* Hack - XXX - MarbleDice - Maximum saveable flags = 96 */ for (i = 0; i < 12 && i < OF_SIZE; i++) rd_byte(&o_ptr->flags[i]); if (i < 12) strip_bytes(OF_SIZE - i); /* Monster holding object */ rd_s16b(&o_ptr->held_m_idx); rd_string(buf, sizeof(buf)); /* Save the inscription */ if (buf[0]) o_ptr->note = quark_add(buf); /* Lookup item kind */ o_ptr->k_idx = lookup_kind(o_ptr->tval, o_ptr->sval); k_ptr = &k_info[o_ptr->k_idx]; /* Return now in case of "blank" or "empty" objects */ if (!k_ptr->name || !o_ptr->k_idx) { o_ptr->k_idx = 0; return 0; } /* Repair non "wearable" items */ if (!wearable_p(o_ptr)) { /* Get the correct fields */ if (!randcalc_valid(k_ptr->to_h, o_ptr->to_h)) o_ptr->to_h = randcalc(k_ptr->to_h, o_ptr->origin_depth, RANDOMISE); if (!randcalc_valid(k_ptr->to_d, o_ptr->to_d)) o_ptr->to_d = randcalc(k_ptr->to_d, o_ptr->origin_depth, RANDOMISE); if (!randcalc_valid(k_ptr->to_a, o_ptr->to_a)) o_ptr->to_a = randcalc(k_ptr->to_a, o_ptr->origin_depth, RANDOMISE); /* Get the correct fields */ o_ptr->ac = k_ptr->ac; o_ptr->dd = k_ptr->dd; o_ptr->ds = k_ptr->ds; /* Get the correct weight */ o_ptr->weight = k_ptr->weight; /* Paranoia */ o_ptr->name1 = o_ptr->name2 = 0; /* All done */ return (0); } /* Paranoia */ if (o_ptr->name1) { artifact_type *a_ptr; /* Paranoia */ if (o_ptr->name1 >= z_info->a_max) return (-1); /* Obtain the artifact info */ a_ptr = &a_info[o_ptr->name1]; /* Verify that artifact */ if (!a_ptr->name) o_ptr->name1 = 0; } /* Paranoia */ if (o_ptr->name2) { ego_item_type *e_ptr; /* Paranoia */ if (o_ptr->name2 >= z_info->e_max) return (-1); /* Obtain the ego-item info */ e_ptr = &e_info[o_ptr->name2]; /* Verify that ego-item */ if (!e_ptr->name) o_ptr->name2 = 0; } /* Get the standard fields */ o_ptr->ac = k_ptr->ac; o_ptr->dd = k_ptr->dd; o_ptr->ds = k_ptr->ds; /* Get the standard weight */ o_ptr->weight = k_ptr->weight; /* Artifacts */ if (o_ptr->name1) { artifact_type *a_ptr; /* Obtain the artifact info */ a_ptr = &a_info[o_ptr->name1]; /* Get the new artifact "pval" */ o_ptr->pval = a_ptr->pval; /* Get the new artifact fields */ o_ptr->ac = a_ptr->ac; o_ptr->dd = a_ptr->dd; o_ptr->ds = a_ptr->ds; /* Get the new artifact weight */ o_ptr->weight = a_ptr->weight; } /* Ego items */ if (o_ptr->name2) { ego_item_type *e_ptr; /* Obtain the ego-item info */ e_ptr = &e_info[o_ptr->name2]; /* Hack -- keep some old fields */ if ((o_ptr->dd < old_dd) && (o_ptr->ds == old_ds)) { /* Keep old boosted damage dice */ o_ptr->dd = old_dd; } /* Hack -- enforce legal pval */ if (flags_test(e_ptr->flags, OF_SIZE, OF_PVAL_MASK, FLAG_END)) { /* Force a meaningful pval */ if (!o_ptr->pval) o_ptr->pval = 1; } } /* Success */ return (0); }
/** * This function takes a pointer to a grid info struct describing the * contents of a grid location (as obtained through the function map_info) * and fills in the character and attr pairs for display. * * ap and cp are filled with the attr/char pair for the monster, object or * floor tile that is at the "top" of the grid (monsters covering objects, * which cover floor, assuming all are present). * * tap and tcp are filled with the attr/char pair for the floor, regardless * of what is on it. This can be used by graphical displays with * transparency to place an object onto a floor tile, is desired. * * Any lighting effects are also applied to these pairs, clear monsters allow * the underlying colour or feature to show through (ATTR_CLEAR and * CHAR_CLEAR), multi-hued colour-changing (ATTR_MULTI) is applied, and so on. * Technically, the flag "CHAR_MULTI" is supposed to indicate that a monster * looks strange when examined, but this flag is currently ignored. * * NOTES: * This is called pretty frequently, whenever a grid on the map display * needs updating, so don't overcomplicate it. * * The "zero" entry in the feature/object/monster arrays are * used to provide "special" attr/char codes, with "monster zero" being * used for the player attr/char, "object zero" being used for the "pile" * attr/char, and "feature zero" being used for the "darkness" attr/char. * * TODO: * The transformations for tile colors, or brightness for the 16x16 * tiles should be handled differently. One possibility would be to * extend feature_type with attr/char definitions for the different states. * This will probably be done outside of the current text->graphics mappings * though. */ void grid_data_as_text(grid_data *g, int *ap, wchar_t *cp, int *tap, wchar_t *tcp) { feature_type *f_ptr = &f_info[g->f_idx]; int a = feat_x_attr[g->lighting][f_ptr->fidx]; wchar_t c = feat_x_char[g->lighting][f_ptr->fidx]; /* Check for trap detection boundaries */ if (use_graphics == GRAPHICS_NONE) grid_get_attr(g, &a); else if (g->trapborder && tf_has(f_ptr->flags, TF_FLOOR) && (g->m_idx || g->first_kind)) { /* if there is an object or monster here, and this is a plain floor * display the border here rather than an overlay below */ a = feat_x_attr[g->lighting][FEAT_DTRAP_FLOOR]; c = feat_x_char[g->lighting][FEAT_DTRAP_FLOOR]; } /* Save the terrain info for the transparency effects */ (*tap) = a; (*tcp) = c; /* There is a trap in this grid, and we are not hallucinating */ if (g->trap && (!g->hallucinate)) /* Change graphics to indicate a trap (if visible) */ get_trap_graphics(cave, g, &a, &c); /* If there's an object, deal with that. */ if (g->unseen_money) { /* $$$ gets an orange star*/ a = object_kind_attr(&k_info[7]); c = object_kind_char(&k_info[7]); } else if (g->unseen_object) { /* Everything else gets a red star */ a = object_kind_attr(&k_info[6]); c = object_kind_char(&k_info[6]); } else if (g->first_kind) { if (g->hallucinate) { /* Just pick a random object to display. */ hallucinatory_object(&a, &c); } else if (g->multiple_objects) { /* Get the "pile" feature instead */ a = object_kind_attr(&k_info[0]); c = object_kind_char(&k_info[0]); } else { /* Normal attr and char */ a = object_kind_attr(g->first_kind); c = object_kind_char(g->first_kind); } } /* Handle monsters, the player and trap borders */ if (g->m_idx > 0) { if (g->hallucinate) { /* Just pick a random monster to display. */ hallucinatory_monster(&a, &c); } else if (!is_mimicking(cave_monster(cave, g->m_idx))) { monster_type *m_ptr = cave_monster(cave, g->m_idx); byte da; wchar_t dc; /* Desired attr & char */ da = monster_x_attr[m_ptr->race->ridx]; dc = monster_x_char[m_ptr->race->ridx]; /* Special handling of attrs and/or chars */ if (da & 0x80) { /* Special attr/char codes */ a = da; c = dc; } else if (OPT(purple_uniques) && rf_has(m_ptr->race->flags, RF_UNIQUE)) { /* Turn uniques purple if desired (violet, actually) */ a = COLOUR_VIOLET; c = dc; } else if (rf_has(m_ptr->race->flags, RF_ATTR_MULTI) || rf_has(m_ptr->race->flags, RF_ATTR_FLICKER) || rf_has(m_ptr->race->flags, RF_ATTR_RAND)) { /* Multi-hued monster */ a = m_ptr->attr ? m_ptr->attr : da; c = dc; } else if (!flags_test(m_ptr->race->flags, RF_SIZE, RF_ATTR_CLEAR, RF_CHAR_CLEAR, FLAG_END)) { /* Normal monster (not "clear" in any way) */ a = da; /* Desired attr & char. da is not used, should a be set to it?*/ /*da = monster_x_attr[m_ptr->race->ridx];*/ dc = monster_x_char[m_ptr->race->ridx]; c = dc; } else if (a & 0x80) { /* Hack -- Bizarre grid under monster */ a = da; c = dc; } else if (!rf_has(m_ptr->race->flags, RF_CHAR_CLEAR)) { /* Normal char, Clear attr, monster */ c = dc; } else if (!rf_has(m_ptr->race->flags, RF_ATTR_CLEAR)) { /* Normal attr, Clear char, monster */ a = da; } /* Store the drawing attr so we can use it elsewhere */ m_ptr->attr = a; } } else if (g->is_player) { monster_race *r_ptr = &r_info[0]; /* Get the "player" attr */ a = monster_x_attr[r_ptr->ridx]; if ((OPT(hp_changes_color)) && !(a & 0x80)) { switch(player->chp * 10 / player->mhp) { case 10: case 9: { a = COLOUR_WHITE; break; } case 8: case 7: { a = COLOUR_YELLOW; break; } case 6: case 5: { a = COLOUR_ORANGE; break; } case 4: case 3: { a = COLOUR_L_RED; break; } case 2: case 1: case 0: { a = COLOUR_RED; break; } default: { a = COLOUR_WHITE; break; } } } /* Get the "player" char */ c = monster_x_char[r_ptr->ridx]; } else if (g->trapborder && (g->f_idx) && !(g->first_kind) && (use_graphics != GRAPHICS_NONE)) { /* No overlay is used, so we can use the trap border overlay */ a = feat_x_attr[g->lighting][FEAT_DTRAP_WALL]; c = feat_x_char[g->lighting][FEAT_DTRAP_WALL]; } /* Result */ (*ap) = a; (*cp) = c; }
/** * Choose "logical" directions for monster movement */ static bool get_move(struct chunk *c, struct monster *mon, int *dir, bool *good) { struct loc decoy = cave_find_decoy(c); struct loc target = (decoy.y && decoy.x) ? decoy : loc(player->px, player->py); int y, x; /* Monsters will run up to z_info->flee_range grids out of sight */ int flee_range = z_info->max_sight + z_info->flee_range; bool done = false; /* Calculate range */ get_move_find_range(mon); /* Assume we're heading towards the player */ if (get_move_advance(c, mon)) { /* Extract the "pseudo-direction" */ y = mon->target.grid.y - mon->fy; x = mon->target.grid.x - mon->fx; *good = true; } else { /* Head blindly straight for the "player" if there's no better idea */ y = target.y - mon->fy; x = target.x - mon->fx; } /* Normal animal packs try to get the player out of corridors. */ if (rf_has(mon->race->flags, RF_GROUP_AI) && !flags_test(mon->race->flags, RF_SIZE, RF_PASS_WALL, RF_KILL_WALL, FLAG_END)) { int i, open = 0; /* Count empty grids next to player */ for (i = 0; i < 8; i++) { int ry = target.y + ddy_ddd[i]; int rx = target.x + ddx_ddd[i]; /* Check grid around the player for room interior (room walls count) * or other empty space */ if (square_ispassable(c, ry, rx) || square_isroom(c, ry, rx)) { /* One more open grid */ open++; } } /* Not in an empty space and strong player */ if ((open < 5) && (player->chp > player->mhp / 2)) { /* Find hiding place */ if (get_move_find_hiding(c, mon)) { done = true; y = mon->target.grid.y - mon->fy; x = mon->target.grid.x - mon->fx; } } } /* Apply fear */ if (!done && (mon->min_range == flee_range)) { /* Try to find safe place */ if (!get_move_find_safety(c, mon)) { /* Just leg it away from the player */ y = (-y); x = (-x); } else { /* Set a course for the safe place */ get_move_flee(c, mon); y = mon->target.grid.y - mon->fy; x = mon->target.grid.x - mon->fx; } done = true; } /* Monster groups try to surround the player */ if (!done && rf_has(mon->race->flags, RF_GROUP_AI) && square_isview(c, mon->fy, mon->fx)) { int i, yy = mon->target.grid.y, xx = mon->target.grid.x; /* If we are not already adjacent */ if (mon->cdis > 1) { /* Find an empty square near the player to fill */ int tmp = randint0(8); for (i = 0; i < 8; i++) { /* Pick squares near player (pseudo-randomly) */ yy = target.y + ddy_ddd[(tmp + i) & 7]; xx = target.x + ddx_ddd[(tmp + i) & 7]; /* Ignore filled grids */ if (!square_isempty(c, yy, xx)) continue; /* Try to fill this hole */ break; } } /* Extract the new "pseudo-direction" */ y = yy - mon->fy; x = xx - mon->fx; } /* Check if the monster has already reached its target */ if (!x && !y) return (false); /* Pick the correct direction */ *dir = get_move_choose_direction(y, x); /* Want to move */ return (true); }
/** * Monster can pass through walls */ bool monster_passes_walls(const struct monster *mon) { return flags_test(mon->race->flags, RF_SIZE, RF_PASS_WALL, RF_KILL_WALL, RF_SMASH_WALL, FLAG_END); }
/* XXX Eddie should messages be adhoc all over the place? perhaps the main * loop should check for change in inventory/wieldeds and all messages be * printed from one place */ void object_notice_on_wield(object_type *o_ptr) { bitflag f[OF_SIZE], obvious_mask[OF_SIZE]; bool obvious = FALSE; const slay_t *s_ptr; flags_init(obvious_mask, OF_SIZE, OF_OBVIOUS_MASK, FLAG_END); /* Save time of wield for later */ object_last_wield = turn; /* Only deal with un-ID'd items */ if (object_is_known(o_ptr)) return; /* Wear it */ object_flavor_tried(o_ptr); if (object_add_ident_flags(o_ptr, IDENT_WORN)) object_check_for_ident(o_ptr); if (obj_is_light(o_ptr) && ego_item_p(o_ptr)) object_notice_ego(o_ptr); if (object_flavor_is_aware(o_ptr) && easy_know(o_ptr)) { object_notice_everything(o_ptr); return; } /* Automatically sense artifacts upon wield */ object_sense_artifact(o_ptr); /* Note artifacts when found */ if (artifact_p(o_ptr)) history_add_artifact(o_ptr->name1, object_is_known(o_ptr), TRUE); /* special case FA, needed at least for mages wielding gloves */ if (object_FA_would_be_obvious(o_ptr)) of_on(obvious_mask, OF_FREE_ACT); /* Learn about obvious flags */ of_union(o_ptr->known_flags, obvious_mask); /* Extract the flags */ object_flags(o_ptr, f); /* Find obvious things (disregarding curses) */ flags_clear(obvious_mask, OF_SIZE, OF_CURSE_MASK, FLAG_END); if (of_is_inter(f, obvious_mask)) obvious = TRUE; flags_init(obvious_mask, OF_SIZE, OF_OBVIOUS_MASK, FLAG_END); /* XXX Eddie should these next NOT call object_check_for_ident due to worries about repairing? */ /* XXX Eddie this is a small hack, but jewelry with anything noticeable really is obvious */ /* XXX Eddie learn =soulkeeping vs =bodykeeping when notice sustain_str */ if (object_is_jewelry(o_ptr)) { /* Learn the flavor of jewelry with obvious flags */ if (EASY_LEARN && obvious) object_flavor_aware(o_ptr); /* Learn all flags on any aware non-artifact jewelry */ if (object_flavor_is_aware(o_ptr) && !artifact_p(o_ptr)) object_know_all_flags(o_ptr); } object_check_for_ident(o_ptr); if (!obvious) return; /* Messages */ for (s_ptr = slay_table; s_ptr->slay_flag; s_ptr++) { if (of_has(f, s_ptr->slay_flag) && s_ptr->brand) { char o_name[40]; object_desc(o_name, sizeof(o_name), o_ptr, ODESC_BASE); msg_format("Your %s %s!", o_name, s_ptr->active_verb); } } /* XXX Eddie need to add stealth here, also need to assert/double-check everything is covered */ if (of_has(f, OF_STR)) msg_format("You feel %s!", o_ptr->pval > 0 ? "stronger" : "weaker"); if (of_has(f, OF_INT)) msg_format("You feel %s!", o_ptr->pval > 0 ? "smarter" : "more stupid"); if (of_has(f, OF_WIS)) msg_format("You feel %s!", o_ptr->pval > 0 ? "wiser" : "more naive"); if (of_has(f, OF_DEX)) msg_format("You feel %s!", o_ptr->pval > 0 ? "more dextrous" : "clumsier"); if (of_has(f, OF_CON)) msg_format("You feel %s!", o_ptr->pval > 0 ? "healthier" : "sicklier"); if (of_has(f, OF_CHR)) msg_format("You feel %s!", o_ptr->pval > 0 ? "cuter" : "uglier"); if (of_has(f, OF_SPEED)) msg_format("You feel strangely %s.", o_ptr->pval > 0 ? "quick" : "sluggish"); if (flags_test(f, OF_SIZE, OF_BLOWS, OF_SHOTS, FLAG_END)) msg_format("Your hands %s", o_ptr->pval > 0 ? "tingle!" : "ache."); if (of_has(f, OF_INFRA)) msg_format("Your eyes tingle."); if (of_has(f, OF_LIGHT)) msg_print("It glows!"); if (of_has(f, OF_TELEPATHY)) msg_print("Your mind feels strangely sharper!"); /* WARNING -- masking f by obvious mask -- this should be at the end of this function */ flags_mask(f, OF_SIZE, OF_OBVIOUS_MASK, FLAG_END); /* learn the ego on any obvious brand or slay */ if (EASY_LEARN && ego_item_p(o_ptr) && obvious && flags_test(f, OF_SIZE, OF_ALL_SLAY_MASK, FLAG_END)) object_notice_ego(o_ptr); /* Remember the flags */ object_notice_sensing(o_ptr); /* XXX Eddie should we check_for_ident here? */ }
static long eval_hp_adjust(struct monster_race *race) { long hp; int resists = 1; int hide_bonus = 0; /* Get the monster base hitpoints */ hp = race->avg_hp; /* Never moves with no ranged attacks - high hit points count for less */ if (rf_has(race->flags, RF_NEVER_MOVE) && !(race->freq_innate || race->freq_spell)) { hp /= 2; if (hp < 1) hp = 1; } /* Just assume healers have more staying power */ if (rsf_has(race->spell_flags, RSF_HEAL)) hp = (hp * 6) / 5; /* Miscellaneous improvements */ if (rf_has(race->flags, RF_REGENERATE)) {hp *= 10; hp /= 9;} if (rf_has(race->flags, RF_PASS_WALL)) {hp *= 3; hp /= 2;} /* Calculate hide bonus */ if (rf_has(race->flags, RF_EMPTY_MIND)) hide_bonus += 2; else { if (rf_has(race->flags, RF_COLD_BLOOD)) hide_bonus += 1; if (rf_has(race->flags, RF_WEIRD_MIND)) hide_bonus += 1; } /* Invisibility */ if (rf_has(race->flags, RF_INVISIBLE)) hp = (hp * (race->level + hide_bonus + 1)) / MAX(1, race->level); /* Monsters that can teleport are a hassle, and can easily run away */ if (flags_test(race->spell_flags, RSF_SIZE, RSF_TPORT, RSF_TELE_AWAY, RSF_TELE_LEVEL, FLAG_END)) hp = (hp * 6) / 5; /* Monsters that multiply are tougher to kill */ if (rf_has(race->flags, RF_MULTIPLY)) hp *= 2; /* Monsters with resistances are harder to kill. * Therefore effective slays / brands against them are worth more. */ if (rf_has(race->flags, RF_IM_ACID)) resists += 2; if (rf_has(race->flags, RF_IM_FIRE)) resists += 2; if (rf_has(race->flags, RF_IM_COLD)) resists += 2; if (rf_has(race->flags, RF_IM_ELEC)) resists += 2; if (rf_has(race->flags, RF_IM_POIS)) resists += 2; /* Bonus for multiple basic resists and weapon resists */ if (resists >= 12) resists *= 6; else if (resists >= 10) resists *= 4; else if (resists >= 8) resists *= 3; else if (resists >= 6) resists *= 2; /* If quite resistant, reduce resists by defense holes */ if (resists >= 6) { if (rf_has(race->flags, RF_HURT_ROCK)) resists -= 1; if (rf_has(race->flags, RF_HURT_LIGHT)) resists -= 1; if (!rf_has(race->flags, RF_NO_SLEEP)) resists -= 3; if (!rf_has(race->flags, RF_NO_FEAR)) resists -= 2; if (!rf_has(race->flags, RF_NO_CONF)) resists -= 2; if (!rf_has(race->flags, RF_NO_STUN)) resists -= 1; if (resists < 5) resists = 5; } /* If quite resistant, bonus for high resists */ if (resists >= 3) { if (rf_has(race->flags, RF_IM_WATER)) resists += 1; if (rf_has(race->flags, RF_IM_NETHER)) resists += 1; if (rf_has(race->flags, RF_IM_NEXUS)) resists += 1; if (rf_has(race->flags, RF_IM_DISEN)) resists += 1; } /* Scale resists */ resists = resists * 25; /* Monster resistances */ if (resists < (race->ac + resists) / 3) hp += (hp * resists) / (150 + race->level); else hp += (hp * (race->ac + resists) / 3) / (150 + race->level); /* Boundary control */ if (hp < 1) hp = 1; return (hp); }
/* * Attempt to change an object into an ego-item -MWK- * Better only called by apply_magic(). * The return value says if we picked a cursed item (if allowed) and is * passed on to a_m_aux1/2(). * If no legal ego item is found, this routine returns 0, resulting in * an unenchanted item. */ static int make_ego_item(object_type *o_ptr, int level, bool force_uncursed) { int i, j; int e_idx; long value, total; ego_item_type *e_ptr; alloc_entry *table = alloc_ego_table; /* Fail if object already is ego or artifact */ if (o_ptr->name1) return (FALSE); if (o_ptr->name2) return (FALSE); /* Boost level (like with object base types) */ if (level > 0) { /* Occasional "boost" */ if (one_in_(GREAT_EGO)) { /* The bizarre calculation again */ level = 1 + (level * MAX_DEPTH / randint1(MAX_DEPTH)); } } /* Reset total */ total = 0L; /* Process probabilities */ for (i = 0; i < alloc_ego_size; i++) { /* Default */ table[i].prob3 = 0; /* Objects are sorted by depth */ if (table[i].level > level) continue; /* Get the index */ e_idx = table[i].index; /* Get the actual kind */ e_ptr = &e_info[e_idx]; /* Avoid cursed items if specified */ if (force_uncursed && cursed_p(e_ptr)) continue; /* Test if this is a legal ego-item type for this object */ for (j = 0; j < EGO_TVALS_MAX; j++) { /* Require identical base type */ if (o_ptr->tval == e_ptr->tval[j]) { /* Require sval in bounds, lower */ if (o_ptr->sval >= e_ptr->min_sval[j]) { /* Require sval in bounds, upper */ if (o_ptr->sval <= e_ptr->max_sval[j]) { /* Accept */ table[i].prob3 = table[i].prob2; } } } } /* Total */ total += table[i].prob3; } /* No legal ego-items -- create a normal unenchanted one */ if (total == 0) return (0); /* Pick an ego-item */ value = randint0(total); /* Find the object */ for (i = 0; i < alloc_ego_size; i++) { /* Found the entry */ if (value < table[i].prob3) break; /* Decrement */ value = value - table[i].prob3; } /* We have one */ e_idx = (byte)table[i].index; o_ptr->name2 = e_idx; return (flags_test(e_info[e_idx].flags, OF_SIZE, OF_CURSE_MASK, FLAG_END) ? -2 : 2); }
static long eval_max_dam(struct monster_race *race, int ridx) { int rlev, i; int melee_dam = 0, atk_dam = 0, spell_dam = 0; int dam = 1; /* Extract the monster level, force 1 for town monsters */ rlev = ((race->level >= 1) ? race->level : 1); /* Assume single resist for the elemental attacks */ spell_dam = best_spell_power(race, 1); /* Hack - Apply over 10 rounds */ spell_dam *= 10; /* Scale for frequency and availability of mana / ammo */ if (spell_dam) { int freq = race->freq_spell; /* Hack -- always get 1 shot */ if (freq < 10) freq = 10; /* Adjust for frequency */ spell_dam = spell_dam * freq / 100; } /* Check attacks */ for (i = 0; i < z_info->mon_blows_max; i++) { int effect, method; random_value dice; if (!race->blow) break; /* Extract the attack infomation */ effect = race->blow[i].effect; method = race->blow[i].method; dice = race->blow[i].dice; /* Assume maximum damage */ atk_dam = eval_blow_effect(effect, dice, race->level); /* Factor for dangerous side effects */ if (monster_blow_method_stun(method)) { /* Stun definitely most dangerous*/ atk_dam *= 4; atk_dam /= 3; } else if (monster_blow_method_stun(method)) { /* Cut */ atk_dam *= 7; atk_dam /= 5; } /* Normal melee attack */ if (!rf_has(race->flags, RF_NEVER_BLOW)) { /* Keep a running total */ melee_dam += atk_dam; } } /* Apply damage over 10 rounds. We assume that the monster has to make * contact first. * Hack - speed has more impact on melee as has to stay in contact with * player. * Hack - this is except for pass wall and kill wall monsters which can * always get to the player. * Hack - use different values for huge monsters as they strike out to * range 2. */ if (flags_test(race->flags, RF_SIZE, RF_KILL_WALL, RF_PASS_WALL, FLAG_END)) melee_dam *= 10; else melee_dam = melee_dam * 3 + melee_dam * adj_energy(race) / 7; /* Scale based on attack accuracy. We make a massive number of * assumptions here and just use monster level. */ melee_dam = melee_dam * MIN(45 + rlev * 3, 95) / 100; /* Hack -- Monsters that multiply ignore the following reductions */ if (!rf_has(race->flags, RF_MULTIPLY)) { /*Reduce damamge potential for monsters that move randomly */ if (flags_test(race->flags, RF_SIZE, RF_RAND_25, RF_RAND_50, FLAG_END)) { int reduce = 100; if (rf_has(race->flags, RF_RAND_25)) reduce -= 25; if (rf_has(race->flags, RF_RAND_50)) reduce -= 50; /* Even moving randomly one in 8 times will hit the player */ reduce += (100 - reduce) / 8; /* Adjust the melee damage */ melee_dam = (melee_dam * reduce) / 100; } /* Monsters who can't move are much less of a combat threat */ if (rf_has(race->flags, RF_NEVER_MOVE)) { if (rsf_has(race->spell_flags, RSF_TELE_TO) || rsf_has(race->spell_flags, RSF_BLINK)) { /* Scale for frequency */ melee_dam = melee_dam / 5 + 4 * melee_dam * race->freq_spell / 500; /* Incorporate spell failure chance */ if (!rf_has(race->flags, RF_STUPID)) melee_dam = melee_dam / 5 + 4 * melee_dam * MIN(75 + (rlev + 3) / 4, 100) / 500; } else if (rf_has(race->flags, RF_INVISIBLE)) melee_dam /= 3; else melee_dam /= 5; } } /* But keep at a minimum */ if (melee_dam < 1) melee_dam = 1; /* Combine spell and melee damage */ dam = (spell_dam + melee_dam); highest_threat[ridx] = dam; final_spell_dam[ridx] = spell_dam; final_melee_dam[ridx] = melee_dam; /* Adjust for speed - monster at speed 120 will do double damage, monster * at speed 100 will do half, etc. Bonus for monsters who can haste self */ dam = (dam * adj_energy(race)) / 10; /* Adjust threat for speed -- multipliers are more threatening. */ if (rf_has(race->flags, RF_MULTIPLY)) highest_threat[ridx] = (highest_threat[ridx] * adj_energy(race)) / 5; /* Adjust threat for friends, this can be improved, but is probably good * enough for now. */ if (race->friends) highest_threat[ridx] *= 2; else if (race->friends_base) /* Friends base is weaker, because they are <= monster level */ highest_threat[ridx] = highest_threat[ridx] * 3 / 2; /* But keep at a minimum */ if (dam < 1) dam = 1; /* We're done */ return (dam); }
/* * Determine the squelch level of an object, which is similar to its pseudo. * * The main point is when the value is undetermined given current info, * return the maximum possible value. */ static byte squelch_level_of(const object_type *o_ptr) { object_kind *k_ptr = &k_info[o_ptr->k_idx]; byte value; bitflag f[OF_SIZE]; object_flags_known(o_ptr, f); if ((object_pval_is_visible(o_ptr)) && (o_ptr->pval < 0)) return SQUELCH_BAD; /* Deal with jewelry specially. */ if (object_is_jewelry(o_ptr)) { if ((object_pval_is_visible(o_ptr)) && (o_ptr->pval > 0)) return SQUELCH_AVERAGE; if ((o_ptr->to_h > 0) || (o_ptr->to_d > 0) || (o_ptr->to_a > 0)) return SQUELCH_AVERAGE; if ((o_ptr->to_h < 0) || (o_ptr->to_d < 0) || (o_ptr->to_a < 0)) return SQUELCH_BAD; return SQUELCH_AVERAGE; } /* And lights */ if (o_ptr->tval == TV_LIGHT) { if (flags_test(f, OF_SIZE, OF_OBVIOUS_MASK, FLAG_END)) return SQUELCH_ALL; if ((o_ptr->to_h > 0) || (o_ptr->to_d > 0) || (o_ptr->to_a > 0)) return SQUELCH_GOOD; if ((o_ptr->to_h < 0) || (o_ptr->to_d < 0) || (o_ptr->to_a < 0)) return SQUELCH_BAD; return SQUELCH_AVERAGE; } if (object_was_sensed(o_ptr)) { obj_pseudo_t pseudo = object_pseudo(o_ptr); switch (pseudo) { case INSCRIP_AVERAGE: value = SQUELCH_AVERAGE; break; case INSCRIP_EXCELLENT: /* have to assume splendid until you have tested it */ if (object_was_worn(o_ptr)) { if (object_high_resist_is_possible(o_ptr)) value = SQUELCH_EXCELLENT_NO_SPL; else value = SQUELCH_EXCELLENT_NO_HI; } else { value = SQUELCH_ALL; } break; case INSCRIP_STRANGE: /* XXX Eddie perhaps some strange count as something else */ case INSCRIP_SPLENDID: value = SQUELCH_ALL; break; case INSCRIP_NULL: case INSCRIP_SPECIAL: value = SQUELCH_MAX; break; /* This is the interesting case */ case INSCRIP_MAGICAL: value = SQUELCH_GOOD; if ((object_attack_plusses_are_visible(o_ptr) || (randcalc_valid(k_ptr->to_h, o_ptr->to_h) && randcalc_valid(k_ptr->to_d, o_ptr->to_d))) && (object_defence_plusses_are_visible(o_ptr) || (randcalc_valid(k_ptr->to_a, o_ptr->to_a))) && (o_ptr->to_h <= randcalc(k_ptr->to_h, 0, MINIMISE)) && (o_ptr->to_d <= randcalc(k_ptr->to_d, 0, MINIMISE)) && (o_ptr->to_a <= randcalc(k_ptr->to_a, 0, MINIMISE))) value = SQUELCH_BAD; break; default: /* do not handle any other possible pseudo values */ assert(0); } } else { if (object_was_worn(o_ptr)) value = SQUELCH_EXCELLENT_NO_SPL; /* object would be sensed if it were splendid */ else if (object_is_known_not_artifact(o_ptr)) value = SQUELCH_ALL; else value = SQUELCH_MAX; } return value; }