#include "AppHdr.h"
#include "species.h"
#include "options.h"
#include "random.h"
// March 2008: change order of species and jobs on character selection
// screen as suggested by Markus Maier. Summarizing comments below are
// copied directly from Markus' SourceForge comments. (jpeg)
//
// These are listed in two columns to match the selection screen output.
// Take care to list all valid species here, or they cannot be directly
// chosen.
//
// The red draconian is later replaced by a random variant.
// The old and new lists are expected to have the same length.
static species_type old_species_order[] = {
SP_HUMAN, SP_HIGH_ELF,
SP_DEEP_ELF, SP_SLUDGE_ELF,
SP_MOUNTAIN_DWARF, SP_HALFLING,
SP_HILL_ORC, SP_KOBOLD,
SP_MUMMY, SP_NAGA,
SP_OGRE, SP_TROLL,
SP_RED_DRACONIAN, SP_CENTAUR,
SP_DEMIGOD, SP_SPRIGGAN,
SP_MINOTAUR, SP_DEMONSPAWN,
SP_GHOUL, SP_KENKU,
SP_MERFOLK, SP_VAMPIRE,
SP_DEEP_DWARF
};
// Fantasy staples and humanoid creatures come first, then diminutive and
// stealthy creatures, then monstrous creatures, then planetouched and after
// all living creatures finally the undead. (MM)
static species_type new_species_order[] = {
// comparatively human-like looks
SP_HUMAN, SP_HIGH_ELF,
SP_DEEP_ELF, SP_SLUDGE_ELF,
SP_MOUNTAIN_DWARF, SP_DEEP_DWARF,
SP_HILL_ORC, SP_MERFOLK,
// small species
SP_HALFLING, SP_KOBOLD,
SP_SPRIGGAN,
// significantly different body type from human
SP_NAGA, SP_CENTAUR,
SP_OGRE, SP_TROLL,
SP_MINOTAUR, SP_KENKU,
SP_RED_DRACONIAN,
// celestial species
SP_DEMIGOD, SP_DEMONSPAWN,
// undead species
SP_MUMMY, SP_GHOUL,
SP_VAMPIRE
};
species_type random_draconian_player_species()
{
const int num_drac = SP_PALE_DRACONIAN - SP_RED_DRACONIAN + 1;
return static_cast<species_type>(SP_RED_DRACONIAN + random2(num_drac));
}
species_type get_species(const int index)
{
if(index < 0 || index >= ng_num_species())
return (SP_UNKNOWN);
return (Options.use_old_selection_order ? old_species_order[index]
: new_species_order[index]);
}
static const char * Species_Abbrev_List[NUM_SPECIES] =
{ "Hu", "HE", "DE", "SE", "MD", "Ha",
"HO", "Ko", "Mu", "Na", "Og", "Tr",
// the draconians
"Dr", "Dr", "Dr", "Dr", "Dr", "Dr", "Dr", "Dr", "Dr", "Dr",
"Ce", "DG", "Sp", "Mi", "DS", "Gh", "Ke", "Mf", "Vp", "DD",
// placeholders
"El", "HD", "OM", "GE", "Gn" };
int get_species_index_by_abbrev(const char *abbrev)
{
COMPILE_CHECK(ARRAYSZ(Species_Abbrev_List) == NUM_SPECIES, c1);
for (unsigned i = 0; i < ARRAYSZ(old_species_order); i++)
{
const int sp = (Options.use_old_selection_order ? old_species_order[i]
: new_species_order[i]);
if (tolower( abbrev[0] ) == tolower( Species_Abbrev_List[sp][0] )
&& tolower( abbrev[1] ) == tolower( Species_Abbrev_List[sp][1] ))
{
return (i);
}
}
return (-1);
}
int get_species_index_by_name(const char *name)
{
unsigned int i;
int sp = -1;
std::string::size_type pos = std::string::npos;
char lowered_buff[80];
strncpy(lowered_buff, name, sizeof(lowered_buff));
strlwr(lowered_buff);
for (i = 0; i < ARRAYSZ(old_species_order); i++)
{
const species_type real_sp
= (Options.use_old_selection_order ? old_species_order[i]
: new_species_order[i]);
const std::string lowered_species =
lowercase_string(species_name(real_sp,1));
pos = lowered_species.find(lowered_buff);
if (pos != std::string::npos)
{
sp = i;
if (pos == 0) // prefix takes preference
break;
}
}
return (sp);
}
const char *get_species_abbrev(species_type which_species)
{
ASSERT(which_species >= 0 && which_species < NUM_SPECIES);
return (Species_Abbrev_List[which_species]);
}
// Needed for debug.cc and hiscores.cc.
species_type get_species_by_abbrev(const char *abbrev)
{
int i;
COMPILE_CHECK(ARRAYSZ(Species_Abbrev_List) == NUM_SPECIES, c1);
for (i = 0; i < NUM_SPECIES; i++)
{
if (tolower(abbrev[0]) == tolower(Species_Abbrev_List[i][0])
&& tolower(abbrev[1]) == tolower(Species_Abbrev_List[i][1]))
{
break;
}
}
return ((i < NUM_SPECIES) ? static_cast<species_type>(i) : SP_UNKNOWN);
}
int ng_num_species()
{
// The list musn't be longer than the number of actual species.
COMPILE_CHECK(ARRAYSZ(old_species_order) <= NUM_SPECIES, c1);
// Check whether the two lists have the same size.
COMPILE_CHECK(ARRAYSZ(old_species_order) == ARRAYSZ(new_species_order), c2);
return (ARRAYSZ(old_species_order));
}
// Does a case-sensitive lookup of the species name supplied.
species_type str_to_species(const std::string &species)
{
species_type sp;
if (species.empty())
return SP_UNKNOWN;
// first look for full name (e.g. Green Draconian)
for (int i = 0; i < NUM_SPECIES; ++i)
{
sp = static_cast<species_type>(i);
if (species == species_name(sp, 10))
return (sp);
}
// nothing found, try again with plain name
for (int i = 0; i < NUM_SPECIES; ++i)
{
sp = static_cast<species_type>(i);
if (species == species_name(sp, 1))
return (sp);
}
return (SP_UNKNOWN);
}
std::string species_name(species_type speci, int level, bool genus, bool adj)
// defaults: false false
{
std::string res;
switch (species_genus(speci))
{
case GENPC_DRACONIAN:
if (adj || genus) // adj doesn't care about exact species
res = "Draconian";
else
{
if (level < 7)
res = "Draconian";
else
{
switch (speci)
{
case SP_RED_DRACONIAN: res = "Red Draconian"; break;
case SP_WHITE_DRACONIAN: res = "White Draconian"; break;
case SP_GREEN_DRACONIAN: res = "Green Draconian"; break;
case SP_YELLOW_DRACONIAN: res = "Yellow Draconian"; break;
case SP_GREY_DRACONIAN: res = "Grey Draconian"; break;
case SP_BLACK_DRACONIAN: res = "Black Draconian"; break;
case SP_PURPLE_DRACONIAN: res = "Purple Draconian"; break;
case SP_MOTTLED_DRACONIAN: res = "Mottled Draconian"; break;
case SP_PALE_DRACONIAN: res = "Pale Draconian"; break;
case SP_BASE_DRACONIAN:
default:
res = "Draconian";
break;
}
}
}
break;
case GENPC_ELVEN:
if (adj) // doesn't care about species/genus
res = "Elven";
else if (genus)
res = "Elf";
else
{
switch (speci)
{
case SP_HIGH_ELF: res = "High Elf"; break;
case SP_DEEP_ELF: res = "Deep Elf"; break;
case SP_SLUDGE_ELF: res = "Sludge Elf"; break;
default: res = "Elf"; break;
}
}
break;
case GENPC_DWARVEN:
if (adj) // doesn't care about species/genus
res = "Dwarven";
else if (genus)
res = "Dwarf";
else
{
switch (speci)
{
case SP_MOUNTAIN_DWARF: res = "Mountain Dwarf"; break;
case SP_DEEP_DWARF: res = "Deep Dwarf"; break;
default: res = "Dwarf"; break;
}
}
break;
case GENPC_NONE:
default:
switch (speci)
{
case SP_HUMAN: res = "Human"; break;
case SP_HALFLING: res = "Halfling"; break;
case SP_KOBOLD: res = "Kobold"; break;
case SP_MUMMY: res = "Mummy"; break;
case SP_NAGA: res = "Naga"; break;
case SP_CENTAUR: res = "Centaur"; break;
case SP_SPRIGGAN: res = "Spriggan"; break;
case SP_MINOTAUR: res = "Minotaur"; break;
case SP_KENKU: res = "Kenku"; break;
case SP_HILL_ORC:
res = (adj ? "Orcish" : genus ? "Orc" : "Hill Orc");
break;
case SP_OGRE: res = (adj ? "Ogreish" : "Ogre"); break;
case SP_TROLL: res = (adj ? "Trollish" : "Troll"); break;
case SP_DEMIGOD: res = (adj ? "Divine" : "Demigod"); break;
case SP_DEMONSPAWN: res = (adj ? "Demonic" : "Demonspawn"); break;
case SP_GHOUL: res = (adj ? "Ghoulish" : "Ghoul"); break;
case SP_MERFOLK: res = (adj ? "Merfolkian" : "Merfolk"); break;
case SP_VAMPIRE: res = (adj ? "Vampiric" : "Vampire"); break;
default: res = (adj ? "Yakish" : "Yak"); break;
}
}
return res;
}
int species_has_claws(species_type species)
{
if (species == SP_TROLL)
return (3);
if (species == SP_GHOUL)
return (1);
return (0);
}
genus_type species_genus(species_type species)
{
switch (species)
{
case SP_RED_DRACONIAN:
case SP_WHITE_DRACONIAN:
case SP_GREEN_DRACONIAN:
case SP_YELLOW_DRACONIAN:
case SP_GREY_DRACONIAN:
case SP_BLACK_DRACONIAN:
case SP_PURPLE_DRACONIAN:
case SP_MOTTLED_DRACONIAN:
case SP_PALE_DRACONIAN:
case SP_BASE_DRACONIAN:
return (GENPC_DRACONIAN);
case SP_HIGH_ELF:
case SP_DEEP_ELF:
case SP_SLUDGE_ELF:
return (GENPC_ELVEN);
case SP_MOUNTAIN_DWARF:
case SP_DEEP_DWARF:
return (GENPC_DWARVEN);
case SP_OGRE:
return (GENPC_OGRE);
default:
return (GENPC_NONE);
}
}
size_type species_size(species_type species, size_part_type psize)
{
switch (species)
{
case SP_OGRE:
case SP_TROLL:
return (SIZE_LARGE);
case SP_NAGA:
// Most of their body is on the ground giving them a low profile.
if (psize == PSIZE_TORSO || psize == PSIZE_PROFILE)
return (SIZE_MEDIUM);
else
return (SIZE_LARGE);
case SP_CENTAUR:
return ((psize == PSIZE_TORSO) ? SIZE_MEDIUM : SIZE_LARGE);
case SP_SPRIGGAN:
return (SIZE_LITTLE);
case SP_HALFLING:
case SP_KOBOLD:
return (SIZE_SMALL);
default:
return(SIZE_MEDIUM);
}
}
bool is_valid_species(species_type species)
{
return (species >= 0 && species < NUM_SPECIES);
}