skylib_fluffos_v3/
skylib_fluffos_v3/bin/
skylib_fluffos_v3/bin/db/
skylib_fluffos_v3/fluffos-2.9-ds2.04/
skylib_fluffos_v3/fluffos-2.9-ds2.04/ChangeLog.old/
skylib_fluffos_v3/fluffos-2.9-ds2.04/Win32/
skylib_fluffos_v3/fluffos-2.9-ds2.04/compat/
skylib_fluffos_v3/fluffos-2.9-ds2.04/compat/simuls/
skylib_fluffos_v3/fluffos-2.9-ds2.04/include/
skylib_fluffos_v3/fluffos-2.9-ds2.04/testsuite/
skylib_fluffos_v3/fluffos-2.9-ds2.04/testsuite/clone/
skylib_fluffos_v3/fluffos-2.9-ds2.04/testsuite/command/
skylib_fluffos_v3/fluffos-2.9-ds2.04/testsuite/data/
skylib_fluffos_v3/fluffos-2.9-ds2.04/testsuite/etc/
skylib_fluffos_v3/fluffos-2.9-ds2.04/testsuite/include/
skylib_fluffos_v3/fluffos-2.9-ds2.04/testsuite/inherit/
skylib_fluffos_v3/fluffos-2.9-ds2.04/testsuite/inherit/master/
skylib_fluffos_v3/fluffos-2.9-ds2.04/testsuite/log/
skylib_fluffos_v3/fluffos-2.9-ds2.04/testsuite/single/
skylib_fluffos_v3/fluffos-2.9-ds2.04/testsuite/single/tests/compiler/
skylib_fluffos_v3/fluffos-2.9-ds2.04/testsuite/single/tests/efuns/
skylib_fluffos_v3/fluffos-2.9-ds2.04/testsuite/single/tests/operators/
skylib_fluffos_v3/fluffos-2.9-ds2.04/testsuite/u/
skylib_fluffos_v3/fluffos-2.9-ds2.04/tmp/
skylib_fluffos_v3/fluffos-2.9-ds2.04/windows/
skylib_fluffos_v3/mudlib/
skylib_fluffos_v3/mudlib/cmds/
skylib_fluffos_v3/mudlib/cmds/admin/
skylib_fluffos_v3/mudlib/cmds/guild-race/
skylib_fluffos_v3/mudlib/cmds/living/broken/
skylib_fluffos_v3/mudlib/cmds/player/group_cmds/
skylib_fluffos_v3/mudlib/cmds/playtester/
skylib_fluffos_v3/mudlib/d/admin/
skylib_fluffos_v3/mudlib/d/admin/room/
skylib_fluffos_v3/mudlib/d/admin/room/we_care/
skylib_fluffos_v3/mudlib/d/admin/save/
skylib_fluffos_v3/mudlib/d/admin/text/
skylib_fluffos_v3/mudlib/d/learning/TinyTown/buildings/
skylib_fluffos_v3/mudlib/d/learning/TinyTown/map/
skylib_fluffos_v3/mudlib/d/learning/TinyTown/roads/
skylib_fluffos_v3/mudlib/d/learning/chars/
skylib_fluffos_v3/mudlib/d/learning/functions/
skylib_fluffos_v3/mudlib/d/learning/handlers/
skylib_fluffos_v3/mudlib/d/learning/help_topics/
skylib_fluffos_v3/mudlib/d/learning/help_topics/npcs/
skylib_fluffos_v3/mudlib/d/learning/help_topics/objects/
skylib_fluffos_v3/mudlib/d/learning/help_topics/rcs_demo/
skylib_fluffos_v3/mudlib/d/learning/help_topics/rcs_demo/RCS/
skylib_fluffos_v3/mudlib/d/learning/help_topics/rooms/
skylib_fluffos_v3/mudlib/d/learning/help_topics/rooms/crowd/
skylib_fluffos_v3/mudlib/d/learning/help_topics/rooms/situations/
skylib_fluffos_v3/mudlib/d/learning/save/
skylib_fluffos_v3/mudlib/d/learning/school/
skylib_fluffos_v3/mudlib/d/learning/school/add_sc/
skylib_fluffos_v3/mudlib/d/learning/school/characters/
skylib_fluffos_v3/mudlib/d/learning/school/general/
skylib_fluffos_v3/mudlib/d/learning/school/getting-started/
skylib_fluffos_v3/mudlib/d/learning/school/getting-started/basic_commands/
skylib_fluffos_v3/mudlib/d/learning/school/getting-started/edtutor/
skylib_fluffos_v3/mudlib/d/learning/school/getting-started/unix_tutor/
skylib_fluffos_v3/mudlib/d/learning/school/items/
skylib_fluffos_v3/mudlib/d/learning/school/npc_school/
skylib_fluffos_v3/mudlib/d/learning/school/room_school/
skylib_fluffos_v3/mudlib/d/learning/school/room_school/room_basic/
skylib_fluffos_v3/mudlib/d/learning/school/room_school/situations/
skylib_fluffos_v3/mudlib/d/learning/school/room_school/terrain_tutor/
skylib_fluffos_v3/mudlib/d/learning/text/
skylib_fluffos_v3/mudlib/d/liaison/
skylib_fluffos_v3/mudlib/d/mudlib/
skylib_fluffos_v3/mudlib/d/mudlib/changes/
skylib_fluffos_v3/mudlib/d/playtesters/
skylib_fluffos_v3/mudlib/d/playtesters/effects/
skylib_fluffos_v3/mudlib/d/playtesters/handlers/
skylib_fluffos_v3/mudlib/d/playtesters/items/
skylib_fluffos_v3/mudlib/d/sage/
skylib_fluffos_v3/mudlib/doc/
skylib_fluffos_v3/mudlib/doc/creator/
skylib_fluffos_v3/mudlib/doc/driver/
skylib_fluffos_v3/mudlib/doc/driver/efuns/arrays/
skylib_fluffos_v3/mudlib/doc/driver/efuns/buffers/
skylib_fluffos_v3/mudlib/doc/driver/efuns/calls/
skylib_fluffos_v3/mudlib/doc/driver/efuns/compile/
skylib_fluffos_v3/mudlib/doc/driver/efuns/filesystem/
skylib_fluffos_v3/mudlib/doc/driver/efuns/floats/
skylib_fluffos_v3/mudlib/doc/driver/efuns/functions/
skylib_fluffos_v3/mudlib/doc/driver/efuns/general/
skylib_fluffos_v3/mudlib/doc/driver/efuns/mappings/
skylib_fluffos_v3/mudlib/doc/driver/efuns/mixed/
skylib_fluffos_v3/mudlib/doc/driver/efuns/mudlib/
skylib_fluffos_v3/mudlib/doc/driver/efuns/numbers/
skylib_fluffos_v3/mudlib/doc/driver/efuns/parsing/
skylib_fluffos_v3/mudlib/doc/login/
skylib_fluffos_v3/mudlib/doc/lpc/basic_manual/
skylib_fluffos_v3/mudlib/doc/lpc/intermediate/
skylib_fluffos_v3/mudlib/doc/new/add_command/
skylib_fluffos_v3/mudlib/doc/new/events/
skylib_fluffos_v3/mudlib/doc/new/handlers/
skylib_fluffos_v3/mudlib/doc/new/living/race/
skylib_fluffos_v3/mudlib/doc/new/living/spells/
skylib_fluffos_v3/mudlib/doc/new/object/
skylib_fluffos_v3/mudlib/doc/new/player/
skylib_fluffos_v3/mudlib/doc/new/room/guild/
skylib_fluffos_v3/mudlib/doc/new/room/outside/
skylib_fluffos_v3/mudlib/doc/new/room/storeroom/
skylib_fluffos_v3/mudlib/doc/object/
skylib_fluffos_v3/mudlib/doc/playtesters/
skylib_fluffos_v3/mudlib/doc/policy/
skylib_fluffos_v3/mudlib/doc/weapons/
skylib_fluffos_v3/mudlib/global/
skylib_fluffos_v3/mudlib/global/creator/
skylib_fluffos_v3/mudlib/handlers/
skylib_fluffos_v3/mudlib/include/casino/
skylib_fluffos_v3/mudlib/include/cmds/
skylib_fluffos_v3/mudlib/include/effects/
skylib_fluffos_v3/mudlib/include/npc/
skylib_fluffos_v3/mudlib/include/room/
skylib_fluffos_v3/mudlib/include/shops/
skylib_fluffos_v3/mudlib/net/daemon/
skylib_fluffos_v3/mudlib/net/daemon/chars/
skylib_fluffos_v3/mudlib/net/inherit/
skylib_fluffos_v3/mudlib/net/obj/
skylib_fluffos_v3/mudlib/net/obj/BACKUPS/
skylib_fluffos_v3/mudlib/obj/amulets/
skylib_fluffos_v3/mudlib/obj/armours/plate/
skylib_fluffos_v3/mudlib/obj/b_day/
skylib_fluffos_v3/mudlib/obj/clothes/transport/horse/
skylib_fluffos_v3/mudlib/obj/faith/symbols/
skylib_fluffos_v3/mudlib/obj/fungi/
skylib_fluffos_v3/mudlib/obj/gatherables/
skylib_fluffos_v3/mudlib/obj/instruments/
skylib_fluffos_v3/mudlib/obj/media/
skylib_fluffos_v3/mudlib/obj/misc/player_shop/
skylib_fluffos_v3/mudlib/obj/monster/godmother/
skylib_fluffos_v3/mudlib/obj/monster/transport/
skylib_fluffos_v3/mudlib/obj/rings/
skylib_fluffos_v3/mudlib/obj/scabbards/
skylib_fluffos_v3/mudlib/obj/spells/
skylib_fluffos_v3/mudlib/obj/stationery/
skylib_fluffos_v3/mudlib/obj/stationery/envelopes/
skylib_fluffos_v3/mudlib/obj/toys/
skylib_fluffos_v3/mudlib/obj/vessels/
skylib_fluffos_v3/mudlib/obj/weapons/axes/
skylib_fluffos_v3/mudlib/obj/weapons/chains/
skylib_fluffos_v3/mudlib/obj/weapons/maces/BACKUPS/
skylib_fluffos_v3/mudlib/save/autodoc/
skylib_fluffos_v3/mudlib/save/book_handler/
skylib_fluffos_v3/mudlib/save/books/history/calarien/
skylib_fluffos_v3/mudlib/save/mail/
skylib_fluffos_v3/mudlib/save/new_soul/data/
skylib_fluffos_v3/mudlib/save/parcels/
skylib_fluffos_v3/mudlib/save/playerinfo/
skylib_fluffos_v3/mudlib/save/players/d/
skylib_fluffos_v3/mudlib/save/players/s/
skylib_fluffos_v3/mudlib/save/random_names/
skylib_fluffos_v3/mudlib/save/random_names/data/
skylib_fluffos_v3/mudlib/save/terrains/
skylib_fluffos_v3/mudlib/save/terrains/tutorial_desert/
skylib_fluffos_v3/mudlib/save/terrains/tutorial_grassy_field/
skylib_fluffos_v3/mudlib/save/terrains/tutorial_mountain/
skylib_fluffos_v3/mudlib/save/todo_lists/
skylib_fluffos_v3/mudlib/secure/
skylib_fluffos_v3/mudlib/secure/cmds/admin/
skylib_fluffos_v3/mudlib/secure/cmds/lord/
skylib_fluffos_v3/mudlib/secure/config/
skylib_fluffos_v3/mudlib/secure/handlers/autodoc/
skylib_fluffos_v3/mudlib/secure/handlers/intermud/
skylib_fluffos_v3/mudlib/secure/include/global/
skylib_fluffos_v3/mudlib/secure/save/
skylib_fluffos_v3/mudlib/secure/save/handlers/
skylib_fluffos_v3/mudlib/secure/std/
skylib_fluffos_v3/mudlib/secure/std/classes/
skylib_fluffos_v3/mudlib/secure/std/modules/
skylib_fluffos_v3/mudlib/std/creator/
skylib_fluffos_v3/mudlib/std/dom/
skylib_fluffos_v3/mudlib/std/effects/
skylib_fluffos_v3/mudlib/std/effects/external/
skylib_fluffos_v3/mudlib/std/effects/fighting/
skylib_fluffos_v3/mudlib/std/effects/magic/
skylib_fluffos_v3/mudlib/std/effects/magic/BACKUPS/
skylib_fluffos_v3/mudlib/std/effects/other/BACKUPS/
skylib_fluffos_v3/mudlib/std/effects/priest/
skylib_fluffos_v3/mudlib/std/effects/room/
skylib_fluffos_v3/mudlib/std/environ/
skylib_fluffos_v3/mudlib/std/guilds/
skylib_fluffos_v3/mudlib/std/guilds/old/
skylib_fluffos_v3/mudlib/std/languages/
skylib_fluffos_v3/mudlib/std/liquids/
skylib_fluffos_v3/mudlib/std/npc/
skylib_fluffos_v3/mudlib/std/npc/goals/
skylib_fluffos_v3/mudlib/std/npc/goals/basic/
skylib_fluffos_v3/mudlib/std/npc/goals/misc/
skylib_fluffos_v3/mudlib/std/npc/plans/
skylib_fluffos_v3/mudlib/std/npc/plans/basic/
skylib_fluffos_v3/mudlib/std/npc/types/
skylib_fluffos_v3/mudlib/std/npc/types/helper/
skylib_fluffos_v3/mudlib/std/npcs/
skylib_fluffos_v3/mudlib/std/outsides/
skylib_fluffos_v3/mudlib/std/races/shadows/
skylib_fluffos_v3/mudlib/std/room/basic/BACKUPS/
skylib_fluffos_v3/mudlib/std/room/basic/topography/
skylib_fluffos_v3/mudlib/std/room/controller/
skylib_fluffos_v3/mudlib/std/room/inherit/topography/
skylib_fluffos_v3/mudlib/std/room/topography/area/
skylib_fluffos_v3/mudlib/std/room/topography/iroom/
skylib_fluffos_v3/mudlib/std/room/topography/milestone/
skylib_fluffos_v3/mudlib/std/shadows/curses/
skylib_fluffos_v3/mudlib/std/shadows/disease/
skylib_fluffos_v3/mudlib/std/shadows/fighting/
skylib_fluffos_v3/mudlib/std/shadows/healing/
skylib_fluffos_v3/mudlib/std/shadows/magic/
skylib_fluffos_v3/mudlib/std/shadows/poison/
skylib_fluffos_v3/mudlib/std/shadows/room/
skylib_fluffos_v3/mudlib/std/shops/controllers/
skylib_fluffos_v3/mudlib/std/shops/objs/
skylib_fluffos_v3/mudlib/std/shops/player_shop/
skylib_fluffos_v3/mudlib/std/socket/
skylib_fluffos_v3/mudlib/std/soul/d/
skylib_fluffos_v3/mudlib/std/soul/e/
skylib_fluffos_v3/mudlib/std/soul/i/
skylib_fluffos_v3/mudlib/std/soul/j/
skylib_fluffos_v3/mudlib/std/soul/k/
skylib_fluffos_v3/mudlib/std/soul/l/
skylib_fluffos_v3/mudlib/std/soul/n/
skylib_fluffos_v3/mudlib/std/soul/o/
skylib_fluffos_v3/mudlib/std/soul/q/
skylib_fluffos_v3/mudlib/std/soul/r/
skylib_fluffos_v3/mudlib/std/soul/u/
skylib_fluffos_v3/mudlib/std/soul/v/
skylib_fluffos_v3/mudlib/std/soul/y/
skylib_fluffos_v3/mudlib/std/soul/z/
skylib_fluffos_v3/mudlib/std/stationery/
skylib_fluffos_v3/mudlib/w/
skylib_fluffos_v3/mudlib/w/default/
skylib_fluffos_v3/mudlib/w/default/armour/
skylib_fluffos_v3/mudlib/w/default/clothes/
skylib_fluffos_v3/mudlib/w/default/item/
skylib_fluffos_v3/mudlib/w/default/npc/
skylib_fluffos_v3/mudlib/w/default/room/
skylib_fluffos_v3/mudlib/w/default/weapon/
skylib_fluffos_v3/mudlib/www/
skylib_fluffos_v3/mudlib/www/java/
skylib_fluffos_v3/mudlib/www/secure/
skylib_fluffos_v3/mudlib/www/secure/lpc/advanced/
skylib_fluffos_v3/mudlib/www/secure/lpc/intermediate/
skylib_fluffos_v3/win32/
/**
 * This skills modules for living creates.  This deals will all the
 * skill interactions needed for the living objects.
 *
 * @author Pinkfish
 */
/*
 * sigh, this will be interesting wont it?
 * quick summary of routines
 * skill_bonus(string,int)     - gives the bonus...
 * skill_lvl(string)           - gives the raw level, with out stat bonus etc
 * modify_skill(string,int,int)- modifies the skills level by int.
 * calc_bonus(int,string,int)  - given the skill lvl, stat bonus str cals bonus
 * calc_lvl(string *)          - calculate number of lvls in the path
 * add_skill_lvl(...)          - horror recursive skill adder. arghh
 * teach_skill(objct *,string) - Used to teach skills to other people.
 */
/*
 * Modifications:
 * Changed to use new creator checks - Shaydz - 07/03/01
 * Fixed a bug with add_skill_level - Shiannar.
 * Fixed a bug with disappearing first skill levels in add_skill_level
 * - Sandoz 31/01/02
 * Fixed another bug with disappearing first skill levels in add_skill_level
 * - Sandoz 06/02/02
 */

#include <skills.h>
#include <tasks.h>
#include <top_ten_tables.h>
#include <tune.h>

// #define BAD_TM "BAD_TM"
#undef LOGGING

varargs int calc_bonus( int lvl, string skill, int use_base_stats );
varargs int stat_modify( int bonus, string skill, int use_base_stats );
mixed recursive_skill_add( mixed skil, string *path, int avr, int lvl, int exp,
                           mixed standard );
private void convert_skills( mixed skills, string path );
int query_skill( string skill );

mapping new_skills;

nosave mapping _bonus_cache, _stat_cache, _teach_offer;

string last_award, last_ob;
int last_award_time;

mapping query_skills() { return copy( new_skills ); }
void set_skills( mapping map ) { new_skills = map; }
int calc_level(string *path);

void create() {
    _bonus_cache = ([ ]);
    _stat_cache = ([ ]);
    new_skills = ([ ]);
} /* create() */

/**
 * This method checks to see if the skill exists in the skill array or
 * not.
 * @param skill the skill to check for non-existance
 * @return 0 if it does not exist, 1 if it does
 */
int not_there( string skill ) {
    return undefinedp( new_skills[skill] );
} /* not_there() */

/**
 * This method returns the current bonus cache for the living thing.
 * The bonus cache is where the calculated bonuses for the skills are
 * kept.
 * @return the bonus cache mapping
 */
mapping query_bonus_cache() { return copy(_bonus_cache); }

/**
 * This method returns the cached values for the stats.
 * @return the caches stat values
 */
mapping query_stat_cache() { return copy(_stat_cache); }

/**
 * This method zaps the stat cache when a certain stat changes.
 * It calls the function stats_to_zap() on the living object to
 * figure out which stats have changed.
 * @see /std/living/stats->stats_to_zap()
 */
void zap_stat_cache() {
    int i;
    string stat, word, *list;

    stat = TO->stats_to_zap();
    if( !stat )
        return;

    if( find_call_out("reset_all2") == -1 )
        call_out("reset_all2", 1 );

    foreach( i in stat ) {
        if( !list = _stat_cache[ i ] )
            continue;
        foreach( word in list )
            map_delete( _stat_cache, word );
    }

    if( word = (string)TO->query_race_ob() )
        word->set_unarmed_attacks( TO );

} /* zap_stat_cache() */

/**
 * This method zaps the bonus cache.
 */
protected void totaly_zap_bonus_cache() {
   _bonus_cache = ([ ]);
} /* zap_bonus_cache() */

/**
 * This method zaps the stat cache.
 */
protected void totaly_zap_stat_cache() {
   _stat_cache = ([ ]);
} /* zap_stat_cache() */

/**
 * This is used to convert a previously not only_leaf tree into an only_leaf
 * tree.
 */
private void flatten_it( string skill ) {
   int value;
   string *same, tmp;

   reset_eval_cost();

   value = new_skills[skill];
   same = SKILL_H->query_immediate_children(skill);

   foreach( tmp in same ) {
      if( not_there( tmp ) )
          new_skills[tmp] = value;
      flatten_it( tmp );
   }

   if( sizeof(same) )
       map_delete( new_skills, skill );

} /* flatten_it() */

/**
 * This method returns the skill bonus for the specified skill.
 * It returns the skill + all its bonsues for stats/whatever.
 * It first checks to see if the skill is in it's cache.   THe
 * real stat values are ones not modified by bonuses or temporary
 * values.
 * @param skill the skill to get the bonus for
 * @param use_base_stats tells the system not to use the real stat values
 * @return the skill bonus
 */
varargs int query_skill_bonus( string skill, int use_base_stats ) {

  if( !stringp(skill) || !strlen(skill) )
      return 0;

  if( !new_skills )
      new_skills = ([ ]);

  if( skill[0] == '.' )
      skill = skill[1..];

  if( _bonus_cache[ skill ] ) {
      TASKER->set_control( ({ TO, skill }) );
      return stat_modify( _bonus_cache[ skill ], skill, use_base_stats );
  }

  return calc_bonus( query_skill(skill), skill, use_base_stats );

} /* query_skill_bonus() */

/**
 * This returns just the skill level.  Used a lot to determine if you
 * can use/teach/whatever a skill.
 * This also uses a cache.
 * @param skill the skill to return the level of
 * @return the skill level
 */
int query_skill( string skill ) {
   string *path, tmp;

   if( !stringp(skill) )
       return 0;

   if( !new_skills )
       new_skills = ([ ]);

   if( skill[0] == '.' )
       skill = skill[1..];

   TASKER->set_control( ({ TO, skill }) );

   if( not_there( skill ) ) {
       if( path = SKILL_H->query_skill_tree(skill) )
           foreach( tmp in path )
             if( !not_there( tmp ) )
                 return new_skills[tmp];
   }

   return new_skills[skill];

} /* query_skill() */

/**
 * This method adds a skill level to the specified skill to the
 * system.
 * @param skill the skill to add a level to
 * @param lvl the number of levels to add
 * @param exp the amount of exp spent on the skill
 * @return 1 if the skill level was changed
 * @see query_skill()
 * @see query_skill_bonus()
 */
varargs int add_skill_level( string skill, int lvl, mixed exp ) {
    string tmp, sk;
    string *recursive_skills;
    string *same_level;
    string *bits;
    string *tree;
    int i;

    reset_eval_cost();

    if( !stringp(skill) || !intp(lvl) || lvl > 1000 )
        return 0;

    if( lvl > 100 && interactive(TO) && !creatorp(TO) ) {
        log_file("CHEAT", "%s %s gave %d levels of %s to %s\n", ctime(time()),
            ( PO->query_name() || base_name(PO) ), lvl, skill,
            ( TO->query_name() || TO ) );
    }

    if( !new_skills || !mapp(new_skills) )
        new_skills = ([ ]);

    if( skill[0] == '.' )
        skill = skill[1..];

    recursive_skills = SKILL_H->query_related_skills(skill);
    if( !recursive_skills )
        return 0;

    bits = explode( skill, ".");

    /*
     * Make sure the path leading up to this skill exists so that we can
     * get the right value for the skill when we add it in.
     * This should only be done if they are not only leaf skills.
     */
    if( not_there( skill ) && !SKILL_H->query_only_leaf(skill) ) {
        int tmp_lvl;

        if( sizeof(bits) > 1 ) {
            tmp_lvl = 0;
            for( i = sizeof(bits)-1; !tmp_lvl && i >= 0; i-- ) {
               if( !not_there( implode( bits[ 0 .. i ], "." ) ) ) {
                   tmp_lvl = new_skills[ implode(bits[0..i], ".") ];
                   break;
               }
            }

            if( i >= 0 ) {
                for( ; i < sizeof(bits); i++ ) {
                    same_level = SKILL_H->query_immediate_children(
                        implode( bits[0..i], ".") );

                    foreach( tmp in same_level ) {
                        if( not_there( tmp ) ) {
                            new_skills[ tmp ] = tmp_lvl;
                            map_delete( _bonus_cache, tmp );
                        }
                    }
                }
            } else {
                tmp_lvl = 0;
            }
        }
    }

    /* Includes the current skill */
    foreach( tmp in recursive_skills ) {
        if( !not_there( tmp ) ) {
            new_skills[ tmp ] += lvl;
            if( new_skills[ tmp ] < 0 )
                new_skills[ tmp ] = 0;
        }
        map_delete( _bonus_cache, tmp );
    }

    if( not_there( skill ) )
        new_skills[skill] = lvl;

    /*
     * If it is not a only_leaf heirarchy, then fix up all the lower level
     * average skill levels.
     * The first element is the current skill.
     */
    tree = SKILL_H->query_skill_tree(skill)[1..];
    foreach( sk in tree ) {
        int total;

        same_level = SKILL_H->query_immediate_children( sk );
        if( sizeof(same_level) ) {
            total = 0;
            foreach( tmp in same_level ) {
                /* If it does not exist.  Set it from the top value down. */
                if( not_there( tmp ) ) {
                    if( !not_there( sk ) )
                        new_skills[ tmp ] = new_skills[ sk ];
                    map_delete( _bonus_cache, tmp );
                }
                total += new_skills[ tmp ];
            }
            new_skills[ sk ] = total / sizeof(same_level);
            map_delete( _bonus_cache, sk );
        }
    }

    // Update the high level players table.
    if( interactive(TO) )
        TOP_TEN_HANDLER->player_skill_advance( TO );

    if( !exp )
        exp = PO;

    if( lvl == 1 && userp(TO) && objectp(exp) )
        TASKMASTER_H->award_made( (string)TO->query_name(), base_name( exp ),
            skill, new_skills[ skill ] );

    // Make sure that there is at most one call_out running.
    if( find_call_out( "reset_all" ) == -1 )
        call_out( "reset_all", 1 );

    return 1;

} /* add_skill_level() */

/**
 * This method returns the skill as it should be modified by the
 * stats associated with it.
 * @param lvl the level to modify
 * @param skill the skill the modify the bonus of
 * @param use_base_stats use the real unmodified stat values
 * @see query_skill_bonus()
 * @return the stat modification
 */
varargs int stat_modify( int lvl, string skill, int use_base_stats ) {
   int i, stat, bonus;
   string stat_bonus;

   if( !_stat_cache[ skill ] || use_base_stats ) {
       stat_bonus = SKILL_H->query_skill_stat(skill);
       foreach( i in stat_bonus ) {
         switch( i ) {
            case 'C' :
              stat = ( use_base_stats ? (int)TO->query_real_con() :
                                        (int)TO->query_con() );
              break;
            case 'D' :
              stat = ( use_base_stats ? (int)TO->query_real_dex() :
                                        (int)TO->query_dex() );
              break;
            case 'I' :
              stat = ( use_base_stats ? (int)TO->query_real_int() :
                                        (int)TO->query_int() );
              break;
            case 'S' :
              stat = ( use_base_stats ? (int)TO->query_real_str() :
                                        (int)TO->query_str() );
              break;
            case 'W' :
            default :
              stat = ( use_base_stats ? (int)TO->query_real_wis() :
                                        (int)TO->query_wis() );
         }
         bonus += ( stat - 13 ) * 3;
         if( !_stat_cache[ i ] ) {
             _stat_cache[ i ] = ({ skill });
         } else {
             if( member_array( skill, _stat_cache[ i ] ) == -1 )
                 _stat_cache[ i ] += ({ skill });
         }
      }
      if( !use_base_stats )
          _stat_cache[ skill ] = ({ bonus, stat_bonus });
   } else {
      bonus = _stat_cache[ skill ][ 0 ];
      stat_bonus = _stat_cache[ skill ][ 1 ];
   }

   if( i = strlen( stat_bonus ) )
       return lvl + ( lvl * bonus ) / ( i * 60 );

   return lvl;

} /* stat_modify() */

/*
 * Handy fact: stat_modify( 100, skill ) - 35 is the stat total
 *  for that skill.
 */

/**
 * This method calculates the bonus for the skill.  It takes the raw
 * level and turns that into a bonus and then adds on the stats
 * modifications.
 * @param lvl the level to turn into bonus
 * @param skill the skill to modify the bonus of
 * @param use_base_stats use the real unmodified stats
 * @return the bonus associated with the skill
 */
varargs int calc_bonus( int lvl, string skill, int use_base_stats ) {

   if( lvl > 60 )
       lvl = 170 + ( ( lvl - 60 ) >> 1 );
   else if( lvl > 40 )
       lvl = 150 + ( lvl - 40 );
   else if( lvl > 20 )
       lvl = 100 + ( ( ( lvl - 20 ) * 5 ) >> 1 );
   else
       lvl = lvl * 5;

   if( !use_base_stats )
       _bonus_cache[ skill ] = lvl;

   return stat_modify( lvl, skill, use_base_stats );

} /* calc_bonus() */

/**
 * This method does a skill successful check.  Does this check:<br>
 * (bonus + mos) >= random(200)
 * @param str the skill to check
 * @param mod the modification value
 * @return 1 if the skill check is successful
 */
int query_skill_successful(string str, int mod) {
  return ( query_skill_bonus( str, 0 ) + mod >= random(200) );
} /* query_skill_successful */

/**
 * This method adds a teaching offer to the living object.
 * @param ob the object teaching us
 * @param skill the skill they are teaching
 * @param num the number of levels they are teaching
 * @param lvl the level they are teaching us from
 * @param xp the cost of the level increase in xp
 */
void add_teach_offer( object ob, string skill, int num, int lvl, int xp ) {
  if( !mapp(_teach_offer) )
      _teach_offer = ([ ]);
  _teach_offer[ob] = ({ skill, num, lvl, xp });
} /* add_teach_offer() */

/**
 * This method returns the current list of teach offerings on the
 * living object.
 * @return the mapping containing the teach offerings
 */
mapping query_teach_offer() { return copy(_teach_offer) || ([ ]); }

/**
 * The method to call when we stop teaching skills.  THis will stop the
 * stuff being taught if the stop is successful, and only teach partial
 * amounts if we are not finished yet.
 * @param left the amount of time left
 * @param bing the data associated with the command
 */
void stop_teaching_skills(int left, mixed bing) {
   object ob;

   if (left > 0) {
      /* Someone did a stop!  Naughty frogs! */

      if (bing[O_OTHER_PER] == TO) {
         say(TO->short() + " stops teaching themselves some "
             "skills.\n");
      } else if (previous_object() == TO) {
         ob = bing[O_OTHER_PER];
         tell_object(ob, TO->short() + " interupts your "
                         "training.\n");
      } else {
         ob = TO;
         tell_object(ob, bing[O_OTHER_PER]->short() + " interupts your "
                         "training.\n");
      }
      say(bing[O_OTHER_PER]->short() + " stops teaching some skills to " +
          TO->short() + ".\n",
          ({ TO, bing[O_OTHER_PER] }));

      TO->adjust_time_left(-((int)TO->query_time_left()));
      TO->set_interupt_command(0);

      return ;
   }

   if (previous_object() != TO) {
      /* First make sure we dont get the level twice... */
      return ;
   }

   // additional test added by ceres coz people are getting put into negative
   // xp by getting taught twice somehow.
   if(TO->query_xp() < bing[O_XP]) {
      write("Something has gone wrong. :(\n");
      return;
   }
   /* Ok...  We did it!  Finished! */
   if (TO != bing[O_OTHER_PER]) {
      bing[O_OTHER_PER]->adjust_xp(bing[O_XP]/10);
   }
   TO->adjust_xp(-bing[O_XP]);
   add_skill_level(bing[O_SKILL], bing[O_NUM], bing[O_XP]);
   if (TO != bing[O_OTHER_PER]) {
      tell_object(TO, "You finish learning " + bing[O_NUM] +
                  " levels of "
                  + bing[O_SKILL] + " from " + bing[O_OTHER_PER]->short() +
                  ".\n");
      tell_object(bing[O_OTHER_PER], TO->short() + " finishes " +
                  "learning " + bing[O_NUM] + " levels of "
                  +bing[O_SKILL] + " from you.\n");
      say(TO->short() + " finishes learning some skills "+
          "from "+bing[O_OTHER_PER]->short()+".\n",
          ({ TO, bing[O_OTHER_PER] }));
   } else {
      tell_object(TO, "You finish teaching yourself " + bing[O_NUM] +
                  " levels of " + bing[O_SKILL] + ".\n");
      say(TO->short() + " finishes learning some skills "
          "from " + TO->query_objective() + "self.\n",
          ({ TO, bing[O_OTHER_PER] }));
   }
} /* stop_teaching_skills() */