/* -*- LPC -*- */ /* * $Locker: $ * $Id: library.c,v 1.19 2003/03/25 23:13:15 drakkos Exp $ * */ /** * The library is a sort of information center. * Currently it supports only the recording on stories about players and * recording the quests that they have done. * @author Furball * @see /include/library.h */ #include <quest_handler.h> #include <player_handler.h> #define XP 10000 #define XP_QUESTS ({"womble's brooch", "balance quest", "easy post quest", \ "hard post quest", "apprentice baker" }) #define RESTORE_PATH "save/library/" #define MAX_CACHE_SIZE 100 #define CACHE_TIMEOUT 900 inherit "/std/object"; int *times; string player_name, *quests, text_file; mapping player_quest_info; nosave mapping qps; nosave int requests, cache_hits; class qinfo { int cached; string player_name; string *quests; int *times; string text_file; mapping player_quest_info; } nosave mapping player_cache = ([ ]); int new_top_quest(); int query_quest_time(string, string); private void init_data(string pl_name) { player_name = pl_name; quests = ({ }); times = ({ }); text_file = ""; player_quest_info = ([ ]); } /* init_data() */ void clean_cache() { string name; foreach(name in keys(player_cache)) if(player_cache[name]->cached < time() - CACHE_TIMEOUT) map_delete(player_cache, name); } private int get_data_file(string name) { int success; class qinfo tmp; requests++; if(player_cache[name]) { player_cache[name]->cached = time(); cache_hits++; return 1; } success = unguarded((: restore_object, RESTORE_PATH+name[0..0]+"/"+name :)); if(!success) init_data(name); tmp = new(class qinfo, cached : time(), player_name : player_name, quests : quests, times : times, text_file : text_file, player_quest_info : player_quest_info); player_cache[name] = tmp; if((sizeof(player_cache) > MAX_CACHE_SIZE) && (find_call_out("clean_cache") == -1)) call_out("clean_cache", 60); return success; } private void save_data_file(string name) { if(!player_cache[name]) return; player_name = player_cache[name]->player_name; quests = player_cache[name]->quests; times = player_cache[name]->times; text_file = player_cache[name]->text_file; player_quest_info = player_cache[name]->player_quest_info; unguarded((: save_object, RESTORE_PATH+name[0..0]+"/"+name :)); return ; } /* save_data_file() */ /** * This method returns the players current title. * @param name the name of the player * @return their current title, 0 if no title * @see /obj/handlers/quest_handler.c */ string query_title( string name ) { string quest; get_data_file( name ); if(!player_cache[name]->quests || !sizeof(player_cache[name]->quests)) { return 0; } quest = player_cache[name]-> quests[random(sizeof(player_cache[name]->quests))]; return (string)QUEST_HANDLER->query_quest_title(quest); } /** * This method returns the set of currently completed quests by the * player. * @param name the name of the player * @return the array of completed quests */ string *query_quests(string name) { get_data_file(name); if(!player_cache[name]->quests ) { return ({ }); } return player_cache[name]->quests + ({ }); } /** * This method returns the most recently completed quest by the player. * @param name the player name * @return the most recently completed quest */ string get_most_recent_quest(string name) { int loop, highest; string quest_name; string *quests; int *times; if(!get_data_file(name)) return "Sorry"; quests = player_cache[name]->quests; times = player_cache[name]->times; if(sizeof(quests) == 0) return "None"; for(highest = loop = 0; loop < sizeof(quests); loop++) { if(times[loop] > highest) { highest = times[loop]; quest_name = quests[loop]; } } return quest_name; } /** * This method get sthe most recent time a quest was complete by the * player. * @param name the name of the player * @return the time of the most recently completed quest */ int get_most_recent_time(string name) { int loop, hightime; string *quests; int *times; if(!get_data_file(name)) { return -1; } quests = player_cache[name]->quests; times = player_cache[name]->times; if(sizeof(quests) == 0) { return 0; } for(hightime = loop = 0; loop < sizeof(quests); loop++) { if(times[loop] > hightime) { hightime = times[loop]; } } return hightime; } /** * This method sets the player as having done the quest and if we * should give them xp for it * This function should be called when a quest is finished. It will then * call the quest_completed function on the quest handler and do all * assorted modifications to the player object etc. The name should be the * players name and the quest should be the name of the quest that is * stored in the quest handler. * <p> * The include file <library.h> should be used for calls to this * handler. * @return 0 if the quest is already completed * @see /obj/handlers/quest_handler->quest_completed() * @param pl_name name of the player * @param qu_name name of the quest * @param no_xp do not give out xp * @example * // Set the player as completing the womble friend quest, they get * // xp for it. * LIBRARAY->set_quest(this_player()->query_name(), "womble friend", 0); */ int set_quest(string pl_name, string qu_name, int no_xp) { int qu_level; object ob; object card; int i; // guests can't do quests. if(!find_player(pl_name) || find_player(pl_name)->query_property("guest")) return 0; i = QUEST_HANDLER->query_quest_status(qu_name); // Inactive quests automatically fail. ob = find_player (pl_name); if (i == 0 && ob->query_playtester()) { tell_object (ob, "%^BOLD%^If this quest weren't inactive, you'd be " "showered in riches right about now!\n%^RESET%^"); } if(i < 1) { user_event( "inform", pl_name +" completes "+ qu_name + " (inactive)", "quest"); return 0; } // Playtesters with playtester protection enabled do not get the // quest. if (ob && ob->advancement_restriction()) { log_file ("/log/secure/playtesters/protection_log", "%s: %s " "completed quest %s while advancement restricted active.\n", ctime(time()), pl_name, qu_name); return 0; } // clean out the qps cache so that we don't get invalid data. if(qps && qps[pl_name]) map_delete(qps, pl_name); get_data_file(pl_name); /* Already done the quest? */ if ( member_array( qu_name, player_cache[pl_name]->quests ) != -1 ) return 0; player_cache[pl_name]->quests += ({ qu_name }); player_cache[pl_name]->times += ({ time() }); /* Make sure the quest exists */ qu_level = (int)QUEST_HANDLER->query_quest_level(qu_name); if ( qu_level < 1 ) return 0; QUEST_HANDLER->quest_completed( pl_name, qu_name, previous_object() ); save_data_file( pl_name ); if ( !ob ) { return 0; } if(member_array(qu_name, XP_QUESTS) != -1) { ob->adjust_xp(XP*qu_level, 0); call_out("save_them", 1, ob); } ob->set_title( "quest", (string)QUEST_HANDLER->query_quest_title( qu_name ) ); card = clone_object ("/d/underworld/creator_cards/creator_card"); card->move (ob, "$N appear$s in your inventory with a flash."); return 1; } /** * This method removes a quest from the players list of completed quests. * @param pl_name the player name * @param qu_name the quest name * @return 0 if they have not done the quest, 1 if they habe * @see /obj/handlers/quest_handler.c */ int unset_quest(string pl_name, string qu_name) { int qu_level; get_data_file(pl_name); /* Already done the quest? */ if ( member_array( qu_name, player_cache[pl_name]->quests ) == -1 ) return 0; /* Make sure the quest exists */ qu_level = (int)QUEST_HANDLER->query_quest_level(qu_name); if ( qu_level < 1 ) return 0; player_cache[pl_name]->quests -= ({ qu_name }); player_cache[pl_name]->times -= ({ time() }); save_data_file( pl_name ); return 1; } /** * This method causes the player to be saved. * @param thing the player to save */ void save_them( object thing ) { if ( thing ) thing->save_me(); } /** * This method sets the information related to the players quest. * This information is used for quests which have several parts to them * and information needs to be stored about the player as they attempt * to complete it. * @param pl_name the name of the player * @param qu_info the quest name * @param details the information associated with the quest. */ void set_player_quest_info( string pl_name, string qu_info, mixed *details ) { get_data_file( pl_name ); if(!player_cache[pl_name]->player_quest_info ) player_cache[pl_name]->player_quest_info = ([ ]); player_cache[pl_name]->player_quest_info[qu_info] = details; save_data_file(pl_name); } /** * This method returns all the quest information for a player. * This information is used for quests which have several parts to them * and information needs to be stored about the player as they attempt * to complete it. The keys of the mapping are the quest names and the * values are the information associated with the quest. * @param pl_name the name of the player * @return the mapping containing all the quest info */ mapping query_all_player_quest_info( string pl_name ) { if(!get_data_file( pl_name )) return 0; if ( !player_cache[pl_name]->player_quest_info ) return 0; return copy(player_cache[pl_name]->player_quest_info); } /** * This method returns the quest info for a specific quest. * This information is used for quests which have several parts to them * and information needs to be stored about the player as they attempt * to complete it. * @param pl_name the player name * @param qu_info the quest name * @return the information associated with the quest */ mixed *query_player_quest_info( string pl_name, string qu_info ) { if(!get_data_file( pl_name)) return 0; if(!player_cache[pl_name]->player_quest_info) return 0; player_cache[pl_name]->player_quest_info = player_cache[pl_name]->player_quest_info + ([ ]); return player_cache[pl_name]->player_quest_info[qu_info]; } /** * This method returns the time at which a quest is completed. * @param name the name of the player to get the time for * @param qu_name the name of the quest * @return the time at which it is completed */ int query_quest_time(string name, string qu_name) { int time; if(!get_data_file(name)) { return 0; } time = member_array(qu_name, player_cache[name]->quests); if(time < 0) { return 0; } return player_cache[name]->times[time]; } /** * This method returns the story associated with the player. This is * created from all the information about the quests they have done * joined together into a neato story. * @param name the player name * @return the story of the player * @see /obj/handlers/quest_handler->query_quest_story() */ string query_story(string name) { string story, *quests; int loop; if(!get_data_file(name)) { return "You can find nothing in the library on " + name + "\n"; } story = ""; if(player_cache[name]->text_file != "") { return unguarded((: read_file, text_file :)); } quests = player_cache[name]->quests; if(!sizeof(quests)) { return "That person has lead a most unadventureous life"; } for(loop = 0; loop < sizeof(quests); loop++) { story += capitalize(QUEST_HANDLER->query_quest_story(quests[loop])) +".\n"; } return story; } /** * This method tells us if the player has completed the quest. * @param player the name of the player * @param quest the quest name * @return 1 if the quest has been done, 0 if it has not */ int query_quest_done(string player, string quest) { if(!get_data_file(player)) return 0; return (member_array(quest, player_cache[player]->quests) != -1); } /* query_quest_done() */ int flush_cache (string name) { qps[name] = 0; } /** * This method returns the current number of quest points gathered * by the player. * @param name the player * @return the current number of quest pointds * @see /obj/handlers/query_handler->query_quest_level() */ int query_quest_points( string name ) { int points; string word; string *quests; // qps is used to cache the count of quest points since it's expensive and // queried a lot. // the reason for storing points+1 is so that someone with 0 points still // comes up true under the test if(qps[name]) if(!qps) qps = ([ ]); if(qps[name]) return qps[name]-1; get_data_file( name ); quests = player_cache[name]->quests; if ( !quests || !sizeof( quests ) ) points = 0; else { foreach( word in quests ) { points += (int)QUEST_HANDLER->query_quest_level( word ); } } qps[name] = points+1; return points; } /* query_quest_points() */ /** * This method is called when a player refreshes totaly so they can * start again from scratch. * @param name the player name * @return 0 if they do not exists, 1 if they do */ int restart(mixed name) { class qinfo tmp; if(objectp(name)) name = name->query_name(); if(!PLAYER_HANDLER->test_user(name)) return 0; init_data(name); tmp = new(class qinfo, cached : time(), player_name : player_name, quests : quests, times : times, text_file : text_file, player_quest_info : player_quest_info); player_cache[name] = tmp; save_data_file(name); return 1; } /* restart() */ mixed *stats() { int percentage; if(requests) percentage = (cache_hits * 100) / requests; return ({ ({ "cache size", sizeof(player_cache) }), ({ "requests", requests }), ({ "cache hits", cache_hits }), ({ "cache misses", requests - cache_hits }), ({ "percentage hits", percentage }), }); }