/**************************************************************************
* Original Diku Mud copyright (C) 1990, 1991 by Sebastian Hammer, *
* Michael Seifert, Hans Henrik St{rfeldt, Tom Madsen, and Katja Nyboe. *
* *
* Merc Diku Mud improvements copyright (C) 1992, 1993 by Michael *
* Chastain, Michael Quan, and Mitchell Tse. *
* *
* In order to use any part of this Merc Diku Mud, you must comply with *
* both the original Diku license in 'license.doc' as well the Merc *
* license in 'license.txt'. In particular, you may not remove either of *
* these copyright notices. *
* *
* Much time and thought has gone into this software and you are *
* benefiting. We hope that you share your changes too. What goes *
* around, comes around. *
***************************************************************************
* ROM 2.4 is copyright 1993-1998 Russ Taylor *
* ROM has been brought to you by the ROM consortium *
* Russ Taylor (rtaylor@hypercube.org) *
* Gabrielle Taylor (gtaylor@hypercube.org) *
* Brian Moore (zump@rom.org) *
* By using this code, you have agreed to follow the terms of the *
* ROM license, in the file Rom24/doc/rom.license *
***************************************************************************
* 1stMUD ROM Derivative (c) 2001-2003 by Ryan Jennings *
* http://1stmud.dlmud.com/ <r-jenn@shaw.ca> *
***************************************************************************/
/****************************************************************************
* Automated Quest code written by Vassago of MOONGATE, moongate.ams.com *
* 4000. Copyright (c) 1996 Ryan Addams, All Rights Reserved. Use of this *
* code is allowed provided you add a credit line to the effect of: *
* "Quest Code (c) 1996 Ryan Addams" to your logon screen with the rest *
* of the standard diku/rom credits. If you use this or a modified version *
* of this code, let me know via email: moongate@moongate.ams.com. Further *
* updates will be posted to the rom mailing list. If you'd like to get *
* the latest version of quest.c, please send a request to the above add- *
* ress. Quest Code v2.03. Please do not remove this notice from this file. *
****************************************************************************/
#include "merc.h"
#include "magic.h"
#include "interp.h"
#include "recycle.h"
struct quest_type
{
char *name;
char *descr;
int vnum;
int cost;
};
#define QUEST_TIME 20
/*
* Object vnums for Quest Rewards
*/
const struct quest_type quest_table[] = {
/*name *//*short description *//*vnum *//*cost */
{"nohunger", "No Hunger/Thirst (quest buy nohunger)", 0, 3000},
{"aura", NULL, QUEST_AURA, 2600},
{"sword", NULL, QUEST_SWORD, 2500},
{"breastplate", NULL, QUEST_BPLATE, 2500},
{"boots", NULL, QUEST_BOOTS, 2500},
{"gloves", NULL, QUEST_GLOVES, 2500},
{"flame", NULL, QUEST_FLAME, 2500},
{"helm", NULL, QUEST_HELM, 2300},
{"bag", NULL, QUEST_BAG, 1000},
{"shield", NULL, QUEST_SHIELD, 750},
{"regeneration", NULL, QUEST_REGEN, 700},
{"invisibility", NULL, QUEST_INVIS, 500},
{"trivia", NULL, QUEST_TRIVIA, 100},
{NULL, NULL, 0, 0}
};
/* Quests to find objects */
#define QUEST_OBJQUEST1 214
#define QUEST_OBJQUEST2 215
#define QUEST_OBJQUEST3 216
#define QUEST_OBJQUEST4 217
/*
* CHANCE function. I use this everywhere in my code, very handy :>
*/
bool chance(int num)
{
if (number_range(1, 100) <= num)
return TRUE;
else
return FALSE;
}
/* is object in quest table? */
int is_qobj(OBJ_DATA * obj)
{
int i;
if (!obj || !obj->pIndexData)
return -1;
for (i = 0; quest_table[i].name != NULL; i++)
{
if (obj->pIndexData->vnum == quest_table[i].vnum)
return i;
}
return -1;
}
int qobj_cost(OBJ_DATA * obj)
{
int i;
if (!obj || !obj->pIndexData)
return 0;
for (i = 0; quest_table[i].name != NULL; i++)
{
if (obj->pIndexData->vnum == quest_table[i].vnum)
return quest_table[i].cost;
}
return obj->cost;
}
/*
* * Add or enhance an obj affect.
*/
void affect_join_obj(OBJ_DATA * obj, AFFECT_DATA * paf)
{
AFFECT_DATA *paf_old;
bool found;
found = FALSE;
for (paf_old = obj->first_affect; paf_old != NULL; paf_old = paf_old->next)
{
if (paf_old->location == paf->location &&
paf_old->type == paf->type &&
paf_old->bitvector == paf->bitvector &&
paf_old->where == paf->where)
{
paf_old->level = paf->level;
paf_old->modifier = paf->modifier;
found = TRUE;
}
}
if (!found)
affect_to_obj(obj, paf);
return;
}
void add_apply(OBJ_DATA * obj, apply_t loc, int mod, where_t where, int type,
int dur, flag_t bit, int level)
{
AFFECT_DATA pAf;
if (obj == NULL)
return;
pAf.location = loc;
pAf.modifier = mod;
pAf.where = where;
pAf.type = type;
pAf.duration = dur;
pAf.bitvector = bit;
pAf.level = level;
affect_join_obj(obj, &pAf);
return;
}
/* Updates a quest object. AFFECTS like sanctuary ect..
are done in game or on area file. (Anything not level based) */
void update_questobjs(CHAR_DATA * ch, OBJ_DATA * obj)
{
int bonus, pbonus, cost;
if (obj == NULL || obj->pIndexData == NULL)
{
bug("update_questobjs: NULL obj");
return;
}
if (ch == NULL)
{
bug("update_questobjs: NULL ch");
return;
}
if (!IS_OBJ_STAT(obj, ITEM_QUEST) && is_qobj(obj) == -1)
return;
bonus = UMAX(5, ch->level / 10);
pbonus = UMAX(5, ch->level / 5);
cost = qobj_cost(obj);
if (obj->level != ch->level)
obj->level = ch->level;
if (obj->condition != -1)
obj->condition = -1;
if (obj->cost != cost)
obj->cost = cost;
if (!CAN_WEAR(obj, ITEM_NO_SAC))
SET_BIT(obj->wear_flags, ITEM_NO_SAC);
if (!IS_OBJ_STAT(obj, ITEM_BURN_PROOF))
SET_BIT(obj->extra_flags, ITEM_BURN_PROOF);
/* Bonuses could get fun here */
switch (obj->pIndexData->vnum)
{
case QUEST_BPLATE:
add_apply(obj, APPLY_DAMROLL, pbonus, TO_OBJECT, 0, -1, 0, ch->level);
add_apply(obj, APPLY_HITROLL, pbonus, TO_OBJECT, 0, -1, 0, ch->level);
break;
case QUEST_SHIELD:
add_apply(obj, APPLY_DAMROLL, pbonus, TO_OBJECT, 0, -1, 0, ch->level);
add_apply(obj, APPLY_HITROLL, pbonus, TO_OBJECT, 0, -1, 0, ch->level);
break;
case QUEST_AURA:
add_apply(obj, APPLY_HIT, UMAX(50, ch->level), TO_OBJECT, 0, -1,
0, ch->level);
add_apply(obj, APPLY_MANA, UMAX(50, ch->level), TO_OBJECT, 0,
-1, 0, ch->level);
add_apply(obj, APPLY_MOVE, UMAX(50, ch->level), TO_OBJECT, 0,
-1, 0, ch->level);
break;
}
switch (obj->item_type)
{
case ITEM_CONTAINER:
/* weight modifiers */
obj->weight = -1 * (50 + (ch->level * 15 / 10));
obj->value[0] = 1000 + (20 * ch->level);
obj->value[3] = 1000 + (20 * ch->level);
break;
case ITEM_WEAPON:
/* weapon values of levelD5 */
obj->value[1] = UMAX(15, ch->level);
obj->value[2] = ch->level < 80 ? 4 : 5;
add_apply(obj, APPLY_DAMROLL, bonus, TO_OBJECT, 0, -1, 0, ch->level);
add_apply(obj, APPLY_HITROLL, bonus, TO_OBJECT, 0, -1, 0, ch->level);
break;
case ITEM_ARMOR:
/* AC value of player level */
obj->value[0] = UMAX(20, ch->level);
obj->value[1] = UMAX(20, ch->level);
obj->value[2] = UMAX(20, ch->level);
obj->value[3] = (5 * UMAX(20, ch->level)) / 6;
break;
}
return;
}
/* why not check non-quest items as well - M. */
void update_all_qobjs(CHAR_DATA * ch)
{
OBJ_DATA *obj;
wloc_t iWear;
for (obj = ch->first_carrying; obj != NULL; obj = obj->next_content)
{
if (IS_OBJ_STAT(obj, ITEM_QUEST))
{
update_questobjs(ch, obj);
if ((iWear = obj->wear_loc) != WEAR_NONE)
{
unequip_char(ch, obj);
equip_char(ch, obj, iWear);
}
}
}
}
void unfinished_quest(CHAR_DATA * ch)
{
if (IS_QUESTOR(ch))
{
bool found = FALSE;
sprintf(log_buf, "Creating unfinished quest for %s.", ch->name);
log_string(log_buf);
wiznet(log_buf, NULL, NULL, 0, 0, 0);
if (ch->pcdata->questobj > 0)
{
OBJ_DATA *obj = NULL;
ROOM_INDEX_DATA *pRoom = NULL;
for (obj = object_first; obj != NULL; obj = obj->next)
if (obj->pIndexData->vnum == ch->pcdata->questobj
&& is_name(ch->name, obj->owner))
found = TRUE;
if (!found)
{
if ((obj =
create_object(get_obj_index
(ch->pcdata->questobj), ch->level)) == NULL)
end_quest(ch, 0);
else
{
if ((pRoom = get_room_index(ch->pcdata->questloc)) == NULL)
{
pRoom = get_random_room(ch);
ch->pcdata->questobj = pRoom->vnum;
}
obj_to_room(obj, pRoom);
}
do_quest(ch, "info");
chprintln(ch, "");
}
}
else if (ch->pcdata->questmob > 0)
{
CHAR_DATA *mob;
for (mob = char_first; mob != NULL; mob = mob->next)
if (IS_NPC(mob)
&& mob->pIndexData->vnum == ch->pcdata->questmob)
break;
if (!mob)
end_quest(ch, QUEST_TIME / 4);
else
{
ch->pcdata->questloc = mob->in_room->vnum;
do_quest(ch, "info");
chprintln(ch, "");
}
}
}
}
/* Usage info on the QUEST commands*/
/* Keep this in line with the do_quest function's keywords */
void quest_usage(CHAR_DATA * ch)
{
chprintln(ch, "QUEST commands: INFO, REQUEST, COMPLETE, LIST,\n\r"
" BUY, QUIT, SELL, IDENTIFY.");
if (IS_IMMORTAL(ch))
chprintln(ch, "QUEST RESET (player): resets players quest.");
chprintln(ch, "For more information, type 'HELP QUEST'.");
return;
}
/* Obtain additional location information about sought item/mob */
void quest_where(CHAR_DATA * ch, char *what)
{
ROOM_INDEX_DATA *room;
if (!ch || IS_NPC(ch))
return;
if (ch->pcdata->questloc <= 0)
{
bugf("QUEST INFO: ch->questloc = %ld", ch->pcdata->questloc);
return;
}
if (ch->in_room == NULL)
return;
room = get_room_index(ch->pcdata->questloc);
if (room->area == NULL)
{
bugf("QUEST INFO: room(%ld)->area == NULL", ch->pcdata->questloc);
return;
}
if (room->area->name == NULL)
{
bug("QUEST INFO: area->name == NULL");
return;
}
chprintlnf(ch,
"Rumor has it this %s was last seen in the area known as %s,",
what, room->area->name);
if (room->name == NULL)
{
bugf("QUEST INFO: room(%ld)->name == NULL", ch->pcdata->questloc);
return;
}
chprintlnf(ch, "near %s.", room->name);
} /* end quest_where() */
OBJ_DATA *has_questobj(CHAR_DATA * ch)
{
OBJ_DATA *obj;
OBJ_INDEX_DATA *pObj;
if (!ch || IS_NPC(ch) || ch->pcdata->questobj <= 0)
return NULL;
if ((pObj = get_obj_index(ch->pcdata->questobj)) == NULL)
return NULL;
for (obj = ch->first_carrying; obj != NULL; obj = obj->next_content)
if (obj != NULL && obj->pIndexData == pObj)
return obj;
return NULL;
}
/*
* The main quest function
*/
CH_CMD(do_quest)
{
CHAR_DATA *questman;
OBJ_DATA *obj = NULL;
char buf[MAX_STRING_LENGTH];
char arg1[MAX_INPUT_LENGTH];
char arg2[MAX_INPUT_LENGTH];
int i = 0;
if (IS_NPC(ch))
{
chprintln(ch, "I'm sorry, you can't quest.");
return;
}
argument = one_argument(argument, arg1);
argument = one_argument(argument, arg2);
if (IS_NULLSTR(arg1))
{
quest_usage(ch);
return;
}
if (!str_prefix(arg1, "info"))
{
MOB_INDEX_DATA *qm_mob;
OBJ_DATA *qinfoobj;
CHAR_DATA *qinfomob;
if (!IS_QUESTOR(ch))
{
chprintln(ch, "You aren't currently on a quest.");
if (ch->pcdata->nextquest > 1)
{
chprintlnf(ch,
"There are %d minutes remaining until you can go on another quest.",
ch->pcdata->nextquest);
}
else if (ch->pcdata->nextquest == 1)
{
chprintln(ch,
"There is less than a minute remaining until you can go on another quest.");
}
chprintlnf(ch, "You have %d quest points.",
ch->pcdata->questpoints);
return;
}
chprintln(ch, "");
if ((qm_mob = get_mob_index(ch->pcdata->questgiver)) == NULL)
{
chprintln(ch,
"Your quest master has fallen very ill. Please contact an imm!");
end_quest(ch, QUEST_TIME / 4);
return;
}
if (ch->pcdata->questmob == -1 || has_questobj(ch) != NULL)
{
chprintln(ch, "Your quest is ALMOST complete!");
chprintlnf(ch,
"You have %d minute%s to get back to %s\n\rbefore your time runs out!",
ch->pcdata->countdown,
ch->pcdata->countdown > 1 ? "s" : "",
qm_mob->short_descr);
return;
}
else if (ch->pcdata->questobj > 0)
{
for (qinfoobj = object_first; qinfoobj != NULL;
qinfoobj = qinfoobj->next)
{
if (qinfoobj->pIndexData->vnum == ch->pcdata->questobj)
{
act("$n recalls the quest $N gave $m.",
ch, NULL, qm_mob, TO_ROOM);
chprintlnf(ch,
"You recall the quest which the %s gave you.",
qm_mob->short_descr);
chprintlnf(ch,
"You are on a quest to recover the fabled %s!",
qinfoobj->short_descr);
quest_where(ch, "treasure");
if (ch->pcdata->countdown > 0)
{
chprintlnf(ch,
"You have %d minute%s to complete this quest.",
ch->pcdata->countdown,
ch->pcdata->countdown > 1 ? "s" : "");
}
return;
}
}
}
else if (ch->pcdata->questmob > 0)
{
for (qinfomob = char_first; qinfomob != NULL;
qinfomob = qinfomob->next)
{
if (IS_NPC(qinfomob)
&& qinfomob->pIndexData->vnum == ch->pcdata->questmob)
{
act("$n recalls the quest $N gave $s.",
ch, NULL, qm_mob, TO_ROOM);
chprintlnf(ch,
"You recall the quest which the %s gave you.",
qm_mob->short_descr);
chprintlnf(ch,
"You are on a quest to slay the dreaded %s!",
qinfomob->short_descr);
quest_where(ch, "fiend");
if (ch->pcdata->countdown > 0)
{
chprintlnf(ch,
"You have %d minute%s to complete this quest.",
ch->pcdata->countdown,
ch->pcdata->countdown > 1 ? "s" : "");
}
return;
}
}
}
bugf("QUEST INFO: Questor with no kill, mob or obj");
end_quest(ch, QUEST_TIME / 4);
return;
}
else if (!str_prefix(arg1, "reset"))
{
CHAR_DATA *victim;
if (!IS_IMMORTAL(ch))
{
quest_usage(ch);
return;
}
if (IS_NULLSTR(arg2))
{
chprintln(ch, "Syntax: quest reset <player>");
return;
}
if ((victim = get_char_world(ch, arg2)) == NULL)
{
chprintln(ch, "They aren't here.");
return;
}
if (IS_NPC(victim))
{
chprintln(ch, "Mobs dont quest.");
return;
}
end_quest(victim, 0);
if (victim != ch)
chprintln(ch, "You clear thier quest.");
chprintlnf(victim, "%s has cleared your quest.", PERS(ch, victim));
return;
}
/*
* Checks for a character in the room with spec_questmaster set. This
* special procedure must be defined in special.c. You could instead use
* an ACT_QUESTMASTER flag instead of a special procedure.
*/
for (questman = ch->in_room->first_person; questman != NULL;
questman = questman->next_in_room)
{
if (!IS_NPC(questman))
continue;
if (questman->spec_fun == spec_lookup("spec_questmaster"))
break;
}
if (questman == NULL
|| questman->spec_fun != spec_lookup("spec_questmaster"))
{
chprintln(ch, "You can't do that here.");
return;
}
if (questman->fighting != NULL)
{
chprintln(ch, "Wait until the fighting stops.");
return;
}
/*
* And, of course, you will need to change the following lines for YOUR
* quest item information. Quest items on Moongate are unbalanced, very
* very nice items, and no one has one yet, because it takes awhile to
* build up quest points :> Make the item worth their while.
*/
if (!str_cmp(arg1, "list"))
{
OBJ_INDEX_DATA *test;
act("$n asks $N for a list of quest items.", ch, NULL, questman,
TO_ROOM);
chprintln(ch, "\tCurrent Quest Items available for Purchase:");
for (i = 0; quest_table[i].name != NULL; i++)
{
test = get_obj_index(quest_table[i].vnum);
chprintlnf(ch, "\t%-4dqp ........ %s",
quest_table[i].cost,
test ? test->short_descr : quest_table[i].descr !=
NULL ? quest_table[i].descr : "Unavailable");
}
chprintln(ch, "\tTo buy an item, type 'QUEST BUY <item>'.");
chprintln(ch, "\tFor more info on quest items type 'help questitems'");
return;
}
else if (!str_cmp(arg1, "buy"))
{
if (IS_NULLSTR(arg2))
{
chprintln(ch, "To buy an item, type 'QUEST BUY <item>'.");
return;
}
for (i = 0; quest_table[i].name != NULL; i++)
{
if (is_name(arg2, quest_table[i].name))
{
if (ch->pcdata->questpoints >= quest_table[i].cost)
{
if (quest_table[i].vnum == 0)
{
ch->pcdata->questpoints -= quest_table[i].cost;
ch->pcdata->condition[COND_FULL] = -1;
ch->pcdata->condition[COND_THIRST] = -1;
act
("$N calls upon the power of the gods to relieve your mortal burdens.",
ch, NULL, questman, TO_CHAR);
act
("$N calls upon the power of the gods to relieve $n's mortal burdens.",
ch, NULL, questman, TO_ROOM);
return;
}
else if ((obj =
create_object(get_obj_index
(quest_table
[i].vnum), ch->level)) == NULL)
{
chprintln(ch,
"That object could not be found, contact an immortal.");
return;
}
else
{
ch->pcdata->questpoints -= quest_table[i].cost;
}
if (!IS_SET(obj->pIndexData->extra_flags, ITEM_QUEST))
{
SET_BIT(obj->pIndexData->extra_flags, ITEM_QUEST);
SET_BIT(obj->extra_flags, ITEM_QUEST);
}
act("$N gives $p to $n.", ch, obj, questman, TO_ROOM);
act("$N gives you $p.", ch, obj, questman, TO_CHAR);
obj_to_char(obj, ch);
save_char_obj(ch);
return;
}
else
{
mob_tell(ch, questman,
"Sorry, %s, but you need %d quest points for that.",
ch->name, quest_table[i].cost);
return;
}
}
}
mob_tell(ch, questman, "I don't have that item, %s.", ch->name);
return;
}
else if (!str_cmp(arg1, "sell"))
{
if (IS_NULLSTR(arg2))
{
chprintln(ch, "To sell an item, type 'QUEST SELL <item>'.");
return;
}
if ((obj = get_obj_carry(ch, arg2, ch)) == NULL)
{
chprintln(ch, "Which item is that?");
return;
}
if (!IS_OBJ_STAT(obj, ITEM_QUEST))
{
mob_tell(ch, questman, "That is not a quest item, %s.", ch->name);
return;
}
for (i = 0; quest_table[i].name != NULL; i++)
{
if (quest_table[i].vnum <= 0)
continue;
if (quest_table[i].vnum == obj->pIndexData->vnum)
{
ch->pcdata->questpoints += quest_table[i].cost / 3;
act("$N takes $p from $n.", ch, obj, questman, TO_ROOM);
sprintf(buf,
"$N takes $p from you for %d quest points.",
quest_table[i].cost / 3);
act(buf, ch, obj, questman, TO_CHAR);
extract_obj(obj);
save_char_obj(ch);
return;
}
}
mob_tell(ch, questman, "I only take items I sell, %s.", ch->name);
return;
}
else if (!str_cmp(arg1, "identify"))
{
if (IS_NULLSTR(arg2))
{
chprintln(ch, "To identify an item, type 'QUEST IDENTIFY <item>'.");
return;
}
for (i = 0; quest_table[i].name != NULL; i++)
{
if (is_name(arg2, quest_table[i].name))
{
if (quest_table[i].vnum == 0)
{
chprintln(ch, "That isn't a quest item.");
return;
}
else if ((obj =
create_object(get_obj_index
(quest_table[i].vnum),
ch->level)) == NULL)
{
chprintln(ch,
"That object could not be found, contact an immortal.");
return;
}
else
{
if (!IS_SET(obj->pIndexData->extra_flags, ITEM_QUEST))
{
SET_BIT(obj->pIndexData->extra_flags, ITEM_QUEST);
SET_BIT(obj->extra_flags, ITEM_QUEST);
}
obj_to_char(obj, ch);
chprintlnf(ch, "%s costs %d questpoints.",
obj->short_descr, quest_table[i].cost);
spell_identify(0, ch->level, ch, obj, TAR_OBJ_INV);
extract_obj(obj);
return;
}
}
}
mob_tell(ch, questman, "I don't have that item, %s.", ch->name);
return;
}
else if (!str_cmp(arg1, "request"))
{
act("$n asks $N for a quest.", ch, NULL, questman, TO_ROOM);
act("You ask $N for a quest.", ch, NULL, questman, TO_CHAR);
if (IS_SET(ch->act, PLR_QUESTOR))
{
mob_tell(ch, questman, "But you're already on a quest!");
return;
}
if (ch->pcdata->nextquest > 0)
{
mob_tell(ch, questman,
"You're very brave, %s, but let someone else have a chance.",
ch->name);
mob_tell(ch, questman, "Come back later.");
return;
}
mob_tell(ch, questman, "Thank you, brave %s!", ch->name);
ch->pcdata->questmob = 0;
ch->pcdata->questobj = 0;
generate_quest(ch, questman);
return;
}
else if (!str_cmp(arg1, "complete"))
{
if (ch->pcdata->questgiver != questman->pIndexData->vnum)
{
mob_tell(ch, questman,
"I never sent you on a quest! Perhaps you're thinking of someone else.");
return;
}
if (IS_SET(ch->act, PLR_QUESTOR))
{
int reward, points;
if (ch->pcdata->questmob == -1 && ch->pcdata->countdown > 0)
{
reward = number_range(125, 375);
points = number_range(25, 70);
act("$n informs $N $e has completed $s quest.",
ch, NULL, questman, TO_ROOM);
act("You inform $N you have completed $s quest.",
ch, NULL, questman, TO_CHAR);
mob_tell(ch, questman,
"Congratulations on completing your quest!");
mob_tell(ch, questman,
"As a reward, I am giving you %d quest points, and %d gold.",
points, reward);
if (chance(points / 5))
{
chprintln(ch, "You gain an extra Trivia Point!");
ch->pcdata->trivia += 1;
}
end_quest(ch, QUEST_TIME);
ch->gold += reward;
ch->pcdata->questpoints += points;
save_char_obj(ch);
return;
}
else if (ch->pcdata->questobj > 0 && ch->pcdata->countdown > 0)
{
if ((obj = has_questobj(ch)) != NULL)
{
reward = number_range(125, 375);
points = number_range(25, 75);
act("$n informs $N $e has completed $s quest.",
ch, NULL, questman, TO_ROOM);
act("You inform $N you have completed $s quest.",
ch, NULL, questman, TO_CHAR);
act("You hand $p to $N.", ch, obj, questman, TO_CHAR);
act("$n hands $p to $N.", ch, obj, questman, TO_ROOM);
mob_tell(ch, questman,
"Congratulations on completing your quest!");
mob_tell(ch, questman,
"As a reward, I am giving you %d quest points, and %d gold.",
points, reward);
if (chance(points / 5))
{
chprintln(ch, "You gain an extra Trivia Point!");
ch->pcdata->trivia += 1;
}
end_quest(ch, QUEST_TIME);
ch->gold += reward;
ch->pcdata->questpoints += points;
extract_obj(obj);
save_char_obj(ch);
return;
}
else
{
mob_tell(ch, questman,
"You haven't completed the quest yet, but there is still time!");
return;
}
return;
}
else if ((ch->pcdata->questmob > 0
|| ch->pcdata->questobj > 0) && ch->pcdata->countdown > 0)
{
mob_tell(ch, questman,
"You haven't completed the quest yet, but there is still time!");
return;
}
}
if (ch->pcdata->nextquest > 0)
mob_tell(ch, questman,
"But you didn't complete your quest in time!");
else
mob_tell(ch, questman, "You have to REQUEST a quest first, %s.",
ch->name);
return;
}
else if (!str_cmp(arg1, "quit") || !str_cmp(arg1, "fail"))
{
act("$n informs $N $e wishes to quit $s quest.", ch, NULL,
questman, TO_ROOM);
act("You inform $N you wish to quit $s quest.", ch, NULL,
questman, TO_CHAR);
if (ch->pcdata->questgiver != questman->pIndexData->vnum)
{
mob_tell(ch, questman,
"I never sent you on a quest! Perhaps you're thinking of someone else.");
return;
}
if (IS_SET(ch->act, PLR_QUESTOR))
{
end_quest(ch, QUEST_TIME - 2);
mob_tell(ch, questman,
"Your quest is over, but for your cowardly behavior, you may not quest again for 15 minutes.");
return;
}
else
{
chprintln(ch, "You aren't on a quest!");
return;
}
}
quest_usage(ch);
return;
}
#define MAX_QMOB_COUNT mobile_count
void generate_quest(CHAR_DATA * ch, CHAR_DATA * questman)
{
CHAR_DATA *victim = NULL;
ROOM_INDEX_DATA *room = NULL;
CHAR_DATA **mobs;
int mob_count;
OBJ_DATA *questitem = NULL;
int mrange;
/*
* * find MAX_QMOB_COUNT quest mobs and store their vnums in mob_buf
*/
alloc_mem(mobs, CHAR_DATA *, MAX_QMOB_COUNT);
mob_count = 0;
for (victim = char_first; victim; victim = victim->next)
{
if (!IS_NPC(victim)
|| !quest_level_diff(ch, victim)
|| victim->pIndexData == NULL
|| victim->in_room == NULL
|| victim->pIndexData->pShop != NULL
|| (IS_EVIL(victim) && IS_EVIL(ch) && chance(50))
|| (IS_GOOD(victim) && IS_GOOD(ch) && chance(50))
|| victim->pIndexData->vnum < 100
|| victim->in_room->clan != NULL
|| IS_SET(victim->imm_flags, IMM_WEAPON | IMM_MAGIC)
|| IS_SET(victim->act,
ACT_TRAIN | ACT_PRACTICE | ACT_IS_HEALER | ACT_PET
| ACT_PET | ACT_GAIN)
|| IS_SET(victim->affected_by, AFF_CHARM)
|| IS_SET(victim->in_room->room_flags, ROOM_PET_SHOP)
|| questman->pIndexData == victim->pIndexData
|| (IS_SET(victim->act, ACT_SENTINEL)
&& IS_SET(victim->in_room->room_flags,
ROOM_PRIVATE | ROOM_SOLITARY | ROOM_SAFE)))
continue;
mobs[mob_count++] = victim;
if (mob_count >= MAX_QMOB_COUNT)
break;
}
if (mob_count == 0) /* not likely but just in case */
{
mob_tell(ch, questman,
"I'm sorry, but I don't have any quests for you at this time.");
mob_tell(ch, questman, "Try again later.");
end_quest(ch, QUEST_TIME / 10);
free_mem(mobs);
return;
}
/*
* at this point the player is sure to get a quest
*/
ch->pcdata->questgiver = questman->pIndexData->vnum;
mrange = number_range(0, mob_count - 1);
while ((victim = mobs[mrange]) == NULL)
mrange = number_range(0, mob_count - 1);
room = victim->in_room;
ch->pcdata->questloc = room->vnum;
/* countdown set here so we can use it for object timers */
ch->pcdata->countdown = number_range(12, 30);
/*
* 20% chance it will send the player on a 'recover item' quest.
*/
if (chance(20))
{
vnum_t objvnum = 0;
switch (number_range(0, 3))
{
case 0:
objvnum = QUEST_OBJQUEST1;
break;
case 1:
objvnum = QUEST_OBJQUEST2;
break;
case 2:
objvnum = QUEST_OBJQUEST3;
break;
case 3:
objvnum = QUEST_OBJQUEST4;
break;
}
questitem = create_object(get_obj_index(objvnum), ch->level);
obj_to_room(questitem, room);
REMOVE_BIT(ch->act, PLR_CANLOOT);
replace_string(questitem->owner, ch->name);
questitem->cost = 0;
questitem->timer = (4 * ch->pcdata->countdown + 10) / 3;
ch->pcdata->questobj = questitem->pIndexData->vnum;
switch (number_range(0, 1))
{
default:
case 0:
mob_tell(ch, questman,
"Vile pilferers have stolen %s from the royal treasury!",
questitem->short_descr);
mob_tell(ch, questman,
"My court wizardess, with her magic mirror, has pinpointed its location.");
break;
case 1:
mob_tell(ch, questman,
"A powerful wizard has stolen %s for his personal power!",
questitem->short_descr);
break;
}
if (room->name != NULL)
{
mob_tell(ch, questman,
"Look for %s somewhere in the vicinity of %s!",
questitem->short_descr, room->name);
mob_tell(ch, questman,
"That location is in the general area of %s.",
room->area->name);
}
}
/*
* Quest to kill a mob
*/
else
{
switch (number_range(0, 3))
{
default:
case 0:
mob_tell(ch, questman,
"An enemy of mine, %s, is making vile threats against the crown.",
victim->short_descr);
mob_tell(ch, questman, "This threat must be eliminated!");
break;
case 1:
mob_tell(ch, questman,
"Thera's most heinous criminal, %s, has escaped from the dungeon!",
victim->short_descr);
mob_tell(ch, questman,
"Since the escape, %s has murdered %d civillians!",
victim->short_descr, number_range(2, 20));
mob_tell(ch, questman,
"The penalty for this crime is death, and you are to deliver the sentence!");
break;
case 2:
mob_tell(ch, questman,
"The Mayor of Midgaard has recently been attacked by %s. This is an act of war!",
victim->short_descr);
mob_tell(ch, questman,
"%s must be severly dealt with for this injustice.",
victim->short_descr);
break;
case 3:
mob_tell(ch, questman,
"%s has been stealing valuables from the citizens of Arkham.",
victim->short_descr);
mob_tell(ch, questman,
"Make sure that %s never has the chance to steal again.",
victim->short_descr);
break;
}
if (room->name != NULL)
{
mob_tell(ch, questman,
"Seek %s out somewhere in the vicinity of %s!",
victim->short_descr, room->name);
mob_tell(ch, questman,
"That location is in the general area of %s.",
room->area->name);
}
ch->pcdata->questmob = victim->pIndexData->vnum;
}
if (ch->pcdata->questmob > 0 || ch->pcdata->questobj > 0)
{
SET_BIT(ch->act, PLR_QUESTOR);
mob_tell(ch, questman, "You have %d minutes to complete this quest.",
ch->pcdata->countdown);
mob_tell(ch, questman, "May the gods go with you!");
}
else
end_quest(ch, QUEST_TIME / 10);
free_mem(mobs);
return;
}
bool quest_level_diff(CHAR_DATA * ch, CHAR_DATA * mob)
{
int bonus = 10 + lvl_bonus(ch); /* can modify this */
if (IS_IMMORTAL(ch))
return TRUE;
else if (ch->level > (mob->level + bonus)
|| ch->level < (mob->level - bonus))
return FALSE;
else
return TRUE;
}
void quest_update(void)
{
DESCRIPTOR_DATA *d;
CHAR_DATA *ch;
for (d = descriptor_first; d != NULL; d = d->next)
{
if (d->connected == CON_PLAYING
&& (ch = d->original ? d->original : d->character) != NULL)
{
if (ch->pcdata->nextquest > 0)
{
ch->pcdata->nextquest--;
if (ch->pcdata->nextquest == 0)
{
chprintln(ch, "You may now quest again.");
return;
}
}
else if (IS_SET(ch->act, PLR_QUESTOR))
{
if (--ch->pcdata->countdown <= 0)
{
end_quest(ch, QUEST_TIME - 2);
chprintlnf(ch,
"You have run out of time for your quest!\n\rYou may quest again in %d minutes.",
ch->pcdata->nextquest);
}
if (ch->pcdata->countdown > 0 && ch->pcdata->countdown < 6)
{
chprintln(ch,
"Better hurry, you're almost out of time for your quest!");
return;
}
}
}
}
return;
}
void end_quest(CHAR_DATA * ch, int time)
{
REMOVE_BIT(ch->act, PLR_QUESTOR);
ch->pcdata->questgiver = 0;
ch->pcdata->countdown = 0;
ch->pcdata->questmob = 0;
ch->pcdata->questobj = 0;
ch->pcdata->questloc = 0;
ch->pcdata->nextquest = time;
}
/* handy dandy, victim must be an NPC */
void mob_tell(CHAR_DATA * ch, CHAR_DATA * victim, const char *argument, ...)
{
char buf[MSL];
va_list args;
if (!victim || IS_NULLSTR(argument))
return;
va_start(args, argument);
vsnprintf(buf, sizeof(buf), argument, args);
va_end(args);
chprintlnf(ch, "" CTAG(_TELLS1) "%s " CTAG(_TELLS1) "tells you '"
CTAG(_TELLS2) "%s" CTAG(_TELLS1) "'{x",
IS_NPC(victim) ? victim->short_descr : victim->name, buf);
return;
}
CH_CMD(do_tpspend)
{
CHAR_DATA *triviamob;
char buf[MAX_STRING_LENGTH];
char arg1[MAX_INPUT_LENGTH];
char arg2[MAX_INPUT_LENGTH];
int sav_trust;
argument = one_argument(argument, arg1);
strcpy(arg2, argument);
if (!ch || IS_NPC(ch))
return;
if (IS_NULLSTR(arg1))
{
chprintln(ch, "Syntax: Tpspend <item>");
chprintln(ch, " Tpspend list");
return;
}
if (is_name(arg1, "list"))
{
chprintln(ch, "Trivia Point Options");
chprintln(ch, "corpse retrival......1tp");
chprintln(ch, "transfer.............1tp");
chprintln(ch, "restore..............1tp");
chprintln(ch, "5 trains.............1tp");
chprintln(ch, "40 practices.........1tp");
chprintln(ch, "75 questpoints.......1tp");
chprintln(ch, "1 Trivia Pill........1tp");
chprintln(ch, "See HELP TRIVIA for important info before buying.");
chprintln(ch,
"For transfers and corpses, you do not have to be at a trivia shop.");
return;
}
else if (is_name(arg1, "corpse"))
{
if (ch->pcdata->trivia >= 1)
{
OBJ_DATA *c;
int count = 0;
for (c = object_first; c != NULL; c = c->next)
{
if (is_name(ch->name, c->owner)
&& c->item_type == ITEM_CORPSE_PC)
{
if (c->in_room)
obj_from_room(c);
else if (c->carried_by)
obj_from_char(c);
else if (c->in_obj)
obj_from_obj(c->in_obj);
obj_to_room(c, ch->in_room);
count++;
}
}
if (count == 0)
chprintln(ch, "You have no corpses in the game.");
else
{
ch->pcdata->trivia -= 1;
if (count == 1)
{
chprintln(ch, "Your corpse appears in the room.");
act("$n's corpse appears in the room.",
ch, NULL, NULL, TO_ROOM);
}
else
{
chprintln(ch, "All your corpses appear in the room.");
act("All of $n's corpses appear in the room.",
ch, NULL, NULL, TO_ROOM);
}
}
return;
}
else
{
chprintln(ch, "You don't have enough trivia points for that.");
return;
}
}
else if (is_name(arg1, "transfer"))
{
if (ch->pcdata->trivia >= 1)
{
ROOM_INDEX_DATA *oldroom;
if (IS_NULLSTR(arg2))
{
chprintln(ch,
"Transfer you where? [recall/room name/character name]");
return;
}
else if (!str_cmp(arg2, "recall"))
sprintf(arg2, "%d", ROOM_VNUM_TEMPLE);
oldroom = ch->in_room;
sprintf(buf, "self '%s'", arg2);
sav_trust = ch->trust;
ch->trust = MAX_LEVEL;
do_function(ch, &do_transfer, buf);
ch->trust = sav_trust;
if (oldroom != ch->in_room)
ch->pcdata->trivia -= 1;
else
chprintln(ch,
"Whoops! You were not charged for that transfer.");
return;
}
else
{
chprintln(ch, "You don't have enough trivia points for that.");
return;
}
}
for (triviamob = ch->in_room->first_person; triviamob != NULL;
triviamob = triviamob->next_in_room)
{
if (!IS_NPC(triviamob))
continue;
if (triviamob->spec_fun == spec_lookup("spec_triviamob"))
break;
}
if (triviamob == NULL
|| triviamob->spec_fun != spec_lookup("spec_triviamob"))
{
chprintln(ch, "You can't do that here.");
return;
}
if (triviamob->fighting != NULL)
{
chprintln(ch, "Wait until the fighting stops.");
return;
}
if (is_name(arg1, "practices pracs practice"))
{
if (ch->pcdata->trivia >= 1)
{
ch->pcdata->trivia -= 1;
ch->practice += 40;
act("$N gives 40 practices to $n.", ch, NULL, triviamob, TO_ROOM);
act("$N gives you 40 practices.", ch, NULL, triviamob, TO_CHAR);
return;
}
else
{
mob_tell(ch, triviamob,
"Sorry, %s, but you don't have enough trivia points for that.",
ch->name);
return;
}
}
else if (is_name(arg1, "trains train"))
{
if (ch->pcdata->trivia >= 1)
{
ch->pcdata->trivia -= 1;
ch->train += 5;
act("$N gives 5 training sessions to $n.", ch, NULL,
triviamob, TO_ROOM);
act("$N gives you 5 training sessions.", ch, NULL,
triviamob, TO_CHAR);
return;
}
else
{
mob_tell(ch, triviamob,
"Sorry, %s, but you don't have enough trivia points for that.",
ch->name);
return;
}
}
else if (is_name(arg1, "questpoints points"))
{
if (ch->pcdata->trivia >= 1)
{
ch->pcdata->trivia -= 1;
ch->pcdata->questpoints += 75; /* a trivia point costs 100 quest points 25% conversion */
act("$N gives 75 questpoints to $n.", ch, NULL, triviamob, TO_ROOM);
act("$N gives you 75 questpoints.", ch, NULL, triviamob, TO_CHAR);
return;
}
else
{
mob_tell(ch, triviamob,
"Sorry, %s, but you don't have enough trivia points for that.",
ch->name);
return;
}
}
else if (is_name(arg1, "pill"))
{
OBJ_DATA *obj = NULL;
if (ch->pcdata->trivia >= 1)
{
obj = create_object(get_obj_index(OBJ_VNUM_TRIVIA_PILL), 1);
if (obj != NULL)
{
act("$N gives $p to $n.", ch, obj, triviamob, TO_ROOM);
act("$N gives you $p.", ch, obj, triviamob, TO_CHAR);
obj_to_char(obj, ch);
ch->pcdata->trivia -= 1;
return;
}
else
{
mob_tell(ch, triviamob,
"I don't any more trivia pills to give, %s.",
ch->name);
}
return;
}
else
{
mob_tell(ch, triviamob,
"Sorry, %s, but you don't have enough trivia points for that.",
ch->name);
return;
}
}
else if (is_name(arg1, "restore"))
{
if (ch->pcdata->trivia >= 1)
{
sav_trust = ch->trust;
ch->trust = MAX_LEVEL;
do_function(ch, &do_restore, "all");
ch->trust = sav_trust;
ch->pcdata->trivia -= 1;
return;
}
else
{
mob_tell(ch, triviamob,
"Sorry, %s, but you don't have enough trivia points for that.",
ch->name);
return;
}
}
else
do_tpspend(ch, "list");
}
CH_CMD(do_qpgive)
{
CHAR_DATA *victim;
char arg[MAX_INPUT_LENGTH], buf[MAX_INPUT_LENGTH];
int amount;
if (!ch || IS_NPC(ch))
return;
argument = one_argument(argument, arg);
if (IS_NULLSTR(argument) || !is_number(arg))
{
chprintln(ch, "Syntax: qpgive [amount] [person]");
return;
}
if ((amount = atoi(arg)) <= 0)
{
chprintln(ch, "Give how many questpoints?");
return;
}
if (amount > ch->pcdata->questpoints)
{
chprintln(ch, "You don't have that many questpoints to give.");
return;
}
if ((victim = get_char_room(ch, NULL, argument)) == NULL)
{
chprintln(ch, "That person is not here.");
return;
}
if (IS_NPC(victim))
{
chprintln(ch, "NPC's don't need quest points.");
return;
}
if (victim == ch)
{
sprintf(buf,
"You give yourself %d questpoints..... don't you feel better?\n\r",
amount);
return;
}
ch->pcdata->questpoints -= amount;
victim->pcdata->questpoints += amount;
sprintf(buf, "%d", amount);
act("$n gives you $t questpoints.", ch, buf, victim, TO_VICT);
act("You give $N $t questpoints.", ch, buf, victim, TO_CHAR);
act("$n gives $N $t questpoints.", ch, buf, victim, TO_ROOM);
return;
}
CH_CMD(do_tpgive)
{
CHAR_DATA *victim;
char arg[MAX_INPUT_LENGTH], buf[MAX_INPUT_LENGTH];
int amount;
if (!ch || IS_NPC(ch))
return;
argument = one_argument(argument, arg);
if (IS_NULLSTR(argument) || !is_number(arg))
{
chprintln(ch, "Syntax: tpgive [amount] [person]");
return;
}
if ((amount = atoi(arg)) <= 0)
{
chprintln(ch, "Give how many trivia points?");
return;
}
if (amount > ch->pcdata->trivia)
{
chprintln(ch, "You don't have that many trivia points to give!!");
return;
}
if ((victim = get_char_room(ch, NULL, argument)) == NULL)
{
chprintln(ch, "That person is not here.");
return;
}
if (IS_NPC(victim))
{
chprintln(ch, "NPC's don't need trivia points.");
return;
}
if (victim == ch)
{
sprintf(buf,
"You give yourself %d trivia points..... don't you feel better?\n\r",
amount);
return;
}
ch->pcdata->trivia -= amount;
victim->pcdata->trivia += amount;
sprintf(buf, "%d", amount);
act("$n gives you $t questpoints.", ch, buf, victim, TO_VICT);
act("You give $N $t questpoints.", ch, buf, victim, TO_CHAR);
act("$n gives $N $t questpoints.", ch, buf, victim, TO_ROOM);
return;
}