Index: msvc/hexen.vcproj
===================================================================
--- msvc/hexen.vcproj	(revision 2353)
+++ msvc/hexen.vcproj	(working copy)
@@ -117,13 +117,14 @@
 				Name="VCCLCompilerTool"
 				Optimization="2"
 				EnableIntrinsicFunctions="true"
-				AdditionalIncludeDirectories=".;..\src;..\src\doom;..\src\heretic;..\textscreen;..\pcsound"
+				FavorSizeOrSpeed="1"
+				AdditionalIncludeDirectories=".;..\src;..\src\strife;..\src\doom;..\textscreen;..\pcsound;..\opl"
 				PreprocessorDefinitions="WIN32;_CRT_SECURE_NO_WARNINGS;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;PROGRAM_PREFIX=&quot;\&quot;chocolate-\&quot;&quot;"
 				StringPooling="true"
 				ExceptionHandling="0"
 				RuntimeLibrary="2"
 				EnableFunctionLevelLinking="true"
-				WarningLevel="0"
+				WarningLevel="3"
 				DebugInformationFormat="0"
 				CompileAs="1"
 			/>
@@ -138,7 +139,7 @@
 			/>
 			<Tool
 				Name="VCLinkerTool"
-				AdditionalDependencies="SDL.lib SDL_mixer.lib SDL_net.lib SDLmain.lib"
+				AdditionalDependencies="SDL.lib SDL_mixer.lib SDL_net.lib SDLmain.lib ..\lib\libpcsound.lib ..\lib\libtextscreen.lib ..\lib\libopl.lib"
 				OutputFile="$(OutDir)\chocolate-hexen.exe"
 				GenerateDebugInformation="false"
 				SubSystem="1"
Index: src/hexen/d_net.c
===================================================================
--- src/hexen/d_net.c	(revision 2352)
+++ src/hexen/d_net.c	(working copy)
@@ -765,7 +765,8 @@
     if (i > 0)
     {
         pClass = atoi(myargv[i + 1]);
-        if (pClass > PCLASS_MAGE || pClass < PCLASS_FIGHTER)
+        // [PIG] haleyjd 20110630: changed upper bound
+        if (pClass >= NUMCLASSES || pClass < PCLASS_FIGHTER)
         {
             I_Error("Invalid player class: %d\n", pClass);
         }
Index: src/hexen/f_finale.c
===================================================================
--- src/hexen/f_finale.c	(revision 2352)
+++ src/hexen/f_finale.c	(working copy)
@@ -181,7 +181,7 @@
            SCREENWIDTH * SCREENHEIGHT);
     if (FinaleStage == 5)
     {                           // Chess pic, draw the correct character graphic
-        if (netgame)
+        if (netgame || PlayerClass[consoleplayer] == PCLASS_PIG) // [PIG] haleyjd 20110702
         {
             V_DrawPatch(20, 0, W_CacheLumpName("chessall", PU_CACHE));
         }
Index: src/hexen/g_game.c
===================================================================
--- src/hexen/g_game.c	(revision 2352)
+++ src/hexen/g_game.c	(working copy)
@@ -1063,12 +1063,12 @@
 {
     player_t *p;
     int frags[MAXPLAYERS];
-    int killcount, itemcount, secretcount;
+    int experience, level, secretcount; // [PIG]: EXP, LV
     unsigned int worldTimer;
 
     memcpy(frags, players[player].frags, sizeof(frags));
-    killcount = players[player].killcount;
-    itemcount = players[player].itemcount;
+    experience = players[player].experience;
+    level = players[player].level;
     secretcount = players[player].secretcount;
     worldTimer = players[player].worldTimer;
 
@@ -1076,8 +1076,8 @@
     memset(p, 0, sizeof(*p));
 
     memcpy(players[player].frags, frags, sizeof(players[player].frags));
-    players[player].killcount = killcount;
-    players[player].itemcount = itemcount;
+    players[player].experience = experience;
+    players[player].level = level;
     players[player].secretcount = secretcount;
     players[player].worldTimer = worldTimer;
     players[player].class = PlayerClass[player];
@@ -1628,6 +1628,8 @@
     {
         players[i].playerstate = PST_REBORN;
         players[i].worldTimer = 0;
+        players[i].level = 0;       // [PIG] haleyjd 20110702: Reset level and EXP
+        players[i].experience = 0;
     }
 
     // Set up a bunch of globals
Index: src/hexen/h2def.h
===================================================================
--- src/hexen/h2def.h	(revision 2352)
+++ src/hexen/h2def.h	(working copy)
@@ -546,7 +546,10 @@
 
     int refire;                 // refired shots are less accurate
 
-    int killcount, itemcount, secretcount;      // for intermission
+    // [PIG] haleyjd 20110701: changed unused killcount/itemcount into EXP
+    // and LV for leveling up as the pig class.
+    int experience, level;
+    int secretcount;      // for intermission
     char message[80];           // hint messages
     int messageTics;            // counter for showing messages
     short ultimateMessage;
Index: src/hexen/mn_menu.c
===================================================================
--- src/hexen/mn_menu.c	(revision 2352)
+++ src/hexen/mn_menu.c	(working copy)
@@ -180,16 +180,19 @@
     MENU_NONE
 };
 
+// [PIG] haleyjd 20110630: Added pig class to menu
 static MenuItem_t ClassItems[] = {
     {ITT_EFUNC, "FIGHTER", SCClass, 0, MENU_NONE},
     {ITT_EFUNC, "CLERIC", SCClass, 1, MENU_NONE},
-    {ITT_EFUNC, "MAGE", SCClass, 2, MENU_NONE}
+    {ITT_EFUNC, "MAGE", SCClass, 2, MENU_NONE},
+    {ITT_EFUNC, "PIG",  SCClass, 3, MENU_NONE}
 };
 
+// [PIG]: Moved up 10 px
 static Menu_t ClassMenu = {
-    66, 66,
+    66, 56,
     DrawClassMenu,
-    3, ClassItems,
+    4, ClassItems,
     0,
     MENU_MAIN
 };
@@ -603,21 +606,34 @@
 static void DrawClassMenu(void)
 {
     pclass_t class;
-    static char *boxLumpName[3] = {
+    // [PIG] haleyjd 20110630: Allow choosing pig from menu
+    static char *boxLumpName[4] = {
         "m_fbox",
         "m_cbox",
-        "m_mbox"
+        "m_mbox",
+        "m_fbox"  // PIG-FIXME: needs his own box really...
     };
-    static char *walkLumpName[3] = {
+    static char *walkLumpName[4] = {
         "m_fwalk1",
         "m_cwalk1",
-        "m_mwalk1"
+        "m_mwalk1",
+        "pigya1"
     };
+    int px = 0, py = 0; // [PIG]
 
-    MN_DrTextB("CHOOSE CLASS:", 34, 24);
+    MN_DrTextB("CHOOSE CLASS:", 34, 14); // [PIG]: Moved up 10px
     class = (pclass_t) CurrentMenu->items[CurrentItPos].option;
+
+    // [PIG] haleyjd 20110630:
+    // The graphics used for the pig need some positional adjustment
+    if(class == PCLASS_PIG)
+    {
+        px = 32;
+        py = 65;
+    }
+
     V_DrawPatch(174, 8, W_CacheLumpName(boxLumpName[class], PU_CACHE));
-    V_DrawPatch(174 + 24, 8 + 12,
+    V_DrawPatch(174 + 24 + px, 8 + 12 + py,
                 W_CacheLumpNum(W_GetNumForName(walkLumpName[class])
                                + ((MenuTime >> 3) & 3), PU_CACHE));
 }
@@ -1000,6 +1016,14 @@
             SkillItems[3].text = "WARLOCK";
             SkillItems[4].text = "ARCHIMAGE";
             break;
+        case PCLASS_PIG: // [PIG] haleyjd 20110630: PIG!
+            SkillMenu.x = 100;
+            SkillItems[0].text = "SQUEALER";
+            SkillItems[1].text = "OINKER";
+            SkillItems[2].text = "PORKCHOPS";
+            SkillItems[3].text = "SAUSAGE";
+            SkillItems[4].text = "MAKIN' BACON";
+            break;
     }
     SetMenu(MENU_SKILL);
 }
Index: src/hexen/p_enemy.c
===================================================================
--- src/hexen/p_enemy.c	(revision 2352)
+++ src/hexen/p_enemy.c	(working copy)
@@ -1484,7 +1484,7 @@
     S_StopSound(actor);
     if (actor->player)
     {
-        if (actor->player->morphTics)
+        if (actor->player->morphTics || actor->player->class == PCLASS_PIG) // [PIG]
         {
             S_StartSound(actor, actor->info->deathsound);
         }
@@ -1665,7 +1665,10 @@
             damage = 10;
             break;
         case MT_DRAGON_FX2:
-            damage = 80;
+            if(actor->target && actor->target->player) // [PIG]: Fired by player?
+                damage = 20;
+            else    
+                damage = 80;
             damageSelf = false;
             break;
         case MT_MSTAFF_FX:
Index: src/hexen/p_inter.c
===================================================================
--- src/hexen/p_inter.c	(revision 2352)
+++ src/hexen/p_inter.c	(working copy)
@@ -559,7 +559,7 @@
     int max;
 
     max = MAXHEALTH;
-    if (player->morphTics)
+    if (player->morphTics) // [PIG] haleyjd 20110630: true pclass not subject
     {
         max = MAXMORPHHEALTH;
     }
@@ -578,6 +578,22 @@
 
 //---------------------------------------------------------------------------
 //
+// FUNC P_AutoArmorSave
+//
+// [PIG] haleyjd 20110702: Pig player class gains basic AC when levels up
+//
+//---------------------------------------------------------------------------
+
+int P_AutoArmorSave(player_t *player)
+{
+    if(player->class != PCLASS_PIG || player->morphTics)
+        return AutoArmorSave[player->class];
+    else
+        return ((player->level + 1) / 2) * FRACUNIT; // Every 10 levels == 1 AC
+}
+
+//---------------------------------------------------------------------------
+//
 // FUNC P_GiveArmor
 //
 // Returns false if the armor is worse than the current armor.
@@ -610,7 +626,7 @@
             + player->armorpoints[ARMOR_SHIELD]
             + player->armorpoints[ARMOR_HELMET]
             + player->armorpoints[ARMOR_AMULET]
-            + AutoArmorSave[player->class];
+            + P_AutoArmorSave(player); // [PIG]: functionalized
         if (totalArmor < ArmorMax[player->class] * 5 * FRACUNIT)
         {
             player->armorpoints[armortype] += hits;
@@ -1293,7 +1309,98 @@
     return (NULL);
 }
 
+//
+// P_MaxExpForLevel
+//
+// haleyjd 20110701: [PIG] New function
+// Calculate the max experience for a level.
+// Note the displayed level is +1 from the internal level (starts @ 0)
+//
+int P_MaxExpForLevel(int level)
+{
+    int   accExp    = 875;    // EXP needed for lv 2 (kill 5 Ettins)
+    int   maxExp    = accExp;
+    float expFactor;
+    int   i;
 
+    for(i = 0; i < level; i++)
+    {
+        expFactor = 1.25f - ((float)i/1.8f * 0.0125f); // declining exponent
+        if(expFactor < 1.0f)
+            expFactor = 1.0f;
+        accExp = (int)(accExp * expFactor);
+        maxExp += accExp;
+    }
+
+    return maxExp;
+}
+
+//
+// P_GiveExperience
+//
+// haleyjd 20110701: [PIG] New function
+//
+void P_GiveExperience(player_t *player, mobj_t *victim)
+{
+    boolean leveledUp = false;
+
+    // No experience for self-kills :P
+    if(player == victim->player)
+        return;
+
+    // Experience scale is based on spawnhealth for simplicity
+    if(victim->flags & MF_COUNTKILL)
+    {
+        switch(victim->type)
+        {
+        case MT_CENTAURLEADER: // Leader Bonus (same HP, but tougher)
+        case MT_SERPENTLEADER:
+            player->experience += victim->info->spawnhealth + 50;
+            break;
+        case MT_WRAITH:  // Late Hub Monsters Bonus
+        case MT_WRAITHB: // These guys' HP don't reflect the difficulty that the
+        case MT_BISHOP:  // pig has with them. Plus he needs more EXP :P
+            player->experience += victim->info->spawnhealth*4;
+            break;
+        case MT_DRAGON: // Dragon Bonus
+            player->experience += victim->info->spawnhealth*3;
+            break;
+        case MT_FIGHTER_BOSS: // PClass Boss Bonus
+        case MT_CLERIC_BOSS:
+        case MT_MAGE_BOSS:
+            player->experience += victim->info->spawnhealth*10; // 8000 a piece
+            break;
+        case MT_SORCBOSS: // Heresiarch Bonus
+        case MT_KORAX:    // Korax Bonus (heh...)
+            player->experience += victim->info->spawnhealth*2; // 10000 smackaroos
+            break;
+        default:
+            player->experience += victim->info->spawnhealth;
+            break;
+        }
+    }
+    else
+        player->experience += 50; // Anything non-COUNTKILL gives 50 EXP :P
+
+    if(victim->player && victim->player->class != PCLASS_PIG)
+        player->experience += 10000; // non-pig player kill bonus
+
+    while(player->experience >= P_MaxExpForLevel(player->level))
+    {
+        player->level++;
+        leveledUp = true;
+    }
+
+    if(leveledUp)
+    {
+        static char msg[128];
+        
+        snprintf(msg, sizeof(msg), "PIG IS NOW LEVEL %d!", player->level+1);
+        P_SetMessage(player, msg, true);
+        S_StartSound(player->mo, SFX_PUZZLE_SUCCESS); // FLONG!
+    }
+}
+
 //---------------------------------------------------------------------------
 //
 // PROC P_KillMobj
@@ -1346,7 +1453,20 @@
                 }
             }
         }
+        // [PIG] haleyjd 20110702: Award experience to pigs
+        if(source->player->class == PCLASS_PIG && !source->player->morphTics)
+            P_GiveExperience(source->player, target);
     }
+    // [PIG] haleyjd 20110702: Pig gets EXP for Dark Servant kills too
+    if (source && source->type == MT_MINOTAUR)
+    {
+        master = (mobj_t *) source->special1;
+        if (master->health > 0 && master->player && 
+            master->player->class == PCLASS_PIG && !master->player->morphTics)
+        {
+            P_GiveExperience(master->player, target);
+        }
+    }
     if (target->player)
     {                           // Player death
         if (!source)
@@ -1571,7 +1691,7 @@
     {                           // Immune when invulnerable
         return (false);
     }
-    if (player->morphTics)
+    if (player->morphTics || player->class == PCLASS_PIG) // [PIG]: can't morph, already a pig
     {                           // Player is already a beast
         return false;
     }
@@ -1906,7 +2026,10 @@
                     {
                         P_PoisonDamage(target->player, source, 15 + (P_Random() & 15), false);  // Don't play painsound
                         P_PoisonPlayer(target->player, source, 50);
-                        S_StartSound(target, SFX_PLAYER_POISONCOUGH);
+                        if(target->player->class != PCLASS_PIG) // [PIG] haleyjd: actually a bug in Hexen...
+                          S_StartSound(target, SFX_PLAYER_POISONCOUGH);
+                        else
+                          S_StartSound(target, SFX_PIG_PAIN);
                     }
                     return;
                 }
@@ -1950,7 +2073,7 @@
     //
     if (player)
     {
-        savedPercent = AutoArmorSave[player->class]
+        savedPercent = P_AutoArmorSave(player) // [PIG]: functionalized
             + player->armorpoints[ARMOR_ARMOR] +
             player->armorpoints[ARMOR_SHIELD] +
             player->armorpoints[ARMOR_HELMET] +
@@ -2017,7 +2140,7 @@
         {                       // check for special fire damage or ice damage deaths
             if (inflictor->flags2 & MF2_FIREDAMAGE)
             {
-                if (player && !player->morphTics)
+                if (player && !player->morphTics && player->class != PCLASS_PIG) // [PIG]
                 {               // Check for flame death
                     if (target->health > -50 && damage > 25)
                     {
@@ -2220,7 +2343,7 @@
     if (target->health <= 0)
     {                           // Death
         target->special1 = damage;
-        if (player && inflictor && !player->morphTics)
+        if (player && inflictor && !player->morphTics && player->class != PCLASS_PIG) // [PIG]
         {                       // Check for flame death
             if ((inflictor->flags2 & MF2_FIREDAMAGE)
                 && (target->health > -50) && (damage > 25))
Index: src/hexen/p_map.c
===================================================================
--- src/hexen/p_map.c	(revision 2352)
+++ src/hexen/p_map.c	(working copy)
@@ -2036,6 +2036,9 @@
                         case PCLASS_MAGE:
                             sound = SFX_PUZZLE_FAIL_MAGE;
                             break;
+                        case PCLASS_PIG: // [PIG] haleyjd 20110630: PIG!
+                            sound = SFX_PIG_PAIN;
+                            break;
                         default:
                             sound = SFX_NONE;
                             break;
Index: src/hexen/p_mobj.c
===================================================================
--- src/hexen/p_mobj.c	(revision 2352)
+++ src/hexen/p_mobj.c	(working copy)
@@ -823,12 +823,15 @@
                             case PCLASS_MAGE:
                                 S_StartSound(mo, SFX_PLAYER_MAGE_GRUNT);
                                 break;
+                            case PCLASS_PIG: // [PIG] haleyjd 20110630: Pig!
+                                S_StartSound(mo, SFX_PIG_ACTIVE2);
+                                break;
                             default:
                                 break;
                         }
                     }
                     else if ((P_GetThingFloorType(mo) < FLOOR_LIQUID) &&
-                             (!mo->player->morphTics))
+                             (!mo->player->morphTics) && mo->player->class != PCLASS_PIG) // [PIG]
                     {
                         S_StartSound(mo, SFX_PLAYER_LAND);
                     }
@@ -1042,11 +1045,13 @@
             case PCLASS_MAGE:
                 S_StartSound(mo, SFX_PLAYER_MAGE_GRUNT);
                 break;
+            case PCLASS_PIG:
+                S_StartSound(mo, SFX_PIG_ACTIVE2);
             default:
                 break;
         }
     }
-    else if (!mo->player->morphTics)
+    else if (!mo->player->morphTics && mo->player->class != PCLASS_PIG) // [PIG]
     {
         S_StartSound(mo, SFX_PLAYER_LAND);
     }
@@ -1317,10 +1322,10 @@
     z = ONFLOORZ;
     if (randomclass && deathmatch)
     {
-        p->class = P_Random() % 3;
+        p->class = P_Random() % 4; // [PIG] haleyjd 20110630
         if (p->class == PlayerClass[mthing->type - 1])
         {
-            p->class = (p->class + 1) % 3;
+            p->class = (p->class + 1) % 4;
         }
         PlayerClass[mthing->type - 1] = p->class;
         SB_SetClassData();
@@ -1340,6 +1345,10 @@
         case PCLASS_MAGE:
             mobj = P_SpawnMobj(x, y, z, MT_PLAYER_MAGE);
             break;
+        case PCLASS_PIG: // [PIG] haleyjd 20110630: allow spawn
+            mobj = P_SpawnMobj(x, y, z, MT_PIGPLAYER);
+            mobj->flags |= MF_PICKUP; // allow true pclass pigs to collect items
+            break;
         default:
             I_Error("P_SpawnPlayer: Unknown class type");
             return;
@@ -1356,7 +1365,7 @@
             mobj->flags |= 2 << MF_TRANSSHIFT;
         }
     }
-    else if (mthing->type > 1)
+    else if (mthing->type > 1 && p->class != PCLASS_PIG) // [PIG]
     {                           // Set color translation bits for player sprites
         mobj->flags |= (mthing->type - 1) << MF_TRANSSHIFT;
     }
@@ -1375,7 +1384,15 @@
     p->extralight = 0;
     p->fixedcolormap = 0;
     p->viewheight = VIEWHEIGHT;
-    P_SetupPsprites(p);
+    if(p->class == PCLASS_PIG) // [PIG] haleyjd 20110701
+    {
+        p->pendingweapon = WP_NOCHANGE;
+        p->psprites[ps_weapon].sy = /*WEAPONTOP*/32*FRACUNIT;
+        p->readyweapon = WP_FIRST;
+        P_SetPsprite(p, ps_weapon, S_SNOUTREADY);
+    }
+    else
+        P_SetupPsprites(p);
     if (deathmatch)
     {                           // Give all keys in death match mode
         p->keys = 2047;
@@ -1399,7 +1416,8 @@
     static unsigned int classFlags[] = {
         MTF_FIGHTER,
         MTF_CLERIC,
-        MTF_MAGE
+        MTF_MAGE,
+        (MTF_FIGHTER|MTF_CLERIC|MTF_MAGE) // [PIG]: Spawn everything
     };
 
     // Count deathmatch start positions
Index: src/hexen/p_pspr.c
===================================================================
--- src/hexen/p_pspr.c	(revision 2352)
+++ src/hexen/p_pspr.c	(working copy)
@@ -531,7 +531,7 @@
         player->attackdown = false;
     }
 
-    if (!player->morphTics)
+    if (!player->morphTics && player->class != PCLASS_PIG) // [PIG]
     {
         // Bob the weapon based on movement speed.
         angle = (128 * leveltime) & FINEMASK;
@@ -572,7 +572,7 @@
 
 void A_Lower(player_t * player, pspdef_t * psp)
 {
-    if (player->morphTics)
+    if (player->morphTics || player->class == PCLASS_PIG) // [PIG]
     {
         psp->sy = WEAPONBOTTOM;
     }
@@ -706,20 +706,70 @@
     int slope;
 
     damage = 3 + (P_Random() & 3);
+    // [PIG] haleyjd 20110702: Snout attack gains damage as level increases
+    if(!player->morphTics) // Not a morphed pig?
+        damage += player->level * 4 / 3;
+
     angle = player->mo->angle;
     slope = P_AimLineAttack(player->mo, angle, MELEERANGE);
+
+    // [PIG] Level 15 bonus: ice attack
+    // [PIG] Level 20 bonus: fire attack
+    if(!player->morphTics &&
+       (!linetarget ||
+        P_AproxDistance(linetarget->x - player->mo->x,
+                        linetarget->y - player->mo->y) > 2*MELEERANGE))
+    {
+        mobj_t *mo;
+        
+        if(player->level >= 19 && player->mana[MANA_2] >= 2)
+        {
+            player->mana[MANA_2] -= 2;
+
+            mo = P_SpawnPlayerMissile(player->mo, MT_DRAGON_FX);
+            if(mo)
+               mo->z -= 10*FRACUNIT;
+        }
+        else if(player->level >= 14 && player->mana[MANA_1] >= 1)
+        {
+            player->mana[MANA_1]--;
+        
+            mo = P_SpawnPlayerMissile(player->mo, MT_SHARDFX1);
+            if(mo)
+            {
+                mo->z -= 10*FRACUNIT;
+                mo->args[0] = 3;
+            }
+        }
+    }
+
     PuffType = MT_SNOUTPUFF;
     PuffSpawned = NULL;
+    if(!player->morphTics && player->level >= 14) // [PIG]: Ice bite
+        player->mo->flags2 |= MF2_ICEDAMAGE;
     P_LineAttack(player->mo, angle, MELEERANGE, slope, damage);
+    player->mo->flags2 &= ~MF2_ICEDAMAGE;
     S_StartSound(player->mo, SFX_PIG_ACTIVE1 + (P_Random() & 1));
     if (linetarget)
     {
         AdjustPlayerAngle(player->mo);
-//              player->mo->angle = R_PointToAngle2(player->mo->x,
-//                      player->mo->y, linetarget->x, linetarget->y);
         if (PuffSpawned)
         {                       // Bit something
             S_StartSound(player->mo, SFX_PIG_ATTACK);
+            
+            if(!player->morphTics &&   // [PIG] Lv 30 bonus: drain mana
+                player->level >= 29 &&
+                (linetarget->flags & MF_COUNTKILL) &&
+                linetarget->health > 0)
+
+            {
+                player->mana[MANA_1] += 2;
+                player->mana[MANA_2] += 2;
+                if(player->mana[MANA_1] > 200)
+                    player->mana[MANA_1] = 200;
+                if(player->mana[MANA_2] > 200)
+                    player->mana[MANA_2] = 200;
+            }
         }
     }
 }
Index: src/hexen/p_setup.c
===================================================================
--- src/hexen/p_setup.c	(revision 2352)
+++ src/hexen/p_setup.c	(working copy)
@@ -686,8 +686,8 @@
 
     for (i = 0; i < MAXPLAYERS; i++)
     {
-        players[i].killcount = players[i].secretcount
-            = players[i].itemcount = 0;
+        // [PIG] haleyjd 20110701: don't clear EXP, LV; only mostly unused secretcount
+        players[i].secretcount = 0;
     }
     players[consoleplayer].viewz = 1;   // will be set by player think
  
Index: src/hexen/p_user.c
===================================================================
--- src/hexen/p_user.c	(revision 2352)
+++ src/hexen/p_user.c	(working copy)
@@ -170,7 +170,7 @@
         }
     }
 
-    if (player->morphTics)
+    if (player->morphTics || player->class == PCLASS_PIG) // [PIG]
     {
         player->viewz = player->mo->z + player->viewheight - (20 * FRACUNIT);
     }
@@ -452,11 +452,14 @@
 //
 //----------------------------------------------------------------------------
 
+extern int leveltime;
+
 void P_MorphPlayerThink(player_t * player)
 {
     mobj_t *pmo;
 
-    if (player->morphTics & 15)
+    if (player->morphTics & 15 ||
+        (player->class == PCLASS_PIG && (leveltime & 15))) // [PIG]
     {
         return;
     }
@@ -521,6 +524,11 @@
     int oldFlags2;
     int oldBeast;
 
+    // [PIG] haleyjd 20110630: Pig-class players don't unmorph
+    playerNum = P_GetPlayerNum(player);
+    if(PlayerClass[playerNum] == PCLASS_PIG)
+        return false;
+
     pmo = player->mo;
     x = pmo->x;
     y = pmo->y;
@@ -531,7 +539,7 @@
     oldFlags2 = pmo->flags2;
     oldBeast = pmo->type;
     P_SetMobjState(pmo, S_FREETARGMOBJ);
-    playerNum = P_GetPlayerNum(player);
+
     switch (PlayerClass[playerNum])
     {
         case PCLASS_FIGHTER:
@@ -652,7 +660,7 @@
     {
         player->jumpTics--;
     }
-    if (player->morphTics)
+    if (player->morphTics || player->class == PCLASS_PIG) // [PIG] haleyjd 20110630
     {
         P_MorphPlayerThink(player);
     }
@@ -753,7 +761,7 @@
     {                           // Use an artifact
         if ((cmd->arti & AFLAG_JUMP) && onground && !player->jumpTics)
         {
-            if (player->morphTics)
+            if (player->morphTics) // [PIG]: Deliberately not subject, for playability
             {
                 player->mo->momz = 6 * FRACUNIT;
             }
@@ -787,7 +795,7 @@
     {                           // A special event has no other buttons
         cmd->buttons = 0;
     }
-    if (cmd->buttons & BT_CHANGE && !player->morphTics)
+    if (cmd->buttons & BT_CHANGE && !player->morphTics) // [PIG]: TODO...
     {
         // The actual changing of the weapon is done when the weapon
         // psprite can do it (A_WeaponReady), so it doesn't happen in
@@ -1319,7 +1327,13 @@
                     S_StartSound(mo, SFX_MYSTICINCANT);
                 }
                 break;
-            case PCLASS_PIG:
+            case PCLASS_PIG: // [PIG] Turn everyone into pigs!
+                if(P_MorphPlayer(mo->player))
+                {
+                    effective = true;
+                    S_StartSound(mo, SFX_MYSTICINCANT);
+                }
+                break;
             default:
                 break;
         }
@@ -1533,7 +1547,8 @@
             break;
         case arti_poisonbag:
             angle = player->mo->angle >> ANGLETOFINESHIFT;
-            if (player->class == PCLASS_CLERIC)
+            if (player->class == PCLASS_CLERIC ||
+                player->class == PCLASS_PIG) // [PIG]: Changed to use Cleric's flechettes
             {
                 mo = P_SpawnMobj(player->mo->x + 16 * finecosine[angle],
                                  player->mo->y + 24 * finesine[angle],
@@ -1556,7 +1571,7 @@
                 }
             }
             else                // PCLASS_FIGHTER, obviously (also pig, not so obviously)
-            {
+            {                   // [PIG] Not any more; see above.
                 mo = P_SpawnMobj(player->mo->x, player->mo->y,
                                  player->mo->z - player->mo->floorclip +
                                  35 * FRACUNIT, MT_THROWINGBOMB);
Index: src/hexen/sb_bar.c
===================================================================
--- src/hexen/sb_bar.c	(revision 2352)
+++ src/hexen/sb_bar.c	(working copy)
@@ -61,6 +61,7 @@
 static void DrawInventoryBar(void);
 static void DrawKeyBar(void);
 static void DrawWeaponPieces(void);
+static void DrawPigExperience(void); // [PIG]
 static void DrawFullScreenStuff(void);
 static void DrawAnimatedIcons(void);
 static boolean HandleCheats(byte key);
@@ -98,6 +99,7 @@
 
 extern int ArmorIncrement[NUMCLASSES][NUMARMOR];
 extern int AutoArmorSave[NUMCLASSES];
+int P_AutoArmorSave(player_t *player);
 
 // haleyjd FIXME: CDMUSIC
 #ifdef __WATCOMC__
@@ -341,6 +343,11 @@
     int class;
 
     class = PlayerClass[consoleplayer]; // original player class (not pig)
+
+    // [PIG] haleyjd 20110630: use Cleric's resources here
+    if(class == PCLASS_PIG)
+        class = PCLASS_CLERIC;
+
     PatchWEAPONSLOT = W_CacheLumpNum(W_GetNumForName("wpslot0")
                                      + class, PU_STATIC);
     PatchWEAPONFULL = W_CacheLumpNum(W_GetNumForName("wpfull0")
@@ -733,6 +740,10 @@
 static int oldweapon = -1;
 static int oldkeys = -1;
 
+// [PIG]
+static int oldlevel = -1;
+static int oldexperience = -1;
+
 extern boolean automapactive;
 
 void SB_Drawer(void)
@@ -778,6 +789,9 @@
                 oldlife = -1;
                 oldweapon = -1;
                 oldkeys = -1;
+                oldlevel = -1;          // [PIG]: reset LV/EXP
+                oldexperience = -1;
+
             }
             if (!automapactive)
             {
@@ -1185,7 +1199,7 @@
         UpdateState |= I_STATBAR;
     }
     // Armor
-    temp = AutoArmorSave[CPlayer->class]
+    temp = P_AutoArmorSave(CPlayer) // [PIG]: functionalized
         + CPlayer->armorpoints[ARMOR_ARMOR] +
         CPlayer->armorpoints[ARMOR_SHIELD] +
         CPlayer->armorpoints[ARMOR_HELMET] +
@@ -1197,8 +1211,19 @@
         DrINumber(FixedDiv(temp, 5 * FRACUNIT) >> FRACBITS, 250, 176);
         UpdateState |= I_STATBAR;
     }
+    // [PIG] haleyjd 20110702: exp/lv display
+    if(CPlayer->class == PCLASS_PIG && !CPlayer->morphTics)
+    {
+        if(oldlevel != CPlayer->level || oldexperience != CPlayer->experience)
+        {
+            DrawPigExperience();
+            oldlevel = CPlayer->level;
+            oldexperience = CPlayer->experience;
+            UpdateState |= I_STATBAR;
+        }
+    }
     // Weapon Pieces
-    if (oldpieces != CPlayer->pieces)
+    else if (oldpieces != CPlayer->pieces)
     {
         DrawWeaponPieces();
         oldpieces = CPlayer->pieces;
@@ -1278,7 +1303,7 @@
         oldkeys = CPlayer->keys;
         UpdateState |= I_STATBAR;
     }
-    temp = AutoArmorSave[CPlayer->class]
+    temp = P_AutoArmorSave(CPlayer) // [PIG]: functionalized
         + CPlayer->armorpoints[ARMOR_ARMOR] +
         CPlayer->armorpoints[ARMOR_SHIELD] +
         CPlayer->armorpoints[ARMOR_HELMET] +
@@ -1354,6 +1379,47 @@
 
 //==========================================================================
 //
+// DrawPigExperience
+//
+// [PIG] haleyjd 20110702: called instead of the above routine for pig mode.
+//
+//==========================================================================
+
+static void DrSmallNumberUL(int val, int x, int y)
+{
+    char buf[33];
+    char *rover;
+    int result;
+    patch_t *patch;
+
+    sprintf(buf, "%d", val);
+
+    if (val <= 0)
+    {
+        return;
+    }
+
+    for(rover = buf; *rover; rover++)
+    {
+        int i = *rover - '0';
+        patch = PatchSmNumbers[i];
+        V_DrawPatch(x, y, patch);
+        x += 4;
+    } 
+}
+
+extern int P_MaxExpForLevel(int level);
+
+static void DrawPigExperience(void)
+{
+    V_DrawPatch(190, 162, PatchWEAPONSLOT);
+    DrINumber(CPlayer->level+1, 190, 162);
+    DrSmallNumberUL(CPlayer->experience, 220, 176);
+    DrSmallNumberUL(P_MaxExpForLevel(CPlayer->level), 220, 184);
+}
+
+//==========================================================================
+//
 // DrawFullScreenStuff
 //
 //==========================================================================
@@ -1651,7 +1717,7 @@
 
 static void CheatHealthFunc(player_t * player, Cheat_t * cheat)
 {
-    if (player->morphTics)
+    if (player->morphTics) // [PIG] haleyjd 20110630: true pclass not subject 
     {
         player->health = player->mo->health = MAXMORPHHEALTH;
     }
@@ -1770,6 +1836,13 @@
 {
     extern boolean P_UndoPlayerMorph(player_t * player);
 
+    // [PIG] haleyjd 20110630: unnecessary cheat ;)
+    if(player->class == PCLASS_PIG)
+    {
+        P_SetMessage(player, "YOU'RE ALREADY A PIG!", true);
+        return;
+    }
+
     if (player->morphTics)
     {
         P_UndoPlayerMorph(player);
@@ -1824,7 +1897,7 @@
 
 static void CheatClassFunc1(player_t * player, Cheat_t * cheat)
 {
-    P_SetMessage(player, "ENTER NEW PLAYER CLASS (0 - 2)", true);
+    P_SetMessage(player, "ENTER NEW PLAYER CLASS (0 - 3)", true);
 }
 
 static void CheatClassFunc2(player_t * player, Cheat_t * cheat)
@@ -1840,7 +1913,7 @@
         return;
     }
     class = args[0] - '0';
-    if (class > 2 || class < 0)
+    if (class > 3 || class < 0) // [PIG] Allow pig
     {
         P_SetMessage(player, "INVALID PLAYER CLASS", true);
         return;
