org.tdod.ether.taimpl.player.DefaultPlayer.java Source code

Java tutorial

Introduction

Here is the source code for org.tdod.ether.taimpl.player.DefaultPlayer.java

Source

/****************************************************************************
 * Ether Code base, version 1.0                                             *
 *==========================================================================*
 * Copyright (C) 2011 by Ron Kinney                                         *
 * All rights reserved.                                                     *
 *                                                                          *
 * This program is free software; you can redistribute it and/or modify     *
 * it under the terms of the GNU General Public License as published        *
 * by the Free Software Foundation; either version 2 of the License, or     *
 * (at your option) any later version.                                      *
 *                                                                          *
 * This program is distributed in the hope that it will be useful,          *
 * but WITHOUT ANY WARRANTY; without even the implied warranty of           *
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the            *
 * GNU General Public License for more details.                             *
 *                                                                          *
 * Redistribution and use in source and binary forms, with or without       *
 * modification, are permitted provided that the following conditions are   *
 * met:                                                                     *
 *                                                                          *
 * * Redistributions of source code must retain this copyright notice,      *
 *   this list of conditions and the following disclaimer.                  *
 * * Redistributions in binary form must reproduce this copyright notice    *
 *   this list of conditions and the following disclaimer in the            *
 *   documentation and/or other materials provided with the distribution.   *
 *                                                                          *
 *==========================================================================*
 * Ron Kinney (ronkinney@gmail.com)                                         *
 * Ether Homepage:   http://tdod.org/ether                                  *
 ****************************************************************************/

package org.tdod.ether.taimpl.player;

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.io.ObjectStreamException;
import java.io.Serializable;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.Formatter;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.zip.GZIPOutputStream;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.tdod.ether.ta.Entity;
import org.tdod.ether.ta.EntityMessageHandler;
import org.tdod.ether.ta.EntityType;
import org.tdod.ether.ta.cosmos.Exit;
import org.tdod.ether.ta.cosmos.Room;
import org.tdod.ether.ta.cosmos.enums.MoveFailCode;
import org.tdod.ether.ta.items.Item;
import org.tdod.ether.ta.items.ItemType;
import org.tdod.ether.ta.items.RuneScroll;
import org.tdod.ether.ta.items.armor.Armor;
import org.tdod.ether.ta.items.equipment.Equipment;
import org.tdod.ether.ta.items.weapons.Weapon;
import org.tdod.ether.ta.manager.PlayerFacade;
import org.tdod.ether.ta.manager.WorldManager;
import org.tdod.ether.ta.mobs.Mob;
import org.tdod.ether.ta.output.GameOutput;
import org.tdod.ether.ta.player.Attacks;
import org.tdod.ether.ta.player.BaseStats;
import org.tdod.ether.ta.player.Encumbrance;
import org.tdod.ether.ta.player.Mana;
import org.tdod.ether.ta.player.Player;
import org.tdod.ether.ta.player.PlayerClassData;
import org.tdod.ether.ta.player.RaceData;
import org.tdod.ether.ta.player.Spellbook;
import org.tdod.ether.ta.player.Stat;
import org.tdod.ether.ta.player.Vitality;
import org.tdod.ether.ta.player.enums.Complexion;
import org.tdod.ether.ta.player.enums.EyeColor;
import org.tdod.ether.ta.player.enums.HairColor;
import org.tdod.ether.ta.player.enums.HairLength;
import org.tdod.ether.ta.player.enums.HairStyle;
import org.tdod.ether.ta.player.enums.InventoryFailCode;
import org.tdod.ether.ta.player.enums.PlayerClass;
import org.tdod.ether.ta.player.enums.PlayerStateEnum;
import org.tdod.ether.ta.player.enums.RaceEnum;
import org.tdod.ether.ta.player.enums.Rune;
import org.tdod.ether.ta.player.enums.Status;
import org.tdod.ether.taimpl.commands.DoDefault;
import org.tdod.ether.taimpl.commands.handler.HandlePromotion;
import org.tdod.ether.taimpl.cosmos.ExitDirectionEnum;
import org.tdod.ether.taimpl.cosmos.enums.TriggerResult;
import org.tdod.ether.taimpl.items.equipment.enums.EquipmentSubType;
import org.tdod.ether.taimpl.mobs.enums.Gender;
import org.tdod.ether.util.Dice;
import org.tdod.ether.util.GameUtil;
import org.tdod.ether.util.MessageManager;
import org.tdod.ether.util.MyStringUtil;
import org.tdod.ether.util.PropertiesManager;
import org.tdod.ether.util.TaMessageManager;

/**
 * This is the default implementation class of a player.
 * @author Ron Kinney
 */
public class DefaultPlayer implements Player, Serializable {

    private static final long serialVersionUID = -5236115285008480704L;

    private static Log _log = LogFactory.getLog(DefaultPlayer.class);

    private static final int STARTING_ROOM = Integer
            .valueOf(PropertiesManager.getInstance().getProperty(PropertiesManager.STARTING_ROOM));
    private static final int DEATH_ROOM_AWAKEN = Integer
            .valueOf(PropertiesManager.getInstance().getProperty(PropertiesManager.DEATH_ROOM_AWAKEN));
    private static final int REROLL_REST_TIME = Integer
            .valueOf(PropertiesManager.getInstance().getProperty(PropertiesManager.REROLL_REST_TIME));
    private static final int MAX_INVENTORY_GOLD = Integer
            .valueOf(PropertiesManager.getInstance().getProperty(PropertiesManager.MAX_INVENTORY_GOLD));
    private static final int MAX_TRAILING_LEVEL = Integer
            .valueOf(PropertiesManager.getInstance().getProperty(PropertiesManager.MAX_TRAILING_LEVEL));
    private static final int PLAYER_SPELL_HIT_DIFFICULTY = Integer
            .valueOf(PropertiesManager.getInstance().getProperty(PropertiesManager.PLAYER_SPELL_HIT_DIFFICULTY))
            .intValue();
    private static final int MAX_THIRST = new Integer(
            PropertiesManager.getInstance().getProperty(PropertiesManager.MAX_HUNGER)).intValue();
    private static final int MAX_HUNGER = new Integer(
            PropertiesManager.getInstance().getProperty(PropertiesManager.MAX_HUNGER)).intValue();

    private boolean _isSysop;
    private int _pnum;
    private String _name = null;
    private RaceEnum _race = null;
    private Complexion _complexion = null;
    private EyeColor _eyeColor = null;
    private HairColor _hairColor = null;
    private HairLength _hairLength = null;
    private HairStyle _hairStyle = null;
    private PlayerClass _playerClass = null;
    private int _level;
    private long _experience;
    private Rune _rune = Rune.NONE;
    private BaseStats _stats = new DefaultBaseStats();
    private Mana _mana = new DefaultMana();
    private Vitality _vitality = new DefaultVitality();
    private Status _status = Status.HEALTHY;
    private int _poisonDamage = 0;
    private Encumbrance _encumbrance = new DefaultEncumbrance(this);
    private int _hungerTicker;
    private int _thirstTicker;
    private int _restTicker;
    private int _combatTicker;
    private int _mentalExhaustionTicker;
    private int _gold;
    private Gender _gender = Gender.MALE;
    private PlayerStateEnum _state = PlayerStateEnum.LOGIN;
    private Spellbook _spellbook = new DefaultSpellbook();
    private int _gameOfChanceCount;
    private String _password;
    private Attacks _attacks = new DefaultAttacks(this);
    private int _vaultBalance;
    private ArrayList<Item> _inventory = new ArrayList<Item>();
    private Weapon _weapon;
    private Armor _armor;
    private boolean _promoted;

    // Spell Buffs -- may want to move this into another object at a later time.
    private int _regenerationTicker = 0;
    private int _magicProtectionTimer = 0;
    private int _magicProtection = 0;
    private int _invisibilityTimer = 0;
    private int _paralysisTimer = 0;

    // Used for saving the player.  Just need to save the number, not the entire object.
    private int _roomVnumSave;

    private transient long _lastMove = 0;
    private transient long _lastGlobalChat = 0;
    private transient int _globalChatCount = 0;
    private transient boolean _isTripping = false;
    private transient boolean _ignoreTrip = false;
    private transient boolean _ignoreSustenance = false;
    private transient Room _room;
    private transient Entity _groupLeader = this;
    private transient ArrayList<Entity> _groupList = new ArrayList<Entity>();
    private transient boolean _isFollowingGroup;
    private transient Player _dragging;
    private transient Player _draggedBy;

    private transient HashMap<Room, ExitDirectionEnum> _trackingMap = new HashMap<Room, ExitDirectionEnum>();
    private transient GameOutput _output;
    private transient boolean _disconnected;

    private transient EntityMessageHandler _messageHandler = new PlayerMessageHandler(this);

    /**
     * Creates a new DefaultPlayer.
     */
    public DefaultPlayer() {
    }

    //**********
    // Methods inherited from Player.
    //**********

    /**
     * Sets the players password.
     * @param password the players password.
     */
    public void setPassword(String password) {
        _password = GameUtil.byteArrayToHexString(GameUtil.computeHash(password));
    }

    /**
     * Gets the players password.
     * @return the players password.
     */
    public String getPassword() {
        return _password;
    }

    /**
     * Checks if this player is a sysop.
     * @return true if this player is a sysop.
     */
    public boolean isSysop() {
        return _isSysop;
    }

    /**
     * Sets the sysop flag.
     * @param isAdmin the sysop flag.
     */
    public void setIsSysop(boolean isAdmin) {
        _isSysop = isAdmin;
    }

    /**
     * Gets the players unique number.
     * @return the players unique number.
     */
    public int getPnum() {
        return _pnum;
    }

    /**
     * Sets the players unique number.
     * @param pnum the players unique number.
     */
    public void setPnum(int pnum) {
        _pnum = pnum;
    }

    /**
     * Gets the race.
     * @return the race.
     */
    public RaceEnum getRace() {
        return _race;
    }

    /**
     * Sets the race.
     * @param race the race.
     */
    public void setRace(RaceEnum race) {
        _race = race;
    }

    /**
     * Gets the complexion.
     * @return the complexion.
     */
    public Complexion getComplexion() {
        return _complexion;
    }

    /**
     * Sets the complexion.
     * @param complexion the complexion.
     */
    public void setComplexion(Complexion complexion) {
        _complexion = complexion;
    }

    /**
     * Gets the eye color.
     * @return the eye color.
     */
    public EyeColor getEyeColor() {
        return _eyeColor;
    }

    /**
     * Sets the eye color.
     * @param eyeColor the eye color.
     */
    public void setEyeColor(EyeColor eyeColor) {
        _eyeColor = eyeColor;
    }

    /**
     * Sets the hair color.
     * @return the hair color.
     */
    public HairColor getHairColor() {
        return _hairColor;
    }

    /**
     * Sets the hair color.
     * @param hairColor the hair color.
     */
    public void setHairColor(HairColor hairColor) {
        _hairColor = hairColor;
    }

    /**
     * Gets the hair color.
     * @return the hair color.
     */
    public HairLength getHairLength() {
        return _hairLength;
    }

    /**
     * Sets the hair length.
     * @param hairLength the hair length.
     */
    public void setHairLength(HairLength hairLength) {
        _hairLength = hairLength;
    }

    /**
     * Sets the hair style.
     * @return the hair style.
     */
    public HairStyle getHairStyle() {
        return _hairStyle;
    }

    /**
     * Sets the hair style.
     * @param hairStyle the hair style.
     */
    public void setHairStyle(HairStyle hairStyle) {
        _hairStyle = hairStyle;
    }

    /**
     * Sets the players class.
     * @param playerClass the players class.
     */
    public void setPlayerClass(PlayerClass playerClass) {
        _playerClass = playerClass;
    }

    /**
     * Gets the experience.
     * @return the experience.
     */
    public long getExperience() {
        return _experience;
    }

    /**
     * Sets the experience.
     * @param experience the experience.
     */
    public void setExperience(long experience) {
        _experience = experience;
    }

    /**
     * Gets the rune.
     * @return the rune.
     */
    public Rune getRune() {
        return _rune;
    }

    /**
     * Sets the rune.
     * @param rune the rune.
     */
    public void setRune(Rune rune) {
        _rune = rune;
    }

    /**
     * Gets the encumbrance.
     * @return the encumbrance.
     */
    public Encumbrance getEncumbrance() {
        return _encumbrance;
    }

    /**
     * Gets an item in the players inventory.  Returns null if none was found.
     * @param str the partial or full item name.
     * @return an item in the players inventory.  null if none was found.
     */
    public Item getItem(String str) {
        for (Item item : _inventory) {
            if (MyStringUtil.contains(item.getName(), str)) {
                return item;
            }
        }
        return null;
    }

    /**
     * Sets every variable for the player back to level one.  This should only be called when
     * the player has just been created.
     */
    public void setDefaults() {
        _log.info("Generating character stats for " + getName());
        rerollStats();
        _room = WorldManager.getRoom(STARTING_ROOM);
        _restTicker = 0;
    }

    /**
     * Rerolls the players stats.
     */
    public void rerollStats() {
        _level = 1;

        getStats().resetEnchants();

        BaseStats minStats = PlayerFacade.getStartingStats().getMinStats();
        BaseStats maxStats = PlayerFacade.getStartingStats().getMaxStats();

        getStats().getIntellect()
                .setValue(Dice.roll(minStats.getIntellect().getValue(), maxStats.getIntellect().getValue()));
        getStats().getKnowledge()
                .setValue(Dice.roll(minStats.getKnowledge().getValue(), maxStats.getKnowledge().getValue()));
        getStats().getPhysique()
                .setValue(Dice.roll(minStats.getPhysique().getValue(), maxStats.getPhysique().getValue()));
        getStats().getStamina()
                .setValue(Dice.roll(minStats.getStamina().getValue(), maxStats.getStamina().getValue()));
        getStats().getAgility()
                .setValue(Dice.roll(minStats.getAgility().getValue(), maxStats.getAgility().getValue()));
        getStats().getCharisma()
                .setValue(Dice.roll(minStats.getCharisma().getValue(), maxStats.getCharisma().getValue()));

        RaceData raceMod = PlayerFacade.getStartingStats().getRaceDatabase().getRaceData(getRace());
        PlayerClassData classMod = PlayerFacade.getStartingStats().getPlayerClassDatabase()
                .getPlayerClassData(getPlayerClass());

        getStats().add(raceMod.getStatModifiers());
        getStats().add(classMod.getStatModifiers());

        int vitality = Dice.roll(PlayerFacade.getStartingStats().getMinVitality(),
                PlayerFacade.getStartingStats().getMaxVitality());
        vitality += raceMod.getVitality();
        vitality += classMod.getVitality();
        vitality += getStats().getStamina().getHpBonus();
        _vitality.setCurVitality(vitality);
        _vitality.setMaxVitality(vitality);

        int raceGold = Dice.roll(raceMod.getMinStartingGold(), raceMod.getMaxStartingGold());
        int classGold = Dice.roll(classMod.getMinStartingGold(), classMod.getMaxStartingGold());

        _gold = raceGold + classGold;
        clearInventory();
        _hungerTicker = MAX_HUNGER;
        _thirstTicker = MAX_THIRST;

        _status = Status.HEALTHY;

        _restTicker = REROLL_REST_TIME;
        _combatTicker = 0;
        _experience = 0;
        _spellbook.getSpells().clear();

        if (_playerClass.equals(PlayerClass.ACOLYTE) || _playerClass.equals(PlayerClass.DRUID)
                || _playerClass.equals(PlayerClass.NECROLYTE) || _playerClass.equals(PlayerClass.SORCEROR)) {
            _mana.setMaxMana(_level * getPlayerClass().getManaIncreasePerLevel());
            _mana.setCurMana(_level * getPlayerClass().getManaIncreasePerLevel());

        }
    }

    /**
     * Gets the current weapon being wielded.
     * @return the wielded weapon.
     */
    public Weapon getWeapon() {
        return _weapon;
    }

    /**
     * Sets the wielded weapon.  This method has no error checking and the previous weapon
     * will be wiped out.
     * @param weapon the weapon to wield.
     */
    public void setWeapon(Weapon weapon) {
        _weapon = weapon;
    }

    /**
     * Gets the currently equipped armor.
     * @return the currently equipped armor.
     */
    public Armor getArmor() {
        return _armor;
    }

    /**
     * Equips the armor.  This method has no error checking and the previous armor
     * will be wiped out.
     * @param armor the armor to equip.
     */
    public void setArmor(Armor armor) {
        _armor = armor;
    }

    /**
     * Returns a string in the format of :
     *
     * 'Name' is a 'Charisma' 'Physique' 'Knowledge'.
     * 'Gender' has a 'complexion', 'eyes', 'color hair', 'style hair', 'length hair'.
     * 'Agility' 'Intellect'
     * 'Gender' is wearing 'armor', is armed with 'weapon' 'health'
     *
     * @return a String in the above format.
     */
    public String getDescription() {
        StringBuffer buffer = new StringBuffer();
        buffer.append(_name);
        buffer.append(" is a ");
        buffer.append(_stats.getCharisma().getDescription());
        buffer.append(_stats.getPhysique().getDescription());

        String knowledgeString = MessageFormat.format(_stats.getKnowledge().getDescription(),
                _gender.getObjective().toLowerCase());
        buffer.append(knowledgeString);

        buffer.append("{3} has a ");
        buffer.append(_complexion.getDescription() + ", ");
        buffer.append(_eyeColor.getDescription() + ", and ");
        buffer.append(_hairColor.getDescription() + ", ");
        buffer.append(_hairStyle.getDescription() + ", ");
        buffer.append(_hairLength.getDescription() + ". ");

        String agilityString = MessageFormat.format(_stats.getAgility().getDescription(),
                _gender.getPronoun().toLowerCase());
        buffer.append(agilityString);

        String intellectString = MessageFormat.format(_stats.getIntellect().getDescription(), _gender.getNoun(),
                _gender.getPronoun().toLowerCase());
        buffer.append(intellectString);

        String wearingString = MessageFormat.format(TaMessageManager.INSP3.getMessage(), _gender.getNoun(),
                _armor.getLongDescription(), _weapon.getLongDescription());
        buffer.append(wearingString);

        buffer.append(_vitality.getDescription(_gender));

        if (!_rune.equals(Rune.NONE)) {
            String runeString = MessageFormat.format(TaMessageManager.BMSG1.getMessage(),
                    _rune.toString().toLowerCase(), _gender.getPronoun().toLowerCase());
            buffer.append(runeString);
        }

        String description = MessageFormat.format(buffer.toString(), _gender.getDescription().toLowerCase(),
                _race.getName().toLowerCase(), getPromotedClass().getName().toLowerCase(), _gender.getNoun());

        return GameUtil.formatStringTo80Columns(description);
    }

    /**
     * Gets the current playing state.
     * @return the playing state.
     */
    public PlayerStateEnum getState() {
        return _state;
    }

    /**
     * Sets the playing state.
     * @param state the playing state.
     */
    public void setState(PlayerStateEnum state) {
        _state = state;
    }

    /**
     * Called when this player dies.
     * @param restTicker the amount of time the player must rest.
     */
    public void handleDeath(int restTicker) {
        _hungerTicker = MAX_HUNGER;
        _thirstTicker = MAX_THIRST;
        _vitality.setCurVitality(_vitality.getMaxVitality());
        _restTicker = restTicker;
        setStatus(Status.HEALTHY);
        teleportToRoom(DEATH_ROOM_AWAKEN);

    }

    /**
     * Sets the game output.
     * @param output the game output.
     */
    public void setOutput(GameOutput output) {
        _output = output;
    }

    /**
     * Gets the disconnected flag of this player.  Set this to true to disconnect the connection
     * the next time the engine recieves the disconnect event.
     * @return true if this player is disconnected.
     */
    public boolean isDisconnected() {
        return _disconnected;
    }

    /**
     * Sets the disconnected flag of this player.  Set this to true to disconnect the connection
     * the next time the engine recieves the disconnect event.
     * @param disconnected the disconnected flag.
     */
    public void setDisconnected(boolean disconnected) {
        _disconnected = disconnected;
    }

    /**
     * Gets the total number of times the player has played a game of chance this level.
     * @return the total number of times the player has played a game of chance this level.
     */
    public int getGameOfChanceCount() {
        return _gameOfChanceCount;
    }

    /**
     * Sets the total number of times the player has played a game of chance this level.
     * @param gameOfChanceCount the total number of times the player has played a game of chance this level.
     */
    public void setGameOfChanceCount(int gameOfChanceCount) {
        _gameOfChanceCount = gameOfChanceCount;
    }

    /**
     * Saves the player.
     */
    public void save() {
        try {
            if (_name == null) {
                return;
            }

            if (_room != null) {
                _roomVnumSave = _room.getRoomNumber();
            } else {
                _roomVnumSave = 1;
            }

            String filename = PropertiesManager.getInstance().getProperty(PropertiesManager.PLAYER_DIR) + _name;
            FileOutputStream fos = new FileOutputStream(filename);
            GZIPOutputStream gzos = new GZIPOutputStream(fos);
            ObjectOutputStream out = new ObjectOutputStream(gzos);
            out.writeObject(this);
            out.flush();
            out.close();
        } catch (IOException e) {
            println("A problem occurred saving your character.  Contact an admin.");
            _log.fatal(e);
        }
    }

    /**
     * Cleans up any resources taken by this object.  This should be called when this player has logged.
     */
    public void cleanup() {
        if (getWeapon() != null) {
            getWeapon().destroy();
        }
        if (getArmor() != null) {
            getArmor().destroy();
        }
        for (Item item : getInventory()) {
            item.destroy();
        }
        for (Mob mob : WorldManager.getMobsInExistance()) {
            if (equals(mob.getChasing())) {
                mob.setChasing(null);
            }
            if (equals(mob.getTamedBy())) {
                mob.setTamedBy(null);
            }
        }
    }

    /**
     * Gets the vault balance.
     * @return the vault balance.
     */
    public int getVaultBalance() {
        return _vaultBalance;
    }

    /**
     * Sets the vault balance.
     * @param vaultBalance the vault balance.
     */
    public void setVaultBalance(int vaultBalance) {
        _vaultBalance = vaultBalance;
    }

    /**
     * Gets the promoted class of the player, if the player was promoted.  Otherwise,
     * return the base class.
     * @return the promoted class of the player, if the player was promoted.  Otherwise,
     * return the base class.
     */
    public PlayerClass getPromotedClass() {
        if (isPromoted()) {
            return getPlayerClass().getPromotedClass();
        } else {
            return getPlayerClass();
        }
    }

    /**
     * Gets the promoted equivalent level.
     * @return the promoted equivalent level.
     */
    public int getPromotedLevel() {
        if (isPromoted()) {
            return getLevel() - HandlePromotion.PROMOTION_LEVEL;
        } else {
            return getLevel();
        }
    }

    /**
     * Sets the last global chat time.
     * @param lastGlobalChat the last global chat time.
     */
    public void setLastGlobalChat(long lastGlobalChat) {
        _lastGlobalChat = lastGlobalChat;
    }

    /**
     * Gets the last global chat time.
     * @return the last global chat time.
     */
    public long getLastGlobalChat() {
        return _lastGlobalChat;
    }

    /**
     * Sets the global chat count.
     * @param globalChatCount the global chat count.
     */
    public void setGlobalChatCount(int globalChatCount) {
        _globalChatCount = globalChatCount;
    }

    /**
     * Gets the global chat count.
     * @return the global chat count.
     */
    public int getGlobalChatCount() {
        return _globalChatCount;
    }

    /**
     * This is a debug command.  This gets the sustenance ignore flag.
     * @return true if sustenance does not affect this player.
     */
    public boolean getIgnoreSustenance() {
        return _ignoreSustenance;
    }

    /**
     * This is a debug command.  This sets the player so that sustenance does not effect him.
     * @param ignoreSustenance The sustenance ignore flag.
     */
    public void setIgnoreSustenance(boolean ignoreSustenance) {
        _ignoreSustenance = ignoreSustenance;
    }

    /**
     * This method clears the players inventory of all objects.  All resources should
     * be cleaned up within this call.
     */
    public void clearInventory() {
        List<Item> itemsToDestroy = new ArrayList<Item>();
        for (Item item : _inventory) {
            itemsToDestroy.add(item);
        }

        for (Item item : itemsToDestroy) {
            _inventory.remove(item);
            item.destroy();
            item = null;
        }

        if (_weapon != null) {
            _weapon.destroy();
        }

        if (_armor != null) {
            _armor.destroy();
        }

        PlayerClassData classMod = PlayerFacade.getStartingStats().getPlayerClassDatabase()
                .getPlayerClassData(getPlayerClass());
        _weapon = WorldManager.getWeapon(classMod.getWeapon());
        _armor = WorldManager.getArmor(classMod.getArmor());

        _spellbook.clearSpells();
    }

    //**********
    // Methods inherited from Entity.
    //**********

    /**
     * Gets the name of the entity.
     * @return the name of the entity.
     */
    public String getName() {
        return _name;
    }

    /**
     * Sets the name of the entity.
     * @param name the name of the entity.
     */
    public void setName(String name) {
        _name = name;
    }

    /**
     * Gets the vitality.
     * @return the vitality.
     */
    public Vitality getVitality() {
        return _vitality;
    }

    /**
     * Gets the amount of experience given per point of damage.
     * @param playerLevel the attacking players level.
     * @return the amount of experience given per point of damage.
     */
    public float getExpPerPointOfDamage(int playerLevel) {
        return 1f;
    }

    /**
     * Takes the given amount of damage.
     * @param amount the amount of damage taken.
     * @return true if the entity's health goes below 0.
     */
    public boolean takeDamage(int amount) {
        _vitality.subtractCurVitality(amount);
        if (_vitality.getCurVitality() <= 0) {
            return true;
        }
        return false;
    }

    /**
     * Sends a message to the entity followed by a carriage return.
     * @param str the message to send.
     */
    public void println(String str) {
        _output.println(str);
    }

    /**
     * Sends a message to the entity without the carriage return.
     * @param str the message to send.
     */
    public void print(String str) {
        _output.print(str);
    }

    /**
     * Gets the entity's level.
     * @return the entity's level.
     */
    public int getLevel() {
        return _level;
    }

    /**
     * Sets the entity's level.
     * @param level the entity's level.
     */
    public void setLevel(int level) {
        _level = level;
    }

    /**
     * Gets the entity's combat skill.
     * @return the entity's combat skill.
     */
    public int getCombatSkill() {
        return 70;
    }

    /**
     * Gets the entity type.
     * @return the entity type.
     */
    public EntityType getEntityType() {
        return EntityType.PLAYER;
    }

    /**
     * Gets the armor rating.
     * @return the armor rating.
     */
    public int getArmorRating() {
        if (getMagicProtection() > 0) {
            return getMagicProtection();
        } else {
            return _armor.getArmorRating() + _weapon.getArmorRating();
        }
    }

    /**
     * Gets the room the entity currently resides in.
     * @return the room the entity currently resides in.
     */
    public Room getRoom() {
        return _room;
    }

    /**
     * A convenience method that sets the room the entity will reside in.
     * @param room the room the entity will reside in.
     */
    public void setRoom(Room room) {
        _room = room;
    }

    /**
     * Sets the room that the entity will reside in based on the room number.
     * @param roomNumber the room number.
     */
    public void setRoom(int roomNumber) {
        _room = WorldManager.getRoom(roomNumber);
    }

    /**
     * Teleports this player to the specified room, ignoring all triggers.
     * @param toRoomNumber the room to teleport to.
     */
    public synchronized void teleportToRoomIgnoreTriggers(int toRoomNumber) {
        Room fromRoom = WorldManager.getRoom(_room.getRoomNumber());
        Room toRoom = WorldManager.getRoom(toRoomNumber);

        fromRoom.removePlayer(this);

        // Add to new room.
        _room = toRoom;
        toRoom.addPlayer(this);

        new DoDefault().execute(this, null);
    }

    /**
     * Moves the entity to this room.  No messages are displayed.
     * @param toRoomNumber the room number to move the entity to.
     * @return a TriggerResult, since teleporting an entity can still trigger a trigger.
     */
    public synchronized TriggerResult teleportToRoom(int toRoomNumber) {
        Room fromRoom = WorldManager.getRoom(_room.getRoomNumber());
        Room toRoom = WorldManager.getRoom(toRoomNumber);

        fromRoom.removePlayer(this);

        // Add to new room.
        _room = toRoom;
        toRoom.addPlayer(this);

        new DoDefault().execute(this, null);

        return toRoom.handleTriggers(this);
    }

    /**
     * Move the entity to this room.
     * @param toRoomNumber the destination room number.
     * @param ignoreBarrier true if any barriers can be ignored.
     * @return a move fail code.
     */
    public synchronized MoveFailCode moveToRoom(int toRoomNumber, boolean ignoreBarrier) {
        Room fromRoom = WorldManager.getRoom(_room.getRoomNumber());
        Room toRoom = WorldManager.getRoom(toRoomNumber);

        if (toRoom == null) {
            _log.error(
                    _name + " attempted to move into null room " + toRoomNumber + " from " + _room.getRoomNumber());
            return MoveFailCode.NO_EXIT;
        }

        MoveFailCode code = MoveFailCode.NONE;
        Exit exit = fromRoom.getExit(toRoomNumber);
        if (exit == null) {
            _log.error(_name + " attempted to move through a null exit " + toRoomNumber + " from "
                    + _room.getRoomNumber());
            return MoveFailCode.NO_EXIT;
        }
        // Handle barriers.
        if (!ignoreBarrier) {
            code = handleBarrier(fromRoom, exit);
        }

        if (code != MoveFailCode.NONE && code != MoveFailCode.PICKED_BARRIER
                && code != MoveFailCode.UNLOCK_BARRIER) {
            return code;
        }

        // Update the tracking information of the room.
        getTrackingMap().put(fromRoom, exit.getExitDirection());

        fromRoom.removePlayer(this);

        // Add to new room.
        _room = toRoom;
        toRoom.addPlayer(this);

        return code;
    }

    /**
     * Gets the status.
     * @return the status.
     */
    public Status getStatus() {
        return _status;
    }

    /**
     * Sets the status.
     * @param status the status.
     */
    public void setStatus(Status status) {
        _status = status;
    }

    /**
     * Gets the poison damage per tick.
     * @return the poison damage per tick.
     */
    public int getPoisonDamage() {
        return _poisonDamage;
    }

    /**
     * Sets the poison damage per tick.
     * @param poisonDamage the poison damage per tick.
     */
    public void setPoisonDamage(int poisonDamage) {
        _poisonDamage = poisonDamage;
    }

    /**
     * Gets the mana.
     * @return the mana.
     */
    public Mana getMana() {
        return _mana;
    }

    /**
     * Gets the regeneration ticker.  If this value is above 0, the entity has higher than
     * normal vitality regeneration.
     * @return the regeneration ticker.
     */
    public int getRegenerationTicker() {
        return _regenerationTicker;
    }

    /**
     * Sets the regeneration ticker.  If this value is above 0, the entity has higher than
     * normal vitality regeneration.
     * @param value the regeneration ticker.
     */
    public void setRegenerationTicker(int value) {
        _regenerationTicker = value;
    }

    /**
     * Reduces the regeneration ticker.  If the ticker is above 0, the entity has higher than
     * normal vitality regeneration.
     * @param value the amount to reduce the ticker.
     */
    public void reduceRegenerationTicker(int value) {
        _regenerationTicker = _regenerationTicker - value;
        if (_regenerationTicker < 0) {
            _regenerationTicker = 0;
        }
    }

    /**
     * Gets the hunger ticker.  If this value is below zero, the entity will starve.
     * @return the hunger ticker.
     */
    public int getHungerTicker() {
        return _hungerTicker;
    }

    /**
     * Increases the hunger ticker by the specified amount.
     * @param value the amount to increase the hunger ticker.
     */
    public void addHungerTicker(int value) {
        _hungerTicker += value;
        if (_hungerTicker > MAX_HUNGER) {
            _hungerTicker = MAX_HUNGER;
        }

        restoreHungerStatus();
    }

    /**
     * Sets the hunger ticker by the specified amount.
     * @param value the amount to set the hunger ticker.
     */
    public void setHungerTicker(int value) {
        _hungerTicker = value;
        if (_hungerTicker > MAX_HUNGER) {
            _hungerTicker = MAX_HUNGER;
        }

        restoreHungerStatus();
    }

    /**
     * Gets the thirst ticker.  If this value is below zero, the entity will be thirsty.
     * @return the thirst ticker.
     */
    public int getThirstTicker() {
        return _thirstTicker;
    }

    /**
     * Increases the thirst ticker by the specified amount.
     * @param value the amount to increase the thirst ticker.
     */
    public void addThirstTicker(int value) {
        _thirstTicker += value;
        if (_thirstTicker > MAX_THIRST) {
            _thirstTicker = MAX_THIRST;
        }

        restoreThirstStatus();
    }

    /**
     * Sets the thirst ticker by the specified amount.
     * @param value the amount to set the thirst ticker.
     */
    public void setThirstTicker(int value) {
        _thirstTicker = value;
        if (_thirstTicker > MAX_THIRST) {
            _thirstTicker = MAX_THIRST;
        }

        restoreThirstStatus();
    }

    /**
     * Decreases both the hunger and thirst ticker and accordingly updates the entities status
     * if any of these tickers are reduced below 0.
     * @param value the amount to decrease the hunger and thirst ticker.
     */
    public void subtractSustenance(int value) {
        if (_ignoreSustenance) {
            return;
        }

        _hungerTicker -= value;
        _thirstTicker -= value;

        if (_hungerTicker <= 0) {
            _hungerTicker = 0;
            _status = Status.HUNGRY;
        }

        if (_thirstTicker <= 0) {
            _thirstTicker = 0;
            _status = Status.THIRSTY;
        }

        restoreHungerStatus();
        restoreThirstStatus();
    }

    /**
     * Increase both the hunger and thirst ticker by the specified amount.
     * @param value the amount to increase both the thirst and hunger ticker.
     */
    public void addSustenance(int value) {
        addThirstTicker(value);
        addHungerTicker(value);
    }

    /**
     * Gets the stats.
     * @return the stats.
     */
    public BaseStats getStats() {
        return _stats;
    }

    /**
     * Decreases the effect timers.
     * @return true if the effect timer hits 0.
     */
    public boolean decreaseEffectTimers() {
        boolean magicProtectionExpired = false;
        if (_magicProtectionTimer > 0) {
            _magicProtectionTimer--;
            if (_magicProtectionTimer <= 0) {
                _magicProtection = 0;
                magicProtectionExpired = true;
            }
        }

        boolean invisibilityExpired = false;
        if (_invisibilityTimer > 0) {
            _invisibilityTimer--;
            if (_invisibilityTimer <= 0) {
                invisibilityExpired = true;
            }
        }

        boolean statEffectsExpired = _stats.decreaseTimers();

        if (magicProtectionExpired || statEffectsExpired || invisibilityExpired) {
            return true;
        }
        return false;
    }

    /**
     * Sets the magic protection.
     * @param magicProtection the magic protection.
     */
    public void setMagicProtection(int magicProtection) {
        _magicProtection = magicProtection;
    }

    /**
     * Gets the magic protection.
     * @return the magic protection.
     */
    public int getMagicProtection() {
        return _magicProtection;
    }

    /**
     * Sets the magic protection timer.
     * @param magicProtectionTimer the magic protection timer value.
     */
    public void setMagicProtectionTimer(int magicProtectionTimer) {
        _magicProtectionTimer = magicProtectionTimer;
    }

    /**
     * Sets the invisibility timer.
     * @param invisibilityTimer the invisibility timer value.
     */
    public void setInvisibiltyTimer(int invisibilityTimer) {
        _invisibilityTimer = invisibilityTimer;
    }

    /**
     * Checks if this entity is invisible.
     * @return true if this entity is invisible.
     */
    public boolean isInvisible() {
        if (_invisibilityTimer > 0) {
            return true;
        }
        return false;
    }

    /**
     * Gets the resting string.  This is currently used for the DoOrder command.
     * @return the resting string.
     */
    public String getRestingString() {
        if (isMentallyExhausted() || isResting()) {
            return MessageManager.RESTING_STRING.getMessage();
        } else {
            return MessageManager.READY_STRING.getMessage();
        }
    }

    /**
     * Gets the group leader.  If the entity is not in a group, this method returns
     * the entity itself.  Hopefully, this method never returns null.
     * @return the group leader.
     */
    public Entity getGroupLeader() {
        return _groupLeader;
    }

    /**
     * Sets the group leader.  If the entity is not in a group, set this to the
     * entity itself.  This value should not be null.
     * @param leader the group leader.
     */
    public void setGroupLeader(Entity leader) {
        _groupLeader = leader;
    }

    /**
     * Gets the list of entities in the group if this entity is the leader of a group.
     * An empty list is returned otherwise.
     * @return the list of entities in the group if this entity is the leader of a group.
     */
    public ArrayList<Entity> getGroupList() {
        return _groupList;
    }

    /**
     * Sets the group list.
     * @param groupList the group list.
     */
    public void setGroupList(ArrayList<Entity> groupList) {
        _groupList = groupList;
    }

    /**
     * Checks if this entity is following a group.
     * @return true if this entity is following a group.
     */
    public boolean isFollowingGroup() {
        return _isFollowingGroup;
    }

    /**
     * Sets the is following flag.
     * @param isFollowingGroup true if this entity is in a group.
     */
    public void setFollowingGroup(boolean isFollowingGroup) {
        _isFollowingGroup = isFollowingGroup;
    }

    /**
     * Checks if this entity is in the middle of attacking.
     * @return true if this entity is in the middle of attacking.
     */
    public boolean isAttacking() {
        if (_combatTicker > 0) {
            return true;
        }
        return false;
    }

    /**
     * Checks if this entity is resting.
     * @return true if this entity is resting.
     */
    public boolean isResting() {
        if (_restTicker > 0) {
            return true;
        }
        return false;
    }

    /**
     * Checks if this entity is mentally exhausted.
     * @return true if this entity is mentally exhausted.
     */
    public boolean isMentallyExhausted() {
        if (_mentalExhaustionTicker > 0) {
            return true;
        }
        return false;
    }

    /**
     * The amount of gold being carried.
     * @return the amount of gold being carried.
     */
    public int getGold() {
        return _gold;
    }

    /**
     * This method sets the amount of gold that the player has. Note that this
     * method is constrained by the MAX_INVENTORY_GOLD properties value. This
     * method returns the amount of over flow, or 0 if the set did not go over
     * the MAX_INVENTORY_GOLD value.
     *
     * @param gold The amount of gold the player will have.
     * @return the amount of over flow, or 0 if the set did not go over the
     *         MAX_INVENTORY_GOLD value.
     */
    public int setGold(int gold) {
        _gold = gold;
        return checkGoldOverflow();
    }

    /**
     * This method adds to the amount of gold that the player has. Note that this
     * method is constrained by the MAX_INVENTORY_GOLD properties value. This
     * method returns the amount of over flow, or 0 if the set did not go over
     * the MAX_INVENTORY_GOLD value.
     *
     * @param amount The amount of gold to give the player.
     * @return the amount of over flow, or 0 if the set did not go over the
     *         MAX_INVENTORY_GOLD value.
     */
    public int addGold(int amount) {
        _gold += amount;

        return checkGoldOverflow();
    }

    /**
     * Subtracts the amount of gold from the entity.  If the entity's gold goes
     * below 0, the gold amount will be set to 0 and the overflow will be returned.
     * @param amount the amount of gold to subtract.
     * @return the overflow amount.
     */
    public int subtractGold(int amount) {
        _gold -= amount;

        int overflow = 0;
        if (_gold < 0) {
            overflow = 0 - _gold;
            _gold = 0;
        }

        return overflow;
    }

    /**
     * Checks if this entity has a lit torch.
     * @return true if a lit torch is being carried.
     */
    public boolean hasLitTorch() {
        for (Item item : _inventory) {
            if (item.getItemType().equals(ItemType.EQUIPMENT)) {
                Equipment eq = (Equipment) item;
                if (eq.getEquipmentSubType().equals(EquipmentSubType.LIGHT)) {
                    if (eq.isActivated()) {
                        return true;
                    }
                }
            }
        }
        return false;
    }

    /**
     * Checks if this entity has light.
     * @return true if this entity has light.
     */
    public boolean hasLight() {
        for (Item item : _inventory) {
            if (item.getItemType().equals(ItemType.EQUIPMENT)) {
                Equipment eq = (Equipment) item;
                if (eq.getEquipmentSubType().equals(EquipmentSubType.LIGHT)) {
                    if (eq.isActivated()) {
                        return true;
                    }
                } else if (eq.getEquipmentSubType().equals(EquipmentSubType.ETERNAL_LIGHT)) {
                    return true;
                }
            }
        }
        return false;
    }

    /**
     * This removes an item of the same vnum from the inventory.
     *
     * @param vnum The vnum of the item to remove.
     * @return true if the item was removed, false otherwise.
     */
    public synchronized boolean removeItemFromInventory(int vnum) {
        Item itemToRemove = null;
        for (Item item : _inventory) {
            if (item.getVnum() == vnum) {
                itemToRemove = item;
                break;
            }
        }
        return _inventory.remove(itemToRemove);
    }

    /**
     * This removes the exact item from the inventory. That is, the object has to
     * be of the same instance.
     *
     * @param item The item to remove.
     * @return true if the item was removed, false otherwise.
     */
    public synchronized boolean removeItemFromInventory(Item item) {
        return _inventory.remove(item);
    }

    /**
     * Places an item into the entities inventory.
     * @param item the item to place.
     * @param ignoreWeight set to true if the item should ignore weight constraints.
     * @return an InventoryFailCode.
     */
    public synchronized InventoryFailCode placeItemInInventory(Item item, boolean ignoreWeight) {
        if (item.getItemType().equals(ItemType.RUNE_SCROLL)) {
            return handleRunestaff((RuneScroll) item);
        }

        int maxInventorySize = new Integer(
                PropertiesManager.getInstance().getProperty(PropertiesManager.MAX_INVENTORY_SIZE));
        if (_inventory.size() >= maxInventorySize) {
            return InventoryFailCode.SPACE;
        }

        if (!ignoreWeight) {
            int modifiedEncumbrance = _encumbrance.getCurEncumbrance() + item.getWeight();
            if (modifiedEncumbrance > _encumbrance.getMaxEncumbrance()) {
                return InventoryFailCode.ENCUMBRANCE;
            }
        }

        _inventory.add(item);
        return InventoryFailCode.NONE;
    }

    /**
     * Gets the entire inventory list.
     * @return the inventory list.
     */
    public ArrayList<Item> getInventory() {
        return _inventory;
    }

    /**
     * Gets the player class.
     * @return the player class.
     */
    public PlayerClass getPlayerClass() {
        return _playerClass;
    }

    /**
     * Gets the gender.
     * @return the gender.
     */
    public Gender getGender() {
        return _gender;
    }

    /**
     * Sets the gender.
     * @param gender the gender.
     */
    public void setGender(Gender gender) {
        _gender = gender;
    }

    /**
     * Gets the tracking map.  This is a map of rooms the entity went through along
     * with the direction.  This is used for tracking.
     * @return the tracking map.
     */
    public HashMap<Room, ExitDirectionEnum> getTrackingMap() {
        return _trackingMap;
    }

    /**
     * Checks if this entity is similar to the specified entity.
     * @param entity the entity to compare.
     * @return true if this entity is similar to the specified entity.
     */
    public boolean isSimilar(Entity entity) {
        if (!entity.getEntityType().equals(EntityType.PLAYER)) {
            return false;
        }

        Player player = (Player) entity;
        if (player.equals(this)) {
            return true;
        }
        return false;
    }

    /**
     * Gets the spell book.
     * @return the spell book.
     */
    public Spellbook getSpellbook() {
        return _spellbook;
    }

    /**
     * Sets the spell book.
     * @param spellbook the spell book.
     */
    public void setSpellbook(Spellbook spellbook) {
        _spellbook = spellbook;
    }

    /**
     * Decreases the mental exhaustion ticker.
     */
    public void decreaseMentalExhaustionTicker() {
        if (_mentalExhaustionTicker > 0) {
            _mentalExhaustionTicker -= 1;
        }
    }

    /**
     * Sets the mental exhaustion ticker.  If this value is above 0, then the entity is
     * mentally exhausted.
     * @param value the mental exhaustion ticker.
     */
    public void setMentalExhaustionTicker(int value) {
        _mentalExhaustionTicker = value;
    }

    /**
     * Adds the amount of experience.
     * @param experience the amount of experience to add.
     */
    public void addExperience(long experience) {
        int maxExp = getPromotedClass().getExpRequirement(getPromotedLevel() + MAX_TRAILING_LEVEL, isPromoted())
                - 1;
        _experience += experience;
        if (_experience > maxExp) {
            _experience = maxExp;
        }
    }

    /**
     * Gets the attacks.
     * @return the attacks.
     */
    public Attacks getAttacks() {
        return _attacks;
    }

    /**
     * Decreases the rest ticker.
     */
    public void decreaseRestTicker() {
        if (_restTicker > 0) {
            _restTicker -= 1;
        }
    }

    /**
     * Sets the rest ticker.  If this number is greater than 0, then the
     * entity is resting.
     * @param value the rest ticker value.
     */
    public void setRestTicker(int value) {
        _restTicker = value;
    }

    /**
     * Sets the combat ticker.  If this number is greater than 0, then the entity
     * has attacked recently and is considered in combat.
     * @param value the combat ticker.
     */
    public void setCombatTicker(int value) {
        _combatTicker = value;
    }

    /**
     * Gets the spell hit difficulty for this entity.
     * @return the spell hit difficulty for this entity.
     */
    public int getSpellHitDifficulty() {
        return PLAYER_SPELL_HIT_DIFFICULTY;
    }

    /**
     * Gets the message handler.
     * @return the message handler.
     */
    public EntityMessageHandler getMessageHandler() {
        return _messageHandler;
    }

    /**
     * Sets the message handler.
     * @param messageHandler the message handler.
     */
    public void setMessageHandler(EntityMessageHandler messageHandler) {
        _messageHandler = messageHandler;
    }

    /**
     * Sets the paralysis ticker.  If this value is greater than 0, than the entity
     * is considered to be paralyzed.
     * @param value the paralysis ticker value.
     */
    public void setParalysisTicker(int value) {
        _paralysisTimer = value;
    }

    /**
     * Decreases the paralysis ticker.
     * @param amount the amount to decrease the ticker.
     * @return true if the entity is no longer paralyzed.
     */
    public boolean decreaseParalysisTicker(int amount) {
        if (_paralysisTimer > 0) {
            _paralysisTimer -= amount;
        }

        if (_paralysisTimer <= 0) {
            return true;
        }

        return false;
    }

    /**
     * Gets the player that this entity is dragging.  Null if none.
     * @return the player that this entity is dragging.  Null if none.
     */
    public Player getDragging() {
        return _dragging;
    }

    /**
     * Sets the dragging player.
     * @param dragging the dragging player.
     */
    public void setDragging(Player dragging) {
        _dragging = dragging;
    }

    /**
     * Gets the player that this entity is being dragged by.
     * @return the player that this entity is being dragged by.
     */
    public Player getDraggedBy() {
        return _draggedBy;
    }

    /**
     * Sets the player that this entity is being dragged by.
     * @param draggedBy the player that this entity is being dragged by.
     */
    public void setDraggedBy(Player draggedBy) {
        _draggedBy = draggedBy;
    }

    /**
     * Gets the last time this entity has moved.
     * @return the last time this entity has moved.
     */
    public long getLastMove() {
        return _lastMove;
    }

    /**
     * Sets the last time this entity has moved.
     * @param lastMove the last time this entity has moved.
     */
    public void setLastMove(long lastMove) {
        _lastMove = lastMove;
    }

    /**
     * This checks if the entity is currently tripping.
     * @return true if this entity is currently tripping.
     */
    public boolean isTripping() {
        return _isTripping;
    }

    /**
     * This sets the entity into a tripping state.
     * @param isTripping the tripping flag.
     */
    public void setIsTripping(boolean isTripping) {
        _isTripping = isTripping;
    }

    /**
     * This is a debug command.  This checks if the entity can trip
     * by moving too fast.
     * @return true if this entity can ignore the tripping mechanism when moving too fast.
     */
    public boolean getIgnoreTrip() {
        return _ignoreTrip;
    }

    /**
     * This is a debug command.  This checks if the entity can trip
     * by moving too fast.
     * @param ignoreTrip the ignore trip flag.
     */
    public void setIgnoreTrip(boolean ignoreTrip) {
        _ignoreTrip = ignoreTrip;
    }

    /**
     * Decrease the combat ticker.
     */
    public void decreaseCombatTicker() {
        if (_combatTicker > 0) {
            _combatTicker -= 1;
        }
    }

    /**
     * Checks if this player is promoted.
     * @return true if this player is promoted.
     */
    public boolean isPromoted() {
        return _promoted;
    }

    /**
     * Sets the promoted flag of this player.
     * @param promoted the promoted flag.
     */
    public void setPromoted(boolean promoted) {
        _promoted = promoted;
    }

    /**
     * Gets the debug string for this entity.
     * @return the debug string for this entity.
     */
    public String getDebugString() {
        StringBuffer buffer = new StringBuffer();

        try {
            Formatter formatter = new Formatter(buffer, Locale.US);
            formatter.format("&CName:&W%s  &CPNum:&W%d  &CSysop:&W%s  &CState:&W%s\n", _name, _pnum, _isSysop,
                    _state);
            formatter.format("&CClass:&W%s  &CRace:&W%s  &CGender:&W%s  &CPromoted:&W%s\n", _playerClass, _race,
                    _gender, isPromoted());
            formatter.format("&CLevel:&W%d  &CExperience:&W%d  &CRune:&W%s\n", _level, _experience, _rune);

            formatter.format("%s %s %s\n", getStatDebugString("Int", _stats.getIntellect()),
                    getStatDebugString("Kno", _stats.getKnowledge()),
                    getStatDebugString("Phy", _stats.getPhysique()));
            formatter.format("%s %s %s\n", getStatDebugString("Sta", _stats.getStamina()),
                    getStatDebugString("Agi", _stats.getAgility()),
                    getStatDebugString("Cha", _stats.getCharisma()));

            formatter.format(
                    "&CVitality:&W%d&C/&W%d  &CDrained:&W%d  &CMana:&W%d&C/&W%d  &CStatus:&W%s  &CPoison:&W%d\n",
                    _vitality.getCurVitality(), _vitality.getMaxVitality(), _vitality.getDrained(),
                    _mana.getCurMana(), _mana.getMaxMana(), _status, _poisonDamage);
            formatter.format("&CComplexion:&W%s  &CEye Color:&W%s\n", _complexion, _eyeColor);
            formatter.format("&CHair Color:&W%s  &CHair Length:&W%s  &CHair Style:&W%s\n", _hairColor, _hairLength,
                    _hairStyle);
            formatter.format("&CWeapon:&W%s  &CArmor:&W%s  &CAttacks:&W%d&C/&W%d\n", _weapon.getName(),
                    _armor.getName(), _attacks.getAttacksLeft(), _attacks.getMaxAttacks());
            formatter.format("&CEncumbrance:&W%d&C/&W%d  &CGold:&W%d  &CVault:&W%d\n",
                    _encumbrance.getCurEncumbrance(), _encumbrance.getMaxEncumbrance(), _gold, _vaultBalance);
            formatter.format("&CInventory:&W%s\n", _inventory);
            formatter.format("&CSpells:&W%s\n", _spellbook.getSpells());
            formatter.format("&CGame of Chance Count:&W%s\n", _gameOfChanceCount);
            formatter.format("&CRest:&W%d  &CCombat:&W%d  &CMental:&W%d  &CHunger:&W%d  &CThirst:&W%d\n",
                    _restTicker, _combatTicker, _mentalExhaustionTicker, _hungerTicker, _thirstTicker);
            formatter.format("&CRegeneration Ticker:&W%d  &CMagic Protection Timer:&W%d  &CMagic Protection:&W%d\n",
                    _regenerationTicker, _magicProtectionTimer, _magicProtection);
            formatter.format("&CInvisibility Timer:&W%d  &CParalysis Timer:&W%d\n", _invisibilityTimer,
                    _paralysisTimer);
            formatter.format("&CLast Global Chat:&W%s  &CGlobal Chat Count:&W%d\n", new Date(_lastGlobalChat),
                    _globalChatCount);
            formatter.format("&CLast Move:&W%s  &CIs Tripping:&W%s  &CIgnore Trip:&W%s\n", new Date(_lastMove),
                    _isTripping, _ignoreTrip);
            if (_room != null) {
                formatter.format("&CRoom:&W%d&C(&W%s&C)\n", _room.getRoomNumber(), _room.getShortDescription());
            }
            formatter.format("&CGroup Leader:&W%s  &CIs Following Group:&W%s\n", _groupLeader, _isFollowingGroup);
            formatter.format("&CGroup List:&W%s\n", _groupList);
            formatter.format("&CDragging:&W%s  &CDragged By:&W%s\n", _dragging, _draggedBy);
            formatter.format("&CTracking Map Size:&W%d  &CDisconnected:&W%s\n", _trackingMap.size(), _disconnected);
            formatter.format("&CGameOutput:&W%s\n", _output);
            formatter.format("&CMessage Handler:&W%s\n", _messageHandler);
        } catch (Exception e) {
            buffer.append("&RGot the following exception creating the DefaultPlayer debug string:\n");
            buffer.append("&R" + e.getMessage());
            _log.error(e);
        }

        return buffer.toString();
    }

    //**********
    // Methods overridden from Object.
    //**********

    /**
     * Gets the String representation of this object.
     * @return the String representation of this object.
     */
    public String toString() {
        return _name;
    }

    /**
     * This is the overridden readResolve method that initializes transient objects.
     * @return the instance of this class.
     * @throws ObjectStreamException if something goes wrong during the readResolve.
     */
    private Object readResolve() throws ObjectStreamException {
        _room = WorldManager.getRoom(_roomVnumSave);
        _attacks = new DefaultAttacks(this);
        _groupLeader = this;
        _groupList = new ArrayList<Entity>();
        _trackingMap = new HashMap<Room, ExitDirectionEnum>();
        _messageHandler = new PlayerMessageHandler(this);

        // Hack to fix mana for players who are already playing.
        if (_playerClass.isSpellCaster()) {
            int maxMana = _level * getPlayerClass().getManaIncreasePerLevel();
            if (_mana.getMaxMana() < maxMana) {
                _mana.setMaxMana(maxMana);
            }
        }
        return this;
    }

    //**********
    // Private methods.
    //**********

    /**
     * Adds a scroll to the runestaff.
     * @param runeScroll the RuneScroll.
     * @return an InventoryFailCode.
     */
    private InventoryFailCode handleRunestaff(RuneScroll runeScroll) {
        Equipment runestaff = null;
        InventoryFailCode code = InventoryFailCode.RUNE_SCROLL_DUD;
        for (Item item : _inventory) {
            if (GameUtil.isRunestaff(item)) {
                runestaff = (Equipment) item;
                break;
            }
        }

        if (runestaff != null) {
            if (runeScroll.getRuneNumber() > runestaff.getCharges()) {
                runestaff.subractCharge();
                code = InventoryFailCode.RUNE_SCROLL;
            }
        }

        runeScroll.destroy();
        return code;
    }

    /**
     * Handles movement into a barrier.
     * @param fromRoom the source room.
     * @param exit the exit.
     * @return a MoveFailCode.
     */
    private MoveFailCode handleBarrier(Room fromRoom, Exit exit) {
        if (exit.getDoor() != null && !exit.getDoor().isUnlocked()) {
            return exit.getDoor().getMoveFailCode(this);
        } else {
            return MoveFailCode.NONE;
        }
    }

    /**
     * Checks if the player has more gold than is allowed.
     * @return the amount of gold that went over the max.
     */
    private int checkGoldOverflow() {
        int overflow = 0;

        if (_gold > MAX_INVENTORY_GOLD) {
            overflow = _gold - MAX_INVENTORY_GOLD;
            _gold = MAX_INVENTORY_GOLD;
        }

        return overflow;
    }

    /**
     * Gets the stat as a string used for debugging purposes.
     * @param str the string representation of the stat to display.
     * @param stat the stat.
     * @return the stat as a string used for debugging purposes.
     */
    private String getStatDebugString(String str, Stat stat) {
        StringBuffer buffer = new StringBuffer();

        Formatter formatter = new Formatter(buffer, Locale.US);
        formatter.format("&C%s:&W%d&C/&W%d&C(+&W%d&C:&W%d&C/-&W%d&C:&W%d&C)", str, stat.getValue(),
                stat.getModifiedStat(), stat.getBoost(), stat.getBoostTimer(), stat.getDrain(),
                stat.getDrainTimer());

        return buffer.toString();
    }

    /**
     * Resets the hunger status if the player is no longer hungry.
     */
    private void restoreHungerStatus() {
        if (_hungerTicker > 0) {
            if (_status.equals(Status.HUNGRY)) {
                _status = Status.HEALTHY;
            }
        }
    }

    /**
     * Resets the thirst status if the player is no longer hungry.
     */
    private void restoreThirstStatus() {
        if (_thirstTicker > 0) {
            if (_status.equals(Status.THIRSTY)) {
                _status = Status.HEALTHY;
            }
        }
    }
}