Showing posts with label software. Show all posts
Showing posts with label software. Show all posts

Microsoft Has Nerve

I just directly and personally witnessed the Automatic Updates system of Windows 10, flashing a computer's firmware without getting any kind of permission for that specific action from the user or system administrator.

The PC in question is a recent-model (less than three months old) Dell desktop, though I'm not sure whether this fact is significant.

I'm not going to explain the implications in detail, because if you don't already have a pretty good idea, you likely wouldn't understand the explanation either.

I don't happen to know whether the motherboard on this model has a backup firmware chip, in case of power outage during flash. Hopefully it should, because that is a reasonably common feature these days. But I don't feel like opening the case to verify it at the moment. Nonetheless, I'm going to give Microsoft the benefit of every doubt here and tentatively assume that surely they are only doing this on systems that have that safeguard. (Not because of virtue, but because of the potential for really nasty bad publicity.)

The stated reason (which was displayed on screen as the flash was happening) is, of course, security; but Microsoft cares about your security like Apple cares about your budget. With that said, I looked at the firmware setup after the flash occurred, and it doesn't seem any more egregious than the usual UEFI setup. I'm tempted to put a small Devuan install on the thing, even though it isn't needed on this system, just to verify that it works as expected; but I don't see any indications in the firmware setup that would suggest a problem in that regard, at this time. Secure Boot is enabled, but it appears to still be possible to disable it. So if this is a major power grab or exclusivity lock, I'm missing something.

But I sure don't like the precedent.

Nice error message, Microsoft

We're sorry, but your password couldn't be changed. Code: 0x8007052d

What does this mean? It took me a bit of experimentation, but I figured it out. Windows Ten has, believe it or not, a maximum password length. (That's not a typo. I don't mean a minimum. That would actually make sense.) It has two maximum password lengths, actually.

The above error message is what you get if you exceed the longer length limit of 63, when changing the password at some point after the account is set up or, at any rate, after the computer is set up. (I haven't tested what the rules are for additional accounts you create later.)

So what's the shorter limit? 20. When you turn on your computer for the first time and create an account and enter a password, you must enter a password that is 20 characters or shorter. There's nothing on the account creation screen to indicate this, and no warning if you exceed it. Everything seems to go fine, in fact, until you later try to log in, at which point you can't. (This can, of course, be solved in the usual ways. So much fun.)

Now if you'll excuse me, I have an urgent need to go install a real operating system. Immediately.

ENABLE_EXTENDED_FLAGS undeclared

Ok, so I acquire an older-model computer (Pentium4, 2GB of RAM), and of course it comes with Windows XP installed. What's one of the first things I try to do with it?

What would a normal person try to do with it? Maybe watch some YouTube videos, or check Yahoo Mail, something like that? Facebook?

I tried to do this:

I my defense, I already have a computer, which in the first place is several years newer (multi-core, 8GB of RAM about to be increased to 16 next time I'm willing to reboot it) and, additionally, runs a much better operating system. So if I wanted to do just regular stuff on the computer, I could do it on my main system. I got this other, older system, and my thinking is, while it still has the default Windows install on it, is there anything I want to try doing in Windows? And in that context, this is the answer I came up with: Didn't somebody on IRC say that building NetHack4 on Windows is broken right now? Maybe I should try that.

Convict Role patch 0.7 ported to NH4

As anyone who follows rgrn knows, Karl Garrison has created a patch for NetHack 3.4.3, which adds a Convict role. Meanwhile, some other people have been working on a fork, called "NetHack4" (which can be easily downloaded here).

I have now ported the Convict role patch to work with NetHack4. For lack of a better place to put it, here it is:

diff -Nurd nitrohack-ais523//libnethack/dat/CMakeLists.txt nitrohack-ais523-convict//libnethack/dat/CMakeLists.txt
--- nitrohack-ais523//libnethack/dat/CMakeLists.txt 2012-04-25 16:53:48.000000000 -0400
+++ nitrohack-ais523-convict//libnethack/dat/CMakeLists.txt 2012-04-27 11:44:41.000000000 -0400
@@ -6,6 +6,7 @@
     ${LNH_DAT}/bigroom.des
     ${LNH_DAT}/castle.des
     ${LNH_DAT}/Caveman.des
+    ${LNH_DAT}/Convict.des
     ${LNH_DAT}/endgame.des
     ${LNH_DAT}/gehennom.des
     ${LNH_DAT}/Healer.des
@@ -35,29 +36,30 @@
     Bar-loca.lev Bar-strt.lev bigrm-1.lev  bigrm-2.lev
     bigrm-3.lev  bigrm-4.lev  bigrm-5.lev  castle.lev
     Cav-fila.lev Cav-filb.lev Cav-goal.lev Cav-loca.lev
-    Cav-strt.lev earth.lev    fakewiz1.lev fakewiz2.lev
-    fire.lev     Hea-fila.lev Hea-filb.lev Hea-goal.lev
-    Hea-loca.lev Hea-strt.lev juiblex.lev  Kni-fila.lev
-    Kni-filb.lev Kni-goal.lev Kni-loca.lev Kni-strt.lev
-    knox.lev     medusa-1.lev medusa-2.lev minefill.lev
-    minend-1.lev minend-2.lev minend-3.lev minetn-1.lev
-    minetn-2.lev minetn-3.lev minetn-4.lev minetn-5.lev
-    minetn-6.lev minetn-7.lev Mon-fila.lev Mon-filb.lev
-    Mon-goal.lev Mon-loca.lev Mon-strt.lev oracle.lev
-    orcus.lev    Pri-fila.lev Pri-filb.lev Pri-goal.lev
-    Pri-loca.lev Pri-strt.lev Ran-fila.lev Ran-filb.lev
-    Ran-goal.lev Ran-loca.lev Ran-strt.lev Rog-fila.lev
-    Rog-filb.lev Rog-goal.lev Rog-loca.lev Rog-strt.lev
-    Sam-fila.lev Sam-filb.lev Sam-goal.lev Sam-loca.lev
-    Sam-strt.lev sanctum.lev  soko1-1.lev  soko1-2.lev
-    soko2-1.lev  soko2-2.lev  soko3-1.lev  soko3-2.lev
-    soko4-1.lev  soko4-2.lev  Tou-fila.lev Tou-filb.lev
-    Tou-goal.lev Tou-loca.lev Tou-strt.lev tower1.lev
-    tower2.lev   tower3.lev   Val-fila.lev Val-filb.lev
-    Val-goal.lev valley.lev   Val-loca.lev Val-strt.lev
-    water.lev    wizard1.lev  wizard2.lev  wizard3.lev
-    Wiz-fila.lev Wiz-filb.lev Wiz-goal.lev Wiz-loca.lev
-    Wiz-strt.lev
+    Cav-strt.lev Con-strt.lev Con-loca.lev Con-goal.lev
+    Con-fila.lev Con-filb.lev earth.lev    fakewiz1.lev
+    fakewiz2.lev fire.lev     Hea-fila.lev Hea-filb.lev
+    Hea-goal.lev Hea-loca.lev Hea-strt.lev juiblex.lev 
+    Kni-fila.lev Kni-filb.lev Kni-goal.lev Kni-loca.lev
+    Kni-strt.lev knox.lev     medusa-1.lev medusa-2.lev
+    minefill.lev minend-1.lev minend-2.lev minend-3.lev
+    minetn-1.lev minetn-2.lev minetn-3.lev minetn-4.lev
+    minetn-5.lev minetn-6.lev minetn-7.lev Mon-fila.lev
+    Mon-filb.lev Mon-goal.lev Mon-loca.lev Mon-strt.lev
+    oracle.lev   orcus.lev    Pri-fila.lev Pri-filb.lev
+    Pri-goal.lev Pri-loca.lev Pri-strt.lev Ran-fila.lev
+    Ran-filb.lev Ran-goal.lev Ran-loca.lev Ran-strt.lev
+    Rog-fila.lev Rog-filb.lev Rog-goal.lev Rog-loca.lev
+    Rog-strt.lev Sam-fila.lev Sam-filb.lev Sam-goal.lev
+    Sam-loca.lev Sam-strt.lev sanctum.lev  soko1-1.lev 
+    soko1-2.lev  soko2-1.lev  soko2-2.lev  soko3-1.lev
+    soko3-2.lev  soko4-1.lev  soko4-2.lev  Tou-fila.lev
+    Tou-filb.lev Tou-goal.lev Tou-loca.lev Tou-strt.lev
+    tower1.lev   tower2.lev   tower3.lev   Val-fila.lev
+    Val-filb.lev Val-goal.lev valley.lev   Val-loca.lev
+    Val-strt.lev water.lev    wizard1.lev  wizard2.lev 
+    wizard3.lev  Wiz-fila.lev Wiz-filb.lev Wiz-goal.lev
+    Wiz-loca.lev Wiz-strt.lev
     )
 
 set (NHDAT_SRC dungeon quest.dat rumors oracles ${COMPILED_LEVELS} history data)
diff -Nurd nitrohack-ais523//libnethack/dat/Convict.des nitrohack-ais523-convict//libnethack/dat/Convict.des
--- nitrohack-ais523//libnethack/dat/Convict.des 1969-12-31 19:00:00.000000000 -0500
+++ nitrohack-ais523-convict//libnethack/dat/Convict.des 2012-04-27 09:37:17.000000000 -0400
@@ -0,0 +1,411 @@
+#
+# The "start" level for the quest.
+#
+# Here you meet your (besieged) class leader, Robert the Lifer
+# and receive your quest assignment.
+#
+MAZE:"Con-strt",' '
+FLAGS:noteleport,hardfloor
+GEOMETRY:left,top
+#123456789012345678901234567890123456789012345678901234567890123456789012345
+MAP
+|--------------------------------------------------------|-----------------|
+|....|...|...|...|...|...|...|...|...|...|...|...|...|...|................K|
+|....|...|...|...|...|...|...|...|...|...|...|...|...|...|.................|
+|---.---.---.---.---.---.---.---.---.---.---.---.---.---.|.................|
+|..........................................................................|
+|..........................................................................|
+|....|.---.---.---.---.---.---.---.---.---.---.---.---.--------------..----|
+|....|...|...|...|...|...|...|...|...|...|...|...|...|...|.................|
+|....|...|...|...|...|...|...|...|...|...|...|...|...|...|.................|
+|....|---------------------------------------------------|.................|
+|....|...|...|...|...|...|...|...|...|...|...|...|...|...|.................|
+|....|...|...|...|...|...|...|...|...|...|...|...|...|...|.................|
+|....|--.---.---.---.---.---.---.---.---.---.---.---.---.|-----------------|
+|..........................................................................|
+|..........................................................................|
+|..........................................................................|
+|.----.---.---.---.---.---.---.---.---.---.---.---.---.--|.................|
+|....|...|...|...|...|...|...|...|...|...|...|...|...|...|.................|
+|....|...|...|...|...|...|...|...|...|...|...|...|...|...|.................|
+|--------------------------------------------------------|-----------------|
+ENDMAP
+# Dungeon Description
+REGION:(00,00,75,19),lit,"ordinary"
+# Stairs
+STAIR:(64,08),down
+# Portal arrival point
+BRANCH:(71,03,71,03),(0,0,0,0)
+# Altar
+ALTAR:(70,16),chaos,shrine
+# Robert the Lifer
+MONSTER:'@',"Robert the Lifer",(74,18)
+# fellow prisoners
+MONSTER:'@',"inmate",random
+MONSTER:'@',"inmate",random
+MONSTER:'@',"inmate",random
+MONSTER:'@',"inmate",random
+MONSTER:'@',"inmate",random
+MONSTER:'@',"inmate",random
+# Corrupt guards
+MONSTER[50%]:'@',"prison guard",random
+MONSTER[50%]:'@',"prison guard",random
+MONSTER[50%]:'@',"prison guard",random
+MONSTER[50%]:'@',"prison guard",random
+MONSTER[50%]:'@',"prison guard",random
+MONSTER[50%]:'@',"prison guard",random
+# Good `ol mimics
+MONSTER:'m',"giant mimic", (74,05), m_feature "staircase up"
+# Random traps
+TRAP:"web",random
+TRAP:"web",random
+TRAP:"web",random
+TRAP:"web",random
+# Prison debris
+OBJECT[75%]: '_', "iron chain", random
+OBJECT[75%]: '_', "iron chain", random
+OBJECT[75%]: '_', "iron chain", random
+OBJECT[75%]: '_', "iron chain", random
+OBJECT[75%]: '_', "iron chain", random
+OBJECT[50%]: '0', "heavy iron ball", random
+OBJECT[50%]: '0', "heavy iron ball", random
+# Non diggable walls
+NON_DIGGABLE:(00,00,75,19)
+
+#
+# The "locate" level for the quest.
+#
+# Here you have to find the exit from the Royal Dungeons
+# to go further towards your assigned quest.
+#
+
+MAZE:"Con-loca",' '
+FLAGS:noteleport,hardfloor
+GEOMETRY:left,top
+#123456789012345678901234567890123456789012345678901234567890123456789012345
+MAP
+---------------                                              ---------------
+|.....F.......| #################################            |............K|
+|.....F.......+##              #                #############+.............|
+|.....F.......|                #                #            |.............|
+|.....+.......|                #                #            |.............|
+|.....F.......|                #                #            |.............|
+|.....F.......|              ###                #            |.............|
+|.....F.......|             ##                  #            |FFFFFFFFFFFFF|
+|--------------     #########                   #            |......|......|
+                    #      |S-------|           #            |......|......|
+                    #      |........|      ######            |-------------|
+    #################      |........|      #    H                           
+    #                      |........+#######    #                           
+    #                      |----S---|           #            |----------|   
+ |--+-------|                   #               #####        |..........|   
+ |..........|                   #               #   #        |..........|   
+ |..........|                   #     |------|  #   #########+..........|   
+ |..........|                   ######S......+###            |..........|   
+ |----------|                         |......|               |----------|   
+                                      |------|                              
+ENDMAP
+# Dungeon Description
+REGION:(01,01,13,07),lit,"ordinary"
+REGION:(02,15,11,17),lit,"ordinary"
+REGION:(62,01,74,09),lit,"ordinary"
+REGION:(62,14,71,17),lit,"shop"
+REGION:(28,10,35,12),lit,"barracks"
+REGION:(39,17,44,18),lit,"ordinary"
+# Stairs
+STAIR:(02,03),up
+STAIR:(39,17),down
+# Non diggable walls
+NON_DIGGABLE:(00,00,75,19)
+DOOR:locked,(04,14)
+DOOR:locked,(06,04)
+DOOR:open,(14,02)
+DOOR:closed,(61,16)
+DOOR:closed,(45,17)
+DOOR:locked,(36,12)
+DOOR:closed,(61,02)
+# Police station kops
+MONSTER: '@',"inmate",(63,08)
+MONSTER: '@',"inmate",(70,08)
+MONSTER: 'K',"Keystone Kop",(62,01),asleep
+MONSTER: 'K',"Keystone Kop",(65,01),asleep
+MONSTER: 'K',"Keystone Kop",(66,01),asleep
+MONSTER: 'K',"Keystone Kop",(74,02),asleep
+MONSTER: 'K',"Kop Sergeant",(63,03),asleep
+MONSTER: 'K',"Kop Sergeant",(72,03),asleep
+MONSTER: 'K',"Kop Lieutenant",(71,04),asleep
+MONSTER: 'K',"Kop Lieutenant",(68,05),asleep
+MONSTER: 'K',"Kop Kaptain",(64,06),asleep
+# Iron golem in supply room
+MONSTER: ''',"iron golem",(11,17),asleep
+# Supply room junk
+OBJECT: '_', "iron chain", (2,15)
+OBJECT: '_', "iron chain", (2,15)
+OBJECT: '_', "iron chain", (3,15)
+OBJECT: '_', "iron chain", (3,15)
+OBJECT: '_', "iron chain", (4,15)
+OBJECT: '0', "heavy iron ball", (4,15)
+OBJECT: '_', "iron chain", (4,15)
+OBJECT: '_', "iron chain", (5,15)
+OBJECT: '_', "iron chain", (5,15)
+OBJECT: '_', "iron chain", (6,15)
+OBJECT: '_', "iron chain", (6,15)
+OBJECT: '_', "iron chain", (6,15)
+OBJECT: '_', "iron chain", (7,15)
+OBJECT: '0', "heavy iron ball", (7,15)
+OBJECT: '_', "iron chain", (8,15)
+OBJECT: '_', "iron chain", (8,15)
+OBJECT: '_', "iron chain", (9,15)
+OBJECT: '_', "iron chain", (9,15)
+OBJECT: '_', "iron chain", (9,15)
+OBJECT: '_', "iron chain", (10,15)
+OBJECT: '_', "iron chain", (10,15)
+OBJECT: '_', "iron chain", (10,15)
+OBJECT: '_', "iron chain", (11,15)
+OBJECT: '_', "iron chain", (11,15)
+OBJECT: '0', "heavy iron ball", (11,15)
+OBJECT: '_', "iron chain", (2,16)
+OBJECT: '0', "heavy iron ball", (2,16)
+OBJECT: '_', "iron chain", (3,16)
+OBJECT: '_', "iron chain", (3,16)
+OBJECT: '_', "iron chain", (3,16)
+OBJECT: '_', "iron chain", (4,16)
+OBJECT: '_', "iron chain", (4,16)
+OBJECT: '_', "iron chain", (5,16)
+OBJECT: '_', "iron chain", (6,16)
+OBJECT: '_', "iron chain", (6,16)
+OBJECT: '_', "iron chain", (6,16)
+OBJECT: '_', "iron chain", (7,16)
+OBJECT: '_', "iron chain", (7,16)
+OBJECT: '0', "heavy iron ball", (7,16)
+OBJECT: '_', "iron chain", (7,16)
+OBJECT: '_', "iron chain", (8,16)
+OBJECT: '_', "iron chain", (8,16)
+OBJECT: '_', "iron chain", (9,16)
+OBJECT: '_', "iron chain", (9,16)
+OBJECT: '_', "iron chain", (9,16)
+OBJECT: '_', "iron chain", (10,16)
+OBJECT: '_', "iron chain", (10,16)
+OBJECT: '_', "iron chain", (11,16)
+OBJECT: '_', "iron chain", (2,17)
+OBJECT: '_', "iron chain", (3,17)
+OBJECT: '_', "iron chain", (3,17)
+OBJECT: '_', "iron chain", (3,17)
+OBJECT: '_', "iron chain", (4,17)
+OBJECT: '0', "heavy iron ball", (4,17)
+OBJECT: '_', "iron chain", (4,17)
+OBJECT: '_', "iron chain", (4,17)
+OBJECT: '0', "heavy iron ball", (4,17)
+OBJECT: '_', "iron chain", (5,17)
+OBJECT: '_', "iron chain", (6,17)
+OBJECT: '_', "iron chain", (7,17)
+OBJECT: '_', "iron chain", (7,17)
+OBJECT: '_', "iron chain", (7,17)
+OBJECT: '_', "iron chain", (8,17)
+OBJECT: '0', "heavy iron ball", (8,17)
+OBJECT: '_', "iron chain", (9,17)
+OBJECT: '_', "iron chain", (9,17)
+OBJECT: '0', "heavy iron ball", (9,17)
+OBJECT: '_', "iron chain", (9,17)
+OBJECT: '_', "iron chain", (10,17)
+OBJECT: '_', "iron chain", (10,17)
+OBJECT: '_', "iron chain", (10,17)
+OBJECT: '0', "heavy iron ball", (10,17)
+OBJECT: '_', "iron chain", (11,17)
+
+
+#
+# The "goal" level for the quest.
+#
+# Here you meet Warden Arianna, your nemesis monster.  You have to
+# defeat Warden Arianna in combat to gain the artifact you have
+# been assigned to retrieve.
+#
+
+MAZE:"Con-goal",' '
+FLAGS:hardfloor
+GEOMETRY:left,top
+#123456789012345678901234567890123456789012345678901234567890123456789012345
+MAP
+---------------------------------------------------------
+|...|        |............................|             |
+|...|       --............................|             |
+|...|       |.............................|             |
+|...|      --.............................|             |
+|...|      |..............................|             |
+|...|     --...................L..........|             |
+|...|     |....................L..........|             |
+|...|    --...................L.L.........|             |
+|...|    |..............L.L.L.L.L.L.L.L...|             |
+|...|-----................L..L...L..L.....|             |
+|...........................L.L.L.L.......|             |
+|...........................L.L.L.L.......|             |
+|..........................L.L...L.L......|             |
+|.........................................|-------------|
+|.........................................|.............|
+|.........................................S.............|
+|.........................................|.............|
+|.........................................|.............|
+---------------------------------------------------------
+ENDMAP
+# Dungeon Description
+REGION:(00,00,56,19),lit,"ordinary"
+# Stairs
+STAIR:(02,02),up
+# Non diggable walls
+NON_DIGGABLE:(00,00,56,19)
+# Random traps
+TRAP:"fire",random
+TRAP:"fire",random
+TRAP:"fire",random
+TRAP:"fire",random
+TRAP:"fire",random
+TRAP:"fire",random
+# Lava demons
+MONSTER: '&',"lava demon",(30,05),hostile,awake
+MONSTER: '&',"lava demon",(23,09),hostile,awake
+MONSTER: '&',"lava demon",(39,09),hostile,awake
+MONSTER: '&',"lava demon",(36,14),hostile,awake
+MONSTER: '&',"lava demon",(26,14),hostile,awake
+# Elite guard
+MONSTER: ''',"iron golem",(04,13),hostile
+# Objects
+OBJECT:'0',"heavy iron ball",(31,10),blessed,0,"The Iron Ball of Liberation"
+OBJECT:'(',"chest",(55,18),blessed,2
+# Warden Arianna
+MONSTER:'@',"Warden Arianna",(31,10),hostile
+
+#
+#       The "fill" levels for the quest.
+#
+#       These levels are used to fill out any levels not occupied by specific
+#       levels as defined above. "filla" is the upper filler, between the
+#       start and locate levels, and "fillb" the lower between the locate
+#       and goal levels.
+#
+
+MAZE:"Con-fila",' '
+FLAGS:hardfloor
+GEOMETRY:left,top
+#123456789012345678901234567890123456789012345678901234567890123456789012345
+MAP
+----------------------------------------------------------------------------
+|....................---------.......................---------.............|
+|....................F...|...|.......................F...|...|.............|
+|....................|...|...F.......................|...|...F.............|
+|....|---|---|.......|---|---|.......|---|---|.......|---|---|.............|
+|....F...|...|.......F...|...|.......F...|...|.......F...|...|.............|
+|....|...|...F.......|...|...F.......|...|...F.......|...|...F.............|
+|....|---|---|.......|---|---|.......|---|---|.......|---|---|.............|
+|....F...|...|.......F...|...|.......F...|...|.......F...|...|.............|
+|....|...|...F.......|...|...F.......|...|...F.......|...|...F.............|
+|....|---|---|.......|---|---|.......|---|---|.......|---|---|.............|
+|........|...|.......F...|...|.......F...|...|.......F...|...|.............|
+|....|...|...F.......|...|...F.......|...|...F.......|...|...F.............|
+|....|---|---|.......|---|---|.......|---|---|.......|---|---|.............|
+|....F...|...|.......F...|...|.......F...|...|.......F...|...|.............|
+|....|...|...F.......|...|...........|...|...F.......|...|...F.............|
+|....|---|---|.......|---|---|.......|---|---|.......|---|---|.............|
+|........|...|.......................F...|...|.............................|
+|....|...|...F.......................|...|...F.............................|
+|--------------------------------------------------------------------------|
+ENDMAP
+# Dungeon Description
+REGION:(00,00,75,19),lit,"ordinary"
+# Stairs
+STAIR:(74,03),up
+STAIR:(03,17),down
+# Non diggable walls
+NON_DIGGABLE:(00,00,75,19)
+# "Regular" prisoners
+MONSTER:'@',"inmate",(59,02)
+MONSTER:'@',"inmate",(55,08)
+MONSTER:'@',"inmate",(43,14)
+MONSTER:'@',"inmate",(38,05)
+MONSTER:'@',"inmate",(27,02)
+MONSTER:'@',"inmate",(23,08)
+MONSTER:'@',"inmate",(11,14)
+MONSTER:'@',"inmate",(06,05)
+# Undead prisoners
+MONSTER:' ',"ghost",(42,17),"Orzo the Inmate"
+MONSTER[50%]:' ',"ghost",(40,18),"Fredgar the Inmate"
+MONSTER[50%]:' ',"ghost",(06,12),"Rastilon the Inmate"
+MONSTER:'Z',"skeleton",(28,15),awake
+# Bugs and snakes
+MONSTER:'S',"pit viper",(06,17)
+MONSTER:'x',"xan",random
+# Corrupt guards
+MONSTER[50%]:'@',"prison guard",random
+MONSTER[50%]:'@',"prison guard",random
+MONSTER[50%]:'@',"prison guard",random
+MONSTER[50%]:'@',"prison guard",random
+# Random traps
+TRAP:"web",random
+TRAP:"web",random
+TRAP:"web",random
+TRAP:"web",random
+# Prison debris
+OBJECT[75%]: '_', "iron chain", random
+OBJECT[75%]: '_', "iron chain", random
+OBJECT[75%]: '_', "iron chain", random
+OBJECT[75%]: '_', "iron chain", random
+OBJECT[75%]: '_', "iron chain", random
+OBJECT[50%]: '0', "heavy iron ball", random
+OBJECT[50%]: '0', "heavy iron ball", random
+
+
+MAZE:"Con-filb",' '
+FLAGS:hardfloor
+INIT_MAP: '#' , ' ', true, true, unlit, true
+MESSAGE: "This appears to be a prison level that is still under construction"
+NOMAP
+#
+STAIR: random, up
+STAIR: random, down
+# Peaceful miners
+MONSTER:'@',"miner",random,peaceful
+MONSTER:'@',"miner",random,peaceful
+MONSTER:'@',"miner",random,peaceful
+MONSTER:'@',"miner",random,peaceful
+MONSTER:'@',"miner",random,peaceful
+MONSTER:'@',"miner",random,peaceful
+MONSTER:'@',"miner",random,peaceful
+MONSTER:'@',"miner",random,peaceful
+# Earth creatures
+MONSTER:'X',"xorn",random
+MONSTER:'E',"earth elemental",random
+MONSTER[50%]:'X',"xorn",random
+MONSTER[50%]:'E',"earth elemental",random
+# Other nasties
+MONSTER:'t',"lurker above",random
+MONSTER:'t',"trapper",random
+MONSTER[50%]:'t',"lurker above",random
+MONSTER[50%]:'t',"trapper",random
+MONSTER[50%]:'p',"rock piercer",random
+MONSTER[50%]:'p',"rock piercer",random
+MONSTER[50%]:'p',"iron piercer",random
+MONSTER[50%]:'p',"iron piercer",random
+MONSTER[50%]:'p',"glass piercer",random
+MONSTER[50%]:'p',"glass piercer",random
+# Tools and corpses
+OBJECT[50%]: '(', "pick-axe", random
+OBJECT[75%]: '(', "brass lantern", random
+OBJECT[50%]: '(', random, random
+OBJECT[25%]: '%', "corpse", random, "miner", 0
+OBJECT[25%]: '%', "corpse", random, "miner", 0
+OBJECT[25%]: '%', "corpse", random, "miner", 0
+OBJECT[25%]: '%', "corpse", random, "miner", 0
+# Natural cavern hazards
+TRAP: "pit", random
+TRAP: "pit", random
+TRAP: "pit", random
+TRAP: "pit", random
+TRAP: "pit", random
+TRAP: "pit", random
+TRAP: "falling rock", random
+TRAP: "falling rock", random
+TRAP: "falling rock", random
+TRAP: "falling rock", random
+TRAP: "web", random
+TRAP: "web", random
+
diff -Nurd nitrohack-ais523//libnethack/dat/quest.txt nitrohack-ais523-convict//libnethack/dat/quest.txt
--- nitrohack-ais523//libnethack/dat/quest.txt 2012-04-25 16:53:48.000000000 -0400
+++ nitrohack-ais523-convict//libnethack/dat/quest.txt 2012-04-27 09:41:09.000000000 -0400
@@ -809,6 +809,302 @@
 "Sacrifice the Amulet on the altar.  Thus shall %d become supreme!"
 %E
 #
+# Convict
+#
+%Cc Con 00001
+Somehow, you have been captured and teleported back to
+%H!  But wait, the portal you went through is still
+here.  You had heard your old mentor %l,
+the prison chaplain who taught you the ways of
+%d calling for your help.  Also, %H
+is not as you had left it.  There seems to be damage
+to the walls, and you hear sounds of strife all
+around you.
+%E
+%Cp Con 00002
+You have returned to %H.  Somehow, things are even
+worse now than when you were on your previous visit.  Has
+%l managed to still hold out?
+%E
+%Cp Con 00003
+The situation seems to degrade, and you wonder how long
+your fellow %gp can hold out against the corrupt
+guards and creatures of the Underdark that pervade the
+formerly tranquil %H.
+%E
+%Cp Con 00005
+"How did you manage to escape?"
+%E
+%Cp Con 00006
+"How is life on the outside?"
+%E
+%Cp Con 00007
+"Between the abusive guards, and the nasty vermin, I
+don't know how we will hold out."
+%E
+%Cp Con 00008
+"This place used to be so much nicer before %n
+became corrupt."
+%E
+%Cp Con 00009
+"You are really back?  I thought you were gone for
+good."
+%E
+%Cp Con 00010
+"Thank you for ridding us of %n.  I hope the
+new warden will be better."
+%E
+%Cp Con 00011
+"Take me with you!  I will be glad to be rid of this
+awful place."
+%E
+%Cp Con 00012
+"Don't forget to come back for us once you have
+completed your quest!"
+%E
+%Cp Con 00013
+"Thank you for coming back to help us in our hour
+of need!"
+%E
+%Cp Con 00014
+"Leave while you still can!  If you can escape,
+maybe there is hope for the rest of us."
+%E
+%Cc Con 00015
+"%p, I am very happy to see that you are
+alive and well!  We were all proud of your daring
+escape, and hoped that you were doing well.
+Things have become much worse in your absence,
+however, and we desperately need your help.  Let
+me have a look at you, and see if you are ready."
+%E
+%Cp Con 00016
+"I see that you are back, %p.  I hope that
+you are now ready to help us?  Let me see if you
+are."
+%E
+%Cp Con 00017
+"Once again, you have returned.  Let me see if you
+are finally ready to help us."
+%E
+%Cc Con 00018
+"You have betrayed us all, %p!  Begone from
+here, and don't come back!  You are not even worthy
+to share a cell with the least of us now."
+%E
+%Cc Con 00019
+"%p, I am afraid that a %r will be no
+match for %n.  If I sent you now,
+she would dispatch you all too easily.
+
+"Continue your other quest, and practice your
+skills.  Return to us once you have achieved the rank
+of %R."
+%E
+%Cc Con 00020
+"%p, I see that you are not yet a loyal servant of
+%d.  Leave this place for now, and only
+return when you have cleansed your spirit.  Only then
+will you be ready to stand up to %n and
+recover %o."
+%E
+%Cc Con 00021
+"Yes, %p, it looks like you are now ready to
+help us.  Things have become much worse since you
+left.  The dungeons are overrun by vermin, guards
+have become brutal and corrupt, and I believe that
+%n has had dealings with dark powers.
+
+"I had spent many years enchanting my iron ball
+with powers to help lead an escape from this place,
+turning it into %o.
+Unfortunately, %n stole it from me,
+likely using it for dark, twisted purposes.
+
+"We need your help to go down to the warden's level
+to defeat the corrupt %n and recover
+%o for us so that
+%H will be safe again."
+%E
+%Cp Con 00025
+"The sooner you are able to recover %o,
+the better off we will be."
+%E
+%Cp Con 00026
+"I am not sure what has happened, but even without
+%o, %n will make a formidable opponent."
+%E
+%Cp Con 00027
+"Stay true to the teachings of %d, and good
+fortune will be with you."
+%E
+%Cp Con 00028
+"I am afraid without the power of %o
+to protect us, we will soon be overwhelmed."
+%E
+%Cp Con 00029
+"%n used to be a woman of honor before turning
+to dark ways.  It is much too late for
+redemption, however."
+%E
+%Cp Con 00030
+"%n is arrogant, and will try to diminish
+your confidence.  Do not let yourself be
+discouraged!"
+%E
+%Cp Con 00031
+"You will have to be very alert, and will need
+to use all of your cunning if you hope to
+defeat %n."
+%E
+%Cp Con 00032
+"Call upon the power of %d to protect you
+when you encounter %n."
+%E
+%Cp Con 00033
+"If you remain true to your faith, you should
+be able to sense the power of %o when
+you are near."
+%E
+%Cp Con 00034
+"You should be able to defeat %n easily
+enough in a fair fight.  Unfortunately, she doesn't
+fight fair."
+%E
+%Cc Con 00035
+This must be the level that leads to the warden's
+area.  You must be getting closer to
+%o now!
+%E
+%Cp Con 00036
+Once again, you find yourself near the entrance to
+the warden's area.  Hopefully, you can acquire
+%o before it's too late!
+%E
+%Cc Con 00040
+You sense the presence of %o as soon as you
+enter this level.  If %o is here, then
+%n must not be far away.
+%E
+%Cp Con 00041
+Once again, you find yourself in the abode of
+%n, and feel the presence of
+%o.
+%E
+%Cc Con 00050
+"So, %p.  You have returned, and are here to get
+%o for %l?
+What makes you think you can get it if
+%l couldn't?  I have many
+allies that have made me even stronger."
+%E
+%Cp Con 00051
+"I see that you have returned, %p.  I
+suggest you leave and save yourself while you still
+have a chance."
+%E
+%Cp Con 00052
+"Back again, %p?  You will certainly die in
+your attempt to defeat me."
+%E
+%Cp Con 00053
+"I will get %o from you, just as I did
+%l!  You can not defeat me."
+%E
+%Cp Con 00060
+"You should never come back.  You will not escape
+here alive again!"
+%E
+%Cp Con 00061
+"Even all of your pathetic lot put together would
+not be enough to defeat me."
+%E
+%Cp Con 00062
+"You are no more than lowly prison scum.  You are
+unworthy to even challenge me."
+%E
+%Cp Con 00063
+"Even %l could not stand up to me, so
+you have no chance at all."
+%E
+%Cp Con 00064
+"Even %o would not be enough to
+make you my equal."
+%E
+%Cp Con 00065
+"Your pathetic devotion to %d will not
+save you, either."
+%E
+%Cp Con 00066
+"After I kill you, I will go after %l next,
+and any that dare to defy me again!"
+%E
+%Cp Con 00067
+"Die with dishonor, lowly %c!"
+%E
+%Cp Con 00068
+"Once a %c, always a %c."
+%E
+%Cp Con 00069
+"How about you find yourself a cosy little cell,
+and hope that I forgive your insolence?"
+%E
+%Cc Con 00070
+As you pick up %o, you feel its power
+flow through you, protecting you, and making you
+more aware of your surroundings.  You know that
+you need to get it back to %l as soon
+as possible now.
+%E
+%Cc Con 00080
+As %n approaches death, you see sudden
+clarity in her eyes.
+
+"What have I done?  Those demons have been clouding my mind.
+Please forgive me, %p, although I know that I
+probably do not deserve it.  Take 
+%o back to %l
+with my blessing."
+
+With a final coughing fit, %n spasms, and her
+eyes see no more.
+%E
+%Cc Con 00081
+"Congratulations, %p!  You have returned with
+your life, and with %o!  I had
+planned to use %o to lead an
+escape from here, but with %n defeated,
+life should go back to normal before long.
+
+"Whether I like it or not, I know my place is here.  I
+see that %o has already attuned
+itself to you.  I offer it to you in hopes that
+it will aid in your quest to recover the Amulet
+of Yendor." 
+%E
+%Cc Con 00082
+%l looks upon %o
+with fondness.
+"You are its keeper now.  Take it with you back to
+%Z, through the magic portal
+that brought you here."
+%E
+%Cc Con 00090
+"Welcome back, %p.  We have done well in your
+absence.  How far have you come with your quest to
+regain the Amulet of Yendor for %d?"
+%E
+%Cc Con 00091
+"Congratulations, %p!  You have redeemed yourself
+and proven yourself a worthy servant of %d,
+and in doing so have made all of us proud.
+
+"One final task remains for you now.  You must take
+the Amulet up to the Great Temple of %d, on
+the Astral plane.  There you must offer the Amulet
+to %d."
+%E
+#
 # Healer
 #
 %Cc Hea 00001
@@ -3504,3 +3800,25 @@
  S suffix: return s_suffix(capitalized(root));
  t suffix: return strip_the_prefix(root);
 %E
+#
+# Alternate legacy text for Convict role
+#
+%Cc - 00199
+It is written in the Book of %d:
+
+    After the Creation, the cruel god Moloch rebelled
+    against the authority of Marduk the Creator.
+    Moloch stole from Marduk the most powerful of all
+    the artifacts of the gods, the Amulet of Yendor,
+    and he hid it in the dark cavities of Gehennom, the
+    Under World, where he now lurks, and bides his time.
+
+Your %G %d seeks to possess the Amulet, and with it
+to gain deserved ascendance over the other gods.
+
+You, a newly escaped %r, have chosen to redeem
+yourself by recovering the Amulet for %d.  You are
+determined to recover the Amulet for your deity, or die
+in the attempt.  Your hour of destiny has come.  For
+the sake of us all:  Go bravely with %d!
+%E
diff -Nurd nitrohack-ais523//libnethack/include/artifact.h nitrohack-ais523-convict//libnethack/include/artifact.h
--- nitrohack-ais523//libnethack/include/artifact.h 2012-04-25 16:53:48.000000000 -0400
+++ nitrohack-ais523-convict//libnethack/include/artifact.h 2012-04-27 09:42:29.000000000 -0400
@@ -59,5 +59,6 @@
 #define CREATE_PORTAL (LAST_PROP+7)
 #define ENLIGHTENING (LAST_PROP+8)
 #define CREATE_AMMO (LAST_PROP+9)
+#define PHASING  (LAST_PROP+10)
 
 #endif /* ARTIFACT_H */
diff -Nurd nitrohack-ais523//libnethack/include/artilist.h nitrohack-ais523-convict//libnethack/include/artilist.h
--- nitrohack-ais523//libnethack/include/artilist.h 2012-04-25 16:53:48.000000000 -0400
+++ nitrohack-ais523-convict//libnethack/include/artilist.h 2012-04-27 09:45:41.000000000 -0400
@@ -42,6 +42,10 @@
 A("Excalibur",   LONG_SWORD,
  (SPFX_NOGEN|SPFX_RESTR|SPFX_SEEK|SPFX_DEFN|SPFX_INTEL|SPFX_SEARCH),0,0,
  PHYS(5,10), DRLI(0,0), NO_CARY, 0, A_LAWFUL, PM_KNIGHT, NON_PM, 4000L ),
+/* Convict role first sacrifice gift: */
+A("Luck Blade",   BROADSWORD,
+ (SPFX_RESTR|SPFX_LUCK|SPFX_INTEL),0,0,
+ PHYS(5,6), NO_DFNS, NO_CARY, 0, A_CHAOTIC, PM_CONVICT, NON_PM, 3000L ),
 /*
  * Stormbringer only has a 2 because it can drain a level,
  * providing 8 more.
@@ -167,6 +171,12 @@
  PHYS(5,0), NO_DFNS, CARY(AD_MAGM),
  CONFLICT, A_LAWFUL, PM_CAVEMAN, NON_PM, 2500L ),
 
+A("The Iron Ball of Liberation", HEAVY_IRON_BALL,
+ (SPFX_NOGEN|SPFX_RESTR|SPFX_INTEL),
+  (SPFX_STLTH|SPFX_SEARCH|SPFX_WARN), 0,
+ NO_ATTK, NO_DFNS, CARY(AD_MAGM),
+ PHASING, A_NEUTRAL, PM_PRISONER, NON_PM, 5000L ),
+
 A("The Staff of Aesculapius", QUARTERSTAFF,
  (SPFX_NOGEN|SPFX_RESTR|SPFX_ATTK|SPFX_INTEL|SPFX_DRLI|SPFX_REGEN), 0,0,
  DRLI(0,0), DRLI(0,0), NO_CARY,
diff -Nurd nitrohack-ais523//libnethack/include/decl.h nitrohack-ais523-convict//libnethack/include/decl.h
--- nitrohack-ais523//libnethack/include/decl.h 2012-04-25 16:53:48.000000000 -0400
+++ nitrohack-ais523-convict//libnethack/include/decl.h 2012-04-27 09:46:23.000000000 -0400
@@ -142,6 +142,7 @@
 extern char dogname[];
 extern char catname[];
 extern char horsename[];
+extern char ratname[];
 extern char preferred_pet;
 extern const char *occtxt;   /* defined when occupation != NULL */
 extern const char *nomovemsg;
diff -Nurd nitrohack-ais523//libnethack/include/eshk.h nitrohack-ais523-convict//libnethack/include/eshk.h
--- nitrohack-ais523//libnethack/include/eshk.h 2012-04-25 16:53:48.000000000 -0400
+++ nitrohack-ais523-convict//libnethack/include/eshk.h 2012-04-27 09:47:07.000000000 -0400
@@ -31,6 +31,7 @@
  schar shoproom;  /* index in rooms; set by inshop() */
  boolean following; /* following customer since he owes us sth */
  boolean surcharge; /* angry shk inflates prices */
+ boolean pbanned; /* player is banned from the shop (convict role) */
  char customer[PL_NSIZ]; /* most recent customer */
  char shknam[PL_NSIZ];
 };
diff -Nurd nitrohack-ais523//libnethack/include/mondata.h nitrohack-ais523-convict//libnethack/include/mondata.h
--- nitrohack-ais523//libnethack/include/mondata.h 2012-04-25 16:53:48.000000000 -0400
+++ nitrohack-ais523-convict//libnethack/include/mondata.h 2012-04-27 09:48:46.000000000 -0400
@@ -89,6 +89,11 @@
 #define is_bat(ptr)  ((ptr) == &mons[PM_BAT] || \
      (ptr) == &mons[PM_GIANT_BAT] || \
      (ptr) == &mons[PM_VAMPIRE_BAT])
+# define is_rat(ptr)  ((ptr) == &mons[PM_SEWER_RAT] || \
+     (ptr) == &mons[PM_GIANT_RAT] || \
+     (ptr) == &mons[PM_RABID_RAT] || \
+     (ptr) == &mons[PM_ENORMOUS_RAT] || \
+     (ptr) == &mons[PM_RODENT_OF_UNUSUAL_SIZE])
 #define is_bird(ptr)  ((ptr)->mlet == S_BAT && !is_bat(ptr))
 #define is_giant(ptr)  (((ptr)->mflags2 & M2_GIANT) != 0L)
 #define is_golem(ptr)  ((ptr)->mlet == S_GOLEM)
@@ -195,6 +200,6 @@
      (ptr) != &mons[PM_BLACK_PUDDING]))
 
 #define befriend_with_obj(ptr, obj) ((obj)->oclass == FOOD_CLASS && \
-         is_domestic(ptr))
+         (is_domestic(ptr) || (is_rat(ptr) && Role_if(PM_CONVICT))))
 
 #endif /* MONDATA_H */
diff -Nurd nitrohack-ais523//libnethack/include/youprop.h nitrohack-ais523-convict//libnethack/include/youprop.h
--- nitrohack-ais523//libnethack/include/youprop.h 2012-04-25 16:53:48.000000000 -0400
+++ nitrohack-ais523-convict//libnethack/include/youprop.h 2012-04-27 09:49:40.000000000 -0400
@@ -268,7 +268,7 @@
 #define EPasses_walls  u.uprops[PASSES_WALLS].extrinsic
 #define Passes_walls  (HPasses_walls || EPasses_walls || \
      passes_walls(youmonst.data))
-
+#define Phasing                 u.uprops[PASSES_WALLS].intrinsic
 
 /*** Physical attributes ***/
 #define HSlow_digestion  u.uprops[SLOW_DIGESTION].intrinsic
diff -Nurd nitrohack-ais523//libnethack/src/allmain.c nitrohack-ais523-convict//libnethack/src/allmain.c
--- nitrohack-ais523//libnethack/src/allmain.c 2012-04-25 16:53:48.000000000 -0400
+++ nitrohack-ais523-convict//libnethack/src/allmain.c 2012-04-27 18:45:55.000000000 -0400
@@ -882,13 +882,26 @@
     if (MON_AT(level, u.ux, u.uy)) mnexto(m_at(level, u.ux, u.uy));
     makedog();
     doredraw();
+
+    if (Role_if(PM_CONVICT)) {
+        setworn(mkobj(level, CHAIN_CLASS, TRUE), W_CHAIN);
+        setworn(mkobj(level, BALL_CLASS, TRUE), W_BALL);
+        uball->spe = 1;
+        placebc();
+        newsym(u.ux,u.uy);
+    }
     
     /* help the window port get it's display charset/tiles sorted out */
     notify_levelchange();
 
     if (flags.legacy) {
-     flush_screen();
-     com_pager(1);
+       flush_screen();
+       if (Role_if(PM_CONVICT)) {
+          com_pager(199); /* Convicts are "escaped" rather than
+                           * "trained", among other things. */
+       } else {
+          com_pager(1);
+       }
     }
 
     program_state.something_worth_saving++; /* useful data now exists */
diff -Nurd nitrohack-ais523//libnethack/src/artifact.c nitrohack-ais523-convict//libnethack/src/artifact.c
--- nitrohack-ais523//libnethack/src/artifact.c 2012-04-25 16:53:48.000000000 -0400
+++ nitrohack-ais523-convict//libnethack/src/artifact.c 2012-04-27 19:13:20.000000000 -0400
@@ -504,6 +504,17 @@
  if (yours) pline("%s your grasp!", Tobjnam(obj, "evade"));
  return 0;
     }
+    /* This is a kludge, but I'm not sure where else to put it */
+    if (oart == &artilist[ART_IRON_BALL_OF_LIBERATION]) {
+ if (Role_if(PM_CONVICT) && (!obj->oerodeproof)) {
+     obj->oerodeproof = TRUE;
+     obj->owt = 300; /* Magically lightened, but still heavy */
+ }
+
+ if (Punished && (obj != uball)) {
+     unpunish(); /* Remove a mundane heavy iron ball */
+ }
+    }
 
     return 1;
 }
@@ -1328,6 +1339,33 @@
      otmp->owt = weight(otmp);
      hold_another_object(otmp, "Suddenly %s out.", aobjnam(otmp, "fall"), NULL);
      break;
+ case PHASING:   /* Walk through walls and stone like a xorn */
+            if (Passes_walls) goto nothing_special;
+            if (oart == &artilist[ART_IRON_BALL_OF_LIBERATION]) {
+                if (Punished && (obj != uball)) {
+                    unpunish(); /* Remove a mundane heavy iron ball */
+                }
+                
+                if (!Punished) {
+                    setworn(mkobj(level, CHAIN_CLASS, TRUE), W_CHAIN);
+                    setworn(obj, W_BALL);
+                    uball->spe = 1;
+                    if (!u.uswallow) {
+                        placebc();
+                        if (Blind) set_bc(1); /* set up ball and chain variables */
+                        newsym(u.ux,u.uy);    /* see ball&chain if can't see self */
+                    }
+                    pline("Your %s chains itself to you!", xname(obj));
+                }
+            }
+            if (!Hallucination) {    
+                pline("Your body begins to feel less solid.");
+            } else {
+                pline("You feel one with the spirit world.");
+            }
+            incr_itimeout(&Phasing, (50 + rnd(100)));
+            obj->age += Phasing; /* Time begins after phasing ends */
+            break;
    }
  }
     } else {
diff -Nurd nitrohack-ais523//libnethack/src/attrib.c nitrohack-ais523-convict//libnethack/src/attrib.c
--- nitrohack-ais523//libnethack/src/attrib.c 2012-04-25 16:53:48.000000000 -0400
+++ nitrohack-ais523-convict//libnethack/src/attrib.c 2012-04-27 10:08:56.000000000 -0400
@@ -36,6 +36,11 @@
        { 15, &(HWarning), "sensitive", "" },
        {  0, 0, 0, 0 } },
 
+ con_abil[] = { {   1, &(HSick_resistance), "", "" },
+                       {   7, &(HPoison_resistance), "healthy", "" },
+                       {  20, &(HSearching), "perceptive", "unaware" },
+                       {   0, 0, 0, 0 } },
+
  hea_abil[] = { {  1, &(HPoison_resistance), "", "" },
        { 15, &(HWarning), "sensitive", "" },
        {  0, 0, 0, 0 } },
@@ -467,6 +472,7 @@
  case PM_ARCHEOLOGIST:   abil = arc_abil; break;
  case PM_BARBARIAN:      abil = bar_abil; break;
  case PM_CAVEMAN:        abil = cav_abil; break;
+ case PM_CONVICT:        abil = con_abil; break;
  case PM_HEALER:         abil = hea_abil; break;
  case PM_KNIGHT:         abil = kni_abil; break;
  case PM_MONK:           abil = mon_abil; break;
diff -Nurd nitrohack-ais523//libnethack/src/cmd.c nitrohack-ais523-convict//libnethack/src/cmd.c
--- nitrohack-ais523//libnethack/src/cmd.c 2012-04-25 16:53:48.000000000 -0400
+++ nitrohack-ais523-convict//libnethack/src/cmd.c 2012-04-27 18:53:41.000000000 -0400
@@ -632,6 +632,9 @@
    if (u.usick_type & SICK_NONVOMITABLE)
     you_are(&menu, "sick from illness");
   }
+                if (Punished) {
+                    you_are(&menu, "punished");
+                }
  }
  if (Stoned) you_are(&menu, "turning to stone");
  if (Slimed) you_are(&menu, "turning into slime");
diff -Nurd nitrohack-ais523//libnethack/src/decl.c nitrohack-ais523-convict//libnethack/src/decl.c
--- nitrohack-ais523//libnethack/src/decl.c 2012-04-25 16:53:48.000000000 -0400
+++ nitrohack-ais523-convict//libnethack/src/decl.c 2012-04-27 10:24:44.000000000 -0400
@@ -135,6 +135,7 @@
 char dogname[PL_PSIZ];
 char catname[PL_PSIZ];
 char horsename[PL_PSIZ];
+char ratname[PL_PSIZ];
 char preferred_pet; /* '\0', 'c', 'd', 'n' (none) */
 /* monsters that went down/up together with @ */
 struct monst *mydogs;
@@ -233,6 +234,7 @@
     memset(dogname, 0, sizeof(dogname));
     memset(catname, 0, sizeof(catname));
     memset(horsename, 0, sizeof(horsename));
+    memset(ratname, 0, sizeof(ratname));
     memset(&youmonst, 0, sizeof(youmonst));
     memset(&zeroobj, 0, sizeof(zeroobj));
     memset(mvitals, 0, sizeof(mvitals));
diff -Nurd nitrohack-ais523//libnethack/src/do.c nitrohack-ais523-convict//libnethack/src/do.c
--- nitrohack-ais523//libnethack/src/do.c 2012-04-25 16:53:48.000000000 -0400
+++ nitrohack-ais523-convict//libnethack/src/do.c 2012-04-27 10:13:15.000000000 -0400
@@ -1022,7 +1022,11 @@
       pline("You fly down along the %s.",
    at_ladder ? "ladder" : "stairs");
   else if (at_stairs &&
-      (near_capacity() > UNENCUMBERED || Punished || Fumbling)) {
+      (near_capacity() > UNENCUMBERED
+                     || (Punished && ((uwep != uball)
+                                      || ((P_SKILL(P_FLAIL) < P_BASIC))
+                                      || !Role_if(PM_CONVICT)))
+       || Fumbling)) {
       pline("You fall down the %s.", at_ladder ? "ladder" : "stairs");
       if (Punished) {
    drag_down();
diff -Nurd nitrohack-ais523//libnethack/src/dog.c nitrohack-ais523-convict//libnethack/src/dog.c
--- nitrohack-ais523//libnethack/src/dog.c 2012-04-25 16:53:48.000000000 -0400
+++ nitrohack-ais523-convict//libnethack/src/dog.c 2012-04-27 10:18:02.000000000 -0400
@@ -131,6 +131,8 @@
   petname = dogname;
  else if (pettype == PM_PONY)
   petname = horsename;
+ else if (pettype == PM_SEWER_RAT)
+  petname = ratname;
  else
   petname = catname;
 
@@ -142,6 +144,9 @@
      if (Role_if(PM_BARBARIAN)) petname = "Idefix";  /* Obelix */
      if (Role_if(PM_RANGER)) petname = "Sirius";     /* Orion's dog */
  }
+ if (!*petname && pettype == PM_SEWER_RAT) {
+     if(Role_if(PM_CONVICT)) petname = "Nicodemus"; /* Rats of NIMH */
+        }
 
  mtmp = makemon(&mons[pettype], level, u.ux, u.uy, MM_EDOG);
 
@@ -708,6 +713,12 @@
       && mtmp->data->mlet == S_DOG)
   return NULL;
 
+        if (Role_if(PM_CONVICT) && (is_domestic(mtmp->data) && obj)) {
+            /* Domestic animals are wary of the Convict */
+            pline("%s still looks wary of you.", Monnam(mtmp));
+            return NULL;
+        }
+
  /* If we cannot tame it, at least it's no longer afraid. */
  mtmp->mflee = 0;
  mtmp->mfleetim = 0;
diff -Nurd nitrohack-ais523//libnethack/src/dokick.c nitrohack-ais523-convict//libnethack/src/dokick.c
--- nitrohack-ais523//libnethack/src/dokick.c 2012-04-25 16:53:48.000000000 -0400
+++ nitrohack-ais523-convict//libnethack/src/dokick.c 2012-04-27 10:18:35.000000000 -0400
@@ -309,6 +309,8 @@
       goldreqd = 500L;
    else if (mtmp->data == &mons[PM_CAPTAIN])
       goldreqd = 750L;
+   else if (mtmp->data == &mons[PM_PRISON_GUARD])
+      goldreqd = 200L;
 
    if (goldreqd) {
       if (value > goldreqd +
diff -Nurd nitrohack-ais523//libnethack/src/eat.c nitrohack-ais523-convict//libnethack/src/eat.c
--- nitrohack-ais523//libnethack/src/eat.c 2012-04-25 16:53:48.000000000 -0400
+++ nitrohack-ais523-convict//libnethack/src/eat.c 2012-04-27 10:22:08.000000000 -0400
@@ -1210,12 +1210,15 @@
      "Mmm, tripe... not bad!");
   else {
       pline("Yak - dog food!");
+      if (Role_if(PM_CONVICT))
+   pline("At least it's not prison food.");
       more_experienced(1,0);
       newexplevel();
       /* not cannibalism, but we use similar criteria
          for deciding whether to be sickened by this meal */
       if (rn2(2) && !CANNIBAL_ALLOWED())
-   make_vomiting((long)rn1(victual.reqtime, 14), FALSE);
+                        if (!Role_if(PM_CONVICT))
+                            make_vomiting((long)rn1(victual.reqtime, 14), FALSE);
   }
   break;
      case MEATBALL:
@@ -1253,7 +1256,10 @@
 #endif
   if (otmp->otyp == EGG && stale_egg(otmp)) {
       pline("Ugh.  Rotten egg."); /* perhaps others like it */
-      make_vomiting(Vomiting+dice(10,4), TRUE);
+                    if (Role_if(PM_CONVICT) && (rn2(8) > u.ulevel)) {
+                        pline("You feel a slight stomach ache."); /* prisoners are used to bad food */
+                    } else
+                        make_vomiting(Vomiting+dice(10,4), TRUE);
   } else
  give_feedback:
       pline("This %s is %s", singular(otmp, xname),
@@ -1943,6 +1949,8 @@
 
  if ((!u.usleep || !rn2(10)) /* slow metabolic rate while asleep */
   && (carnivorous(youmonst.data) || herbivorous(youmonst.data))
+            /* Convicts can last twice as long at hungry and below */
+            && (!Role_if(PM_CONVICT) || (moves % 2) || (u.uhs < HUNGRY))
   && !Slow_digestion)
      u.uhunger--;  /* ordinary food consumption */
 
diff -Nurd nitrohack-ais523//libnethack/src/hack.c nitrohack-ais523-convict//libnethack/src/hack.c
--- nitrohack-ais523//libnethack/src/hack.c 2012-04-25 16:53:48.000000000 -0400
+++ nitrohack-ais523-convict//libnethack/src/hack.c 2012-04-27 10:25:45.000000000 -0400
@@ -590,7 +590,8 @@
  }
  if (invent && (inv_weight() + weight_cap() > 600)) {
      if (mode == DO_MOVE)
-  pline("You are carrying too much to get through.");
+                if (!Passes_walls)
+                    pline("You are carrying too much to get through.");
      return FALSE;
  }
     }
diff -Nurd nitrohack-ais523//libnethack/src/makemon.c nitrohack-ais523-convict//libnethack/src/makemon.c
--- nitrohack-ais523//libnethack/src/makemon.c 2012-04-25 16:53:48.000000000 -0400
+++ nitrohack-ais523-convict//libnethack/src/makemon.c 2012-04-27 18:56:16.000000000 -0400
@@ -179,6 +179,7 @@
    case PM_LIEUTENANT:
      w1 = rn2(2) ? BROADSWORD : LONG_SWORD;
      break;
+   case PM_PRISON_GUARD:
    case PM_CAPTAIN:
    case PM_WATCH_CAPTAIN:
      w1 = rn2(2) ? LONG_SWORD : SILVER_SABER;
@@ -229,6 +230,11 @@
    if (!rn2(2)) curse(otmp);
    mpickobj(mtmp, otmp);
       }
+  } else if (mm == PM_MINER) {
+      (void)mongets(mtmp, PICK_AXE);
+      otmp = mksobj(level, BRASS_LANTERN, TRUE, FALSE);
+                    (void) mpickobj(mtmp, otmp);
+                    begin_burn(otmp, FALSE);
   }
   break;
 
@@ -469,6 +475,7 @@
 
       switch(monsndx(ptr)) {
    case PM_GUARD: mac = -1; break;
+   case PM_PRISON_GUARD: mac = -2; break;
    case PM_SOLDIER: mac = 3; break;
    case PM_SERGEANT: mac = 0; break;
    case PM_LIEUTENANT: mac = -2; break;
@@ -513,6 +520,7 @@
    mongets(mtmp, LEATHER_CLOAK);
 
       if (ptr != &mons[PM_GUARD] &&
+   ptr != &mons[PM_PRISON_GUARD] &&
    ptr != &mons[PM_WATCHMAN] &&
    ptr != &mons[PM_WATCH_CAPTAIN]) {
    if (!rn2(3)) mongets(mtmp, K_RATION);
diff -Nurd nitrohack-ais523//libnethack/src/mondata.c nitrohack-ais523-convict//libnethack/src/mondata.c
--- nitrohack-ais523//libnethack/src/mondata.c 2012-04-25 16:53:48.000000000 -0400
+++ nitrohack-ais523-convict//libnethack/src/mondata.c 2012-04-27 10:29:44.000000000 -0400
@@ -511,6 +511,8 @@
  {PM_ORC, PM_ORC_CAPTAIN}, {PM_HILL_ORC, PM_ORC_CAPTAIN},
  {PM_MORDOR_ORC, PM_ORC_CAPTAIN}, {PM_URUK_HAI, PM_ORC_CAPTAIN},
  {PM_SEWER_RAT, PM_GIANT_RAT},
+ {PM_GIANT_RAT, PM_ENORMOUS_RAT},
+ {PM_ENORMOUS_RAT, PM_RODENT_OF_UNUSUAL_SIZE},
  {PM_CAVE_SPIDER, PM_GIANT_SPIDER},
  {PM_OGRE, PM_OGRE_LORD}, {PM_OGRE_LORD, PM_OGRE_KING},
  {PM_ELF, PM_ELF_LORD}, {PM_WOODLAND_ELF, PM_ELF_LORD},
diff -Nurd nitrohack-ais523//libnethack/src/monmove.c nitrohack-ais523-convict//libnethack/src/monmove.c
--- nitrohack-ais523//libnethack/src/monmove.c 2012-04-25 16:53:48.000000000 -0400
+++ nitrohack-ais523-convict//libnethack/src/monmove.c 2012-04-27 10:32:57.000000000 -0400
@@ -45,6 +45,13 @@
  if (mtmp->mpeaceful && in_town(u.ux, u.uy) &&
     mtmp->mcansee && m_canseeu(mtmp) && !rn2(3)) {
 
+            if(Role_if(PM_CONVICT) && !Upolyd) {
+                verbalize("%s yells: Hey!  You are the one from the wanted poster!",
+                          Amonnam(mtmp));
+                angry_guards(!(flags.soundok));
+                stop_occupation();
+                return;
+            }
      if (picking_lock(&x, &y) && IS_DOOR(level->locations[x][y].typ) &&
         (level->locations[x][y].doormask & D_LOCKED)) {
 
@@ -327,7 +334,8 @@
 
  /* Demonic Blackmail! */
  if (nearby && mdat->msound == MS_BRIBE &&
-    mtmp->mpeaceful && !mtmp->mtame && !u.uswallow) {
+            (monsndx(mdat) != PM_PRISON_GUARD) &&
+            mtmp->mpeaceful && !mtmp->mtame && !u.uswallow) {
      if (multi < 0) return(0); /* wait for you to be able to respond */
   if (mtmp->mux != u.ux || mtmp->muy != u.uy) {
    pline("%s whispers at thin air.",
@@ -346,6 +354,24 @@
   } else if (demon_talk(mtmp)) return 1; /* you paid it off */
  }
 
+ /* Prison guard extortion */
+        if(nearby && (monsndx(mdat) == PM_PRISON_GUARD) && !mtmp->mpeaceful
+           && !mtmp->mtame && !u.uswallow && (!mtmp->mspec_used)) {
+            long gdemand = 500 * u.ulevel;
+            long goffer = 0;
+            
+            pline("%s demands %ld %s to avoid re-arrest.", Amonnam(mtmp),
+                  gdemand, currency(gdemand));
+            if ((goffer = bribe(mtmp)) >= gdemand) {
+                verbalize("Good.  Now beat it, scum!");
+                mtmp->mpeaceful = 1;
+                set_malign(mtmp);
+            } else {
+                pline("I said %ld!", gdemand);
+                mtmp->mspec_used = 1000;
+            }
+        }
+
  /* the watch will look around and see if you are up to no good :-) */
  if (mdat == &mons[PM_WATCHMAN] || mdat == &mons[PM_WATCH_CAPTAIN])
   watch_on_duty(mtmp);
diff -Nurd nitrohack-ais523//libnethack/src/monst.c nitrohack-ais523-convict//libnethack/src/monst.c
--- nitrohack-ais523//libnethack/src/monst.c 2012-04-25 16:53:48.000000000 -0400
+++ nitrohack-ais523-convict//libnethack/src/monst.c 2012-04-27 10:38:38.000000000 -0400
@@ -810,6 +810,20 @@
  SIZ(40, 30, 0, MS_SQEEK, MZ_TINY), MR_POISON, 0,
  M1_NOHANDS|M1_POIS|M1_REGEN|M1_CARNIVORE,
  M2_NOPOLY|M2_WERE|M2_HOSTILE, M3_INFRAVISIBLE, CLR_BROWN|HI_ULINE),
+    MON("enormous rat", S_RODENT,
+ LVL(3, 9, 6, 0, 0), (G_GENO|G_SGROUP|1),
+ A(ATTK(AT_BITE, AD_PHYS, 1, 6),
+   NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK),
+ SIZ(200, 100, 0, MS_SQEEK, MZ_SMALL), 0, 0,
+ M1_ANIMAL|M1_NOHANDS|M1_CARNIVORE, M2_HOSTILE, M3_INFRAVISIBLE,
+ CLR_BROWN),
+    MON("rodent of unusual size", S_RODENT,
+ LVL(7, 8, 4, 0, 0), (G_GENO|1),
+ A(ATTK(AT_BITE, AD_PHYS, 2, 6),
+   NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK),
+ SIZ(500, 200, 0, MS_SQEEK, MZ_MEDIUM), 0, 0,
+ M1_ANIMAL|M1_NOHANDS|M1_CARNIVORE, M2_HOSTILE, M3_INFRAVISIBLE,
+ CLR_BROWN),
     MON("rock mole", S_RODENT,
  LVL(3, 3, 0, 20, 0), (G_GENO|2),
  A(ATTK(AT_BITE, AD_PHYS, 1, 6),
@@ -2234,6 +2248,22 @@
  M1_HUMANOID|M1_OMNIVORE,
  M2_NOPOLY|M2_HUMAN|M2_STRONG|M2_COLLECT,
  M3_INFRAVISIBLE, HI_DOMESTIC2),
+    MON("miner", S_HUMAN,
+ LVL(2, 6, 10, 10, 4), (G_GENO|G_NOGEN),
+ A(ATTK(AT_WEAP, AD_PHYS, 1, 8),
+   NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK),
+ SIZ(WT_HUMAN, 400, 0, MS_HUMANOID, MZ_HUMAN), 0, 0,
+ M1_TUNNEL|M1_NEEDPICK|M1_HUMANOID|M1_OMNIVORE,
+ M2_NOPOLY|M2_HUMAN|M2_STRONG,
+ M3_INFRAVISIBLE, CLR_GRAY),
+    MON("prison guard", S_HUMAN,
+ LVL(12, 10, 10, 15, -6), G_NOGEN,
+ A(ATTK(AT_WEAP, AD_PHYS, 4, 4), ATTK(AT_WEAP, AD_PHYS, 4, 4),
+   NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK),
+ SIZ(WT_HUMAN, 400, 0, MS_BRIBE, MZ_HUMAN), 0, 0,
+ M1_HUMANOID|M1_OMNIVORE,
+ M2_NOPOLY|M2_HUMAN|M2_MERC|M2_STALK|M2_HOSTILE|M2_STRONG|M2_COLLECT,
+ M3_INFRAVISIBLE, HI_METAL),
     MON("wererat", S_HUMAN,
  LVL(2, 12, 10, 10, -7), (1),
  A(ATTK(AT_WEAP, AD_PHYS, 2, 4),
@@ -2475,6 +2505,14 @@
  M3_INFRAVISIBLE|M3_INFRAVISION, CLR_BLUE),
  /* standard demons & devils
   */
+    MON("lava demon", S_DEMON,
+ LVL(12, 12,-8, 40, -7), (G_NOCORPSE|G_NOGEN),
+ A(ATTK(AT_WEAP, AD_PHYS, 2, 4), ATTK(AT_BITE, AD_PHYS, 1, 8),
+   ATTK(AT_BITE, AD_FIRE, 2, 6), NO_ATTK, NO_ATTK, NO_ATTK),
+ SIZ(WT_HUMAN, 400, 0, MS_SILENT, MZ_HUMAN), MR_FIRE|MR_POISON, 0,
+ M1_HUMANOID|M1_POIS,
+ M2_NOPOLY|M2_DEMON|M2_STALK|M2_HOSTILE|M2_NASTY|M2_COLLECT,
+ M3_INFRAVISIBLE|M3_INFRAVISION, CLR_RED),
     MON("horned devil", S_DEMON,
  LVL(6, 9, -5, 50, 11), (G_HELL|G_NOCORPSE|2),
  A(ATTK(AT_WEAP, AD_PHYS, 1, 4), ATTK(AT_CLAW, AD_PHYS, 1, 4),
@@ -2483,7 +2521,7 @@
  SIZ(WT_HUMAN, 400, 0, MS_SILENT, MZ_HUMAN), MR_FIRE|MR_POISON, 0,
  M1_POIS|M1_THICK_HIDE,
  M2_DEMON|M2_STALK|M2_HOSTILE|M2_NASTY, M3_INFRAVISIBLE|M3_INFRAVISION,
- CLR_BROWN),
+ CLR_RED),
 #define SEDUCTION_ATTACKS \
  A(ATTK(AT_BITE, AD_SSEX, 2, 6), ATTK(AT_CLAW, AD_PHYS, 1, 3), \
    ATTK(AT_CLAW, AD_PHYS, 1, 3), NO_ATTK, NO_ATTK, NO_ATTK)
@@ -2870,6 +2908,13 @@
  M1_HUMANOID|M1_OMNIVORE,
  M2_NOPOLY|M2_HUMAN|M2_STRONG|M2_FEMALE|M2_COLLECT, M3_INFRAVISIBLE,
  HI_DOMESTIC3),
+    MON("convict", S_HUMAN,
+ LVL(10, 12, 10, 1, 0), G_NOGEN,
+ A(ATTK(AT_WEAP, AD_PHYS, 1, 6), ATTK(AT_WEAP, AD_PHYS, 1, 6),
+   NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK),
+ SIZ(WT_HUMAN, 400, 0, MS_HUMANOID, MZ_HUMAN), 0, 0,
+ M1_HUMANOID|M1_OMNIVORE,
+ M2_NOPOLY|M2_HUMAN|M2_STRONG|M2_COLLECT, M3_INFRAVISIBLE, HI_DOMESTIC),
     MON("healer", S_HUMAN,
  LVL(10, 12, 10, 1, 0), G_NOGEN,
  A(ATTK(AT_WEAP, AD_PHYS, 1, 6),
@@ -2984,6 +3029,15 @@
  M2_NOPOLY|M2_HUMAN|M2_PNAME|M2_PEACEFUL|M2_STRONG|M2_MALE|
    M2_COLLECT|M2_MAGIC,
  M3_CLOSE|M3_INFRAVISIBLE, HI_QUEST),
+    MON("Robert the Lifer", S_HUMAN,
+ LVL(20, 12, 0, 40, -20), (G_NOGEN|G_UNIQ),
+ A(ATTK(AT_WEAP, AD_PHYS, 1, 6), ATTK(AT_WEAP, AD_PHYS, 1, 6),
+   NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK),
+ SIZ(WT_HUMAN, 400, 0, MS_LEADER, MZ_HUMAN), 0, 0,
+ M1_HUMANOID|M1_OMNIVORE,
+ M2_NOPOLY|M2_HUMAN|M2_PNAME|M2_PEACEFUL|M2_STRONG|M2_MALE|
+   M2_COLLECT|M2_MAGIC,
+ M3_CLOSE|M3_INFRAVISIBLE, HI_DOMESTIC),
     MON("Hippocrates", S_HUMAN,
  LVL(20, 12, 0, 40, 0), (G_NOGEN|G_UNIQ),
  A(ATTK(AT_WEAP, AD_PHYS, 1, 6),
@@ -3115,6 +3169,15 @@
  M2_NOPOLY|M2_HOSTILE|M2_FEMALE|M2_STALK|M2_STRONG|M2_NASTY|
    M2_GREEDY|M2_JEWELS|M2_MAGIC,
  M3_WANTSARTI|M3_WAITFORU|M3_INFRAVISIBLE, HI_QUEST),
+    MON("Warden Arianna", S_HUMAN,
+ LVL(20, 12, 0, 40, -14), (G_NOGEN|G_UNIQ|G_NOCORPSE),
+ A(ATTK(AT_WEAP, AD_PHYS, 1, 25), ATTK(AT_WEAP, AD_PHYS, 1, 25),
+   ATTK(AT_CLAW, AD_SAMU, 1, 6), NO_ATTK, NO_ATTK, NO_ATTK),
+ SIZ(WT_HUMAN, 400, 0, MS_NEMESIS, MZ_HUMAN), MR_FIRE|MR_POISON, 0,
+ M1_HUMANOID|M1_OMNIVORE,
+ M2_NOPOLY|M2_HUMAN|M2_PNAME|M2_HOSTILE|M2_STRONG|M2_STALK|
+   M2_NASTY|M2_FEMALE|M2_COLLECT|M2_MAGIC,
+ M3_WANTSARTI|M3_WAITFORU|M3_INFRAVISIBLE, HI_LORD),
     MON("Cyclops", S_GIANT,
  LVL(18, 12, 0, 0, -15), (G_NOGEN|G_UNIQ),
  A(ATTK(AT_WEAP, AD_PHYS, 4, 8), ATTK(AT_WEAP, AD_PHYS, 4, 8),
@@ -3235,6 +3298,14 @@
  M1_HUMANOID|M1_OMNIVORE,
  M2_NOPOLY|M2_HUMAN|M2_PEACEFUL|M2_STRONG|M2_COLLECT, M3_INFRAVISIBLE,
  HI_GUARDIAN),
+    MON("inmate", S_HUMAN,
+ LVL(12, 12, 10, 0, 0), G_NOGEN,
+ A(ATTK(AT_WEAP, AD_PHYS, 1, 6),
+   NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK),
+ SIZ(WT_HUMAN, 400, 0, MS_GUARDIAN, MZ_HUMAN), 0, 0,
+ M1_HUMANOID|M1_OMNIVORE,
+ M2_NOPOLY|M2_HUMAN|M2_PEACEFUL|M2_STRONG|M2_COLLECT,
+ M3_INFRAVISIBLE|M3_CLOSE, CLR_BLACK),
     MON("attendant", S_HUMAN,
  LVL(5, 12, 10, 10, 3), G_NOGEN,
  A(ATTK(AT_WEAP, AD_PHYS, 1, 6),
diff -Nurd nitrohack-ais523//libnethack/src/objects.c nitrohack-ais523-convict//libnethack/src/objects.c
--- nitrohack-ais523//libnethack/src/objects.c 2012-04-25 16:53:48.000000000 -0400
+++ nitrohack-ais523-convict//libnethack/src/objects.c 2012-04-27 10:40:09.000000000 -0400
@@ -383,6 +383,8 @@
 /* shirts */
 ARMOR("Hawaiian shirt", NULL,
  1, 0, 0, 0,  8, 0,  5,   3, 10, 0, ARM_SHIRT, CLOTH, CLR_MAGENTA),
+ARMOR("striped shirt", NULL,
+ 1, 0, 0, 0,  0, 0,  5,   2, 10, 0, ARM_SHIRT, CLOTH, CLR_GRAY),
 ARMOR("T-shirt", NULL,
  1, 0, 0, 0,  2, 0,  5,   2, 10, 0, ARM_SHIRT, CLOTH, CLR_WHITE),
 
@@ -917,7 +919,7 @@
 OBJECT(OBJ("statue", NULL), BITS(1,0,0,1,0,0,0,0,0,0,0,P_NONE,MINERAL), 0,
   ROCK_CLASS,   900, 0, 2500,  0, 20, 20, 0, 0, 2500, CLR_WHITE),
 
-OBJECT(OBJ("heavy iron ball", NULL), BITS(1,0,0,0,0,0,0,0,0,0,WHACK,P_NONE,IRON), 0,
+OBJECT(OBJ("heavy iron ball", NULL), BITS(1,0,0,0,0,0,0,0,0,0,WHACK,P_FLAIL,IRON), 0,
   BALL_CLASS,  1000, 0,  480, 10, 25, 25, 0, 0,  200, HI_METAL),
       /* +d4 when "very heavy" */
 OBJECT(OBJ("iron chain", NULL), BITS(1,0,0,0,0,0,0,0,0,0,WHACK,P_NONE,IRON), 0,
diff -Nurd nitrohack-ais523//libnethack/src/options.c nitrohack-ais523-convict//libnethack/src/options.c
--- nitrohack-ais523//libnethack/src/options.c 2012-04-25 16:53:48.000000000 -0400
+++ nitrohack-ais523-convict//libnethack/src/options.c 2012-04-27 10:44:05.000000000 -0400
@@ -158,6 +158,7 @@
     { "catname",       "the name of your (first) cat", OPTTYPE_STRING, {NULL}},
     { "dogname",       "the name of your (first) dog", OPTTYPE_STRING, {NULL}},
     { "horsename",     "the name of your (first) horse", OPTTYPE_STRING, {NULL}},
+    { "ratname",       "the name of your (first) rat (e.g., ratname:Squeak)", OPTTYPE_STRING, {NULL}},
     { "pettype",       "your preferred initial pet type", OPTTYPE_ENUM, {0}},
     
     {NULL, NULL, OPTTYPE_BOOL, { NULL }}
@@ -308,6 +309,7 @@
  find_option(birth_options, "catname")->s.maxlen = PL_PSIZ;
  find_option(birth_options, "dogname")->s.maxlen = PL_PSIZ;
  find_option(birth_options, "horsename")->s.maxlen = PL_PSIZ;
+ find_option(birth_options, "ratname")->s.maxlen = PL_PSIZ;
 
  /* If no config file exists, these values will not get set until they
   * have already been used during game startup.  (-1) is a much better
@@ -638,6 +640,9 @@
  else if (!strcmp("horsename", option->name)) {
   strncpy(horsename, option->value.s, PL_PSIZ);
  }
+ else if (!strcmp("ratname", option->name)) {
+  strncpy(ratname, option->value.s, PL_PSIZ);
+ }
  else if (!strcmp("pettype", option->name)) {
   preferred_pet = (char)option->value.e;
  }
diff -Nurd nitrohack-ais523//libnethack/src/read.c nitrohack-ais523-convict//libnethack/src/read.c
--- nitrohack-ais523//libnethack/src/read.c 2012-04-25 16:53:48.000000000 -0400
+++ nitrohack-ais523-convict//libnethack/src/read.c 2012-04-27 10:47:04.000000000 -0400
@@ -1605,6 +1605,7 @@
 
 void punish(struct obj *sobj)
 {
+        struct obj *otmp;
  /* KMH -- Punishment is still okay when you are riding */
  pline("You are being punished for your misbehavior!");
  if (Punished){
@@ -1618,7 +1619,13 @@
   return;
  }
  setworn(mkobj(level, CHAIN_CLASS, TRUE), W_CHAIN);
- setworn(mkobj(level, BALL_CLASS, TRUE), W_BALL);
+        if (((otmp = carrying(HEAVY_IRON_BALL)) != 0)
+            &&(otmp->oartifact == ART_IRON_BALL_OF_LIBERATION)) {
+            setworn(otmp, W_BALL);
+            pline("Your %s chains itself to you!", xname(otmp));
+        } else {
+            setworn(mkobj(level, BALL_CLASS, TRUE), W_BALL);
+        }
  uball->spe = 1;  /* special ball (see save) */
 
  /*
diff -Nurd nitrohack-ais523//libnethack/src/role.c nitrohack-ais523-convict//libnethack/src/role.c
--- nitrohack-ais523//libnethack/src/role.c 2012-04-25 16:53:48.000000000 -0400
+++ nitrohack-ais523-convict//libnethack/src/role.c 2012-04-27 10:48:59.000000000 -0400
@@ -108,6 +108,32 @@
  {  1, 0,  0, 1,  0, 1 },10, /* Energy */
  0, 12, 0, 1,  8, A_INT, SPE_DIG,             -4
 },
+{ {"Convict", 0}, {
+ {"Detainee",     0},
+ {"Inmate",   0},
+ {"Jail-bird",0},
+ {"Prisoner",0},
+ {"Outlaw",    0},
+ {"Crook",   0},
+ {"Desperado",     0},
+ {"Felon",    0},
+ {"Fugitive",  0} },
+ "Ilmater", "Grumbar", "_Tymora", /* Faerunian */
+ "Con", "Castle Waterdeep Dungeon", "the Warden's Level",
+ PM_CONVICT, NON_PM, PM_SEWER_RAT,
+ PM_ROBERT_THE_LIFER, PM_INMATE, PM_WARDEN_ARIANNA,
+ PM_GIANT_BEETLE, PM_SOLDIER_ANT, S_RODENT, S_SPIDER,
+ ART_IRON_BALL_OF_LIBERATION,
+ MH_HUMAN|MH_DWARF|MH_GNOME|MH_ORC | ROLE_MALE|ROLE_FEMALE |
+   ROLE_CHAOTIC,
+ /* Str Int Wis Dex Con Cha */
+ {  10,  7,  7,  7, 13,  6 },
+ {  20, 20, 10, 20, 20, 10 },
+ /* Init   Lower  Higher */
+ {  8, 0,  0, 8,  0, 0 }, /* Hit points */
+ {  1, 0,  0, 1,  0, 1 },10, /* Energy */
+ -10, 5, 0, 2, 10, A_INT, SPE_TELEPORT_AWAY,   -4
+},
 { {"Healer", 0}, {
  {"Rhizotomist",    0},
  {"Empiric",        0},
diff -Nurd nitrohack-ais523//libnethack/src/shk.c nitrohack-ais523-convict//libnethack/src/shk.c
--- nitrohack-ais523//libnethack/src/shk.c 2012-04-25 16:53:48.000000000 -0400
+++ nitrohack-ais523-convict//libnethack/src/shk.c 2012-04-27 18:58:40.000000000 -0400
@@ -485,6 +485,11 @@
      return;
  }
 
+ /* Visible striped prison shirt */
+ if ((uarmu && (uarmu->otyp == STRIPED_SHIRT)) && !uarm && !uarmc) {
+     eshkp->pbanned = TRUE;
+ }
+
  rt = level->rooms[*enterstring - ROOMOFFSET].rtype;
 
  if (ANGRY(shkp)) {
@@ -495,11 +500,12 @@
  } else if (eshkp->robbed) {
      pline("%s mutters imprecations against shoplifters.", shkname(shkp));
  } else {
-     verbalize("%s, %s!  Welcome%s to %s %s!",
-        Hello(shkp), plname,
-        eshkp->visitct++ ? " again" : "",
-        s_suffix(shkname(shkp)),
-        shtypes[rt - SHOPBASE].name);
+            if (!eshkp->pbanned || inside_shop(level, u.ux, u.uy))
+                verbalize("%s, %s!  Welcome%s to %s %s!",
+                          Hello(shkp), plname,
+                          eshkp->visitct++ ? " again" : "",
+                          s_suffix(shkname(shkp)),
+                          shtypes[rt - SHOPBASE].name);
  }
  /* can't do anything about blocking if teleported in */
  if (!inside_shop(level, u.ux, u.uy)) {
@@ -536,6 +542,9 @@
      "Will you please leave %s outside?" :
      "Leave %s outside.", y_monnam(u.usteed));
   should_block = TRUE;
+     } else if (eshkp->pbanned) {
+                verbalize("I don't sell to your kind here.");
+  should_block = TRUE;
      } else {
   should_block = (Fast && (sobj_at(PICK_AXE, level, u.ux, u.uy) ||
           sobj_at(DWARVISH_MATTOCK, level, u.ux, u.uy)));
@@ -1400,6 +1409,7 @@
   else {
       numsk++;
       taken |= inherits(mtmp, numsk, croaked);
+      ESHK(mtmp)->pbanned = FALSE; /* Un-ban for bones levels */
   }
      }
  }
@@ -2948,6 +2958,7 @@
       uondoor = (u.ux == eshkp->shd.x && u.uy == eshkp->shd.y);
       if (uondoor) {
    badinv = (carrying(PICK_AXE) || carrying(DWARVISH_MATTOCK) ||
+                                  eshkp->pbanned ||
       (Fast && (sobj_at(PICK_AXE, level, u.ux, u.uy) ||
       sobj_at(DWARVISH_MATTOCK, level, u.ux, u.uy))));
    if (satdoor && badinv)
diff -Nurd nitrohack-ais523//libnethack/src/sounds.c nitrohack-ais523-convict//libnethack/src/sounds.c
--- nitrohack-ais523//libnethack/src/sounds.c 2012-04-25 16:53:48.000000000 -0400
+++ nitrohack-ais523-convict//libnethack/src/sounds.c 2012-04-27 10:55:01.000000000 -0400
@@ -712,10 +712,31 @@
      }
      break;
  case MS_BRIBE:
-     if (mtmp->mpeaceful && !mtmp->mtame) {
-  demon_talk(mtmp);
-  break;
-     }
+            if (monsndx(ptr) == PM_PRISON_GUARD) {
+                long gdemand = 500 * u.ulevel;
+                long goffer = 0;
+                
+                if (!mtmp->mpeaceful && !mtmp->mtame) {
+                    pline("%s demands %ld %s to avoid re-arrest.",
+                          Amonnam(mtmp), gdemand, currency(gdemand));
+                    if ((goffer = bribe(mtmp)) >= gdemand) {
+                        verbl_msg = "Good.  Now beat it, scum!";
+                        mtmp->mpeaceful = 1;
+                        set_malign(mtmp);
+                        break;
+                    } else {
+                        pline("I said %ld!", gdemand);
+                        mtmp->mspec_used = 1000;
+                        break;
+                    }
+                } else {
+                    verbl_msg = "Out of my way, scum!"; /* still a jerk */
+                }
+            } else
+                if (mtmp->mpeaceful && !mtmp->mtame) {
+                    demon_talk(mtmp);
+                    break;
+                }
      /* fall through */
  case MS_CUSS:
      if (!mtmp->mpeaceful)
@@ -881,6 +902,23 @@
  return 0;
     }
 
+    if (Role_if(PM_CONVICT) && is_rat(mtmp->data) && !mtmp->mpeaceful &&
+        !mtmp->mtame) {
+        pline("You attempt to soothe the %s with chittering sounds.",
+            l_monnam(mtmp));
+        if (rnl(10) < 2) {
+            (void) tamedog(mtmp, (struct obj *) 0);
+        } else {
+            if (rnl(10) > 8) {
+                pline("%s unfortunately ignores your overtures.",
+                      Monnam(mtmp));
+                return 0;
+            }
+            mtmp->mpeaceful = 1;
+            set_malign(mtmp);
+        }
+        return 0;
+    }
     return domonnoise(mtmp);
 }
 
diff -Nurd nitrohack-ais523//libnethack/src/timeout.c nitrohack-ais523-convict//libnethack/src/timeout.c
--- nitrohack-ais523//libnethack/src/timeout.c 2012-04-25 16:53:48.000000000 -0400
+++ nitrohack-ais523-convict//libnethack/src/timeout.c 2012-04-27 19:01:19.000000000 -0400
@@ -6,6 +6,7 @@
 
 static void stoned_dialogue(void);
 static void vomiting_dialogue(void);
+static void phasing_dialogue(void);
 static void choke_dialogue(void);
 static void slime_dialogue(void);
 static void slip_or_trip(void);
@@ -40,6 +41,26 @@
  exercise(A_DEX, FALSE);
 }
 
+static void
+phasing_dialogue()
+{
+    if (Phasing == 15) {
+        if (!Hallucination) {
+            pline("Your body is beginning to feel more solid.");
+        } else {
+            pline("You feel more distant from the spirit world.");
+        }
+        stop_occupation();
+    } else if (Phasing == 1) {
+        if (!Hallucination) {
+            pline("Your body is solid again.");
+        } else {
+            pline("You feel totally separated from the spirit world.");
+        }
+        stop_occupation();
+    }
+}
+
 /* He is getting sicker and sicker prior to vomiting */
 static const char * const vomiting_texts[] = {
  "You are feeling mildly nauseated.", /* 14 */
@@ -176,6 +197,7 @@
      else if (u.uluck < baseluck && (nostone || time_luck > 0))
   u.uluck++;
  }
+        if (Phasing) phasing_dialogue();
  if (u.uinvulnerable) return; /* things past this point could kill you */
  if (Stoned) stoned_dialogue();
  if (Slimed) slime_dialogue();
diff -Nurd nitrohack-ais523//libnethack/src/uhitm.c nitrohack-ais523-convict//libnethack/src/uhitm.c
--- nitrohack-ais523//libnethack/src/uhitm.c 2012-04-25 16:53:48.000000000 -0400
+++ nitrohack-ais523-convict//libnethack/src/uhitm.c 2012-04-27 10:59:32.000000000 -0400
@@ -255,6 +255,14 @@
  if (is_orc(mtmp->data) && maybe_polyd(is_elf(youmonst.data),
    Race_if (PM_ELF)))
      tmp++;
+        /* Now that iron ball uses a weapon skill (flail), that gives
+           a -4 penalty for unskilled vs no penalty for non-weapon
+           objects.  Add 4 to compensate. */
+        if (uwep && (uwep->otyp == HEAVY_IRON_BALL)) {
+            tmp += 4;   /* Compensate for flail weapon skill -4
+                           penalty for unskilled vs no penalty for
+                           non- weapon objects. */
+        }
  if (Role_if(PM_MONK) && !Upolyd) {
      if (uarm) {
   pline("Your armor is rather cumbersome...");
@@ -551,7 +559,7 @@
  } else {
      strcpy(saved_oname, cxname(obj));
      if (obj->oclass == WEAPON_CLASS || is_weptool(obj) ||
-        obj->oclass == GEM_CLASS) {
+        obj->oclass == GEM_CLASS || obj->otyp == HEAVY_IRON_BALL) {
 
   /* is it not a melee weapon? */
   if (/* if you strike with a bow... */
diff -Nurd nitrohack-ais523//libnethack/src/u_init.c nitrohack-ais523-convict//libnethack/src/u_init.c
--- nitrohack-ais523//libnethack/src/u_init.c 2012-04-25 16:53:48.000000000 -0400
+++ nitrohack-ais523-convict//libnethack/src/u_init.c 2012-04-27 19:07:02.000000000 -0400
@@ -54,6 +54,11 @@
  { LEATHER_ARMOR, 0, ARMOR_CLASS, 1, UNDEF_BLESS },
  { 0, 0, 0, 0, 0 }
 };
+static struct trobj Convict[] = {
+ { ROCK, 0, GEM_CLASS, 1, 0 },
+ { STRIPED_SHIRT, 0, ARMOR_CLASS, 1, 0 },
+ { 0, 0, 0, 0, 0 }
+};
 static const struct trobj Healer[] = {
  { SCALPEL, 0, WEAPON_CLASS, 1, UNDEF_BLESS },
  { LEATHER_GLOVES, 1, ARMOR_CLASS, 1, UNDEF_BLESS },
@@ -292,6 +297,18 @@
     { P_NONE, 0 }
 };
 
+static const struct def_skill Skill_Con[] = {
+    { P_DAGGER, P_SKILLED },  { P_KNIFE,  P_EXPERT },
+    { P_HAMMER, P_SKILLED },  { P_PICK_AXE, P_EXPERT },
+    { P_CLUB, P_EXPERT },      { P_MACE, P_BASIC },
+    { P_DART, P_SKILLED },      { P_FLAIL, P_EXPERT },
+    { P_SHORT_SWORD, P_BASIC },  { P_SLING, P_SKILLED },
+    { P_ATTACK_SPELL, P_BASIC }, { P_ESCAPE_SPELL, P_EXPERT },
+    { P_TWO_WEAPON_COMBAT, P_SKILLED },
+    { P_BARE_HANDED_COMBAT, P_SKILLED },
+    { P_NONE, 0 }
+};
+
 static const struct def_skill Skill_H[] = {
     { P_DAGGER, P_SKILLED },  { P_KNIFE, P_EXPERT },
     { P_SHORT_SWORD, P_SKILLED }, { P_SCIMITAR, P_BASIC },
@@ -590,6 +607,18 @@
   ini_inv(trobj_list, nclist);
   skill_init(Skill_C);
   break;
+ case PM_CONVICT:
+            trobj_list = copy_trobj_list(Convict);
+            ini_inv(trobj_list, nclist);
+            knows_object(SKELETON_KEY);
+            knows_object(GRAPPLING_HOOK);
+            skill_init(Skill_Con);
+            u.uhunger = 200;  /* On the verge of hungry */
+            u.ualignbase[A_CURRENT] = u.ualignbase[A_ORIGINAL] =
+                u.ualign.type = A_CHAOTIC; /* Override racial alignment */
+            urace.hatemask |= urace.lovemask;   /* Hated by the race's allies */
+            urace.lovemask = 0; /* Convicts are pariahs of their race */
+            break;
  case PM_HEALER:
   u.umoney0 = rn1(1000, 1001);
   ini_inv(Healer, nclist);
@@ -744,7 +773,8 @@
  case PM_ORC:
      /* compensate for generally inferior equipment */
      if (!Role_if (PM_WIZARD))
-  ini_inv(Xtra_food, nclist);
+                if (!Role_if(PM_CONVICT))
+                    ini_inv(Xtra_food, nclist);
      /* Orcs can recognize all orcish objects */
      knows_object(ORCISH_SHORT_SWORD);
      knows_object(ORCISH_ARROW);
@@ -936,6 +966,9 @@
     is_graystone(obj) && obj->otyp != FLINT) {
        obj->quan = 1L;
    }
+                        if (obj->otyp == STRIPED_SHIRT ) {
+                            obj->cursed = TRUE;
+                        }
    if (trop->trspe != UNDEF_SPE)
        obj->spe = trop->trspe;
    if (trop->trbless != UNDEF_BLESS)
diff -Nurd nitrohack-ais523//libnethack/src/vault.c nitrohack-ais523-convict//libnethack/src/vault.c
--- nitrohack-ais523//libnethack/src/vault.c 2012-04-25 16:53:48.000000000 -0400
+++ nitrohack-ais523-convict//libnethack/src/vault.c 2012-04-27 11:34:59.000000000 -0400
@@ -265,6 +265,15 @@
      mongone(guard);
      return;
  }
+        if (Role_if(PM_CONVICT) && !Upolyd) {
+            setmangry(guard);
+            verbalize("I saw your pic on the wanted poster!");
+            if (!MON_WEP(guard)) {
+                guard->weapon_check = NEED_HTH_WEAPON;
+                mon_wield_item(guard);
+            }
+            return;
+        }
  if (Strangled || is_silent(youmonst.data) || multi < 0) {
      /* [we ought to record whether this this message has already
         been given in order to vary it upon repeat visits, but
diff -Nurd nitrohack-ais523//libnethack/src/weapon.c nitrohack-ais523-convict//libnethack/src/weapon.c
--- nitrohack-ais523//libnethack/src/weapon.c 2012-04-25 16:53:48.000000000 -0400
+++ nitrohack-ais523-convict//libnethack/src/weapon.c 2012-04-27 11:35:36.000000000 -0400
@@ -1127,7 +1127,9 @@
  if (!obj)
   /* Not using a weapon */
   return P_BARE_HANDED_COMBAT;
- if (obj->oclass != WEAPON_CLASS && obj->oclass != TOOL_CLASS &&
+        if ((obj->otyp == HEAVY_IRON_BALL) && Role_if(PM_CONVICT))
+            return objects[obj->otyp].oc_skill;
+        if (obj->oclass != WEAPON_CLASS && obj->oclass != TOOL_CLASS &&
      obj->oclass != GEM_CLASS)
   /* Not a weapon, weapon-tool, or ammo */
   return P_NONE;
diff -Nurd nitrohack-ais523//README-convict.txt nitrohack-ais523-convict//README-convict.txt
--- nitrohack-ais523//README-convict.txt 1969-12-31 19:00:00.000000000 -0500
+++ nitrohack-ais523-convict//README-convict.txt 2012-04-27 09:51:30.000000000 -0400
@@ -0,0 +1,115 @@
+Convict role patch v0.7
+Karl Garrison <kgarrison@pobox.com>
+Originally for NetHack 3.4.3
+Ported to NetHack4 by Jonadab
+
+
+Non-spoiler Information
+~~~~~~~~~~~~~~~~~~~~~~~
+
+This patch adds a new role to NetHack: the Convict.  The Convict is an
+escaped prisoner who seeks to redeem himself by seeking the Amulet in
+the Dungeons of Doom.  They start out with a striped shirt, some rocks,
+and heavy iron ball chained to them.  A trusty sewer rat accompanies
+the Convict into the dungeon: a companion from his days of
+imprisonment.  Convicts have an affinity with rats, but many other
+animals do not trust him.
+
+Race: Human, Dwarf, Gnome, or Orc
+Alignment: Chaotic
+Deity: Tymora (Forgotten Realms)
+
+New configuration file option: ratname.  Example:
+
+OPTIONS=ratname:Squeak
+
+
+
+
+
+
+
+
+
+
+
+
+
+Spoiler Information
+~~~~~~~~~~~~~~~~~~~
+
+There may be a few surprises to this role for the unaware.  They start
+with a negative alignment ("You have transgressed").  This means that a
+Convict will not be able to pray successfully early on.  Some time and
+killing of monsters will make up for this before too long, though.
+They also start the game on the verge of hungry, which also makes the
+very early game challenging for Convicts.  On the bright side, it takes
+them longer to progress from hungry to weak, etc, due to their higher
+tolerance for irregular meals.
+
+Stats: Excellent CON, good STR, average DEX and INT, poor WIS and CHA.
+
+Intrinsics: Sick resistance at level 1 (due to exposure to unhealthy
+prison conditions, as well as bad food), poison resistance at level 7,
+search at level 20.
+
+Pet: The convict starts with a sewer rat, which quickly advances to a
+giant rat, and can eventually become an enormous rat, then a rodent of
+unusual size.  Convicts can tame rats with food, but not domestic
+amimals like dogs and cats.  The latter are instead made peaceful.
+
+Convicts can also usually make rats peaceful and occasionally tame by
+#chatting with them.
+
+Iron Ball: The Convict starts with a heavy iron ball chained to him, as
+if he had read a scroll of punishment.  Since he starts with no real
+weapons apart from his rocks, he may wish to wield it as a weapon.
+Convicts (and only Convicts) will practice the flail skill when he
+weilds an iron ball.  Advancing flail skill improves his chance to hit,
+and prevents him from automatically falling down the stairs if one is
+chained to him, so long as he is currently wielding it.
+
+Striped Shirt: Shopkeepers who can see the Convict wearing his striped
+shirt will recognize the player as an escaped prisoner, and will ban
+him from the shop.  Even if he later hides the shirt, that shopkeeper
+will remember the player.
+
+Guards: Guards (town watch and vault guards) who see a Convict will
+recognize him from wanted posters, and immediately become hostile.
+
+Hunger: Convicts start on the verge of hungry, but the effects of
+hunger kick in a bit more slowly for this role, due to their learned
+tolerance of inadequate nutrition.
+
+Weapon Skills:
+
+Basic: mace, short sword
+
+Skilled: dagger, dart, hammer, sling, two-weapon combat
+bare-handed combat
+
+Expert: club, flail, knife, pick-axe
+
+
+Spellcasting Skills:
+
+Basic: Matter
+
+Expert: Escape
+
+
+Luck Sword: This is a Chaotic broadsword which acts as a luckstone, and
+is the Convict's first sacrifice gift.
+
+The Quest: The Convict quest has the player return to Castle
+Waterdeep's dungeons in order to retrieve the Iron Ball of Liberation
+from the corrupted Warden Arianna on behalf of their quest leader,
+Robert the Lifer.
+
+The Iron Ball of Liberation: This is a (slightly lighter) heavy iron
+ball that grants magic resistance, stealth, searching and warning.
+When invoked, it allows phasing for a limited time (the ability to
+walk through walls/stone like a xorn).  The downsides are its weight
+(almost as heavy as a normal iron ball) and the fact that it chains
+itself to the player everytime its power is invoked.
+

Web Browser Upgrade Cycles (Update: including Webkit browsers)


Okay, my last post was purely editorial, so this one is going to be purely observational, with no editorial commentary. These graphs show how the two most popular browsers (MSIE and Firefox) compare in terms of new-version uptake, among users of a website that I maintain. From looking at these graphs, it is clear that new Firefox versions achieve, on average, much more punctual uptake than new MSIE versions. This is apparent despite the fact that the graph skews things significantly in MSIE's favor by using version numbers (as opposed to release dates), even though Firefox versions are released much more frequently.

Firefox 2.0 was released in October 2006; MSIE 6 was released more than twice as long ago, in August of 2001. IE 7 was released in October of 2006, around the same time of Firefox 2.0, and has an absolutely overwhelmingly higher remaining usage share, taken as a percentage of the overall usage share for all versions of each respective browser: around twenty percent versus around half a percent during the last two months. (If you do the arithmetic naively you get 19.4% versus 0.54%, but there's significant false precision there. It's based on only two months of data for just one site, so you really have to consider the results as round, approximate figures.)

If you look at the stats for a different website, your numbers will of course vary. Nonetheless, it's very clear from these graphs that new Firefox versions are installed, on average, rather sooner than new IE versions. I could speculate on possible reasons for that difference, but for now I'll leave that as an exercise for the astute reader.

Update: Here are my graphs for the two major Webkit browsers.


Note that, with the sample size being only one site and the usage share of these browsers being smaller, these graphs can (potentially) be significantly skewed by a relatively small number of users. For example, why did usage of Safari 4.1 only become noticeable after 5.0 was released? My guess would be that this is an anomaly. At its peak, Safari 4.1 made 218 page loads from our site in a quarter. That's full page loads, not raw hits, but it could still be explained by a single user.

Still, you can definitely see a trend toward fast uptake of new versions, particularly on the Chrome graph. I could speculate on the reasons for this, but at the moment I'll refrain. If you really want to see a difference, compare the Chrome graph here to the MSIE graph up top. There's obviously a marked difference in upgrade frequency from one browser to another.


Finally, here's a version of the Firefox graph that breaks down minor versions (3.1 versus 3.0 and 3.6 versus 3.5), which is better for comparing Firefox to the Webkit browsers. The first Firefox graph, which groups minor versions, was intended for comparing against MSIE.

Firefox: Why I Refuse to Upgrade

Everybody knows, newer is better, right? Well, sometimes. Sometimes not so much. Sometimes newer is worse. Sometimes newer is a lot worse.

Recent versions of Mozilla Firefox, for example, have been getting a great deal worse in a wide variety of ways. In this post I will catalog just a few of the most annoying reasons why they are worse, and why I am steadfastly not upgrading.

However, lest anyone think I am entirely negative all the time, I want to start out by spending a couple of paragraphs pointing out a couple of good things.

In the first place, I want to note that I am only talking about this at all because Firefox is my primary browser, the one I use most. Being a web developer and a geek, I experiment with a wide variety of browsers. Firefox is the best of them. When I first started discovering the problems that the rest of this diatribe will talk about, I considered switching to another browser, but the plain and simple fact is that I was unable to find another one that's as good. There isn't another one that's as good. Firefox is the best.

On top of that, the Firefox dev team have been working, trying to make improvements, and a handful of the improvements they've made have been good ones. They've added support for CSS properties that weren't handled before. One of the ones I personally find useful, not just for aesthetic purposes but in some cases for practical reasons, e.g., improving legibility when there's an image in the background, is text-shadow. That's a very worthwhile thing. Even better is the new support for display: inline-block, which makes whole categories of layouts easy to do that were previously, in a word, not. There have also been some performance improvements, which are quite noticeable on older single-core hardware. So I don't want to imply that the Firefox developers haven't been doing anything good. They have.

But they've also been making mistakes lately, some of which are quite serious.



Here's something that's easy to see: recent versions of Firefox can't seem to display certain images (a LOT of the images on the web) without darkening them considerably. Here are a couple of screenshots (one cropped, the other scaled down, but they're not doctored in any other way) showing the same image in Firefox 3.6 and in Gimp. I want to be clear that this is exactly the same image, bit-for-bit. Notice how much darker it looks in Firefox? It's not supposed to be like that.


The same problem shows up in every build starting around version 3.5. Here's a screenshot of Firefox 4.0.1. Again, this is cropped but otherwise undoctored.


How did Firefox 2.0 handle it? Well, let's see here... Oh, look, Firefox 2.0 displays exactly the same thing as Gimp. The latest versions of other browsers, such as Opera and Chrome and even MSIE, do the same thing as Firefox 2. So does every other image display program I have tested. Recent versions of Firefox are the only software I have found to have the odd darkening effect.

Update: Here it is in an alpha build of Firefox 8:

(Yeah, the desktop there looks different. When testing new versions, I use a separate user account, so as not to mess up the profile -- add-ons and configuration and such -- in my regular account. It's easier than dorking around restoring from backups.)

Okay, so that's purely an aesthetic issue. I mean, it's annoying, but it doesn't cause any real material harm. It's not like the images are totally black and impossible to see, or anything. It's just like you're looking at them through really dark glasses. So, no big deal, really. Sure, it's a bug, and they should fix it, but on the balance if that were the only problem I'd upgrade in a heartbeat.

Here's something more serious: all versions of Firefox starting from the 3.0 dev cycle have a serious dataloss bug that shows up if you use bookmarked tabsets. What happens is this: the first time you choose Open all in tabs after upgrading, every single website you already had open in a tab vanishes. You panic and just about keel over from a heart attack on the spot, but once you take a few deep breaths you discover, much to your relief, that the back button does work. You have to go through and hit the back button on every tab, but you're able to recover (at least most of) your tabs. Okay. So, now, how to stop that from EVER happening again? You hunt through the prefs and find the option that controls this disastrous never-should-have-been-implemented new behavior, and you turn it the everliving %#$@! off, and you think you've solved the problem.

But you have not solved the problem by changing the preference. You've just made the problem more subtle, so that each time it happens it can go unnoticed until it's too late to retrieve what was lost. What happens now, each time you click Open all in tabs, is that one of your existing tabs is replaced. If you don't notice this, you make the mistake of hitting close-tab like you normally would when you're done with a page that you've opened, and now you're now short one of the pages that you had open, probably one you weren't done with, possibly something important that you needed to remember to deal with. This happens every single time you click Open all in tabs, until over the course of a few hours or days of regular browser use you eventually figure out what's happening. It took me about three days. I knew I was losing tabs, some of which were kind of important, and I was very much in a lather about it, but I didn't understand out how or why it was happening. Undo Close Tab only showed me pages I didn't need any more, and I couldn't find the ones I'd lost. It was like returning to the bad old days before sessionstore, when a plugin crash or power outage meant things you'd had open were just gone. (Update: this one will be fixed in version 8, but the fact that such a serious bug persisted across no fewer than six releases before finally being eliminated is very telling. Somebody was a little too concerned about dorking with the toolbar layout and just completely forgot about checking to see if there were any dataloss bugs that should be fixed before release -- several times in a row.)

Firefox 3 also crashes significantly more often than Firefox 2. (I don't rightly know how crashy Firefox 4 is or isn't. I haven't used it very much, on account of the fact that it hasn't addressed the above problems.) Firefox 2 never crashes, unless you try to open completely insane numbers of tabs at once. Firefox 3 crashes more than any previously released version of Firefox since clear back when it was called Phoenix.

I haven't even talked about insane new behaviors and UI (*cough* tabs on top *cough*) that can be configured away by changing some settings, because hey, if changing some settings is all I have to do to get things working right, I can handle that. End users might feel differently, but I'm a network administrator. If I can handle tracking down dependencies and compiling things from source, I think I can manage changing a couple of settings. I could write another whole post explaining why e.g. Tabs on Top is stupid and why the arguments in its favor are nonsense (maybe I will write that post later, if I have time), but ultimately it's not important, because I can just turn it off anyway with a pref, so who cares?

But I do want to say one general thing about the UI changes in recent versions: starting with Firefox 3.0, every single UI change, without exception, is something so undesirable that I have difficulty imagining anyone would ever actually want it. Not a single one of them is useful, even potentially. As long as I can turn them all off with preferences I don't really care, but when there are big outstanding bugs, including dataloss bugs, persisting over *multiple* versions (and I'm not talking about a couple of point releases), maybe it's time to stop needlessly fiddling around so much with the UI for a while and concentrate on basic stuff like stability and correctness. IMO, if the Firefox team spent the next entire release cycle just fixing bugs and not introducing any other changes at all, that would be a good thing.

So that's why I'm not upgrading to recent versions of Firefox. I've got 2.0.0.20 both at home and at work now, and I'm sticking with that as my primary browser for the forseeable future.

What would have to happen for me to change my mind? Someone would have to release a browser that's better than Firefox 2.0.0.20. That is all.