/** * Save the lore to a file in the user directory. * * \param path is the path to the filename * * \returns true on success, false otherwise. */ bool dump_save(const char *path) { if (text_lines_to_file(path, write_character_dump)) { msg("Failed to create file %s.new", path); return false; } return true; }
/** * 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; }