/* ************************************************************************ * file: handler.c , Handler module. Part of DIKUMUD * * Usage: Various routines for moving about objects/players * * Copyright (C) 1990, 1991 - see 'license.doc' for complete information. * ************************************************************************* */ #include <string.h> #include <stdio.h> #include <stdlib.h> #include <ctype.h> #include <assert.h> #include "structs.h" #include "utility.h" #include "interpreter.h" #include "comm.h" #include "db.h" #include "handler.h" #include "fight.h" #include "spells.h" #include "constants.h" #include "ansi.h" /* External procedures */ void remove_follower(struct char_data *ch); struct time_info_data age(struct char_data *ch); /* not really sure where to put this but good a place as any I suppose */ void do_sys(char *arg, int min_level) { struct descriptor_data *i; for (i = descriptor_list; i; i = i->next) if (!i->connected && !IS_SET(i->character->specials.act, PLR_NOSYS) && GET_LEVEL(i->character) >= min_level) ansi_act(arg, 0, i->character, 0, 0, TO_CHAR, CLR_SYS); } char *fname(char *namelist) { static char holder[30]; register char *point; if (!strncmp(namelist,"obj ",4) || !strncmp(namelist,"mob ",4)) namelist += 4; /* skip leading "obj " or "mob " */ for (point = holder; isalpha(*namelist); namelist++, point++) *point = *namelist; *point = '\0'; return(holder); } int isname(char *str, char *namelist) { register char *curname, *curstr; if (!str || !namelist) return 0; curname = namelist; for (;;){ for (curstr = str;; curstr++, curname++){ if (!*curstr) return(1); if (!*curname) return(0); if (!*curstr || *curname == ' ') break; if (LOWER(*curstr) != LOWER(*curname)) break; } /* skip to next name */ for (; isalpha(*curname); curname++); if (!*curname) return(0); curname++; /* first char of new name */ } } int isexactname(char *str, char *namelist) { register char *curname, *curstr; if (!str || !namelist) return 0; curname = namelist; for (;;){ for (curstr = str;; curstr++, curname++){ if (!*curstr && !isalpha(*curname)) return(1); if (!*curname) return(0); if (!*curstr || *curname == ' ') break; if (LOWER(*curstr) != LOWER(*curname)) break; } /* skip to next name */ for (; isalpha(*curname); curname++); if (!*curname) return(0); curname++; /* first char of new name */ } } void affect_modify(struct char_data *ch, signed char loc, signed char mod, long bitv, bool add) { struct affected_type af; char buf[256]; af.duration = -1; af.modifier = 0; af.location = APPLY_NONE; af.bitvector = 0; if (add) { if (bitv) SET_BIT(ch->specials.affected_by, bitv); } else { if (bitv) REMOVE_BIT(ch->specials.affected_by, bitv); mod = -mod; } switch(loc) { case APPLY_NONE: break; case APPLY_STR: case APPLY_GOD_STR: GET_STR(ch) +=mod; break; case APPLY_DEX: case APPLY_GOD_DEX: GET_DEX(ch) +=mod; break; case APPLY_INT: case APPLY_GOD_INT: GET_INT(ch) +=mod; break; case APPLY_WIS: case APPLY_GOD_WIS: GET_WIS(ch) +=mod; break; case APPLY_CON: case APPLY_GOD_CON: GET_CON(ch) +=mod; break; case APPLY_SEX: case APPLY_CLASS: case APPLY_LEVEL: break; case APPLY_AGE: ch->player.time.birth -= mod*SECS_PER_MUD_YEAR; if (age(ch).year < 1){ send_to_char("Oh Oh that item just de-aged you prior to birth... (you're now dead)\n\r", ch); death_cry(ch); extract_char(ch); } break; case APPLY_CHAR_WEIGHT: GET_WEIGHT(ch) +=mod; break; case APPLY_CHAR_HEIGHT: GET_HEIGHT(ch) +=mod; break; case APPLY_MANA: ch->points.max_mana += mod; break; case APPLY_HIT: ch->points.max_hit += mod; break; case APPLY_MOVE: ch->points.max_move += mod; break; case APPLY_GOLD: case APPLY_EXP: break; case APPLY_AC: GET_AC(ch) += mod; break; case APPLY_HITROLL: GET_HITROLL(ch) +=mod; break; case APPLY_DAMROLL: GET_DAMROLL(ch) +=mod; break; case APPLY_SAVING_PARA: ch->specials.apply_saving_throw[0] += mod; break; case APPLY_SAVING_ROD: ch->specials.apply_saving_throw[1] += mod; break; case APPLY_SAVING_PETRI: ch->specials.apply_saving_throw[2] += mod; break; case APPLY_SAVING_BREATH: ch->specials.apply_saving_throw[3] += mod; break; case APPLY_SAVING_SPELL: ch->specials.apply_saving_throw[4] += mod; break; case APPLY_DETECT_INVIS: if (add) { if (!affected_by_spell(ch,SPELL_DETECT_INVISIBLE)) { af.type = SPELL_DETECT_INVISIBLE; af.bitvector = AFF_DETECT_INVISIBLE; affect_to_char(ch, &af); } } else if(affected_by_spell(ch, SPELL_DETECT_INVISIBLE)) affect_from_char(ch, SPELL_DETECT_INVISIBLE); break; case APPLY_DARKSIGHT: if (add) { if (!affected_by_spell(ch,SPELL_DARKSIGHT)) { af.type = SPELL_DARKSIGHT; af.bitvector = AFF_DARKSIGHT; affect_to_char(ch, &af); } } else if(affected_by_spell(ch, SPELL_DARKSIGHT)) affect_from_char(ch, SPELL_DARKSIGHT); break; case APPLY_BREATHWATER: if (add) { if (!affected_by_spell(ch,SPELL_BREATHWATER)) { af.type = SPELL_BREATHWATER; af.bitvector = AFF_BREATHWATER; affect_to_char(ch, &af); } } else if(affected_by_spell(ch, SPELL_BREATHWATER)) affect_from_char(ch, SPELL_BREATHWATER); break; case APPLY_DETECT_MAGIC: if (add) { if (!affected_by_spell(ch,SPELL_DETECT_MAGIC)) { af.type = SPELL_DETECT_MAGIC; af.bitvector = AFF_DETECT_MAGIC; affect_to_char(ch, &af); } } else if(affected_by_spell(ch, SPELL_DETECT_MAGIC)) affect_from_char(ch, SPELL_DETECT_MAGIC); break; case APPLY_SENSE_LIFE: if (add) { if (!affected_by_spell(ch,SPELL_SENSE_LIFE)) { af.type = SPELL_SENSE_LIFE; af.bitvector = AFF_SENSE_LIFE; affect_to_char(ch, &af); } } else if(affected_by_spell(ch, SPELL_SENSE_LIFE)) affect_from_char(ch, SPELL_SENSE_LIFE); break; case APPLY_DETECT_EVIL: if (add) { if (!affected_by_spell(ch,SPELL_DETECT_EVIL)) { af.type = SPELL_DETECT_EVIL; af.bitvector = AFF_DETECT_EVIL; affect_to_char(ch, &af); } } else if(affected_by_spell(ch, SPELL_DETECT_EVIL)) affect_from_char(ch, SPELL_DETECT_EVIL); break; case APPLY_INVIS: if (add) { if (!affected_by_spell(ch,SPELL_INVISIBLE) && !affected_by_spell(ch,SPELL_IMPROVED_INVIS) ) { af.type = SPELL_INVISIBLE; af.bitvector = AFF_INVISIBLE; af.location = APPLY_AC; af.modifier = -40; affect_to_char(ch, &af); } } else if (affected_by_spell(ch,SPELL_INVISIBLE)) { affect_from_char(ch, SPELL_INVISIBLE); } break; case APPLY_IMPROVED_INVIS: if (add) { if (!affected_by_spell(ch,SPELL_INVISIBLE) && !affected_by_spell(ch,SPELL_IMPROVED_INVIS) ) { af.type = SPELL_IMPROVED_INVIS; af.bitvector = AFF_INVISIBLE; af.location = APPLY_AC; af.modifier = -40; affect_to_char(ch, &af); } } else if (affected_by_spell(ch,SPELL_IMPROVED_INVIS)) { affect_from_char(ch, SPELL_IMPROVED_INVIS); } break; case APPLY_REGENERATION: if (add) { if (!affected_by_spell(ch,SPELL_REGENERATION)) { af.type = SPELL_REGENERATION; af.bitvector = AFF_REGENERATION; affect_to_char(ch, &af); } } else if(affected_by_spell(ch, SPELL_REGENERATION)) affect_from_char(ch, SPELL_REGENERATION); break; default: sprintf(buf,"unknown apply (handler.c) %d",loc); log(buf); break; } /* switch */ } /* This updates a character by subtracting everything he is affected by */ /* restoring original abilities, and then affecting all again */ /* this is also a really stupid way to do things, especially when you consider wear all and the fact that it makes spells on items impossible, hence its mostly not used */ void affect_total(struct char_data *ch) { struct affected_type *af; int i,j; for(i=0; i<MAX_WEAR; i++) { if (ch->equipment[i]) for(j=0; j<MAX_OBJ_AFFECT; j++) affect_modify(ch, ch->equipment[i]->affected[j].location, ch->equipment[i]->affected[j].modifier, ch->equipment[i]->obj_flags.bitvector, FALSE); } for(af = ch->affected; af; af=af->next) affect_modify(ch, af->location, af->modifier, af->bitvector, FALSE); ch->tmpabilities = ch->abilities; for(i=0; i<MAX_WEAR; i++) { if (ch->equipment[i]) for(j=0; j<MAX_OBJ_AFFECT; j++) affect_modify(ch, ch->equipment[i]->affected[j].location, ch->equipment[i]->affected[j].modifier, ch->equipment[i]->obj_flags.bitvector, TRUE); } for(af = ch->affected; af; af=af->next) affect_modify(ch, af->location, af->modifier, af->bitvector, TRUE); /* Make certain values are between 0..25, not < 0 and not > 25! */ i = (IS_NPC(ch) ? 25 :18); i = MINV(i+GET_LEVEL(ch)/9,25); GET_DEX(ch) = MAXV(0,MINV(GET_DEX(ch), i)); GET_INT(ch) = MAXV(0,MINV(GET_INT(ch), i)); GET_WIS(ch) = MAXV(0,MINV(GET_WIS(ch), i)); GET_CON(ch) = MAXV(0,MINV(GET_CON(ch), i)); GET_STR(ch) = MAXV(0,MINV(GET_STR(ch), i)); } /* This affects all of a particular location type to a char */ void one_affect_total(struct char_data *ch, sbyte location) { struct affected_type *af; int i,j; /* since we are only going to change the the one ability dont bother to remove anything */ /* depends on the ability in question being reset prior to this being called */ for(i=0; i<MAX_WEAR; i++) { if (ch->equipment[i]) for(j=0; j<MAX_OBJ_AFFECT; j++) if (ch->equipment[i]->affected[j].location == location) affect_modify(ch, ch->equipment[i]->affected[j].location, ch->equipment[i]->affected[j].modifier, ch->equipment[i]->obj_flags.bitvector, TRUE); } for(af = ch->affected; af; af=af->next) if (af->location == location) affect_modify(ch, af->location, af->modifier, af->bitvector, TRUE); } /* This updates a character by subtracting everything he is affected by */ /* restoring original abilities, and then affecting all again */ /* except it will only affect the ONE! ability is absolutely has to */ /* called by affect_to_char, affect_from_char, equip_char & unequip_char */ /* make sure for equip_char & unequip char it gets inserted into the for */ /* loop after each call to affect_modify */ void smart_affect_total(struct char_data *ch, sbyte location) { int i; i = (IS_NPC(ch) ? 25 :18); i = MINV(i+GET_LEVEL(ch)/9,25); /* calc ability maxes */ if ((location == APPLY_DEX) || (location == APPLY_GOD_DEX)) { GET_DEX(ch) = ch->abilities.dex; /* reset dex to original*/ one_affect_total(ch, APPLY_DEX); GET_DEX(ch) = MAXV(0,MINV(GET_DEX(ch), i)); one_affect_total(ch, APPLY_GOD_DEX); GET_DEX(ch) = MAXV(0, MINV(GET_DEX(ch), 30)); } else if ((location == APPLY_INT) || (location == APPLY_GOD_INT)) { GET_INT(ch) = ch->abilities.intel; /* reset int to original*/ one_affect_total(ch, APPLY_INT); GET_INT(ch) = MAXV(0,MINV(GET_INT(ch), i)); one_affect_total(ch, APPLY_GOD_INT); GET_INT(ch) = MAXV(0,MINV(GET_INT(ch), 30)); } else if ((location == APPLY_WIS) || (location == APPLY_GOD_WIS)) { GET_WIS(ch) = ch->abilities.wis; /* reset wis to original*/ one_affect_total(ch, APPLY_WIS); GET_WIS(ch) = MAXV(0,MINV(GET_WIS(ch), i)); one_affect_total(ch, APPLY_GOD_WIS); GET_WIS(ch) = MAXV(0,MINV(GET_WIS(ch), 30)); } else if ((location == APPLY_CON) || (location == APPLY_GOD_CON)) { GET_CON(ch) = ch->abilities.con; /* reset con to original*/ one_affect_total(ch, APPLY_CON); GET_CON(ch) = MAXV(0,MINV(GET_CON(ch), i)); one_affect_total(ch, APPLY_GOD_CON); GET_CON(ch) = MAXV(0,MINV(GET_CON(ch), 30)); } else if ((location == APPLY_STR) || (location == APPLY_GOD_STR)) { GET_STR(ch) = ch->abilities.str; /* reset str to original*/ one_affect_total(ch, APPLY_STR); GET_STR(ch) = MAXV(0,MINV(GET_STR(ch), i)); one_affect_total(ch, APPLY_GOD_STR); GET_STR(ch) = MAXV(0,MINV(GET_STR(ch), 30)); } } /* Insert an affect_type in a char_data structure Automatically sets apropriate bits and apply's */ void affect_to_char( struct char_data *ch, struct affected_type *af ) { struct affected_type *affected_alloc; CREATE(affected_alloc, struct affected_type, 1); *affected_alloc = *af; affected_alloc->next = ch->affected; ch->affected = affected_alloc; affect_modify(ch, af->location, af->modifier, af->bitvector, TRUE); smart_affect_total(ch, af->location); } /* Remove an affected_type structure from a char (called when duration reaches zero). Pointer *af must never be NIL! Frees mem and calls affect_location_apply */ void affect_remove( struct char_data *ch, struct affected_type *af ) { struct affected_type *hjp; assert(ch->affected); affect_modify(ch, af->location, af->modifier, af->bitvector, FALSE); /* remove structure *af from linked list */ if (ch->affected == af) { /* remove head of list */ ch->affected = af->next; } else { for(hjp = ch->affected; (hjp->next) && (hjp->next != af); hjp = hjp->next); if (hjp->next != af) { log("FATAL : Could not locate affected_type in ch->affected. (handler.c, affect_remove)"); exit(1); } hjp->next = af->next; /* skip the af element */ } smart_affect_total(ch, af->location); free ( af ); } /* Call affect_remove with every spell of spelltype "skill" */ void affect_from_char( struct char_data *ch, signed char skill) { struct affected_type *hjp,*next; for(hjp = ch->affected; hjp; hjp = next) { next = hjp->next; /* Have to do this here cuz hjp might be free'd */ if (hjp->type == skill) affect_remove( ch, hjp ); } } /* Return if a char is affected by a spell (SPELL_XXX), NULL indicates not affected */ bool affected_by_spell( struct char_data *ch, signed char skill ) { struct affected_type *hjp; for (hjp = ch->affected; hjp; hjp = hjp->next) if ( hjp->type == skill ) return( TRUE ); return( FALSE ); } void affect_join( struct char_data *ch, struct affected_type *af, bool avg_dur, bool avg_mod ) { struct affected_type *hjp, *next; bool found = FALSE; for (hjp = ch->affected; !found && hjp; hjp = next) { next = hjp->next; if ( hjp->type == af->type ) { af->duration += hjp->duration; if (avg_dur) af->duration /= 2; af->modifier += hjp->modifier; if (avg_mod) af->modifier /= 2; affect_remove(ch, hjp); affect_to_char(ch, af); found = TRUE; } } if (!found) affect_to_char(ch, af); } /* move a player out of a room */ void char_from_room(struct char_data *ch) { struct char_data *i; if (ch->in_room == NOWHERE) { log("NOWHERE extracting char from room (handler.c, char_from_room)"); exit(1); } if (ch->equipment[WEAR_LIGHT]) if (ch->equipment[WEAR_LIGHT]->obj_flags.type_flag == ITEM_LIGHT) if (ch->equipment[WEAR_LIGHT]->obj_flags.value[2]) /* Light is ON */ world[ch->in_room].light--; if (ch == world[ch->in_room].people) /* head of list */ world[ch->in_room].people = ch->next_in_room; else /* locate the previous element */ { for (i = world[ch->in_room].people; i->next_in_room != ch; i = i->next_in_room); i->next_in_room = ch->next_in_room; } ch->in_room = NOWHERE; ch->next_in_room = 0; } /* place a character in a room */ void char_to_room(struct char_data *ch, int room) { void raw_kill(struct char_data *ch); ch->next_in_room = world[room].people; world[room].people = ch; ch->in_room = room; if (ch->equipment[WEAR_LIGHT]) if (ch->equipment[WEAR_LIGHT]->obj_flags.type_flag == ITEM_LIGHT) if (ch->equipment[WEAR_LIGHT]->obj_flags.value[2]) /* Light is ON */ world[room].light++; } /* give an object to a char */ void obj_to_char(struct obj_data *object, struct char_data *ch) { object->next_content = ch->carrying; ch->carrying = object; object->carried_by = ch; object->in_room = NOWHERE; IS_CARRYING_W(ch) += GET_OBJ_WEIGHT(object); IS_CARRYING_N(ch)++; sort_obj_list(ch->carrying); } /* take an object from a char */ void obj_from_char(struct obj_data *object) { struct obj_data *tmp; if (object->carried_by->carrying == object) /* head of list */ object->carried_by->carrying = object->next_content; else { for (tmp = object->carried_by->carrying; tmp && (tmp->next_content != object); tmp = tmp->next_content); /* locate previous */ tmp->next_content = object->next_content; } IS_CARRYING_W(object->carried_by) -= GET_OBJ_WEIGHT(object); IS_CARRYING_N(object->carried_by)--; object->carried_by = 0; object->next_content = 0; } /* Return the effect of a piece of armor in position eq_pos */ int apply_ac(struct char_data *ch, int eq_pos) { assert(ch->equipment[eq_pos]); if (!(GET_ITEM_TYPE(ch->equipment[eq_pos]) == ITEM_ARMOR)) return 0; switch (eq_pos) { case WEAR_BODY: return (3*ch->equipment[eq_pos]->obj_flags.value[0]); /* 30% */ case WEAR_HEAD: return (2*ch->equipment[eq_pos]->obj_flags.value[0]); /* 20% */ case WEAR_LEGS: return (2*ch->equipment[eq_pos]->obj_flags.value[0]); /* 20% */ /* case WEAR_FEET: return (ch->equipment[eq_pos]->obj_flags.value[0]); case WEAR_HANDS: return (ch->equipment[eq_pos]->obj_flags.value[0]); case WEAR_ARMS: return (ch->equipment[eq_pos]->obj_flags.value[0]); */ default: /* used to be shield */ return (ch->equipment[eq_pos]->obj_flags.value[0]); /* 10% */ } return 0; } void equip_char(struct char_data *ch, struct obj_data *obj, int pos) { int j; if(!(pos>=0 && pos<MAX_WEAR)) { log("EQUIP: loc out of range"); return; } if (ch->equipment[pos]) { log("EQUIP: Obj is equipped_by when equip."); return; } if (obj->carried_by) { log("EQUIP: Obj is carried_by when equip."); return; } if (obj->in_room!=NOWHERE) { log("EQUIP: Obj is in_room when equip."); return; } if (!IS_NPC(ch)) { if ((IS_OBJ_STAT(obj, ITEM_ANTI_EVIL) && IS_EVIL(ch)) || (IS_OBJ_STAT(obj, ITEM_ANTI_GOOD) && IS_GOOD(ch)) || (IS_OBJ_STAT(obj, ITEM_ANTI_NEUTRAL) && IS_NEUTRAL(ch))) { if (ch->in_room != NOWHERE) { act("You are zapped by $p and instantly drop it.", FALSE, ch, obj, 0, TO_CHAR); act("$n is zapped by $p and instantly drop it.", FALSE, ch, obj, 0, TO_ROOM); obj_to_room(obj, ch->in_room); return; } else { log("ch->in_room = NOWHERE when equipping char."); } } if ((IS_OBJ_STAT(obj, ITEM_DIFFICULT) && GET_LEVEL(ch) < 10) || (IS_OBJ_STAT(obj, ITEM_EXPERT) && GET_LEVEL(ch) < 20) || (IS_OBJ_STAT(obj, ITEM_HEROIC) && GET_LEVEL(ch) < 30) || (IS_OBJ_STAT(obj, ITEM_AVATAR) && GET_LEVEL(ch) < 41)) { if (ch->in_room != NOWHERE) { ansi_act("You are too inexperienced to use $p.", FALSE, ch, obj, 0, TO_CHAR,CLR_ERROR); ansi_act("$n realizes that $e is too inexperienced to use $p.", FALSE, ch, obj, 0, TO_ROOM,CLR_ACTION); obj_to_room(obj, ch->in_room); return; } else { log("ch->in_room = NOWHERE when equipping char."); } } if ((GET_LEVEL(ch)<IMO_LEV2)&&( ((IS_OBJ_STAT(obj, ITEM_ANTI_WARRIOR) && (GET_CLASS(ch)==CLASS_WARRIOR)) || (IS_OBJ_STAT(obj, ITEM_ANTI_DRAGON) && (GET_CLASS(ch)==CLASS_DRAGONW)) || (IS_OBJ_STAT(obj, ITEM_ANTI_MAGE) && (GET_CLASS(ch)==CLASS_MAGIC_USER)) || (IS_OBJ_STAT(obj, ITEM_ANTI_CLERIC) && (GET_CLASS(ch)==CLASS_CLERIC)) || (IS_OBJ_STAT(obj, ITEM_ANTI_THIEF) && (GET_CLASS(ch)==CLASS_THIEF)) || (IS_OBJ_STAT(obj, ITEM_ANTI_THIEF) && IS_OBJ_STAT(obj, ITEM_ANTI_MAGE) && (GET_CLASS(ch)==CLASS_BARD)) || (IS_OBJ_STAT(obj, ITEM_ANTI_WARRIOR) && IS_OBJ_STAT(obj, ITEM_ANTI_MAGE) && (GET_CLASS(ch)==CLASS_EKNIGHT)) || (IS_OBJ_STAT(obj, ITEM_ANTI_WARRIOR) && IS_OBJ_STAT(obj, ITEM_ANTI_MAGE) && (GET_CLASS(ch)==CLASS_DKNIGHT)) || (IS_OBJ_STAT(obj, ITEM_ANTI_WARRIOR) && IS_OBJ_STAT(obj, ITEM_ANTI_CLERIC) && (GET_CLASS(ch)==CLASS_KAI)) || (IS_OBJ_STAT(obj, ITEM_ANTI_WARRIOR) && IS_OBJ_STAT(obj, ITEM_ANTI_CLERIC) && (GET_CLASS(ch)==CLASS_DRAKKHAR)) || (IS_OBJ_STAT(obj, ITEM_ANTI_WARRIOR) && (GET_CLASS(ch)==CLASS_DRAGONW))) )){ if (ch->in_room != NOWHERE) { ansi_act("You are the wrong class to use that.", FALSE, ch, obj, 0, TO_CHAR,CLR_ERROR); ansi_act("$n realizes that $p isn't the sort of thing $e can use.", FALSE, ch, obj, 0, TO_ROOM,CLR_ERROR); obj_to_room(obj, ch->in_room); return; } else { log("ch->in_room = NOWHERE when equipping char."); } } } ch->equipment[pos] = obj; obj->carried_by = ch; if (GET_ITEM_TYPE(obj) == ITEM_ARMOR) GET_AC(ch) -= apply_ac(ch, pos); for(j=0; j<MAX_OBJ_AFFECT; j++) { affect_modify(ch, obj->affected[j].location, obj->affected[j].modifier, obj->obj_flags.bitvector, TRUE); smart_affect_total(ch, obj->affected[j].location); } } struct obj_data *unequip_char(struct char_data *ch, int pos) { int j; struct obj_data *obj; assert(pos>=0 && pos<MAX_WEAR); assert(ch->equipment[pos]); obj = ch->equipment[pos]; obj->carried_by = NULL; if (GET_ITEM_TYPE(obj) == ITEM_ARMOR) GET_AC(ch) += apply_ac(ch, pos); ch->equipment[pos] = 0; for(j=0; j<MAX_OBJ_AFFECT; j++) { affect_modify(ch, obj->affected[j].location, obj->affected[j].modifier, obj->obj_flags.bitvector, FALSE); smart_affect_total(ch, obj->affected[j].location); } return(obj); } int get_number(char **name) { int i; char *ppos; char number[MAX_INPUT_LENGTH] = ""; if ( (ppos = (char *) index(*name, '.')) ){ *ppos++ = '\0'; strcpy(number,*name); strcpy(*name, ppos); for(i=0; *(number+i); i++) if (!isdigit(*(number+i))) return(0); return(atoi(number)); } return(1); } /* Search a given list for an object, and return a pointer to that object */ struct obj_data *get_obj_in_list(char *name, struct obj_data *list) { struct obj_data *i; int j, number; char tmpname[MAX_INPUT_LENGTH]; char *tmp; strcpy(tmpname,name); tmp = tmpname; if(!(number = get_number(&tmp))) return(0); for (i = list, j = 1; i && (j <= number); i = i->next_content) if (isname(tmp, i->name)) { if (j == number) return(i); j++; } return(0); } struct obj_data *get_obj_in_list_num(int num, struct obj_data *list) { struct obj_data *i; for (i = list; i; i = i->next_content) if (i->item_number == num) return(i); return(0); } /* Search a given list for an object number, and return a ptr to that obj */ /* changed from stock code to search for virtual numbers not real */ struct obj_data *get_obj_in_list_virtual(int num, struct obj_data *list) { struct obj_data *i; for (i = list; i; i = i->next_content) if (obj_index[i->item_number].virtual == num) return(i); return(0); } /*search the entire world for an object, and return a pointer */ struct obj_data *get_obj(char *name) { struct obj_data *i; int j, number; char tmpname[MAX_INPUT_LENGTH]; char *tmp; strcpy(tmpname,name); tmp = tmpname; if(!(number = get_number(&tmp))) return(0); for (i = object_list, j = 1; i && (j <= number); i = i->next) if (isname(tmp, i->name)) { if (j == number) return(i); j++; } return(0); } /*search the entire world for an object number, and return a pointer */ struct obj_data *get_obj_num(int nr) { struct obj_data *i; for (i = object_list; i; i = i->next) if (i->item_number == nr) return(i); return(0); } /* search a room for a char, and return a pointer if found.. */ struct char_data *get_char_room(char *name, int room) { struct char_data *i; int j, number; char tmpname[MAX_INPUT_LENGTH]; char *tmp; strcpy(tmpname,name); tmp = tmpname; if(!(number = get_number(&tmp))) return(0); for (i = world[room].people, j = 1; i && (j <= number); i = i->next_in_room) if (isname(tmp, GET_NAME(i))) { if (j == number) return(i); j++; } return(0); } /* search all over the world for a char, and return a pointer if found */ struct char_data *get_char(char *name) { struct char_data *i; int j, number; char tmpname[MAX_INPUT_LENGTH]; char *tmp; strcpy(tmpname,name); tmp = tmpname; if(!(number = get_number(&tmp))) return(0); for (i = character_list, j = 1; i && (j <= number); i = i->next) if (isname(tmp, GET_NAME(i))) { if (j == number) return(i); j++; } return(0); } /* search all over the world for a char num, and return a pointer if found */ struct char_data *get_char_num(int nr) { struct char_data *i; for (i = character_list; i; i = i->next) if (i->nr == nr) return(i); return(0); } /* finds objects the same as the one at the front of the list and moves them to the front too */ void sort_obj_list(struct obj_data *object) { struct obj_data *list_obj, *middle, *old_next; if (!object || GET_ITEM_TYPE(object)==ITEM_ZCMD || !object->next_content) { return; } else { /* search for chain of like objects to group with start */ list_obj=object->next_content; /* start at next entry in list */ while( (list_obj->next_content) &&( (list_obj->next_content->item_number != object->item_number) ||(GET_ITEM_TYPE(list_obj->next_content) != GET_ITEM_TYPE(object)) ||(list_obj->next_content->obj_flags.extra_flags != object->obj_flags.extra_flags) ) ) list_obj = list_obj->next_content; if (!list_obj->next_content) return; /* return if nothing found */ /* found something I guess move it after top of list */ middle = list_obj; /* this one is going to be in the middle */ old_next = object->next_content; object->next_content = list_obj->next_content; while( (list_obj->next_content) &&( list_obj->next_content->item_number == object->item_number ) &&( GET_ITEM_TYPE(list_obj->next_content) == GET_ITEM_TYPE(object) ) &&( list_obj->next_content->obj_flags.extra_flags == object->obj_flags.extra_flags ) ) list_obj = list_obj->next_content; middle->next_content = list_obj->next_content; list_obj->next_content = old_next; } } /* put an object in a room, zone commmands should be first */ /* l8r will sort like items together somehow */ void obj_to_room(struct obj_data *object, int room) { struct obj_data *room_obj; room_obj = world[room].contents; if ((!room_obj) || (room_obj->obj_flags.type_flag != ITEM_ZCMD)) { object->next_content = world[room].contents; world[room].contents = object; } else { while( (room_obj->next_content) &&(room_obj->next_content->obj_flags.type_flag == ITEM_ZCMD) ) room_obj = room_obj->next_content; object->next_content = room_obj->next_content; room_obj->next_content = object; } /* internal obj data update stuff */ object->in_room = room; object->carried_by = 0; /* group like objects together */ sort_obj_list(world[room].contents); } /* Take an object from a room */ void obj_from_room(struct obj_data *object) { struct obj_data *i; /* remove object from room */ if (object == world[object->in_room].contents) /* head of list */ world[object->in_room].contents = object->next_content; else /* locate previous element in list */ { for (i = world[object->in_room].contents; i && (i->next_content != object); i = i->next_content); i->next_content = object->next_content; } object->in_room = NOWHERE; object->next_content = 0; } /* put an object in an object (quaint) */ void obj_to_obj(struct obj_data *obj, struct obj_data *obj_to) { struct obj_data *tmp_obj; if (!obj || !obj_to) { log("obj_to_obj: NULL object pointer"); return; } obj->next_content = obj_to->contains; obj_to->contains = obj; obj->in_obj = obj_to; for(tmp_obj = obj->in_obj; tmp_obj; GET_OBJ_WEIGHT(tmp_obj) += GET_OBJ_WEIGHT(obj), tmp_obj = tmp_obj->in_obj); sort_obj_list(obj_to->contains); } /* remove an object from an object */ void obj_from_obj(struct obj_data *obj) { struct obj_data *tmp, *obj_from; if (obj->in_obj) { obj_from = obj->in_obj; if (obj == obj_from->contains) /* head of list */ obj_from->contains = obj->next_content; else { for (tmp = obj_from->contains; tmp && (tmp->next_content != obj); tmp = tmp->next_content); /* locate previous */ if (!tmp) { perror("Fatal error in object structures."); abort(); } tmp->next_content = obj->next_content; } /* Subtract weight from containers container */ for(tmp = obj->in_obj; tmp->in_obj; tmp = tmp->in_obj) GET_OBJ_WEIGHT(tmp) -= GET_OBJ_WEIGHT(obj); GET_OBJ_WEIGHT(tmp) -= GET_OBJ_WEIGHT(obj); /* Subtract weight from char that carries the object */ if (tmp->carried_by) IS_CARRYING_W(tmp->carried_by) -= GET_OBJ_WEIGHT(obj); obj->in_obj = 0; obj->next_content = 0; } else { perror("Trying to object from object when in no object."); abort(); } } /* Set all carried_by to point to new owner */ void object_list_new_owner(struct obj_data *list, struct char_data *ch) { if (list) { object_list_new_owner(list->contains, ch); object_list_new_owner(list->next_content, ch); list->carried_by = ch; } } /* Extract an object from the world */ void extract_obj(struct obj_data *obj) { struct obj_data *temp1, *temp2; if(obj->in_room != NOWHERE) obj_from_room(obj); else if(obj->carried_by) obj_from_char(obj); else if(obj->in_obj) { temp1 = obj->in_obj; if(temp1->contains == obj) /* head of list */ temp1->contains = obj->next_content; else { for( temp2 = temp1->contains ; temp2 && (temp2->next_content != obj); temp2 = temp2->next_content ); if(temp2) { temp2->next_content = obj->next_content; } } } for( ; obj->contains; extract_obj(obj->contains)); /* leaves nothing ! */ if (object_list == obj ) /* head of list */ object_list = obj->next; else { for(temp1 = object_list; temp1 && (temp1->next != obj); temp1 = temp1->next); if(temp1) temp1->next = obj->next; } if(obj->item_number>=0) (obj_index[obj->item_number].number)--; free_obj(obj); } void update_object( struct obj_data *obj, int use){ if (obj->obj_flags.timer > 0) obj->obj_flags.timer -= use; if (obj->contains) update_object(obj->contains, use); if (obj->next_content) update_object(obj->next_content, use); } void update_char_objects( struct char_data *ch ) { int i; if (ch->equipment[WEAR_LIGHT]) if (ch->equipment[WEAR_LIGHT]->obj_flags.type_flag == ITEM_LIGHT) if (ch->equipment[WEAR_LIGHT]->obj_flags.value[2] > 0) (ch->equipment[WEAR_LIGHT]->obj_flags.value[2])--; for(i = 0;i < MAX_WEAR;i++) if (ch->equipment[i]) update_object(ch->equipment[i],2); if (ch->carrying) update_object(ch->carrying,1); } /* Extract a ch completely from the world, and leave his stuff behind */ void extract_char(struct char_data *ch) { struct obj_data *i; struct char_data *k, *next_char; struct descriptor_data *t_desc; int l, was_in; extern struct char_data *combat_list; void do_save(struct char_data *ch, char *argument, int cmd); void do_return(struct char_data *ch, char *argument, int cmd); void die_follower(struct char_data *ch); if(!IS_NPC(ch) && !ch->desc) { for(t_desc = descriptor_list; t_desc; t_desc = t_desc->next) if(t_desc->original==ch) do_return(t_desc->character, "", 0); } if (ch->in_room == NOWHERE) { log("NOWHERE extracting char. (handler.c, extract_char)"); exit(1); } if (ch->followers || ch->master) die_follower(ch); if(ch->desc) { /* Forget snooping */ if (ch->desc->snoop.snooping) ch->desc->snoop.snooping->desc->snoop.snoop_by = 0; if (ch->desc->snoop.snoop_by) { send_to_char("Your victim is no longer among us.\n\r", ch->desc->snoop.snoop_by); ch->desc->snoop.snoop_by->desc->snoop.snooping = 0; } ch->desc->snoop.snooping = ch->desc->snoop.snoop_by = 0; } if (ch->carrying) { /* transfer ch's objects to room */ if (world[ch->in_room].contents) /* room nonempty */ { /* locate tail of room-contents */ for (i = world[ch->in_room].contents; i->next_content; i = i->next_content); /* append ch's stuff to room-contents */ i->next_content = ch->carrying; } else world[ch->in_room].contents = ch->carrying; /* connect the stuff to the room */ for (i = ch->carrying; i; i = i->next_content) { i->carried_by = 0; i->in_room = ch->in_room; } } if (ch->specials.fighting) stop_fighting(ch); for (k = combat_list; k ; k = next_char) { next_char = k->next_fighting; if (k->specials.fighting == ch) stop_fighting(k); } /* Must remove from room before removing the equipment! */ was_in = ch->in_room; char_from_room(ch); /* clear equipment_list */ for (l = 0; l < MAX_WEAR; l++) if (ch->equipment[l]) obj_to_room(unequip_char(ch,l), was_in); /* pull the char from the list */ if (ch == character_list) character_list = ch->next; else { for(k = character_list; (k) && (k->next != ch); k = k->next); if(k) k->next = ch->next; else { log("Trying to remove ?? from character_list. (handler.c, extract_char)"); abort(); } } GET_AC(ch) = MAX_ARMOUR; if (ch->desc) { if (ch->desc->original) do_return(ch, "", 0); save_char(ch, NOWHERE); } if (IS_NPC(ch)) { if (ch->nr > -1) /* if mobile */ mob_index[ch->nr].number--; free_char(ch); } if (ch->desc) { ch->desc->connected = CON_SLCT; SEND_TO_Q(MENU, ch->desc); } } /* *********************************************************************** Here follows high-level versions of some earlier routines, ie functions which incorporate the actual player-data. *********************************************************************** */ struct char_data *get_char_room_vis(struct char_data *ch, char *name) { struct char_data *i; int j, number; char tmpname[MAX_INPUT_LENGTH]; char *tmp; strcpy(tmpname,name); tmp = tmpname; if(!(number = get_number(&tmp))) return(0); for (i = world[ch->in_room].people, j = 1; i && (j <= number); i = i->next_in_room) if (isname(tmp, GET_NAME(i))) if (CAN_SEE(ch, i)) { if (j == number) return(i); j++; } return(0); } struct char_data *get_char_vis(struct char_data *ch, char *name) { struct char_data *i; int j, number; char tmpname[MAX_INPUT_LENGTH]; char *tmp; /* check location */ if ( (i = get_char_room_vis(ch, name)) ) return(i); strcpy(tmpname,name); tmp = tmpname; if(!(number = get_number(&tmp))) return(0); for (i = character_list, j = 1; i && (j <= number); i = i->next) if (isname(tmp, GET_NAME(i))) if (CAN_SEE(ch, i)) { if (j == number) return(i); j++; } return(0); } struct obj_data *get_obj_in_list_vis(struct char_data *ch, char *name, struct obj_data *list) { struct obj_data *i; int j, number; char tmpname[MAX_INPUT_LENGTH]; char *tmp; strcpy(tmpname,name); tmp = tmpname; if(!(number = get_number(&tmp))) return(0); for (i = list, j = 1; i && (j <= number); i = i->next_content) if (isname(tmp, i->name)) if (CAN_SEE_OBJ(ch, i)) { if (j == number) return(i); j++; } return(0); } /*search the entire world for an object, and return a pointer */ struct obj_data *get_obj_vis(struct char_data *ch, char *name) { struct obj_data *i; int j, number; char tmpname[MAX_INPUT_LENGTH]; char *tmp; /* scan items carried */ if ( (i = get_obj_in_list_vis(ch, name, ch->carrying)) ) return(i); /* scan room */ if ( (i = get_obj_in_list_vis(ch, name, world[ch->in_room].contents)) ) return(i); strcpy(tmpname,name); tmp = tmpname; if(!(number = get_number(&tmp))) return(0); /* ok.. no luck yet. scan the entire obj list */ for (i = object_list, j = 1; i && (j <= number); i = i->next) if (isname(tmp, i->name)) if (CAN_SEE_OBJ(ch, i)) { if (j == number) return(i); j++; } return(0); } struct obj_data *create_money( int amount ) { struct obj_data *obj; struct extra_descr_data *new_descr; char buf[80]; if(amount<=0) { log("ERROR: Try to create negative money."); exit(1); } CREATE(obj, struct obj_data, 1); CREATE(new_descr, struct extra_descr_data, 1); clear_object(obj); if(amount==1) { obj->name = str_alloc("coin gold"); obj->short_description = str_alloc("a gold coin"); obj->description = str_alloc("One miserable gold coin."); new_descr->keyword = str_alloc("coin gold"); new_descr->description = str_alloc("One miserable gold coin."); } else { obj->name = str_alloc("coins gold"); obj->short_description = str_alloc("gold coins"); obj->description = str_alloc("A pile of gold coins."); new_descr->keyword = str_alloc("coins gold"); if(amount<10) { sprintf(buf,"There is %d coins.",amount); new_descr->description = str_alloc(buf); } else if (amount<100) { sprintf(buf,"There is about %d coins",10*(amount/10)); new_descr->description = str_alloc(buf); } else if (amount<1000) { sprintf(buf,"It looks like something round %d coins",100*(amount/100)); new_descr->description = str_alloc(buf); } else if (amount<100000) { sprintf(buf,"You guess there is %d coins",1000*((amount/1000)+ number(0,(amount/1000)))); new_descr->description = str_alloc(buf); } else new_descr->description = str_alloc("There is A LOT of coins"); } new_descr->next = 0; obj->ex_description = new_descr; obj->obj_flags.type_flag = ITEM_MONEY; obj->obj_flags.wear_flags = ITEM_TAKE; obj->obj_flags.value[0] = amount; obj->obj_flags.cost = amount; obj->item_number = -1; obj->next = object_list; object_list = obj; return(obj); } /* Generic Find, designed to find any object/character */ /* Calling : */ /* *arg is the sting containing the string to be searched for. */ /* This string doesn't have to be a single word, the routine */ /* extracts the next word itself. */ /* bitv.. All those bits that you want to "search through". */ /* Bit found will be result of the function */ /* *ch This is the person that is trying to "find" */ /* **tar_ch Will be NULL if no character was found, otherwise points */ /* **tar_obj Will be NULL if no object was found, otherwise points */ /* */ /* The routine returns a pointer to the next word in *arg (just like the */ /* one_argument routine). */ int generic_find(char *arg, int bitvector, struct char_data *ch, struct char_data **tar_ch, struct obj_data **tar_obj) { static const char *ignore[] = { "the", "in", "on", "at", "\n" }; int i; char name[256]; bool found; found = FALSE; /* Eliminate spaces and "ignore" words */ while (*arg && !found) { for(; *arg == ' '; arg++) ; for(i=0; (name[i] = *(arg+i)) && (name[i]!=' '); i++) ; name[i] = 0; arg+=i; if (search_block(name, ignore, TRUE) > -1) found = TRUE; } if (!name[0]) return(0); *tar_ch = 0; *tar_obj = 0; if (IS_SET(bitvector, FIND_CHAR_ROOM)) { /* Find person in room */ if ( (*tar_ch = get_char_room_vis(ch, name)) ){ return(FIND_CHAR_ROOM); } } if (IS_SET(bitvector, FIND_CHAR_WORLD)) { if ( (*tar_ch = get_char_vis(ch, name)) ){ return(FIND_CHAR_WORLD); } } if (IS_SET(bitvector, FIND_OBJ_EQUIP)) { for(found=FALSE, i=0; i<MAX_WEAR && !found; i++) /* used to be str_cmp */ if (ch->equipment[i] && isname(name, ch->equipment[i]->name)) { *tar_obj = ch->equipment[i]; found = TRUE; } if (found) { return(FIND_OBJ_EQUIP); } } if (IS_SET(bitvector, FIND_OBJ_INV)) { if ( (*tar_obj = get_obj_in_list_vis(ch, name, ch->carrying)) ){ return(FIND_OBJ_INV); } } if (IS_SET(bitvector, FIND_OBJ_ROOM)) { if ( (*tar_obj = get_obj_in_list_vis(ch, name, world[ch->in_room].contents)) ){ return(FIND_OBJ_ROOM); } } if (IS_SET(bitvector, FIND_OBJ_WORLD)) { if ( (*tar_obj = get_obj_vis(ch, name)) ){ return(FIND_OBJ_WORLD); } } return(0); } void extract_zone_objs(struct obj_data *obj) { if (obj) { extract_zone_objs(obj->contains); extract_zone_objs(obj->next_content); if (obj->item_number >= 0 && obj->item_number <= top_of_objt) if (IS_SET(zone_table[obj_index[obj->item_number].zone].flags, ZONE_TESTING)) { obj_from_char(obj); extract_obj(obj); } } } void char_del_zone_objs(struct char_data *ch) { int i; struct obj_data *obj; send_to_char("Removing objects still being tested.\n\r", ch); for(i=0; i<MAX_WEAR; i++) if ( (obj=ch->equipment[i]) ) { if (obj->item_number >= 0 && obj->item_number <= top_of_objt) if (IS_SET(zone_table[obj_index[obj->item_number].zone].flags, ZONE_TESTING)) obj_to_char(unequip_char(ch, i), ch); } if (ch->carrying) extract_zone_objs(ch->carrying); } /* 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; /* assert(ch->master); */ if (!ch->master) return; if (!ch->master->followers) return; if (IS_AFFECTED(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_PERSON)) affect_from_char(ch, SPELL_CHARM_PERSON); } else { act("You stop following $N.", FALSE, ch, 0, ch->master, TO_CHAR); act("$n stops following $N.", FALSE, ch, 0, ch->master, TO_NOTVICT); act("$n stops following you.", FALSE, 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 = 0; REMOVE_BIT(ch->specials.affected_by, AFF_CHARM | AFF_GROUP); } /* Called when a character that follows/is followed dies */ void die_follower(struct char_data *ch) { struct follow_type *next, *k, *j, *new; struct char_data *leader; if (ch->master) {/* leader didnt die */ stop_follower(ch); return; } /* next person steps up to bat */ new = ch->followers; if (!(new)) return; /* no leader, no followers */ if (new->next) { /* make new the leader */ leader = new->follower; leader->master = 0; /* we're the leader ! */ send_to_char("Gratz! you just became the new leader of the group!\n\r", leader); for (k = new->next; k; k=next) { next = k->next; send_to_char(GET_NAME(leader), k->follower); send_to_char(" is now the new leader of the group!\n\r", k->follower); k->follower->master = leader; CREATE(j, struct follow_type, 1); j->follower = k->follower; j->next = leader->followers; leader->followers = j; free(k); } free(new); } else { /* down to last person in group */ stop_follower(new->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) { int num_mobs = 0; int num_chars = 0; struct follow_type *k; if(ch->master)return; for (k=leader->followers; k; k=k->next) if ((k->follower) && IS_NPC(k->follower)) num_mobs++; else num_chars++; if (num_mobs + num_chars >= 10) { send_to_char("You can only be followed by a maximum of 10 people\n\r", leader); send_to_char("That person has too many followers allready\n\r", ch); return; } if ((IS_NPC(ch)) && (num_mobs > GET_LEVEL(leader)/10) ) { send_to_char("You allready being followed by the maximum number of mobs\n\r", leader); send_to_char("That person has too many mobs following allready\n\r", ch); 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); act("$n starts following you.", TRUE, ch, 0, leader, TO_VICT); act("$n now follows $N.", TRUE, ch, 0, leader, TO_NOTVICT); }