summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorpubby <pubby8@gmail.com>2013-06-12 02:34:30 -0500
committerNeil Moore <neil@s-z.org>2013-08-25 00:12:25 -0400
commit2211e5ff2f25adfb931eb0ff32b274afa7eb7ed5 (patch)
tree4568295ed3a6c1bd2c0df4bc8db167db1809c043
parent9c1491e134d7c43d0ad2c4ebd444a3a2199b457b (diff)
downloadcrawl-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 ]
-rw-r--r--crawl-ref/docs/crawl_manual.reST21
-rw-r--r--crawl-ref/source/abl-show.cc61
-rw-r--r--crawl-ref/source/acquire.cc9
-rw-r--r--crawl-ref/source/actor.cc6
-rw-r--r--crawl-ref/source/actor.h2
-rw-r--r--crawl-ref/source/aptitudes.h34
-rw-r--r--crawl-ref/source/artefact.cc10
-rw-r--r--crawl-ref/source/dat/descript/ability.txt9
-rw-r--r--crawl-ref/source/dat/descript/monsters.txt12
-rw-r--r--crawl-ref/source/dat/descript/species.txt5
-rw-r--r--crawl-ref/source/defines.h2
-rw-r--r--crawl-ref/source/delay.cc27
-rw-r--r--crawl-ref/source/describe.cc13
-rw-r--r--crawl-ref/source/enum.h18
-rw-r--r--crawl-ref/source/godpassive.cc2
-rw-r--r--crawl-ref/source/item_use.cc62
-rw-r--r--crawl-ref/source/itemname.cc33
-rw-r--r--crawl-ref/source/itemprop.cc32
-rw-r--r--crawl-ref/source/itemprop.h7
-rw-r--r--crawl-ref/source/main.cc27
-rw-r--r--crawl-ref/source/mon-cast.cc83
-rw-r--r--crawl-ref/source/mon-data.h40
-rw-r--r--crawl-ref/source/mon-flags.h6
-rw-r--r--crawl-ref/source/mon-gear.cc44
-rw-r--r--crawl-ref/source/mon-grow.cc2
-rw-r--r--crawl-ref/source/mon-spll.h22
-rw-r--r--crawl-ref/source/monster.cc40
-rw-r--r--crawl-ref/source/monster.h4
-rw-r--r--crawl-ref/source/mutation-data.h18
-rw-r--r--crawl-ref/source/mutation.cc78
-rw-r--r--crawl-ref/source/newgame.cc3
-rw-r--r--crawl-ref/source/ng-restr.cc21
-rw-r--r--crawl-ref/source/ng-setup.cc11
-rw-r--r--crawl-ref/source/output.cc11
-rw-r--r--crawl-ref/source/player-act.cc23
-rw-r--r--crawl-ref/source/player-equip.cc10
-rw-r--r--crawl-ref/source/player.cc123
-rw-r--r--crawl-ref/source/player.h8
-rw-r--r--crawl-ref/source/rltiles/dc-player.txt4
-rw-r--r--crawl-ref/source/rltiles/player/base/formicid0.pngbin0 -> 397 bytes
-rw-r--r--crawl-ref/source/rltiles/player/base/formicid1.pngbin0 -> 385 bytes
-rw-r--r--crawl-ref/source/rltiles/player/base/formicid2.pngbin0 -> 385 bytes
-rw-r--r--crawl-ref/source/rltiles/player/base/formicid3.pngbin0 -> 380 bytes
-rw-r--r--crawl-ref/source/species.cc14
-rw-r--r--crawl-ref/source/spl-book.cc9
-rw-r--r--crawl-ref/source/spl-cast.cc5
-rw-r--r--crawl-ref/source/spl-data.h26
-rw-r--r--crawl-ref/source/stairs.cc4
-rw-r--r--crawl-ref/source/throw.cc4
-rw-r--r--crawl-ref/source/tilepick-p.cc18
-rw-r--r--crawl-ref/source/transform.cc5
-rw-r--r--crawl-ref/source/wiz-you.cc1
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
new file mode 100644
index 0000000000..117ded66fd
--- /dev/null
+++ b/crawl-ref/source/rltiles/player/base/formicid0.png
Binary files differ
diff --git a/crawl-ref/source/rltiles/player/base/formicid1.png b/crawl-ref/source/rltiles/player/base/formicid1.png
new file mode 100644
index 0000000000..da92da25a7
--- /dev/null
+++ b/crawl-ref/source/rltiles/player/base/formicid1.png
Binary files differ
diff --git a/crawl-ref/source/rltiles/player/base/formicid2.png b/crawl-ref/source/rltiles/player/base/formicid2.png
new file mode 100644
index 0000000000..471ffeecdb
--- /dev/null
+++ b/crawl-ref/source/rltiles/player/base/formicid2.png
Binary files differ
diff --git a/crawl-ref/source/rltiles/player/base/formicid3.png b/crawl-ref/source/rltiles/player/base/formicid3.png
new file mode 100644
index 0000000000..c225a40d9f
--- /dev/null
+++ b/crawl-ref/source/rltiles/player/base/formicid3.png
Binary files differ
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)