//*****************************************************************************
//
// character.c
//
// Basic implementation of the character datastructure (PCs and NPCs). Includes
// data for storing room, inventory, body, equipment, and other "essential"
// information. If you plan on adding any other information to characters, it
// is strongly suggested you do so through auxiliary data (see auxiliary.h).
//
// For a recap, IF YOU PLAN ON ADDING ANY OTHER INFORMATION TO CHARACTERS, IT
// IS STRONGLY SUGGESTED YOU DO SO THROUGH AUXILIARY DATA (see auxiliary.h).
//
//*****************************************************************************
#include "mud.h"
#include "utils.h"
#include "body.h"
#include "races.h"
#include "auxiliary.h"
#include "storage.h"
#include "character.h"
// mob UIDs (unique IDs) start at a million and go
// up by one every time a new NPC is created
#define START_MOB_UID 1000000
int next_mob_uid = START_MOB_UID;
const char *sex_names[NUM_SEXES] = {
"male",
"female",
"neutral"
};
const char *sexGetName(int sex) {
return sex_names[sex];
}
int sexGetNum(const char *sex) {
int i;
for(i = 0; i < NUM_SEXES; i++)
if(!strcasecmp(sex, sex_names[i]))
return i;
return SEX_NONE;
}
struct pos_data {
char *name;
char *action_self;
char *action_other;
};
const struct pos_data pos_info[NUM_POSITIONS] = {
{ "unconscious", "fall unconscious", "falls unconscious" },
{ "sleeping", "sleep", "sleeps" },
{ "sitting", "sit", "sits" },
{ "standing", "stand", "stands" },
{ "flying", "fly", "flies" }
};
const char *posGetName(int pos) {
return pos_info[pos].name;
}
const char *posGetActionSelf(int pos) {
return pos_info[pos].action_self;
}
const char *posGetActionOther(int pos) {
return pos_info[pos].action_other;
}
int posGetNum(const char *pos) {
int i;
for(i = 0; i < NUM_POSITIONS; i++)
if(!strcasecmp(pos, pos_info[i].name))
return i;
return POS_NONE;
}
int poscmp(int pos1, int pos2) {
// a little hack... assumes that they
// are ordered in their definitions
if(pos1 == pos2) return 0;
else if(pos1 < pos2) return -1;
else return 1;
}
struct char_data {
// data for PCs only
char * loadroom;
// shared data for PCs and NPCs
int uid;
BODY_DATA * body;
char * race;
char * prototypes;
char * class;
SOCKET_DATA * socket;
ROOM_DATA * room;
ROOM_DATA * last_room;
OBJ_DATA * furniture;
BUFFER * desc;
BUFFER * look_buf;
char * name;
int sex;
int position;
LIST * inventory;
HASHTABLE * auxiliary_data;
BITVECTOR * prfs;
BITVECTOR * user_groups;
// data for NPCs only
char * rdesc;
char * multi_name;
char * multi_rdesc;
char * keywords;
};
CHAR_DATA *newChar() {
CHAR_DATA *ch = calloc(1, sizeof(CHAR_DATA));
ch->loadroom = strdup("");
ch->uid = NOBODY;
ch->race = strdup(raceDefault());
ch->body = raceCreateBody(ch->race);
ch->room = NULL;
ch->last_room = NULL;
ch->furniture = NULL;
ch->socket = NULL;
ch->desc = newBuffer(1);
ch->look_buf = newBuffer(1);
ch->name = strdup("");
ch->sex = SEX_NEUTRAL;
ch->position = POS_STANDING;
ch->inventory = newList();
ch->class = strdup("");
ch->prototypes = strdup("");
ch->rdesc = strdup("");
ch->keywords = strdup("");
ch->multi_rdesc = strdup("");
ch->multi_name = strdup("");
ch->prfs = bitvectorInstanceOf("char_prfs");
ch->user_groups = bitvectorInstanceOf("user_groups");
bitSet(ch->user_groups, DFLT_USER_GROUP);
ch->auxiliary_data = newAuxiliaryData(AUXILIARY_TYPE_CHAR);
return ch;
};
//*****************************************************************************
// utility functions
//*****************************************************************************
void charSetRdesc(CHAR_DATA *ch, const char *rdesc) {
if(ch->rdesc) free(ch->rdesc);
ch->rdesc = strdupsafe(rdesc);
}
void charSetMultiRdesc(CHAR_DATA *ch, const char *multi_rdesc) {
if(ch->multi_rdesc) free(ch->multi_rdesc);
ch->multi_rdesc = strdupsafe(multi_rdesc);
}
void charSetMultiName(CHAR_DATA *ch, const char *multi_name) {
if(ch->multi_name) free(ch->multi_name);
ch->multi_name = strdupsafe(multi_name);
}
bool charIsInstance(CHAR_DATA *ch, const char *prototype) {
return is_keyword(ch->prototypes, prototype, FALSE);
}
bool charIsNPC( CHAR_DATA *ch) {
return (ch->uid >= START_MOB_UID);
}
bool charIsName( CHAR_DATA *ch, const char *name) {
if(charIsNPC(ch))
return is_keyword(ch->keywords, name, TRUE);
else
return !strncasecmp(ch->name, name, strlen(name));
}
//*****************************************************************************
// set and get functions
//*****************************************************************************
LIST *charGetInventory ( CHAR_DATA *ch) {
return ch->inventory;
}
SOCKET_DATA *charGetSocket ( CHAR_DATA *ch) {
return ch->socket;
}
ROOM_DATA *charGetRoom ( CHAR_DATA *ch) {
return ch->room;
}
ROOM_DATA *charGetLastRoom(CHAR_DATA *ch) {
return ch->last_room;
}
const char *charGetClass(CHAR_DATA *ch) {
return ch->class;
}
const char *charGetPrototypes( CHAR_DATA *ch) {
return ch->prototypes;
}
const char *charGetName ( CHAR_DATA *ch) {
return ch->name;
}
const char *charGetDesc ( CHAR_DATA *ch) {
return bufferString(ch->desc);
}
BUFFER *charGetDescBuffer( CHAR_DATA *ch) {
return ch->desc;
}
BUFFER *charGetLookBuffer( CHAR_DATA *ch) {
return ch->look_buf;
}
const char *charGetRdesc ( CHAR_DATA *ch) {
return ch->rdesc;
}
const char *charGetMultiRdesc( CHAR_DATA *ch) {
return ch->multi_rdesc;
}
const char *charGetMultiName( CHAR_DATA *ch) {
return ch->multi_name;
}
int charGetSex ( CHAR_DATA *ch) {
return ch->sex;
}
int charGetPos ( CHAR_DATA *ch) {
return ch->position;
}
BODY_DATA *charGetBody ( CHAR_DATA *ch) {
return ch->body;
}
const char *charGetRace ( CHAR_DATA *ch) {
return ch->race;
}
int charGetUID ( const CHAR_DATA *ch) {
return ch->uid;
}
const char *charGetLoadroom (CHAR_DATA *ch) {
return ch->loadroom;
}
void *charGetAuxiliaryData(const CHAR_DATA *ch, const char *name) {
return hashGet(ch->auxiliary_data, name);
}
OBJ_DATA *charGetFurniture(CHAR_DATA *ch) {
return ch->furniture;
}
BITVECTOR *charGetPrfs(CHAR_DATA *ch) {
return ch->prfs;
}
BITVECTOR *charGetUserGroups(CHAR_DATA *ch) {
return ch->user_groups;
}
void charSetSocket ( CHAR_DATA *ch, SOCKET_DATA *socket) {
ch->socket = socket;
}
void charSetRoom ( CHAR_DATA *ch, ROOM_DATA *room) {
ch->room = room;
}
void charSetLastRoom(CHAR_DATA *ch, ROOM_DATA *room) {
ch->last_room = room;
}
void charSetClass(CHAR_DATA *ch, const char *prototype) {
if(ch->class) free(ch->class);
ch->class = strdupsafe(prototype);
}
void charSetPrototypes(CHAR_DATA *ch, const char *prototypes) {
if(ch->prototypes) free(ch->prototypes);
ch->prototypes = strdupsafe(prototypes);
}
void charAddPrototype(CHAR_DATA *ch, const char *prototype) {
add_keyword(&ch->prototypes, prototype);
}
void charSetName ( CHAR_DATA *ch, const char *name) {
if(ch->name) free(ch->name);
ch->name = strdupsafe(name);
}
void charSetSex ( CHAR_DATA *ch, int sex) {
ch->sex = sex;
}
void charSetPos ( CHAR_DATA *ch, int pos) {
ch->position = pos;
}
void charSetDesc ( CHAR_DATA *ch, const char *desc) {
bufferClear(ch->desc);
bufferCat(ch->desc, (desc ? desc : ""));
}
void charSetBody ( CHAR_DATA *ch, BODY_DATA *body) {
if(ch->body)
deleteBody(ch->body);
ch->body = body;
}
void charSetRace (CHAR_DATA *ch, const char *race) {
if(ch->race) free(ch->race);
ch->race = strdupsafe(race);
}
void charSetUID(CHAR_DATA *ch, int uid) {
ch->uid = uid;
}
void charResetBody(CHAR_DATA *ch) {
charSetBody(ch, raceCreateBody(ch->race));
}
void charSetLoadroom(CHAR_DATA *ch, const char *loadroom) {
if(ch->loadroom) free(ch->loadroom);
ch->loadroom = strdupsafe(loadroom);
}
void charSetFurniture(CHAR_DATA *ch, OBJ_DATA *furniture) {
ch->furniture = furniture;
}
//*****************************************************************************
//
// mob-specific functions
//
//*****************************************************************************
CHAR_DATA *newMobile() {
CHAR_DATA *mob = newChar();
mob->uid = next_mob_uid++;
return mob;
};
void deleteChar( CHAR_DATA *mob) {
// it is assumed we've already unequipped
// all of the items that are on our body (extract_char)
if(mob->body) deleteBody(mob->body);
// it's also assumed we've extracted our inventory
deleteList(mob->inventory);
if(mob->class) free(mob->class);
if(mob->prototypes) free(mob->prototypes);
if(mob->name) free(mob->name);
if(mob->desc) deleteBuffer(mob->desc);
if(mob->look_buf) deleteBuffer(mob->look_buf);
if(mob->rdesc) free(mob->rdesc);
if(mob->multi_rdesc) free(mob->multi_rdesc);
if(mob->multi_name) free(mob->multi_name);
if(mob->keywords) free(mob->keywords);
if(mob->loadroom) free(mob->loadroom);
if(mob->race) free(mob->race);
if(mob->prfs) deleteBitvector(mob->prfs);
if(mob->user_groups) deleteBitvector(mob->user_groups);
deleteAuxiliaryData(mob->auxiliary_data);
free(mob);
}
CHAR_DATA *charRead(STORAGE_SET *set) {
CHAR_DATA *mob = newMobile();
charSetClass(mob, read_string(set, "class"));
charSetPrototypes(mob, read_string(set, "prototypes"));
charSetName(mob, read_string(set, "name"));
charSetKeywords(mob, read_string(set, "keywords"));
charSetRdesc(mob, read_string(set, "rdesc"));
charSetDesc(mob, read_string(set, "desc"));
charSetMultiRdesc(mob, read_string(set, "multirdesc"));
charSetMultiName(mob, read_string(set, "multiname"));
charSetSex(mob, read_int (set, "sex"));
charSetRace(mob, read_string(set, "race"));
bitSet(mob->prfs, read_string(set, "prfs"));
bitSet(mob->user_groups, read_string(set, "user_groups"));
charSetLoadroom(mob, read_string(set, "loadroom"));
charSetPos(mob, read_int (set, "position"));
// make sure we always have the default group assigned
if(!*bitvectorGetBits(mob->user_groups))
bitSet(mob->user_groups, DFLT_USER_GROUP);
// read in PC data if it exists
if(storage_contains(set, "uid"))
charSetUID(mob, read_int (set, "uid"));
deleteAuxiliaryData(mob->auxiliary_data);
mob->auxiliary_data = auxiliaryDataRead(read_set(set, "auxiliary"),
AUXILIARY_TYPE_CHAR);
// make sure our race is OK
if(!isRace(charGetRace(mob)))
charSetRace(mob, raceDefault());
// reset our body to the default for our race
charResetBody(mob);
return mob;
}
STORAGE_SET *charStore(CHAR_DATA *mob) {
STORAGE_SET *set = new_storage_set();
store_string(set, "class", mob->class);
store_string(set, "prototypes", mob->prototypes);
store_string(set, "name", mob->name);
store_string(set, "keywords", mob->keywords);
store_string(set, "rdesc", mob->rdesc);
store_string(set, "desc", bufferString(mob->desc));
store_string(set, "multirdesc", mob->multi_rdesc);
store_string(set, "multiname", mob->multi_name);
store_int (set, "sex", mob->sex);
store_string(set, "race", mob->race);
store_string(set, "prfs", bitvectorGetBits(mob->prfs));
store_string(set, "user_groups",bitvectorGetBits(mob->user_groups));
// PC-only data
if(!charIsNPC(mob)) {
store_int (set, "position", mob->position);
store_int (set, "uid", mob->uid);
store_string(set, "loadroom", mob->loadroom);
}
store_set(set,"auxiliary", auxiliaryDataStore(mob->auxiliary_data));
return set;
}
void charCopyTo( CHAR_DATA *from, CHAR_DATA *to) {
charSetKeywords (to, charGetKeywords(from));
charSetClass (to, charGetClass(from));
charSetPrototypes (to, charGetPrototypes(from));
charSetName (to, charGetName(from));
charSetDesc (to, charGetDesc(from));
charSetRdesc (to, charGetRdesc(from));
charSetMultiRdesc (to, charGetMultiRdesc(from));
charSetMultiName (to, charGetMultiName(from));
charSetSex (to, charGetSex(from));
charSetPos (to, charGetPos(from));
charSetRace (to, charGetRace(from));
charSetBody (to, bodyCopy(charGetBody(from)));
bitvectorCopyTo (from->prfs, to->prfs);
bitvectorCopyTo (from->prfs, to->prfs);
auxiliaryDataCopyTo(from->auxiliary_data, to->auxiliary_data);
}
CHAR_DATA *charCopy( CHAR_DATA *mob) {
// use newMobile() ... we want to create a new UID
CHAR_DATA *newmob = newMobile();
charCopyTo(mob, newmob);
return newmob;
}
//*****************************************************************************
// mob set and get functions
//*****************************************************************************
void charSetKeywords(CHAR_DATA *ch, const char *keywords) {
if(ch->keywords) free(ch->keywords);
ch->keywords = strdupsafe(keywords);
}
const char *charGetKeywords ( CHAR_DATA *ch) {
return ch->keywords;
}