/* Higher level inheritable for learning a skill This could be inherited by a room or an NPC Belle April 2002 */ #include <money.h> #include <tasks.h> class step { string *blurbs; string skill; int difficulty; string *responses; string *params; string award_mess; string succeed_mess; string fail_mess; } class lesson { mapping minimum_skills; mapping maximum_skills; string *materials; int cost; string currency; string *lesson_ending; object teacher; class step *plan; } private nosave mapping _lessons; private nosave int current_step; private nosave int attempt; int lesson_response(string, object, int, string); /** * Register a lesson with the learning system. * * @param name the name of the lesson * @param minimum_skills a mapping of skill names & bonuses that are * required in order to take this lesson * @param maximum_skills a mapping of skill names & bonuses that are the * maximum allowed to take this lesson * @param materials a list items required in order to take the lesson * @param cost the price of the lesson * @param currency the name of the currency required for the lesson * @param lesson_ending an array of strings that happen at the end the lesson to end it * @param teacher an optional teacher object * */ int register_lesson(string name, mapping minimum_skills, mapping maximum_skills, string *materials, int cost, string currency, string *lesson_ending, object teacher ) { if(!_lessons) _lessons = ([ ]); if(_lessons[name]) return 0; _lessons[name] = new(class lesson, minimum_skills : minimum_skills, maximum_skills : maximum_skills, materials : materials, cost : cost, currency : currency, lesson_ending : lesson_ending, teacher : teacher ); return 1; } /** * Add a step to a lesson plan. This is the interaction between student and * teacher. One step should be defined for each thing you want the student * to learn. * * @param lesson_name the name of the lesson this lesson plan is * associated with. * @param blurbs An array of strings in order that they should be given * during the lesson. This is what the teacher says to the student. ', :, #, etc. * can be used for say, emote and execute a command respectively. * @param skill The skill being learned. * @param difficulty The bonus level of the skill being learned. * @param responses An array of strings. These are the expected responses * to the teacher's blurbs. * @param params an array of strings given as arguments to the responses in * order. * @param award_mess The string a player sees if they get a TM award during this step. It * should not end in a newline \n. * @param succeed_mess The string a player gets if they are successful in doing what the * step asks. It should not end in a newline \n. * @param fail_mess A string telling the player that they've failed. It should not * end in a newline \n. */ int add_step( string lesson_name, string *blurbs, string skill, int diff, string *responses, string *params, string award_mess, string succeed_mess, string fail_mess) { if(!_lessons) _lessons = ([ ]); if( !_lessons[lesson_name] ){ return 0; } if( !_lessons[lesson_name]->plan ){ _lessons[lesson_name]->plan = ({ }); } _lessons[lesson_name]->plan += ({ new(class step, blurbs : blurbs, skill : skill, difficulty : diff, responses : responses, params : params, award_mess : award_mess, succeed_mess : succeed_mess, fail_mess : fail_mess ) }); return 1; } /** has_required_skills( string name, object player ) * Skills: Skills and skill levels which may be necessary to learn something * For instance, one should learn to walk before one learns to run. If you * don't have sufficient skill levels in walking, you won't be able to learn * how to run. * @param name name of the lesson they're trying to learn * @param player player object of the player trying to learn */ int has_required_skills( string name, object player ) { string skill; int p_level; // make sure the player meets the minimum skill requirements foreach( skill in (keys(_lessons[name]->minimum_skills)) ){ p_level = player->query_skill(skill); // debug_printf( "Skill check: Player has %d in %s. Must be a min of %d", // p_level, skill, _lessons[name]->minimum_skills[skill]); if( p_level < _lessons[name]->minimum_skills[skill] ){ // player doesn't have enough skill to learn this return 0; } } // make sure the player is under the maximum skill requirements foreach( skill in (keys(_lessons[name]->maximum_skills)) ){ p_level = player->query_skill(skill); //debug_printf( "Skill check: Player must have a max of %d", // _lessons[name]->maximum_skills[skill] ); if( p_level > _lessons[name]->maximum_skills[skill] ){ // player has too much skill to learn this return 0; } } return 1; } /** has_required_materials( string name, object player ) * Materials: Materials are objects which are necessary for a student to learn * something. For instance, to learn to speak a language, you might need * a textbook. To learn to ride a horse, you might want to require a helmet. * @param name name of the lesson they're trying to learn * @param player player object of the player trying to learn */ int has_required_materials( string name, object player ) { // if it's in their inventory they have it string mat; foreach( mat in _lessons[name]->materials ) { if( sizeof( filter_array(deep_inventory(player), (: ($1)->query_short() :) ) ) ){ return 1; } } // these are not the materials we're looking for return 0; } /** has_required_cash( string name, object player ) * Give some, get some. Money makes a teacher more likely to teach you. * @param name name of the lesson they're trying to learn * @param player player object of the player trying to learn */ int has_required_cash( string name, object player ) { //debug_printf( "Cash check: Player has %d in %s needs to have %d", // player->query_value_in(_lessons[name]->currency), // _lessons[name]->currency, _lessons[name]->cost ); if(player->query_value_in(_lessons[name]->currency) < _lessons[name]->cost) { return 0; } else { return 1; } } /** * This method starts a lesson for a given player. * @param the lesson name * @param the player */ int start_lesson(string name, object player) { string *needed; if(!_lessons[name]) return notify_fail(player, "Sorry, it's not clear what you want to learn " "here.\n"); needed = ({ }); if(!has_required_skills(name, player)) needed += ({ "skills" }); if(!has_required_materials( name, player )) needed += ({ "materials" }); if(!has_required_cash( name, player )) needed += ({ "cash" }); if(sizeof(needed)) { debug_printf( "Player doesn't have the correct %s\n", query_multiple_short(needed) ); return add_failed_mess("Sorry, You don't have the correct " + query_multiple_short(needed) + " to learn today.\n"); } // Call the lesson step function for the first step. call_out("lesson_step", 1, name, player, 0); return 1; } /** * This method returns an array of the registered lessons. * @return a list of lesson names. */ string *query_lessons() { return keys(_lessons); } protected void lesson_step(string name, object student, int lessonstep) { string str; class step current; object teacher = _lessons[name]->teacher; int i; // The current step in the lesson plan. current = _lessons[name]->plan[lessonstep]; //tell_creator( "belle", "Current lessonstep is %O\n", current ); // register response commmand str = ""; //tell_creator( "belle", "command %s\n params %s\n", current->responses[0], current->params[0] ); //tell_creator( "belle", "Sizeof current responses is %d", sizeof(current->responses) ); for(i=0; i<sizeof(current->responses); i++) { if(current->params && sizeof(current->params) > i) { str = " <string:'" + current->params[i] + "'>"; //tell_creator( "belle", "Str is %s\n", str ); student->add_command(current->responses[i], this_object(), str, (: lesson_response($(name), $(student), $(lessonstep), $4) :)); //tell_creator( "belle", "Added command %s to %O with extra string %s\n", // current->responses[i], this_object(), str ); } else { str = ""; student->add_command(current->responses[i], this_object(), str, (: lesson_response($(name), $(student), $(lessonstep), "") :)); //tell_creator( "belle", "Added command %s to %O with extra string %s\n", // current->responses[i], this_object(), str ); } } // output blurbs (via teacher if appropriate) foreach( str in (current->blurbs) ) { if( teacher ) teacher->queue_command(str); else tell_object(student, str); } } int lesson_response(string name, object student, int lessonstep, string args) { class step current; // validate response if(this_player() != student) return 0; current = _lessons[name]->plan[lessonstep]; //tell_creator( "belle", "Args is %O\nSizeof Current-params is %d\n", args[0], sizeof(current->params) ); // If this step has response paramters we need to validate them. if(sizeof(current->params) && member_array(args[0], current->params) == -1) return student->add_failed_mess(this_object(), "Invalid response.\n", ({ })); // perform TM check //tell_creator( "belle", "TM Check skill: %s diff: %d\n", current->skill, current->difficulty ); switch(TASKER->perform_task(student, current->skill, current->difficulty, TM_FIXED)) { case AWARD: // display award message tell_object(student, "%^YELLOW%^"+current->award_mess+"%^RESET%^\n"); case SUCCEED: //move on. tell_object( student, current->succeed_mess + "\n"); // This removes the last add_command. student->remove_object(this_object(), 1); //tell_creator( "belle", "lessonstep is %d, number of lesson steps is %d\n", // lessonstep, (sizeof(_lessons[name]->plan)-1) ); // Is this the last step? if( (sizeof(_lessons[name]->plan)-1) == lessonstep ) { tell_creator("belle", "Lesson is on its last step: %d\n", lessonstep ); call_out( "lesson_end", 1, name, student ); return 1; } else { call_out("lesson_step", 1, name, student, ++lessonstep); } break; default: // repeat tell_object(student, current->fail_mess + "\n" ); break; } return 1; } int lesson_end ( string name, object student ) { string str; object teacher = _lessons[name]->teacher; // output blurbs (via teacher if appropriate) foreach( str in (_lessons[name]->lesson_ending) ) { if( teacher ) teacher->queue_command(str); else tell_object(student, str); } return 1; }