diff --git a/src/asciimap.c b/src/asciimap.c new file mode 100644 index 0000000..b1970e6 --- /dev/null +++ b/src/asciimap.c @@ -0,0 +1,541 @@ +/************************************************************************** +* File: asciimap.c Part of tbaMUD * +* Usage: Generates an ASCII map of the player's surroundings. * +* * +* All rights reserved. See license for complete information. * +* * +* Copyright (C) 1993, 94 by the Trustees of the Johns Hopkins University * +* CircleMUD is based on DikuMUD, Copyright (C) 1990, 1991. * +**************************************************************************/ + +#include "conf.h" +#include "sysdep.h" + +#include "structs.h" +#include "utils.h" +#include "comm.h" +#include "interpreter.h" +#include "handler.h" +#include "db.h" +#include "spells.h" +#include "house.h" +#include "constants.h" +#include "dg_scripts.h" +#include "asciimap.h" + +/****************************************************************************** + * Begin Local (File Scope) Defines and Global Variables + *****************************************************************************/ +/* Do not blindly change these values, as many values cause the map to stop working - backup first */ +#define CANVAS_HEIGHT 19 +#define CANVAS_WIDTH 51 +#define LEGEND_WIDTH 15 + +#define DEFAULT_MAP_SIZE CONFIG_MAP_SIZE + +#define MAX_MAP_SIZE (CANVAS_WIDTH - 1)/4 +#define MAX_MAP CANVAS_WIDTH + +#define MAX_MAP_DIR 6 +#define MAX_MAP_FOLLOW 4 + +#define SECT_EMPTY 30 /* anything greater than num sect types */ +#define SECT_STRANGE (SECT_EMPTY + 1) +#define SECT_HERE (SECT_STRANGE + 1) + +#define DOOR_NS -1 +#define DOOR_EW -2 +#define DOOR_UP -3 +#define DOOR_DOWN -4 +#define VDOOR_NS -5 +#define VDOOR_EW -6 +#define DOOR_NONE -7 +#define NUM_DOOR_TYPES 7 + +#define MAP_CIRCLE 0 +#define MAP_RECTANGLE 1 + +#define MAP_NORMAL 0 +#define MAP_COMPACT 1 + +struct map_info_type +{ + int sector_type; + char disp[20]; +}; + +static struct map_info_type door_info[] = +{ + { DOOR_NONE, " " }, + { VDOOR_EW, " @m+@n " }, + { VDOOR_NS, " @m+@n "}, + { DOOR_DOWN, "@r-@n " }, + { DOOR_UP, "@r+@n " }, + { DOOR_EW, " - " }, + { DOOR_NS, " | " } +}; + +static struct map_info_type compact_door_info[] = +{ + { DOOR_NONE, " " }, + { VDOOR_EW, " @m+@n " }, + { VDOOR_NS, " @m+@n "}, + { DOOR_DOWN, "@r-@n" }, + { DOOR_UP, "@r+@n" }, + { DOOR_EW, "-" }, + { DOOR_NS, " | " } +}; + +/* Add new sector types below for both map_info and world_map_info */ +/* The last 3 MUST remain the same, although the symbol can be changed */ +/* New sectors also need to be added to the perform_map function below */ +static struct map_info_type map_info[] = +{ + { SECT_INSIDE, "@c[@n.@c]@n" }, /* 0 */ + { SECT_CITY, "@c[@wC@c]@n" }, + { SECT_FIELD, "@c[@g,@c]@n" }, + { SECT_FOREST, "@c[@gY@c]@n" }, + { SECT_HILLS, "@c[@Mm@c]@n" }, + { SECT_MOUNTAIN, "@c[@rM@c]@n" }, /* 5 */ + { SECT_WATER_SWIM, "@c[@c~@c]@n" }, + { SECT_WATER_NOSWIM, "@c[@b=@c]@n" }, + { SECT_FLYING, "@c[@C^@c]@n" }, + { SECT_UNDERWATER, "@c[@bU@c]@n" }, + { -1, "" }, /* 10 */ + { -1, "" }, + { -1, "" }, + { -1, "" }, + { -1, "" }, + { -1, "" }, /* 15 */ + { -1, "" }, + { -1, "" }, + { -1, "" }, + { -1, "" }, + { -1, "" }, /* 20 */ + { -1, "" }, + { -1, "" }, + { -1, "" }, + { -1, "" }, + { -1, "" }, /* 25 */ + { -1, "" }, + { -1, "" }, + { -1, "" }, + { -1, "" }, + { SECT_EMPTY, " " }, /* 30 */ + { SECT_STRANGE, "@c[@R?@c]@n" }, + { SECT_HERE, "@c[@B!@c]@n" }, +}; + +static struct map_info_type world_map_info[] = +{ + { SECT_INSIDE, "@n." }, /* 0 */ + { SECT_CITY, "@wC" }, + { SECT_FIELD, "@g," }, + { SECT_FOREST, "@gY" }, + { SECT_HILLS, "@Mm" }, + { SECT_MOUNTAIN, "@rM" }, /* 5 */ + { SECT_WATER_SWIM, "@c~" }, + { SECT_WATER_NOSWIM, "@b=" }, + { SECT_FLYING, "@C^" }, + { SECT_UNDERWATER, "@bU" }, + { -1, "" }, /* 10 */ + { -1, "" }, + { -1, "" }, + { -1, "" }, + { -1, "" }, + { -1, "" }, /* 15 */ + { -1, "" }, + { -1, "" }, + { -1, "" }, + { -1, "" }, + { -1, "" }, /* 20 */ + { -1, "" }, + { -1, "" }, + { -1, "" }, + { -1, "" }, + { -1, "" }, /* 25 */ + { -1, "" }, + { -1, "" }, + { -1, "" }, + { -1, "" }, + { SECT_EMPTY, " " }, /* 30 */ + { SECT_STRANGE, "@R?" }, + { SECT_HERE, "@B!" }, +}; + + +static int map[MAX_MAP][MAX_MAP]; +static int offsets[4][2] ={ {-2, 0},{ 0, 2},{ 2, 0},{ 0, -2} }; +static int offsets_worldmap[4][2] ={ {-1, 0},{ 0, 1},{ 1, 0},{ 0, -1} }; +static int door_offsets[6][2] ={ {-1, 0},{ 0, 1},{ 1, 0},{ 0, -1},{ -1, 1},{ 1, 1} }; +static int door_marks[6] = { DOOR_NS, DOOR_EW, DOOR_NS, DOOR_EW, DOOR_UP, DOOR_DOWN }; +static int vdoor_marks[4] = { VDOOR_NS, VDOOR_EW, VDOOR_NS, VDOOR_EW }; +/****************************************************************************** + * End Local (File Scope) Defines and Global Variables + *****************************************************************************/ + +/****************************************************************************** + * Begin Local (File Scope) Function Prototypes + *****************************************************************************/ +static void MapArea(room_rnum room, struct char_data *ch, int x, int y, int min, int max, sh_int xpos, sh_int ypos, bool worldmap); +static char *StringMap(int centre, int size); +static char *WorldMap(int centre, int size, int mapshape, int maptype ); +static char *CompactStringMap(int centre, int size); +static void perform_map( struct char_data *ch, char *argument, bool worldmap ); +/****************************************************************************** + * End Local (File Scope) Function Prototypes + *****************************************************************************/ + + +bool can_see_map(struct char_data *ch) { + /* Is the map funcionality disabled? */ + if (CONFIG_MAP == MAP_OFF) + return FALSE; + else if ((CONFIG_MAP == MAP_IMM_ONLY) && (GET_LEVEL(ch) < LVL_IMMORT)) + return FALSE; + + return TRUE; +} + +/* MapArea function - create the actual map */ +static void MapArea(room_rnum room, struct char_data *ch, int x, int y, int min, int max, sh_int xpos, sh_int ypos, bool worldmap) +{ + room_rnum prospect_room; + struct room_direction_data *pexit; + int door, ew_size=0, ns_size=0, x_exit_pos=0, y_exit_pos=0; + sh_int prospect_xpos, prospect_ypos; + + if (map[x][y] < 0) + return; /* this is a door */ + + /* marks the room as visited */ + if(room == IN_ROOM(ch)) + map[x][y] = SECT_HERE; + else + map[x][y] = SECT(room); + + if ( (x < min) || ( y < min) || ( x > max ) || ( y > max) ) return; + + /* Check for exits */ + for ( door = 0; door < MAX_MAP_DIR; door++ ) { + + if( door < MAX_MAP_FOLLOW && + xpos+door_offsets[door][0] >= 0 && + xpos+door_offsets[door][0] <= ns_size && + ypos+door_offsets[door][1] >= 0 && + ypos+door_offsets[door][1] <= ew_size) + { /* Virtual exit */ + + map[x+door_offsets[door][0]][y+door_offsets[door][1]] = vdoor_marks[door] ; + if (map[x+offsets[door][0]][y+offsets[door][1]] == SECT_EMPTY ) + MapArea(room,ch,x + offsets[door][0], y + offsets[door][1], min, max, xpos+door_offsets[door][0], ypos+door_offsets[door][1], worldmap); + continue; + } + + if ( (pexit = world[room].dir_option[door]) > 0 && + (pexit->to_room > 0 ) && (pexit->to_room != NOWHERE) && + (!IS_SET(pexit->exit_info, EX_CLOSED))) { /* A real exit */ + + /* But is the door here... */ + switch (door) { + case NORTH: + if(xpos > 0 || ypos!=y_exit_pos) continue; + break; + case SOUTH: + if(xpos < ns_size || ypos!=y_exit_pos) continue; + break; + case EAST: + if(ypos < ew_size || xpos!=x_exit_pos) continue; + break; + case WEST: + if(ypos > 0 || xpos!=x_exit_pos) continue; + break; + } + + + /* if ( (x < min) || ( y < min) || ( x > max ) || ( y > max) ) return;*/ + prospect_room = pexit->to_room; + + /* one way into area OR maze */ + if ( world[prospect_room].dir_option[rev_dir[door]] && + world[prospect_room].dir_option[rev_dir[door]]->to_room != room) { + map[x][y] = SECT_STRANGE; + return; + } + + if(!worldmap) + map[x+door_offsets[door][0]][y+door_offsets[door][1]] = door_marks[door] ; + + prospect_xpos = prospect_ypos = 0; + switch (door) { + case NORTH: + prospect_xpos = ns_size; + case SOUTH: + prospect_ypos = world[prospect_room].dir_option[rev_dir[door]] ? y_exit_pos : ew_size/2; + break; + case WEST: + prospect_ypos = ew_size; + case EAST: + prospect_xpos = world[prospect_room].dir_option[rev_dir[door]] ? x_exit_pos : ns_size/2; + } + + if(worldmap) { + if ( door < MAX_MAP_FOLLOW && map[x+offsets_worldmap[door][0]][y+offsets_worldmap[door][1]] == SECT_EMPTY ) + MapArea(pexit->to_room,ch,x + offsets_worldmap[door][0], y + offsets_worldmap[door][1], min, max, prospect_xpos, prospect_ypos, worldmap); + } else { + if ( door < MAX_MAP_FOLLOW && map[x+offsets[door][0]][y+offsets[door][1]] == SECT_EMPTY ) + MapArea(pexit->to_room,ch,x + offsets[door][0], y + offsets[door][1], min, max, prospect_xpos, prospect_ypos, worldmap); + } + } /* end if exit there */ + } + return; +} + +/* Returns a string representation of the map */ +static char *StringMap(int centre, int size) +{ + static char strmap[MAX_MAP*MAX_MAP*11 + MAX_MAP*2 + 1]; + char *mp = strmap; + char *tmp; + int x, y; + + /* every row */ + for (x = centre - CANVAS_HEIGHT/2; x <= centre + CANVAS_HEIGHT/2; x++) { + /* every column */ + for (y = centre - CANVAS_WIDTH/6; y <= centre + CANVAS_WIDTH/6; y++) { + if (abs(centre - x)<=size && abs(centre-y)<=size) + tmp = (map[x][y]<0) ? \ + door_info[NUM_DOOR_TYPES + map[x][y]].disp : \ + map_info[map[x][y]].disp ; + else + tmp = map_info[SECT_EMPTY].disp; + strcpy(mp, tmp); + mp += strlen(tmp); + } + strcpy(mp, "\r\n"); + mp+=2; + } + *mp='\0'; + return strmap; +} + +static char *WorldMap(int centre, int size, int mapshape, int maptype ) +{ + static char strmap[MAX_MAP*MAX_MAP*4 + MAX_MAP*2 + 1]; + char *mp = strmap; + int x, y; + int xmin, xmax, ymin, ymax; + + switch(maptype) { + case MAP_COMPACT: + xmin = centre - size; + xmax = centre + size; + ymin = centre - 2*size; + ymax = centre + 2*size; + break; + default: + xmin = centre - CANVAS_HEIGHT/2; + xmax = centre + CANVAS_HEIGHT/2; + ymin = centre - CANVAS_WIDTH/2; + ymax = centre + CANVAS_WIDTH/2; + } + + + /* every row */ + /* for (x = centre - size; x <= centre + size; x++) { */ + for (x = xmin; x <= xmax; x++) { + /* every column */ + /* for (y = centre - (2*size) ; y <= centre + (2*size) ; y++) { */ + for (y = ymin ; y <= ymax ; y++) { + + if((mapshape == MAP_RECTANGLE && abs(centre - y) <= size*2 && abs(centre - x) <= size ) || + ((mapshape == MAP_CIRCLE) && (centre-x)*(centre-x) + (centre-y)*(centre-y)/4 <= (size * size + 1))) { + strcpy(mp, world_map_info[map[x][y]].disp); + mp += strlen(world_map_info[map[x][y]].disp); + } else { + strcpy(mp++, " "); + } + } + strcpy(mp, "@n\r\n"); + mp+=4; + } + *mp='\0'; + return strmap; +} + +static char *CompactStringMap(int centre, int size) +{ + static char strmap[MAX_MAP*MAX_MAP*12 + MAX_MAP*2 + 1]; + char *mp = strmap; + int x, y; + + /* every row */ + for (x = centre - size; x <= centre + size; x++) { + /* every column */ + for (y = centre - size; y <= centre + size; y++) { + strcpy(mp, (map[x][y]<0) ? \ + compact_door_info[NUM_DOOR_TYPES + map[x][y]].disp : \ + map_info[map[x][y]].disp); + mp += strlen((map[x][y]<0) ? \ + compact_door_info[NUM_DOOR_TYPES + map[x][y]].disp : \ + map_info[map[x][y]].disp); + } + strcpy(mp, "\r\n"); + mp+=2; + } + *mp='\0'; + return strmap; +} + +/* Display a nicely formatted map with a legend */ +static void perform_map( struct char_data *ch, char *argument, bool worldmap ) +{ + int size = DEFAULT_MAP_SIZE; + int centre, x, y, min, max; + char arg1[MAX_INPUT_LENGTH], arg2[MAX_INPUT_LENGTH], buf[MAX_STRING_LENGTH], buf1[MAX_STRING_LENGTH], buf2[MAX_STRING_LENGTH]; + int count = 0; + int ew_size=0, ns_size=0; + int mapshape = MAP_CIRCLE; + + two_arguments( argument, arg1 , arg2 ); + if(*arg1) + { + size = atoi(arg1); + } + if (*arg2) + { + if (is_abbrev(arg2, "normal")) worldmap=FALSE; + else if (is_abbrev(arg2, "world")) worldmap=TRUE; + else { + send_to_char(ch, "Usage: @ymap [ normal | world ]@n"); + return; + } + } + + if(size<0) { + size = -size; + mapshape = MAP_RECTANGLE; + } + size = URANGE(1,size,MAX_MAP_SIZE); + + centre = MAX_MAP/2; + + if(worldmap) { + min = centre - 2*size; + max = centre + 2*size; + } else { + min = centre - size; + max = centre + size; + } + + /* Blank the map */ + for (x = 0; x < MAX_MAP; ++x) + for (y = 0; y < MAX_MAP; ++y) + map[x][y]= (!(y%2) && !worldmap) ? DOOR_NONE : SECT_EMPTY; + + /* starts the mapping with the centre room */ + MapArea(IN_ROOM(ch), ch, centre, centre, min, max, ns_size/2, ew_size/2, worldmap); + + /* marks the center, where ch is */ + map[centre][centre] = SECT_HERE; + + /* Feel free to put your own MUD name or header in here */ + send_to_char(ch, " @Y-@ytbaMUD Map System@Y-@n\r\n" + "@D .-.__--.,--.__.-.@n\r\n" ); + + count += sprintf(buf + count, "@n@n@n%s Up\\\\", door_info[NUM_DOOR_TYPES + DOOR_UP].disp); + count += sprintf(buf + count, "@n@n@n%s Down\\\\", door_info[NUM_DOOR_TYPES + DOOR_DOWN].disp); + count += sprintf(buf + count, "@n%s You\\\\", map_info[SECT_HERE].disp); + count += sprintf(buf + count, "@n%s Inside\\\\", map_info[SECT_INSIDE].disp); + count += sprintf(buf + count, "@n%s City\\\\", map_info[SECT_CITY].disp); + count += sprintf(buf + count, "@n%s Field\\\\", map_info[SECT_FIELD].disp); + count += sprintf(buf + count, "@n%s Forest\\\\", map_info[SECT_FOREST].disp); + count += sprintf(buf + count, "@n%s Hills\\\\", map_info[SECT_HILLS].disp); + count += sprintf(buf + count, "@n%s Mountain\\\\", map_info[SECT_MOUNTAIN].disp); + count += sprintf(buf + count, "@n%s Swim\\\\", map_info[SECT_WATER_SWIM].disp); + count += sprintf(buf + count, "@n%s Boat\\\\", map_info[SECT_WATER_NOSWIM].disp); + count += sprintf(buf + count, "@n%s Flying\\\\", map_info[SECT_FLYING].disp); + count += sprintf(buf + count, "@n%s Underwater\\\\", map_info[SECT_UNDERWATER].disp); + + strcpy(buf, strfrmt(buf, LEGEND_WIDTH, CANVAS_HEIGHT + 2, FALSE, TRUE, TRUE)); + + /* Start with an empty column */ + strcpy(buf1, strfrmt("",0, CANVAS_HEIGHT + 2, FALSE, FALSE, TRUE)); + + /* Paste the legend */ + strcpy(buf2, strpaste(buf1, buf, "@D | @n")); + + /* Set up the map */ + memset(buf, ' ', CANVAS_WIDTH); + count = (CANVAS_WIDTH); + if(worldmap) + count += sprintf(buf + count , "\r\n%s", WorldMap(centre, size, mapshape, MAP_NORMAL)); + else + count += sprintf(buf + count , "\r\n%s", StringMap(centre, size)); + memset(buf + count, ' ', CANVAS_WIDTH); + strcpy(buf + count + CANVAS_WIDTH, "\r\n"); + /* Paste it on */ + strcpy(buf2, strpaste(buf2, buf, "@D | @n")); + /* Paste on the right border */ + strcpy(buf2, strpaste(buf2, buf1, " ")); + /* Print it all out */ + send_to_char(ch, buf2); + + send_to_char(ch, "@D `.-.__--.,-.__.-.-'@n\r\n"); + return; +} + +/* Display a string with the map beside it */ +void str_and_map(char *str, struct char_data *ch ) { + int size, centre, x, y, min, max, char_size; + int ew_size=0, ns_size=0; + bool worldmap; + + /* Check MUDs map config options - if disabled, just show room decsription */ + if (!can_see_map(ch)) { + send_to_char(ch, strfrmt(str, GET_SCREEN_WIDTH(ch), 1, FALSE, FALSE, FALSE)); + return; + } + + worldmap = ROOM_FLAGGED(IN_ROOM(ch), ROOM_WORLDMAP) ? TRUE : FALSE ; + + if(!PRF_FLAGGED(ch, PRF_AUTOMAP)) { + send_to_char(ch, strfrmt(str, GET_SCREEN_WIDTH(ch), 1, FALSE, FALSE, FALSE)); + return; + } + + size = CONFIG_MINIMAP_SIZE; + centre = MAX_MAP/2; + min = centre - 2*size; + max = centre + 2*size; + + for (x = 0; x < MAX_MAP; ++x) + for (y = 0; y < MAX_MAP; ++y) + map[x][y]= (!(y%2) && !worldmap) ? DOOR_NONE : SECT_EMPTY; + + /* starts the mapping with the center room */ + MapArea(IN_ROOM(ch), ch, centre, centre, min, max, ns_size/2, ew_size/2, worldmap ); + map[centre][centre] = SECT_HERE; + + /* char_size = rooms + doors + padding */ + if(worldmap) + char_size = size * 4 + 5; + else + char_size = 3*(size+1) + (size) + 4; + + if(worldmap) + send_to_char(ch, strpaste(strfrmt(str, GET_SCREEN_WIDTH(ch) - char_size, size*2 + 1, FALSE, TRUE, TRUE), WorldMap(centre, size, MAP_CIRCLE, MAP_COMPACT), " ")); + else + send_to_char(ch, strpaste(strfrmt(str, GET_SCREEN_WIDTH(ch) - char_size, size*2 + 1, FALSE, TRUE, TRUE), CompactStringMap(centre, size), " ")); + +} + +ACMD(do_map) { + if (!can_see_map(ch)) { + send_to_char(ch, "Sorry, the map is disabled!\r\n"); + return; + } + perform_map(ch, argument, ROOM_FLAGGED(IN_ROOM(ch), ROOM_WORLDMAP) ? 1 : 0 ); +} + + diff --git a/src/genqst.c b/src/genqst.c new file mode 100644 index 0000000..cd0725c --- /dev/null +++ b/src/genqst.c @@ -0,0 +1,265 @@ +/* *********************************************************************** +* File: genqst.c Part of CircleMUD * +* Version: 2.0 (November 2005) Written for CircleMud CWG / Suntzu * +* Purpose: To provide special quest-related code. * +* Copyright: Kenneth Ray * +* Original Version Details: * +* Copyright 1996 by Harvey Gilpin * +* Copyright 1997-2001 by George Greer (greerga@circlemud.org) * +************************************************************************ */ + +#include "conf.h" +#include "sysdep.h" +#include "structs.h" +#include "utils.h" +#include "db.h" +#include "quest.h" +#include "genolc.h" +#include "genzon.h" +#include "genzon.h" /* for create_world_index */ + + +/*-------------------------------------------------------------------*/ + +int copy_quest(struct aq_data *to, struct aq_data *from, int free_old_strings) +{ + int i; + if (free_old_strings) + free_quest_strings(to); + to->vnum = from->vnum; + to->flags = from->flags; + to->type = from->type; + to->qm = from->qm; + to->target = from->target; + to->prereq = from->prereq; + to->prev_quest = from->prev_quest; + to->next_quest = from->next_quest; + for (i = 0; i < 7; i++){ + to->value[i] = from->value[i]; + } + to->gold_reward = from->gold_reward; + to->exp_reward = from->exp_reward; + to->obj_reward = from->obj_reward; + to->func = from->func; + return copy_quest_strings(to, from); +} + +int copy_quest_strings(struct aq_data *to, struct aq_data *from) +{ + if (from == NULL || to == NULL) { + log("SYSERR: GenQST: copy_quest_strings: Null values passed."); + return FALSE; + } + to->name = str_udup(from->name); + to->desc = str_udup(from->desc); + to->info = str_udup(from->info); + to->done = str_udup(from->done); + to->quit = str_udup(from->quit); + return TRUE; +} + +void free_quest_strings(struct aq_data *quest) +{ + if (quest->name) + free(quest->name); + if (quest->desc) + free(quest->desc); + if (quest->info) + free(quest->info); + if (quest->done) + free(quest->done); + if (quest->quit) + free(quest->quit); +} + +void free_quest(struct aq_data *quest) +{ + free_quest_strings(quest); + free(quest); +} + +/*-------------------------------------------------------------------*/ + +int add_quest(struct aq_data *nqst) +{ + qst_rnum rnum; + zone_rnum rznum = real_zone_by_thing(nqst->vnum); + + /* The quest already exists, just update it. */ + if ((rnum = real_quest(nqst->vnum)) != NOWHERE) { + copy_quest(&aquest_table[rnum], nqst, TRUE); + } else { + /* increase the number of quest table entries */ + total_quests++; + RECREATE(aquest_table, struct aq_data, total_quests ); + /* Initialise top quest strings to null */ + QST_NAME(total_quests - 1) = NULL; + QST_DESC(total_quests - 1) = NULL; + QST_INFO(total_quests - 1) = NULL; + QST_DONE(total_quests - 1) = NULL; + QST_QUIT(total_quests - 1) = NULL; + /* Now process enties from the top down to see where the new one goes */ + for (rnum = total_quests - 1; rnum > 0; rnum--) { + if (nqst->vnum > QST_NUM(rnum - 1)) + break; //found the place + aquest_table[rnum] = aquest_table[rnum - 1]; //shift quest up one + } + copy_quest(&aquest_table[rnum], nqst, FALSE); + } + /* Make sure we assign spec procs to the questmaster */ + if (mob_index[QST_MASTER(rnum)].func && + mob_index[QST_MASTER(rnum)].func != questmaster) + QST_FUNC(rnum) = mob_index[QST_MASTER(rnum)].func; + mob_index[QST_MASTER(rnum)].func = questmaster; + + /* And make sure we save the updated quest information to disk */ + if (rznum != NOWHERE) + add_to_save_list(zone_table[rznum].number, SL_QST); + else + mudlog(BRF, LVL_BUILDER, TRUE, + "SYSERR: GenOLC: Cannot determine quest zone."); + + return rnum; +} + +/*-------------------------------------------------------------------*/ + +int delete_quest(qst_rnum rnum) +{ + qst_rnum i; + zone_rnum rznum = real_zone_by_thing(QST_NUM(rnum)); + mob_rnum qm = QST_MASTER(rnum); + SPECIAL (*tempfunc); + int quests_remaining = 0; + + if (rnum >= total_quests) + return FALSE; + log("GenOLC: delete_quest: Deleting quest #%d (%s).", + QST_NUM(rnum), QST_NAME(rnum)); + /* make a note of the quest master's secondary spec proc */ + tempfunc = QST_FUNC(rnum); + + + free_quest_strings(&aquest_table[rnum]); + for (i = rnum; i < total_quests - 1; i++) { + aquest_table[i] = aquest_table[i + 1]; + } + total_quests--; + RECREATE(aquest_table, struct aq_data, total_quests); + + if (rznum != NOWHERE) + add_to_save_list(zone_table[rznum].number, SL_QST); + else + mudlog(BRF, LVL_BUILDER, TRUE, + "SYSERR: GenOLC: Cannot determine quest zone."); + /* does the questmaster mob have any quests left? */ + if (qm != NOBODY) { + for (i = 0; i < total_quests; i++) { + if (QST_MASTER(i) == qm) + quests_remaining++; + } + if (quests_remaining == 0) + mob_index[qm].func = tempfunc; // point back to original spec proc + } + return TRUE; +} + +/*-------------------------------------------------------------------*/ + +int save_quests(zone_rnum zone_num) +{ + FILE *sf; + char filename[128], oldname[128], quest_flags[MAX_STRING_LENGTH]; + char quest_desc[MAX_STRING_LENGTH], quest_info[MAX_STRING_LENGTH]; + char quest_done[MAX_STRING_LENGTH], quest_quit[MAX_STRING_LENGTH]; + int i, num_quests = 0; + +#if CIRCLE_UNSIGNED_INDEX + if (zone_num == NOWHERE || zone_num > top_of_zone_table) { +#else + if (zone_num < 0 || zone_num > top_of_zone_table) { +#endif + log("SYSERR: GenOLC: save_quests: Invalid zone number %d passed! (0-%d)", + zone_num, top_of_zone_table); + return FALSE; + } + + log("GenOLC: save_quests: Saving quests in zone #%d (%d-%d).", + zone_table[zone_num].number, + genolc_zone_bottom(zone_num), zone_table[zone_num].top); + + snprintf(filename, sizeof(filename), "%s/%d.new", + QST_PREFIX, zone_table[zone_num].number); + if (!(sf = fopen(filename, "w"))) { + perror("SYSERR: save_quests"); + return FALSE; + } + for (i = genolc_zone_bottom(zone_num); i <= zone_table[zone_num].top; i++) { + qst_rnum rnum; + if ((rnum = real_quest(i)) != NOTHING) { + /* Copy the text strings and strip off trailing newlines. */ + strncpy(quest_desc, QST_DESC(rnum) ? QST_DESC(rnum) : "undefined", + sizeof(quest_desc)-1 ); + strncpy(quest_info, QST_INFO(rnum) ? QST_INFO(rnum) : "undefined", + sizeof(quest_info)-1 ); + strncpy(quest_done, QST_DONE(rnum) ? QST_DONE(rnum) : "undefined", + sizeof(quest_done)-1 ); + strncpy(quest_quit, QST_QUIT(rnum) ? QST_QUIT(rnum) : "undefined", + sizeof(quest_quit)-1 ); + strip_cr(quest_desc); + strip_cr(quest_info); + strip_cr(quest_done); + strip_cr(quest_quit); + /* Save the quest details to the file. */ + sprintascii(quest_flags, QST_FLAGS(rnum)); + fprintf(sf, + "#%d\n" + "%s%c\n" + "%s%c\n" + "%s%c\n" + "%s%c\n" + "%s%c\n" + "%d %d %s %d %d %d %d\n" + "%d %d %d %d %d %d %d\n" + "%d %d %d\n" + "S\n", + QST_NUM(rnum), + QST_NAME(rnum) ? QST_NAME(rnum) : "Untitled", STRING_TERMINATOR, + quest_desc, STRING_TERMINATOR, + quest_info, STRING_TERMINATOR, + quest_done, STRING_TERMINATOR, + quest_quit, STRING_TERMINATOR, + QST_TYPE(rnum), + QST_MASTER(rnum) == NOBODY ? -1 : mob_index[QST_MASTER(rnum)].vnum, + quest_flags, + QST_TARGET(rnum) == NOTHING ? -1 : QST_TARGET(rnum), + QST_PREV(rnum) == NOTHING ? -1 : QST_PREV(rnum), + QST_NEXT(rnum) == NOTHING ? -1 : QST_NEXT(rnum), + QST_PREREQ(rnum) == NOTHING ? -1 : QST_PREREQ(rnum), + QST_POINTS(rnum), QST_PENALTY(rnum), QST_MINLEVEL(rnum), + QST_MAXLEVEL(rnum), QST_TIME(rnum), + QST_RETURNMOB(rnum) == NOBODY ? -1 : QST_RETURNMOB(rnum), + QST_QUANTITY(rnum), QST_GOLD(rnum), QST_EXP(rnum), QST_OBJ(rnum) + ); + num_quests++; + } + } + /* Write the final line and close it. */ + fprintf(sf, "$~\n"); + fclose(sf); + + /* Old file we're replacing. */ + snprintf(oldname, sizeof(oldname), "%s/%d.qst", + QST_PREFIX, zone_table[zone_num].number); + remove(oldname); + rename(filename, oldname); + + /* Do we need to update the index file? */ + if (num_quests > 0) + create_world_index(zone_table[zone_num].number, "qst"); + + if (in_save_list(zone_table[zone_num].number, SL_QST)) + remove_from_save_list(zone_table[zone_num].number, SL_QST); + return TRUE; +} + diff --git a/src/mobact.c b/src/mobact.c index 21e4bcd..acd0d2f 100644 --- a/src/mobact.c +++ b/src/mobact.c @@ -85,7 +85,7 @@ void mobile_activity(void) (world[EXIT(ch, door)->to_room].zone == world[IN_ROOM(ch)].zone))) { /* If the mob is charmed, do not move the mob. */ - if (ch->master != NULL) + if (ch->master == NULL) perform_move(ch, door, 1); } diff --git a/src/qedit.c b/src/qedit.c new file mode 100644 index 0000000..f26ed5c --- /dev/null +++ b/src/qedit.c @@ -0,0 +1,742 @@ +/* *********************************************************************** +* File: qedit.c Part of CircleMUD * +* Version: 2.0 (November 2005) Written for CircleMud CWG / Suntzu * +* Purpose: To provide special quest-related code. * +* Copyright: Kenneth Ray * +* * +* Made for Oasis OLC * +* Copyright 1996 Harvey Gilpin. * +*********************************************************************** */ + +#include "conf.h" +#include "sysdep.h" + +#include "structs.h" +#include "utils.h" + +#include "comm.h" +#include "db.h" +#include "oasis.h" +#include "improved-edit.h" +#include "screen.h" +#include "genolc.h" +#include "genzon.h" +#include "interpreter.h" +#include "modify.h" +#include "quest.h" + +/*-------------------------------------------------------------------*/ +/*. Function prototypes . */ + +static void qedit_setup_new(struct descriptor_data *d); +static void qedit_setup_existing(struct descriptor_data *d, qst_rnum rnum); +static void qedit_disp_menu(struct descriptor_data *d); +static void qedit_save_internally(struct descriptor_data *d); +static void qedit_save_to_disk(int num); + +/*-------------------------------------------------------------------*/ + +static void qedit_save_internally(struct descriptor_data *d) +{ + add_quest(OLC_QUEST(d)); +} + +static void qedit_save_to_disk(int num) +{ + save_quests(num); +} + +/*-------------------------------------------------------------------*\ + utility functions + \*-------------------------------------------------------------------*/ + +ACMD(do_oasis_qedit) +{ + int save = 0; + qst_rnum real_num; + qst_vnum number = NOWHERE; + struct descriptor_data *d; + char *buf3; + char buf1[MAX_INPUT_LENGTH]; + char buf2[MAX_INPUT_LENGTH]; + + /****************************************************************************/ + /** Parse any arguments. **/ + /****************************************************************************/ + buf3 = two_arguments(argument, buf1, buf2); + + if (!*buf1) { + send_to_char(ch, "Specify a quest VNUM to edit.\r\n"); + return; + } else if (!isdigit(*buf1)) { + if (str_cmp("save", buf1) != 0) { + send_to_char(ch, "Yikes! Stop that, someone will get hurt!\r\n"); + return; + } + + save = TRUE; + + if (is_number(buf2)) + number = atoi(buf2); + else if (GET_OLC_ZONE(ch) > 0) { + zone_rnum zlok; + + if ((zlok = real_zone(GET_OLC_ZONE(ch))) == NOWHERE) + number = NOWHERE; + else + number = genolc_zone_bottom(zlok); + } + + if (number == NOWHERE) { + send_to_char(ch, "Save which zone?\r\n"); + return; + } + } + + /****************************************************************************/ + /** If a numeric argument was given, get it. **/ + /****************************************************************************/ + if (number == NOWHERE) + number = atoi(buf1); + + /****************************************************************************/ + /** Check that the guild isn't already being edited. **/ + /****************************************************************************/ + for (d = descriptor_list; d; d = d->next) { + if (STATE(d) == CON_QEDIT) { + if (d->olc && OLC_NUM(d) == number) { + send_to_char(ch, "That quest is currently being edited by %s.\r\n", + PERS(d->character, ch)); + return; + } + } + } + + /****************************************************************************/ + /** Point d to the builder's descriptor. **/ + /****************************************************************************/ + d = ch->desc; + + /****************************************************************************/ + /** Give the descriptor an OLC structure. **/ + /****************************************************************************/ + if (d->olc) { + mudlog(BRF, LVL_IMMORT, TRUE, + "SYSERR: do_oasis_quest: Player already had olc structure."); + free(d->olc); + } + + CREATE(d->olc, struct oasis_olc_data, 1); + + /****************************************************************************/ + /** Find the zone. **/ + /****************************************************************************/ + if ((OLC_ZNUM(d) = real_zone_by_thing(number)) == NOWHERE) { + send_to_char(ch, "Sorry, there is no zone for that number!\r\n"); + free(d->olc); + d->olc = NULL; + return; + } + + /****************************************************************************/ + /** Everyone but IMPLs can only edit zones they have been assigned. **/ + /****************************************************************************/ + if (!can_edit_zone(ch, OLC_ZNUM(d))) { + send_to_char(ch, "You do not have permission to edit this zone.\r\n"); + + /**************************************************************************/ + /** Free the OLC structure. **/ + /**************************************************************************/ + free(d->olc); + d->olc = NULL; + return; + } + + if (save) { + send_to_char(ch, "Saving all quests in zone %d.\r\n", + zone_table[OLC_ZNUM(d)].number); + mudlog(CMP, MAX(LVL_BUILDER, GET_INVIS_LEV(ch)), TRUE, + "OLC: %s saves quest info for zone %d.", + GET_NAME(ch), zone_table[OLC_ZNUM(d)].number); + + /**************************************************************************/ + /** Save the quest to the quest file. **/ + /**************************************************************************/ + qedit_save_to_disk(OLC_ZNUM(d)); + + /**************************************************************************/ + /** Free the OLC structure. **/ + /**************************************************************************/ + free(d->olc); + d->olc = NULL; + return; + } + + OLC_NUM(d) = number; + + if ((real_num = real_quest(number)) != NOTHING) + qedit_setup_existing(d, real_num); + else + qedit_setup_new(d); + + STATE(d) = CON_QEDIT; + + act("$n starts using OLC.", TRUE, d->character, 0, 0, TO_ROOM); + SET_BIT_AR(PLR_FLAGS(ch), PLR_WRITING); + + mudlog(BRF, LVL_IMMORT, TRUE, + "OLC: %s starts editing zone %d allowed zone %d", + GET_NAME(ch), zone_table[OLC_ZNUM(d)].number, GET_OLC_ZONE(ch)); +} + +static void qedit_setup_new(struct descriptor_data *d) +{ + struct aq_data *quest; + + /* Allociate some quest shaped space */ + CREATE(quest, struct aq_data, 1); + /* Set default values */ + quest->vnum = OLC_NUM(d); /* Quest vnum */ + quest->qm = NOBODY; /* Questmaster rnum */ + quest->flags = 0; /* Quest bitflags */ + quest->type = AQ_UNDEFINED; /* Quest type */ + quest->target = NOTHING; /* Quest target */ + quest->prereq = NOTHING; /* Prerequisite object */ + quest->value[0] = 0; /* Points for completing */ + quest->value[1] = 0; /* Points for abandoning */ + quest->value[2] = 0; /* Minimum level */ + quest->value[3] = LVL_IMPL; /* Maximim level */ + quest->value[4] = -1; /* Time limit */ + quest->value[5] = NOBODY; /* Mob to return object */ + quest->value[6] = 1; /* Quantity of targets */ + quest->prev_quest = NOTHING; /* Previous quest */ + quest->next_quest = NOTHING; /* Next quest */ + quest->gold_reward= 0; /* Prize in gold coins */ + quest->exp_reward = 0; /* Prize in exp points */ + quest->obj_reward = NOTHING; /* vnum of reward object */ + quest->name = strdup("Undefined Quest"); + quest->desc = strdup("Quest definition is incomplete."); + quest->info = strdup("There is no information on this quest.\r\n"); + quest->done = strdup("You have completed the quest.\r\n"); + quest->quit = strdup("You have abandoned the quest.\r\n"); + quest->func = NULL; /* Secondary qm spec proc */ + /* Set the descriptor OLC structure to point to this quest */ + OLC_QUEST(d) = quest; + /* Show the main quest edit menu */ + qedit_disp_menu(d); +} + +/*-------------------------------------------------------------------*/ + +static void qedit_setup_existing(struct descriptor_data *d, qst_rnum r_num) +{ + /*. Alloc some quest shaped space . */ + CREATE(OLC_QUEST(d), struct aq_data, 1); + copy_quest(OLC_QUEST(d), aquest_table + r_num, FALSE); + qedit_disp_menu(d); +} + +/*-------------------------------------------------------------------*/ + +/************************************************************************** + Menu functions +**************************************************************************/ + +/*-------------------------------------------------------------------*/ +/*. Display main menu . */ + +static void qedit_disp_menu(struct descriptor_data *d) +{ + struct aq_data *quest; + char quest_flags[MAX_STRING_LENGTH], buf2[MAX_STRING_LENGTH]; + char targetname[MAX_STRING_LENGTH]; + mob_vnum return_mob; + + quest = OLC_QUEST(d); + + clear_screen(d); + sprintbit(quest->flags, aq_flags, quest_flags, sizeof(quest_flags)); + if (quest->type == AQ_OBJ_RETURN) { + if ((return_mob = real_mobile(quest->value[5])) != NOBODY) + snprintf(buf2, sizeof(buf2), "to %s [%d]", + mob_proto[return_mob].player.short_descr, + quest->value[5]); + else + snprintf(buf2, sizeof(buf2), "to an unknown mob [%d].", + quest->value[5]); + } + switch (quest->type) { + case AQ_OBJ_FIND: + case AQ_OBJ_RETURN: + snprintf(targetname, sizeof(targetname), "%s", + real_object(quest->target) == NOTHING ? + "An unknown object" : + obj_proto[real_object(quest->target)].short_description); + break; + case AQ_ROOM_FIND: + case AQ_ROOM_CLEAR: + snprintf(targetname, sizeof(targetname), "%s", + real_room(quest->target) == NOWHERE ? + "An unknown room" : + world[real_room(quest->target)].name); + break; + case AQ_MOB_FIND: + case AQ_MOB_KILL: + case AQ_MOB_SAVE: + snprintf(targetname, sizeof(targetname), "%s", + real_mobile(quest->target) == NOBODY ? + "An unknown mobile" : + GET_NAME(&mob_proto[real_mobile(quest->target)])); + break; + default: + snprintf(targetname, sizeof(targetname), "Unknown"); + break; + } + write_to_output(d, + "-- Quest Number : @n[@c%6d@n]\r\n" + "@g 1@n) Quest Name : @y%s\r\n" + "@g 2@n) Description : @y%s\r\n" + "@g 3@n) Accept Message\r\n@y%s" + "@g 4@n) Completion Message\r\n@y%s" + "@g 5@n) Quit Message\r\n@y%s" + "@g 6@n) Quest Flags : @c%s\r\n" + "@g 7@n) Quest Type : @c%s %s\r\n" + "@g 8@n) Quest Master : [@c%6d@n] @y%s\r\n" + "@g 9@n) Quest Target : [@c%6d@n] @y%s\r\n" + "@g A@n) Quantity : [@c%6d@n]\r\n" + "@n Quest Point Rewards\r\n" + "@g B@n) Completed : [@c%6d@n] @g C@n) Abandoned : [@c%6d@n]\r\n" + "@n Other Rewards Rewards\r\n" + "@g G@n) Gold Coins : [@c%6d@n] @g T@n) Exp Points : [@c%6d@n] @g O@n) Object : [@c%6d@n]\r\n" + "@n Level Limits to Accept Quest\r\n" + "@g D@n) Lower Level : [@c%6d@n] @g E@n) Upper Level : [@c%6d@n]\r\n" + "@g F@n) Prerequisite : [@c%6d@n] @y%s\r\n" + "@g L@n) Time Limit : [@c%6d@n]\r\n" + "@g N@n) Next Quest : [@c%6d@n] @y%s\r\n" + "@g P@n) Previous Quest : [@c%6d@n] @y%s\r\n" + "@g X@n) Delete Quest\r\n" + "@g Q@n) Quit\r\n" + "Enter Choice : ", + quest->vnum, + quest->name, + quest->desc, + quest->info && (str_cmp(quest->info, "undefined")) + ? quest->info : "Nothing\r\n", + quest->done && (str_cmp(quest->done, "undefined")) + ? quest->done : "Nothing\r\n", + quest->quit && (str_cmp(quest->quit, "undefined")) + ? quest->quit : "Nothing\r\n", + quest_flags, + quest_types[quest->type], + quest->type == AQ_OBJ_RETURN ? buf2 : "", + quest->qm == NOBODY ? -1 : mob_index[quest->qm].vnum, + quest->qm == NOBODY ? "none" : mob_proto[quest->qm].player.short_descr, + quest->target == NOBODY ? -1 : quest->target, targetname, + quest->value[6], + quest->value[0], quest->value[1], + quest->gold_reward, quest->exp_reward, quest->obj_reward == NOTHING ? -1 : quest->obj_reward, + quest->value[2], quest->value[3], + quest->prereq == NOTHING ? -1 : quest->prereq, + quest->prereq == NOTHING ? "" : + real_object(quest->prereq) == NOTHING ? "an unknown object" : + obj_proto[real_object(quest->prereq)].short_description, + quest->value[4], + quest->next_quest == NOTHING ? -1 : quest->next_quest, + quest->next_quest == NOTHING ? "" : QST_DESC(real_quest(quest->next_quest)), + quest->prev_quest == NOTHING ? -1 : quest->prev_quest, + quest->prev_quest == NOTHING ? "" : QST_DESC(real_quest(quest->prev_quest)) + ); + OLC_MODE(d) = QEDIT_MAIN_MENU; +} +/* For sector type. */ +void qedit_disp_type_menu(struct descriptor_data *d) +{ + int counter, columns = 0; + + clear_screen(d); + for (counter = 0; counter < NUM_AQ_TYPES; counter++) { + write_to_output(d, "@g%2d@n) %-20.20s %s", counter, + quest_types[counter], !(++columns % 2) ? "\r\n" : ""); + } + write_to_output(d, "\r\nEnter Quest type : "); + OLC_MODE(d) = QEDIT_TYPES; +} +/* For quest flags. */ +void qedit_disp_flag_menu(struct descriptor_data *d) +{ + char bits[MAX_STRING_LENGTH]; + int counter, columns = 0; + + get_char_colors(d->character); + clear_screen(d); + for (counter = 0; counter < NUM_AQ_FLAGS; counter++) { + write_to_output(d, "%s%2d%s) %-20.20s %s", grn, counter + 1, nrm, + aq_flags[counter], !(++columns % 2) ? "\r\n" : ""); + } + sprintbit(OLC_QUEST(d)->flags, aq_flags, bits, sizeof(bits)); + write_to_output(d, "\r\nQuest flags: @c%s@n\r\n" + "Enter quest flags, 0 to quit : ", bits); + OLC_MODE(d) = QEDIT_FLAGS; +} +/************************************************************************** + The GARGANTUAN event handler +**************************************************************************/ + +void qedit_parse(struct descriptor_data *d, char *arg) +{ + int number = atoi(arg); + char *oldtext = NULL; + + switch (OLC_MODE(d)) { + /*-------------------------------------------------------------------*/ + case QEDIT_CONFIRM_SAVESTRING: + switch (*arg) { + case 'y': + case 'Y': + send_to_char(d->character, "Saving Quest to memory.\r\n"); + qedit_save_internally(d); + mudlog(CMP, MAX(LVL_BUILDER, GET_INVIS_LEV(d->character)), TRUE, + "OLC: %s edits quest %d", GET_NAME(d->character), OLC_NUM(d)); + if (CONFIG_OLC_SAVE) { + qedit_save_to_disk(real_zone_by_thing(OLC_NUM(d))); + write_to_output(d, "Quest %d saved to disk.\r\n", OLC_NUM(d)); + } else + write_to_output(d, "Quest %d saved to memory.\r\n", OLC_NUM(d)); + cleanup_olc(d, CLEANUP_STRUCTS); + return; + case 'n': + case 'N': + cleanup_olc(d, CLEANUP_ALL); + return; + default: + write_to_output(d, + "Invalid choice!\r\nDo you wish to save the quest? : "); + return; + } + break; + /*-------------------------------------------------------------------*/ + case QEDIT_CONFIRM_DELETE: + switch (*arg) { + case 'y': + case 'Y': + if (delete_quest(real_quest(OLC_NUM(d)))) + write_to_output(d, "Quest deleted.\r\n"); + else + write_to_output(d, "Couldn't delete the quest!\r\n"); + if (CONFIG_OLC_SAVE) { + qedit_save_to_disk(real_zone_by_thing(OLC_NUM(d))); + write_to_output(d, "Quest file saved to disk.\r\n"); + } else + write_to_output(d, "Quest file saved to memory.\r\n"); + cleanup_olc(d, CLEANUP_ALL); + return; + case 'n': + case 'N': + qedit_disp_menu(d); + return; + default: + write_to_output(d, + "Invalid choice!\r\nDo you wish to delete the quest? : "); + return; + } + break; + + /*-------------------------------------------------------------------*/ + case QEDIT_MAIN_MENU: + switch (*arg) { + case 'q': + case 'Q': + if (OLC_VAL(d)) { /*. Anything been changed? . */ + write_to_output(d, + "Do you wish to save the changes to the Quest? (y/n) : "); + OLC_MODE(d) = QEDIT_CONFIRM_SAVESTRING; + } else + cleanup_olc(d, CLEANUP_ALL); + return; + case 'x': + case 'X': + OLC_MODE(d) = QEDIT_CONFIRM_DELETE; + write_to_output(d, "Do you wish to delete the Quest? (y/n) : "); + break; + case '1': + OLC_MODE(d) = QEDIT_NAME; + write_to_output(d, "Enter the quest name : "); + break; + case '2': + OLC_MODE(d) = QEDIT_DESC; + write_to_output(d, "Enter the quest description :-\r\n] "); + break; + case '3': + OLC_MODE(d) = QEDIT_INFO; + clear_screen(d); + send_editor_help(d); + write_to_output(d, "Enter quest acceptance message:\r\n\r\n"); + + if (OLC_QUEST(d)->info) { + write_to_output(d, "%s", OLC_QUEST(d)->info); + oldtext = strdup(OLC_QUEST(d)->info); + } + string_write(d, &OLC_QUEST(d)->info, MAX_QUEST_MSG, 0, oldtext); + OLC_VAL(d) = 1; + break; + case '4': + OLC_MODE(d) = QEDIT_COMPLETE; + clear_screen(d); + send_editor_help(d); + write_to_output(d, "Enter quest completion message:\r\n\r\n"); + + if (OLC_QUEST(d)->done) { + write_to_output(d, "%s", OLC_QUEST(d)->done); + oldtext = strdup(OLC_QUEST(d)->done); + } + string_write(d, &OLC_QUEST(d)->done, MAX_QUEST_MSG, 0, oldtext); + OLC_VAL(d) = 1; + break; + case '5': + OLC_MODE(d) = QEDIT_ABANDON; + clear_screen(d); + send_editor_help(d); + write_to_output(d, "Enter quest quit message:\r\n\r\n"); + + if (OLC_QUEST(d)->quit) { + write_to_output(d, "%s", OLC_QUEST(d)->quit); + oldtext = strdup(OLC_QUEST(d)->quit); + } + string_write(d, &OLC_QUEST(d)->quit, MAX_QUEST_MSG, 0, oldtext); + OLC_VAL(d) = 1; + break; + case '6': + OLC_MODE(d) = QEDIT_FLAGS; + qedit_disp_flag_menu(d); + break; + case '7': + OLC_MODE(d) = QEDIT_TYPES; + qedit_disp_type_menu(d); + break; + case '8': + OLC_MODE(d) = QEDIT_QUESTMASTER; + write_to_output(d, "Enter vnum of quest master : "); + break; + case '9': + OLC_MODE(d) = QEDIT_TARGET; + write_to_output(d, "Enter target vnum : "); + break; + case 'a': + case 'A': + OLC_MODE(d) = QEDIT_QUANTITY; + write_to_output(d, "Enter quantity of target : "); + break; + case 'b': + case 'B': + OLC_MODE(d) = QEDIT_POINTSCOMP; + write_to_output(d, "Enter points for completing the quest : " ); + break; + case 'c': + case 'C': + OLC_MODE(d) = QEDIT_POINTSQUIT; + write_to_output(d, "Enter points for quitting the quest : " ); + break; + case 'd': + case 'D': + OLC_MODE(d) = QEDIT_LEVELMIN; + write_to_output(d, "Enter minimum level to accept the quest : " ); + break; + case 'e': + case 'E': + OLC_MODE(d) = QEDIT_LEVELMAX; + write_to_output(d, "Enter maximum level to accept the quest : " ); + break; + case 'f': + case 'F': + OLC_MODE(d) = QEDIT_PREREQ; + write_to_output(d, "Enter a prerequisite object vnum (-1 for none) : "); + break; + case 'g': + case 'G': + OLC_MODE(d) = QEDIT_GOLD; + write_to_output(d, "Enter the number of gold coins (0 for none) : "); + break; + case 't': + case 'T': + OLC_MODE(d) = QEDIT_EXP; + write_to_output(d, "Enter a number of experience points (0 for none) : "); + break; + case 'o': + case 'O': + OLC_MODE(d) = QEDIT_OBJ; + write_to_output(d, "Enter the prize object vnum (-1 for none) : "); + break; + case 'l': + case 'L': + OLC_MODE(d) = QEDIT_TIMELIMIT; + write_to_output(d, "Enter time limit to complete (-1 for none) : " ); + break; + case 'n': + case 'N': + OLC_MODE(d) = QEDIT_NEXTQUEST; + write_to_output(d, "Enter vnum of next quest (-1 for none) : "); + break; + case 'p': + case 'P': + OLC_MODE(d) = QEDIT_PREVQUEST; + write_to_output(d, "Enter vnum of previous quest (-1 for none) : "); + break; + default: + write_to_output(d, "Invalid choice!\r\n"); + qedit_disp_menu(d); + break; + } + return; + /*-------------------------------------------------------------------*/ + case QEDIT_NAME: + if (!genolc_checkstring(d, arg)) + break; + if (OLC_QUEST(d)->name) + free(OLC_QUEST(d)->name); + arg[MAX_QUEST_NAME - 1] = '\0'; + OLC_QUEST(d)->name = str_udup(arg); + break; + case QEDIT_DESC: + if (!genolc_checkstring(d, arg)) + break; + if (OLC_QUEST(d)->desc) + free(OLC_QUEST(d)->desc); + arg[MAX_QUEST_DESC - 1] = '\0'; + OLC_QUEST(d)->desc = str_udup(arg); + break; + case QEDIT_QUESTMASTER: + if (number != -1) + if ((number = real_mobile(number)) == NOBODY) { + write_to_output(d, "That mobile does not exist, try again : "); + return; + } + OLC_QUEST(d)->qm = number; + break; + case QEDIT_TYPES: + if (number < 0 || number >= NUM_AQ_TYPES) { + write_to_output(d, "Invalid choice!\r\n"); + qedit_disp_type_menu(d); + return; + } + OLC_QUEST(d)->type = number; + if (number == AQ_OBJ_RETURN) { + OLC_MODE(d) = QEDIT_RETURNMOB; + write_to_output(d, "Enter mob vnum to return object to : "); + return; + } + break; + case QEDIT_FLAGS: + if (number < 0 || number > NUM_AQ_FLAGS) { + write_to_output(d, "That is not a valid choice!\r\n"); + qedit_disp_flag_menu(d); + } else if (number == 0) + break; + else { + TOGGLE_BIT(OLC_QUEST(d)->flags, number ); + qedit_disp_flag_menu(d); + } + return; + case QEDIT_QUANTITY: + OLC_QUEST(d)->value[6] = LIMIT(number, 1, 50); + break; + case QEDIT_POINTSCOMP: + OLC_QUEST(d)->value[0] = LIMIT(number, 0, 999999); + break; + case QEDIT_POINTSQUIT: + OLC_QUEST(d)->value[1] = LIMIT(number, 0, 999999); + break; + case QEDIT_PREREQ: + if ((number = atoi(arg)) != -1) + if (real_object(number) == NOTHING) { + write_to_output(d, "That object does not exist, try again : "); + return; + } + OLC_QUEST(d)->prereq = number; + break; + case QEDIT_LEVELMIN: + if (number < 0 || number > LVL_IMPL) { + write_to_output(d, "Level must be between 0 and %d!\r\n", LVL_IMPL); + write_to_output(d, "Enter minimum level to accept the quest : " ); + return; + } else if (number > OLC_QUEST(d)->value[3]) { + write_to_output(d, "Minimum level can't be above maximum level!\r\n"); + write_to_output(d, "Enter minimum level to accept the quest : " ); + return; + } else { + OLC_QUEST(d)->value[2] = number; + break; + } + case QEDIT_LEVELMAX: + if (number < 0 || number > LVL_IMPL) { + write_to_output(d, "Level must be between 0 and %d!\r\n", LVL_IMPL); + write_to_output(d, "Enter maximum level to accept the quest : " ); + return; + } else if (number < OLC_QUEST(d)->value[2]) { + write_to_output(d, "Maximum level can't be below minimum level!\r\n"); + write_to_output(d, "Enter maximum level to accept the quest : " ); + return; + } else { + OLC_QUEST(d)->value[3] = number; + break; + } + case QEDIT_TIMELIMIT: + OLC_QUEST(d)->value[4] = LIMIT(number, -1, 100); + break; + case QEDIT_RETURNMOB: + if ((number = atoi(arg)) != -1) + if (real_mobile(number) == NOBODY) { + write_to_output(d, "That mobile does not exist, try again : "); + return; + } + OLC_QUEST(d)->value[5] = number; + break; + case QEDIT_TARGET: + OLC_QUEST(d)->target = number; + break; + case QEDIT_NEXTQUEST: + OLC_QUEST(d)->next_quest = (number == -1 ? NOTHING : atoi(arg)); + break; + case QEDIT_PREVQUEST: + OLC_QUEST(d)->prev_quest = (number == -1 ? NOTHING : atoi(arg)); + break; + case QEDIT_GOLD: + OLC_QUEST(d)->gold_reward = LIMIT(number, 0, 99999); + break; + case QEDIT_EXP: + OLC_QUEST(d)->exp_reward = LIMIT(number, 0, 99999); + break; + case QEDIT_OBJ: + if ((number = atoi(arg)) != -1) + if (real_object(number) == NOTHING) { + write_to_output(d, "That object does not exist, try again : "); + return; + } + OLC_QUEST(d)->obj_reward = number; + break; + default: + /*. We should never get here . */ + cleanup_olc(d, CLEANUP_ALL); + mudlog(BRF, LVL_BUILDER, TRUE, "SYSERR: OLC: qedit_parse(): " + "Reached default case!"); + write_to_output(d, "Oops...\r\n"); + break; + } + /*-------------------------------------------------------------------*/ + /*. END OF CASE + If we get here, we have probably changed something, and now want to + return to main menu. Use OLC_VAL as a 'has changed' flag . */ + + OLC_VAL(d) = 1; + qedit_disp_menu(d); +} + +void qedit_string_cleanup(struct descriptor_data *d, int terminator) +{ + switch (OLC_MODE(d)) { + case QEDIT_INFO: + case QEDIT_COMPLETE: + case QEDIT_ABANDON: + qedit_disp_menu(d); + break; + } +} diff --git a/src/quest.c b/src/quest.c new file mode 100644 index 0000000..be55794 --- /dev/null +++ b/src/quest.c @@ -0,0 +1,805 @@ +/* *********************************************************************** +* File: quest.c Part of CircleMUD * +* Version: 2.1 (December 2005) Written for CircleMud CWG / Suntzu * +* Purpose: To provide special quest-related code. * +* Copyright: Kenneth Ray * +* Original Version Details: * +* Morgaelin - quest.c * +* Copyright (C) 1997 MS * +*********************************************************************** */ + +#define __QUEST_C__ + +#include "conf.h" +#include "sysdep.h" + +#include "structs.h" +#include "utils.h" +#include "interpreter.h" +#include "handler.h" +#include "db.h" +#include "comm.h" +#include "screen.h" +#include "quest.h" +#include "act.h" /* for do_tell */ + + +/*-------------------------------------------------------------------------- + * Exported global variables + *--------------------------------------------------------------------------*/ +const char *quest_types[] = { + "Object", + "Room", + "Find mob", + "Kill mob", + "Save mob", + "Return object", + "Clear room", + "\n" +}; +const char *aq_flags[] = { + "REPEATABLE", + "\n" +}; + + +/*-------------------------------------------------------------------------- + * Local (file scope) global variables + *--------------------------------------------------------------------------*/ +static int cmd_tell; + +static const char *quest_cmd[] = { + "list", "history", "join", "leave", "progress", "status", "\n"}; + +static const char *quest_mort_usage = + "Usage: quest list | history | progress | join | leave"; + +static const char *quest_imm_usage = + "Usage: quest list | history | progress | join | leave | status "; + +/*--------------------------------------------------------------------------*/ +/* Utility Functions */ +/*--------------------------------------------------------------------------*/ + +qst_rnum real_quest(qst_vnum vnum) +{ + int rnum; + + for (rnum = 0; rnum < total_quests; rnum++) + if (QST_NUM(rnum) == vnum) + return(rnum); + return(NOTHING); +} + +int is_complete(struct char_data *ch, qst_vnum vnum) +{ + int i; + + for (i = 0; i < GET_NUM_QUESTS(ch); i++) + if (ch->player_specials->saved.completed_quests[i] == vnum) + return TRUE; + return FALSE; +} + +qst_vnum find_quest_by_qmnum(struct char_data *ch, mob_rnum qm, int num) +{ + qst_rnum rnum; + int found=0; + for (rnum = 0; rnum < total_quests; rnum++) { + if (qm == QST_MASTER(rnum)) + if (++found == num) + return (QST_NUM(rnum)); + } + return NOTHING; +} + +/*--------------------------------------------------------------------------*/ +/* Quest Loading and Unloading Functions */ +/*--------------------------------------------------------------------------*/ + +void destroy_quests(void) +{ + qst_rnum rnum = 0; + + if (!aquest_table) + return; + + for (rnum = 0; rnum < total_quests; rnum++){ + free_quest_strings(&aquest_table[rnum]); + } + free(aquest_table); + aquest_table = NULL; + total_quests = 0; + + return; +} + +int count_quests(qst_vnum low, qst_vnum high) +{ + int i, j; + + for (i = j = 0; QST_NUM(i) <= high; i++) + if (QST_NUM(i) >= low) + j++; + + return j; +} + +void parse_quest(FILE *quest_f, int nr) +{ + static char line[256]; + static int i = 0, j; + int retval = 0, t[7]; + char f1[128], buf2[MAX_STRING_LENGTH]; + aquest_table[i].vnum = nr; + aquest_table[i].qm = NOBODY; + aquest_table[i].name = NULL; + aquest_table[i].desc = NULL; + aquest_table[i].info = NULL; + aquest_table[i].done = NULL; + aquest_table[i].quit = NULL; + aquest_table[i].flags = 0; + aquest_table[i].type = -1; + aquest_table[i].target = -1; + aquest_table[i].prereq = NOTHING; + for (j = 0; j < 7; j++) + aquest_table[i].value[j] = 0; + aquest_table[i].prev_quest = NOTHING; + aquest_table[i].next_quest = NOTHING; + aquest_table[i].func = NULL; + + aquest_table[i].gold_reward = 0; + aquest_table[i].exp_reward = 0; + aquest_table[i].obj_reward = NOTHING; + + /* begin to parse the data */ + aquest_table[i].name = fread_string(quest_f, buf2); + aquest_table[i].desc = fread_string(quest_f, buf2); + aquest_table[i].info = fread_string(quest_f, buf2); + aquest_table[i].done = fread_string(quest_f, buf2); + aquest_table[i].quit = fread_string(quest_f, buf2); + if (!get_line(quest_f, line) || + (retval = sscanf(line, " %d %d %s %d %d %d %d", + t, t+1, f1, t+2, t+3, t + 4, t + 5)) != 7) { + log("Format error in numeric line (expected 7, got %d), %s\n", + retval, line); + exit(1); + } + aquest_table[i].type = t[0]; + aquest_table[i].qm = real_mobile(t[1]); + aquest_table[i].flags = asciiflag_conv(f1); + aquest_table[i].target = (t[2] == -1) ? NOTHING : t[2]; + aquest_table[i].prev_quest = (t[3] == -1) ? NOTHING : t[3]; + aquest_table[i].next_quest = (t[4] == -1) ? NOTHING : t[4]; + aquest_table[i].prereq = (t[5] == -1) ? NOTHING : t[5]; + if (!get_line(quest_f, line) || + (retval = sscanf(line, " %d %d %d %d %d %d %d", + t, t+1, t+2, t+3, t+4, t + 5, t + 6)) != 7) { + log("Format error in numeric line (expected 7, got %d), %s\n", + retval, line); + exit(1); + } + for (j = 0; j < 7; j++) + aquest_table[i].value[j] = t[j]; + + if (!get_line(quest_f, line) || + (retval = sscanf(line, " %d %d %d", + t, t+1, t+2)) != 3) { + log("Format error in numeric (rewards) line (expected 3, got %d), %s\n", + retval, line); + exit(1); + } + + aquest_table[i].gold_reward = t[0]; + aquest_table[i].exp_reward = t[1]; + aquest_table[i].obj_reward = (t[2] == -1) ? NOTHING : t[2]; + + for (;;) { + if (!get_line(quest_f, line)) { + log("Format error in %s\n", line); + exit(1); + } + switch(*line) { + case 'S': + total_quests = ++i; + return; + break; + } + } +} /* parse_quest */ + +void assign_the_quests(void) +{ + qst_rnum rnum; + + cmd_tell = find_command("tell"); + + for (rnum = 0; rnum < total_quests; rnum ++) { + if (QST_MASTER(rnum) == NOBODY) { + log("SYSERR: Quest #%d has no questmaster specified.", QST_NUM(rnum)); + continue; + } + if (mob_index[QST_MASTER(rnum)].func && + mob_index[QST_MASTER(rnum)].func != questmaster) + QST_FUNC(rnum) = mob_index[QST_MASTER(rnum)].func; + mob_index[QST_MASTER(rnum)].func = questmaster; + } +} + +/*--------------------------------------------------------------------------*/ +/* Quest Completion Functions */ +/*--------------------------------------------------------------------------*/ +void set_quest(struct char_data *ch, qst_rnum rnum) +{ + GET_QUEST(ch) = QST_NUM(rnum); + GET_QUEST_TIME(ch) = QST_TIME(rnum); + GET_QUEST_COUNTER(ch) = QST_QUANTITY(rnum); + SET_BIT_AR(PRF_FLAGS(ch), PRF_QUEST); + return; +} + +void clear_quest(struct char_data *ch) +{ + GET_QUEST(ch) = NOTHING; + GET_QUEST_TIME(ch) = -1; + GET_QUEST_COUNTER(ch) = 0; + REMOVE_BIT_AR(PRF_FLAGS(ch), PRF_QUEST); + return; +} + +void add_completed_quest(struct char_data *ch, qst_vnum vnum) +{ + qst_vnum *temp; + int i; + + CREATE(temp, qst_vnum, GET_NUM_QUESTS(ch) +1); + for (i=0; i < GET_NUM_QUESTS(ch); i++) + temp[i] = ch->player_specials->saved.completed_quests[i]; + + temp[GET_NUM_QUESTS(ch)] = vnum; + GET_NUM_QUESTS(ch)++; + + if (ch->player_specials->saved.completed_quests) + free(ch->player_specials->saved.completed_quests); + ch->player_specials->saved.completed_quests = temp; +} + +void remove_completed_quest(struct char_data *ch, qst_vnum vnum) +{ + qst_vnum *temp; + int i, j = 0; + + CREATE(temp, qst_vnum, GET_NUM_QUESTS(ch)); + for (i = 0; i < GET_NUM_QUESTS(ch); i++) + if (ch->player_specials->saved.completed_quests[i] != vnum) + temp[j++] = ch->player_specials->saved.completed_quests[i]; + + GET_NUM_QUESTS(ch)--; + + if (ch->player_specials->saved.completed_quests) + free(ch->player_specials->saved.completed_quests); + ch->player_specials->saved.completed_quests = temp; +} + +void generic_complete_quest(struct char_data *ch) +{ + qst_rnum rnum; + qst_vnum vnum = GET_QUEST(ch); + struct obj_data *new_obj; + + if (--GET_QUEST_COUNTER(ch) <= 0) { + rnum = real_quest(vnum); + GET_QUESTPOINTS(ch) += QST_POINTS(rnum); + send_to_char(ch, + "%s\r\nYou have been awarded %d quest points for your service.\r\n", + QST_DONE(rnum), QST_POINTS(rnum)); + if (QST_GOLD(rnum)) { + GET_GOLD(ch) += QST_GOLD(rnum); + send_to_char(ch, + "You have been awarded %d gold coins for your service.\r\n", + QST_GOLD(rnum)); + } + if (QST_EXP(rnum)) { + gain_exp(ch, QST_GOLD(rnum)); + send_to_char(ch, + "You have been awarded %d experience points for your service.\r\n", + QST_EXP(rnum)); + } + if (QST_OBJ(rnum)) { + if (real_object(QST_OBJ(rnum))) { + if ((new_obj = create_obj()) != NULL) { + new_obj = read_object((QST_OBJ(rnum)),VIRTUAL); + obj_to_char(new_obj, ch); + send_to_char(ch, + "You have been presented with %s%s for your service.\r\n", + GET_OBJ_SHORT(new_obj), CCNRM(ch, C_NRM)); + } + } + } + if (!IS_SET(QST_FLAGS(rnum), AQ_REPEATABLE)) + add_completed_quest(ch, vnum); + clear_quest(ch); + if ((real_quest(QST_NEXT(rnum)) != NOTHING) && + (QST_NEXT(rnum) != vnum) && + !is_complete(ch, QST_NEXT(rnum))) { + rnum = real_quest(QST_NEXT(rnum)); + set_quest(ch, rnum); + send_to_char(ch, + "The next stage of your quest awaits:\r\n%s", + QST_INFO(rnum)); + } + } + save_char(ch); +} + +void autoquest_trigger_check(struct char_data *ch, struct char_data *vict, + struct obj_data *object, int type) +{ + struct char_data *i; + qst_rnum rnum; + int found = TRUE; + + if (IS_NPC(ch)) + return; + if (GET_QUEST(ch) == NOTHING) /* No current quest, skip this */ + return; + if (GET_QUEST_TYPE(ch) != type) + return; + if ((rnum = real_quest(GET_QUEST(ch))) == NOTHING) + return; + switch (type) { + case AQ_OBJ_FIND: + if (QST_TARGET(rnum) == GET_OBJ_VNUM(object)) + generic_complete_quest(ch); + break; + case AQ_ROOM_FIND: + if (QST_TARGET(rnum) == world[IN_ROOM(ch)].number) + generic_complete_quest(ch); + break; + case AQ_MOB_FIND: + for (i=world[IN_ROOM(ch)].people; i; i = i->next_in_room) + if (IS_NPC(i)) + if (QST_TARGET(rnum) == GET_MOB_VNUM(i)) + generic_complete_quest(ch); + break; + case AQ_MOB_KILL: + if (!IS_NPC(ch) && IS_NPC(vict) && (ch != vict)) + if (QST_TARGET(rnum) == GET_MOB_VNUM(vict)) + generic_complete_quest(ch); + break; + case AQ_MOB_SAVE: + if (ch == vict) + found = FALSE; + for (i = world[IN_ROOM(ch)].people; i && found; i = i->next_in_room) + if (i && IS_NPC(i) && !MOB_FLAGGED(i, MOB_NOTDEADYET)) + if ((GET_MOB_VNUM(i) != QST_TARGET(rnum)) && + !AFF_FLAGGED(i, AFF_CHARM)) + found = FALSE; + if (found) + generic_complete_quest(ch); + break; + case AQ_OBJ_RETURN: + if (IS_NPC(vict) && (GET_MOB_VNUM(vict) == QST_RETURNMOB(rnum))) + if (object && (GET_OBJ_VNUM(object) == QST_TARGET(rnum))) + generic_complete_quest(ch); + break; + case AQ_ROOM_CLEAR: + if (QST_TARGET(rnum) == world[IN_ROOM(ch)].number) { + for (i = world[IN_ROOM(ch)].people; i && found; i = i->next_in_room) + if (i && IS_NPC(i) && !MOB_FLAGGED(i, MOB_NOTDEADYET)) + found = FALSE; + if (found) + generic_complete_quest(ch); + } + break; + default: + log("SYSERR: Invalid quest type passed to autoquest_trigger_check"); + break; + } +} + +void quest_timeout(struct char_data *ch) +{ + if ((GET_QUEST(ch) != NOTHING) && (GET_QUEST_TIME(ch) != -1)) { + clear_quest(ch); + send_to_char(ch, "You have run out of time to complete the quest.\r\n"); + } +} + +void check_timed_quests(void) +{ + struct char_data *ch; + + for (ch = character_list; ch; ch = ch->next) + if (!IS_NPC(ch) && (GET_QUEST(ch) != NOTHING) && (GET_QUEST_TIME(ch) != -1)) + if (--GET_QUEST_TIME(ch) == 0) + quest_timeout(ch); +} + +/*--------------------------------------------------------------------------*/ +/* Quest Command Helper Functions */ +/*--------------------------------------------------------------------------*/ + +void list_quests(struct char_data *ch, zone_rnum zone, qst_vnum vmin, qst_vnum vmax) +{ + qst_rnum rnum; + qst_vnum bottom, top; + int counter = 0; + + if (zone != NOWHERE) { + bottom = zone_table[zone].bot; + top = zone_table[zone].top; + } else { + bottom = vmin; + top = vmax; + } + /* Print the header for the quest listing. */ + send_to_char (ch, + "Index VNum Description Questmaster\r\n" + "----- ------- -------------------------------------------- -----------\r\n"); + for (rnum = 0; rnum < total_quests ; rnum++) + if (QST_NUM(rnum) >= bottom && QST_NUM(rnum) <= top) + send_to_char(ch, "@g%4d@n) [@g%-5d@n] @c%-44.44s@n @y[%5d]@n\r\n", + ++counter, + QST_NUM(rnum), QST_NAME(rnum), + mob_index[QST_MASTER(rnum)].vnum); + if (!counter) + send_to_char(ch, "None found.\r\n"); +} + +void quest_hist(struct char_data *ch) +{ + int i = 0, counter = 0; + qst_rnum rnum = NOTHING; + + send_to_char(ch, "Quests that you have completed:\r\n" + "Index Description Questmaster\r\n" + "----- ---------------------------------------------------- -----------\r\n"); + for (i = 0; i < GET_NUM_QUESTS(ch); i++) { + if ((rnum = real_quest(ch->player_specials->saved.completed_quests[i])) != NOTHING) + send_to_char(ch, "@g%4d@n) @c%-52.52s@n @y%s@n\r\n", + ++counter, QST_DESC(rnum), GET_NAME(&mob_proto[QST_MASTER(rnum)])); + else + send_to_char(ch, + "@g%4d@n) @cUnknown Quest (it no longer exists)@n\r\n", ++counter); + } + if (!counter) + send_to_char(ch, "You haven't completed any quests yet.\r\n"); +} + +void quest_join(struct char_data *ch, struct char_data *qm, char argument[MAX_INPUT_LENGTH]) +{ + qst_vnum vnum; + qst_rnum rnum; + char buf[MAX_INPUT_LENGTH]; + + if (!*argument) + snprintf(buf, sizeof(buf), + "%s What quest did you wish to join?", GET_NAME(ch)); + else if (GET_QUEST(ch) != NOTHING) + snprintf(buf, sizeof(buf), + "%s But you are already part of a quest!", GET_NAME(ch)); + else if((vnum = find_quest_by_qmnum(ch, qm->nr, atoi(argument))) == NOTHING) + snprintf(buf, sizeof(buf), + "%s I don't know of such a quest!", GET_NAME(ch)); + else if ((rnum = real_quest(vnum)) == NOTHING) + snprintf(buf, sizeof(buf), + "%s I don't know of such a quest!", GET_NAME(ch)); + else if (GET_LEVEL(ch) < QST_MINLEVEL(rnum)) + snprintf(buf, sizeof(buf), + "%s You are not experienced enough for that quest!", GET_NAME(ch)); + else if (GET_LEVEL(ch) > QST_MAXLEVEL(rnum)) + snprintf(buf, sizeof(buf), + "%s You are too experienced for that quest!", GET_NAME(ch)); + else if (is_complete(ch, vnum)) + snprintf(buf, sizeof(buf), + "%s You have already completed that quest!", GET_NAME(ch)); + else if ((QST_PREV(rnum) != NOTHING) && !is_complete(ch, vnum)) + snprintf(buf, sizeof(buf), + "%s That quest is not available to you yet!", GET_NAME(ch)); + else if ((QST_PREREQ(rnum) != NOTHING) && + (real_object(QST_PREREQ(rnum)) != NOTHING) && + (get_obj_in_list_num(real_object(QST_PREREQ(rnum)), + ch->carrying) == NULL)) + snprintf(buf, sizeof(buf), + "%s You need to have %s first!", GET_NAME(ch), + obj_proto[real_object(QST_PREREQ(rnum))].short_description); + else { + act("You join the quest.", TRUE, ch, NULL, NULL, TO_CHAR); + act("$n has joined a quest.", TRUE, ch, NULL, NULL, TO_ROOM); + snprintf(buf, sizeof(buf), + "%s Listen carefully to the instructions.", GET_NAME(ch)); + do_tell(qm, buf, cmd_tell, 0); + set_quest(ch, rnum); + send_to_char(ch, QST_INFO(rnum)); + if (QST_TIME(rnum) != -1) + snprintf(buf, sizeof(buf), + "%s You have a time limit of %d turn%s to complete the quest.", + GET_NAME(ch), QST_TIME(rnum), QST_TIME(rnum) == 1 ? "" : "s"); + else + snprintf(buf, sizeof(buf), + "%s You can take however long you want to complete the quest.", + GET_NAME(ch)); + } + do_tell(qm, buf, cmd_tell, 0); + save_char(ch); +} + +void quest_list(struct char_data *ch, struct char_data *qm, char argument[MAX_INPUT_LENGTH]) +{ + qst_vnum vnum; + qst_rnum rnum; + + if ((vnum = find_quest_by_qmnum(ch, qm->nr, atoi(argument))) == NOTHING) + send_to_char(ch, "That is not a valid quest!\r\n"); + else if ((rnum = real_quest(vnum)) == NOTHING) + send_to_char(ch, "That is not a valid quest!\r\n"); + else if (QST_INFO(rnum)) { + send_to_char(ch,"Complete Details on Quest %d @c%s@n:\r\n%s", + vnum, + QST_DESC(rnum), + QST_INFO(rnum)); + if (QST_PREV(rnum) != NOTHING) + send_to_char(ch, "You have to have completed quest %s first.\r\n", + QST_NAME(real_quest(QST_PREV(rnum)))); + if (QST_TIME(rnum) != -1) + send_to_char(ch, + "There is a time limit of %d turn%s to complete the quest.\r\n", + QST_TIME(rnum), + QST_TIME(rnum) == 1 ? "" : "s"); + } else + send_to_char(ch, "There is no further information on that quest.\r\n"); +} + +void quest_quit(struct char_data *ch) +{ + qst_rnum rnum; + + if (GET_QUEST(ch) == NOTHING) + send_to_char(ch, "But you currently aren't on a quest!\r\n"); + else if ((rnum = real_quest(GET_QUEST(ch))) == NOTHING) { + clear_quest(ch); + send_to_char(ch, "You are now no longer part of the quest.\r\n"); + save_char(ch); + } else { + clear_quest(ch); + if (QST_QUIT(rnum) && (str_cmp(QST_QUIT(rnum), "undefined") != 0)) + send_to_char(ch, "%s", QST_QUIT(rnum)); + else + send_to_char(ch, "You are now no longer part of the quest.\r\n"); + if (QST_PENALTY(rnum)) { + GET_QUESTPOINTS(ch) -= QST_PENALTY(rnum); + send_to_char(ch, + "You have lost %d quest points for your cowardice.\r\n", + QST_PENALTY(rnum)); + } + save_char(ch); + } +} + +void quest_progress(struct char_data *ch) +{ + qst_rnum rnum; + + if (GET_QUEST(ch) == NOTHING) + send_to_char(ch, "But you currently aren't on a quest!\r\n"); + else if ((rnum = real_quest(GET_QUEST(ch))) == NOTHING) { + clear_quest(ch); + send_to_char(ch, "Your quest seems to no longer exist.\r\n"); + } else { + send_to_char(ch, "You are on the following quest:\r\n%s\r\n%s", + QST_DESC(rnum), QST_INFO(rnum)); + if (QST_QUANTITY(rnum) > 1) + send_to_char(ch, + "You still have to achieve %d out of %d goals for the quest.\r\n", + GET_QUEST_COUNTER(ch), QST_QUANTITY(rnum)); + if (GET_QUEST_TIME(ch) > 0) + send_to_char(ch, + "You have %d turn%s remaining to complete the quest.\r\n", + GET_QUEST_TIME(ch), + GET_QUEST_TIME(ch) == 1 ? "" : "s"); + } +} + +void quest_show(struct char_data *ch, mob_rnum qm) +{ + qst_rnum rnum; + int counter = 0; + + send_to_char(ch, + "The following quests are available:\r\n" + "Index Description ( Vnum) Done?\r\n" + "----- ---------------------------------------------------- ------- -----\r\n"); + for (rnum = 0; rnum < total_quests; rnum++) + if (qm == QST_MASTER(rnum)) + send_to_char(ch, "@g%4d@n) @c%-52.52s@n @y(%5d)@n @y(%s)@n\r\n", + ++counter, QST_DESC(rnum), QST_NUM(rnum), + (is_complete(ch, QST_NUM(rnum)) ? "Yes" : "No ")); + if (!counter) + send_to_char(ch, "There are no quests available here at the moment.\r\n"); +} + +void quest_stat(struct char_data *ch, char argument[MAX_STRING_LENGTH]) +{ + qst_rnum rnum; + char buf[MAX_STRING_LENGTH]; + char targetname[MAX_STRING_LENGTH]; + + if (GET_LEVEL(ch) < LVL_IMMORT) + send_to_char(ch, "Huh!?!\r\n"); + else if (!*argument) + send_to_char(ch, "%s\r\n", quest_imm_usage); + else if ((rnum = real_quest(atoi(argument))) == NOTHING ) + send_to_char(ch, "That quest does not exist.\r\n"); + else { + sprintbit(QST_FLAGS(rnum), aq_flags, buf, sizeof(buf)); + switch (QST_TYPE(rnum)) { + case AQ_OBJ_FIND: + case AQ_OBJ_RETURN: + snprintf(targetname, sizeof(targetname), "%s", + real_object(QST_TARGET(rnum)) == NOTHING ? + "An unknown object" : + obj_proto[real_object(QST_TARGET(rnum))].short_description); + break; + case AQ_ROOM_FIND: + case AQ_ROOM_CLEAR: + snprintf(targetname, sizeof(targetname), "%s", + real_room(QST_TARGET(rnum)) == NOWHERE ? + "An unknown room" : + world[real_room(QST_TARGET(rnum))].name); + break; + case AQ_MOB_FIND: + case AQ_MOB_KILL: + case AQ_MOB_SAVE: + snprintf(targetname, sizeof(targetname), "%s", + real_mobile(QST_TARGET(rnum)) == NOBODY ? + "An unknown mobile" : + GET_NAME(&mob_proto[real_mobile(QST_TARGET(rnum))])); + break; + default: + snprintf(targetname, sizeof(targetname), "Unknown"); + break; + } + send_to_char(ch, + "VNum : [@y%5d@n], RNum: [@y%5d@n] -- Questmaster: [@y%5d@n] @y%s@n\r\n" + "Name : @y%s@n\r\n" + "Desc : @y%s@n\r\n" + "Accept Message:\r\n@c%s@n" + "Completion Message:\r\n@c%s@n" + "Quit Message:\r\n@c%s@n" + "Type : @y%s@n\r\n" + "Target: @y%d@n @y%s@n, Quantity: @y%d@n\r\n" + "Value : @y%d@n, Penalty: @y%d@n, Min Level: @y%2d@n, Max Level: @y%2d@n\r\n" + "Flags : @c%s@n\r\n", + QST_NUM(rnum), rnum, + QST_MASTER(rnum) == NOBODY ? -1 : mob_index[QST_MASTER(rnum)].vnum, + QST_MASTER(rnum) == NOBODY ? "" : GET_NAME(&mob_proto[QST_MASTER(rnum)]), + QST_NAME(rnum), QST_DESC(rnum), + QST_INFO(rnum), QST_DONE(rnum), + (QST_QUIT(rnum) && + (str_cmp(QST_QUIT(rnum), "undefined") != 0) + ? QST_QUIT(rnum) : "Nothing\r\n"), + quest_types[QST_TYPE(rnum)], + QST_TARGET(rnum) == NOBODY ? -1 : QST_TARGET(rnum), + targetname, + QST_QUANTITY(rnum), + QST_POINTS(rnum), QST_PENALTY(rnum), QST_MINLEVEL(rnum), + QST_MAXLEVEL(rnum), buf); + if (QST_PREREQ(rnum) != NOTHING) + send_to_char(ch, "Preq : [@y%5d@n] @y%s@n\r\n", + QST_PREREQ(rnum) == NOTHING ? -1 : QST_PREREQ(rnum), + QST_PREREQ(rnum) == NOTHING ? "" : + real_object(QST_PREREQ(rnum)) == NOTHING ? "an unknown object" : + obj_proto[real_object(QST_PREREQ(rnum))].short_description); + if (QST_TYPE(rnum) == AQ_OBJ_RETURN) + send_to_char(ch, "Mob : [@y%5d@n] @y%s@n\r\n", + QST_RETURNMOB(rnum), + real_mobile(QST_RETURNMOB(rnum)) == NOBODY ? "an unknown mob" : + mob_proto[real_mobile(QST_RETURNMOB(rnum))].player.short_descr); + if (QST_TIME(rnum) != -1) + send_to_char(ch, "Limit : There is a time limit of %d turn%s to complete.\r\n", + QST_TIME(rnum), + QST_TIME(rnum) == 1 ? "" : "s"); + else + send_to_char(ch, "Limit : There is no time limit on this quest.\r\n"); + send_to_char(ch, "Prior :"); + if (QST_PREV(rnum) == NOTHING) + send_to_char(ch, " @yNone.@n\r\n"); + else + send_to_char(ch, " [@y%5d@n] @c%s@n\r\n", + QST_PREV(rnum), QST_DESC(real_quest(QST_PREV(rnum)))); + send_to_char(ch, "Next :"); + if (QST_NEXT(rnum) == NOTHING) + send_to_char(ch, " @yNone.@n\r\n"); + else + send_to_char(ch, " [@y%5d@n] @c%s@n\r\n", + QST_NEXT(rnum), QST_DESC(real_quest(QST_NEXT(rnum)))); + } +} + +/*--------------------------------------------------------------------------*/ +/* Quest Command Processing Function and Questmaster Special */ +/*--------------------------------------------------------------------------*/ + +ACMD(do_quest) +{ + char arg1[MAX_INPUT_LENGTH], arg2[MAX_INPUT_LENGTH]; + int tp; + + two_arguments(argument, arg1, arg2); + if (!*arg1) + send_to_char(ch, "%s\r\n", GET_LEVEL(ch) < LVL_IMMORT ? + quest_mort_usage : quest_imm_usage); + else if (((tp = search_block(arg1, quest_cmd, FALSE)) == -1)) + send_to_char(ch, "%s\r\n", GET_LEVEL(ch) < LVL_IMMORT ? + quest_mort_usage : quest_imm_usage); + else { + switch (tp) { + case SCMD_QUEST_LIST: + case SCMD_QUEST_JOIN: + /* list, join should hve been handled by questmaster spec proc */ + send_to_char(ch, "Sorry, but you cannot do that here!\r\n"); + break; + case SCMD_QUEST_HISTORY: + quest_hist(ch); + break; + case SCMD_QUEST_LEAVE: + quest_quit(ch); + break; + case SCMD_QUEST_PROGRESS: + quest_progress(ch); + break; + case SCMD_QUEST_STATUS: + if (GET_LEVEL(ch) < LVL_IMMORT) + send_to_char(ch, "%s\r\n", quest_mort_usage); + else + quest_stat(ch, arg2); + break; + default: /* Whe should never get here, but... */ + send_to_char(ch, "%s\r\n", GET_LEVEL(ch) < LVL_IMMORT ? + quest_mort_usage : quest_imm_usage); + break; + } /* switch on subcmd number */ + } +} + +SPECIAL(questmaster) +{ + qst_rnum rnum; + char arg1[MAX_INPUT_LENGTH], arg2[MAX_INPUT_LENGTH]; + int tp; + struct char_data *qm = (struct char_data *)me; + + /* check that qm mob has quests assigned */ + for (rnum = 0; (rnum < total_quests && + QST_MASTER(rnum) != GET_MOB_RNUM(qm)) ; rnum ++); + if (rnum >= total_quests) + return FALSE; /* No quests for this mob */ + else if (QST_FUNC(rnum) && (QST_FUNC(rnum) (ch, me, cmd, argument))) + return TRUE; /* The secondary spec proc handled this command */ + else if (CMD_IS("quest")) { + two_arguments(argument, arg1, arg2); + if (!*arg1) + return FALSE; + else if (((tp = search_block(arg1, quest_cmd, FALSE)) == -1)) + return FALSE; + else { + switch (tp) { + case SCMD_QUEST_LIST: + if (!*arg2) + quest_show(ch, GET_MOB_RNUM(qm)); + else + quest_list(ch, qm, arg2); + break; + case SCMD_QUEST_JOIN: + quest_join(ch, qm, arg2); + break; + default: + return FALSE; /* fall through to the do_quest command processor */ + } /* switch on subcmd number */ + return TRUE; + } + } else { + return FALSE; /* not a questmaster command */ + } +}