circlemud_squared_0.5.153/cnf/
circlemud_squared_0.5.153/etc/
circlemud_squared_0.5.153/etc/etc/
circlemud_squared_0.5.153/etc/house/
circlemud_squared_0.5.153/etc/misc/
circlemud_squared_0.5.153/etc/plralias/A-E/
circlemud_squared_0.5.153/etc/plralias/F-J/
circlemud_squared_0.5.153/etc/plralias/K-O/
circlemud_squared_0.5.153/etc/plralias/P-T/
circlemud_squared_0.5.153/etc/plralias/U-Z/
circlemud_squared_0.5.153/etc/plralias/ZZZ/
circlemud_squared_0.5.153/etc/plrobjs/
circlemud_squared_0.5.153/etc/plrobjs/A-E/
circlemud_squared_0.5.153/etc/plrobjs/F-J/
circlemud_squared_0.5.153/etc/plrobjs/K-O/
circlemud_squared_0.5.153/etc/plrobjs/P-T/
circlemud_squared_0.5.153/etc/plrobjs/U-Z/
circlemud_squared_0.5.153/etc/plrobjs/ZZZ/
circlemud_squared_0.5.153/etc/text/
circlemud_squared_0.5.153/etc/text/help/
circlemud_squared_0.5.153/src/util/
circlemud_squared_0.5.153/src/util/worldconv/
/**
 * @file character.c
 * @ingroup character
 *
 * Character based code
 *
 * @author Geoff Davis <geoff@circlemudsquared.org>
 * @author Greg Buxton <greg@circlemudsquared.org>
 *
 * @par Copyright:
 *   Copyright (C) 2006 Geoff Davis <geoff@circlemudsquared.org><br>
 *                      Greg Buxton <greg@circlemudsquared.org>
 *
 * @par
 *   Copyright (C) 1993, 94 by the Trustees of the Johns Hopkins University<br>
 *   CircleMUD is based on DikuMUD, Copyright (C) 1990, 1991.
 *
 * @par
 *   All rights reserved.  See license.doc for complete information.
 *
 * @package cs
 * @version 1.0
 */

#define __CHARACTER_C__

#include "base.h"
#include "structs.h"
#include "utils.h"
#include "comm.h"
#include "command.h"
#include "constants.h"
#include "db.h"
#include "handler.h"
#include "interpreter.h"
#include "log.h"
#include "dao.h"
#include "character.h"
#include "item.h"
#include "room.h"

/*
 * Global variables.
 */

/**
 * The number of pending character extractions.
 * @var unsigned long
 */
unsigned long extractions_pending = 0UL;

/*
 * External variables.
 */
extern charData_t *combat_list;
extern const char *MENU;
extern const char *genders[];
extern const char *npc_class_types[];
extern const char *pc_class_types[];
extern const char *save_types[];
extern const char *preference_bits[];
extern const char *player_bits[];
extern const char *action_bits[];

/*
 * External functions.
 */
int apply_ac(charData_t *ch, int eq_pos);
void clearMemory(charData_t *ch);
bool invalid_align(charData_t *ch, itemData_t *obj);
bool invalid_class(charData_t *ch, itemData_t *obj);

/**
 * Equips an item in a wear slot.
 * @param ch the character to be equipped
 * @param obj the item with which the character is to be equipped
 * @param pos the wear slot (WEAR_xxx) where the item is to be worn
 * @return none
 */
void char_equipItem(charData_t *ch, itemData_t *obj, int pos) {
  if (ch == NULL) {
    log("char_equipItem(): invalid 'ch' charData_t.");
  } else if (obj == NULL) {
    log("char_equipItem(): invalid 'obj' itemData_t.");
  } else if (pos < 0 || pos >= NUM_WEARS) {
    log("char_equipItem(): invalid 'pos' value %d.", pos);
  } else if (obj->carriedBy != NULL) {
    log("char_equipItem(): itemData_t 'obj' is being carried.");
  } else if (obj->wornBy != NULL) {
    log("char_equipItem(): itemData_t 'obj' is being worn.");
  } else if (IN_ROOM(obj) != NULL) {
    log("char_equipItem(): itemData_t 'obj' is in room #%s:%d.", IN_ROOM(obj)->zone->keyword, IN_ROOM(obj)->number);
  } else {
    int j;
    if (GET_EQ(ch, pos) != NULL) {
      itemData_toChar(char_unequipItem(ch, pos), ch);
    }
    if (invalid_align(ch, obj) || invalid_class(ch, obj)) {
      act("You are zapped by $p and instantly let go of it.", FALSE, ch, obj, 0, TO_CHAR);
      act("$n is zapped by $p and instantly lets go of it.", FALSE, ch, obj, 0, TO_ROOM);
      /* Changed to drop in inventory instead of the ground. */
      itemData_toChar(obj, ch);
    } else {
      GET_EQ(ch, pos) = obj;
      obj->wornBy = ch;
      obj->wornOn = pos;

      if (GET_ITEM_TYPE(obj) == ITEM_ARMOR) {
        GET_AC(ch) -= apply_ac(ch, pos);
      }

      if (IN_ROOM(ch) != NULL) {
        if (pos == WEAR_LIGHT && GET_ITEM_TYPE(obj) == ITEM_LIGHT) {
          if (GET_ITEM_VAL(obj, 2)) { /* if light is ON */
            ch->room->light++;
          }
        }
      }

      for (j = 0; j < MAX_ITEM_AFFECT; j++) {
        effectData_modify(ch, obj->affected[j].location,
          obj->affected[j].modifier, GET_ITEM_AFFECT(obj), TRUE);
      }

      effectData_total(ch);
    }
  }
}

/**
 * Removes a character from its room.
 * @param ch the character to be removed
 * @return none
 */
void char_fromRoom(charData_t *ch) {

  if (ch == NULL) {
    log("char_fromRoom(): invalid 'ch' charData_t.");
  } else {
    if (IN_ROOM(ch) == NULL) {
      log("char_fromRoom(): charData_t 'ch' in nowhere.");
    } else {
      /* Declare an iterator variable. */
      register charData_t *temp;

      /* Stop the character from fighting. */
      if (FIGHTING(ch) != NULL) {
        charData_stopFighting(ch);
      }

      /* Remove any lighting supplied by the character. */
      if (GET_EQ(ch, WEAR_LIGHT) != NULL) {
        if (GET_ITEM_TYPE(GET_EQ(ch, WEAR_LIGHT)) == ITEM_LIGHT) {
          if (GET_ITEM_VAL(GET_EQ(ch, WEAR_LIGHT), 2)) { /* Light is ON */
            (IN_ROOM(ch))->light--;
          }
        }
      }

      /* Unlink the character from the room. */
      REMOVE_FROM_LIST(ch, (IN_ROOM(ch))->people, next_in_room);
      IN_ROOM(ch) = NULL;
      ch->next_in_room = NULL;
    }
  }
}

/**
 * Places a character into a room.
 * @param ch the character to be placed
 * @param room the room the place the character into
 * @return none
 */
void char_toRoom(charData_t *ch, roomData_t *room) {

  if (ch == NULL) {
    log("char_toRoom(): invalid 'ch' charData_t.");
  } else if (room == NULL) {
    log("char_toRoom(): invalid 'room' roomData_t.");
  } else {
    /* Remove the character from its current room. */
    if (IN_ROOM(ch) != NULL) {
      char_fromRoom(ch);
    }

    /* Link the character into the room. */
    ch->next_in_room = room->people;
    room->people = ch;
    IN_ROOM(ch) = room;

    /* Adjust the room's lighting. */
    if (GET_EQ(ch, WEAR_LIGHT)) {
      if (GET_ITEM_TYPE(GET_EQ(ch, WEAR_LIGHT)) == ITEM_LIGHT) {
        if (GET_ITEM_VAL(GET_EQ(ch, WEAR_LIGHT), 2)) { /* Light ON */
          room->light++;
        }
      }
    }

    /* Stop fighting now, if we left. */
    if (FIGHTING(ch) && IN_ROOM(ch) != IN_ROOM(FIGHTING(ch))) {
      charData_stopFighting(FIGHTING(ch));
      charData_stopFighting(ch);
    }
  }
}

/**
 * Removes the item equipped in a wear slot.
 * @param ch the character to be unequipped
 * @param pos the wear slot (WEAR_xxx) to be unequipped
 * @return the item unequipped from the wear slot, or NULL
 */
itemData_t *char_unequipItem(charData_t *ch, int pos) {
  itemData_t *obj = NULL;

  if (ch == NULL) {
    log("char_unequipItem(): invalid 'ch' charData_t.");
  } else if (pos < 0 || pos >= NUM_WEARS) {
    log("char_unequipItem(): invalid 'pos' value %d.", pos);
  } else if (GET_EQ(ch, pos) != NULL) {
    register int j = 0;

    obj = GET_EQ(ch, pos);
    obj->wornBy = NULL;
    obj->wornOn = -1;

    if (GET_ITEM_TYPE(obj) == ITEM_ARMOR) {
      GET_AC(ch) += apply_ac(ch, pos);
    }
    if (IN_ROOM(ch) != NULL) {
      if (pos == WEAR_LIGHT && GET_ITEM_TYPE(obj) == ITEM_LIGHT) {
        if (GET_ITEM_VAL(obj, 2)) { /* if light is ON */
          ch->room->light--;
        }
      }
    }
    GET_EQ(ch, pos) = NULL;

    for (j = 0; j < MAX_ITEM_AFFECT; j++) {
      effectData_modify(ch, obj->affected[j].location,
        obj->affected[j].modifier, GET_ITEM_AFFECT(obj), FALSE);
    }
    effectData_total(ch);
  }
  return (obj);
}

/**
 * Extracts a character.
 * @param ch the character to be extracted
 * @return none
 */
void char_extract(charData_t *ch) {
  /*
   * Q: Why do we do this?
   * A: Because trying to iterate over the character
   *    list with 'ch = ch->next' does bad things if
   *    the current character happens to die. The
   *    trivial workaround of 'vict = next_vict'
   *    doesn't work if the _next_ person in the list
   *    gets killed, for example, by an area spell.
   *
   * Q: Why do we leave them on the character_list?
   * A: Because code doing 'vict = vict->next' would
   *    get really confused otherwise.
   */
  if (ch == NULL) {
    log("char_extract(): inalid 'ch' charData_t.");
  } else {
    if (IS_NPC(ch)) {
      SET_BIT(MOB_FLAGS(ch), MOB_NOTDEADYET);
    } else {
      SET_BIT(PLR_FLAGS(ch), PLR_NOTDEADYET);
    }
    extractions_pending++;
  }
}

/**
 * Extracts a character.
 * @param ch the character to be extracted
 * @return none
 */
void char_extractFinal(charData_t *ch) {

  if (ch == NULL) {
    log("char_extractFinal(): invalid 'ch' charData_t.");
  } else if (IN_ROOM(ch) == NULL) {
    log("char_extractFinal(): charData_t 'ch' nowhere.");
  } else {
    charData_t *k = NULL, *temp = NULL;
    descriptorData_t *d = NULL;
    itemData_t *obj = NULL;
    int i = 0;

    /*
     * We're booting the character of someone who has switched so first we
     * need to stuff them back into their own body.  This will set ch->desc
     * we're checking below this loop to the proper value.
     */
    if (!IS_NPC(ch) && !ch->desc) {
      for (d = descriptor_list; d; d = d->next) {
        if (d->original == ch) {
          do_return(d->character, NULL, 0);
          break;
        }
      }
    }

    if (ch->desc) {
      /*
       * This time we're extracting the body someone has switched into
       * (not the body of someone switching as above) so we need to put
       * the switcher back to their own body.
       *
       * If this body is not possessed, the owner won't have a
       * body after the removal so dump them to the main menu.
       */
      if (ch->desc->original) {
        do_return(ch, NULL, 0);
      } else {
        /*
         * Now we boot anybody trying to log in with the same character, to
         * help guard against duping.  CON_DISCONNECT is used to close a
         * descriptor without extracting the d->character associated with it,
         * for being link-dead, so we want CON_CLOSE to clean everything up.
         * If we're here, we know it's a player so no IS_NPC check required.
         */
        for (d = descriptor_list; d; d = d->next) {
          if (d == ch->desc) {
            continue;
          }
          if (d->character && GET_IDNUM(ch) == GET_IDNUM(d->character)) {
            STATE(d) = CON_CLOSE;
          }
        }
        STATE(ch->desc) = CON_MENU;
        write_to_output(ch->desc, "%s", MENU);
      }
    }

    /* On with the character's assets... */
    if (ch->followers || ch->master) {
      die_follower(ch);
    }

    /* transfer objects to room, if any */
    while (ch->carrying) {
      obj = ch->carrying;
      itemData_fromChar(obj);
      itemData_toRoom(obj, IN_ROOM(ch));
    }

    /* transfer equipment to room, if any */
    for (i = 0; i < NUM_WEARS; i++) {
      if (GET_EQ(ch, i)) {
        itemData_toRoom(char_unequipItem(ch, i), IN_ROOM(ch));
      }
    }

    if (FIGHTING(ch)) {
      charData_stopFighting(ch);
    }

    for (k = combat_list; k; k = temp) {
      temp = k->next_fighting;
      if (FIGHTING(k) == ch) {
        charData_stopFighting(k);
      }
    }

    /* we can't forget the hunters either... */
    for (temp = character_list; temp; temp = temp->next) {
      if (HUNTING(temp) == ch) {
        HUNTING(temp) = NULL;
      }
    }

    char_fromRoom(ch);

    if (IS_NPC(ch)) {
      if (ch->prototype) { /* prototyped */
        ch->prototype->number--;
      }
      clearMemory(ch);
    } else {
      save_char(ch);
      Crash_delete_crashfile(ch);
    }

    /* If there's a descriptor, they're in the menu now. */
    if (IS_NPC(ch) || ch->desc == NULL) {
      free_char(ch);
    }
  }
}

/**
 * Extracts any characters with a pending extraction.
 * @return none
 */
void char_extractPendingChars(void) {
  /* Declare some iterator variables. */
  register charData_t *ch = NULL, *nextCh = NULL, *prevCh = NULL;

  /* Iterate over the character list. */
  for (ch = character_list; ch && extractions_pending > 0; ch = nextCh) {
    /* Save off the next character in the character list. */
    nextCh = ch->next;

    if (MOB_FLAGGED(ch, MOB_NOTDEADYET)) {
      REMOVE_BIT(MOB_FLAGS(ch), MOB_NOTDEADYET);
    } else if (PLR_FLAGGED(ch, PLR_NOTDEADYET)) {
      REMOVE_BIT(PLR_FLAGS(ch), PLR_NOTDEADYET);
    } else {
      /* Last non-free'd character to continue chain from. */
      prevCh = ch;
      continue;
    }

    char_extractFinal(ch);
    extractions_pending--;

    if (prevCh) {
      prevCh->next = nextCh;
    } else {
      character_list = nextCh;
    }
  }
  extractions_pending = 0;
}

/**
 * Convert a charPlayerData_t to it's DAO representation
 *
 * @param parentDao the container DAO to contain the item's DAO
 * @param cpData the charPlayerData_t to be converted
 * @param isPlayer TRUE if being called for a Player, FALSE if being called for a Mobile
 * @return none
 */
void charPlayerData_toDao(daoData_t *parentDao, charPlayerData_t *cpData, bool isPlayer) {
  if (parentDao == NULL) {
    log("charPlayerData_toDao(): invalid 'parentDao' daoData_t.");
  } else if (cpData == NULL) {
    log("charPlayerData_toDao(): invalid 'cpData' charPlayerData_t.");
  } else {
    /* Declare some working DAO pointers */
    daoData_t *cpDao = NULL;

    /* Declare some temporary buffer space. */
    char temp[MAX_STRING_LENGTH] = {'\0'};

    cpDao = dao_newChild(parentDao, "playerData");

    if (isPlayer) {
      /* Save PC specific data */
      dao_newScalar(cpDao, "password", "%s", cpData->passwd);
      dao_newScalar(cpDao, "birth", "%ld", cpData->time.birth);
      dao_newScalar(cpDao, "logon", "%ld", cpData->time.logon);
      dao_newScalar(cpDao, "timePlayed", "%ld", cpData->time.played);
      sprinttype(cpData->chclass, pc_class_types, temp, sizeof(temp));
      if (cpData->name && *(cpData->name) != '\0') {
        dao_newScalar(cpDao, "name", "%s", cpData->name);
      }
    } else {
      /* Save Mob specific data */
      sprinttype(cpData->chclass, npc_class_types, temp, sizeof(temp));
      if (cpData->short_descr && *(cpData->short_descr) != '\0') {
        dao_newScalar(cpDao, "shortDescription", "%s", cpData->short_descr);
      }
      if (cpData->name && *(cpData->name) != '\0') {
        dao_newScalar(cpDao, "keywords", "%s", cpData->name);
      }
    }
    dao_newScalar(cpDao, "class", "%s", temp);
    /* Data in common to both PCs and NPCs */
    if (cpData->long_descr && *(cpData->long_descr) != '\0') {
      dao_newScalar(cpDao, "longDescription", "%s", cpData->long_descr);
    }
    if (cpData->description && *(cpData->description) != '\0') {
      dao_newScalar(cpDao, "description", "%s", cpData->description);
    }
    if (cpData->title && *(cpData->title) != '\0') {
      dao_newScalar(cpDao, "title", "%s", cpData->title);
    }
    dao_newScalar(cpDao, "level", "%d", cpData->level);
    sprinttype(cpData->sex, genders, temp, sizeof(temp));
    dao_newScalar(cpDao, "sex", "%s", temp);
    sprinttype(cpData->auth, auth_types, temp, sizeof(temp));
    dao_newScalar(cpDao, "auth", "%s", temp);
    dao_newScalar(cpDao, "homeTown", "%d", cpData->hometown);
    dao_newScalar(cpDao, "weight", "%d", cpData->weight);
    dao_newScalar(cpDao, "height", "%d", cpData->height);
  }
}

/**
 * Convert a charAbilities_t to it's DAO representation
 *
 * @param parentDao the container DAO to contain the item's DAO
 * @param caData the charAbilities_t to be converted
 * @return none
 */
void charAbilities_toDao(daoData_t *parentDao, charAbilities_t *caData) {
  if (parentDao == NULL) {
    log("charAbilities_toDao(): invalid 'parentDao' daoData_t.");
  } else if (caData == NULL) {
    log("charAbilities_toDao(): invalid 'caData' charAbilities_t.");
  } else {
    dao_newScalar(parentDao, "strength", "%d", caData->str);
    dao_newScalar(parentDao, "strengthAdd", "%d", caData->str_add);
    dao_newScalar(parentDao, "intelligence", "%d", caData->intel);
    dao_newScalar(parentDao, "wisdom", "%d", caData->wis);
    dao_newScalar(parentDao, "dexterity", "%d", caData->dex);
    dao_newScalar(parentDao, "constitution", "%d", caData->con);
    dao_newScalar(parentDao, "charisma", "%d", caData->cha);
  }
}

/**
 * Convert a charPointData_t to it's DAO representation
 *
 * @param parentDao the container DAO to contain the item's DAO
 * @param cpData the charPointData_t to be converted
 * @return none
 */
void charPointData_toDao(daoData_t *parentDao, charPointData_t *cpData, bool isPlayer) {
  if (parentDao == NULL) {
    log("charPointData_toDao(): invalid 'parentDao' daoData_t.");
  } else if (cpData == NULL) {
    log("charPointData_toDao(): invalid 'cpData' charPointData_t.");
  } else {
    /* Declare a DAO pointer */
    daoData_t *subContainerDao = NULL;
    if (isPlayer == TRUE) {
      /* Save points for a Player */

      subContainerDao = dao_newChild(parentDao, "hitPoints");
      dao_newScalar(subContainerDao, "current", "%d", cpData->hit);
      dao_newScalar(subContainerDao, "maximum", "%d", cpData->max_hit);
      subContainerDao = NULL;

      subContainerDao = dao_newChild(parentDao, "manaPoints");
      dao_newScalar(subContainerDao, "current", "%d", cpData->mana);
      dao_newScalar(subContainerDao, "maximum", "%d", cpData->max_mana);
      subContainerDao = NULL;

      subContainerDao = dao_newChild(parentDao, "movePoints");
      dao_newScalar(subContainerDao, "current", "%d", cpData->move);
      dao_newScalar(subContainerDao, "maximum", "%d", cpData->max_move);
      subContainerDao = NULL;
    } else {
      /* Save points for a Mobile Prototype.  No currents, just maxes so we can trim things down */

      if (cpData->max_hit == 0) {
        subContainerDao = dao_newChild(parentDao, "hitPoints");
        dao_newScalar(subContainerDao, "diceNumber", "%d", cpData->hit);
        dao_newScalar(subContainerDao, "diceSize", "%d", cpData->mana);
        dao_newScalar(subContainerDao, "diceAdd", "%d", cpData->move);
        subContainerDao = NULL;
      } else {
        dao_newScalar(parentDao, "hitPoints", "%d", cpData->hit);
        dao_newScalar(parentDao, "manaPoints", "%d", cpData->mana);
        dao_newScalar(parentDao, "maxHitPoints", "%d", cpData->max_hit);
      }

      dao_newScalar(parentDao, "maxManaPoints", "%d", cpData->max_mana);
      dao_newScalar(parentDao, "maxMovePoints", "%d", cpData->max_move);
    }
    /* Common data between players and mobiles */

    subContainerDao = dao_newChild(parentDao, "gold");
    dao_newScalar(subContainerDao, "inHand", "%d", cpData->gold);
    dao_newScalar(subContainerDao, "inBank", "%d", cpData->bank_gold);
    subContainerDao = NULL;

    dao_newScalar(parentDao, "armor", "%d", cpData->armor);
    dao_newScalar(parentDao, "experience", "%d", cpData->exp);
    dao_newScalar(parentDao, "hitRoll", "%d", cpData->hitroll);
    dao_newScalar(parentDao, "damRoll", "%d", cpData->damroll);
  }
}

/**
 * Convert a playerSpecials_t to it's DAO representation
 *
 * @param parentDao the container DAO to contain the item's DAO
 * @param psData the playerSpecials_t to be converted
 * @return none
 */
void playerSpecials_toDao(daoData_t *parentDao, playerSpecials_t *psData) {
  if (parentDao == NULL) {
    log("playerSpecials_toDao(): invalid 'parentDao' daoData_t.");
  } else if (psData == NULL) {
    log("playerSpecials_toDao(): invalid 'psData' playerSpecials_t.");
  } else {
    /* Declare a DAO pointer */
    daoData_t *subContainerDao = NULL;

    subContainerDao = dao_newChild(parentDao, "poofs");
    dao_newScalar(parentDao, "in", "%s", psData->poofin);
    dao_newScalar(parentDao, "out", "%s", psData->poofout);
    subContainerDao = NULL;

    /* Now for the savedPlayerSpecials */
    subContainerDao = dao_newChild(parentDao, "savedPlayerSpecials");
    savedPlayerSpecials_toDao(subContainerDao, &(psData->saved));
    subContainerDao = NULL;

    /* Aliases */
    /** @todo Code alias saving */
    subContainerDao = dao_newChild(parentDao, "aliases");
    subContainerDao = NULL;
  }
}

/**
 * Convert a savedPlayerSpecials_t to it's DAO representation
 *
 * @param parentDao the container DAO to contain the item's DAO
 * @param psData the savedPlayerSpecials_t to be converted
 * @return none
 */
void savedPlayerSpecials_toDao(daoData_t *parentDao, savedPlayerSpecials_t *psData) {
  if (parentDao == NULL) {
    log("savedPlayerSpecials_toDao(): invalid 'parentDao' daoData_t.");
  } else if (psData == NULL) {
    log("savedPlayerSpecials_toDao(): invalid 'psData' savedPlayerSpecials_t.");
  } else {
    /* Declare a DAO pointer */
    daoData_t *subContainerDao = NULL;
    /* Declare an iterator variable. */
    register int i = 0;
    /* Declare some temporary buffer space. */
    char temp[MAX_STRING_LENGTH] = {'\0'};

    if (psData->wimp_level > 0) {
      dao_newScalar(parentDao, "wimpLevel", "%d", psData->wimp_level);
    }
    if (psData->freeze_auth > AUTH_NONE) {
      sprinttype(psData->freeze_auth, auth_types, temp, sizeof(temp));
      dao_newScalar(parentDao, "freezeAuth", "%s", temp);
    }
    if (psData->invis_auth > AUTH_NONE) {
      sprinttype(psData->invis_auth, auth_types, temp, sizeof(temp));
      dao_newScalar(parentDao, "invisAuth", "%s", temp);
    }
    dao_newScalar(parentDao, "loadRoom", "%d", psData->load_room);
    dao_newScalar(parentDao, "badPWAttempts", "%d", psData->bad_pws);
    dao_newScalar(parentDao, "spellsToLearn", "%d", psData->spells_to_learn);

    subContainerDao = dao_newChild(parentDao, "skills");
    for (i = 0; i <= MAX_SKILLS; i++) {
      if (psData->skills[i] > 0) {
        snprintf(temp, sizeof(temp), "%d", i);
        dao_newScalar(subContainerDao, temp, "%d", psData->skills[i]);
      }
    }
    subContainerDao = NULL;

    /* Saving these for now, but it doesn't appear they're actually used anywhere */
    subContainerDao = dao_newChild(parentDao, "talks");
    for (i = 0; i <= MAX_TONGUE; i++) {
      snprintf(temp, sizeof(temp), "%d", i);
      if (psData->talks[i]) {
        dao_newScalar(subContainerDao, temp, "%s", "Yes");
      } else {
        dao_newScalar(subContainerDao, temp, "%s", "No");
      }
    }
    subContainerDao = NULL;

    if (psData->pref != 0UL) {
      subContainerDao = dao_newChild(parentDao, "preferences");
      dao_newScalar(subContainerDao, "brief", YESNO(IS_SET(psData->pref, PRF_BRIEF)));
      dao_newScalar(subContainerDao, "compact", YESNO(IS_SET(psData->pref, PRF_COMPACT)));
      dao_newScalar(subContainerDao, "deaf", YESNO(IS_SET(psData->pref, PRF_DEAF)));
      dao_newScalar(subContainerDao, "noTell", YESNO(IS_SET(psData->pref, PRF_NOTELL)));
      dao_newScalar(subContainerDao, "displayHP", YESNO(IS_SET(psData->pref, PRF_DISPHP)));
      dao_newScalar(subContainerDao, "displayMana", YESNO(IS_SET(psData->pref, PRF_DISPMANA)));
      dao_newScalar(subContainerDao, "displayMove", YESNO(IS_SET(psData->pref, PRF_DISPMOVE)));
      dao_newScalar(subContainerDao, "autoExits", YESNO(IS_SET(psData->pref, PRF_AUTOEXIT)));
      dao_newScalar(subContainerDao, "noHassle", YESNO(IS_SET(psData->pref, PRF_NOHASSLE)));
      dao_newScalar(subContainerDao, "quest", YESNO(IS_SET(psData->pref, PRF_QUEST)));
      dao_newScalar(subContainerDao, "summon", YESNO(IS_SET(psData->pref, PRF_SUMMONABLE)));
      dao_newScalar(subContainerDao, "noRepeat", YESNO(IS_SET(psData->pref, PRF_NOREPEAT)));
      dao_newScalar(subContainerDao, "holyLight", YESNO(IS_SET(psData->pref, PRF_HOLYLIGHT)));
      dao_newScalar(subContainerDao, "color1", YESNO(IS_SET(psData->pref, PRF_COLOR_1)));
      dao_newScalar(subContainerDao, "color2", YESNO(IS_SET(psData->pref, PRF_COLOR_2)));
      dao_newScalar(subContainerDao, "noWiznet", YESNO(IS_SET(psData->pref, PRF_NOWIZ)));
      dao_newScalar(subContainerDao, "log1", YESNO(IS_SET(psData->pref, PRF_LOG1)));
      dao_newScalar(subContainerDao, "log2", YESNO(IS_SET(psData->pref, PRF_LOG2)));
      dao_newScalar(subContainerDao, "noAuction", YESNO(IS_SET(psData->pref, PRF_NOAUCT)));
      dao_newScalar(subContainerDao, "noGossip", YESNO(IS_SET(psData->pref, PRF_NOGOSS)));
      dao_newScalar(subContainerDao, "noGrats", YESNO(IS_SET(psData->pref, PRF_NOGRATZ)));
      dao_newScalar(subContainerDao, "roomFlags", YESNO(IS_SET(psData->pref, PRF_ROOMFLAGS)));
      dao_newScalar(subContainerDao, "displayAuto", YESNO(IS_SET(psData->pref, PRF_DISPAUTO)));
      subContainerDao = NULL;
    }

    subContainerDao = dao_newChild(parentDao, "conditions");
    dao_newScalar(subContainerDao, "drunk", "%d", psData->conditions[DRUNK]);
    dao_newScalar(subContainerDao, "hunger", "%d", psData->conditions[FULL]);
    dao_newScalar(subContainerDao, "thirst", "%d", psData->conditions[THIRST]);
    subContainerDao = NULL;
  }
}

/**
 * Convert a savedCharSpecials_t to it's DAO representation
 *
 * @param parentDao the container DAO to contain the item's DAO
 * @param csData the savedCharSpecials_t to be converted
 * @param isPlayer TRUE if being called for a Player, FALSE if being called for a Mobile
 * @return none
 */
void savedCharSpecials_toDao(daoData_t *parentDao, savedCharSpecials_t *csData, bool isPlayer) {
  if (parentDao == NULL) {
    log("savedCharSpecials_toDao(): invalid 'parentDao' daoData_t.");
  } else if (csData == NULL) {
    log("savedCharSpecials_toDao(): invalid 'csData' savedCharSpecials_t.");
  } else {
    /* Declare a DAO pointer */
    daoData_t *subContainerDao = NULL;

    /* Declare an iterator variable. */
    register int i = 0;

    /* Declare some temporary buffer space. */
    char temp[MAX_STRING_LENGTH] = {'\0'};

    if (isPlayer) {
      /* PC Data */
      dao_newScalar(parentDao, "idnum", "%d", csData->idnum);

      subContainerDao = dao_newChild(parentDao, "playerFlags");
      dao_newScalar(subContainerDao, "killer", YESNO(IS_SET(csData->act, PLR_KILLER)));
      dao_newScalar(subContainerDao, "thief", YESNO(IS_SET(csData->act, PLR_THIEF)));
      dao_newScalar(subContainerDao, "frozen", YESNO(IS_SET(csData->act, PLR_FROZEN)));
      dao_newScalar(subContainerDao, "writing", YESNO(IS_SET(csData->act, PLR_WRITING)));
      dao_newScalar(subContainerDao, "mailing", YESNO(IS_SET(csData->act, PLR_MAILING)));
      dao_newScalar(subContainerDao, "crash", YESNO(IS_SET(csData->act, PLR_CRASH)));
      dao_newScalar(subContainerDao, "siteOK", YESNO(IS_SET(csData->act, PLR_SITEOK)));
      dao_newScalar(subContainerDao, "noShout", YESNO(IS_SET(csData->act, PLR_NOSHOUT)));
      dao_newScalar(subContainerDao, "noTitle", YESNO(IS_SET(csData->act, PLR_NOTITLE)));
      dao_newScalar(subContainerDao, "deleted", YESNO(IS_SET(csData->act, PLR_DELETED)));
      dao_newScalar(subContainerDao, "loadRoom", YESNO(IS_SET(csData->act, PLR_LOADROOM)));
      dao_newScalar(subContainerDao, "noWizlist", YESNO(IS_SET(csData->act, PLR_NOWIZLIST)));
      dao_newScalar(subContainerDao, "noDelete", YESNO(IS_SET(csData->act, PLR_NODELETE)));
      dao_newScalar(subContainerDao, "invisStart", YESNO(IS_SET(csData->act, PLR_INVSTART)));
      dao_newScalar(subContainerDao, "cryoSaved", YESNO(IS_SET(csData->act, PLR_CRYO)));
      dao_newScalar(subContainerDao, "Dead", YESNO(IS_SET(csData->act, PLR_NOTDEADYET)));
      subContainerDao = NULL;
    } else {
      /* NPC Data */
      subContainerDao = dao_newChild(parentDao, "mobileFlags");
      dao_newScalar(subContainerDao, "spec", YESNO(IS_SET(csData->act, MOB_SPEC)));
      dao_newScalar(subContainerDao, "sentinel", YESNO(IS_SET(csData->act, MOB_SENTINEL)));
      dao_newScalar(subContainerDao, "scavenger", YESNO(IS_SET(csData->act, MOB_SCAVENGER)));
      dao_newScalar(subContainerDao, "isNPC", YESNO(IS_SET(csData->act, MOB_ISNPC)));
      dao_newScalar(subContainerDao, "aware", YESNO(IS_SET(csData->act, MOB_AWARE)));
      dao_newScalar(subContainerDao, "aggressive", YESNO(IS_SET(csData->act, MOB_AGGRESSIVE)));
      dao_newScalar(subContainerDao, "stayZone", YESNO(IS_SET(csData->act, MOB_STAY_ZONE)));
      dao_newScalar(subContainerDao, "wimpy", YESNO(IS_SET(csData->act, MOB_WIMPY)));
      dao_newScalar(subContainerDao, "aggroEvil", YESNO(IS_SET(csData->act, MOB_AGGR_EVIL)));
      dao_newScalar(subContainerDao, "aggroGood", YESNO(IS_SET(csData->act, MOB_AGGR_GOOD)));
      dao_newScalar(subContainerDao, "aggroNeutral", YESNO(IS_SET(csData->act, MOB_AGGR_NEUTRAL)));
      dao_newScalar(subContainerDao, "memory", YESNO(IS_SET(csData->act, MOB_MEMORY)));
      dao_newScalar(subContainerDao, "helper", YESNO(IS_SET(csData->act, MOB_HELPER)));
      dao_newScalar(subContainerDao, "noCharm", YESNO(IS_SET(csData->act, MOB_NOCHARM)));
      dao_newScalar(subContainerDao, "noSummon", YESNO(IS_SET(csData->act, MOB_NOSUMMON)));
      dao_newScalar(subContainerDao, "noSleep", YESNO(IS_SET(csData->act, MOB_NOSLEEP)));
      dao_newScalar(subContainerDao, "noBash", YESNO(IS_SET(csData->act, MOB_NOBASH)));
      dao_newScalar(subContainerDao, "noBlind", YESNO(IS_SET(csData->act, MOB_NOBLIND)));
      dao_newScalar(subContainerDao, "Dead", YESNO(IS_SET(csData->act, MOB_NOTDEADYET)));
      subContainerDao = NULL;
    }

    dao_newScalar(parentDao, "alignment", "%d", csData->alignment);

    subContainerDao = dao_newChild(parentDao, "savingThrows");
    for (i = 0; i < 5; i++) {
      snprintf(temp, sizeof(temp), "%s", save_types[i]);
      dao_newScalar(subContainerDao, temp, "%d", csData->apply_saving_throw[i]);
    }
    subContainerDao = NULL;
  }
}

/* ************************************************************************ */
/*  Load Character Data from DAO                                            */
/* ************************************************************************ */

/**
 * Load a charPlayerData_t from it's dao representation
 *
 * NOTE: This will re-set all string variables, so should only be used for players
 *       or mobile prototypes
 *
 * @param cpData charPlayerData_t to populate
 * @param isPlayer TRUE or FALSE if player character
 * @param dao charPlayerData_t in dao format
 */
void charPlayerData_fromDao(charPlayerData_t *cpData, bool isPlayer, daoData_t *dao) {

  if (cpData == NULL) {
    log("charPlayerData_fromDao(): invalid 'charPlayerData_t' cpData.");
  } else if (dao == NULL) {
    log("charPlayerData_fromDao(): invalid 'daoData_t' dao.");
  } else {
    char *sTemp = NULL;

    if (isPlayer) {
      /* Load data that's player specific */
      /* Password */
      sTemp = (char *)dao_queryString(dao, "password", NULL);
      if (sTemp) {
        strncpy(cpData->passwd, sTemp, MAX(strlen(sTemp), MAX_PWD_LENGTH));
      }
      sTemp = NULL;
      /* Age */
      cpData->time.birth = dao_queryLong(dao, "birth", time(0));
      cpData->time.logon = dao_queryLong(dao, "logon", time(0));
      cpData->time.played = dao_queryLong(dao, "timePlayed", 0);

      /* Class */
      cpData->chclass = dao_queryType(dao, "class", pc_class_types, 0);

      /* Name */
      cpData->name = stringReplace(cpData->name, (char *)dao_queryString(dao, "name", NULL));
    } else {
      /* Load data that's mobile specific */
      /* Class */
      cpData->chclass = dao_queryType(dao, "class", npc_class_types, 0);

      /* Short Description */
      cpData->short_descr = stringReplace(cpData->short_descr, (char *)dao_queryString(dao, "shortDescription", NULL));

      /* Keywords */
      cpData->name = stringReplace(cpData->name, (char *)dao_queryString(dao, "keywords", NULL));
    }
    /* Load data shared by PCs and Mobiles */
    /* Long Description */
    cpData->long_descr = stringReplace(cpData->long_descr, (char *)dao_queryString(dao, "longDescription", NULL));

    /* Description */
    cpData->description = stringReplace(cpData->description, (char *)dao_queryString(dao, "description", NULL));

    /* Title */
    cpData->title = stringReplace(cpData->title, (char *)dao_queryString(dao, "title", NULL));

    /* Level */
    cpData->level = dao_queryInt(dao, "level", 0);

    /* Gender */
    cpData->sex = dao_queryType(dao, "sex", genders, 0);

    /* Home Town */
    cpData->hometown = dao_queryInt(dao, "homeTown", 0);    

    /* Weight */
    cpData->weight = dao_queryInt(dao, "weight", 0);

    /* Height */
    cpData->height = dao_queryInt(dao, "height", 0);
  }
}

/**
 * Load a charAbilities_t from it's dao representation
 *
 * @param caData charAbilities_t to populate
 * @param dao charPlayerData_t in dao format
 */
void charAbilities_fromDao(charAbilities_t *caData, daoData_t *dao) {

  if (caData == NULL) {
    log("charAbilities_fromDao(): invalid 'charAbilities_t' caData.");
  } else if (dao == NULL) {
    log("charAbilities_fromDao(): invalid 'daoData_t' dao.");
  } else {
    caData->str = dao_queryInt(dao, "strength", 0);
    caData->str_add = dao_queryInt(dao, "strengthAdd", 0);
    caData->intel = dao_queryInt(dao, "intelligence", 0);
    caData->wis = dao_queryInt(dao, "wisdom", 0);
    caData->dex = dao_queryInt(dao, "dexterity", 0);
    caData->con = dao_queryInt(dao, "constitution", 0);
    caData->cha = dao_queryInt(dao, "charisma", 0);
  }
}

/**
 * Load a charPointData_t from it's dao representation
 *
 * @param cpData charPointData_t to populate
 * @param isPlayer TRUE or FLASE if player character
 * @param dao charPointData_t in dao format
 */
void charPointData_fromDao(charPointData_t *cpData, bool isPlayer, daoData_t *dao) {

  if (cpData == NULL) {
    log("charPointData_fromDao(): invalid 'charPointData_t' cpData.");
  } else if (dao == NULL) {
    log("charPointData_fromDao(): invalid 'daoData_t' dao.");
  } else {

    /* HP, MP, MV */
    if (isPlayer) {
      /* Load data that's player specific */
      cpData->hit = dao_queryInt(dao, "hitPoints/current", 0);
      cpData->max_hit = dao_queryInt(dao, "hitPoints/maximum", 0);
      cpData->mana = dao_queryInt(dao, "manaPoints/current", 0);
      cpData->max_mana = dao_queryInt(dao, "manaPoints/maximum", 0);
      cpData->move = dao_queryInt(dao, "movePoints/current", 0);
      cpData->max_move = dao_queryInt(dao, "movePoints/maximum", 0);
    } else {
      /* Load data that's mobile specific */
      /* Declare a holder variable, and init off query */
      int max_hit = dao_queryInt(dao, "maxHitPoints", 0);

      if (max_hit == 0) {
        cpData->hit = dao_queryInt(dao, "hitPoints/diceNumber", 0);
        cpData->mana = dao_queryInt(dao, "hitPoints/diceSize", 0);
        cpData->move = dao_queryInt(dao, "hitPoints/diceAdd", 0);
      } else {
        cpData->hit = dao_queryInt(dao, "hitPoints", 0);
        cpData->mana = dao_queryInt(dao, "manaPoints", 0);
        cpData->max_hit = max_hit;
      }
      cpData->max_mana = dao_queryInt(dao, "maxManaPoints", 0);
      cpData->max_move = dao_queryInt(dao, "maxMovePoints", 0);
    }
    /* Load data that's shared by PCs and Mobiles */

    /* Gold */
    cpData->gold = dao_queryInt(dao, "gold/inHand", 0);
    cpData->bank_gold = dao_queryInt(dao, "gold/inBank", 0);

    /* Armor, XP, Hit/Dam */
    cpData->armor = dao_queryInt(dao, "armor", 0);
    cpData->exp = dao_queryInt(dao, "experience", 0);
    cpData->hitroll = dao_queryInt(dao, "hitRoll", 0);
    cpData->damroll = dao_queryInt(dao, "damRoll", 0);
  }
}

/**
 * Load a playerSpecials_t from it's dao representation
 *
 * @param psData playerSpecials_t to populate
 * @param dao playerSpecials_t in dao format
 */
void playerSpecials_fromDao(playerSpecials_t *psData, daoData_t *dao) {

  if (psData == NULL) {
    log("playerSpecials_fromDao(): invalid 'playerSpecials_t' psData.");
  } else if (dao == NULL) {
    log("playerSpecials_fromDao(): invalid 'daoData_t' dao.");
  } else {
    daoData_t *subDao = NULL;

    /* Poofin */
    psData->poofin = stringReplace(psData->poofin, (char *)dao_queryString(dao, "poofs/in", NULL));

    /* Poofout */
    psData->poofin = stringReplace(psData->poofout, (char *)dao_queryString(dao, "poofs/out", NULL));

    /* savedPlayerSpecials */
    subDao = dao_query(dao, "savedPlayerSpecials");
    if (subDao) {
      savedPlayerSpecials_fromDao(&(psData->saved), subDao);
    }
    subDao = NULL;

    /* Aliases */
    /** @todo Code alias loading */
  }
}

/**
 * Load a savedPlayerSpecials_t from it's dao representation
 *
 * @param psData savedPlayerSpecials_t to populate
 * @param dao savedPlayerSpecials_t in dao format
 */
void savedPlayerSpecials_fromDao(savedPlayerSpecials_t *psData, daoData_t *dao) {

  if (psData == NULL) {
    log("savedPlayerSpecials_fromDao(): invalid 'savedPlayerSpecials_t' psData.");
  } else if (dao == NULL) {
    log("savedPlayerSpecials_fromDao(): invalid 'daoData_t' dao.");
  } else {
    /* Declare some DAO pointers */
    daoData_t *subDao = NULL, *childDao = NULL;

    /* Load the main data */
    psData->wimp_level = dao_queryInt(dao, "wimpLevel", 0);
    psData->freeze_auth = dao_queryInt(dao, "freezeLevel", 0);
    psData->invis_auth = dao_queryInt(dao, "invisLevel", 0);
    psData->load_room = dao_queryInt(dao, "loadRoom", 0);
    psData->bad_pws = dao_queryInt(dao, "badPWAttempts", 0);
    psData->spells_to_learn = dao_queryInt(dao, "spellsToLearn", 0);

    /* Skills */
    subDao = dao_query(dao, "skills");
    if (subDao) {
      /* Loop through the entries for skills and set the values as needed */
      for (childDao = subDao->children; childDao; childDao = childDao->next) {
        register int sNum = dao_keyInt(childDao, -1);

        /* Sanity check */
        if (sNum >= 0 && sNum <= MAX_SKILLS) {
          psData->skills[sNum] = dao_valueInt(childDao, 0);
        }
      }
      childDao = NULL;
    }
    subDao = NULL;

    /* Talks/Tongues/Languages */
    /*   Not used in the code, but we're saving 'em anyway
     *   Even though we don't have pfiles saving to DAO yet. 
     *   Yes, we're odd.
     */
    subDao = dao_query(dao, "talks");
    if (subDao) {
      /* Loop through the entries for the languages/talks and set the values */
      for (childDao = subDao->children; childDao; childDao = childDao->next) {
        register int lNum = dao_keyInt(childDao, -1);

        /* Sanity */
        if (lNum >= 0 && lNum <= MAX_TONGUE) {
          if (strcasecmp(dao_valueString(childDao, "No"), "Yes") == 0) {
            psData->talks[lNum] = 1;
          }
        }
      }
      childDao = NULL;
    }
    subDao = NULL;

    /* Preferences */
    psData->pref = dao_queryBits(dao, "preferences", preference_bits, 0);

    /* Conditions */
    psData->conditions[DRUNK] = dao_queryInt(dao, "conditions/drunk", 0);
    psData->conditions[FULL] = dao_queryInt(dao, "conditions/hunger", 0);
    psData->conditions[THIRST] = dao_queryInt(dao, "conditions/thirst", 0);

    /* Done */
  }
}

/**
 * Load a savedCharSpecials_t from it's dao representation
 *
 * @param csData savedCharSpecials_t to populate
 * @param isPlayer TRUE or FALSE if player character
 * @param dao savedCharSpecials_t in dao format
 */
void savedCharSpecials_fromDao(savedCharSpecials_t *csData, bool isPlayer, daoData_t *dao) {

  if (csData == NULL) {
    log("savedCharSpecials_fromDao(): invalid 'savedCharSpecials_t' csData.");
  } else if (dao == NULL) {
    log("savedCharSpecials_fromDao(): invalid 'daoData_t' dao.");
  } else {
    /* Declare some DAO pointers */
    daoData_t *subDao = NULL, *childDao = NULL;
    
    if (isPlayer) {
      /* Player Specific Data */
      /* IDNum */
      csData->idnum = dao_queryInt(dao, "idnum", -1);

      /* Player Flags */
      csData->act = dao_queryBits(dao, "playerFlags", player_bits, 0);
    
    } else {
      /* Mobile Specific Data */
      /* Mobile Flags */
      csData->act = dao_queryBits(dao, "mobileFlags", action_bits, 0);
    }
    /* Shared Data */
    /* Alignment */
    csData->alignment = dao_queryInt(dao, "alignment", 0);

    /* Saving Throws */
    /* This is ugly, but it works ... I think */
    subDao = dao_query(dao, "savingThrows");
    if (subDao) {
      /* Loop over the contents */
      for (childDao = subDao->children; childDao; childDao = childDao->next) {
        /* Declare an iterator var */
        register int n = 0;
        /* Declare and init a string pointer */
        char *sTemp = (char *)dao_keyString(childDao, NULL);

        /* Find the value in the save_types[] array if possible */
        for (n = 0; n < 5; n++) {
          if (sTemp && *sTemp && strcasecmp(sTemp, save_types[n]) == 0) {
            /* Set the value */
            csData->apply_saving_throw[n] = dao_valueInt(childDao, 0);
            break;
          }
        }
        /* Sanity */
        sTemp = NULL;
      }
    }
    subDao = NULL;

    /* Done */
  }
}