/
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

   File: warlock.c		Realms of Aurealis OLWSC
				Online Warlock Spell Creation

   warlocks will get very few of the traditional 'spells' which
   accompany the more normal classes such as mages and clerics...
   warlocks are a special breed of magic wielders and deal exclusively
   in combat damaging spells...purely combat oriented classes have their
   disadvantages obviously, not much in the way of offering self protection
   or battle preparation spells as clerics and mages have, but far more
   combat spells and those are far more powerful...and of course what
   seperates the warlock class from all others in existence... the ability
   to add a new spell online at any time using the RoAOLC menu driven system
   UPDATE: 2/19/97
   Warlocks will soon have the ability to have the more traditional types
   of spells created/editted online.  This will be quite cool. -jtrhone

		******** 100% completely original code ********
		*** BE AWARE OF ALL RIGHTS AND RESERVATIONS ***
		******** 100% completely original code ********
		        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 "comm.h"
#include "interpreter.h"
#include "acmd.h"
#include "handler.h"
#include "db.h"
#include "roaolc.h"
#include "magic.h"
#include "warlock.h"
#include "affect.h"
#include "fight.h"
#include "exshell.h"
#include "global.h"

/* internal functions */
ROA_MENU(wedit_top_menu);
ROA_MENU(wedit_confirm_quit);
ROA_MENU(wedit_confirm_save);

/* some structures used thruout... */
struct wspell_index wspells[MAX_WARLOCKSPELLS];
int wspellcount = 0;

// external vars
extern char *apply_types[];
extern char *affected_bits[];
extern char *affected2_bits[];

// external functions
extern void send_spell_listing(chdata *ch);

/* updates and sorts the WARLOCK_INDEX in lib using unix ls */
/* called at bootup and after a warlock spell is editted or created */
void read_sort_wspells(void)
{
  FILE *fp;
  int i;
  char buf[MAX_INPUT_LENGTH];

  sprintf(buf, "ls warlock > WARLOCK_INDEX");
  roa_system(buf);

  if (!(fp = fopen("WARLOCK_INDEX", "r")))
  {
    sprintf(buf, "SYSERR: Error opening WARLOCK_INDEX.");
    mudlog(buf, BRF, LEV_IMM, FALSE);
    return;
  }

  i = 0;
  while (!feof(fp))
  {
    fscanf(fp, "%s\n", buf);
    strcpy(wspells[i].name, buf);
    i++;
  }
  fclose(fp);

  /* keep cout of how many we have and show stats perhaps www */
  wspellcount = i;

  /* blank out remaining spells */
  for ( ; i < MAX_WARLOCKSPELLS; i++)
    strcpy(wspells[i].name, "");
}

/* overkill recursive proc for binary searchin -jtrhone */
BOOL wbin_search(char *comm, int low, int high)
{
  int mid = (low+high) / 2;
  int cond;

  if (!(cond = str_cmp(wspells[mid].name, comm)))
    return TRUE;
  else
  if (low >= high) 
    return FALSE;
  else
  if (cond > 0)  /* search bottom half */
    return wbin_search(comm, low, (mid-1));
  else /* search top half */
    return wbin_search(comm, (mid+1), high);
}

/* binary search on the wspells list to see if the given spell is
   found in the wspell list, returns TRUE if its in the index */ 
BOOL find_wspell(char *comm)
{
   char buf[MAX_INPUT_LENGTH];
   int top = 0;

   if (strlen(comm) >= MAX_INPUT_LENGTH)
     return FALSE;

   while (wspells[top].name[0])
     top++;
   one_argument(comm, buf);
   return wbin_search(buf, 0, top);
}

/* NOTE!! NAME HAS BEEN ALLOCATED BEFORE WE ENTER THIS FUNCTION!! */
/* THAT IS HOW WE FIND IT AND READ IN THE REST OF THE DATA MAN! */
/* THERE IS NO NEED TO RE ALLOCATE roa->name IN THIS FUNCTION!! */
/* -roa */
BOOL read_wspell_data(struct wspell_type *roa)
{
  FILE *fp;
  char buf[MAX_STRING_LENGTH];
  char letter;

  one_argument(roa->name, buf);
  sprintf(buf1, "warlock/%s", buf);
  if (!(fp = fopen(buf1, "r")))
    return FALSE;

  // now read in the data from disk
  fscanf(fp, "%d %d %d %d %d %d %d\n", &roa->level, &roa->mana, &roa->delay,
	 &roa->num_dice, &roa->size_dice, &roa->dice_bonus, &roa->save_off);
  roa->level = MIN(LEV_IMM, roa->level);

  // UPDATED 2/19/97, check for new affect format, if so read in data
  letter=fread_letter(fp);
  ungetc(letter, fp);
  if (letter == '@') 
  {
    roa->type = WSPELL_AFFECT;
    fscanf(fp, "@ %d %d %d %d %d %d %d\n", &roa->spell, &roa->violent, &roa->apply,
	   &roa->modifier, &roa->duration, &roa->aff1, &roa->aff2);
  }
  else
  {
    roa->type = WSPELL_DAMAGE;
    roa->spell = roa->apply = roa->modifier = roa->duration = 0;
    roa->violent = roa->aff1 = roa->aff2 = 0;
  }

  /* NOTE these must be freed... done so after spell is cast in do_wspell */
  roa->to_char_miss = fread_string(fp, buf);
  roa->to_char_hit  = fread_string(fp, buf);
  roa->to_char_kill = fread_string(fp, buf);

  roa->to_vict_miss = fread_string(fp, buf);
  roa->to_vict_hit  = fread_string(fp, buf);
  roa->to_vict_kill = fread_string(fp, buf);

  roa->to_room_miss = fread_string(fp, buf);
  roa->to_room_hit  = fread_string(fp, buf);
  roa->to_room_kill = fread_string(fp, buf);

  fclose(fp);
  return TRUE;
}

BOOL check_wspell_format(struct wspell_type *roa)
{
  if (roa->type != WSPELL_DAMAGE && roa->type != WSPELL_AFFECT)
    return FALSE;

  if (!roa->name || !roa->to_char_miss || !roa->to_char_hit ||
      !roa->to_char_kill || !roa->to_vict_miss || !roa->to_vict_hit ||
      !roa->to_vict_kill || !roa->to_room_miss || !roa->to_room_hit || 
      !roa->to_room_kill)
   return FALSE;

  if (roa->type == WSPELL_AFFECT)
    if (roa->spell <= 0 || roa->spell >= MAX_SPELLS)
      return FALSE;

  return TRUE;
}

BOOL save_wspell(struct wspell_type *roa)
{
  FILE *fp;

  one_argument(roa->name, buf);
  sprintf(buf1, "warlock/%s", buf);
  if (!(fp = fopen(buf1, "wt")))
  {
    sprintf(buf, "SYSERR:  Unable to save warlock spell.");
    mudlog(buf, BRF, LEV_IMM, FALSE);
    return FALSE;
  }
  fprintf(fp, "%d %d %d %d %d %d %d\n", roa->level, roa->mana, roa->delay,
	  roa->num_dice, roa->size_dice, roa->dice_bonus, roa->save_off);

  if (roa->type == WSPELL_AFFECT)
  {
    fprintf(fp, "@ %d %d %d %d %d %d %d\n",roa->spell, roa->violent, roa->apply,
           roa->modifier, roa->duration, roa->aff1, roa->aff2);
  }

  fprintf(fp, "%s~\n", (roa->to_char_miss)?   roa->to_char_miss: "");
  fprintf(fp, "%s~\n", (roa->to_char_hit )?   roa->to_char_hit : "");
  fprintf(fp, "%s~\n", (roa->to_char_kill)?   roa->to_char_kill: "");

  fprintf(fp, "%s~\n", (roa->to_vict_miss)?   roa->to_vict_miss: "");
  fprintf(fp, "%s~\n", (roa->to_vict_hit )?   roa->to_vict_hit : "");
  fprintf(fp, "%s~\n", (roa->to_vict_kill)?   roa->to_vict_kill: "");

  fprintf(fp, "%s~\n", (roa->to_room_miss)?   roa->to_room_miss: "");
  fprintf(fp, "%s~\n", (roa->to_room_hit )?   roa->to_room_hit : "");
  fprintf(fp, "%s~\n", (roa->to_room_kill)?   roa->to_room_kill: "");
  fclose(fp);
  read_sort_wspells();
  return TRUE;
}

/* free the strings associated with the wspell -roa */
void free_wspell(struct wspell_type *s)
{
  FREENULL(s->name);
  FREENULL(s->to_char_miss);
  FREENULL(s->to_char_hit);
  FREENULL(s->to_char_kill);
  FREENULL(s->to_vict_miss);
  FREENULL(s->to_vict_hit);
  FREENULL(s->to_vict_kill);
  FREENULL(s->to_room_miss);
  FREENULL(s->to_room_hit);
  FREENULL(s->to_room_kill);
}

/* 
   Beginning of OLWSC - Online warlock spell creation only found here on RoA
   author - James Rhone aka jtrhone aka Vall

   The following code is the menuing system that provides builders with
   a way of creating warlock battle spells online...
   UPDATE 2/19/97 - added affectual spell functionality, it rocks :) -jtrhone
*/
ACMD(do_wedit)
{
  char buf[80];

  if (IS_NPC(ch))
    return;

  one_argument(argument, buf);

  if (!*buf)
  {
    send_to_char("Usage: waredit <spell name>\n\r", ch);
    return;
  }
    
  CREATE(ch->pc_specials->war_editted, struct wspell_type, 1);
  ch->pc_specials->war_editted->name = str_dup(buf);

  if (find_wspell(buf))
    if (!read_wspell_data(ch->pc_specials->war_editted))
    {
	sprintf(buf, "SYSERR: spell exists but could not be loaded into struct.");
	mudlog(buf, BRF, LEV_IMM, FALSE);
	free_wspell(ch->pc_specials->war_editted);
	FREENULL(ch->pc_specials->war_editted);
	ch->pc_specials->war_editted = NULL;
	return;
    }

  SET_BIT(PLR_FLAGS(ch), PLR_BUILDING);
  MENU_DEPTH(ch) = 0;
  ch->pc_specials->field_changed = FALSE;    
  menu_jump(ch, wedit_top_menu);
}

/* yank those double subs chars out for act() */
void kill_spell_dollars(struct wspell_type *s)
{
  extern char *delete_doubledollar(char *string);

  s->to_char_miss = delete_doubledollar(s->to_char_miss);
  s->to_char_hit  = delete_doubledollar(s->to_char_hit);
  s->to_char_kill = delete_doubledollar(s->to_char_kill);

  s->to_vict_miss = delete_doubledollar(s->to_vict_miss);
  s->to_vict_hit  = delete_doubledollar(s->to_vict_hit);
  s->to_vict_kill = delete_doubledollar(s->to_vict_kill);

  s->to_room_miss = delete_doubledollar(s->to_room_miss);
  s->to_room_hit  = delete_doubledollar(s->to_room_hit);
  s->to_room_kill = delete_doubledollar(s->to_room_kill);
}

void wedit_mesg_menu(chdata *ch, char *input_str)
{
  char buf[MAX_STRING_LENGTH];
  char *p;
  int field;
  struct wspell_type *s = ch->pc_specials->war_editted;
    
  if (!input_str)
  {
    menu_title_send("WarlockEdit Message Edit Menu", ch);
    kill_spell_dollars(s);
    sprintf(buf, " 1.) %%6To char miss%%0:\n\r%s\n\r", s->to_char_miss); 
    S2C();
    sprintf(buf, " 2.) %%6To char hit%%0 :\n\r%s\n\r", s->to_char_hit);
    S2C();
    sprintf(buf, " 3.) %%6To char kill%%0:\n\r%s\n\r", s->to_char_kill);
    S2C();

    sprintf(buf, " 4.) %%6To vict miss%%0:\n\r%s\n\r", s->to_vict_miss); 
    S2C();
    sprintf(buf, " 5.) %%6To vict hit%%0 :\n\r%s\n\r", s->to_vict_hit);
    S2C();
    sprintf(buf, " 6.) %%6To vict kill%%0:\n\r%s\n\r", s->to_vict_kill);
    S2C();

    sprintf(buf, " 7.) %%6To room miss%%0:\n\r%s\n\r", s->to_room_miss); 
    S2C();
    sprintf(buf, " 8.) %%6To room hit%%0 :\n\r%s\n\r", s->to_room_hit);
    S2C();
    sprintf(buf, " 9.) %%6To room kill%%0:\n\r%s\n\r", s->to_room_kill);
    S2C();

    send_to_char("\n\r", ch);
    MENU_PROMPT(ch) = "Enter field number to change or 0 to end: ";
    return;
  }

  strcpy(buf, input_str);
  p = strtok(buf, " 	\n\r");
  if (p)
	field = atoi(p);
  else
	field = 0;

  switch (field)
  {
    case 0:
	menu_back(ch);
	break;
    case 1:
	do_string_arg(ch, "Message to char if miss:\n\r", &s->to_char_miss, "");
	break;
    case 2:
	do_string_arg(ch, "Message to char if hit:\n\r", &s->to_char_hit, "");
	break;
    case 3:
	do_string_arg(ch, "Message to char if kill:\n\r", &s->to_char_kill, "");
	break;

    case 4:
	do_string_arg(ch, "Message to vict if miss:\n\r", &s->to_vict_miss, "");
	break;
    case 5:
	do_string_arg(ch, "Message to vict if hit:\n\r", &s->to_vict_hit, "");
	break;
    case 6:
	do_string_arg(ch, "Message to vict if kill:\n\r", &s->to_vict_kill, "");
	break;

    case 7:
	do_string_arg(ch, "Message to room if miss:\n\r", &s->to_room_miss, "");
	break;
    case 8:
	do_string_arg(ch, "Message to room if hit:\n\r", &s->to_room_hit, "");
	break;
    case 9:
	do_string_arg(ch, "Message to room if kill:\n\r", &s->to_room_kill, "");
	break;

    default:
	send_to_char("That field cannot be changed.\n\r", ch);
	break;
  }
}

ROA_MENU(wedit_top_menu)
{
  char buf[MAX_STRING_LENGTH];
  char *p;
  int field;
  struct wspell_type *s = ch->pc_specials->war_editted;
  char *violent_strs[] = {
	"%6Non-Violent%0",
	"%B%1Violent%0",
	"\n"
  };
  char *wspell_strs[] = {
	"%B%1Damage%0",
	"%B%6Affectual%0",
	"\n"
  };
    
  if (!input_str)
  {
    menu_title_send("WarlockEdit Main Menu", ch);
    sprintf(buf, " 1.) %%6SpellName%%0 : %s\n\r", s->name); S2C();
    sprinttype(s->type, wspell_strs, buf2);
    sprintf(buf, " 2.) %%6SpellType%%0 : %s\n\r",buf2); S2C();
    sprintf(buf, " 3.) %%6MinLevel%%0  : %d\n\r", s->level); S2C();
    sprintf(buf, " 4.) %%6MaxMana%%0   : %d\n\r", s->mana); S2C();
    sprintf(buf, " 5.) %%6DelayTime%%0 : %d\n\r", s->delay); S2C();

    if (s->type == WSPELL_DAMAGE)
    {
      sprintf(buf, " 6.) %%6Num DamDice%%0  : %d\n\r", s->num_dice); S2C();
      sprintf(buf, " 7.) %%6Size DamDice%%0 : %d\n\r", s->size_dice); S2C();
      sprintf(buf, " 8.) %%6DamDice Bonus%%0: %d\n\r", s->dice_bonus); S2C();
      sprintf(buf, " 9.) %%6Sav throw reduces damage by%%0: %d percent.\n\r", 
		s->save_off); S2C();
    }
    else
    {
	if (s->spell > 0 && s->spell < MAX_SPELLS)
	  strcpy(buf2, skill_names[s->spell]);
	else
	  strcpy(buf2, "Undefined");
	sprintf(buf, "10.) %%6Spell #%%0   : %d (%s)\r\n",s->spell,buf2);S2C();

	sprinttype(s->apply, apply_types, buf2);
	sprintf(buf, "11.) %%6Apply to%%0  : %s\r\n",buf2);S2C();

	sprintf(buf, "12.) %%6Modifier%%0  : %d\r\n",s->modifier);S2C();
	sprintf(buf, "13.) %%6Duration%%0  : %d\r\n",s->duration);S2C();

	sprintbit(s->aff1, affected_bits, buf2);
	sprintf(buf, "14.) %%6Affects1%%0  : %%5%s%%0\r\n",buf2);S2C();

	sprintbit(s->aff2, affected2_bits, buf2);
	sprintf(buf, "15.) %%6Affects2%%0  : %%5%s%%0\r\n",buf2);S2C();

	sprinttype(s->violent, violent_strs, buf2);
	sprintf(buf, "16.) %%6VFlag%%0     : %s\r\n",buf2);S2C();
    }

    sprintf(buf, "20.) %%6Message Menu...%%0\n\r"); S2C();

    send_to_char("\n\r", ch);
    MENU_PROMPT(ch) = "Enter field number to change or 0 to end: ";
    return;
  }

  strcpy(buf, input_str);
  p = strtok(buf, " 	\n\r");
  if (p)
	field = atoi(p);
  else
	field = 0;

    switch (field)
    {
    case 0:
	menu_jump(ch, wedit_confirm_quit);
	break;

    case 1:
	do_string_arg(ch, "Enter new spell name:\n\r", &s->name, "");	
	break;

    case 2:
	get_integer_list(ch, "Enter warlock spell type: ", &s->type,
			 sizeof(s->type), wspell_strs);
	break;

    case 3:
	GET_INTEGER_ARG(ch, "Enter spell minlevel: ", s->level, 0, 70);
        break;
    case 4:
	GET_INTEGER_ARG(ch, "Enter spell max mana cost: ", s->mana, 0, 1000);
        break;
    case 5:
	GET_INTEGER_ARG(ch, "Enter number of combat pulses to delay caster: ",
			s->delay, 0, 500);
        break;
    case 6:
	GET_INTEGER_ARG(ch, "Enter number of damage dice: ", s->num_dice, 0, 100);
        break;
    case 7:
	GET_INTEGER_ARG(ch, "Enter size of damage dice: ", s->size_dice, 0, 100);
        break;
    case 8:
	GET_INTEGER_ARG(ch, "Enter damage dice bonus: ", s->dice_bonus, 0, 500);
        break;
    case 9:
	GET_INTEGER_ARG(ch, "Enter percentage of damage reduction on saving throw: ", 
			s->save_off, 0, 100);
        break;

    case 10:
	send_spell_listing(ch);
	GET_INTEGER_ARG(ch, "Enter spell number: ", s->spell, -1, MAX_SPELLS-1);
	break;

    case 11:
	get_integer_list(ch, "Enter Apply type: ", &s->apply, sizeof(s->apply),
			 apply_types);
	break;

    case 12:
	GET_INTEGER_ARG(ch, "Enter apply modifier: ", s->modifier, -100, 100);
	break;
    case 13:
	GET_INTEGER_ARG(ch, "Enter affect duration: ", s->duration, 1, 75);
	break;

    case 14:
	toggle_menu(ch, "Enter affect to toggle: ", &s->aff1, affected_bits);
	break;

    case 15:
	toggle_menu(ch, "Enter affect to toggle: ", &s->aff2, affected2_bits);
	break;

    case 16:
	get_integer_list(ch, "Enter VFlag type: ", &s->violent, sizeof(s->violent),
			 violent_strs);
	break;

    case 20:
	menu_push(ch);
	MENU_RETURN(ch) = 0;
	MENU_HANDLER_ARG(ch) = (void *) 0;
	menu_jump(ch, wedit_mesg_menu);
	break;

    default:
	send_to_char("That field cannot be changed.\n\r", ch);
	break;
  }
}

ROA_MENU(wedit_confirm_quit)
{
  char buf[MAX_STRING_LENGTH];
  char *p;
    
  if (!input_str)
  {
    MENU_PROMPT(ch) = "Quit editting spell (yes/NO)? ";
    return;
  }
    
  strcpy(buf, input_str);
  p = strtok(buf, " 	\n\r");
  if (p && strncasecmp("yes", p, strlen(p)) == 0)
    menu_jump(ch, wedit_confirm_save);
  else
    menu_jump(ch, wedit_top_menu);
}

void wedit_confirm_save(chdata *ch, char *input_str)
{
  char buf[MAX_STRING_LENGTH];
  char *p;
  struct wspell_type *s = ch->pc_specials->war_editted;
    
  if (!input_str)
  {
	MENU_PROMPT(ch) = "Save editted spell (YES/no)? ";
	return;
  }
    
  strcpy(buf, input_str);
  p = strtok(buf, " 	\n\r");
  if (!p || strncasecmp("no", p, strlen(p)) != 0)
  {
	if (!check_wspell_format(s))
	{
	  send_to_char("Error in spell format, spell not saved.\n\r",ch);
	  menu_jump(ch, wedit_top_menu);
	  return;
	}
	else
	{
	  if (find_wspell(s->name))
	      sprintf(buf, "SYSUPD: %s has editted warlock spell %s", GET_NAME(ch), 
  		      s->name);
	  else
	      sprintf(buf, "SYSUPD: %s has created warlock spell %s", GET_NAME(ch), 
	  	      s->name);
	  mudlog(buf, BRF, GET_LEVEL(ch), TRUE);
	  save_wspell(s);
	}
  }

  free_wspell(s);
  FREENULL(s);
  MENU_PROMPT(ch) = NULL;
  MENU_HANDLER(ch) = NULL;
  MENU_DEPTH(ch) = 0;
  REMOVE_BIT(PLR_FLAGS(ch), PLR_BUILDING);
}

// wardel <spelname, asks for confirmation so not done on accident...
void wedit_confirm_delete(chdata *ch, char *input_str)
{
  char buf[MAX_STRING_LENGTH];
  char *p;
    
  if (!input_str)
  {
	MENU_PROMPT(ch) = "Delete spell (YES/no)? ";
	return;
  }
    
  strcpy(buf, input_str);
  p = strtok(buf, " 	\n\r");
  if (PSTR1(ch))  /* name of spell file was stored here */
  {
    if (!p || strncasecmp("no", p, strlen(p)) != 0)
    {
	roa_system(PSTR1(ch));
	read_sort_wspells();
	send_to_char("Spell removed. Index updated.\n\r",ch);
    }
    else
      send_to_char("Spell not removed.\n\r",ch);
  }
  else
    send_to_char("Spell not removed, invalid filename.\n\r",ch);

  FREENULL(PSTR1(ch));
  MENU_PROMPT(ch) = NULL;
  MENU_HANDLER(ch) = NULL;
  MENU_DEPTH(ch) = 0;
}

ACMD(do_wspell_delete)
{
  char *argu = argument;

  if (IS_NPC(ch)) return;

  if (!*argu)
  {
    send_to_char("Usage: wardelete <spell name>.\n\r",ch);
    return;
  }

  skip_spaces(&argu);
  if (!find_wspell(argu))
  {
    send_to_char("Spell not found.\n\r",ch);
    return;
  }

  sprintf(buf, "rm warlock/%s", argu);
  PSTR1(ch) = str_dup(buf);  /* save command to process on character */

  MENU_DEPTH(ch) = 0;    
  menu_jump(ch, wedit_confirm_delete); 
}

// return true if the spell is of a violent nature
int violent_wspell(struct wspell_type *roa)
{
  if (roa->type == WSPELL_DAMAGE)
    return TRUE;
  else
    return roa->violent;
}

// cast a damage type warlock spell
void do_wcast_damage(chdata *ch, chdata *vict, struct wspell_type *spl)
{
  int total_damage;

  total_damage = dice(spl->num_dice, spl->size_dice) + spl->dice_bonus;

  // all warlock damage spells match magic missile type
  if (saves_spell(vict, SPELL_MAGIC_MISSILE))
  {
    total_damage -= (int) ((float)(spl->save_off / 100.0) * (float)total_damage);
    total_damage = MAX(0, total_damage);
  }

  if (!total_damage) /* we missed */
  {
    act(spl->to_char_miss, FALSE, ch, NULL, vict, TO_CHAR);
    act(spl->to_vict_miss, FALSE, ch, NULL, vict, TO_VICT);
    act(spl->to_room_miss, FALSE, ch, NULL, vict, TO_ROOM);
  }
  else
  if (total_damage < GET_HIT(vict)) /* hit em normally */
  {
    act(spl->to_char_hit, FALSE, ch, NULL, vict, TO_CHAR);
    act(spl->to_vict_hit, FALSE, ch, NULL, vict, TO_VICT);
    act(spl->to_room_hit, FALSE, ch, NULL, vict, TO_ROOM);
  }
  else
  if (total_damage >= GET_HIT(vict))  /* kill the sucka */
  {
    act(spl->to_char_kill, FALSE, ch, NULL, vict, TO_CHAR);
    act(spl->to_vict_kill, FALSE, ch, NULL, vict, TO_VICT);
    act(spl->to_room_kill, FALSE, ch, NULL, vict, TO_ROOM);
  }

  damage(ch, vict, total_damage, TYPE_UNDEFINED, FALSE);
}

// cast an affectual type warlock spell
void do_wcast_affect(chdata *ch, chdata *vict, struct wspell_type *spl)
{
  struct affected_type af;

  if (spl->type != WSPELL_AFFECT)
    return;

  if (spl->spell <= 0 || spl->spell >= MAX_SPELLS)
  {
    sprintf(buf, "SYSERR: %s casted invalid warlock spell.",GET_NAME(ch));
    mudlog(buf, BRF, LEV_IMM, TRUE);
    return;
  }

  if (spl->violent)
    if (saves_spell(vict, spl->spell) || affected_by_spell(vict, spl->spell))
    {
      act(spl->to_char_miss, FALSE, ch, NULL, vict, TO_CHAR);
      act(spl->to_vict_miss, FALSE, ch, NULL, vict, TO_VICT);
      act(spl->to_room_miss, FALSE, ch, NULL, vict, TO_NOTVICT);
      return;
    }

  if (affected_by_spell(vict, spl->spell))
  {
    if (vict == ch)
    {
      act("$n fails to cast a spell on $mself.",TRUE,ch,0,0,TO_ROOM);
      act("You fail to cast the spell on yourself.",TRUE,ch,0,0,TO_CHAR);
      return;
    }
    act(spl->to_char_miss, FALSE, ch, NULL, vict, TO_CHAR);
    act(spl->to_vict_miss, FALSE, ch, NULL, vict, TO_VICT);
    act(spl->to_room_miss, FALSE, ch, NULL, vict, TO_NOTVICT);
    return;
  }
  
  if (ch == vict)
  {
    act("$n's eyes briefly become %4dark%0 as $e utters some words.",TRUE,ch,0,0,TO_ROOM);
    act("You briefly lose vision as you utter %4dark words%0.",TRUE,ch,0,0,TO_CHAR);
  }
  else
  {
    act(spl->to_char_hit, FALSE, ch, NULL, vict, TO_CHAR);
    act(spl->to_vict_hit, FALSE, ch, NULL, vict, TO_VICT);
    act(spl->to_room_hit, FALSE, ch, NULL, vict, TO_NOTVICT);
  }

  af.type 	= spl->spell;
  af.location 	= spl->apply;
  af.duration 	= spl->duration;
  af.modifier 	= spl->modifier;
  af.bitvector 	= spl->aff1;
  af.bitvector2 = spl->aff2;
  affect_to_char(vict, &af);
}

/* main warlock interface for casting olcable spells */
ACMD(do_wcast)
{
  char arg1[MAX_INPUT_LENGTH];
  char arg2[MAX_INPUT_LENGTH];
  struct wspell_type spl;
  chdata *vict;
  int i, mana_needed;
  char buf[20000];

  if (CHARMED(ch))
  {
    send_to_char("You must be in total control of your mind to use this magic.\n\r",ch);
    return;
  }

  if (IS_NPC(ch) /* && !SPC_FLAGGED(ch, SPC_WARLOCK) */)
    return;

  if (IS_PC(ch) && !IS_WARLOCK(ch))
  {
    send_to_char("You fumble for the void needed to control such magic.\n\r",ch);
    return;
  }

  /* arg1 = spell name, arg2 = target if specified */
  half_chop(argument, arg1, arg2);

  if (!*arg1)
  {
    send_to_char("Usage: wcast <spell> <victim>.\n\r",ch);
    send_to_char("These are the %5Warlock%0 spells available to you.\n\r",ch);
    for (i = 0, *buf = '\0'; *wspells[i].name; i++)
    {
      spl.name = str_dup(wspells[i].name);
      if (read_wspell_data(&spl) && spl.level <= (int)GET_WLEVEL(ch))
      {
	sprintf(buf+strlen(buf), 
		"%%6%-13.13s%%0  Level: %%6%2d%%0  Mana: %%6%4d%%0\n\r",
		spl.name, spl.level, spl.mana);
	free_wspell(&spl);
      }
    }
    if (*buf)
      page_string(ch->desc, buf, 1);
    else
      send_to_char("None.\n\r",ch);
    return;
  }

  if (!find_wspell(arg1))
  {
    send_to_char("You do not know of that spell.\n\r",ch);
    return;
  }
  
  spl.name = str_dup(arg1);
  if (!read_wspell_data(&spl))
  {
    send_to_char("Spell not supported, notify an immortal.\n\r",ch);
    FREENULL(spl.name);
    return;
  }

  if (!(vict = get_char_room_vis(ch, arg2)) &&
      !(vict = FIGHTING(ch)))
  {
    if (!str_cmp(arg2, "me") || !str_cmp(arg2, "self"))
      vict = ch;
    else
    {
      send_to_char("You must supply a target to focus on.\n\r",ch);
      free_wspell(&spl);
      return;
    }
  }

  if (violent_wspell(&spl))
  {
    if (ch == vict)
    {
      send_to_char("The %4void%0 prevents you from harming yourself.\n\r",ch);
      free_wspell(&spl);
      return;
    }

    if (!check_mortal_combat(ch, vict))
    {
      send_to_char("Assassin vs Assassin only.\n\r",ch);
      free_wspell(&spl);
      return;
    }

    if (!check_truce(ch, vict))
    {
      free_wspell(&spl);
      return;
    }
    if (!check_mortal_combat(ch, vict))
    {
      free_wspell(&spl);
      return;
    }
  }
  else
  if (vict == FIGHTING(ch))
  {
    send_to_char("The %4void%0 prevents you from aiding your enemies.\n\r",ch);
    free_wspell(&spl);
    return;
  }

  if (spl.level > (int)GET_WLEVEL(ch))
  {
    send_to_char("You have not attained that spell casting level yet.\n\r",ch);
    free_wspell(&spl);
    return;
  }

  /* for every point of align over 0, add another 1/2 mana point */
  mana_needed = spl.mana + (MAX(0, GET_ALIGNMENT(ch)) / 2);

  if (GET_MANA(ch) < mana_needed)
  {
    act("You fail to call forth the %4void%0.", FALSE, ch, NULL, NULL, TO_CHAR);
    act("$n fails to call forth the %4void%0.", FALSE, ch, NULL, NULL, TO_ROOM);
    free_wspell(&spl);
    return;
  }
  GET_MANA(ch) -= mana_needed;

  switch (spl.type)
  {
    case WSPELL_DAMAGE:
      do_wcast_damage(ch, vict, &spl);
      break;
    case WSPELL_AFFECT:
      do_wcast_affect(ch, vict, &spl);
      break;
    default:
      break;
  }

  if (spl.delay > 0)
    WAIT_STATE(ch, spl.delay * PULSE_VIOLENCE);
  free_wspell(&spl);
}