summaryrefslogblamecommitdiffstats
path: root/crawl-ref/source/species.cc
blob: 1aae71bf4829dd1d404df5863f51a2bda5e8abcb (plain) (tree)
1
2
3
4
5
6



                    
                    
                   

























































                                                                            

                                              





                                                                        
                                         

























                                                                                
                                               

















                                                                              
                                                 










                                                     
                                                          
 
                                                              
 
                                                


                                       
                                                      


                                                                   
                                     
     

                                                                        




                  
                                                                           











                                                                                
                                                       
 
                    
                        
                          

                                                      
                                         
     


                                            


                                               
                                         
     


                                           

     
                        






                                                                             
                                 
     
                         



























                                                                            

                     













                                                          

                       












                                                                             


                    



























                                                                             








































































                                                                         
#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);
}