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
IA4AZVXJEZP6QRPKUUPBLZQWY36PSN4UMGXVLINXJNTKJR2NXHEAC
AAHHHRIYK53GIYQOZIGCBFI6ZM3SKN7OBRNLHQ5WVKV73MPK2H5QC
KOVRG7IPNFPXQNBFVMYEY7T6WM55CHJ37I4E763S6EJ6IZH32FYAC
K2CS6TCX2NDVL2ASEHGP4J4K4IJ6FP3ANNKTSIWVG43HPYSBX6ZQC
RX6575DZOHRUXQUZH34YZGPZJF4STUPLBQDIVTINA2L6LVCKRIGQC
RPOZZWKG5GLPHVZZ7ZKMKS64ZMV2LDCQSARBJFJ6FZOTOKCQO7FAC
SVY2PTCLXR3KNPQAWXVXTTGCC5DR334HOAKHYO3VDDRWM2BWMALAC
O6ZMFKDI3XO2SWPNEYHIPYFDWJR4TVDP5BAATK6LVCVETQID6E7AC
R32CQ6FQJTQLB35P3HENIDCBDT3UWXBBCDAAUWHUQO6G6NKEDPKQC
PKXXBHS3LWLPZI2QVRX22MSQ4R2626IXRSNHFFYHXYTLJJQU54LQC
CKKQS65COJ4KKOWYCKSEAPTH2X6HZSCFOT62JQOK5QDSILBKSFDAC
JQKRFPNPLYBHUWMVQLPX5UB5RRP5PKY6NQ5LRHJRIOY6IKN2AH6AC
PTZ3HVXCKCJ3R2Q6R2MYNSNFJMZ5ZGJEGLBNFC5WG5VQROS6HR3QC
WGM77XP66ZEIRWAOKAJDICJ4ZBEUKGAEY6MZQQJ2LUWG5PCG722QC
K4CJM2GNQKZAPQT3FD2FSG4IXBUUZS42VSXH3QCVSWTZ3JOZT6YQC
3EREXXQMWTOWPI5ZV4UABEIR4RTGPNM7V2O6CG2EOFPHA5GEB2WAC
Y26UG3JWXZG3USM5R36UIZAZWVSC67ZKPE6CSIP3SUBVPB7MZSGQC
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;
}
}
// 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);
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)
{
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)
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))
// 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