summaryrefslogtreecommitdiffstats
path: root/crawl-ref/source/debug.cc
diff options
context:
space:
mode:
Diffstat (limited to 'crawl-ref/source/debug.cc')
-rw-r--r--crawl-ref/source/debug.cc316
1 files changed, 299 insertions, 17 deletions
diff --git a/crawl-ref/source/debug.cc b/crawl-ref/source/debug.cc
index 83b910d236..591cf7ab38 100644
--- a/crawl-ref/source/debug.cc
+++ b/crawl-ref/source/debug.cc
@@ -29,9 +29,11 @@
#include "direct.h"
#include "dungeon.h"
+#include "fight.h"
#include "invent.h"
#include "itemname.h"
#include "itemprop.h"
+#include "item_use.h"
#include "items.h"
#include "misc.h"
#include "monplace.h"
@@ -393,30 +395,24 @@ void TRACE(const char *format, ...)
//
//---------------------------------------------------------------
#ifdef WIZARD
-static int debug_prompt_for_monster( void )
-{
- char specs[80];
- char obj_name[ ITEMNAME_SIZE ];
- char *ptr;
- mpr( "(Hint: 'generated' names, eg 'orc zombie', won't work)", MSGCH_PROMPT );
- mpr( "Which monster by name? ", MSGCH_PROMPT );
- get_input_line( specs, sizeof( specs ) );
-
- if (specs[0] == '\0')
- return (-1);
+static int get_monnum(const char *name)
+{
+ char search[ITEMNAME_SIZE],
+ mname[ITEMNAME_SIZE];
+ strncpy(search, name, sizeof search);
+ search[ITEMNAME_SIZE - 1] = 0;
+ strlwr(search);
int mon = -1;
-
for (int i = 0; i < NUM_MONSTERS; i++)
{
- moname( i, true, DESC_PLAIN, obj_name );
+ moname( i, true, DESC_PLAIN, mname );
- ptr = strstr( strlwr(obj_name), strlwr(specs) );
+ const char *ptr = strstr( strlwr(mname), search );
if (ptr != NULL)
{
- mpr( obj_name );
- if (ptr == obj_name)
+ if (ptr == mname)
{
// we prefer prefixes over partial matches
mon = i;
@@ -426,9 +422,22 @@ static int debug_prompt_for_monster( void )
mon = i;
}
}
-
return (mon);
}
+
+static int debug_prompt_for_monster( void )
+{
+ char specs[80];
+
+ mpr( "(Hint: 'generated' names, eg 'orc zombie', won't work)", MSGCH_PROMPT );
+ mpr( "Which monster by name? ", MSGCH_PROMPT );
+ get_input_line( specs, sizeof( specs ) );
+
+ if (specs[0] == '\0')
+ return (-1);
+
+ return (get_monnum(specs));
+}
#endif
//---------------------------------------------------------------
@@ -1715,3 +1724,276 @@ void error_message_to_player(void)
mpr("I suggest you leave this level then save as soon as possible.");
} // end error_message_to_player()
+
+#ifdef WIZARD
+
+static int create_dfs_monster(int mtype, int hp)
+{
+ const int mi =
+ create_monster( mtype, 0, BEH_HOSTILE, you.x_pos, you.y_pos,
+ MHITNOT, 250 );
+
+ if (mi == -1)
+ return (mi);
+
+ monsters *mon = &menv[mi];
+ mon->hit_points = mon->max_hit_points = hp;
+ return (mi);
+}
+
+static skill_type dfs_melee_skill(const item_def *item)
+{
+ skill_type sk = SK_UNARMED_COMBAT;
+ if (item)
+ sk = weapon_skill(*item);
+ return (sk);
+}
+
+static void dfs_set_melee_skill(int skill, const item_def *item)
+{
+ you.skills[dfs_melee_skill(item)] = skill;
+ you.skills[SK_FIGHTING] = skill * 15 / 27;
+}
+
+static void dfs_set_ranged_skill(int skill, const item_def *item)
+{
+ you.skills[range_skill(*item)] = skill;
+ you.skills[SK_RANGED_COMBAT] = skill * 15 / 27;
+}
+
+static void dfs_ranged_item(FILE *out, const item_def *launcher,
+ int wskill, unsigned long damage,
+ long iterations, long hits,
+ int maxdam)
+{
+ double hitdam = hits? double(damage) / hits : 0.0;
+ fprintf(out, "Ranged: %s %d. Accuracy: %ld%%, av damage: %.2f, av hitdam: %.2f, max: %d\n",
+ skill_name( range_skill(*launcher) ),
+ wskill,
+ 100 * hits / iterations,
+ double(damage) / iterations,
+ hitdam,
+ maxdam);
+}
+
+static void dfs_melee_item(FILE *out, const item_def *item,
+ int wskill, unsigned long damage,
+ long iterations, long hits,
+ int maxdam)
+{
+ double hitdam = hits? double(damage) / hits : 0.0;
+ fprintf(out, "Melee: %s %d. Accuracy: %ld%%, av damage: %.2f, av hitdam: %.2f, max: %d\n",
+ skill_name( dfs_melee_skill(item) ),
+ wskill,
+ 100 * hits / iterations,
+ double(damage) / iterations,
+ hitdam,
+ maxdam);
+}
+
+static bool dfs_ranged_combat(FILE *out, int wskill, int mi,
+ const item_def *item)
+{
+ monsters &mon = menv[mi];
+ unsigned long cumulative_damage = 0L;
+ long hits = 0L;
+ int maxdam = 0;
+
+ const int thrown = get_fire_item_index();
+ if (thrown == ENDOFPACK)
+ {
+ mprf("No suitable missiles for combat simulation.");
+ return (false);
+ }
+
+ dfs_set_ranged_skill(wskill, item);
+
+ no_messages mx;
+ const long iter_limit = Options.fsim_rounds;
+ const int hunger = you.hunger;
+ for (long i = 0; i < iter_limit; ++i)
+ {
+ mon.hit_points = mon.max_hit_points;
+ bolt beam;
+ if (throw_it(beam, thrown, &mon))
+ hits++;
+ you.hunger = hunger;
+
+ int damage = (mon.max_hit_points - mon.hit_points);
+ cumulative_damage += damage;
+ if (damage > maxdam)
+ maxdam = damage;
+ }
+ dfs_ranged_item(out, item, wskill, cumulative_damage,
+ iter_limit, hits, maxdam);
+
+ return (true);
+}
+
+static bool dfs_melee_combat(FILE *out, int wskill, int mi,
+ const item_def *item)
+{
+ monsters &mon = menv[mi];
+ unsigned long cumulative_damage = 0L;
+ long hits = 0L;
+ int maxdam = 0;
+
+ dfs_set_melee_skill(wskill, item);
+
+ no_messages mx;
+ const long iter_limit = Options.fsim_rounds;
+ const int hunger = you.hunger;
+ for (long i = 0; i < iter_limit; ++i)
+ {
+ mon.hit_points = mon.max_hit_points;
+ if (you_attack(mi, true))
+ hits++;
+ you.hunger = hunger;
+
+ int damage = (mon.max_hit_points - mon.hit_points);
+ cumulative_damage += damage;
+ if (damage > maxdam)
+ maxdam = damage;
+ }
+ dfs_melee_item(out, item, wskill, cumulative_damage, iter_limit, hits,
+ maxdam);
+
+ return (true);
+}
+
+static bool debug_fight_simulate(FILE *out, int wskill, int mi)
+{
+ int weapon = you.equip[EQ_WEAPON];
+ const item_def *iweap = weapon != -1? &you.inv[weapon] : NULL;
+
+ if (iweap && iweap->base_type == OBJ_WEAPONS
+ && is_range_weapon(*iweap))
+ return dfs_ranged_combat(out, wskill, mi, iweap);
+ else
+ return dfs_melee_combat(out, wskill, mi, iweap);
+}
+
+static std::string dfs_weapon()
+{
+ char item_buf[ITEMNAME_SIZE];
+ if (you.equip[EQ_WEAPON] != -1)
+ {
+ const item_def &weapon = you.inv[ you.equip[EQ_WEAPON] ];
+ item_name(weapon, DESC_PLAIN, item_buf, true);
+
+ if (is_range_weapon(weapon))
+ {
+ const int missile = get_fire_item_index();
+ if (missile < ENDOFPACK)
+ {
+ std::string base = item_buf;
+ base += " with ";
+ in_name(missile, DESC_PLAIN, item_buf, true);
+ return (base + item_buf);
+ }
+ }
+ }
+ else
+ {
+ strncpy(item_buf, "unarmed", sizeof item_buf);
+ }
+ return (item_buf);
+}
+
+static void dfs_title(FILE *o, int mon)
+{
+ char buf[ITEMNAME_SIZE];
+ fprintf(o, "Combat simulation: %s %s vs. %s (%ld turns)\n",
+ species_name(you.species, you.experience_level),
+ you.class_name,
+ moname(menv[mon].type, true, DESC_PLAIN, buf),
+ Options.fsim_rounds);
+ fprintf(o, "Experience: %d\n", you.experience_level);
+ fprintf(o, "Strength : %d\n", you.strength);
+ fprintf(o, "Intel. : %d\n", you.intel);
+ fprintf(o, "Dexterity : %d\n", you.dex);
+ fprintf(o, "\nWeapon : %s\n", dfs_weapon().c_str());
+ fprintf(o, "\n");
+}
+
+static int fsim_stat(int stat)
+{
+ return (stat < 1 ? 1 :
+ stat > 60 ? 60 :
+ stat);
+}
+
+// Writes statistics about a fight to fight.stat in the current directory.
+// For fight purposes, a punching bag is summoned and given lots of hp, and the
+// average damage the player does to the p. bag over 10000 hits is noted,
+// advancing the weapon skill from 0 to 27, and keeping fighting skill to 2/5
+// of current weapon skill.
+void debug_fight_statistics()
+{
+ int punching_bag = get_monnum(Options.fsim_mons.c_str());
+ if (punching_bag == -1)
+ punching_bag = MONS_WORM;
+
+ int mindex = create_dfs_monster(punching_bag, 500);
+ if (mindex == -1)
+ {
+ mprf("Failed to create punching bag");
+ return;
+ }
+
+ FILE *ostat = fopen("fight.stat", "a");
+ if (!ostat)
+ {
+ mprf("Can't write fight.stat: %s", strerror(errno));
+ return;
+ }
+
+ FixedVector<unsigned char, 50> skill_backup = you.skills;
+ int ystr = you.strength,
+ yint = you.intel,
+ ydex = you.dex;
+ int yxp = you.experience_level;
+
+ you.experience_level = Options.fsim_xl;
+ if (you.experience_level < 1)
+ you.experience_level = 1;
+ if (you.experience_level > 27)
+ you.experience_level = 27;
+
+ you.strength = fsim_stat(Options.fsim_str);
+ you.intel = fsim_stat(Options.fsim_int);
+ you.dex = fsim_stat(Options.fsim_dex);
+
+ dfs_title(ostat, mindex);
+ for (int wskill = 0; wskill <= 27; ++wskill)
+ {
+ mesclr();
+ mprf("Calculating average damage for %s at skill %d",
+ dfs_weapon().c_str(), wskill);
+ if (!debug_fight_simulate(ostat, wskill, mindex))
+ goto done_combat_sim;
+
+ fflush(ostat);
+ // Not checking in the combat loop itself; that would be more responsive
+ // for the user, but slow down the sim with all the calls to kbhit().
+ if (kbhit() && getch() == 27)
+ {
+ mprf("Canceling simulation\n");
+ goto done_combat_sim;
+ }
+ }
+ you.skills = skill_backup;
+ you.strength = ystr;
+ you.intel = yint;
+ you.dex = ydex;
+ you.experience_level = yxp;
+
+ mprf("Done fight simulation with %s", dfs_weapon().c_str());
+
+done_combat_sim:
+ fprintf(ostat, "-----------------------------------\n\n");
+
+ fclose(ostat);
+ monster_die(&menv[mindex], KILL_DISMISSED, 0);
+}
+#endif