/* 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;
}