/** * This will print out a list of all the current quests * @author Ceres */ #include <http.h> #include <quest_handler.h> #include <library.h> #include <player_handler.h> #undef SAVE_FILE #define SAVE_FILE "/save/www/quests" #define TEXTS_DIR "/save/quests/" #define NUM_COLS 4 // Minimum number of times the quest must be done before it is shown here. #define MIN_TIMES 100 // How long the player must be on to timeout seen hints. #define TIMEOUT (3600 * 24) // Delay between successive hints (in player age). #define HINT_DELAY (3600 * level) #define MAX_LIST 20 #define MAX_HINTS ({ 10, 5, 2 }) // When sorting the list how much weight to give to a quests level as // opposed to how many times it's been done. #define WEIGHTING 500 mapping seen_quests; nosave string *ql, *qt; class seen_info { string which; int time; int level; } int sort_list(string first, string second) { int lfirst, lsecond; // sort by level lfirst = ( 15 - QUEST_HANDLER->query_quest_level(first)) * WEIGHTING + QUEST_HANDLER->query_quest_times(first); lsecond = (QUEST_HANDLER->query_quest_level(second) * WEIGHTING) + QUEST_HANDLER->query_quest_times(second); if(lfirst > lsecond) return -1; if(lsecond < lfirst) return 1; return 0; } void create() { int i; seteuid("Room"); unguarded( (: restore_object(SAVE_FILE) :) ); if (!seen_quests) { seen_quests = ([ ]); } ql = QUEST_HANDLER->query_quest_names(); ql = sort_array(ql, (: sort_list($1, $2) :)); qt = allocate(sizeof(ql)); for(i=0; i<sizeof(ql); i++) qt[i] = QUEST_HANDLER->query_quest_title(ql[i]); } private string create_header(string title) { return "<HTML>\n<HEAD>\n" "<TITLE>" + title + "</TITLE>\n" "</HEAD>\n<BODY bgcolor=\"#ffffff\" text=\"#000030\" link=\"#4a529c\" " "vlink=\"#b57339\">\n<FONT face=\"arial,helvetica\">\n" "<IMG align=left src=\"/pics/dw6.gif\"></A>\n" "<H2>Discworld Quest Page</H2>\n" "<H3><I>For what it's worth.</I></H3>\n<BR clear=\"left\">\n" "<BR>\n"; } /* create_header() */ private string create_footer(class http_request req) { return ("/www/footer"->www_function("footer", ([ ]), req))+"</BODY>\n</HTML>\n"; } private string htmlify(string str) { return replace(str, ({ "&", "&", "<", "<", ">", ">", "\n", "<br>\n", }) ); } private string calc_filename(string qname) { string fname; fname = lower_case(qname); fname = replace(fname, ({ " ", "_", "'", "" })) + ".txt"; return fname; } private string get_file(string fname) { string tmp; tmp = unguarded( (: read_file, TEXTS_DIR + fname :) ); if(tmp) tmp = htmlify(tmp); return tmp; } private string get_plain_file(string fname) { string tmp; tmp = unguarded( (: read_file, TEXTS_DIR + fname :) ); return tmp; } private void save_file() { unguarded( (: save_object(SAVE_FILE) :) ); } int can_see_hint(string name, string which, int level) { class seen_info tmp; int hints, i, age; if ("/secure/bastards"->query_suspended(name)) return 0; if(!PLAYER_HANDLER->test_active(name)) return 0; if(LIBRARY->query_quest_done(name, which)) return 0; age = - (PLAYER_HANDLER->test_age(name)); for(i=0; i<sizeof(seen_quests[name]); i++) { tmp = seen_quests[name][i]; // remove quests which they've done or which have timedout. if(LIBRARY->query_quest_done(name, tmp->which) || tmp->time < age - TIMEOUT) { seen_quests[name] = seen_quests[name][0..i-1] + seen_quests[name][i+1..]; save_file(); } else if(tmp->which == which) { // Ok, this quest is on the list. // they've seen this level or higher so let them see it again. if(tmp->level >= level) return 1; // they saw the last level too recently so tell them so. if(tmp->time > (age - HINT_DELAY)) return -1; return 1; } else if(tmp->level == level) { hints++; } } return (hints < (MAX_HINTS[level-1])); } void add_seen(string name, string which, int level) { class seen_info seen, tmp; int i; if(LIBRARY->query_quest_done(name, which)) return; seen = new(class seen_info, which : which, time : - (PLAYER_HANDLER->test_age(name)), level : level); if(seen_quests[name]) { for(i = 0; i < sizeof(seen_quests[name]); i++) { if(seen_quests[name][i]->which == which) { tmp = (class seen_info)seen_quests[name][i]; if(level > tmp->level) tmp->level = level; seen_quests[name][i] = tmp; save_file(); return; } } seen_quests[name] += ({ seen }); } else seen_quests[name] = ({ seen }); save_file(); } string www_request(string str, mapping args, class http_request req) { string ret, user, fname, tmp; int i, j, k; user = req->user; if(!PLAYER_HANDLER->test_creator(user)) return create_header("Quest Hints") + "You are not a creator!" + create_footer(req); #ifdef 0 if(!"/secure/master"->query_leader(user) && user != "valentijn") { return create_header("Quest Hints") + "This page is currently only " "available to lords.\n" + create_footer(req); } #endif ret = create_header("Quest List"); ret += ""; if(args["param"]) sscanf(args["param"], "%d", i); switch(args["op"]) { case "write": if(req->body->data["hint"] != "0") { fname = "hints/" + calc_filename(ql[i]); ret += "Writing hint to: " + TEXTS_DIR + fname + "<br>"; ret += "[" + req->body->data["hint"] + "]<br>"; unguarded((: write_file, TEXTS_DIR + fname, req->body->data["hint"] + "\n", 1 :)); } if(req->body->data["second_hint"] != "0") { fname = "second_hints/" + calc_filename(ql[i]); ret += "Writing second hint to: " + TEXTS_DIR + fname + "<br>"; ret += "[" + req->body->data["second_hint"] + "]<br>"; unguarded((: write_file, TEXTS_DIR + fname, req->body->data["second_hint"] + "\n", 1 :)); } if(req->body->data["final_hint"] != "0") { fname = "final_hints/" + calc_filename(ql[i]); ret += "Writing final hint to: " + TEXTS_DIR + fname + "<br>"; ret += "[" + req->body->data["final_hint"] + "]<br>"; unguarded((: write_file, TEXTS_DIR + fname, req->body->data["final_hint"] + "\n", 1 :)); } ret += "\n<p><a href=\"quests.c\">Return to main list.</a></p>\n"; break; case "add": ret += "<p><b>Title</b>: " + QUEST_HANDLER->query_quest_title(ql[i]) + "<br>"; ret += "<b>Story</b>: " + QUEST_HANDLER->query_quest_story(ql[i])+ "</p>"; fname = calc_filename(ql[i]); ret += "\n<form action=\"quests.c?op=write¶m="+i+"\" " "method=\"post\">\n" "<br>First Hint:<br>\n" "<textarea name=\"hint\" cols=75 rows=10>" + get_plain_file("hints/"+fname) + "</textarea>\n" "<br>Second Hint:<br>\n" "<textarea name=\"second_hint\" cols=75 rows=10>" + get_plain_file("second_hints/"+fname) + "</textarea>\n" "<br>Final Hint: <br>\n" "<textarea name=\"final_hint\" cols=75 rows=10>" + get_plain_file("final_hints/"+fname) + "</textarea>\n" "<br><input type=\"submit\">\n" "</form>\n"; break; case "hint": ret += "<h3>Quest Hint</h3>\n"; ret += "<p><b>Title</b>: " + QUEST_HANDLER->query_quest_title(ql[i]) + "<br>"; if(!can_see_hint(user, ql[i], 1)) { ret += "<p>Sorry, you have seen all the hints you're allowed for the " "moment. Why not go and complete some of those quests you've " "been shown then more will be available.</p>\n"; } else { add_seen(user, ql[i], 1); fname = "hints/" + calc_filename(ql[i]); ret += "<p><b>First Hint</b>:<br> " + get_file(fname) + "</p>"; fname = "second_hints/" + calc_filename(ql[i]); if(file_size(TEXTS_DIR + fname) > 0) { ret += "<b>Second Hint</b>: " "<a href=\"quests.c?op=second_hint¶m="+i+"\">Available</a><br>"; } else { ret += "<b>No second hint available</b><br>\n"; if(file_size(TEXTS_DIR + fname) > 0) ret += "<b>Final Hint</b>: <a href=\"quests.c?op=final_hint¶m="+i+"\">" "Available</a><br>"; else ret += "<b>No final hint available</b><br>\n"; } } break; case "second_hint": ret += "<h3>Quest Hint</h3>\n"; ret += "<p><b>Title</b>: " + QUEST_HANDLER->query_quest_title(ql[i]) + "<br>"; switch(can_see_hint(user, ql[i], 2)) { case 0: ret += "<p>Sorry, you have seen all the second hints you're allowed " "for the moment. Why not go and complete some of those quests you've " "been shown then more will be available.</p>\n"; break; case -1: ret += "<p>You've only just seen the first hint. Why not try solving " "the quest before seeing the next hint.</p>\n"; break; default: add_seen(user, ql[i], 2); fname = "second_hints/" + calc_filename(ql[i]); ret += "<p><b>Second Hint</b>:<br> " + get_file(fname) + "</p>"; if(file_size(TEXTS_DIR + fname) > 0) ret += "<b>Final Hint</b>: " "<a href=\"quests.c?op=final_hint¶m="+i+"\">Available</a><br>"; else ret += "<b>No final hint available</b><br>\n"; } break; case "final_hint": ret += "<h3>Final Hint</h3>\n"; ret += "<p><b>Title</b>: " + QUEST_HANDLER->query_quest_title(ql[i]) + "<br>"; switch(can_see_hint(user, ql[i], 3)) { case 0: ret += "<p>Sorry, you have seen all the final hints you're allowed for " "the moment. Why not go and complete some of those quests you've " "been shown then more will be available.</p>\n"; break; case -1: ret += "<p>You've only just seen the second hint. Why not try solving " "the quest before seeing the final hint.</p>\n"; break; default: add_seen(user, ql[i], 3); fname = "final_hints/" + calc_filename(ql[i]); ret += "<p><b>Final Hint</b>:<br> "+ get_file(fname) + "</p>"; } break; #ifdef 0 case "search": ret += "<h3>Search Results</h3>\n"; tmp = "<ul>\n"; for(i=0, j=0; i<sizeof(ql); i++) { if(strsrch(lower_case(ql[i]), lower_case(args["search"])) != -1 || strsrch(lower_case(qt[i]), lower_case(args["search"])) != -1) { tmp += "<li><a href=\"quests.c?op=basic¶m="+i+"\">" + lower_case(ql[i]) + "</a>\n"; j++; } } if(!j) ret += "<p>No matches.</p>" "<form action=\"quests.c\" method=\"get\">\n" "<input type=\"hidden\" name=\"op\" value=\"search\">\n" "Search: <input type=\"text\" name=\"search\">\n" "</form>\n"; else if(j > 5) ret += "<p>Too many matches, please refine your search.</p>" "<form action=\"quests.c\" method=\"get\">\n" "<input type=\"hidden\" name=\"op\" value=\"search\">\n" "Search: <input type=\"text\" name=\"search\">\n" "</form>\n"; else ret += tmp + "</ul>\n"; break; #endif case "basic": ret += "<h3>Quest Information</h3>\n"; ret += "<p><b>Title</b>: " + QUEST_HANDLER->query_quest_title(ql[i]) + "<br>\n"; ret += "<b>Story</b>: " + QUEST_HANDLER->query_quest_story(ql[i])+ "</p>\n"; if(PLAYER_HANDLER->test_creator(user)) { ret += "<p>Level: " + QUEST_HANDLER->query_quest_level(ql[i]) + "<br>\n"; ret += "Times done: " + QUEST_HANDLER->query_quest_times(ql[i]) + "</p>\n"; ret += "<br>Rating: " + (( 16 - QUEST_HANDLER->query_quest_level(ql[i])) *WEIGHTING + QUEST_HANDLER->query_quest_times(ql[i])) + "\n"; } fname = calc_filename(ql[i]); if(file_size(TEXTS_DIR + "hints/" + fname) > 0) ret += "<p><b>First Hint</b>: <a href=\"quests.c?op=hint¶m="+i+"\">" "Available</a></p>"; else { ret += "<p><b>No first hint available</b></p>\n"; if(file_size(TEXTS_DIR + "final_hints/" + fname) > 0) ret += "<p><b>Final Hint</b>: <a href=\"quests.c?op=final_hint¶m="+i+ "\">Available</a></p>"; else ret += "<p><b>No final hint available</b></p>\n"; } break; case "list": default: ret += "<h3>Recommended Quests</h3>\n"; ret += "<p>The following quests are recommended for you.</p>\n"; ret += "<table><tr>\n<td>\n"; for(i=0, j=0, k=0; i<sizeof(ql) && j < MAX_LIST; i++) { // Dont show them quests they've done already. if(LIBRARY->query_quest_done(user, ql[i])) continue; // Dont show inactive quests if(!QUEST_HANDLER->query_quest_status(ql[i])) continue; if(QUEST_HANDLER->query_quest_times(ql[i]) < MIN_TIMES) continue; // columnise the output if(!(k++ % (MAX_LIST / NUM_COLS))) ret += "</td>\n<td valign=\"top\" nowrap>\n"; // link the quest info page. ret += "<li><a href=\"quests.c?op=basic¶m="+i+"\">" + lower_case(ql[i]) + "</a>"; j++; } ret += "</tr></table>\n"; ret += "<form action=\"quests.c\" method=\"get\">\n" "<p>If you need to find assistance with a specific quest you can use " "the search tool to find it.</p>\n" "<input type=\"hidden\" name=\"op\" value=\"search\">\n" "Search: <input type=\"text\" name=\"search\">\n" "</form>\n"; /* * This piece is for creators only and provides a full list of quests * and indicates which ones are missing hints. */ if("/secure/master"->query_administrator(user) || "/secure/master"->query_leader(user) || user == "valentijn") { ret += "<hr>"; ret += "<h3>Full quest list (for lords+ only).</h3>"; ret += "<p>As a creator you can use this to update the hints " "for quests to make sure they are complete and " "accurate.</p>"; ret += "<table><tr>\n<td>\n"; for(i=0; i<sizeof(ql); i++) { // columnise the output if(!(i % ((sizeof(ql) + (NUM_COLS/2)) / NUM_COLS))) ret += "</td>\n<td valign=\"top\" nowrap>\n"; // link the quest info page. ret += "<br><a href=\"quests.c?op=basic¶m="+i+"\">" + lower_case(ql[i]) + "</a>"; ret += "<br>" + qt[i] + "<br>"; ret += "Level " + QUEST_HANDLER->query_quest_level(ql[i]) + "<br>"; ret += "Done " + QUEST_HANDLER->query_quest_times(ql[i]) + " times<br>"; ret += "Status: " + (QUEST_HANDLER->query_quest_status(ql[i]) ? "Active" : "Inactive")+ "<br>\n"; fname = calc_filename(ql[i]); if(file_size(TEXTS_DIR + "hints/" + fname) == -1) ret += "<b>No</b> <a href=\"quests.c?op=add¶m="+i+"\">First Hint</a>" "<br>"; else ret += "<a href=\"quests.c?op=add¶m="+i+"\">First Hint</a><br>"; if(file_size(TEXTS_DIR + "second_hints/" + fname) == -1) ret += "<b>No</b> <a href=\"quests.c?op=add¶m="+i+"\">" "Second Hint</a><br>"; else ret += "<a href=\"quests.c?op=add¶m="+i+"\">Second Hint</a>" "<br>"; if(file_size(TEXTS_DIR + "final_hints/" + fname) == -1) ret += "<b>No</b> <a href=\"quests.c?op=add¶m="+i+"\">" "Final hint</a><br>"; else ret += "<a href=\"quests.c?op=add¶m="+i+"\">Final Hint</a>" "<br>"; } ret += "</tr></table>\n"; } } ret += create_footer(req); return ret; } /* www_request() */