FlCodebase3.1/
FlCodebase3.1/bounty/
FlCodebase3.1/challenge/
FlCodebase3.1/clans/
FlCodebase3.1/gods/
FlCodebase3.1/mobprogs/
FlCodebase3.1/player/
FlCodebase3.1/savemud/
/***************************************************************************
 *  Original Diku Mud copyright (C) 1990, 1991 by Sebastian Hammer,        *
 *  Michael Seifert, Hans Henrik St{rfeldt, Tom Madsen, and Katja Nyboe.   *
 *                                                                         *
 *  Merc Diku Mud improvments copyright (C) 1992, 1993 by Michael          *
 *  Chastain, Michael Quan, and Mitchell Tse.                              *
 *                                                                         *
 *  In order to use any part of this Envy Diku Mud, you must comply with   *
 *  the original Diku license in 'license.doc', the Merc license in        *
 *  'license.txt', as well as the Envy license in 'license.nvy'.           *
 *  In particular, you may not remove either of these copyright notices.   *
 *                                                                         *
 *  Much time and thought has gone into this software and you are          *
 *  benefitting.  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                     *
 *                                                                         *
 * Code Adapted and Improved by Abandoned Realms Mud                       *
 * and Aabahran: The Forsaken Lands Mud by Virigoth                        *
 *                                                                         *
 * Continued Production of this code is available at www.flcodebase.com    *
 ***************************************************************************/

#include <sys/types.h>
#include <ctype.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <time.h>
#include <malloc.h>
#include "merc.h"
#include "misc.h"
#include "olc.h"
#include "recycle.h"
#include "armies.h"
#include "interp.h"
#include "cabal.h"

/***************************************************************/
/*Following are army orientanted routines for Forsaken Lands  */
/*mud created by Virigoth circa Oct 2003.  Copyrighted for Forsaken*/
/*Lands mud Oct 01, 2003.  Do not use or copy without explicit */
/*permission of author. (Voytek Plawny aka Virigoth)	       */
/***************************************************************/
const struct state_table_s state_table [] = {
  /*	name		bit			       life	*/
    {  "idle",		AS_NONE,			0	},
    {  "garrison",	AS_GARR,			0	},
    {  "fortify",       AS_FORT,			0	},
    {  "patrol",        AS_PATR,			11	},
    //attack causes combat from attack spot to defender
    {  "attack",	AS_ATTA,			0	},
    {  "move",		AS_MOVE,			5	},
    {  "return",	AS_RETU,			5	},
    {  "leaving",	AS_LEAV,			0	},
    //Shadow waits untill no attackers in room
    {  "circling",	AS_SHAD,			11	}, 
    {  "fighting",	AS_FIGH,			0	},
    {  "disbanding",	AS_DISB,			0	},
    //Defend causes combat from defender spot to attacker
    {  "defending",	AS_DEFE,			0	},
    //Circle waits untill defender spot open in room
    {  "circling",	AS_CIRC,			11	},
    {  "besieged",	AS_BESE,			0	},
    {  NULL,            0,				0	}
};

extern char *fix_string( const char *str );
extern int top_area;

static ARMY_INDEX_DATA army_index_list;
static int top_army_index;

static ARMY_DATA* army_list[ARMY_HASH_SIZE];
static int top_army;

static int	free_army_ids[MAX_ARMY_ID];
static int	last_army_id = 0;


/* PROTOTYPES */ 
void show_armies( CHAR_DATA* ch, CABAL_DATA* pc, char* name );
void show_area_armies(CHAR_DATA* ch,AREA_DATA* area, CABAL_DATA* pCabal);
int* gen_vars( int v0, int v1, int v2 );
int set_army_path( ARMY_DATA* pa, int src_vnum, int dest_vnum );
int count_attackers( ROOM_INDEX_DATA* room );
int count_defenders( ROOM_INDEX_DATA* room, bool fCountBastion );
int get_army_support( ARMY_DATA* pa, AREA_DATA* area );
void army_ai( ARMY_DATA* pa );

TOWER_DATA* get_tower_data( CABAL_DATA* pCabal, AREA_DATA* area );
void tower_from_area( CABAL_DATA* pCabal, AREA_DATA* area );
TOWER_DATA* tower_to_area( CABAL_DATA* pCabal, AREA_DATA* area, bool fSilent );
void reinforce_area( CABAL_DATA* pCabal, AREA_DATA* area );
bool unreinforce_area( CABAL_DATA* pCabal, AREA_DATA* area, bool fShort);

void show_report_q(REPORT_QUEUE* q, CHAR_DATA* ch );
void show_world_armies( CHAR_DATA* ch, CABAL_DATA* pc );

ARMY_DATA* get_bastion(ROOM_INDEX_DATA* room );
ARMY_DATA* get_attack_target( CABAL_DATA* pc, ROOM_INDEX_DATA* pRoom );
ARMY_DATA* get_defend_target( CABAL_DATA* pc, ROOM_INDEX_DATA* pRoom );

void stop_attacking( ARMY_DATA* pa );

//army actions
bool army_garrison( ARMY_DATA* pa);
bool army_return( ARMY_DATA* pa );
bool army_patrol( ARMY_DATA* pa );
bool army_fortify( ARMY_DATA* pa );
bool army_attack( ARMY_DATA* pa );
bool army_defend( ARMY_DATA* pa );
bool army_move( ARMY_DATA* pa );
bool army_leave( ARMY_DATA* pa );
bool army_intercept( ARMY_DATA* pa );

//checks for army actions
bool can_bastion(CHAR_DATA* ch,  ARMY_DATA* pa, ROOM_INDEX_DATA* in_room, CABAL_DATA* pc, bool fQuiet );
bool can_attack(CHAR_DATA* ch,  ARMY_DATA* pa, ROOM_INDEX_DATA* in_room, CABAL_DATA* pc, bool fQuiet );
bool can_defend(CHAR_DATA* ch,  ARMY_DATA* pa, ROOM_INDEX_DATA* in_room, CABAL_DATA* pc, bool fQuiet );
bool can_fortify(CHAR_DATA* ch,  ROOM_INDEX_DATA* in_room, CABAL_DATA* pc, bool fQuiet );
bool can_patrol(CHAR_DATA* ch,  ARMY_DATA* pa, ROOM_INDEX_DATA* in_room, CABAL_DATA* pc, bool fQuiet );
bool can_return(CHAR_DATA* ch,  ROOM_INDEX_DATA* in_room, ARMY_DATA* pa, bool fQuiet );



void order_army( ARMY_DATA* pa, sh_int state, int* vars );
/* NEW */

//PRIVATE FUNCTIONS
/* gets the next free army id */
int get_army_id(){
  static int top_army_id;
  int id = 0;

  /* check if there are any unused ones waiting */
  if (last_army_id > 0){
    id = free_army_ids[--last_army_id];
  }
  else{
    id = ++top_army_id;
  }
  return id;
}

/* records an army id to be reused */
void free_army_id( int id ){
  if (last_army_id < MAX_ARMY_ID)
    free_army_ids[last_army_id++] = id;
}

static ARMY_DATA* army_free;
ARMY_DATA* new_army(){
  ARMY_DATA *pa;

  if (army_free == NULL){
    pa = alloc_mem( sizeof( ARMY_DATA ));
  }
  else{
    pa = army_free;
    army_free = army_free->next;
  }
  memset(pa, 0, sizeof(*pa));

  //set id
  pa->ID	= get_army_id();
  //set commander to default
  pa->commander = str_dup("none");
  pa->next = NULL;

  return pa;
}

void free_army( ARMY_DATA* pa ){
  
  //free path if any
  if (pa->path)
    clean_path_queue( pa->path );
  //free id
  free_army_id( pa->ID );

  //Free commander name
  if (!IS_NULLSTR(pa->commander))
    free_string( pa->commander );
  pa->attacking = NULL;

  pa->next = army_free;
  army_free = pa;
}

void add_army( ARMY_DATA* pa ){
  word hash = ARMY_HASHVAL(pa);

  /* add army onto the hash index for quick lookup */
  pa->next = army_list[hash];
  army_list[hash] = pa;

  top_army++;

}

void rem_army( ARMY_DATA* pa ){
  word hash = ARMY_HASHVAL(pa);
  ARMY_DATA* prev = army_list[hash];

  if (prev == NULL){
    bug("armies.c>rem_army : Cannot remove army as non exist.", 0);
    return;
  }
  else if (army_list[hash] == pa ){
    army_list[hash] = pa->next;
    free_army( pa );
    top_army--;
    return;
  }
  
  for (; prev->next; prev = prev->next){
    if (prev->next == pa){
      prev->next = pa->next;
      free_army( pa );
      top_army--;
      return;
    }
  }
  bug("armies.c>rem_army : Cannot remove army as non found.", 0);
  return;
}

ARMY_INDEX_DATA* new_army_index(){
  ARMY_INDEX_DATA *pai = malloc( sizeof( *pai ));
  memset( pai, 0, sizeof( ARMY_INDEX_DATA ));
  return pai;
}

void free_army_index( ARMY_INDEX_DATA* pai ){
  
  if (!IS_NULLSTR(pai->noun)){
    free_string( pai->noun );
  }
  if (!IS_NULLSTR(pai->short_desc)){
    free_string( pai->short_desc );
  }
  if (!IS_NULLSTR(pai->long_desc)){
    free_string( pai->long_desc );
  }
  if (!IS_NULLSTR(pai->desc)){
    free_string( pai->desc );
  }
  free( pai );
}

void add_army_index( ARMY_INDEX_DATA* pai ){

  pai->next = army_index_list.next;
  army_index_list.next = pai;
  top_army_index++;
}

void rem_army_index( ARMY_INDEX_DATA* pai ){
  ARMY_INDEX_DATA* prev = &army_index_list;

  if (prev->next == NULL){
    bug("armies.c>rem_army_index : Cannot remove index as non exist.", 0);
    return;
  }
  for (; prev->next; prev = prev->next){
    if (prev->next == pai){
      prev->next = pai->next;
      free_army_index( pai );
      top_army_index--;
      return;
    }
  }
  bug("armies.c>rem_army_index : Cannot remove index as non found.", 0);
  return;
}

void fwrite_army_index( FILE* fp, ARMY_INDEX_DATA* pai ){
  char buf[MIL];

  fprintf( fp, "#%d\n", pai->vnum );
  fprintf( fp, "%s\n", fwrite_flag(pai->type, buf) );
  fprintf( fp, "%d\n", pai->cost );
  fprintf( fp, "%d\n", pai->support );
  fprintf( fp, "%s\n", fwrite_flag(pai->army_flags, buf) );  
  fprintf( fp, "%d %d %d\n", 
	   pai->off_dice[DICE_NUMBER],
	   pai->off_dice[DICE_TYPE],
	   pai->off_dice[DICE_BONUS]);
  fprintf( fp, "%d %d %d\n", 
	   pai->hit_dice[DICE_NUMBER],
	   pai->hit_dice[DICE_TYPE],
	   pai->hit_dice[DICE_BONUS]);
  fprintf( fp, "%d %d %d\n", 
	   pai->arm_dice[DICE_NUMBER],
	   pai->arm_dice[DICE_TYPE],
	   pai->arm_dice[DICE_BONUS]);
  fprintf( fp, "%s~\n", fix_string(pai->noun) );
  fprintf( fp, "%s~\n", fix_string(pai->short_desc) );
  fprintf( fp, "%s~\n", fix_string(pai->long_desc) );
  fprintf( fp, "%s~\n", fix_string(pai->desc) );
}



bool fread_army_index( FILE* fp, ARMY_INDEX_DATA* pai, AREA_DATA* pArea, int vnum ){

  pai->vnum			= vnum;
  pai->area			= pArea;
  pai->type			= fread_flag( fp );
  pai->cost			= fread_number( fp );
  pai->support			= fread_number( fp );
  pai->army_flags		= fread_flag( fp );    

  pai->off_dice[DICE_NUMBER]	= fread_number( fp );
  pai->off_dice[DICE_TYPE]	= fread_number( fp );
  pai->off_dice[DICE_BONUS]	= fread_number( fp );

  pai->hit_dice[DICE_NUMBER]	= fread_number( fp );
  pai->hit_dice[DICE_TYPE]	= fread_number( fp );
  pai->hit_dice[DICE_BONUS]	= fread_number( fp );

  pai->arm_dice[DICE_NUMBER]	= fread_number( fp );
  pai->arm_dice[DICE_TYPE]	= fread_number( fp );
  pai->arm_dice[DICE_BONUS]	= fread_number( fp );

  pai->noun			= fread_string( fp );
  pai->short_desc		= fread_string( fp );
  pai->long_desc		= fread_string( fp );
  pai->desc			= fread_string( fp );

  if (get_army_index( vnum ) != NULL){
    bug("armies.c>fread_army_index: vnum %d duplicated.", vnum );
    return FALSE;
  }
  else
    return TRUE;
}

//sets armie's commander to given string
void set_army_commander( ARMY_DATA* pa, char* name, sh_int rank ){
  if (!IS_NULLSTR(pa->commander))
    free_string( pa->commander);
  pa->commander = str_dup( name );
  if (rank >= 0)
    pa->command_rank = rank;
}

//gets finds an army in the world with given id or NULL
ARMY_DATA* get_army_world( word ID ){
  ARMY_DATA* pa;
  word hash = ID_HASHVAL( ID );

  for (pa = army_list[hash]; pa; pa = pa->next){
    if (pa->ID == ID)
      return pa;
  }
  
  return NULL;
}

//takes army out of slot
void army_from_slot( ARMY_DATA* pa ){
  int slot = pa->in_slot;
  if (pa->in_room == NULL){
    bug("armies.c>army_from_slot: Army not in room. (%d)", pa->ID );
    return;
  }
  if (pa->in_slot < 0){
    bug("armies.c>army_from_slot: Army not in slot. (%d)", pa->ID );
    return;
  }
  /* decrease support */
  SUPPORT_GAIN(pa->pCabal, ECO_INCOME, -get_army_support( pa, pa->in_room->area) );

  if (pa->pIndexData->type == ARMY_TYPE_BASTION)
    tower_from_area(pa->pCabal, pa->in_room->area);

  pa->in_room->room_armies.armies[pa->in_slot] = NULL;

  if (slot == INROOM_BASTION)
    pa->in_room->room_armies.bastions--;
  else if (slot < INROOM_ATTACKERS)
    pa->in_room->room_armies.defenders--;
  else
    pa->in_room->room_armies.attackers--;
  pa->in_room->room_armies.count--;

  pa->in_slot = -1;
}

//puts an army into given slot in given room
void army_to_slot( ARMY_DATA* pa, int slot, bool fSilent ){
  if (pa->in_room == NULL){
    bug("armies.c>army_to_slot: Army not in room. (%d)", pa->ID );
    return;
  }
  
  if (pa->in_slot >= 0)
    army_from_slot( pa );

  if (pa->in_room->room_armies.armies[slot]){
    bug("armies.c>army_to_slot: Tried to place army in non empty slot.", 0);
    return;
  }

  /* increase support for this army */
  SUPPORT_GAIN(pa->pCabal, ECO_INCOME, get_army_support( pa, pa->in_room->area) );

  if (pa->pIndexData->type == ARMY_TYPE_BASTION)
    tower_to_area(pa->pCabal, pa->in_room->area, fSilent);

  pa->in_room->room_armies.armies[slot] = pa;
  if (slot == INROOM_BASTION)
    pa->in_room->room_armies.bastions++;
  else if (slot < INROOM_ATTACKERS)
    pa->in_room->room_armies.defenders++;
  else
    pa->in_room->room_armies.attackers++;
  pa->in_room->room_armies.count++;

  pa->in_slot = slot;
}
    
  
//moves an army out of a room
void army_from_room( ARMY_DATA* pa ){
  int i;
  ARMY_DATA* pAr;

  if (pa->in_room == NULL){
    bug("armies.c>army_from_room: army (%u) not in room.",  pa->ID);
    return;
  }
  
  stop_attacking( pa );

  if ( (i = pa->in_slot) >= 0)
    army_from_slot( pa );

  /* if this was bastion, reset armor on all defenders */
  /* end remove tower from area */
  if (i == INROOM_BASTION){
    for (i = INROOM_DEFENDERS; i < INROOM_ATTACKERS; i++){
      pAr = pa->in_room->room_armies.armies[i];
      if (pAr != NULL)
	pAr->armor = pAr->max_armor;
    }
  }
  pa->in_room = NULL;
}

//moves the army to a room
void army_to_room( ARMY_DATA* pa, ROOM_INDEX_DATA* pRoom ){
  if (pa->in_room)
    army_from_room( pa );
  pa->in_room = pRoom;
}



  
//creates a single ready to use army based on index
ARMY_DATA* create_army( ARMY_INDEX_DATA* pIndex, CABAL_DATA* pCabal ){
  ARMY_DATA* pa;

  if (pIndex == NULL)
    return NULL;

  //new army automaticly sets id on the army
  pa = new_army();

  pa->pIndexData		= pIndex;
  pa->pCabal			= pCabal;
  pa->off_dice[DICE_NUMBER]	= pIndex->off_dice[DICE_NUMBER];
  pa->off_dice[DICE_TYPE]	= pIndex->off_dice[DICE_TYPE];
  pa->off_dice[DICE_BONUS]	= pIndex->off_dice[DICE_BONUS];
  pa->max_hit			= dice( pIndex->hit_dice[DICE_NUMBER], pIndex->hit_dice[DICE_TYPE]) + pIndex->hit_dice[DICE_BONUS];
  pa->max_armor			= dice( pIndex->arm_dice[DICE_NUMBER], pIndex->arm_dice[DICE_TYPE]) + pIndex->arm_dice[DICE_BONUS];

  //allows for easy balancing
  pa->max_hit = ARMY_DEFENSE_MULT * pa->max_hit / 100;

  pa->hit			= pa->max_hit;
  pa->armor			= pa->max_armor;
  pa->army_flags		= pIndex->army_flags;
  pa->noun			= str_dup( pIndex->noun );
  pa->short_desc		= str_dup( pIndex->short_desc );
  pa->long_desc			= str_dup( pIndex->long_desc );
  pa->desc			= str_dup( pIndex->desc );

  /* some safeties */
  pa->hit = UMAX(1, pa->hit );
  pa->max_hit = UMAX(1, pa->max_hit );
  pa->in_slot = -1; //no slot

  //set commander to cabal name initial
  if (pCabal)
    set_army_commander( pa, pCabal->name, 0 );
  else
    set_army_commander( pa, "Unknown", 0 );

  //if this army vnum is cabal's upgrade vnum set elite flag
  if (pCabal){
    if (pIndex->type == ARMY_TYPE_UNIT && pIndex->vnum == pCabal->pIndexData->army_upgrade)
      SET_BIT(pa->army_flags, ARMY_FLAG_ELITE);
    else if (pIndex->type == ARMY_TYPE_BASTION && pIndex->vnum == pCabal->pIndexData->tower_upgrade)
      SET_BIT(pa->army_flags, ARMY_FLAG_ELITE);
  }
  //add army to the list of armies in the game
  add_army( pa );
  return pa;
}

//extracts a single army out of the game
void extract_army( ARMY_DATA* pa ){

  //army_from_room does stop attackin as well, no sense doing it twice
  if (pa->in_room)
    army_from_room( pa );
  else
    stop_attacking( pa );
  //remove army from list of armies in the game, this will also free it
  //as well as its id
  rem_army( pa );
}





//begins a recruitment of an army fElite TRUE to recruit elite unit.
void start_recruitment( CHAR_DATA* ch, bool fUpgrade, int num, int cost ){
  CABAL_DATA* pCab = get_parent( ch->pCabal );
  int* queue, *timer, start_dur;

//SAFETIES
  if (pCab == NULL || ch->pcdata == NULL || ch->pcdata->member == NULL)
    return;
  else
    num = URANGE(-1, num, 10);

  if (fUpgrade){
    start_dur = IS_CABAL(pCab, CABAL_SWARM) ? ARMY_DUR_SUPG : ARMY_DUR_UPG;
    queue = &ch->pcdata->member->armies[UPGQ];
    timer = &ch->pcdata->member->armies[TIMU];
  }
  else{
    start_dur = IS_CABAL(pCab, CABAL_SWARM) ? ARMY_DUR_SREC : ARMY_DUR_REC;
    queue = &ch->pcdata->member->armies[NORQ];
    timer = &ch->pcdata->member->armies[TIMN];
  }

  //if value == -1 we increase the queue by one
  if (num == -1)	*queue = UMIN(10, *queue + 1);
  else			*queue = num;

  if (num == 0){
    send_to_char("Training halted.\n\r", ch);
    *timer = -1;
  }
  else{
    sendf(ch, "%d units remaining to %s.\n\r", 
	  *queue,
	  fUpgrade ? "train" : "conscript");
    if (*timer < 1)
      *timer = start_dur;
  }
}

/* sets state of a single army and when it will be re-examined */
/* INTERNAL, do not use */
void set_state( ARMY_DATA* pa, sh_int state, int lifetime, int* vars ){
  int i = 0;

  pa->state	= state;
  pa->lifetime	= lifetime;

  for (i = 0; i < ARMY_VARS; i++)
    pa->vars[i] = vars ? vars[i] : 0;
}

/* short AI function to allow cabals with no members to respond to attacks */
void army_cabal_ai( ARMY_DATA* pBas){
  CABAL_DATA* pCab;
  ARMY_DATA* pa;
  ROOM_INDEX_DATA* room = pBas->in_room;
  char buf[MIL];
  int vnum, i;
  int armies;
  
  if (pBas == NULL || room == NULL)
    return;
  else if (pBas->cabal_ai_life > 0)
    return;
  else if ( (pCab = get_parent(pBas->pCabal)) == NULL)
    return;
  else if (pCab->armies_ready < 1 || pCab->present > 0)
    return;
  else if ( (mud_data.current_time - pCab->present_stamp) < DEFENSE_AIWAIT)
    return;

  //check if the room is being attacked and we have space to defend
  if (room->room_armies.bastions < 1 
      || room->room_armies.defenders  > 1
      || room->room_armies.attackers < 1)
    return;
  else if (!can_defend( NULL, NULL, room, pCab, TRUE ))
    return;

  /* Conditions fulfilled:
     1) We have a bastion in room
     2) There is a defender spot open
     3) There are attackers in room
     4) Can defend the room
     5) We have armies to defend with
  */
  
  if (room->room_armies.defenders < 1)
    armies = 2;
  else
    armies = 1;

  //spawn an army and send it to hold the room
  //if we have more then 5 recruits send an elite
  if (pCab->armies_ready - armies > 5){
    vnum = pCab->pIndexData->army_upgrade;
    pCab->armies_ready -= 1 * armies;
  }
  else{
    vnum = pCab->pIndexData->army;
    pCab->armies_ready -= armies;
  }

  for (i = 0; i < armies; i++){
    if ( (pa = create_army(get_army_index( vnum ), pCab)) == NULL){
      return;
    }
  
    /* set commander and order */
    set_army_commander( pa, pCab->name, RANK_ELDER - 1 );
    order_army( pa, AS_DEFE, gen_vars( room->vnum, 0, 0));
    army_ai( pa );

    //Bastion lifetime marks when we should send armies again
    //pa->lifetime is movement length + 1 turn to enter, + 1 to make new decision
    pBas->cabal_ai_life = 2 + pa->lifetime;

    sprintf( buf, "%s %s confirmed.", 
	     capitalize(state_table[pa->order].name), 
	     room->name );
    army_report( pa, buf, REPORT_NEUT, FALSE );
  }

}
  
/* Main army AI function, switches states based on current state */
void army_ai( ARMY_DATA* pa ){
  int lifetime = 0, to_vnum, from_vnum;
  char buf[MIL];

  switch (pa->state){
  default:
  case AS_RETU:
    return;
// DISBANDING: does nothing, hp regen is negative army will disband when dies
  case AS_DISB:
    break;
// IDLE: reverts to original order if any
  case AS_NONE:
    set_state(pa, pa->order, lifetime, pa->ovars );
    break;
// FIGHTIN/BESIEGEDG: if not in combat, reverts to old orders
  case AS_BESE:
  case AS_FIGH:
    if (pa->attacking == NULL){
      //check if we defeated everything
      if (pa->in_slot < INROOM_ATTACKERS){
	if ( (pa->attacking = get_defend_target( pa->pCabal, pa->in_room )) == NULL){
	  sprintf(buf, "Standing down in %s.", pa->in_room->name);
	  army_report( pa, buf, REPORT_GOOD, FALSE );
	  //this is not needed as we cascade down to it anyway, but just in ase
	  set_state(pa, pa->order, 1, pa->ovars );
	}
	else
	  break;
      }
      /* attacker check (attackers pause if defenders dead) */
      else{
	ARMY_DATA* tar = get_attack_target(pa->pCabal, pa->in_room);
	if (tar == NULL){
	  sprintf(buf, "Victorious in %s.", pa->in_room->name);
	  army_report( pa, buf, REPORT_GOOD, FALSE );
	  //this is not needed as we cascade down to it anyway, but just in ase
	  set_state(pa, pa->order, 1, pa->ovars );
	  break;
	}
	else if (tar->pIndexData->type != ARMY_TYPE_BASTION){
	  pa->attacking = tar;
	  break;
	}
      }
      set_state(pa, pa->order, 1, pa->ovars );
    }
    else if (is_friendly(pa->pCabal, pa->attacking->pCabal) != CABAL_ENEMY){
      stop_attacking( pa );
    }
    break;
// ATTACK <vnum>: attack defenders in room v0, move there if not there
  case AS_ATTA:
    /* check if we are in wrong room, if so move */
    if (pa->in_room == NULL || pa->in_room->vnum != pa->vars[0]){
      /* not in right room, move */
      lifetime = state_table[AS_LEAV].lifetime;
      set_state(pa, AS_LEAV, lifetime, gen_vars( pa->vars[0], 0, 0));
    }
    // check if we can attack something here
    else if (can_attack(NULL, pa, pa->in_room, pa->pCabal, TRUE)){
      break;
    }
    /* if nothing in room to attack, check if can patrol, or return */
    else{
      if (can_patrol(NULL, pa, pa->in_room, pa->pCabal, TRUE)){
	/* set current state to patrol, set over all order to return 
	   so we return after patrol is over
	*/
	pa->order = AS_RETU;
	lifetime = state_table[AS_PATR].lifetime;
	set_state(pa, AS_PATR, 0, gen_vars( pa->vars[0], 0, 0));
	
	sprintf(buf, "Nothing to attack in %s.", pa->in_room->name);
	army_report( pa, buf, REPORT_NEUT, FALSE );
      }
      else{
	lifetime = state_table[AS_RETU].lifetime;
	set_state(pa, AS_RETU, 0, gen_vars( 0, 0, 0));      
	sprintf(buf, "No enemies in %s.", pa->in_room->name);
	army_report( pa, buf, REPORT_NEUT, FALSE );
      }
    }
    break;
// DEFEND <vnum>: attack attackers in room v0, move there if not there
  case AS_DEFE:
    /* check if we are in wrong room, if so move */
    if (pa->in_room == NULL || pa->in_room->vnum != pa->vars[0]){
      /* not in right room, move */
      lifetime = state_table[AS_LEAV].lifetime;
      set_state(pa, AS_LEAV, lifetime, gen_vars( pa->vars[0], 0, 0));
    }
    // check if we can attack something here
    else if (can_defend(NULL, pa, pa->in_room, pa->pCabal, TRUE)){
      //reset post circle TRUE in v2
      pa->vars[2] = 0;
      break;
    }
    /* if nothing in room to defend, check if can patrol, or shadow */
    else{
      //if there is no space for defense, but there are attackers circle
      //If we've already circled then vars2 is TRUE
      if (!pa->vars[2] 
	  && (INROOM_ATTACKERS - 1 - count_defenders( pa->in_room, FALSE)) < 1
	  && get_defend_target( pa->pCabal, pa->in_room )){
	//we leave order as is, as we want to try to defend after shadowing
	lifetime = state_table[AS_CIRC].lifetime;
	//set v0 to updates we want to shadow for
	set_state(pa, AS_CIRC, -1, gen_vars( 0, 0, lifetime));      
	sprintf(buf, "Circling. Lacking space to defend.");
	army_report( pa, buf, REPORT_NEUT, TRUE );
      }
      else if (can_patrol(NULL, pa, pa->in_room, pa->pCabal, TRUE)){
	/* set current state to patrol, set over all order to return 
	   so we return after patrol is over
	*/
	pa->order = AS_RETU;
	lifetime = state_table[AS_PATR].lifetime;
	set_state(pa, AS_PATR, 0, gen_vars( pa->vars[0], 0, 0));
	sprintf(buf, "Defend. No enemies in area.");
	army_report( pa, buf, REPORT_NEUT, FALSE );
      }
      //return
      else{
	lifetime = state_table[AS_RETU].lifetime;
	set_state(pa, AS_RETU, 0, gen_vars( 0, 0, 0));      
	sprintf(buf, "Returning. Cannot defend area.");
	army_report( pa, buf, REPORT_NEUT, TRUE );
      }
    }
    break;
// GARRISON <vnum>: garrison room v0, move there if not there
  case AS_GARR:
    /* check if we are in wrong room, if so move */
    if (pa->in_room == NULL || pa->in_room->vnum != pa->vars[0]){
      /* not in right room, move */
      lifetime = state_table[AS_LEAV].lifetime;
      set_state(pa, AS_LEAV, lifetime, gen_vars( pa->vars[0], 0, 0));
    }
    /* if we can bastion, check if there are attackers in room, if so
       start shadowing in order to reinforce the garrison once attackers
       leave.  If we've already shadowed then var2 is TRUE
    */
    else if (pa->vars[2] == 0 && count_attackers(pa->in_room) > 0){
      lifetime = state_table[AS_SHAD].lifetime;
      set_state(pa, AS_SHAD, 0, gen_vars( 0, 0, lifetime));      
      sprintf(buf, "Circling. Can't garrison due to battle.");
      army_report( pa, buf, REPORT_BAD, TRUE );
    }
    /* if not in wrong room, check if we can bastion, if not return */
    else if (count_attackers(pa->in_room) > 0 
	     || !can_bastion( NULL, pa, pa->in_room, pa->pCabal, TRUE )){
      lifetime = state_table[AS_RETU].lifetime;
      set_state(pa, AS_RETU, 0, gen_vars( 0, 0, 0));      
      sprintf(buf, "Can't garrison %s.", pa->in_room->area->name);
      army_report( pa, buf, REPORT_NEUT, TRUE );
    }
    break;
// FORTIFY <vnum>: upgrade bastion in room v0, move there if not there
  case AS_FORT:
    /* check if we are in wrong room, if so move */
    if (pa->in_room == NULL || pa->in_room->vnum != pa->vars[0]){
      /* not in right room, move */
      lifetime = state_table[AS_LEAV].lifetime;
      set_state(pa, AS_LEAV, lifetime, gen_vars( pa->vars[0], 0, 0));
    }
    /* we are in correct room, check if fortifications not started (v2 == 0) */
    else if (pa->vars[2] == 0){
      //can we start fortifications?
      if (!can_fortify( NULL, pa->in_room, pa->pCabal, TRUE )){
	lifetime = state_table[AS_RETU].lifetime;
	set_state(pa, AS_RETU, 0, gen_vars( 0, 0, 0));      
	sprintf(buf, "Unable to fortify %s.", pa->in_room->name);
	army_report( pa, buf, REPORT_NEUT, TRUE );
      }
      else{
	sprintf(buf, "Fortifying in %s.", pa->in_room->name);
	army_report( pa, buf, REPORT_GOOD, TRUE );
	pa->vars[2] = ARMY_DUR_FORTIFY;
      }
    }
    /* we are in process of foritfying, check for interruptions */
    else{
      /* check for not being able to build a garrison anymore */
      if (pa->vars[2] && !can_fortify( NULL, pa->in_room, pa->pCabal, TRUE )){
	lifetime = state_table[AS_RETU].lifetime;
	set_state(pa, AS_RETU, 0, gen_vars( 0, 0, 0));      
	sprintf(buf, "Unable to fortify %s.", pa->in_room->name);
	army_report( pa, buf, REPORT_BAD, TRUE );
	pa->vars[2] = 0;
      }
      /* if we can fortify, check if there are attackers in room, if so
	 start shadowing in order to fortify once attackers are gone
	 leave.  If 'we already circled then vars[2] is TRUE
      */
      else if (count_attackers(pa->in_room) > 0){
	lifetime = state_table[AS_SHAD].lifetime;
	set_state(pa, AS_SHAD, 0, gen_vars( 0, 0, lifetime));      
	sprintf(buf, "Circling.  Fortification interrupted by battle.");
	army_report( pa, buf, REPORT_BAD, TRUE );
      }
    }
    break;
// LEAVE <for-room>: leave room before moving to another room
  case AS_LEAV:
    //set the path before leaving
    if (pa->vars[2] == 0)
      from_vnum = pa->pCabal->anchor->vnum;
    else
      from_vnum = pa->vars[2];
    to_vnum = pa->vars[0];

    /* try to get the path to the destination */
    if ( (lifetime = set_army_path( pa, from_vnum, to_vnum)) < 0){
      lifetime = state_table[AS_RETU].lifetime;
      set_state(pa, AS_RETU, 0, gen_vars( 0, 0, 0));      
      sprintf(buf, "Returning. Could not reach destination.");
      army_report( pa, buf, REPORT_NEUT, TRUE );      
      break;
    }
    //lifetime set by set_army_path above
    set_state(pa, AS_MOVE, lifetime, gen_vars( pa->vars[0], 0, 0));
    break;
// MOVE <roomvnum>: move to v0
  case AS_MOVE:
    /* are we in the destination room? */
    if (pa->in_room && pa->in_room->vnum == pa->vars[0]){
      //reverts to original order
      set_state(pa, pa->order, 0, pa->ovars );
      //run ai once
      if (pa->state != AS_MOVE)
	army_ai( pa );
    }
    break;
//PATROL: check if in room, move if not.  Otherwise try to patrol
  case AS_PATR:
    /* check if we are in wrong room, if so move */
    if (pa->in_room == NULL || pa->in_room->vnum != pa->vars[0]){
      /* not in right room, move */
      lifetime = state_table[AS_LEAV].lifetime;
      set_state(pa, AS_LEAV, lifetime, gen_vars( pa->vars[0], 0, 0));
    }
    /* if we are in a room and in a slot (already patroling) check if
       we should return
    */
    else if (pa->in_slot >= 0){
      /* if our order was something else then patrol execute it now */
      if (pa->order != AS_PATR){
	set_state(pa, pa->order, 0, pa->ovars);
	sprintf(buf, "Commencing %s.  Finished patroling.", state_table[pa->order].name);
	army_report( pa, buf, REPORT_NEUT, TRUE );      
      }
      else{
	//no reason to return, reset lifetime untill next check
	pa->lifetime = state_table[pa->state].lifetime;
      }
    }
    /* if right room, but not in slot check if we can patrol, if not return */
    else if (!can_patrol( NULL, pa, pa->in_room, pa->pCabal, TRUE )){
      lifetime = state_table[AS_RETU].lifetime;
      set_state(pa, AS_RETU, 0, gen_vars( 0, 0, 0));      
      sprintf(buf, "Could not patrol %s.", pa->in_room->name);
      army_report( pa, buf, REPORT_NEUT, TRUE );
    }
    break;
//SHADOW: Stay around the room untill v2 runs out
//	  or there are no more enemies.  Then revert to order.
  case AS_SHAD:
    if (pa->in_room == NULL 
	|| --pa->vars[2] < 1 
	|| count_attackers(pa->in_room) < 1){
      //revert to original order but v2 is now TRUE
      set_state(pa, pa->order, 1, gen_vars(pa->ovars[0], pa->ovars[1], 1) );
    }
    break;
//CIRCLE: Stay around the room untill v2 runs out
//	  or there are empty defender slots.  Then revert to order.
  case AS_CIRC:
    if (pa->in_room == NULL 
	|| --pa->vars[2] < 1 
	|| (INROOM_ATTACKERS - 1 - count_defenders(pa->in_room, FALSE) > 0)){
      //revert to original order but v2 is now TRUE
      set_state(pa, pa->order, 1, gen_vars(pa->ovars[0], pa->ovars[1], 1) );
    }
    break;
  }
}

//performs actions when state's lifetime reaches 0
//This function should NEVER switch state
void army_action( ARMY_DATA* pa ){
  int lifetime = 0;
  
  switch (pa->state){
  default:
    return;
//FIGHT/BESIEGED:
  case AS_FIGH:
  case AS_BESE:
    break;
//LEAVE: If in room, exit it before moving to v0
  case AS_LEAV:
    army_leave( pa );
    pa->lifetime = lifetime;
    break;
//MOVE: Move along the armie's path to destination
  case AS_MOVE:
    if (pa->in_room){
      bug("armies.c>army_action: AS_MOVE before army left.", 0);
      army_from_room( pa );
    }
    else if (!army_move( pa )){
      bug("armies.c>army_action: AS_MOVE with invalid path.", 0);
      extract_army( pa );
      return;
    }
    //if by some chance an army got lost and keeps trying to move to NULL room
    else if (pa->in_room == NULL){
      bug("armies.c>army_action: AS_MOVE with NULL destination. Army killed", 0);
      extract_army( pa );
    }
    else
      pa->lifetime = lifetime;
    break;
//SHADOW:  leave slot if in one
  case AS_SHAD:
    if (pa->in_slot >= 0)
      army_from_slot( pa );
    break;
//GARRISON: Garrison/Build a bastion in room the army is in
  case AS_GARR:
    if (pa->in_room == NULL){
      bug("armies.c>army_action: AS_GARR while not in room.", 0);
      extract_army( pa );
      return;
    }
    //army is extracted inside army_garrison
    army_garrison( pa);
    break;
//FORTIFY: upgrade a bastion in room the army is in
  case AS_FORT:
    if (pa->in_room == NULL){
      bug("armies.c>army_action: AS_FORT while not in room.", 0);
      extract_army( pa );
      return;
    }
    /* build fortifications when v2 reaches 0 */
    else if (pa->vars[2] > 0 && --pa->vars[2] == 0){
      //army is extracted inside army_fortify
      army_fortify( pa);
    }
    break;
//PATROL: Patrol in room the army is in
  case AS_PATR:
    if (pa->in_room == NULL){
      bug("armies.c>army_action: AS_PATR while not in room.", 0);
      extract_army( pa );
      return;
    }
    army_patrol( pa);
    /* set lifetime before next decision if should continue patrol */
    pa->lifetime = state_table[pa->state].lifetime;
    break;
//ATTACK
  case AS_ATTA:
    if (pa->in_room == NULL){
      bug("armies.c>army_action: AS_ATTA while not in room.", 0);
      extract_army( pa );
      return;
    }
    if (army_attack( pa))
      /* set infinite lifetime, army will switch back after attack is done */
      pa->lifetime = -1;
    else
      pa->lifetime = 0;
    break;
//DEFEND
  case AS_DEFE:
    if (pa->in_room == NULL){
      bug("armies.c>army_action: AS_DEFE while not in room.", 0);
      extract_army( pa );
      return;
    }
    if (army_defend( pa))
      /* set infinite lifetime, army will switch back after attack is done */
      pa->lifetime = -1;
    else
      pa->lifetime = 0;
    break;
//RETURN: put army back in barracks
  case AS_RETU:
    //If we are in room leave it, then start returning
    if (pa->in_room){
      army_leave( pa );
      pa->lifetime = state_table[AS_RETU].lifetime;
      break;
    }
    //army extracted inside
    army_return( pa );
    break;
  }
}  

/* gives an army an order.  Use this to give an army its general order */
void order_army( ARMY_DATA* pa, sh_int state, int* vars ){
  int i = 0;

  pa->order	= state;
  pa->state	= state;
  pa->lifetime	= state_table[state].lifetime;

  for (i = 0; i < ARMY_VARS; i++){
    pa->vars[i] = vars ? vars[i] : 0;
    pa->ovars[i] = pa->vars[i];
  }

  army_ai( pa );
  army_action( pa );
}

bool can_bastion(CHAR_DATA* ch,  ARMY_DATA* pa, ROOM_INDEX_DATA* in_room, CABAL_DATA* pc, bool fQuiet ){
  int door, def = 0, i;
  EXIT_DATA* pExit;
  ARMY_INDEX_DATA* pai;
  bool fDoor = TRUE;

  /* safety */
  if (ch == NULL)
    fQuiet = TRUE;

  /* check for targetting virtual rooms */
  if (IS_VIRVNUM(in_room->vnum)){
    if (!fQuiet)
      send_to_char("You cannot garrison virtual rooms.", ch );
    return FALSE;
  }
  else if (!can_reinforce( pc, in_room->area)){
    if (!fQuiet)
      send_to_char("You cannot reinforce this area. (\"help reinforce\")\n\r", ch );
    return FALSE;
  }
  else if ( (pai = get_army_index(pc->pIndexData->tower)) == NULL){
    if (!fQuiet)
      send_to_char("Your cabal has no fortificantion's to build.\n\r", ch );
    return FALSE;
  }
  /* check if we can make a garrison here */
  else if (in_room->room_armies.armies[INROOM_BASTION] == NULL
	   && in_room->area->bastion_current >= in_room->area->bastion_max){
    if (!fQuiet)
      send_to_char("This area cannot support any more bastions.\n\r", ch );
    return FALSE;
    /* check money */
    if (GET_CAB_CP(pc) < pai->cost){
      if (!fQuiet){
	sendf(ch, "[%s] coffers must hold at least %d %ss in order to construct a bastion.\n\r", 
	      pc->who_name,
	      pai->cost,
	      pc->currency);
      }
      return FALSE;
    }
  }
/* cannot take over cabal areas */
  else if (IS_AREA(in_room->area, AREA_CABAL)){
    if (!fQuiet)
      send_to_char("You cannot take over a Cabal!\n\r", ch );
    return  FALSE;
  }
/* check for pacts */
  else if ( in_room->area->pCabal
	    && is_friendly( pc, in_room->area->pCabal) == CABAL_NEUTRAL){
    if (!fQuiet)
      sendf( ch, "You will have to declare a Vendetta against %s before taking armed action against them!\n\r", 
	     in_room->area->pCabal->who_name);
    return FALSE;
  }
  if (IS_SET(in_room->area->area_flags, AREA_RESTRICTED)
      || IS_SET(in_room->room_flags, ROOM_SAFE)
      || IS_SET(in_room->room_flags, ROOM_PRIVATE)
      || IS_SET(in_room->room_flags, ROOM_SOLITARY)
      || IS_SET(in_room->room_flags, ROOM_NOWHERE)
      || IS_SET(in_room->room_flags, ROOM_DAMAGE)
      || IS_SET(in_room->room_flags2, ROOM_NO_MAGIC)
      || IS_SET(in_room->room_flags2, ROOM_JAILCELL)
      || IS_VIRVNUM(in_room->vnum)){
    if (!fQuiet)
      send_to_char("You cannot build a tower due to nature of this room.\n\r", ch );
    return FALSE;
  }
  /* check doors */
  for (door = 0; door < MAX_DOOR; door++){
    if ( (pExit = in_room->exit[door]) == NULL)
      continue;
    //exit must be two way in same dir.
    else if (pExit->to_room->exit[rev_dir[door]] == NULL){
      fDoor = FALSE;
      break;
    }
    //exit must be two way in same dir.
    else if (pExit->to_room->exit[rev_dir[door]]->to_room != in_room){
      fDoor = FALSE;
      break;
    }
  }
  if (!fDoor){
    if (!fQuiet)
      send_to_char("The room cannot have one way exits.\n\r", ch);
    return FALSE;
  }
  /* check how many defender spots are open */
  for (i = INROOM_DEFENDERS; i < INROOM_ATTACKERS; i++){
    if (in_room->room_armies.armies[i] == NULL)
      def++;
  }
  /* two cases 
     1) Bastion is not there, we can garrison
     2) Bastion is there we can garrison if:
     a) bastion is friendly
     b) spots are open
  */
  if (in_room->room_armies.armies[INROOM_BASTION] != NULL){
    if (is_friendly( pc, in_room->room_armies.armies[INROOM_BASTION]->pCabal) != CABAL_FRIEND){
      if (!fQuiet)
	send_to_char("Unable to defend non-allied bastion.\n\r", ch);
      return FALSE;
    }
    else if (def < 1
	     && (pa == NULL 
		 || pa->in_room != in_room 
		 || pa->in_slot <= INROOM_BASTION
		 || pa->in_slot >= INROOM_ATTACKERS)
	     ){
      if (!fQuiet)
	send_to_char("Unable to garrison due to lack of space.\n\r", ch);
      return FALSE;
    }
  }
  
  /* all seems good */
  return TRUE;
}

//checks if we can fortify bastion in this room
bool can_fortify(CHAR_DATA* ch,  ROOM_INDEX_DATA* in_room, CABAL_DATA* pc, bool fQuiet ){

  /* safety */
  if (ch == NULL)
    fQuiet = TRUE;

  if (!can_reinforce( pc, in_room->area)){
    if (!fQuiet)
      send_to_char("You cannot reinforce this area. (\"help reinforce\")\n\r", ch );
    return FALSE;
  }
  else if (get_army_index(pc->pIndexData->tower_upgrade) == NULL){
    if (!fQuiet)
      send_to_char("Your cabal has no fortificantion's to build.\n\r", ch );
    return FALSE;
  }
  /* check if our bastion exists in this room */
  else if (in_room->room_armies.armies[INROOM_BASTION] == NULL){
    if (!fQuiet)
      send_to_char("There is no bastion there to fortify.\n\r", ch);
    return FALSE;
  }
  else if (!is_same_cabal(in_room->room_armies.armies[INROOM_BASTION]->pCabal, pc)){
    if (!fQuiet)
      send_to_char("You cannot fortify bastions that do not belong to you.\n\r", ch);
    return FALSE;
  }

  /* all seems good */
  return TRUE;
}

//checks if a patrol can arrive in the room
bool can_patrol(CHAR_DATA* ch,  ARMY_DATA* pa, ROOM_INDEX_DATA* in_room, CABAL_DATA* pc, bool fQuiet ){
  int def = 0, i;

  /* safety */
  if (ch == NULL)
    fQuiet = TRUE;

  /* check for targetting virtual rooms */
  if (IS_VIRVNUM(in_room->vnum)){
    if (!fQuiet)
      send_to_char("You cannot patrol virtual rooms.", ch );
    return FALSE;
  }
  else if (!can_reinforce( pc, in_room->area)){
    if (!fQuiet)
      send_to_char("You cannot reinforce this area. (\"help reinforce\")\n\r", ch );
    return FALSE;
  }
/* cannot take over cabal areas */
  else if (in_room->area->pCabal && in_room->area->pCabal->anchor 
	   && in_room->area->pCabal->anchor->area == in_room->area){
    if (!fQuiet)
      send_to_char("You cannot patrol inside a Cabal!\n\r", ch );
    return  FALSE;
  }
/* check for pacts */
  else if ( in_room->area->pCabal
	    && is_friendly( pc, in_room->area->pCabal) == CABAL_NEUTRAL){
    if (!fQuiet)
      sendf( ch, "You will have to declare a Vendetta against %s before taking armed action against them!\n\r", 
	     in_room->area->pCabal->who_name);
    return FALSE;
  }
  /* check if bastion is friendly */
  if (in_room->room_armies.armies[INROOM_BASTION] != NULL
      && is_friendly(pc, in_room->room_armies.armies[INROOM_BASTION]->pCabal) != CABAL_FRIEND){
    if (!fQuiet)
      send_to_char("Cannot patrol around unfriendly garrison.\n\r", ch);
    return FALSE;
  }
  /* check how many defender spots are open */
  for (i = INROOM_DEFENDERS; i < INROOM_ATTACKERS; i++){
    if (in_room->room_armies.armies[i] == NULL
	|| in_room->room_armies.armies[i] == pa)
      def++;
  }
  if (def < 1){
    if (!fQuiet)
      send_to_char("Unable to patrol due to lack of space.\n\r", ch);
    return FALSE;
  }
  /* all seems good */
  return TRUE;
}

//checks if an army can be order to another location
bool can_relocate(CHAR_DATA* ch, ARMY_DATA* pa, bool fQuiet ){
  /* safety */
  if (ch == NULL)
    fQuiet = TRUE;
  
  //is army in room
  if (pa->in_room == NULL){
    if (!fQuiet)
      send_to_char("This army is not stationed anywhere.\n\r", ch);
    return FALSE;
  }
  //is the army incombat?
  else if (pa->attacking){
    if (!fQuiet)
      send_to_char("Army cannot leave while engaged in battle.\n\r", ch);
    return FALSE;
  }
  //is army a bastion or garrison
  else if (pa->in_slot == INROOM_BASTION){
    if (!fQuiet){
      send_to_char("You cannot relocate bastions.\n\r", ch);
    }
    return FALSE;
  }
  //is this a garrison
  else if (IS_ARMY(pa, ARMY_FLAG_GARRISON)){
    if (!fQuiet){
      send_to_char("You cannot relocate garrisons.\n\r", ch);
    }
    return FALSE;
  }
  return TRUE;
}

//checks if an army can return to garrison
bool can_return(CHAR_DATA* ch,  ROOM_INDEX_DATA* in_room, ARMY_DATA* pa, bool fQuiet ){

  /* safety */
  if (ch == NULL)
    fQuiet = TRUE;

  //is army in room
  if (pa->in_room == NULL){
    if (!fQuiet)
      send_to_char("This army is not stationed anywhere.\n\r", ch);
    return FALSE;
  }
  //is the army incombat?
  else if (pa->attacking){
    if (!fQuiet)
      send_to_char("Army cannot leave while engaged in battle.\n\r", ch);
    return FALSE;
  }
  //is army a bastion with garrison?
  else if (pa->in_slot == INROOM_BASTION){
    ARMY_DATA* pa;
    int i;
    for (i = INROOM_DEFENDERS; i < INROOM_ATTACKERS; i++){
      if ( (pa = in_room->room_armies.armies[i]) == NULL)
	continue;
      else if (IS_ARMY(pa, ARMY_FLAG_GARRISON)){
	if (!fQuiet)
	  send_to_char("Cannot disband a bastion with garrisons.\n\r", ch);
	return FALSE;
      }
    }
  }
  return TRUE;
}

//checks if army can/has anything to attack in room
bool can_attack(CHAR_DATA* ch,  ARMY_DATA* pa, ROOM_INDEX_DATA* in_room, CABAL_DATA* pc, bool fQuiet ){
  int att = 0;
  
  /* safety */
  if (ch == NULL)
    fQuiet = TRUE;

  /* check for targetting virtual rooms */
  if (IS_VIRVNUM(in_room->vnum)){
    if (!fQuiet)
      send_to_char("You cannot attack virtual rooms.", ch );
    return FALSE;
  }
  else if (!can_reinforce( pc, in_room->area)){
    if (!fQuiet)
      send_to_char("You cannot reinforce this area. (\"help reinforce\")\n\r", ch );
    return FALSE;
  }
/* cannot take over cabal areas */
  else if (in_room->area->pCabal && in_room->area->pCabal->anchor 
	   && in_room->area->pCabal->anchor->area == in_room->area){
    if (!fQuiet)
      send_to_char("You cannot attack inside a Cabal!\n\r", ch );
    return  FALSE;
  }
/* check for pacts */
  else if ( in_room->area->pCabal
	    && is_friendly( pc, in_room->area->pCabal) == CABAL_NEUTRAL){
    if (!fQuiet)
      sendf( ch, "You will have to declare a Vendetta against %s before taking armed action against them!\n\r", 
	     in_room->area->pCabal->who_name);
    return FALSE;
  }
  /* check if any attacker slots open */
  if (pa == NULL || 
      pa->in_room == NULL || pa->in_room != in_room 
      || pa->in_slot < 0 || pa->in_slot < INROOM_ATTACKERS){
    att = INROOM_MAXARMIES - INROOM_ATTACKERS - count_attackers( in_room);
    if (att < 1){
      if (!fQuiet)
	send_to_char("Unable to attack due to lack of space.\n\r", ch);
      return FALSE;
    }
  }
  /* check if we have possible targets */
  if (get_attack_target( pc, in_room) == NULL){
    if (!fQuiet)
      send_to_char("No valid attack targets detected.\n\r", ch);
    return FALSE;
  }
  /* all seems good */
  return TRUE;
}

//checks if army can/has anything to defend from in room
bool can_defend(CHAR_DATA* ch,  ARMY_DATA* pa, ROOM_INDEX_DATA* in_room, CABAL_DATA* pc, bool fQuiet ){
  int def = 0;
  
  /* safety */
  if (ch == NULL)
    fQuiet = TRUE;

  /* check for targetting virtual rooms */
  if (IS_VIRVNUM(in_room->vnum)){
    if (!fQuiet)
      send_to_char("You cannot defend virtual rooms.", ch );
    return FALSE;
  }
  else if (!can_reinforce( pc, in_room->area)){
    if (!fQuiet)
      send_to_char("You cannot reinforce this area. (\"help reinforce\")\n\r", ch );
    return FALSE;
  }
/* cannot take over cabal areas */
  else if (in_room->area->pCabal && in_room->area->pCabal->anchor 
	   && in_room->area->pCabal->anchor->area == in_room->area){
    if (!fQuiet)
      send_to_char("You cannot attack inside a Cabal!\n\r", ch );
    return  FALSE;
  }
/* check for pacts */
  else if ( in_room->area->pCabal
	    && is_friendly( pc, in_room->area->pCabal) == CABAL_NEUTRAL){
    if (!fQuiet)
      sendf( ch, "You will have to declare a Vendetta against %s before taking armed action against them!\n\r", 
	     in_room->area->pCabal->who_name);
    return FALSE;
  }
  /* check if any attacker slots open */
  if (pa == NULL 
      || pa->in_room == NULL || pa->in_room != in_room
      || pa->in_slot < 0 || pa->in_slot >= INROOM_ATTACKERS){
    def = INROOM_ATTACKERS - 1 - count_defenders( in_room, FALSE);
    
    if (def < 1){
      if (!fQuiet)
	send_to_char("Unable to defend due to lack of space.\n\r", ch);
      return FALSE;
    }
  }
  /* check if we have possible targets */
  if (get_defend_target( pc, in_room) == NULL){
    if (!fQuiet)
      send_to_char("No valid attack targets detected.\n\r", ch);
    return FALSE;
  }
  /* all seems good */
  return TRUE;
}

ARMY_DATA* bastion( ARMY_INDEX_DATA* pai, ROOM_INDEX_DATA* room, CABAL_DATA* pc, int hp){
  ARMY_DATA* pBas;
  CABAL_DATA* pOld;

  if (room == NULL)
    return NULL;

  /* create new army */
  pBas = create_army( pai, pc );

  /* we have a living tower now, knock it down to 1% hp */
  pBas->hit = UMAX(1, hp * pBas->max_hit / 100 );

  /* save current cabal */
  pOld = room->area->pCabal;

  /* stick it into the bastion slot */
  army_to_room( pBas, room );
  army_to_slot( pBas, INROOM_BASTION, FALSE);

  /* check if changed hands */
  if (room->area->pCabal && !is_same_cabal(pOld, room->area->pCabal)){
    if (pOld == NULL)
      cp_event_army( pBas, pOld, room->area, CP_EVENT_AREA );
    else if (pOld != NULL){
      cp_event_army( pBas, pOld, room->area, CP_EVENT_CONQUER );
    }
  }
  return pBas;
}

/* creates a cabal bastion in the room the army is in */
ARMY_DATA* bastion_room( ROOM_INDEX_DATA* room, CABAL_DATA* pc, int hp){
  ARMY_INDEX_DATA* pai;
  char buf[MIL];

  if (room == NULL)
    return NULL;
  /* make sure the cabal has a prototype */
  else if ( (pai = get_army_index( pc->pIndexData->tower )) == NULL)
    return NULL;
  else if ( GET_CAB_CP( pc ) < pai->cost){
    cabal_echo(pc, "Coffers could not cover creation of bastion." );
    return NULL;
  }
  CP_CAB_GAIN( pc, -pai->cost);

  sprintf( buf, "%d %ss have been drawn from coffers to cover construction.", 
	   pai->cost,
	   pc->currency);
  cabal_echo(pc, buf );

  //put bastion in
  return (bastion( pai, room, pc, hp ));
}

int* gen_vars( int v0, int v1, int v2){
  static int vars[ARMY_VARS];

  memset(&vars, 0, sizeof(int) * ARMY_VARS);

  vars[0]	= v0;
  vars[1]	= v1;
  vars[2]	= v2;

  return vars;
}

//leaves a room, reports leaving if in room returns duration of travel
bool army_leave(ARMY_DATA* pa ){
  char buf[MIL];
  if (pa->in_room == NULL)
    return FALSE;
  else
    pa->vars[2] = pa->in_room->vnum;

  sprintf( buf, "Departing from %s.", pa->in_room->area->name );
  army_report( pa, buf, REPORT_NEUT, FALSE );

  if (pa->in_room && pa->in_room->people)
    act("$t leaves the area.",  pa->in_room->people, pa->short_desc, NULL, TO_ALL);
  army_from_room( pa );
  return TRUE;
}

/* moves an army to designated room vnum if possible 
 * will switch armies state if something happends along the way 
 * returns TRUE on normal uneventful move 
 *
 * Follows the path room by room and tests for patrols
 * If hostile ungarrisoned, defender detected:
   1) If attack slots open, attack
   2) If attacks slots not open enter state untill can attack or leave
*/
   
bool army_move( ARMY_DATA* pa ){
  PATH_QUEUE* curr = pa->path;
  ROOM_INDEX_DATA* last_room = NULL;

  for (;curr; curr = curr->next){
    last_room = curr->room;
    //make sure there are defenders
    if (curr->room->room_armies.defenders < 1)
      continue;
    //if defenders are busy with max attacking armies, move on
    else if (curr->room->room_armies.attackers > INROOM_MAXARMIES - INROOM_ATTACKERS)
      continue;
    //if this is an army with attack order to this room ignore
    else if (pa->order == AS_ATTA && pa->vars[0] == curr->room->vnum)
      continue;
    // possibility of intercept, enter rooom 
    army_to_room( pa, curr->room );
    //check for intercept
    if (!army_intercept( pa ))
      army_from_room( pa );
    else{
      return TRUE;
    }
  }
  //reached destination safely
  army_to_room( pa, last_room );
  clean_path_queue( pa->path );
  pa->path = NULL;

  if (pa->in_room && pa->in_room->people)
    act("$t enters the area.",  pa->in_room->people, pa->short_desc, NULL, TO_ALL);
  return TRUE;
}

/* returns an army to garrison */
bool army_return( ARMY_DATA* pa ){
  CMEMBER_DATA* cm = get_cmember( pa->commander, pa->pCabal );
  char buf[MIL];

  //army that has a commander
  if (cm != NULL){
    //report in
    sprintf( buf, "Returned to barracks (%s).", pa->commander);
    army_report( pa, buf, REPORT_NEUT, FALSE );

    if (IS_SET(pa->army_flags, ARMY_FLAG_ELITE))
      update_garrison_cm( cm, 0, 1);
    else
      update_garrison_cm( cm, 1, 0);
  }
  else{
    if (pa->pCabal->armies_ready < 10)
      pa->pCabal->armies_ready ++;
    sprintf( buf, "Returned to recruitment pool.");
    army_report( pa, buf, REPORT_NEUT, FALSE );
  }
  extract_army( pa );
  return TRUE;
}

/* builds a bastion or garrisons one of the spots */
bool army_garrison( ARMY_DATA* pa ){
  ARMY_DATA* pBas;
  int i;
  char buf[MIL];

  if (pa->in_room == NULL)
    return FALSE;
  else if (!can_bastion( NULL, pa, pa->in_room, pa->pCabal, TRUE ))
    return FALSE;

  /* bastion does not exist */
  if ( (pBas = get_bastion( pa->in_room )) == NULL){
    ROOM_INDEX_DATA* room = pa->in_room;
    CABAL_DATA* pc = pa->pCabal;

    //create a new bastion, elite army at 50%hp, otherwise 1&
    if (IS_SET(pa->army_flags, ARMY_FLAG_ELITE))
      pBas = bastion_room( room, pc,  50);
    else
      pBas = bastion_room( room, pc,  1);

    if (pBas == NULL){
      bug("armies.c>army_garrison: Expected a new bastion but found NULL.", 0);
    }
    else{
      set_army_commander( pBas, pBas->pCabal->name, pa->command_rank );
      //report in 
      sprintf( buf, "Constructed bastion[#%-4d] at %-20.20s``.", pBas->ID, pBas->in_room->name);
      army_report( pa, buf, REPORT_GOOD, TRUE );
      if (pa->in_room->people){
	act("$t construct $T in the area.",  
	    pa->in_room->people, 
	    pa->short_desc, 
	    pBas->short_desc, 
	    TO_ALL);
      }
    }

    //get rid of old army
    extract_army( pa );
    return TRUE;
  }

  /* bastion exists */
  /* get the slot we can garrison in */
  for (i = INROOM_DEFENDERS; i < INROOM_ATTACKERS; i++){
    if (pa->in_room->room_armies.armies[i] == NULL
	|| pa == pa->in_room->room_armies.armies[i]){

      /* set their order to idle */
      order_army( pa, AS_NONE, gen_vars(0, 0, 0));

      if (pa->in_room->people){
	act("$t garrison $T.",  
	    pa->in_room->people, 
	    pa->short_desc, 
	    pBas->short_desc, 
	    TO_ALL);
      }
      //report in
      sprintf( buf, "Garrisoned bastion at %-20.20s``.", pa->in_room->name);
      army_report( pa, buf, REPORT_GOOD, TRUE );

      //if cabal is different from bastion, switch sides
      if (!is_same_cabal(pa->pCabal, pBas->pCabal )){
	sprintf( buf, "Now under %s's command.", pBas->pCabal->name);
	army_report( pa, buf, REPORT_GOOD, TRUE );
	pa->pCabal = pBas->pCabal;
	set_army_commander( pa, pa->pCabal->name, RANK_ELDER -1 );
	sprintf( buf, "Joined your forces in %s.", pa->in_room->name);
	army_report( pa, buf, REPORT_GOOD, TRUE );
      }
      set_army_commander( pa, pa->pCabal->name, -1 );

      //set garrison bit so the support is increased when inserted into slot
      SET_BIT(pa->army_flags, ARMY_FLAG_GARRISON );

      /* insert into slot and gain armor */
      army_to_slot( pa, i, FALSE );
      pa->armor = pa->max_armor + pBas->armor;

      return TRUE;
    }
  }
  return FALSE;
}

//upgrades a bastion in the room
//extracts old bastion and checks for increase in friendly defender armor
bool army_fortify( ARMY_DATA* pa ){
  ARMY_DATA* pAr, *pBas;
  ROOM_INDEX_DATA* room = pa->in_room;
  CABAL_DATA* pc = pa->pCabal;
  int hit, i;
  bool fElite = IS_SET(pa->army_flags, ARMY_FLAG_ELITE) ? TRUE : FALSE;
  char buf[MIL];

  if (room == NULL)
    return FALSE;
  else if (!can_fortify( NULL, room, pa->pCabal, TRUE ))
    return FALSE;

  /* bastion must exists if can_fortify worked */
  if (pa->in_room->people){
    act("$t completes the fortifications.",  
	pa->in_room->people, 
	pa->short_desc, 
	NULL, 
	TO_ALL);
  }
  //report in 
  sprintf( buf, "Fortified bastion[#%-4d] at %s.", 
	   room->room_armies.armies[INROOM_BASTION]->ID,
	   room->name);
  army_report( pa, buf, REPORT_GOOD, TRUE );

  /* extract fortifying army */
  extract_army( pa );
  
  /* store current hp */
  hit = room->room_armies.armies[INROOM_BASTION]->hit;
  /* extract old bastion */
  extract_army(room->room_armies.armies[INROOM_BASTION]);

  //create an upgraded version 
  if ( (pBas = create_army( get_army_index( pc->pIndexData->tower_upgrade ), pc)) == NULL){
    bug("armies.c->army_fortify: could not create bastion. (%d)", pc->pIndexData->tower_upgrade);
    return FALSE;
  }

  pBas->hit = hit;
  //set upgrade start hp to 1/2 if not elite old bastion's hp
  if (!fElite)
    pBas->hit = 1 + pBas->hit / 2;
  
  /* put it in room and into bastion slot */
  army_to_room( pBas, room );
  army_to_slot( pBas, INROOM_BASTION, FALSE );

/* run through garrisons and reset armor to new number */
  for (i = INROOM_DEFENDERS; i < INROOM_ATTACKERS; i++){
    pAr = room->room_armies.armies[i];
    if (pAr == NULL || !IS_SET(pAr->army_flags, ARMY_FLAG_GARRISON ))
      continue;
    else
      pAr->armor = pAr->max_armor + pBas->armor;
  }
  return TRUE;
}

/* sets an army as patrol */
bool army_patrol( ARMY_DATA* pa ){
  int i;
  char buf[MIL];

  if (pa->in_room == NULL)
    return FALSE;
  else if (!can_patrol( NULL, pa, pa->in_room, pa->pCabal, TRUE ))
    return FALSE;

  /* get the slot we can patrol in */
  for (i = INROOM_DEFENDERS; i < INROOM_ATTACKERS; i++){
    if (pa->in_room->room_armies.armies[i] == NULL){

      /* insert into slot */
      army_to_slot( pa, i, FALSE );

      if (pa->in_room->people){
	act("$t begins to patrol the area.",  
	    pa->in_room->people, 
	    pa->short_desc, 
	    NULL, 
	    TO_ALL);
      }
      //report in
      sprintf( buf, "Patroling %s.", pa->in_room->name);
      army_report( pa, buf, REPORT_GOOD, TRUE );
      return TRUE;
    }
  }
  return FALSE;
}

//sets armies attacking each other
void set_attacking( ARMY_DATA* att, ARMY_DATA* def){
  att->attacking = def;
  if (def->attacking == NULL)
    def->attacking = att;
}

void stop_attacking( ARMY_DATA* pa ){
  int i;

  if (pa->in_room == NULL){
    ARMY_DATA* tpa, *pa_next;
    int hash;
    nlogf("BUG: stop_attacking: army not in room. (%s with ID %d)",
	  pa->noun, pa->ID);
    for (hash = 0; hash < ARMY_HASH_SIZE; hash++){
      for (tpa = army_list[hash]; tpa; tpa = pa_next){
	pa_next = tpa->next; 
	if (tpa->attacking == pa)
	  tpa->attacking = NULL;
      }
    }
    pa->attacking = NULL;
    return;
  }
  for (i = INROOM_BASTION; i < INROOM_MAXARMIES; i++){
    if (pa->in_room->room_armies.armies[i]
	&& pa->in_room->room_armies.armies[i]->attacking == pa)
      pa->in_room->room_armies.armies[i]->attacking = NULL;
  }
  pa->attacking = NULL;
}

//forces any allies of defender at war with attacker to attack attacker
//bastions checked last
void ally_attack_check( ARMY_DATA* att, ARMY_DATA* def, bool fQuiet ){
  ARMY_DATA* pa;
  int i;
  bool fGarr = FALSE;

  for (i = INROOM_ATTACKERS - 1; i >= INROOM_BASTION; i--){
    if ( (pa = att->in_room->room_armies.armies[i]) == NULL)
      continue;
    else if (pa == att)
      continue;
    else if (is_friendly(pa->pCabal, def->pCabal) != CABAL_FRIEND)
      continue;
    else if (is_friendly(pa->pCabal, att->pCabal) != CABAL_ENEMY)
      continue;
    else{
      if (i == INROOM_BASTION){
	if (fGarr)
	  set_state(pa, AS_BESE, 0, gen_vars(0,0,0));
	else
	  set_state(pa, AS_FIGH, 0, gen_vars(0,0,0));
	continue;
      }
      else if (i != INROOM_BASTION)
	fGarr = TRUE;

      if (pa->attacking)
	continue;

      if (!fQuiet && pa != def){
	char buf[MIL];

	if (is_same_cabal(def->pCabal, pa->pCabal)){
	  sprintf( buf, "Providing battle support for [#%-4d].", def->ID);
	  army_report( pa, buf, REPORT_GOOD, TRUE );
	}
	else{
	  sprintf( buf, "Providing support to %s's %s[#%-4d].", 
		   def->pCabal->name,
		   def->noun,
		   def->ID);
	  army_report( pa, buf, REPORT_GOOD, TRUE );
	  sprintf( buf, "%s's %s[#%-4d] providing support.", 
		   pa->pCabal->name,
		   pa->noun,
		   pa->ID);
	  army_report( def, buf, REPORT_GOOD, TRUE );
	}
      }
      set_attacking( pa, att );
    }
  }
}

/* sets an army as attacker, combat starts on next army_combat update */
bool army_attack( ARMY_DATA* pa ){
  ARMY_DATA* tar;
  int i;
  bool fQuiet = TRUE;
  char buf[MIL];

  if (pa->in_room == NULL)
    return FALSE;
  else if (!can_attack( NULL, pa, pa->in_room, pa->pCabal, TRUE ))
    return FALSE;

  /* get the slot we can attack from */
  if (pa->in_slot < 0){
    fQuiet = FALSE;
    for (i = INROOM_ATTACKERS; i < INROOM_MAXARMIES; i++){
      if (pa->in_room->room_armies.armies[i] == NULL){
	
	/* insert into slot */
	army_to_slot( pa, i, FALSE );
	//report ion
	sprintf( buf, "Engaging enemies.");
	army_report( pa, buf, REPORT_GOOD, TRUE );
	break;
      }
    }
  }
  if ( (tar = get_attack_target( pa->pCabal, pa->in_room)) == NULL){
    bug("armies.c>army_attack: Attacking without valid target. (%d)", pa->ID);
    return TRUE;
  }
  if (pa->in_room->people){
    act("$t begins battle with $T.",  
	pa->in_room->people, 
	pa->short_desc, 
	tar->short_desc, 
	TO_ALL);
  }
  //report to enemies
  sprintf( buf, "Attacked by %s[#%-4d] in %s.", 
	   pa->noun, 
	   pa->ID,
	   tar->in_room->area->name);
  army_report( tar, buf, REPORT_BAD, TRUE );
  set_attacking( pa, tar );
  ally_attack_check( tar, pa, fQuiet );
  return FALSE;
}

/* checks if an army can be intercepted in this room */
bool army_intercept( ARMY_DATA* pa ){
  ARMY_DATA* pTar = NULL, *pAr;
  char buf[MIL];
  int i;

  if (pa->in_room == NULL)
    return FALSE;

  /* check for any defender threats, then for slots open */
  for (i = INROOM_DEFENDERS; i < INROOM_MAXARMIES; i++){
    /* defender check */
    if (i < INROOM_ATTACKERS){
      if ( (pAr = pa->in_room->room_armies.armies[i]) == NULL)
	continue;
      else if (is_friendly( pa->pCabal, pAr->pCabal) != CABAL_ENEMY)
	continue;
      //found possible enemy
      if (pTar == NULL || pAr->hit < pTar->hit)
	pTar = pAr;
    }
    /* attacker check */
    else{
      //if there were no threats, break off
      if (pTar == NULL)
	return FALSE;
      //look for valid spot
      else if (pa->in_room->room_armies.armies[i] == NULL){
	/* insert into slot */
	army_to_slot( pa, i, FALSE );

	/* start the attack */
	set_attacking( pa, pTar );
	ally_attack_check(pa, pTar, TRUE );

	if (pa->in_room->people){
	  act("$T intercepts $t as it moves past.",  
	      pa->in_room->people, 
	      pa->short_desc, 
	      pTar->short_desc, 
	      TO_ALL);
	}
	//report in
	sprintf( buf, "Intercepted by enemy %s[#%-4d].", pTar->noun, pTar->ID);
	army_report( pa, buf, REPORT_BAD, TRUE );
	sprintf( buf, "Intercepted enemy %s[#%-4d]. Engaging.", pa->noun, pa->ID);
	army_report( pTar, buf, REPORT_BAD, TRUE );
	return TRUE;
      }//end if slot open
    }//end for attacker slots
  }//end for each slot

  /* if were are here, means there were targets, but no valid slots to enter */
  return FALSE;
}

/* sets an army as defender, combat starts on next army_combat update */
bool army_defend( ARMY_DATA* pa ){
  ARMY_DATA* tar, *pAr;
  int i;
  bool fQuiet = TRUE;
  char buf[MIL];

  if (pa->in_room == NULL)
    return FALSE;
  else if (!can_defend( NULL, pa, pa->in_room, pa->pCabal, TRUE ))
    return FALSE;

  /* get the slot we can defend from */
  if (pa->in_slot < 0){
    fQuiet = FALSE;
    for (i = INROOM_DEFENDERS; i < INROOM_ATTACKERS; i++){
      if (pa->in_room->room_armies.armies[i] == NULL){
	
	/* insert into slot */
	army_to_slot( pa, i, FALSE );

	//report in
	sprintf( buf, "Defending %s from enemies.", pa->in_room->area->name);
	army_report( pa, buf, REPORT_GOOD, TRUE );
	break;
      }
    }
  }
  if ( (tar = get_defend_target( pa->pCabal, pa->in_room)) == NULL){
    bug("armies.c>army_defend: Defending without valid target. (%d)", pa->ID);
    return TRUE;
  }
  if (pa->in_room->people){
    act("$t manouvers to defend the area.",  
	pa->in_room->people, 
	pa->short_desc, 
	NULL,
	TO_ALL);
  }
  //report to enemies
  sprintf( buf, "Attacked by %s[#%-4d] in %s.", 
	   pa->noun, 
	   pa->ID,
	   tar->in_room->area->name);
  army_report( tar, buf, REPORT_BAD, TRUE );
  set_attacking( pa, tar );
  ally_attack_check( tar, pa, fQuiet );

  /* retarget enemies */
  for (i = INROOM_ATTACKERS; i < INROOM_MAXARMIES; i++){
    if ( (pAr = pa->in_room->room_armies.armies[i]) != NULL){
      if ( (tar = get_attack_target( pAr->pCabal, pa->in_room)) == NULL){
	bug("armies.c>army_defend: Attacker without valid target. (%d)", pa->ID);
	return TRUE;
      }
      set_attacking( pAr, tar );
    }
  }

  return FALSE;
}

//initilizes an empty report queue for use
void init_repq( REPORT_QUEUE* q){
  q->bot.prev = NULL;
  q->bot.next = &q->top;
  q->top.prev = &q->bot;
  q->top.next = NULL;

  q->size = 0;
}

//create a new report
ARMY_REPORT* new_report( char* string ){
  ARMY_REPORT* pRep = malloc( sizeof( *pRep )); 

  memset( pRep, 0, sizeof( ARMY_REPORT ));

  pRep->string = str_dup( string );
  pRep->time   = mud_data.current_time;

  return pRep;
}

//frees a report
void free_report( ARMY_REPORT* pRep ){

  if (!IS_NULLSTR( pRep->string ))
    free_string( pRep->string );
  
  free( pRep );
}

//pops a report off the top of queue
void pop_report(  REPORT_QUEUE* q ){
  ARMY_REPORT* pRep;

  if (q->size < 1)
    return;
  else
    pRep = q->top.prev;

  pRep->next->prev	= pRep->prev;
  pRep->prev->next	= pRep->next;

  q->size --;

  free_report( pRep );
}

//inserts a report at bottom of queue
void add_report( REPORT_QUEUE* q, ARMY_REPORT* pRep ){
  if (q == NULL || pRep == NULL)
    return;
  
  pRep->next		= q->bot.next;
  pRep->next->prev	= pRep;
  pRep->prev		= &q->bot;
  pRep->prev->next	= pRep;

  if (++q->size > ARMY_MAXREPORT)
    pop_report( q );
}


//adds a report into a queue
void add_army_report( REPORT_QUEUE* q, char* string ){
  ARMY_REPORT* pRep;

  if (IS_NULLSTR(string))
    return;
  else
    pRep = new_report( string );
  add_report( q, pRep );
}

bool is_commander( CHAR_DATA* ch, ARMY_DATA* pa ){
  if (IS_NPC(ch))
    return FALSE;
  else if (!is_same_cabal( ch->pCabal, pa->pCabal))
    return FALSE;
  else if (IS_ELDER(ch))
    return TRUE;
  else if (ch->pcdata->rank > pa->command_rank)
    return TRUE;
  else
    return (!str_cmp(ch->name, pa->commander));
}
  
//counts how many attackers in area
int count_attackers( ROOM_INDEX_DATA* room ){
  int i;
  int count = 0;
  
  for (i = INROOM_ATTACKERS; i < INROOM_MAXARMIES; i++){
    if (room->room_armies.armies[i] != NULL)
      count++;
  }
  return count;
}

//counts how many attackers in area
int count_defenders( ROOM_INDEX_DATA* room, bool fCountBastion ){
  int i;
  int count = 0;
  if (fCountBastion)
    i = INROOM_BASTION;
  else
    i = INROOM_DEFENDERS;

  for (; i < INROOM_ATTACKERS; i++){
    if (room->room_armies.armies[i] != NULL)
      count++;
  }
  return count;
}

//returns poitner to bastion if there is one in area
ARMY_DATA* get_bastion(ROOM_INDEX_DATA* room ){
  return (room->room_armies.armies[INROOM_BASTION]);
}

/* get a target defender for an attacker */
ARMY_DATA* get_attack_target( CABAL_DATA* pc, ROOM_INDEX_DATA* pRoom ){
  ARMY_DATA* pa, *pBas;
  int i = 0;
  int low_hp = 9999;
  int low_spot = -1;

  if (pc == NULL ||pRoom == NULL)
    return NULL;

  /* check each defender spot to see if they are at war with us */
  for (i = INROOM_DEFENDERS; i < INROOM_ATTACKERS; i++){
    if ( (pa = pRoom->room_armies.armies[i]) == NULL)
      continue;
    else if (is_friendly(pc, pa->pCabal) != CABAL_ENEMY)
      continue;
    //got a target
    if (pa->hit < low_hp){
      low_hp = pa->hit;
      low_spot = i;
    }
  }
  /* get bastion from here */
  if (low_spot == -1 
      && (pBas = get_bastion( pRoom )) != NULL
      && is_friendly( pc, pBas->pCabal) == CABAL_ENEMY)
    low_spot = pBas->in_slot;
  
  if (low_spot == -1)
    return NULL;
  else
    return (pRoom->room_armies.armies[low_spot]);
}

/* get a target attacker for a defender */
ARMY_DATA* get_defend_target( CABAL_DATA* pc, ROOM_INDEX_DATA* pRoom ){
  ARMY_DATA* pa;
  int i = 0;
  int low_hp = 9999;
  int low_spot = -1;

  if (pc == NULL ||pRoom == NULL)
    return NULL;

  /* check each attacker spot to see if they are at war with us */
  for (i = INROOM_ATTACKERS; i < INROOM_MAXARMIES; i++){
    if ( (pa = pRoom->room_armies.armies[i]) == NULL)
      continue;
    else if (is_friendly(pc, pa->pCabal) != CABAL_ENEMY)
      continue;
    //got a target
    if (pa->hit < low_hp){
      low_hp = pa->hit;
      low_spot = i;
    }
  }
  
  if (low_spot == -1)
    return NULL;
  else
    return (pRoom->room_armies.armies[low_spot]);
}


//check if an army is dead
bool is_army_dead( ARMY_DATA* pa ){
  if (pa->hit <= 0)
    return TRUE;
  else
    return FALSE;
}

//creates an army corpse object in room
void army_make_corpse( ARMY_DATA* pa, ROOM_INDEX_DATA* room ){
  OBJ_DATA*  obj;
  char buf[MIL];

  if (room == NULL)
    return;
  if (pa->pIndexData->type == ARMY_TYPE_BASTION)
    obj = create_object( get_obj_index( OBJ_VNUM_TOWER_CORPSE), 60 );
  else
    obj = create_object( get_obj_index( OBJ_VNUM_ARMY_CORPSE), 60 );
  if (obj == NULL)
    return;
  

  //set the strings
  sprintf( buf, obj->name, pa->noun );
  free_string( obj->name );
  obj->name = str_dup( buf );

  sprintf( buf, obj->short_descr, pa->short_desc );
  free_string( obj->short_descr );
  obj->short_descr = str_dup( buf );

  sprintf( buf, obj->description, pa->short_desc);
  free_string( obj->description );
  obj->description = str_dup( buf );

  obj->timer = 24;
  obj_to_room( obj, room );

}

//forces all the defenders to return
void return_defenders( ROOM_INDEX_DATA* room ){
  ARMY_DATA* pa;
  int i;

  if (room->room_armies.defenders < 1)
    return;
  for (i =INROOM_DEFENDERS; i < INROOM_ATTACKERS; i++){
    if ( (pa = room->room_armies.armies[i]) == NULL)
      return;
    else{
      stop_attacking( pa );
      order_army( pa, AS_RETU, gen_vars( 0, 0, 0));
    }
  }
}

//performs necessary actions for army to die
void army_death( ARMY_DATA* att, ARMY_DATA* def ){
  char buf[MIL];
  char* word_room, *word;

  if (def->in_room == NULL){
    bug("armies.c>army_death: Defender is not in room. (ID%d)", def->ID);
  }
  /* if this is bastion being killed, return defenders */
  if (def->in_room && def->pIndexData->type == ARMY_TYPE_BASTION){
    return_defenders( def->in_room );
  }
/* SELF KILL */
  if (att == def){
  //show echo
    if (def->pIndexData->type == ARMY_TYPE_BASTION){
      word = "Dismantled";
      word_room = "dismantled";
    }
    else{
      word = "disbanded";
      word_room = "disbanded";
    }
    if (def->in_room && def->in_room->people){
      if (def->short_desc[strlen(def->short_desc)] == 's')
	act("$t have been $T!", def->in_room->people, def->short_desc, word_room, TO_ALL);
      else
	act("$t has been $T!", def->in_room->people, def->short_desc, word_room, TO_ALL);
    }
    //echo to cabal of defeated army
    sprintf(buf, "%s %s[#%-4d].", word, def->noun, att->ID );
    army_report( def, buf, REPORT_BAD, TRUE );
  }
/* NORMAL KILL */
  else{
    //show echo
    if (def->pIndexData->type == ARMY_TYPE_BASTION){
      word = "Captured";
      word_room = "CAPTURED";
    }
    else{
      word = "Defeated";
      word_room = "DEFEATED";
    }
    //echo to room
    if (def->in_room && def->in_room->people){
      act("$t has been $T!", def->in_room->people, def->short_desc, word_room, TO_ALL);
    }
    //echo to cabal of defeated army
    sprintf(buf, "%s by %s[#%-4d].", word, att->noun, att->ID );
    army_report( def, buf, REPORT_BAD, TRUE );
    
    //echo to cabal of attacking army
    if (def->pIndexData->type == ARMY_TYPE_BASTION){
      ROOM_INDEX_DATA* room = def->in_room;
      ARMY_INDEX_DATA* pai;
      
      //reward
      cp_event_army( att, def->pCabal, NULL, CP_EVENT_SIEGE );

      //try to get index for new tower, we always downgrade when capturing
      pai = get_army_index( att->pCabal->pIndexData->tower);
      if (pai == NULL){
	bug("armies.c>army_death: Could not get tower index for cabal %d", att->pCabal->vnum);
	extract_army( def );
	return;
      }
      
      //extract the defeated bastion, and put in a fresh one of ours
      extract_army( def );
      def = bastion( pai, room, att->pCabal,  1);
      
      sprintf(buf, "Captured enemy bastion.[#%-4d].", def->ID );
      army_report( att, buf, REPORT_GOOD, TRUE );
      return;
    }
    else{
      cp_event_army( att, def->pCabal, NULL, CP_EVENT_VICTORY );
      sprintf(buf, "Triumphed over %s.", def->noun );
      army_report( att, buf, REPORT_GOOD, TRUE );
    }
  }

  /* COMMON THINGS TO DO AFTER DEATH */
  army_make_corpse( def, def->in_room );
  extract_army( def );
}

/* Meat and potatoes of army combat
   Phases:
   1) Precombat
     1.1) Are we fighting anyone (return if not)
     2.2) Zero dam, splash_dam
     2.3) Set state to AS_FIGH
   2) Combat
     2.1) Calculate damage
     2.2) Splash_dam = 1/4 dam if flying
     2.3) Apply damage minus armor absorption armor * dam / 100;
     2.4) Apply splash damage but never to less then 1 hit
   3) Post Combat
     3.1) Is enemy dead? (army_death)
     3.2) Return damage done
*/
     
int army_combat( ARMY_DATA* pa ){
  int dam, dam_splash = 0;
  //  int a_sup, d_sup;

  if (pa->attacking == NULL)
    return 0;
  if (pa->pIndexData->type == ARMY_TYPE_BASTION 
      && pa->state != AS_FIGH)
    return 0;
  else if (pa->state != AS_FIGH)
    set_state( pa, AS_FIGH, 0, gen_vars(0, 0, 0));


  dam = dice( pa->off_dice[DICE_NUMBER], pa->off_dice[DICE_TYPE]);
  dam += pa->off_dice[DICE_BONUS];

  //if no members on the defending cabal, lower damage
  if (pa->attacking->pCabal && pa->attacking->pCabal->present < 1)
    dam = ARMY_NODEFENDER_MULT * dam / 100;
  else
    dam = ARMY_DAMAGE_MULT * dam / 100;

  /* disabled as not needed
  //underdog modifier for cabals with low support.
  if (pa->pCabal->present > 0 
      && (a_sup = pa->pCabal->support[ECO_INCOME]) < CABAL_UNDERDOG_SUP
      && (d_sup = pa->attacking->pCabal->support[ECO_INCOME]) > CABAL_UNDERDOG_SUP){
    dam = URANGE(dam, dam * (1 + (4 * (CABAL_UNDERDOG_SUP - a_sup)) / CABAL_UNDERDOG_SUP), 5 * dam);
  }
  */
  //boost from leadership
  if (pa->pCabal->present > 0 && pa->in_room){
    CHAR_DATA* ch = pa->in_room->people;
    int temp = 0, mod = 0;
    
    for (; ch; ch = ch->next_in_room){
      if (ch->pCabal == NULL || IS_NPC(ch) || !IS_AWAKE(ch) || !is_same_cabal(ch->pCabal, pa->pCabal))
	continue;
      else if ( (temp = get_skill( ch, gsn_leadership)) > mod){
	mod = temp;
	check_improve( ch, gsn_leadership, TRUE, 1);
      }
    }
    if ( pa->attacking->pCabal->present > 0 )
      dam += mod * dam / 100;
  }
    
  if (IS_ARMY(pa, ARMY_FLAG_FLYING))
    dam_splash = dam / 4;

  /* adjust damage for armor */
  dam = (100 - pa->attacking->armor) * dam / 100;

  /* damage target */
  pa->attacking->hit -= dam;

  /* DEBUG 
  if (pa->pIndexData->type == ARMY_TYPE_BASTION && pa->in_room->people){
    char buf[MIL];
    sprintf( buf, "%s did %d damage to %s(%d/%d)",
	     pa->short_desc,
	     dam,
	     pa->attacking->short_desc,
	     pa->attacking->hit,
	     pa->attacking->max_hit);
    act(buf, pa->in_room->people, NULL, NULL, TO_ALL);
  }
  */

  /* splash damage */
  if (dam_splash){
    ARMY_DATA* pAr;
    int i;
    for (i = INROOM_MAXARMIES - 1; i >= INROOM_BASTION; i--){
      if ( (pAr = pa->in_room->room_armies.armies[i]) == NULL)
	continue;
      else if (pAr->attacking != pa)
	continue;
      else if (pa->attacking == pAr)
	continue;
      else if (pAr->hit > 1){
	pAr->hit = UMAX( 1, pAr->hit - (100 - pAr->armor) * dam_splash / 100);
	dam_splash /= 2;
      }
    }
  }
  if (is_army_dead(pa->attacking)){
    ARMY_DATA* victim = pa->attacking;
    stop_attacking( pa );
    army_death( pa, victim);
  }
  return dam;
}

/* MAIN COMBAT UPDATE, called from army_update */
void army_combat_update(){
  ARMY_DATA* pa, *pa_next;
  int hash;
  
  /* to avoid problems with extraction, there are following pahses *
     - Start of loop
     1) Is this army dead? (army_death)
     2) Is army in room and in slot and attacking ? (skip army if not )
     3) Perform combat in room that this army is in.
       3.0) do ally_assist check
       3.1) calculate damage to person we are attacking
       3.2) apply the damage to person we are attacking
       3.3) is the person we are attacking dead? (army_death)
  */

  for (hash = 0; hash < ARMY_HASH_SIZE; hash++){
    for (pa = army_list[hash]; pa; pa = pa_next){
      pa_next = pa->next;
      
      /* if army is dead, kill it and continue */
      if (is_army_dead( pa )){
	stop_attacking( pa );
	army_death( pa, pa );
	continue;
      }
      else if (pa->in_room == NULL || pa->in_slot < 0 || pa->attacking == NULL)
	continue;

      /* ally assist check */
      ally_attack_check( pa, pa->attacking, FALSE );

      /* rudimentary ai for cabal with no members */
      /* called only if this is a bastion */
      /* disabled as not needed
      if (pa->in_slot == INROOM_BASTION 
	  && (pa->state == AS_FIGH || pa->state == AS_BESE))
	army_cabal_ai( pa );
      */
      /* perform combat for everyone in room */
      army_combat( pa );

      /* if army is dead, kill it and continue */
      if (is_army_dead( pa )){
	stop_attacking( pa );
	army_death( pa, pa );
	continue;
      }
      /* END OF COMBAT UPDATE FOR THIS ARMY */
    }//end for army in hash cell
  }//end for hash cell
}

/* ARMY PATH FUNCTIONS */
//creates an army path, returns duration of movement or FALSE
int set_army_path( ARMY_DATA* pa, int src_vnum, int dest_vnum ){
  ROOM_INDEX_DATA* src, *dest;
  PATH_QUEUE* path;
  int dist = 0, dur;

  if ( (src = get_room_index( src_vnum )) == NULL)
    return -1;
  if ( (dest = get_room_index( dest_vnum )) == NULL)
    return -1;

  if ( (path = generate_path(src, dest, ARMY_PATH_MAX, TRUE, &dist, pa->pCabal)) == NULL)
    return -1;
  
  clean_path();
  if (pa->path)
    clean_path_queue( pa->path );
  pa->path = path;
  pa->src_room = src_vnum;

  /* get duration of move */
  if (src->area == dest->area)
    dur = 0;
  else
    dur = UMIN(10, dist / 15 );

  if (pa->pCabal && IS_CABAL(pa->pCabal, CABAL_ROYAL))
    dur = dur / 2;
  return (UMAX(0, dur));
}
  
/* calculates support this army uses in given area */
int get_army_support( ARMY_DATA* pa, AREA_DATA* area ){

  if (pa == NULL )
    return 0;
  else if (!IS_ARMY(pa, ARMY_FLAG_GARRISON))
    return 0;
  else 
    return (pa->pIndexData->support * area->support/(100 * area->bastion_max));
}

/* writes a single army to file */
void fwrite_army( FILE* fp, ARMY_DATA* pa ){
  char buf[MIL];
  int i = 0;

  if (pa == NULL || pa->pCabal == NULL)
    return;

  //Easy stuff
  fprintf( fp, "#%d\n",			pa->pIndexData->vnum );
  fprintf( fp, "Cabal %s~\n",		pa->pCabal->name );
  fprintf( fp, "Comm %s~\n",		pa->commander );
  fprintf( fp, "CommRank %d\n",		pa->command_rank );
  fprintf( fp, "HitMax %d\n",		pa->max_hit );
  fprintf( fp, "Hit %d\n",		pa->hit );
  fprintf( fp, "Arm %d\n",		pa->armor );
  fprintf( fp, "ArmMax  %d\n",		pa->max_armor );

  //State/Order vars
  fprintf( fp, "Vars %d ", ARMY_VARS);
  for (i = 0; i < ARMY_VARS; i++){
    fprintf( fp, "%d %d   ",		pa->ovars[i], pa->vars[i] );
  }
  fprintf( fp, "\n");

  //Offense dice
  fprintf( fp, "OffDice %d ", DICE_BONUS );
  for (i = 0; i < DICE_BONUS; i++){
    fprintf( fp, "%d ", pa->off_dice[i]);
  }
  fprintf( fp, "\n" );

  //Things that can be taken from index
  if (pa->order)
    fprintf( fp, "Order %d\n",		pa->order );
  if (pa->state)
    fprintf( fp, "State %d\n",		pa->state );
  if (pa->lifetime)
    fprintf( fp, "Life  %d\n",		pa->lifetime);
  if (pa->army_flags != pa->pIndexData->army_flags)
    fprintf( fp, "Flags %s\n",		fwrite_flag( pa->army_flags, buf ) );
  if (pa->cabal_ai_life)
    fprintf( fp, "AiLife  %d\n",		pa->cabal_ai_life);
  
/* loaded from indexes each time 
  if (str_cmp( pa->noun, pa->pIndexData->noun))
    fprintf( fp, "Noun %s~\n",		pa->noun );
  if (str_cmp( pa->short_desc, pa->pIndexData->short_desc))
    fprintf( fp, "Short %s~\n",		pa->short_desc );
  if (str_cmp( pa->long_desc, pa->pIndexData->long_desc))
    fprintf( fp, "Long %s~\n",		pa->long_desc );
  if (str_cmp( pa->desc, pa->pIndexData->desc))
    fprintf( fp, "Desc %s~\n",		pa->desc );
*/

  //things that might or might not be there
  if (pa->path){
    PATH_QUEUE* curr = pa->path;
    while (curr->next){curr = curr->next;}
    fprintf( fp, "Path %d %d\n",	pa->src_room, curr->room->vnum );
  }
  if (pa->in_room)
    fprintf( fp, "Room %d %d\n",	pa->in_room->vnum, pa->in_slot );
  if (pa->attacking)
    fprintf( fp, "Attack %d\n", pa->attacking->in_slot );

  //put some space before next one
  fprintf( fp, "END\n\n");
}

/* writes ALL armies to file */
bool fwrite_armies( FILE* fp ){
  AREA_DATA* pe;
  ARMY_DATA* pa;
  int hash;

  for (hash = 0; hash < ARMY_HASH_SIZE; hash++){
    for (pa = army_list[hash]; pa; pa = pa->next){
      //write army
      fwrite_army( fp, pa );
    }
  }
  fprintf( fp, "#0\n" );

  //write the status of area influences
  for (pe = area_first; pe; pe = pe->next){
    /* never write the 0th vnum */
    if (pe->vnum == 0)
      continue;
    fprintf( fp, "#%d %d %d ", pe->vnum, MAX_CABAL, pe->pCabal ? pe->pCabal->vnum : 0);
    for (hash = 0; hash < MAX_CABAL; hash++){
      fprintf( fp, "%d ", IS_AREA(pe, AREA_CABAL) ? 0 : pe->cabal_influence[hash]);
    }
    fprintf( fp, "\n");
  }
  fprintf( fp, "#0\n");

  return TRUE;
}

/* reads influence of a single area from file */
void fread_influence( FILE* fp, int vnum ){
  AREA_DATA* pe = get_area_data( vnum );
  CABAL_DATA* pCab = NULL;
  int max = fread_number( fp );
  int cab = fread_number( fp );
  int i;

  if (pe == NULL){
    bug("armise.c>fread_influence: could not load area %d.", vnum );
  }
  if ( cab ){
    if ((pCab = get_cabal_vnum( cab )) == NULL){
      bug("armise.c>fread_influence: could not assign cabal %d.", cab );
    }
    else if (!IS_AREA(pe, AREA_CABAL))
      pe->pCabal = pCab;
  }

  for (i = 0; i < max; i++){
    if (pe && max <= MAX_CABAL)
      pe->cabal_influence[i] = fread_number( fp );
    else
      fread_number( fp );
  }
}
  
/* read a single army from file */
ARMY_DATA* fread_army( FILE* fp, int vnum ){
  ARMY_DATA* pa;
  ARMY_INDEX_DATA* pAi;
  char *word = NULL;
  bool fMatch = FALSE, fExtract = FALSE;
  int i = 0;

  if ( vnum < 1 )
    return NULL;
  else if ( (pAi = get_army_index( vnum )) == NULL){
    bug("fread_army: Could not load index %d", vnum);
    return NULL;
  }
  //get a new army data
  pa = create_army( pAi, NULL );

  //reset any data here that is NOT 0 normaly
  pa->att_slot = -1;

  /* start reading the data in */
  for (; ;){
    fExtract = FALSE;
    word = feof(fp) ? "END" : fread_word( fp );

    //select the commands
    switch( UPPER(word[0]) ){
    case 'A':
      AKEY(  "AiLife",		pa->cabal_ai_life,	fread_number( fp ) );
      AKEY(  "Arm",		pa->armor,		fread_number( fp ) );
      AKEY(  "ArmMax",		pa->max_armor,		fread_number( fp ) );
      AKEY(  "Attack",		pa->att_slot,		fread_number( fp ) );
      break;
    case 'C':
      if (!str_cmp( "Cabal", word )){
	CABAL_DATA* pCabal;
	char* name = fread_string( fp );

	if ( (pCabal = get_cabal( name )) == NULL){
	  char buf[MIL];
	  sprintf( buf, "fread_army:Cabal %s not found.", name );
	  bug( buf, 0 );
	  fExtract = TRUE;
	}
	free_string( name );
	pa->pCabal = pCabal;

	fMatch = TRUE;
	break;
      }
      AKEYS( "Comm",		pa->commander,		fread_string( fp ) );
      AKEY(  "CommRank",	pa->command_rank,	fread_number( fp ) );
      break;
    case 'D':
      AKEYS( "Desc",		pa->desc,		fread_string( fp ) );
      break;
    case 'E':
      if (!str_cmp("END", word)){
	if (fExtract){
	  extract_army( pa );
	  return NULL;
	}
	return pa;
      }
      break;
    case 'F':
      AKEY(  "Flags",		pa->army_flags,		fread_flag( fp )   );
      break;
    case 'H':
      AKEY(  "Hit",		pa->hit,		fread_number( fp ) );
      AKEY(  "HitMax",		pa->max_hit,		fread_number( fp ) );
      break;
    case 'L':
      AKEY(  "Life",		pa->lifetime,		fread_number( fp ) );
      AKEYS( "Long",		pa->long_desc,		fread_string( fp ) );
      break;
    case 'N':
      AKEYS( "Noun",		pa->noun,		fread_string( fp ) );
      break;
    case 'O':
      if (!str_cmp( "OffDice", word )){
	int max = fread_number( fp );

	for (i = 0; i < max && i < DICE_BONUS; i++){
	  pa->off_dice[i]	=	fread_number( fp );
	}
	
	fMatch = TRUE;
	break;
      }
      AKEY(  "Order",		pa->order,		fread_number( fp ) );
      break;
    case 'P':
      if (!str_cmp( "Path", word)){
	int src_room	=	fread_number( fp );
	int des_room	=	fread_number( fp );
	if (set_army_path( pa, src_room, des_room ) < 0){
	  char buf[MIL];
	  sprintf( buf, "fread_army: could not load path from %d to %d.", src_room, des_room );
	  bug( buf, 0);
	}
	fMatch = TRUE;
	break;
      }
      break;
    case 'R':
      if (!str_cmp("Room", word)){
	int vnum	= fread_number( fp );
	int slot	= fread_number( fp );
	ROOM_INDEX_DATA* room = get_room_index( vnum );

	if (room == NULL){
	  bug( "fread_army: could not place army in room %d.", vnum );
	  fExtract = TRUE;
	}
	else{
	  army_to_room( pa, room );
	  if (slot >= 0 )
	    army_to_slot( pa, slot, FALSE );
	}
	fMatch = TRUE;
	break;
      }
    case 'S':
      AKEYS( "Short",		pa->short_desc,		fread_string( fp ) );
      AKEY(  "State",		pa->state,		fread_number( fp ) );
      break;
    case 'V':
      if (!str_cmp( "Vars", word )){
	int max = fread_number( fp );

	for (i = 0; i < max && i < ARMY_VARS; i++){
	  pa->ovars[i]	=	fread_number( fp );
	  pa->vars[i]	=	fread_number( fp );
	}
	
	fMatch = TRUE;
	break;
      }
      break;
    }//end select
    if ( !fMatch ){
      bugf("fread_army: [%s] no match.", word);
      fread_to_eol( fp );
    }
  }//end for

  if (fExtract)
    extract_army( pa );
  return NULL;
}

void fread_influences( FILE* fp ){
  char c;
  int vnum;

  for (;;){
    c = fread_letter( fp );

    if (c != '#'){
      bug("fread_influences: # not found.", 0);
      exit( 1 );
    }
    else
      vnum = fread_number( fp );
    if (vnum == 0)
      break;
    fread_influence( fp, vnum);
  }
}

void fread_armies( FILE* fp ){
  char c;
  int vnum;

  for (;;){
    c = fread_letter( fp );

    if (c != '#'){
      bug("fread_armies: # not found.", 0);
      exit( 1 );
    }
    else
      vnum = fread_number( fp );
    if (vnum == 0)
      break;
    else if (fread_army( fp, vnum) == NULL){
      bug("fread_armies: could not read army vnum %d", vnum );
    }
  }
}

//repoints any data that needs repointing after loading armies from save files
void fix_armies(){
  ARMY_DATA* pa;
  ARMY_DATA* pTar;
  int hash;

  for (hash = 0; hash < ARMY_HASH_SIZE; hash++){
    for (pa = army_list[hash]; pa; pa = pa->next){
      if (pa->att_slot < 0)
	continue;
      else if ( pa->in_room == NULL){
	bug("fix_armies: army attacking in NULL room. ID:%d", pa->ID);
	continue;
      }
      else if ( (pTar = pa->in_room->room_armies.armies[pa->att_slot]) == NULL){
	bugf("fix_armies: Army attacking a null slot. ID%d Slot %d", pa->ID, pa->att_slot);
	continue;
      }
      else
	pa->attacking = pTar;
    }
  }
}

//sets charactery army focus onto a given army id, returns TRUE on success
//ID of -1 sets to the room person is in
bool set_focus( CHAR_DATA* ch, ROOM_INDEX_DATA* focus ){
  ROOM_INDEX_DATA* last = GET_FOCUS(ch);
  if (focus == NULL){
    SET_LASTFOCUS( ch, GET_FOCUS(ch) );
    SET_FOCUS( ch, focus );
    return TRUE;
  }
  SET_FOCUS( ch, focus );
  if (last == NULL)
    SET_LASTFOCUS( ch, focus );
  else
    SET_LASTFOCUS( ch, last );
  return TRUE;
}
  

//returns focus of an army
ROOM_INDEX_DATA* get_army_focus( CHAR_DATA* ch, int ID ){
  ARMY_DATA* pa;

  if (IS_NPC(ch))
    return NULL;
  else if (ch->in_room == NULL)
    return NULL;
  else if (ID < 0){
    return ch->in_room;
  }

  if ( (pa = get_army_world( ID )) == NULL || pa->in_room == NULL)
    return NULL;
  return pa->in_room;
}

//SHOWS ARMY COMMAND PROMPT
/*
A = Attack,	G = Garrison
P = Patrol,	F = Fortify
H = Hold,	B = Barracks

C = Conscript,	T = Train
R = Release,

Z = Zoom,	M = Map
O = Orders,	Q = Queue

? = Help

*/
void show_army_prompt( CHAR_DATA* ch ){
  bool fBlind = FALSE;

  if (IS_AFFECTED(ch, AFF_BLIND) || IS_AFFECTED2(ch, AFF_TERRAIN) || is_affected(ch, gsn_forest_mist)){
    fBlind = TRUE;
  }

  /* DEBUG PROMPT
  sendf( ch, "\n\r<%d: %d/%d.%d/%d >[APHGFB]-[CTR]-[ZMOQ]-[?] Map#%-3d: %-20.20s``",
	 GARR_RED( ch ),
	 GARR_NOR( ch ),
	 ch->pcdata->member->armies[TIMN],
	 GARR_UPG( ch ),
	 ch->pcdata->member->armies[TIMU],
	 GET_FOCUS(ch) ? GET_FOCUS(ch)->area->vnum : 0,
	 GET_FOCUS(ch) ? GET_FOCUS(ch)->area->name: "???");
  */
  sendf( ch, "\n\r<%d: %d.%d >[APHGFB]-[CTR]-[ZMOQ]-[?] Map#%-3d: %-20.20s``",
	 GARR_RED( ch ),
	 GARR_NOR( ch ),
	 GARR_UPG( ch ),
	 GET_FOCUS(ch) ? GET_FOCUS(ch)->area->vnum : 0,
	 GET_FOCUS(ch) ? fBlind ? "???" : GET_FOCUS(ch)->area->name : "???");
}

//shows standart army command info
void show_army_screen( CHAR_DATA* ch, ROOM_INDEX_DATA* room ){
  TOWER_DATA* tow = get_tower_data( get_parent(ch->pCabal), room->area);
  bool fBlind = FALSE;

  if (IS_AFFECTED(ch, AFF_BLIND) || IS_AFFECTED2(ch, AFF_TERRAIN) || is_affected(ch, gsn_forest_mist)){
    fBlind = TRUE;
  }

  sendf( ch, "%-20.20s`` %d/%d%s\n\r%-25s``\n\r",
	 fBlind ? "???" : room->area->name,
	 tow ? tow->towers : 0,
	 room->area->bastion_max,
	 tow && tow->fRein ? "R" : "",
	 fBlind ? "???" : room->name);
  send_to_char("\n\r",ch);
  if (room->room_armies.count){
    examine_room_armies(ch, &room->room_armies );      
  }
  else
    send_to_char("No units spotted.\n\r", ch);
}

//shows report queue
void army_order_queue( CHAR_DATA* ch ){
  
  show_report_q(&get_parent(ch->pCabal)->report_q, ch );
}

//shows world or specific map
void army_order_map( CHAR_DATA* ch, char* argument ){
  int vnum = atoi( argument );
  AREA_DATA* area;
  
  if (IS_AFFECTED(ch, AFF_BLIND) || IS_AFFECTED2(ch, AFF_TERRAIN) || is_affected(ch, gsn_forest_mist)){
    send_to_char("You can't see the map!\n\r", ch);
    return;
  }
  if (IS_NULLSTR(argument)){
    show_area_armies( ch, GET_FOCUS(ch)->area, ch->pCabal );
    return;
  }
  else if (vnum){
    if ( (area = get_area_data( vnum )) == NULL){
      send_to_char("No such map.\n\r", ch);
      return;
    }
    show_area_armies( ch, area, ch->pCabal );
    return;
  }
  else if (!str_prefix( argument, "world")){
    show_world_armies( ch, ch->pCabal );
    return;
  }
  else{
    CABAL_DATA* pc = get_cabal( argument );
    if ( (pc = get_parent(pc)) == NULL){
      send_to_char("No such cabal.\n\r", ch);
      return;
    }
    show_world_armies( ch, pc);
    return;
  }
}

//train order
void army_order_muster( CHAR_DATA* ch, char* argument ){
  CABAL_DATA* pc = get_parent( ch->pCabal );
  ARMY_INDEX_DATA* pai;
  int cost, val;

  if ( (pai = get_army_index( pc->pIndexData->army)) == NULL){
    send_to_char("Your cabal has no armies to be recruited.\n\r", ch );
    return;
  }
  else
    cost = pai->cost;
  
  /* get number to recruit */
  if(IS_NULLSTR( argument))
    val = -1;//+1 to current queue
  else
    val = atoi( argument );
  start_recruitment( ch, FALSE, val, cost );
}

//sets forcus on army or current room
bool army_order_focus( CHAR_DATA* ch, char* argument ){
  ROOM_INDEX_DATA* focus;
  
  if (IS_NULLSTR(argument)){
    set_focus( ch, ch->in_room);
    send_to_char("Focus set to current room.\n\r", ch);
  }
  else if ( (focus = get_army_focus( ch, atoi(argument))) == NULL)
    send_to_char("No army with such ID.\n\r", ch);
  else if (!set_focus( ch, focus))
    send_to_char("Error! Unable to set focus.\n\r", ch);
  else{
    return TRUE;
  }
  return FALSE;
}

void army_order_scrap( CHAR_DATA* ch, char* argument ){
  int tot;
  int nor;
  int upg;
  
  if (IS_NULLSTR( argument ))
    tot = 1;
  else
    tot = atoi( argument );
  
  /* decide how many armies of each type to disband */
  if (tot > GARR_NOR(ch)){
    nor = GARR_NOR(ch);
    upg = UMIN(GARR_UPG(ch), tot - nor);
  }
  else{
    upg = 0;
    nor = tot;
  }
  update_garrison( ch, -nor, -upg );
  sendf( ch, "Barracks at: %d.%d\n\r", GARR_NOR(ch), GARR_UPG(ch));
}

void army_order_list( CHAR_DATA* ch ){
  show_armies( ch, ch->pCabal, ch->name );
}
  
void army_order_upgrade( CHAR_DATA* ch, char* argument ){
  CABAL_DATA* pc = get_parent( ch->pCabal );
  ARMY_INDEX_DATA* pai;
  int cost, val;
  
  if ( (pai = get_army_index( pc->pIndexData->army_upgrade)) == NULL){
    send_to_char("Your cabal has no elite armies.\n\r", ch );
    return;
  }
  else
    cost = pai->cost;
  
  /* get number to recruit */
  if(IS_NULLSTR( argument))
    val = -1;
  else
    val = atoi( argument );
  start_recruitment( ch, TRUE, val, cost );
}

void army_order_attack( CHAR_DATA* ch, char* argument, bool fElite){
  CABAL_DATA* pc = get_parent(ch->pCabal );
  ROOM_INDEX_DATA* in_room = GET_FOCUS(ch);
  ARMY_DATA* pa = NULL;
  char buf[MIL];
  int ID, vnum = 0;
  
  /* find the army to be ordered to garrison */
  if (!IS_NULLSTR(argument)){
    if ( (ID = atoi( argument )) < 1){
      send_to_char("Attack focus with which army?\n\r", ch);
      return;
    }
    else if ( (pa = get_army_world( ID )) == NULL){
      send_to_char("No such army detected.\n\r", ch);
      return;
    }
    else if (!is_commander( ch, pa)){
      send_to_char("You cannot command that army.\n\r", ch);
      return;
    }
    else if (!can_relocate( ch, pa, FALSE ))
      return;
  }
  else{
    if (!fElite && GARR_NOR(ch) > 0){
      vnum = pc->pIndexData->army;
    }
    else if (fElite && GARR_UPG(ch) > 0){
      vnum = pc->pIndexData->army_upgrade;
    }
    else{
      send_to_char("You lack required armies.\n\r", ch);
      return;
    }
  }
  
  if (!can_attack( ch, NULL, in_room, pc, FALSE))
    return;
  
  /* check for army prototype if army not pre-selected */
  if ( !pa){
    if ( (pa = create_army(get_army_index( vnum ), pc)) == NULL){
      send_to_char("Your cabal does not have an army to send.\n\r", ch );
      return;
    }
    else{
      /* remove an army from barracks */
      if (fElite)
	update_garrison( ch, 0, -1 );
      else
	update_garrison( ch, -1, 0 );
    }
  }
  
  /* set commander and order */
  set_army_commander( pa, ch->name, ch->pcdata->rank );
  order_army( pa, AS_ATTA, gen_vars( in_room->vnum, 0, 0));
  sprintf( buf, "%s %s confirmed.", 
	   capitalize(state_table[pa->order].name), 
	   in_room->name );
  army_report( pa, buf, REPORT_NEUT, FALSE );
  
  sprintf( buf, "Spy Report: [%s`0] %s about to attack %s`0 in %s.",
	   get_parent(ch->pCabal)->who_name,
	   pa->noun,
	   in_room->name, 
	   in_room->area->name);
//  cabal_echo_flag(CABAL_SPIES, buf );
}

void army_order_patrol( CHAR_DATA* ch, char* argument, bool fElite){
  CABAL_DATA* pc = get_parent(ch->pCabal );
  ROOM_INDEX_DATA* in_room = GET_FOCUS(ch);
  ARMY_DATA* pa = NULL;
  char buf[MIL];
  int ID, vnum = 0;
  
  /* find the army to be ordered to garrison */
  if (!IS_NULLSTR(argument)){
    if ( (ID = atoi( argument )) < 1){
      send_to_char("Patrol focus with which army?\n\r", ch);
      return;
    }
    else if ( (pa = get_army_world( ID )) == NULL){
      send_to_char("No such army detected.\n\r", ch);
      return;
    }
    else if (!is_commander( ch, pa)){
      send_to_char("You cannot command that army.\n\r", ch);
      return;
    }
    else if (!can_relocate( ch, pa, FALSE ))
      return;
  }
  else{
    if (!fElite && GARR_NOR(ch) > 0){
      vnum = pc->pIndexData->army;
    }
    else if (fElite && GARR_UPG(ch) > 0){
      vnum = pc->pIndexData->army_upgrade;
    }
    else{
      send_to_char("You lack required armies.\n\r", ch);
      return;
    }
  }
  
  if (!can_patrol( ch, pa, in_room, pc, FALSE))
    return;
  
  /* check for army prototype if army not pre-selected */
  if ( !pa){
    if ( (pa = create_army(get_army_index( vnum ), pc)) == NULL){
      send_to_char("Your cabal does not have an army to send.\n\r", ch );
      return;
    }
    else{
      /* remove an army from barracks */
      if (fElite)
	update_garrison( ch, 0, -1 );
      else
	update_garrison( ch, -1, 0 );
    }
  }
  
  /* set commander and order */
  set_army_commander( pa, ch->name, ch->pcdata->rank );
  order_army( pa, AS_PATR, gen_vars( in_room->vnum, 0, 0));
  sprintf( buf, "%s %s confirmed.", 
	   capitalize(state_table[pa->order].name), 
	   in_room->name );
  army_report( pa, buf, REPORT_NEUT, FALSE );
  
  sprintf( buf, "Spy Report: [%s`0] %s about to patrol %s`0 in %s.",
	   get_parent(ch->pCabal)->who_name,
	   pa->noun,
	   in_room->name, 
	   in_room->area->name);
//  cabal_echo_flag(CABAL_SPIES, buf );
}

void army_order_defend( CHAR_DATA* ch, char* argument, bool fElite){
  CABAL_DATA* pc = get_parent(ch->pCabal );
  ROOM_INDEX_DATA* in_room = GET_FOCUS(ch);
  ARMY_DATA* pa = NULL;
  char buf[MIL];
  int ID, vnum = 0;
  
  /* find the army to be ordered to garrison */
  if (!IS_NULLSTR(argument)){
    if ( (ID = atoi( argument )) < 1){
      send_to_char("Defend focus with which army?\n\r", ch);
      return;
    }
    else if ( (pa = get_army_world( ID )) == NULL){
      send_to_char("No such army detected.\n\r", ch);
      return;
    }
    else if (!is_commander( ch, pa)){
      send_to_char("You cannot command that army.\n\r", ch);
      return;
    }
    else if (!can_relocate( ch, pa, FALSE ))
      return;
  }
  else{
    if (!fElite && GARR_NOR(ch) > 0){
      vnum = pc->pIndexData->army;
    }
    else if (fElite && GARR_UPG(ch) > 0){
      vnum = pc->pIndexData->army_upgrade;
    }
    else{
      send_to_char("You lack required armies.\n\r", ch);
      return;
    }
  }
  
  if (!can_defend( ch, pa, in_room, pc, FALSE))
    return;
  
  /* check for army prototype if army not pre-selected */
  if ( !pa){
    if ( (pa = create_army(get_army_index( vnum ), pc)) == NULL){
      send_to_char("Your cabal does not have an army to send.\n\r", ch );
      return;
    }
    else{
      /* remove an army from barracks */
      if (fElite)
	update_garrison( ch, 0, -1 );
      else
	update_garrison( ch, -1, 0 );
    }
  }
  
  /* set commander and order */
  set_army_commander( pa, ch->name, ch->pcdata->rank );
  order_army( pa, AS_DEFE, gen_vars( in_room->vnum, 0, 0));
  sprintf( buf, "%s %s confirmed.", 
	   capitalize(state_table[pa->order].name), 
	   in_room->name );
  army_report( pa, buf, REPORT_NEUT, FALSE );
  
  sprintf( buf, "Spy Report: [%s`0] %s about to defend %s`0 in %s.",
	   get_parent(ch->pCabal)->who_name,
	   pa->noun,
	   in_room->name, 
	   in_room->area->name);
//  cabal_echo_flag(CABAL_SPIES, buf );
}

void army_order_garrison( CHAR_DATA* ch, char* argument, bool fElite ){
  char buf[MIL];
  CABAL_DATA* pc = get_parent(ch->pCabal );
  ROOM_INDEX_DATA* in_room = GET_FOCUS(ch);
  ARMY_DATA* pa = NULL;
  ARMY_INDEX_DATA* pai;
  int ID, vnum = 0;
  
  /* find the army to be ordered to garrison */
  if (!IS_NULLSTR(argument)){
    if ( (ID = atoi( argument )) < 1){
      send_to_char("Garrison focus with which army?\n\r", ch);
      return;
    }
    else if ( (pa = get_army_world( ID )) == NULL){
      send_to_char("No such army detected.\n\r", ch);
      return;
    }
    else if (!is_commander( ch, pa)){
      send_to_char("You cannot command that army.\n\r", ch);
      return;
    }
    else if (!can_relocate( ch, pa, FALSE ))
      return;
  }
  else{
    if (!fElite && GARR_NOR(ch) > 0){
      vnum = pc->pIndexData->army;
    }
    else if (fElite && GARR_UPG(ch) > 0){
      vnum = pc->pIndexData->army_upgrade;
    }
    else{
      send_to_char("You lack required armies.\n\r", ch);
      return;
    }
  }

  if (!can_bastion(ch, pa, in_room, pc, FALSE ))
    return;

  /* check for army prototype if army not pre-selected */
  if ( !pa){
    if ( (pa = create_army(get_army_index( vnum ), pc)) == NULL){
      send_to_char("Your cabal does not have an army to send.\n\r", ch );
      return;
    }
    else{
      /* remove an army from barracks */
      if (fElite)
	update_garrison( ch, 0, -1 );
      else
	update_garrison( ch, -1, 0 );
    }
  }
  else if ( (pai = get_army_index( pc->pIndexData->tower )) == NULL){
    send_to_char("Your cabal does not have a tower to build.\n\r", ch );
    return;
  }
  
  /* set commander and order */
  set_army_commander( pa, ch->name, ch->pcdata->rank );
  order_army( pa, AS_GARR, gen_vars( in_room->vnum, 0, 0));
  sprintf( buf, "%s %s confirmed.", 
	   capitalize(state_table[pa->order].name), 
	   in_room->name );
  army_report( pa, buf, REPORT_NEUT, FALSE );
  
  sprintf( buf, "Spy Report: [%s`0] %s about to garrison %s`0 in %s.",
	   get_parent(ch->pCabal)->who_name,
	   pa->noun,
	   in_room->name, 
	   in_room->area->name);
//  cabal_echo_flag(CABAL_SPIES, buf );
}

void army_order_fortify( CHAR_DATA* ch, char* argument, bool fElite ){
  char buf[MIL];
  CABAL_DATA* pc = get_parent(ch->pCabal );
  ROOM_INDEX_DATA* in_room = GET_FOCUS(ch);
  ARMY_DATA* pa = NULL;
  ARMY_INDEX_DATA* pai;
  int ID, vnum = 0;
  
  /* find the army to be ordered to garrison */
  if (!IS_NULLSTR(argument)){
    if ( (ID = atoi( argument )) < 1){
      send_to_char("Fortify focus with which army?\n\r", ch);
      return;
    }
    else if ( (pa = get_army_world( ID )) == NULL){
      send_to_char("No such army detected.\n\r", ch);
      return;
    }
    else if (!is_commander( ch, pa)){
      send_to_char("You cannot command that army.\n\r", ch);
      return;
    }
    else if (!can_relocate( ch, pa, FALSE ))
      return;
  }
  else{
    if (!fElite && GARR_NOR(ch) > 0){
      vnum = pc->pIndexData->army;
    }
    else if (fElite && GARR_UPG(ch) > 0){
      vnum = pc->pIndexData->army_upgrade;
    }
    else{
      send_to_char("You lack required armies.\n\r", ch);
      return;
    }
  }

  if (!can_fortify(ch, in_room, pc, FALSE ))
    return;

  /* check for army prototype if army not pre-selected */
  if ( !pa){
    if ( (pa = create_army(get_army_index( vnum ), pc)) == NULL){
      send_to_char("Your cabal does not have an army to send.\n\r", ch );
      return;
    }
    else{
      /* remove an army from barracks */
      if (fElite)
	update_garrison( ch, 0, -1 );
      else
	update_garrison( ch, -1, 0 );
    }
  }
  else if ( (pai = get_army_index( pc->pIndexData->tower_upgrade )) == NULL){
    send_to_char("Your cabal does not have a fortified tower to build.\n\r", ch );
    return;
  }
  
  /* set commander and order */
  set_army_commander( pa, ch->name, ch->pcdata->rank );
  order_army( pa, AS_FORT, gen_vars( in_room->vnum, 0, 0));
  sprintf( buf, "%s %s confirmed.", 
	   capitalize(state_table[pa->order].name), 
	   in_room->name );
  army_report( pa, buf, REPORT_NEUT, FALSE );
  
  sprintf( buf, "Spy Report: [%s`0] %s about to fortify %s`0 in %s.",
	   get_parent(ch->pCabal)->who_name,
	   pa->noun,
	   in_room->name, 
	   in_room->area->name);
//  cabal_echo_flag(CABAL_SPIES, buf );
}

void army_order_return( CHAR_DATA* ch, char* argument ){
  char buf[MIL];
  ARMY_DATA* pa = NULL;
  int tar_ID;
  
  /* get target army */
  if (IS_NULLSTR(argument)){
    send_to_char("Order which army to return?\n\r", ch);
    return;
  }

  if ( (tar_ID = atoi( argument )) < 1){
    send_to_char("That is not a valid army ID.\n\r", ch);
    return;
  }
  else if ( (pa = get_army_world( tar_ID)) == NULL){
    sendf( ch, "No unit with ID: %d found.\n\r", tar_ID);
    return;
  }
  else if (!is_commander(ch, pa)){
    send_to_char("You cannot command that army.\n\r", ch );
    return;
  }
  else if (!can_return( ch, pa->in_room, pa, FALSE))
    return;
  
  if (pa->pIndexData->type != ARMY_TYPE_BASTION){
    sprintf( buf, "Spy Report: [%s`0] %s departing %s`0 in %s.",
	     get_parent(ch->pCabal)->who_name,
	     pa->noun,
	     pa->in_room->name, 
	     pa->in_room->area->name);
    /* set order */
    order_army( pa, AS_RETU, gen_vars( 0, 0, 0));
  }
  else{
    sprintf( buf, "Spy Report: [%s`0] %s disbanded in %s`0 in %s.",
	     get_parent(ch->pCabal)->who_name,
	     pa->noun,
	     pa->in_room->name, 
	     pa->in_room->area->name);

    sprintf( buf, "%s %s confirmed.", 
	     capitalize(state_table[AS_DISB].name), 
	     pa->in_room->name );
    /* set order */
    order_army( pa, AS_DISB, gen_vars( 0, 0, 0));
    army_report( pa, buf, REPORT_NEUT, FALSE );
  }
//  cabal_echo_flag(CABAL_SPIES, buf );
}

//army interpret function
bool army_interpret( CHAR_DATA* ch, char* argument ){
  char cmd[MIL];
  bool fMatch = FALSE;
  
  /* interpret armies only if person has a focus */
  if (IS_NPC(ch) || GET_FOCUS( ch ) == NULL)
    return FALSE;
  else if (ch->pCabal == NULL){
    SET_FOCUS(ch, NULL );
    return FALSE;
  }

  while ( isspace(*argument) ) {
    argument++;
  }
  argument = one_argument( argument, cmd );

  if (IS_NULLSTR( cmd )){
    show_army_screen( ch, GET_FOCUS(ch) );
    show_army_prompt( ch );
    return TRUE;
  }
/* HELP */
  else if (!str_prefix( cmd, "?")){
    send_to_char("[A]ttack,    [P]atrol,  [H]old,   [G]arrison,  [F]ortify, [B]arracks\n\r"\
		 "[aA]ttack,   [pP]atrol, [hH]old,  [gG]arrison, [fF]ortify,[bB]arracks\n\r\n\r"\
		 "[C]onscript, [T]rain,   [R]elease\n\r"\
		 "[Z]oom,      [zZ]oom    [M]ap,    [O]rders,    [Q]ueue\n\r"\
		 "[?] - This screen.\n\r", ch );
    return TRUE;
  }
/* GARRISON  */
  else if (!str_prefix( cmd, "garrison")){
    fMatch = TRUE;
    army_order_garrison( ch, argument, FALSE);
  }
  else if (!str_prefix( cmd, "ggarrison")){
    fMatch = TRUE;
    army_order_garrison( ch, argument, TRUE);
  }
/* FORTIFY  */
  else if (!str_prefix( cmd, "fortify")){
    fMatch = TRUE;
    army_order_fortify( ch, argument, FALSE);
  }
  else if (!str_prefix( cmd, "ffortify")){
    fMatch = TRUE;
    army_order_fortify( ch, argument, TRUE);
  }
/* ATTACK  */
  else if (!str_prefix( cmd, "attack")){
    fMatch = TRUE;
    army_order_attack( ch, argument, FALSE );
  }
  else if (!str_prefix( cmd, "aattack")){
    fMatch = TRUE;
    army_order_attack( ch, argument, TRUE );
  }
/* DEFEND/HOLD  */
  else if (!str_prefix( cmd, "hold")){
    fMatch = TRUE;
    army_order_defend( ch, argument, FALSE );
  }
  else if (!str_prefix( cmd, "hhold")){
    fMatch = TRUE;
    army_order_defend( ch, argument, TRUE );
  }
/* PATROL  */
  else if (!str_prefix( cmd, "patrol")){
    fMatch = TRUE;
    army_order_patrol( ch, argument, FALSE );
  }
  else if (!str_prefix( cmd, "ppatrol")){
    fMatch = TRUE;
    army_order_patrol( ch, argument, TRUE);
  }
/* RETURNBARRACKS   */
  else if (!str_prefix( cmd, "barracks")){
    fMatch = TRUE;
    army_order_return( ch, argument );
  }
/* CONSCRIPT (recruit) */
  else if (!str_prefix( cmd, "conscript")){
    fMatch = TRUE;
    army_order_muster( ch, argument );
  }
/* TRAIN (make elites) */
  else if (!str_prefix( cmd, "train")){
    fMatch = TRUE;
    army_order_upgrade( ch, argument );
  }
/* RELEASE (get rid of armies in barracks) */
  else if (!str_prefix( cmd, "release")){
    fMatch = TRUE;
    army_order_scrap( ch, argument );
  }
/* ORDERS (shows list of armies) */
  else if (!str_prefix( cmd, "orders")){
    fMatch = TRUE;
    army_order_list( ch );
  }
/* QUEUE (show past reports) */
  else if (!str_prefix( cmd, "queue")){
    fMatch = TRUE;
    army_order_queue( ch );
  }
/* MAP (show maps) */
  else if (!str_prefix( cmd, "map")){
    fMatch = TRUE;
    army_order_map( ch, argument );
  }
/* ZOOM (sets focus) */
  else if (!str_prefix( cmd, "zoom")){
    fMatch = TRUE;
    if (army_order_focus( ch, argument ))
      show_army_screen( ch, GET_FOCUS( ch ) );
  }
/* ZZOOM (sets focus to last focus) */
  else if (!str_prefix( cmd, "zzoom")){
    fMatch = TRUE;
    set_focus( ch, GET_LASTFOCUS( ch ));
    show_army_screen( ch, GET_FOCUS( ch ) );
  }
  if (fMatch)
    show_army_prompt( ch );
  return fMatch;
}

struct area_info_s {
  AREA_DATA*	area;
  int		bas;
  int		ubas;
  int		sup_loss;
  bool		fAtt;
  

  int		ebas;
  int		eubas;
};

//shows status of all areas owned by a cabal
void show_world_armies( CHAR_DATA* ch, CABAL_DATA* pc ){
  struct area_info_s ainfo[top_area];
  ARMY_DATA* pa;
  BUFFER* output;
  AREA_DATA* area;
  char buf[MIL];
  int hash, i;
  float support;
  bool fUpg, fBas;

  memset( &ainfo, 0, sizeof( struct area_info_s ) * top_area );

  for (hash = 0; hash < ARMY_HASH_SIZE; hash++){
    for (pa = army_list[hash]; pa; pa = pa->next){
      /* skip armies not in room/slot */
      if (pa->in_room == NULL || pa->in_slot < 0)
	continue;

      /* updates counts in the given area */
      fUpg = IS_ARMY(pa, ARMY_FLAG_ELITE) ? TRUE : FALSE;
      fBas = pa->in_slot == INROOM_BASTION;

      if (is_same_cabal(pa->pCabal, pc)){
	ainfo[pa->in_room->area->vnum].area = pa->in_room->area;
	ainfo[pa->in_room->area->vnum].bas += fBas && !fUpg;
	ainfo[pa->in_room->area->vnum].ubas += fBas && fUpg;
	if (pa->attacking)
	  ainfo[pa->in_room->area->vnum].fAtt = TRUE;
	if (is_same_cabal(pa->in_room->area->pCabal, pa->pCabal))
	  ainfo[pa->in_room->area->vnum].sup_loss += get_army_support( pa, pa->in_room->area);
      }
      else{
	ainfo[pa->in_room->area->vnum].ebas += fBas && !fUpg;
	ainfo[pa->in_room->area->vnum].eubas += fBas && fUpg;
      }
    }//end for cell
  }//end for hash

  /* we have complete world info now, spit it out*/
  output = new_buf();
  sprintf( buf, "%-3.3s %-26.26s`` %-4.4s %-5.5s  %-7.7s  %-3.3s\n\r",
	   "MAP",
	   "          Name",
	   "Our",
	   "Tot",
	   "Enemy",
	   "Support");
  add_buf( output, buf );

  for (i = 0; i < top_area; i++){
    if ( (area = ainfo[i].area) == NULL)
      ainfo[i].area = get_area_data( i );

    if ( (area = ainfo[i].area) == NULL || area->pCabal == NULL || !is_same_cabal(area->pCabal, pc))
      continue;
    
    if (area->pCabal == pc)
      support = 100.0 * (float)ainfo[i].sup_loss / CABAL_FULL_SUPPORT_VAL;
    else
      support = 0;
    sprintf( buf, "%3d   %-24.24s``  %2d   %2d    %2d    %3.3f%% %s\n\r",
	     i,
	     area->name,
	     ainfo[i].bas + ainfo[i].ubas,
	     area->bastion_max,
	     ainfo[i].ebas + ainfo[i].eubas,
	     support,
	     ainfo[i].fAtt ? "`![B]``" : "");
    add_buf( output, buf );
  }
  page_to_char(buf_string(output),ch);
  free_buf(output);  
}

//shows all units by room in the area
// Focus   Bas  Gar    Pat  Attackers    Room
//[#1234]  [K] [K V]  [K K]  [n n S] Central Square of Val Miran
//[#1234]             [k k]  [S S S] Southern Square of Val Miran

void show_area_armies(CHAR_DATA* ch, AREA_DATA* area, CABAL_DATA* pCabal){
  const int MAX_ROOMS = 36;
  struct room_info_s{
    int		ID;
    ARMY_DATA*	pBas;
    int		gar;
    ARMY_DATA*	pGar[2];
    int		pat;
    ARMY_DATA*	pPat[2];
    int		att;
    ARMY_DATA*	pAtt[3];
    ROOM_INDEX_DATA* pRoom;
  }rinfo[MAX_ROOMS];

  ROOM_INDEX_DATA* room;
  BUFFER* output;
  ARMY_DATA* pa;
  int rooms = 0, slot, vnum, i;
  char buf[MIL], bas_buf[MIL], gar_buf[MIL], pat_buf[MIL], att_buf[MIL];
  char tem[8];
  int tot_bas = 0, tot_gar = 0, tot_pat = 0, sup_los = 0, support = 0;
  bool fOur = TRUE;

  memset( &rinfo, 0, sizeof( struct room_info_s ) * MAX_ROOMS );
  for (vnum = area->min_vnum; vnum <= area->max_vnum; vnum++){
    if ( (room = get_room_index( vnum )) == NULL)
      continue;
    else if (room->room_armies.count < 1)
      continue;
    else if (rooms >= MAX_ROOMS){
      bug("armies.c>show_area_armies: MAX_ROOM overflow.\n\r", 0);
      break;
    }
    for (slot = INROOM_BASTION; slot < INROOM_MAXARMIES; slot++){
      if ( (pa = room->room_armies.armies[slot]) == NULL)
	continue;

      //set our bit
      if (pCabal && is_same_cabal( pCabal, pa->pCabal))
	fOur = TRUE;
      else
	fOur = FALSE;
      //set room and ID for this room
      rinfo[rooms].pRoom = room;
      if (rinfo[rooms].ID < 1)
	rinfo[rooms].ID = pa->ID;
      
      /* sort data */
      if (pa->in_slot == INROOM_BASTION){
	rinfo[rooms].pBas = pa;
	if (fOur)
	  tot_bas++;
      }
      else if (pa->in_slot < INROOM_ATTACKERS ){
	if (IS_ARMY(pa, ARMY_FLAG_GARRISON)){
	  rinfo[rooms].pGar[rinfo[rooms].gar++] = pa;
	  if (fOur){
	    tot_gar++;
	    sup_los += get_army_support( pa, area );
	  }
	}
	else{
	  rinfo[rooms].pPat[rinfo[rooms].pat++] = pa;
	  if (fOur)
	    tot_pat++;
	}
      }
      else
	rinfo[rooms].pAtt[rinfo[rooms].att++] = pa;
    }//end for army
    rooms++;
  }//end for room

  /* time to spit the data out */
  output = new_buf();
  sprintf( buf, " Focus   Bas  Gar  Pat    Attackers    Room\n\r" );
  add_buf( output, buf );
  for (i = 0; i < rooms; i++){
    /* GENREATE BASTION STRING*/
    if (rinfo[i].pBas){
      sprintf( bas_buf, "[`%c%c``]", 
	       rinfo[i].pBas->pCabal->who_name[1],
	       GET_LETTER(rinfo[i].pBas));
      
    }
    else
      sprintf( bas_buf, " `1 `` " );

    /* GENREATE GARRISON STRING */
    if (rinfo[i].gar){
      int j;
      sprintf( gar_buf, "[");
      for (j = 0; j < INROOM_ATTACKERS - INROOM_DEFENDERS; j++){
	if (rinfo[i].pGar[j]){
	  sprintf( tem, "`%c%c``",
		   rinfo[i].pGar[j]->pCabal->who_name[1],
		   GET_LETTER(rinfo[i].pGar[j]));
	  strcat( gar_buf, tem );
	}
	else
	  strcat( gar_buf, "`1 ``");
      }
      strcat( gar_buf, "]");
    }//end if garrisons
    else
      sprintf( gar_buf, " `1 ```1 `` " );
    /* GENREATE patrol STRING */
    if (rinfo[i].pat){
      int j;
      sprintf( pat_buf, "[");
      for (j = 0; j < INROOM_ATTACKERS - INROOM_DEFENDERS; j++){
	if (rinfo[i].pPat[j]){
	  sprintf( tem, "`%c%c``",
		   rinfo[i].pPat[j]->pCabal->who_name[1],
		   GET_LETTER(rinfo[i].pPat[j]));
	  strcat( pat_buf, tem );
	}
	else
	  strcat( pat_buf, "`1 ``");
      }
      strcat( pat_buf, "]");
    }//end if patrols
    else
      sprintf( pat_buf, " `1 ```1 `` " );
    /* GENREATE STTACK STRING */
    if (rinfo[i].att){
      int j;
      sprintf( att_buf, "[");
      for (j = 0; j < INROOM_MAXARMIES - INROOM_ATTACKERS; j++){
	if (rinfo[i].pAtt[j]){
	  sprintf( tem, "`%c%c``",
		   rinfo[i].pAtt[j]->pCabal->who_name[1],
		   GET_LETTER(rinfo[i].pAtt[j]));
	  strcat( att_buf, tem );
	}
	else
	  strcat( att_buf, "`1 ``");
      }
      strcat( att_buf, "]");
    }//end if garrisons
    else
      sprintf( att_buf, " `1 ```1 ```1 `` " );

    //            " Focus   Bas  Gar   Pat   Attackers    Room\n\r"
    //            "[#1234]  [K] [K V] [K K]   [n n S] Central Square"
    sprintf(buf,"[#%-4d]  %-7.7s %-13.13s %-13.13s   %-19.19s  %-30.30s``\n\r",
	    rinfo[i].ID,
	    bas_buf,
	    gar_buf,
	    pat_buf,
	    att_buf,
	    rinfo[i].pRoom->name);
    add_buf( output, buf );
  }
  if (rooms < 1){
    sprintf( buf, "No units detected.\n\r");
    add_buf( output, buf );
  }
  if (can_reinforce(pCabal, area)){
    support = 100 * (area->support - sup_los) / UMAX(1, area->support);
    add_buf( output, "[R] " );
  }
  else{
    support = 0;
    add_buf( output, "    " );
  }
    
      
  //last line has info about the area
  sprintf( buf, "\n\r%-20.20s B:%d/%d  G:%d  P:%d  S:%d(%d%%)\n\r",
	   area->name,
	   tot_bas,
	   area->bastion_max,
	   tot_gar,
	   tot_pat,
	   support * area->support / 100,
	   support);
  add_buf( output, buf );

  page_to_char(buf_string(output),ch);
  free_buf(output);    
}

/* shows list of armies based on owner name
 */
void show_armies( CHAR_DATA* ch, CABAL_DATA* pc, char* name ){
  BUFFER* buffer;
  ARMY_DATA* pa;
  int hash;
  char buf[MSL];
  int cnt = 0;

  buffer = new_buf();
  
  sprintf(buf, "   ID    Hp     State        Area       \n\r" );
  add_buf( buffer, buf );
  sprintf(buf, "------------------------------------------\n\r" );
  add_buf( buffer, buf );
  
  for (hash = 0; hash < ARMY_HASH_SIZE; hash++){
    for (pa = army_list[hash]; pa; pa = pa->next){
      if (IS_NULLSTR(pa->commander) || pa->commander[0] != name[0] 
	  || str_cmp(pa->commander, name))
	continue;
      else
	cnt++;
      sprintf( buf, "%-5u    %-3d   %-10.10s  %-25.25s``\n\r",
	       pa->ID,
	       100 * pa->hit / pa->max_hit,
	       state_table[pa->state].name,
	       pa->in_room ? pa->in_room->area->name : "No Report");
      add_buf( buffer, buf );
    }
  }
  if (cnt)
    page_to_char(buf_string(buffer),ch);
  free_buf(buffer);
}

/*                    INTERFACE FUNCTIONS                    */
//reports an army report
void army_report( ARMY_DATA* pa, char* string, int type, bool fSave ){
  DESCRIPTOR_DATA* d;
  CABAL_DATA* pc;
  char* color;
  char buf[MIL];

  if (pa == NULL || IS_NULLSTR(string ))
    return;
  else
    pc = get_parent( pa->pCabal );

  switch( type ){
  case REPORT_GOOD:	color = "`@";	break;
  case REPORT_BAD:	color = "`!";	break;
  default:		color = "`8";	break;
  }
  sprintf( buf, "%s[#%-4d]%-12.12s``: %s``\n\r",
	   color,
	   pa->ID,
	   pa->noun,
	   string);

  /* DEBUG 
     nlogf( "DEBUG: %s", buf );
  */
  /* echo the message */
  for ( d = descriptor_list; d != NULL; d = d->next ){
    CHAR_DATA* rch = d->character;
    bool fDemon = FALSE;

    if ( d->connected != CON_PLAYING 
	 || rch == NULL
	 || rch->pCabal == NULL
	 || IS_AFFECTED2(rch, AFF_SILENCE)
	 || IS_SET(rch->comm, COMM_NOCABAL) 
	 || IS_SET(rch->comm, COMM_QUIET) 
	 || !is_same_cabal(rch->pCabal, pc) ){
      continue;
    }

    fDemon = IS_TELEPATH( rch );
    send_to_char( buf, rch );

    if (!IS_NPC(rch) && rch->pcdata->eavesdropped != NULL
	&& is_affected(rch->pcdata->eavesdropped,gsn_eavesdrop)
	&& !fDemon){
      
      send_to_char("A faint message transmits from the parasite.\n\r",rch->pcdata->eavesdropped);
      act_new(buf, rch, string, NULL, TO_CHAR, POS_DEAD);      
    }
  }

  /* add the message onto the report queue */
  if (!fSave)
    return;
  add_army_report( &pc->report_q, buf );
  /* set the last focus from army */
  SET_QFOCUS( pc, pa->in_room );
}

//reports a cabal report (army report without unit)
void cabal_report( CABAL_DATA* pc, char* string, int type, bool fSave ){
  DESCRIPTOR_DATA* d;
  char* color;
  char buf[MIL];

  if (pc == NULL || IS_NULLSTR(string ))
    return;
  switch( type ){
  case REPORT_GOOD:	color = "`@";	break;
  case REPORT_BAD:	color = "`!";	break;
  default:		color = "`8";	break;
  }
  sprintf( buf, "%s[#%-4s]%-12.12s``: %s``\n\r",
	   color,
	   "----",
	   pc->name,
	   string);

  /* echo the message */
  for ( d = descriptor_list; d != NULL; d = d->next ){
    CHAR_DATA* rch = d->character;
    bool fDemon = FALSE;

    if ( d->connected != CON_PLAYING 
	 || rch == NULL
	 || rch->pCabal == NULL
	 || IS_AFFECTED2(rch, AFF_SILENCE)
	 || IS_SET(rch->comm, COMM_NOCABAL) 
	 || IS_SET(rch->comm, COMM_QUIET) 
	 || !is_same_cabal(rch->pCabal, pc) ){
      continue;
    }

    fDemon = IS_TELEPATH( rch );
    send_to_char( buf, rch );

    if (!IS_NPC(rch) && rch->pcdata->eavesdropped != NULL
	&& is_affected(rch->pcdata->eavesdropped,gsn_eavesdrop)
	&& !fDemon){
      
      send_to_char("A faint message transmits from the parasite.\n\r",rch->pcdata->eavesdropped);
      act_new(buf, rch, string, NULL, TO_CHAR, POS_DEAD);      
    }
  }

  /* add the message onto the report queue */
  if (!fSave)
    return;
  add_army_report( &pc->report_q, buf );
}

//shows the reports in the cabal's queue
void show_report_q(REPORT_QUEUE* q, CHAR_DATA* ch ){
  ARMY_REPORT* pRep;
  BUFFER* output;
  char buf[MIL];
  struct tm* time_data;
  bool fFound = FALSE;

  output = new_buf();

  for (pRep = q->top.prev; pRep != &q->bot; pRep = pRep->prev ){
    time_data = localtime( &pRep->time );

    fFound = TRUE;
    sprintf( buf, "%2d:%02d %s", 
	     time_data->tm_hour,
	     time_data->tm_min,
	     pRep->string);
    add_buf( output, buf );
  }

  if (!fFound)
    send_to_char("No recent reports.\n\r", ch);
  else
    page_to_char(buf_string(output),ch);
  free_buf(output);
}

//returns maximum number of armies commanded at given level
int get_max_army( int level ){
  return (3 + UMAX(0, (level - 30) / 10));
}

//returns count of deployed armies by given character name
int get_army_count( char* name ){
  ARMY_DATA* pa;
  int sum = 0;
  int hash;

  for (hash = 0; hash < ARMY_HASH_SIZE; hash++){
    for (pa = army_list[hash]; pa; pa = pa->next){
      if (!IS_NULLSTR(pa->commander)
	  && pa->commander[0] == name[0] 
	  && !str_cmp(pa->commander, name))
	sum++;
    }
  }

  return sum;
}

//checks if a character has a maximum number of armies
//TRUE if at or above max
bool check_army_max( CMEMBER_DATA* cm ){
  int max_armies = get_max_army( cm->level );
  int armies = get_army_count( cm->name ) + cm->armies[NOR] + cm->armies[UPG];

  if (IS_CABAL(cm->pCabal, CABAL_ELITE))
    max_armies += 2;
  if (armies >= max_armies){
    return TRUE;
  }
  return FALSE;
}

//Shows army index data in olc
void show_army_index( CHAR_DATA* ch, ARMY_INDEX_DATA* pai ){
  char buf[MSL];

  sprintf( buf, "%-12.12s: [%-12d] (%-12.12s)\n\r", "Vnum", pai->vnum, pai->area->name);
  send_to_char( buf, ch );
  sprintf( buf, "%-12.12s: [%-12.12s]\n\r", "Type", flag_string( army_types, pai->type ) );
  send_to_char( buf, ch );
  sprintf( buf, "%-12.12s: [%-12d]\n\r", "Cost", pai->cost);
  send_to_char( buf, ch );
  sprintf( buf, "%-12.12s: [%-12d] (%% of gain/bastion)\n\r", "Support", pai->support);
  send_to_char( buf, ch );
  sprintf( buf, "%-12.12s: [%-12s]\n\r", "Flags", flag_string( army_flags, pai->army_flags ) );
  send_to_char( buf, ch );
  sprintf( buf, "%-12.12s: [%3dd%-3d+%4d] (Avg %d)\n\r", "Off Dice",
	   pai->off_dice[DICE_NUMBER], 
	   pai->off_dice[DICE_TYPE], 
	   pai->off_dice[DICE_BONUS],
	   get_dice_avg(pai->off_dice[DICE_NUMBER], pai->off_dice[DICE_TYPE], pai->off_dice[DICE_BONUS]));
  send_to_char( buf, ch );
  sprintf( buf, "%-12.12s: [%3dd%-3d+%4d] (Avg %d)\n\r", "Hit Dice",
	   pai->hit_dice[DICE_NUMBER], 
	   pai->hit_dice[DICE_TYPE], 
	   pai->hit_dice[DICE_BONUS], 
	   get_dice_avg(pai->hit_dice[DICE_NUMBER], pai->hit_dice[DICE_TYPE], pai->hit_dice[DICE_BONUS]));
  send_to_char( buf, ch );
  sprintf( buf, "%-12.12s: [%3dd%-3d+%4d] (Avg %d)\n\r", "Armor Dice",
	   pai->arm_dice[DICE_NUMBER], 
	   pai->arm_dice[DICE_TYPE], 
	   pai->arm_dice[DICE_BONUS],
	   get_dice_avg(pai->arm_dice[DICE_NUMBER], pai->arm_dice[DICE_TYPE], pai->arm_dice[DICE_BONUS]) );
  send_to_char( buf, ch );
  sprintf( buf, "%-12.12s: %-12s\n\r", "Noun", pai->noun);
  send_to_char( buf, ch );
  sprintf( buf, "%-12.12s: %-12s\n\r", "Short Desc", pai->short_desc);
  send_to_char( buf, ch );
  sprintf( buf, "%-12.12s: %-12s\n\r", "Long Desc", pai->long_desc);
  send_to_char( buf, ch );
  sprintf( buf, "%-12.12s:\n\r%s\n\r", "Description", pai->desc);
  send_to_char( buf, ch );
}

//Saves army index data in area file
void save_army_indexes( FILE *fp, AREA_DATA *pArea ){
  int i;
  ARMY_INDEX_DATA *pai;

  fprintf( fp, "#ARMIES\n" );
  
  for( i = pArea->min_vnum; i <= pArea->max_vnum; i++ )
    if ( (pai = get_army_index( i )) ){
      fwrite_army_index( fp, pai );
    }
  fprintf( fp, "#0\n\n\n\n" );
  return;
}

//reads the army index data from area file
void fread_army_indexes( FILE* fp, AREA_DATA* pArea ){
  ARMY_INDEX_DATA *pai;

  for ( ; ; ){
    int vnum;
    char letter;
    letter                          = fread_letter( fp );
    if ( letter != '#' ){
      bug( "armies.c>fread_army_indexes: # not found.", 0 );
      exit( 1 );
    }
    vnum = fread_number( fp );

    /* terminated by #0 */
    if ( vnum < 1)
      break;

    pai = new_army_index();
    if (!fread_army_index(fp, pai, pArea, vnum)){
      free_army_index( pai );
      continue;
    }
    add_army_index( pai );
  }
}


void do_army( CHAR_DATA *ch, char *argument ) {
  CABAL_DATA* pc = get_parent(ch->pCabal);

  if (IS_NPC(ch) || pc == NULL){
    send_to_char("Huh?\n\r", ch );
    return;
  }
  else if (!IS_CABAL(pc, CABAL_AREA)){
    send_to_char("Your cabal has no interest in direct conquest.\n\r", ch );
    return;
  }
  else if (GET_FOCUS(ch) != NULL){
    set_focus( ch, NULL );
    send_to_char("You roll up your maps.\n\r", ch);
    act("$n rolls up the tactical maps.", ch, NULL, NULL, TO_ROOM );
    return;
  }

  if (IS_NULLSTR(argument)){
    if (GET_LASTFOCUS(ch) == NULL)
      set_focus( ch,  ch->in_room);
    else
      set_focus( ch,  GET_LASTFOCUS( ch ));
  }
  else{
    if (GET_QFOCUS(ch->pCabal) == NULL){
      if (GET_LASTFOCUS(ch) == NULL)
	set_focus( ch,  ch->in_room);
      else
	set_focus( ch,  GET_LASTFOCUS( ch ));
    }
    else
      set_focus( ch, GET_QFOCUS( ch->pCabal ));
  }

  act("$n spreads open a tactical map of $t.", ch, GET_FOCUS(ch)->area->name, NULL, TO_ROOM);
  act("You spreads open a tactical map of $t.", ch,GET_FOCUS(ch)->area->name, NULL, TO_CHAR);
  army_interpret( ch, "" );
}

//recruits an army into given char's barracks (called in effect.c)
void recruit_army( CHAR_DATA* ch, bool fElite ){

  if (fElite)
    update_garrison( ch, -1, 1);
  else
    update_garrison( ch, 1, 0);

  if (fElite)
    send_to_char("Unit upgraded.  ", ch );
  else
    send_to_char("Unit recruited.  ", ch );
  sendf( ch, "Barracks at: %d.%d\n\r", GARR_NOR(ch), GARR_UPG(ch));
}

//updates the garrison data based on cabalmember data not ch
void update_garrison_cm( CMEMBER_DATA*  cm, int nor, int upg ){
  if (cm == NULL)
    return;
  cm->armies[NOR] = UMAX(0, cm->armies[NOR] + nor );
  cm->armies[UPG] = UMAX(0, cm->armies[UPG] + upg );
}

//adds a given amount of gain to normal and elite garrisons for character
void update_garrison( CHAR_DATA*  ch, int nor, int upg ){
  if (IS_NPC(ch))
    return;
  else if (ch->pcdata->member == NULL)
    return;

  update_garrison_cm( ch->pcdata->member, nor, upg );
}

ARMY_INDEX_DATA* get_army_index( int vnum ){
  ARMY_INDEX_DATA* pai = army_index_list.next;

  for(; pai; pai = pai->next){
    if (pai->vnum == vnum)
      return pai;
  }
  return NULL;
}

void examine_room_armies( CHAR_DATA* ch, ARMY_ROOM_DATA* pArd ){
  ARMY_DATA* pa;
  char buf[MSL];
  int i;

  sprintf( buf, "%-7.7s %-10.10s  %-3.3s     %-12.12s %-12.12s %s\n\r",
	   "  ID",
	   " Defense",
	   "Arm",
	   "Commander",
	   " Name",
	   "Status");
  send_to_char( buf, ch );
  for (i = 0; i < INROOM_MAXARMIES; i++){
    if ( (pa = pArd->armies[i]) == NULL)
      continue;

    sprintf(buf, "[%-4d] %4d%s %-3d %-3.3s %-12.12s %-12.12s %s``\n\r",
	    pa->ID,
	    pa->hit,
	    short_bar(pa->hit, pa->max_hit),
	    pa->armor,
	    IS_SET(pa->army_flags, ARMY_FLAG_FLYING ) ? "~v~" : "   ",
	    pa->commander,
	    pa->noun,
	    state_table[pa->state].name);
    send_to_char( buf, ch );
  }
}

struct show_room_s {
  ARMY_DATA* attacking; //whom are we fighting
  char* short_desc;//short desc
  char* long_desc;//long desc
  char* cabal;	//cabal who name
  bool fGar;	//garrisoned
  int type;	//INROOM TYPE
  int state;	//state
  int sub;	//subscript
  int cur;	//current hp of weakest (to be attacked) member
  int max;	//max total hp of weakest (to be attacked) member
};

//Shows armies in room to character
void show_room_armies(CHAR_DATA* ch, ARMY_ROOM_DATA* pArd ){
  BUFFER* buffer;
  ARMY_DATA* pa;
  char sub_buf[8];
  char cab_buf[16];
  char lon_buf[MSL];
  char buf[MSL];
  const int size = INROOM_MAXARMIES;
  struct show_room_s info[size];

  int i, j, bas = 0, def = 0, att = 0, type;
  memset( &info, 0, sizeof( struct show_room_s ) * size);

  /* we gather info in order of:
     - Bastion
     - Defenders
     - Attackers
     Same long_desc are kept together
  */

  for (i = 0; i < INROOM_MAXARMIES; i++){
    if ( (pa = pArd->armies[i]) == NULL)
      continue;
    /* search for same long desc, or first null spot */
    for (j = 0; j < size; j++){

      if (i == INROOM_BASTION){
	type = i;
	bas++;
      }
      else if(i > INROOM_BASTION && i < INROOM_ATTACKERS){
	type = INROOM_DEFENDERS;
	def++;
      }
      else{
	type = INROOM_ATTACKERS;
	att++;
      }

      if (IS_NULLSTR(info[j].long_desc)){
	info[j].long_desc  = pa->long_desc;
	info[j].short_desc  = pa->short_desc;
	info[j].cabal = pa->pCabal->who_name;
	info[j].type = type;
	info[j].attacking = pa->attacking;
	info[j].state = pa->state;
	info[j].cur = pa->hit;
	info[j].max = pa->max_hit;
      }
      else if (info[j].type != type 
	       || str_cmp(info[j].long_desc, pa->long_desc)
	       || info[j].attacking != pa->attacking
	       || info[j].state != pa->state
	       || str_cmp(info[j].cabal, pa->pCabal->who_name))
	continue;
      else if (info[j].fGar && !IS_SET(pa->army_flags, ARMY_FLAG_GARRISON))
	continue;
      info[j].type = type;
      info[j].sub++;
      info[j].fGar = IS_SET(pa->army_flags, ARMY_FLAG_GARRISON) ? TRUE : FALSE;
      if (info[j].cur == 0 || pa->hit < info[j].cur){
	info[j].cur = pa->hit;
	info[j].max = pa->max_hit;
      }
      break;
    }
  }
  if (!bas && !def && !att)
    return;

  /* if there is a bastion, compound data from defenders into it */
  if (bas && def){
    for (i = INROOM_DEFENDERS; i < INROOM_ATTACKERS; i++){
      if (info[i].type == INROOM_DEFENDERS && info[i].fGar){
	info[i].long_desc = NULL;
	info[0].sub += info[i].sub;
	info[0].attacking = info[i].attacking;
	info[0].state = info[i].state;
	if (info[i].cur < info[0].cur){
	  info[0].cur = info[i].cur;
	  info[0].max = info[i].max;
	}
      }
    }
  }
  /* show each individual info record which has a string */
  buffer = new_buf();
  for (i = 0; i < INROOM_MAXARMIES; i++){
    if (IS_NULLSTR(info[i].long_desc))
      continue;
    if (info[i].sub > 1){
      sprintf(sub_buf, "(%d)", info[i].type == INROOM_BASTION ? info[i].sub - 1 : info[i].sub);
    }
    else
      sprintf(sub_buf, "   ");

    if (ch->pCabal && IS_CABAL(ch->pCabal, CABAL_AREA))
      sprintf(cab_buf, "[%s]", info[i].cabal );
    else
      cab_buf[0] = 0;

    /* compose the long desc */
    if (info[i].state == AS_BESE){
      sprintf( lon_buf, "%s besieged by %s.", 
	       capitalize(info[i].short_desc),
	       info[i].attacking ? info[i].attacking->pCabal->name :"Someone?");
    }
    else if (info[i].state == AS_FIGH ){
      if (info[i].type == INROOM_BASTION)
	sprintf( lon_buf, "%s being overrun by %s.",
		 capitalize(info[i].short_desc),
		 info[i].attacking->pCabal->name);
      else if (info[i].type == INROOM_DEFENDERS)
	sprintf( lon_buf, "%s defending from %s.",
		 capitalize(info[i].short_desc),
		 info[i].attacking->pCabal->name);
      else
	sprintf( lon_buf, "%s attacking %s.", 
		 capitalize(info[i].short_desc),
		 info[i].attacking->pCabal->name);
    }
    else
      sprintf( lon_buf, "%s", info[i].long_desc);
    
    if (ch->pCabal == NULL || !IS_CABAL(ch->pCabal, CABAL_AREA)){
      sprintf(buf, "%s``\n\r",
	      lon_buf);
      add_buf( buffer, buf );
    }
    else{
      sprintf(buf, "%-15s`` %s%s %s``\n\r",
	      cab_buf,
	      short_bar(info[i].cur, info[i].max),
	      sub_buf,
	      lon_buf);
      add_buf( buffer, buf );
    }
  }
  send_to_char(buf_string(buffer),ch);
  free_buf(buffer);
}
  
/* updates states/combat of all armies */
void army_update(){
  ARMY_DATA* pa, *pa_next;
  CABAL_DATA* pCab;
  int hash;

  army_combat_update();

  

  for (pCab = cabal_list; pCab; pCab = pCab->next ){
    pCab->support[ECO_LAST_ARMY] = 0;
  }

  for (hash = 0; hash < ARMY_HASH_SIZE; hash++){
    for (pa = army_list[hash]; pa; pa = pa_next){
      pa_next = pa->next;

      /* add this armie's support if it is in a room and a garrison */
      if (pa->in_room && pa->in_slot >= 0 && pa->pCabal
	  && IS_ARMY(pa, ARMY_FLAG_GARRISON)){
	pa->pCabal->support[ECO_LAST_ARMY] += get_army_support( pa, pa->in_room->area);
      }
      /* ROOM REQUIRED UPDATES */
      if (pa->in_room 
	  && pa->state != AS_BESE
	  && pa->state != AS_FIGH){
	if (pa->state == AS_DISB)
	  pa->hit = UMAX(0, pa->hit - pa->max_hit / 5);
	/* hp gain */
	else if (pa->hit < pa->max_hit 
		 && can_reinforce( pa->pCabal, pa->in_room->area )){
	  /* bastions reinforce a bit slower */
	  if (pa->pIndexData->type == ARMY_TYPE_BASTION)
	    pa->hit = UMIN(pa->max_hit, 1 + pa->hit + pa->max_hit / 20);
	  else
	    pa->hit = UMIN(pa->max_hit, 1 + pa->hit + pa->max_hit / 10);
	}
      }
      
      /* If lifetime > 0 decrease count and do not check *
       * If lifetime == 0 check
       * If lifetime < 0 skip
       */

      if (pa->lifetime > 0)
	pa->lifetime --;
      if (pa->cabal_ai_life > 0)
	pa->cabal_ai_life --;

      /* check lifetime once  for decision making */
      if (pa->lifetime == 0){
	army_ai( pa );
      }
      /* and again for actual action */
      if (pa->lifetime == 0){
	army_action( pa );
      }
    }//end for each army in the list
  }//end for each hash cell
}

//echos a combat string based on armies fighting in the room
//called from room update in db.c
struct battle_info{
  CABAL_DATA* pAtt;
  CABAL_DATA* pDef;
  bool fSiege;
};
void army_battle_echo( ROOM_INDEX_DATA* room ){
  const int battle_max = 6;
  const int siege_max = 5;
  char* battle_msg[] = 
  {
    "With a thunderous roar the armies meet and clash.",
    "As $t and $T battle screams of the wounded fill the area.",
    "$t's armies charge at $T with a mighty cry.",
    "$t's forces surge forward forcing $T to retreat.",
    "$t's forces fall back before $T's offense.",
    "The clash of metal fills the air as the battle rages.",
  };
  char* siege_msg[] = 
  {
    "The ground shakes as $t siege engines take new position.",
    "Death errupts amidst $T's defenders as siege engines fire.",
    "$t's forces break like a wave against the defenses.",
    "$t's armies charge at $T defenses.",
    "Battlehorns bellow as $t regroups for another attack.",
  };
  
  ARMY_DATA* pa;
  char* msg;
  struct battle_info battles[INROOM_MAXARMIES];
  int i, j;

  if (room->people == NULL || room->room_armies.count < 1)
    return;

  memset( &battles, 0, sizeof( struct battle_info ) * INROOM_MAXARMIES );

  /* we run through any attackers in the room, marking cabas that fight */
  for (i = INROOM_ATTACKERS; i < INROOM_MAXARMIES; i++){
    if ( (pa = room->room_armies.armies[i]) == NULL)
      continue;
    else if (pa->attacking == NULL)
      continue;

    /* run through the battle info and fill in a non duplicate spot */
    for (j = 0; j < INROOM_MAXARMIES; j++){
      //initial entry
      if (battles[j].pAtt == NULL){
	battles[j].pAtt = pa->pCabal;
	battles[j].pDef = pa->attacking->pCabal;
	if (IS_ARMY(pa->attacking, ARMY_FLAG_GARRISON) 
	    || pa->attacking->pIndexData->type == ARMY_TYPE_BASTION)
	  battles[j].fSiege = TRUE;
	break;
      }
      //matching entry
      else if (battles[j].pAtt == pa->pCabal 
	       && battles[j].pDef == pa->attacking->pCabal){
	if (IS_ARMY(pa->attacking, ARMY_FLAG_GARRISON) 
	    || pa->attacking->pIndexData->type == ARMY_TYPE_BASTION)
	  battles[j].fSiege = TRUE;
	break;
      }
      else
	break;
    }
  }

  /* spit out a message for each pair of cabals */
  for (j = 0; j < INROOM_MAXARMIES; j++){
    if (battles[j].pAtt == NULL)
      break;
    if (battles[j].fSiege)
      msg = siege_msg[rand() % siege_max];
    else
      msg = battle_msg[rand() % battle_max];
    act(msg,room->people, battles[j].pAtt->name, battles[j].pDef->name, TO_ALL );
  }
}

/* save all armies in the game */
void save_armies(){
  FILE* fp;
  char path[MIL];

  log_string("Saving Army Data...");
  fclose(fpReserve);
  sprintf(path, "%s%s",SAVEARMY_DIR, SAVEARMY_TEMPFILE);

  if ((fp = fopen(path,"w")) == NULL){
    bug("save_armies: could not open SAVEARMY_TEMPFILE for write.", 0);
    perror(path);
    fpReserve = fopen( NULL_FILE, "r" );
    return;
  }

/* have save file to be written too */
  if (!fwrite_armies( fp )){
    bug("save_armies: Error occured. Removing tempfile.", 0);
    fclose( fp );
    fpReserve = fopen( NULL_FILE, "r" );
    remove( path );
    log_string("Saving Armies: FAILURE...");

  }
  else{
    char truepath[MIL];
    fclose( fp );
    fpReserve = fopen( NULL_FILE, "r" );
    sprintf(truepath, "%s%s",SAVEARMY_DIR, SAVEARMY_FILE);
    rename( path, truepath );
  }
}

/* loads all armies in the game */
void load_armies( ){
  FILE* fp;
  char path[MIL];

  fclose(fpReserve);
  sprintf(path, "%s%s",SAVEARMY_DIR, SAVEARMY_FILE);

  if ((fp = fopen(path,"r")) == NULL){
    fpReserve = fopen( NULL_FILE, "r" );
    return;
  }

  fread_armies( fp );
  fix_armies();
  fread_influences( fp );
  fclose( fp );
  fpReserve = fopen( NULL_FILE, "r" );
}


TOWER_DATA* new_tower(){

  TOWER_DATA *ptow = malloc( sizeof( *ptow ));

  ptow->next	= NULL;
  ptow->pCabal	= NULL;
  ptow->pArea	= NULL;
  ptow->towers	= 0;
  ptow->fRein	= FALSE;

  return ptow;
}

void free_tower( TOWER_DATA* ptow ){
  free( ptow );
}


void add_tower( TOWER_DATA* tower, AREA_DATA* area ){
  TOWER_DATA* ptow = area->towers;

  if (area->towers == NULL){
    area->towers = tower;
    tower->next = NULL;
  }
  else{
    while( ptow->next ){
      ptow = ptow->next;
    }
    
    ptow->next = tower;
    tower->next = NULL;
  }
  tower->pArea = area;
}

bool rem_tower( TOWER_DATA* tower, AREA_DATA* area ){
  TOWER_DATA* prev = area->towers;

  if (area->towers == NULL){
    bug("rem_tower: area towers null.", 0);
    return FALSE;
  }
  if (area->towers == tower )
    area->towers = tower->next;
  else{
    while (prev->next && prev->next != tower){
      prev = prev->next;
    }
  }
  if (prev == NULL){
    bug("rem_tower: tower not found.", 0);
    return FALSE;
  }
  prev->next = tower->next;
  tower->next = NULL;
  tower->pArea = NULL;
  return TRUE;
}

TOWER_DATA* get_tower_data( CABAL_DATA* pCabal, AREA_DATA* area ){
  TOWER_DATA* ptow;

  for (ptow = area->towers; ptow; ptow = ptow->next){
    if (ptow->pCabal == pCabal)
      return ptow;
  }
  return NULL;
}

/* changes the alliegence of given area and announces it */
void area_to_cabal( AREA_DATA* area, CABAL_DATA* pc, bool fSilent ){
  char buf[MIL];
  DESCRIPTOR_DATA* d;

  if (area == NULL || pc == NULL)
    return;
  else if (area->pCabal == pc )
    return;
  else if (area->pCabal == NULL){
    if (!fSilent){
      sprintf(buf, "\n\rA messenger hands you a scroll '[%s] has conquered %s!'\n\r",
	      pc->who_name, area->name);
      for ( d = descriptor_list; d; d = d->next ){
	if ( d->connected == CON_PLAYING ){
	  send_to_char( buf, d->character );
	}
      }
    }
    area->pCabal = pc;
/* add the support of this area to sup income */
    SUPPORT_GAIN(pc, ECO_INCOME, area->support );
  }
  else{
    if (!fSilent){
      sprintf(buf, "\n\rA messenger hands you a scroll '[%s] has taken %s from [%s]!'\n\r",
	      pc->who_name, area->name, area->pCabal->who_name);
      for ( d = descriptor_list; d; d = d->next ){
	if ( d->connected == CON_PLAYING ){
	  send_to_char( buf, d->character );
	}
      }
    }
/* remove the support of this area from sup income */
    SUPPORT_GAIN(area->pCabal, ECO_INCOME, -area->support );

/* remove reinforcement from this area, and any other connecting to it */
    unreinforce_area( area->pCabal, area, FALSE );

    area->pCabal = pc;
/* add the support of this area to sup income */
    SUPPORT_GAIN(area->pCabal, ECO_INCOME, area->support );
  }
  reinforce_area( pc, area );
}

/* changes the alliegence of given area to neutral */
void area_from_cabal( AREA_DATA* area,  bool fSilent ){
  CABAL_DATA* pc = NULL;
  char buf[MIL];
  DESCRIPTOR_DATA* d;

  if (area == NULL)
    return;
  else if (area->pCabal == NULL )
    return;
  else{
    if (!fSilent){
      sprintf(buf, "\n\rA messenger hands you a scroll '%s has successfuly revolted against [%s]!'\n\r",
	      area->name, area->pCabal->who_name);
      for ( d = descriptor_list; d; d = d->next ){
	if ( d->connected == CON_PLAYING ){
	  send_to_char( buf, d->character );
	}
      }
    }
    SUPPORT_GAIN(area->pCabal, ECO_INCOME, area->support );
    pc = get_parent( area->pCabal );
    area->pCabal = NULL;
  }
/* remove reinforcement from this area, and any other connecting to it */
  unreinforce_area( pc, area, FALSE );

}

TOWER_DATA* tower_to_area( CABAL_DATA* pCabal, AREA_DATA* area, bool fSilent ){
  CABAL_DATA* pc = get_parent( pCabal );
  TOWER_DATA* ptow;

  if (pc == NULL)
    return NULL;

  if ( (ptow = get_tower_data(pc, area)) != NULL){
    ptow->towers ++;
  }
  else{
    ptow = new_tower();
    
    ptow->pCabal	= pc;
    ptow->pArea		= area;
    ptow->towers	++;
    add_tower(ptow, area );
  }
  area->bastion_current = UMIN(area->bastion_current + 1, area->bastion_max);

/* check if we own majority */
  if ( (area->bastion_max - ptow->towers) < ptow->towers ){
    area_to_cabal(area, pc, fSilent );
    //refresh global reinforcements
    refresh_cabal_support();    
  }
  return ptow;
}

/* reinforces the area, if owned by friendly reinforces all connected, unreinforced, unmarked areas */
void reinforce_area( CABAL_DATA* pCabal, AREA_DATA* area ){
  CABAL_DATA* pc = get_parent( pCabal );
  TOWER_DATA* ptow;
  EXIT_DATA* pe;

  if (pc == NULL)
    return;

  if ( (ptow = get_tower_data(pc, area)) != NULL){
    ptow->fRein  = TRUE;
  }
  else{
    ptow = new_tower();
    
    ptow->pCabal	= pc;
    ptow->pArea		= area;
    ptow->towers	= 0;
    ptow->fRein		= TRUE;
    add_tower(ptow, area );
  }

  /* mark this area */
  SET_BIT(area->area_flags, AREA_MARKED);

  /* if this area is friendly reinforce connected areas */
  if ( area->pCabal != NULL 
       && is_friendly(area->pCabal, pCabal) == CABAL_FRIEND){
    /* check each exit from this area */
    for (pe = area->exits; pe; pe = pe->next_in_area ){
      if (pe->to_room == NULL || pe->to_room->area == NULL || pe->to_room->area == area)
	continue;
      if (IS_AREA(pe->to_room->area, AREA_MARKED))
	continue;
      /* do not check areas already reinforced */
      if ( (ptow = get_tower_data(pc, pe->to_room->area)) != NULL
	   && ptow->fRein)
	continue;

      /* reinforce this areas now */
      reinforce_area( pCabal, pe->to_room->area);
    }//end for
  }//END REINFORCE AREA CONNECTED TO THIS AREA
  
  REMOVE_BIT(area->area_flags, AREA_MARKED);
  return;
}

void tower_from_area( CABAL_DATA* pCabal, AREA_DATA* area ){
  CABAL_DATA* pc = get_parent( pCabal );
  TOWER_DATA* ptow;
  
  if (pc == NULL)
    return;
  
  if ( (ptow = get_tower_data(pc, area)) == NULL){
    bug("tower_from_area: could not find a tower for cabal %d", pc->vnum );
    return;
  }
  
  if (--ptow->towers < 0){
    ptow->towers = 0;
  }
  if (--area->bastion_current < 0)
    area->bastion_current = 0;
}

void reinforce_refresh( CABAL_DATA* pCabal ){
  AREA_DATA* pa;

  if (pCabal == NULL 
      || pCabal->anchor == NULL 
      || !IS_CABAL(pCabal, CABAL_AREA))
    return;
  
  /* reset this cabal's reinforcement on ALL areas */
  for (pa = area_first; pa; pa = pa->next ){
    if (pa->towers)
      unreinforce_area( pCabal, pa, TRUE );
  }

  /* start the chain reaction in the cabal */
  reinforce_area( pCabal, pCabal->anchor->area );
}

/* returns true if a refresh is needed */
bool unreinforce_area( CABAL_DATA* pCabal, AREA_DATA* area, bool fShort ){
  CABAL_DATA* pc = get_parent( pCabal );
  CABAL_DATA* pCab;
  TOWER_DATA* ptow;
  EXIT_DATA* pe;
  int cnt = 0;

  if (pc == NULL)
    return FALSE;
  
  if ( (ptow = get_tower_data(pc, area)) == NULL){
    return FALSE;
  }
  else
    ptow->fRein = FALSE;

    
  if (fShort)
    return FALSE;

  /* we now count how many reinforced areas this area connected to */
  for (pe = area->exits; pe; pe = pe->next_in_area ){
    if (pe->to_room == NULL || pe->to_room->area == NULL)
      continue;

    if ( (ptow = get_tower_data(pc, pe->to_room->area)) != NULL)
      cnt ++;
  }

  if (cnt > 1){
    for (pCab = cabal_list; pCab; pCab = pCab->next){
      if (!IS_CABAL(pCab, CABAL_AREA) || IS_CABAL(pCab, CABAL_SUBCABAL) || pCab->anchor == NULL)
	continue;
      reinforce_refresh( pCab );
    }
  }
  return cnt;
}

/* checks if a given cabal can reinforce the area with troops *
   - If owned by any cabal, then friendly cabals have reinforcement
   - otherwise check for flag on tower data
*/
bool can_reinforce( CABAL_DATA* pCabal, AREA_DATA* area ){
  TOWER_DATA* pt;
  CABAL_DATA* pCab = get_parent(pCabal);
/*
  if (mud_data.mudport == TEST_PORT)
    return TRUE;
  else
*/
  if (pCab == NULL || area == NULL)
    return FALSE;
  else if ( (pt = get_tower_data(pCab, area)) != NULL && pt->fRein)
    return TRUE;
  else
    return FALSE;
}

/* used on reboot and when bastion max is changed in olc */
void refresh_area_support(){
  AREA_DATA* pa;

  /* first we get the total number of bastions in the game */
  mud_data.tot_bastion = 1;

  for (pa = area_first; pa; pa = pa->next){
    mud_data.tot_bastion += pa->bastion_max;
  }

  /* now we run thruogh the areas and calculate the support values */
  for (pa = area_first; pa; pa = pa->next){
    pa->support = pa->bastion_max * WORLD_SUPPORT_VALUE / mud_data.tot_bastion;
  }

/* done */
}

/* returns a pointer to army if one is present in this room */
ARMY_DATA* get_army_room( CHAR_DATA* ch, ROOM_INDEX_DATA* room,char* name){
  CHAR_DATA* vch = ch;
  int i;
  
  if (room->room_armies.count < 1)
    return NULL;
  for (i = INROOM_BASTION; i < INROOM_MAXARMIES; i++){
    if (room->room_armies.armies[i] == NULL)
      continue;
    else if (!is_name(name, room->room_armies.armies[i]->noun))
      continue;
    else
      return (room->room_armies.armies[i]);
  }

  //auto
  if (IS_AFFECTED(ch, AFF_CHARM) && ch->leader)
    vch = ch->leader;
  else
    vch = ch;
  if (!IS_GAME(vch, GAME_AMOB))
    return NULL;
  for (i = INROOM_BASTION; i < INROOM_MAXARMIES; i++){
    if (room->room_armies.armies[i] == NULL)
      continue;
    else if (!is_auto_name(name, room->room_armies.armies[i]->noun))
      continue;
    else
      return (room->room_armies.armies[i]);
  }
  return NULL;
}
    
/* OLD */  
/* removes any pExAlly armies garrisoned in pCab's areas with pCab's bastions */
bool break_alliance_check( CABAL_DATA* pCab, CABAL_DATA* pExAlly ){
  //  ARMY_DATA* pa;
  bool fFound = FALSE;

  /* run through all armies of pExAlly cabal */
  /* check if the army is in pCab area */

  return fFound;
}