diff -urN source.orig/abl-show.cc source/abl-show.cc
--- source.orig/abl-show.cc	2008-08-01 02:00:50.000000000 +0300
+++ source/abl-show.cc	2008-09-28 23:21:39.000000000 +0300
@@ -182,6 +182,26 @@
     { ABIL_TELEPORTATION, "Teleportation", 3, 0, 200, 0, ABFLAG_NONE },
     { ABIL_BLINK, "Blink", 1, 0, 50, 0, ABFLAG_NONE },
 
+    { ABIL_SHAPESHIFT, "Shapeshift", 1, 0, 50, 0, ABFLAG_NONE },
+    { ABIL_STEAL_FORM, "Steal Form", 5, 0, 100, 0, ABFLAG_NONE },
+    { ABIL_CONTROLLED_SHAPESHIFT, "Controlled Shapeshift", 10, 0, 250, 0, ABFLAG_NONE },
+
+    { ABIL_EAT_ITEMS, "Eat Items", 0, 0, 0, 0, ABFLAG_NONE },
+    { ABIL_CONFUSE, "Confuse", 1, 0, 50, 0, ABFLAG_NONE },
+    { ABIL_PARALYSE, "Paralyse", 1, 0, 50, 0, ABFLAG_NONE },
+    { ABIL_SLOW, "Slow", 1, 0, 50, 0, ABFLAG_NONE },
+    { ABIL_DISINTEGRATE, "Disintegrate", 1, 0, 50, 0, ABFLAG_NONE },
+    { ABIL_POLYMORPH_OTHER, "Polymorph Other", 1, 0, 50, 0, ABFLAG_NONE },
+    { ABIL_SHADOW_CREATURES, "Summon Shadow Creatures", 1, 0, 50, 0, ABFLAG_NONE },
+    { ABIL_LIGHTNING_BOLT, "Lightning Bolt", 1, 0, 50, 0, ABFLAG_NONE },
+    { ABIL_LESSER_HEALING, "Lesser Healing", 1, 0, 50, 0, ABFLAG_NONE },
+    { ABIL_MEPHITIC_CLOUD, "Breathe Foul Vapour", 1, 0, 50, 0, ABFLAG_BREATH },
+    { ABIL_QUICKSILVER_BOLT, "Breathe Quicksilver Bolt", 1, 0, 50, 0, ABFLAG_NONE },
+    { ABIL_ENERGY_BOLT, "Energy Bolt", 1, 0, 50, 0, ABFLAG_NONE },
+    { ABIL_METAL_SPLINTERS, "Breathe Metal Splinters", 1, 0, 50, 0, ABFLAG_BREATH },
+    { ABIL_MIASMA, "Breathe Miasma", 1, 0, 50, 0, ABFLAG_BREATH },
+//    { ABIL_, "", 1, 0, 50, 0, ABFLAG_NONE },
+
     { ABIL_BREATHE_FIRE, "Breathe Fire", 0, 0, 125, 0, ABFLAG_BREATH },
     { ABIL_BREATHE_FROST, "Breathe Frost", 0, 0, 125, 0, ABFLAG_BREATH },
     { ABIL_BREATHE_POISON, "Breathe Poison Gas", 0, 0, 125, 0, ABFLAG_BREATH },
@@ -579,6 +599,32 @@
                   - you.experience_level;
         break;
 
+    // Shapeshifter 
+    case ABIL_SHAPESHIFT:
+    case ABIL_STEAL_FORM:
+    case ABIL_CONTROLLED_SHAPESHIFT:
+        failure = 30 - (you.experience_level);
+        break;
+    case ABIL_EAT_ITEMS:
+        perfect = true;
+        failure = 0;
+        break;
+    case ABIL_CONFUSE:
+    case ABIL_PARALYSE:
+    case ABIL_SLOW:
+    case ABIL_DISINTEGRATE:
+    case ABIL_POLYMORPH_OTHER:
+    case ABIL_SHADOW_CREATURES:
+    case ABIL_LIGHTNING_BOLT:
+    case ABIL_LESSER_HEALING:
+    case ABIL_MEPHITIC_CLOUD:
+    case ABIL_QUICKSILVER_BOLT:
+    case ABIL_ENERGY_BOLT:
+    case ABIL_METAL_SPLINTERS:
+    case ABIL_MIASMA:
+        failure = 30 - (you.experience_level);
+        break;
+
     case ABIL_TELEPORTATION:
         failure = ((player_mutation_level(MUT_TELEPORT_AT_WILL) > 1) ? 30 : 50)
                     - you.experience_level;
@@ -984,6 +1030,7 @@
         case ABIL_DELAYED_FIREBALL:
         case ABIL_MUMMY_RESTORATION:
         case ABIL_TRAN_BAT:
+        case ABIL_EAT_ITEMS:
             hungerCheck = false;
             break;
         default:
@@ -1280,6 +1327,80 @@
             exercise( SK_EVOCATIONS, 1 );
         break;
 
+    case ABIL_SHAPESHIFT:
+        shapeshift(RANDOM_MONSTER, SHIFT_ABILITY, false);
+        break;
+    case ABIL_CONTROLLED_SHAPESHIFT:
+        if (!controlled_shapeshift(SHIFT_ABILITY))
+            return false;
+        break;
+    case ABIL_STEAL_FORM:
+        if (!steal_form())
+            return (false);
+        break;
+    case ABIL_EAT_ITEMS:
+        if (!eat_items())
+            return (false);
+        break;
+    case ABIL_CONFUSE:
+        if (your_spells(SPELL_CONFUSE, you.experience_level, false) == SPRET_ABORT)
+            return (false);
+        break;
+    case ABIL_PARALYSE:
+        if (your_spells(SPELL_PARALYSE, you.experience_level, false) == SPRET_ABORT)
+            return (false);
+        break;
+    case ABIL_SLOW:
+        if (your_spells(SPELL_SLOW, you.experience_level, false) == SPRET_ABORT)
+            return (false);
+        break;
+    case ABIL_DISINTEGRATE:
+        if (your_spells(SPELL_DISINTEGRATE, you.experience_level, false) == SPRET_ABORT)
+            return (false);
+        break;
+    case ABIL_POLYMORPH_OTHER:
+        if (your_spells(SPELL_POLYMORPH_OTHER, you.experience_level, false) == SPRET_ABORT)
+            return (false);
+        break;
+    case ABIL_SHADOW_CREATURES:
+        cast_shadow_creatures(GOD_NO_GOD);
+        break;
+    case ABIL_LIGHTNING_BOLT:
+        if (you.duration[DUR_BREATH_WEAPON])
+        {
+            canned_msg(MSG_CANNOT_DO_YET);
+            return (false);
+        }
+        if (your_spells(SPELL_LIGHTNING_BOLT, you.experience_level, false) == SPRET_ABORT)
+            return (false);
+        you.duration[DUR_BREATH_WEAPON] =
+                3 + random2(4) + random2(30 - you.experience_level) / 2;
+        break;
+    case ABIL_LESSER_HEALING:
+        if (your_spells(SPELL_LESSER_HEALING, you.experience_level, false) == SPRET_ABORT)
+            return (false);
+        break;
+    case ABIL_MEPHITIC_CLOUD:
+        if (you.duration[DUR_BREATH_WEAPON])
+        {
+            canned_msg(MSG_CANNOT_DO_YET);
+            return (false);
+        }
+        if (your_spells(SPELL_MEPHITIC_CLOUD, you.experience_level, false) == SPRET_ABORT)
+            return (false);
+        you.duration[DUR_BREATH_WEAPON] =
+                3 + random2(4) + random2(30 - you.experience_level) / 2;
+        break;
+    case ABIL_QUICKSILVER_BOLT:
+        break;
+    case ABIL_ENERGY_BOLT:
+        break;
+    case ABIL_METAL_SPLINTERS:
+        break;
+    case ABIL_MIASMA:
+        break;
+
+
     case ABIL_EVOKE_BERSERK:    // amulet of rage, randarts
         // FIXME This is not the best way to do stuff, because
         // you have a chance of failing the evocation test, in
@@ -2104,6 +2225,15 @@
         }
     }
 
+    if (you.species == SP_SHAPESHIFTER)
+    {
+        _add_talent(talents, ABIL_SHAPESHIFT, check_confused );
+        if (you.experience_level >= 7)
+            _add_talent(talents, ABIL_STEAL_FORM, check_confused );
+        if (you.experience_level >= 20)
+            _add_talent(talents, ABIL_CONTROLLED_SHAPESHIFT, check_confused );
+    }
+
     // Mutations.
     if (player_mutation_level(MUT_MAPPING))
         _add_talent(talents, ABIL_MAPPING, check_confused );
@@ -2239,6 +2369,111 @@
         _add_talent(talents, ABIL_EVOKE_TELEPORTATION, check_confused );
     }
 
+    // Shapeshifter abilities
+    if (player_is_shapeshifted())
+    {
+        if (mons_genus(you.form) == MONS_NAGA)
+            _add_talent(talents, ABIL_SPIT_POISON, check_confused );
+
+        if (mons_itemuse(you.form) == MONUSE_EATS_ITEMS)
+            _add_talent(talents, ABIL_EAT_ITEMS, check_confused );
+
+
+        switch (you.form)
+        {
+        case MONS_GOLDEN_DRAGON:
+            _add_talent(talents, ABIL_BREATHE_FIRE, check_confused );
+            _add_talent(talents, ABIL_BREATHE_FROST, check_confused );
+        case MONS_GREEN_DRACONIAN:
+        case MONS_SWAMP_DRAGON:
+            _add_talent(talents, ABIL_BREATHE_POISON, check_confused );
+            break;
+        case MONS_SWAMP_DRAKE:
+            _add_talent(talents, ABIL_MEPHITIC_CLOUD, check_confused );
+            break;
+        case MONS_RED_DRACONIAN:
+        case MONS_DRAGON:
+        case MONS_LINDWURM:
+        case MONS_FIREDRAKE:
+            _add_talent(talents, ABIL_BREATHE_FIRE, check_confused );
+            break;
+        case MONS_DEATH_DRAKE:
+            _add_talent(talents, ABIL_MIASMA, check_confused );
+            break;
+        case MONS_WHITE_DRACONIAN:
+        case MONS_ICE_DRAGON:
+            _add_talent(talents, ABIL_BREATHE_FROST, check_confused );
+            break;
+        case MONS_BLACK_DRACONIAN:
+        case MONS_ELECTRICAL_EEL:
+            _add_talent(talents, ABIL_BREATHE_LIGHTNING, check_confused );
+            break;
+        case MONS_STORM_DRAGON:
+            _add_talent(talents, ABIL_LIGHTNING_BOLT, check_confused );
+            break;
+        case MONS_PALE_DRACONIAN:
+        case MONS_STEAM_DRAGON:
+            _add_talent(talents, ABIL_BREATHE_STEAM, check_confused );
+            break;
+        case MONS_MOTTLED_DRACONIAN:
+        case MONS_MOTTLED_DRAGON:
+            _add_talent(talents, ABIL_BREATHE_STICKY_FLAME, check_confused );
+            break;
+        case MONS_ACID_BLOB:
+        case MONS_YELLOW_DRACONIAN:
+            _add_talent(talents, ABIL_SPIT_ACID, check_confused );
+            break;
+        case MONS_QUICKSILVER_DRAGON:
+            _add_talent(talents, ABIL_QUICKSILVER_BOLT, check_confused );
+            break;
+        case MONS_SHADOW_DRAGON:
+            _add_talent(talents, ABIL_BOLT_OF_DRAINING, check_confused );
+            break;
+        case MONS_IRON_DRAGON:
+            _add_talent(talents, ABIL_METAL_SPLINTERS, check_confused );
+            break;
+        case MONS_FIRE_GIANT:
+            //_add_talent(talents, ABIL__, check_confused );
+            break;
+        case MONS_FROST_GIANT:
+            //_add_talent(talents, ABIL__, check_confused );
+            break;
+        case MONS_TITAN:
+            _add_talent(talents, ABIL_LIGHTNING_BOLT, check_confused );
+            break;
+        case MONS_GREAT_ORB_OF_EYES:
+            _add_talent(talents, ABIL_PARALYSE, check_confused );
+            _add_talent(talents, ABIL_DISINTEGRATE, check_confused );
+            _add_talent(talents, ABIL_SLOW, check_confused );
+            _add_talent(talents, ABIL_CONFUSE, check_confused );
+            //_add_talent(talents, ABIL_, check_confused ); telep other
+            break;
+        case MONS_SHINING_EYE:
+            _add_talent(talents, ABIL_POLYMORPH_OTHER, check_confused );
+            break;
+        case MONS_EYE_OF_DEVASTATION:
+            _add_talent(talents, ABIL_ENERGY_BOLT, check_confused );
+            break;
+        case MONS_GIANT_ORANGE_BRAIN:
+            _add_talent(talents, ABIL_POLYMORPH_OTHER, check_confused );
+            _add_talent(talents, ABIL_SHADOW_CREATURES, check_confused );
+            _add_talent(talents, ABIL_CONFUSE, check_confused );
+            _add_talent(talents, ABIL_TELEPORTATION, check_confused );
+            _add_talent(talents, ABIL_BLINK, check_confused );
+            break;
+        case MONS_GIANT_EYEBALL:
+            _add_talent(talents, ABIL_PARALYSE, check_confused );
+            break;
+
+        case MONS_BLINK_FROG:
+            _add_talent(talents, ABIL_BLINK, check_confused );
+            break;
+
+        default:
+            break;
+        }
+    }
+
     // Find hotkeys for the non-hotkeyed talents.
     for (unsigned int i = 0; i < talents.size(); ++i)
     {
diff -urN source.orig/acr.cc source/acr.cc
--- source.orig/acr.cc	2008-08-01 02:01:03.000000000 +0300
+++ source/acr.cc	2008-09-30 17:55:38.000000000 +0300
@@ -265,6 +265,10 @@
 
     _god_greeting_message( game_start );
 
+
+    if (game_start && you.species == SP_SHAPESHIFTER)
+        shapeshift(RANDOM_MONSTER, SHIFT_TIMEOUT);
+
     // Warn player about their weapon, if unsuitable.
     wield_warning(false);
 
@@ -518,6 +522,12 @@
 
     switch (wiz_command)
     {
+    case CONTROL('S'):
+        for (int i=0 ; i<NUM_MONSTERS; i++)
+            you.ok_forms[i] = true;
+        mpr("All shapeshifter forms possible.");
+        break;
+
     case '?':
         list_commands(true, 0, true);  // tell it to list wizard commands
         break;
@@ -2091,7 +2101,7 @@
 
     case CMD_REMOVE_ARMOUR:
     {
-        if (you.attribute[ATTR_TRANSFORMATION] == TRAN_BAT)
+        if (you.attribute[ATTR_TRANSFORMATION] == TRAN_BAT || !shapeshifter_can_wear(you.form, EQ_WEAPON))
         {
             mpr("You can't wear or remove anything in your present form.");
             break;
@@ -2116,7 +2126,7 @@
         break;
 
     case CMD_MEMORISE_SPELL:
-        if (you.attribute[ATTR_TRANSFORMATION] == TRAN_BAT)
+        if (you.attribute[ATTR_TRANSFORMATION] == TRAN_BAT || !shapeshifter_can_wear(you.form, EQ_WEAPON))
         {
            canned_msg(MSG_PRESENT_FORM);
            break;
@@ -2126,7 +2136,7 @@
         break;
 
     case CMD_ZAP_WAND:
-        if (you.attribute[ATTR_TRANSFORMATION] == TRAN_BAT)
+        if (you.attribute[ATTR_TRANSFORMATION] == TRAN_BAT || !shapeshifter_can_wear(you.form, EQ_WEAPON))
         {
            canned_msg(MSG_PRESENT_FORM);
            break;
@@ -2172,7 +2182,7 @@
         break;
 
     case CMD_READ:
-        if (you.attribute[ATTR_TRANSFORMATION] == TRAN_BAT)
+        if (you.attribute[ATTR_TRANSFORMATION] == TRAN_BAT || !shapeshifter_can_wear(you.form, EQ_WEAPON))
         {
            canned_msg(MSG_PRESENT_FORM);
            break;
@@ -2194,7 +2204,7 @@
     }
 
     case CMD_CAST_SPELL:
-        if (you.attribute[ATTR_TRANSFORMATION] == TRAN_BAT)
+        if (you.attribute[ATTR_TRANSFORMATION] == TRAN_BAT || !shapeshifter_can_wear(you.form, EQ_WEAPON))
         {
            canned_msg(MSG_PRESENT_FORM);
            break;
@@ -2704,6 +2714,32 @@
         }
     }
 
+    // Shapeshifters
+    if (you.duration[DUR_SHAPESHIFT] > 2)
+    {
+        if (mons_genus(you.form) == MONS_PLANT)
+            you.duration[DUR_SHAPESHIFT]--;
+        else if (one_chance_in(player_sust_abil(true) ?  7 : 5 ))
+        {
+            if (you.duration[DUR_SHAPESHIFT] == 8)
+                mpr("You feel uncomfortable in your current form.", MSGCH_DURATION);
+            if (you.duration[DUR_SHAPESHIFT] == 4)
+                mpr("You feel very uncomfortable in your current form!", MSGCH_DURATION);
+
+            you.duration[DUR_SHAPESHIFT]--;
+        }
+    }
+    else if (you.duration[DUR_SHAPESHIFT] == 2)
+    {
+        mpr("You can't keep your form any longer!", MSGCH_DURATION);
+        you.duration[DUR_SHAPESHIFT]--;
+        shapeshift(RANDOM_MONSTER, SHIFT_TIMEOUT);
+    }
+    else if (you.duration[DUR_SHAPESHIFT] == 1)
+    {
+        shapeshift(RANDOM_MONSTER, SHIFT_TIMEOUT);
+    }
+
     // Must come after transformation duration.
     _decrement_a_duration(DUR_BREATH_WEAPON, "You have got your breath back.",
                           -1, 0, NULL, MSGCH_RECOVERY);
@@ -3865,6 +3901,7 @@
 
     you.unique_creatures.init(false);
     you.unique_items.init(UNIQ_NOT_EXISTS);
+    you.ok_forms.init(false);
 
     // Set up the Lua interpreter for the dungeon builder.
     init_dungeon_lua();
diff -urN source.orig/beam.cc source/beam.cc
--- source.orig/beam.cc	2008-08-01 02:00:55.000000000 +0300
+++ source/beam.cc	2008-09-21 15:05:25.000000000 +0300
@@ -3706,10 +3706,10 @@
         return (player_prot_life(false) >= 3);
 
     case BEAM_POISON:
-        return (player_res_poison(false));
+        return (player_res_poison(false) > 0);
 
     case BEAM_POTION_STINKING_CLOUD:
-        return (player_res_poison(false) || player_mental_clarity(false));
+        return (player_res_poison(false) > 0 || player_mental_clarity(false));
 
     case BEAM_ELECTRICITY:
         return (player_res_electricity(false));
@@ -4224,7 +4224,7 @@
         }
         else if (item->special == SPMSL_POISONED)
         {
-            if (!player_res_poison()
+            if (player_res_poison() <= 0
                 && (hurted || beam.ench_power == AUTOMATIC_HIT
                               && x_chance_in_y(90 - 3 * player_AC(), 100)))
             {
diff -urN source.orig/cloud.cc source/cloud.cc
--- source.orig/cloud.cc	2008-08-01 02:01:03.000000000 +0300
+++ source/cloud.cc	2008-09-21 15:01:04.000000000 +0300
@@ -449,7 +449,7 @@
     case CLOUD_STINK:
         // If you don't have to breathe, unaffected
         mpr("You are engulfed in noxious fumes!");
-        if (player_res_poison())
+        if (player_res_poison() > 0)
             break;
 
         hurted += (random2(3) * you.time_taken) / 10;
@@ -497,7 +497,7 @@
     case CLOUD_POISON:
         // If you don't have to breathe, unaffected
         mpr("You are engulfed in poison gas!");
-        if (!player_res_poison())
+        if (player_res_poison() <= 0)
         {
             ouch( (random2(10) * you.time_taken) / 10, cl, KILLED_BY_CLOUD,
                   "poison gas" );
@@ -579,7 +579,7 @@
     // also expect to be the case a few turns later (ignores spells).
     case CLOUD_STINK:
     case CLOUD_POISON:
-        return (!player_res_poison(false, temp));
+        return (player_res_poison(false, temp) <= 0);
     case CLOUD_STEAM:
         return (player_res_steam(false, temp) <= 0);
     case CLOUD_MIASMA:
diff -urN source.orig/command.cc source/command.cc
--- source.orig/command.cc	2008-08-01 02:00:52.000000000 +0300
+++ source/command.cc	2008-09-23 20:53:46.000000000 +0300
@@ -610,6 +610,8 @@
         }
         else if (!you_tran_can_wear(i))
             jstr << "    (currently unavailable)";
+        else if (!shapeshifter_can_wear(you.form, i, false))
+            jstr << "    (currently unavailable)";
         else
             jstr << "    none";
 
diff -urN source.orig/describe.cc source/describe.cc
--- source.orig/describe.cc	2008-08-01 02:00:52.000000000 +0300
+++ source/describe.cc	2008-09-09 17:10:47.000000000 +0300
@@ -2561,6 +2561,7 @@
     case SP_HUMAN:
     case SP_DEMIGOD:
     case SP_DEMONSPAWN:
+    case SP_SHAPESHIFTER:
         str = 10;
         break;
 
diff -urN source.orig/effects.cc source/effects.cc
--- source.orig/effects.cc	2008-08-01 02:00:52.000000000 +0300
+++ source/effects.cc	2008-09-26 14:29:44.000000000 +0300
@@ -2541,13 +2541,14 @@
                 "energies until Zin's power calms it.", MSGCH_GOD);
         }
         else if (you.magic_contamination >= 5
-                 && x_chance_in_y(you.magic_contamination + 1, 25))
+                 && x_chance_in_y(you.magic_contamination + 1, 25)
+                 && you.species != SP_SHAPESHIFTER)
         {
             mpr("Your body shudders with the violent release "
                 "of wild energies!", MSGCH_WARN);
 
             // For particularly violent releases, make a little boom.
-            if (you.magic_contamination >= 10 && coinflip())
+            if (you.magic_contamination >= 10 && coinflip() )
             {
                 struct bolt boom;
                 boom.type     = dchar_glyph(DCHAR_FIRED_BURST);
@@ -2585,6 +2586,20 @@
             // mutating you.  -- GDL
             contaminate_player(-(random2(you.magic_contamination / 4) + 1));
         }
+        else if (you.magic_contamination >= 5 + you.experience_level/5
+                 && x_chance_in_y(you.magic_contamination + 1, 25 + you.experience_level)
+                 && you.species == SP_SHAPESHIFTER)
+        {
+            mpr("Your body shudders with the violent release "
+                "of wild energies!", MSGCH_WARN);
+
+            if (one_chance_in(5))
+                mutate(RANDOM_MUTATION);
+            else
+                give_bad_mutation(true, coinflip());
+
+            contaminate_player(-(random2(you.magic_contamination / 4) + 1));
+        }
     }
 
     // Random chance to identify staff in hand based off of Spellcasting
diff -urN source.orig/enum.h source/enum.h
--- source.orig/enum.h	2008-08-01 02:00:52.000000000 +0300
+++ source/enum.h	2008-09-24 19:25:08.000000000 +0300
@@ -67,6 +67,27 @@
     ABIL_EVOKE_MAPPING,                //   30
     ABIL_EVOKE_TELEPORTATION,
     ABIL_EVOKE_BLINK,                  //   32
+
+    ABIL_SHAPESHIFT,
+    ABIL_STEAL_FORM,
+    ABIL_CONTROLLED_SHAPESHIFT,
+
+    ABIL_EAT_ITEMS,                    //   36
+    ABIL_CONFUSE,
+    ABIL_PARALYSE,
+    ABIL_SLOW,
+    ABIL_DISINTEGRATE,
+    ABIL_POLYMORPH_OTHER,
+    ABIL_SHADOW_CREATURES,
+    ABIL_LIGHTNING_BOLT,
+    ABIL_LESSER_HEALING,
+    ABIL_MEPHITIC_CLOUD,
+    ABIL_QUICKSILVER_BOLT,
+    ABIL_ENERGY_BOLT,
+    ABIL_METAL_SPLINTERS,
+    ABIL_MIASMA,
+//    ABIL_,                             //   50
+
     // 33 - 50 unused
     ABIL_EVOKE_TURN_INVISIBLE = 51,    //   51
     ABIL_EVOKE_TURN_VISIBLE,
@@ -1144,6 +1165,8 @@
     DUR_VITALISATION_CHAIN,
     DUR_PETRIFIED,
 
+    DUR_SHAPESHIFT,
+
     NUM_DURATIONS
 };
 
@@ -2417,6 +2440,7 @@
     SP_KENKU,
     SP_MERFOLK,
     SP_VAMPIRE,                        //   34
+    SP_SHAPESHIFTER,
     SP_ELF,                            // (placeholder)
     SP_HILL_DWARF,                     // (placeholder)
     NUM_SPECIES,                       // always after the last species
diff -urN source.orig/externs.h source/externs.h
--- source.orig/externs.h	2008-08-01 02:00:56.000000000 +0300
+++ source/externs.h	2008-09-23 20:06:32.000000000 +0300
@@ -594,6 +594,8 @@
   int hunger;
   FixedVector<char, NUM_EQUIP> equip;
 
+  int form;                   // Current form for shapeshifter 
+
   int hp;
   int hp_max;
   int base_hp;                // temporary max HP loss (rotting)
@@ -689,6 +691,7 @@
   FixedArray<unsigned char, 6, 50> item_description;
   FixedVector<unique_item_status_type, 50> unique_items;
   FixedVector<bool, NUM_MONSTERS> unique_creatures;
+  FixedVector<bool, NUM_MONSTERS> ok_forms;
 
   // NOTE: The kills member is a pointer to a KillMaster object,
   // rather than the object itself, so that we can get away with
diff -urN source.orig/fight.cc source/fight.cc
--- source.orig/fight.cc	2008-08-01 02:01:01.000000000 +0300
+++ source/fight.cc	2008-09-26 16:54:06.000000000 +0300
@@ -702,7 +702,7 @@
 
     // Don't drink poisonous or mutagenic blood.
     return (chunk_type == CE_CLEAN || chunk_type == CE_CONTAMINATED
-            || chunk_type == CE_POISONOUS && player_res_poison());
+            || chunk_type == CE_POISONOUS && player_res_poison() >= 0);
 }
 
 // Should life protection protect from this?
@@ -895,6 +895,89 @@
     bool simple_miss_message = false;
     std::string miss_verb;
 
+    if (can_do_unarmed && player_is_shapeshifted())
+    {
+        for (int count=1; count<4; count++)
+        {
+            unarmed_attack.clear();
+            miss_verb.clear();
+            simple_miss_message = false;
+            damage_brand = shapeshifter_get_damage_brand(count);
+            aux_damage = get_monster_data(you.form)->attack[count].damage;
+
+            switch (get_monster_data(you.form)->attack[count].type)
+            {
+            case AT_BITE:
+                unarmed_attack = "bite";
+                break;
+            case AT_STING:
+                unarmed_attack = "sting";
+                break;
+            case AT_TOUCH:
+                unarmed_attack = "touch";
+                break;
+            case AT_TAIL_SLAP:
+                unarmed_attack = "tail-slap";
+                break;
+            case AT_BUTT:
+                unarmed_attack = "headbutt";
+                break;
+            case AT_CLAW:
+                unarmed_attack = "claw";
+                break;
+            case AT_NONE:
+            default:
+                return false;
+            }
+
+            // unified to-hit calculation
+            to_hit = random2( calc_your_to_hit_unarmed(uattack, false) );
+
+            make_hungry(2, true);
+
+            alert_nearby_monsters();
+
+            // XXX We're clobbering did_hit
+            did_hit = false;
+
+            bool ely_block = false;
+            if (you.religion != GOD_ELYVILON && you.penance[GOD_ELYVILON]
+                && to_hit >= def->ev && one_chance_in(20))
+            {
+                simple_god_message(" blocks your attack.", GOD_ELYVILON);
+                ely_block = true;
+            }
+
+            if (!ely_block && (to_hit >= def->ev || one_chance_in(30)))
+            {
+                if (attack_shield_blocked(true))
+                    continue;
+                if (player_apply_aux_unarmed())
+                    return (true);
+            }
+            else
+            {
+                if (simple_miss_message)
+                {
+                    mprf("You miss %s.",
+                         defender->name(DESC_NOCAP_THE).c_str());
+                }
+                else
+                {
+                    mprf("Your %s misses %s.",
+                         miss_verb.empty()? unarmed_attack.c_str()
+                         : miss_verb.c_str(),
+                         defender->name(DESC_NOCAP_THE).c_str());
+                }
+
+                if (ely_block)
+                    dec_penance(GOD_ELYVILON, 1 + random2(to_hit - def->ev));
+            }
+        }
+
+        return false;
+    } // shapeshifters end here
+
     if (can_do_unarmed)
     {
         if (you.species == SP_NAGA)
@@ -1597,6 +1680,18 @@
     else if (weapon->base_type == OBJ_WEAPONS)
         weap_type = weapon->sub_type;
 
+    if (weap_type == WPN_UNARMED && player_is_shapeshifted())
+    {
+        if (mons_damage(you.form, 0) == 0)
+        {
+            attack_verb = "clumsily bash";
+            return (damage);
+        }
+
+        attack_verb = mons_attack_verb(get_monster_data(you.form)->attack[0]).c_str();
+        return (damage);
+    }
+
     // All weak hits look the same, except for when the player
     // has a non-weapon in hand.  -- bwr
     // Exception: vampire bats only _bite_ to allow for drawing blood
@@ -2249,7 +2344,12 @@
 
         }
         break;
-
+    case SPWPN_PARALYSE:
+        wasp_paralyse_defender();
+        break;
+    case SPWPN_ACID:
+        splash_defender_with_acid(1);
+        break;
     case SPWPN_DRAINING:
         drain_defender();
         break;
@@ -3045,6 +3145,13 @@
         damage = 0;
     }
 
+    if (player_is_shapeshifted())
+    {
+        damage = mons_damage(you.form, 0);
+        if (damage == 0)
+            return 0;
+    }
+
     if (you.attribute[ATTR_TRANSFORMATION] != TRAN_NONE)
     {
         switch (you.attribute[ATTR_TRANSFORMATION])
@@ -3570,6 +3677,16 @@
     if (attacker->id() == MONS_YELLOW_WASP)
         paralyse_roll += 3;
 
+    if (attacker->id() == -1) // player shapeshifter
+    {
+        if (you.form == MONS_RED_WASP || one_chance_in(3))
+            defender->poison( attacker, coinflip()? 2 : 1 );
+
+        paralyse_roll = (damage_done > 4? 3 : 20);
+        if (you.form == MONS_YELLOW_WASP)
+            paralyse_roll += 3;
+    }
+
     if (defender->res_poison() <= 0)
     {
         if (one_chance_in(paralyse_roll))
diff -urN source.orig/food.cc source/food.cc
--- source.orig/food.cc	2008-08-01 02:00:55.000000000 +0300
+++ source/food.cc	2008-09-23 23:53:19.000000000 +0300
@@ -255,7 +255,8 @@
         if (you.has_claws()
             || transform_can_butcher_barehanded(
                     static_cast<transformation_type>(
-                        you.attribute[ATTR_TRANSFORMATION])))
+                        you.attribute[ATTR_TRANSFORMATION]))
+            || (you.species == SP_SHAPESHIFTER && shapeshifter_can_butcher_barehanded(you.form)))
         {
             butcher_tool = -1;
             return (true);
@@ -483,6 +484,12 @@
                             || you.equip[EQ_WEAPON] != -1
                                && can_cut_meat(you.inv[you.equip[EQ_WEAPON]]);
 
+    if (!can_butcher && you.species == SP_SHAPESHIFTER
+                    && shapeshifter_can_butcher_barehanded(you.form))
+    {
+        can_butcher = true;
+    }
+
     if (!Options.easy_butcher && !can_butcher)
     {
         mpr("Maybe you should try using a sharper implement.");
@@ -1896,7 +1903,7 @@
     if (food.base_type != OBJ_FOOD && food.base_type != OBJ_CORPSES)
         return (false);
 
-    if (player_res_poison(false))
+    if (player_res_poison(false) > 0)
         return (false);
 
     return (mons_corpse_effect(food.plus) == CE_POISONOUS);
diff -urN source.orig/itemname.cc source/itemname.cc
--- source.orig/itemname.cc	2008-08-01 02:01:01.000000000 +0300
+++ source/itemname.cc	2008-09-21 15:03:51.000000000 +0300
@@ -2305,7 +2305,7 @@
         case POT_POISON:
         case POT_STRONG_POISON:
             // Poison is not that bad if you're poison resistant.
-            return (!player_res_poison(false)
+            return (player_res_poison(false) <= 0
                     || !temp && you.species == SP_VAMPIRE);
         case POT_MUTATION:
             return (you.is_undead
@@ -2464,7 +2464,7 @@
         case POT_POISON:
         case POT_STRONG_POISON:
             // If you're poison resistant, poison is only useless.
-            return player_res_poison(false);
+            return (player_res_poison(false) > 0);
         }
 
         return (false);
@@ -2499,7 +2499,7 @@
             return (player_mutation_level(MUT_ACUTE_VISION));
 
         case RING_POISON_RESISTANCE:
-            return (player_res_poison(false, temp, false)
+            return (player_res_poison(false, temp, false) > 0
                     && (temp || you.species != SP_VAMPIRE));
 
         case AMU_CONTROLLED_FLIGHT:
diff -urN source.orig/itemprop.h source/itemprop.h
--- source.orig/itemprop.h	2008-08-01 02:00:58.000000000 +0300
+++ source/itemprop.h	2008-09-21 00:03:22.000000000 +0300
@@ -102,6 +102,9 @@
 
     SPWPN_RETURNING = MAX_PAN_LORD_BRANDS,
     SPWPN_CONFUSE,
+    SPWPN_PARALYSE,
+    SPWPN_ACID,
+
     SPWPN_RANDART_I = 25,              //   25
     SPWPN_RANDART_II,
     SPWPN_RANDART_III,
diff -urN source.orig/item_use.cc source/item_use.cc
--- source.orig/item_use.cc	2008-08-01 02:00:47.000000000 +0300
+++ source/item_use.cc	2008-09-23 00:52:00.000000000 +0300
@@ -818,7 +818,7 @@
 //---------------------------------------------------------------
 void wear_armour( int slot ) // slot is for tiles
 {
-    if (you.attribute[ATTR_TRANSFORMATION] == TRAN_BAT)
+    if (you.attribute[ATTR_TRANSFORMATION] == TRAN_BAT || !shapeshifter_can_wear(you.form, EQ_WEAPON))
     {
         mpr("You can't wear anything in your present form.");
         return;
@@ -869,9 +869,9 @@
     const equipment_type slot = get_armour_slot(item);
 
     if (sub_type == ARM_NAGA_BARDING)
-        can_wear = (you.species == SP_NAGA);
+        can_wear = (you.species == SP_NAGA || mons_genus(you.form) == MONS_NAGA);
     else if (sub_type == ARM_CENTAUR_BARDING)
-        can_wear = (you.species == SP_CENTAUR);
+        can_wear = (you.species == SP_CENTAUR || mons_genus(you.form) == MONS_CENTAUR);
 
     if (!can_wear)
     {
@@ -908,7 +908,7 @@
            return (false);
         }
 
-        if (you.species == SP_NAGA)
+        if (you.species == SP_NAGA || mons_genus(you.form) == MONS_NAGA)
         {
             if (verbose)
                 mpr("You can't wear that!");
@@ -916,7 +916,8 @@
         }
 
         if (!ignore_temporary && player_is_swimming()
-            && you.species == SP_MERFOLK)
+            && (you.species == SP_MERFOLK || mons_genus(you.form) == MONS_MERFOLK
+                 || mons_genus(you.form) == MONS_MERMAID))
         {
             if (verbose)
                mpr("You don't currently have feet!");
@@ -925,13 +926,14 @@
         }
     }
 
-    if (you.species == SP_NAGA && sub_type == ARM_NAGA_BARDING
-        && (ignore_temporary || !player_is_shapechanged()))
+    if ((you.species == SP_NAGA || mons_genus(you.form) == MONS_NAGA)
+           && sub_type == ARM_NAGA_BARDING
+           && (ignore_temporary || !player_is_shapechanged()))
     {
         // It fits.
         return (true);
     }
-    else if (you.species == SP_CENTAUR
+    else if ((you.species == SP_CENTAUR || mons_genus(you.form) == MONS_CENTAUR)
              && sub_type == ARM_CENTAUR_BARDING
              && (ignore_temporary || !player_is_shapechanged()))
     {
@@ -944,7 +946,8 @@
         if (!is_hard_helmet( item ))
             return (true);
 
-        if (player_mutation_level(MUT_HORNS))
+        if (player_mutation_level(MUT_HORNS)
+            || mons_genus(you.form) == MONS_MINOTAUR)
         {
             if (verbose)
                 mpr("You can't wear that with your horns!");
@@ -983,7 +986,8 @@
     }
 
     // Giant races and draconians.
-    if (player_size(PSIZE_TORSO) >= SIZE_LARGE || player_genus(GENPC_DRACONIAN))
+    if (player_size(PSIZE_TORSO) >= SIZE_LARGE || player_genus(GENPC_DRACONIAN)
+        || mons_genus(you.form) == MONS_DRACONIAN)
     {
         if (sub_type >= ARM_LEATHER_ARMOUR
                && sub_type <= ARM_PLATE_MAIL
@@ -1529,7 +1533,7 @@
 // Returns true if warning is given.
 static bool _fire_warn_if_impossible()
 {
-    if (you.attribute[ATTR_TRANSFORMATION] == TRAN_BAT)
+    if (you.attribute[ATTR_TRANSFORMATION] == TRAN_BAT || !shapeshifter_can_wear(you.form, EQ_WEAPON))
     {
         canned_msg(MSG_PRESENT_FORM);
         return (true);
@@ -3138,7 +3142,7 @@
 
 bool puton_ring(int slot, bool prompt_finger)
 {
-    if (you.attribute[ATTR_TRANSFORMATION] == TRAN_BAT)
+    if (you.attribute[ATTR_TRANSFORMATION] == TRAN_BAT || !shapeshifter_can_wear(you.form, EQ_WEAPON, false))
     {
         mpr("You can't put on anything in your present form.");
         return (false);
@@ -3258,7 +3262,8 @@
 
 bool remove_ring(int slot, bool announce)
 {
-    if (you.attribute[ATTR_TRANSFORMATION] == TRAN_BAT)
+    if (you.attribute[ATTR_TRANSFORMATION] == TRAN_BAT
+        || !shapeshifter_can_wear(you.form, EQ_WEAPON, false))
     {
         mpr("You can't wear or remove anything in your present form.");
         return (false);
@@ -3637,7 +3642,7 @@
         return;
     }
 
-    if (you.attribute[ATTR_TRANSFORMATION] == TRAN_BAT)
+    if (you.attribute[ATTR_TRANSFORMATION] == TRAN_BAT || !shapeshifter_can_wear(you.form, EQ_WEAPON))
     {
        canned_msg(MSG_PRESENT_FORM);
        return;
diff -urN source.orig/it_use2.cc source/it_use2.cc
--- source.orig/it_use2.cc	2008-08-01 02:00:59.000000000 +0300
+++ source/it_use2.cc	2008-09-21 14:59:07.000000000 +0300
@@ -198,7 +198,7 @@
 
     case POT_POISON:
     case POT_STRONG_POISON:
-        if (player_res_poison())
+        if (player_res_poison() > 0)
         {
             mprf("You feel %s nauseous.",
                  (pot_eff == POT_POISON) ? "slightly" : "quite" );
@@ -555,7 +555,7 @@
         break;
 
     case SPARM_POISON_RESISTANCE:
-        if (!player_res_poison())
+        if (player_res_poison() <= 0)
             mpr("You feel less healthy.");
         break;
 
diff -urN source.orig/misc.cc source/misc.cc
--- source.orig/misc.cc	2008-08-01 02:00:56.000000000 +0300
+++ source/misc.cc	2008-09-22 17:25:24.000000000 +0300
@@ -1309,7 +1309,7 @@
 
 void curare_hits_player(int agent, int degree)
 {
-    const bool res_poison = player_res_poison();
+    const bool res_poison = player_res_poison() > 0;
 
     poison_player(degree);
 
@@ -2665,6 +2665,48 @@
 {
     std::string result;
 
+    if (player_is_shapeshifted())
+    {
+        switch (mons_genus(you.form))
+        {
+        case MONS_HUMAN:
+        case MONS_DRACONIAN:
+        case MONS_KOBOLD:
+        case MONS_GNOLL:
+        case MONS_GOBLIN:
+        case MONS_TROLL:
+        case MONS_ELF:
+        case MONS_OGRE:
+        case MONS_CENTAUR:
+        case MONS_ORC:
+        case MONS_CYCLOPS:
+        case MONS_HILL_GIANT:
+        case MONS_NAGA:
+        case MONS_MINOTAUR:
+        case MONS_BOGGART:
+        case MONS_YAKTAUR:
+            result = you.has_usable_claws() ? "claw" : "hand";
+            break;
+        case MONS_GIANT_ANT:
+        case MONS_WOLF_SPIDER:
+        case MONS_RAT:
+        case MONS_GIANT_COCKROACH:
+        case MONS_BEAR:
+        case MONS_YAK:
+        case MONS_SHEEP:
+        case MONS_HOUND:
+            result = "front leg";
+            break;
+        case MONS_DRAGON:
+        case MONS_GIANT_BAT:
+            result = "foreclaw";
+            break;
+        default:
+            result = "whatever-you-use-for-hitting";
+            break;
+        }
+    }
+
     switch (you.attribute[ATTR_TRANSFORMATION])
     {
     default:
diff -urN source.orig/monstuff.cc source/monstuff.cc
--- source.orig/monstuff.cc	2008-08-01 02:00:55.000000000 +0300
+++ source/monstuff.cc	2008-09-24 00:50:34.000000000 +0300
@@ -777,6 +777,12 @@
         crawl_state.cancel_cmd_repeat();
     }
 
+    if (killer == KILL_YOU) {
+        you.ok_forms[monster->type] = true;
+        if (mons_is_unique(monster->type))
+            you.ok_forms[mons_species(monster->type)] = true;
+    }
+
     if (killer == KILL_YOU)
         crawl_state.cancel_cmd_repeat();
 
@@ -6291,7 +6297,7 @@
         menv[i].flags &= ~MF_JUST_SUMMONED;
 }
 
-static bool _is_item_jelly_edible(const item_def &item)
+bool is_item_jelly_edible(const item_def &item)
 {
     // Don't eat artefacts (note that unrandarts are randarts).
     if (is_fixed_artefact(item) || is_random_artefact(item))
@@ -6354,7 +6360,7 @@
         {
             int quant = mitm[item].quantity;
 
-            if (!_is_item_jelly_edible(mitm[item]))
+            if (!is_item_jelly_edible(mitm[item]))
                 continue;
 
 #if DEBUG_DIAGNOSTICS || DEBUG_EATERS
diff -urN source.orig/monstuff.h source/monstuff.h
--- source.orig/monstuff.h	2008-08-01 02:00:59.000000000 +0300
+++ source/monstuff.h	2008-09-20 23:24:57.000000000 +0300
@@ -207,4 +207,6 @@
 
 int mons_natural_regen_rate(monsters *monster);
 
+bool is_item_jelly_edible(const item_def &item);
+
 #endif
diff -urN source.orig/mutation.cc source/mutation.cc
--- source.orig/mutation.cc	2008-08-01 02:00:55.000000000 +0300
+++ source/mutation.cc	2008-09-30 18:43:37.000000000 +0300
@@ -1724,6 +1724,12 @@
         }
     }
 
+    if (you.species == SP_SHAPESHIFTER)
+    {
+        return shapeshift(RANDOM_MONSTER, SHIFT_MUTATION);
+    }
+
+
     bool rotting = you.is_undead;
 
     if (you.is_undead == US_SEMI_UNDEAD)
@@ -2854,6 +2860,15 @@
 {
     mutation_type mutat = NUM_MUTATIONS;
 
+    if (you.species == SP_SHAPESHIFTER)
+    {
+        if (one_chance_in(5))
+        {
+            return shapeshift(coinflip() ? MONS_PLANT : MONS_FUNGUS, SHIFT_MUTATION);
+        }
+        return shapeshift(RANDOM_MONSTER, SHIFT_MUTATION);
+    }
+
     switch (random2(13))
     {
     case 0: mutat = MUT_CARNIVOROUS; break;
diff -urN source.orig/newgame.cc source/newgame.cc
--- source.orig/newgame.cc	2008-08-01 02:01:02.000000000 +0300
+++ source/newgame.cc	2008-09-24 01:46:16.000000000 +0300
@@ -133,6 +133,7 @@
 static void _give_random_secondary_armour( int slot );
 static bool _give_wanderer_weapon( int slot, int wpn_skill );
 static void _create_wanderer(void);
+static void _create_shapeshifter(void);
 static bool _give_items_skills(void);
 
 ////////////////////////////////////////////////////////////////////////
@@ -167,7 +168,7 @@
     SP_SPRIGGAN,    SP_MINOTAUR,
     SP_DEMONSPAWN,  SP_GHOUL,
     SP_KENKU,       SP_MERFOLK,
-    SP_VAMPIRE
+    SP_VAMPIRE,     SP_SHAPESHIFTER
 };
 
 // Fantasy staples and humanoid creatures come first, then dimunitive and
@@ -191,7 +192,9 @@
     SP_DEMIGOD,     SP_DEMONSPAWN,
     // undead species
     SP_MUMMY,       SP_GHOUL,
-    SP_VAMPIRE
+    SP_VAMPIRE,
+    //
+    SP_SHAPESHIFTER
 };
 
 static species_type _random_draconian_species()
@@ -268,7 +271,7 @@
       "HO", "Ko", "Mu", "Na", "Gn", "Og", "Tr", "OM",
       // the draconians
       "Dr", "Dr", "Dr", "Dr", "Dr", "Dr", "Dr", "Dr", "Dr", "Dr",
-      "Ce", "DG", "Sp", "Mi", "DS", "Gh", "Ke", "Mf", "Vp",
+      "Ce", "DG", "Sp", "Mi", "DS", "Gh", "Ke", "Mf", "Vp", "SS",
       // placeholders
       "HD", "El" };
 
@@ -1322,6 +1325,12 @@
 static char_choice_restriction _class_allowed( species_type speci,
                                                job_type char_class )
 {
+    // Only wanderer is allowed for shapeshifters
+    if (speci == SP_SHAPESHIFTER)
+    {
+        return (char_class == JOB_WANDERER ? CC_UNRESTRICTED : CC_BANNED);
+    }
+        
     switch (char_class)
     {
     case JOB_FIGHTER:
@@ -2826,6 +2835,11 @@
         set_ident_type( OBJ_POTIONS, POT_BLOOD_COAGULATED, ID_KNOWN_TYPE );
     }
 
+    if (you.species == SP_SHAPESHIFTER)
+    {
+        set_ident_type( OBJ_POTIONS, POT_MUTATION, ID_KNOWN_TYPE );
+    }
+
     switch (which_job)
     {
     case JOB_PRIEST:
@@ -3625,6 +3639,28 @@
     you.equip[EQ_BODY_ARMOUR] = 2;
 }
 
+// 
+static void _create_shapeshifter( void )
+{
+    _newgame_make_item(0, EQ_NONE, OBJ_POTIONS, POT_MUTATION);
+    you.skills[SK_FIGHTING] = 3;
+    you.skills[SK_UNARMED_COMBAT] = 4;
+    you.skills[SK_DODGING] = 2;
+    you.skills[SK_STEALTH] = 2;
+    // shapeshifter should have at least two forms
+    if (coinflip())
+    {
+        you.ok_forms[MONS_GOBLIN] = true;
+        you.ok_forms[MONS_GIANT_COCKROACH] = true;
+    }
+    else
+    {
+        you.ok_forms[MONS_KOBOLD] = true;
+        you.ok_forms[MONS_RAT] = true;
+    }
+}
+
+
 // choose_race returns true if the player should also pick a class.
 // This is done because of the '!' option which will pick a random
 // character, obviating the necessity of choosing a class.
@@ -5320,7 +5356,10 @@
         break;
 
     case JOB_WANDERER:
-        _create_wanderer();
+        if (you.species == SP_SHAPESHIFTER)
+            _create_shapeshifter();
+        else
+            _create_wanderer();
         break;
 
     default:
diff -urN source.orig/output.cc source/output.cc
--- source.orig/output.cc	2008-08-01 02:01:03.000000000 +0300
+++ source/output.cc	2008-10-04 14:01:10.000000000 +0300
@@ -651,7 +651,7 @@
         }
     }
 
-    if (you.duration[DUR_INVIS])
+    if (you.duration[DUR_INVIS] || you.form == MONS_UNSEEN_HORROR)
     {
         int color = _dur_colour( BLUE, (you.duration[DUR_INVIS] <= 6) );
         out.push_back(status_light(color, "Invis"));
@@ -1593,6 +1593,13 @@
     return ( (stat >= 1) ? "+  " : ".  " );
 }
 
+const char* itosym2(int stat)
+{
+    return ( (stat >= 1) ? "+  " :
+             (stat == 0) ? ".  " :
+                           "x  " );
+}
+
 const char* itosym3(int stat)
 {
     return ( (stat >=  3) ? "+ + +" :
@@ -1934,7 +1941,7 @@
              _determine_colour_string(rfire, 3), itosym3(rfire),
              _determine_colour_string(rcold, 3), itosym3(rcold),
              _determine_colour_string(rlife, 3), itosym3(rlife),
-             _determine_colour_string(rpois, 1), itosym1(rpois),
+             _determine_colour_string(rpois, 1), itosym2(rpois),
              _determine_colour_string(relec, 1), itosym1(relec),
              _determine_colour_string(rsust, 1), itosym1(rsust),
              _determine_colour_string(rmuta, 1), itosym1(rmuta),
@@ -2187,7 +2194,7 @@
     if (you.duration[DUR_SILENCE])
         text += "radiating silence, ";
 
-    if (you.duration[DUR_INVIS])
+    if (you.duration[DUR_INVIS] || you.form == MONS_UNSEEN_HORROR)
         text += "invisible, ";
 
     if (you.duration[DUR_CONF])
@@ -2347,6 +2354,13 @@
           text += "\nYou are a cloud of diffuse gas.";
           break;
     }
+
+    if (player_is_shapeshifted())
+    {
+        text += "\nYou are shapeshifted into ";
+        text += get_monster_data(you.form)->name;
+        text += ".";
+    }
 /*
 //  Commenting out until this information is actually meaningful. (jpeg)
     const int to_hit = calc_your_to_hit( false ) * 2;
diff -urN source.orig/player.cc source/player.cc
--- source.orig/player.cc	2008-08-01 02:00:56.000000000 +0300
+++ source/player.cc	2008-10-01 17:35:34.000000000 +0300
@@ -478,6 +478,11 @@
 // (See mon-data.h for species/genus use.)
 bool is_player_same_species(const int mon, bool transform)
 {
+    if (player_is_shapeshifted())
+    {
+        mon == you.form;
+    }
+
     if (transform)
     {
         switch (you.attribute[ATTR_TRANSFORMATION])
@@ -596,6 +601,9 @@
 // -------------------------------------------------
 bool you_can_wear(int eq, bool special_armour)
 {
+    if (player_is_shapeshifted())
+        return shapeshifter_can_wear(you.form, eq, special_armour);
+
     // These can be used by all.
     if (eq == EQ_LEFT_RING || eq == EQ_RIGHT_RING || eq == EQ_AMULET
         || eq == EQ_WEAPON || eq == EQ_CLOAK)
@@ -991,6 +999,18 @@
     // fast heal mutation
     rr += player_mutation_level(MUT_REGENERATION) * 20;
 
+    if (player_is_shapeshifted())
+    {
+        switch(mons_genus(you.form))
+        {
+        case MONS_TROLL:
+        case MONS_SLIME_CREATURE:
+            rr += 40;
+        default:
+            break;
+        }
+    }
+
     // Ghouls heal slowly.
     // Dematerialised people heal slowly.
     // Dematerialised ghouls shouldn't heal any more slowly. -- bwr
@@ -1044,6 +1064,18 @@
     if (you.species == SP_TROLL)
         hunger += 3;            // in addition to the +3 for fast metabolism
 
+    if (player_is_shapeshifted())
+    {
+        switch(mons_genus(you.form))
+        {
+        case MONS_TROLL:
+        case MONS_SLIME_CREATURE:
+            hunger += 6;
+        default:
+            break;
+        }
+    }
+
     if (you.duration[DUR_REGENERATION] > 0)
         hunger += 4;
 
@@ -1227,6 +1259,11 @@
 {
     int res = 0;
 
+    if (player_is_shapeshifted())
+    {
+        res += ((get_monster_data(you.form))->resists.steam);
+    }
+
     if (you.species == SP_PALE_DRACONIAN && you.experience_level > 5)
         res += 2;
 
@@ -1246,6 +1283,12 @@
 {
     int rf = 0;
 
+    if (player_is_shapeshifted())
+    {
+        rf += ((get_monster_data(you.form))->resists.fire);
+        rf += ((get_monster_data(you.form))->resists.hellfire);
+    }
+
     if (items)
     {
         /* rings of fire resistance/fire */
@@ -1317,6 +1360,11 @@
 {
     int rc = 0;
 
+    if (player_is_shapeshifted())
+    {
+        rc += ((get_monster_data(you.form))->resists.cold);
+    }
+
     if (temp)
     {
         // spells:
@@ -1395,6 +1443,12 @@
 int player_res_acid(bool calc_unid, bool items)
 {
     int res = 0;
+
+    if (player_is_shapeshifted())
+    {
+        res += ((get_monster_data(you.form))->resists.acid);
+    }
+
     if (!transform_changed_physiology())
     {
         if (you.species == SP_YELLOW_DRACONIAN
@@ -1436,6 +1490,11 @@
 {
     int re = 0;
 
+    if (player_is_shapeshifted())
+    {
+        re += ((get_monster_data(you.form))->resists.elec);
+    }
+
     if (temp)
     {
         if (you.duration[DUR_INSULATION])
@@ -1484,6 +1543,11 @@
     if (you.is_undead)
         return (true);
 
+    if (player_is_shapeshifted())
+    {
+        return (get_monster_data(you.form))->resists.asphyx > 0;
+    }
+
     switch (you.attribute[ATTR_TRANSFORMATION])
     {
     case TRAN_LICH:
@@ -1516,6 +1580,11 @@
 {
     int rp = 0;
 
+    if (player_is_shapeshifted())
+    {
+        rp += ((get_monster_data(you.form))->resists.poison);
+    }
+
     if (items)
     {
         // rings of poison resistance
@@ -1848,6 +1917,11 @@
 {
     int mv = 10;
 
+    if (player_is_shapeshifted())
+    {
+        mv = 10 + (10 - get_monster_data(you.form)->speed);
+    }
+
     if (player_is_swimming())
     {
         // This is swimming... so it doesn't make sense to really
@@ -1855,6 +1929,9 @@
         // swiftness is an air spell, can't wear boots, can't be
         // transformed).
         mv = 6;
+
+        if (player_is_shapeshifted())
+            mv = 10 + (10 - get_monster_data(you.form)->speed);
     }
     else
     {
@@ -1999,6 +2076,13 @@
     int AC = 0;
     int i;                      // loop variable
 
+    if (player_is_shapeshifted())
+    {
+        AC = 100 * ((get_monster_data(you.form))->AC);
+        if (shapeshifter_armoured(you.form))
+            AC = AC * (30 + 2 * you.skills[SK_ARMOUR]) / 30;
+    }
+
     for (i = EQ_CLOAK; i <= EQ_BODY_ARMOUR; i++)
     {
         if ( i == EQ_SHIELD )
@@ -2218,6 +2302,9 @@
 {
     const int arm = you.equip[EQ_BODY_ARMOUR];
 
+    if (player_is_shapeshifted() && shapeshifter_armoured(you.form))
+        return true;
+
     if (arm == -1)
         return (true);
 
@@ -2268,6 +2355,12 @@
     const int size_factor = SIZE_MEDIUM - size;
     int ev = 10 + 2 * size_factor;
 
+    if (player_is_shapeshifted())
+    {
+        ev = ((get_monster_data(you.form))->ev);
+    }
+
+
     // Repulsion fields and size are all that matters when paralysed.
     if (you.cannot_move())
     {
@@ -2576,6 +2669,11 @@
 {
     int si = 0;
 
+    if (player_is_shapeshifted() && mons_class_flag(you.form, M_SEE_INVIS))
+    {
+        si++;
+    }
+
     si += player_equip( EQ_RINGS, RING_SEE_INVISIBLE, calc_unid );
 
     /* armour: (checks head armour only) */
@@ -3501,6 +3599,28 @@
                     modify_stat(STAT_RANDOM, 1, false, "level gain");
                 break;
 
+            case SP_SHAPESHIFTER:
+                if (you.experience_level == 5)
+                {
+                    mpr("You can now slightly control deliberate shapeshifts.", MSGCH_INTRINSIC_GAIN);
+                }
+                else if (you.experience_level == 7)
+                {
+                    mpr("You can now try to imitate any natural creature.", MSGCH_INTRINSIC_GAIN);
+                }
+                else if (you.experience_level == 10)
+                {
+                    mpr("You can now better control deliberate shapeshifts.", MSGCH_INTRINSIC_GAIN);
+                }
+                else if (you.experience_level == 15)
+                {
+                    mpr("You can now better control deliberate shapeshifts.", MSGCH_INTRINSIC_GAIN);
+                }
+                else if (you.experience_level == 20)
+                {
+                    mpr("You can now shapeshift into almost any natural creature!", MSGCH_INTRINSIC_GAIN);
+                }
+                break;
             default:
                 break;
             }
@@ -3658,6 +3778,13 @@
     if (you.duration[DUR_SILENCE])
         stealth -= 50;
 
+
+    if (player_is_shapeshifted())
+    {
+        if (mons_genus(you.form) == MONS_PLANT)
+            stealth += 200;
+    }
+
     stealth = std::max(0, stealth);
 
     return (stealth);
@@ -3800,6 +3927,11 @@
         }
     }
 
+    if (player_is_shapeshifted())
+    {
+        mprf( "You are shapeshifted into %s.", get_monster_data(you.form)->name);
+    }
+
     switch (you.attribute[ATTR_TRANSFORMATION])
     {
     case TRAN_SPIDER:
@@ -3909,7 +4041,7 @@
     if (you.duration[DUR_SEE_INVISIBLE])
         mpr( "You can see invisible." );
 
-    if (you.duration[DUR_INVIS])
+    if (you.duration[DUR_INVIS] || you.form == MONS_UNSEEN_HORROR)
         mpr( "You are invisible." );
 
     if (you.duration[DUR_CONF])
@@ -4194,6 +4326,7 @@
         case SP_MINOTAUR:   res = "Minotaur";                        break;
         case SP_KENKU:      res = "Kenku";                           break;
         case SP_VAMPIRE:    res = "Vampire";                         break;
+        case SP_SHAPESHIFTER: res = "Shapeshifter";                  break;
 
         case SP_HILL_ORC:
             res = (adj ? "Orcish" : genus ? "Orc" : "Hill Orc");
@@ -4235,6 +4368,12 @@
 
 bool wearing_amulet(char amulet, bool calc_unid)
 {
+    if (player_is_shapeshifted() && amulet == AMU_CONTROLLED_FLIGHT
+        && mons_class_flag(you.form, M_FLIES))
+    {
+        return (true);
+    }
+
     if (amulet == AMU_CONTROLLED_FLIGHT
         && (you.duration[DUR_CONTROLLED_FLIGHT]
             || player_genus(GENPC_DRACONIAN)
@@ -4284,6 +4423,7 @@
         return 13;
     switch (species)
     {
+        case SP_SHAPESHIFTER:
         case SP_HUMAN:
         case SP_HALFLING:
         case SP_HILL_ORC:
@@ -5035,7 +5175,7 @@
 
 bool poison_player( int amount, bool force )
 {
-    if (!force && player_res_poison() || amount <= 0)
+    if (!force && player_res_poison() > 0 || amount <= 0)
         return (false);
 
     const int old_value = you.duration[DUR_POISONING];
@@ -5546,6 +5686,8 @@
     hunger       = 6000;
     hunger_state = HS_SATIATED;
 
+    form = -1;
+
     wield_change            = false;
     redraw_quiver           = false;
     received_weapon_warning = false;
@@ -5706,6 +5848,11 @@
 
 bool player::is_levitating() const
 {
+    if (player_is_shapeshifted() && mons_class_flag(you.form, M_LEVITATE))
+    {
+        return (true);
+    }
+
     return (duration[DUR_LEVITATION]);
 }
 
@@ -5717,6 +5864,11 @@
 
 bool player::can_swim() const
 {
+    if (player_is_shapeshifted() && get_monster_data(you.form)->habitat == HT_WATER)
+    {
+        return (true);
+    }
+
     return (species == SP_MERFOLK);
 }
 
@@ -5756,6 +5908,24 @@
     size_type ret = (base) ? SIZE_CHARACTER
                            : transform_size( psize );
 
+    if (player_is_shapeshifted())
+    {
+        switch (mons_genus(you.form))
+        {
+        case MONS_NAGA:
+            if (psize == PSIZE_TORSO || psize == PSIZE_PROFILE)
+                return SIZE_MEDIUM;
+            else
+                return SIZE_LARGE;
+        case MONS_CENTAUR:
+            return ((psize == PSIZE_TORSO) ? SIZE_MEDIUM : SIZE_LARGE);
+        default:
+            break;
+        }
+
+        return get_monster_data(you.form)->size;
+    }
+
     if (ret == SIZE_CHARACTER)
     {
         // Transformation has size of character's species.
@@ -5928,6 +6098,10 @@
         ret = SPWPN_CONFUSE;
     else if (mutation[MUT_DRAIN_LIFE])
         ret = SPWPN_DRAINING;
+    else if (player_is_shapeshifted())
+    {
+        ret = shapeshifter_get_damage_brand(0);
+    }
     else
     {
         switch (attribute[ATTR_TRANSFORMATION])
@@ -6183,6 +6357,9 @@
 
 bool player::cannot_move() const
 {
+    if (player_is_shapeshifted() && mons_genus(you.form) == MONS_PLANT)
+        return true;
+
     return (duration[DUR_PARALYSIS] || duration[DUR_PETRIFIED]);
 }
 
@@ -6364,11 +6541,16 @@
 
 bool player::omnivorous() const
 {
-    return (species == SP_TROLL || species == SP_OGRE);
+    return (species == SP_TROLL || species == SP_OGRE || species == SP_SHAPESHIFTER);
 }
 
 flight_type player::flight_mode() const
 {
+    if (player_is_shapeshifted() && mons_class_flag(you.form, M_FLIES))
+        return FL_FLY;
+    if (player_is_shapeshifted() && mons_class_flag(you.form, M_LEVITATE))
+        return FL_LEVITATE;
+
     if (attribute[ATTR_TRANSFORMATION] == TRAN_DRAGON
         || attribute[ATTR_TRANSFORMATION] == TRAN_BAT)
     {
@@ -6386,6 +6568,9 @@
 
 bool player::permanent_levitation() const
 {
+    if (player_is_shapeshifted() && mons_class_flag(you.form, M_LEVITATE))
+        return true;
+
     // Boots of levitation keep you with DUR_LEVITATION >= 2 at
     // all times. This is so that you can evoke stop-levitation
     // in order to actually cancel levitation (by setting
@@ -6396,6 +6581,9 @@
 
 bool player::permanent_flight() const
 {
+    if (player_is_shapeshifted() && mons_class_flag(you.form, M_FLIES))
+        return true;
+
     return (airborne() && wearing_amulet( AMU_CONTROLLED_FLIGHT )
             && you.species == SP_KENKU && you.experience_level >= 15);
 }
@@ -6554,6 +6742,17 @@
         }
     }
 
+    if (player_is_shapeshifted())
+    {
+        for (int a=0; a<4; a++)
+        {
+            // at least these monsters have claws
+            if (get_monster_data(you.form)->attack[a].type == AT_CLAW)
+                return true;
+        }
+        return false;
+    }
+
     // these are the only other sources for claws
     if (species == SP_TROLL || species == SP_GHOUL)
         return 3;
@@ -6597,6 +6796,8 @@
 
 bool player::invisible() const
 {
+    if (player_is_shapeshifted() && mons_class_flag(you.form, M_INVIS) && !backlit())
+        return true;
     return (duration[DUR_INVIS] && !backlit());
 }
 
@@ -7050,3 +7251,11 @@
 
     return (true);
 }
+
+bool player_is_shapeshifted(void)
+{
+    if (you.species == SP_SHAPESHIFTER && you.form != -1)
+        return (true);
+    return (false);
+}
+
diff -urN source.orig/player.h source/player.h
--- source.orig/player.h	2008-08-01 02:00:55.000000000 +0300
+++ source/player.h	2008-09-09 19:30:44.000000000 +0300
@@ -448,4 +448,9 @@
 int count_worn_ego( int which_ego );
 int stat_modifier( stat_type stat );
 
+
+// Shapeshifter
+bool player_is_shapeshifted(void);
+
+
 #endif
diff -urN source.orig/skills2.cc source/skills2.cc
--- source.orig/skills2.cc	2008-08-01 02:00:58.000000000 +0300
+++ source/skills2.cc	2008-09-26 15:51:46.000000000 +0300
@@ -1622,6 +1622,50 @@
      (120 * 75)/100,            // SK_EVOCATIONS
     },
 
+    {                           // SP_SHAPESHIFTER
+     100,                       // SK_FIGHTING
+     120,                       // SK_SHORT_BLADES
+     120,                       // SK_LONG_BLADES
+     120,                       // SK_UNUSED_1
+     120,                       // SK_AXES
+     120,                       // SK_MACES_FLAILS
+     120,                       // SK_POLEARMS
+     120,                       // SK_STAVES
+     120,                       // SK_SLINGS
+     120,                       // SK_BOWS
+     120,                       // SK_CROSSBOWS
+     120,                       // SK_DARTS
+     100,                       // SK_THROWING
+     100,                       // SK_ARMOUR
+      90,                       // SK_DODGING
+      80,                       // SK_STEALTH
+     100,                       // SK_STABBING
+     120,                       // SK_SHIELDS
+     100,                       // SK_TRAPS_DOORS
+      80,                       // SK_UNARMED_COMBAT
+     100,                       // undefined
+     100,                       // undefined
+     100,                       // undefined
+     100,                       // undefined
+     100,                       // undefined
+     (200 * 130) / 100,         // SK_SPELLCASTING
+     200,                       // SK_CONJURATIONS
+     200,                       // SK_ENCHANTMENTS
+     200,                       // SK_SUMMONINGS
+     200,                       // SK_NECROMANCY
+     200,                       // SK_TRANSLOCATIONS
+     200,                       // SK_TRANSMIGRATION
+     200,                       // SK_DIVINATIONS
+     200,                       // SK_FIRE_MAGIC
+     200,                       // SK_ICE_MAGIC
+     200,                       // SK_AIR_MAGIC
+     200,                       // SK_EARTH_MAGIC
+     200,                       // SK_POISON_MAGIC
+     (100 * 75) / 100,          // SK_INVOCATIONS
+     (100 * 75) / 100,          // SK_EVOCATIONS
+     },
+
+
     // SP_HILL_DWARF placeholder.
     {
     },
diff -urN source.orig/spells2.cc source/spells2.cc
--- source.orig/spells2.cc	2008-08-01 02:00:47.000000000 +0300
+++ source/spells2.cc	2008-09-21 15:09:48.000000000 +0300
@@ -641,7 +641,7 @@
     {
         mpr("The light passes straight through your body.");
     }
-    else if (!player_res_poison())
+    else if (player_res_poison() <= 0)
     {
         mpr("You feel rather sick.");
         poison_player(2);
diff -urN source.orig/spl-cast.cc source/spl-cast.cc
--- source.orig/spl-cast.cc	2008-08-01 02:01:03.000000000 +0300
+++ source/spl-cast.cc	2008-09-21 15:10:47.000000000 +0300
@@ -3754,7 +3754,7 @@
         switch (random2(2))
         {
         case 0:
-            if (player_res_poison())
+            if (player_res_poison() > 0)
                 canned_msg(MSG_NOTHING_HAPPENS);
             else
             {
@@ -3776,7 +3776,7 @@
         switch (random2(3))
         {
         case 0:
-            if (player_res_poison())
+            if (player_res_poison() > 0)
                 canned_msg(MSG_NOTHING_HAPPENS);
             else
             {
@@ -3792,7 +3792,7 @@
             break;
 
         case 2:
-            if (player_res_poison())
+            if (player_res_poison() > 0)
                 canned_msg(MSG_NOTHING_HAPPENS);
             else
                 lose_stat(STAT_RANDOM, 1, false, cause);
@@ -3804,7 +3804,7 @@
         switch (random2(3))
         {
         case 0:
-            if (player_res_poison())
+            if (player_res_poison() > 0)
                 canned_msg(MSG_NOTHING_HAPPENS);
             else
             {
@@ -3819,7 +3819,7 @@
                       you.x_pos, you.y_pos, 20, 7 + random2(7));
             break;
         case 2:
-            if (player_res_poison())
+            if (player_res_poison() > 0)
                 canned_msg(MSG_NOTHING_HAPPENS);
             else
                 lose_stat(STAT_RANDOM, 1 + random2avg(5, 2), false, cause);
diff -urN source.orig/tags.cc source/tags.cc
--- source.orig/tags.cc	2008-08-01 02:01:01.000000000 +0300
+++ source/tags.cc	2008-09-23 20:11:34.000000000 +0300
@@ -863,6 +863,8 @@
 
     marshallShort(th, you.hunger);
 
+    marshallShort(th, you.form);
+
     // how many you.equip?
     marshallByte(th, NUM_EQUIP);
     for (i = 0; i < NUM_EQUIP; ++i)
@@ -1097,6 +1099,8 @@
     marshallShort(th, NUM_MONSTERS);
     for (j = 0; j < NUM_MONSTERS; ++j)
         marshallByte(th,you.unique_creatures[j]); // unique beasties
+    for (j = 0; j < NUM_MONSTERS; ++j)
+        marshallByte(th,you.ok_forms[j]);
 
     // how many branches?
     marshallByte(th, NUM_BRANCHES);
@@ -1248,6 +1252,7 @@
     you.species         = static_cast<species_type>(unmarshallByte(th));
     you.hp              = unmarshallShort(th);
     you.hunger          = unmarshallShort(th);
+    you.form            = unmarshallShort(th);
 
     // How many you.equip?
     count_c = unmarshallByte(th);
@@ -1535,6 +1540,14 @@
         if (j < NUM_MONSTERS)
             you.unique_creatures[j] = created;
     }
+    you.ok_forms.init(false);
+    for (j = 0; j < count_c; ++j)
+    {
+        const bool ok = static_cast<bool>(unmarshallByte(th));
+
+        if (j < NUM_MONSTERS)
+            you.ok_forms[j] = ok;
+    }
 
     // how many branches?
     count_c = unmarshallByte(th);
diff -urN source.orig/transfor.cc source/transfor.cc
--- source.orig/transfor.cc	2008-08-01 02:00:46.000000000 +0300
+++ source/transfor.cc	2008-09-30 19:06:14.000000000 +0300
@@ -20,22 +20,464 @@
 
 #include "externs.h"
 
+#include "beam.h"
+#include "cio.h"
 #include "delay.h"
+#include "food.h"
 #include "it_use2.h"
 #include "item_use.h"
 #include "itemprop.h"
 #include "items.h"
+#include "mapdef.h"
+#include "message.h"
 #include "misc.h"
+#include "mon-util.h"
+#include "monplace.h"
+#include "monstuff.h"
 #include "output.h"
 #include "player.h"
 #include "randart.h"
 #include "skills2.h"
+#include "spl-util.h"
 #include "stuff.h"
 #include "traps.h"
+#include "view.h"
 
 void drop_everything(void);
 void extra_hp(int amount_extra);
 
+static monster_type ok_forms[214] =
+{
+    MONS_GIANT_ANT,
+    MONS_GIANT_BAT,
+    MONS_CENTAUR,
+    MONS_ETTIN,
+    MONS_GOBLIN,
+    MONS_HOUND,
+    MONS_JACKAL,
+    MONS_KILLER_BEE,
+    MONS_KILLER_BEE_LARVA,
+    MONS_ORC,
+    MONS_RAT,
+    MONS_SCORPION,
+    MONS_UGLY_THING,
+    MONS_WORM,
+    MONS_YELLOW_WASP,
+    MONS_GIANT_BEETLE,
+    MONS_CYCLOPS,
+    MONS_DRAGON,
+    MONS_TWO_HEADED_OGRE,
+    MONS_HOBGOBLIN,
+    MONS_ICE_BEAST,
+    MONS_JELLY,
+    MONS_KOBOLD,
+    MONS_GUARDIAN_NAGA,
+    MONS_OGRE,
+    MONS_QUEEN_BEE,
+    MONS_SNAKE,
+    MONS_TROLL,
+    MONS_UNSEEN_HORROR,
+    MONS_YAK,
+    MONS_ORC_WARRIOR,
+    MONS_KOBOLD_DEMONOLOGIST,
+    MONS_ORC_WIZARD,
+    MONS_ORC_KNIGHT,
+    MONS_WYVERN,
+    MONS_BIG_KOBOLD,
+    MONS_GIANT_EYEBALL,
+    MONS_WOLF_SPIDER,
+    MONS_EYE_OF_DRAINING,
+    MONS_BUTTERFLY,
+    MONS_WANDERING_MUSHROOM,
+    MONS_BRAIN_WORM,
+    MONS_GIANT_ORANGE_BRAIN,
+    MONS_BOULDER_BEETLE,
+    MONS_MINOTAUR,
+    MONS_ICE_DRAGON,
+    MONS_SLIME_CREATURE,
+    MONS_GREAT_ORB_OF_EYES,
+    MONS_GIANT_MITE,
+    MONS_STEAM_DRAGON,
+    MONS_VERY_UGLY_THING,
+    MONS_ORC_SORCERER,
+    MONS_HIPPOGRIFF,
+    MONS_GRIFFON,
+    MONS_HYDRA,
+    MONS_HELL_KNIGHT,
+    MONS_NECROMANCER,
+    MONS_WIZARD,
+    MONS_ORC_PRIEST,
+    MONS_ORC_HIGH_PRIEST,
+    MONS_HUMAN,
+    MONS_GNOLL,
+    MONS_MOTTLED_DRAGON,
+    MONS_BROWN_SNAKE,
+    MONS_GIANT_LIZARD,
+    MONS_PULSATING_LUMP,
+    MONS_STORM_DRAGON,
+    MONS_YAKTAUR,
+    MONS_DEATH_YAK,
+    MONS_ROCK_TROLL,
+    MONS_STONE_GIANT,
+    MONS_BUMBLEBEE,
+    MONS_REDBACK,
+    MONS_OGRE_MAGE,
+    MONS_SPINY_WORM,
+    MONS_TITAN,
+    MONS_GOLDEN_DRAGON,
+    MONS_ELF,
+    MONS_LINDWURM,
+    MONS_ELEPHANT_SLUG,
+    MONS_WAR_DOG,
+    MONS_GREY_RAT,
+    MONS_GREEN_RAT,
+    MONS_ORANGE_RAT,
+    MONS_BLACK_SNAKE,
+    MONS_SHEEP,
+    MONS_HOG,
+    MONS_GIANT_MOSQUITO,
+    MONS_GIANT_CENTIPEDE,
+    MONS_IRON_TROLL,
+    MONS_NAGA,
+    MONS_FIRE_GIANT,
+    MONS_FROST_GIANT,
+    MONS_FIREDRAKE,
+    MONS_SHADOW_DRAGON,
+    MONS_YELLOW_SNAKE,
+    MONS_GREY_SNAKE,
+    MONS_DEEP_TROLL,
+    MONS_GIANT_BLOWFLY,
+    MONS_RED_WASP,
+    MONS_SWAMP_DRAGON,
+    MONS_SWAMP_DRAKE,
+    MONS_DEATH_DRAKE,
+    MONS_SOLDIER_ANT,
+    MONS_HILL_GIANT,
+    MONS_QUEEN_ANT,
+    MONS_ANT_LARVA,
+    MONS_GIANT_FROG,
+    MONS_GIANT_BROWN_FROG,
+    MONS_SPINY_FROG,
+    MONS_BLINK_FROG,
+    MONS_GIANT_COCKROACH,
+    MONS_SMALL_SNAKE,
+    MONS_WOLF,
+    MONS_WARG,
+    MONS_BEAR,
+    MONS_GRIZZLY_BEAR,
+    MONS_POLAR_BEAR,
+    MONS_BLACK_BEAR,
+    MONS_MERFOLK,
+    MONS_MERMAID,
+    MONS_GIANT_AMOEBA,
+    MONS_GIANT_SLUG,
+    MONS_GIANT_SNAIL,
+    MONS_BORING_BEETLE,
+    MONS_NAGA_MAGE,
+    MONS_NAGA_WARRIOR,
+    MONS_ORC_WARLORD,
+    MONS_DEEP_ELF_SOLDIER,
+    MONS_DEEP_ELF_FIGHTER,
+    MONS_DEEP_ELF_KNIGHT,
+    MONS_DEEP_ELF_MAGE,
+    MONS_DEEP_ELF_SUMMONER,
+    MONS_DEEP_ELF_CONJURER,
+    MONS_DEEP_ELF_PRIEST,
+    MONS_DEEP_ELF_HIGH_PRIEST,
+    MONS_DEEP_ELF_DEMONOLOGIST,
+    MONS_DEEP_ELF_ANNIHILATOR,
+    MONS_DEEP_ELF_SORCERER,
+    MONS_DEEP_ELF_DEATH_MAGE,
+    MONS_BROWN_OOZE,
+    MONS_AZURE_JELLY,
+    MONS_ACID_BLOB,
+    MONS_ROYAL_JELLY,
+// uniques are ok when using steal form (except Boris or Murray)
+    MONS_TERENCE,
+    MONS_JESSICA,
+    MONS_IJYB,
+    MONS_SIGMUND,
+    MONS_BLORK_THE_ORC,
+    MONS_EDMUND,
+    MONS_PSYCHE,
+    MONS_EROLCHA,
+    MONS_DONALD,
+    MONS_URUG,
+    MONS_MICHAEL,
+    MONS_JOSEPH,
+    MONS_SNORG,
+    MONS_ERICA,
+    MONS_JOSEPHINE,
+    MONS_HAROLD,
+    MONS_NORBERT,
+    MONS_JOZEF,
+    MONS_AGNES,
+    MONS_MAUD,
+    MONS_LOUISE,
+    MONS_FRANCIS,
+    MONS_FRANCES,
+    MONS_RUPERT,
+    MONS_WAYNE,
+    MONS_DUANE,
+    MONS_XTAHUA,
+    MONS_NORRIS,
+    MONS_FREDERICK,
+    MONS_MARGERY,
+    MONS_POLYPHEMUS,
+    MONS_DRACONIAN,
+    MONS_BLACK_DRACONIAN,
+    MONS_MOTTLED_DRACONIAN,
+    MONS_YELLOW_DRACONIAN,
+    MONS_GREEN_DRACONIAN,
+    MONS_PURPLE_DRACONIAN,
+    MONS_RED_DRACONIAN,
+    MONS_WHITE_DRACONIAN,
+    MONS_PALE_DRACONIAN,
+    MONS_DRACONIAN_CALLER,
+    MONS_DRACONIAN_MONK,
+    MONS_DRACONIAN_ZEALOT,
+    MONS_DRACONIAN_SHIFTER,
+    MONS_DRACONIAN_ANNIHILATOR,
+    MONS_DRACONIAN_KNIGHT,
+    MONS_DRACONIAN_SCORCHER,
+    MONS_TIAMAT,
+    MONS_DEEP_ELF_BLADEMASTER,
+    MONS_DEEP_ELF_MASTER_ARCHER,
+    MONS_OOZE,
+    MONS_VAULT_GUARD, 
+    MONS_SHINING_EYE,
+    MONS_ORB_GUARDIAN,
+    MONS_GREATER_NAGA,
+    MONS_SPHINX,
+    MONS_CENTAUR_WARRIOR,
+    MONS_YAKTAUR_CAPTAIN,
+    MONS_KILLER_KLOWN,
+    MONS_QUOKKA,
+    MONS_EYE_OF_DEVASTATION, 
+    MONS_MOTH_OF_WRATH,
+    MONS_BOGGART,
+    MONS_QUICKSILVER_DRAGON,
+    MONS_IRON_DRAGON,
+    MONS_GIANT_NEWT,
+    MONS_GIANT_GECKO,
+    MONS_GIANT_IGUANA,
+    MONS_GILA_MONSTER,
+    MONS_KOMODO_DRAGON,
+     // No lava or water monsters (for now)
+};
+
+int _controlled_shapeshift_prompt()
+{
+    char specs[100];
+    mpr( "Which monster by name? ", MSGCH_PROMPT );
+    if (cancelable_get_line(specs, sizeof specs) || !*specs)
+    {
+        canned_msg(MSG_OK);
+        return -2;
+    }
+
+    mons_list mlist;
+    std::string err = mlist.add_mons(specs);
+
+    if (!err.empty())
+    {
+        mpr(err.c_str());
+        return -1;
+    }
+
+    mons_spec mspec = mlist.get_monster(0);
+    if (mspec.mid == -1)
+    {
+        return -1;
+    }
+
+    return mspec.mid;
+}
+
+static bool _valid_shapeshift(int which_mons, int min_hd=1, int max_hd=27)
+{
+    const dungeon_feature_type current_tile = grd[you.x_pos][you.y_pos];
+    int new_mclass = mons_species(which_mons);
+
+    // Various inappropriate polymorph targets.
+    if ((mons_class_holiness( new_mclass ) != MH_NATURAL && mons_genus(which_mons) != MONS_PLANT)
+//        || mons_class_flag( new_mclass, M_NO_EXP_GAIN ) // not helpless
+        || new_mclass == MONS_PROGRAM_BUG
+        || new_mclass == MONS_SHAPESHIFTER
+        || new_mclass == MONS_GLOWING_SHAPESHIFTER
+        || new_mclass == MONS_GIANT_SPORE
+        // These require manual setting of mons.base_monster to indicate
+        // what they are a skeleton/zombie/simulacrum/spectral thing of,
+        // which we currently aren't smart enough to handle.
+        || mons_class_is_zombified(new_mclass)
+
+        // These shouldn't happen anyways (demons unaffected + holiness check),
+        // but if we ever do have polydemon, these will be needed:
+        || new_mclass == MONS_PLAYER_GHOST
+        || new_mclass == MONS_PANDEMONIUM_DEMON
+
+        // Monsters still under development
+        || which_mons == MONS_ROCK_WORM
+        || which_mons == MONS_TRAPDOOR_SPIDER
+
+        // Other poly-unsuitable things.
+        || which_mons == MONS_ORB_GUARDIAN
+        || which_mons == MONS_ORANGE_STATUE
+        || which_mons == MONS_SILVER_STATUE
+        || which_mons == MONS_ICE_STATUE
+        || which_mons >= MONS_GERYON && which_mons <= MONS_ERESHKIGAL
+
+        || new_mclass == mons_species( you.form ) )  // must be different
+    {
+        return (false);
+    }
+
+    if (get_monster_data(which_mons)->hpdice[0] > max_hd
+        || get_monster_data(which_mons)->hpdice[0] < min_hd)
+    {
+        return (false);
+    }
+
+    return (monster_habitable_grid(which_mons, current_tile));
+}
+
+static int _random_form(int min_hd=1, int max_hd=100, form_type type=FORM_RANDOM)
+{
+    const dungeon_feature_type current_tile = grd[you.x_pos][you.y_pos];
+    int form = -1;
+    int count = 0;
+    int rand = random2(214);
+    int dir = coinflip() ? 1 : -1;
+    do
+    {
+        form = ok_forms[rand];
+
+        if (get_monster_data(form)->hpdice[0] <= max_hd
+            && get_monster_data(form)->hpdice[0] >= min_hd
+            && monster_habitable_grid(form, current_tile)
+            && !mons_is_unique(form) // no uniques when selecting random form
+            && you.ok_forms[form]
+            && form != you.form)
+        {
+            if (type == FORM_RANDOM)
+                return form;
+            if (type == FORM_HUMANOID && shapeshifter_can_wear(form, EQ_WEAPON))
+                return form;
+            if (type == FORM_ANIMAL && !shapeshifter_can_wear(form, EQ_WEAPON))
+                return form;
+        }
+
+        if (rand == 0)
+            dir = 1;
+        if (rand == 213)
+            dir = -1;
+        rand += dir;
+        count++;
+    } while (count < 500);
+
+
+    if (type == FORM_HUMANOID)
+        return MONS_GOBLIN;
+    if (type == FORM_ANIMAL)
+        return MONS_RAT;
+    return (coinflip() ? MONS_RAT : MONS_GOBLIN);
+}
+
+int _semi_controlled_shapeshift_prompt(int control_level=0)
+{
+    int form_1 = RANDOM_MONSTER;
+    int form_2 = RANDOM_MONSTER;
+    int form_3 = RANDOM_MONSTER;
+    int form_4 = RANDOM_MONSTER;
+    int form_5 = RANDOM_MONSTER;
+    int form_6 = RANDOM_MONSTER;
+    int form_7 = RANDOM_MONSTER;
+    int form_8 = RANDOM_MONSTER;
+    int form_9 = RANDOM_MONSTER;
+
+    int c;
+    int count;
+
+    form_1 = _random_form(1, you.experience_level, FORM_HUMANOID);
+    form_2 = _random_form(1, you.experience_level, FORM_ANIMAL);
+    form_3 = _random_form(1, you.experience_level, FORM_RANDOM);
+    form_4 = _random_form(1, you.experience_level, FORM_RANDOM);
+    form_5 = _random_form(1, you.experience_level, FORM_RANDOM);
+    form_6 = _random_form(1, you.experience_level, FORM_RANDOM);
+    form_7 = _random_form(1, you.experience_level, FORM_RANDOM);
+    form_8 = _random_form(1, you.experience_level, FORM_RANDOM);
+    form_9 = _random_form(1, you.experience_level, FORM_RANDOM);
+
+    if (control_level > 0)
+    {
+        do
+        {
+            mesclr( true );
+            mpr("Which one you want to be?", MSGCH_PROMPT);
+
+            if (control_level == 1)
+            {
+                mprf(MSGCH_PROMPT, "a: %-22.22s b: %-22.22s",
+                     get_monster_data(form_1)->name,
+                     get_monster_data(form_2)->name);
+            }
+            else
+            {
+                mprf(MSGCH_PROMPT, "a: %-22.22s b: %-22.22s c: %-22.22s",
+                     get_monster_data(form_1)->name,
+                     get_monster_data(form_2)->name,
+                     get_monster_data(form_3)->name);
+            }
+            if (control_level > 2)
+            {
+                mprf(MSGCH_PROMPT, "d: %-22.22s e: %-22.22s f: %-22.22s",
+                     get_monster_data(form_4)->name,
+                     get_monster_data(form_5)->name,
+                     get_monster_data(form_6)->name);
+            }
+            if (control_level > 3)
+            {
+                mprf(MSGCH_PROMPT, "g: %-22.22s h: %-22.22s i: %-22.22s",
+                     get_monster_data(form_7)->name,
+                     get_monster_data(form_8)->name,
+                     get_monster_data(form_9)->name);
+            }
+
+            mprf(MSGCH_PROMPT, "r: random (no magic contamination)");
+            c = getch();
+        }
+        while (c != 'a' && c != 'b' && c != 'r'
+               && (c != 'c' && control_level > 1)
+               && (c != 'd' && c != 'e' && c != 'f' && control_level > 2)
+               && (c != 'g' && c != 'h' && c != 'i' && control_level > 3));
+
+        if (c != 'r')
+            contaminate_player(2 + random2(3), true);
+
+        mesclr( true );
+        if (c == 'a')
+            return form_1;
+        else if (c == 'b')
+            return form_2;
+        else if (c == 'c')
+            return form_3;
+        else if (c == 'd')
+            return form_4;
+        else if (c == 'e')
+            return form_5;
+        else if (c == 'f')
+            return form_6;
+        else if (c == 'g')
+            return form_7;
+        else if (c == 'h')
+            return form_8;
+        else if (c == 'i')
+            return form_9;
+    }
+
+    return RANDOM_MONSTER;
+}
+
 static void _init_equipment_removal(std::set<equipment_type> &rem_stuff,
                                     int which_trans)
 {
@@ -91,6 +533,32 @@
     }
 }
 
+static void _init_shapeshifter_equipment_removal(std::set<equipment_type> &rem_stuff,
+                                                 int which_form)
+{
+
+    if (shapeshifter_can_wear(which_form, EQ_HELMET, false))
+        rem_stuff.erase(EQ_HELMET);
+    if (shapeshifter_can_wear(which_form, EQ_AMULET, false))
+        rem_stuff.erase(EQ_AMULET);
+    if (shapeshifter_can_wear(which_form, EQ_CLOAK, false))
+        rem_stuff.erase(EQ_CLOAK);
+    if (shapeshifter_can_wear(which_form, EQ_GLOVES, false))
+        rem_stuff.erase(EQ_GLOVES);
+    if (shapeshifter_can_wear(which_form, EQ_LEFT_RING, false))
+        rem_stuff.erase(EQ_LEFT_RING);
+    if (shapeshifter_can_wear(which_form, EQ_RIGHT_RING, false))
+        rem_stuff.erase(EQ_RIGHT_RING);
+    if (shapeshifter_can_wear(which_form, EQ_BODY_ARMOUR, false))
+        rem_stuff.erase(EQ_BODY_ARMOUR);
+    if (shapeshifter_can_wear(which_form, EQ_BOOTS, false))
+        rem_stuff.erase(EQ_BOOTS);
+    if (shapeshifter_can_wear(which_form, EQ_SHIELD, false))
+        rem_stuff.erase(EQ_SHIELD);
+    if (shapeshifter_can_wear(which_form, EQ_WEAPON, false))
+        rem_stuff.erase(EQ_WEAPON);
+}
+
 bool remove_equipment(std::set<equipment_type> removed)
 {
     if (removed.find(EQ_WEAPON) != removed.end()
@@ -792,7 +1260,7 @@
 {
     // if more cases are added to this if must also change in
     // item_use for naga barding
-    if (ignore_temporary || !player_is_shapechanged())
+    if (ignore_temporary)
         /* or a transformation which doesn't change overall shape */
     {
         if (use_which == EQ_HELMET)
@@ -816,6 +1284,12 @@
     if (use_which == EQ_GLOVES && you.has_claws(false) >= 3)
         return (false);
 
+    if (!ignore_temporary && player_is_shapeshifted())
+    {
+        return shapeshifter_can_wear(you.form, use_which, true);
+    }
+
+
     if (!ignore_temporary)
     {
         switch (you.attribute[ATTR_TRANSFORMATION])
@@ -898,3 +1372,500 @@
                 || (you.attribute[ATTR_TRANSFORMATION] != TRAN_LICH
                     && you.attribute[ATTR_TRANSFORMATION] != TRAN_STATUE)));
 }
+
+int shapeshifter_control_level()
+{
+    int e_level = you.experience_level;
+
+    if (e_level >= 25)
+        return 5;
+    if (e_level >= 20)
+        return 4;
+    if (e_level >= 15)
+        return 3;
+    if (e_level >= 10)
+        return 2;
+    if (e_level >= 5)
+        return 1;
+
+    return 0;
+}
+
+// return false if shapeshift fails
+bool shapeshift(int which_mons, shapeshift_type cause, bool quiet)
+{
+    int contaminate = 0;
+    int old_form = you.form;
+    int hp_gain = 0;
+    int control_level = shapeshifter_control_level();
+
+    if (you.species != SP_SHAPESHIFTER)
+    {
+        if (!quiet)
+            mpr("You are not a shapeshifter!", MSGCH_ERROR);
+        return (false);
+    }
+
+    if (you.duration[DUR_BERSERKER] > 0)
+    {
+        you.duration[DUR_SHAPESHIFT] = 1;
+        return false;
+    }
+
+    if (cause == SHIFT_ABILITY && which_mons == RANDOM_MONSTER)
+    {
+        which_mons = _semi_controlled_shapeshift_prompt(control_level);
+    }
+
+    if (which_mons == RANDOM_MONSTER) {
+        contaminate = -(1 + random2(3));
+        hp_gain = 1 + random2(you.experience_level);
+    }
+    else
+        contaminate += coinflip() ? 1 : 2;
+
+    if (wearing_amulet(AMU_RESIST_MUTATION) && !one_chance_in(3))
+    {
+        mpr("You shudder.");
+        contaminate_player(coinflip() ? 1 : 2);
+        return (false);
+    }
+
+    you.redraw_evasion = true;
+    you.redraw_armour_class = true;
+    you.wield_change = true;
+
+    if (which_mons != RANDOM_MONSTER)
+    {
+        if (!_valid_shapeshift(which_mons, 1, 100))
+        {
+            mpr("You shudder.");
+            return (false);
+        }
+
+        int target_hd = get_monster_data(which_mons)->hpdice[0];
+
+        if (target_hd > you.experience_level && !one_chance_in(target_hd+1 - you.experience_level))
+        {
+            mprf(MSGCH_WARN, "You fail to change into %s.", (get_monster_data(which_mons))->name);
+            contaminate_player( contaminate, true );
+            return (false);
+        }
+    }
+    else
+    {
+        while (!_valid_shapeshift(which_mons, 1, you.experience_level))
+        {
+            which_mons = _random_form(1, you.experience_level, FORM_RANDOM);
+        }
+    }
+
+    // remove everything
+    equipment_type default_rem[] = { 
+        EQ_WEAPON, EQ_CLOAK, EQ_HELMET, EQ_GLOVES, EQ_BOOTS,
+        EQ_SHIELD, EQ_BODY_ARMOUR, EQ_LEFT_RING, EQ_RIGHT_RING, EQ_AMULET };
+
+    std::set<equipment_type> rem_stuff(default_rem,
+                                       default_rem + ARRAYSZ(default_rem));
+    _init_shapeshifter_equipment_removal(rem_stuff, which_mons);
+
+    if (check_for_cursed_equipment(rem_stuff, true))
+    {
+        mprf(MSGCH_WARN, "You try to change into %s,", (get_monster_data(which_mons))->name);
+        mpr("but your cursed equipment prevents it!", MSGCH_WARN);
+
+        contaminate_player( 1, true );
+        return (false);
+    }
+
+    you.form   = which_mons;
+    you.symbol = mons_char(which_mons);
+    you.colour = mons_class_colour(which_mons);
+
+    if (!you.ok_forms[which_mons])
+        you.ok_forms[which_mons] = true;
+
+    if (mons_genus(you.form) == MONS_PLANT)
+        you.duration[DUR_SHAPESHIFT] = 3 + random2(6);
+    else
+        you.duration[DUR_SHAPESHIFT] = 40 + you.experience_level * 7;
+
+    remove_equipment(rem_stuff);
+
+    if (!quiet)
+    {
+        mprf("You change into %s.", (get_monster_data(which_mons))->name);
+    }
+
+    if (you.attribute[ATTR_HELD] && get_monster_data(you.form)->size >= SIZE_GIANT)
+    {
+        mpr("The net rips apart!");
+        you.attribute[ATTR_HELD] = 0;
+        int net = get_trapping_net(you.x_pos, you.y_pos);
+        if (net != NON_ITEM)
+            destroy_item(net);
+    }
+
+
+    if (_valid_shapeshift(old_form))
+        hp_gain += (get_monster_data(old_form)->hpdice[0] - get_monster_data(which_mons)->hpdice[0]);
+
+    if (hp_gain > 0)
+        inc_hp( hp_gain, false );
+
+    if (you.duration[DUR_POISONING] && player_res_poison() > 0)
+        reduce_poison_player( you.duration[DUR_POISONING]/2 );
+    if (you.duration[DUR_POISONING] && player_res_poison() < 0)
+        poison_player( you.duration[DUR_POISONING] );
+
+
+    if (contaminate != 0)
+        contaminate_player( contaminate, true );
+
+
+    move_player_to_grid(you.x_pos, you.y_pos, false, true, true);
+
+    return (true);
+}                               // end shapeshift()
+
+// return false if shapeshift was cancelled
+bool controlled_shapeshift(shapeshift_type cause)
+{
+    int form = RANDOM_MONSTER;
+
+    do
+    {
+        form = _controlled_shapeshift_prompt();
+
+        if (form == -2)
+            return false;
+
+        if (mons_is_unique(form))
+            form = mons_species(form);
+
+        if (!_valid_shapeshift(form, 1, 100))
+        {
+            mpr("That shapeshift is not possible.");
+            form = RANDOM_MONSTER;
+        }
+        else if (!you.ok_forms[form])
+        {
+            mpr("You can't recall such a form.");
+            form = RANDOM_MONSTER;
+        }
+
+    } while (form == RANDOM_MONSTER);
+
+    shapeshift(form, cause);
+    return true;
+}
+
+bool shapeshifter_can_wear(int form, int eq, bool special_wear)
+{
+    int species = mons_species(form);
+    int genus = mons_genus(form);
+    int items_used = get_monster_data(form)->gmon_use;
+
+    if (items_used == MONUSE_EATS_ITEMS) // Jellies don't use anything
+        return false;
+
+    if (items_used >= MONUSE_STARTING_EQUIPMENT && eq == EQ_WEAPON)
+        return true;
+
+    // If you can equip weapon, you can use jewelry
+    if (items_used >= MONUSE_STARTING_EQUIPMENT)
+    {
+        if (eq == EQ_AMULET || eq == EQ_RIGHT_RING || eq == EQ_LEFT_RING)
+            return true;
+    }
+
+    if (eq == EQ_AMULET && genus != MONS_SNAKE && genus != MONS_BUTTERFLY
+                        && genus != MONS_GIANT_EYEBALL && genus != MONS_GIANT_MOSQUITO)
+    {
+        return true;
+    }
+
+    switch (genus)
+    {
+        case MONS_HUMAN:
+        case MONS_ELF:
+        case MONS_ORC:
+        case MONS_KOBOLD:
+        case MONS_GOBLIN:
+        case MONS_MERFOLK:
+        case MONS_MERMAID:
+        case MONS_GNOLL:
+            return true;
+        case MONS_MINOTAUR:
+            if (eq == EQ_HELMET && !special_wear)
+                return false;
+            return true;
+        case MONS_NAGA:
+        case MONS_CENTAUR:
+            if (eq == EQ_BOOTS && !special_wear)
+                return false;
+            return true;
+        case MONS_DRACONIAN:
+        case MONS_TROLL:
+        case MONS_OGRE:
+        case MONS_HILL_GIANT:
+            if (!special_wear && (eq == EQ_HELMET || eq == EQ_BODY_ARMOUR || eq == EQ_SHIELD))
+                return false;
+            if (eq == EQ_GLOVES || eq == EQ_BOOTS)
+                return false;
+            return true;
+        case MONS_UGLY_THING:
+        case MONS_ICE_BEAST:
+            if (eq == EQ_CLOAK)
+                return true;
+        case MONS_DRAGON:
+        case MONS_WYVERN:
+        case MONS_RAT:
+        case MONS_QUOKKA:
+        case MONS_WOLF_SPIDER:
+        case MONS_SCORPION:
+        case MONS_GIANT_MITE:
+            if (eq == EQ_LEFT_RING || eq == EQ_RIGHT_RING)
+                return true;
+            return false;
+        default:
+            break;
+    }
+
+    return false;
+}
+
+int shapeshifter_get_damage_brand(int attack)
+{
+    int ret = SPWPN_NORMAL;
+
+        mon_attack_flavour flav = get_monster_data(you.form)->attack[0].flavour;
+        switch (flav)
+        {
+        case AF_POISON:
+        case AF_POISON_NASTY:
+        case AF_POISON_MEDIUM:
+        case AF_POISON_STRONG:
+        case AF_POISON_STR:
+        //case AF_DISEASE:
+            ret = SPWPN_VENOM;
+            break;
+        case AF_PARALYSE:
+            ret = SPWPN_PARALYSE;
+            break;
+        case AF_ACID:
+            ret = SPWPN_ACID;
+            break;
+        case AF_COLD:
+            ret = SPWPN_FREEZING;
+            break;
+        case AF_FIRE:
+            ret = SPWPN_FLAMING;
+            break;
+        case AF_ELEC:
+            ret = SPWPN_ELECTROCUTION;
+            break;
+        case AF_CONFUSE:
+            ret = SPWPN_CONFUSE;
+            break;
+        case AF_DRAIN_XP:
+        case AF_DRAIN_DEX:
+        case AF_DRAIN_STR:
+        case AF_ROT:
+            ret = SPWPN_DRAINING;
+            break;
+        case AF_VAMPIRIC:
+            ret = SPWPN_VAMPIRICISM;
+            break;
+        case AF_KLOWN:
+            switch (random2(6))
+            {
+            case 0: ret = SPWPN_VENOM; break;
+            case 1: ret = SPWPN_CONFUSE; break;
+            case 2: ret = SPWPN_DRAINING; break;
+            case 3: ret = SPWPN_FLAMING; break;
+            case 4: ret = SPWPN_FREEZING; break;
+            case 5: ret = SPWPN_ACID; break;
+            }
+            break;
+        case AF_DISTORT:
+            ret = SPWPN_DISTORTION;
+            break;
+        default:
+            ret = SPWPN_NORMAL;
+            break;
+        }
+
+    return ret;
+}
+
+// return false if aborted
+bool steal_form()
+{
+    struct dist targ;
+    struct bolt beam;
+
+    const char *prompt = "Who do you want to be?";
+
+    if (!spell_direction(targ, beam, DIR_TARGET, TARG_ANY, false, true,
+                         false, prompt,
+                         true))
+    {
+        return (false);
+    }
+
+    if (targ.isMe)
+    {
+        mpr( "Sorry, this ability works on others only." );
+        return (false);
+    }
+
+    if (!targ.isValid || mgrd[targ.tx][targ.ty] == NON_MONSTER
+        || !player_monster_visible(&env.mons[mgrd[targ.tx][targ.ty]]))
+    {
+        mpr("Yeah, whatever.");
+        return (false);
+    }
+
+    int mons = mgrd[targ.tx][targ.ty];
+    if (shapeshift(menv[mons].type))
+    {
+        if (mons_intel(menv[mons].type) > I_ANIMAL && coinflip())
+        {
+            menv[mons].add_ench(mon_enchant(ENCH_CONFUSION, 0, KC_YOU));
+            mprf("%s seems confused.", menv[mons].name(DESC_CAP_THE).c_str());
+        }
+    }
+
+    return true;
+}
+
+bool eat_items()
+{
+    int  item = NON_ITEM;
+    int  hps_gained = 0;
+    int  max_eat    = roll_dice( 1, 10 );
+    int  eaten      = 0;
+    bool eaten_net  = false;
+
+    for (item = igrd[you.x_pos][you.y_pos];
+         item != NON_ITEM && eaten < max_eat && hps_gained < 50;
+         item = mitm[item].link)
+    {
+        int quant = mitm[item].quantity;
+
+        if (!is_item_jelly_edible(mitm[item]))
+            continue;
+
+        if (mitm[igrd[you.x_pos][you.y_pos]].base_type != OBJ_GOLD)
+        {
+            if (quant > max_eat - eaten)
+                quant = max_eat - eaten;
+
+            hps_gained += (quant * item_mass( mitm[item] )) / 20 + quant;
+            eaten += quant;
+
+            if (you.attribute[ATTR_HELD])
+            {
+                //mpr("The net rips apart!");
+                you.attribute[ATTR_HELD] = 0;
+                int net = get_trapping_net(you.x_pos, you.y_pos);
+                if (net != NON_ITEM)
+                    destroy_item(net);
+                eaten_net = true;
+            }
+        }
+        else
+        {
+            // shouldn't be much trouble to digest a huge pile of gold!
+            if (quant > 500)
+                quant = 500 + roll_dice( 2, (quant - 500) / 2 );
+
+            hps_gained += quant / 10 + 1;
+            eaten++;
+        }
+
+        if (quant >= mitm[item].quantity)
+            item_was_destroyed(mitm[item], NON_MONSTER);
+
+        dec_mitm_item_quantity( item, quant );
+    }
+
+    if (eaten)
+    {
+        if (hps_gained < 1)
+            hps_gained = 1;
+        else if (hps_gained > 50)
+            hps_gained = 50;
+
+        inc_hp(hps_gained, false);
+        lessen_hunger(10 * hps_gained, true);
+
+        if (!silenced(you.x_pos, you.y_pos))
+        {
+            mpr("SLURP!");
+            noisy(25, you.x_pos, you.y_pos);
+        }
+        if (eaten_net)
+            mpr("You devour the net!");
+
+        // TODO? splitting. tame jellies, until you change your form...
+        return (true);
+    }
+    else
+        mpr("There's nothing to eat here!");
+
+    return (false);
+}
+
+bool shapeshifter_can_butcher_barehanded(int form)
+{
+    if (player_is_shapeshifted())
+    {
+        for (int a=0; a<4; a++)
+        {
+            // at least these monsters have claws
+            if (get_monster_data(form)->attack[a].type == AT_CLAW)
+                return true;
+        }
+
+        switch (mons_genus(form))
+        {
+        case MONS_GIANT_ANT:
+        case MONS_HOUND:
+            return true;
+        default:
+            break;
+        }
+
+        return false;
+    }
+
+    return false;
+}
+
+bool shapeshifter_armoured(int form)
+{
+    bool arm = false;
+
+    // general rule
+    if (get_monster_data(form)->AC > get_monster_data(form)->ev)
+        arm = true;
+
+    // and few exceptions
+    switch(mons_genus(form))
+    {
+    case MONS_GIANT_EYEBALL:
+    case MONS_JELLY:
+    case MONS_PLANT:
+    case MONS_YAK:
+    case MONS_YAKTAUR:
+        arm = false;
+        break;
+    default:
+        break;
+    }
+
+    return arm;
+}
+
diff -urN source.orig/transfor.h source/transfor.h
--- source.orig/transfor.h	2008-08-01 02:00:58.000000000 +0300
+++ source/transfor.h	2008-09-28 23:21:20.000000000 +0300
@@ -34,6 +34,23 @@
     NUM_TRANSFORMATIONS                // must remain last member {dlb}
 };
 
+enum form_type
+{
+    FORM_NONE,
+    FORM_HUMANOID,  // can wear some armours and use item (human, gnoll, ogre...)
+    FORM_ANIMAL,
+    FORM_RANDOM
+};
+
+enum shapeshift_type
+{
+    SHIFT_NONE,
+    SHIFT_ABILITY,     // deliberate use of shapeshift ability
+    SHIFT_TIMEOUT,
+    SHIFT_MUTATION,    // mutation (potions, wands...)
+    SHIFT_FORCED
+};
+
 bool transform_can_butcher_barehanded(transformation_type tt);
 
 // last updated 12may2000 {dlb}
@@ -69,4 +86,17 @@
 
 bool transform_changed_physiology( bool phys_scales = false );
 
+
+bool shapeshift(int which_mons, shapeshift_type cause=SHIFT_ABILITY, bool quiet=false);
+bool controlled_shapeshift(shapeshift_type cause=SHIFT_ABILITY);
+bool shapeshifter_can_wear(int form, int eq, bool special_wear=false);
+bool shapeshifter_can_use_items(void);
+bool shapeshifter_can_butcher_barehanded(int form);
+int shapeshifter_control_level(void);
+bool shapeshifter_armoured(int form);
+
+int shapeshifter_get_damage_brand(int attack);
+bool steal_form(void);
+bool eat_items(void);
+
 #endif

