HUNTING ------- Some time ago someone posted graph.c on the list, and it was pretty nice. The code was clean and well commented, but I found it too slow. A longer while ago there was another hunt code posted here, and it was a lot faster. (Though it contained a whole bunch of files and the code didn't look as nice.) Well, here I post it again, in one file and re-indended to look a bit cleaner. The code was obtained from ftp.atinc.com:/pub/mud/outgoing/track.merc21.tar.gz. It seems to originate from SillyMUD. Installation (Merc22) --------------------- The code should work as well with envies and older mercs, though some changes may be needed. The following files will be modified: Makefile, act_wiz.c, const.c, db.c, fight.c, interp.c, and merc.h Makefile -------- - add hunt.o to O_FILES list act_wiz.c --------- - add to do_mstat right after showing the mob spec: if ( IS_NPC(victim) && victim->hunting != NULL ) { sprintf(buf, "Hunting victim: %s (%s)\n\r", IS_NPC(victim->hunting) ? victim->hunting->short_descr : victim->hunting->name, IS_NPC(victim->hunting) ? "MOB" : "PLAYER" ); strcat(buf1, buf); } - change the line send_to_char( " thirst drunk full", ch ); in do_mset to: send_to_char( " thirst drunk full hunt", ch ); - add to do_mset: /* Change their hunting status. */ else if (!str_cmp(arg2, "hunt")) { CHAR_DATA *hunted = 0; if ( !IS_NPC(victim) ) { send_to_char( "Not on PC's.\n\r", ch ); return; } if ( str_cmp( arg3, "." ) ) if ( (hunted = get_char_area(victim, arg3)) == NULL ) { send_to_char("Mob couldn't locate the victim to hunt.\n\r", ch); return; } victim->hunting = hunted; return; } const.c ------- - add skill for hunting in skill table: { "hunt", { 37, 37, 30, 20 }, spell_null, TAR_IGNORE, POS_RESTING, &gsn_hunt, SLOT( 0), 0, 12, "", "!Hunt!" }, db.c ---- - add gsn declaration: sh_int gsn_hunt; fight.c ------- - add the violence_update before lines if ( ( victim = ch->fighting ) == NULL || ch->in_room == NULL ) continue; right after lines for ( ch = char_list; ch != NULL; ch = ch->next ) { ch_next = ch->next; the following: /* * Hunting mobs. */ if ( IS_NPC(ch) && ch->fighting == NULL && IS_AWAKE(ch) && ch->hunting != NULL ) { hunt_victim(ch); continue; } interp.c -------- - add command for hunting: { "hunt", do_hunt, POS_STANDING, 0, LOG_NORMAL }, merc.h ------ - add hunting data to char_data structure: CHAR_DATA * hunting; /* Used by hunting routine */ - add declaration: extern sh_int gsn_hunt; - add declaration for do_hunt: DECLARE_DO_FUN( do_hunt ); - add hunt_victim definition: /* hunt.c */ void hunt_victim args( ( CHAR_DATA *ch ) ); I hope I didn't leave anything vital out, but if anything fails to work, feel free to consult me. I myself have the code installed in my mud, and it works fine. The times needed to hunt down someone are really a _lot_ quicker that with graph.c. hunt.c ------ And here is the hunt.c file itself: /* SillyMUD Distribution V1.1b (c) 1993 SillyMUD Developement See license.doc for distribution terms. SillyMUD is based on DIKUMUD Modifications by Rip in attempt to port to merc 2.1 */ /* Modified by Turtle for Merc22 (07-Nov-94) I got this one from ftp.atinc.com:/pub/mud/outgoing/track.merc21.tar.gz. It cointained 5 files: README, hash.c, hash.h, skills.c, and skills.h. I combined the *.c and *.h files in this hunt.c, which should compile without any warnings or errors. */ /* Modified by Tyche for Merc22 - 9/5/2011 Removed extraneous code. Added application specific hash and queue implementations. Cleaned up and tweaked code for performance. */ #include #include #include #ifdef TIME_HUNT #include #endif #include "merc.h" extern const char *dir_name[]; #define ROOMS_TABLE_SIZE 1063 #define MAKE_ROOM_HASH(key) (((unsigned int)(key))%ROOMS_TABLE_SIZE) struct rooms_node { ROOM_INDEX_DATA *key; int value; struct rooms_node *next; }; struct rooms_table { struct rooms_node *buckets[ROOMS_TABLE_SIZE]; }; struct search_node { ROOM_INDEX_DATA *room; struct search_node *next; }; struct search_queue { struct search_node *head; struct search_node *tail; }; void init_rooms_table (struct rooms_table *rt) { int i; for (i = 0; i < ROOMS_TABLE_SIZE; i++) rt->buckets[i] = NULL; } void destroy_rooms_table (struct rooms_table *rt) { int i; struct rooms_node *entry, *temp; for (i = 0; i < ROOMS_TABLE_SIZE; i++) for (entry = rt->buckets[i]; entry;) { temp = entry->next; free (entry); entry = temp; } } void rooms_table_add (struct rooms_table *rt, ROOM_INDEX_DATA * key, int value) { /* precondition: there is no entry for yet */ struct rooms_node *temp; unsigned int idx; idx = MAKE_ROOM_HASH (key); temp = (struct rooms_node *) malloc (sizeof (struct rooms_node)); temp->key = key; temp->next = rt->buckets[idx]; temp->value = value; rt->buckets[idx] = temp; } int rooms_table_find (struct rooms_table *rt, ROOM_INDEX_DATA * key) { struct rooms_node *entry; unsigned int idx; idx = MAKE_ROOM_HASH (key); entry = rt->buckets[idx]; while (entry && entry->key != key) entry = entry->next; return entry ? entry->value : 0; } void init_search_queue (struct search_queue *sq) { sq->head = NULL; sq->tail = NULL; } void destroy_search_queue (struct search_queue *sq) { struct search_node *entry, *temp; for (entry = sq->head; entry;) { temp = entry->next; free (entry); entry = temp; } } ROOM_INDEX_DATA *search_queue_pop (struct search_queue *sq) { ROOM_INDEX_DATA *room; struct search_node *entry; if (sq->head == NULL) return NULL; room = sq->head->room; entry = sq->head->next; free (sq->head); sq->head = entry; return room; } void search_queue_push (struct search_queue *sq, ROOM_INDEX_DATA * room) { struct search_node *entry; entry = (struct search_node *) malloc (sizeof (struct search_node)); entry->room = room; if (sq->head == NULL) { entry->next = NULL; sq->head = entry; sq->tail = entry; } else { entry->next = sq->tail->next; sq->tail->next = entry; sq->tail = sq->tail->next; } } struct hunting_data { char *name; struct char_data **victim; }; bool exit_ok (EXIT_DATA * pexit, bool go_thru_doors) { if (pexit == NULL || pexit->to_room == NULL) return FALSE; if (go_thru_doors) return TRUE; if (IS_SET (pexit->exit_info, EX_CLOSED)) return FALSE; return TRUE; } int find_path (ROOM_INDEX_DATA * startp, ROOM_INDEX_DATA * endp, CHAR_DATA * ch, int depth, bool in_zone) { struct search_queue s_queue; struct rooms_table x_room; int i, count = 0, direction = -1; bool thru_doors, first_pass = TRUE; ROOM_INDEX_DATA *herep, *tmp_room; #ifdef TIME_HUNT char buf[MAX_STRING_LENGTH]; struct timeval start = { 0, 0 }; struct timeval stop = { 0, 0 }; struct timeval result = { 0, 0 }; double timing; gettimeofday (&start, 0); #endif if (depth < 0) { thru_doors = TRUE; depth = -depth; } else { thru_doors = FALSE; } init_rooms_table (&x_room); rooms_table_add (&x_room, startp, -1); /* initialize queue */ init_search_queue (&s_queue); search_queue_push (&s_queue, startp); while ((herep = search_queue_pop (&s_queue)) != NULL) { /* only look in the same zone...unless option picked */ if (herep->area == startp->area || !in_zone) { /* for each room look in all directions */ for (i = 0; i <= 5; i++) { /* make sure exit is valid */ if (exit_ok (herep->exit[i], thru_doors)) { tmp_room = herep->exit[i]->to_room; if (tmp_room != endp) { /* shall we add room to queue? count determines total breadth and depth */ if (!rooms_table_find (&x_room, tmp_room) && (count < depth)) { count++; /* put room on queue for further checking of it's exits */ search_queue_push (&s_queue, tmp_room); /* add room to list of already searched, using it's ancestor for the direction, unless this is the first pass */ rooms_table_add (&x_room, tmp_room, first_pass ? (i + 1) : rooms_table_find (&x_room, herep)); } } else { /* we have found our target room */ direction = rooms_table_find (&x_room, herep); /* return direction if first layer */ if (direction == -1) direction = i; else --direction; goto finished; } } } } first_pass = FALSE; } finished: /* couldn't find path */ destroy_search_queue (&s_queue); destroy_rooms_table (&x_room); #ifdef TIME_HUNT gettimeofday (&stop, 0); timersub (&stop, &start, &result); timing = ((double) result.tv_sec * 1000000) + ((double) result.tv_usec); if (IS_IMMORTAL (ch)) { sprintf (buf, "Total time was %f microseconds in search of %d rooms.\n", timing, count); send_to_char (buf, ch); } #endif return direction; } void do_hunt (CHAR_DATA * ch, char *argument) { char buf[MAX_STRING_LENGTH]; char arg[MAX_STRING_LENGTH]; CHAR_DATA *victim; int direction; bool fArea; one_argument (argument, arg); if (arg[0] == '\0') { send_to_char ("Whom are you trying to hunt?\n\r", ch); return; } /* only imps can hunt to different areas */ fArea = (get_trust (ch) < MAX_LEVEL); if (fArea) victim = get_char_area (ch, arg); else victim = get_char_world (ch, arg); if (victim == NULL) { send_to_char ("No-one around by that name.\n\r", ch); return; } if (ch->in_room == victim->in_room) { act ("$N is here!", ch, NULL, victim, TO_CHAR); return; } /* * Deduct some movement. */ if (ch->move > 2) ch->move -= 3; else { send_to_char ("You're too exhausted to hunt anyone!\n\r", ch); return; } act ("$n carefully sniffs the air.", ch, NULL, NULL, TO_ROOM); WAIT_STATE (ch, skill_table[gsn_hunt].beats); direction = find_path (ch->in_room, victim->in_room, ch, -40000, fArea); if (direction == -1) { act ("You couldn't find a path to $N from here.", ch, NULL, victim, TO_CHAR); return; } if (direction < 0 || direction > 5) { send_to_char ("Hmm... Something seems to be wrong.\n\r", ch); return; } /* * Give a random direction if the player misses the die roll. */ if ((IS_NPC (ch) && number_percent () > 75) /* NPC @ 25% */ ||(!IS_NPC (ch) && number_percent () > /* PC @ norm */ ch->pcdata->learned[gsn_hunt])) { do { direction = number_door (); } while ((ch->in_room->exit[direction] == NULL) || (ch->in_room->exit[direction]->to_room == NULL)); } /* * Display the results of the search. */ sprintf (buf, "$N is %s from here.", dir_name[direction]); act (buf, ch, NULL, victim, TO_CHAR); return; } void hunt_victim (CHAR_DATA * ch) { int dir; bool found; CHAR_DATA *tmp; if (ch == NULL || ch->hunting == NULL || !IS_NPC (ch)) return; /* * Make sure the victim still exists. */ for (found = 0, tmp = char_list; tmp && !found; tmp = tmp->next) if (ch->hunting == tmp) found = 1; if (!found || !can_see (ch, ch->hunting)) { do_say (ch, "Damn! My prey is gone!!"); ch->hunting = NULL; return; } if (ch->in_room == ch->hunting->in_room) { act ("$n glares at $N and says, 'Ye shall DIE!'", ch, NULL, ch->hunting, TO_NOTVICT); act ("$n glares at you and says, 'Ye shall DIE!'", ch, NULL, ch->hunting, TO_VICT); act ("You glare at $N and say, 'Ye shall DIE!", ch, NULL, ch->hunting, TO_CHAR); multi_hit (ch, ch->hunting, TYPE_UNDEFINED); ch->hunting = NULL; return; } WAIT_STATE (ch, skill_table[gsn_hunt].beats); dir = find_path (ch->in_room, ch->hunting->in_room, ch, -40000, TRUE); if (dir < 0 || dir > 5) { act ("$n says 'Damn! Lost $M!'", ch, NULL, ch->hunting, TO_ROOM); ch->hunting = NULL; return; } /* * Give a random direction if the mob misses the die roll. */ if (number_percent () > 75) { /* @ 25% */ do { dir = number_door (); } while ((ch->in_room->exit[dir] == NULL) || (ch->in_room->exit[dir]->to_room == NULL)); } if (IS_SET (ch->in_room->exit[dir]->exit_info, EX_CLOSED)) { do_open (ch, (char *) dir_name[dir]); return; } move_char (ch, dir); return; } That's all there is to hunt... Read you later :) -- Mikko Kilpikoski __ Sig? Naaah... E-Mail: turtle@modeemi.cs.tut.fi /__\@ I feel too relaxed WWW: http://modeemi.cs.tut.fi/~turtle L L for that... MUD: vichy.modeemi.cs.tut.fi (130.230.11.124) 2011