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