1 #include "AppHdr.h"
2 #include <map>
3
4 #include "mpr.h"
5 #include "species.h"
6
7 #include "item-prop.h"
8 #include "mutation.h"
9 #include "output.h"
10 #include "playable.h"
11 #include "player.h"
12 #include "player-stats.h"
13 #include "random.h"
14 #include "skills.h"
15 #include "stringutil.h"
16 #include "tag-version.h"
17 #include "tiledoll.h"
18
19 #include "species-data.h"
20
21 /*
22 * Get the species_def for the given species type. Asserts if the species_type
23 * is not less than NUM_SPECIES.
24 *
25 * @param species The species type.
26 * @returns The species_def of that species.
27 */
get_species_def(species_type species)28 const species_def& get_species_def(species_type species)
29 {
30 if (species != SP_UNKNOWN)
31 ASSERT_RANGE(species, 0, NUM_SPECIES);
32 return species_data.at(species);
33 }
34
35 namespace species
36 {
37 /**
38 * Return the name of the given species.
39 * @param speci the species to be named.
40 * @param spname_type the kind of name to get: adjectival, the genus, or plain.
41 * @returns the requested name, which will just be plain if no adjective
42 * or genus is defined.
43 */
name(species_type speci,name_type spname_type)44 string name(species_type speci, name_type spname_type)
45 {
46 const species_def& def = get_species_def(speci);
47 if (spname_type == SPNAME_GENUS && def.genus_name)
48 return def.genus_name;
49 else if (spname_type == SPNAME_ADJ && def.adj_name)
50 return def.adj_name;
51 return def.name;
52 }
53
54 /// Does exact case-sensitive lookup of a species name
from_str(const string & species)55 species_type from_str(const string &species)
56 {
57 species_type sp;
58 if (species.empty())
59 return SP_UNKNOWN;
60
61 for (int i = 0; i < NUM_SPECIES; ++i)
62 {
63 sp = static_cast<species_type>(i);
64 if (species == name(sp))
65 return sp;
66 }
67
68 return SP_UNKNOWN;
69 }
70
71 /// Does loose, non-case-sensitive lookup of a species based on a string
from_str_loose(const string & species,bool initial_only)72 species_type from_str_loose(const string &species, bool initial_only)
73 {
74 // XX consolidate with from_str?
75 string spec = lowercase_string(species);
76
77 species_type sp = SP_UNKNOWN;
78
79 for (int i = 0; i < NUM_SPECIES; ++i)
80 {
81 const species_type si = static_cast<species_type>(i);
82 const string sp_name = lowercase_string(name(si));
83
84 string::size_type pos = sp_name.find(spec);
85 if (pos != string::npos)
86 {
87 if (pos == 0)
88 {
89 // We prefer prefixes over partial matches.
90 sp = si;
91 break;
92 }
93 else if (!initial_only)
94 sp = si;
95 }
96 }
97
98 return sp;
99 }
100
101
from_abbrev(const char * abbrev)102 species_type from_abbrev(const char *abbrev)
103 {
104 if (lowercase_string(abbrev) == "dr")
105 return SP_BASE_DRACONIAN;
106
107 for (auto& entry : species_data)
108 if (lowercase_string(abbrev) == lowercase_string(entry.second.abbrev))
109 return entry.first;
110
111 return SP_UNKNOWN;
112 }
113
get_abbrev(species_type which_species)114 const char *get_abbrev(species_type which_species)
115 {
116 return get_species_def(which_species).abbrev;
117 }
118
is_elven(species_type species)119 bool is_elven(species_type species)
120 {
121 return species == SP_DEEP_ELF;
122 }
123
is_orcish(species_type species)124 bool is_orcish(species_type species)
125 {
126 return species == SP_HILL_ORC;
127 }
128
is_undead(species_type species)129 bool is_undead(species_type species)
130 {
131 return undead_type(species) != US_ALIVE;
132 }
133
is_draconian(species_type species)134 bool is_draconian(species_type species)
135 {
136 return bool(get_species_def(species).flags & SPF_DRACONIAN);
137 }
138
139 // A random non-base draconian colour appropriate for the player.
random_draconian_colour()140 species_type random_draconian_colour()
141 {
142 species_type species;
143 do {
144 species =
145 static_cast<species_type>(random_range(0, NUM_SPECIES - 1));
146 } while (!is_draconian(species)
147 || is_removed(species)
148 || species == SP_BASE_DRACONIAN);
149
150 return species;
151 }
152
153 /**
154 * Where does a given species fall on the Undead Spectrum?
155 *
156 * @param species The species in question.
157 * @return What class of undead the given species falls on, if any.
158 */
undead_type(species_type species)159 undead_state_type undead_type(species_type species)
160 {
161 return get_species_def(species).undeadness;
162 }
163
to_mons_species(species_type species)164 monster_type to_mons_species(species_type species)
165 {
166 return get_species_def(species).monster_species;
167 }
168
169 // XX non-draconians, unify with skin names?
scale_type(species_type species)170 const char* scale_type(species_type species)
171 {
172 switch (species)
173 {
174 case SP_RED_DRACONIAN:
175 return "fiery red";
176 case SP_WHITE_DRACONIAN:
177 return "icy white";
178 case SP_GREEN_DRACONIAN:
179 return "lurid green";
180 case SP_YELLOW_DRACONIAN:
181 return "golden yellow";
182 case SP_GREY_DRACONIAN:
183 return "dull iron-grey";
184 case SP_BLACK_DRACONIAN:
185 return "glossy black";
186 case SP_PURPLE_DRACONIAN:
187 return "rich purple";
188 case SP_PALE_DRACONIAN:
189 return "pale cyan-grey";
190 case SP_BASE_DRACONIAN:
191 return "plain brown";
192 default:
193 return "";
194 }
195 }
196
dragon_form(species_type s)197 monster_type dragon_form(species_type s)
198 {
199 switch (s)
200 {
201 case SP_WHITE_DRACONIAN:
202 return MONS_ICE_DRAGON;
203 case SP_GREEN_DRACONIAN:
204 return MONS_SWAMP_DRAGON;
205 case SP_YELLOW_DRACONIAN:
206 return MONS_GOLDEN_DRAGON;
207 case SP_GREY_DRACONIAN:
208 return MONS_IRON_DRAGON;
209 case SP_BLACK_DRACONIAN:
210 return MONS_STORM_DRAGON;
211 case SP_PURPLE_DRACONIAN:
212 return MONS_QUICKSILVER_DRAGON;
213 case SP_PALE_DRACONIAN:
214 return MONS_STEAM_DRAGON;
215 case SP_RED_DRACONIAN:
216 default:
217 return MONS_FIRE_DRAGON;
218 }
219 }
220
draconian_breath(species_type species)221 ability_type draconian_breath(species_type species)
222 {
223 switch (species)
224 {
225 case SP_GREEN_DRACONIAN: return ABIL_BREATHE_MEPHITIC;
226 case SP_RED_DRACONIAN: return ABIL_BREATHE_FIRE;
227 case SP_WHITE_DRACONIAN: return ABIL_BREATHE_FROST;
228 case SP_YELLOW_DRACONIAN: return ABIL_BREATHE_ACID;
229 case SP_BLACK_DRACONIAN: return ABIL_BREATHE_LIGHTNING;
230 case SP_PURPLE_DRACONIAN: return ABIL_BREATHE_POWER;
231 case SP_PALE_DRACONIAN: return ABIL_BREATHE_STEAM;
232 case SP_BASE_DRACONIAN: case SP_GREY_DRACONIAN:
233 default: return ABIL_NON_ABILITY;
234 }
235 }
236
237 /// Does the species have (real) mutation `mut`? Not for demonspawn.
238 /// @return the first xl at which the species gains the mutation, or 0 if it
239 /// does not ever gain it.
mutation_level(species_type species,mutation_type mut,int mut_level)240 int mutation_level(species_type species, mutation_type mut, int mut_level)
241 {
242 int total = 0;
243 // relies on levels being in order -- I think this is safe?
244 for (const auto& lum : get_species_def(species).level_up_mutations)
245 if (mut == lum.mut)
246 {
247 total += lum.mut_level;
248 if (total >= mut_level)
249 return lum.xp_level;
250 }
251
252 return 0;
253 }
254
fake_mutations(species_type species,bool terse)255 const vector<string>& fake_mutations(species_type species, bool terse)
256 {
257 return terse ? get_species_def(species).terse_fake_mutations
258 : get_species_def(species).verbose_fake_mutations;
259 }
260
has_hair(species_type species)261 bool has_hair(species_type species)
262 {
263 return !bool(get_species_def(species).flags & (SPF_NO_HAIR | SPF_DRACONIAN));
264 }
265
has_bones(species_type species)266 bool has_bones(species_type species)
267 {
268 return !bool(get_species_def(species).flags & SPF_NO_BONES);
269 }
270
can_throw_large_rocks(species_type species)271 bool can_throw_large_rocks(species_type species)
272 {
273 return size(species) >= SIZE_LARGE;
274 }
275
wears_barding(species_type species)276 bool wears_barding(species_type species)
277 {
278 return bool(get_species_def(species).flags & SPF_SMALL_TORSO);
279 }
280
has_claws(species_type species)281 bool has_claws(species_type species)
282 {
283 return mutation_level(species, MUT_CLAWS) == 1;
284 }
285
is_nonliving(species_type species)286 bool is_nonliving(species_type species)
287 {
288 // XXX: move to data?
289 return species == SP_GARGOYLE || species == SP_DJINNI;
290 }
291
can_swim(species_type species)292 bool can_swim(species_type species)
293 {
294 return get_species_def(species).habitat == HT_WATER;
295 }
296
likes_water(species_type species)297 bool likes_water(species_type species)
298 {
299 return can_swim(species)
300 || get_species_def(species).habitat == HT_AMPHIBIOUS;
301 }
302
size(species_type species,size_part_type psize)303 size_type size(species_type species, size_part_type psize)
304 {
305 const size_type size = get_species_def(species).size;
306 if (psize == PSIZE_TORSO
307 && bool(get_species_def(species).flags & SPF_SMALL_TORSO))
308 {
309 return static_cast<size_type>(static_cast<int>(size) - 1);
310 }
311 return size;
312 }
313
314
315 /** What walking-like thing does this species do?
316 *
317 * @param sp what kind of species to look at
318 * @returns a "word" to which "-er" or "-ing" can be appended.
319 */
walking_verb(species_type sp)320 string walking_verb(species_type sp)
321 {
322 auto verb = get_species_def(sp).walking_verb;
323 return verb ? verb : "Walk";
324 }
325
326 /**
327 * What message should be printed when a character of the specified species
328 * prays at an altar, if not in some form?
329 * To be inserted into "You %s the altar of foo."
330 *
331 * @param species The species in question.
332 * @return An action to be printed when the player prays at an altar.
333 * E.g., "coil in front of", "kneel at", etc.
334 */
prayer_action(species_type species)335 string prayer_action(species_type species)
336 {
337 auto action = get_species_def(species).altar_action;
338 return action ? action : "kneel at";
339 }
340
341 static const string shout_verbs[] = {"shout", "yell", "scream"};
342 static const string felid_shout_verbs[] = {"meow", "yowl", "caterwaul"};
343 static const string frog_shout_verbs[] = {"croak", "ribbit", "bellow"};
344 static const string dog_shout_verbs[] = {"bark", "howl", "screech"};
345
346 /**
347 * What verb should be used to describe the species' shouting?
348 * @param sp a species
349 * @param screaminess a loudness level; in range [0,2]
350 * @param directed with this is to be directed at another actor
351 * @return A shouty kind of verb
352 */
shout_verb(species_type sp,int screaminess,bool directed)353 string shout_verb(species_type sp, int screaminess, bool directed)
354 {
355 screaminess = max(min(screaminess,
356 static_cast<int>(sizeof(shout_verbs) - 1)), 0);
357 switch (sp)
358 {
359 case SP_GNOLL:
360 if (screaminess == 0 && directed && coinflip())
361 return "growl";
362 return dog_shout_verbs[screaminess];
363 case SP_BARACHI:
364 return frog_shout_verbs[screaminess];
365 case SP_FELID:
366 if (screaminess == 0 && directed)
367 return "hiss"; // hiss at, not meow at
368 return felid_shout_verbs[screaminess];
369 default:
370 return shout_verbs[screaminess];
371 }
372 }
373
374 /**
375 * Return an adjective or noun for the species' skin.
376 * @param adj whether to provide an adjective (if true), or a noun (if false).
377 * @return a non-empty string. Nouns will be pluralised if they are count nouns.
378 * Right now, plurality can be determined by `ends_with(noun, "s")`.
379 */
skin_name(species_type species,bool adj)380 string skin_name(species_type species, bool adj)
381 {
382 // Aside from direct uses, some flavor stuff checks the strings
383 // here. TODO: should some of these be species flags a la hair?
384 // Also, some skin mutations should have a way of overriding these perhaps
385 if (is_draconian(species) || species == SP_NAGA)
386 return adj ? "scaled" : "scales";
387 else if (species == SP_TENGU)
388 return adj ? "feathered" : "feathers";
389 else if (species == SP_FELID)
390 return adj ? "furry" : "fur";
391 else if (species == SP_MUMMY)
392 return adj ? "bandage-wrapped" : "bandages";
393 else
394 return adj ? "fleshy" : "skin";
395 }
396
arm_name(species_type species)397 string arm_name(species_type species)
398 {
399 if (mutation_level(species, MUT_TENTACLE_ARMS))
400 return "tentacle";
401 else if (species == SP_FELID)
402 return "leg";
403 else
404 return "arm";
405 }
406
hand_name(species_type species)407 string hand_name(species_type species)
408 {
409 // see also player::hand_name
410 if (mutation_level(species, MUT_PAWS))
411 return "paw";
412 else if (mutation_level(species, MUT_TENTACLE_ARMS))
413 return "tentacle";
414 else if (mutation_level(species, MUT_CLAWS))
415 return "claw"; // overridden for felids by first check
416 else
417 return "hand";
418 }
419
arm_count(species_type species)420 int arm_count(species_type species)
421 {
422 return species == SP_OCTOPODE ? 8 : 2;
423 }
424
sacrificial_arm(species_type species)425 equipment_type sacrificial_arm(species_type species)
426 {
427 // this is a bit special-case-y because the sac slot doesn't follow
428 // from the enum; for 2-armed species it is the left ring (which is first),
429 // but for 8-armed species it is ring 8 (which is last).
430 // XX maybe swap the targeted sac hand? But this requires some painful
431 // save compat
432 return arm_count(species) == 2 ? EQ_LEFT_RING : EQ_RING_EIGHT;
433 }
434
435 /**
436 * Checks some species-level equipment slot constraints. Anything hard-coded
437 * per species, but not handled by a mutation should be here. See also
438 * player.cc::you_can_wear and item-use.cc::can_wear_armour for the full
439 * division of labor. This function is guaranteed to handle species ring
440 * slots.
441 *
442 * @param species the species type to check
443 * @param eq the equipment slot to check
444 * @return true if the equipment slot is not used by the species; false
445 * indicates only that nothing in this check bans the slot. For
446 * example, this function does not check felid mutations.
447 */
bans_eq(species_type species,equipment_type eq)448 bool bans_eq(species_type species, equipment_type eq)
449 {
450 const int arms = arm_count(species);
451 // only handles 2 or 8
452 switch (eq)
453 {
454 case EQ_LEFT_RING:
455 case EQ_RIGHT_RING:
456 return arms > 2;
457 case EQ_RING_ONE:
458 case EQ_RING_TWO:
459 case EQ_RING_THREE:
460 case EQ_RING_FOUR:
461 case EQ_RING_FIVE:
462 case EQ_RING_SIX:
463 case EQ_RING_SEVEN:
464 case EQ_RING_EIGHT:
465 return arms <= 2;
466 // not banned by any species
467 case EQ_AMULET:
468 case EQ_RING_AMULET:
469 // not handled here:
470 case EQ_WEAPON:
471 case EQ_STAFF:
472 case EQ_RINGS:
473 case EQ_RINGS_PLUS: // what is this stuff
474 case EQ_ALL_ARMOUR:
475 return false;
476 default:
477 break;
478 }
479 // remaining should be armour only
480 if (species == SP_OCTOPODE && eq != EQ_HELMET && eq != EQ_SHIELD)
481 return true;
482
483 if (is_draconian(species) && eq == EQ_BODY_ARMOUR)
484 return true;
485
486 // for everything else that is handled by mutations, including felid
487 // restrictions, see item-use.cc::can_wear_armour. (TODO: move more of the
488 // code here to mutations?)
489 return false;
490 }
491
492 /**
493 * Get ring slots available to a species.
494 * @param species the species to check
495 * @param missing_hand if true, removes a designated hand from the result
496 */
ring_slots(species_type species,bool missing_hand)497 vector<equipment_type> ring_slots(species_type species, bool missing_hand)
498 {
499 vector<equipment_type> result;
500
501 const equipment_type missing = missing_hand
502 ? sacrificial_arm(species) : EQ_NONE;
503
504 for (int i = EQ_FIRST_JEWELLERY; i <= EQ_LAST_JEWELLERY; i++)
505 {
506 const auto eq = static_cast<equipment_type>(i);
507 if (eq != EQ_AMULET
508 && eq != EQ_RING_AMULET
509 && eq != missing
510 && !bans_eq(species, eq))
511 {
512 result.push_back(eq);
513 }
514 }
515 return result;
516 }
517
get_exp_modifier(species_type species)518 int get_exp_modifier(species_type species)
519 {
520 return get_species_def(species).xp_mod;
521 }
522
get_hp_modifier(species_type species)523 int get_hp_modifier(species_type species)
524 {
525 return get_species_def(species).hp_mod;
526 }
527
get_mp_modifier(species_type species)528 int get_mp_modifier(species_type species)
529 {
530 return get_species_def(species).mp_mod;
531 }
532
get_wl_modifier(species_type species)533 int get_wl_modifier(species_type species)
534 {
535 return get_species_def(species).wl_mod;
536 }
537
get_stat_gain_multiplier(species_type species)538 int get_stat_gain_multiplier(species_type species)
539 {
540 // TODO: is this worth dataifying? Currently matters only for
541 // player-stats.cc:attribute_increase
542 return species == SP_DEMIGOD ? 4 : 1;
543 }
544
545 /**
546 * Does this species have (relatively) low strength?
547 * Used to generate the title for UC ghosts.
548 *
549 * @param species the speciecs to check.
550 * @returns whether the starting str is lower than the starting dex.
551 */
has_low_str(species_type species)552 bool has_low_str(species_type species)
553 {
554 return get_species_def(species).d >= get_species_def(species).s;
555 }
556
557
recommends_job(species_type species,job_type job)558 bool recommends_job(species_type species, job_type job)
559 {
560 return find(get_species_def(species).recommended_jobs.begin(),
561 get_species_def(species).recommended_jobs.end(),
562 job) != get_species_def(species).recommended_jobs.end();
563 }
564
recommends_weapon(species_type species,weapon_type wpn)565 bool recommends_weapon(species_type species, weapon_type wpn)
566 {
567 const skill_type sk =
568 wpn == WPN_THROWN ? SK_THROWING :
569 wpn == WPN_UNARMED ? SK_UNARMED_COMBAT :
570 item_attack_skill(OBJ_WEAPONS, wpn);
571
572 return find(get_species_def(species).recommended_weapons.begin(),
573 get_species_def(species).recommended_weapons.end(),
574 sk) != get_species_def(species).recommended_weapons.end();
575 }
576
is_valid(species_type species)577 bool is_valid(species_type species)
578 {
579 return 0 <= species && species < NUM_SPECIES;
580 }
581
582
583 // Ensure the species isn't SP_RANDOM/SP_VIABLE and it has recommended jobs
584 // (old disabled species have none).
is_starting_species(species_type species)585 bool is_starting_species(species_type species)
586 {
587 return is_valid(species)
588 && !get_species_def(species).recommended_jobs.empty();
589 }
590
591 // A random valid (selectable on the new game screen) species.
random_starting_species()592 species_type random_starting_species()
593 {
594 const auto species = playable_species();
595 return species[random2(species.size())];
596 }
597
is_removed(species_type species)598 bool is_removed(species_type species)
599 {
600 #if TAG_MAJOR_VERSION == 34
601 if (species == SP_MOTTLED_DRACONIAN)
602 return true;
603 #endif
604 // all other derived Dr are ok and don't have recommended jobs
605 if (is_draconian(species))
606 return false;
607 if (get_species_def(species).recommended_jobs.empty())
608 return true;
609 return false;
610 }
611
612
613 /** All non-removed species, including base and derived species */
get_all_species()614 vector<species_type> get_all_species()
615 {
616 vector<species_type> species;
617 for (int i = 0; i < NUM_SPECIES; ++i)
618 {
619 const auto sp = static_cast<species_type>(i);
620 if (!is_removed(sp))
621 species.push_back(sp);
622 }
623 return species;
624 }
625 }
626
give_basic_mutations(species_type species)627 void give_basic_mutations(species_type species)
628 {
629 // Don't perma_mutate since that gives messages.
630 for (const auto& lum : get_species_def(species).level_up_mutations)
631 if (lum.xp_level == 1)
632 you.mutation[lum.mut] = you.innate_mutation[lum.mut] = lum.mut_level;
633 }
634
give_level_mutations(species_type species,int xp_level)635 void give_level_mutations(species_type species, int xp_level)
636 {
637 for (const auto& lum : get_species_def(species).level_up_mutations)
638 if (lum.xp_level == xp_level)
639 {
640 perma_mutate(lum.mut, lum.mut_level,
641 species::name(species) + " growth");
642 }
643 }
644
species_stat_init(species_type species)645 void species_stat_init(species_type species)
646 {
647 you.base_stats[STAT_STR] = get_species_def(species).s;
648 you.base_stats[STAT_INT] = get_species_def(species).i;
649 you.base_stats[STAT_DEX] = get_species_def(species).d;
650 }
651
species_stat_gain(species_type species)652 void species_stat_gain(species_type species)
653 {
654 const species_def& sd = get_species_def(species);
655 if (sd.level_stats.size() > 0 && you.experience_level % sd.how_often == 0)
656 {
657 modify_stat(*random_iterator(sd.level_stats),
658 species::get_stat_gain_multiplier(species), false);
659 }
660 }
661
_swap_equip(equipment_type a,equipment_type b)662 static void _swap_equip(equipment_type a, equipment_type b)
663 {
664 swap(you.equip[a], you.equip[b]);
665 bool tmp = you.melded[a];
666 you.melded.set(a, you.melded[b]);
667 you.melded.set(b, tmp);
668 }
669
670 /**
671 * Change the player's species to something else.
672 *
673 * This is used primarily in wizmode, but is also used for extreme
674 * cases of save compatibility (see `files.cc:_convert_obsolete_species`).
675 * This does *not* check for obsoleteness -- as long as it's in
676 * species_data it'll do something.
677 *
678 * @param sp the new species.
679 */
change_species_to(species_type sp)680 void change_species_to(species_type sp)
681 {
682 ASSERT(sp != SP_UNKNOWN);
683
684 // Re-scale skill-points.
685 for (skill_type sk = SK_FIRST_SKILL; sk < NUM_SKILLS; ++sk)
686 {
687 you.skill_points[sk] *= species_apt_factor(sk, sp)
688 / species_apt_factor(sk);
689 }
690
691 species_type old_sp = you.species;
692 you.species = sp;
693 you.chr_species_name = species::name(sp);
694
695 // Change permanent mutations, but preserve non-permanent ones.
696 uint8_t prev_muts[NUM_MUTATIONS];
697
698 // remove all innate mutations
699 for (int i = 0; i < NUM_MUTATIONS; ++i)
700 {
701 if (you.has_innate_mutation(static_cast<mutation_type>(i)))
702 {
703 you.mutation[i] -= you.innate_mutation[i];
704 you.innate_mutation[i] = 0;
705 }
706 prev_muts[i] = you.mutation[i];
707 }
708 // add the appropriate innate mutations for the new species and xl
709 give_basic_mutations(sp);
710 for (int i = 2; i <= you.experience_level; ++i)
711 give_level_mutations(sp, i);
712
713 for (int i = 0; i < NUM_MUTATIONS; ++i)
714 {
715 // TODO: why do previous non-innate mutations override innate ones? Shouldn't this be the other way around?
716 if (prev_muts[i] > you.innate_mutation[i])
717 you.innate_mutation[i] = 0;
718 else
719 you.innate_mutation[i] -= prev_muts[i];
720 }
721
722 if (sp == SP_DEMONSPAWN)
723 {
724 roll_demonspawn_mutations();
725 for (int i = 0; i < int(you.demonic_traits.size()); ++i)
726 {
727 mutation_type m = you.demonic_traits[i].mutation;
728
729 if (you.demonic_traits[i].level_gained > you.experience_level)
730 continue;
731
732 ++you.mutation[m];
733 ++you.innate_mutation[m];
734 }
735 }
736
737 update_vision_range(); // for Ba, and for Ko
738
739 // XX not general if there are ever any other options
740 if ((old_sp == SP_OCTOPODE) != (sp == SP_OCTOPODE))
741 {
742 _swap_equip(EQ_LEFT_RING, EQ_RING_ONE);
743 _swap_equip(EQ_RIGHT_RING, EQ_RING_TWO);
744 // All species allow exactly one amulet.
745 }
746
747 // FIXME: this checks only for valid slots, not for suitability of the
748 // item in question. This is enough to make assertions happy, though.
749 for (int i = EQ_FIRST_EQUIP; i < NUM_EQUIP; ++i)
750 if (you_can_wear(static_cast<equipment_type>(i)) == MB_FALSE
751 && you.equip[i] != -1)
752 {
753 mprf("%s fall%s away.",
754 you.inv[you.equip[i]].name(DESC_YOUR).c_str(),
755 you.inv[you.equip[i]].quantity > 1 ? "" : "s");
756 // Unwear items without the usual processing.
757 you.equip[i] = -1;
758 you.melded.set(i, false);
759 }
760
761 // Sanitize skills.
762 fixup_skills();
763
764 calc_hp();
765 calc_mp();
766
767 // The player symbol depends on species.
768 update_player_symbol();
769 #ifdef USE_TILE
770 init_player_doll();
771 #endif
772 redraw_screen();
773 update_screen();
774 }
775