diff options
author | pubby <pubby8@gmail.com> | 2013-06-12 02:34:30 -0500 |
---|---|---|
committer | Neil Moore <neil@s-z.org> | 2013-08-25 00:12:25 -0400 |
commit | 2211e5ff2f25adfb931eb0ff32b274afa7eb7ed5 (patch) | |
tree | 4568295ed3a6c1bd2c0df4bc8db167db1809c043 | |
parent | 9c1491e134d7c43d0ad2c4ebd444a3a2199b457b (diff) | |
download | crawl-ref-2211e5ff2f25adfb931eb0ff32b274afa7eb7ed5.tar.gz crawl-ref-2211e5ff2f25adfb931eb0ff32b274afa7eb7ed5.zip |
Create Formicid species and monsters.
Tavern post:
https://crawl.develz.org/tavern/viewtopic.php?f=8&t=8298
Wierdness & mutations:
- poison weakness
- retractable antennae (can wear headgear and ignore mutation)
- chitin skin (+3 AC)
- most weapons 1-handed, big weapons 2-handed
- permanent stasis
- ability to shaft self
- ability to dig
- Starts with 2 curing pots
3 monster versions of formicids were added:
formicid, a weak fighter
formicid drone, a stronger fighter
formicid venom mage, a magician with olgreb's and mass cure poison
[ Pushing to a branch for experimental playtesting on CSZO.
Also optimised the new tiles. -nfm ]
52 files changed, 907 insertions, 122 deletions
diff --git a/crawl-ref/docs/crawl_manual.reST b/crawl-ref/docs/crawl_manual.reST index ff2b0d9dbe..54126b48f1 100644 --- a/crawl-ref/docs/crawl_manual.reST +++ b/crawl-ref/docs/crawl_manual.reST @@ -1778,6 +1778,27 @@ Lava Orcs Lava Orcs make good melee fighters, preferring axes. They're pretty bad spellcasters, except for transmutations, fire and earth magic. +Formicids + The Formicids are an species of humanoid ants. Just like their tiny insect + ancestors, the Formicids are well adept at earth work, both on the physical + and magical sides. Their abilities have been used to tunnel immense underground + communities and structures, many of which are tens of thousands of years old. + + Perhaps unfortunately, their strong ties to to earth have left them completely + impervious to being teleported or hasted; Formicids are tied to the Earth with + a complete sense of stasis. While this is a seemingly bad property for a + dungeon adventurer, stasis has the beneficial effect of preventing many types + of nasty hexes and maledictions. + + With the ability to lift ten times their own weight, the Formicids have + strength rivaling that of ogres. This, along with the fact that they have four + arms, allows Formicid warriors to carry both a shield and a two-handed weapon + at the same time. + + Formicids make good earth and venom mages, but are quite capable at both melee + and ranged combat too, albeit a bit flimsy. They are naturally bad at air magic + and conjurations. + Gargoyles A cross between ordinary stone gargoyles and living beings, Gargoyles are hideous humanoids with an affinity to rock. They are immune to petrification, diff --git a/crawl-ref/source/abl-show.cc b/crawl-ref/source/abl-show.cc index 62cee08ed1..191583ac17 100644 --- a/crawl-ref/source/abl-show.cc +++ b/crawl-ref/source/abl-show.cc @@ -55,6 +55,7 @@ #include "player-stats.h" #include "potion.h" #include "religion.h" +#include "shout.h" #include "skills.h" #include "skills2.h" #include "species.h" @@ -72,6 +73,7 @@ #include "stuff.h" #include "target.h" #include "tilepick.h" +#include "traps.h" #include "areas.h" #include "transform.h" #include "hints.h" @@ -229,6 +231,9 @@ static const ability_def Ability_List[] = 0, 0, 0, 0, 0, ABFLAG_INSTANT}, { ABIL_MUMMY_RESTORATION, "Self-Restoration", 1, 0, 0, 0, 0, ABFLAG_PERMANENT_MP}, + + { ABIL_DIG, "Dig", 0, 0, 75, 0, 0, ABFLAG_NONE}, + { ABIL_SHAFT_SELF, "Shaft Self", 0, 0, 250, 0, 0, ABFLAG_NONE}, // EVOKE abilities use Evocations and come from items. // Teleportation and Blink can also come from mutations @@ -903,6 +908,8 @@ talent get_talent(ability_type ability, bool check_confused) case ABIL_MAKE_GRENADES: case ABIL_MAKE_SAGE: case ABIL_REMOVE_CURSE: + case ABIL_DIG: + case ABIL_SHAFT_SELF: failure = 0; break; @@ -1494,7 +1501,12 @@ static bool _check_ability_possible(const ability_def& abil, if (you.no_tele(false, false, true)) { if (!quiet) - mpr("You cannot teleport right now."); + { + if (you.species == SP_FORMICID) + mpr("You cannot teleport."); + else + mpr("You cannot teleport right now."); + } return false; } return true; @@ -1888,6 +1900,42 @@ static bool _do_ability(const ability_def& abil) if (recharge_wand() <= 0) return false; // fail message is already given break; + + case ABIL_DIG: + { + power = 0; + beam.range = LOS_RADIUS; + + if (!spell_direction(abild, beam, DIR_NONE, TARG_ANY, 0, + true, true, false, NULL, + "Aiming: Dig", + true)) + { + return false; + } + else + { + zapping(ZAP_DIG, power, beam); + } + break; + } + + case ABIL_SHAFT_SELF: + { + if (you.can_do_shaft_ability()) + { + if (yesno("Are you sure you want to shaft yourself?")) + start_delay(DELAY_SHAFT_SELF, 1); + else + return false; + } + else + { + mpr("You can't shaft here."); + return false; + } + break; + } case ABIL_DELAYED_FIREBALL: { @@ -2309,6 +2357,11 @@ static bool _do_ability(const ability_def& abil) break; case ABIL_OKAWARU_FINESSE: + if (you.species == SP_FORMICID) + { + mpr("You cannot use finnese because of your stasis."); + return false; + } if (stasis_blocks_effect(true, true, "%s emits a piercing whistle.", 20, "%s makes your neck tingle.")) { @@ -3080,6 +3133,12 @@ vector<talent> your_talents(bool check_confused, bool include_unusable) if (you.species == SP_DEEP_DWARF) _add_talent(talents, ABIL_RECHARGING, check_confused); + + if (you.species == SP_FORMICID) + { + _add_talent(talents, ABIL_DIG, check_confused); + _add_talent(talents, ABIL_SHAFT_SELF, check_confused); + } // Spit Poison. Nontransformed nagas can upgrade to Breathe Poison. // Transformed nagas, or non-nagas, can only get Spit Poison. diff --git a/crawl-ref/source/acquire.cc b/crawl-ref/source/acquire.cc index 11e4b81c5d..cbd9d3bcf8 100644 --- a/crawl-ref/source/acquire.cc +++ b/crawl-ref/source/acquire.cc @@ -153,7 +153,7 @@ static armour_type _pick_wearable_armour(const armour_type arm) if (arm == ARM_HELMET && (!you_can_wear(EQ_HELMET) || you.mutation[MUT_HORNS] - || you.mutation[MUT_ANTENNAE])) + || (you.mutation[MUT_ANTENNAE] && you.species != SP_FORMICID))) { // Check for Horns 3 & Antennae 3 - Don't give a cap if those mutation // levels have been reached. @@ -528,7 +528,7 @@ static int _acquirement_weapon_subtype(bool divine) if (!acqweight) continue; - const bool two_handed = hands_reqd(item_considered, you.body_size()) == HANDS_TWO; + const bool two_handed = you.hands_reqd(item_considered) == HANDS_TWO; // For non-Trog/Okawaru acquirements, give a boost to high-end items. if (!divine && !is_range_weapon(item_considered)) @@ -607,8 +607,11 @@ static missile_type _acquirement_missile_subtype() if (_have_item_with_types(OBJ_WEAPONS, WPN_BLOWGUN)) missile_weights.push_back(make_pair(MI_NEEDLE, 100)); - if (you.body_size() >= SIZE_MEDIUM) + if (you.can_throw_large_rocks() + || you.body_size() >= SIZE_MEDIUM) + { missile_weights.push_back(make_pair(MI_JAVELIN, 100)); + } if (you.can_throw_large_rocks()) missile_weights.push_back(make_pair(MI_LARGE_ROCK, 100)); diff --git a/crawl-ref/source/actor.cc b/crawl-ref/source/actor.cc index a612e2ac98..056f35f900 100644 --- a/crawl-ref/source/actor.cc +++ b/crawl-ref/source/actor.cc @@ -60,6 +60,12 @@ bool actor::stand_on_solid_ground() const && !feat_is_water(grd(pos())); } +// Give hands required to wield weapon. +hands_reqd_type actor::hands_reqd(const item_def &item) const +{ + return basic_hands_reqd(item, body_size()); +} + /** * Wrapper around the virtual actor::can_wield(const item_def&,bool,bool,bool,bool) const overload. * @param item May be NULL, in which case a dummy item will be passed in. diff --git a/crawl-ref/source/actor.h b/crawl-ref/source/actor.h index 59b0973212..cda7c20d9e 100644 --- a/crawl-ref/source/actor.h +++ b/crawl-ref/source/actor.h @@ -120,6 +120,8 @@ public: bool calc_unid = true) const = 0; virtual int scan_artefacts(artefact_prop_type which_property, bool calc_unid = true) const = 0; + + virtual hands_reqd_type hands_reqd(const item_def &item) const; bool can_wield(const item_def* item, bool ignore_curse = false, diff --git a/crawl-ref/source/aptitudes.h b/crawl-ref/source/aptitudes.h index 7ee6fc4d72..273def1b6a 100644 --- a/crawl-ref/source/aptitudes.h +++ b/crawl-ref/source/aptitudes.h @@ -1340,6 +1340,40 @@ static const species_skill_aptitude species_skill_aptitudes[] = APT(SP_LAVA_ORC, SK_POISON_MAGIC, -1), APT(SP_LAVA_ORC, SK_INVOCATIONS, 3), APT(SP_LAVA_ORC, SK_EVOCATIONS, 1), + + // SP_FORMICID + APT(SP_FORMICID, SK_FIGHTING, 0), + APT(SP_FORMICID, SK_SHORT_BLADES, 1), + APT(SP_FORMICID, SK_LONG_BLADES, 0), + APT(SP_FORMICID, SK_AXES, 0), + APT(SP_FORMICID, SK_MACES_FLAILS, 0), + APT(SP_FORMICID, SK_POLEARMS, 0), + APT(SP_FORMICID, SK_STAVES, 0), + APT(SP_FORMICID, SK_SLINGS, 1), + APT(SP_FORMICID, SK_BOWS, -2), + APT(SP_FORMICID, SK_CROSSBOWS, 0), + APT(SP_FORMICID, SK_THROWING, -2), + APT(SP_FORMICID, SK_ARMOUR, 3), + APT(SP_FORMICID, SK_DODGING, -1), + APT(SP_FORMICID, SK_STEALTH, 3), + APT(SP_FORMICID, SK_SHIELDS, 1), + APT(SP_FORMICID, SK_TRAPS, 1), + APT(SP_FORMICID, SK_UNARMED_COMBAT, 0), + APT(SP_FORMICID, SK_SPELLCASTING, -1), + APT(SP_FORMICID, SK_CONJURATIONS, -1), + APT(SP_FORMICID, SK_HEXES, 2), + APT(SP_FORMICID, SK_CHARMS, 0), + APT(SP_FORMICID, SK_SUMMONINGS, 0), + APT(SP_FORMICID, SK_NECROMANCY, 0), + APT(SP_FORMICID, SK_TRANSLOCATIONS, 0), + APT(SP_FORMICID, SK_TRANSMUTATIONS, 1), + APT(SP_FORMICID, SK_FIRE_MAGIC, 0), + APT(SP_FORMICID, SK_ICE_MAGIC, 0), + APT(SP_FORMICID, SK_AIR_MAGIC, -2), + APT(SP_FORMICID, SK_EARTH_MAGIC, 2), + APT(SP_FORMICID, SK_POISON_MAGIC, 3), + APT(SP_FORMICID, SK_INVOCATIONS, 2), + APT(SP_FORMICID, SK_EVOCATIONS, 2), }; #endif diff --git a/crawl-ref/source/artefact.cc b/crawl-ref/source/artefact.cc index 321e70cd67..a4561b9f89 100644 --- a/crawl-ref/source/artefact.cc +++ b/crawl-ref/source/artefact.cc @@ -1595,12 +1595,10 @@ int find_okay_unrandart(uint8_t aclass, uint8_t atype, bool in_abyss) && (aclass != OBJ_WEAPONS || weapon_skill(entry->base_type, atype) != weapon_skill(entry->base_type, entry->sub_type) - || hands_reqd(entry->base_type, - atype, - you.body_size()) != - hands_reqd(entry->base_type, - entry->sub_type, - you.body_size()))) + || hands_reqd(&you, entry->base_type, + atype) != + hands_reqd(&you, entry->base_type, + entry->sub_type))) { continue; } diff --git a/crawl-ref/source/dat/descript/ability.txt b/crawl-ref/source/dat/descript/ability.txt index bfa125d0b7..7133eb31e8 100644 --- a/crawl-ref/source/dat/descript/ability.txt +++ b/crawl-ref/source/dat/descript/ability.txt @@ -92,6 +92,15 @@ Device Recharging At the permanent loss of one magic point recharge a wand or rod. %%%% +Dig + +Tunnels through rock walls. +%%%% +Shaft Self + +Sends you to a random position one to three floors down, +as if you fell through a shaft trap. +%%%% Evoke Teleportation Teleport yourself to a random location on the level. diff --git a/crawl-ref/source/dat/descript/monsters.txt b/crawl-ref/source/dat/descript/monsters.txt index 11ff62a8f4..4f4fc93265 100644 --- a/crawl-ref/source/dat/descript/monsters.txt +++ b/crawl-ref/source/dat/descript/monsters.txt @@ -1485,6 +1485,18 @@ dryad A spirit of nature, bound to the forest in which it resides. It is capable of calling upon the trees to defend against incursions. %%%% +formicid + +A sentient ant that walks upright and is competant at digging. +%%%% +formicid drone + +An ant-like soldier trained in combat and ready to defend its home. +%%%% +formicid venom mage + +A practitioner of its greatest fear. It knows how to create and cleanse poison. +%%%% dwarf A member of a short, sturdy mountain-dwelling folk. More robust than their deep diff --git a/crawl-ref/source/dat/descript/species.txt b/crawl-ref/source/dat/descript/species.txt index ffadcb54ec..58dded462d 100644 --- a/crawl-ref/source/dat/descript/species.txt +++ b/crawl-ref/source/dat/descript/species.txt @@ -32,6 +32,11 @@ Draconian Draconians are versatile hybrids. They mature when reaching level 7 and develop a colour. %%%% +Formicid + +Formicids are humanoid ants adept at digging. Their limbs are exceptionally +strong and they have a permanent sense of stasis. +%%%% Felid These many-lived cats possess sentience, but are incapable of advanced item diff --git a/crawl-ref/source/defines.h b/crawl-ref/source/defines.h index 55e85b28bf..21a863e5cf 100644 --- a/crawl-ref/source/defines.h +++ b/crawl-ref/source/defines.h @@ -183,6 +183,8 @@ const int CHUNK_BASE_NUTRITION = 1000; const int ICEMAIL_MAX = 10; const int ICEMAIL_TIME = 300 * BASELINE_DELAY; +const int ANTENNAE_EXTEND_TIME = 10 * BASELINE_DELAY; + // The maximum number of abilities any god can have #define MAX_GOD_ABILITIES 5 diff --git a/crawl-ref/source/delay.cc b/crawl-ref/source/delay.cc index ac4d835101..63cb187077 100644 --- a/crawl-ref/source/delay.cc +++ b/crawl-ref/source/delay.cc @@ -20,6 +20,7 @@ #include "delay.h" #include "describe.h" #include "directn.h" +#include "dungeon.h" #include "exercise.h" #include "enum.h" #include "fprop.h" @@ -58,6 +59,7 @@ #include "stuff.h" #include "env.h" #include "transform.h" +#include "traps.h" #include "travel.h" #include "hints.h" #include "view.h" @@ -384,6 +386,14 @@ void stop_delay(bool stop_stair_travel, bool force_unsafe) } break; + case DELAY_SHAFT_SELF: + if (stop_stair_travel) + { + mpr("You stop digging."); + _pop_delay(); + } + break; + case DELAY_WEAPON_SWAP: // one turn... too much trouble case DELAY_DROP_ITEM: // one turn... only used for easy armour drops case DELAY_JEWELLERY_ON: // one turn @@ -708,6 +718,10 @@ void handle_delay() case DELAY_PASSWALL: mpr("You begin to meditate on the wall.", MSGCH_MULTITURN_ACTION); break; + + case DELAY_SHAFT_SELF: + mpr("You begin to dig a shaft.", MSGCH_MULTITURN_ACTION); + break; case DELAY_RECITE: { @@ -924,6 +938,11 @@ void handle_delay() MSGCH_MULTITURN_ACTION); break; + case DELAY_SHAFT_SELF: + mpr("You continue digging a shaft.", + MSGCH_MULTITURN_ACTION); + break; + case DELAY_RECITE: { mprf(MSGCH_MULTITURN_ACTION, "\"%s\"", @@ -1173,6 +1192,12 @@ static void _finish_delay(const delay_queue_item &delay) break; } + case DELAY_SHAFT_SELF: + { + you.do_shaft_ability(); + break; + } + case DELAY_BUTCHER: case DELAY_BOTTLE_BLOOD: { @@ -1929,7 +1954,7 @@ static const char *delay_names[] = "jewellery_on", "memorise", "butcher", "bottle_blood", "weapon_swap", "passwall", "drop_item", "multidrop", "ascending_stairs", "descending_stairs", "recite", "run", "rest", "travel", "macro", - "macro_process_key", "interruptible", "uninterruptible" + "macro_process_key", "interruptible", "uninterruptible", "shaft self" }; // Gets a delay given its name. diff --git a/crawl-ref/source/describe.cc b/crawl-ref/source/describe.cc index 01dfa25446..ef0874a68f 100644 --- a/crawl-ref/source/describe.cc +++ b/crawl-ref/source/describe.cc @@ -760,13 +760,19 @@ static string _handedness_string(const item_def &item) { string description; - switch (hands_reqd(item, you.body_size())) + switch (you.hands_reqd(item)) { case HANDS_ONE: - description += "It is a one handed weapon."; + if (you.species == SP_FORMICID) + description += "It is a weapon for one hand-pair."; + else + description += "It is a one handed weapon."; break; case HANDS_TWO: - description += "It is a two handed weapon."; + if (you.species == SP_FORMICID) + description += "It is a weapon for two hand-pairs."; + else + description += "It is a two handed weapon."; break; } @@ -3683,6 +3689,7 @@ string get_ghost_description(const monster_info &mi, bool concise) case SP_NAGA: case SP_MUMMY: case SP_GHOUL: + case SP_FORMICID: str += 10; break; diff --git a/crawl-ref/source/enum.h b/crawl-ref/source/enum.h index 9b3079fea7..1711874924 100644 --- a/crawl-ref/source/enum.h +++ b/crawl-ref/source/enum.h @@ -73,7 +73,10 @@ enum ability_type ABIL_BOTTLE_BLOOD, // Deep Dwarves ABIL_RECHARGING, - ABIL_MAX_INTRINSIC = ABIL_RECHARGING, + // Formicids + ABIL_DIG, + ABIL_SHAFT_SELF, + ABIL_MAX_INTRINSIC = ABIL_SHAFT_SELF, // Evoking items. ABIL_EVOKE_BERSERK = 40, @@ -1064,6 +1067,8 @@ enum delay_type DELAY_INTERRUPTIBLE, // simple interruptible delay DELAY_UNINTERRUPTIBLE, // simple uninterruptible delay + + DELAY_SHAFT_SELF, // Formicid ability NUM_DELAYS }; @@ -1509,6 +1514,7 @@ enum duration_type DUR_WEAK, DUR_DIMENSION_ANCHOR, DUR_ANTIMAGIC, + DUR_ANTENNAE_EXTEND, NUM_DURATIONS }; @@ -2659,6 +2665,10 @@ enum monster_type // menv[].type MONS_SOJOBO, + MONS_FORMICID, + MONS_FORMICID_DRONE, + MONS_FORMICID_VENOM_MAGE, + NUM_MONSTERS, // used for polymorph // MONS_NO_MONSTER can get put in savefiles, so it shouldn't change @@ -2851,6 +2861,7 @@ enum mutation_type MUT_MANA_LINK, MUT_PETRIFICATION_RESISTANCE, MUT_TRAMPLE_RESISTANCE, + MUT_CHITIN_SKIN, NUM_MUTATIONS, RANDOM_MUTATION, @@ -3199,7 +3210,8 @@ enum species_type SP_DJINNI, SP_LAVA_ORC, SP_GARGOYLE, - LAST_VALID_SPECIES = SP_GARGOYLE, + SP_FORMICID, + LAST_VALID_SPECIES = SP_FORMICID, // The high scores viewer still needs enums for removed species. SP_ELF, // (placeholder) SP_HILL_DWARF, // (placeholder) @@ -3486,6 +3498,8 @@ enum spell_type SPELL_CALL_LOST_SOUL, SPELL_DIMENSION_ANCHOR, SPELL_BLINK_ALLIES_ENCIRCLE, + SPELL_SHAFT_SELF, + SPELL_MASS_CURE_POISON, NUM_SPELLS }; diff --git a/crawl-ref/source/godpassive.cc b/crawl-ref/source/godpassive.cc index cd0d3cedab..8f7f7111e8 100644 --- a/crawl-ref/source/godpassive.cc +++ b/crawl-ref/source/godpassive.cc @@ -161,7 +161,7 @@ static bool _two_handed() if (!wpn) return false; - hands_reqd_type wep_type = hands_reqd(*wpn, you.body_size()); + hands_reqd_type wep_type = you.hands_reqd(*wpn); return wep_type == HANDS_TWO; } diff --git a/crawl-ref/source/item_use.cc b/crawl-ref/source/item_use.cc index 1e883918e1..db0d74aca1 100644 --- a/crawl-ref/source/item_use.cc +++ b/crawl-ref/source/item_use.cc @@ -136,9 +136,9 @@ bool can_wield(item_def *weapon, bool say_reason, return false; } - // Only ogres and trolls can wield giant clubs (>= 30 aum) + // Only ogres, trolls, and formicids can wield giant clubs (>= 30 aum) // and large rocks (60 aum). - if (you.body_size() < SIZE_LARGE + if ((you.body_size() < SIZE_LARGE && you.species != SP_FORMICID) && (item_mass(*weapon) >= 500 || weapon->base_type == OBJ_WEAPONS && item_mass(*weapon) >= 300)) @@ -161,6 +161,7 @@ bool can_wield(item_def *weapon, bool say_reason, // Small species wielding large weapons... if (you.body_size(PSIZE_BODY) < SIZE_MEDIUM + && you.species != SP_FORMICID && !check_weapon_wieldable_size(*weapon, you.body_size(PSIZE_BODY))) { SAY(mpr("That's too large for you to wield.")); @@ -572,6 +573,12 @@ static int armour_equip_delay(const item_def &item) // Shields are comparatively easy to wear. if (is_shield(item)) delay = delay / 2 + 1; + + // Formicids are slow to put on helmets + if (you.species == SP_FORMICID && get_armour_slot(item) == EQ_HELMET) + { + delay += 2; + } if (delay < 1) delay = 1; @@ -746,7 +753,7 @@ bool can_wear_armour(const item_def &item, bool verbose, bool ignore_temporary) return false; } - if (player_mutation_level(MUT_ANTENNAE) == 3) + if (player_mutation_level(MUT_ANTENNAE) == 3 && you.species != SP_FORMICID) { if (verbose) mpr("You can't wear any headgear with your large antennae!"); @@ -770,7 +777,7 @@ bool can_wear_armour(const item_def &item, bool verbose, bool ignore_temporary) return false; } - if (player_mutation_level(MUT_ANTENNAE)) + if (player_mutation_level(MUT_ANTENNAE) && you.species != SP_FORMICID) { if (verbose) mpr("You can't wear that with your antennae!"); @@ -846,6 +853,8 @@ bool do_wear_armour(int item, bool quiet) { if (you.species == SP_OCTOPODE) mpr("You need the rest of your tentacles for walking."); + else if (you.species == SP_FORMICID) + mprf("You'd need six %s to do that!", you.hand_name(true).c_str()); else mprf("You'd need three %s to do that!", you.hand_name(true).c_str()); } @@ -1918,7 +1927,10 @@ void zap_wand(int slot) if (wand.sub_type == WAND_TELEPORTATION && you.no_tele(false, false)) { - mpr("You cannot teleport right now."); + if (you.species == SP_FORMICID) + mpr("You cannot teleport."); + else + mpr("You cannot teleport right now."); return; } else if (wand.sub_type == WAND_INVISIBILITY @@ -2965,7 +2977,10 @@ void read_scroll(int slot) case SCR_TELEPORTATION: if (you.no_tele(false, false, which_scroll == SCR_BLINKING)) { - mpr("You cannot teleport right now."); + if (you.species == SP_FORMICID) + mpr("You cannot teleport."); + else + mpr("You cannot teleport right now."); return; } break; @@ -3376,30 +3391,41 @@ bool stasis_blocks_effect(bool calc_unid, { item_def *amulet = you.slot_item(EQ_AMULET, false); - // Just in case a non-amulet stasis source is added. + // For non-amulet sources of stasis. if (amulet && amulet->sub_type != AMU_STASIS) amulet = 0; if (msg) { - const string name(amulet? amulet->name(DESC_YOUR) : "Something"); - const string message = make_stringf(msg, name.c_str()); - - if (noise) + // Override message for formicids + if (you.species == SP_FORMICID) { - if (!noisy(noise, you.pos(), message.c_str()) - && silenced_msg) + mpr("Your sense of stasis keeps you stable."); + } + else + { + const string name(amulet? amulet->name(DESC_YOUR) : "Something"); + const string message = make_stringf(msg, name.c_str()); + + if (noise) { - mprf(silenced_msg, name.c_str()); + if (!noisy(noise, you.pos(), message.c_str()) + && silenced_msg) + { + mprf(silenced_msg, name.c_str()); + } } + else + mpr(message.c_str()); } - else - mpr(message.c_str()); } - // In all cases, the amulet auto-ids if requested. - if (amulet && identify && !item_type_known(*amulet)) + // The amulet auto-ids if requested. + if (you.species != SP_FORMICID + && amulet && identify && !item_type_known(*amulet)) + { wear_id_type(*amulet); + } return true; } return false; diff --git a/crawl-ref/source/itemname.cc b/crawl-ref/source/itemname.cc index 19bb80ef93..ae23ff3ee1 100644 --- a/crawl-ref/source/itemname.cc +++ b/crawl-ref/source/itemname.cc @@ -3174,11 +3174,11 @@ bool is_useless_item(const item_def &item, bool temp) switch (item.sub_type) { case MI_LARGE_ROCK: - return (you.body_size(PSIZE_BODY, !temp) < SIZE_LARGE - || !you.can_throw_large_rocks()); + return (!you.can_throw_large_rocks()); case MI_JAVELIN: case MI_THROWING_NET: - return (you.body_size(PSIZE_BODY, !temp) < SIZE_MEDIUM); + return (you.body_size(PSIZE_BODY, !temp) < SIZE_MEDIUM + && !you.can_throw_large_rocks()); } return false; @@ -3202,7 +3202,10 @@ bool is_useless_item(const item_def &item, bool temp) case SCR_RANDOM_USELESSNESS: return true; case SCR_TELEPORTATION: - return crawl_state.game_is_sprint(); + return (you.species == SP_FORMICID + || crawl_state.game_is_sprint()); + case SCR_BLINKING: + return (you.species == SP_FORMICID); case SCR_AMNESIA: return (you.religion == GOD_TROG); case SCR_RECHARGING: @@ -3248,9 +3251,13 @@ bool is_useless_item(const item_def &item, bool temp) switch (item.sub_type) { case POT_BERSERK_RAGE: - return (you.is_undead + return (you.species == SP_FORMICID + || (you.is_undead && (you.species != SP_VAMPIRE - || temp && you.hunger_state <= HS_SATIATED)); + || temp && you.hunger_state <= HS_SATIATED))); + + case POT_SPEED: + return (you.species == SP_FORMICID); case POT_CURE_MUTATION: #if TAG_MAJOR_VERSION == 34 @@ -3297,9 +3304,13 @@ bool is_useless_item(const item_def &item, bool temp) switch (item.sub_type) { case AMU_RAGE: - return (you.is_undead + return (you.species == SP_FORMICID + || (you.is_undead && (you.species != SP_VAMPIRE - || temp && you.hunger_state <= HS_SATIATED)); + || temp && you.hunger_state <= HS_SATIATED))); + + case AMU_STASIS: + return (you.stasis(false, false)); case AMU_CLARITY: return (you.clarity(false, false)); @@ -3360,10 +3371,12 @@ bool is_useless_item(const item_def &item, bool temp) return (you.religion == GOD_TROG); case RING_TELEPORT_CONTROL: - return crawl_state.game_is_zotdef(); + return (you.species == SP_FORMICID + || crawl_state.game_is_zotdef()); case RING_TELEPORTATION: - return crawl_state.game_is_sprint(); + return (you.species == SP_FORMICID + || crawl_state.game_is_sprint()); case RING_INVISIBILITY: return _invisibility_is_useless(temp); diff --git a/crawl-ref/source/itemprop.cc b/crawl-ref/source/itemprop.cc index ab007ef3b4..46cab4e5c2 100644 --- a/crawl-ref/source/itemprop.cc +++ b/crawl-ref/source/itemprop.cc @@ -1461,17 +1461,14 @@ int single_damage_type(const item_def &item) return ret; } -hands_reqd_type hands_reqd(object_class_type base_type, int sub_type, - size_type size) +size_type weapon_size(const item_def &item) { - item_def item; - item.base_type = base_type; - item.sub_type = sub_type; - return hands_reqd(item, size); + return Weapon_prop[Weapon_index[item.sub_type]].fit_size; } // Give hands required to wield weapon for a torso of "size". -hands_reqd_type hands_reqd(const item_def &item, size_type size) +// Not adjusted by species or anything, which is why it's "basic". +hands_reqd_type basic_hands_reqd(const item_def &item, size_type size) { hands_reqd_type ret = HANDS_ONE; @@ -1486,11 +1483,12 @@ hands_reqd_type hands_reqd(const item_def &item, size_type size) case OBJ_WEAPONS: ret = Weapon_prop[ Weapon_index[item.sub_type] ].hands; + // Adjust handedness only for small races using melee weapons // that are larger than they are. if (!is_range_weapon(item) && size < SIZE_MEDIUM - && Weapon_prop[Weapon_index[item.sub_type]].fit_size > size) + && weapon_size(item) > size) { ret = HANDS_TWO; } @@ -1515,6 +1513,14 @@ hands_reqd_type hands_reqd(const item_def &item, size_type size) return ret; } +hands_reqd_type hands_reqd(const actor* ac, object_class_type base_type, int sub_type) +{ + item_def item; + item.base_type = base_type; + item.sub_type = sub_type; + return ac->hands_reqd(item); +} + bool is_giant_club_type(int wpn_type) { return (wpn_type == WPN_GIANT_CLUB @@ -1911,14 +1917,14 @@ bool is_throwable(const actor *actor, const item_def &wpn, bool force) { if (!force) { - if ((bodysize < SIZE_LARGE - || !actor->can_throw_large_rocks()) + if (!actor->can_throw_large_rocks() && wpn.sub_type == MI_LARGE_ROCK) { return false; } - if (bodysize < SIZE_MEDIUM + if (!actor->can_throw_large_rocks() + && bodysize < SIZE_MEDIUM && (wpn.sub_type == MI_JAVELIN || wpn.sub_type == MI_THROWING_NET)) { @@ -2809,7 +2815,7 @@ bool is_shield(const item_def &item) && get_armour_slot(item) == EQ_SHIELD); } -// Returns true if the given item cannot be wielded with the given shield. +// Returns true if the given item cannot be wielded _by you_ with the given shield. // The currently equipped shield is used if no shield is passed in. bool is_shield_incompatible(const item_def &weapon, const item_def *shield) { @@ -2817,7 +2823,7 @@ bool is_shield_incompatible(const item_def &weapon, const item_def *shield) if (!shield && !(shield = you.shield())) return false; - hands_reqd_type hand = hands_reqd(weapon, you.body_size()); + hands_reqd_type hand = you.hands_reqd(weapon); return (hand == HANDS_TWO && !is_range_weapon(weapon)); } diff --git a/crawl-ref/source/itemprop.h b/crawl-ref/source/itemprop.h index d4cbe67020..54fc185e94 100644 --- a/crawl-ref/source/itemprop.h +++ b/crawl-ref/source/itemprop.h @@ -89,9 +89,10 @@ int weapon_rarity(int w_type) IMMUTABLE; int cmp_weapon_size(const item_def &item, size_type size) PURE; bool check_weapon_wieldable_size(const item_def &item, size_type size) PURE; -hands_reqd_type hands_reqd(const item_def &item, size_type size) PURE; -hands_reqd_type hands_reqd(object_class_type base_type, int sub_type, - size_type size) IMMUTABLE; +size_type weapon_size(const item_def &item) PURE; + +hands_reqd_type basic_hands_reqd(const item_def &item, size_type size) PURE; +hands_reqd_type hands_reqd(const actor* ac, object_class_type base_type, int sub_type); bool is_giant_club_type(int wpn_type) IMMUTABLE; diff --git a/crawl-ref/source/main.cc b/crawl-ref/source/main.cc index 7342073338..79205f1e59 100644 --- a/crawl-ref/source/main.cc +++ b/crawl-ref/source/main.cc @@ -2299,6 +2299,31 @@ static void _decrement_durations() } else you.duration[DUR_GOURMAND] = 0; + + // Retractable antennae + if (you.species == SP_FORMICID && !player_wearing_slot(EQ_HELMET)) + { + if (form_keeps_mutations() + && you.duration[DUR_ANTENNAE_EXTEND] < ANTENNAE_EXTEND_TIME) + { + you.duration[DUR_ANTENNAE_EXTEND] += delay; + if (you.duration[DUR_ANTENNAE_EXTEND] >= ANTENNAE_EXTEND_TIME) + { + you.duration[DUR_ANTENNAE_EXTEND] = ANTENNAE_EXTEND_TIME; + // sinv comes back. + if (you.has_antennae() >= 3) + autotoggle_autopickup(false); + mpr("Your antennae are now fully extended."); + } + #ifdef USE_TILE + init_player_doll(); + #endif + } + } + else + { + you.duration[DUR_ANTENNAE_EXTEND] = 0; + } if (you.duration[DUR_ICEMAIL_DEPLETED] > 0) { @@ -3181,7 +3206,7 @@ static void _player_reacts_to_monsters() manage_fire_shield(you.time_taken); // penance checked there (as you can have antennae too) - if (player_mutation_level(MUT_ANTENNAE) || you.religion == GOD_ASHENZARI) + if (you.has_antennae(true) || you.religion == GOD_ASHENZARI) check_antennae_detect(); if ((you.religion == GOD_ASHENZARI && !player_under_penance()) diff --git a/crawl-ref/source/mon-cast.cc b/crawl-ref/source/mon-cast.cc index e23e9bbf4b..efe668c8f1 100644 --- a/crawl-ref/source/mon-cast.cc +++ b/crawl-ref/source/mon-cast.cc @@ -66,6 +66,7 @@ static bool _valid_mon_spells[NUM_SPELLS]; static int _mons_mesmerise(monster* mons, bool actual = true); static int _mons_cause_fear(monster* mons, bool actual = true); static int _mons_mass_confuse(monster* mons, bool actual = true); +static int _mons_mass_cure_poison(monster* mons, bool actual = true); static int _mons_available_tentacles(monster* head); static coord_def _mons_fragment_target(monster *mons); @@ -1137,6 +1138,8 @@ bool setup_mons_cast(monster* mons, bolt &pbolt, spell_type spell_cast, case SPELL_BLINK_ALLIES_ENCIRCLE: case SPELL_MASS_CONFUSION: case SPELL_ENGLACIATION: + case SPELL_SHAFT_SELF: + case SPELL_MASS_CURE_POISON: return true; default: if (check_validity) @@ -1667,6 +1670,7 @@ static bool _ms_useful_fleeing_out_of_sight(const monster* mon, case SPELL_MAJOR_HEALING: case SPELL_ANIMATE_DEAD: case SPELL_TWISTED_RESURRECTION: + case SPELL_SHAFT_SELF: return true; default: @@ -1750,6 +1754,7 @@ static bool _ms_low_hitpoint_cast(const monster* mon, spell_type monspell) case SPELL_CONTROLLED_BLINK: return targ_adj; case SPELL_TOMB_OF_DOROKLOHE: + case SPELL_SHAFT_SELF: return true; case SPELL_NO_SPELL: return false; @@ -2341,13 +2346,21 @@ bool handle_mon_spell(monster* mons, bolt &beem) } // Check use of LOS attack spells. else if (spell_cast == SPELL_DRAIN_LIFE - || spell_cast == SPELL_OZOCUBUS_REFRIGERATION - || spell_cast == SPELL_OLGREBS_TOXIC_RADIANCE) + || spell_cast == SPELL_OZOCUBUS_REFRIGERATION) { if (cast_los_attack_spell(spell_cast, mons->hit_dice, mons, false) != SPRET_SUCCESS) return false; } + else if (spell_cast == SPELL_OLGREBS_TOXIC_RADIANCE) + { + // Formicids can cast freely as they also have mass cure poison. + // TODO: generalize this? + if (mons->type != MONS_FORMICID_VENOM_MAGE + && cast_los_attack_spell(spell_cast, mons->hit_dice, mons, + false) != SPRET_SUCCESS) + return false; + } // See if we have a good spot to cast LRD at. else if (spell_cast == SPELL_LRD) { @@ -2378,6 +2391,12 @@ bool handle_mon_spell(monster* mons, bolt &beem) if (_mons_mass_confuse(mons, false) < 0) return false; } + // Try to mass cure poison; if we can't, pretend nothing happened. + else if (spell_cast == SPELL_MASS_CURE_POISON) + { + if (_mons_mass_cure_poison(mons, false) < 0) + return false; + } // Check if our enemy can be slowed for Metabolic Englaciation. else if (spell_cast == SPELL_ENGLACIATION) { @@ -3049,6 +3068,54 @@ static int _mons_mass_confuse(monster* mons, bool actual) return retval; } +static int _mons_mass_cure_poison(monster* mons, bool actual) +{ + int retval = -1; + + if (actual) + simple_monster_message(mons, " cures the poison of those in need."); + + for (actor_iterator ai(mons->get_los()); ai; ++ai) + { + if (ai->is_player()) + { + if (!mons->pacified() && !mons->friendly()) + continue; + + if (you.duration[DUR_POISONING] == 0) + continue; + + retval = 0; + + if (actual && you.duration[DUR_POISONING]) + { + reduce_poison_player(2 + random2(3)); + retval = 1; + } + } + else + { + monster* m = ai->as_monster(); + + if (!mons->pacified() && mons->friendly() != m->friendly()) + continue; + + if (!m->has_ench(ENCH_POISON)) + continue; + + retval = 0; + + if (actual) + { + retval = 1; + m->del_ench(ENCH_POISON); + } + } + } + + return retval; +} + static coord_def _mons_fragment_target(monster *mons) { coord_def target(GXM+1, GYM+1); @@ -4215,6 +4282,14 @@ void mons_cast(monster* mons, bolt &pbolt, spell_type spell_cast, } return; } + case SPELL_SHAFT_SELF: + { + if (is_valid_shaft_level()) + { + mons->do_shaft(); + } + return; + } case SPELL_CHAIN_LIGHTNING: cast_chain_lightning(4 * mons->hit_dice, mons); return; @@ -4379,6 +4454,10 @@ void mons_cast(monster* mons, bolt &pbolt, spell_type spell_cast, _mons_mass_confuse(mons); return; + case SPELL_MASS_CURE_POISON: + _mons_mass_cure_poison(mons); + return; + case SPELL_ENGLACIATION: if (you.can_see(mons)) simple_monster_message(mons, " radiates an aura of cold."); diff --git a/crawl-ref/source/mon-data.h b/crawl-ref/source/mon-data.h index a48d70314c..6b4c2e40f4 100644 --- a/crawl-ref/source/mon-data.h +++ b/crawl-ref/source/mon-data.h @@ -204,7 +204,7 @@ static monsterentry mondata[] = { // Real monsters begin here {dlb}: -// ants ('a') +// ants and formicids ('a') { MONS_WORKER_ANT, 'a', RED, "worker ant", M_NO_SKELETON, @@ -236,11 +236,47 @@ static monsterentry mondata[] = { 600, 10, MONS_WORKER_ANT, MONS_SOLDIER_ANT, MH_NATURAL, -3, { {AT_STING, AF_POISON_NASTY, 14}, AT_NO_ATK, AT_NO_ATK, AT_NO_ATK }, { 6, 3, 5, 0 }, - 8, 10, MST_NO_SPELLS, CE_POISONOUS, Z_SMALL, S_SILENT, + 8, 10, MST_NO_SPELLS, CE_POISONOUS, Z_NOZOMBIE, S_SILENT, I_INSECT, HT_LAND, FL_NONE, 10, DEFAULT_ENERGY, MONUSE_NOTHING, MONEAT_NOTHING, SIZE_MEDIUM }, +{ + MONS_FORMICID, 'a', GREEN, "formicid", + M_WARM_BLOOD | M_SEE_INVIS | M_FAKE_SPELLS | M_FIGHTER | M_SPEAKS | M_PERMA_STASIS | M_FOUR_HANDS, + MR_VUL_POISON, + 600, 10, MONS_FORMICID, MONS_FORMICID, MH_NATURAL, -4, + { {AT_HIT, AF_PLAIN, 10}, AT_NO_ATK, AT_NO_ATK, AT_NO_ATK }, + { 4, 4, 6, 0 }, + 3, 10, MST_FORMICID, CE_CONTAMINATED, Z_NOZOMBIE, S_SHOUT, + I_NORMAL, HT_LAND, FL_NONE, 10, DEFAULT_ENERGY, + MONUSE_WEAPONS_ARMOUR, MONEAT_NOTHING, SIZE_MEDIUM +}, + +{ + MONS_FORMICID_DRONE, 'a', YELLOW, "formicid drone", + M_WARM_BLOOD | M_SEE_INVIS | M_FAKE_SPELLS | M_SPEAKS | M_PERMA_STASIS | M_FOUR_HANDS, + MR_VUL_POISON, + 600, 12, MONS_FORMICID, MONS_FORMICID, MH_NATURAL, -4, + { {AT_HIT, AF_PLAIN, 20}, AT_NO_ATK, AT_NO_ATK, AT_NO_ATK }, + { 6, 4, 8, 0 }, + 3, 10, MST_FORMICID, CE_CONTAMINATED, Z_NOZOMBIE, S_SHOUT, + I_NORMAL, HT_LAND, FL_NONE, 10, DEFAULT_ENERGY, + MONUSE_WEAPONS_ARMOUR, MONEAT_NOTHING, SIZE_MEDIUM +}, + +{ + MONS_FORMICID_VENOM_MAGE, 'a', MAGENTA, "formicid venom mage", + M_WARM_BLOOD | M_SEE_INVIS | M_SPELLCASTER | M_SPEAKS | M_PERMA_STASIS | M_FOUR_HANDS, + MR_VUL_POISON, + 600, 12, MONS_FORMICID, MONS_FORMICID, MH_NATURAL, -4, + { {AT_HIT, AF_PLAIN, 10}, AT_NO_ATK, AT_NO_ATK, AT_NO_ATK }, + { 7, 3, 5, 0 }, + 3, 8, MST_FORMICID_VENOM_MAGE, CE_CONTAMINATED, Z_NOZOMBIE, S_SHOUT, + I_HIGH, HT_LAND, FL_NONE, 10, DEFAULT_ENERGY, + MONUSE_WEAPONS_ARMOUR, MONEAT_NOTHING, SIZE_MEDIUM +}, + // batty monsters ('b') { MONS_BAT, 'b', LIGHTGREY, "bat", diff --git a/crawl-ref/source/mon-flags.h b/crawl-ref/source/mon-flags.h index 2a2be1a2b3..3d5926d3dd 100644 --- a/crawl-ref/source/mon-flags.h +++ b/crawl-ref/source/mon-flags.h @@ -155,6 +155,12 @@ const uint64_t M_NO_GEN_DERIVED = (uint64_t)1<<47; // tries to maintain range unless target is incapacitated const uint64_t M_STABBER = (uint64_t)1<<48; +// always under stasis effect +const uint64_t M_PERMA_STASIS = (uint64_t)1<<49; + +// can wield large weapons with a shield and big weapons without a shield +const uint64_t M_FOUR_HANDS = (uint64_t)1<<50; + // Same for flags for actual monsters. typedef uint64_t monster_flag_type; const uint64_t MF_NO_REWARD = BIT(0); // no benefit from killing diff --git a/crawl-ref/source/mon-gear.cc b/crawl-ref/source/mon-gear.cc index bbf3fe6b63..885e266e41 100644 --- a/crawl-ref/source/mon-gear.cc +++ b/crawl-ref/source/mon-gear.cc @@ -367,6 +367,33 @@ static item_make_species_type _give_weapon(monster* mon, int level, } break; + case MONS_FORMICID: + case MONS_FORMICID_DRONE: + item_race = MAKE_ITEM_NO_RACE; + item.base_type = OBJ_WEAPONS; + if (one_chance_in(4)) + { + item.sub_type = coinflip() ? WPN_GIANT_CLUB : WPN_GIANT_SPIKED_CLUB; + } + else + { + item.sub_type = random_choose_weighted(15, WPN_HAND_AXE, 15, WPN_SABRE, + 15, WPN_MACE, 15, WPN_FLAIL, + 5, WPN_GREAT_SWORD, 5, WPN_GREAT_MACE, + 5, WPN_BATTLEAXE, 0); + } + if (coinflip()) + { + force_item = true; + item.plus += random2(3); + item.plus2 += random2(3); + if (one_chance_in(20)) + { + level = MAKE_GOOD_ITEM; + } + } + break; + case MONS_DWARF: case MONS_DEEP_DWARF: item_race = MAKE_ITEM_DWARVEN; @@ -497,9 +524,9 @@ static item_make_species_type _give_weapon(monster* mon, int level, case MONS_ORC_PRIEST: item_race = MAKE_ITEM_ORCISH; // deliberate fall through {gdl} - case MONS_DRACONIAN: case MONS_DRACONIAN_ZEALOT: + case MONS_FORMICID_VENOM_MAGE: if (!one_chance_in(5)) { item.base_type = OBJ_WEAPONS; @@ -1829,8 +1856,8 @@ static void _give_shield(monster* mon, int level) // If the monster is already wielding/carrying a two-handed weapon, // it doesn't get a shield. (Monsters always prefer raw damage to // protection!) - if (main_weap && hands_reqd(*main_weap, mon->body_size()) == HANDS_TWO - || alt_weap && hands_reqd(*alt_weap, mon->body_size()) == HANDS_TWO) + if (main_weap && mon->hands_reqd(*main_weap) == HANDS_TWO + || alt_weap && mon->hands_reqd(*alt_weap) == HANDS_TWO) { return; } @@ -2192,12 +2219,23 @@ static void _give_armour(monster* mon, int level, bool spectral_orcs) case MONS_DEEP_DWARF_NECROMANCER: case MONS_DEEP_DWARF_ARTIFICER: + case MONS_FORMICID_VENOM_MAGE: case MONS_HELLBINDER: item_race = MAKE_ITEM_NO_RACE; item.base_type = OBJ_ARMOUR; item.sub_type = ARM_ROBE; break; + case MONS_FORMICID: + case MONS_FORMICID_DRONE: + item_race = MAKE_ITEM_NO_RACE; + item.base_type = OBJ_ARMOUR; + item.sub_type = random_choose_weighted(5, ARM_SCALE_MAIL, + 3, ARM_CHAIN_MAIL, + 1, ARM_PLATE_ARMOUR, + 0); + break; + case MONS_DWARF: case MONS_DEEP_DWARF: case MONS_DEEP_DWARF_SCION: diff --git a/crawl-ref/source/mon-grow.cc b/crawl-ref/source/mon-grow.cc index f18c6cfacd..e7b83b1276 100644 --- a/crawl-ref/source/mon-grow.cc +++ b/crawl-ref/source/mon-grow.cc @@ -76,6 +76,8 @@ static const monster_level_up mon_grow[] = monster_level_up(MONS_TENGU, MONS_TENGU_WARRIOR), monster_level_up(MONS_TENGU_CONJURER, MONS_TENGU_REAVER), monster_level_up(MONS_TENGU_WARRIOR, MONS_TENGU_REAVER), + + monster_level_up(MONS_FORMICID, MONS_FORMICID_DRONE), }; mons_experience_levels::mons_experience_levels() diff --git a/crawl-ref/source/mon-spll.h b/crawl-ref/source/mon-spll.h index f5ee5af00e..1ef0869e6e 100644 --- a/crawl-ref/source/mon-spll.h +++ b/crawl-ref/source/mon-spll.h @@ -2141,6 +2141,28 @@ SPELL_MELEE, SPELL_NO_SPELL } + }, + + { MST_FORMICID, + { + SPELL_NO_SPELL, + SPELL_NO_SPELL, + SPELL_NO_SPELL, + SPELL_NO_SPELL, + SPELL_DIG, + SPELL_SHAFT_SELF, + } + }, + + { MST_FORMICID_VENOM_MAGE, + { + SPELL_OLGREBS_TOXIC_RADIANCE, + SPELL_OLGREBS_TOXIC_RADIANCE, + SPELL_NO_SPELL, + SPELL_MASS_CURE_POISON, + SPELL_DIG, + SPELL_SHAFT_SELF, + } } #endif diff --git a/crawl-ref/source/monster.cc b/crawl-ref/source/monster.cc index ca223edd7b..56568edc5a 100644 --- a/crawl-ref/source/monster.cc +++ b/crawl-ref/source/monster.cc @@ -536,6 +536,21 @@ item_def *monster::weapon(int which_attack) const return (weap == NON_ITEM ? NULL : &mitm[weap]); } +// Give hands required to wield weapon. +hands_reqd_type monster::hands_reqd(const item_def &item) const +{ + if (mons_class_flag(type, M_FOUR_HANDS)) + { + if (weapon_size(item) >= SIZE_BIG) + return HANDS_TWO; + else + return HANDS_ONE; + } + else + return actor::hands_reqd(item); +} + + bool monster::can_wield(const item_def& item, bool ignore_curse, bool ignore_brand, bool ignore_shield, bool ignore_transform) const @@ -568,7 +583,7 @@ bool monster::can_wield(const item_def& item, bool ignore_curse, item_def* weap2 = NULL; if (mons_wields_two_weapons(this)) { - if (!weap1 || hands_reqd(*weap1, body_size()) != HANDS_TWO) + if (!weap1 || hands_reqd(*weap1) != HANDS_TWO) avail_slots = 2; const int offhand = _mons_offhand_weapon_index(this); @@ -582,7 +597,7 @@ bool monster::can_wield(const item_def& item, bool ignore_curse, // Barehanded needs two hands. const bool two_handed = item.base_type == OBJ_UNASSIGNED - || hands_reqd(item, body_size()) == HANDS_TWO; + || hands_reqd(item) == HANDS_TWO; item_def* _shield = NULL; if (inv[MSLOT_SHIELD] != NON_ITEM) @@ -1228,7 +1243,7 @@ bool monster::pickup(item_def &item, int slot, int near, bool force_merge) // (Monsters will always favour damage over protection.) if ((slot == MSLOT_WEAPON || slot == MSLOT_ALT_WEAPON) && inv[MSLOT_SHIELD] != NON_ITEM - && hands_reqd(item, body_size()) == HANDS_TWO) + && hands_reqd(item) == HANDS_TWO) { if (!drop_item(MSLOT_SHIELD, near)) return false; @@ -1240,9 +1255,9 @@ bool monster::pickup(item_def &item, int slot, int near, bool force_merge) { const item_def* wpn = mslot_item(MSLOT_WEAPON); const item_def* alt = mslot_item(MSLOT_ALT_WEAPON); - if (wpn && hands_reqd(*wpn, body_size()) == HANDS_TWO) + if (wpn && hands_reqd(*wpn) == HANDS_TWO) return false; - if (alt && hands_reqd(*alt, body_size()) == HANDS_TWO) + if (alt && hands_reqd(*alt) == HANDS_TWO) return false; } @@ -1747,7 +1762,7 @@ bool monster::wants_weapon(const item_def &weap) const // Monsters capable of dual-wielding will always prefer two weapons // to a single two-handed one, however strong. if (mons_wields_two_weapons(this) - && hands_reqd(weap, body_size()) == HANDS_TWO) + && hands_reqd(weap) == HANDS_TWO) { return false; } @@ -1774,7 +1789,7 @@ bool monster::wants_armour(const item_def &item) const if (is_shield(item) && (mons_wields_two_weapons(this) || mslot_item(MSLOT_WEAPON) - && hands_reqd(*mslot_item(MSLOT_WEAPON), body_size()) + && hands_reqd(*mslot_item(MSLOT_WEAPON)) == HANDS_TWO)) { return false; @@ -5960,8 +5975,19 @@ bool monster::check_clarity(bool silent) const return true; } +bool monster::stasis(bool calc_unid, bool items) const +{ + if (mons_class_flag(type, M_PERMA_STASIS)) + return true; + + return actor::stasis(calc_unid, items); +} + bool monster::check_stasis(bool silent, bool calc_unid) const { + if (mons_class_flag(type, M_PERMA_STASIS)) + return true; + if (!stasis()) return false; diff --git a/crawl-ref/source/monster.h b/crawl-ref/source/monster.h index c2a4ba2be5..83a02298d2 100644 --- a/crawl-ref/source/monster.h +++ b/crawl-ref/source/monster.h @@ -239,6 +239,8 @@ public: item_def *launcher(); item_def *missiles(); item_def *shield(); + + hands_reqd_type hands_reqd(const item_def &item) const; bool can_wield(const item_def &item, bool ignore_curse = false, @@ -348,6 +350,8 @@ public: bool no_tele(bool calc_unid = true, bool permit_id = true, bool blink = false) const; bool res_corr(bool calc_unid = true, bool items = true) const; + + bool stasis(bool calc_unid = true, bool items = true) const; flight_type flight_mode() const; bool can_cling_to_walls() const; diff --git a/crawl-ref/source/mutation-data.h b/crawl-ref/source/mutation-data.h index 622936cb02..fdf5346e58 100644 --- a/crawl-ref/source/mutation-data.h +++ b/crawl-ref/source/mutation-data.h @@ -1517,5 +1517,23 @@ "trample resistance" }, +{ MUT_CHITIN_SKIN, 0, 2, false, true, true, + "chitin skin", + + {"Your skin is composed of chitin. (AC +3)", + "Your body is surrounded by an exoskeleton. (AC +6)", + ""}, + + {"Your skin hardens.", + "Your skin hardens into an exoskeleton.", + ""}, + + {"Your skin softens.", + "Your skin softens.", + ""}, + + "chitin skin" +} + #endif diff --git a/crawl-ref/source/mutation.cc b/crawl-ref/source/mutation.cc index c77b9374bc..6341a3522e 100644 --- a/crawl-ref/source/mutation.cc +++ b/crawl-ref/source/mutation.cc @@ -202,6 +202,7 @@ mutation_activity_type mutation_activity_level(mutation_type mut) case MUT_FAST: case MUT_SLOW: case MUT_IRIDESCENT_SCALES: + case MUT_CHITIN_SKIN: return MUTACT_INACTIVE; case MUT_LARGE_BONE_PLATES: case MUT_ROUGH_BLACK_SCALES: @@ -599,28 +600,38 @@ string describe_mutations(bool center_title) break; } + case SP_FORMICID: + result += "Your are under a permanent stasis effect.\n"; + result += "You can dig through walls and to a lower floor.\n"; + result += "Your four strong arms can wield any weapon.\n"; + result += "You are susceptible to poison.\n"; + result += "The antennae on your head can retract.\n"; + have_any = true; + break; + default: break; } - switch (you.body_size(PSIZE_TORSO, true)) + if (you.species != SP_FELID) { - case SIZE_LITTLE: - if (you.species == SP_FELID) + switch (you.body_size(PSIZE_TORSO, true)) + { + case SIZE_LITTLE: + result += "You are tiny and cannot use many weapons and most armour.\n"; + have_any = true; break; - result += "You are tiny and cannot use many weapons and most armour.\n"; - have_any = true; - break; - case SIZE_SMALL: - result += "You are small and have problems with some larger weapons.\n"; - have_any = true; - break; - case SIZE_LARGE: - result += "You are too large for most types of armour.\n"; - have_any = true; - break; - default: - break; + case SIZE_SMALL: + result += "You are small and have problems with some larger weapons.\n"; + have_any = true; + break; + case SIZE_LARGE: + result += "You are too large for most types of armour.\n"; + have_any = true; + break; + default: + break; + } } if (player_genus(GENPC_DRACONIAN)) @@ -1343,6 +1354,20 @@ bool physiology_mutation_conflict(mutation_type mutat) return true; } + if (you.species == SP_FORMICID) + { + // Formicids have stasis and so prevent mutations that would do nothing. + // Formicids are weak to poison so prevent poison resistance mutation. + if (mutat == MUT_BERSERK + || mutat == MUT_BLINK + || mutat == MUT_TELEPORT + || mutat == MUT_TELEPORT_CONTROL + || mutat == MUT_POISON_RESISTANCE) + { + return true; + } + } + // Heat doesn't hurt fire, djinn don't care about hunger. if (you.species == SP_DJINNI && (mutat == MUT_HEAT_RESISTANCE || mutat == MUT_FAST_METABOLISM || mutat == MUT_SLOW_METABOLISM @@ -1678,6 +1703,11 @@ bool mutate(mutation_type which_mutation, const string &reason, bool failMsg, case MUT_ANTENNAE: // Horns & Antennae 3 removes all headgear. Same algorithm as with // glove removal. + + // Formicids can keep wearing helmets. + if (mutat == MUT_ANTENNAE && you.species == SP_FORMICID) + break; + if (you.mutation[mutat] >= 3 && !you.melded[EQ_HELMET]) remove_one_equip(EQ_HELMET, false, true); // Intentional fall-through @@ -1691,11 +1721,6 @@ bool mutate(mutation_type which_mutation, const string &reason, bool failMsg, } break; - case MUT_ACUTE_VISION: - // We might have to turn autopickup back on again. - autotoggle_autopickup(false); - break; - case MUT_NIGHTSTALKER: update_vision_range(); break; @@ -1712,6 +1737,15 @@ bool mutate(mutation_type which_mutation, const string &reason, bool failMsg, break; } + // We might have to turn autopickup back on again. + // Check it after removing helmets because they could be suppressed + // for Formicids. + if (mutat == MUT_ACUTE_VISION + || (mutat == MUT_ANTENNAE && you.has_antennae() >= 3)) + { + autotoggle_autopickup(false); + } + // Amusement value will be 12 * (11-rarity) * Xom's-sense-of-humor. xom_is_stimulated(_calc_mutation_amusement_value(mutat)); @@ -2522,7 +2556,7 @@ void check_demonic_guardian() void check_antennae_detect() { - int radius = player_mutation_level(MUT_ANTENNAE) * 2; + int radius = you.has_antennae(true) * 2; if (you.religion == GOD_ASHENZARI && !player_under_penance()) radius = max(radius, you.piety / 20); if (radius <= 0) diff --git a/crawl-ref/source/newgame.cc b/crawl-ref/source/newgame.cc index daefd44a0f..7a59e7876f 100644 --- a/crawl-ref/source/newgame.cc +++ b/crawl-ref/source/newgame.cc @@ -1644,7 +1644,8 @@ static vector<weapon_choice> _get_weapons(const newgame_def* ng) if (wp.first == WPN_THROWN) { - if (species_size(ng->species, PSIZE_TORSO) == SIZE_LARGE) + if (species_size(ng->species, PSIZE_TORSO) == SIZE_LARGE + || ng->species == SP_FORMICID) wp.first = WPN_ROCKS; else if (species_size(ng->species, PSIZE_TORSO) <= SIZE_SMALL) wp.first = WPN_DARTS; diff --git a/crawl-ref/source/ng-restr.cc b/crawl-ref/source/ng-restr.cc index 930a60c501..5e6591fceb 100644 --- a/crawl-ref/source/ng-restr.cc +++ b/crawl-ref/source/ng-restr.cc @@ -57,6 +57,7 @@ char_choice_restriction job_allowed(species_type speci, job_type job) case SP_OCTOPODE: case SP_DJINNI: case SP_GARGOYLE: + case SP_FORMICID: return CC_RESTRICTED; default: return CC_UNRESTRICTED; @@ -97,6 +98,7 @@ char_choice_restriction job_allowed(species_type speci, job_type job) case SP_OCTOPODE: case SP_GARGOYLE: case SP_DJINNI: + case SP_FORMICID: return CC_RESTRICTED; default: return CC_UNRESTRICTED; @@ -201,6 +203,7 @@ char_choice_restriction job_allowed(species_type speci, job_type job) case SP_GHOUL: case SP_FELID: case SP_OCTOPODE: + case SP_FORMICID: return CC_RESTRICTED; default: return CC_UNRESTRICTED; @@ -226,6 +229,7 @@ char_choice_restriction job_allowed(species_type speci, job_type job) case SP_GHOUL: case SP_VAMPIRE: case SP_OCTOPODE: + case SP_FORMICID: return CC_RESTRICTED; default: return CC_UNRESTRICTED; @@ -245,6 +249,7 @@ char_choice_restriction job_allowed(species_type speci, job_type job) case SP_GHOUL: case SP_VAMPIRE: case SP_FELID: + case SP_FORMICID: return CC_RESTRICTED; default: return CC_UNRESTRICTED; @@ -265,6 +270,7 @@ char_choice_restriction job_allowed(species_type speci, job_type job) case SP_MINOTAUR: case SP_GHOUL: case SP_VAMPIRE: + case SP_FORMICID: return CC_RESTRICTED; default: return CC_UNRESTRICTED; @@ -311,6 +317,7 @@ char_choice_restriction job_allowed(species_type speci, job_type job) case SP_TROLL: case SP_MINOTAUR: case SP_GHOUL: + case SP_FORMICID: return CC_RESTRICTED; default: return CC_UNRESTRICTED; @@ -328,6 +335,7 @@ char_choice_restriction job_allowed(species_type speci, job_type job) case SP_MINOTAUR: case SP_DJINNI: case SP_GARGOYLE: + case SP_FORMICID: return CC_RESTRICTED; default: return CC_UNRESTRICTED; @@ -370,6 +378,7 @@ char_choice_restriction job_allowed(species_type speci, job_type job) case SP_GHOUL: case SP_VAMPIRE: case SP_FELID: + case SP_FORMICID: return CC_RESTRICTED; default: return CC_UNRESTRICTED; @@ -390,6 +399,7 @@ char_choice_restriction job_allowed(species_type speci, job_type job) case SP_TENGU: case SP_FELID: case SP_DJINNI: + case SP_FORMICID: return CC_RESTRICTED; default: return CC_UNRESTRICTED; @@ -411,6 +421,7 @@ char_choice_restriction job_allowed(species_type speci, job_type job) case SP_VAMPIRE: case SP_FELID: case SP_GARGOYLE: + case SP_FORMICID: return CC_RESTRICTED; default: return CC_UNRESTRICTED; @@ -479,6 +490,7 @@ char_choice_restriction job_allowed(species_type speci, job_type job) case SP_GHOUL: case SP_OCTOPODE: case SP_DJINNI: + case SP_FORMICID: return CC_RESTRICTED; default: return CC_UNRESTRICTED; @@ -605,6 +617,7 @@ char_choice_restriction weapon_restriction(weapon_type wpn, case SP_HALFLING: case SP_KOBOLD: case SP_SPRIGGAN: + case SP_FORMICID: return CC_UNRESTRICTED; default: @@ -640,6 +653,7 @@ char_choice_restriction weapon_restriction(weapon_type wpn, case SP_VAMPIRE: case SP_OCTOPODE: case SP_BASE_DRACONIAN: + case SP_FORMICID: return CC_UNRESTRICTED; default: @@ -664,6 +678,7 @@ char_choice_restriction weapon_restriction(weapon_type wpn, case SP_OCTOPODE: case SP_BASE_DRACONIAN: case SP_DJINNI: + case SP_FORMICID: return CC_UNRESTRICTED; case SP_SPRIGGAN: @@ -701,6 +716,7 @@ char_choice_restriction weapon_restriction(weapon_type wpn, case SP_OCTOPODE: case SP_BASE_DRACONIAN: case SP_DJINNI: + case SP_FORMICID: return CC_UNRESTRICTED; default: @@ -748,6 +764,7 @@ char_choice_restriction weapon_restriction(weapon_type wpn, case SP_SLUDGE_ELF: case SP_BASE_DRACONIAN: case SP_DJINNI: + case SP_FORMICID: return CC_UNRESTRICTED; default: @@ -786,6 +803,7 @@ char_choice_restriction weapon_restriction(weapon_type wpn, case SP_LAVA_ORC: case SP_SLUDGE_ELF: case SP_DJINNI: + case SP_FORMICID: return CC_RESTRICTED; case SP_FELID: return CC_BANNED; @@ -818,6 +836,7 @@ char_choice_restriction weapon_restriction(weapon_type wpn, { case SP_OGRE: case SP_TROLL: + case SP_FORMICID: return CC_UNRESTRICTED; default: return CC_BANNED; @@ -828,6 +847,7 @@ char_choice_restriction weapon_restriction(weapon_type wpn, { case SP_DEEP_DWARF: case SP_DJINNI: + case SP_FORMICID: return CC_RESTRICTED; case SP_SPRIGGAN: case SP_FELID: @@ -841,6 +861,7 @@ char_choice_restriction weapon_restriction(weapon_type wpn, { case SP_DEEP_DWARF: case SP_DJINNI: + case SP_FORMICID: return CC_RESTRICTED; case SP_FELID: return CC_BANNED; diff --git a/crawl-ref/source/ng-setup.cc b/crawl-ref/source/ng-setup.cc index ba978e4a62..b2ddad316b 100644 --- a/crawl-ref/source/ng-setup.cc +++ b/crawl-ref/source/ng-setup.cc @@ -79,6 +79,7 @@ static void _species_stat_init(species_type which_species) case SP_MERFOLK: sb = 6; ib = 5; db = 7; break; // 18 case SP_TENGU: sb = 6; ib = 6; db = 7; break; // 19 + case SP_FORMICID: sb = 10; ib = 5; db = 4; break; // 19 case SP_KOBOLD: sb = 5; ib = 4; db = 8; break; // 17 case SP_HALFLING: sb = 3; ib = 6; db = 9; break; // 18 @@ -348,6 +349,10 @@ void give_basic_mutations(species_type speci) you.mutation[MUT_CAMOUFLAGE] = 1; you.mutation[MUT_GELATINOUS_BODY] = 1; break; + case SP_FORMICID: + you.mutation[MUT_ANTENNAE] = 1; + you.mutation[MUT_CHITIN_SKIN] = 1; + break; default: break; } @@ -972,6 +977,12 @@ static void _give_items_skills(const newgame_def& ng) if (you.species == SP_DEEP_DWARF) newgame_make_item(-1, EQ_NONE, OBJ_WANDS, WAND_HEAL_WOUNDS, -1, 1, 5); + // Formicids get curing potions to offset their poison weakness. + if (you.species == SP_FORMICID) + { + newgame_make_item(-1, EQ_NONE, OBJ_POTIONS, POT_CURING, -1, 2); + } + // Zotdef: everyone gets a bonus two potions of curing, plus two // free levels in Traps & Doors so they can replace old traps with // better ones. diff --git a/crawl-ref/source/output.cc b/crawl-ref/source/output.cc index 1fa0a3ba7a..bd284eecb5 100644 --- a/crawl-ref/source/output.cc +++ b/crawl-ref/source/output.cc @@ -2652,6 +2652,14 @@ static string _status_mut_abilities(int sw) mutations.push_back(_dragon_abil("breathe steam")); break; + case SP_FORMICID: + mutations.push_back("permanent stasis"); + mutations.push_back("dig shafts and tunnels"); + mutations.push_back("four strong arms"); + mutations.push_back("poison weakness"); + mutations.push_back("retractable antennae"); + break; + default: break; } //end switch - innate abilities @@ -2820,6 +2828,9 @@ static string _status_mut_abilities(int sw) AC_change += (level == 3) ? 2 : 1; EV_change += level - 1; break; + case MUT_CHITIN_SKIN: + AC_change += level * 3; + break; default: die("mutation without a short desc: %d", i); } diff --git a/crawl-ref/source/player-act.cc b/crawl-ref/source/player-act.cc index d5ea690c83..8d724090a2 100644 --- a/crawl-ref/source/player-act.cc +++ b/crawl-ref/source/player-act.cc @@ -287,6 +287,20 @@ item_def *player::weapon(int /* which_attack */) const return slot_item(EQ_WEAPON, false); } +// Give hands required to wield weapon. +hands_reqd_type player::hands_reqd(const item_def &item) const +{ + if (species == SP_FORMICID) + { + if (weapon_size(item) >= SIZE_BIG) + return HANDS_TWO; + else + return HANDS_ONE; + } + else + return actor::hands_reqd(item); +} + bool player::can_wield(const item_def& item, bool ignore_curse, bool ignore_brand, bool ignore_shield, bool ignore_transform) const @@ -299,7 +313,7 @@ bool player::can_wield(const item_def& item, bool ignore_curse, // Unassigned means unarmed combat. const bool two_handed = item.base_type == OBJ_UNASSIGNED - || hands_reqd(item, body_size()) == HANDS_TWO; + || hands_reqd(item) == HANDS_TWO; if (two_handed && !ignore_shield && player_wearing_slot(EQ_SHIELD)) return false; @@ -312,6 +326,9 @@ bool player::could_wield(const item_def &item, bool ignore_brand, { if (species == SP_FELID) return false; + if (species == SP_FORMICID) + return true; + if (body_size(PSIZE_TORSO, ignore_transform) < SIZE_LARGE && (item_mass(item) >= 500 || item.base_type == OBJ_WEAPONS @@ -690,9 +707,7 @@ bool player::can_go_berserk(bool intentional, bool potion, bool quiet) const { if (verbose) { - const item_def *amulet = you.slot_item(EQ_AMULET, false); - mprf("You cannot go berserk with %s on.", - amulet? amulet->name(DESC_YOUR).c_str() : "your amulet"); + mprf("You cannot go berserk while under stasis"); } return false; } diff --git a/crawl-ref/source/player-equip.cc b/crawl-ref/source/player-equip.cc index 7749b7a1cd..bd427a2beb 100644 --- a/crawl-ref/source/player-equip.cc +++ b/crawl-ref/source/player-equip.cc @@ -1007,6 +1007,16 @@ static void _equip_armour_effect(item_def& arm, bool unmeld) if (get_item_slot(arm) == EQ_SHIELD) warn_shield_penalties(); + + if (you.species == SP_FORMICID + && get_item_slot(arm) == EQ_HELMET) + { + mpr("Your antennae retract into your head."); + you.duration[DUR_ANTENNAE_EXTEND] = 0; + #ifdef USE_TILE + init_player_doll(); + #endif + } you.redraw_armour_class = true; you.redraw_evasion = true; diff --git a/crawl-ref/source/player.cc b/crawl-ref/source/player.cc index c9cae7779f..8e9a00c40d 100644 --- a/crawl-ref/source/player.cc +++ b/crawl-ref/source/player.cc @@ -731,7 +731,8 @@ bool you_can_wear(int eq, bool special_armour) case EQ_HELMET: // No caps or hats with Horns 3 or Antennae 3. if (player_mutation_level(MUT_HORNS, false) == 3 - || player_mutation_level(MUT_ANTENNAE, false) == 3) + || (player_mutation_level(MUT_ANTENNAE, false) == 3 + && you.species != SP_FORMICID)) { return false; } @@ -740,7 +741,8 @@ bool you_can_wear(int eq, bool special_armour) return true; if (player_mutation_level(MUT_HORNS, false) || player_mutation_level(MUT_BEAK, false) - || player_mutation_level(MUT_ANTENNAE, false)) + || (player_mutation_level(MUT_ANTENNAE, false) + && you.species != SP_FORMICID)) { return false; } @@ -840,8 +842,12 @@ bool you_tran_can_wear(int eq, bool check_mutation) if (eq == EQ_HELMET && player_mutation_level(MUT_HORNS) == 3) return false; - if (eq == EQ_HELMET && player_mutation_level(MUT_ANTENNAE) == 3) + if (eq == EQ_HELMET + && player_mutation_level(MUT_ANTENNAE) == 3 + && you.species != SP_FORMICID) + { return false; + } if (eq == EQ_BOOTS && (you.fishtail @@ -1908,6 +1914,10 @@ int player_res_poison(bool calc_unid, bool temp, bool items) // Only thirsty vampires are naturally poison resistant. if (you.species == SP_VAMPIRE && you.hunger_state < HS_SATIATED) rp++; + + // Formicids are vulnerable, but can make up for it with 2 rPois sources. + if (you.species == SP_FORMICID) + rp--; if (temp) { @@ -1937,7 +1947,7 @@ int player_res_poison(bool calc_unid, bool temp, bool items) // Give vulnerability for Spider Form, and only let one level of rP to make // up for it (never be poison resistant in Spider Form). - rp = (rp > 0 ? 1 : 0); + rp = (rp > 0 ? 1 : rp); if (temp) { @@ -3610,6 +3620,22 @@ void level_change(int source, const char* aux, bool skip_attribute_increase) modify_stat(STAT_RANDOM, 1, false, "level gain"); break; + case SP_FORMICID: + if ((you.experience_level == 8) + || (you.experience_level == 16)) + { + perma_mutate(MUT_ANTENNAE, 1, "level up"); + #ifdef USE_TILE + init_player_doll(); + #endif + } + + if (!(you.experience_level % 4)) + { + modify_stat(STAT_STR, 1, false, "level gain"); + } + break; + default: break; } @@ -4375,6 +4401,14 @@ bool player::gourmand(bool calc_unid, bool items) const return actor::gourmand(calc_unid, items); } +bool player::stasis(bool calc_unid, bool items) const +{ + if (species == SP_FORMICID) + return true; + + return actor::stasis(calc_unid, items); +} + unsigned int exp_needed(int lev, int exp_apt) { unsigned int level = 0; @@ -5738,6 +5772,8 @@ void player::init() temperature = 1; // 1 is min; 15 is max. temperature_last = 1; + + duration[DUR_ANTENNAE_EXTEND] = ANTENNAE_EXTEND_TIME; xray_vision = false; @@ -6504,6 +6540,8 @@ int player::armour_class() const ? 100 + _mut_level(MUT_THIN_METALLIC_SCALES, MUTACT_FULL) * 100 : 0; // +2, +3, +4 AC += _mut_level(MUT_YELLOW_SCALES, MUTACT_FULL) ? 100 + _mut_level(MUT_YELLOW_SCALES, MUTACT_FULL) * 100 : 0; // +2, +3, +4 + AC += player_mutation_level(MUT_CHITIN_SKIN) + ? player_mutation_level(MUT_CHITIN_SKIN) * 300 : 0; // +3, +6 return (AC / 100); } @@ -6814,6 +6852,7 @@ int player_res_magic(bool calc_unid, bool temp) case SP_VAMPIRE: case SP_DEMIGOD: case SP_OGRE: + case SP_FORMICID: rm = you.experience_level * 4; break; case SP_NAGA: @@ -7233,7 +7272,7 @@ bool player::has_usable_offhand() const return false; const item_def* wp = slot_item(EQ_WEAPON); - return (!wp || hands_reqd(*wp, body_size()) != HANDS_TWO); + return (!wp || hands_reqd(*wp) != HANDS_TWO); } bool player::has_usable_tentacle() const @@ -7256,7 +7295,7 @@ int player::usable_tentacles() const const item_def* wp = slot_item(EQ_WEAPON); if (wp) { - hands_reqd_type hands_req = hands_reqd(*wp, body_size()); + hands_reqd_type hands_req = hands_reqd(*wp); free_tentacles -= 2 * hands_req + 2; } @@ -7293,6 +7332,34 @@ int player::has_usable_tentacles(bool allow_tran) const return has_tentacles(allow_tran); } +int player::has_antennae(bool allow_tran) const +{ + if (allow_tran) + { + // Most forms suppress antennae. + if (!form_keeps_mutations()) + return 0; + } + + if (you.species == SP_FORMICID) + { + if (player_wearing_slot(EQ_HELMET)) + return 0; + + return (you.duration[DUR_ANTENNAE_EXTEND] + * player_mutation_level(MUT_ANTENNAE, allow_tran)) + / ANTENNAE_EXTEND_TIME; + } + + return player_mutation_level(MUT_ANTENNAE, allow_tran); +} + +bool player::has_usable_antennae(bool allow_tran) const +{ + // Assumes that armour cannot be placed over normal antennae. + return has_antennae(allow_tran); +} + bool player::sicken(int amount, bool allow_hint, bool quiet) { ASSERT(!crawl_state.game_is_arena()); @@ -7344,7 +7411,7 @@ bool player::can_see_invisible(bool calc_unid, bool items) const } // antennae give sInvis at 3 - if (player_mutation_level(MUT_ANTENNAE) == 3) + if (has_antennae(true) == 3) return true; if (player_mutation_level(MUT_EYEBALLS) == 3) @@ -7584,7 +7651,9 @@ bool player::cannot_act() const bool player::can_throw_large_rocks() const { - return (species == SP_OGRE || species == SP_TROLL); + return (species == SP_OGRE + || species == SP_TROLL + || species == SP_FORMICID); } bool player::can_smell() const @@ -7680,6 +7749,8 @@ vector<PlaceInfo> player::get_all_place_info(bool visited_only, return list; } +// Used for falling into traps and other bad effects, but is a slightly +// different effect from the player invokable ability. bool player::do_shaft() { dungeon_feature_type force_stair = DNGN_UNSEEN; @@ -7719,6 +7790,42 @@ bool player::do_shaft() return true; } +bool player::can_do_shaft_ability() const +{ + switch (grd(pos())) + { + case DNGN_FLOOR: + case DNGN_OPEN_DOOR: + return is_valid_shaft_level(); + + default: + return false; + } +} + +// Like do_shaft, but forced by the player. +// It has a slightly different set of rules. +bool player::do_shaft_ability() +{ + if (can_do_shaft_ability()) + { + mpr("A shaft appears beneath you!"); + down_stairs(DNGN_TRAP_NATURAL); + mpr("The earth vibrates loudly upon landing!"); + fake_noisy(30, pos()); + // Make it more likely for OOD to spawn + env.turns_on_level *= 4 + experience_level / 9; + env.turns_on_level /= 3; + return true; + } + else + { + canned_msg(MSG_NOTHING_HAPPENS); + redraw_screen(); + return false; + } +} + bool player::did_escape_death() const { return (escaped_death_cause != NUM_KILLBY); diff --git a/crawl-ref/source/player.h b/crawl-ref/source/player.h index dddf445fff..755a53e4a6 100644 --- a/crawl-ref/source/player.h +++ b/crawl-ref/source/player.h @@ -528,6 +528,8 @@ public: int has_usable_pseudopods(bool allow_tran = true) const; int has_tentacles(bool allow_tran = true) const; int has_usable_tentacles(bool allow_tran = true) const; + int has_antennae(bool allow_tran = true) const; + bool has_usable_antennae(bool allow_tran = true) const; int wearing(equipment_type slot, int sub_type, bool calc_unid = true) const; int wearing_ego(equipment_type slot, int type, bool calc_unid = true) const; @@ -536,6 +538,8 @@ public: item_def *weapon(int which_attack = -1) const; item_def *shield(); + + hands_reqd_type hands_reqd(const item_def &item) const; bool can_wield(const item_def &item, bool ignore_curse = false, @@ -641,6 +645,7 @@ public: bool gourmand(bool calc_unid = true, bool items = true) const; bool res_corr(bool calc_unid = true, bool items = true) const; bool clarity(bool calc_unid = true, bool items = true) const; + bool stasis(bool calc_unid = true, bool items = true) const; flight_type flight_mode() const; bool cancellable_flight() const; @@ -707,6 +712,9 @@ public: int skill(skill_type skill, int scale =1, bool real = false) const; bool do_shaft(); + + bool can_do_shaft_ability() const; + bool do_shaft_ability(); void apply_location_effects(const coord_def &oldpos, killer_type killer = KILL_NONE, diff --git a/crawl-ref/source/rltiles/dc-player.txt b/crawl-ref/source/rltiles/dc-player.txt index 7c1f965fcb..bcd9875538 100644 --- a/crawl-ref/source/rltiles/dc-player.txt +++ b/crawl-ref/source/rltiles/dc-player.txt @@ -69,6 +69,10 @@ octopode3 octopode4 octopode5 djinni DJINNI +formicid0 FORMICID +formicid1 +formicid2 +formicid3 # draconians draconian_f DRACONIAN DRACONIAN_FIRST diff --git a/crawl-ref/source/rltiles/player/base/formicid0.png b/crawl-ref/source/rltiles/player/base/formicid0.png Binary files differnew file mode 100644 index 0000000000..117ded66fd --- /dev/null +++ b/crawl-ref/source/rltiles/player/base/formicid0.png diff --git a/crawl-ref/source/rltiles/player/base/formicid1.png b/crawl-ref/source/rltiles/player/base/formicid1.png Binary files differnew file mode 100644 index 0000000000..da92da25a7 --- /dev/null +++ b/crawl-ref/source/rltiles/player/base/formicid1.png diff --git a/crawl-ref/source/rltiles/player/base/formicid2.png b/crawl-ref/source/rltiles/player/base/formicid2.png Binary files differnew file mode 100644 index 0000000000..471ffeecdb --- /dev/null +++ b/crawl-ref/source/rltiles/player/base/formicid2.png diff --git a/crawl-ref/source/rltiles/player/base/formicid3.png b/crawl-ref/source/rltiles/player/base/formicid3.png Binary files differnew file mode 100644 index 0000000000..c225a40d9f --- /dev/null +++ b/crawl-ref/source/rltiles/player/base/formicid3.png diff --git a/crawl-ref/source/species.cc b/crawl-ref/source/species.cc index df8f1e4a8a..c7012eae00 100644 --- a/crawl-ref/source/species.cc +++ b/crawl-ref/source/species.cc @@ -19,9 +19,9 @@ static species_type species_order[] = { // comparatively human-like looks SP_HUMAN, SP_HIGH_ELF, - SP_DEEP_ELF, - SP_DEEP_DWARF, SP_HILL_ORC, - SP_LAVA_ORC, SP_MERFOLK, + SP_DEEP_ELF, SP_DEEP_DWARF, + SP_HILL_ORC, SP_LAVA_ORC, + SP_MERFOLK, SP_FORMICID, // small species SP_HALFLING, SP_KOBOLD, SP_SPRIGGAN, @@ -64,7 +64,7 @@ static const char * Species_Abbrev_List[NUM_SPECIES] = // the draconians "Dr", "Dr", "Dr", "Dr", "Dr", "Dr", "Dr", "Dr", "Dr", "Dr", "Ce", "Dg", "Sp", "Mi", "Ds", "Gh", "Te", "Mf", "Vp", "DD", - "Fe", "Op", "Dj", "LO", "Gr", + "Fe", "Op", "Dj", "LO", "Gr", "Fo", // placeholders "El", "HD", "OM", "GE", "Gn", "MD", #if TAG_MAJOR_VERSION > 34 @@ -199,6 +199,7 @@ string species_name(species_type speci, bool genus, bool adj) case SP_MINOTAUR: res = "Minotaur"; break; case SP_TENGU: res = "Tengu"; break; case SP_GARGOYLE: res = "Gargoyle"; break; + case SP_FORMICID: res = "Formicid"; break; case SP_HILL_ORC: res = (adj ? "Orcish" : genus ? "Orc" : "Hill Orc"); @@ -385,6 +386,8 @@ monster_type player_species_to_mons_species(species_type species) return MONS_OCTOPODE; case SP_DJINNI: return MONS_DJINNI; + case SP_FORMICID: + return MONS_FORMICID; case SP_ELF: case SP_HILL_DWARF: case SP_MOUNTAIN_DWARF: @@ -413,6 +416,7 @@ int species_exp_modifier(species_type species) case SP_HUMAN: case SP_HALFLING: case SP_KOBOLD: + case SP_FORMICID: return 1; case SP_HILL_ORC: case SP_OGRE: @@ -466,6 +470,7 @@ int species_hp_modifier(species_type species) case SP_DEEP_ELF: case SP_TENGU: case SP_KOBOLD: + case SP_FORMICID: return -2; case SP_HIGH_ELF: case SP_SLUDGE_ELF: @@ -511,6 +516,7 @@ int species_mp_modifier(species_type species) return -2; case SP_CENTAUR: case SP_GHOUL: + case SP_FORMICID: return -1; default: return 0; diff --git a/crawl-ref/source/spl-book.cc b/crawl-ref/source/spl-book.cc index cde5c969da..a550da6389 100644 --- a/crawl-ref/source/spl-book.cc +++ b/crawl-ref/source/spl-book.cc @@ -615,6 +615,15 @@ bool you_cannot_memorise(spell_type spell, bool &undead) if (you.species == SP_LAVA_ORC && spell == SPELL_STONESKIN) rc = true, undead = false; + if (you.species == SP_FORMICID + && (spell == SPELL_BLINK + || spell == SPELL_CONTROL_TELEPORT + || spell == SPELL_CONTROLLED_BLINK + || spell == SPELL_SWIFTNESS)) + { + rc = true, undead = false; + } + return rc; } diff --git a/crawl-ref/source/spl-cast.cc b/crawl-ref/source/spl-cast.cc index 91f1217ae4..70f0a22256 100644 --- a/crawl-ref/source/spl-cast.cc +++ b/crawl-ref/source/spl-cast.cc @@ -1026,7 +1026,10 @@ static bool _spellcasting_aborted(spell_type spell, if (is_prevented_teleport(spell)) { - mpr("You cannot teleport right now."); + if (you.species == SP_FORMICID) + mpr("You cannot teleport."); + else + mpr("You cannot teleport right now."); return true; } diff --git a/crawl-ref/source/spl-data.h b/crawl-ref/source/spl-data.h index 83eb52692e..b31b4d9ce1 100644 --- a/crawl-ref/source/spl-data.h +++ b/crawl-ref/source/spl-data.h @@ -3089,6 +3089,32 @@ struct spell_desc }, { + SPELL_SHAFT_SELF, "Shaft Self", + SPTYP_EARTH, + SPFLAG_ESCAPE, + 1, + 0, + -1, -1, + 100, + NULL, + false, + true +}, + +{ + SPELL_MASS_CURE_POISON, "Mass Cure Poison", + SPTYP_POISON, + SPFLAG_AREA, + 1, + 0, + -1, -1, + 0, + NULL, + false, + true +}, + +{ SPELL_NO_SPELL, "nonexistent spell", 0, SPFLAG_TESTING, diff --git a/crawl-ref/source/stairs.cc b/crawl-ref/source/stairs.cc index 3195801fb2..205e9d7a2c 100644 --- a/crawl-ref/source/stairs.cc +++ b/crawl-ref/source/stairs.cc @@ -668,8 +668,8 @@ void down_stairs(dungeon_feature_type force_stair) } // Only check the current position for a legal stair traverse. - // If it's a known shaft that we're taking, then we're already good. - if (!known_shaft && !_check_stairs(stair_find, true)) + // If it's a shaft that we're taking, then we're already good. + if (!shaft && !_check_stairs(stair_find, true)) return; if (_stair_moves_pre(stair_find)) diff --git a/crawl-ref/source/throw.cc b/crawl-ref/source/throw.cc index ff89c3a732..8f80b45fbd 100644 --- a/crawl-ref/source/throw.cc +++ b/crawl-ref/source/throw.cc @@ -487,7 +487,7 @@ static int _launcher_shield_slowdown(const item_def &launcher, const item_def *shield) { int speed_adjust = 100; - if (!shield || hands_reqd(launcher, you.body_size()) == HANDS_ONE) + if (!shield || you.hands_reqd(launcher) == HANDS_ONE) return speed_adjust; const int shield_type = shield->sub_type; @@ -1855,7 +1855,7 @@ bool throw_it(bolt &pbolt, int throw_2, bool teleport, int acc_bonus, // Dwarves/orcs with dwarven/orcish weapons. if (get_equip_race(item) == ISFLAG_DWARVEN - && you.species == SP_DEEP_DWARF + && (you.species == SP_DEEP_DWARF) || get_equip_race(item) == ISFLAG_ORCISH && you.species == SP_HILL_ORC) { diff --git a/crawl-ref/source/tilepick-p.cc b/crawl-ref/source/tilepick-p.cc index 80bab0a820..9cf3e7bad3 100644 --- a/crawl-ref/source/tilepick-p.cc +++ b/crawl-ref/source/tilepick-p.cc @@ -676,6 +676,8 @@ tileidx_t tilep_species_to_base_tile(int sp, int level) return TILEP_BASE_OCTOPODE; case SP_DJINNI: return TILEP_BASE_DJINNI; + case SP_FORMICID: + return TILEP_BASE_FORMICID; default: return TILEP_BASE_HUMAN; } @@ -804,6 +806,22 @@ void tilep_race_default(int sp, int level, dolls_data *doll) hair = 0; beard = TILEP_BEARD_MEDIUM_GREEN; break; + case SP_FORMICID: + hair = 0; + // Three levels of antennae retraction + // and one with no antennae. + if (player_wearing_slot(EQ_HELMET)) + { + result = TILEP_BASE_FORMICID; + } + else + { + result = TILEP_BASE_FORMICID + + ((you.duration[DUR_ANTENNAE_EXTEND] + * player_mutation_level(MUT_ANTENNAE)) + / ANTENNAE_EXTEND_TIME); + } + break; default: // nothing to do break; diff --git a/crawl-ref/source/transform.cc b/crawl-ref/source/transform.cc index aeb15dcfd4..0958997344 100644 --- a/crawl-ref/source/transform.cc +++ b/crawl-ref/source/transform.cc @@ -300,7 +300,7 @@ static bool _mutations_prevent_wearing(const item_def& item) if (is_hard_helmet(item) && (player_mutation_level(MUT_HORNS) - || player_mutation_level(MUT_ANTENNAE) + || (player_mutation_level(MUT_ANTENNAE) && you.species != SP_FORMICID) || player_mutation_level(MUT_BEAK))) { return true; @@ -318,7 +318,8 @@ static bool _mutations_prevent_wearing(const item_def& item) return true; if (eqslot == EQ_HELMET && (player_mutation_level(MUT_HORNS) == 3 - || player_mutation_level(MUT_ANTENNAE) == 3)) + || (player_mutation_level(MUT_ANTENNAE) == 3) + && you.species != SP_FORMICID)) { return true; } diff --git a/crawl-ref/source/wiz-you.cc b/crawl-ref/source/wiz-you.cc index 096893eede..f70db3cc27 100644 --- a/crawl-ref/source/wiz-you.cc +++ b/crawl-ref/source/wiz-you.cc @@ -814,6 +814,7 @@ static const char* dur_names[] = "weak", "dimension anchor", "antimagic", + "antennae extend", }; void wizard_edit_durations(void) |