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 mobile.c
 * @ingroup mobile
 *
 * Mobile 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 __MOBILE_C__

#include "base.h"
#include "structs.h"
#include "utils.h"
#include "character.h"
#include "comm.h"
#include "db.h"
#include "handler.h"
#include "interpreter.h"
#include "log.h"
#include "dao.h"
#include "character.h"
#include "zone.h"
#include "mobile.h"

/*
 * Local functions
 */

void mobileSpecialData_toDao(daoData_t *parentDao, mobileSpecialData_t *msData);
void charData_addMobileFromDao(zoneData_t *zone, daoData_t *mobileDao);
void charData_freeMobilePrototype(void *m);
void mobileSpecialData_fromDao(mobileSpecialData_t *msData, daoData_t *dao);
charData_t *charData_findMobilePrototypeInZone(zoneData_t *zone, int mobileNumber);
charData_t *charData_newMobilePrototype(zoneData_t *zone, mobileVnum_t mVnum);

/*
 * External variables
 */

extern const char *position_types[];

/**
 * Convert a mobile prototype to its DAO representation.
 *
 * @param parentDao the container DAO to contain the item's DAO
 * @param mob the mobile to be converted
 * @return none
 */
void mobile_toDao(daoData_t *parentDao, charData_t *mob) {
  if (parentDao == NULL) {
    log("mobile_toDao(): invalid 'parentDao' daoData_t.");
  } else if (mob == NULL) {
    log("mobile_toDao(): invalid 'mob' charData_t.");
  } else {
    /* Declare some working DAO pointers */
    daoData_t *mobDao = NULL, *subContainerDao = NULL;

    mobDao = dao_newChild(parentDao, "%d", mob->vnum);

    charPlayerData_toDao(mobDao, &(mob->player), FALSE);

    /*
     * Okay, we save the abilities for all mobiles.  This is different
     * from stock Circle which only saved the abilities for mob prototypes
     * when they're the Circle 3 "Enhanced" mobs.  Standard mobs (the majority)
     */
    subContainerDao = dao_newChild(mobDao, "abilities");
    charAbilities_toDao(subContainerDao, &(mob->real_abils));
    subContainerDao = NULL;

    subContainerDao = dao_newChild(mobDao, "points");
    charPointData_toDao(subContainerDao, &(mob->points), FALSE);
    subContainerDao = NULL;

    subContainerDao = dao_newChild(mobDao, "savedCharSpecials");
    savedCharSpecials_toDao(subContainerDao, &(mob->char_specials.saved), FALSE);
    subContainerDao = NULL;

    subContainerDao = dao_newChild(mobDao, "mobileSpecials");
    mobileSpecialData_toDao(subContainerDao, &(mob->mob_specials));
    subContainerDao = NULL;

    /*
     * Affects, equipment, and carrying are unused for mobiles
     * at this time.  This will likely be changed in the future
     * as part of cleanup with the zone reset commands.
     */
  }
}

/**
 * Convert a mobileSpecialData_t to it's DAO representation
 *
 * @param parentDao the container DAO to contain the item's DAO
 * @param msData the mobileSpecialData_t to be converted
 * @return none
 */
void mobileSpecialData_toDao(daoData_t *parentDao, mobileSpecialData_t *msData) {
  if (parentDao == NULL) {
    log("mobileSpecialData_toDao(): invalid 'parentDao' daoData_t.");
  } else if (msData == NULL) {
    log("mobileSpecialData_toDao(): invalid 'msData' mobileSpecialData_t.");
  } else {
    dao_newScalar(parentDao, "attackType", "%d", msData->attack_type);
    dao_newScalar(parentDao, "defaultPosition", "%s", position_types[msData->default_pos]);
    dao_newScalar(parentDao, "damageDiceNumber", "%d", msData->damnodice);
    dao_newScalar(parentDao, "damageDiceSize", "%d", msData->damsizedice);
  }
}

/* ************************************************************************ */
/*  Load Mobile Data from DAO                                               */
/* ************************************************************************ */

/**
 * Load all mobiles in zone
 *
 * @param zone Zone to load mobiles into
 * @param mobileListDao daoData_t of mobiles to load
 * @return none
 */
void charData_loadMobilesInZone(zoneData_t *zone, daoData_t *mobileListDao) {

  if (zone == NULL) {
    log("charData_loadMobilesInZone(): Invalid 'zone' zoneData_t.");
  } else {
    if (mobileListDao != NULL) {
      daoData_t *entDao = NULL;

      for (entDao = mobileListDao->children; entDao; entDao = entDao->next) {
        charData_addMobileFromDao(zone, entDao);
      }
    }
  }
}

/**
 * Load a single mobile from a dao entry
 *
 * @param zone Zone to load mobile into
 * @param mobileDao daoData_t of the mobile to load
 * @return none
 */
void charData_addMobileFromDao(zoneData_t *zone, daoData_t *mobileDao) {

  if (zone == NULL) {
    log("charData_addMobileFromDao(): Invalid 'zone' zoneData_t.");
  } else if (mobileDao == NULL) {
    log("charData_addMobileFromDao(): Invalid 'mobileDao' daoData_t.");
  } else {
    charData_t *mobile = charData_newMobilePrototype(zone, dao_keyInt(mobileDao, -1));
    daoData_t *subDao = NULL;

    if (mobile != NULL) {
      /* Ok.  We've got our new mobile prototype alloc'd in the zone's mobiles hashMap */
      /* Now let's set about loading data */

      /* playerData */
      subDao = dao_query(mobileDao, "playerData");
      if (subDao) {
        charPlayerData_fromDao(&(mobile->player), FALSE, subDao);
      }
      subDao = NULL;

      /* real abilities */
      subDao = dao_query(mobileDao, "abilities");
      if (subDao) {
        charAbilities_fromDao(&(mobile->real_abils), subDao);
      }
      subDao = NULL;

      /* points */
      subDao = dao_query(mobileDao, "points");
      if (subDao) {
        charPointData_fromDao(&(mobile->points), FALSE, subDao);
      }
      subDao = NULL;

      /* saveCharSpecials */
      subDao = dao_query(mobileDao, "savedCharSpecials");
      if (subDao) {
        savedCharSpecials_fromDao(&(mobile->char_specials.saved), FALSE, subDao);
      }
      subDao = NULL;

      /* mobileSpecials */
      subDao = dao_query(mobileDao, "mobileSpecials");
      if (subDao) {
        mobileSpecialData_fromDao(&(mobile->mob_specials), subDao);
      }
      subDao = NULL;

      /* Done! */
    }
  }
}

/**
 * Load a mobileSpecialData_t from it's DAO representation
 *
 * @param msData mobileSpecialData_t to populate
 * @param dao mobileSpecialData_t in dao format
 */
void mobileSpecialData_fromDao(mobileSpecialData_t *msData, daoData_t *dao) {

  if (msData == NULL) {
    log("mobileSpecialData_fromDao(): Invalid 'mobileSpecialData_t' msData.");
  } else if (dao == NULL) {
    log("mobileSpecialData_fromDao(): Invalid 'daoData_t' dao.");
  } else {

    /* Attack Type */
    msData->attack_type = dao_queryInt(dao, "attackType", 0);

    /* Default Position */
    msData->default_pos = dao_queryType(dao, "defaultPosition", position_types, 0);

    /* Number of Damage Dice */
    msData->damnodice = dao_queryInt(dao, "damageDiceNumber", 0);

    /* Size of Damage Dice */
    msData->damsizedice = dao_queryInt(dao, "damageDiceSize", 0);

    /* Done */
  }
}

/* ************************************************************************ */
/*  General Mobile Functions                                                */
/* ************************************************************************ */

/**
 * Create a new mobile prototype, or re-use existing with this vnum
 *
 * @param zone Zone to create the mobile prototype in
 * @param mVnum VNum of mobile to create
 * @return pointer to mobile entry or NULL
 */
charData_t *charData_newMobilePrototype(zoneData_t *zone, mobileVnum_t mVnum) {
  charData_t *mobile = NULL;

  if (zone == NULL) {
    log("charData_newMobilePrototype(): invalid 'zone' zoneData_t.");
  } else if (mVnum < 0) {
    log("charData_newMobilePrototype(): invalid 'mVnum' mobileVnum_t.");
  } else {
    /* If the zone's items hashMap doesn't exist, create it */
    if (zone->mobiles == NULL) {
      zone->mobiles = hashMap_create(0, hashVnum, NULL, charData_freeMobilePrototype);
      if (zone->mobiles == NULL) {
        log("charData_newMobilePrototype(): NULL returned from hashMap_create().  Aborting.");
        exit(1);
      }
    }

    /* Process the mobile */
    mobile = charData_findMobilePrototypeInZone(zone, mVnum);

    /* If we don't have a mobile returned, create one.  Otherwise free it out */
    if (mobile == NULL) {
      CREATE(mobile, charData_t, 1);

      /* Set the VNum */
      mobile->vnum = mVnum;

      /* Set the zone */
      mobile->zone = zone;

      /* Since it wasn't in the hashMap, add it */
      hashMap_insert(zone->mobiles, &mobile->vnum, mobile);
    } else {
      if (mobile->player.name)
        free(mobile->player.name);
      if (mobile->player.title)
        free(mobile->player.title);
      if (mobile->player.short_descr)
        free(mobile->player.short_descr);
      if (mobile->player.long_descr)
        free(mobile->player.long_descr);
      if (mobile->player.description)
        free(mobile->player.description);
    }

    /* Sanity's Sake */

    mobile->player.name = NULL;
    mobile->player.title = NULL;
    mobile->player.short_descr = NULL;
    mobile->player.long_descr = NULL;
    mobile->player.description = NULL;

    mobile->shop = NULL;
  }

  return (mobile);
}

/**
 * Free a mobile prototype
 *
 * This function takes a void pointer and casts back to a charData_t so
 * that it will work with the hashMap code.
 *
 * @param m Mobile prototype entry to be freed (void pointer)
 * @return none
 */
void charData_freeMobilePrototype(void *m) {
  charData_t *mobile = (charData_t *)(m);

  if (mobile->zone)
    mobile->zone = NULL;

  if (mobile->player.name)
    free(mobile->player.name);
  if (mobile->player.title)
    free(mobile->player.title);
  if (mobile->player.short_descr)
    free(mobile->player.short_descr);
  if (mobile->player.long_descr)
    free(mobile->player.long_descr);
  if (mobile->player.description)
    free(mobile->player.description);

  free(mobile);
  mobile = NULL;
}

/**
 * Find a mobile prototype in hash map of rooms, by number
 *
 * (To be honest, I don't know if this will really be needed, but for now it's
 * just an abstraction used by the following function.)
 *
 * @param mobileList hashMap_t of mobile prototypes
 * @param mobileNumber Number (VNum) of mobile to find
 * @return pointer to mobile prototype or NULL
 */
charData_t *charData_findMobilePrototypeByNumber(hashMap_t *mobileList, int mobileNumber) {
  charData_t *mobile = (charData_t *)(hashMap_getValue(mobileList, &mobileNumber, NULL));

  return (mobile);
}

/**
 * Find a mobile prototype in a zone, by number
 *
 * @param zone Zone to check for the room
 * @param mobileNumber Number (VNum) of mobile to find
 * @return pointer to mobile or NULL
 */
charData_t *charData_findMobilePrototypeInZone(zoneData_t *zone, int mobileNumber) {
  charData_t *mobile = charData_findMobilePrototypeByNumber(zone->mobiles, mobileNumber);

  return (mobile);
}

/**
 * Check to see if a charData_t is a mobile prototype or not
 *
 * @param mobile Mobile charData_t to check
 * @return TRUE or FALSE
 */
bool charData_isPrototype(charData_t *mobile) {

  if (mobile->prototype == NULL && mobile == charData_findMobilePrototypeInZone(mobile->zone, mobile->vnum))
    return TRUE;
  else
    return FALSE;
}

/**
 * Find a mobile in the world by segmented "vnum"
 *
 * This is the function which whill be used the most, as it gives a referece
 * to the zone by keyword and the mobile by number
 *
 * @param strVnum Segemented vnum string
 * @return pointer to the mobile or NULL
 */
charData_t *charData_findMobilePrototype(char *strVnum) {
  charData_t *mob = NULL;

  if (strVnum == NULL || *strVnum == '\0') {
    log("itemData_find(): Invalid char 'strVnum'.");
  } else {
    zoneData_t *zone = NULL;
    char *z = NULL, *s = NULL;
    char *holder = strdup(strVnum);
    int mv = 0;

    z = holder;
    s = strstr(holder, ":");

    if (s) {
      *s = '\0';
      s++;
      mv = atoi(s);
    }

    if (z) {
      zone = zoneData_find(z);
      if (zone && zone->mobiles) {
        mob = charData_findMobilePrototypeInZone(zone, mv);
      }
    }

    if (holder)
      free(holder);
  }

  return mob;
}

/**
 * Find a mobile prototype for a character.
 *
 * This function does some processing to let the user "drop" the zone if they want
 * to reference an item in the current zone.
 *
 * @param ch Character to look up an item for
 * @param strVnum vnum string which might be segmented or not
 */
charData_t *charData_findMobilePrototypeForChar(charData_t *ch, char *strVnum) {
  charData_t *mob = NULL;

  if (strVnum == NULL || *strVnum == '\0') {
    log("charData_findMobilePrototypeForChar(): Invalid char 'strVnum'.");
  } else if (ch == NULL) {
    log("charData_findMobilePrototypeForChar(): Invalid charData_t 'ch'.");
  } else {
    char segmented[MAX_INPUT_LENGTH] = { '\0' };

    if (!strstr(strVnum, ":")) {
      snprintf(segmented, sizeof(segmented), "%s:%s", ch->room->zone->keyword, strVnum);
      mob = charData_findMobilePrototype(segmented);
    } else {
      mob = charData_findMobilePrototype(strVnum);
    }
  }

  return mob;
}

/**
 * Search a list of mobiles for an instance of a mobile, by prototype
 *
 * @param list List of items to search
 * @param itemProto Prototype of the item to look for
 */
charData_t *charData_getMobileInList(charData_t *list, charData_t *mobileProto) {

  if (mobileProto == NULL) {
    log("charData_getInList(): Invalid charData_t 'mobileProto'.  NULL.");
  } else if (charData_isPrototype(mobileProto) == FALSE) {
    log("charData_getInList(): Invalid charData_t 'mobileProto.'  Not a prototype.");
  } else {
    charData_t *mobile = NULL;

    for (mobile = list; mobile; mobile = mobile->next_in_room)
      if (mobile->prototype && mobile->prototype == mobileProto)
        return (mobile);
  }

  return (NULL);
}