summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorzelgadis <zelgadis@c06c8d41-db1a-0410-9941-cceddc491573>2009-01-07 12:03:36 +0000
committerzelgadis <zelgadis@c06c8d41-db1a-0410-9941-cceddc491573>2009-01-07 12:03:36 +0000
commit7ce4f0c31ffe27368d7a62298dcb45fd244904b1 (patch)
tree582135bc457bb1aa6d8978f3208f291469719c59
parentf001432886469258339851993d506a8ec3d0b097 (diff)
downloadcrawl-ref-7ce4f0c31ffe27368d7a62298dcb45fd244904b1.tar.gz
crawl-ref-7ce4f0c31ffe27368d7a62298dcb45fd244904b1.zip
Yet another fix to the arena win/loss/tie logic.
During arena mode don't reserve any of mitm[] when creating new items, and when mitm[] fills up call arena_cull_items() instead of _cull_items(), since in arena mode we can cull via how boring/interesting the items are rather than having to consider game balance and fairness to the player. Allow the arena to veto monster placement, rather than culling them immediately after they're placed. New arena tags: * "no_bands" prevents band members from being placed. * "move_spawners" teleports test spawners every turn to spread their summons randomly over the arena. * "ban_glyphs:" lists a set of text glyphs of types of monsters which shouldn't be allowed in the arena. git-svn-id: https://crawl-ref.svn.sourceforge.net/svnroot/crawl-ref/trunk@8300 c06c8d41-db1a-0410-9941-cceddc491573
-rw-r--r--crawl-ref/source/arena.cc282
-rw-r--r--crawl-ref/source/arena.h6
-rw-r--r--crawl-ref/source/items.cc16
-rw-r--r--crawl-ref/source/monplace.cc9
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