1stMud/CVS/
1stMud/area/CVS/
1stMud/backup/CVS/
1stMud/bin/
1stMud/bin/CVS/
1stMud/bin/extras/
1stMud/bin/extras/CVS/
1stMud/data/CVS/
1stMud/data/i3/CVS/
1stMud/doc/1stMud/
1stMud/doc/1stMud/CVS/
1stMud/doc/CVS/
1stMud/doc/Diku/
1stMud/doc/Diku/CVS/
1stMud/doc/MPDocs/CVS/
1stMud/doc/Merc/CVS/
1stMud/doc/Rom/
1stMud/doc/Rom/CVS/
1stMud/log/CVS/
1stMud/notes/
1stMud/notes/CVS/
1stMud/player/CVS/
1stMud/player/backup/CVS/
1stMud/player/deleted/CVS/
1stMud/src/CVS/
1stMud/src/config/CVS/
1stMud/src/h/CVS/
1stMud/src/o/CVS/
1stMud/win/CVS/
/**************************************************************************
*  Original Diku Mud copyright (C) 1990, 1991 by Sebastian Hammer,        *
*  Michael Seifert, Hans Henrik St{rfeldt, Tom Madsen, and Katja Nyboe.   *
*                                                                         *
*  Merc Diku Mud improvements copyright (C) 1992, 1993 by Michael         *
*  Chastain, Michael Quan, and Mitchell Tse.                              *
*                                                                         *
*  In order to use any part of this Merc Diku Mud, you must comply with   *
*  both the original Diku license in 'license.doc' as well the Merc       *
*  license in 'license.txt'.  In particular, you may not remove either of *
*  these copyright notices.                                               *
*                                                                         *
*  Much time and thought has gone into this software and you are          *
*  benefiting.  We hope that you share your changes too.  What goes       *
*  around, comes around.                                                  *
***************************************************************************
*       ROM 2.4 is copyright 1993-1998 Russ Taylor                        *
*       ROM has been brought to you by the ROM consortium                 *
*           Russ Taylor (rtaylor@hypercube.org)                           *
*           Gabrielle Taylor (gtaylor@hypercube.org)                      *
*           Brian Moore (zump@rom.org)                                    *
*       By using this code, you have agreed to follow the terms of the    *
*       ROM license, in the file Rom24/doc/rom.license                    *
***************************************************************************
*          1stMud ROM Derivative (c) 2001-2004 by Markanth                *
*            http://www.firstmud.com/  <markanth@firstmud.com>            *
*         By using this code you have agreed to follow the term of        *
*             the 1stMud license in ../doc/1stMud/LICENSE                 *
***************************************************************************/

#include "merc.h"
#include "tables.h"
#include "recycle.h"
#include "special.h"
#include "vnums.h"
#include "interp.h"

#define WAR_KILLED    	(BIT_A)
#define WAR_DECOY    	(BIT_B)

struct wars_t
{
  const char *name;
  const char *plural;
  war_types type;
};

const struct wars_t war_table[MAX_WAR] = {
  {"none", "none", WAR_NONE},
  {"clan", "clans", WAR_CLAN},
  {"race", "races", WAR_RACE},
  {"class", "classes", WAR_CLASS},
  {"genocide", "people", WAR_GENOCIDE},
  {"deity", "deities", WAR_DEITY},
  {"sex", "sexes", WAR_SEX}
};

Lookup_Fun (war_lookup)
{
  int i;

  for (i = WAR_NONE; i < MAX_WAR; i++)
    {
      if (is_number (name) ? atoi (name) == war_table[i].type
	  : !str_prefix (name, war_table[i].name))
	return i;
    }
  return -1;
}

char *
wartype_name (int type, bool plural)
{
  int i;

  for (i = 0; i < MAX_WAR; i++)
    if (war_table[i].type == type)
      return capitalize (!plural ? war_table[i].name : war_table[i].plural);

  return "Unknown";
}

#define GET_WAR_CLASS(ch) \
    	Range(0, ch->war->Class, top_class)

#define WAR_COST 3

bool
start_war (const char *n_fun, CharData * ch, const char *argument)
{
  char arg1[MIL], arg2[MIL];
  char arg3[MIL];
  CharData *warmaster = NULL;
  int blevel, elevel, type;

  for (warmaster = ch->in_room->person_first; warmaster != NULL;
       warmaster = warmaster->next_in_room)
    {
      if (!IsNPC (warmaster))
	continue;
      if (warmaster->spec_fun == spec_warmaster)
	break;
    }

  if (!IsImmortal (ch) && warmaster == NULL)
    {
      chprintln (ch, "You can't do that here.");
      return false;
    }

  if (!IsImmortal (ch) && warmaster->fighting != NULL)
    {
      chprintln (ch, "Wait until the fighting stops.");
      return false;
    }

  argument = one_argument (argument, arg1);
  argument = one_argument (argument, arg2);
  argument = one_argument (argument, arg3);

  if (NullStr (arg1) || NullStr (arg2) || NullStr (arg3))
    {
      int i;

      cmd_syntax (ch, NULL, n_fun, "start <min_level> <max_level> <type>",
		  NULL);
      chprintln (ch, "where <type> is either:");
      for (i = 1; war_table[i].name != NULL; i++)
	chprintlnf (ch, "%d - %s war", war_table[i].type, war_table[i].name);
      return false;
    }

  blevel = atoi (arg1);
  elevel = atoi (arg2);
  type = war_lookup (arg3);

  if (blevel <= 0 || blevel > MAX_LEVEL)
    {
      chprintlnf (ch, "Level must be between 1 and %d.", MAX_LEVEL);
      return false;
    }

  if (blevel <= 0 || elevel > MAX_LEVEL)
    {
      chprintlnf (ch, "Level must be between 1 and %d.", MAX_LEVEL);
      return false;
    }

  if (elevel < blevel)
    {
      chprintln (ch, "Max level must be greater than the min level.");
      return false;
    }

  if (elevel - blevel < 5)
    {
      chprintln (ch, "Levels must have a difference of at least 5.");
      return false;
    }

  if (type == -1)
    {
      int i;

      chprintln (ch, "The type either has to be:");
      for (i = 1; war_table[i].name != NULL; i++)
	chprintlnf (ch, "%d (%s)", war_table[i].type, war_table[i].name);
      return false;
    }

  if (war_info.status != WAR_OFF)
    {
      chprintln (ch, "There is already a war going!");
      return false;
    }

  if (!IsImmortal (ch))
    {
      if (ch->pcdata->trivia < WAR_COST)
	{

	  mob_tell (ch, warmaster,
		    "It costs %d Trivia Points to start a %s war.",
		    WAR_COST, wartype_name (type, false));
	  return false;
	}
      else
	{

	  mob_tell (ch, warmaster,
		    "Thank you %s, %s war started, you are %d trivia points lighter.",
		    ch->name, wartype_name (type, false), WAR_COST);
	}
    }

  war_info.status = WAR_WAITING;
  replace_str (&war_info.who, ch->name);
  war_info.min_level = blevel;
  war_info.max_level = elevel;
  war_info.wartype = war_table[type].type;
  announce (ch, INFO_WAR,
	    "$n announces a %s war for levels %d to %d.  Type 'WAR JOIN' to kill or be killed.",
	    wartype_name (war_info.wartype, false), war_info.min_level,
	    war_info.max_level);
  announce (ch, INFO_WAR | INFO_PRIVATE,
	    "You announce a %s war for levels %d to %d.  Type 'WAR JOIN' to kill or be killed.",
	    wartype_name (war_info.wartype, false), war_info.min_level,
	    war_info.max_level);
  war_info.timer = 3;
  mud_info.stats.wars++;
  return true;
}

void
auto_war (void)
{
  CharData *wch, *wch_last;
  static CharData *warmaster = NULL;
  int maxlvl = 0, minlvl = MAX_LEVEL, middle = MAX_MORTAL_LEVEL / 2;
  int clan = 0, count = 0, lbonus = 0, half = 0;
  int heros = 0;

  if (war_info.status != WAR_OFF)
    return;

  for (wch = player_first; wch != NULL; wch = wch->next_player)
    {
      if (!wch->desc)
	continue;

      if (!IsNPC (wch) && !IsImmortal (wch))
	{
	  count++;
	  maxlvl = Max (maxlvl, wch->level);
	  minlvl = Min (minlvl, wch->level);
	  if (wch->level >= LEVEL_HERO && wch->level <= MAX_MORTAL_LEVEL)
	    heros++;
	  if (is_clan (wch))
	    {
	      for (wch_last = player_first; wch_last != wch;
		   wch_last = wch_last->next_player)
		{
		  if (!IsNPC (wch_last)
		      && !IsImmortal (wch_last)
		      && is_clan (wch_last) && !is_same_clan (wch, wch_last))
		    clan++;
		}
	    }
	}
    }
  if (count < 2)
    {
      end_war ();
      return;
    }

  lbonus = number_range (15, 30);
  minlvl = Max (1, minlvl - lbonus);
  maxlvl = Min (MAX_MORTAL_LEVEL, maxlvl + lbonus);
  half = ((maxlvl - minlvl) / 2);
  middle = Range (minlvl, maxlvl - half, maxlvl);
  minlvl = Max (1, number_range (minlvl, (middle * 2) / 3));
  if (heros > 2 && number_percent () < 25)
    maxlvl = MAX_MORTAL_LEVEL;
  else
    maxlvl = Min (MAX_MORTAL_LEVEL, number_range ((middle * 3) / 2, maxlvl));
  if (warmaster == NULL)
    {
      for (warmaster = char_first; warmaster != NULL;
	   warmaster = warmaster->next)
	if (warmaster->pIndexData
	    && warmaster->pIndexData->vnum == MOB_VNUM_WARMASTER)
	  break;
    }
  war_info.status = WAR_WAITING;
  replace_str (&war_info.who,
	       (!warmaster ? "AutoWar" : warmaster->short_descr));
  war_info.min_level = minlvl;
  war_info.max_level = maxlvl;

  if (clan >= 2)
    war_info.wartype = (war_types) number_range (WAR_NONE + 1, MAX_WAR - 1);
  else
    war_info.wartype = (war_types) number_range (WAR_CLAN + 1, MAX_WAR - 1);

  if (war_info.wartype == WAR_NONE)
    war_info.wartype = WAR_GENOCIDE;

  announce (warmaster, INFO_WAR,
	    "%s %s war for levels %d to %d%s.  Type 'WAR JOIN' to kill or be killed.",
	    !warmaster ? "A" : "$n announces a",
	    wartype_name (war_info.wartype, false), war_info.min_level,
	    war_info.max_level, !warmaster ? " has started" : "");
  announce (warmaster, INFO_WAR | INFO_PRIVATE,
	    "You announce a %s war for levels %d"
	    " to %d.  Type 'WAR JOIN' to kill or be killed.",
	    wartype_name (war_info.wartype, false), war_info.min_level,
	    war_info.max_level);
  war_info.timer = 3;
  mud_info.stats.wars++;
}

void
end_war (void)
{
  WarData *wl, *wl_next;

  for (wl = warlist_first; wl != NULL; wl = wl_next)
    {
      wl_next = wl->next;

      if (wl->ch)
	{
	  stop_fighting (wl->ch, true);
	  if (!IsSet (wl->flags, WAR_DECOY))
	    {
	      if (!IsSet (wl->flags, WAR_KILLED))
		{
		  char_from_room (wl->ch);
		  char_to_room (wl->ch, get_room_index (ROOM_VNUM_TEMPLE));
		}
	      wl->ch->hit = wl->hit;
	      wl->ch->mana = wl->mana;
	      wl->ch->move = wl->move;
	      update_pos (wl->ch);
	      do_function (wl->ch, &do_look, "auto");
	    }
	}
      UnLink (wl, warlist, next, prev);
      free_warlist (wl);
    }
  if (war_info.status == WAR_RUNNING && !NullStr (war_info.who))
    {
      CharData *vch;

      for (vch = player_first; vch; vch = vch->next_player)
	{
	  if (!IsImmortal (vch) && !str_cmp (GetName (vch), war_info.who))
	    {
	      chprintlnf (vch,
			  "War not started, you are being refunded %d TP.",
			  WAR_COST);
	      vch->pcdata->trivia += WAR_COST;
	      break;
	    }
	}
    }
  war_info.wartype = WAR_NONE;
  war_info.min_level = 0;
  war_info.max_level = 0;
  war_info.status = WAR_OFF;
  war_info.inwar = 0;
  war_info.timer = number_range (100, 200);
  warlist_first = warlist_last = NULL;
}

const char *
wartype_info (CharData * wch)
{
  CharData *ch;

  ch = (wch->war->owner ? wch->war->owner : wch);

  switch (war_info.wartype)
    {
    default:
      return "";
    case WAR_RACE:
      return ch->race->name;
    case WAR_CLASS:
      return ClassName (ch, GET_WAR_CLASS (ch));
    case WAR_GENOCIDE:
      return ch->name;
    case WAR_CLAN:
      return CharClan (ch)->who_name;
    case WAR_DEITY:
      return ch->deity->name;
    case WAR_SEX:
      return flag_string (sex_flags, ch->sex);
    }
}

bool
check_wartype_data (CharData * wch, CharData * vict)
{
  CharData *ch = (wch->war->owner ? wch->war->owner : wch);
  CharData *victim = (vict->war->owner ? vict->war->owner : vict);

  switch (war_info.wartype)
    {
    default:
      return false;
    case WAR_RACE:
      return (ch->race == victim->race);
    case WAR_CLASS:
      return (GET_WAR_CLASS (ch) == GET_WAR_CLASS (victim));
    case WAR_GENOCIDE:
      return (ch->id == victim->id);
    case WAR_CLAN:
      return (CharClan (ch) == CharClan (victim));
    case WAR_DEITY:
      return (ch->deity == victim->deity);
    case WAR_SEX:
      return (ch->sex == victim->sex);
    }
}

char *
warrior_status (CharData * ch)
{
  if (war_info.wartype != WAR_GENOCIDE)
    return FORMATF ("%s (%s, Lvl %d)", ch->name, wartype_info (ch),
		    ch->level);
  else
    return FORMATF ("%s (Lvl %d)", ch->name, ch->level);
}

int
count_type_in_war (CharData * join)
{
  WarData *wl;
  int count = 0;

  for (wl = warlist_first; wl != NULL; wl = wl->next)
    {
      if (IsSet (wl->flags, WAR_DECOY) || !wl->ch || wl->ch == join)
	continue;

      if (check_wartype_data (wl->ch, join))
	count++;
    }
  return count;
}

Do_Fun (do_war)
{
  char arg[MIL];
  RoomIndex *location;
  int i = 0;

  if (IsNPC (ch))
    {
      chprintln (ch, "Mobiles not supported yet.");
      return;
    }

  argument = one_argument (argument, arg);

  if (NullStr (arg))
    {
      cmd_syntax (ch, NULL, n_fun, "start <minlev> <maxlev> <#type>",
		  "talk <msg>", "status", "info", "join", "decoy", NULL);
      if (IsImmortal (ch))
	cmd_syntax (ch, NULL, n_fun, "next", "end", NULL);
      return;
    }
  else if (!str_cmp (arg, "start"))
    {
      if (ch->pcdata->trivia < WAR_COST && !IsImmortal (ch))
	{
	  chprintlnf (ch,
		      "It costs %d Trivia Points to start a war.", WAR_COST);
	  return;
	}
      if (start_war (n_fun, ch, argument) && !IsImmortal (ch))
	ch->pcdata->trivia -= WAR_COST;
      return;
    }
  else if (!str_cmp (arg, "talk"))
    {
      war_talk (ch, argument);
      return;
    }
  else if (!str_cmp (arg, "next") && IsImmortal (ch))
    {
      if (war_info.status != WAR_OFF)
	{
	  chprintln (ch, "Not while a war is running.");
	  return;
	}

      i = is_number (argument) ? atoi (argument) : number_range (30, 100);
      war_info.timer = i;
      chprintlnf (ch, "The next war will start in %s.",
		  intstr (war_info.timer, "minute"));
      return;
    }

  if (war_info.status == WAR_OFF)
    {
      chprintf (ch,
		"There is no war going! The next war will start in %s.",
		intstr (war_info.timer, "minute"));
      return;
    }

  if (!str_cmp (arg, "end") && IsImmortal (ch))
    {
      end_war ();
      announce (ch, INFO_WAR,
		"$n has ended the war. The next autowar will start in %s.",
		intstr (war_info.timer, "minute"));
      announce (ch, INFO_WAR | INFO_PRIVATE,
		"You have ended the war. The next autowar will start in %s.",
		intstr (war_info.timer, "minute"));
      return;
    }
  else if (!str_cmp (arg, "info"))
    {
      chprintln (ch, stringf (ch, 0, Center, "{g-{G-", "{W[ WAR INFO ]"));
      chprintlnf (ch, "{RStarted by  : {W%s",
		  GetStr (war_info.who, "Unknown"));
      chprintlnf (ch, "{RFighting    : {W%s.",
		  intstr (war_info.inwar, "player"));
      chprintlnf (ch, "{RLevels      : {W%d - %d{x", war_info.min_level,
		  war_info.max_level);
      chprintlnf (ch, "{RStatus      : {W%s for %s.{x",
		  war_info.status == WAR_WAITING ? "Waiting" : "Running",
		  intstr (war_info.timer, "minute"));
      chprintlnf (ch, "{RType        : {W%s war.{x",
		  wartype_name (war_info.wartype, false));
      chprintln (ch, draw_line (ch, "{g-{G-", 0));
      return;
    }
  else if (!str_cmp (arg, "status"))
    {
      WarData *wl;
      bool found = false;

      chprintln (ch, stringf (ch, 0, Center, "{g-{G-",
			      "{W[ WAR COMBATENTS ]"));
      for (wl = warlist_first; wl != NULL; wl = wl->next)
	{
	  if (!IsSet (wl->flags, WAR_DECOY) && wl->ch)
	    {
	      if (!IsSet (wl->flags, WAR_KILLED))
		chprintlnf (ch,
			    "{W%-25s : [{R%ld%% hit{W] [{M%ld%% mana{W] [Pos: {G%s{W]{x",
			    warrior_status (wl->ch),
			    Percent (wl->ch->hit, wl->ch->max_hit),
			    Percent (wl->ch->mana, wl->ch->max_mana),
			    position_flags[wl->ch->position].name);
	      else
		chprintlnf (ch, "{W%-25s [{RKILLED{W]{x",
			    warrior_status (wl->ch));
	      found = true;
	    }
	}
      if (!found)
	chprintln (ch, "No one in the war yet.");
      chprintln (ch, draw_line (ch, "{g-{G-", 0));
      return;
    }
  else if (!str_cmp (arg, "decoy"))
    {
      WarData *wl;
      CharData *dc;
      int count = 0;

      if (war_info.status != WAR_RUNNING)
	{
	  chprintln (ch, "Wait untill the war starts.");
	  return;
	}

      if (!InWar (ch))
	{
	  chprintln (ch, "You aren't in the war.");
	  return;
	}

      for (wl = warlist_first; wl; wl = wl->next)
	{
	  if (!IsSet (wl->flags, WAR_DECOY))
	    continue;

	  if (wl->owner != ch)
	    continue;

	  count++;
	}

      if (count >= 5)
	{
	  chprintln (ch,
		     "I'm sorry you are only allowed to deploy 5 decoys.");
	  return;
	}

      if ((dc = create_mobile (get_char_index (MOB_VNUM_DUMMY))) == NULL)
	{
	  chprintln (ch, "Opps, seems there was a problem creating a decoy!");
	  return;
	}

      replace_str (&dc->name, ch->name);
      replace_strf (&dc->short_descr, "%s's Decoy", ch->name);
      replace_strf (&dc->long_descr, "%s%s is here." NEWLINE, ch->name,
		    ch->pcdata->title);
      replace_str (&dc->description, ch->description);
      dc->affected_by = ch->affected_by;
      dc->level = ch->level;
      dc->sex = ch->sex;
      dc->race = ch->race;
      memcpy (dc->Class, ch->Class, MAX_MCLASS);
      dc->deity = ch->deity;
      dc->hit = ch->hit;
      dc->max_hit = ch->max_hit;
      char_to_room (dc, ch->in_room);
      dc->war = new_warlist ();
      dc->war->hit = ch->hit;
      dc->war->mana = ch->mana;
      dc->war->move = ch->move;
      dc->war->flags = WAR_DECOY;
      dc->war->ch = dc;
      dc->war->owner = ch;
      Link (dc->war, warlist, next, prev);
      chprintln (ch, "A decoy of yourself suddenly appears in the room.");
      return;
    }
  else if (!str_cmp (arg, "join"))
    {
      int iClass = -1;

      if (ch->fighting != NULL)
	{
	  chprintln (ch, "You're a little busy right now.");
	  return;
	}

      if (war_info.status == WAR_RUNNING)
	{
	  chprintln (ch, "The war has already started, your too late.");
	  return;
	}

      if (ch->level < war_info.min_level || ch->level > war_info.max_level)
	{
	  chprintln (ch, "Sorry, you can't join this war.");
	  return;
	}

      if (ch->war != NULL)
	{
	  chprintln (ch, "You are already in the war.");
	  return;
	}
      if (IsQuester (ch) || Gquester (ch))
	{
	  chprintln (ch, "What? And leave your quest?");
	  return;
	}

      if (IsSet (ch->in_room->room_flags, ROOM_NO_RECALL))
	{
	  chprintln (ch, "Something prevents you from leaving.");
	  return;
	}

      if (war_info.wartype == WAR_CLAN && !is_clan (ch))
	{
	  chprintln (ch, "You aren't in a clan, you can't join this war.");
	  return;
	}

      if (war_info.wartype == WAR_CLASS)
	{
	  if (IsRemort (ch))
	    {
	      if (NullStr (argument))
		{
		  cmd_syntax (ch, NULL, n_fun, "join <class>", NULL);
		  chprintln (ch, "Which class do you want to go to war as?");
		  return;
		}

	      if ((iClass = class_lookup (argument)) == -1)
		{
		  chprintln (ch, "That is not a class.");
		  return;
		}

	      if (!is_class (ch, iClass))
		{
		  chprintlnf (ch, "You are not part %s!",
			      ClassName (ch, iClass));
		  return;
		}
	    }
	}

      ch->war = new_warlist ();
      ch->war->hit = ch->hit;
      ch->war->mana = ch->mana;
      ch->war->move = ch->move;
      if (IsNPC (ch))
	ch->war->flags = WAR_DECOY;
      else
	ch->war->flags = 0;
      ch->war->ch = ch;
      Link (ch->war, warlist, next, prev);
      ch->war->Class = iClass;

      if (war_info.inwar > 2 && war_info.wartype != WAR_GENOCIDE)
	{
	  if (count_type_in_war (ch) > war_info.inwar - 2)
	    {
	      chprintlnf
		(ch,
		 "%s is already represented well enough in this war. Try again later.",
		 wartype_info (ch));
	      UnLink (ch->war, warlist, next, prev);
	      free_warlist (ch->war);
	      return;
	    }
	}

      if ((location = get_room_index (ROOM_VNUM_WAITROOM)) == NULL)
	{
	  chprintln (ch, "Arena is not yet completed, sorry.");
	  UnLink (ch->war, warlist, next, prev);
	  free_warlist (ch->war);
	  return;
	}
      else
	{
	  act ("$n goes to get $s ass whipped in war!", ch, NULL,
	       NULL, TO_ROOM);
	  char_from_room (ch);
	  char_to_room (ch, location);
	  war_info.inwar++;
	  announce (NULL, INFO_WAR, "%s joins the war!", warrior_status (ch));
	  act ("$n arrives to get $s ass whipped!", ch, NULL, NULL, TO_ROOM);
	  do_function (ch, &do_look, "auto");
	}
      return;
    }
  do_war (n_fun, ch, "");
  return;
}

bool
abort_war (void)
{
  WarData *cwl, *vwl;

  for (cwl = warlist_first; cwl != NULL; cwl = cwl->next)
    {
      if (IsSet (cwl->flags, WAR_KILLED | WAR_DECOY))
	continue;

      for (vwl = warlist_first; vwl != NULL; vwl = vwl->next)
	{
	  if (IsSet (vwl->flags, WAR_KILLED | WAR_DECOY))
	    continue;

	  if (!check_wartype_data (cwl->ch, vwl->ch))
	    return false;
	}
    }
  return true;
}

void
note_war (CharData * ch)
{
  Buffer *output;
  char sender[MIL], subject[MIL];
  WarData *wl;

  if (war_info.status != WAR_RUNNING)
    return;

  output = new_buf ();
  bprintln (output, "{WWAR INFO{g" NEWLINE "--------{x");
  bprintlnf (output, "{RStarted by  : {W%s",
	     GetStr (war_info.who, "AutoWar"));
  bprintlnf (output, "{RLevels      : {W%d - %d{x", war_info.min_level,
	     war_info.max_level);
  bprintlnf (output, "{RType        : {W%s war.{x",
	     wartype_name (war_info.wartype, false));
  bprintln (output, "{WWAR COMBATENTS{g" NEWLINE "--------------{x");
  for (wl = warlist_first; wl != NULL; wl = wl->next)
    {
      if (IsSet (wl->flags, WAR_DECOY) || !wl->ch)
	continue;

      bprintlnf (output, "{W%s{x", warrior_status (wl->ch));
    }
  bprintln (output, "{g--------------{x");
  switch (war_info.wartype)
    {
    case WAR_RACE:
    case WAR_CLASS:
      bprintlnf (output, "{WThe {R%s's{W won this war.{x", wartype_info (ch));
      break;
    default:
      bprintlnf (output, "{R%s{W won this war.{x", wartype_info (ch));
      break;
    }

  sprintf (subject, "War Info %s", str_time (current_time, -1, NULL));
  sprintf (sender, "%s", GetStr (war_info.who, "AutoWar"));
  make_note ("Games", sender, "All", subject, 15, buf_string (output));
  free_buf (output);
  return;
}

void
war_update (void)
{
  switch (war_info.status)
    {
    case WAR_OFF:
      if (war_info.timer > 0 && --war_info.timer == 0)
	auto_war ();
      break;

    case WAR_WAITING:
      if (--war_info.timer > 0)
	{
	  announce (NULL, INFO_WAR,
		    "%s left to join the war. (Levels %d - %d, %s War)",
		    intstr (war_info.timer, "minute"),
		    war_info.min_level, war_info.max_level,
		    wartype_name (war_info.wartype, false));
	}
      else
	{
	  if (war_info.inwar < 2)
	    {
	      end_war ();
	      announce (NULL, INFO_WAR, "Not enough people for war.");
	    }
	  else if (abort_war ())
	    {
	      announce (NULL, INFO_WAR, "Not enough %s for war.",
			wartype_name (war_info.wartype, true));
	      end_war ();
	    }
	  else
	    {
	      WarData *wl;

	      announce (NULL, INFO_WAR,
			"The battle begins! %s are fighting!",
			intstr (war_info.inwar, "player"));
	      war_info.timer =
		number_range (3 * war_info.inwar, 5 * war_info.inwar);
	      war_info.status = WAR_RUNNING;
	      for (wl = warlist_first; wl != NULL; wl = wl->next)
		{
		  vnum_t randm = number_range (ROOM_VNUM_WAR_START,

					       ROOM_VNUM_WAR_END);

		  char_from_room (wl->ch);
		  char_to_room (wl->ch, get_room_index (randm));
		  do_function (wl->ch, &do_look, "auto");
		}
	    }
	}
      break;

    case WAR_RUNNING:
      if (war_info.inwar == 0)
	{
	  end_war ();
	  announce (NULL, INFO_WAR, "No one left in the war");
	  return;
	}

      switch (war_info.timer)
	{
	case 0:
	  end_war ();
	  announce (NULL, INFO_WAR, "Time has run out!");
	  return;
	case 1:
	case 2:
	case 3:
	case 4:
	case 5:
	case 10:
	case 15:
	  announce (NULL, INFO_WAR,
		    "%s remaining in the war.",
		    intstr (war_info.timer, "minute"));
	default:
	  war_info.timer--;
	  break;
	}
      break;
    }
}

void
check_war (CharData * ch, CharData * victim)
{
  WarData *wl, *wl_next;

  if (war_info.status == WAR_OFF)
    return;

  if (!InWar (ch) || !InWar (victim))
    return;

  war_info.inwar--;
  stop_fighting (victim, true);
  stop_fighting (ch, true);
  if (IsNPC (victim))
    {
      act ("$n disappears suddenly.", victim, NULL, NULL, TO_ROOM);
      UnLink (victim->war, warlist, next, prev);
      free_warlist (victim->war);
      extract_char (victim, true);
      return;
    }
  char_from_room (victim);
  char_to_room (victim, get_room_index (ROOM_VNUM_TEMPLE));
  for (wl = warlist_first; wl; wl = wl_next)
    {
      wl_next = wl->next;

      if (!IsSet (wl->flags, WAR_DECOY))
	continue;

      if (wl->owner == victim)
	{
	  act ("$n dissapears suddenly.", wl->ch, NULL, NULL, TO_ROOM);
	  UnLink (wl, warlist, next, prev);
	  free_warlist (wl);
	}
    }
  chprintln (ch, NULL);
  victim->hit = victim->war->hit;
  victim->mana = victim->war->mana;
  victim->move = victim->war->move;
  SetBit (victim->war->flags, WAR_KILLED);
  update_pos (victim);
  do_function (victim, &do_look, "auto");
  announce (NULL, INFO_WAR, "%s was killed in combat by %s!{x",
	    GetName (victim), GetName (ch));

  if (abort_war ())
    {
      int reward;
      int qreward;

      switch (war_info.wartype)
	{
	case WAR_RACE:
	case WAR_CLASS:
	  announce (NULL, INFO_WAR, "The %s's have won the war!",
		    wartype_info (ch));
	  break;
	default:
	  announce (NULL, INFO_WAR, "%s has won the war!", wartype_info (ch));
	  break;
	}
      note_war (ch);
      reward = number_range (500, 1500);
      qreward = number_range (50, 150);
      for (wl = warlist_first; wl != NULL; wl = wl->next)
	{
	  if (IsSet (wl->flags, WAR_DECOY))
	    continue;

	  if (check_wartype_data (wl->ch, ch))
	    {
	      add_cost (wl->ch, reward, VALUE_GOLD);
	      qreward = add_qp (wl->ch, qreward);
	      chprintf (wl->ch,
			"You recieve %d gold and %d questpoints from the war tribunal!",
			reward, qreward);
	    }
	}
      end_war ();
      return;
    }
  return;
}

bool
is_safe_war (CharData * ch, CharData * wch)
{
  if (war_info.status == WAR_OFF)
    return false;

  if (!InWar (ch) || !InWar (wch))
    return false;

  return check_wartype_data (ch, wch);
}

void
war_talk (CharData * ch, const char *argument)
{
  Descriptor *d;

  if (NullStr (argument))
    {
      chprintlnf (ch,
		  "Wartalk about what?" NEWLINE
		  "Use '%s war' to toggle this channel.", cmd_name (do_info));
      return;
    }

  chprintlnf (ch, "{Y({RWarTalk{Y) {gYou drum: %s{x", argument);

  for (d = descriptor_first; d != NULL; d = d->next)
    {
      CharData *victim;

      if (d->connected == CON_PLAYING && (victim = CH (d)) != ch
	  && !IsSet (victim->info_settings, INFO_WAR)
	  && !IsSet (victim->comm, COMM_QUIET)
	  && !is_ignoring (ch, victim->name, IGNORE_CHANNELS))
	{
	  chprintlnf (victim, "{Y({RWarTalk{Y) {g%s drums: %s{x",
		      Pers (ch, victim), argument);
	}
    }
  return;
}

void
extract_war (CharData * ch)
{
  if (war_info.status != WAR_OFF && ch->war != NULL)
    {
      war_info.inwar--;
      if (war_info.status == WAR_RUNNING)
	{
	  if (war_info.inwar == 0 || war_info.inwar == 1)
	    {
	      announce (ch, INFO_WAR, "$n has left. War over.");
	      end_war ();
	    }
	  if (abort_war ())
	    {
	      announce (ch, INFO_WAR, "$n has left. War over.");
	      end_war ();
	    }
	  else
	    {
	      announce (ch, INFO_WAR,
			"$n has left. %s in the war.",
			intstr (war_info.inwar, "player"));
	    }
	}
      char_from_room (ch);
      char_to_room (ch, get_room_index (ROOM_VNUM_TEMPLE));
      UnLink (ch->war, warlist, next, prev);
      free_warlist (ch->war);
    }
}