/
roa/
roa/lib/boards/
roa/lib/config/
roa/lib/edits/
roa/lib/help/
roa/lib/misc/
roa/lib/plrobjs/
roa/lib/quests/
roa/lib/socials/
roa/lib/www/
roa/lib/www/LEDSign/
roa/lib/www/LEDSign/fonts/
roa/lib/www/LEDSign/scripts/
roa/src/s_inc/
roa/src/sclient/
roa/src/sclient/binary/
roa/src/sclient/text/
roa/src/util/
/************************************************************************
	Realms of Aurealis 		James Rhone aka Vall of RoA

skills.c				Some additional skills for various
 					classes/races.


		******** Heavily modified and expanded ********
		*** BE AWARE OF ALL RIGHTS AND RESERVATIONS ***
		******** Heavily modified and expanded ********
		        All rights reserved henceforth. 

    Please note that no guarantees are associated with any code from
Realms of Aurealis.  All code which has been released to the general
public has been done so with an 'as is' pretense.  RoA is based on both
Diku and CircleMUD and ALL licenses from both *MUST* be adhered to as well
as the RoA license.   *** Read, Learn, Understand, Improve ***
*************************************************************************/
#include "conf.h"
#include "sysdep.h"

#include "structures.h"
#include "utils.h"
#include "db.h"
#include "comm.h"
#include "handler.h"
#include "mudlimits.h"
#include "magic.h"
#include "mail.h"
#include "interpreter.h"
#include "acmd.h"
#include "nature.h"
#include "affect.h"
#include "fight.h"
#include "lists.h"
#include "global.h"

/* some protos */
int find_exit(chdata *ch);

/* external vars */
extern char *comm_dirs[];
extern char *dirs[];

// for sorting skill lists
typedef struct skill_data {
  int skill;
  char name[64];
  int level;
  int mana;
} skdata;

// this will be the sorting structure...
skdata skill_list[MAX_SKILLS];

// write known skills to disk  6/20/98 -jtrhone
int save_skills(chdata *ch)
{
  char fname[MAX_INPUT_LENGTH];
  FILE *fp;
  int i;
  BOOL found = FALSE;

  if (IS_NPC(ch) || !ch->skills)
    return FALSE;

  if (!get_pdata_filename(GET_NAME(ch), "skl", fname))
    return FALSE;

  if (!(fp = fopen(fname, "wt")))
    return FALSE;
  
  for (i = 0; i < MAX_SKILLS; i++)
    if (ch->skills[i].perc || ch->skills[i].learned)
    {
      fprintf(fp, "%d %d %d\n", i, ch->skills[i].learned, ch->skills[i].perc);
      found = TRUE;
    }

  // if we didnt write anything, just put something in there...
  if (!found)
    fprintf(fp, "1 0 0\n");
  fclose(fp);

  return TRUE;
}

// load known skills from disk  6/20/98 -jtrhone
int load_skills(chdata *ch)
{
  char fname[MAX_INPUT_LENGTH];
  FILE *fp;
  int i, l, p;

  if (IS_NPC(ch) || !ch->skills)
    return FALSE;

  if (!get_pdata_filename(GET_NAME(ch), "skl", fname))
    return FALSE;

  if (!(fp = fopen(fname, "rt")))
    return FALSE;

  while (!feof(fp))
  {
    fscanf(fp, "%d %d %d\n", &i, &l, &p);
    if (i < 0 || i >= MAX_SKILLS)
    {
      sprintf(buf, "SYSERR: Skill out of range, load_skills (%s - %d).", GET_NAME(ch), i);
      mudlog(buf, BRF, LEV_IMM, TRUE);
      continue;
    }

    ch->skills[i].learned = l;
    ch->skills[i].perc = p;
  }

  fclose(fp);
  return TRUE;
}

// on char load, create skill array, fill em from disk  6/20/98 -jtrhone
int alloc_skills(chdata *ch)
{
  if (IS_NPC(ch))
    return FALSE;

  FREENULL(ch->skills);
  CREATE(ch->skills, skl_info, MAX_SKILLS);

  return load_skills(ch);
}

// write known gskills to disk  6/20/98 -jtrhone
int save_gskills(chdata *ch)
{
  char fname[MAX_INPUT_LENGTH];
  FILE *fp;
  int i;

  if (IS_NPC(ch) || !ch->gskills)
    return FALSE;

  if (!get_pdata_filename(GET_NAME(ch), "lang", fname))
    return FALSE;

  if (!(fp = fopen(fname, "wt")))
    return FALSE;

  for (i = 0; i < MAX_GSKILLS; i++)
    if (ch->gskills[i].perc || ch->gskills[i].learned)
      fprintf(fp, "%d %d %d\n", i, ch->gskills[i].learned, ch->gskills[i].perc);
  fclose(fp);
  return TRUE;
}

// load known gskills from disk  6/20/98 -jtrhone
int load_gskills(chdata *ch)
{
  char fname[MAX_INPUT_LENGTH];
  FILE *fp;
  int i, l, p;

  if (IS_NPC(ch) || !ch->gskills)
    return FALSE;

  if (!get_pdata_filename(GET_NAME(ch), "lang", fname))
    return FALSE;

  if (!(fp = fopen(fname, "rt")))
    return FALSE;

  while (!feof(fp))
  {
    fscanf(fp, "%d %d %d\n", &i, &l, &p);
    if (i < 0 || i >= MAX_GSKILLS)
    {
      sprintf(buf, "SYSERR: Dialect out of range, load_gskills (%s - %d).", GET_NAME(ch), i);
      mudlog(buf, BRF, LEV_IMM, TRUE);
      continue;
    }

    ch->gskills[i].learned = l;
    ch->gskills[i].perc = p;
  }

  fclose(fp);
  return TRUE;
}

// on char load, create dialect array, fill em from disk  6/20/98 -jtrhone
int alloc_gskills(chdata *ch)
{
  if (IS_NPC(ch))
    return FALSE;

  FREENULL(ch->gskills);
  CREATE(ch->gskills, skl_info, MAX_GSKILLS);

  return load_gskills(ch);
}

// GET and SET_SKILLS are no longer macros... 12/6/97 -jtrhone
// easier to work with new formats this way...
int get_skill(chdata *ch, int skl)
{
  if (!ch->skills)
    if (IS_PC(ch))
    {
      sprintf(buf, "SYSERR: %s has no skill array allocated.", GET_NAME(ch)); 
      mudlog(buf, BRF, LEV_IMM, TRUE);
      return 0;
    }
    else
      return 80;	// 80 default level for mobs?? (stock)

  if (skl < SPELL_ARMOR || skl >= MAX_SKILLS)
  {
    sprintf(buf, "SYSERR: %s requesting out of range skill (%d).", GET_NAME(ch), skl); 
    mudlog(buf, BRF, LEV_IMM, TRUE);
    return 0;
  }

  // only know skills they have in class list or learned...
  if (ch->skills[skl].learned)
    return ch->skills[skl].perc;

  return 0;
}

int set_skill(chdata *ch, int skl, int perc)
{
  if (!ch->skills)
    if (IS_PC(ch))
    {
      sprintf(buf, "SYSERR: %s has no skill array allocated.", GET_NAME(ch)); 
      mudlog(buf, BRF, LEV_IMM, TRUE);
      return 0;
    }
    else
      return 0;	// 80 default level for mobs?? (stock)

  if (skl < SPELL_ARMOR || skl >= MAX_SKILLS)
  {
    sprintf(buf, "SYSERR: %s requesting set of out of range skill (%d).", 
            GET_NAME(ch), skl); 
    mudlog(buf, BRF, LEV_IMM, TRUE);
    return 0;
  }

  // if it's getting set, must be learned
  ch->skills[skl].learned = 1;
  ch->skills[skl].perc = perc;

  return 1;
}

// for general skill array (gskills, etc.) 7/1/98 -jtrhone
int get_gskill(chdata *ch, int skl)
{
  if (!ch->gskills)
    if (IS_PC(ch))
    {
      sprintf(buf, "SYSERR: %s has no gskills array allocated.", GET_NAME(ch)); 
      mudlog(buf, BRF, LEV_IMM, TRUE);
      return 0;
    }
    else
      return 80;

  if (skl < 0 || skl >= MAX_GSKILLS)
  {
    sprintf(buf, "SYSERR: %s requesting out of range dialect/gskill (%d).", GET_NAME(ch), skl); 
    mudlog(buf, BRF, LEV_IMM, TRUE);
    return 0;
  }

  // only know skills they have in class list or learned...
  if (ch->gskills[skl].learned)
    return ch->gskills[skl].perc;

  return 0;
}

int set_gskill(chdata *ch, int skl, int perc)
{
  if (!ch->gskills)
    if (IS_PC(ch))
    {
      sprintf(buf, "SYSERR: %s has no gskills array allocated.", GET_NAME(ch)); 
      mudlog(buf, BRF, LEV_IMM, TRUE);
      return 0;
    }
    else
      return 0;	

  if (skl < 0 || skl >= MAX_GSKILLS)
  {
    sprintf(buf, "SYSERR: %s requesting set of out of range dialect/gskill (%d).", GET_NAME(ch), skl); 
    mudlog(buf, BRF, LEV_IMM, TRUE);
    return 0;
  }

  // if it's getting set, must be learned
  ch->gskills[skl].learned = 1;
  ch->gskills[skl].perc = perc;

  return 1;
}

// used by qsort, sorts in ASCENDING order... 7/9/98 -jtrhone
int sort_skills(const void *a, const void *b)
{
  skdata *one, *two;
  one = (skdata *) a;
  two = (skdata *) b;

  if (one->level > two->level)
    return 1;
  if (one->level < two->level)
    return -1;
  return 0;
}

void clear_skill_list(void)
{
  memset(skill_list, 0, sizeof(skdata)*MAX_SKILLS);
}

// 2/11/98 -jtrhone   update this to show how much mana spells will use NOW using the 
//                    manause() rather than the hard numbers...
ACMD(do_show_skills)
{
  int cindex, cls, i;
  static char buf[20000];

  if (IS_NPC(ch))
    return;

  if (!IS_IMMORTAL(ch) || !*argument)
    cls = GET_CLASS(ch);
  else
    cls = atoi(argument);

  cls = MAX(1, MIN(cls, NUM_CLASSES));

  clear_skill_list();

  // first , fill in the skill_list array
  for (cindex = 0, i = 1; i < MAX_SKILLS; i++)
    if (clarray[cls].skills[i].innate && (!IS_NAT_MADEPT(ch) || ch->skills[i].learned) && *skill_names[i])
    {
      skill_list[cindex].skill = i;
      strcpy(skill_list[cindex].name, skill_names[i]);
      skill_list[cindex].level = clarray[cls].skills[i].minlevel;
      skill_list[cindex].mana = spell_info[i].min_usesmana;
      cindex++;
    }

  // now qsort the array...
  qsort((void *) skill_list, MAX_SKILLS, sizeof(skdata), sort_skills);

//  strcpy(buf,"%6-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-%0 "
 //            "%BInnate%0"
  //           "%6 -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-%0\n\r");

  strcpy(buf,"%B%6    Spl/Skl Name    \t\tLevel%0\n\r"
             "%B%6--------------------\t\t-----%0\n\r");

  // now, add in the sorted skill_list
  for (i = 0; i < MAX_SKILLS; i++)
    if (*skill_list[i].name)
    {
      sprintf(buf+strlen(buf), "%%6%-20.20s%%0\t\t%d", skill_list[i].name, skill_list[i].level);

      if (spell_info[skill_list[i].skill].min_usesmana > 0)
	sprintf(buf+strlen(buf), " (%%6%d%%0 mana)", manause(ch, skill_list[i].skill));
      strcat(buf, "\n\r");
    }

  if (IS_WARLOCK(ch))
    strcat(buf, "Use the command %B%5wcast%0 for your known warlock spells.\n\r");
    
  page_string(ch->desc, buf, 1);
}


// Racial Skills - should be in racial skill array someday...
/* MUNCH!! :) Lets ogres n orcs change a corpse into a piece of meat*/
/* James Rhone  4/95 */
// cleanup 7/8/98 -jtrhone
ACMD(do_munch)
{
  obdata *o, *temp_obj;
  obdata *jj, *next_thing2;
  int meat = 3015;  /* vnum of chunk of meat */
  int r_num;

  if (IS_NPC(ch)) 
  {
    if (!MOB_CLASS_FLAGGED(ch, MOB_ORC | MOB_OGRE | MOB_DRAKYN | MOB_GIANT) && GET_SIZE(ch) > SIZE_LARGE)
    {
      send_to_char("You cannot munch!\n\r",ch);
      return;
    } 
  }
  else
  if (GET_RACE(ch) != RACE_OGRE && GET_RACE(ch) != RACE_ORC)
  {
    send_to_char("Ewwe...You wonder how Ogres and Orcs can do such a thing.\n\r",ch);
    send_to_char("Too bad you're not either race ....\n\r",ch);
    return;
  }

  one_argument(argument, arg);

  if (!*arg) 
  {
    send_to_char("Munch what??\n\r",ch);
    return;
  }

  /*check to see if obj is in room*/
  if (!(o = get_obj_in_list_vis(ch, arg, world[ch->in_room].contents))) 
  {
    send_to_char("You don't see that here...\n\r",ch);
    return;
  }

  // is it a corpse?
  if (!IS_CORPSE(o))
  {
    send_to_char("That is not a corpse!!\n\r",ch);
    return;
  }

  if ((r_num = real_object(meat)) < 0) 
  {
    send_to_char("Sorry, couldn't get a meat out of that...\n\r",ch);
    return;
  }

  temp_obj = read_object(r_num, REAL);
  obj_to_char(temp_obj, ch);
  act("You mutilate $p into a piece of meat!", FALSE, ch, o, 0, TO_CHAR);
  act("$n mutilates $p into a piece of meat!", FALSE, ch, o, 0, TO_ROOM);
	    
  for (jj = o->contains; jj; jj = next_thing2) 
  {
    next_thing2 = jj->next_content;
    obj_from_obj(jj);
    float_sink_object(jj, o->in_room);
  }

  extract_obj(o);
}

// General Skills

// cleanup 7/8/98 -jtrhone
ACMD(do_first_aid)
{
   int pts;

   if (IS_NPC(ch)) return;
  
   if (FIGHTING(ch))
   {
     send_to_char("You cannot tend to your wounds while fighting!\n\r",ch);
     return;
   }

   pts = (GET_LEVEL(ch) / 10) + number(1, 8);

   if (pts > GET_MOVE(ch))
   {
     send_to_char("You are too exhausted!\n\r",ch);
     return;
   }

   if (GET_SKILL(ch, SKILL_FIRST_AID) > number(1,101))
   {
     GET_HIT(ch) += pts;
     GET_HIT(ch) = MIN(GET_MAX_HIT(ch), GET_HIT(ch));
     GET_MOVE(ch) -= pts;
     GET_MOVE(ch) = MAX(GET_MOVE(ch), 0);

     send_to_char("You apply first aid to your wounds.\n\r",ch);
     act("$n tends to $s wounds.", FALSE, ch, 0, 0, TO_ROOM);

     WAIT_STATE(ch, spell_info[SKILL_FIRST_AID].beats);
   }
   else
     send_to_char("You fail to heal your wounds!\n\r",ch);
}

ACMD(do_double)
{
   chdata *victim;
   byte percent, prob;

   if (IS_NPC(ch))
     return;

   if (FIGHTING(ch)) 
      victim = FIGHTING(ch);
   else
      return;

   if (victim == ch) {
      send_to_char("Aren't we funny today...\n\r", ch);
      return;
   }

  // 101% is a complete failure
  percent = ((10 - (GET_AC(victim) / 10)) << 1) + number(1, 101);

  // Modified for Druid spell 'forest warrior' 05/03/98 -callahan
  if (affected_by_spell(ch, SPELL_FOREST_WARRIOR))
    prob = GET_SKILL(ch, SPELL_FOREST_WARRIOR);
  else
    prob = GET_SKILL(ch, SKILL_DOUBLE);
       
   /* ogres lack the quickness to doublehit James Rhone */
   if (GET_RACE(ch) == RACE_OGRE || GET_RACE(ch) == RACE_DRAGON)
      percent += 30;

   if (percent < prob) 
      hit(ch, victim, TYPE_UNDEFINED, FALSE); 
}

ACMD(do_triple)
{
  chdata *victim;
  byte percent, prob;

  if (IS_NPC(ch))
    return;

  if (!(victim = FIGHTING(ch)))
    return;

  if (victim == ch) {
     send_to_char("Aren't we funny today...\n\r", ch);
     return;
  }

  percent = ((10 - (GET_AC(victim) / 10)) << 1) + number(1, 101); /* 101% is a complete failure */
  prob = GET_SKILL(ch, SKILL_TRIPLE);
       
  /* ogres lack the quickness to triplehit  -jtrhone */
  if (GET_RACE(ch) == RACE_OGRE || GET_RACE(ch) == RACE_DRAGON)
     percent += 30;

  if (percent < prob) 
     hit(ch, victim, TYPE_UNDEFINED, FALSE);
 }

ACMD(do_stuntouch)
{
  chdata *vict;
  int prob = GET_SKILL(ch, SKILL_STUNTOUCH);
  int lvldiff;

  if (IS_NPC(ch))
    return;
  
  if (IS_PC(ch) && !IS_RANGER(ch) && !IS_WARRIOR(ch) && !IS_THIEF(ch) && !IS_MONK(ch))
  {
    send_to_char("You don't posess the ability to stun touch.\n\r",ch);
    return;
  }

  if (!FIGHTING(ch))
  {
    send_to_char("You have to be fighting to stun touch.\n\r",ch);
    return;
  }

  if (prob == 0) {
    send_to_char("But you don't have the touch yet!\r\n", ch);
    return;
  }
  one_argument(argument, arg);

  if (EQ(ch, W_HOLD) && EQ(ch, W_WIELD)) {
    send_to_char("You need a free hand to stuntouch!\r\n", ch);
    return;
  }

  if(!(vict = get_char_room_vis(ch, arg))) {
    if(FIGHTING(ch)) {
      vict = FIGHTING(ch); 
    }
    else {
      send_to_char("Stuntouch who?\r\n", ch);
      return;
    }
  }

  if(vict == ch) {
    send_to_char("Aren't we funny today...\r\n", ch);
    return;
  }

  if (!check_mortal_combat(ch, vict))
    return;

  lvldiff = GET_LEVEL(ch) - GET_LEVEL(vict);
  if(lvldiff < 0) prob += lvldiff;
  else prob += (lvldiff/2);
  prob = MIN(prob, 99);

  if(prob < number(1, 101)) 
    damage(ch, vict, 0, SKILL_STUNTOUCH, TRUE);
  else 
  {
    if (damage(ch, vict, number(1, 3), SKILL_STUNTOUCH, TRUE) == CHAR_OK)
      SET_BIT(CHAR_FLAGS(vict), CH_STUNTOUCHED);
    WAIT_STATE(ch, PULSE_VIOLENCE);
  }
  WAIT_STATE(ch, 2 * PULSE_VIOLENCE);
}
 
ACMD(do_parry)
{
   chdata *vict = 0;

   if (!(vict = FIGHTING(ch))) return;  /* not fighting anybody */
   if (WAITING(vict)) return;
   act("You %5parry%0 $N's attack!", TRUE, ch, NULL, vict, TO_CHAR);
   act("$n %5parries%0 your attack!.", TRUE, ch, NULL, vict, TO_VICT);
   act("$n %0parries%0 $N's attack!.", TRUE, ch, NULL, vict, TO_NOTVICT);
}

ACMD(do_dodge)
{
   chdata *vict = 0;

   if (!(vict = FIGHTING(ch))) return;  /* not fighting anybody */
   if (WAITING(vict)) return;
   act("You %5dodge%0 $N's attack!", TRUE, ch, NULL, vict, TO_CHAR);
   act("$n %5dodges%0 your attack!.", TRUE, ch, NULL, vict, TO_VICT);
   act("$n %5dodges%0 $N's attack!.", TRUE, ch, NULL, vict, TO_NOTVICT);
}

// generic command front end to particular skills... 4/26/98 -jtrhone
ACMD(do_search)
{
  char *argu = argument;
  int dir;
  rmdirdata *d;
  extern void do_look_dir(chdata *ch, int dir);

  if (IS_NPC(ch))
    return;

  skip_spaces(&argu);
  if (!*argu)
  {
    send_to_char("Usage: search < direction >.\n\r",ch);
    return;
  }

  if (!str_cmp(argu, "room"))
  {
    send_to_char("Searching a room is not yet available.\n\r",ch);
    return;
  }

  // searching a direction...
  dir = search_block(argu, comm_dirs, FALSE);

  if (dir < 0 || dir > NUM_OF_DIRS)
  {
    send_to_char("Usage: search < direction >.\n\r",ch);
    return;
  }

  sprintf(buf, "You search the way %s.\n\r", dirs[dir]);
  S2C();
  sprintf(buf, "$n searches the way %s.", dirs[dir]);
  act(buf,TRUE,ch,0,0,TO_ROOM);

  d = EXIT(ch, dir);
  if (d && !EXIT_SECRET(d))
  {
    do_look_dir(ch, dir);
    return;
  }

  if (GET_SKILL(ch, SKILL_DETECTSECRET) < number(1, 101))
  {
    act("You find nothing there.",FALSE,ch,0,0,TO_CHAR);
    return;
  }

  // ok, they succeeded...
  if (!d)
  {
    act("You find nothing there.",FALSE,ch,0,0,TO_CHAR);
    return;
  }

  if (d && EXIT_SECRET(d))
  {
    act("You find a concealed passage!",FALSE,ch,0,0,TO_CHAR);
    act("$n finds a concealed passage!",TRUE,ch,0,0,TO_ROOM);

    // will be reset on zone reset...
    UNFLAG_EXIT(d, EX_SECRET);

    do_look_dir(ch, dir);
    return;
  }
}