/* *************************************************************************
* File: quest.c Part of CircleMUD *
* Purpose: To provide special quest-related code. *
* *
* Morgaelin - quest.c *
* Copyright (C) 1997 MS *
************************************************************************* */
#include "conf.h"
#include "sysdep.h"
#include "structs.h"
#include "utils.h"
#include "interpreter.h"
#include "db.h"
#include "comm.h"
#include "screen.h"
#include "olc.h"
#include "quest.h"
extern struct aq_data *aquest_table;
extern struct index_data *mob_index;
extern int top_of_aquestt;
extern struct index_data *obj_index;
extern struct room_data *world;
extern long asciiflag_conv(char *flag);
const char *quest_types[] = {
"Object",
"Room",
"Find mob",
"Kill mob",
"Save mob",
"Return object",
"\n"
};
/* Autoquest flags */
char *aq_flags[] = {
"REPEATABLE",
"\n"
};
int real_quest(int vnum)
{
int rnum;
for (rnum=0; rnum <= top_of_aquestt; rnum++)
{
if (rnum <= top_of_aquestt)
if (aquest_table[rnum].virtual==vnum) break;
}
if (rnum>top_of_aquestt) rnum = -1;
return (rnum);
}
int is_complete(struct char_data *ch, int vnum)
{
int i;
for (i=0; i <= ch->num_completed_quests; i++)
if (ch->completed_quests[i] == vnum)
return TRUE;
return FALSE;
}
int find_quest_by_qmnum(int qm, int num)
{
int i;
int found=0;
for (i=0; i <= top_of_aquestt; i++)
if (qm == aquest_table[i].mob_vnum) {
found++;
if (found == num)
return (aquest_table[i].virtual);
}
return -1;
}
/* PARSE_QUEST */
void parse_quest(FILE * quest_f, int nr)
{
static char line[256];
static int i = 0, j;
int retval = 0, t[5];
char f1[128];
aquest_table[i].virtual = nr;
aquest_table[i].mob_vnum = -1;
aquest_table[i].short_desc = NULL;
aquest_table[i].desc = NULL;
aquest_table[i].info = NULL;
aquest_table[i].ending = NULL;
aquest_table[i].flags = 0;
aquest_table[i].type = -1;
aquest_table[i].target = -1;
aquest_table[i].exp = 0;
for (j=0; j < 4; j++)
aquest_table[i].value[j] = 0;
aquest_table[i].next_quest = -1;
/* begin to parse the data */
aquest_table[i].short_desc = 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].ending = fread_string(quest_f, buf2);
if (!get_line(quest_f, line) ||
(retval = sscanf(line, " %d %d %s %d %d %d", t, t+1, f1, t+2, t+3, t + 4)) != 6) {
fprintf(stderr, "Format error in numeric line (expected 6, got %d), %s\n", retval, buf2);
exit(1);
}
aquest_table[i].type = t[0];
aquest_table[i].mob_vnum = t[1];
aquest_table[i].flags = asciiflag_conv(f1);
aquest_table[i].target = t[2];
aquest_table[i].exp = t[3];
aquest_table[i].next_quest = t[4];
if (!get_line(quest_f, line) ||
(retval = sscanf(line, " %d %d %d %d", t, t+1, t+2, t+3)) != 4) {
fprintf(stderr, "Format error in numeric line (expected 4, got %d), %s\n", retval, buf2);
exit(1);
}
aquest_table[i].value[0] = t[0];
aquest_table[i].value[1] = t[1];
aquest_table[i].value[2] = t[2];
aquest_table[i].value[3] = t[3];
for (;;) {
if (!get_line(quest_f, line)) {
fprintf(stderr, "Format error in %s\n", buf2);
exit(1);
}
switch(*line) {
case 'S':
top_of_aquestt = i++;
return;
break;
}
}
} /* parse_quest */
void list_quests(struct char_data *ch, int questmaster)
{
int i;
int number = 1;
sprintf(buf, "The following quests are available:\r\n"
"Num Description\r\n"
"--- ------------------------------------------------------------------------\r\n");
for (i = 0; i <= top_of_aquestt; i++)
if (questmaster == aquest_table[i].mob_vnum) {
sprintf(buf, "%s%3d %s %s\r\n", buf, number, aquest_table[i].desc,
is_complete(ch, aquest_table[i].virtual) ? "(completed)" : "");
number++;
}
send_to_char(buf, ch);
}
#ifdef 0
/* Add to int* completed quest vnum */
void add_completed_quest_u(struct char_file_u *player, int num)
{
int i;
sh_int *temp;
CREATE(temp, sh_int, player->num_completed_quests +1);
for (i=0; i <= player->num_completed_quests; i++)
temp[i] = player->completed_quests[i];
player->num_completed_quests++;
temp[player->num_completed_quests] = num;
if (player->completed_quests)
free(player->completed_quests);
player->completed_quests = temp;
}
#endif
void add_completed_quest(struct char_data *player, int num)
{
sh_int *temp;
int i;
CREATE(temp, sh_int, player->num_completed_quests +1);
for (i=0; i <= player->num_completed_quests; i++)
temp[i] = player->completed_quests[i];
player->num_completed_quests++;
temp[player->num_completed_quests] = num;
if (player->completed_quests)
free(player->completed_quests);
player->completed_quests = temp;
}
/* Generic reward character, cleanup stuff fn */
void generic_complete_quest(struct char_data *ch)
{
GET_EXP(ch) += aquest_table[real_quest((int)GET_QUEST(ch))].exp;
send_to_char(aquest_table[real_quest((int)GET_QUEST(ch))].ending, ch);
if (!IS_SET(aquest_table[real_quest((int)GET_QUEST(ch))].flags, AQ_REPEATABLE))
add_completed_quest(ch, GET_QUEST(ch));
if ((aquest_table[real_quest((int)GET_QUEST(ch))].next_quest >= 0) &&
(aquest_table[real_quest((int)GET_QUEST(ch))].next_quest != GET_QUEST(ch)) &&
!is_complete(ch, aquest_table[real_quest((int)GET_QUEST(ch))].next_quest)) {
GET_QUEST(ch) = aquest_table[real_quest((int)GET_QUEST(ch))].next_quest;
send_to_char(aquest_table[real_quest((int)GET_QUEST(ch))].info, ch);
} else
GET_QUEST(ch) = -1;
save_char(ch, ch->in_room);
}
void autoquest_trigger_check(struct char_data *ch, struct char_data *vict,
struct obj_data *object, int type)
{
struct char_data *i;
int rnum, found;
if (IS_NPC(ch))
return;
if (GET_QUEST(ch) < 0) /* No current quest, skip this */
return;
if (GET_QUEST_TYPE(ch) != type)
return;
if ((rnum = real_quest(GET_QUEST(ch))) < 0)
return;
switch (type) {
case AQ_OBJECT:
if (aquest_table[rnum].target == GET_OBJ_VNUM(object))
generic_complete_quest(ch);
break;
case AQ_ROOM:
if (aquest_table[rnum].target == world[ch->in_room].number)
generic_complete_quest(ch);
break;
case AQ_MOB_FIND:
for (i=world[ch->in_room].people; i; i = i->next_in_room)
if (IS_NPC(i))
if (aquest_table[rnum].target == GET_MOB_VNUM(i))
generic_complete_quest(ch);
break;
case AQ_MOB_KILL:
if (!IS_NPC(ch) && IS_NPC(vict) && (ch != vict))
if (aquest_table[rnum].target == GET_MOB_VNUM(vict))
generic_complete_quest(ch);
break;
case AQ_MOB_SAVE:
found = TRUE;
if (ch == vict)
found = FALSE;
for (i = world[ch->in_room].people; i && found; i = i->next_in_room)
if (i && IS_NPC(i))
if ((GET_MOB_VNUM(i) != aquest_table[rnum].target) && !AFF_FLAGGED(i, AFF_MAJIN))
found = FALSE;
if (found)
generic_complete_quest(ch);
break;
case AQ_RETURN_OBJ:
if (IS_NPC(vict) && (GET_MOB_VNUM(vict) == aquest_table[rnum].value[0]))
if (object && (GET_OBJ_VNUM(object) == aquest_table[rnum].target))
generic_complete_quest(ch);
break;
default:
log("SYSERR: Invalid quest type passed to autoquest_trigger_check");
break;
}
}
SPECIAL(questmaster)
{
int tmp, num;
struct char_data *qm = me;
if (CMD_IS("list")) {
if (!*argument)
list_quests(ch, GET_MOB_VNUM(qm));
else {
if ((num = find_quest_by_qmnum(GET_MOB_VNUM(qm), atoi(argument))) >= 0) {
if (aquest_table[real_quest(num)].info)
send_to_char(aquest_table[real_quest(num)].info, ch);
else
send_to_char("There is no further information on that quest.\r\n", ch);
} else
send_to_char("That is not a valid quest number!\r\n", ch);
}
return 1;
}
if (CMD_IS("join")) {
if (!*argument) {
send_to_char("Join what quest?\r\n",ch);
return 1;
}
if (GET_QUEST(ch) != -1) {
send_to_char("You are already part of a quest!\r\n",ch);
return 1;
}
tmp = atoi(argument);
tmp = find_quest_by_qmnum(GET_MOB_VNUM(qm), tmp);
if ((tmp >= 0) && !is_complete(ch, tmp)) {
send_to_char(aquest_table[real_quest(tmp)].info, ch);
send_to_char("You are now part of that quest!\r\n", ch);
GET_QUEST(ch) = tmp;
return 1;
} else if (is_complete(ch, tmp)) {
send_to_char("You have already completed that quest!\r\n", ch);
return 1;
} else {
send_to_char("That is not a valid quest!\r\n",ch);
return 1;
}
}
return 0;
}
ACMD(do_qstat)
{
int vnum, rnum;
char str[MAX_INPUT_LENGTH];
struct char_data *tmp;
half_chop(argument, str, argument);
if (*str) {
vnum = atoi(str);
rnum = real_quest(vnum);
if (rnum < 0) {
send_to_char("That vnum does not exist.\r\n", ch);
return;
}
*buf = '\0';
tmp = read_mobile(aquest_table[rnum].mob_vnum, VIRTUAL);
sprintbit(aquest_table[rnum].flags, aq_flags, buf2);
sprintf(buf, "VNum: [%s%5d%s], RNum: [%5d] -- Questmaster: %s%s%s\r\n",
CCGRN(ch, C_NRM), vnum, CCNRM(ch, C_NRM), rnum,
CCYEL(ch, C_NRM), GET_NAME(tmp), CCNRM(ch, C_NRM));
sprintf(buf, "%sName: %s\r\n", buf, aquest_table[rnum].short_desc);
sprintf(buf, "%sDesc: %s\r\n", buf, aquest_table[rnum].desc);
sprintf(buf, "%sInformation:\r\n%s%s%s", buf, CCCYN(ch, C_NRM), aquest_table[rnum].info, CCNRM(ch, C_NRM));
sprintf(buf, "%sEnding:\r\n%s%s%s", buf, CCCYN(ch, C_NRM), aquest_table[rnum].ending, CCNRM(ch, C_NRM));
sprintf(buf, "%sType : %s, Target: %d, Experience: %d, Next quest: %d\r\n",
buf, quest_types[aquest_table[rnum].type], aquest_table[rnum].target,
aquest_table[rnum].exp, aquest_table[rnum].next_quest);
sprintf(buf, "%sValue: %d %d %d %d\r\n", buf, aquest_table[rnum].value[0],
aquest_table[rnum].value[1], aquest_table[rnum].value[2],
aquest_table[rnum].value[3]);
sprintf(buf, "%sFlags: %s\r\n", buf, buf2);
send_to_char(buf, ch);
} else send_to_char("Usage: qstat <vnum>\r\n", ch);
}
ACMD(do_qlist)
{
int first, last, nr, found = 0;
char pagebuf[65536];
strcpy(pagebuf,"");
two_arguments(argument, buf, buf2);
if (!*buf) {
send_to_char("Usage: qlist <begining number or zone> [<ending number>]\r\n", ch);
return;
}
first = atoi(buf);
if (*buf2) last = atoi(buf2);
else {
first *= 100;
last = first+99;
}
if ((first < 0) || (first > 99999) || (last < 0) || (last > 99999)) {
send_to_char("Values must be between 0 and 99999.\n\r", ch);
return;
}
if (first >= last) {
send_to_char("Second value must be greater than first.\n\r", ch);
return;
}
for (nr = 0; nr <= top_of_aquestt && (aquest_table[nr].virtual <= last); nr++)
{
if (aquest_table[nr].virtual >= first) {
sprintf(buf, "%5d. [%5d] %s\r\n", ++found,
aquest_table[nr].virtual,
aquest_table[nr].short_desc);
strcat(pagebuf, buf);
}
}
if (!found)
send_to_char("No quests were found in those parameters.\n\r", ch);
else page_string(ch->desc, pagebuf, TRUE);
}