/** * Handles updating the skill systems to new levels when the skill tree * is changed. * @author Deutha * @see /std/living/skills.c * @change 1/2/97 Pinkfish * Updated to include the new language skill tree */ #include <login_handler.h> #include <language.h> #include <tune.h> #include <skills.h> #define ORIGINAL "alpha" #define VERSION "beta" #define VERSION_2 "gamma" #define VERSION_3 "delta" #define VERSION_4 "epsilon" #define VERSION_5 "zeta" #define OLD_SKILLS ({ \ "magic.spells.offensive.area", \ "magic.spells.offensive.target", \ "magic.spells.defensive.area", \ "magic.spells.defensive.self", \ "magic.spells.defensive.target", \ "magic.spells.misc.area", \ "magic.spells.misc.self", \ "magic.spells.misc.target", \ "magic.spells.special", \ "magic.items.held.wand", \ "magic.items.held.rod", \ "magic.items.held.staff", \ "magic.items.held.broom", \ "magic.items.worn.amulet", \ "magic.items.worn.ring", \ "magic.items.scroll", \ "magic.points", \ "occult.methods.brewing", \ "occult.methods.dancing", \ "occult.methods.chanting", \ "occult.methods.flying", \ "occult.spells.cursing", \ "occult.spells.summoning", \ "occult.spells.enchanting", \ "occult.spells.charming", \ "occult.spells.healing", \ }) #define OTHER_SKILLS ({ \ "magic", \ "magic.spells", \ "magic.spells.offensive", \ "magic.spells.defensive", \ "magic.spells.misc", \ "magic.items", \ "magic.items.held", \ "magic.items.worn", \ "occult", \ "occult.methods", \ "occult.spells", \ }) /** @ignore yes */ int query_level_in( mapping skills, string skill ) { string *bits; if ( !undefinedp( skills[ skill ] ) ) return skills[ skill ]; bits = explode( skill, "." ); if ( sizeof( bits ) == 1 ) return 0; bits = delete( bits, sizeof( bits ) - 1, 1 ); skill = implode( bits, "." ); return query_level_in( skills, skill ); } /* query_level_in() */ /** * Update the skills from the original skills system to the * beta skills system. * @param thing the person to update */ void change_skills( object thing ) { int i, total; mapping skills; skills = (mapping)thing->query_skills(); if ( !m_sizeof( skills ) ) return; for ( i = 0; i < sizeof( OLD_SKILLS ); i++ ) { total += query_level_in( skills, OLD_SKILLS[ i ] ); if ( !undefinedp( skills[ OLD_SKILLS[ i ] ] ) ) map_delete( skills, OLD_SKILLS[ i ] ); } for ( i = 0; i < sizeof( OTHER_SKILLS ); i++ ) if ( !undefinedp( skills[ OTHER_SKILLS[ i ] ] ) ) map_delete( skills, OTHER_SKILLS[ i ] ); if ( !total ) return; thing->add_property( "magic levels", total ); thing->set_skills( skills ); tell_object( thing, "\n\nAn imp that only you can see flashes into "+ "existence.\n" ); tell_object( thing, "The imp tells you: The magic and occult skill "+ "categories are being amalgamated and restructured.\n" ); tell_object( thing, "The imp tells you: You have a total of "+ total + " levels that you can spend on the new magic skills.\n" ); tell_object( thing, "The imp tells you: Go and find the black "+ "monolith in the middle of Sator Square which you can use to "+ "to spend them.\n" ); tell_object( thing, "The imp flashes back to wherever it came from.\n\n" ); } /* change_skills() */ /** * Figure out thge minimum amount of xp that could have been used to * get the skill level. This assumes a leaf skill * This appears to be the xp equation. Using it anyway... * @param lvl the level to get to * @return the skill cost in xp */ int query_minimum_skill_xp(int lvl) { int i; int xp; xp = 0; for (i = 0; i < lvl; i++) { xp += ( DEFAULT_COST * STD_COST * 100 ) / (( LEVEL_DIV + 1) * 500); } return xp; } /* query_minimum_skill_xp() */ /** * Changes the skill array up to the new tree involving the * languages. * @param thing the object to upgrade * @param lev the level we are changing up to. */ void change_language_skills( object thing, int lev ) { int i; int total_xp; mapping skills; string *bits; string start; string *langs; string health; skills = thing->query_skills(); if (!sizeof(skills)) return ; bits = keys(skills); health = skills["general.health"]; /* * Instead of changing the skill levels directly... We will just * call the appropriate functions on the player object to fiddle * them. */ start = "general.language."; total_xp = 0; for (i = 0; i < sizeof(bits); i++) { if (bits[i][0..strlen(start)-1] == start) { /* Ok, now figure out how much xp to give back... */ total_xp += query_minimum_skill_xp(skills[bits[i]]); map_delete(skills, bits[i]); } } langs = thing->query_languages(); for (i = 0; i < sizeof(langs); i++) { thing->add_language(langs[i]); thing->remove_language( langs[i] ); } if (skills["general.language"]) { total_xp += query_minimum_skill_xp(skills["general.language"]); map_delete(skills, "general.language"); } /* Everyone gets common... */ thing->add_language("common"); if( skills["general.health"] != health ) { tell_object(thing, "A fluffy cabbage tells you: Something has gone wrong " "with your language adjustment, please tell Ceres or " "Pinkfish\n"); return; } /* thing->set_skills( skills ); */ thing->adjust_xp( total_xp ); tell_object( thing, "A fluffy cabbage turns up and stares straight into your eyes.\n" "You feel that the fluffy cabbage has shuffled around some bits of your mind.\n"); if (total_xp > 0 && lev < 2) { tell_object( thing, "Your languages have been shifted, you appear to have gained "+total_xp+" xp.\n"); } } /* change_language_skills() */ mapping recalc_parents(mapping skills, string skill) { int i, j, lvl; string *tree, *bits, this_skill; bits = explode(skill, "."); for(i=sizeof(bits)-1; i>=0; i--) { this_skill = implode(bits[0..i], "."); tree = (string *)SKILL_OB->query_immediate_children(this_skill); lvl = 0; for(j=0; j<sizeof(tree); j++) { lvl += skills[tree[j]]; } if(sizeof(tree)) skills[this_skill] = lvl / sizeof(tree); } return skills; } /** * Update the skills from the original skills system to the * beta skills system. * @param thing the person to update */ void change_skills_epsilon( object thing ) { int lvl, rest; mapping skills; string *bits; skills = thing->query_skills(); if (!sizeof(skills)) return ; bits = keys(skills); tell_object(thing, "A squirrel appears and says: I'm just updating your " "skill tree, will be just a jiffy.\n"); // Do sleight-of-hand first. lvl = skills["covert.sleight-of-hand"]; if( lvl > 0 ) { map_delete( skills, "covert.sleight-of-hand"); if( lvl > 300 ) { // they get skills up to 300 at 2:1 rest = lvl - 300; lvl = 300; if( rest > 600 ) { // 300-600 at 1:1 lvl += 300; rest -= 600; lvl += rest / 5; // >600 at 4:10 } else lvl += rest / 2; } skills["covert.manipulation.stealing"] = lvl; skills["covert.manipulation.palming"] = lvl; skills["covert.manipulation.passing"] = lvl; skills["covert.manipulation.sleight-of-hand"] = lvl; skills["covert.manipulation"] = lvl; recalc_parents(skills, "covert"); } // then sharp lvl = skills["fighting.combat.melee.sharp"]; if(lvl > 0) { if(lvl > 300) { // they get skills up to 300 at 2:1 rest = lvl - 300; lvl = 300; if(rest > 600) { // 300-600 at 1:1 lvl += 300; rest -= 600; lvl += rest / 5; // >600 at 4:10 } else lvl += rest / 2; } skills["fighting.combat.melee.sharp"] = lvl; skills["fighting.combat.melee.pierce"] = lvl; skills = recalc_parents(skills, "fighting.combat.melee"); } // then parry lvl = skills["fighting.combat.parry.thrown"]; rest = skills["fighting.combat.parry.fired"]; if(lvl > 0 || rest > 0) { if(rest > lvl) lvl = rest + (lvl / 5); else lvl += (rest / 5); map_delete( skills, "fighting.combat.parry.thrown"); map_delete( skills, "fighting.combat.parry.fired"); skills["fighting.combat.parry.range"] = lvl; lvl = skills["fighting.combat.parry.held"]; map_delete( skills, "fighting.combat.parry.held"); skills["fighting.combat.parry.melee"] = lvl; skills = recalc_parents(skills, "fighting.combat.parry"); } // then special lvl = skills["fighting.combat.special"]; if(lvl > 0) { if(lvl > 300) { // they get skills up to 300 at 2:1 rest = lvl - 300; lvl = 300; if(rest > 600) { // 300-600 at 1:1 lvl += 300; rest -= 600; lvl += rest / 5; // >600 at 4:10 } else lvl += rest / 2; } skills["fighting.combat.special.weapon"] = lvl; skills["fighting.combat.special.unarmed"] = lvl; skills["fighting.combat.special.tactics"] = lvl - lvl / 4; skills = recalc_parents(skills, "fighting.combat.special"); } // do tracking lvl = skills["general.tracking.woodland"]; map_delete(skills, "general.tracking.woodland"); lvl += skills["general.tracking.desert"]; map_delete(skills, "general.tracking.desert"); lvl += skills["general.tracking.mountain"]; map_delete(skills, "general.tracking.mountain"); lvl += skills["general.tracking.road"]; map_delete(skills, "general.tracking.road"); lvl += skills["general.tracking.field"]; map_delete(skills, "general.tracking.field"); if( lvl > 0 ) skills["general.tracking"] = lvl; thing->set_skills( skills ); tell_object(thing, "The squirrel says: Ok, all done now.\n"); } /* change_skills_epsilon() */ /** * Calulate the new skill value for the skills version zeta */ int new_skill_value(int sk) { float f = 1000.0; float k = 0.3; if(sk == 0) return 0; return to_int(f * log( 1.0 + sk/(f + sk * k) ) + 0.5); } /** * Change everyones skill levels (downwards). * @param thing the person to update */ void change_skills_zeta( object thing ) { mapping skills; string *bits, bit; skills = thing->query_skills(); if (!sizeof(skills)) return ; bits = keys(skills); tell_object(thing, "A small blue frog appears and says: I'm just updating " "your skill levels, this won't take a second.\n"); // go through all the skills one by one foreach(bit in bits) { // if it's not a language skill & it's a leaf skill recalculate it and // then recalculate it's parents. if(bit[0..13] != "general.language" && SKILL_OB->query_immediate_children(bit) == ({ })) { skills[bit] = new_skill_value(skills[bit]); skills = recalc_parents(skills, bit); } } // set these skills as their new skills and we're done. thing->set_skills( skills ); tell_object(thing, "The frog says: Ok, all done now.\n"); } /** * Called by the login handler, checks to see if an upgrade * is neede. * @param name the name of the player logging in * @param type the type of the login * @see /handlers/login_handler.c */ void entering( string name, string type ) { object thing; int lvl; if ( type != LOGIN ) return; if ( !( thing = find_player( name ) ) ) return; switch((string)thing->query_property( "skills version" )) { case VERSION: change_skills( thing ); case VERSION_2: change_language_skills( thing, lvl ); case VERSION_3: change_skills_epsilon( thing ); case VERSION_4: change_skills_zeta(thing); default: // If they don't have a skill version then they're a new player and // don't need any modification to their skill tree. thing->add_property("skills version", VERSION_5); return; } } /* entering() */ /** * The current version of the skill tree. * @return the version of the skill system */ string query_version() { return VERSION_5; }