//*****************************************************************************
//
// body.c
//
// Different creatures are shaped in fundamentally different ways (e.g.
// bipedal humans and quadrapedal bears). Here's our attempt to create a
// structure that captures this idea.
//
//*****************************************************************************
#include "mud.h"
#include "utils.h"
#include "body.h"
struct bodypart_data {
char *name; // the name of the position
int type; // what kind of position type is this?
int size; // how big is it, relative to other positions?
OBJ_DATA *equipment; // what is being worn here?
};
typedef struct bodypart_data BODYPART;
struct body_data {
LIST *parts; // a list of all the parts on the body
int size; // how big is our body?
};
//*****************************************************************************
//
// Local functions. Mosty concerned with the handling of bodypart_datas, as
// opposed to their container, the body.
//
//*****************************************************************************
const char *bodypos_list[NUM_BODYPOS] = {
"floating about head",
"head",
"face",
"ear",
"neck",
"about body",
"torso",
"arm",
"wing",
"wrist",
"left hand",
"right hand",
"finger",
"waist",
"leg",
"left foot",
"right foot",
"hoof",
"claw",
"tail",
"held"
};
const char *bodysize_list[NUM_BODYSIZES] = {
"diminuitive",
"tiny",
"small",
"medium",
"large",
"huge",
"gargantuan",
"collosal"
};
/**
* Create a new bodypart_data
*/
BODYPART *newBodypart(const char *name, int type, int size) {
BODYPART *P = malloc(sizeof(BODYPART));
P->equipment = NULL;
P->type = type;
P->size = MAX(0, size); // parts of size 0 cannot be hit
P->name = strdup((name ? name : "nothing"));
return P;
};
/**
* Delete a bodypart_data, but do not delete the equipment on it
*/
void deleteBodypart(BODYPART *P) {
if(P->name) free(P->name);
free(P);
};
/**
* Copy a bodypart
*/
BODYPART *bodypartCopy(BODYPART *P) {
BODYPART *p_new = malloc(sizeof(BODYPART));
p_new->equipment = NULL;
p_new->type = P->type;
p_new->size = P->size;
p_new->name = strdup(P->name);
return p_new;
}
//*****************************************************************************
//
// Functions for body interface. Documentation contained in body.h
//
//*****************************************************************************
char *list_postypes(const BODY_DATA *B, const char *posnames) {
LIST *names = parse_keywords(posnames);
LIST_ITERATOR *name_i = newListIterator(names);
BUFFER *buf = newBuffer(MAX_BUFFER);
char *name = NULL;
char *retval = NULL;
int found = 0;
ITERATE_LIST(name, name_i) {
int part = bodyGetPart(B, name);
if(part != BODYPOS_NONE) {
found++;
if(found != 1)
bufferCat(buf, ", ");
bufferCat(buf, bodyposGetName(part));
}
} deleteListIterator(name_i);
deleteListWith(names, free);
retval = strdup(bufferString(buf));
deleteBuffer(buf);
return retval;
}
const char *bodysizeGetName(int size) {
return bodysize_list[size];
}
int bodysizeGetNum(const char *size) {
int i;
for(i = 0; i < NUM_BODYSIZES; i++)
if(!strcasecmp(size, bodysize_list[i]))
return i;
return BODYSIZE_NONE;
}
const char *bodyposGetName(int bodypos) {
return bodypos_list[bodypos];
}
int bodyposGetNum(const char *bodypos) {
int i;
for(i = 0; i < NUM_BODYPOS; i++)
if(!strcasecmp(bodypos, bodypos_list[i]))
return i;
return BODYPOS_NONE;
}
BODY_DATA *newBody() {
struct body_data*B = malloc(sizeof(BODY_DATA));
B->parts = newList();
return B;
}
void deleteBody(BODY_DATA *B) {
// delete all of the bodyparts
deleteListWith(B->parts, deleteBodypart);
// free us
free(B);
}
BODY_DATA *bodyCopy(const BODY_DATA *B) {
BODY_DATA *Bnew = newBody();
deleteListWith(Bnew->parts, deleteBodypart);
Bnew->parts = listCopyWith(B->parts, bodypartCopy);
Bnew->size = B->size;
return Bnew;
}
int bodyGetSize(const BODY_DATA *B) {
return B->size;
}
void bodySetSize(BODY_DATA *B, int size) {
B->size = size;
}
//
// Find a bodypart on the body with the given name
//
BODYPART *findBodypart(const BODY_DATA *B, const char *pos) {
LIST_ITERATOR *part_i = newListIterator(B->parts);
BODYPART *part = NULL;
ITERATE_LIST(part, part_i)
if(!strcasecmp(part->name, pos))
break;
deleteListIterator(part_i);
return part;
}
//
// Find a bodypart on the body with of the specified type that
// is not yet equipped with an item
//
BODYPART *findFreeBodypart(BODY_DATA *B, const char *type) {
int typenum = bodyposGetNum(type);
if(typenum == BODYPOS_NONE) return NULL;
LIST_ITERATOR *part_i = newListIterator(B->parts);
BODYPART *part = NULL;
ITERATE_LIST(part, part_i)
if(part->type == typenum && part->equipment == NULL)
break;
deleteListIterator(part_i);
return part;
}
void bodyAddPosition(BODY_DATA *B, const char *pos, int type, int size) {
BODYPART *part = findBodypart(B, pos);
// if we've already found the part, just modify it
if(part) {
part->type = type;
part->size = size;
}
// otherwise, add a new part
else
listPut(B->parts, newBodypart(pos, type, size));
}
bool bodyRemovePosition(BODY_DATA *B, const char *pos) {
BODYPART *part = findBodypart(B, pos);
if(!part)
return FALSE;
listRemove(B->parts, part);
deleteBodypart(part);
return TRUE;
}
int bodyGetPart(const BODY_DATA *B, const char *pos) {
BODYPART *part = findBodypart(B, pos);
if(part) return part->type;
else return BODYPOS_NONE;
}
double bodyPartRatio(const BODY_DATA *B, const char *pos) {
LIST_ITERATOR *part_i = newListIterator(B->parts);
BODYPART *part = NULL;
double part_size = 0.0;
double body_size = 0.0;
// add up all of the weights, and find the weight of our pos
ITERATE_LIST(part, part_i) {
body_size += part->size;
if(is_keyword(pos, part->name, FALSE))
part_size += part->size;
}
deleteListIterator(part_i);
// to prevent div0, albeit an unlikely event
return (body_size == 0.0 ? 0 : (part_size / body_size));
}
const char *bodyRandPart(const BODY_DATA *B, const char *pos) {
LIST_ITERATOR *part_i = newListIterator(B->parts);
BODYPART *part = NULL;
char *name = NULL;
int size_sum = 0;
int pos_roll = 0;
// add up all of the weights
ITERATE_LIST(part, part_i) {
// if we have a list of positions to draw from, only factor in those
if(pos && *pos && !is_keyword(pos, part->name, FALSE))
continue;
size_sum += part->size;
} deleteListIterator(part_i);
// nothing that can be hit was found
if(size_sum <= 1) {
deleteListIterator(part_i);
return NULL;
}
pos_roll = rand_number(1, size_sum);
// find the position the roll corresponds to
part_i = newListIterator(B->parts);
ITERATE_LIST(part, part_i) {
// if we have a list of positions to draw from, only factor in those
if(pos && *pos && !is_keyword(pos, part->name, FALSE))
continue;
pos_roll -= part->size;
if(pos_roll <= 0) {
name = part->name;
break;
}
} deleteListIterator(part_i);
return name;
}
const char **bodyGetParts(const BODY_DATA *B, bool sort, int *num_pos) {
*num_pos = listSize(B->parts);
const char **parts = malloc(sizeof(char *) * *num_pos);
LIST_ITERATOR *part_i = newListIterator(B->parts);
BODYPART *part = NULL;
int i = 0;
// if we don't need to sort, this should be fine ...
if(!sort) {
ITERATE_LIST(part, part_i)
parts[i] = part->name;
deleteListIterator(part_i);
}
// take some extra steps to make sure everything is sorted
else {
int pos[*num_pos];
i = 0;
ITERATE_LIST(part, part_i) {
parts[i] = part->name;
pos[i] = part->type;
i++;
} deleteListIterator(part_i);
// now sort everything in the array
for(i = 0; i < *num_pos; i++) {
// max_pos in this context is not the position in the array with the
// highest value. Rather, it is the position in the array whose value
// corresponds to the highest position on a body, which is negatively
// related to the value of the position.
int j, max_pos = i;
for(j = i+1; j < *num_pos; j++) {
if(pos[j] < pos[max_pos])
max_pos = j;
}
// do a shuffle
if(max_pos != i) {
const char *tmp_ptr = parts[i];
parts[i] = parts[max_pos];
parts[max_pos] = tmp_ptr;
int tmp_val = pos[i];
pos[i] = pos[max_pos];
pos[max_pos] = tmp_val;
}
}
}
return parts;
}
bool bodyEquipPostypes(BODY_DATA *B, OBJ_DATA *obj, const char *types) {
LIST *pos_list = parse_keywords(types);
LIST *parts = NULL;
BODYPART *part = NULL;
bool success = TRUE;
// make sure we have more than zero positions
if(listSize(pos_list) == 0) {
deleteList(pos_list);
return FALSE;
}
// create our list of parts
parts = newList();
// get a list of all open slots in the list provided ...
// equip them as we go along, incase we more than one of a piece.
// if we don't do it this way, findFreeBodypart might find the same
// piece multiple times (e.g. the same ear when it's looking for two ears)
LIST_ITERATOR *pos_i = newListIterator(pos_list);
char *pos = NULL;
ITERATE_LIST(pos, pos_i) {
part = findFreeBodypart(B, pos);
if(part && !part->equipment) {
part->equipment = obj;
listPut(parts, part);
}
} deleteListIterator(pos_i);
// make sure we supplied a valid number of empty positions
if(listSize(pos_list) != listSize(parts)) {
// remove equipment for every part we put it on
while((part = listPop(parts)) != NULL)
part->equipment = NULL;
success = FALSE;
}
// garbage collection
deleteListWith(pos_list, free);
deleteList(parts);
return success;
}
bool bodyEquipPosnames(BODY_DATA *B, OBJ_DATA *obj, const char *positions) {
LIST *pos_list = parse_keywords(positions);
LIST *parts = NULL;
BODYPART *part = NULL;
bool success = TRUE;
// make sure we have more than zero positions
if(listSize(pos_list) == 0) {
deleteList(pos_list);
return FALSE;
}
// create our list of parts
parts = newList();
// get a list of all open slots in the list provided
LIST_ITERATOR *pos_i = newListIterator(pos_list);
char *pos = NULL;
ITERATE_LIST(pos, pos_i) {
part = findBodypart(B, pos);
if(part && !part->equipment && !listIn(parts, part))
listPut(parts, part);
} deleteListIterator(pos_i);
// make sure we found the right amount of parts
if(listSize(parts) != listSize(pos_list) || listSize(parts) == 0)
success = FALSE;
// fill in all of the parts that need to be filled
while( (part = listPop(parts)) != NULL)
part->equipment = obj;
// clean up our garbage
deleteListWith(pos_list, free);
deleteList(parts);
return success;
}
const char *bodyEquippedWhere(BODY_DATA *B, OBJ_DATA *obj) {
static char buf[SMALL_BUFFER];
*buf = '\0';
LIST_ITERATOR *part_i = newListIterator(B->parts);
BODYPART *part = NULL;
// go through the list of all parts, and print the name of any one
// with the piece of equipment on it, onto the buf
ITERATE_LIST(part, part_i) {
if(part->equipment == obj) {
// if we've already printed something, add a comma
if(*buf)
strcat(buf, ", ");
strcat(buf, part->name);
}
}
deleteListIterator(part_i);
return buf;
}
OBJ_DATA *bodyGetEquipment(BODY_DATA *B, const char *pos) {
BODYPART *part = findBodypart(B, pos);
return (part ? part->equipment : NULL);
}
bool bodyUnequip(BODY_DATA *B, const OBJ_DATA *obj) {
LIST_ITERATOR *part_i = newListIterator(B->parts);
BODYPART *part = NULL;
bool found = FALSE;
ITERATE_LIST(part, part_i) {
if(part->equipment == obj) {
part->equipment = NULL;
found = TRUE;
}
}
deleteListIterator(part_i);
return found;
}
LIST *bodyGetAllEq(BODY_DATA *B) {
LIST *equipment = newList();
LIST_ITERATOR *part_i = newListIterator(B->parts);
BODYPART *part = NULL;
ITERATE_LIST(part, part_i)
if(part->equipment && !listIn(equipment, part->equipment))
listPut(equipment, part->equipment);
deleteListIterator(part_i);
return equipment;
}
LIST *bodyUnequipAll(BODY_DATA *B) {
LIST *equipment = newList();
LIST_ITERATOR *part_i = newListIterator(B->parts);
BODYPART *part = NULL;
ITERATE_LIST(part, part_i) {
if(part->equipment && !listIn(equipment, part->equipment)) {
listPut(equipment, part->equipment);
part->equipment = NULL;
}
}
deleteListIterator(part_i);
return equipment;
}
int numBodyparts(const BODY_DATA *B) {
return listSize(B->parts);
}