/* Do not remove the headers from this file! see /USAGE for more info. */ /* ** DESIGN ** ** Skills are represented as slash-separated names, organized as a tree ** of skills. Learning a skill propagates improvement up the chain to ** the parent skills. The total, aggregate value of a skill at any point ** is computed from a skill and all of its parent skills. This implies ** that use of one skill can benefit sibling skills; for example, using a ** sword improves the general combat skill which can help when you are ** forced to use a polearm (which you are otherwise unskilled at). ** ** A secondary concept of "training points" exist for each skill. These ** represent potential, additional improvement through formal training. ** Similar to skill values, the training points also propagate upwards. ** The points can then be applied to the skill and/or back down the tree; ** for example, using a sword earns training points in combat which can ** then be applied towards formal training with polearms or archery. ** ** MECHANICS ** ** Progression within a skill is based on a curve (specifically, a third- ** order polynomial). At the low- and top-end of the skill value range, ** it is hard to advance. This is to represent the initial difficulty ** of learning a new skill and the difficulty of perfecting it. In the ** middle of the skill value range, growth is much faster (learned points ** are applied at a near one-to-one ratio). ** ** At the ends of the range, skills are gained in a proportion based on ** the LEARNING_FACTOR symbol. ** ** As learning a skill propogates up the tree of skills, the amount ** applied decreases exponentially (set by PROPOGATION_FACTOR). ** ** Training points are added proportionally to the increase in the main ** skill (set by the TRAINING_FACTOR symbol). ** ** Learning a skill is based on three symbols: SKILL_ON_FAILURE, ** SKILL_MIN_ON_WIN, SKILL_MAX_ON_WIN. When a test_skill() fails, the ** SKILL_ON_FAILURE amount is learned. When the test_skill() succeeds, ** then you learn something in the range of MIN/MAX based on your ratio ** against the opposing skill (MAX if you're totally outclassed or MIN ** if you stomp all over your opponent). ** ** All these parameters are set in the skills.h file. ** ** Note: policy decision says that we aren't protecting skills from ** "unauthorized" tampering. This is consistent with much of ** lib -- wizards can help players in any numbers of ways and ** we won't try to guard against all of them. */ #include <classes.h> #include <skills.h> inherit CLASS_SKILL; private mapping skills = ([ ]); int base_test_skill(string skill, int opposing_skill); class skill set_skill(string skill, int skill_points, int training_points) { class skill cs = skills[skill]; if ( member_array(skill, SKILL_D->query_skills()) == -1 ) error("illegal skill; cannot set new skill values.\n"); if ( !cs ) { cs = skills[skill] = new(class skill, skill_points : skill_points, training_points : training_points); } else { cs->skill_points = skill_points; cs->training_points = training_points; } return cs; } mapping get_skills() { return skills; } class skill get_skill(string skill) { return skills[skill]; } //:FUNCTION aggregate_skill // Returns the aggregated skill value for the specified skill. This value // incorporates the value of the skill itself plus all values of the parent // skills. int aggregate_skill(string skill) { int total_skill = 0; int coef = 1; while ( 1 ) { class skill my_skill; int i; my_skill = skills[skill]; if ( my_skill ) { total_skill += fuzzy_divide(my_skill->skill_points, coef); } coef = coef * AGGREGATION_FACTOR; i = strsrch(skill, '/', -1); if ( i == -1 ) break; skill = skill[0..i-1]; } return total_skill; } //:FUNCTION learn_skill // Add some number of skill points to the given skill, propogating skill // values up through the parent skills. Training points are also assigned // as appropriate. void learn_skill(string skill, int value) { while ( 1 ) { class skill my_skill; int divisor; int i; int s; my_skill = skills[skill]; if ( !my_skill ) { /* use set_skill() for verification of the skill */ my_skill = set_skill(skill, 0, 0); } /* centered within skill range */ s = my_skill->skill_points - (MAX_SKILL_VALUE / 2); /* as a person's skill increases, the amount they learn decreases */ divisor = ((LEARNING_FACTOR - 1) * s * s) / (MAX_SKILL_VALUE * MAX_SKILL_VALUE / 4) + 1; my_skill->skill_points += fuzzy_divide(value, divisor); if ( my_skill->skill_points > MAX_SKILL_VALUE ) my_skill->skill_points = MAX_SKILL_VALUE; /* accum training points */ my_skill->training_points += fuzzy_divide(value, divisor * TRAINING_FACTOR); /* as skill moves up tree, it decreases */ value = fuzzy_divide(value, PROPAGATION_FACTOR); if ( !value ) break; i = strsrch(skill, '/', -1); if ( i == -1 ) break; skill = skill[0..i-1]; } } //:FUNCTION test_skill // This replaces the basic adversary test_skill function, // adding an attempt to improve the skill int test_skill(string skill, int opposing_skill) { int total_skill; int amount; int combined_total; int res; total_skill = aggregate_skill(skill); combined_total = total_skill + opposing_skill; res = base_test_skill(skill, opposing_skill); if(res) { /* ** Successful skill attempts win a number of skill points based on ** the ratio between the total_skill and opposing_skill. ** ** The range is MIN to MAX, centered between the two for evenly matched ** skills. As the opposing skill increases, so does the amount learned. */ amount = ( ( (SKILL_MAX_ON_WIN - SKILL_MIN_ON_WIN) * opposing_skill + (combined_total / 2) ) / combined_total + SKILL_MIN_ON_WIN ); learn_skill(skill, amount); } else learn_skill(skill, SKILL_ON_FAILURE); return res; } //:FUNCTION query_evaluation //Returns the player's overall evaluation (0 to 100 percent) of their skill //level. This evaluation corresponds to how they are doing with respect //to the maximum possible skill level. int query_evaluation() { return implode(values(skills), (: $1 + ((class skill)$2)->skill_points :), 0) * 100 / EVALUATION_SKILL_LEVEL; }