/* ************************************************************************ * File: mobact.c Part of CircleMUD * * Usage: Functions for generating intelligent (?) behavior in mobiles * * * * All rights reserved. See license.doc for complete information. * * * * Copyright (C) 1993 by the Trustees of the Johns Hopkins University * * CircleMUD is based on DikuMUD, Copyright (C) 1990, 1991. * ************************************************************************ */ /* Archipelago changes by Alastair J. Neil Copyright (C) 1993, 94, 95, 96 */ #include <stdio.h> #include <stdlib.h> #include "structs.h" #include "utils.h" #include "db.h" #include "comm.h" #include "interpreter.h" #include "handler.h" #include "spells.h" /* external structs */ extern struct list_index_type mob_beneficial[]; extern struct list_index_type combat_self[]; extern struct list_index_type combat_other[]; extern int top_of_world; extern struct char_data *character_list; extern struct index_data *mob_index; extern struct room_data *world; extern struct str_app_type str_app[]; extern struct spell_info_type spell_info[]; char *first_name(char *buf); ACMD(do_move); ACMD(do_get); ACMD(do_wear); ACMD(do_gen_com); ACMD(do_open); ACMD(do_close); ACMD(do_flee); ACMD(do_leave); ACMD(do_enter); void hunt_victim(struct char_data *ch); void remember_id(struct char_data *ch, long id_num); bool hates(struct char_data *ch, struct char_data *vict); bool hates_id(struct char_data *ch, long id_num); int can_see_hidden(struct char_data *sub, struct char_data *obj); void reimund_story(struct char_data *ch); void add_event(int plse, int event, int inf1, int inf2, int inf3 , int inf4, char *arg, void *subj, void *vict); void mob_cast(struct char_data *mob); void mobile_activity(void) { SPECIAL(citizen); SPECIAL(cityguard); SPECIAL(moods); ACMD(do_stand); register struct char_data *ch; struct char_data *tmp_ch, *vict, *tch; struct obj_data *obj,*i, *best_obj; int door, found, max; bool present = TRUE,found_door, ctzn = FALSE; char buffer[100]; memory_rec * names; struct affected_type *affect=0; extern int no_specials; for (ch = character_list; ch; ch = ch->next) if (IS_MOB(ch)) { if (MOB_FLAGGED(ch,MOB_SPELL_CASTER)) mob_cast(ch); if (CAN_SPEAK(ch) && is_afflicted(ch) && (GET_ALIGNMENT(ch) > 500) && !number(0,1)) do_bless(ch, "",0,SCMD_PRAY); if ((GET_POS(ch) < POSITION_STANDING) && (GET_POS(ch) > POSITION_SLEEPING) && (ch->specials.default_pos == POSITION_STANDING)) do_stand(ch, "",0,0); if ((affect = affected_by_spell(ch, SPELL_ENCASE_IN_ICE)) && ((number(0,31) + 2*(affect->duration)) < GET_STR(ch))){ act("$n shatters $s casing of ice.",TRUE,ch,0,0, TO_ROOM); act("You shatter your casing of ice.",TRUE,ch,0,0, TO_CHAR); affect_from_char(ch, SPELL_ENCASE_IN_ICE); } if ((affect = affected_by_spell(ch, SPELL_WEB)) && ((number(0,31) + 2*(affect->duration)) < GET_STR(ch))){ act("$n rips the webs binding $m.",TRUE,ch,0,0, TO_ROOM); act("You tear the webs holding you to shreds.",TRUE,ch,0,0, TO_CHAR); affect_from_char(ch, SPELL_WEB); } if (IS_AFFECTED(ch, AFF_PARALYSIS)) continue; if (isname("reimund",ch->player.name) && !number(0,5)){ reimund_story(ch); } if (MOB_FLAGGED(ch,MOB_CITIZEN) && !number(0,65)) citizen(ch, ch, SPEC_MOBACT, ""); else if (MOB_FLAGGED(ch,MOB_MOODS) && (number(0,3000) < abs(GET_MOOD(ch)) || !number(0,160))) moods(ch, ch, SPEC_MOBACT, ""); /* Examine call for special procedure */ if (IS_SET(ch->specials2.act, MOB_SPEC) && !no_specials) { if (!mob_index[ch->nr].func) { sprintf(buf, "%s (#%d): Attempting to call non-existing mob func", GET_NAME(ch), (int)mob_index[ch->nr].virtual); logg(buf); REMOVE_BIT(ch->specials2.act, MOB_SPEC); } else { if ((*mob_index[ch->nr].func)(ch, ch, SPEC_MOBACT, "")) continue; /* go to next char */ } } if (isname("zoog",ch->player.name)){ if (!number(0,20)) act("You see a strange pair of eyes looking at you." ,FALSE,ch,0,0,TO_ROOM); else if (!number(0,40)) act("$n makes a low fluttering noise." ,TRUE,ch,0,0,TO_ROOM); } if (ch->specials.hunting) if (AWAKE(ch) && !ch->specials.fighting && !number(0,4)) { hunt_victim(ch); } ctzn = FALSE; if (IS_ANIMAL(ch) && !number(0,20)){ for (tch = world[ch->in_room].people; tch; tch = tch->next_in_room) if (!number(0,3)) break; if (tch) sprintf(buffer,"%s", GET_NAME(tch)); else *buffer = '\0'; if (isname("dog",ch->player.name) || isname("puppy",ch->player.name)){ switch(number(0,3)){ case 0: do_action(ch,buffer,196,0); break; case 1: do_action(ch,buffer,352,0); break; case 2: do_action(ch,buffer,265,0); break; default: break; } if (isname("mad", ch->player.name)){ switch(number(0,1)){ case 0: do_action(ch,buffer,391,0); break; case 1: do_action(ch,buffer,390,0); break; } } } else if(isname("cat",ch->player.name) || isname("kitten",ch->player.name)){ switch(number(0,2)){ case 0: do_action(ch,buffer,411,0); break; case 1: do_action(ch,buffer,125,0); break; default: break; } } else if(isname("snake",ch->player.name)){ switch(number(0,2)){ case 0: act("$n hisses.",FALSE,ch,0,0,TO_ROOM); break; case 1: act("$n slithers.",FALSE,ch,0,0,TO_ROOM); break; default: break; } } else if(isname("cow",ch->player.name) || isname("ox",ch->player.name) && !number(0,2)) do_action(ch,buffer,412,0); else if(isname("squirrel",ch->player.name) && !number(0,2)) do_action(ch,buffer,370,0); } if (ch->specials.fighting) { if (CAN_SPEAK(ch) && is_afflicted(ch) && (GET_ALIGNMENT(ch) > 350) && !number(0,2)) do_bless(ch, "",0,SCMD_PRAY); if (MOB_FLAGGED(ch,MOB_CITIZEN) && !number(0,6) && ch->specials.fighting->specials.fighting == ch) { ctzn = TRUE; act("$n yells 'HELP!! MURDER!! Someone help me!'",TRUE,ch,0,0,TO_ROOM); sprintf(buffer,"Help, I'm being attacked by %s!",GET_NAME(ch->specials.fighting)); do_gen_com(ch,buffer,0,SCMD_SHOUT); for (tch = character_list;tch;tch= tch->next) { if ((world[tch->in_room].zone == world[ch->in_room].zone) && (mob_index[tch->nr].func == cityguard) && !number(0,10)) { tch->specials.hunting = ch; hunt_victim(tch); } } } for (tch = world[ch->in_room].people; tch; tch = tch->next_in_room) if (IS_NPC(tch) && AWAKE(tch) && (tch != ch) && abs(GET_ALIGNMENT(tch) - GET_ALIGNMENT(ch)) < 200 && !number(0,4) && !(tch->specials.fighting) && ((ctzn && MOB_FLAGGED(tch, MOB_CITIZEN)) || (GET_INT(tch) > 10 || MOB_FLAGGED(tch,MOB_HELPER)))) { act("$n enters the fray!",TRUE,tch,0,0,TO_ROOM); if (ch->specials.fighting) { if (ch->specials.fighting == tch->master) hit(tch,ch,TYPE_UNDEFINED); else hit(tch,ch->specials.fighting,TYPE_UNDEFINED); break; } continue; } } if (AWAKE(ch) && !(ch->specials.fighting)) { if (IS_SET(ch->specials2.act, MOB_WILL_LOOT)){ for (i=world[ch->in_room].contents;i;i = i->next_content){ if (GET_ITEM_TYPE(i) == ITEM_CONTAINER && i->obj_flags.value[3] < 0) add_event(number(200,299),EVENT_LOOT,0,0,0,0,0,ch,i); continue; } } if (IS_SET(ch->specials2.act, MOB_SCAVENGER)) { /* if scavenger */ if (world[ch->in_room].contents && !number(0, 5)) { for (max = 1, best_obj = 0, obj = world[ch->in_room].contents; obj; obj = obj->next_content) { if (CAN_GET_OBJ(ch, obj)) { if (obj->obj_flags.cost > max) { best_obj = obj; max = obj->obj_flags.cost; } } } /* for */ if (best_obj && (CAN_CARRY_W(ch)>(IS_CARRYING_W(ch)+GET_OBJ_WEIGHT(best_obj))) && (IS_CARRYING_N(ch) < CAN_CARRY_N(ch))) { obj_from_room(best_obj); obj_to_char(best_obj, ch,0); act("$n gets $p.", FALSE, ch, best_obj, 0, TO_ROOM); } } }/* Scavenger */ if (!IS_SET(ch->specials2.act, MOB_SENTINEL) && (!IS_AFFECTED(ch, AFF_PARALYSIS)) && (GET_POS(ch) > POSITION_SITTING) && (GET_MOVE(ch) > 25) && (!ch->specials.fighting) && (!ch->master)){ if (((door = number(0, 60)) < NUM_OF_DIRS) && CAN_GO(ch,door) && !IS_SET(world[real_room(EXIT(ch, door)->to_room)].room_flags, NO_MOB) && !IS_SET(world[real_room(EXIT(ch, door)->to_room)].room_flags, DEATH)) { if (ch->specials.last_direction == door) ch->specials.last_direction = -1; else if ((IS_SET(ch->specials2.act, MOB_STAY_ZONE) && (world[real_room(EXIT(ch, door)->to_room)].zone == world[ch->in_room].zone)) || (IS_SET(ch->specials2.act, MOB_STAY_SECTOR) && (world[real_room(EXIT(ch, door)->to_room)].sector_type == world[ch->in_room].sector_type))) { ch->specials.last_direction = door; do_move(ch, "", ++door, 0); } else { if (!IS_SET(ch->specials2.act, MOB_STAY_ZONE) && !IS_SET(ch->specials2.act,MOB_STAY_SECTOR)){ ch->specials.last_direction = door; do_move(ch, "", ++door, 0); } } } else if (world[ch->in_room].obj && !IS_SET(world[ch->in_room].obj->obj_flags.value[1] , CONT_ONEWAY) && !IS_SET(world[ch->in_room].obj->obj_flags.value[1] , CONT_CLOSED) && !number(0,20)) do_leave(ch,"",0,0); else { for (obj = world[ch->in_room].contents ;obj;obj = obj->next_content){ if (obj && (obj->description && *obj->description != '\0') && HASROOM(obj) && !number(0,20) &&(!IS_SET(obj->obj_flags.value[1] ,CONT_CLOSED)) &&(!IS_SET(obj->obj_flags.extra_flags ,ITEM_DARK))){ strcpy(buf,first_name(obj->name)); do_enter(ch,buf,0,0); break; } } } } if (!IS_SET(ch->specials2.act, MOB_SENTINEL) && (GET_POS(ch) == POSITION_STANDING) && CAN_SPEAK(ch) && !ch->specials.fighting && !ch->specials.hunting){ found_door = FALSE; for (door=0;door < NUM_OF_DIRS;door++) if(EXIT(ch,door) && EXIT(ch,door)->keyword ){ found_door = TRUE; break;} if (found_door && !number(0,40)){ half_chop(EXIT(ch,door)->keyword, buffer,buf); if (!IS_SET(EXIT(ch,door)->exit_info,EX_SECRET) && !IS_SET(EXIT(ch,door)->exit_info,EX_CLOSED)) do_close(ch,buffer,0,0); else if (!IS_SET(EXIT(ch,door)->exit_info,EX_SECRET) && IS_SET(EXIT(ch,door)->exit_info,EX_CLOSED)) do_open(ch,buffer,0,0); } } } if (IS_SET(ch->specials2.act, MOB_AGGRESSIVE) && AWAKE(ch) && !ch->specials.fighting ) { found = FALSE; for (tmp_ch = world[ch->in_room].people; tmp_ch && !found; tmp_ch = tmp_ch->next_in_room) { if (!IS_NPC(tmp_ch) && CAN_SEE(ch, tmp_ch) && !PRF_FLAGGED(tmp_ch, PRF_NOHASSLE) && !(IS_AFFECTED(tmp_ch,AFF_HIDE) && !can_see_hidden(ch,tmp_ch)) ) { if (!IS_SET(ch->specials2.act, MOB_WIMPY) || !AWAKE(tmp_ch)) { if ((IS_SET(ch->specials2.act, MOB_AGGRESSIVE_EVIL) && IS_EVIL(tmp_ch)) || (IS_SET(ch->specials2.act, MOB_AGGRESSIVE_GOOD) && IS_GOOD(tmp_ch)) || (IS_SET(ch->specials2.act, MOB_AGGRESSIVE_NEUTRAL) && IS_NEUTRAL(tmp_ch)) || (!IS_SET(ch->specials2.act, MOB_AGGRESSIVE_EVIL) && !IS_SET(ch->specials2.act, MOB_AGGRESSIVE_NEUTRAL) && !IS_SET(ch->specials2.act, MOB_AGGRESSIVE_GOOD) )) { if (GET_POS(ch) < POSITION_STANDING) do_stand(ch, "",0,0); add_event(MAX(1,(number(30,40)-GET_PER(ch))/2) ,EVENT_ATTACK, 0,0,0,0,0,ch, tmp_ch); found = TRUE; } } } } } /* if aggressive */ if (IS_SET(ch->specials2.act, MOB_MEMORY) && ch->specials.memory && MOB_FLAGGED(ch,MOB_CITIZEN) && AWAKE(ch) && !number(0,9)) { for (tmp_ch = world[ch->in_room].people; tmp_ch; tmp_ch = tmp_ch->next_in_room) if (IS_NPC(tmp_ch) && MOB_FLAGGED(tmp_ch,MOB_CITIZEN) && IS_SET(tmp_ch->specials2.act, MOB_MEMORY) && tmp_ch != ch && AWAKE(tmp_ch)){ present = TRUE; if (tmp_ch->specials.memory){ for (names = ch->specials.memory ;names;names=names->next) if (!hates_id(tmp_ch, names->id)){ present = FALSE; remember_id(tmp_ch,names->id);} } else{ for (names = ch->specials.memory; names; names = names->next){ remember_id(tmp_ch, names->id); present = FALSE;} } if (!present) act("$n whispers to $N.",FALSE,ch,0,tmp_ch,TO_ROOM); } } if(IS_SET(ch->specials2.act, MOB_MEMORY) && ch->specials.memory) { for (vict = 0, tmp_ch = world[ch->in_room].people; tmp_ch && !vict; tmp_ch = tmp_ch->next_in_room) if (hates(ch,tmp_ch) && CAN_SEE(ch, tmp_ch)) vict = tmp_ch; if (vict) { if ((IS_SET(ch->specials2.act, MOB_WIMPY) && (number(0,30) < GET_PER(ch))) && (GET_HIT(ch) < GET_MAX_HIT(ch)/5)){ if (CAN_SPEAK(ch) && AWAKE(ch)) act("$n screams 'Leave me alone!!'", FALSE, ch, 0, 0, TO_ROOM); else if (AWAKE(ch)) act("$n whimpers.",FALSE, ch, 0, 0, TO_ROOM); do_flee(ch,"",0,0); } else { if (AWAKE(ch) && !ch->specials.fighting){ if (!IS_SET(world[ch->in_room].room_flags, PEACEFULL)){ if (CAN_SEE(ch, vict)) add_event(MAX(1,(number(30,40)-GET_PER(ch))/2) ,EVENT_ATTACK, 0,0,0,0,0,ch, vict); else if (CAN_SPEAK(ch)) act("$n growls, 'Where are you $N. I know you're here somewhere!'", FALSE, ch, 0, vict, TO_ROOM); else act("$n growls!", FALSE, ch, 0, vict, TO_ROOM); } else{ if (CAN_SEE(ch, vict)) act("$n growls, 'Step outside $N. Let's settle this!'", FALSE, ch, 0, vict, TO_ROOM); else if (CAN_SPEAK(ch)) act("$n growls, 'Where are you $N. I know you're here somewhere!'", FALSE, ch, 0, vict, TO_ROOM); else act("$n growls!", FALSE, ch, 0, vict, TO_ROOM); } } } } } /* mob memory */ } /* If IS_MOB(ch) */ } /* Mob Memory Routines */ /* make ch remember victim */ void remember (struct char_data *ch, struct char_data *victim) { memory_rec * tmp; bool present = FALSE; if (!IS_NPC(ch) || IS_NPC(victim) || GET_LEVEL(victim) >= LEVEL_BUILDER) return; for (tmp = ch->specials.memory; tmp && !present; tmp = tmp->next) if (tmp->id == GET_IDNUM(victim)) present = TRUE; if (!present) { CREATE(tmp, memory_rec, 1); tmp->next = ch->specials.memory; tmp->id = GET_IDNUM(victim); ch->specials.memory = tmp; } } void remember_id(struct char_data *ch, long id) { memory_rec * tmp; bool present = FALSE; if (!IS_NPC(ch)) return; if (id <= 0) return; for (tmp = ch->specials.memory; tmp && !present; tmp = tmp->next) if (tmp->id == id) present = TRUE; if (!present) { CREATE(tmp, memory_rec, 1); tmp->next = ch->specials.memory; tmp->id = id; ch->specials.memory = tmp; } } /* make all mobs forget victim */ void forget (struct char_data *victim) { struct char_data *ch; memory_rec *curr, *prev; for (ch = character_list;ch;ch = ch->next){ if (IS_MOB(ch) && ch->specials.hunting == victim) ch->specials.hunting =0; if (IS_MOB(ch) && IS_SET(ch->specials2.act, MOB_MEMORY)) if (ch->specials.memory){ curr = ch->specials.memory; while (curr && curr->id != GET_IDNUM(victim)) { prev = curr; curr = curr->next; } if(curr){ if(curr == ch->specials.memory) ch->specials.memory = curr->next; else prev->next = curr->next; free(curr); } } } } bool hates(struct char_data *ch, struct char_data *vict) { memory_rec *names; if (!IS_NPC(ch) || IS_NPC(vict)) return(FALSE); if (!IS_SET(ch->specials2.act, MOB_MEMORY)) return(FALSE); if (!ch->specials.memory) return(FALSE); for (names = ch->specials.memory;names; names = names->next) if (names->id == GET_IDNUM(vict)) return(TRUE); return(FALSE); } bool hates_id(struct char_data *ch, long id) { memory_rec *names; if (!IS_NPC(ch)) return(FALSE); if (!IS_SET(ch->specials2.act, MOB_MEMORY)) return(FALSE); if (!ch->specials.memory) return(FALSE); for (names = ch->specials.memory;names; names = names->next) if (names->id == id) return(TRUE); return(FALSE); } /* erase ch's memory */ void clearMemory(struct char_data *ch) { memory_rec *curr, *next; curr = ch->specials.memory; while (curr) { next = curr->next; free(curr); curr = next; } ch->specials.memory = NULL; } void mob_cast(struct char_data *ch) { ACMD(do_invoke); ACMD(do_sit); ACMD(do_stand); ACMD(do_rest); int spell_num=-1,i; char buffer[100]; struct char_data *target; bool sitting=0, resting=0; *buffer = '\0'; if (ch->specials.timer){ ch->specials.timer--; return; } if (GET_LEVEL(ch) < 10) return; if (ch->specials.fighting){ if (!number(0,1)){ /* offensive spells */ while (spell_num == -1){ for (i=0;combat_other[i].index != -1;i++) if (spell_info[combat_other[i].index].min_level <= 2*GET_LEVEL(ch) && !number(0,20)){ spell_num = i; break; } if (!can_see_char(ch, ch->specials.fighting) && !number(0,2)) sprintf(buffer,"'reveal the lost image'"); else sprintf(buffer,"'%s' %s",combat_other[spell_num].entry,GET_NAME(ch->specials.fighting)); do_invoke(ch,buffer,0,0); } } else{ /* heal self spells */ while (spell_num == -1){ for (i=0;combat_self[i].index != -1;i++) if (spell_info[combat_self[i].index].min_level <= 2*GET_LEVEL(ch) && !number(0,20)){ spell_num = i; break; } if (!can_see_char(ch, ch->specials.fighting) && !number(0,2)) sprintf(buffer,"'discern image' self"); else sprintf(buffer,"'%s' self",combat_self[spell_num].entry); do_invoke(ch,buffer,0,0); } } } else if (GET_ALIGNMENT(ch) > 500 && !number(0,50)) { /* benevolent spells on chars */ for (target = world[ch->in_room].people; target; target = target->next_in_room){ if (can_see_char(ch, target) && (GET_ALIGNMENT(target) > -500)) { while (spell_num == -1){ for (i=0;mob_beneficial[i].index != -1;i++) if ((spell_info[mob_beneficial[i].index].min_level <= 2*GET_LEVEL(ch))&& !number(0,20)){ spell_num = i; break; } sprintf(buffer,"'%s' %s",mob_beneficial[spell_num].entry, GET_NAME(target)); if (GET_POS(ch) == POSITION_SITTING) sitting = TRUE; if (GET_POS(ch) == POSITION_RESTING) resting = TRUE; do_stand(ch,"",0,0); do_invoke(ch,buffer,0,0); if (sitting || resting) do_sit(ch,"",0,0); if (resting) do_rest(ch,"",0,0); } } } } }