summaryrefslogtreecommitdiffstats
path: root/crawl-ref/source/arena.cc
diff options
context:
space:
mode:
authordshaligram <dshaligram@c06c8d41-db1a-0410-9941-cceddc491573>2008-12-31 08:41:27 +0000
committerdshaligram <dshaligram@c06c8d41-db1a-0410-9941-cceddc491573>2008-12-31 08:41:27 +0000
commit86d488e3efa0b1d8ec12223527235e3ef3a5fec2 (patch)
tree88b3d97a6f74ea6eb21405b3072e1e1a9c6830b0 /crawl-ref/source/arena.cc
parent496a1b4c58651aa28878db51bb1d5f3534d8a0a5 (diff)
downloadcrawl-ref-86d488e3efa0b1d8ec12223527235e3ef3a5fec2.tar.gz
crawl-ref-86d488e3efa0b1d8ec12223527235e3ef3a5fec2.zip
Add arena mode, activated on the command-line by 'crawl -arena "monster v monster"' (eg: crawl -arena "Sigmund v Jessica") to let monsters fight each other undisturbed by the player. Good to examine monster AI and monster behaviour when the player is AWOL.
git-svn-id: https://crawl-ref.svn.sourceforge.net/svnroot/crawl-ref/trunk@8059 c06c8d41-db1a-0410-9941-cceddc491573
Diffstat (limited to 'crawl-ref/source/arena.cc')
-rw-r--r--crawl-ref/source/arena.cc450
1 files changed, 450 insertions, 0 deletions
diff --git a/crawl-ref/source/arena.cc b/crawl-ref/source/arena.cc
new file mode 100644
index 0000000000..95855fc328
--- /dev/null
+++ b/crawl-ref/source/arena.cc
@@ -0,0 +1,450 @@
+#include "AppHdr.h"
+
+#include "externs.h"
+#include "arena.h"
+#include "chardump.h"
+#include "cio.h"
+#include "dungeon.h"
+#include "initfile.h"
+#include "libutil.h"
+#include "maps.h"
+#include "message.h"
+#include "mon-util.h"
+#include "monstuff.h"
+#include "monplace.h"
+#include "output.h"
+#include "skills2.h"
+#include "spl-util.h"
+#include "state.h"
+#include "version.h"
+#include "view.h"
+
+extern void world_reacts();
+
+namespace arena
+{
+ // A faction is just a big list of monsters. Monsters will be dropped
+ // around the appropriate marker.
+ struct faction
+ {
+ std::string desc;
+ mons_list members;
+ bool friendly;
+
+ faction(bool fr) : members(), friendly(fr) { }
+
+ void place_at(const coord_def &pos);
+
+ void clear()
+ {
+ members.clear();
+ }
+ };
+
+ int total_trials = 0;
+
+ int trials_done = 0;
+ int team_a_wins = 0;
+ bool allow_summons = true;
+ faction faction_a(true);
+ faction faction_b(false);
+
+ void adjust_monsters()
+ {
+ if (!allow_summons)
+ {
+ for (int m = 0; m < MAX_MONSTERS; ++m)
+ {
+ monsters *mons(&menv[m]);
+ if (!mons->alive())
+ continue;
+
+ monster_spells &spells(mons->spells);
+ for (int i = 0; i < NUM_MONSTER_SPELL_SLOTS; ++i)
+ {
+ spell_type sp = spells[i];
+ if (spell_typematch(sp, SPTYP_SUMMONING))
+ spells[i] = SPELL_NO_SPELL;
+ }
+ }
+ }
+ }
+
+ void faction::place_at(const coord_def &pos)
+ {
+ ASSERT(in_bounds(pos));
+ for (int i = 0, size = members.size(); i < size; ++i)
+ {
+ mons_spec spec = members.get_monster(i);
+
+ if (friendly)
+ spec.attitude = ATT_FRIENDLY;
+
+ for (int q = 0; q < spec.quantity; ++q)
+ {
+ const coord_def place =
+ find_newmons_square_contiguous(MONS_GIANT_BAT, pos, 6);
+ if (!in_bounds(place))
+ break;
+
+ const int imon = dgn_place_monster(spec, you.your_level,
+ place, false, true, false);
+ if (imon == -1)
+ end(1, false, "Failed to create monster at (%d,%d)",
+ place.x, place.y);
+ }
+ }
+ }
+
+ void center_print(unsigned sz, std::string text, int number = -1)
+ {
+ if (number >= 0)
+ text = make_stringf("(%d) %s", number, text.c_str());
+
+ if (text.length() > sz)
+ text = text.substr(0, sz);
+
+ int padding = (sz - text.length()) / 2 + text.length();
+ cprintf("%*s", padding, text.c_str());
+ }
+
+ void setup_level()
+ {
+ dgn_reset_level();
+
+ for (int x = 0; x < GXM; ++x)
+ for (int y = 0; y < GYM; ++y)
+ grd[x][y] = DNGN_ROCK_WALL;
+
+ unwind_bool gen(Generating_Level, true);
+
+ typedef unwind_var< std::set<std::string> > unwind_stringset;
+
+ const unwind_stringset mtags(you.uniq_map_tags);
+ const unwind_stringset mnames(you.uniq_map_names);
+
+ const map_def *map = random_map_for_tag("arena_level", false);
+ ASSERT(map);
+ dgn_place_map(map, true, true);
+
+ if (!env.rock_colour)
+ env.rock_colour = CYAN;
+ if (!env.floor_colour)
+ env.floor_colour = LIGHTGREY;
+ }
+
+ std::string find_monster_spec()
+ {
+ if (!SysEnv.arena_teams.empty())
+ return (SysEnv.arena_teams);
+ throw std::string("No monsters specified for the arena.");
+ }
+
+ void parse_faction(faction &fact, std::string spec)
+ throw (std::string)
+ {
+ fact.clear();
+ fact.desc = spec;
+
+ std::vector<std::string> monsters = split_string(",", spec);
+ for (int i = 0, size = monsters.size(); i < size; ++i)
+ {
+ const std::string err = fact.members.add_mons(monsters[i], false);
+ if (!err.empty())
+ throw err;
+ }
+ }
+
+ void parse_monster_spec()
+ throw (std::string)
+ {
+ std::string spec = find_monster_spec();
+
+ allow_summons = !strip_tag(spec, "no_summons");
+
+ const int ntrials = strip_number_tag(spec, "t:");
+ if (ntrials != TAG_UNFOUND && ntrials >= 1 && ntrials <= 99
+ && !total_trials)
+ total_trials = ntrials;
+
+ std::vector<std::string> factions = split_string(" v ", spec);
+
+ if (factions.size() == 1)
+ factions = split_string(" vs ", spec);
+
+ if (factions.size() != 2)
+ throw make_stringf("Expected arena monster spec \"xxx v yyy\", "
+ "but got \"%s\"", spec.c_str());
+
+ try
+ {
+ parse_faction(faction_a, factions[0]);
+ parse_faction(faction_b, factions[1]);
+ }
+ catch (const std::string &err)
+ {
+ throw make_stringf("Bad monster spec \"%s\": %s",
+ spec.c_str(),
+ err.c_str());
+ }
+ }
+
+ void setup_monsters()
+ throw (std::string)
+ {
+ unwind_var< FixedVector<bool, NUM_MONSTERS> >
+ uniq(you.unique_creatures);
+
+ parse_monster_spec();
+ coord_def place_a(dgn_find_feature_marker(DNGN_STONE_STAIRS_UP_I));
+ coord_def place_b(dgn_find_feature_marker(DNGN_STONE_STAIRS_DOWN_I));
+ faction_a.place_at(place_a);
+ faction_b.place_at(place_b);
+ adjust_monsters();
+ }
+
+ void show_fight_banner(bool after_fight = false)
+ {
+ int line = 1;
+
+ cgotoxy(1, line++, GOTO_STAT);
+ textcolor(WHITE);
+ center_print(crawl_view.hudsz.x,
+ "Crawl " VER_NUM VER_QUAL " " VERSION_DETAIL);
+ line++;
+
+ cgotoxy(1, line++, GOTO_STAT);
+ textcolor(YELLOW);
+ center_print(crawl_view.hudsz.x, faction_a.desc,
+ total_trials ? team_a_wins : -1);
+ cgotoxy(1, line++, GOTO_STAT);
+ textcolor(LIGHTGREY);
+ center_print(crawl_view.hudsz.x, "vs");
+ cgotoxy(1, line++, GOTO_STAT);
+ textcolor(YELLOW);
+ center_print(crawl_view.hudsz.x, faction_b.desc,
+ total_trials ? trials_done - team_a_wins : -1);
+
+ if (total_trials > 1 && trials_done < total_trials)
+ {
+ cgotoxy(1, line++, GOTO_STAT);
+ textcolor(BROWN);
+ center_print(crawl_view.hudsz.x,
+ make_stringf("Round %d of %d",
+ after_fight ? trials_done
+ : trials_done + 1,
+ total_trials));
+ }
+ else
+ {
+ cgotoxy(1, line++, GOTO_STAT);
+ textcolor(BROWN);
+ clear_to_end_of_line();
+ }
+ }
+
+ void setup_others()
+ {
+ you.species = SP_HUMAN;
+ you.char_class = JOB_FIGHTER;
+ you.experience_level = 27;
+
+ you.mutation[MUT_ACUTE_VISION] = 3;
+
+ coord_def yplace(dgn_find_feature_marker(DNGN_ESCAPE_HATCH_UP));
+ // Fix up the viewport.
+ you.moveto(yplace);
+
+ strcpy(you.your_name, "Arena");
+
+ you.hp = you.hp_max = 99;
+ you.your_level = 20;
+
+ Options.show_gold_turns = false;
+
+ show_fight_banner();
+ }
+
+ void expand_mlist(int exp)
+ {
+ crawl_view.mlistp.y -= exp;
+ crawl_view.mlistsz.y += exp;
+ }
+
+ void setup_fight()
+ throw (std::string)
+ {
+ //no_messages mx;
+ setup_level();
+
+ // Monster set up may block waiting for matchups.
+ setup_monsters();
+
+ setup_others();
+ }
+
+ // Returns true as long as at least one member of each faction is alive.
+ bool fight_is_on()
+ {
+ bool found_friend = false;
+ bool found_enemy = false;
+ for (int i = 0; i < MAX_MONSTERS; ++i)
+ {
+ const monsters *mons(&menv[i]);
+ if (mons->alive())
+ {
+ if (mons->attitude == ATT_FRIENDLY)
+ found_friend = true;
+ else if (mons->attitude == ATT_HOSTILE)
+ found_enemy = true;
+ if (found_friend && found_enemy)
+ return (true);
+ }
+ }
+ return (false);
+ }
+
+ void report_foes()
+ {
+ for (int i = 0; i < MAX_MONSTERS; ++i)
+ {
+ monsters *mons(&menv[i]);
+ if (mons->alive())
+ {
+ if (mons->type == MONS_SIGMUND)
+ {
+ coord_def where;
+ if (mons->get_foe())
+ where = mons->get_foe()->pos();
+ mprf("%s (%d,%d) foe: %s (%d,%d)",
+ mons->name(DESC_PLAIN).c_str(),
+ mons->pos().x, mons->pos().y,
+ mons->get_foe()? mons->get_foe()->name(DESC_PLAIN).c_str()
+ : "(none)",
+ where.x, where.y);
+ }
+ }
+ }
+ }
+
+ void fixup_foes()
+ {
+ for (int i = 0; i < MAX_MONSTERS; ++i)
+ {
+ monsters *mons(&menv[i]);
+ if (mons->alive())
+ {
+ behaviour_event(mons, ME_DISTURB, MHITNOT, mons->pos());
+ }
+ }
+ }
+
+ bool friendlies_win()
+ {
+ for (int i = 0; i < MAX_MONSTERS; ++i)
+ {
+ monsters *mons(&menv[i]);
+ if (mons->alive())
+ return (mons->attitude == ATT_FRIENDLY);
+ }
+ return (false);
+ }
+
+ void do_fight()
+ {
+ mesclr(true);
+ {
+ cursor_control coff(false);
+ while (fight_is_on())
+ {
+ if (kbhit() && getch() == ESCAPE)
+ end(0, false, "Canceled contest at user request");
+
+ viewwindow(true, false);
+ unwind_var<coord_def> pos(you.position);
+ // Move hero offscreen.
+ you.position.y = -1;
+ you.time_taken = 10;
+ //report_foes();
+ world_reacts();
+ delay(Options.arena_delay);
+ mesclr();
+ }
+ viewwindow(true, false);
+ }
+
+ mesclr();
+
+ const bool team_a_won = friendlies_win();
+
+ trials_done++;
+
+ if (team_a_won)
+ team_a_wins++;
+
+ show_fight_banner(true);
+
+ mprf("Winner: %s!",
+ team_a_won ? faction_a.desc.c_str() : faction_b.desc.c_str());
+ }
+
+ void global_setup()
+ {
+ expand_mlist(5);
+ }
+
+ void write_results()
+ {
+ if (FILE *f = fopen("arena.result", "w"))
+ {
+ fprintf(f, "%d-%d\n", team_a_wins, trials_done - team_a_wins);
+ fclose(f);
+ }
+ }
+
+ void write_error(const std::string &error)
+ {
+ if (FILE *f = fopen("arena.result", "w"))
+ {
+ fprintf(f, "err: %s\n", error.c_str());
+ fclose(f);
+ }
+ }
+
+ void simulate()
+ {
+ init_level_connectivity();
+ do
+ {
+ try
+ {
+ setup_fight();
+ }
+ catch (const std::string &error)
+ {
+ write_error(error);
+ end(0, false, "%s", error.c_str());
+ }
+ do_fight();
+
+ if (trials_done < total_trials)
+ delay(Options.arena_delay * 8);
+ } while (trials_done < total_trials);
+
+ if (total_trials > 0)
+ {
+ mprf("Final score: %s (%d); %s (%d)",
+ faction_a.desc.c_str(), team_a_wins,
+ faction_b.desc.c_str(), trials_done - team_a_wins);
+ delay(Options.arena_delay * 8);
+ }
+
+ write_results();
+ }
+}
+
+void run_arena()
+{
+ arena::global_setup();
+ arena::simulate();
+}