//*****************************************************************************
//
// utils.c
//
// This file contains all sorts of utility functions used
// all sorts of places in the code.
//
//*****************************************************************************
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <stdio.h>
#include <ctype.h>
#include <unistd.h>
#include <dirent.h>
#include <time.h>
// include main header file
#include "mud.h"
#include "prototype.h"
#include "character.h"
#include "object.h"
#include "world.h"
#include "zone.h"
#include "room.h"
#include "room_reset.h"
#include "exit.h"
#include "socket.h"
#include "utils.h"
#include "save.h"
#include "handler.h"
#include "inform.h"
#include "event.h"
#include "action.h"
#include "hooks.h"
void extract_obj_final(OBJ_DATA *obj) {
obj_from_game(obj);
deleteObj(obj);
}
void extract_obj(OBJ_DATA *obj) {
// make sure we're not attached to anything
CHAR_DATA *sitter = NULL;
while( (sitter = listGet(objGetUsers(obj), 0)) != NULL)
char_from_furniture(sitter);
OBJ_DATA *content = NULL;
while( (content = listGet(objGetContents(obj), 0)) != NULL)
extract_obj(content);
if(objGetRoom(obj))
obj_from_room(obj);
if(objGetWearer(obj))
try_unequip(objGetWearer(obj), obj);
if(objGetCarrier(obj))
obj_from_char(obj);
if(objGetContainer(obj))
obj_from_obj(obj);
if(!listIn(objs_to_delete, obj))
listPut(objs_to_delete, obj);
}
void extract_mobile_final(CHAR_DATA *ch) {
char_from_game(ch);
if(charIsNPC(ch))
deleteChar(ch);
else
unreference_player(ch);
}
void extract_mobile(CHAR_DATA *ch) {
// unequip everything the character is wearing
// and send it to inventory
unequip_all(ch);
// extract everything in the character's inventory
LIST_ITERATOR *obj_i = newListIterator(charGetInventory(ch));
OBJ_DATA *obj = NULL;
ITERATE_LIST(obj, obj_i) {
extract_obj(obj);
} deleteListIterator(obj_i);
// make sure we're not attached to anything
if(charGetFurniture(ch))
char_from_furniture(ch);
if(charGetRoom(ch))
char_from_room(ch);
// make sure we close down the socket
if(charGetSocket(ch) != NULL) {
close_socket(charGetSocket(ch), FALSE);
charSetSocket(ch, NULL);
}
if(!listIn(mobs_to_delete, ch))
listPut(mobs_to_delete, ch);
}
void extract_room_final(ROOM_DATA *room) {
room_from_game(room);
deleteRoom(room);
}
void extract_room(ROOM_DATA *room) {
// extract all of the objects characters in us
CHAR_DATA *ch = NULL;
while( (ch = listGet(roomGetCharacters(room), 0)) != NULL)
extract_mobile(ch);
// and the objects
OBJ_DATA *obj = NULL;
while( (obj = listGet(roomGetContents(room), 0)) != NULL)
extract_obj(obj);
// remove us from the world we're in ... the if should always be true if
// we are in fact part of the world.
if(worldRoomLoaded(gameworld, roomGetClass(room)) &&
worldGetRoom(gameworld, roomGetClass(room)) == room)
worldRemoveRoom(gameworld, roomGetClass(room));
// If anyone's last room was us, make sure we set the last room to NULL
LIST_ITERATOR *ch_i = newListIterator(mobile_list);
ITERATE_LIST(ch, ch_i) {
if(charGetLastRoom(ch) == room)
charSetLastRoom(ch, NULL);
} deleteListIterator(ch_i);
if(!listIn(rooms_to_delete, room))
listPut(rooms_to_delete, room);
}
void communicate(CHAR_DATA *dMob, char *txt, int range)
{
switch(range) {
default:
bug("Communicate: Bad Range %d.", range);
return;
// to everyone in the same room
case COMM_LOCAL:
mssgprintf(dMob, NULL, NULL, NULL, FALSE, TO_CHAR,
"{yYou say, '%s'{n", txt);
mssgprintf(dMob, NULL, NULL, NULL, FALSE, TO_ROOM,
"{y$n says, '%s'{n", txt);
break;
// to everyone in the world
case COMM_GLOBAL:
mssgprintf(dMob, NULL, NULL, NULL, FALSE, TO_CHAR,
"{cYou chat, '%s'{n", txt);
mssgprintf(dMob, NULL, NULL, NULL, FALSE, TO_WORLD,
"{c$n chats, '%s'{n", txt);
break;
case COMM_LOG:
send_to_groups("admin", "[LOG: %s]\r\n", txt);
break;
}
}
/*
* load the world and its inhabitants, as well as other misc game data
*/
void load_muddata() {
if(gameworld == NULL) {
log_string("ERROR: Could not boot game world.");
abort();
}
worldAddType(gameworld, "mproto", protoRead, protoStore, deleteProto,
protoSetKey);
worldAddType(gameworld, "oproto", protoRead, protoStore, deleteProto,
protoSetKey);
worldAddType(gameworld, "rproto", protoRead, protoStore, deleteProto,
protoSetKey);
worldAddType(gameworld, "reset", resetListRead, resetListStore,
deleteResetList, resetListSetKey);
worldSetPath(gameworld, WORLD_PATH);
worldInit(gameworld);
greeting = read_file("../lib/txt/greeting");
motd = read_file("../lib/txt/motd");
}
char *get_time()
{
static char buf[16];
char *strtime;
int i;
strtime = ctime(¤t_time);
for (i = 0; i < 15; i++)
buf[i] = strtime[i + 4];
buf[15] = '\0';
return buf;
}
// try entering into the game
// disconnect the socket if we fail.
bool try_enter_game(CHAR_DATA *ch) {
// find the loadroom
ROOM_DATA *loadroom = worldGetRoom(gameworld, charGetLoadroom(ch));
// if the loadroom doesn't exist,
// try loading the character to the start room
if(loadroom == NULL) {
send_to_char(ch,
"Your loadroom seems to be missing. Attempting to "
"load you to the start room.\r\n");
log_string("ERROR: %s had invalid loadroom, %s",
charGetName(ch), charGetLoadroom(ch));
loadroom = worldGetRoom(gameworld, START_ROOM);
}
// we have a loadroom... all is good
if(loadroom != NULL) {
// add him to the game
char_to_game(ch);
char_to_room(ch, loadroom);
return TRUE;
}
// oops! No rooms seem to exist
else {
send_to_char(ch,
"The gameworld was not properly loaded. The MUD may "
"be going through maintenance. Try again later.\r\n");
log_string("ERROR: When loading %s, start room did not exist!",
charGetName(ch));
return FALSE;
}
}
CHAR_DATA *check_reconnect(const char *player) {
CHAR_DATA *dMob;
LIST_ITERATOR *mob_i = newListIterator(mobile_list);
ITERATE_LIST(dMob, mob_i) {
if (!charIsNPC(dMob) && charIsName(dMob, player)) {
if (charGetSocket(dMob)) {
close_socket(charGetSocket(dMob), TRUE);
charSetSocket(dMob, NULL);
}
break;
}
} deleteListIterator(mob_i);
return dMob;
}
void do_mass_transfer(ROOM_DATA *from, ROOM_DATA *to, bool chars, bool mobs,
bool objs) {
if(chars || mobs) {
LIST_ITERATOR *ch_i = newListIterator(roomGetCharacters(from));
CHAR_DATA *ch = NULL;
ITERATE_LIST(ch, ch_i) {
if(mobs || (chars && !charIsNPC(ch))) {
char_from_room(ch);
char_to_room(ch, to);
}
} deleteListIterator(ch_i);
}
if(objs) {
LIST_ITERATOR *obj_i = newListIterator(roomGetContents(from));
OBJ_DATA *obj = NULL;
ITERATE_LIST(obj, obj_i) {
obj_from_room(obj);
obj_to_room(obj, to);
} deleteListIterator(obj_i);
}
}
int can_see_hidden ( CHAR_DATA *ch) {
return 0;
}
int can_see_invis ( CHAR_DATA *ch) {
return 0;
}
bool can_see_char ( CHAR_DATA *ch, CHAR_DATA *target) {
if(ch == target)
return TRUE;
if(poscmp(charGetPos(ch), POS_SLEEPING) <= 0)
return FALSE;
return TRUE;
}
bool can_see_obj ( CHAR_DATA *ch, OBJ_DATA *target) {
if(poscmp(charGetPos(ch), POS_SLEEPING) <= 0)
return FALSE;
return TRUE;
}
bool can_see_exit ( CHAR_DATA *ch, EXIT_DATA *exit) {
if(poscmp(charGetPos(ch), POS_SLEEPING) <= 0)
return FALSE;
if(exitGetHidden(exit) > can_see_hidden(ch))
return FALSE;
return TRUE;
}
const char *see_char_as (CHAR_DATA *ch, CHAR_DATA *target) {
if(can_see_char(ch, target))
return charGetName(target);
return SOMEONE;
}
const char *see_obj_as (CHAR_DATA *ch, OBJ_DATA *target) {
if(can_see_obj(ch, target))
return objGetName(target);
return SOMETHING;
}
int count_objs(CHAR_DATA *looker, LIST *list, const char *name,
const char *prototype, bool must_see) {
LIST_ITERATOR *obj_i = newListIterator(list);
OBJ_DATA *obj;
int count = 0;
ITERATE_LIST(obj, obj_i) {
if(must_see && !can_see_obj(looker, obj))
continue;
// if we have a name, search by it
if(name && *name && objIsName(obj, name))
count++;
// otherwise search by prototype
else if(prototype && *prototype && objIsInstance(obj, prototype))
count++;
}
deleteListIterator(obj_i);
return count;
}
int count_chars(CHAR_DATA *looker, LIST *list, const char *name,
const char *prototype, bool must_see) {
LIST_ITERATOR *char_i = newListIterator(list);
CHAR_DATA *ch;
int count = 0;
ITERATE_LIST(ch, char_i) {
if(must_see && !can_see_char(looker, ch))
continue;
// if we have a name, search by it
if(name && *name && charIsName(ch, name))
count++;
// otherwise, search by prototype
else if(prototype && *prototype && charIsInstance(ch, prototype))
count++;
}
deleteListIterator(char_i);
return count;
}
//
// find the numth occurance of the character with name, "name"
// if name is not supplied, search by prototype
//
CHAR_DATA *find_char(CHAR_DATA *looker, LIST *list, int num, const char *name,
const char *prototype, bool must_see) {
if(num <= 0)
return NULL;
LIST_ITERATOR *char_i = newListIterator(list);
CHAR_DATA *ch;
ITERATE_LIST(ch, char_i) {
if(must_see && !can_see_char(looker, ch))
continue;
// if we have a name, search by name
if(name && *name && charIsName(ch, name))
num--;
// otherwise search by prototype
else if(prototype && *prototype && charIsInstance(ch, prototype))
num--;
if(num == 0)
break;
}
deleteListIterator(char_i);
return ch;
}
//
// find the numth occurance of the object with name, "name"
// if name is not supplied, search by prototype
//
OBJ_DATA *find_obj(CHAR_DATA *looker, LIST *list, int num,
const char *name, const char *prototype, bool must_see) {
if(num == 0)
return NULL;
LIST_ITERATOR *obj_i = newListIterator(list);
OBJ_DATA *obj;
ITERATE_LIST(obj, obj_i) {
if(must_see && !can_see_obj(looker, obj))
continue;
// if a name is supplied, search by name
if(name && *name && objIsName(obj, name))
num--;
// otherwise, search by prototype
else if(prototype && *prototype && objIsInstance(obj, prototype))
num--;
if(num == 0)
break;
}
deleteListIterator(obj_i);
return obj;
}
//
// Find all characters in the list with the given name. Or all characters
// if name is NULL
//
LIST *find_all_chars(CHAR_DATA *looker, LIST *list, const char *name,
const char *prototype, bool must_see) {
LIST_ITERATOR *char_i = newListIterator(list);
LIST *char_list = newList();
CHAR_DATA *ch;
ITERATE_LIST(ch, char_i) {
if(must_see && !can_see_char(looker, ch))
continue;
if(name && (!*name || charIsName(ch, name)))
listPut(char_list, ch);
else if(prototype && *prototype && charIsInstance(ch, prototype))
listPut(char_list, ch);
}
deleteListIterator(char_i);
return char_list;
}
//
// Returns a llist of all the chars found with a specific name in the list.
// the list must be deleted after use.
//
LIST *find_all_objs(CHAR_DATA *looker, LIST *list, const char *name,
const char *prototype, bool must_see) {
LIST_ITERATOR *obj_i = newListIterator(list);
LIST *obj_list = newList();
OBJ_DATA *obj;
ITERATE_LIST(obj, obj_i) {
if(must_see && !can_see_obj(looker, obj))
continue;
if(name && (!*name || objIsName(obj, name)))
listPut(obj_list, obj);
else if(prototype && *prototype && objIsInstance(obj, prototype))
listPut(obj_list, obj);
}
deleteListIterator(obj_i);
return obj_list;
}
//
// separates a joint item-count (e.g. 2.sword, all.woman) into its
// count and its word
//
void get_count(const char *buf, char *target, int *count) {
// hmmm... we can probably condense these two checks into one, non?
if(!strncasecmp(buf, "all.", 4)) {
sscanf(buf, "all.%s", target);
*count = COUNT_ALL;
}
else if(!strcasecmp(buf, "all")) {
*target = '\0';
*count = COUNT_ALL;
}
else if(!strstr(buf, ".")) {
sscanf(buf, "%s", target);
*count = 1;
}
else
sscanf(buf, "%d.%s", count, target);
}
//
// concats an item count and a target into a single term
//
void print_count(char *buf, const char *target, int count) {
if(count > 1)
sprintf(buf, "%d.%s", count, target);
else if(count == COUNT_ALL && *target)
sprintf(buf, "all.%s", target);
else if(count == COUNT_ALL)
sprintf(buf, "all");
else
sprintf(buf, "%s", target);
}
//
// Calculates how many letters until we hit the next one
// of a specified type
//
int next_letter_in(const char *string, char marker) {
int i = 0;
for(i = 0; string[i] != '\0'; i++)
if(string[i] == marker)
return i;
return -1; // none found
}
//
// Calculates how many characters until we hit the next whitespace. Newlines,
// tabs, and spaces are treated as whitespace.
int next_space_in(const char *string) {
int i = 0;
for(i = 0; string[i] != '\0'; i++)
if(isspace(string[i]))
return i;
return -1; // none found
}
//
// If we're at the beginning of a new paragraph, return where the new paragraph
// starts. Otherwise, return our current position (index)
int is_paragraph_marker(const char *string, int index) {
int nl_count = 0;
int i = 0;
for(i = index; isspace(string[i]); i++) {
if(string[i] == '\n')
nl_count++;
}
if(nl_count > 1)
return i;
else
return index;
}
//
// just a generic function for hashing a string. This could be
// sped up tremendously if it's performance becoming a problem.
int string_hash(const char *key) {
const int BASE = 2;
int base = 1;
int hvalue = 0;
for (; *key; key++) {
base *= BASE;
hvalue += tolower(*key) * base;
}
return (hvalue < 0 ? hvalue * -1 : hvalue);
}
bool endswith(const char *string, const char *end) {
int slen = strlen(string);
int elen = strlen(end);
return (slen >= elen && !strcasecmp(string + slen - elen, end));
}
bool startswith(const char *string, const char *start) {
return !strncasecmp(string, start, strlen(start));
}
const char *strcpyto(char *to, const char *from, char end) {
// copy everything up to end, and then delimit our destination buffer
for(; *from != '\0' && *from != end; to++, from++)
*to = *from;
*to = '\0';
// skip our end character and return whatever's left
if(*from != '\0')
from++;
return from;
}
//
// counts how many times ch occurs in string
//
int count_letters(const char *string, const char ch, const int strlen) {
int i, n;
for(i = n = 0; i < strlen; i++)
if(string[i] == ch)
n++;
return n;
}
//
// counts how many times word occurs in string. Assumes strlen(word) >= 1
//
int count_occurences(const char *string, const char *word) {
int count = 0, i = 0, word_len = strlen(word);
for(; string[i] != '\0'; i++) {
if(!strncmp(string+i, word, word_len)) {
count++;
i += word_len;
}
}
return count;
}
//
// return a pointer to the start of the line num (lines are ended with \n's).
// return NULL if the line does not exist
//
char *line_start(char *string, int line) {
// skip forward to the appropriate line
int i, count = 1;
// are we looking for the start?
if(line == 1) return string;
for(i = 0; string[i] != '\0'; i++) {
if(string[i] == '\n')
count++;
if(count == line)
return string+i+1;
}
return NULL;
}
//
// trim trailing and leading whitespace
//
void trim(char *string) {
int len = strlen(string);
int max = len-1;
int min = 0;
int i;
// kill all whitespace to the right
for(max = len - 1; max >= 0; max--) {
if(isspace(string[max]))
string[max] = '\0';
else
break;
}
// find our first non-whitespace
while(isspace(string[min]))
min++;
// shift everything to the left
for(i = 0; i <= max-min; i++)
string[i] = string[i+min];
string[i] = '\0';
}
//
// Returns true if "word" is found in the comma-separated list of
// keywords
//
bool is_keyword(const char *keywords, const char *word, bool abbrev_ok) {
int word_len = strlen(word);
if(word_len < 1)
return FALSE;
// while we haven't reached the end of the string
while(*keywords != '\0') {
// skip all spaces and commas
while(isspace(*keywords) || *keywords == ',')
keywords = keywords+1;
// figure out the length of the current keyword
int keyword_len = next_letter_in(keywords, ',');
if(keyword_len == -1)
keyword_len = strlen(keywords);
// see if we compare to the current keyword
if(!abbrev_ok && !strncasecmp(keywords, word, keyword_len) &&
keyword_len == word_len)
return TRUE;
if(abbrev_ok && !strncasecmp(keywords, word, word_len))
return TRUE;
// we didn't. skip this current keyword, and find
// the start of the next keyword or the end of the string
else while(!(*keywords == '\0' || *keywords == ','))
keywords = keywords+1;
}
return FALSE;
}
//
// Return 0 if the head of the string does not match any of our
// keywords. Returns the length of the keyword if it does match
int find_keyword(const char *keywords, const char *string) {
LIST *words = parse_keywords(keywords);
LIST_ITERATOR *word_i = newListIterator(words);
char *word = NULL;
int len = 0;
// try to find the longest keyword
ITERATE_LIST(word, word_i) {
int word_len = strlen(word);
if(!strncasecmp(word, string, word_len) && word_len > len)
len = word_len;
} deleteListIterator(word_i);
deleteListWith(words, free);
return len;
}
//
// returns whether or not a keyword exists twice in the list
bool dup_keywords_exist(const char *keywords) {
LIST *keys = parse_keywords(keywords);
char *key = NULL;
bool dup_found = FALSE;
while( !dup_found && (key = listPop(keys)) != NULL) {
if(listGetWith(keys, key, strcasecmp) != NULL)
dup_found = TRUE;
free(key);
} deleteListWith(keys, free);
return dup_found;
}
//
// Return a list of all the strings in this list. String are separated by the
// delimeter. The list and contents must be deleted after use.
LIST *parse_strings(const char *string, char delimeter) {
// make our list that we will be returning
LIST *list = newList();
// first, we check if the string have any non-spaces
int i;
bool nonspace_found = FALSE;
for(i = 0; string[i] != '\0'; i++) {
if(!isspace(string[i])) {
nonspace_found = TRUE;
break;
}
}
// we didn't find any non-spaces. Return NULL
if(!nonspace_found)
return list;
// find all of our keywords
while(*string != '\0') {
i = 0;
// find the endpoint
while(string[i] != delimeter && string[i] != '\0')
i++;
char buf[i+1];
strncpy(buf, string, i); buf[i] = '\0';
trim(buf); // skip all whitespaces
// make sure something still exists. If it does, queue it
if(*buf != '\0')
listQueue(list, strdup(buf));
// skip everything we just copied, plus our delimeter
string = &string[i+(string[i] != '\0' ? 1 : 0)];
}
// return whatever we found
return list;
}
//
// return a list of all the unique keywords found in the keywords string
// keywords can be more than one word long (e.g. blue flame) and each
// keyword must be separated by a comma
LIST *parse_keywords(const char *keywords) {
return parse_strings(keywords, ',');
}
//
// If the keyword does not already exist, add it to the keyword list
// keywords may be freed and re-built in the process, to make room
void add_keyword(char **keywords_ptr, const char *word) {
// if it's already a keyword, do nothing
if(!is_keyword(*keywords_ptr, word, FALSE)) {
BUFFER *buf = newBuffer(MAX_BUFFER);
// make our new keyword list
bprintf(buf, "%s%s%s", *keywords_ptr, (**keywords_ptr ? ", " : ""), word);
// free the old string, copy the new one
free(*keywords_ptr);
*keywords_ptr = strdup(bufferString(buf));
// clean up our garbage
deleteBuffer(buf);
}
}
//
// go through the keywords and if the word is found, remove it
void remove_keyword(char *keywords, const char *word) {
LIST *words = parse_keywords(keywords);
char *copy = NULL;
int count = 0;
// remove all copies of it
while( (copy = listRemoveWith(words, word, strcasecmp)) != NULL) {
free(copy);
count++;
}
// did we find a copy of the bad keyword?
if(count > 0) {
LIST_ITERATOR *word_i = newListIterator(words);
int key_i = 0;
count = 0;
// clear the current keywords... we'll rebuild them
*keywords = '\0';
ITERATE_LIST(copy, word_i) {
count++;
key_i += sprintf(keywords+key_i, "%s", copy);
if(count < listSize(words) - 1)
key_i += sprintf(keywords+key_i, ", ");
} deleteListIterator(word_i);
}
// clean up our garbage
deleteListWith(words, free);
}
//
// print the string on the buffer, centered for a line of length linelen.
// if border is TRUE, put a border to either side of the string.
//
void center_string(char *buf, const char *string, int linelen, int buflen,
bool border) {
char fmt[32];
int str_len = strlen(string);
int spaces = (linelen - str_len)/2;
if(border) {
int i, buf_i = 0;
sprintf(fmt, "{g%%-%ds[{c", spaces-2);
buf_i = snprintf(buf, buflen, fmt, " ");
// replace all of the spaces with -
for(i = 0; buf[i] != '\0'; i++) if(buf[i] == ' ') buf[i] = '-';
buf_i += snprintf(buf+buf_i, buflen-buf_i, " %s ", string);
sprintf(fmt, "{g]%%-%ds\r\n",
spaces-2 + (((linelen-str_len) % 2) == 1 ? 1 : 0));
i = buf_i;
buf_i += snprintf(buf+buf_i, buflen-buf_i, fmt, " ");
// replace all of the spaces with -
for(; buf[i] != '\0'; i++) if(buf[i] == ' ') buf[i] = '-';
}
else {
int buf_i = 0;
sprintf(fmt, "%%-%ds", spaces);
buf_i = snprintf(buf, buflen, fmt, " ");
buf_i += snprintf(buf+buf_i, buflen-buf_i, "%s\r\n", string);
}
}
//
// fill up the buffer with characters until
// a newline is reached, or we hit our critical
// length. Return how many characters were read
//
int fgetline(FILE *file, char *p, int maxlen)
{
int count = 0;
while(!feof(file) && count < maxlen - 1) {
p[count] = fgetc(file);
if(p[count] == '\n')
break;
count++;
}
p[count] = '\0';
return count;
}
int rand_number(int min, int max) {
if(min > max) {
log_string("ERROR: rand_number passed a min (%d) higher than its max (%d).",
min, max);
return 0;
}
return min + rand() % (max-min + 1);
}
double rand_percent(void) {
double rnd = rand_number(0, RAND_MAX);
return rnd / (double)RAND_MAX;
}
double rand_gaussian(void) {
return sqrt(-2.0 * log(rand_percent())) * cos(2.0 * PI * rand_percent());
}
double sigmoid(double val) {
return 1.0 / (1.0 + pow(MATH_E, -val));
}
//
// returns "st", "nd", "rd", or "th", based on the number passed in
//
const char *numth(int num) {
if(num != 11 && num % 10 == 1)
return "st";
else if(num != 12 && num % 10 == 2)
return "nd";
else if(num != 13 && num % 10 == 3)
return "rd";
else
return "th";
}
//
// Strips all occurances of a word from the string
//
// used to just replace occurances of 'word' with nothing,
// but this doesn't work so well since replace_string will
// replace it, even if the 'word' is embedded inside of another
// word. This is undesirable.
//
// replace_string(&string, word, "", TRUE);
//
void strip_word(char *string, const char *word) {
int word_len = strlen(word);
int i = 0;
for(i = 0; string[i] != '\0'; i++) {
// skip all spaces and newline stuff
while(isspace(string[i]) || string[i] == '\n' || string[i] == '\r')
i++;
// we've found a match
if(!strncmp(string+i, word, word_len) &&
(string[i+word_len] == '\0' || isspace(string[i+word_len]) ||
string[i+word_len] == '\n' || string[i+word_len] == '\r')) {
// count all of the spaces following us, so we can remove them too
int space_count = 0;
while(isspace(string[i+word_len+space_count]))
space_count++;
// shuffle everything down
int j = i-1;
do {
j++;
string[j] = string[j+word_len+space_count];
} while(string[j+word_len+space_count] != '\0');
}
}
}
char *tag_keywords(const char *string, const char *keywords,
const char *start_tag, const char *end_tag) {
char buf[strlen(string) * 2];
int i = 0, j = 0;
int start_len = strlen(start_tag);
int end_len = strlen(end_tag);
while(*string) {
int keyword_len;
// check for a match
if( (keyword_len = find_keyword(keywords, string)) != 0) {
for(j = 0; j < start_len; j++)
buf[i++] = start_tag[j];
while(keyword_len > 0) {
buf[i++] = *string;
string++;
keyword_len--;
}
for(j = 0; j < end_len; j++)
buf[i++] = end_tag[j];
}
// skip ahead one word
else {
while(!isspace(*string) && *string != '\0') {
buf[i++] = *string;
string++;
}
// put in our space
if(*string != '\0') {
buf[i++] = *string;
string++;
}
}
}
buf[i++] = '\0';//dialogue++;
return strdup(buf);
}
void buf_tag_keywords(BUFFER *buf, const char *keywords, const char *start_tag,
const char *end_tag) {
char *new_str = tag_keywords(bufferString(buf), keywords, start_tag, end_tag);
bufferClear(buf);
bufferCat(buf, new_str);
free(new_str);
}
bitvector_t parse_bits(const char *string) {
bitvector_t bits = 0;
while(*string) {
SET_BIT(bits, (1 << (*string-'a')));
string = string+1;
}
return bits;
}
const char *write_bits(bitvector_t bits) {
static char buf[SMALL_BUFFER]; // really, we only need about 64...
int buf_i = 0, i = 0;
for(i = 0; i < BITS_PER_BITVECTOR; i++)
if(IS_SET(bits, (1 << i)))
buf[buf_i++] = 'a'+i;
buf[buf_i] = '\0';
return buf;
}
void print_bits(bitvector_t bits, const char **names, char *buf) {
int i, count = 0;
for(i = 0; i < BITS_PER_BITVECTOR; i++) {
if(IS_SET(bits, (1 << i))) {
count++;
sprintf(buf, "%s%s%s",
(count == 1 ? "" : buf ),
(count == 1 ? "" : ", "),
names[i]);
}
}
if(count == 0)
sprintf(buf, "NOBITS");
}
char *print_list(LIST *list, void *descriptor, void *multi_descriptor) {
const char *(* desc_func)(void *) = descriptor;
const char *(* multi_desc)(void *) = multi_descriptor;
if(listSize(list) == 0)
return strdup("");
char buf[MAX_BUFFER] = "";
char one_thing[SMALL_BUFFER];
int i;
int tot_things = 0;
int size = listSize(list);
int counts[size];
void *things[size];
void *thing;
LIST_ITERATOR *thing_i = newListIterator(list);
for(i = 0; i < size; i++) {
counts[i] = 0;
things[i] = NULL;
}
// first, collect groups of all the things in the list
ITERATE_LIST(thing, thing_i) {
// see if we have one with the current name already
for(i = 0; i < size; i++) {
// it's a thing with a new name
if(things[i] == NULL) {
things[i] = thing;
counts[i]++;
tot_things++;
break;
}
// it has the same name as something else
else if(!strcmp(desc_func(thing), desc_func(things[i]))) {
counts[i]++;
break;
}
}
}
deleteListIterator(thing_i);
// now, print everything to the buffer
for(i = 0; i < tot_things; i++) {
if(counts[i] == 1)
sprintf(one_thing, "%s", desc_func(things[i]));
else if(multi_desc == NULL || !*multi_desc(things[i]))
sprintf(one_thing, "(%d) %s", counts[i], desc_func(things[i]));
else
sprintf(one_thing, multi_desc(things[i]), counts[i]);
// we're at the last one... we have to print an and
if(i == tot_things - 1)
sprintf(buf, "%s%s%s", buf,
(i < 1 ? "" : (i == 1 ? " and " : ", and ")),
one_thing);
// regular comma-separated dealy
else
sprintf(buf, "%s%s%s",
(i == 0 ? "" : buf), (i == 0 ? "" : ", "), one_thing);
}
return strdup(buf);
}
void show_list(CHAR_DATA *ch, LIST *list, void *descriptor,
void *multi_descriptor) {
const char *(* desc_func)(void *) = descriptor;
const char *(* multi_desc)(void *) = multi_descriptor;
int i;
int size = listSize(list);
int counts[size];
void *things[size];
void *thing;
LIST_ITERATOR *thing_i = newListIterator(list);
for(i = 0; i < size; i++) {
counts[i] = 0;
things[i] = NULL;
}
// find a list of all things with unique names
ITERATE_LIST(thing, thing_i) {
// see if we have one with the current name already
for(i = 0; i < size; i++) {
// it's a thing with a new name
if(things[i] == NULL) {
things[i] = thing;
counts[i]++;
break;
}
// it has the same name as something else
else if(!strcmp(desc_func(thing), desc_func(things[i]))) {
counts[i]++;
break;
}
}
}
deleteListIterator(thing_i);
// print out all of the things
for(i = 0; i < size; i++) {
// we've run into the end of the list
if(things[i] == NULL)
break;
else {
if(counts[i] == 1)
send_to_char(ch, "{g%s\r\n", desc_func(things[i]));
else if(multi_desc == NULL || !*multi_desc(things[i]))
send_to_char(ch, "{g(%d) %s\r\n", counts[i], desc_func(things[i]));
else {
char fmt[SMALL_BUFFER];
sprintf(fmt, "{g%s\r\n", multi_desc(things[i]));
send_to_char(ch, fmt, counts[i]);
}
}
}
}
LIST *get_unused_items(CHAR_DATA *ch, LIST *list, bool invis_ok) {
OBJ_DATA *obj;
LIST *newlist = newList();
LIST_ITERATOR *obj_i = newListIterator(list);
ITERATE_LIST(obj, obj_i) {
if(listSize(objGetUsers(obj)) != 0)
continue;
if(!(invis_ok || can_see_obj(ch, obj)))
continue;
listPut(newlist, obj);
}
deleteListIterator(obj_i);
return newlist;
}
LIST *get_used_items(CHAR_DATA *ch, LIST *list, bool invis_ok) {
OBJ_DATA *obj;
LIST *newlist = newList();
LIST_ITERATOR *obj_i = newListIterator(list);
ITERATE_LIST(obj, obj_i) {
if(listSize(objGetUsers(obj)) < 1)
continue;
if(!(invis_ok || can_see_obj(ch, obj)))
continue;
listPut(newlist, obj);
}
deleteListIterator(obj_i);
return newlist;
}
bool has_obj(CHAR_DATA *ch, const char *prototype) {
LIST *vis_inv = find_all_objs(ch, charGetInventory(ch), NULL, prototype,TRUE);
int ret_val = FALSE;
if(listSize(vis_inv) > 0)
ret_val = TRUE;
deleteList(vis_inv);
return ret_val;
}
void *identity_func(void *data) {
return data;
}
LIST *reverse_list(LIST *list) {
LIST *newlist = newList();
LIST_ITERATOR *list_i = newListIterator(list);
void *elem = NULL;
ITERATE_LIST(elem, list_i) {
listPut(newlist, elem);
} deleteListIterator(list_i);
return newlist;
}
bool parse_worldkey(const char *key, char *name, char *locale) {
*name = *locale = '\0';
int pos = next_letter_in(key, '@');
if(pos == -1)
return FALSE;
else {
strncpy(name, key, pos);
*(name+pos) = '\0';
strcpy(locale, key+pos+1);
if(*name == '\0' || *locale == '\0')
return FALSE;
return TRUE;
}
}
bool parse_worldkey_relative(CHAR_DATA *ch, const char *key, char *name,
char *locale) {
int pos = next_letter_in(key, '@');
if(pos != -1)
return parse_worldkey(key, name, locale);
else {
strcpy(name, key);
strcpy(locale, get_key_locale(roomGetClass(charGetRoom(ch))));
return TRUE;
}
}
const char *get_key_locale(const char *key) {
int pos = next_letter_in(key, '@');
if(pos == -1)
return "";
else
return key+pos+1;
}
const char *get_key_name(const char *key) {
int pos = next_letter_in(key, '@');
if(pos == -1)
return key;
else {
static char name[SMALL_BUFFER];
strcpy(name, key);
name[pos] = '\0';
return name;
}
}
const char *get_fullkey(const char *name, const char *locale) {
static char full_key[SMALL_BUFFER];
sprintf(full_key, "%s@%s", name, locale);
return full_key;
}
const char *get_fullkey_relative(const char *key, const char *locale) {
int pos = next_letter_in(key, '@');
if(pos > 0)
return key;
else {
static char full_key[SMALL_BUFFER];
sprintf(full_key, "%s@%s", key, locale);
return full_key;
}
}
bool cmd_matches(const char *pattern, const char *cmd) {
int len = next_letter_in(pattern, '*');
// we have to match exactly
if(len == -1)
return !strcasecmp(pattern, cmd);
else if(len == 0)
return TRUE;
else
return !strncasecmp(pattern, cmd, len);
}
bool charHasMoreUserGroups(CHAR_DATA *ch1, CHAR_DATA *ch2) {
return (bitIsAllSet(charGetUserGroups(ch1),
bitvectorGetBits(charGetUserGroups(ch2))) &&
!bitIsAllSet(charGetUserGroups(ch2),
bitvectorGetBits(charGetUserGroups(ch1))));
}
bool canEditZone(ZONE_DATA *zone, CHAR_DATA *ch) {
return (!charIsNPC(ch) &&
(is_keyword(zoneGetEditors(zone), charGetName(ch), FALSE) ||
bitIsOneSet(charGetUserGroups(ch), "admin")));
}
bool file_exists(const char *fname) {
FILE *fl = fopen(fname, "r");
if(fl == NULL) return FALSE;
fclose(fl);
return TRUE;
}
bool dir_exists(const char *dname) {
DIR *dir = opendir(dname);
if(dir == NULL) return FALSE;
closedir(dir);
return TRUE;
}
void show_prompt(SOCKET_DATA *socket) {
if(socketGetChar(socket))
text_to_buffer(socket, custom_prompt(socketGetChar(socket)));
}
const char *custom_prompt(CHAR_DATA *ch) {
static char prompt[SMALL_BUFFER];
*prompt = '\0';
strcat(prompt, "\r\n{nprompt> ");
return prompt;
}
//
// Delete an object, room, mobile, etc... from the game. First remove it from
// the gameworld database, and then delete it and its contents. The onus is on
// the builder to make sure deleting a prototype won't screw anything up (e.g.
// people standing about in a room when it's deleted).
bool do_delete(CHAR_DATA *ch, const char *type, void *deleter, const char *arg){
void (* delete_func)(void *data) = deleter;
void *data = NULL;
const char *key = get_fullkey_relative(arg,
get_key_locale(roomGetClass(charGetRoom(ch))));
ZONE_DATA *zone = worldGetZone(gameworld, get_key_locale(key));
if(!arg || !*arg)
send_to_char(ch, "Which %s did you want to delete?\r\n", type);
else if(zone == NULL)
send_to_char(ch, "No such zone exists.\r\n");
else if(!canEditZone(zone, ch))
send_to_char(ch, "You are not authorized to edit that zone.\r\n");
else if((data = worldRemoveType(gameworld, type, key)) == NULL)
send_to_char(ch, "The %s you tried to delete does not exist.\r\n", type);
else {
send_to_char(ch, "You remove the %s %s from the world database.\r\n",
key, type);
delete_func(data);
return TRUE;
}
return FALSE;
}
//
// generic xxxlist for builders.
void do_list(CHAR_DATA *ch, const char *locale, const char *type,
const char *header, void *informer) {
ZONE_DATA *zone = worldGetZone(gameworld, locale);
if(zone == NULL)
send_to_char(ch, "No such zone exists.\r\n");
else {
const char *(* info_func)(void *) = informer;
LIST *name_list = zoneGetTypeKeys(zone, type);
listSortWith(name_list, strcasecmp);
LIST_ITERATOR *name_i = newListIterator(name_list);
char *name = NULL;
BUFFER *buf = newBuffer(1);
void *data = NULL;
bprintf(buf, " {wKey %74s \r\n"
"{b--------------------------------------------------------------------------------{n\r\n", header);
ITERATE_LIST(name, name_i) {
data = worldGetType(gameworld, type, get_fullkey(name, locale));
if(data != NULL)
bprintf(buf, " {c%-20s %57s{n\r\n", name,
(info_func ? info_func(zoneGetType(zone, type, name)) : ""));
} deleteListIterator(name_i);
deleteListWith(name_list, free);
page_string(charGetSocket(ch), bufferString(buf));
deleteBuffer(buf);
}
}
//
// moves something of the given type with one name to the other name
bool do_rename(CHAR_DATA *ch,const char *type,const char *from,const char *to) {
// make sure we can edit all of the zones involved
const char *locale = get_key_locale(roomGetClass(charGetRoom(ch)));
void *tgt = NULL;
ZONE_DATA *zone = NULL;
// make sure "to" does not already exist, and that we have zone editing privs
if((zone = worldGetZone(gameworld,
get_key_locale(get_fullkey_relative(to, locale)))) == NULL)
send_to_char(ch, "Destination zone does not exist!\r\n");
else if(!canEditZone(zone, ch))
send_to_char(ch, "You cannot edit the destination zone.\r\n");
else if(worldGetType(gameworld, type, get_fullkey_relative(to, locale)))
send_to_char(ch, "%s already exists!\r\n", to);
else {
// make sure "from" exists, and that we have zone editing privs
if((zone = worldGetZone(gameworld,
get_key_locale(get_fullkey_relative(from, locale)))) == NULL)
send_to_char(ch, "The originating zone does not exist!\r\n");
else if(!canEditZone(zone, ch))
send_to_char(ch, "You do not have editing priviledges for %s!\r\n", from);
else if( (tgt = worldRemoveType(gameworld, type,
get_fullkey_relative(from, locale))) == NULL)
send_to_char(ch, "%s %s does not exist.\r\n", from, type);
else {
send_to_char(ch, "%s %s renamed to %s.\r\n", from, type, to);
worldPutType(gameworld, type, get_fullkey_relative(to, locale), tgt);
worldSaveType(gameworld, type, get_fullkey_relative(to, locale));
return TRUE;
}
}
// failed!
return FALSE;
}