/**
* @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);
}