/* ************************************************************************ * 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 <stdio.h> #include <stdlib.h> #include <string.h> #include <ctype.h> #include <limits.h> #include <time.h> #include <sys/types.h> #include <stdarg.h> #include <iostream> using namespace std; #if defined(WIN32) && !defined(__CYGWIN__) #include <winsock.h> #define random() rand() #define srandom(x) srand(x) #else #include <netinet/in.h> #include <unistd.h> #endif #include "telnet.h" #include "structs.h" #include "utils.h" #include "awake.h" #include "comm.h" #include "handler.h" #include "memory.h" #include "house.h" #include "db.h" #include "constants.h" #include "newmagic.h" extern class memoryClass *Mem; extern struct time_info_data time_info; extern void die(struct char_data * ch); extern const char *log_types[]; extern long beginning_of_time; extern int ability_cost(int abil, int level); /* creates a random number in interval [from;to] */ int number(int from, int to) { if (from == to) return from; else if (from > to) { // it should not happen, but if it does... int temp = to; to = from; from = temp; } return ((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 += ((random() % size) + 1); return sum; } #ifndef __GNUG__ int MIN(long a, long b) { return a < b ? a : b; } int MAX(long a, long b) { return a > b ? a : b; } #endif /* rolls a 6-sided dice by rule of 6 and rule of 1 */ int srdice(void) { static int roll; int sum = 0, num = 1; register int i; for (i = 1; i <= num; i++) { roll = ((random() % 6) + 1); if (roll == 6) num++; sum += roll; } return sum; } int success_test(int number, int target) { if (number < 1) return -1; int total = 0, roll, one = 0; register int i; target = MAX(target, 2); for (i = 1; i <= number; i++) { if ((roll = srdice()) == 1) one++; else if (roll >= target) total++; } if (one == number) return -1; return total; } int resisted_test(int num4ch, int tar4ch, int num4vict, int tar4vict) { return (success_test(num4ch, tar4ch) - success_test(num4vict, tar4vict)); } int dec_staging(int successes, int wound) { while (successes >= 2) { wound--; successes -= 2; } return wound; } int stage(int successes, int wound) { if (successes >= 0) while (successes >= 2) { wound++; successes -= 2; } else while (successes <= -2) { wound--; successes += 2; } return wound; } int convert_damage(int damage) { int extra = 0; if (damage < 0) damage = 0; else if (damage > 4) { extra = (damage - 4); damage = 10; // deadly } else damage = damage_array[damage]; return (damage + extra); } int light_level(rnum_t room) { if (world[room].sector_type == SPIRIT_HEARTH) return world[room].vision[0]; if (world[room].sector_type == SPIRIT_CITY) { if ((time_info.hours > 6 && time_info.hours < 19)) return world[room].vision[0]; else if (world[room].vision[0] == LIGHT_NORMALNOLIT) return LIGHT_MINLIGHT; else return LIGHT_PARTLIGHT; } if ((time_info.hours < 6 && time_info.hours > 19) && (world[room].vision[0] > LIGHT_MINLIGHT || world[room].vision[0] <= LIGHT_NORMALNOLIT)) return LIGHT_MINLIGHT; else return world[room].vision[0]; } int damage_modifier(struct char_data *ch, char *rbuf) { int physical = GET_PHYSICAL(ch), mental = GET_MENTAL(ch), base_target = 0; for (struct obj_data *obj = ch->bioware; obj; obj = obj->next_content) { if (GET_OBJ_VAL(obj, 0) == BIO_DAMAGECOMPENSATOR) { physical += GET_OBJ_VAL(obj, 1) * 100; mental += GET_OBJ_VAL(obj, 1) * 100; } else if (GET_OBJ_VAL(obj, 0) == BIO_PAINEDITOR && GET_OBJ_VAL(obj, 3)) mental = 1000; } if (AFF_FLAGGED(ch, AFF_RESISTPAIN)) { physical += ch->points.resistpain * 100; mental += ch->points.resistpain * 100; } if (GET_TRADITION(ch) == TRAD_ADEPT && GET_POWER(ch, ADEPT_PAIN_RESISTANCE) > 0) { physical += GET_POWER(ch, ADEPT_PAIN_RESISTANCE) * 100; mental += GET_POWER(ch, ADEPT_PAIN_RESISTANCE) * 100; } // first apply physical damage modifiers if (physical <= 400) { base_target += 3; buf_mod( rbuf, "PhyS", 3 ); } else if (physical <= 700) { base_target += 2; buf_mod( rbuf, "PhyM", 2 ); } else if (GET_PHYSICAL(ch) <= 900) { base_target += 1; buf_mod( rbuf, "PhyL", 1 ); } if (mental <= 400) { base_target += 3; buf_mod( rbuf, "MenS", 3 ); } else if (mental <= 700) { base_target += 2; buf_mod( rbuf, "MenM", 2 ); } else if (mental <= 900) { base_target += 1; buf_mod( rbuf, "MenL", 1 ); } return base_target; } int modify_target_rbuf(struct char_data *ch, char *rbuf) { extern time_info_data time_info; int base_target = 0, light_target = 0; base_target += damage_modifier(ch, rbuf); // then apply modifiers for sustained spells if (GET_SUSTAINED_NUM(ch) > 0) { base_target += ((GET_SUSTAINED_NUM(ch) - GET_SUSTAINED_FOCI(ch)) * 2); buf_mod( rbuf, "Sustain", (GET_SUSTAINED_NUM(ch) - GET_SUSTAINED_FOCI(ch)) * 2); } if (PLR_FLAGGED(ch, PLR_PERCEIVE)) { base_target += 2; buf_mod(rbuf, "AstralPercep", 2); } else { switch (light_level(ch->in_room)) { case LIGHT_FULLDARK: if (CURRENT_VISION(ch) == THERMOGRAPHIC) { if (NATURAL_VISION(ch) == THERMOGRAPHIC) { light_target += 2; buf_mod(rbuf, "FullDark", 2); } else { light_target += 4; buf_mod(rbuf, "FullDark", 4); } } else { light_target += 8; buf_mod(rbuf, "FullDark", 8); } break; case LIGHT_MINLIGHT: if (CURRENT_VISION(ch) == NORMAL) { light_target += 6; buf_mod(rbuf, "MinLight", 6); } else { if (NATURAL_VISION(ch) == NORMAL) { light_target += 4; buf_mod(rbuf, "MinLight", 4); } else { base_target += 2; buf_mod(rbuf, "MinLight", 2); } } break; case LIGHT_PARTLIGHT: if (CURRENT_VISION(ch) == NORMAL) { light_target += 2; buf_mod(rbuf, "PartLight", 2); } else if (CURRENT_VISION(ch) == LOWLIGHT) { if (NATURAL_VISION(ch) != LOWLIGHT) { light_target++; buf_mod(rbuf, "PartLight", 1); } } else { if (NATURAL_VISION(ch) != THERMOGRAPHIC) { light_target += 2; buf_mod(rbuf, "PartLight", 2); } else { light_target++; buf_mod(rbuf, "PartLight", 1); } } break; case LIGHT_GLARE: if (CURRENT_VISION(ch) == NORMAL) { light_target += 2; buf_mod(rbuf, "Glare", 2); } else { if (NATURAL_VISION(ch) == NORMAL) { light_target += 4; buf_mod(rbuf, "Glare", 2); } else { light_target += 2; buf_mod(rbuf, "Glare", 2); } } break; } if (light_target > 0 && world[ch->in_room].light[1]) { if (world[ch->in_room].light[2]) { light_target = MAX(0, light_target - world[ch->in_room].light[2]); buf_mod(rbuf, "LightSpell", - world[ch->in_room].light[2]); } else light_target /= 2; } if (world[ch->in_room].shadow[0]) { light_target += world[ch->in_room].shadow[1]; buf_mod(rbuf, "ShadowSpell", world[ch->in_room].shadow[1]); } base_target += light_target; if (world[ch->in_room].vision[1] == LIGHT_MIST) if (CURRENT_VISION(ch) == NORMAL || (CURRENT_VISION(ch) == LOWLIGHT && NATURAL_VISION(ch) == LOWLIGHT)) { base_target += 2; buf_mod(rbuf, "Mist", 2); } if (world[ch->in_room].vision[1] == LIGHT_LIGHTSMOKE || (weather_info.sky == SKY_RAINING && world[ch->in_room].sector_type != SPIRIT_HEARTH && !ROOM_FLAGGED(ch->in_room, ROOM_INDOORS))) if (CURRENT_VISION(ch) == NORMAL || (CURRENT_VISION(ch) == LOWLIGHT && NATURAL_VISION(ch) != LOWLIGHT)) { base_target += 4; buf_mod(rbuf, "LSmoke", 4); } else if (CURRENT_VISION(ch) == LOWLIGHT) { base_target += 2; buf_mod(rbuf, "LSmoke", 2); } if (world[ch->in_room].vision[1] == LIGHT_HEAVYSMOKE || (weather_info.sky == SKY_LIGHTNING && world[ch->in_room].sector_type != SPIRIT_HEARTH && !ROOM_FLAGGED(ch->in_room, ROOM_INDOORS))) if (CURRENT_VISION(ch) == NORMAL || (CURRENT_VISION(ch) == LOWLIGHT && NATURAL_VISION(ch) == NORMAL)) { base_target += 6; buf_mod(rbuf, "HSmoke", 6); } else if (CURRENT_VISION(ch) == LOWLIGHT) { base_target += 4; buf_mod(rbuf, "HSmoke", 4); } else if (CURRENT_VISION(ch) == THERMOGRAPHIC && NATURAL_VISION(ch) != THERMOGRAPHIC) { base_target++; buf_mod(rbuf, "HSmoke", 1); } if (world[ch->in_room].vision[1] == LIGHT_THERMALSMOKE) if (CURRENT_VISION(ch) == NORMAL || CURRENT_VISION(ch) == LOWLIGHT) { base_target += 4; buf_mod(rbuf, "TSmoke", 4); } else { if (NATURAL_VISION(ch) == THERMOGRAPHIC) { base_target += 6; buf_mod(rbuf, "TSmoke", 6); } else { base_target += 8; buf_mod(rbuf, "TSmoke", 8); } } } base_target += GET_TARGET_MOD(ch); buf_mod( rbuf, "GET_TARGET_MOD", GET_TARGET_MOD(ch) ); if (GET_RACE(ch) == RACE_NIGHTONE && ((time_info.hours > 6) && (time_info.hours < 19)) && OUTSIDE(ch)) { base_target += 1; buf_mod( rbuf, "Sunlight", 1); } if (world[ch->in_room].poltergeist[0] && !IS_ASTRAL(ch) && !IS_DUAL(ch)) { base_target += 2; buf_mod(rbuf, "Polter", 2); } if (AFF_FLAGGED(ch, AFF_ACID)) { base_target += 4; buf_mod(rbuf, "Acid", 4); } if (ch->points.fire[0] > 0) { base_target += 4; buf_mod(rbuf, "OnFire", 4); } for (struct sustain_data *sust = GET_SUSTAINED(ch); sust; sust = sust->next) { if (sust->caster == FALSE && (sust->spell == SPELL_CONFUSION || sust->spell == SPELL_CHAOS)) { base_target += MIN(sust->force, sust->success); buf_mod(rbuf, "Confused", MIN(sust->force, sust->success)); } } if (!(IS_ELEMENTAL(ch) || IS_SPIRIT(ch))) for (struct spirit_sustained *sust = SPIRIT_SUST(ch); sust; sust = sust->next) if (sust == CONFUSION) { base_target += GET_LEVEL(sust->target); buf_mod(rbuf, "SConfused", GET_LEVEL(sust->target)); } if (ch->in_room != NOWHERE && ROOM_FLAGGED(ch->in_room, ROOM_INDOORS)) { float heightdif = (float)(GET_HEIGHT(ch)/100) / world[ch->in_room].z; if (heightdif > 1) { base_target += 2; buf_mod(rbuf, "TooTallRatio", (int)(heightdif*100)); } if (heightdif > 1.2) base_target += 2; if (heightdif > 1.5) base_target += 2; if (heightdif > 2) base_target += 2; } return base_target; } int modify_target(struct char_data *ch) { return modify_target_rbuf(ch, NULL); } // this returns the general skill int return_general(int skill_num) { switch (skill_num) { case SKILL_PISTOLS: case SKILL_RIFLES: case SKILL_SHOTGUNS: case SKILL_ASSAULT_RIFLES: case SKILL_SMG: case SKILL_GRENADE_LAUNCHERS: case SKILL_TASERS: case SKILL_MACHINE_GUNS: case SKILL_MISSILE_LAUNCHERS: case SKILL_ASSAULT_CANNON: case SKILL_ARTILLERY: return (SKILL_FIREARMS); case SKILL_EDGED_WEAPONS: case SKILL_POLE_ARMS: case SKILL_WHIPS_FLAILS: case SKILL_CLUBS: return (SKILL_ARMED_COMBAT); default: return (skill_num); } } // capitalize a string char *capitalize(const char *source) { static char dest[MAX_STRING_LENGTH]; strcpy(dest, source); *dest = UPPER(*dest); return dest; } // duplicate a string -- uses new! char *str_dup(const char *source) { if (!source) return NULL; char *New = new char[strlen(source) + 1]; sprintf(New, "%s", source); return New; } // this function runs through 'str' and copies the first token to 'token'. // it assumes that token is already allocated--it returns a pointer to the // next char after the token in str char *get_token(char *str, char *token) { if (!str) return NULL; register char *temp = str; register char *temp1 = token; // first eat up any white space while (isspace(*temp)) temp++; // now loop through the string and copy each char till we find a space while (*temp && !isspace(*temp)) *temp1++ = *temp++; // terminate the string properly *temp1 = '\0'; return temp; } /* strips \r's from line -- Chris*/ char *cleanup(char *dest, const char *src) { if (!src) // this is because sometimes a null gets sent to src return NULL; register char *temp = &dest[0]; for (; *src; src++) if (*src != '\r') *temp++ = *src; *temp = '\0'; return dest; } /* str_cmp: a case-insensitive version of strcmp */ /* returns: 0 if equal, pos if arg1 > arg2, neg if arg1 < arg2 */ /* scan 'till found different or end of both */ int str_cmp(const char *one, const char *two) { for (; *one; one++, two++) { int diff = LOWER(*one) - LOWER(*two); if (diff!= 0) return diff; } return (LOWER(*one) - LOWER(*two)); } /* str_str: A case-insensitive version of strstr */ /* returns: A pointer to the first occurance of str2 in str1 */ /* or a null pointer if it isn't found. */ char *str_str( const char *str1, const char *str2 ) { int i; char temp1[MAX_INPUT_LENGTH], temp2[MAX_INPUT_LENGTH]; for ( i = 0; *(str1 + i); i++ ) { temp1[i] = LOWER(*(str1 + i)); } temp1[i] = '\0'; for ( i = 0; *(str2 + i); i++ ) { temp2[i] = LOWER(*(str2 + i)); } temp2[i] = '\0'; return (strstr(temp1, temp2)); } /* 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; if (arg1 == NULL || arg2 == NULL) { log("SYSERR: strn_cmp() passed a NULL pointer, %p or %p.", arg1, arg2); return (0); } for (i = 0; (arg1[i] || arg2[i]) && (n > 0); i++, n--) if ((chk = LOWER(arg1[i]) - LOWER(arg2[i])) != 0) return (chk); /* not equal */ return (0); } /* returns 1 if the character has a cyberweapon; 0 otherwise */ bool has_cyberweapon(struct char_data * ch) { struct obj_data *obj = NULL; for (obj = ch->cyberware; obj ; obj = obj->next_content) if ((GET_OBJ_VAL(obj, 0) == CYB_HANDSPUR || GET_OBJ_VAL(obj, 0) == CYB_HANDRAZOR || GET_OBJ_VAL(obj, 0) == CYB_HANDBLADE || GET_OBJ_VAL(obj, 0) == CYB_FIN) && !GET_OBJ_VAL(obj, 9)) return TRUE; return 0; } /* log a death trap hit */ void log_death_trap(struct char_data * ch) { char buf[150]; extern struct room_data *world; sprintf(buf, "%s hit DeathTrap #%ld (%s)", GET_CHAR_NAME(ch), world[ch->in_room].number, world[ch->in_room].name); mudlog(buf, ch, LOG_DEATHLOG, TRUE); } void log(const char *format, ...) { va_list args; time_t ct = time(0); char *tmstr; tmstr = asctime(localtime(&ct)); *(tmstr + strlen(tmstr) - 1) = '\0'; fprintf(stderr, "%-15.15s :: ", tmstr + 4); va_start(args, format); vfprintf(stderr, format, args); va_end(args); fprintf(stderr, "\n"); } /* * mudlog -- log mud messages to a file & to online imm's syslogs * based on syslog by Fen Jul 3, 1992 */ void mudlog(char *str, struct char_data *ch, int log, bool file) { char buf[MAX_STRING_LENGTH], buf2[MAX_STRING_LENGTH]; extern struct descriptor_data *descriptor_list; struct descriptor_data *i; struct char_data *tch; char *tmp; time_t ct; int check_log = 0; ct = time(0); tmp = asctime(localtime(&ct)); if ( ch && ch->desc && ch->desc->original ) sprintf(buf2, "[%5ld] (%s) ", world[ch->in_room].number, GET_CHAR_NAME(ch)); else if (ch && ch->in_room != NOWHERE) sprintf(buf2, "[%5ld] ", world[ch->in_room].number); else strcpy(buf2, ""); if (file) fprintf(stderr, "%-19.19s :: %s: %s%s\n", tmp, log_types[log], buf2, str); ct = ct; sprintf(buf, "^g[%s: %s%s]^n\r\n", log_types[log], buf2, str); for (i = descriptor_list; i; i = i->next) if (!i->connected) { if (i->original) tch = i->original; else tch = i->character; if (!tch || PLR_FLAGS(tch).AreAnySet(PLR_WRITING, PLR_MAILING, PLR_EDITING, ENDBIT)) continue; if (ch && !access_level(tch, GET_INVIS_LEV(ch)) && !access_level(tch, LVL_VICEPRES)) continue; switch (log) { case LOG_CONNLOG: check_log = PRF_CONNLOG; break; case LOG_DEATHLOG: check_log = PRF_DEATHLOG; break; case LOG_MISCLOG: check_log = PRF_MISCLOG; break; case LOG_WIZLOG: check_log = PRF_WIZLOG; break; case LOG_SYSLOG: check_log = PRF_SYSLOG; break; case LOG_ZONELOG: check_log = PRF_ZONELOG; break; case LOG_CHEATLOG: check_log = PRF_CHEATLOG; break; case LOG_WIZITEMLOG: check_log = PRF_CHEATLOG; break; case LOG_BANLOG: check_log = PRF_BANLOG; break; case LOG_GRIDLOG: check_log = PRF_GRIDLOG; break; case LOG_WRECKLOG: check_log = PRF_WRECKLOG; break; } if (PRF_FLAGGED(tch, check_log)) SEND_TO_Q(buf, i); } } void sprintbit(long vektor, const char *names[], char *result) { long nr; *result = '\0'; if (vektor < 0) { strcpy(result, "SPRINTBIT ERROR!"); return; } for (nr = 0; vektor; vektor >>= 1) { if (IS_SET(1, vektor)) { if (*names[nr] != '\n') { strcat(result, names[nr]); strcat(result, " "); } else strcat(result, "UNDEFINED "); } if (*names[nr] != '\n') nr++; } if (!*result) strcat(result, "None "); } void sprinttype(int type, const char *names[], char *result) { sprintf(result, "%s", names[type]); if (result == "(null") result = "UNDEFINED"; } void sprint_obj_mods(struct obj_data *obj, char *result) { *result = 0; if (obj->obj_flags.bitvector.GetNumSet() > 0) { char xbuf[MAX_STRING_LENGTH]; obj->obj_flags.bitvector.PrintBits(xbuf, MAX_STRING_LENGTH, affected_bits, AFF_MAX); sprintf(result,"%s %s", result, xbuf); } for (register int i = 0; i < MAX_OBJ_AFFECT; i++) if (obj->affected[i].modifier != 0) { char xbuf[MAX_STRING_LENGTH]; sprinttype(obj->affected[i].location, apply_types, xbuf); sprintf(result,"%s (%+d %s)", result, obj->affected[i].modifier, xbuf); } return; } /* 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; 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 = 0; now.year = 0; 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; 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) % 30; /* 0..34 days */ secs -= SECS_PER_MUD_DAY * now.day; now.month = (secs / SECS_PER_MUD_MONTH) % 12; /* 0..16 months */ secs -= SECS_PER_MUD_MONTH * now.month; now.year = (secs / SECS_PER_MUD_YEAR); /* 0..XX? years */ return now; } bool access_level(struct char_data *ch, int level) { ch = ch->desc && ch->desc->original ? ch->desc->original : ch; return (!IS_NPC(ch) && (GET_LEVEL(ch) >= level )); } /* 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->followers->follower == ch) { /* Head of follower-list? */ k = ch->master->followers; ch->master->followers = k->next; delete 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; delete j; } AFF_FLAGS(ch).RemoveBits(AFF_CHARM, AFF_GROUP, ENDBIT); 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); ch->master = NULL; } /* 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); } if (ch->player_specials->gname) { delete [] ch->player_specials->gname; ch->player_specials->gname = NULL; } } /* 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; ch->master = leader; k = new follow_type; k->follower = ch; k->next = leader->followers; leader->followers = k; if (!IS_SPIRIT(ch) && !IS_ELEMENTAL(ch)) { 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)) return 0; else { strcpy(buf, temp); return lines; } } bool PRF_TOG_CHK(char_data *ch, dword offset) { PRF_FLAGS(ch).ToggleBit(offset); return PRF_FLAGS(ch).IsSet(offset); } bool PLR_TOG_CHK(char_data *ch, dword offset) { PLR_FLAGS(ch).ToggleBit(offset); return PLR_FLAGS(ch).IsSet(offset); } char * buf_mod(char *rbuf, char *name, int bonus) { if ( !rbuf ) return rbuf; if ( bonus == 0 ) return rbuf; rbuf += strlen(rbuf); if ( bonus > 0 ) sprintf(rbuf, "%s +%d, ", name, bonus); else sprintf(rbuf, "%s %d, ", name, bonus); rbuf += strlen(rbuf); return rbuf; } char * buf_roll(char *rbuf, char *name, int bonus) { if ( !rbuf ) return rbuf; rbuf += strlen(rbuf); sprintf(rbuf, " [%s %d]", name, bonus); return rbuf; } int get_speed(struct veh_data *veh) { int speed = 0; switch (veh->cspeed) { case SPEED_OFF: case SPEED_IDLE: speed = 0; break; case SPEED_CRUISING: if (ROOM_FLAGGED(veh->in_room, ROOM_INDOORS)) speed = MIN(veh->speed, 3); else speed = MIN(veh->speed, 55); break; case SPEED_SPEEDING: if (ROOM_FLAGGED(veh->in_room, ROOM_INDOORS)) speed = MIN(veh->speed, MAX(5, (int)(veh->speed * .7))); else speed = MIN(veh->speed, MAX(55, (int)(veh->speed * .7))); break; case SPEED_MAX: if (ROOM_FLAGGED(veh->in_room, ROOM_INDOORS)) speed = MIN(veh->speed, 8); else speed = veh->speed; break; } return (speed); } int negotiate(struct char_data *ch, struct char_data *tch, int comp, int basevalue, int mod, bool buy) { struct obj_data *bio; int cmod = 0, tmod = 0; int cskill = get_skill(ch, SKILL_NEGOTIATION, cmod); cmod -= GET_POWER(ch, ADEPT_KINESICS); tmod -= GET_POWER(tch, ADEPT_KINESICS); if (GET_RACE(ch) != GET_RACE(tch)) { switch (GET_RACE(ch)) { case RACE_HUMAN: case RACE_ELF: case RACE_ORK: case RACE_TROLL: case RACE_DWARF: break; default: cmod += 4; break; } switch (GET_RACE(tch)) { case RACE_HUMAN: case RACE_ELF: case RACE_ORK: case RACE_TROLL: case RACE_DWARF: break; default: tmod += 4; break; } } for (bio = ch->bioware; bio; bio = bio->next_content) if (GET_OBJ_VAL(bio, 0) == BIO_TAILOREDPHEREMONES) { cskill += GET_OBJ_VAL(bio, 2) ? GET_OBJ_VAL(bio, 1) * 2: GET_OBJ_VAL(bio, 1); break; } int tskill = get_skill(tch, SKILL_NEGOTIATION, tmod); for (bio = tch->bioware; bio; bio = bio->next_content) if (GET_OBJ_VAL(bio, 0) == BIO_TAILOREDPHEREMONES) { tskill += GET_OBJ_VAL(bio, 2) ? GET_OBJ_VAL(bio, 1) * 2: GET_OBJ_VAL(bio, 1); break; } int chnego = success_test(cskill, GET_INT(tch)+mod+cmod); int tchnego = success_test(tskill, GET_INT(ch)+mod+tmod); if (comp) { chnego += success_test(GET_SKILL(ch, comp), GET_INT(tch)+mod+cmod) / 2; tchnego += success_test(GET_SKILL(tch, comp), GET_INT(ch)+mod+tmod) / 2; } int num = chnego - tchnego; if (num > 0) { if (buy) basevalue = MAX((int)(basevalue * 3/4), basevalue - (num * (basevalue / 20))); else basevalue = MIN((int)(basevalue * 5/4), basevalue + (num * (basevalue / 15))); } else { if (buy) basevalue = MIN((int)(basevalue * 5/4), basevalue + (num * (basevalue / 15))); else basevalue = MAX((int)(basevalue * 3/4), basevalue - (num * (basevalue / 20))); } return basevalue; } int get_skill(struct char_data *ch, int skill, int &target) { if (GET_SKILL(ch, skill)) { int totalskill, mbw = 0, enhan = 0, synth = 0; totalskill = GET_SKILL(ch, skill); if (REAL_SKILL(ch, skill) == GET_SKILL(ch, skill)) totalskill += MIN(REAL_SKILL(ch, skill), GET_TASK_POOL(ch, skills[skill].attribute)); else if (ch->cyberware) { int expert = 0; bool chip; for (struct obj_data *obj = ch->cyberware; obj; obj = obj->next_content) if (GET_OBJ_VAL(obj, 0) == CYB_MOVEBYWIRE) mbw = GET_OBJ_VAL(obj, 1); else if (GET_OBJ_VAL(obj, 0) == CYB_CHIPJACKEXPERT) expert = GET_OBJ_VAL(obj, 1); else if (GET_OBJ_VAL(obj, 0) == CYB_CHIPJACK) for (int i = 5; i < 10; i++) if (real_object(GET_OBJ_VAL(obj, i)) && obj_proto[real_object(GET_OBJ_VAL(obj, i))].obj_flags.value[0] == skill) chip = TRUE; if (chip && expert) totalskill += MIN(REAL_SKILL(ch, skill), expert); } else if (ch->bioware) for (struct obj_data *bio = ch->bioware; bio; bio = bio->next_content) if (GET_OBJ_VAL(bio, 0) == BIO_REFLEXRECORDER && GET_OBJ_VAL(bio, 3) == skill) totalskill++; else if (GET_OBJ_VAL(bio, 0) == BIO_ENHANCEDARTIC) enhan = TRUE; else if (GET_OBJ_VAL(bio, 0) == BIO_SYNTHACARDIUM) synth = GET_OBJ_VAL(bio, 1); if (skill == SKILL_STEALTH) totalskill += mbw; if (skill == SKILL_ATHLETICS) totalskill += synth + mbw; if (enhan && (skills[skill].attribute == QUI || skill == SKILL_BR_CAR || skill == SKILL_BR_BIKE || skill == SKILL_BR_COMPUTER || skill == SKILL_BR_ELECTRONICS || skill == SKILL_BR_TRUCK || skill == SKILL_BR_DRONE)) totalskill++; if (skills[skill].attribute == QUI) { if (GET_TOTALIMP(ch) > GET_QUI(ch)) target += GET_TOTALIMP(ch) - GET_QUI(ch); if (GET_TOTALBAL(ch) > GET_QUI(ch)) target += GET_TOTALBAL(ch) - GET_QUI(ch); } return totalskill; } else { if (skills[skill].attribute == QUI) { if (GET_TOTALIMP(ch) > GET_QUI(ch)) target += GET_TOTALIMP(ch) - GET_QUI(ch); if (GET_TOTALBAL(ch) > GET_QUI(ch)) target += GET_TOTALBAL(ch) - GET_QUI(ch); } if (target >= 8) return 0; target += 4; return GET_ATT(ch, skills[skill].attribute); } } bool biocyber_compatibility(struct obj_data *obj1, struct obj_data *obj2, struct char_data *ch) { struct obj_data *cyber1 = NULL, *cyber2 = NULL, *bio1 = NULL; if (GET_OBJ_TYPE(obj1) == ITEM_CYBERWARE) cyber1 = obj1; else bio1 = obj1; if (GET_OBJ_TYPE(obj2) == ITEM_BIOWARE) bio1 = obj2; else { if (cyber1) cyber2 = obj2; else cyber1 = obj2; } if (cyber1 && cyber2) { if (GET_OBJ_VAL(cyber1, 0) != CYB_EYES) switch (GET_OBJ_VAL(cyber1, 0)) { case CYB_FILTRATION: if (GET_OBJ_VAL(cyber1, 3) == GET_OBJ_VAL(cyber2, 3)) { send_to_char("You already have this type of filtration installed.\r\n", ch); return FALSE; } break; case CYB_DATAJACK: if (GET_OBJ_VAL(cyber2, 0) == CYB_EYES && IS_SET(GET_OBJ_VAL(cyber2, 3), EYE_DATAJACK)) { send_to_char("You already have an eye datajack installed.\r\n", ch); return FALSE; } break; case CYB_VCR: if (GET_OBJ_VAL(cyber2, 0) == CYB_BOOSTEDREFLEXES) { send_to_char("Vehicle Control Rigs are not compatible with Boosted reflexes.\r\n", ch); return FALSE; } break; case CYB_BOOSTEDREFLEXES: if (GET_OBJ_VAL(cyber2, 0) == CYB_VCR) { send_to_char("Boosted reflexes is not compatible with Vehicle Control Rigs.\r\n", ch); return FALSE; } case CYB_MOVEBYWIRE: case CYB_WIREDREFLEXES: if (GET_OBJ_VAL(cyber2, 0) == CYB_WIREDREFLEXES || GET_OBJ_VAL(cyber2, 0) == CYB_MOVEBYWIRE || GET_OBJ_VAL(cyber2, 0) == CYB_BOOSTEDREFLEXES) { send_to_char("You already have reaction enhancing cyberware installed.\r\n", ch); return FALSE; } if (GET_OBJ_VAL(cyber1, 0) == CYB_MOVEBYWIRE && GET_OBJ_VAL(cyber2, 0) == CYB_REACTIONENHANCE) { send_to_char("Move-by-wire is not compatible with reaction enhancers.\r\n", ch); return FALSE; } break; case CYB_ORALSLASHER: case CYB_ORALDART: case CYB_ORALGUN: if (GET_OBJ_VAL(cyber2, 0) == CYB_ORALSLASHER || GET_OBJ_VAL(cyber2, 0) == CYB_ORALDART || GET_OBJ_VAL(cyber2, 0) == CYB_ORALGUN) { send_to_char("You already have a weapon in your mouth.\r\n", ch); return FALSE; } break; case CYB_DERMALPLATING: case CYB_DERMALSHEATHING: if (GET_OBJ_VAL(cyber2, 0) == CYB_DERMALPLATING || GET_OBJ_VAL(cyber2, 0) == CYB_DERMALSHEATHING) { send_to_char("You already have an skin modifiaction.\r\n", ch); return FALSE; } break; case CYB_REACTIONENHANCE: if (GET_OBJ_VAL(cyber2, 0) == CYB_MOVEBYWIRE) { send_to_char("Reaction enhancers are not compatible with Move-by-wire.\r\n", ch); return FALSE; } break; } if (IS_SET(GET_OBJ_VAL(cyber1, 3), EYE_DATAJACK) && GET_OBJ_VAL(cyber2, 0) == CYB_DATAJACK) { send_to_char("You already have a datajack installed.\r\n", ch); return FALSE; } if (GET_OBJ_VAL(cyber2, 0) == CYB_EYES && GET_OBJ_VAL(cyber1, 0) == CYB_EYES) for (int bit = EYE_CAMERA; bit <= EYE_ULTRASOUND; bit *= 2) { if (IS_SET(GET_OBJ_VAL(cyber2, 3), bit) && IS_SET(GET_OBJ_VAL(cyber1, 3), bit)) { send_to_char("You already have eye modifications with this option installed.\r\n", ch); return FALSE; } if (bit >= EYE_OPTMAG1 && bit <= EYE_OPTMAG3 && IS_SET(GET_OBJ_VAL(cyber1, 3), bit)) if (IS_SET(GET_OBJ_VAL(cyber2, 3), EYE_ELECMAG1) || IS_SET(GET_OBJ_VAL(cyber2, 3), EYE_ELECMAG2) || IS_SET(GET_OBJ_VAL(cyber2, 3), EYE_ELECMAG3)) { send_to_char("Optical magnification is not compatible with electronic magnification.\r\n", ch); return FALSE; } if (bit >= EYE_ELECMAG1 && bit <= EYE_ELECMAG3 && IS_SET(GET_OBJ_VAL(cyber1, 3), bit)) if (IS_SET(GET_OBJ_VAL(cyber2, 3), EYE_OPTMAG1) || IS_SET(GET_OBJ_VAL(cyber2, 3), EYE_OPTMAG2) || IS_SET(GET_OBJ_VAL(cyber2, 3), EYE_OPTMAG3)) { send_to_char("Optical magnification is not compatible with electronic magnification.\r\n", ch); return FALSE; } } } else if (bio1 && cyber1) { switch (GET_OBJ_VAL(cyber1, 0)) { case CYB_FILTRATION: if (GET_OBJ_VAL(bio1, 0) == BIO_TRACHEALFILTER && GET_OBJ_VAL(cyber1, 3) == FILTER_AIR) { send_to_char("Air filtration cyberware is not compatible with a Tracheal Filter.\r\n", ch); return FALSE; } if (GET_OBJ_VAL(bio1, 0) == BIO_DIGESTIVEEXPANSION && GET_OBJ_VAL(cyber1, 3) == FILTER_INGESTED) { send_to_char("Tracheal Filtration cyberware is not compatible with Digestive Expansion.\r\n", ch); return FALSE; } break; case CYB_MOVEBYWIRE: if (GET_OBJ_VAL(bio1, 0) == BIO_ADRENALPUMP) { send_to_char("Adrenal Pumps are not compatible with Move-By-Wire.\r\n", ch); return FALSE; } if (GET_OBJ_VAL(bio1, 0) == BIO_SYNAPTICACCELERATOR) { send_to_char("Synaptic Accelerators are not compatible with Move-By-Wire.\r\n", ch); return FALSE; } if (GET_OBJ_VAL(bio1, 0) == BIO_SUPRATHYROIDGLAND) { send_to_char("Suprathyroid Glands are not compatible with Move-By-Wire.\r\n", ch); return FALSE; } break; case CYB_WIREDREFLEXES: if (GET_OBJ_VAL(bio1, 0) == BIO_SYNAPTICACCELERATOR) { send_to_char("Your Synaptic Accelerator is not compatible with Wired Reflexes.\r\n", ch); return FALSE; } break; case CYB_EYES: if (GET_OBJ_VAL(bio1, 0) == BIO_CATSEYES || GET_OBJ_VAL(bio1, 0) == BIO_NICTATINGGLAND) { send_to_char("Bioware and cyberware eye modifications aren't compatible.\r\n", ch); return FALSE; } break; case CYB_MUSCLEREP: if (GET_OBJ_VAL(bio1, 0) == BIO_MUSCLEAUG || GET_OBJ_VAL(bio1, 0) == BIO_MUSCLETONER) { send_to_char("Muscle replacement isn't compatible with Muscle Augmentation or Toners.\r\n", ch); return FALSE; } break; case CYB_DERMALPLATING: case CYB_DERMALSHEATHING: if (GET_OBJ_VAL(bio1, 0) == BIO_ORTHOSKIN) { send_to_char("Orthoskin is not compatible with Dermal Plating or Sheathing.\r\n", ch); return FALSE; } break; } } return TRUE; } void reduce_abilities(struct char_data *vict) { int i; for (i = 0; i < ADEPT_NUMPOWER; i++) if (GET_POWER_TOTAL(vict, i) > GET_MAG(vict) / 100) { GET_PP(vict) += ability_cost(i, GET_POWER_TOTAL(vict, i)); GET_POWER_TOTAL(vict, i)--; send_to_char(vict, "Your loss in magic makes you feel less " "skilled in %s.\r\n", adept_powers[i]); } if (GET_PP(vict) >= 0) return; for (i = number(1, ADEPT_NUMPOWER); GET_PP(vict) < 0; i = number(1, ADEPT_NUMPOWER)) { if (GET_POWER_TOTAL(vict, i) > 0) { GET_PP(vict) += ability_cost(i, GET_POWER_TOTAL(vict, i)); GET_POWER_TOTAL(vict, i)--; send_to_char(vict, "Your loss in magic makes you feel less " "skilled in %s.\r\n", adept_powers[i]); } int y = 0; for (int x = 0; x < ADEPT_NUMPOWER; x++) if (GET_POWER_TOTAL(vict, x)) y += ability_cost(x, GET_POWER_TOTAL(vict, x)); if (y < 1) return; } } void magic_loss(struct char_data *ch, int magic, bool msg) { if (GET_TRADITION(ch) == TRAD_MUNDANE) return; GET_REAL_MAG(ch) = MAX(0, GET_REAL_MAG(ch) - magic); if (GET_REAL_MAG(ch) < 100) { send_to_char(ch, "You feel the last of your magic leave your body.\r\n", ch); PLR_FLAGS(ch).RemoveBit(PLR_PERCEIVE); GET_TRADITION(ch) = TRAD_MUNDANE; for (int i = 0; i < NUM_WEARS; i++) if (GET_EQ(ch, i) && GET_OBJ_TYPE(GET_EQ(ch, i)) == ITEM_FOCUS && GET_OBJ_VAL(GET_EQ(ch, i), 2) == GET_IDNUM(ch)) GET_OBJ_VAL(GET_EQ(ch, i), 2) = GET_OBJ_VAL(GET_EQ(ch, i), 4) = GET_OBJ_VAL(GET_EQ(ch, i), 9) = 0; struct sustain_data *nextsust; for (struct sustain_data *sust = GET_SUSTAINED(ch); sust; sust = nextsust) { nextsust = sust->next; if (sust->caster) end_sustained_spell(ch, sust); } return; } if (msg) send_to_char(ch, "You feel some of your magic leave your body.\r\n", ch); if (GET_TRADITION(ch) == TRAD_ADEPT) { GET_PP(ch) -= magic; reduce_abilities(ch); } }