diff options
-rw-r--r-- | crawl-ref/source/arena.cc | 282 | ||||
-rw-r--r-- | crawl-ref/source/arena.h | 6 | ||||
-rw-r--r-- | crawl-ref/source/items.cc | 16 | ||||
-rw-r--r-- | crawl-ref/source/monplace.cc | 9 |
4 files changed, 286 insertions, 27 deletions
diff --git a/crawl-ref/source/arena.cc b/crawl-ref/source/arena.cc index 530c421532..aca2daedd9 100644 --- a/crawl-ref/source/arena.cc +++ b/crawl-ref/source/arena.cc @@ -16,7 +16,9 @@ REVISION("$Rev$"); #include "mon-util.h" #include "monstuff.h" #include "monplace.h" +#include "mstuff2.h" #include "output.h" +#include "randart.h" #include "skills2.h" #include "spl-util.h" #include "state.h" @@ -69,11 +71,16 @@ namespace arena bool allow_chain_summons = true; bool allow_zero_xp = false; bool allow_immobile = true; + bool allow_bands = true; bool name_monsters = false; bool random_uniques = false; bool real_summons = false; + bool do_move_spawners = false; std::vector<int> uniques_list; + std::vector<int> spawner_list; + + bool banned_glyphs[256]; std::string arena_type = ""; faction faction_a(true); @@ -189,6 +196,8 @@ namespace arena void setup_level() { + spawner_list.clear(); + if (place.is_valid()) { you.level_type = place.level_type; @@ -271,8 +280,10 @@ namespace arena allow_summons = !strip_tag(spec, "no_summons"); allow_immobile = !strip_tag(spec, "no_immobile"); + allow_bands = !strip_tag(spec, "no_bands"); allow_zero_xp = strip_tag(spec, "allow_zero_xp"); real_summons = strip_tag(spec, "real_summons"); + do_move_spawners = strip_tag(spec, "move_spawners"); cycle_random = strip_tag(spec, "cycle_random"); name_monsters = strip_tag(spec, "names"); @@ -312,6 +323,10 @@ namespace arena "vault.")); } + std::string glyphs = strip_tag_prefix(spec, "ban_glyphs:"); + for (unsigned int i = 0; i < glyphs.size(); i++) + banned_glyphs[(int)glyphs[i]] = true; + std::vector<std::string> factions = split_string(" v ", spec); if (factions.size() == 1) @@ -513,7 +528,7 @@ namespace arena faction_a.won = false; } } - } // count_foes() + } // Returns true as long as at least one member of each faction is alive. bool fight_is_on() @@ -593,6 +608,7 @@ namespace arena switch(chan) { case MSGCH_ERROR: prefix = "ERROR: "; break; + case MSGCH_WARN: prefix = "WARN: "; break; case MSGCH_DIAGNOSTICS: prefix = "DIAG: "; break; case MSGCH_SOUND: prefix = "SOUND: "; break; @@ -605,6 +621,24 @@ namespace arena } } + // Move test spawners to a new position every turn to scatter each + // faction all over the arena. + void move_spawners() + { + if (!do_move_spawners) + return; + + for (unsigned int i = 0; i < spawner_list.size(); i++) + { + monsters* mon = &menv[spawner_list[i]]; + + if (!mon->alive() || mon->type != MONS_TEST_SPAWNER) + continue; + + monster_teleport(mon, true, true); + } + } + void do_fight() { mesclr(true); @@ -625,6 +659,10 @@ namespace arena } } +#ifdef DEBUG_DIAGNOSTICS + mprf("---- Turn #%d ----", turns); +#endif + // Check the consistency of our book-keeping every 100 turns. if ((turns++ % 100) == 0) count_foes(); @@ -638,6 +676,7 @@ namespace arena you.hunger = 10999; //report_foes(); world_reacts(); + move_spawners(); delay(Options.arena_delay); mesclr(); dump_messages(); @@ -847,6 +886,7 @@ monster_type arena_pick_random_monster(const level_id &place, int power, monster_type type = (monster_type) uniques[random2(uniques.size())]; you.unique_creatures[type] = false; + return (type); } @@ -881,10 +921,20 @@ bool arena_veto_random_monster(monster_type type) return (true); if (!arena::allow_zero_xp && mons_class_flag(type, M_NO_EXP_GAIN)) return (true); + if (arena::banned_glyphs[mons_char(type)]) + return (true); return (false); } +bool arena_veto_place_monster(const mgen_data &mg, bool first_band_member, + const coord_def& pos) +{ + return (!arena::allow_bands && !first_band_member + || arena::banned_glyphs[mons_char(mg.cls)]); +} + + void arena_placed_monster(monsters *monster, const mgen_data &mg, bool first_band_member) { @@ -893,6 +943,13 @@ void arena_placed_monster(monsters *monster, const mgen_data &mg, else if (monster->attitude == ATT_HOSTILE) arena::faction_b.active_members++; + if (monster->type == MONS_TEST_SPAWNER) + arena::spawner_list.push_back(monster->mindex()); + +#ifdef DEBUG_DIAGNOSTICS + mprf("%s enters the arena!", monster->name(DESC_CAP_A).c_str()); +#endif + for (int i = 0; i < NUM_MONSTER_SLOTS; i++) { short it = monster->inv[i]; @@ -939,39 +996,212 @@ void arena_monster_died(monsters *monster, killer_type killer, else if (monster->attitude == ATT_HOSTILE) arena::faction_b.active_members--; - const monsters* atk = - (invalid_monster_index(killer_index) || menv[killer_index].type == -1) - ? NULL : &menv[killer_index]; - - if (atk && atk->alive()) + if (arena::faction_a.active_members > 0 + && arena::faction_b.active_members <= 0) + { + arena::faction_a.won = true; + } + else if (arena::faction_b.active_members > 0 + && arena::faction_a.active_members <= 0) { - if (arena::faction_a.active_members > 0 - && arena::faction_b.active_members <= 0) + arena::faction_b.won = true; + } + // Everyone is dead. Is it a tie, or something else? + else if (arena::faction_a.active_members <= 0 + && arena::faction_b.active_members <= 0) + { + if (monster->flags & MF_HARD_RESET && !MON_KILL(killer)) { - arena::faction_a.won = true; + end(1, false, "Last arena monster was dismissed."); } - else if (arena::faction_b.active_members > 0 - && arena::faction_a.active_members <= 0) + // If all monsters are dead and the last one to die is a giant spore + // or ball lightning then that monster's faction is the winner, + // since self destruction is their purpose. But if a trap causes + // the spore to explode and that kills everything it's a tie since + // it counts as the trap killing everyone. + else if (mons_self_destructs(monster) && MON_KILL(killer)) { - arena::faction_b.won = true; + if (monster->attitude == ATT_FRIENDLY) + arena::faction_a.won = true; + else if (monster->attitude == ATT_HOSTILE) + arena::faction_b.won = true; } } - // If all monsters are dead and the last one to die is a giant spore - // or ball lightning then that monster's faction is the winner, - // since self destruction is their purpose. But if a trap causes - // the spore to explode and that kills everything it's a tie since - // it counts as the trap killing everyone. - else if (arena::faction_a.active_members <= 0 - && arena::faction_b.active_members <= 0 - && mons_self_destructs(monster) - && MON_KILL(killer)) +} + +static bool _sort_corpses(int a, int b) +{ + return (mitm[a].special < mitm[b].special); +} + +#define DESTROY_ITEM(i) \ +{ \ + destroy_item(i, true); \ + cull_count++; \ + if (first_avail == NON_ITEM) \ + first_avail = i; \ +} + +int arena_cull_items() +{ + // Try to cull 15% of items. + const int cull_target = MAX_ITEMS * 15 / 100; + + int cull_count = 0; + + std::vector<int> artefacts; + std::vector<int> egos; + std::vector<int> ammo; + std::vector<int> wands; + std::vector<int> potions; + std::vector<int> scrolls; + std::vector<int> corpses; + + int first_avail = NON_ITEM; + +#ifdef DEBUG_DIAGNOSTICS + int non_floor_items = 0; +#endif + + for (int i = 0; i < MAX_ITEMS; i++) { - if (monster->attitude == ATT_FRIENDLY) - arena::faction_a.won = true; - else if (monster->attitude == ATT_HOSTILE) - arena::faction_b.won = true; + // All items in mitm[] are valid when we're called. + const item_def &item(mitm[i]); + +#ifdef DEBUG_DIAGNOSTICS + if (!is_valid_item(item)) + { + mprf("Invalid item in arena_cull_items()!!", MSGCH_ERROR); + non_floor_items++; + continue; + } +#endif + + // We want floor items. + if (!in_bounds(item.pos)) + { +#ifdef DEBUG_DIAGNOSTICS + if (item.pos == coord_def(-1, -1)) + mprf("Player item in arena_cull_items()!!", MSGCH_ERROR); + non_floor_items++; +#endif + continue; + } + + bool cull = false; + switch(item.base_type) + { + case OBJ_WEAPONS: + case OBJ_ARMOUR: + if (is_artefact(item)) + artefacts.push_back(i); + else if (item.special != 0) + egos.push_back(i); + else + // Always cull boring equipment. + cull = true; + break; + + case OBJ_MISSILES: + if (item.sub_type == MI_JAVELIN + || item.sub_type == MI_THROWING_NET) + { + ammo.push_back(i); + } + else + // Arrows/needles/etc on the floor are just clutter. + cull = true; + break; + + case OBJ_WANDS: + if (item.plus == 0) + // Get rid of empty wands. + cull = true; + else + wands.push_back(i); + break; + + case OBJ_SCROLLS: + scrolls.push_back(i); + break; + + case OBJ_POTIONS: + potions.push_back(i); + break; + + case OBJ_CORPSES: + if (item.sub_type == CORPSE_SKELETON) + // If it's rotted away into a skeleton then there's no one + // around who can uses corpses. + cull = true; + else + corpses.push_back(i); + break; + + default: + // Get rid of everything else. + cull = true; + } // switch(item.base_type) + + if (cull) + DESTROY_ITEM(i); + } // for (int i = 0; i < MAX_ITEMS; i++) + + if (cull_count >= cull_target) + { +#ifdef DEBUG_DIAGNOSTICS + mprf(MSGCH_DIAGNOSTICS, "Arena culled %d misc items, done.", + cull_count); +#endif + return (first_avail); } -} + +#ifdef DEBUG_DIAGNOSTICS + if (cull_count > 0) + mprf(MSGCH_DIAGNOSTICS, "Arena culled %d misc items.", cull_count); +#endif + + // Get rid of oldest corpses first. + std::sort(corpses.begin(), corpses.end(), _sort_corpses); + + std::vector<int>* lists[] = {&corpses, &ammo, &egos, &potions, + &scrolls, &wands, &artefacts, NULL}; + +#ifdef DEBUG_DIAGNOSTICS + const char* list_names[] = {"corpses", "ammo", "egos", "potions", + "scrolls", "wands", "artefacts"}; +#endif + + for (int i = 0; lists[i] != NULL; i++) + { + std::vector<int>* vec = lists[i]; + + for (unsigned int j = 0; j < vec->size(); j++) + { + DESTROY_ITEM((*vec)[j]); + if (cull_count >= cull_target) + { +#ifdef DEBUG_DIAGNOSTICS + mprf(MSGCH_DIAGNOSTICS, "Arena culled %d %s, done.", j + 1, + list_names[i]); +#endif + return (first_avail); + } + } // for (unsigned int j = 0; j < vec->size(); j++) +#ifdef DEBUG_DIAGNOSTICS + if (vec->size() > 0) + mprf(MSGCH_DIAGNOSTICS, "Arena culled %d %s.", vec->size(), + list_names[i]); +#endif + } // for (int i = 0; lists[i] != NULL; i++) + +#ifdef DEBUG_DIAGNOSTICS + mprf(MSGCH_DIAGNOSTICS, "Arena only culled %d of a desired %d items.", + cull_count, cull_target); +#endif // DEBUG_DIAGNOSTICS + + return (first_avail); +} // arena_cull_items ///////////////////////////////////////////////////////////////////////////// diff --git a/crawl-ref/source/arena.h b/crawl-ref/source/arena.h index 8352860538..4547957b1a 100644 --- a/crawl-ref/source/arena.h +++ b/crawl-ref/source/arena.h @@ -8,6 +8,8 @@ class level_id; class monsters; class mgen_data; +struct coord_def; + void run_arena(); monster_type arena_pick_random_monster(const level_id &place, int power, @@ -15,9 +17,13 @@ monster_type arena_pick_random_monster(const level_id &place, int power, bool arena_veto_random_monster(monster_type type); +bool arena_veto_place_monster(const mgen_data &mg, bool first_band_member, + const coord_def& pos); void arena_placed_monster(monsters *monster, const mgen_data &mg, bool first_band_member); void arena_monster_died(monsters *monster, killer_type killer, int killer_index, bool silent); + +int arena_cull_items(); #endif diff --git a/crawl-ref/source/items.cc b/crawl-ref/source/items.cc index a05a5d8e9e..200656794c 100644 --- a/crawl-ref/source/items.cc +++ b/crawl-ref/source/items.cc @@ -26,6 +26,7 @@ REVISION("$Rev$"); #include "externs.h" +#include "arena.h" #include "beam.h" #include "branch.h" #include "cloud.h" @@ -60,6 +61,7 @@ REVISION("$Rev$"); #include "skills2.h" #include "spl-book.h" #include "spl-util.h" +#include "state.h" #include "stuff.h" #include "stash.h" #include "tiles.h" @@ -323,6 +325,9 @@ int get_item_slot( int reserve ) { ASSERT( reserve >= 0 ); + if (crawl_state.arena) + reserve = 0; + int item = NON_ITEM; for (item = 0; item < (MAX_ITEMS - reserve); item++) @@ -331,7 +336,16 @@ int get_item_slot( int reserve ) if (item >= MAX_ITEMS - reserve) { - item = (reserve <= 10) ? _cull_items() : NON_ITEM; + if (crawl_state.arena) + { + item = arena_cull_items(); + // If arena_cull_items() can't free up any space then + // _cull_items() won't be able to either, so give up. + if (item == NON_ITEM) + return (NON_ITEM); + } + else + item = (reserve <= 10) ? _cull_items() : NON_ITEM; if (item == NON_ITEM) return (NON_ITEM); diff --git a/crawl-ref/source/monplace.cc b/crawl-ref/source/monplace.cc index d1ace875bf..0a0574b523 100644 --- a/crawl-ref/source/monplace.cc +++ b/crawl-ref/source/monplace.cc @@ -1003,6 +1003,12 @@ static int _place_monster_aux(const mgen_data &mg, ASSERT(mgrd(fpos) == NON_MONSTER); + if (crawl_state.arena) + { + if (arena_veto_place_monster(mg, first_band_member, fpos)) + return (-1); + } + // Now, actually create the monster. (Wheeee!) menv[id].type = mg.cls; menv[id].base_monster = mg.base_type; @@ -2041,6 +2047,9 @@ static int _ood_limit() void mark_interesting_monst(struct monsters* monster, beh_type behaviour) { + if (crawl_state.arena) + return; + bool interesting = false; // Unique monsters are always intersting |