/* ************************************************************************ * File: utils.c Part of CircleMUD * * Usage: various internal functions of a utility nature * * * * All rights reserved. See license.doc for complete information. * * * * Copyright (C) 1993, 94 by the Trustees of the Johns Hopkins University * * CircleMUD is based on DikuMUD, Copyright (C) 1990, 1991. * ************************************************************************ */ #include "conf.h" #include "sysdep.h" #include "structs.h" #include "utils.h" #include "db.h" #include "comm.h" #include "screen.h" #include "spells.h" #include "handler.h" extern struct descriptor_data *descriptor_list; extern struct time_data time_info; extern struct room_data *world; extern int top_of_world; /* local functions */ struct time_info_data *real_time_passed(time_t t2, time_t t1); struct time_info_data *mud_time_passed(time_t t2, time_t t1); void die_follower(struct char_data * ch); void add_follower(struct char_data * ch, struct char_data * leader); /* creates a random number in interval [from;to] */ int number(int from, int to) { /* error checking in case people call number() incorrectly */ if (from > to) { int tmp = from; from = to; to = tmp; log("SYSERR: number() should be called with lowest, then highest. number(%d, %d), not number(%d, %d).", from, to, to, from); } return ((circle_random() % (to - from + 1)) + from); } /* simulates dice roll */ int dice(int number, int size) { int sum = 0; if (size <= 0 || number <= 0) return 0; while (number-- > 0) sum += ((circle_random() % size) + 1); return sum; } int MIN(int a, int b) { return a < b ? a : b; } int MAX(int a, int b) { return a > b ? a : b; } /* Create a duplicate of a string */ char *str_dup(const char *source) { char *new_z; CREATE(new_z, char, strlen(source) + 1); return (strcpy(new_z, source)); } /* str_cmp: a case-insensitive version of strcmp */ /* returns: 0 if equal, 1 if arg1 > arg2, -1 if arg1 < arg2 */ /* scan 'till found different or end of both */ int str_cmp(const char *arg1, const char *arg2) { int chk, i; for (i = 0; *(arg1 + i) || *(arg2 + i); i++) if ((chk = LOWER(*(arg1 + i)) - LOWER(*(arg2 + i)))) { if (chk < 0) return (-1); else return (1); } return (0); } /* strn_cmp: a case-insensitive version of strncmp */ /* returns: 0 if equal, 1 if arg1 > arg2, -1 if arg1 < arg2 */ /* scan 'till found different, end of both, or n reached */ int strn_cmp(const char *arg1, const char *arg2, int n) { int chk, i; for (i = 0; (*(arg1 + i) || *(arg2 + i)) && (n > 0); i++, n--) if ((chk = LOWER(*(arg1 + i)) - LOWER(*(arg2 + i)))) { if (chk < 0) return (-1); else return (1); } return (0); } /* log a death trap hit */ void log_death_trap(struct char_data * ch) { char buf[150]; sprintf(buf, "%s hit death trap #%d (%s)", GET_NAME(ch), GET_ROOM_VNUM(IN_ROOM(ch)), world[ch->in_room].name); mudlog(buf, BRF, LVL_IMMORT, TRUE); } /* * New variable argument log() function. Works the same as the old for * previously written code but is very nice for new code. */ void basic_mud_log(const char *format, ...) { va_list args; time_t ct = time(0); char *time_s = asctime(localtime(&ct)); time_s[strlen(time_s) - 1] = '\0'; fprintf(logfile, "%-15.15s :: ", time_s + 4); va_start(args, format); vfprintf(logfile, format, args); va_end(args); fprintf(logfile, "\n"); fflush(logfile); } /* the "touch" command, essentially. */ int touch(const char *path) { FILE *fl; if (!(fl = fopen(path, "a"))) { perror(path); return -1; } else { fclose(fl); return 0; } } /* * mudlog -- log mud messages to a file & to online imm's syslogs * based on syslog by Fen Jul 3, 1992 */ void mudlog(const char *str, int type, int level, int file) { char buf[MAX_STRING_LENGTH], tp; struct descriptor_data *i; if (file) log(str); if (level < 0) return; sprintf(buf, "[ %s ]\r\n", str); for (i = descriptor_list; i; i = i->next) if (STATE(i) == CON_PLAYING && !PLR_FLAGGED(i->character, PLR_WRITING)) { tp = ((PRF_FLAGGED(i->character, PRF_LOG1) ? 1 : 0) + (PRF_FLAGGED(i->character, PRF_LOG2) ? 2 : 0)); if ((GET_LEVEL(i->character) >= level) && (tp >= type)) { send_to_char(CCGRN(i->character, C_NRM), i->character); send_to_char(buf, i->character); send_to_char(CCNRM(i->character, C_NRM), i->character); } } } void sprintbit(long bitvector, const char *names[], char *result) { long nr; *result = '\0'; if (bitvector < 0) { strcpy(result, "<INVALID BITVECTOR>"); return; } for (nr = 0; bitvector; bitvector >>= 1) { if (IS_SET(bitvector, 1)) { if (*names[nr] != '\n') { strcat(result, names[nr]); strcat(result, " "); } else strcat(result, "UNDEFINED "); } if (*names[nr] != '\n') nr++; } if (!*result) strcpy(result, "NOBITS "); } void sprinttype(int type, const char *names[], char *result) { int nr = 0; while (type && *names[nr] != '\n') { type--; nr++; } if (*names[nr] != '\n') strcpy(result, names[nr]); else strcpy(result, "UNDEFINED"); } /* Calculate the REAL time passed over the last t2-t1 centuries (secs) */ struct time_info_data *real_time_passed(time_t t2, time_t t1) { long secs; static struct time_info_data now; secs = (long) (t2 - t1); now.hours = (secs / SECS_PER_REAL_HOUR) % 24; /* 0..23 hours */ secs -= SECS_PER_REAL_HOUR * now.hours; now.day = (secs / SECS_PER_REAL_DAY); /* 0..34 days */ secs -= SECS_PER_REAL_DAY * now.day; now.month = -1; now.year = -1; return &now; } /* Calculate the MUD time passed over the last t2-t1 centuries (secs) */ struct time_info_data *mud_time_passed(time_t t2, time_t t1) { long secs; static struct time_info_data now; secs = (long) (t2 - t1); now.hours = (secs / SECS_PER_MUD_HOUR) % 24; /* 0..23 hours */ secs -= SECS_PER_MUD_HOUR * now.hours; now.day = (secs / SECS_PER_MUD_DAY) % 35; /* 0..34 days */ secs -= SECS_PER_MUD_DAY * now.day; now.month = (secs / SECS_PER_MUD_MONTH) % 17; /* 0..16 months */ secs -= SECS_PER_MUD_MONTH * now.month; now.year = (secs / SECS_PER_MUD_YEAR); /* 0..XX? years */ return &now; } struct time_info_data *age(struct char_data * ch) { static struct time_info_data player_age; player_age = *mud_time_passed(time(0), ch->player.time.birth); player_age.year += 17; /* All players start at 17 */ return &player_age; } /* Check if making CH follow VICTIM will create an illegal */ /* Follow "Loop/circle" */ bool circle_follow(struct char_data * ch, struct char_data * victim) { struct char_data *k; for (k = victim; k; k = k->master) { if (k == ch) return TRUE; } return FALSE; } /* Called when stop following persons, or stopping charm */ /* This will NOT do if a character quits/dies!! */ void stop_follower(struct char_data * ch) { struct follow_type *j, *k; if (ch->master == NULL) { core_dump(); return; } if (AFF_FLAGGED(ch, AFF_CHARM)) { act("You realize that $N is a jerk!", FALSE, ch, 0, ch->master, TO_CHAR); act("$n realizes that $N is a jerk!", FALSE, ch, 0, ch->master, TO_NOTVICT); act("$n hates your guts!", FALSE, ch, 0, ch->master, TO_VICT); if (affected_by_spell(ch, SPELL_CHARM)) affect_from_char(ch, SPELL_CHARM); } else { act("You stop following $N.", FALSE, ch, 0, ch->master, TO_CHAR); act("$n stops following $N.", TRUE, ch, 0, ch->master, TO_NOTVICT); act("$n stops following you.", TRUE, ch, 0, ch->master, TO_VICT); } if (ch->master->followers->follower == ch) { /* Head of follower-list? */ k = ch->master->followers; ch->master->followers = k->next; free(k); } else { /* locate follower who is not head of list */ for (k = ch->master->followers; k->next->follower != ch; k = k->next); j = k->next; k->next = j->next; free(j); } ch->master = NULL; REMOVE_BIT(AFF_FLAGS(ch), AFF_CHARM | AFF_GROUP); } /* Called when a character that follows/is followed dies */ void die_follower(struct char_data * ch) { struct follow_type *j, *k; if (ch->master) stop_follower(ch); for (k = ch->followers; k; k = j) { j = k->next; stop_follower(k->follower); } } /* Do NOT call this before having checked if a circle of followers */ /* will arise. CH will follow leader */ void add_follower(struct char_data * ch, struct char_data * leader) { struct follow_type *k; if (ch->master) { core_dump(); return; } ch->master = leader; CREATE(k, struct follow_type, 1); k->follower = ch; k->next = leader->followers; leader->followers = k; act("You now follow $N.", FALSE, ch, 0, leader, TO_CHAR); if (CAN_SEE(leader, ch)) act("$n starts following you.", TRUE, ch, 0, leader, TO_VICT); act("$n starts to follow $N.", TRUE, ch, 0, leader, TO_NOTVICT); } /* * get_line reads the next non-blank line off of the input stream. * The newline character is removed from the input. Lines which begin * with '*' are considered to be comments. * * Returns the number of lines advanced in the file. */ int get_line(FILE * fl, char *buf) { char temp[256]; int lines = 0; do { lines++; fgets(temp, 256, fl); if (*temp) temp[strlen(temp) - 1] = '\0'; } while (!feof(fl) && (*temp == '*' || !*temp)); if (feof(fl)) { *buf = '\0'; return 0; } else { strcpy(buf, temp); return lines; } } int get_filename(char *orig_name, char *filename, int mode) { const char *prefix, *middle, *suffix; char name[64], *ptr; switch (mode) { case CRASH_FILE: prefix = LIB_PLROBJS; suffix = SUF_OBJS; break; case ETEXT_FILE: prefix = LIB_PLRTEXT; suffix = SUF_TEXT; break; default: return 0; } if (!*orig_name) return 0; strcpy(name, orig_name); for (ptr = name; *ptr; ptr++) *ptr = LOWER(*ptr); switch (LOWER(*name)) { case 'a': case 'b': case 'c': case 'd': case 'e': middle = "A-E"; break; case 'f': case 'g': case 'h': case 'i': case 'j': middle = "F-J"; break; case 'k': case 'l': case 'm': case 'n': case 'o': middle = "K-O"; break; case 'p': case 'q': case 'r': case 's': case 't': middle = "P-T"; break; case 'u': case 'v': case 'w': case 'x': case 'y': case 'z': middle = "U-Z"; break; default: middle = "ZZZ"; break; } sprintf(filename, "%s%s"SLASH"%s.%s", prefix, middle, name, suffix); return 1; } int num_pc_in_room(struct room_data *room) { int i = 0; struct char_data *ch; for (ch = room->people; ch != NULL; ch = ch->next_in_room) if (!IS_NPC(ch)) i++; return i; } /* * This function (derived from basic fork(); abort(); idea by Erwin S. * Andreasen) causes your MUD to dump core (assuming you can) but * continue running. The core dump will allow post-mortem debugging * that is less severe than assert(); Don't call this directly as * core_dump_unix() but as simply 'core_dump()' so that it will be * excluded from systems not supporting them. (e.g. Windows '95). * * XXX: Wonder if flushing streams includes sockets? */ void core_dump_real(const char *who, ush_int line) { log("SYSERR: Assertion failed at %s:%d!", who, line); #if defined(CIRCLE_UNIX) /* These would be duplicated otherwise... */ fflush(stdout); fflush(stderr); fflush(logfile); /* * Kill the child so the debugger or script doesn't think the MUD * crashed. The 'autorun' script would otherwise run it again. */ if (fork() == 0) abort(); #endif }