/
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

fightmes.c			Code dealing with sending the various 
				damage messages to characters.

				Also contains CMOLCv0.1 (RoA)
				Combat Message Online Creation
				Based on RoAOLCv2.1 standards.
				Now RoAOLCable. 1/10/97

		******** 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 "comm.h"
#include "handler.h"
#include "interpreter.h"
#include "acmd.h"
#include "db.h"
#include "magic.h"
#include "mudlimits.h"
#include "roaolc.h"
#include "global.h"
#include "exshell.h"

/* External variables / structures */
extern struct message_list fight_messages[MAX_MESSAGES];

// external functions 
extern char *delete_doubledollar(char *string);

// internal functions
void messedit_top_menu(chdata *ch, char *input_str);
void messedit_confirm_message_quit(chdata *ch, char *input_str);
void messedit_confirm_message_save(chdata *ch, char *input_str);
void messedit_edit_message(chdata *ch, char *input_str);
void messedit_confirm_quit(chdata *ch, char *input_str);
void dupe_mesg_over(struct message_type *orig, struct message_type *m);
struct message_type *get_mesg(struct message_type *m, int num);
int count_mesgs(struct message_type *m);

struct attack_hit_type {
   char *singular;
   char *plural;
} attack_hit_text[] = 
{
   { "hit",   "hits" },
   { "pound", "pounds" },
   { "pierce", "pierces" },
   { "slash", "slashes" },
   { "blast", "blasts" },
   { "whip", "whips" },
   { "pierce", "pierces" },
   { "claw", "claws" },
   { "bite", "bites" },
   { "sting", "stings" },
   { "crush", "crushes" },
   { "BDEFINED", "BDEFINED"}		/* Builder defined */ 
};

// revamped, 1/10/97, jtrhone
void clear_messages(void)
{
  int i;

  for (i = 0; i < MAX_MESSAGES; i++) {
    fight_messages[i].a_type    = 0;
    fight_messages[i].num_mesgs = 0;
    fight_messages[i].msg       = NULL;
  }
}

// free this particular message (part of a larger message struct)
void free_this_message(struct message_type *m)
{
  FREENULL(m->die_msg.attacker_msg);
  FREENULL(m->die_msg.victim_msg);
  FREENULL(m->die_msg.room_msg);
  FREENULL(m->miss_msg.attacker_msg);
  FREENULL(m->miss_msg.victim_msg);
  FREENULL(m->miss_msg.room_msg);
  FREENULL(m->hit_msg.attacker_msg);
  FREENULL(m->hit_msg.victim_msg);
  FREENULL(m->hit_msg.room_msg);
  FREENULL(m->god_msg.attacker_msg);
  FREENULL(m->god_msg.victim_msg);
  FREENULL(m->god_msg.room_msg);
  FREENULL(m);
}

// go thru each message, traverse list of each one, free em up
void free_messages(void)
{
  int i;
  struct message_type *m;

  for (i = 0; i < MAX_MESSAGES; i++) 
    if (fight_messages[i].a_type > 0 && fight_messages[i].msg)
    {
      while (fight_messages[i].msg)
      {
	m = fight_messages[i].msg;
	fight_messages[i].msg = m->next;
	free_this_message(m);
      }
    }
}

// save the combat messages to file (copy over first)
void save_messages(void)
{
  FILE *fp;
  struct message_type *m;
  int i;

  sprintf(buf, "cp %s %s.BAK", MESS_FILE, MESS_FILE);
  roa_system(buf);

  if (!(fp = fopen(MESS_FILE, "w")))
  {
    sprintf(buf2, "Error writing combat message file %s", MESS_FILE);
    perror(buf2);
    exit(0);
  }

  i = 0;
  while (fight_messages[i].a_type > 0 && i < MAX_MESSAGES)
  {
   m = fight_messages[i].msg;
   while (m)
   {
    fprintf(fp, "M\n %d\n", fight_messages[i].a_type);

    fprintf(fp, "L %d\n", m->level);

    fprintf(fp, "%s~\n", m->die_msg.attacker_msg);
    fprintf(fp, "%s~\n", m->die_msg.victim_msg);
    fprintf(fp, "%s~\n", m->die_msg.room_msg);

    fprintf(fp, "%s~\n", m->miss_msg.attacker_msg);
    fprintf(fp, "%s~\n", m->miss_msg.victim_msg);
    fprintf(fp, "%s~\n", m->miss_msg.room_msg);

    fprintf(fp, "%s~\n", m->hit_msg.attacker_msg);
    fprintf(fp, "%s~\n", m->hit_msg.victim_msg);
    fprintf(fp, "%s~\n", m->hit_msg.room_msg);

    fprintf(fp, "%s~\n", m->god_msg.attacker_msg);
    fprintf(fp, "%s~\n", m->god_msg.victim_msg);
    fprintf(fp, "%s~\n", m->god_msg.room_msg);

    m = m->next;
   }
   i++;
  }

  fprintf(fp, "$~\n");
  fclose(fp);
}

// given a mesg slot and level, return ptr to appropriate mesg 
struct message_type *locate_appropriate_mesg(int i, int level)
{
  int j;		// to be used as a MAX_MESG_LINK check sometime
  struct message_type *m, *pm;

  for (j = 1, pm = m = fight_messages[i].msg; m; j++)
  {
    pm = m;
    m = m->next; 
    if (!m || level < m->level)
      break;
  }
  return pm;
}

// allocate and insert message into appropriate slot, return ptr to new mesg
struct message_type *insert_combat_message(int i, int level, int type)
{
  struct message_type *insmesg;
  struct message_type *newmesg;

  fight_messages[i].num_mesgs++;
  fight_messages[i].a_type = type;

  CREATE(newmesg, struct message_type, 1);
  newmesg->level = level;

  // now find where to insert into, not just top anymore
  if (!fight_messages[i].msg)	// just insert first one
  {
    fight_messages[i].msg = newmesg;
    newmesg->next = NULL;
  }
  else		// locate a spot based on level
  {
    insmesg = locate_appropriate_mesg(i, level);
    newmesg->next = insmesg->next;
    insmesg->next = newmesg;
  }

  return newmesg;
}

// send a file ptr and message struct, fill it in
void fill_message_data(FILE *f1, struct message_type *messages)
{
  messages->die_msg.attacker_msg      = fread_string(f1, buf2);
  messages->die_msg.victim_msg        = fread_string(f1, buf2);
  messages->die_msg.room_msg          = fread_string(f1, buf2);
  messages->miss_msg.attacker_msg     = fread_string(f1, buf2);
  messages->miss_msg.victim_msg       = fread_string(f1, buf2);
  messages->miss_msg.room_msg         = fread_string(f1, buf2);
  messages->hit_msg.attacker_msg      = fread_string(f1, buf2);
  messages->hit_msg.victim_msg        = fread_string(f1, buf2);
  messages->hit_msg.room_msg          = fread_string(f1, buf2);
  messages->god_msg.attacker_msg      = fread_string(f1, buf2);
  messages->god_msg.victim_msg        = fread_string(f1, buf2);
  messages->god_msg.room_msg          = fread_string(f1, buf2);
}

// load the combat messages from disk
void	load_messages(void)
{
  FILE *f1;
  int	i, type, level;
  struct message_type *newmesg;
  char	chk[100];
  char letter;

  if (!(f1 = fopen(MESS_FILE, "r"))) {
     sprintf(buf2, "Error reading combat message file %s", MESS_FILE);
     perror(buf2);
     exit(0);
  }

  clear_messages();

  fscanf(f1, " %s \n", chk);
  while (*chk == 'M') 
  {
    fscanf(f1, " %d\n", &type);

    // now check for RoA message level
    letter=fread_letter(f1);  
    ungetc(letter, f1);
    if (letter == 'L')
	fscanf(f1, "L %d\n",&level);
    else
	level = 0;

    // find the right skill/spell number to insert into
    for (i = 0; (i < MAX_MESSAGES) && (fight_messages[i].a_type != type) && 
        (fight_messages[i].a_type); i++)
      ;

    if (i >= MAX_MESSAGES) {
      log("SYSERR: Too many combat messages.");
      exit(0);
    }

    newmesg = insert_combat_message(i, level, type);
    fill_message_data(f1, newmesg);
    fscanf(f1, " %s \n", chk);
  }
  fclose(f1);
  sprintf(buf, "SYSUPD: %d combat message slots loaded.",i);
  mudlog(buf, BRF, LEV_IMM, TRUE);
}

// basically, just free and reload messages
void reload_messages(void)
{
  free_messages();
  clear_messages();
  load_messages();
}

ACMD(do_reload_messages)
{
  if (IS_NPC(ch))
    return;

  reload_messages();
  send_to_char("Messages reloaded.\n\r",ch);

  sprintf(buf, "IMMUPD: %s reloaded combat messages.",GET_NAME(ch));
  mudlog(buf, CMP, MAX(LEV_IMM, GET_INVIS_LEV(ch)), TRUE);
}

ACMD(do_save_messages)
{
  if (IS_NPC(ch)) return;
  save_messages();
  send_to_char("Messages saved.\n\r",ch);
  sprintf(buf, "IMMUPD: %s saved combat messages.",GET_NAME(ch));
  mudlog(buf, CMP, MAX(LEV_IMM, GET_INVIS_LEV(ch)), TRUE);
}

// replace the strings for weapons in dam_message() (to be changed -roa)
char	*replace_string(char *str, char *weapon_singular, char *weapon_plural)
{
   static char	buf[256];
   char	*cp;

   cp = buf;

   for (; *str; str++) 
   {
      if (*str == '#') 
      {
	 switch (*(++str)) 
         {
	 case 'W' :
	    for (; *weapon_plural; *(cp++) = *(weapon_plural++)) ;
	    break;
	 case 'w' :
	    for (; *weapon_singular; *(cp++) = *(weapon_singular++)) ;
	    break;
	 default :
	    *(cp++) = '#';
	    break;
	 }
      } 
      else
	*(cp++) = *str;

      *cp = 0;
   } /* For */

   return(buf);
}

// a better way? someday ill waste mind time on this... jtrhone
int get_dam_msgnum(int dam)
{
  int msgnum;

  if (dam == 0)	msgnum = 0;
  else if (dam <= 2)	msgnum = 1;
  else if (dam <= 4)	msgnum = 2;
  else if (dam <= 6)	msgnum = 3;
  else if (dam <= 8)	msgnum = 4;
  else if (dam <= 10)	msgnum = 5;
  else if (dam <= 12)	msgnum = 6;
  else if (dam <= 14)   msgnum = 7;
  else if (dam <= 16)   msgnum = 8;
  else if (dam <= 18)   msgnum = 9;
  else if (dam <= 20)   msgnum = 10;
  else if (dam <= 23)   msgnum = 11;
  else if (dam <= 26)   msgnum = 12;
  else if (dam <= 30)   msgnum = 13;
  else if (dam <= 34)   msgnum = 14;
  else if (dam <= 37)   msgnum = 15;
  else if (dam <= 41)   msgnum = 16;
  else if (dam <= 47)   msgnum = 17;

  else if (dam <= 53)   msgnum = 18;
  else if (dam <= 59)   msgnum = 19;
  else if (dam <= 65)   msgnum = 20;
  else if (dam <= 71)   msgnum = 21;
  else if (dam <= 77)	msgnum = 22;
  else if (dam <= 85)   msgnum = 23;
  else msgnum = 24;

  return msgnum;
}

// send a message to room/char/vict based on damage and weapon
void	dam_message(int dam, chdata *ch, chdata *victim, int w_type)
{
   obdata *wield;
   char	*buf;
   int msgnum;

   static struct dam_weapon_type {
      char	*to_room;
      char	*to_char;
      char	*to_victim;
   } dam_weapons[] = {

   /* use #w for singular (i.e. "slash") and #W for plural (i.e. "slashes") */

      { "$n misses $N with $s #w.",			/* 0: 0     */
      "You miss $N with your #w.",
      "$n misses you with $s #w." },

      { "$n tickles $N with $s #w.",			/* 1: 1..2  */
      "You tickle $N as you #w $M.",
      "$n tickles you as $e #W you." },

      { "$n barely #W $N.",				/* 2: 3..4  */
      "You barely #w $N.",
      "$n barely #W you." },

      { "$n #W $N.",					/* 3: 5..6  */
      "You #w $N.",
      "$n #W you." },

      { "$n nicks $N with $s #w.",                    /* 4: 7..8 */
      "You nick $N with your #w.",
      "$n nicks you with $s #w." },

      { "$n #W $N hard.",				/* 5: 9..10  */
      "You #w $N hard.",
      "$n #W you hard." },

      { "$n #W $N very hard.",				/* 6: 11..12  */
      "You #w $N very hard.",
      "$n #W you very hard." },

      { "$n #W $N extremely hard.", 			/* 7: 13..14  */
      "You #w $N extremely hard.",
      "$n #W you extremely hard." },

      { "$n dismembers $N with $s #w.",                    /* 8: 15..16 */
      "You dismember $N with your #w.",
      "$n dismembers you with $s #w." },

      { "$n shakes $N with $s #w.",                       /* 9: 17..18 */
      "You shake $N with your #w.",
      "$n shakes you with $s #w." },

      { "$n decomposes $N with $s #w.",                    /* 10: 19..20 */
      "You decompose $N with your #w.",
      "$n decomposes you with $s #w." },

      { "$n Maims $N with $s #w.",                        /* 11 : 21..23 */
      "You Maim $N with your #w.",
      "$n Maims you with $s #w." },

      { "$n Shreds $N with $s #w.",                        /* 12 : 24..26 */
      "You Shred $N with your #w.",
      "$n Shreds you with $s #w." },

      { "$n Massacres $N to small fragments with $s #w.", /* 13: 27..30 */
      "You Massacre $N to small fragments with your #w.",
      "$n Massacres you to small fragments with $s #w." },

      { "$n Obliterates $N with $s nasty #w!",	         /* 14: 31..34  */
      "You Obliterate $N with your nasty #w!",
      "$n Obliterates you with $s nasty #w!" }, 

      { "$n Implodes $N with $s nasty #w!",	         /* 15: 34..37  */
      "You Implode $N with your nasty #w!",
      "$n Implodes you with $s nasty #w!" }, 

     { "$n PULVERIZES $N with $s wicked #w!!",	         /* 16: 37..41  */
      "You PULVERIZE $N with your wicked #w!!",
      "$n PULVERIZES you with $s wicked #w!!" }, 

     { "$n DISINTEGRATES $N with $s deadly #w!!",         /* 17: 42..47  */
      "You DISINTEGRATE $N with your deadly #w!!",
      "$n DISINTEGRATES you with $s deadly #w!!" }, 

     { "$n %BLIQUIFIES%0 $N with $s deadly #w!!",         /* 18: 48..53  */
      "You %BLIQUIFY%0 $N with your deadly #w!!",
      "$n %BLIQUIFIES%0 you with $s deadly #w!!" }, 

     { "$n %BSUBLIMATES%0 $N with $s deadly #w!!",         /* 19: 54..59  */
      "You %BSUBLIMATE%0 $N with your deadly #w!!",
      "$n %BSUBLIMATES%0 you with $s deadly #w!!" }, 

     { "$n %5ANNIHILATES%0 $N with $s triumphant #w!!",         /* 20: 60..65  */
      "You %5ANNIHILATE%0 $N with your triumphant #w!!",
      "$n %5ANNIHILATES%0 you with $s triumphant #w!!" }, 

    { "$n %5ATOMIZES%0 $N with $s triumphant #w!!",         /* 21: 66.. 71  */
      "You %5ATOMIZE%0 $N with your triumphant #w!!",
      "$n %5ATOMIZES%0 you with $s triumphant #w!!" }, 

    { "$n %5EVAPORATES%0 $N with $s triumphant #w!!",         /* 22: 72..77  */
      "You %5EVAPORATE%0 $N with your triumphant #w!!",
      "$n %5EVAPORATES%0 you with $s triumphant #w!!" }, 

    { "$n %5ERADICATES%0 $N with $s triumphant #w!!",         /* 23: 78..85  */
      "You %5ERADICATE%0 $N with your triumphant #w!!",
      "$n %5ERADICATES%0 you with $s triumphant #w!!" }, 

    { "$n  %5%BG E T S   M E D I E V A L%0  on $N with $s triumphant #w!!", /* 24: >85  */
      "You  %5%BG E T   M E D I E V A L%0  on $N with your triumphant #w!!",
      "$n  %5%BG E T S   M E D I E V A L%0  on you with $s triumphant #w!!" } 

   };   

   w_type -= TYPE_HIT;   /* Change to base of table with text */
   wield = EQ(ch, W_WIELD);

   msgnum = get_dam_msgnum(dam);

   /* damage message to onlookers */
   if (wield && wield->weap_plur && wield->weap_sing && w_type == TYPE_BDEFINED - TYPE_HIT)
     buf = replace_string(dam_weapons[msgnum].to_room, wield->weap_sing, wield->weap_plur);
   else
     buf = replace_string(dam_weapons[msgnum].to_room, attack_hit_text[w_type].singular, attack_hit_text[w_type].plural);
   act(buf, FALSE, ch, wield, victim, TO_NOTVICT);

   /* damage message to damager */
   if (wield && wield->weap_plur && wield->weap_sing && w_type == TYPE_BDEFINED - TYPE_HIT)
     buf = replace_string(dam_weapons[msgnum].to_char, wield->weap_sing, wield->weap_plur);
   else
     buf = replace_string(dam_weapons[msgnum].to_char, attack_hit_text[w_type].singular, attack_hit_text[w_type].plural);
   act(buf, FALSE, ch, wield, victim, TO_CHAR);

   /* damage message to damagee */
  if (ch != victim)
  {
   if (wield && wield->weap_plur && wield->weap_sing && w_type == TYPE_BDEFINED - TYPE_HIT)
     buf = replace_string(dam_weapons[msgnum].to_victim, wield->weap_sing, wield->weap_plur);
   else
     buf = replace_string(dam_weapons[msgnum].to_victim, attack_hit_text[w_type].singular, attack_hit_text[w_type].plural);
   act(buf, FALSE, ch, wield, victim, TO_VICT);
  }
}

// send spell type message from the fight_messages[] array read in from disk
void spl_message(int dam, chdata *ch, chdata *vict, int spl)
{
  int i;
  struct message_type *m;

  for (i = 0; i < MAX_MESSAGES; i++)
  {
    if (fight_messages[i].a_type == spl)
    {
      m = locate_appropriate_mesg(i, GET_LEVEL(ch));

      if (IS_IMMORTAL(vict))
      {
        act(m->god_msg.attacker_msg, FALSE, ch, EQ(ch, W_WIELD), vict, TO_CHAR);
       if (ch != vict)	/* why send it twice? */
        act(m->god_msg.victim_msg, FALSE, ch, EQ(ch, W_WIELD), vict, TO_VICT);
        act(m->god_msg.room_msg, FALSE, ch, EQ(ch, W_WIELD), vict, TO_NOTVICT);
      }
      else
      if (dam)
      {
	if (GET_POS(vict) == POS_DEAD) 
        {
	  act(m->die_msg.attacker_msg, FALSE, ch, EQ(ch, W_WIELD), vict, TO_CHAR);
	 if (ch != vict)	/* why send it twice? */
	  act(m->die_msg.victim_msg, FALSE, ch, EQ(ch, W_WIELD), vict, TO_VICT);
	  act(m->die_msg.room_msg, FALSE, ch, EQ(ch, W_WIELD), vict, TO_NOTVICT);
        } 
        else 
        {
 	  act(m->hit_msg.attacker_msg, FALSE, ch, EQ(ch, W_WIELD), vict, TO_CHAR);
	 if (ch != vict)	/* why send it twice? */
 	  act(m->hit_msg.victim_msg, FALSE, ch, EQ(ch, W_WIELD), vict, TO_VICT);
 	  act(m->hit_msg.room_msg, FALSE, ch, EQ(ch, W_WIELD), vict, TO_NOTVICT);
        }
      }
      else
      if (!dam)
      {
        act(m->miss_msg.attacker_msg, FALSE, ch, EQ(ch, W_WIELD), vict, TO_CHAR);
        act(m->miss_msg.victim_msg, FALSE, ch, EQ(ch, W_WIELD), vict, TO_VICT);
        act(m->miss_msg.room_msg, FALSE, ch, EQ(ch, W_WIELD), vict, TO_NOTVICT);
      }
    }
  }
}

// use this to parse out the damage message to ppl, call from fight.c (damage())
void send_combat_messages(int dam, chdata *ch, chdata *vict, int spl)
{
  // if its normal weapon type hit
  if (spl >= TYPE_HIT && spl <= TYPE_BDEFINED)
  {
    if (!EQ(ch, W_WIELD))
      spl = TYPE_HIT;
    dam_message(dam, ch, vict, spl);
  }
  else
    spl_message(dam, ch, vict, spl);
}

// called from damage() in fight.c, will add randomness here soon
void send_position_messages(int dam, chdata *ch, chdata *victim)
{
   switch (GET_POS(victim)) {
   case POS_MORTALLYW:
      act("$n is mortally wounded, and will die soon, if not aided.", TRUE, victim, 0, 0, TO_ROOM);
      send_to_char("You are mortally wounded, and will die soon, if not aided.\n\r", victim);
      break;
   case POS_INCAP:
      act("$n is incapacitated and will slowly die, if not aided.", TRUE, victim, 0, 0, TO_ROOM);
      send_to_char("You are incapacitated an will slowly die, if not aided.\n\r", victim);
      break;
   case POS_STUNNED:
      act("$n is stunned, but will probably regain consciousness again.", TRUE, victim, 0, 0, TO_ROOM);
      send_to_char("You're stunned, but will probably regain consciousness again.\n\r", victim);
      break;
   case POS_DEAD:
// don't send this AND death message -roa
//      act("$n is dead!  R.I.P.", TRUE, victim, 0, 0, TO_ROOM);
      send_to_char("Darkness settles in as you breath your last breath...\n\r", victim);
      break;

   default:  /* >= POS SLEEPING */
      if (dam > (GET_MAX_HIT(victim) / 5))
	 act("%B%5AAAAAAHHHH%0!! That really %B%1HURT%0!!!", FALSE,
		victim, 0, 0, TO_CHAR);

      if (GET_HIT(victim) < (GET_MAX_HIT(victim) / 5)) 
      {
	 act("You are %1BLEEDING%0 seriously!", FALSE, victim, 0, 0, TO_CHAR);
	 if (MOB_FLAGGED(victim, MOB_WIMPY))
	   do_flee(victim, "", 0, 0);
      }

      if (IS_PC(victim) && WIMP_LEVEL(victim) && victim != ch && 
          GET_HIT(victim) <= WIMP_LEVEL(victim)) 
      {
	 send_to_char("You wimp out, and attempt to flee!\n\r", victim);
	 do_flee(victim, "", 0, 0);
      }
      break;
   }
}


// RoA CMOLC V0.1 BETA	Combat Message OnLine Creation version 0.1
// 			actual editting/creating/saving begins here

ACMD(do_mess_edit)
{
  char *argu = argument;
  int slot, cnt;

  skip_spaces(&argu);

  if (!*argu)
  {
    sprintf(buf, "Combat Messages\n\r---------------\n\r");
    for (cnt = slot = 0; slot<MAX_MESSAGES && fight_messages[slot].a_type>0; slot++)
    {
      if (fight_messages[slot].a_type < MAX_SPELLS)
        strcpy(buf2, skill_names[fight_messages[slot].a_type]);
      else
        strcpy(buf2, "Out of Range");

      sprintf(buf+strlen(buf), "(%%B%3d%%0) %%6%-15.15s%%0-%3d  ",slot, buf2,
	      fight_messages[slot].a_type);
      cnt++;
      if (!(cnt % 3))
        str_cat(buf, "\n\r", MAX_STRING_LENGTH, "do_mess_edit"); 
    }
    page_string(ch->desc, buf, 1);
    return;
  }

  if (!is_number(argu))
  {
    send_to_char("Usage: mesgedit < slot number >\n\r",ch);
    send_to_char("No arguments for a list of slot correlations.\n\r",ch);
    return;
  }

  slot = atoi(argu);
  if (slot < 0 || slot >= MAX_MESSAGES)
  {
    send_to_char("Usage: mesgedit < slot number >\n\r",ch);
    send_to_char("No arguments for a list of slot correlations.\n\r",ch);
    return;
  }

  SET_BIT(PLR_FLAGS(ch), PLR_BUILDING);
  ch->pc_specials->index 	 = slot;
  ch->pc_specials->index2 	 = 0;
  MENU_DEPTH(ch) 	 = 0;
  ch->pc_specials->field_changed = FALSE;
  menu_jump(ch, messedit_top_menu);
}

int count_mesgs(struct message_type *m)
{
  struct message_type *tm;
  int i;

  for (i = 0, tm = m; tm; tm = tm->next, i++)
   ;
 
  return i;
}

struct message_type *get_mesg(struct message_type *m, int num)
{
  int i;
  struct message_type *tm;

  if (!m || num <= 0 || num > count_mesgs(m))
    return NULL;

  for (i=1, tm=m; tm && i < num; i++, tm=tm->next)
   ;

  return tm;
}

// duplicate a another copy of orig into m
void dupe_mesg_over(struct message_type *orig, struct message_type *m)
{
  m->level = orig->level;
  FREENULL(m->die_msg.attacker_msg);
  m->die_msg.attacker_msg = str_dup(orig->die_msg.attacker_msg);
  FREENULL(m->die_msg.victim_msg);
  m->die_msg.victim_msg = str_dup(orig->die_msg.victim_msg);
  FREENULL(m->die_msg.room_msg);
  m->die_msg.room_msg = str_dup(orig->die_msg.room_msg);

  FREENULL(m->miss_msg.attacker_msg);
  m->miss_msg.attacker_msg = str_dup(orig->miss_msg.attacker_msg);
  FREENULL(m->miss_msg.victim_msg);
  m->miss_msg.victim_msg = str_dup(orig->miss_msg.victim_msg);
  FREENULL(m->miss_msg.room_msg);
  m->miss_msg.room_msg = str_dup(orig->miss_msg.room_msg);

  FREENULL(m->hit_msg.attacker_msg);
  m->hit_msg.attacker_msg = str_dup(orig->hit_msg.attacker_msg);
  FREENULL(m->hit_msg.victim_msg);
  m->hit_msg.victim_msg = str_dup(orig->hit_msg.victim_msg);
  FREENULL(m->hit_msg.room_msg);
  m->hit_msg.room_msg = str_dup(orig->hit_msg.room_msg);

  FREENULL(m->god_msg.attacker_msg);
  m->god_msg.attacker_msg = str_dup(orig->god_msg.attacker_msg);
  FREENULL(m->god_msg.victim_msg);
  m->god_msg.victim_msg = str_dup(orig->god_msg.victim_msg);
  FREENULL(m->god_msg.room_msg);
  m->god_msg.room_msg = str_dup(orig->god_msg.room_msg);
}

// confirm that user wants to quit editting a message block
void messedit_confirm_message_quit(chdata *ch, char *input_str)
{
  char buf[MAX_STRING_LENGTH];
  char *p;

  if (!input_str)
  {
    MENU_PROMPT(ch) = "Quit editting message block (yes/NO)? ";
    return;
  }

  strcpy(buf, input_str);
  p = strtok(buf, "   \n\r");
  if (p && strncasecmp("yes", p, strlen(p)) == 0)
    menu_jump(ch, messedit_confirm_message_save);
  else
    menu_jump(ch, messedit_edit_message);
}

// search for null strings in message block, fill with error messages
void validate_message(struct message_type *m)
{
  if(!m->die_msg.attacker_msg)
    m->die_msg.attacker_msg = str_dup("Invalid message.  Report immediately.");
  if(!m->die_msg.victim_msg)
    m->die_msg.victim_msg = str_dup("Invalid message.  Report immediately.");
  if(!m->die_msg.room_msg)
    m->die_msg.room_msg = str_dup("Invalid message.  Report immediately.");

  if(!m->miss_msg.attacker_msg)
    m->miss_msg.attacker_msg = str_dup("Invalid message.  Report immediately."); 
  if(!m->miss_msg.victim_msg)
    m->miss_msg.victim_msg = str_dup("Invalid message.  Report immediately."); 
  if(!m->miss_msg.room_msg)
    m->miss_msg.room_msg = str_dup("Invalid message.  Report immediately."); 

  if(!m->hit_msg.attacker_msg)
    m->hit_msg.attacker_msg = str_dup("Invalid message.  Report immediately."); 
  if(!m->hit_msg.victim_msg)
    m->hit_msg.victim_msg = str_dup("Invalid message.  Report immediately."); 
  if(!m->hit_msg.room_msg)
    m->hit_msg.room_msg = str_dup("Invalid message.  Report immediately."); 

  if(!m->god_msg.attacker_msg)
    m->god_msg.attacker_msg = str_dup("Invalid message.  Report immediately."); 
  if(!m->god_msg.victim_msg)
    m->god_msg.victim_msg = str_dup("Invalid message.  Report immediately."); 
  if(!m->god_msg.room_msg)
    m->god_msg.room_msg = str_dup("Invalid message.  Report immediately."); 
}

// if user wants to save, check format, dupe back over to actual, save file
// else free it up, menu_pop to messedit_top
void messedit_confirm_message_save(chdata *ch, char *input_str)
{
  char buf[MAX_STRING_LENGTH];
  char *p;
  int num = ch->pc_specials->index2;
  struct message_type *m = ch->pc_specials->msg_editted;
  struct message_type *orig, *targ;

  if (!input_str)
  {
    MENU_PROMPT(ch) = "Save editted message block (yes/NO)? ";
    return;
  }

  strcpy(buf, input_str);
  p = strtok(buf, "   \n\r");
  if (p && strncasecmp("yes", p, strlen(p)) == 0) // yes, save it
  {
    validate_message(m);
    orig = fight_messages[ch->pc_specials->index].msg;
    if (!(targ = get_mesg(orig, num)))
      send_to_char("Unable to find target message. Not saved.\n\r",ch);
    else
      dupe_mesg_over(m, targ); 
  }

  free_this_message(m);
  ch->pc_specials->msg_editted = NULL;
  ch->pc_specials->index2 = 0;

  // now pop back out to messedit_top menu
  menu_back(ch);
}
void wax_doubles(struct message_type *m)
{
  m->die_msg.attacker_msg = delete_doubledollar(m->die_msg.attacker_msg);
  m->die_msg.victim_msg = delete_doubledollar(m->die_msg.victim_msg);
  m->die_msg.room_msg = delete_doubledollar(m->die_msg.room_msg);
 
  m->miss_msg.attacker_msg = delete_doubledollar(m->miss_msg.attacker_msg);
  m->miss_msg.victim_msg = delete_doubledollar(m->miss_msg.victim_msg);
  m->miss_msg.room_msg = delete_doubledollar(m->miss_msg.room_msg);

  m->hit_msg.attacker_msg = delete_doubledollar(m->hit_msg.attacker_msg);
  m->hit_msg.victim_msg = delete_doubledollar(m->hit_msg.victim_msg);
  m->hit_msg.room_msg = delete_doubledollar(m->hit_msg.room_msg);

  m->god_msg.attacker_msg = delete_doubledollar(m->god_msg.attacker_msg);
  m->god_msg.victim_msg = delete_doubledollar(m->god_msg.victim_msg);
  m->god_msg.room_msg = delete_doubledollar(m->god_msg.room_msg);
}

void messedit_edit_message(chdata *ch, char *input_str)
{
  char *p;
  int field;
  char buf[MAX_STRING_LENGTH];
  struct message_type *m = ch->pc_specials->msg_editted;
  int slot = ch->pc_specials->index;

  if (!input_str)
  {
    menu_title_send("MessageEdit SubMenu", ch);

    if (fight_messages[slot].a_type < MAX_SPELLS)
      strcpy(buf2, skill_names[fight_messages[slot].a_type]);
    else
      strcpy(buf2, "Out of Range");

    wax_doubles(m);
    sprintf(buf, "%%B%%6Skill / Spell%%0: %%5%%B%s%%0\n\r", buf2);
    S2C();
    sprintf(buf, " 1.) %%6Message Level%%0: %%B%d%%0\n\r",m->level); S2C();
    sprintf(buf, " 2.) %%6Die Msg, to Attacker%%0:\n\r%s\n\r",m->die_msg.attacker_msg);
    S2C(); 
    sprintf(buf, " 3.) %%6Die Msg, to Victim  %%0:\n\r%s\n\r",m->die_msg.victim_msg);
    S2C(); 
    sprintf(buf, " 4.) %%6Die Msg, to Room    %%0:\n\r%s\n\r",m->die_msg.room_msg);
    S2C(); 

    sprintf(buf, " 5.) %%6Miss Msg, to Attacker%%0:\n\r%s\n\r",m->miss_msg.attacker_msg);
    S2C(); 
    sprintf(buf, " 6.) %%6Miss Msg, to Victim  %%0:\n\r%s\n\r",m->miss_msg.victim_msg);
    S2C(); 
    sprintf(buf, " 7.) %%6Miss Msg, to Room    %%0:\n\r%s\n\r",m->miss_msg.room_msg);
    S2C(); 

    sprintf(buf, " 8.) %%6Hit Msg, to Attacker%%0:\n\r%s\n\r",m->hit_msg.attacker_msg);
    S2C(); 
    sprintf(buf, " 9.) %%6Hit Msg, to Victim  %%0:\n\r%s\n\r",m->hit_msg.victim_msg);
    S2C(); 
    sprintf(buf, "10.) %%6Hit Msg, to Room    %%0:\n\r%s\n\r",m->hit_msg.room_msg);
    S2C(); 

    sprintf(buf, "11.) %%6God Msg, to Attacker%%0:\n\r%s\n\r",m->god_msg.attacker_msg);
    S2C(); 
    sprintf(buf, "12.) %%6God Msg, to Victim  %%0:\n\r%s\n\r",m->god_msg.victim_msg);
    S2C(); 
    sprintf(buf, "13.) %%6God Msg, to Room    %%0:\n\r%s\n\r",m->god_msg.room_msg);
    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, messedit_confirm_message_quit);
      break;

    case 1:
      GET_INTEGER_ARG(ch, "Enter min level of this combat message block: ", 
		      m->level, 0, LEV_IMPL);
      break;

    case 2:
      do_string_arg(ch, "Enter die msg to attacker:\n\r ", &m->die_msg.attacker_msg, "");
      break;
    case 3:
      do_string_arg(ch, "Enter die msg to victim:\n\r ", &m->die_msg.victim_msg, "");
      break;
    case 4:
      do_string_arg(ch, "Enter die msg to room:\n\r ", &m->die_msg.room_msg, "");
      break;

    case 5:
      do_string_arg(ch, "Enter miss msg to attacker:\n\r ", &m->miss_msg.attacker_msg, "");
      break;
    case 6:
      do_string_arg(ch, "Enter miss msg to victim:\n\r ", &m->miss_msg.victim_msg, "");
      break;
    case 7:
      do_string_arg(ch, "Enter miss msg to room:\n\r ", &m->miss_msg.room_msg, "");
      break;

    case 8:
      do_string_arg(ch, "Enter hit msg to attacker:\n\r ", &m->hit_msg.attacker_msg, "");
      break;
    case 9:
      do_string_arg(ch, "Enter hit msg to victim:\n\r ", &m->hit_msg.victim_msg, "");
      break;
    case 10:
      do_string_arg(ch, "Enter hit msg to room:\n\r ", &m->hit_msg.room_msg, "");
      break;

    case 11:
      do_string_arg(ch, "Enter god msg to attacker:\n\r ", &m->god_msg.attacker_msg, "");
      break;
    case 12:
      do_string_arg(ch, "Enter god msg to victim:\n\r ", &m->god_msg.victim_msg, "");
      break;
    case 13:
      do_string_arg(ch, "Enter god msg to room:\n\r ", &m->god_msg.room_msg, "");
      break;

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

void messedit_top_menu(chdata *ch, char *input_str)
{
  char *p;
  int i, num, field;
  char buf[MAX_STRING_LENGTH];
  struct message_type *m = fight_messages[ch->pc_specials->index].msg;
  struct message_type *tm, *ttm;
  int slot = ch->pc_specials->index;

  if (!input_str)
  {
    menu_title_send("MessageEdit Main Menu", ch);

    if (fight_messages[slot].a_type < MAX_SPELLS)
      strcpy(buf2, skill_names[fight_messages[slot].a_type]);
    else
      strcpy(buf2, "Out of Range");

    sprintf(buf, "\n%%5Skill / Spell%%0: %%B%%6%s%%0\n\r", buf2);
	    
    S2C();

    for (i = 1, tm = m; tm; tm = tm->next, i++) 
    {
      sprintf(buf, " %d.) %%6Message Lvl%%0: %2d\n\r",i, tm->level);
      S2C();
      if (tm->level == LEV_IMPL)
      {
	sprintf(buf, "%%B%%1Note: Message number %d must be completed.\n\r",i);
	S2C();
      }
    }

    send_to_char("\n10.) Add a message level...\n\r",ch);
    send_to_char("11.) Delete a message level...\n\r",ch);

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

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

  num = count_mesgs(m);

  // edit an existing one, depending on the number entered, ptr ch's editor to 
  // the correct one in the m list
  if (field > 0 && field <= num)
  {
    if (!(tm = get_mesg(m, field)))
    {
      GET_INTEGER_ARG(ch, "Not a valid message block... (hit return): ",
                            MENU_RETURN(ch), 0, 0);
      return;
    }

    // must create local copy so we dont alter original while
    // players are using them -roa
    CREATE(ch->pc_specials->msg_editted, struct message_type, 1);
    dupe_mesg_over(tm, ch->pc_specials->msg_editted);
    ch->pc_specials->index2 = field;	// save this for later reference

    menu_push(ch);
    MENU_HANDLER_ARG(ch) = (void *) 0;
    menu_jump(ch, messedit_edit_message);
    return;
  }
  else
  switch (field)
  {
    case 0:
      menu_jump(ch, messedit_confirm_quit);
      break;

    // if we create, dupe first message block over, find last message in list
    // point its next to this new block, new block next = NULL
    case 10:
      CREATE(tm, struct message_type, 1);
      if (m)
      {
        dupe_mesg_over(m, tm);
        ttm = get_mesg(m, num);
        ttm->next = tm;
        tm->next = NULL;
      }
      else	// empty msg block list, fill up new one
      {
	validate_message(tm);
	tm->next = NULL;
	fight_messages[ch->pc_specials->index].msg = tm;
      }
      tm->level = LEV_IMPL;
      send_to_char("%B%6Message added.%0\n\r",ch);
      (*MENU_HANDLER(ch))(ch, NULL);
      break;

    case 11:
      break;

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

void messedit_confirm_quit(chdata *ch, char *input_str) 
{       
    char buf[MAX_STRING_LENGTH];
    char *p;
      
    if (!input_str)
    {   
        MENU_PROMPT(ch) = "Quit editting messages (yes/NO)? ";
        return;
    }       
        
    strcpy(buf, input_str);
    p = strtok(buf, "   \n\r");
    if (p && strncasecmp("yes", p, strlen(p)) == 0)
    {
      sprintf(buf, "IMMUPD: %s editted message slot %d", GET_NAME(ch), 
		ch->pc_specials->index);
      mudlog(buf, CMP, MAX(LEV_IMM, GET_INVIS_LEV(ch)), TRUE);
      ch->pc_specials->index = 0;
      ch->pc_specials->index2 = 0;
      if (ch->pc_specials->msg_editted)
	free_this_message(ch->pc_specials->msg_editted);
      ch->pc_specials->msg_editted = NULL;
      MENU_PROMPT(ch) = NULL;
      MENU_HANDLER(ch) = NULL;
      MENU_DEPTH(ch) = 0;
      REMOVE_BIT(PLR_FLAGS(ch), PLR_BUILDING);
    }
    else
      menu_jump(ch, messedit_top_menu);
}