/************************************************************************** * 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-2002 by Ryan Jennings * * http://1stmud.dlmud.com/ <r-jenn@shaw.ca> * ***************************************************************************/ #include <ctype.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <time.h> #include "merc.h" #include "recycle.h" typedef struct gquest_hist GQUEST_HIST; struct gquest_hist { GQUEST_HIST *next; GQUEST_HIST *prev; const char *short_descr; const char *text; }; GQUEST_HIST *gqhist_first = NULL; GQUEST_HIST *gqhist_last = NULL; bool start_gquest(CHAR_DATA * ch, const char *argument) { char arg1[MAX_INPUT_LENGTH], arg2[MAX_INPUT_LENGTH]; char arg3[MAX_INPUT_LENGTH]; char buf[MAX_STRING_LENGTH]; CHAR_DATA *registar = NULL; int mobs, blevel, elevel, cost; for (registar = ch->in_room->first_person; registar != NULL; registar = registar->next_in_room) { if (!IS_NPC(registar)) continue; if (registar->spec_fun == spec_lookup("spec_registar")) break; } if (!IS_IMMORTAL(ch) && (registar == NULL || registar->spec_fun != spec_lookup("spec_registar"))) { chprintln(ch, "You can't do that here."); return FALSE; } if (!IS_IMMORTAL(ch)) { if (registar->fighting != NULL) { chprintln(ch, "Wait until the fighting stops."); return FALSE; } if (gquest_info.last_registar == ch) { chprintln(ch, "Let someone else have a chance."); return FALSE; } } argument = one_argument(argument, arg1); argument = one_argument(argument, arg2); argument = one_argument(argument, arg3); if (arg1[0] == '\0' || arg2[0] == '\0' || arg3[0] == '\0') { chprintln(ch, "Syntax: gquest start <min level> <max level> <#mobs>"); return FALSE; } blevel = atoi(arg1); elevel = atoi(arg2); mobs = atoi(arg3); if (blevel <= 0 || blevel > MAX_LEVEL) { chprintlnf(ch, "Level must be between 1 and %d.", MAX_LEVEL); return FALSE; } if (blevel <= 0 || elevel > MAX_LEVEL) { chprintlnf(ch, "Level must be between 1 and %d.", MAX_LEVEL); return FALSE; } if (elevel <= blevel) { chprintln(ch, "Max level must be greater than the min level."); return FALSE; } if (elevel - blevel < 10) { chprintln(ch, "Level difference must 10 levels or higher."); return FALSE; } if (mobs < 5 || mobs >= MAX_GQUEST_MOB) { chprintlnf(ch, "Number of mobs must be between 5 and %d.", MAX_GQUEST_MOB - 1); return FALSE; } if (gquest_info.running != GQUEST_OFF) { chprintln(ch, "There is already a global quest running!"); return FALSE; } cost = 5 + (mobs / 5); if (!IS_IMMORTAL(ch)) { if (ch->pcdata->trivia < cost) { sprintf(buf, "$N tells you 'It costs %d Trivia Points to start a global quest with %d mobs.'", cost, mobs); act(buf, ch, NULL, registar, TO_CHAR); return FALSE; } else { sprintf(buf, "$N tells you '%d mobs have cost you %d trivia points.'", mobs, cost); act(buf, ch, NULL, registar, TO_CHAR); ch->pcdata->trivia -= cost; } } gquest_info.running = GQUEST_WAITING; gquest_info.minlevel = blevel; gquest_info.maxlevel = elevel; gquest_info.mob_count = mobs; free_string(gquest_info.who); gquest_info.who = str_dup(ch->name); if (!generate_gquest(ch)) { if (!IS_IMMORTAL(ch)) { chprintlnf(ch, "Failed to start Gquest, you are being reimbursed %d TP.", cost); ch->pcdata->trivia += cost; } else chprintln(ch, "Failed to start a gquest, not enogh mobs found."); return FALSE; } return TRUE; } void auto_gquest(void) { CHAR_DATA *wch = NULL, *registar = NULL; int middle = LEVEL_HERO / 2, maxlvl = 0; int minlvl = MAX_LEVEL, count = 0, lbonus = 0, half = 0; if (gquest_info.running != GQUEST_OFF) return; for (wch = char_first; wch != NULL; wch = wch->next) { if (!IS_NPC(wch) && !IS_IMMORTAL(wch)) { count++; maxlvl = UMAX(maxlvl, wch->level); minlvl = UMIN(minlvl, wch->level); } } if (count < 1) { end_gquest(gquest_info.last_registar); return; } /* all this is basically so level ranges aren't to far apart */ lbonus = number_range(5, 10); minlvl = UMAX(1, minlvl - lbonus); maxlvl = UMIN(LEVEL_HERO, maxlvl + lbonus); half = ((maxlvl - minlvl) / 2); middle = URANGE(minlvl, maxlvl - half, maxlvl); minlvl = number_range(minlvl, middle - lbonus); maxlvl = number_range(middle + lbonus, maxlvl); /* find the registar mob if he exits, (not needed only put in for RP aspects) */ for (registar = char_first; registar != NULL; registar = registar->next) { if (!IS_NPC(registar)) continue; if (registar->pIndexData->vnum == MOB_VNUM_REGISTAR) break; } gquest_info.running = GQUEST_WAITING; gquest_info.mob_count = number_range(5, MAX_GQUEST_MOB - lbonus); gquest_info.minlevel = UMAX(1, minlvl); gquest_info.maxlevel = UMIN(LEVEL_HERO, maxlvl); free_string(gquest_info.who); gquest_info.who = !registar ? str_dup("AutoQuest (tm)") : str_dup(registar->short_descr); generate_gquest(registar); return; } void post_gquest(CHAR_DATA * ch) { BUFFER *output; CHAR_DATA *wch; MOB_INDEX_DATA *mob; int i; GQUEST_HIST *hist; char *strtime; char shortd[MAX_INPUT_LENGTH]; char buf[MAX_STRING_LENGTH]; if (gquest_info.running == GQUEST_OFF || gquest_info.involved == 0) return; alloc_mem(hist, GQUEST_HIST, 1); strtime = ctime(¤t_time); strtime[strlen(strtime) - 1] = '\0'; sprintf(shortd, "%24s %3d %3d %4d %12s\n\r", strtime, gquest_info.minlevel, gquest_info.maxlevel, gquest_info.mob_count, ch->name); hist->short_descr = str_dup(shortd); output = new_buf(); sprintf(buf, "GLOBAL QUEST INFO\n\r-----------------\n\r"); add_buf(output, buf); sprintf(buf, "Started by : %s\n\r", gquest_info.who[0] == '\0' ? "Unknown" : gquest_info.who); add_buf(output, buf); sprintf(buf, "Levels : %d - %d\n\r", gquest_info.minlevel, gquest_info.maxlevel); add_buf(output, buf); sprintf(buf, "Those Playing\n\r-------------\n\r"); add_buf(output, buf); for (wch = char_first; wch != NULL; wch = wch->next) if (!IS_NPC(ch) && ON_GQUEST(wch) && count_gqmobs(wch) != gquest_info.mob_count) sprintf(buf, "%s [%d mobs left]\n\r", wch->name, gquest_info.mob_count - count_gqmobs(wch)); add_buf(output, buf); sprintf(buf, "%s won the GQuest.\n\r", ch->name); add_buf(output, buf); sprintf(buf, "Quest Rewards\n\r-------------\n\r"); add_buf(output, buf); sprintf(buf, "Qp Reward : %d + 3 QPs for each target.\n\r", gquest_info.qpoints); add_buf(output, buf); sprintf(buf, "Gold Reward : %d\n\r", gquest_info.gold); add_buf(output, buf); sprintf(buf, "Quest Targets\n\r-------------\n\r"); add_buf(output, buf); for (i = 0; i < gquest_info.mob_count; i++) { if ((mob = get_mob_index(gquest_info.mobs[i])) != NULL) { sprintf(buf, "%2d) [%-20s] %-30s (level %3d)\n\r", i + 1, mob->area->name, mob->short_descr, mob->level); add_buf(output, buf); } } hist->text = str_dup(buf_string(output)); LINK(hist, gqhist_first, gqhist_last, next, prev); free_buf(output); return; } CH_CMD(do_gquest) { char arg1[MAX_INPUT_LENGTH]; char buf[MAX_STRING_LENGTH]; CHAR_DATA *wch; MOB_INDEX_DATA *mob; int i = 0; if (IS_NPC(ch)) { chprintln(ch, "Your the victim not the player."); return; } argument = one_argument(argument, arg1); if (arg1[0] == '\0') { chprintln(ch, "Syntax: gquest join - join a global quest\n\r" " gquest quit - quit the global quest\n\r" " gquest info - show global quest info\n\r" " gquest time - show global quest time\n\r" " gquest check - show what targets you have left\n\r" " gquest progress - show progress of other players\n\r" " gquest complete - completes the current quest\n\r" " gquest hist - shows gquest history since last reboot\n\r" " gquest start - starts a gquest"); if (IS_IMMORTAL(ch)) { chprintln(ch, " gquest end - ends the gquest (IMM)"); chprintln(ch, " gquest next - sets time to next gquest."); } return; } else if (!str_prefix(arg1, "start")) { start_gquest(ch, argument); return; } else if (!str_prefix(arg1, "next") && IS_IMMORTAL(ch)) { if (gquest_info.running != GQUEST_OFF) { chprintln(ch, "Not while a gquest is running."); return; } i = is_number(argument) ? atoi(argument) : number_range(30, 100); gquest_info.next = i; chprintlnf(ch, "The next gquest will start in %d minutes.", gquest_info.next); return; } else if (!str_prefix(arg1, "hist")) { GQUEST_HIST *hist; int count = 0; if (!gqhist_first) { chprintln(ch, "No global quests completed yet."); return; } if (argument[0] == '\0') { BUFFER *output = new_buf(); add_buf(output, "Num Finished Time Levels Mobs Completed by\n\r" "--- ------------------------ ------- ---- ------------\n\r"); for (hist = gqhist_first; hist != NULL; hist = hist->next) { sprintf(buf, "%2d) ", ++count); add_buf(output, buf); add_buf(output, hist->short_descr); } add_buf(output, "Type 'gquest hist #' to view details.\n\r"); page_to_char(buf_string(output), ch); free_buf(output); } else { bool found = FALSE; if (!is_number(argument)) { chprintln(ch, "Syntax: gquest hist #"); return; } for (hist = gqhist_first; hist != NULL; hist = hist->next) if (++count == atoi(argument)) { chprint(ch, hist->text); found = TRUE; } if (!found) chprintln(ch, "History data not found."); } return; } else if (gquest_info.running == GQUEST_OFF) { chprintlnf(ch, "There is no global quest running. The next Gquest will start in %d minutes.", gquest_info.next); return; } else if (!str_prefix(arg1, "end") && IS_IMMORTAL(ch)) { end_gquest(gquest_info.last_registar); chprintlnf(ch, "You end the global quest. Next autoquest in %d minutes.", gquest_info.next); announce(ch, INFO_GQUEST, "$n has ended the global quest. Next gquest in %d minutes.", gquest_info.next); return; } else if (!str_prefix(arg1, "join")) { /* see dlm_quest.c */ if (IS_QUESTOR(ch)) { chprintln(ch, "Why don't you finish your other quest first."); return; } if (ON_GQUEST(ch)) { chprintln(ch, "Your allready in the global quest."); return; } if (gquest_info.minlevel > ch->level || gquest_info.maxlevel < ch->level) { chprintln(ch, "This gquest is not in your level range."); return; } if (!IS_NPC(ch) && IS_SET(ch->act, PLR_WAR)) { chprintlnf(ch, "Your %s combat right now.\n\r", war_info.iswar == WAR_WAITING ? "waiting for" : "in"); return; } /* slight hack here */ if (count_gqmobs(ch) == gquest_info.mob_count) { chprintln(ch, "You have already quit this gquest."); return; } for (i = 0; i < gquest_info.mob_count; i++) ch->pcdata->gq_mobs[i] = gquest_info.mobs[i]; SET_BIT(ch->act, PLR_GQUEST); gquest_info.involved++; chprintln (ch, "Your global quest flag is now on. Use 'gquest info' to see the quest(s)."); announce(ch, INFO_GQUEST, "$n has joined the global quest."); return; } else if (!str_prefix(arg1, "quit")) { if (!ON_GQUEST(ch)) { chprintln(ch, "Your not in a global quest."); return; } /* hack to prevent coming back */ reset_gqmob(ch, -1); REMOVE_BIT(ch->act, PLR_GQUEST); gquest_info.involved--; chprintln(ch, "Your global quest flag is now off. Sorry you couldn't complete it."); announce(ch, INFO_GQUEST, "$n has quit the global quest, what a sore loser."); return; } else if (!str_prefix(arg1, "info")) { chprintln(ch, "[ GLOBAL QUEST INFO ]"); chprintlnf(ch, "Started by : %s", gquest_info.who[0] == '\0' ? "Unknown" : gquest_info.who); chprintlnf(ch, "Playing : %d player%s.", gquest_info.involved, gquest_info.involved == 1 ? "" : "s"); chprintlnf(ch, "Levels : %d - %d", gquest_info.minlevel, gquest_info.maxlevel); chprintlnf(ch, "Status : %s for %d minute%s.", gquest_info.running == GQUEST_WAITING ? "Waiting" : "Running", gquest_info.timer, gquest_info.timer == 1 ? "" : "s"); chprintln(ch, "[ Quest Rewards ]"); chprintlnf(ch, "Qp Reward : %d", gquest_info.qpoints); chprintlnf(ch, "Gold Reward : %d", gquest_info.gold); chprintln(ch, "[ Quest Targets ]"); for (i = 0; i < gquest_info.mob_count; i++) { if ((mob = get_mob_index(gquest_info.mobs[i])) != NULL) { chprintlnf(ch, "%2d) [%-20s] %-30s (level %3d)", i + 1, mob->area->name, smash_colour(mob->short_descr), mob->level); } } return; } else if (!str_prefix(arg1, "time")) { if (gquest_info.next > 0) sprintf(buf, "THe next Global Quest will start in %d minute%s.\n\r", gquest_info.next, gquest_info.next == 1 ? "" : "s"); else chprintlnf(ch, "The Global Quest is %s for %d minute%s.", gquest_info.running == GQUEST_WAITING ? "Waiting" : "Running", gquest_info.timer, gquest_info.timer == 1 ? "" : "s"); return; } else if (!str_prefix(arg1, "progress")) { if (gquest_info.running == GQUEST_WAITING) { chprintln(ch, "The global quest hasn't started yet."); return; } for (wch = char_first; wch != NULL; wch = wch->next) { if (!IS_NPC(wch) && ON_GQUEST(wch) && wch != ch) { chprintlnf(ch, "%-12s has %d of %d mobs left.", wch->name, gquest_info.mob_count - count_gqmobs(wch), gquest_info.mob_count); } } return; } else if (!str_prefix(arg1, "check")) { if (IS_IMMORTAL(ch) && argument[0] != '\0') { if ((wch = get_char_world(ch, argument)) == NULL || IS_NPC(wch)) { chprintln(ch, "That player is not here."); return; } } else wch = ch; if (!ON_GQUEST(wch)) { chprintlnf(ch, "%s aren't on a global quest.", wch == ch ? "You" : wch->name); return; } chprintlnf(ch, "[ %s have %d of %d mobs left ]", wch == ch ? "You" : wch->name, gquest_info.mob_count - count_gqmobs(wch), gquest_info.mob_count); for (i = 0; i < gquest_info.mob_count; i++) { if ((mob = get_mob_index(wch->pcdata->gq_mobs[i])) != NULL) { chprintlnf(ch, "%2d) [%-20s] %-30s (level %3d)", i + 1, mob->area->name, mob->short_descr, mob->level); } } return; } else if (!str_prefix(arg1, "complete")) { if (!ON_GQUEST(ch)) { chprintln(ch, "Your not in a global quest."); return; } if (count_gqmobs(ch) != gquest_info.mob_count) { chprintlnf(ch, "You haven't finished just yet, theres still %d mobs to kill.", gquest_info.mob_count - count_gqmobs(ch)); return; } chprintln(ch, "YES! You have completed the global quest."); ch->pcdata->questpoints += gquest_info.qpoints; ch->gold += gquest_info.gold; post_gquest(ch); chprintlnf(ch, "You receive %d gold and %d quest points.", gquest_info.gold, gquest_info.qpoints); end_gquest(gquest_info.last_registar); sprintf(buf, "$n has completed the global quest, next gquest in %d minutes.", gquest_info.next); announce(ch, INFO_GQUEST, buf); return; } else do_gquest(ch, ""); return; } void end_gquest(CHAR_DATA * who) { CHAR_DATA *wch; gquest_info.running = GQUEST_OFF; free_string(gquest_info.who); gquest_info.who = str_dup(""); gquest_info.mob_count = 0; gquest_info.timer = 0; gquest_info.involved = 0; gquest_info.qpoints = 0; gquest_info.gold = 0; gquest_info.minlevel = 0; gquest_info.maxlevel = 0; gquest_info.next = number_range(100, 200); reset_gqmob(NULL, 0); gquest_info.last_registar = who; for (wch = char_first; wch != NULL; wch = wch->next) { if (!IS_NPC(wch) && IS_SET(wch->act, PLR_GQUEST)) { REMOVE_BIT(wch->act, PLR_GQUEST); reset_gqmob(wch, 0); } } } void gquest_update(void) { char buf[MSL]; if (gquest_info.running == GQUEST_OFF) { if (--gquest_info.next <= 0) auto_gquest(); } else if (gquest_info.running == GQUEST_WAITING) { gquest_info.timer--; if (gquest_info.timer > 0) { sprintf(buf, "%d minute%s left to join the global quest. (Levels %d - %d)", gquest_info.timer, gquest_info.timer == 1 ? "" : "s", gquest_info.minlevel, gquest_info.maxlevel); announce(NULL, INFO_GQUEST, buf); } else { if (gquest_info.involved == 0) { end_gquest(gquest_info.last_registar); sprintf(buf, "Not enough people for the global quest. The next quest will start in %d minutes.", gquest_info.next); announce(NULL, INFO_GQUEST, buf); } else { gquest_info.timer = number_range(4 * gquest_info.mob_count, 6 * gquest_info.mob_count); gquest_info.running = GQUEST_RUNNING; sprintf(buf, "The Global Quest begins! You have %d minutes to complete the task!", gquest_info.timer); announce(NULL, INFO_GQUEST, buf); } } } else if (gquest_info.running == GQUEST_RUNNING) { if (gquest_info.involved == 0) { end_gquest(gquest_info.last_registar); sprintf(buf, "No one left in the Global Quest, next quest will start in %d minutes.", gquest_info.next); announce(NULL, INFO_GQUEST, buf); return; } switch (gquest_info.timer) { case 0: end_gquest(gquest_info.last_registar); sprintf(buf, "Time has run out on the Global Quest, next quest will start in %d minutes.", gquest_info.next); announce(NULL, INFO_GQUEST, buf); return; case 1: case 2: case 3: case 4: case 5: case 10: case 15: sprintf(buf, "%d minute%s remaining in the global quest.", gquest_info.timer, gquest_info.timer > 1 ? "s" : ""); announce(NULL, INFO_GQUEST, buf); default: gquest_info.timer--; break; } return; } } bool generate_gquest(CHAR_DATA * who) { CHAR_DATA *victim = NULL; vnum_t *vnums; int mob_count, randm; char buf[MAX_STRING_LENGTH]; int i; reset_gqmob(NULL, 0); mob_count = 0; alloc_mem(vnums, vnum_t, top_mob_index); for (victim = char_first; victim; victim = victim->next) { if (!IS_NPC(victim)) { REMOVE_BIT(victim->act, PLR_GQUEST); reset_gqmob(victim, 0); continue; } else if (!IS_NPC(victim) || victim->level > (gquest_info.maxlevel + 10) || victim->level < (gquest_info.minlevel - 10) || (victim->pIndexData == NULL || victim->in_room == NULL || victim->pIndexData->pShop != NULL) || victim->pIndexData->vnum < 100 || IS_SET(victim->in_room->room_flags, ROOM_PET_SHOP) || victim->in_room->clan > -1 || IS_SET(victim->imm_flags, IMM_WEAPON | IMM_MAGIC) || IS_SET(victim->act, ACT_TRAIN | ACT_PRACTICE | ACT_IS_HEALER | ACT_PET | ACT_GAIN) || IS_SET(victim->affected_by, AFF_CHARM) || (IS_SET(victim->act, ACT_SENTINEL) && IS_SET(victim->in_room->room_flags, ROOM_PRIVATE | ROOM_SOLITARY | ROOM_SAFE))) continue; vnums[mob_count] = victim->pIndexData->vnum; mob_count++; if (mob_count >= top_mob_index) break; } if (mob_count < 5) { end_gquest(who); free_mem(vnums); return FALSE; } else if (mob_count < gquest_info.mob_count) { gquest_info.mob_count = mob_count; } for (i = 0; i < gquest_info.mob_count; i++) { randm = number_range(0, mob_count - 1); while (!is_random_gqmob(vnums[randm])) randm = number_range(0, mob_count - 1); gquest_info.mobs[i] = vnums[randm]; } gquest_info.qpoints = number_range(15, 30) * gquest_info.mob_count; gquest_info.gold = number_range(100, 150) * gquest_info.mob_count; gquest_info.timer = 3; gquest_info.next = 0; sprintf(buf, "%s Global Quest for levels %d to %d%s. Type 'GQUEST INFO' to see the quest.", !who ? "A" : "$n announces a", gquest_info.minlevel, gquest_info.maxlevel, !who ? " has started" : ""); announce(who, INFO_GQUEST, buf); chprintlnf(who, "You announce a Global Quest for levels %d to %d with %d targets.\n\r", gquest_info.minlevel, gquest_info.maxlevel, gquest_info.mob_count); free_mem(vnums); return TRUE; } int is_gqmob(CHAR_DATA * ch, vnum_t vnum) { int i; if (gquest_info.running == GQUEST_OFF) return -1; for (i = 0; i < gquest_info.mob_count; i++) { if (ch && !IS_NPC(ch)) { if (ch->pcdata->gq_mobs[i] == vnum) return i; else continue; } else { if (gquest_info.mobs[i] == vnum) return i; else continue; } } return -1; } int count_gqmobs(CHAR_DATA * ch) { int i, count = 0; if (IS_NPC(ch)) return 0; for (i = 0; i < gquest_info.mob_count; i++) if (ch->pcdata->gq_mobs[i] == -1) count++; return count; } void reset_gqmob(CHAR_DATA * ch, vnum_t value) { int i; for (i = 0; i < MAX_GQUEST_MOB; i++) { if (ch && !IS_NPC(ch)) ch->pcdata->gq_mobs[i] = value; else gquest_info.mobs[i] = value; } } bool is_random_gqmob(vnum_t vnum) { int i; if (get_mob_index(vnum) == NULL) return FALSE; for (i = 0; i < gquest_info.mob_count; i++) if (gquest_info.mobs[i] == vnum) return FALSE; return TRUE; }