dw_fluffos_v2/
dw_fluffos_v2/fluffos-2.9-ds2.05/
dw_fluffos_v2/fluffos-2.9-ds2.05/ChangeLog.old/
dw_fluffos_v2/fluffos-2.9-ds2.05/Win32/
dw_fluffos_v2/fluffos-2.9-ds2.05/compat/
dw_fluffos_v2/fluffos-2.9-ds2.05/compat/simuls/
dw_fluffos_v2/fluffos-2.9-ds2.05/include/
dw_fluffos_v2/fluffos-2.9-ds2.05/testsuite/
dw_fluffos_v2/fluffos-2.9-ds2.05/testsuite/clone/
dw_fluffos_v2/fluffos-2.9-ds2.05/testsuite/command/
dw_fluffos_v2/fluffos-2.9-ds2.05/testsuite/data/
dw_fluffos_v2/fluffos-2.9-ds2.05/testsuite/etc/
dw_fluffos_v2/fluffos-2.9-ds2.05/testsuite/include/
dw_fluffos_v2/fluffos-2.9-ds2.05/testsuite/inherit/
dw_fluffos_v2/fluffos-2.9-ds2.05/testsuite/inherit/master/
dw_fluffos_v2/fluffos-2.9-ds2.05/testsuite/log/
dw_fluffos_v2/fluffos-2.9-ds2.05/testsuite/single/
dw_fluffos_v2/fluffos-2.9-ds2.05/testsuite/single/tests/compiler/
dw_fluffos_v2/fluffos-2.9-ds2.05/testsuite/single/tests/efuns/
dw_fluffos_v2/fluffos-2.9-ds2.05/testsuite/single/tests/operators/
dw_fluffos_v2/fluffos-2.9-ds2.05/testsuite/u/
dw_fluffos_v2/fluffos-2.9-ds2.05/tmp/
dw_fluffos_v2/fluffos-2.9-ds2.05/windows/
dw_fluffos_v2/lib/
dw_fluffos_v2/lib/binaries/cmds/
dw_fluffos_v2/lib/binaries/cmds/creator/
dw_fluffos_v2/lib/binaries/cmds/living/
dw_fluffos_v2/lib/binaries/cmds/player/
dw_fluffos_v2/lib/binaries/d/admin/obj/
dw_fluffos_v2/lib/binaries/d/liaison/
dw_fluffos_v2/lib/binaries/global/virtual/
dw_fluffos_v2/lib/binaries/global/virtual/setup_compiler/
dw_fluffos_v2/lib/binaries/obj/handlers/autodoc/
dw_fluffos_v2/lib/binaries/obj/handlers/terrain_things/
dw_fluffos_v2/lib/binaries/obj/misc/
dw_fluffos_v2/lib/binaries/obj/misc/buckets/
dw_fluffos_v2/lib/binaries/obj/monster/
dw_fluffos_v2/lib/binaries/obj/reactions/
dw_fluffos_v2/lib/binaries/obj/reagents/
dw_fluffos_v2/lib/binaries/secure/cmds/creator/
dw_fluffos_v2/lib/binaries/secure/master/
dw_fluffos_v2/lib/binaries/std/
dw_fluffos_v2/lib/binaries/std/dom/
dw_fluffos_v2/lib/binaries/std/effects/object/
dw_fluffos_v2/lib/binaries/std/guilds/
dw_fluffos_v2/lib/binaries/std/languages/
dw_fluffos_v2/lib/binaries/std/races/
dw_fluffos_v2/lib/binaries/std/room/
dw_fluffos_v2/lib/binaries/std/room/basic/
dw_fluffos_v2/lib/binaries/std/shops/
dw_fluffos_v2/lib/binaries/std/shops/inherit/
dw_fluffos_v2/lib/binaries/www/
dw_fluffos_v2/lib/cmds/guild-race/
dw_fluffos_v2/lib/cmds/guild-race/crafts/
dw_fluffos_v2/lib/cmds/guild-race/other/
dw_fluffos_v2/lib/cmds/playtester/
dw_fluffos_v2/lib/cmds/playtester/senior/
dw_fluffos_v2/lib/d/admin/
dw_fluffos_v2/lib/d/admin/log/
dw_fluffos_v2/lib/d/admin/mapper/31-10-01/mapmaker/event/
dw_fluffos_v2/lib/d/admin/meetings/
dw_fluffos_v2/lib/d/admin/obj/
dw_fluffos_v2/lib/d/admin/room/we_care/
dw_fluffos_v2/lib/d/admin/save/
dw_fluffos_v2/lib/d/dist/
dw_fluffos_v2/lib/d/dist/mtf/
dw_fluffos_v2/lib/d/dist/pumpkin/
dw_fluffos_v2/lib/d/dist/pumpkin/chars/
dw_fluffos_v2/lib/d/dist/pumpkin/desert/
dw_fluffos_v2/lib/d/dist/pumpkin/gumboot/
dw_fluffos_v2/lib/d/dist/pumpkin/hospital/
dw_fluffos_v2/lib/d/dist/pumpkin/inherit/
dw_fluffos_v2/lib/d/dist/pumpkin/map/
dw_fluffos_v2/lib/d/dist/pumpkin/plain/
dw_fluffos_v2/lib/d/dist/pumpkin/pumpkin/
dw_fluffos_v2/lib/d/dist/pumpkin/save/
dw_fluffos_v2/lib/d/dist/pumpkin/squash/
dw_fluffos_v2/lib/d/dist/pumpkin/terrain/
dw_fluffos_v2/lib/d/dist/pumpkin/woods/
dw_fluffos_v2/lib/d/dist/start/
dw_fluffos_v2/lib/d/learning/TinyTown/buildings/
dw_fluffos_v2/lib/d/learning/TinyTown/map/
dw_fluffos_v2/lib/d/learning/TinyTown/roads/
dw_fluffos_v2/lib/d/learning/add_command/
dw_fluffos_v2/lib/d/learning/arms_and_weps/
dw_fluffos_v2/lib/d/learning/chars/
dw_fluffos_v2/lib/d/learning/cutnpaste/
dw_fluffos_v2/lib/d/learning/examples/npcs/
dw_fluffos_v2/lib/d/learning/examples/player_houses/npcs/
dw_fluffos_v2/lib/d/learning/examples/terrain_map/basic/
dw_fluffos_v2/lib/d/learning/functions/
dw_fluffos_v2/lib/d/learning/handlers/
dw_fluffos_v2/lib/d/learning/help_topics/npcs/
dw_fluffos_v2/lib/d/learning/help_topics/objects/
dw_fluffos_v2/lib/d/learning/help_topics/rcs_demo/
dw_fluffos_v2/lib/d/learning/help_topics/rooms/
dw_fluffos_v2/lib/d/learning/help_topics/rooms/crowd/
dw_fluffos_v2/lib/d/learning/help_topics/rooms/situations/
dw_fluffos_v2/lib/d/learning/items/
dw_fluffos_v2/lib/d/learning/save/
dw_fluffos_v2/lib/d/liaison/
dw_fluffos_v2/lib/d/liaison/NEWBIE/doc/
dw_fluffos_v2/lib/d/liaison/NEWBIE/save/oldlog/
dw_fluffos_v2/lib/db/
dw_fluffos_v2/lib/doc/
dw_fluffos_v2/lib/doc/creator/
dw_fluffos_v2/lib/doc/creator/autodoc/include/reaction/
dw_fluffos_v2/lib/doc/creator/autodoc/include/ritual_system/
dw_fluffos_v2/lib/doc/creator/autodoc/include/talker/
dw_fluffos_v2/lib/doc/creator/autodoc/include/terrain_map/
dw_fluffos_v2/lib/doc/creator/autodoc/obj/baggage/
dw_fluffos_v2/lib/doc/creator/autodoc/obj/clock/
dw_fluffos_v2/lib/doc/creator/autodoc/obj/clothing/
dw_fluffos_v2/lib/doc/creator/autodoc/obj/cont_save/
dw_fluffos_v2/lib/doc/creator/autodoc/obj/corpse/
dw_fluffos_v2/lib/doc/creator/autodoc/obj/money/
dw_fluffos_v2/lib/doc/creator/autodoc/obj/monster/
dw_fluffos_v2/lib/doc/creator/autodoc/obj/scabbard/
dw_fluffos_v2/lib/doc/creator/autodoc/obj/service_provider/
dw_fluffos_v2/lib/doc/creator/autodoc/obj/state_changer/
dw_fluffos_v2/lib/doc/creator/autodoc/obj/wand/
dw_fluffos_v2/lib/doc/creator/autodoc/std/book_dir/
dw_fluffos_v2/lib/doc/creator/autodoc/std/key/
dw_fluffos_v2/lib/doc/creator/autodoc/std/learning/
dw_fluffos_v2/lib/doc/creator/autodoc/std/map/
dw_fluffos_v2/lib/doc/creator/autodoc/std/race/
dw_fluffos_v2/lib/doc/creator/autodoc/std/weapon_logic/
dw_fluffos_v2/lib/doc/creator/files/
dw_fluffos_v2/lib/doc/creator/policy/
dw_fluffos_v2/lib/doc/creator/room/
dw_fluffos_v2/lib/doc/effects/
dw_fluffos_v2/lib/doc/ideas/
dw_fluffos_v2/lib/doc/known_command/
dw_fluffos_v2/lib/doc/lpc/basic_manual/
dw_fluffos_v2/lib/doc/lpc/intermediate/
dw_fluffos_v2/lib/doc/new/add_command/
dw_fluffos_v2/lib/doc/new/handlers/
dw_fluffos_v2/lib/doc/new/living/
dw_fluffos_v2/lib/doc/new/living/race/
dw_fluffos_v2/lib/doc/new/living/spells/
dw_fluffos_v2/lib/doc/new/player/
dw_fluffos_v2/lib/doc/new/room/guild/
dw_fluffos_v2/lib/doc/new/room/outside/
dw_fluffos_v2/lib/doc/new/room/storeroom/
dw_fluffos_v2/lib/doc/object/
dw_fluffos_v2/lib/doc/playtesters/
dw_fluffos_v2/lib/doc/policy/
dw_fluffos_v2/lib/doc/weapons/
dw_fluffos_v2/lib/global/handlers/
dw_fluffos_v2/lib/global/virtual/setup_compiler/
dw_fluffos_v2/lib/include/
dw_fluffos_v2/lib/include/cmds/
dw_fluffos_v2/lib/include/effects/
dw_fluffos_v2/lib/include/npc/
dw_fluffos_v2/lib/include/shops/
dw_fluffos_v2/lib/net/daemon/chars/
dw_fluffos_v2/lib/net/inherit/
dw_fluffos_v2/lib/net/intermud3/
dw_fluffos_v2/lib/net/intermud3/services/
dw_fluffos_v2/lib/net/obj/
dw_fluffos_v2/lib/net/save/
dw_fluffos_v2/lib/net/smnmp/
dw_fluffos_v2/lib/net/snmp/
dw_fluffos_v2/lib/obj/amulets/
dw_fluffos_v2/lib/obj/b_day/
dw_fluffos_v2/lib/obj/examples/
dw_fluffos_v2/lib/obj/food/alcohol/
dw_fluffos_v2/lib/obj/food/chocolates/
dw_fluffos_v2/lib/obj/food/fruits/
dw_fluffos_v2/lib/obj/food/meat/
dw_fluffos_v2/lib/obj/food/nuts/
dw_fluffos_v2/lib/obj/food/seafood/
dw_fluffos_v2/lib/obj/food/vegetables/
dw_fluffos_v2/lib/obj/fungi/
dw_fluffos_v2/lib/obj/furnitures/artwork/
dw_fluffos_v2/lib/obj/furnitures/bathroom/
dw_fluffos_v2/lib/obj/furnitures/beds/
dw_fluffos_v2/lib/obj/furnitures/cabinets/
dw_fluffos_v2/lib/obj/furnitures/chairs/
dw_fluffos_v2/lib/obj/furnitures/chests/
dw_fluffos_v2/lib/obj/furnitures/clocks/
dw_fluffos_v2/lib/obj/furnitures/crockery/
dw_fluffos_v2/lib/obj/furnitures/cupboards/
dw_fluffos_v2/lib/obj/furnitures/cushions/
dw_fluffos_v2/lib/obj/furnitures/fake_plants/
dw_fluffos_v2/lib/obj/furnitures/lamps/
dw_fluffos_v2/lib/obj/furnitures/mirrors/
dw_fluffos_v2/lib/obj/furnitures/outdoor/
dw_fluffos_v2/lib/obj/furnitures/safes/
dw_fluffos_v2/lib/obj/furnitures/shelves/
dw_fluffos_v2/lib/obj/furnitures/sideboards/
dw_fluffos_v2/lib/obj/furnitures/sofas/
dw_fluffos_v2/lib/obj/furnitures/stoves/
dw_fluffos_v2/lib/obj/furnitures/tables/
dw_fluffos_v2/lib/obj/furnitures/wardrobes/
dw_fluffos_v2/lib/obj/handlers/
dw_fluffos_v2/lib/obj/handlers/autodoc/
dw_fluffos_v2/lib/obj/jewellery/anklets/
dw_fluffos_v2/lib/obj/jewellery/bracelets/
dw_fluffos_v2/lib/obj/jewellery/earrings/
dw_fluffos_v2/lib/obj/jewellery/misc/
dw_fluffos_v2/lib/obj/jewellery/necklaces/
dw_fluffos_v2/lib/obj/jewellery/rings/
dw_fluffos_v2/lib/obj/media/
dw_fluffos_v2/lib/obj/misc/buckets/
dw_fluffos_v2/lib/obj/misc/jars/
dw_fluffos_v2/lib/obj/misc/papers/
dw_fluffos_v2/lib/obj/misc/player_shop/
dw_fluffos_v2/lib/obj/misc/shops/
dw_fluffos_v2/lib/obj/misc/traps/
dw_fluffos_v2/lib/obj/monster/
dw_fluffos_v2/lib/obj/monster/godmother/
dw_fluffos_v2/lib/obj/monster/transport/
dw_fluffos_v2/lib/obj/plants/inherit/
dw_fluffos_v2/lib/obj/potions/
dw_fluffos_v2/lib/open/boards/
dw_fluffos_v2/lib/save/autodoc/
dw_fluffos_v2/lib/save/bank_accounts/
dw_fluffos_v2/lib/save/boards/frog/
dw_fluffos_v2/lib/save/books/bed_catalog/
dw_fluffos_v2/lib/save/creators/
dw_fluffos_v2/lib/save/mail/
dw_fluffos_v2/lib/save/mail/p/
dw_fluffos_v2/lib/save/soul/data/
dw_fluffos_v2/lib/save/tasks/
dw_fluffos_v2/lib/save/vaults/
dw_fluffos_v2/lib/secure/cmds/lord/
dw_fluffos_v2/lib/secure/config/
dw_fluffos_v2/lib/secure/items/
dw_fluffos_v2/lib/secure/player/
dw_fluffos_v2/lib/soul/
dw_fluffos_v2/lib/soul/i/
dw_fluffos_v2/lib/soul/j/
dw_fluffos_v2/lib/soul/k/
dw_fluffos_v2/lib/soul/o/
dw_fluffos_v2/lib/soul/q/
dw_fluffos_v2/lib/soul/to_approve/
dw_fluffos_v2/lib/soul/u/
dw_fluffos_v2/lib/soul/v/
dw_fluffos_v2/lib/soul/wish_list/
dw_fluffos_v2/lib/soul/y/
dw_fluffos_v2/lib/soul/z/
dw_fluffos_v2/lib/std/creator/
dw_fluffos_v2/lib/std/effects/
dw_fluffos_v2/lib/std/effects/attached/
dw_fluffos_v2/lib/std/effects/external/
dw_fluffos_v2/lib/std/effects/fighting/
dw_fluffos_v2/lib/std/effects/other/
dw_fluffos_v2/lib/std/environ/
dw_fluffos_v2/lib/std/guilds/
dw_fluffos_v2/lib/std/hospital/
dw_fluffos_v2/lib/std/house/
dw_fluffos_v2/lib/std/house/onebedhouse/
dw_fluffos_v2/lib/std/house/onebedhut/
dw_fluffos_v2/lib/std/house/tworoomflat/
dw_fluffos_v2/lib/std/languages/
dw_fluffos_v2/lib/std/liquids/
dw_fluffos_v2/lib/std/nationality/
dw_fluffos_v2/lib/std/nationality/accents/
dw_fluffos_v2/lib/std/nationality/accents/national/
dw_fluffos_v2/lib/std/nationality/accents/regional/
dw_fluffos_v2/lib/std/npc/goals/
dw_fluffos_v2/lib/std/npc/goals/basic/
dw_fluffos_v2/lib/std/npc/goals/misc/
dw_fluffos_v2/lib/std/npc/inherit/
dw_fluffos_v2/lib/std/npc/plans/
dw_fluffos_v2/lib/std/npc/plans/basic/
dw_fluffos_v2/lib/std/outsides/
dw_fluffos_v2/lib/std/races/shadows/
dw_fluffos_v2/lib/std/room/basic/topography/
dw_fluffos_v2/lib/std/room/controller/
dw_fluffos_v2/lib/std/room/controller/topography/
dw_fluffos_v2/lib/std/room/furniture/games/
dw_fluffos_v2/lib/std/room/furniture/inherit/
dw_fluffos_v2/lib/std/room/inherit/carriage/
dw_fluffos_v2/lib/std/room/inherit/topography/
dw_fluffos_v2/lib/std/room/punishments/
dw_fluffos_v2/lib/std/room/topography/area/
dw_fluffos_v2/lib/std/room/topography/iroom/
dw_fluffos_v2/lib/std/room/topography/milestone/
dw_fluffos_v2/lib/std/shadows/
dw_fluffos_v2/lib/std/shadows/attached/
dw_fluffos_v2/lib/std/shadows/curses/
dw_fluffos_v2/lib/std/shadows/disease/
dw_fluffos_v2/lib/std/shadows/fighting/
dw_fluffos_v2/lib/std/shadows/room/
dw_fluffos_v2/lib/std/shops/controllers/
dw_fluffos_v2/lib/std/shops/objs/
dw_fluffos_v2/lib/std/shops/player_shop/
dw_fluffos_v2/lib/std/shops/player_shop/office_code/
dw_fluffos_v2/lib/std/socket/
dw_fluffos_v2/lib/www/
dw_fluffos_v2/lib/www/external/autodoc/
dw_fluffos_v2/lib/www/external/java/telnet/Documentation/
dw_fluffos_v2/lib/www/external/java/telnet/Documentation/images/
dw_fluffos_v2/lib/www/external/java/telnet/examples/
dw_fluffos_v2/lib/www/external/java/telnet/tools/
dw_fluffos_v2/lib/www/pics/
dw_fluffos_v2/lib/www/secure/creator/
dw_fluffos_v2/lib/www/secure/editors/
dw_fluffos_v2/lib/www/secure/survey_results/
dw_fluffos_v2/win32/
/*  -*- LPC -*-  */
/*
 * $Locker:  $
 * $Id: skills.c,v 1.24 2003/05/07 22:49:32 ceres Exp $
 * 
*/
/**
 * 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.
 * skill_commands()            - all the skill add_actioned commands.
 */
 
#include <skills.h>
#include <tasks.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;

mapping _last_info;

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

void create() {
  _bonus_cache = ([ ]);
  _teach_offer = ([ ]);
  _stat_cache = ([ ]);
  new_skills = ([ ]);
  if(!_last_info)
    _last_info = ([ "time" : time() ]);
} /* 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 member_array( skill, keys( new_skills ) ) == -1;
   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 word, *list, stat;

   stat = this_object()->stats_to_zap();
   if ( !stat ) {
      return;
   }
   if ( find_call_out( "reset_all2" ) == -1 ) {
      call_out( "reset_all2", 1 );
   }
   foreach( i in stat ) {
      list = _stat_cache[ i ];
      if ( !list )
         continue;
      foreach( word in list ) {
         map_delete( _stat_cache, word );
      }
   }
   word = (string)this_object()->query_race_ob();
   if ( word ) {
      word->set_unarmed_attacks( this_object() );
   }   
} /* zap_stat_cache() */

/**
 * This method zaps the bonus cache.
 */
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 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 ) {
#ifdef 0  
   int tmp, lvl;
   string *path;
   object guild, race;
#endif
   
  if (!stringp(skill) || !strlen(skill)) {
    return 0;
  }
  if (!new_skills) {
    new_skills = ([ ]);
  }
  if (skill[0] == '.') {
    skill = skill[1..];
  }
  TASKER->set_control( ({ this_object(), skill }) );
  if ( _bonus_cache[ skill ] )
     return stat_modify( _bonus_cache[ skill ], skill, use_base_stats ); 
  return calc_bonus( query_skill(skill), skill, use_base_stats );
#ifdef 0
  // The following just isn't used anymore so can be removed.
  lvl = query_skill(skill);
  guild = (object)this_object()->query_guild_ob();
  race = (object)this_object()->query_race_ob();
  if (race) {
    tmp = (int)race->query_skill_bonus(lvl, skill);
  }
  if (guild) {
    tmp += (int)guild->query_skill_bonus(lvl, skill);
  }
  return calc_bonus( lvl + tmp, skill, use_base_stats );
#endif  
} /* query_skill_bonus() */

/**
 * This returns jus 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;

   if (!new_skills) {
      new_skills = ([ ]);
   }
   if (!stringp(skill)) {
      return 0;
   }
   if (skill[0] == '.') {
      skill = skill[1..];
   }
   TASKER->set_control( ({ this_object(), skill }) );
   if ( not_there( skill ) ) {
      int i;

      path = (string *)SKILL_OB->query_skill_tree(skill);
      if (path) {
         for (i=0;i<sizeof(path);i++) {
            if ( !not_there( path[ i ] ) ) {
               return new_skills[path[i]];
            }
         }
      }
   } else {
      return new_skills[skill];
   }
   return 0;
} /* query_skill() */

/**
 * This method fills out a complete skill branch, complete with ALL child skills.
 * It saves using many call_others to check skills.
 * @arg string The branch you want to query [ie: faith, magic, etc.]
 */
mapping query_complete_skill_branch( string branch ) {
  string *skills = SKILL_OB->query_all_children( branch );

  if ( !arrayp( skills ) || !sizeof( skills ) ) 
    return ([ ]);
  
  return allocate_mapping( skills, (: query_skill( $1 ) :) );
}

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

   reset_eval_cost();
   value = new_skills[skill];
   same = (mixed *)SKILL_OB->query_immediate_children(skill);
   for (i=0;i<sizeof(same);i++) {
      if ( not_there( same[ i ] ) ) {
         new_skills[same[i]] = value;
      }
      flatten_it(same[i]);
   }
   if (sizeof(same)) {
      map_delete(new_skills, skill);
   }
} /* flatten_it() */

int tm_check_ok(string skill, object exp) {
  string *history, *bits, *abits;
  int i, j, last, delay;

  if ( !_last_info ) { 
    _last_info = ([ "time" : time() ]);
  }

#ifdef LOGGING
  if(base_name(previous_object()) != "/obj/handlers/taskmaster" &&
     base_name(previous_object()) != "/std/effects/fighting/combat" &&
     base_name(previous_object()) != "/std/shadows/misc/team" &&
     base_name(previous_object()) != "/std/shadows/other/group" &&
     base_name(previous_object()) != "/global/player" &&
     base_name(previous_object())[0..2] != "/w/") {
     log_file("ATTEMPT_TASK", "Object %s gave skill increase without using "
              "the taskmaster.\n", base_name(previous_object()));
   }
#endif

  history = this_object()->get_history();
  if(sizeof(history)) {
    // This code looks for people repeating commands over and over in an
    // attempt to gain TMs.
    for(i=0; i<sizeof(history) && history[i]; i++)
      ;
    
    last = i - 1;

    // deal with aliases and command queuing
    if(!this_object()->is_alias(history[last]))
      last -= this_object()->query_queued_commands();
  
    if(last > 0 && sizeof(history[last]) > 1 && skill[<7..] != ".points") {
      for(i=0; i<sizeof(history) && history[i]; i++) {
        if(history[last] == history[i]) {
          j++;
        }
      }
      
      if(j > 5 || j * 100 / i > 30) {
#ifdef BAD_TM
        log_file(BAD_TM, "%s %s in %s by %O too many attempts at %s [%s]\n",
                 ctime(time())[4..18], this_object()->query_name(), skill,
                 exp, history[last], history[i-1]);
#endif

        if(!_last_info["skill"] || _last_info["skill"][0] != skill)
          _last_info["skill"] = ({ skill, 2 });
        else
          _last_info["skill"][1] += 1;
        
        _last_info["time"] = time();
        
        if(!_last_info["object"] || _last_info["object"][0] != base_name(exp))
          _last_info["object"] = ({ base_name(exp), 2 });
        else
          _last_info["object"][1] += 1;

        if(!_last_info["env"] || _last_info["env"][0] != environment())
          _last_info["env"] = ({ environment(), 2 });
        else
          _last_info["env"][1] += 1;
        
        return 0;
      }
    }
  }
  
  // Checks for repeated TMs with the same or similar skill trees.

  // starting value (minimum delay) is proportional to their guild level
  // and the level of this skill.
  delay = 30 + random(this_object()->query_level()) +
    random(this_object()->query_skill(skill));
  
  // if this is the same object as last award then double the delay required
  if(_last_info["object"] && base_name(exp) == _last_info["object"][0])
    delay *= _last_info["object"][1];

  // if this skill is more than twice their guild level then increase the
  // delay.
  if(this_object()->query_level() * 2 < this_object()->query_skill(skill))
    delay *= 2;

  if(_last_info["env"] && environment(this_object()) == _last_info["env"][0]) {
    delay *= _last_info["env"][1];
  }
  
  // see how many parts of the tree matches. The closer the skill
  // matches the longer between repeats.
  // Note that if there is no match at all their delay will be 60 seconds.
  bits = explode(skill, ".");
  if(_last_info["skill"])
    abits = explode(_last_info["skill"][0], ".");
  else
    abits = ({ });
  for(i=0; i<sizeof(bits) && i<sizeof(abits) && bits[i] == abits[i]; i++)
    ;

  if(i && _last_info["skill"])
    delay *= (i * _last_info["skill"][1]);
  else
    delay = 60;
  
  if(_last_info["time"] > (time() - delay)) {
#ifdef BAD_TM
    log_file(BAD_TM, "%s %s in %s by %O last %d secs ago, delay %d.\n",
             ctime(time())[4..18], this_object()->query_name(), skill,
             exp, (time() - _last_info["time"]), delay);
#endif
    if(!_last_info["skill"] || _last_info["skill"][0] != skill)
      _last_info["skill"] = ({ skill, 2 });
    else
      _last_info["skill"][1] += 1;
    
    _last_info["time"] = time();
    
    if(!_last_info["object"] || _last_info["object"][0] != base_name(exp))
      _last_info["object"] = ({ base_name(exp), 2 });
    else
      _last_info["object"][1] += 1;
    
    if(!_last_info["env"] || _last_info["env"][0] != environment())
      _last_info["env"] = ({ environment(), 2 });
    else
      _last_info["env"][1] += 1;
    return 0;
  }

  if(!_last_info["skill"] || _last_info["skill"][0] != skill)
    _last_info["skill"] = ({ skill, 2 });
  else
    _last_info["skill"][1] += 1;
  
  _last_info["time"] = time();
  
  if(!_last_info["object"] || _last_info["object"][0] != base_name(exp))
    _last_info["object"] = ({ base_name(exp), 2 });
  else
    _last_info["object"][1] += 1;
  
  if(!_last_info["env"] || _last_info["env"][0] != environment())
    _last_info["env"] = ({ environment(), 2 });
  else
    _last_info["env"][1] += 1;
  return 1;
}

/**
 * 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 guild, *recursive_skills, *same_level, *bits, *tree;
   int i;

   reset_eval_cost();
   if (!stringp(skill) || !intp(lvl) || lvl > 1000) {
      return 0;
   }
   
   if (!new_skills || (!mapp(new_skills))) {
      new_skills = ([ ]);
   }
   if (skill[0] == '.') {
      skill = skill[1..];
   }

   recursive_skills = (string *)SKILL_OB->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_OB->query_only_leaf(skill)) {
      int tmp_lvl, j;

      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 = (string *)
                 SKILL_OB->query_immediate_children(implode(bits[0..i], "."));
               for ( j = 0; j < sizeof( same_level ); j++ ) {
                  new_skills[ same_level[ j ] ] = tmp_lvl;
                  map_delete( _bonus_cache, same_level[ j ] );
               }
            }
         } else {
            tmp_lvl = 0;
         }
      }
   }

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

   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 = (string *)SKILL_OB->query_skill_tree(skill);
   for (i=1;i<sizeof(tree);i++) {
      int total, j;

      same_level = (string *)SKILL_OB->query_immediate_children(tree[i]);
      if (sizeof(same_level)) {
         total = 0;
         for (j=0;j<sizeof(same_level);j++) {
            /* If it does not exist.  Set it from the top value down. */
            if ( not_there( same_level[ j ] ) ) {
               new_skills[ same_level[ j ] ] = new_skills[ tree[ i ] ];
               map_delete( _bonus_cache, same_level[ j ] );
            }
            total += new_skills[same_level[j]];
         }
         new_skills[tree[i]] = total/sizeof(same_level);
         map_delete( _bonus_cache, tree[ i ] );
      }
   }

   /* Update the high level players table */
   if ( interactive( this_object() ) &&
         ( guild = (string)this_object()->query_guild_ob() ) ) {
      if ( stringp( guild ) ) {
        //         TOP_TEN_HANDLER->player_skill_advance( explode( guild, "/" )[ 2 ],
        //this_object() );
         guild->skills_advanced( this_object(), skill, new_skills[ skill ] );
      }
  }

  if((lvl == 1) && userp(this_object()) && (!exp || objectp(exp))) {
    if(!exp)
      exp = previous_object();
    if(!tm_check_ok(skill, exp)) {
      new_skills[skill] -= 1; // take the award away again.
      return 0;
    } else {
      TASKER->award_made( (string)this_object()->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 );
  }

  if(interactive(this_object()) && !this_object()->query_auto_loading())
     this_object()->save();

  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;
   string stat_bonus;
   float bonus;

   bonus = 0.0;

   if ( !_stat_cache[ skill ] || use_base_stats ) {
      stat_bonus = (string)SKILL_OB->query_skill_stat(skill);
      foreach ( i in stat_bonus ) {
         switch( i ) {
            case 'C' :
               if ( use_base_stats )
                  stat = (int)this_object()->query_real_con();
               else
                  stat = (int)this_object()->query_con();
               break;
            case 'D' :
               if ( use_base_stats )
                  stat = (int)this_object()->query_real_dex();
               else
                  stat = (int)this_object()->query_dex();
               break;
            case 'I' :
               if ( use_base_stats )
                  stat = (int)this_object()->query_real_int();
               else
                  stat = (int)this_object()->query_int();
               break;
            case 'S' :
               if ( use_base_stats )
                  stat = (int)this_object()->query_real_str();
               else
                  stat = (int)this_object()->query_str();
               break;
            case 'W' :
            default :
               if ( use_base_stats )
                  stat = (int)this_object()->query_real_wis();
               else
                  stat = (int)this_object()->query_wis();
         }
         //bonus += ( stat - 13 ) * 3;
         if (stat > 0) {
            bonus += (log(stat) / 9.8) - 0.25;
         } else if (stat < 0) {
            bonus -= (log(-stat) / 9.8) + 0.25;
         } else {
            bonus -= 0.25;
         }
         if ( !_stat_cache[ i ] ) {
            _stat_cache[ i ] = ({ skill });
         } else {
            _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 ];
   }
   i = strlen( stat_bonus );
   if ( i ) {
      //return lvl + ( lvl * bonus ) / ( i * 60 );
      stat = to_int(lvl + ( lvl * bonus ));
      if (stat < 0) {
         return 0;
      }
      return stat;
   }
   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 ) {
//  int bonus, stat, i;
   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) {
  _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] == this_object()) {
         say(this_object()->short() + " stops teaching themselves some "
             "skills.\n");
      } else if (previous_object() == this_object()) {
         ob = bing[O_OTHER_PER];
         tell_object(ob, this_object()->short() + " interupts your "
                         "training.\n");
      } else {
         ob = this_object();
         tell_object(ob, bing[O_OTHER_PER]->short() + " interupts your "
                         "training.\n");
      }
      say(bing[O_OTHER_PER]->short() + " stops teaching some skills to " +
          this_object()->short() + ".\n",
          ({ this_object(), bing[O_OTHER_PER] }));

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

      return ;
   }
  
   if (previous_object() != this_object()) {
      /* 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(this_object()->query_xp() < bing[O_XP]) {
      write("Something has gone wrong. :(\n");
      return;
   }
   /* Ok...  We did it!  Finished! */
   if (this_object() != bing[O_OTHER_PER]) {
      bing[O_OTHER_PER]->adjust_xp(bing[O_XP]/10);
   }
   this_object()->adjust_xp(-bing[O_XP]);
   add_skill_level(bing[O_SKILL], bing[O_NUM], bing[O_XP]);
   if (this_object() != bing[O_OTHER_PER]) {
      tell_object(this_object(), "You finish learning " + bing[O_NUM] +
                  " levels of "
                  + bing[O_SKILL] + " from " + bing[O_OTHER_PER]->short() +
                  ".\n");
      tell_object(bing[O_OTHER_PER], this_object()->short() + " finishes " +
                  "learning " + bing[O_NUM] + " levels of "
                  +bing[O_SKILL] + " from you.\n");
      say(this_object()->short() + " finishes learning some skills "+
          "from "+bing[O_OTHER_PER]->short()+".\n",
          ({ this_object(), bing[O_OTHER_PER] }));
   } else {
      tell_object(this_object(), "You finish teaching yourself " + bing[O_NUM] +
                  " levels of " + bing[O_SKILL] + ".\n");
      say(this_object()->short() + " finishes learning some skills "
          "from " + this_object()->query_objective() + "self.\n",
          ({ this_object(), bing[O_OTHER_PER] }));
   }
} /* stop_teaching_skills() */