Permalink
Switch branches/tags
abyssal-overworld abyssrun adamant adrenaline_rush all_bits altarstats aphorism146 ashtier aspirants avg_hp_extended avg_hp_numeric balefularc basajaun bearkin bonerider boriscaling bosch boulder catlobe chaoscrawl charms-reform cia-name-fullemail city-connection cloud-overlays cloud_gen console-polearm container contrib-updates-in-progress contribguide councilgod crawl-map-inheritance crypt-tiles cup-of-charity cyc datafiles dazzling_ghosts ddredheal debian-trunk demigods des_load destruction devtest diffusion distortionbolt door_items_solidity door_items dpegs_dynamic_monsters ds_trade_offs earthspells eatingprompt effect-timer elf-vaults enslaved-monster-parsing evoker-playstyle evokers faith_and_reason faithful fancyhell farmer fedhas_decay firewalk flags flatweap floodkiller food-time food-unification forest_wyrm forest forge_dwarves frenzy frogs fsim_acgdrev gammafunk-patch-1 gauntlet gcc_cxx_assert gdr ghost_fixes gitorious-merge-requests/2 gitorious-merge-requests/3 gitorious-merge-requests/4 gitorious-merge-requests/5 gitorious-merge-requests/6 gitorious-merge-requests/7 gitorious-merge-requests/8 gitorious-merge-requests/9 gitorious-merge-requests/10 gitorious-merge-requests/11 gitorious-merge-requests/12 gitorious-merge-requests/13 gitorious-merge-requests/14 gitorious-merge-requests/16 gitorious-merge-requests/17 gitorious-merge-requests/18 gitorious-merge-requests/20 gitorious-merge-requests/21 gitorious-merge-requests/22 gitorious-merge-requests/23 gitorious-merge-requests/24 gitorious-merge-requests/25 gitorious-merge-requests/26 gitorious-merge-requests/27 gitorious-merge-requests/28 gitorious-merge-requests/29 gitorious-merge-requests/30 gitorious-merge-requests/31 gitorious-merge-requests/32 gnoll goldify-books graceful_shutdown halftone harpoon header_simplification hell-effect-end hexcrawl hohoho hotspot imp instapickupdrop insulation koboldcultist labrework laplace layout lighting_form lobster-trap marksperson master melee_linear_damage meleemoves merc-shops mons-gods monsterspelldesc more-xv-resists move-to-trap movement-behaviours movespeed msvc2012 mulch_ado_about_nothing multisprint mutation_cleanup myrmec myrmecia new-layouts new-tree-tiles new_nemelex new_squarelos newdolls newgame-menus nfm-bondage-compat nfm-cast-light nimble-hulk no-crawl-ref no_backtracking_god no_item_transit nomes nospawn nostalgia notzombiehands numpad-fix oubliette panlord-info parareform plutonians polypotion pr-599 pr554 pretty_shiny_sparkles props pubby_swamp race_reordering randbook-rare-spells randeffects_malmutate randfood random-choose-weighted-double randon-choose-weighted-lambda ranged_reform rc-control reaver remove_chance_breaks require_rename resize ruin_everything safer-bitfield salamander sanitize_inscripts savefile sdl-2.0.7 seen-test segfault-debug sewer-water-tiles shard shrapnel simple_yesno simpleclouds simplesif sixtytwo slowokhle small_dwarves smithgod_rebased smithgod snake-enemies spell-sources spellres spiderstuff sponcor spooky_cleanup squarelos statlockgnolls steamblast stone_soup-0.1.3 stone_soup-0.1.4 stone_soup-0.1.5 stone_soup-0.1.6 stone_soup-0.1.7 stone_soup-0.2 stone_soup-0.3 stone_soup-0.4 stone_soup-0.5 stone_soup-0.6 stone_soup-0.7 stone_soup-0.8 stone_soup-0.9 stone_soup-0.10 stone_soup-0.11 stone_soup-0.12 stone_soup-0.13 stone_soup-0.14 stone_soup-0.15 stone_soup-0.16 stone_soup-0.17 stone_soup-0.18 stone_soup-0.19 stone_soup-0.20 stone_soup-0.21 stone_soup-0.22 stone_soup stream-drakes taunt thaw thinskin thorn_god tileschat-mute tombhatch tornado4 transporter travis-cc travis-test travis tutorial_kiss ugliness ungendered uniq.seen vehumet_spells wand_merging wand_stacking wandcharges water-beetle webtiles-changes-bsr webtiles-changes-score webtiles-changes webtiles-mouse-control webtiles-options webtiles-unicode willitblend win32-backtrace wip-coloured-FAQ wip-corpse_name wip-ring-jiyva wip-simulacra-tiles wip-splash-logo wizlab_changes zin-mutation zombieform
Nothing to show
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
138 lines (117 sloc) 9.26 KB
Written by Cerol, 2017/02/09
Species are the biggest factor you choose when creating a character in Crawl, and the diversity of options helps make Crawl stand out from other classic roguelikes. On a quick, serious game design note: species aren't balanced against anything in particular, power-wise. In DCSS, it's more important to be unique and interesting than asymmetrically balanced correctly. Human, mummies, and minotaurs are vastly different and interesting in their own ways, but not on the same level.
Because there are so many options for species, theming a new species can be difficult to make them unique. In today's example, we're simply going to revise a classic, the Mountain Dwarf.
First, we go to species-type.h and update the species enums with a new value. Adding enums is going to be a common step in most any addition to the game.
#if TAG_MAJOR_VERSION == 34
SP_DJINNI,
SP_LAVA_ORC,
#endif
SP_GARGOYLE,
SP_FORMICID,
SP_VINE_STALKER,
SP_MD_REVISED, //In the right spot
NUM_SPECIES,
SP_UNKNOWN = 100,
SP_RANDOM = 101,
SP_VIABLE = 102,
};
Unlike many other species, particuarly Lava Orc and Djinni, Mountain Dwarves were entirely removed from the code base. LO and Dj still have everything present to play, but they've been removed from the species selection menu instead of completely stripped out of the whole game (and you should probably do the same if you remove a species). I will use the SP_MD_REVISED tag for this to make it clear that I'm working on my new version, not restoring the old one as it was.
Next up, we go to species-data.h to fill in the core entry for our species. Once again, the basic struct is reasonly well documented and clear.
struct species_def
{
const char* abbrev; ///< Two-letter abbreviation
const char* name; ///< Main name
const char* adj_name; ///< Adjectival form of name; if null, use name
const char* genus_name; ///< Genus name; if null, use name
species_flags flags; ///< Miscellaneous flags
// The following three need to be 2 lines after the name for gen-apt.pl:
int xp_mod; ///< Experience level modifier
int hp_mod; ///< HP modifier (in tenths)
int mp_mod; ///< MP modifier
int mr_mod; ///< MR modifier (multiplied by XL for base MR)
monster_type monster_species; ///< Corresponding monster (for display)
habitat_type habitat; ///< Where it can live; HT_WATER -> no penalties
undead_state_type undeadness; ///< What kind of undead (if any)
size_type size; ///< Size of body
int s, i, d; ///< Starting stats contribution
set<stat_type> level_stats; ///< Which stats to gain on level-up
int how_often; ///< When to level-up stats
vector<level_up_mutation> level_up_mutations; ///< Mutations on level-up
vector<string> verbose_fake_mutations; ///< Additional information on 'A'
vector<string> terse_fake_mutations; ///< Additional information on '%'
vector<job_type> recommended_jobs; ///< Which jobs are "good" for it
vector<skill_type> recommended_weapons; ///< Which weapons types are "good"
};
Species has a much bigger range of potential changes compared to background. More of these options can be null compared to backgrounds, so you should double-check on a few species to see examples of how these can vary. Particularly odd is the stat_choice entry, which handles choosing which of 2 or 3 stats to gain, gaining random stats, gaining a fixed stat, or gaining double of the stat picked, and on vary ranges of levels.
The most complex part of this block is the mutation section. This is how your innate abilties and properties are handled most of the time. If you want to add in an existing mutation and fake-mutations (descriptor text for effects that aren't mutations proper), here is where they go. If you want to add new abilities, you'll also have to add new mutations.
Again, it's often easiest to pick an existing entry and copy/paste it to minimize mistakes in getting the structure right.
{ SP_MD_REVISED, {
"MD",
"Mountain Dwarf", "Dwarven", "Dwarf",
SPF_NONE,
-2, 3, 1, 8, //XP level mod, HP mod, MP mod, MR mod
MONS_DEEP_DWARF, //Creating a new monster is out of scope for this tutorial
HT_LAND, US_UNDEAD, SIZE_MEDIUM,
11, 8, 8, // 27 total, STR - INT - DEX
{ STAT_STR, STAT_DEX }, 4, //Choose to upgrade STR or DEX every 4 levels.
{ { MUT_PREHENSILE_BEARD, 1, 1 }, { MUT_PASSIVE_MAPPING, 1, 1 },
{ MUT_PASSIVE_MAPPING, 1, 9 }, { MUT_PASSIVE_MAPPING, 1, 18 },
{ MUT_LASER_EYES, 1, 1 }, },
{ "You are the best type of dwarf",},
{ "dwarfy greatness",},
{ JOB_FIGHTER, JOB_HUNTER, JOB_BERSERKER, JOB_NECROMANCER,
JOB_EARTH_ELEMENTALIST },
{ SK_UNARMED_COMBAT, SK_AXES, SK_SHORT_BLADES, SK_THROWING, SK_SLINGS },
} },
Here, we've made MD undead, since the devs killed them long ago and they've finally made their return from the afterlife. Passive mapping is kept to share a common point with the deep dwarves for theme. We're also adding in our 2 new mutations from last time as innate racial mutations, and added a fake mutation to remind everyone we're awesome. There's no actual code behind the fake mutation entry here, but many species use it to explain out gameplay elements that are species-dependent without being a proper mutation (like nagas not being able to wear boots, and draconian breath weapons).
Next we need to add entries for our species in aptitudes.h. This is a big list of skills and the modifier the species gets to it. This file isn't sorted in any particular order, and uses a macro shortcut for assigning value. Another place to copy, paste, and modify for minimal mistakes:
// SP_MD_REVISED,
APT(SP_MD_REVISED, SK_FIGHTING, 1),
APT(SP_MD_REVISED, SK_SHORT_BLADES, 2),
APT(SP_MD_REVISED, SK_LONG_BLADES, -2),
APT(SP_MD_REVISED, SK_AXES, 4),
APT(SP_MD_REVISED, SK_MACES_FLAILS, -2),
APT(SP_MD_REVISED, SK_POLEARMS, -3),
APT(SP_MD_REVISED, SK_STAVES, -4),
APT(SP_MD_REVISED, SK_SLINGS, 4),
APT(SP_MD_REVISED, SK_BOWS, -3),
APT(SP_MD_REVISED, SK_CROSSBOWS, -1),
APT(SP_MD_REVISED, SK_THROWING, 4),
APT(SP_MD_REVISED, SK_ARMOUR, 3),
APT(SP_MD_REVISED, SK_DODGING, -3),
APT(SP_MD_REVISED, SK_STEALTH, 1),
APT(SP_MD_REVISED, SK_STABBING, UNUSABLE_SKILL),
APT(SP_MD_REVISED, SK_SHIELDS, 3),
APT(SP_MD_REVISED, SK_TRAPS, UNUSABLE_SKILL),
APT(SP_MD_REVISED, SK_UNARMED_COMBAT, 4),
APT(SP_MD_REVISED, SK_SPELLCASTING, -1),
APT(SP_MD_REVISED, SK_CONJURATIONS, -4),
APT(SP_MD_REVISED, SK_HEXES, -1),
APT(SP_MD_REVISED, SK_CHARMS, -4),
APT(SP_MD_REVISED, SK_SUMMONINGS, 2),
APT(SP_MD_REVISED, SK_NECROMANCY, 3),
APT(SP_MD_REVISED, SK_TRANSLOCATIONS, 1),
APT(SP_MD_REVISED, SK_TRANSMUTATIONS, 1),
APT(SP_MD_REVISED, SK_FIRE_MAGIC, 0),
APT(SP_MD_REVISED, SK_ICE_MAGIC, -1),
APT(SP_MD_REVISED, SK_AIR_MAGIC, -3),
APT(SP_MD_REVISED, SK_EARTH_MAGIC, 3),
APT(SP_MD_REVISED, SK_POISON_MAGIC, -4),
APT(SP_MD_REVISED, SK_INVOCATIONS, 2),
APT(SP_MD_REVISED, SK_EVOCATIONS, 4),
While I've removed the version checks, we still have to have the UNUSABLE_SKILL tag on stabbing and traps to avoid ASSERT checks failing later. Interestingly, there's no actual check on what your aptitude value is. Everything in crawl is currently between -4 and 4, to make the skill train at half or double the base rate respectively. The math for the modifier is (return 1 / exp(log(2) * apt / APT_DOUBLE)), so every 4 points in an aptitude would double the growth rate again (aptitude 8 would require 1/4th the XP of the baseline, aptitude 12 would require 1/8th, etc), and negative values scale the same in the opposite direction.
The last step for this race will be to add them to the menu on startup in newgame.cc:
static const species_type species_order[] =
{
// comparatively human-like looks
SP_HUMAN, SP_HIGH_ELF,
SP_DEEP_ELF, SP_DEEP_DWARF,
SP_HILL_ORC, SP_MD_REVISED,
//More species trimmed
That's all it takes to create a basic species in Crawl. Simpler races might not have any mutations at all, just aptitudes and stat mods. More complex races have checks all over the code for special cases, like felids. Searching for a species by its enum name will show you every place that species gets special consideration. Humans get none, felids and vampires get tons.
Summary:
- species-type.h to update species_type
- species-data.h to add in core information
- aptitudes.h to fill in skill aptitudes.
- newgame.cc to update the species_order array.
- use "you.species == SP_NAME_HERE" elsewhere in code for more complicated species-specific behaviors.