/
Crimson2/alias/
Crimson2/area.tmp/
Crimson2/area.tmp/AnomalySpaceDock/
Crimson2/area.tmp/AnomalyStation/
Crimson2/area.tmp/AntHill/
Crimson2/area.tmp/ArcticTerrarium/
Crimson2/area.tmp/BuilderCity/
Crimson2/area.tmp/Dungeon/
Crimson2/area.tmp/MiningDock/
Crimson2/area.tmp/PipeSystem/
Crimson2/area.tmp/RattArea/
Crimson2/area.tmp/RobotFactory/
Crimson2/area.tmp/SilverDale/
Crimson2/area.tmp/StarshipFearless/
Crimson2/area.tmp/StationConduits/
Crimson2/area.tmp/TerrariumAlpha/
Crimson2/area.tmp/TerrariumBeta/
Crimson2/area.tmp/TestArea/
Crimson2/area.tmp/Void/
Crimson2/area/
Crimson2/area/AnomalySpaceDock/
Crimson2/area/AnomalyStation/
Crimson2/area/MiningDock/
Crimson2/area/PipeSystem/
Crimson2/area/SilverDale/
Crimson2/area/StationConduits/
Crimson2/area/Void/
Crimson2/board/
Crimson2/clone/
Crimson2/lib/
Crimson2/mole/
Crimson2/mole/mole_src/HELP/
Crimson2/player/
Crimson2/util/
Crimson2/wldedit/
Crimson2/wldedit/res/
/* Crimson2 Mud Server
 * All source written/copyright Ryan Haksi 1995 *
 * This source code is proprietary. Use in whole or in part without
 * explicity permission by the author is strictly prohibited
 *
 * Current email address(es): cryogen@infoserve.net
 * Phone number: (604) 591-5295
 *
 * C4 Script Language written/copyright Cam Lesiuk 1995
 * Email: clesiuk@engr.uvic.ca
 */

#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#include "crimson2.h"
#include "macro.h"
#include "log.h"
#include "str.h"
#include "send.h"
#include "queue.h"
#include "thing.h"
#include "base.h"
#include "char.h"
#include "player.h"
#include "cmd_inv.h"
#include "skill.h"

/* types of skills */
BYTE *skillFlagList[] = {
  "*WEAPON*",
  "*MELEE*",
  "*LASER*",
  "*SLUG*",
  "*BLASTER*",
  "*ION*",
  "*SUPPORT*",
  "*PLASMA*",
  "*OFFENSE*",
  "*DEFENSE*",
  "*PSYCHIC*",
  "*BODY*",
  "*TELEKINETIC*",
  "*APPORTATION*",
  "*SPIRIT*",
  "*PYROKINETIC*",
  "*TELEPATHY*",
  "*GENERAL*",
  "*CLASS*",
  "*ALL*",
  "*GOOD*",
  "*EVIL*",
  ""
};

/* Understanding how skills work:
 * skill values range from 0-250
 * in general a skill value denotes:
 * percentage (0-250%) ie lockpicking, spells (% affects duration/damage/cost etc)
 * bonus ie 0.0-25.0 generally (weapon skills bonus to hit/damage)
 * the levels built into the skillist table are only the defaults, they can be overridden
 * to any specific values by the class/race skills (and they are deceptive because unless

 * skill at default level+100 (max player level is 200)
 * if the skill is flagged as bad, then its maximums etc are worse
 * if the skill is flagged as good, then its gained @ lower level etc 
*/

/* The following is all the skill constants, I got sick of updating 
 * a huge number of constants so instead I changed them to variables
 * and copy the offsets in to the variables so I dont have to worry 
 * about where they are in the table when I edit it
 */
WORD SKILL_MELEE_ACCURACY=0;
WORD SKILL_MELEE_DAMAGE=0;
WORD SKILL_MELEE_SPEED=0;

WORD SKILL_PISTOL_ACCURACY=0;
WORD SKILL_PISTOL_DAMAGE=0;
WORD SKILL_PISTOL_SPEED=0;

WORD SKILL_RIFLE_ACCURACY=0;
WORD SKILL_RIFLE_DAMAGE=0;
WORD SKILL_RIFLE_SPEED=0;

WORD SKILL_CANNON_ACCURACY=0;
WORD SKILL_CANNON_DAMAGE=0;
WORD SKILL_CANNON_SPEED=0;

WORD SKILL_MELEE_KNIFE=0;
WORD SKILL_MELEE_BLADE=0;
WORD SKILL_MELEE_BLUDGEON=0;
WORD SKILL_MELEE_AXE=0;

WORD SKILL_LASER_PISTOL=0;
WORD SKILL_SLUG_PISTOL=0;
WORD SKILL_SLUG_MACHINE=0;
WORD SKILL_BLASTER_PISTOL=0;

WORD SKILL_LASER_RIFLE=0;
WORD SKILL_SLUG_RIFLE=0;
WORD SKILL_BLASTER_RIFLE=0;
WORD SKILL_ION_RIFLE=0;
WORD SKILL_GRENADE_RIFLE=0;
WORD SKILL_PLASMA_RIFLE=0;

WORD SKILL_BLASTER_CANNON=0;
WORD SKILL_ION_CANNON=0;
WORD SKILL_MISSILE_CANNON=0;
WORD SKILL_PLASMA_CANNON=0;

/* metabolic skills */
WORD SKILL_CELL_REPAIR;
WORD SKILL_REFRESH=0;
WORD SKILL_ENDURANCE=0;
WORD SKILL_BREATHWATER=0;
WORD SKILL_STRENGTH=0;
WORD SKILL_DARKVISION=0;
WORD SKILL_SLOW_POISON=0;
WORD SKILL_CURE_POISON=0;
WORD SKILL_HEAL_MINOR=0;
WORD SKILL_REGENERATION=0;
WORD SKILL_HEAL_MAJOR=0;
WORD SKILL_DEXTERITY=0;
WORD SKILL_CONSTITUTION=0;
WORD SKILL_HASTE=0;
WORD SKILL_QUENCH=0;
WORD SKILL_SUSTENANCE=0;
WORD SKILL_ACIDTOUCH=0;
WORD SKILL_POISONTOUCH=0;

/* telekinetic skills */
WORD SKILL_CRUSH=0;
WORD SKILL_FORCESTORM=0;
WORD SKILL_GHOSTFIST=0;
WORD SKILL_KINETIC_SHIELD=0;
WORD SKILL_IMMOBILIZE=0;
WORD SKILL_HEARTSTOP=0;
WORD SKILL_ASPHYXIATE=0;
WORD SKILL_INVISIBILITY=0;
WORD SKILL_SLOW=0;        
WORD SKILL_IMPROVEDINVIS=0;
WORD SKILL_VACUUMWALK=0;
/* WORD SKILL_KINETICWEAPON - weapon does concussive damage too */

/* telepathic skills */
WORD SKILL_PHANTOMEAR=0;
WORD SKILL_PHANTOMEYE=0;
WORD SKILL_MINDLINK=0;
WORD SKILL_DOMINATION=0;
WORD SKILL_THOUGHTBLADE=0;
WORD SKILL_MINDCRUSH=0;
WORD SKILL_DEATHDREAM=0;
WORD SKILL_MINDSHIELD=0;
WORD SKILL_SLEEP=0;
WORD SKILL_BERSERK=0;
WORD SKILL_MINDCLEAR=0;
/* WORD SKILL_IRONWILL        SKILL_SLEEP+1  increase willpower for awhile */
/* WORD SKILL_FORCE           SKILL_IRONWILL+1 order command */
/* WORD SKILL_FEEBLEMIND      SKILL_FORCE+1  harder to work psi powers */
/* WORD SKILL_CONFUSE         SKILL_FEEBLEMIND+1  reduce perception/guard */
/* WORD SKILL_COUNTERSTRIKE   attacker feels our pain (mind damage) */
/* WORD SKILL_MESSAGE         talk to sleeping people even */
/* WORD SKILL_SLEEPWATCH      talk/watch while "sleeping" */

/* apportation skills */
WORD SKILL_TELEPORT=0;      /* go somewhere */
WORD SKILL_SUMMON=0;        /* bring someone here */
WORD SKILL_SUCCOR=0;        /* go to someone */
WORD SKILL_BANISH=0;        /* bounce someone a few rooms away */
WORD SKILL_MASSTELEPORT=0;  /* everyone with me to dest */
WORD SKILL_MASSSUCCOR=0;    /* everyone to leader */
WORD SKILL_MASSSUMMON=0;    /* everyone to me */
WORD SKILL_DISRUPTDOOR=0;   /* destroy a teleportal */
WORD SKILL_PHASEWALK=0;     /* walk out of phase, no terrain costs */
WORD SKILL_PHANTOMPOCKET=0; /* can carry stuff in this doesnt weigh anything */
WORD SKILL_WATERWALK=0;     /* dont need a boat on water */
WORD SKILL_TELETRACK=0;     /* go to someone nearby */
WORD SKILL_RECALL=0;        /* go to marked location */
WORD SKILL_MARK=0;          /* mark this location */
WORD SKILL_TRANSLOCATE=0;   /* Swap goto marked location but mark this one */
WORD SKILL_PHASEDOOR=0;      /* Walk through closed door */
/* WORD SKILL_PHANTOMDOOR=0;   -* create a portal you can enter */
/* WORD SKILL_BLIT  go a few rooms in a direction */
/* WORD SKILL_TELESHIELD more than nosummon prevent people from coming */
/* WORD SKILL_FLICKER flicker in and out reduce damage by 25% */

/* Spirit skills */
WORD SKILL_SPIRITWALK=0;    /* set spirit free */
WORD SKILL_SENSELIFE=0;     
WORD SKILL_SEEINVISIBLE=0;  
WORD SKILL_LUCKSHIELD=0;    
WORD SKILL_IDENTIFY=0;      /* identify an object */
WORD SKILL_STAT=0;          /* stat a creature resist, hitroll etc */
WORD SKILL_LUCKYHITS=0;     /* hit bonus */
WORD SKILL_LUCKYDAMAGE=0;   /* damage bonus */
/* WORD SKILL_SECURE - make things hard to find/picklock */
/* WORD SKILL_PSI_RESIST - disrupt psionics */
/* WORD SKILL_PSI_IMMUNE - disrupt psionics completely */
/* WORD SKILL_LUCKYDODGE - improve dodge chances */
/* WORD SKILL_ESP - identify what someone is concentrating on */
/* WORD SKILL_LOCATE_OBJECT - locate some objects */
/* WORD SKILL_POWERSTEAL - steal power points */
/* WORD SKILL_TIMESTOP - decrement tWait, POWERFULL!! */

/* Pyrokinesis */
WORD SKILL_BURNINGFIST=0;   
WORD SKILL_FLAMESTRIKE=0;   
WORD SKILL_INCINERATE=0;    
WORD SKILL_IGNITE=0;        /* set off grenades */
WORD SKILL_HEATSHIELD=0;    
WORD SKILL_FIRE_ACCURACY=0;
WORD SKILL_FIRE_DAMAGE=0;   
WORD SKILL_FIRE_SPEED=0;    
WORD SKILL_FIREBLADE=0;     /* make a burning sword we can wield */
WORD SKILL_FIRESHIELD=0;    /* make a burning shield we can wield */
WORD SKILL_FIREARMOR=0;     /* make burning armor we can wield */
/* WORD SKILL_FLAMEWEAPON=0    weapon is burning does heat damage too */
/* WORD SKILL_BURNINGAURA=0    every round attacker take heat damage */

WORD SKILL_FLEE=0;
WORD SKILL_PURSUE=0;
WORD SKILL_RESCUE=0;
WORD SKILL_PICKLOCK=0;
WORD SKILL_HACKING=0;
WORD SKILL_SEARCH=0;
WORD SKILL_CONCEAL=0;
WORD SKILL_HIDE=0;
WORD SKILL_DODGE=0;
WORD SKILL_WILLPOWER=0;
WORD SKILL_TRACK=0;
WORD SKILL_SNEAK=0;
WORD SKILL_DISARM=0;
WORD SKILL_AMBUSH=0;
WORD SKILL_SPEED=0;
WORD SKILL_PERCEPTION=0;
WORD SKILL_PICKPOCKET=0;
WORD SKILL_PEEK=0;
WORD SKILL_STEAL=0;
WORD SKILL_GUARD=0;

/* the master list of all skills in play */
  /* Name */               /* Skill flags */                            /* Level */    /* Max */          /* Gain Divisor*/
struct SkillListType skillList[] = {
  { "<Undefined>",        0,                                            {254,254,254,254}, {0,0,0,0},    {99,99,99,99}, NULL },

  { "Melee-Accuracy",     SF_WEAPON|SF_MELEE,                           {10,25,55,80}, {20,100,125,200}, {10,15,40,70}, &SKILL_MELEE_ACCURACY },
  { "Melee-Damage",       SF_WEAPON|SF_MELEE,                           {15,30,60,85}, {20, 90,125,200}, {15,25,55,75}, &SKILL_MELEE_DAMAGE },
  { "Melee-Speed",        SF_WEAPON|SF_MELEE,                           {20,35,65,99}, {20,100,150,200}, {20,35,60,99}, &SKILL_MELEE_SPEED },

  { "Pistol-Accuracy",    SF_WEAPON,                                    {10,25,55,75}, {20,100,150,200}, {10,15,40,70}, &SKILL_PISTOL_ACCURACY },
  { "Pistol-Damage",      SF_WEAPON,                                    {15,30,60,80}, {20, 90,125,150}, {15,25,55,75}, &SKILL_PISTOL_DAMAGE },
  { "Pistol-Speed",       SF_WEAPON,                                    {20,35,65,99}, {20,100,150,200}, {20,35,60,99}, &SKILL_PISTOL_SPEED },

  { "Rifle-Accuracy",     SF_WEAPON,                                    {20,35,60,85}, {20,100,150,200}, {10,15,40,70}, &SKILL_RIFLE_ACCURACY },
  { "Rifle-Damage",       SF_WEAPON,                                    {25,40,65,90}, {20, 90,125,150}, {15,25,55,75}, &SKILL_RIFLE_DAMAGE },
  { "Rifle-Speed",        SF_WEAPON,                                    {30,45,70,99}, {20,100,150,200}, {20,35,60,99}, &SKILL_RIFLE_SPEED },

  { "Cannon-Accuracy",    SF_WEAPON,                                    {50,60,75,99}, {20,100,150,200}, {10,20,40,70}, &SKILL_CANNON_ACCURACY },
  { "Cannon-Damage",      SF_WEAPON,                                    {55,65,80,99}, {20, 90,125,150}, {15,25,55,75}, &SKILL_CANNON_DAMAGE },
  { "Cannon-Speed",       SF_WEAPON,                                    {60,70,85,99}, {20,100,150,200}, {20,35,60,99}, &SKILL_CANNON_SPEED },

  { "Melee-Knife",        SF_WEAPON|SF_MELEE,                           { 1,15,40,70}, {20,100,150,200}, {10,15,30,60}, &SKILL_MELEE_KNIFE },
  { "Melee-Blade",        SF_WEAPON|SF_MELEE,                           {10,20,45,75}, {20,100,150,200}, {10,15,30,60}, &SKILL_MELEE_BLADE },
  { "Melee-Bludgeon",     SF_WEAPON|SF_MELEE,                           {15,25,50,80}, {20,100,150,200}, {10,15,30,60}, &SKILL_MELEE_BLUDGEON },
  { "Melee-Axe",          SF_WEAPON|SF_MELEE,                           {20,35,55,85}, {20,100,150,200}, {10,15,30,60}, &SKILL_MELEE_AXE },
                                                                                                                                       
  /* Name */              /* Skill flags */                             /* Level */    /* Max */          /* Gain Divisor*/
  { "Laser-Pistol",       SF_WEAPON|SF_LASER,                           { 1,15,35,65}, {20,100,150,200}, {10,15,30,60}, &SKILL_LASER_PISTOL },
  { "Slug-Pistol",        SF_WEAPON|SF_SLUG,                            {10,20,40,70}, {20,100,150,200}, {10,15,30,60}, &SKILL_SLUG_PISTOL },
  { "Slug-MachinePistol", SF_WEAPON|SF_SLUG,                            {15,25,45,75}, {20,100,150,200}, {10,15,30,60}, &SKILL_SLUG_MACHINE },
  { "Blaster-Pistol",     SF_WEAPON|SF_BLASTER,                         {25,30,50,80}, {20,100,150,200}, {10,15,30,60}, &SKILL_BLASTER_PISTOL },
                                                                                                                                       
  /* Name */              /* Skill flags */                             /* Level */    /* Max */          /* Gain Divisor*/
  { "Laser-Rifle",        SF_WEAPON|SF_LASER,                           {15,25,45,75}, {30,100,150,200}, {10,15,30,60}, &SKILL_LASER_RIFLE },
  { "Slug-Rifle",         SF_WEAPON|SF_SLUG,                            {20,30,50,75}, {30,100,150,200}, {10,15,30,60}, &SKILL_SLUG_RIFLE },
  { "Blaster-Rifle",      SF_WEAPON|SF_BLASTER,                         {25,30,50,80}, {30,100,150,200}, {10,15,30,60}, &SKILL_BLASTER_RIFLE },
  { "Ion-Rifle",          SF_WEAPON|SF_ION,                             {30,40,60,90}, {30,100,150,200}, {10,15,30,60}, &SKILL_ION_RIFLE },
  { "Grenade-Rifle",      SF_WEAPON|SF_SUPPORT,                         {40,50,70,95}, {30,100,150,200}, {10,15,30,60}, &SKILL_GRENADE_RIFLE },
  { "Plasma-Rifle",       SF_WEAPON|SF_PLASMA,                          {50,60,70,99}, {30,100,150,200}, {10,15,30,60}, &SKILL_PLASMA_RIFLE },
                                                                                                                                       
  /* Name */              /* Skill flags */                             /* Level */    /* Max */          /* Gain Divisor*/
  { "Blaster-Cannon",     SF_WEAPON|SF_BLASTER,                         {45,50,70,99}, {30,100,150,200}, {10,17,35,65}, &SKILL_BLASTER_CANNON },
  { "Ion-Cannon",         SF_WEAPON|SF_ION,                             {60,65,75,99}, {30,100,150,200}, {10,17,35,65}, &SKILL_ION_CANNON },
  { "Missile-Cannon",     SF_WEAPON|SF_SUPPORT,                         {70,75,85,99}, {30,100,150,200}, {10,17,35,65}, &SKILL_MISSILE_CANNON },
  { "Plasma-Cannon",      SF_WEAPON|SF_PLASMA,                          {80,85,90,99}, {30,100,150,200}, {10,17,35,65}, &SKILL_PLASMA_CANNON },
                                                                                                                                       
  /* Name */              /* Skill flags */                             /* Level */    /* Max */          /* Gain Divisor*/
  { "Cell-Repair",        SF_PSYCHIC|SF_BODY|SF_GOOD,                   { 8,18,33,53}, {30,150,200,250}, {10,17,35,65}, &SKILL_CELL_REPAIR },
  { "Refresh",            SF_PSYCHIC|SF_BODY|SF_GOOD,                   { 5,15,30,60}, {30,150,200,250}, {10,17,35,65}, &SKILL_REFRESH },
  { "Endurance",          SF_PSYCHIC|SF_BODY,                           { 7,17,27,57}, {30,150,200,250}, {10,17,45,75}, &SKILL_ENDURANCE },
  { "Quench",             SF_PSYCHIC|SF_BODY,                           { 9,19,29,59}, {30,150,200,250}, {10,17,45,75}, &SKILL_QUENCH },
  { "Sustenance",         SF_PSYCHIC|SF_BODY,                           { 9,19,29,59}, {30,150,200,250}, {10,17,45,75}, &SKILL_SUSTENANCE },
  { "Breathwater",        SF_PSYCHIC|SF_BODY,                           {10,20,30,60}, {30,150,200,250}, {10,17,45,75}, &SKILL_BREATHWATER },
  { "Acidtouch",          SF_PSYCHIC|SF_BODY|SF_OFFENSE|SF_EVIL,        {12,22,42,62}, {20,125,150,175}, {10,17,45,75}, &SKILL_ACIDTOUCH },
  { "Strength",           SF_PSYCHIC|SF_BODY,                           {15,25,35,65}, {30,150,200,250}, {10,17,45,75}, &SKILL_STRENGTH },
  { "Constitution",       SF_PSYCHIC|SF_BODY,                           {17,27,37,67}, {30,150,200,250}, {10,17,45,75}, &SKILL_CONSTITUTION },
  { "Darkvision",         SF_PSYCHIC|SF_BODY,                           {20,30,50,70}, {30,150,200,250}, {10,17,45,75}, &SKILL_DARKVISION },
  { "Slow-Poison",        SF_PSYCHIC|SF_BODY,                           {22,24,27,33}, {30,150,200,250}, {10,17,45,75}, &SKILL_SLOW_POISON },
  { "Poisontouch",        SF_PSYCHIC|SF_BODY|SF_OFFENSE|SF_EVIL,        {22,32,52,72}, {20,125,150,175}, {10,17,45,75}, &SKILL_POISONTOUCH },
  { "Haste",              SF_PSYCHIC|SF_BODY,                           {25,35,45,75}, {30,150,200,250}, {15,40,75,99}, &SKILL_HASTE },
  { "Dexterity",          SF_PSYCHIC|SF_BODY,                           {27,37,47,87}, {30,150,200,250}, {10,17,45,75}, &SKILL_DEXTERITY },
  { "Cure-Poison",        SF_PSYCHIC|SF_BODY|SF_GOOD,                   {35,45,55,85}, {30,150,200,250}, {10,17,45,75}, &SKILL_CURE_POISON },
  { "Heal-Minor",         SF_PSYCHIC|SF_BODY|SF_GOOD,                   {40,50,60,90}, {30,150,200,250}, {10,17,45,75}, &SKILL_HEAL_MINOR },
  { "Regeneration",       SF_PSYCHIC|SF_BODY,                           {45,55,75,95}, {30,150,200,250}, {10,17,45,75}, &SKILL_REGENERATION },
  { "Heal-Major",         SF_PSYCHIC|SF_BODY|SF_GOOD,                   {75,80,90,99}, {30,150,200,250}, {10,17,45,75}, &SKILL_HEAL_MAJOR },

  /* Name */              /* Skill flags */                             /* Level */    /* Max */          /* Gain Divisor*/
  { "Ghostfist",          SF_PSYCHIC|SF_TELEKINETIC|SF_OFFENSE|SF_EVIL, { 5,20,40,60}, {30,150,200,250}, {10,17,45,75}, &SKILL_GHOSTFIST },
  { "Invisibility",       SF_PSYCHIC|SF_TELEKINETIC,                    {10,20,30,60}, {30,150,200,250}, {10,17,45,75}, &SKILL_INVISIBILITY },
  { "Slow",               SF_PSYCHIC|SF_TELEKINETIC|SF_OFFENSE,         {12,22,32,62}, {30,150,200,250}, {10,17,45,75}, &SKILL_SLOW },
  { "Vacuumwalk",         SF_PSYCHIC|SF_TELEKINETIC,                    {15,25,35,65}, {30,150,200,250}, {10,17,45,75}, &SKILL_VACUUMWALK },
  { "Kinetic-Shield",     SF_PSYCHIC|SF_TELEKINETIC|SF_DEFENSE,         {17,25,35,65}, {30,150,200,250}, {10,17,45,75}, &SKILL_KINETIC_SHIELD },
  { "Forcestorm",         SF_PSYCHIC|SF_TELEKINETIC|SF_OFFENSE|SF_EVIL, {20,30,55,75}, {30,150,200,250}, {10,17,45,75}, &SKILL_FORCESTORM },
  { "Immobilize",         SF_PSYCHIC|SF_TELEKINETIC|SF_OFFENSE,         {22,32,52,72}, {30,150,200,250}, {10,17,45,75}, &SKILL_IMMOBILIZE },
  { "ImprovedInvis",      SF_PSYCHIC|SF_TELEKINETIC,                    {25,35,45,75}, {30,150,200,250}, {10,17,45,75}, &SKILL_IMPROVEDINVIS },
  { "Crush",              SF_PSYCHIC|SF_TELEKINETIC|SF_OFFENSE|SF_EVIL, {40,50,70,99}, {30,150,200,250}, {10,17,45,75}, &SKILL_CRUSH },
  { "Asphyxiate",         SF_PSYCHIC|SF_TELEKINETIC|SF_EVIL,            {45,55,75,95}, {30,150,200,250}, {10,17,45,75}, &SKILL_ASPHYXIATE },
  { "Heartstop",          SF_PSYCHIC|SF_TELEKINETIC|SF_OFFENSE|SF_EVIL, {75,80,90,99}, {30,150,200,250}, {10,17,45,75}, &SKILL_HEARTSTOP },
                                                                                                                                       
  /* Name */              /* Skill flags */                             /* Level */    /* Max */          /* Gain Divisor*/
  { "PhantomEar",         SF_PSYCHIC|SF_TELEPATHY,                      { 7,17,27,57}, {30,150,200,250}, {10,17,45,75}, &SKILL_PHANTOMEAR },
  { "Thoughtblade",       SF_PSYCHIC|SF_TELEPATHY|SF_OFFENSE|SF_EVIL,   { 5,20,40,60}, {30,150,200,250}, {10,17,45,75}, &SKILL_THOUGHTBLADE },
  { "PhantomEye",         SF_PSYCHIC|SF_TELEPATHY,                      {12,22,42,72}, {30,150,200,250}, {10,17,45,75}, &SKILL_PHANTOMEYE },
  { "Mindshield",         SF_PSYCHIC|SF_TELEPATHY|SF_DEFENSE,           {15,25,45,75}, {30,150,200,250}, {10,17,45,75}, &SKILL_MINDSHIELD },
  { "Sleep",              SF_PSYCHIC|SF_TELEPATHY,                      {17,27,37,67}, {30,150,200,250}, {10,17,45,75}, &SKILL_SLEEP },
  { "Mindclear",          SF_PSYCHIC|SF_TELEPATHY,                      {19,29,39,69}, {30,150,200,250}, {10,17,45,75}, &SKILL_MINDCLEAR },
  { "Mindcrush",          SF_PSYCHIC|SF_TELEPATHY|SF_OFFENSE|SF_EVIL,   {21,30,45,75}, {30,150,200,250}, {10,17,45,75}, &SKILL_MINDCRUSH },
  { "Domination",         SF_PSYCHIC|SF_TELEPATHY|SF_OFFENSE|SF_EVIL,   {23,32,52,82}, {30,150,200,250}, {10,17,45,75}, &SKILL_DOMINATION },
  { "Mindlink",           SF_PSYCHIC|SF_TELEPATHY,                      {25,35,55,85}, {30,150,200,250}, {10,17,45,75}, &SKILL_MINDLINK },
  { "Berserk",            SF_PSYCHIC|SF_TELEPATHY,                      {30,40,50,70}, {30,150,200,250}, {10,17,45,75}, &SKILL_BERSERK },
  { "Deathdream",         SF_PSYCHIC|SF_TELEPATHY|SF_OFFENSE|SF_EVIL,   {40,50,70,99}, {30,150,200,250}, {10,17,45,75}, &SKILL_DEATHDREAM },

  /* Name */              /* Skill flags */                             /* Level */    /* Max */          /* Gain Divisor*/
  { "Phantompocket",      SF_PSYCHIC|SF_APPORTATION,                    {10,20,40,70}, {30,150,200,250}, {10,17,45,75}, &SKILL_PHANTOMPOCKET },
  { "Recall",             SF_PSYCHIC|SF_APPORTATION,                    {13,23,43,73}, {30,150,200,250}, {10,17,45,75}, &SKILL_RECALL },
  { "Mark",               SF_PSYCHIC|SF_APPORTATION,                    {13,23,43,73}, {30,150,200,250}, {10,17,45,75}, &SKILL_MARK },
  { "Waterwalk",          SF_PSYCHIC|SF_APPORTATION,                    {14,24,44,74}, {30,150,200,250}, {10,17,45,75}, &SKILL_WATERWALK },
  { "Phasewalk",          SF_PSYCHIC|SF_APPORTATION,                    {10,17,45,75}, {30,150,200,250}, {10,17,45,75}, &SKILL_PHASEWALK },
  { "Teletrack",          SF_PSYCHIC|SF_APPORTATION,                    {18,27,47,77}, {30,150,200,250}, {10,17,45,75}, &SKILL_TELETRACK },
  { "Translocate",        SF_PSYCHIC|SF_APPORTATION,                    {20,29,49,79}, {30,150,200,250}, {10,17,45,75}, &SKILL_TRANSLOCATE },
  { "Succor",             SF_PSYCHIC|SF_APPORTATION,                    {22,30,50,80}, {30,150,200,250}, {10,17,45,75}, &SKILL_SUCCOR },
  { "DisruptDoor",        SF_PSYCHIC|SF_APPORTATION,                    {24,32,42,82}, {30,150,200,250}, {10,17,45,75}, &SKILL_DISRUPTDOOR },
  { "Summon",             SF_PSYCHIC|SF_APPORTATION,                    {25,35,55,85}, {30,150,200,250}, {10,17,45,75}, &SKILL_SUMMON },
  { "Banish",             SF_PSYCHIC|SF_APPORTATION,                    {30,40,60,90}, {30,150,200,250}, {10,17,45,75}, &SKILL_BANISH },
  { "Teleport",           SF_PSYCHIC|SF_APPORTATION,                    {35,45,65,95}, {30,150,200,250}, {10,17,45,75}, &SKILL_TELEPORT },
  { "MassSuccor",         SF_PSYCHIC|SF_APPORTATION,                    {35,45,65,95}, {30,150,200,250}, {10,17,45,75}, &SKILL_MASSSUCCOR },
  { "MassSummon",         SF_PSYCHIC|SF_APPORTATION,                    {40,50,70,99}, {30,150,200,250}, {10,17,45,75}, &SKILL_MASSSUMMON },
  { "MassTeleport",       SF_PSYCHIC|SF_APPORTATION,                    {40,50,70,99}, {30,150,200,250}, {10,17,45,75}, &SKILL_MASSTELEPORT },
  { "PhaseDoor",          SF_PSYCHIC|SF_APPORTATION,                    {27,37,47,87}, {30,150,200,250}, {10,17,45,75}, &SKILL_PHASEDOOR },

  /* Name */              /* Skill flags */                             /* Level */    /* Max */          /* Gain Divisor*/
  { "SeeInvisible",       SF_PSYCHIC|SF_SPIRIT,                         {10,20,40,70}, {30,150,200,250}, {10,17,45,75}, &SKILL_SEEINVISIBLE },
  { "Luckyhits",          SF_PSYCHIC|SF_SPIRIT,                         {12,32,62,92}, {30,150,200,250}, {10,17,45,75}, &SKILL_LUCKYHITS },
  { "SenseLife",          SF_PSYCHIC|SF_SPIRIT,                         {15,25,45,75}, {30,150,200,250}, {10,17,45,75}, &SKILL_SENSELIFE },
  { "Luckydamage",        SF_PSYCHIC|SF_SPIRIT,                         {17,36,66,96}, {30,150,200,250}, {10,17,45,75}, &SKILL_LUCKYDAMAGE },
  { "SpiritWalk",         SF_PSYCHIC|SF_SPIRIT,                         {20,30,50,80}, {30,150,200,250}, {10,17,45,75}, &SKILL_SPIRITWALK },
  { "Luckshield",         SF_PSYCHIC|SF_SPIRIT,                         {22,32,42,82}, {30,150,200,250}, {10,17,45,75}, &SKILL_LUCKSHIELD },
  { "Identify",           SF_PSYCHIC|SF_SPIRIT,                         {25,35,45,85}, {30,75, 100,125}, {10,17,45,75}, &SKILL_IDENTIFY },
  { "Stat",               SF_PSYCHIC|SF_SPIRIT,                         {27,37,67,97}, {30,150,200,250}, {10,17,45,75}, &SKILL_STAT },

  /* Name */              /* Skill flags */                             /* Level */    /* Max */          /* Gain Divisor*/
  { "Burningfist",        SF_PSYCHIC|SF_PYROKINETIC|SF_OFFENSE|SF_EVIL, { 8,16,32,64}, {30,150,200,250}, {10,17,45,75}, &SKILL_BURNINGFIST },
  { "Fireshield",         SF_PSYCHIC|SF_PYROKINETIC|SF_DEFENSE,         {13,22,42,72}, {30,150,200,250}, {10,17,45,75}, &SKILL_FIRESHIELD },
  { "Fireblade",          SF_PSYCHIC|SF_PYROKINETIC,                    {15,24,44,74}, {30,150,200,250}, {10,17,45,75}, &SKILL_FIREBLADE },
  { "Heatshield",         SF_PSYCHIC|SF_PYROKINETIC|SF_DEFENSE,         {17,25,45,75}, {30,150,200,250}, {10,17,45,75}, &SKILL_HEATSHIELD },
  { "Firearmor",          SF_PSYCHIC|SF_PYROKINETIC|SF_DEFENSE,         {19,27,47,77}, {30,150,200,250}, {10,17,45,75}, &SKILL_FIREARMOR },
  { "Flamestrike",        SF_PSYCHIC|SF_PYROKINETIC|SF_OFFENSE|SF_EVIL, {21,30,45,75}, {30,150,200,250}, {10,17,45,75}, &SKILL_FLAMESTRIKE },
/*
  { "Ignite",             SF_PSYCHIC|SF_PYROKINETIC|SF_OFFENSE|SF_EVIL, {35,45,65,95}, {30,150,200,250}, {10,17,45,75}, &SKILL_IGNITE },
*/
  { "Incinerate",         SF_PSYCHIC|SF_PYROKINETIC|SF_OFFENSE|SF_EVIL, {40,50,70,99}, {30,150,200,250}, {10,17,45,75}, &SKILL_INCINERATE },
  /* Fire Weapon group - keep Together! */
  { "Fire-Accuracy",      SF_PSYCHIC|SF_PYROKINETIC|SF_WEAPON,          {14,24,44,74}, {30,150,200,250}, {10,15,40,70}, &SKILL_FIRE_ACCURACY },
  { "Fire-Damage",        SF_PSYCHIC|SF_PYROKINETIC|SF_WEAPON,          {19,34,54,84}, {30,100,150,200}, {15,25,55,75}, &SKILL_FIRE_DAMAGE },
  { "Fire-Speed",         SF_PSYCHIC|SF_PYROKINETIC|SF_WEAPON,          {30,40,60,90}, {30,150,200,250}, {20,35,60,99}, &SKILL_FIRE_SPEED },

  /* Misc. skills */
  { "Flee",               SF_GENERAL,                                   { 1,15,35,65}, {30,150,200,250}, {10,20,40,75}, &SKILL_FLEE },
/*
  { "Search",             SF_GENERAL,                                   { 1,20,40,70}, {20, 75,100,125}, {10,17,45,75}, &SKILL_SEARCH },
*/
  { "Hide",               SF_CLASS,                                     { 4,24,44,74}, {20, 75,100,150}, {10,17,45,75}, &SKILL_HIDE },
  { "Track",              SF_CLASS,                                     { 4,24,44,74}, {30,150,200,250}, {10,17,45,75}, &SKILL_TRACK },
  { "Ambush",             SF_CLASS,                                     { 4,24,44,74}, {40,200,300,400}, {10,17,45,75}, &SKILL_AMBUSH },
  { "Dodge",              SF_GENERAL,                                   { 5,25,45,75}, {30,150,200,250}, {10,17,45,75}, &SKILL_DODGE },
/*
  { "Conceal",            SF_GENERAL,                                   { 8,18,48,78}, {30,150,200,250}, {10,17,45,75}, &SKILL_CONCEAL },
*/
  { "Pursue",             SF_GENERAL,                                   {10,20,40,70}, {30,150,200,250}, {10,17,45,75}, &SKILL_PURSUE },
  { "Rescue",             SF_CLASS,                                     {10,20,40,70}, {10, 50, 65, 75}, {10,17,45,75}, &SKILL_RESCUE },
  { "Picklock",           SF_CLASS,                                     {10,20,40,70}, {20, 75,100,125}, {10,17,45,75}, &SKILL_PICKLOCK },
  { "Hacking",            SF_CLASS,                                     {10,20,40,70}, {20, 75,100,125}, {10,17,45,75}, &SKILL_HACKING },
  { "WillPower",          SF_GENERAL,                                   {10,20,40,70}, {30,150,200,250}, {10,17,45,75}, &SKILL_WILLPOWER },
  { "Sneak",              SF_CLASS,                                     {12,22,42,72}, {30,150,200,250}, {10,17,45,75}, &SKILL_SNEAK },
/*
  { "Disarm",             SF_CLASS,                                     {15,25,45,75}, {30,150,200,250}, {20,30,50,75}, &SKILL_DISARM },
*/
  { "Perception",         SF_GENERAL,                                   {15,25,45,75}, {20,100,150,175}, {10,17,45,75}, &SKILL_PERCEPTION },
  { "Pickpocket",         SF_CLASS,                                     {15,25,45,75}, {20,100,150,175}, {10,17,45,75}, &SKILL_PICKPOCKET },
  { "Peek",               SF_CLASS,                                     {20,30,50,80}, {20, 75,100,125}, {10,17,45,75}, &SKILL_PEEK },
  { "Steal",              SF_CLASS,                                     {20,30,50,80}, {20, 75,100,125}, {10,17,45,75}, &SKILL_STEAL },
  { "Guard",              SF_GENERAL,                                   {25,35,65,95}, {20, 75,100,125}, {10,17,45,75}, &SKILL_GUARD },
  { "Speed",              SF_GENERAL|SF_WEAPON,                         {25,35,65,95}, {20,100,150,175}, {35,45,70,99}, &SKILL_SPEED },

  /* Terminating entry */
  { "",0, {0,0,0,0},{0,0,0,0},{0,0,0,0},0 }
};

WORD skillNum;

SKILLDETAIL humanDetail[] = {
  { NULL,              0,   0              }
};

SKILLDETAIL artificerDetail[] = {
  { &SKILL_PERCEPTION +1,  SD_MODIFYSKILL },
  { &SKILL_WILLPOWER, +1,  SD_MODIFYSKILL },
  { NULL,              0,   0              }
};

SKILLDETAIL siliconoidDetail[] = {
  { &SKILL_PERCEPTION,-1,  SD_MODIFYSKILL },
  { &SKILL_DODGE,     -1,  SD_MODIFYSKILL },
  { &SKILL_SPEED,     -1,  SD_MODIFYSKILL },
  { &SKILL_WILLPOWER, +2,  SD_MODIFYSKILL },
  { &SKILL_AMBUSH,    +1,  SD_MODIFYSKILL },
  { &SKILL_GUARD,     +1,  SD_MODIFYSKILL },
  { NULL,             0,   0              }
};

SKILLDETAIL salamanderDetail[] = {
  { &SKILL_DODGE,     +1,  SD_MODIFYSKILL },
  { &SKILL_SPEED,     +1,  SD_MODIFYSKILL },
  { &SKILL_AMBUSH,    +1,  SD_MODIFYSKILL },
  { &SKILL_FLEE,      -2,  SD_MODIFYSKILL },
  { &SKILL_PURSUE,    +1,  SD_MODIFYSKILL },
  { &SKILL_TRACK,     +1,  SD_MODIFYSKILL },
  { &SKILL_HIDE,      -2,  SD_MODIFYSKILL },
  { NULL,             0,   0              }
};

struct RaceListType raceList[] = {
  {
    "Human",
    {0,  0,  0,  0,  0,  0,  0,  0,  0}, /* damage resist */
    0, /* cant use skills */
    SF_SLUG,  /* good at skills */
    SF_LASER, /* bad at skills */
    100,100,100, /* max Hunger, Thirst, Intox */
    4,  4,  4,  /* gain Hunger, Thirst, Intox (per tick) */
    100,100,100, /* max Hit, Move, Power points - % class max */
    100,100,100, /* gain Hit, Move, Power points - % class amount */
    {40, 40, 40, 40, 40 },  /* Min Str/Dex/Con/Wis/Int */
    {100,100,100,100,100 }, /* Max */
    {0,  0,  0,  0,  0 },   /* Bonus */
    -1, /* normalize abilities to this */
    100, /* percentage of normal xp needed */
    humanDetail,
    EQU_HEAD|EQU_NECK_1|EQU_NECK_2|EQU_TORSO|EQU_ARM_R|EQU_ARM_L|EQU_WRIST_R|EQU_WRIST_L|EQU_HAND_R
      |EQU_HAND_L|EQU_HELD_R|EQU_HELD_L|EQU_FINGER_R|EQU_FINGER_L|EQU_WAIST|EQU_LEGS|EQU_FEET|EQU_BODY

  },{
    "Artificer",
    {-10, -10, -10,  -5,  +5,  0,  30,  0,  +5}, /* damage resist */
    SF_PLASMA|SF_SUPPORT|SF_SLUG, /* cant use skills */
    SF_TELEPATHY|SF_TELEKINETIC, /* good at skills */
    SF_WEAPON|SF_MELEE, /* bad at skills */
    60, 50, 50, /* max Hunger, Thirst, Intox */
    2,  2,  1,  /* gain Hunger, Thirst, Intox (per tick) */
    90,100,120, /* max Hit, Move, Power points - % class max */
    90,100,120, /* gain Hit, Move, Power points - % class amount */
    {25, 40, 25, 50, 75 },  /* Min Str/Dex/Con/Wis/Int */
    {75, 100,75, 120,150 }, /* Max */
    {0,  0,  0,  5,  10 },  /* Bonus */
    -1, /* normalize abilities to this */
    105, /* percentage of normal xp needed */
    artificerDetail,
    EQU_HEAD|EQU_NECK_1|EQU_NECK_2|EQU_TORSO|EQU_ARM_R|EQU_ARM_L|EQU_WRIST_R|EQU_WRIST_L|EQU_HAND_R
      |EQU_HAND_L|EQU_HELD_R|EQU_HELD_L|EQU_FINGER_R|EQU_FINGER_L|EQU_WAIST|EQU_LEGS|EQU_FEET|EQU_BODY

  },{
    "Siliconoid",
    {20, 20,  0,  30,  0,  -30,  0, -20,  0}, /* damage resist */
    SF_TELEPATHY, /* cant use skills */
    SF_SUPPORT|SF_PLASMA|SF_BODY, /* good at skills */
    SF_SPIRIT|SF_PSYCHIC, /* bad at skills */
    300, 75, 200, /* max Hunger, Thirst, Intox */
    7,  3,  10,  /* gain Hunger, Thirst, Intox (per tick) */
    120,100, 80, /* max Hit, Move, Power points - % class max */
    100,100, 75, /* gain Hit, Move, Power points - % class amount */
    {50, 25, 75, 40, 25 },  /* Min Str/Dex/Con/Wis/Int */
    {150,75,150,100, 90 }, /* Max */
    {10,  0, 10,  0,  0 },  /* Bonus */
    -1, /* normalize abilities to this */
    100, /* percentage of normal xp needed */
    /* Skill Details */
    siliconoidDetail,
    EQU_NECK_1|EQU_TORSO|EQU_ARM_R|EQU_ARM_L|EQU_WRIST_R|EQU_WRIST_L|EQU_HAND_R
      |EQU_HAND_L|EQU_HELD_R|EQU_HELD_L|EQU_FINGER_R|EQU_FINGER_L|EQU_WAIST|EQU_LEGS|EQU_FEET|EQU_BODY

  },{
    "Salamander",
    {10, -30, -10, 30,  -10,  0,  0, 10, 10}, /* damage resist */
    SF_ION, /* cant use skills */
    SF_PYROKINETIC, /* good at skills */
    SF_SPIRIT, /* bad at skills */
    150, 50, 200, /* max Hunger, Thirst, Intox */
    6,   1,  1,  /* gain Hunger, Thirst, Intox (per tick) */
    100, 80,100, /* max Hit, Move, Power points - % class max */
    100,150,100, /* gain Hit, Move, Power points - % class amount */
    {35, 75, 30, 25,  30 },  /* Min Str/Dex/Con/Wis/Int */
    {90,150,100, 75, 120 }, /* Max */
    {0,  10,  5,  0,   0 },  /* Bonus */
    -1, /* normalize abilities to this */
    105, /* percentage of normal xp needed */
    /* Skill Details */
    salamanderDetail,
    EQU_HEAD|EQU_NECK_1|EQU_NECK_2|EQU_TORSO|EQU_ARM_R|EQU_ARM_L|EQU_WRIST_R|EQU_WRIST_L|EQU_HAND_R
      |EQU_HAND_L|EQU_HELD_R|EQU_HELD_L|EQU_FINGER_R|EQU_FINGER_L|EQU_WAIST|EQU_TAIL|EQU_TAILGRIP|EQU_BODY

  },{
    ""
 }
};

SKILLDETAIL soldierDetail[] = {
  { &SKILL_RESCUE,    0,   SD_GETSKILL },
  { &SKILL_TRACK,     0,   SD_GETSKILL },
/*
  { &SKILL_DISARM,    0,   SD_GETSKILL },
*/
  { &SKILL_WILLPOWER, +1,  SD_MODIFYSKILL },
  { NULL,             0,   0           }
};                           

SKILLDETAIL medicDetail[] = {
  { &SKILL_RESCUE,    -2,  SD_GETSKILL },
  { &SKILL_WILLPOWER, +1,  SD_MODIFYSKILL },
  { NULL,             0,   0           }
};

SKILLDETAIL techDetail[] = {
  { &SKILL_TRACK,     -1,  SD_GETSKILL },
  { &SKILL_HACKING,   -1,  SD_GETSKILL },
  { NULL,             0,   0           }
};

SKILLDETAIL researcherDetail[] = {
  { &SKILL_DODGE,     -1,  SD_MODIFYSKILL },
  { &SKILL_WILLPOWER, +2,  SD_MODIFYSKILL },
  { NULL,             0,   0           }
};

SKILLDETAIL mysticDetail[] = {
  { &SKILL_WILLPOWER, +1,  SD_MODIFYSKILL },
  { NULL,             0,   0           }
};

SKILLDETAIL labtechDetail[] = {
  { &SKILL_DODGE,     -1,  SD_MODIFYSKILL },
  { NULL,             0,   0           }
};

SKILLDETAIL rogueDetail[] = {
  { &SKILL_SNEAK,     0,   SD_GETSKILL },
  { &SKILL_HIDE,      0,   SD_GETSKILL },
  { &SKILL_PICKLOCK,  0,   SD_GETSKILL },
  { &SKILL_HACKING,   0,   SD_GETSKILL },
  { &SKILL_AMBUSH,    0,   SD_GETSKILL },
  { &SKILL_PICKPOCKET,0,   SD_GETSKILL },
  { &SKILL_PEEK,      0,   SD_GETSKILL },
  { &SKILL_STEAL,     0,   SD_GETSKILL },
  { &SKILL_PERCEPTION,+1,  SD_MODIFYSKILL },
  { &SKILL_GUARD,     +1,  SD_MODIFYSKILL },
  { &SKILL_DODGE,     +1,  SD_MODIFYSKILL },
  { &SKILL_FLEE,      +1,  SD_MODIFYSKILL },
  { NULL,             0,   0           }
};

struct ClassListType classList[] = {
  {
    "Soldier",
    SF_PYROKINETIC, /* Skill Cant */
    SF_WEAPON|SF_LASER, /* Skill Good */
    SF_PSYCHIC, /* Skill Bad */
    SF_WEAPON|SF_BODY|SF_GENERAL, /* default level skills */
    15,12,5, /* max Hit, Move, Power points - per level */
    10,10,5, /* gain Hit, Move, Power points - per tick */
    15,100,0, /* starting Hit, Move, Power points */
    100,
    soldierDetail,
    {15, 5, 15, -15, -20 }  /* Bonus Str/Dex/Con/Wis/Int */

  }, {
    "Medic",
    SF_PLASMA, /* Skill Cant */
    SF_BODY|SF_DEFENSE, /* Skill Good */
    SF_PYROKINETIC|SF_APPORTATION|SF_OFFENSE, /* Skill Bad */
    SF_WEAPON|SF_BODY|SF_TELEKINETIC|SF_GENERAL, /* default level skills */
    11,11,15, /* max Hit, Move, Power points - per level */
    7,10,15, /* gain Hit, Move, Power points - per tick */
    20,100,100, /* starting Hit, Move, Power points */
    80,
    medicDetail,
    {-15, -5, -10, 20, 10 }  /* Bonus Str/Dex/Con/Wis/Int */

  }, {
    "Tech",
    0, /* Skill Cant */
    SF_TELEKINETIC, /* Skill Good */
    SF_TELEPATHY|SF_PLASMA|SF_SUPPORT|SF_ION, /* Skill Bad */
    SF_WEAPON|SF_TELEKINETIC|SF_APPORTATION|SF_GENERAL, /* default level skills */
    10,11,10, /* max Hit, Move, Power points - per level */
    10,10,10, /* gain Hit, Move, Power points - per tick */
    15,100,50, /* starting Hit, Move, Power points */
    100,
    techDetail,
    {-5, 5, -5, 10, -5 }  /* Bonus Str/Dex/Con/Wis/Int */

  }, {
    "Researcher",
    SF_PLASMA|SF_SUPPORT|SF_ION, /* Skill Cant */
    SF_PYROKINETIC|SF_OFFENSE, /* Skill Good */
    SF_WEAPON|SF_BODY|SF_MELEE|SF_SPIRIT|SF_DEFENSE, /* Skill Bad */
    SF_WEAPON|SF_TELEPATHY|SF_PYROKINETIC|SF_SPIRIT|SF_GENERAL, /* default level skills */
    7,10,15, /* max Hit, Move, Power points - per level */
    5, 5,12, /* gain Hit, Move, Power points - per tick */
    15,80,100, /* starting Hit, Move, Power points */
    110,
    researcherDetail,
    {-20, -5, -10, 10, 25 }  /* Bonus Str/Dex/Con/Wis/Int */

  }, {
    "Rogue",
    0, /* Skill Cant */
    SF_MELEE, /* Skill Good */
    SF_PYROKINETIC|SF_SPIRIT, /* Skill Bad */
    SF_WEAPON|SF_APPORTATION|SF_SPIRIT|SF_GENERAL, /* default level skills */
    12,15,7, /* max Hit, Move, Power points - per level */
    10,10,5, /* gain Hit, Move, Power points - per tick */
    15,120,25, /* starting Hit, Move, Power points */
    85,
    rogueDetail,
    {-5, 20, -10, 5, -10 }  /* Bonus Str/Dex/Con/Wis/Int */

  }, {
    "Mystic",
    SF_PLASMA|SF_SUPPORT|SF_ION, /* Skill Cant */
    SF_TELEPATHY, /* Skill Good */
    SF_MELEE|SF_PYROKINETIC|SF_TELEKINETIC, /* Skill Bad */
    SF_WEAPON|SF_TELEPATHY|SF_SPIRIT|SF_TELEKINETIC|SF_GENERAL, /* default level skills */
    9,10,16, /* max Hit, Move, Power points - per level */
    6, 6,13, /* gain Hit, Move, Power points - per tick */
    18,90,100, /* starting Hit, Move, Power points */
    90,
    mysticDetail,
    {-10, -5, -10, 15, 10 }  /* Bonus Str/Dex/Con/Wis/Int */

  }, {
    "Labtech",
    SF_PLASMA|SF_SUPPORT|SF_ION, /* Skill Cant */
    SF_TELEKINETIC|SF_OFFENSE, /* Skill Good */
    SF_WEAPON|SF_BODY|SF_MELEE|SF_SPIRIT|SF_PYROKINETIC|SF_DEFENSE, /* Skill Bad */
    SF_WEAPON|SF_TELEPATHY|SF_PYROKINETIC|SF_APPORTATION|SF_GENERAL, /* default level skills */
    7,10,15, /* max Hit, Move, Power points - per level */
    5, 5,12, /* gain Hit, Move, Power points - per tick */
    15,80,100, /* starting Hit, Move, Power points */
    110,
    labtechDetail,
    {-20, -5, -10, 10, 25 }  /* Bonus Str/Dex/Con/Wis/Int */

  }, {
    "" /*terminating entry*/
  }
};

void SkillInit(void) {
  LWORD i;
  BYTE  buf[256];
  LWORD error = FALSE;

  /* make sure constant and string match */
  for (i=0; *skillList[i].sName; i++) {
    BITSET(skillList[i].sFlag, SF_ALL);
    if (skillList[i].sCheck)
      *(skillList[i].sCheck)=i;
  }

  sprintf(buf, "Total Skills in play: %ld\n", i);
  Log(LOG_BOOT, buf);
  skillNum = i;
  if (error) exit(1);
}

SKILLDETAIL *SkillDetailFind(SKILLDETAIL *sDetail, LWORD skill) {
  LWORD i;

  i=0;
  if (sDetail[i].sNum == NULL) return NULL;
  while (*(sDetail[i].sNum) != skill) {
    i++;
    if (sDetail[i].sNum == NULL) return NULL;
  }
  return sDetail + i;
}

/* will use maxLevel or Character level, whichever is lower */
LWORD SkillMaximum(THING *thing, LWORD skill, LWORD maxLevel, LWORD *gain) {
#define MAX_DIVISOR   5   /* 20% increase/descrease each time */
#define LEVEL_DIVISOR 7   /* ~15% increase/decrease each time */
#define GAIN_DIVISOR  7   /* ~15% increase/decrease each time */

  LWORD        i;
  LWORD        numSet;
  LWORD        level;
  LWORD        maxFactor; /* a factor of 100% would give normal skill advancement more is better */
  LWORD        levelFactor;
  LWORD        gainFactor;
  SKILLDETAIL *raceDetail;
  SKILLDETAIL *classDetail;
  LWORD        max;

  /* Check the obvious reasons why we cant do this at all */
  if(gain) *gain=0;
  if (thing->tType != TTYPE_PLR)
    return 0;
  if BITANY(skillList[skill].sFlag, raceList[Plr(thing)->pRace].rSkillCant) return 0;
  if BITANY(skillList[skill].sFlag, classList[Plr(thing)->pClass].cSkillCant) return 0;

  numSet = 0; /* Modifier */
  maxFactor = 100;
  levelFactor = 100;
  gainFactor = 100;
  classDetail = SkillDetailFind(raceList[Plr(thing)->pRace].rSkill,   skill);
  raceDetail  = SkillDetailFind(classList[Plr(thing)->pClass].cSkill, skill);

  /* see what levels we are restricted to */
  level = Character(thing)->cLevel;
  if (maxLevel>0) MAXSET(level, maxLevel);
  /* if we dont get this skill by default then we must wait until we are over 100th level */
  if ( !BITANY(skillList[skill].sFlag, classList[Plr(thing)->pClass].cSkillDefault) 
    && !(classDetail && classDetail->sGetSkill)
    && !(raceDetail && raceDetail->sGetSkill)
      ) {
    level -= 100; 
    if (level<0) return 0;
  }
  
  /* check to see if they good or bad at this sort of thing */
  numSet += FlagSetNum(BITANY(skillList[skill].sFlag, raceList[Plr(thing)->pRace].rSkillGood));
  numSet -= FlagSetNum(BITANY(skillList[skill].sFlag, raceList[Plr(thing)->pRace].rSkillBad));
  numSet += FlagSetNum(BITANY(skillList[skill].sFlag, classList[Plr(thing)->pClass].cSkillGood));
  numSet -= FlagSetNum(BITANY(skillList[skill].sFlag, classList[Plr(thing)->pClass].cSkillBad));
  if (raceDetail) {
    numSet += raceDetail->sModifier;
  }
  if (classDetail) {
    numSet += classDetail->sModifier;
  }

  /* Increment how good they are at skill accordingly */
  for(i=0; i<numSet; i++){
    maxFactor   += maxFactor/(MAX_DIVISOR+i*3);
    levelFactor -= levelFactor/(LEVEL_DIVISOR+i*3);
    gainFactor  -= gainFactor/(GAIN_DIVISOR+i*3);
  }
  /* Decrement how good they are at skill accordingly */
  for(i=0; i>numSet; i--){
    maxFactor   -= maxFactor/(MAX_DIVISOR+i*2);
    levelFactor += levelFactor/(LEVEL_DIVISOR+i*2);
    gainFactor  += gainFactor/(GAIN_DIVISOR+i*2);
  }
  
  /* find which of the 4 max's, gains etc to use */  
  for (i=0; i<4 && level>=(skillList[skill].sLevel[i]*levelFactor/100); i++);
  i--;
  if (i==-1) return 0; /* cant do this yet */

  /* calc base max */
  max = skillList[skill].sMax[i];
  /* interpolate between 2 table entries */
  if (i<3) {
    LWORD diffMax;
    LWORD diffLevel;
    LWORD prevLevel;
    LWORD nextLevel;
    LWORD percentLevel;

    prevLevel=( skillList[skill].sLevel[i]*levelFactor/100 );
    nextLevel=( skillList[skill].sLevel[i+1]*levelFactor/100 );
    diffLevel = nextLevel - prevLevel;
    percentLevel = (level - prevLevel)*100 / diffLevel;

    /* interpolate up max */
    diffMax = ( skillList[skill].sMax[i+1] - skillList[skill].sMax[i] );
    max += diffMax * percentLevel / 100;
  }

  if(gain) {
    LWORD        theirSkill;
    LWORD        j;

    theirSkill = Plr(thing)->pSkill[skill];

    /* Base gain */
    for (j=0; j<3; j++) {
      *gain = skillList[skill].sGain[j];
      if (theirSkill < (skillList[skill].sMax[j]*maxFactor/100) ) break;
    }
    /* interpolate gain */
    if (j<3) {
      LWORD diffGain;
      LWORD prevGain;
      LWORD nextGain;

      LWORD prevMax;
      LWORD nextMax;
      LWORD diffMax;
      LWORD percentMax;

      prevGain=( skillList[skill].sGain[j] );
      nextGain=( skillList[skill].sGain[j+1] );
      diffGain = nextGain - prevGain;
      
      if (j>0)
        prevMax = skillList[skill].sMax[j-1] * maxFactor / 100;
      else
        prevMax = 0;
      nextMax = skillList[skill].sMax[j] * maxFactor / 100;
      diffMax = nextMax - prevMax;
      percentMax = (theirSkill - prevMax) * 100 / diffMax;

      *gain += diffGain * percentMax / 100;
    }

    *gain = (*gain) * gainFactor/100;
    *gain = ( Plr(thing)->pInt*10/17 + 50 ) / *gain + 1;
  }

  max = max*maxFactor/100;
  return max;
}

/* 
 * Call from within script to allow mob/obj whatever to train Characters 
 * (the script must intercept Practice commands and replace with a call to this routine)
 * The canPractice flag must be satisfied partially, that is to say a canPractice of
 * SF_OFFENCE|SF_TELEKINETIC will only let you practice skills that have either flags set
 * maxLevel is the maximum level that this can train you to! ie if the levels for a skill
 * are 10/20/30/40 and maxLevel is 35, then you will only be able to practice the skill
 * to the max set for the char as they were at 35th level.
 */
void SkillPractice(THING *thing, BYTE *cmd, FLAG canPractice, FLAG cantPractice, LWORD maxLevel) {
  LWORD i;
  LWORD max;
  LWORD maxHere;
  LWORD gain;
  WORD  oldValue;
  BYTE  buf[256];
  BYTE *originalCmd;
  FLAG  categoryBit = 0;

  originalCmd = cmd;

  /* throwaway practice */
  cmd = StrOneWord(cmd, NULL);

  /* get the next word - ie category to show */
  cmd = StrOneWord(cmd, buf);

  if (*buf) {
    /* Okay have a category?, skill?, validate it */
    i = TYPEFIND(buf, skillFlagList);
    if (i<0) {
      i = TYPEFIND(buf, skillList);
      if (i<0) {
        SendThing("^wOh, good one... that isnt a skill and it isnt a category either\n", thing);
        return;
      }
    } else 
      categoryBit = 1<<i;
  }

  /* just show them a category */
  if (!*buf || categoryBit) {
    SkillShow(thing, originalCmd, canPractice, cantPractice, maxLevel);
    SendHint("^;HINT: Try ^<PRACTICE <CATEGORY>^; or ^<PRACTICE <SKILL>^; (see ^<help practice^;)\n", thing);
    return;
  }

  if (Plr(thing)->pPractice <= 0) {
    SendThing("^wI'm afraid you are out of practices\n", thing);
    return;
  }

  maxHere = SkillMaximum(thing, i, maxLevel, &gain);
  max     = SkillMaximum(thing, i, 0, NULL);
  if (max <= 0) {
    SendThing("^wI'm afraid you cannot learn ^rthat ^wskill\n", thing);
    return;
  }
  if ((maxHere <= 0)
      ||!BITANY(canPractice, skillList[i].sFlag)
      || BITANY(skillList[i].sFlag, cantPractice)){
    SendThing("^wI'm afraid you cannot learn that skill ^gHERE!\n", thing);
    return;
  }

  oldValue = Plr(thing)->pSkill[i];
  if (oldValue >= max) {
    sprintf(buf, "^wYou are as good at ^r%s ^was you can be\n", skillList[i].sName);
    SendThing(buf, thing);
    return;
  } else if (oldValue >= maxHere) {
    sprintf(buf, "^yYou are as good at ^r%s ^was you can be, at least for here and now\n", skillList[i].sName);
    SendThing(buf, thing);
    return;
  } 

  Plr(thing)->pSkill[i] += gain;
  Plr(thing)->pPractice -= 1;
  MAXSET(Plr(thing)->pSkill[i], maxHere);
  sprintf(buf, "^cYou improve %s from %hd to %hd\n", skillList[i].sName, oldValue, Plr(thing)->pSkill[i]);
  SendThing(buf, thing);
}

/* 
 * Show the player what they can practice.
 * Separate skills up into categories so it isnt so overwhelming
 */
void SkillShow(THING *thing, BYTE *cmd, FLAG canPractice, FLAG cantPractice, LWORD maxLevel) {
  LWORD i;
  LWORD j;
  LWORD numShown;
  LWORD max;
  LWORD maxHere;
  LWORD gain;
  BYTE  line[256];
  BYTE  buf[256];
  FLAG  categoryBit;

  /* throwaway practice */
  cmd = StrOneWord(cmd, NULL);
  /* get the next word - ie category to show */
  cmd = StrOneWord(cmd, buf);

  /* If nothing is specified show the categories */
  if (!*buf && Character(thing)->cLevel<10)
    strcpy(buf, "*all");
  if (!*buf) {
    if (maxLevel)
      SendThing("^yYou can practice skills in the following categories:\n^g", thing);
    else
      SendThing("^yYou posess skills in the following categories:\n^g", thing);
    numShown=0;
    for (i=0; *skillFlagList[i]; i++) {
      for(j=0; *skillList[j].sName; j++) {
        if (BITANY(skillList[j].sFlag, 1<<i)) {
          max = SkillMaximum(thing, j, 0, NULL);
          /* Limit the display to what we have unless we can actually pr here */
          if (max > 0 && (maxLevel || Plr(thing)->pSkill[j]>0)) { /* if we can practice this at all */
            sprintf(buf, "  %-30s", skillFlagList[i]);
            SendThing(buf, thing);
            numShown++;
            if (numShown%2 == 0) 
              SendThing("\n", thing);
            break;
          }
        }
      }
    }
    if (numShown%2) 
      SendThing("\n", thing);
    SendHint("^;HINT: To display skills in a category type ^<practice *category^;\n", thing);
    if (maxLevel) {
      SendHint("^;HINT: e.g. ^<PRACTICE *ALL^; shows every skill you can practice\n", thing);
    } else {
      SendHint("^;HINT: e.g. ^<PRACTICE *ALL^; shows every skill you posess\n", thing);
      SendHint("^;HINT: Find a trainer and see what skills you can ^<practice\n", thing);
    }
    return;
  }

  i = TYPEFIND(buf, skillFlagList);
  if (i<0) {
    SendThing("^wOh, good one... I've never even heard of that category\n", thing);
    return;
  }
  categoryBit = 1<<i;
                     
  if (maxLevel) {
    if (!strcmp(skillFlagList[i], "*ALL*")) {
      SendThing("^wYou can practice the following skills\n", thing);
    } else {
      SendThing("^wYou can practice the following ", thing);
      SendThing(skillFlagList[i], thing);
      SendThing(" skills:\n", thing);
    }
  } else {
    if (!strcmp(skillFlagList[i], "*ALL*")) {
      SendThing("^wYou currently posess the following skills\n", thing);
    } else {
      SendThing("^wYou currently posess the following ", thing);
      SendThing(skillFlagList[i], thing);
      SendThing(" skills:\n", thing);
    }
  }

  numShown = 0;
  for(i=0; *skillList[i].sName; i++) {
    if (BITANY(skillList[i].sFlag, categoryBit)) {
      max = SkillMaximum(thing, i, 0, NULL);

      /* Limit the display to what we have unless we can actually pr here */
      if (max > 0 && (maxLevel || Plr(thing)->pSkill[i]>0)) { /* if we can practice this at all */
        maxHere = SkillMaximum(thing, i, maxLevel, &gain);

        /* are at max allready */
        if (max<=Plr(thing)->pSkill[i]) { 
          sprintf(line,"*%s(^Y%hd^y)*", skillList[i].sName, Plr(thing)->pSkill[i]);
          sprintf(buf,"^y%-30s", line);
  
        /* can practice it here to full potential */
        } else if ( (maxHere>=max) 
          && BITANY(canPractice, skillList[i].sFlag)
          &&!BITANY(cantPractice, skillList[i].sFlag)){
          sprintf(line,"%s(^w%hd/%ld^g)[%ld]", skillList[i].sName, Plr(thing)->pSkill[i], maxHere, gain);
          sprintf(buf,"^g%-30s", line);
  
        /* cant practice to full potential */
        } else if (BITANY(canPractice, skillList[i].sFlag) 
          &&!BITANY(cantPractice, skillList[i].sFlag)){
          sprintf(line,"%s(^p%hd/*%ld^C)[%ld]", skillList[i].sName, Plr(thing)->pSkill[i], maxHere, gain);
          sprintf(buf,"^C%-30s", line);
  
        /* can practice but not here */
        } else { 
          sprintf(line,"*%s(%hd/%ld)*", skillList[i].sName, Plr(thing)->pSkill[i], max);
          sprintf(buf,"^r%-26s", line);
        }
        SendThing(buf, thing);
        numShown++;
        if (numShown%3 == 0) 
          SendThing("\n", thing);
      }
    }
  }
  if (numShown%3 != 0)
    SendThing("\n", thing);
  sprintf(buf, "\n^w%ld ^cSkills shown. ^g(And you have %ld Practices left)\n", numShown, Plr(thing)->pPractice);
  SendThing(buf, thing);
  if (!maxLevel) {
    SendHint("^;HINT: You can only practice your skills in the presence of a trainer!\n", thing);
    SendHint("^;HINT: The first trainer in the game is ^<Duah^;. Find him!\n", thing);
  }
  return;
}