asgard/
asgard/.settings/
asgard/area/
asgard/data/clans/
asgard/data/clans/history/
asgard/data/rosters/
asgard/src/notice/
#include <sys/types.h>
#include <sys/time.h> 
#include <malloc.h>
#include <stdio.h> 
#include <string.h> 
#include <stdlib.h> 
#include <assert.h> 
#include <ctype.h> 
#include <time.h> 
#include "merc.h" 
DECLARE_DO_FUN(do_look );

#define LESSER(x,y)             (((x) < (y)) ? (x) : (y)) 
#define GREATER(x,y)    (((x) > (y)) ? (x) : (y)) 

#define TEAM_MAX                6 

typedef struct _challenge_t
{
	struct _challenge_t *next;
	int id;
	int green;
	int flags; /* CC_???, defined below */
	char *team1[TEAM_MAX];
	char *team2[TEAM_MAX];
	int status1[TEAM_MAX];
	int status2[TEAM_MAX];

} challenge_t;

/* condition flags */
#define CC_NOPOTIONS            0x001 
#define CC_NOSCROLLS            0x002 
#define CC_NOWIMPY              0x004 

/* status flags */
#define CS_ACCEPT               0x001 
#define CS_NORECALL             0x002 
#define CS_AFK                  0x004 
#define CS_CURSE                0x008 

// initialize status to CS_INIT 
// challenge can proceed only when everyone's status is CS_GO 
#define CS_INIT                 0 
#define CS_GO                   CS_ACCEPT 

challenge_t *chal_head = (challenge_t *) NULL;
challenge_t *cur_chal = (challenge_t *) NULL;
int challenge_count = 0;

int in_current_challenge(char *pname);
static int named_in_challenge(char *pname, challenge_t *pchal);
static challenge_t *find_challenge(int cnum);
static void cleanup_challenge(challenge_t *pchal);
static void arena_message(char *message);
static void challenge_message(char *message, challenge_t *pchal);
static char *challenge_string(challenge_t *pchal, char *msg, int reverse);
static char *challenge_flags(challenge_t *pchal);
static int player_challenges(char *pname);
static int challenge_update(challenge_t *pchal);
static void start_duel(challenge_t *pchal);
static void challenge_status(int cnum, CHAR_DATA *ch);
static int challenge_finished(challenge_t *pchal);
static void noncom_check(void);
void arena_update( void );

struct _cond
{
	char *keyword;
	int bit;
	char *name;
	char *flag;
};

struct _cond cond_table[] =
{
{ "np", CC_NOPOTIONS, "no_potions", "{^P{x" },
{ "ns", CC_NOSCROLLS, "no_scrolls", "{!S{x" },
{ "nw", CC_NOWIMPY, "no_wimpy", "{$W{x" },
{ NULL, 0, NULL, '\0' }, };

/*=======================================================================* 
 * function: arena_update 
 * purpose: called every tick from update.c.  check on stuff 
 *=======================================================================*/
void arena_update(void)
{
	challenge_t *pchal, *pnext;
	char buf[MAX_STRING_LENGTH];
	int winner, i; // whichteam;
	CHAR_DATA *pplayer;
	//		CHAR_DATA				*pplayercheck;
	CHAR_DATA *rch;

	// update all the individual challenges
	for (pchal = chal_head; pchal; pchal = pnext)
	{
		pnext = pchal->next;
		challenge_update(pchal);
	}

	// scan the arena and boot mortal non-combatants therefrom
	noncom_check();

	// if any challenge is running, see if it's done.  You can tell because
	// one team will be entirely absent from rooms ARENA_LO to ARENA_HI.
	if ((cur_chal) && ((winner = challenge_finished(cur_chal))))
	{
		// tell everyone about it
		sprintf(buf, "[{$ARENA{x] Challenge [%d]:  ", cur_chal->id);
		strcat(buf, challenge_string(cur_chal, "DEFEATED ", ((winner == 2))));
		strcat(buf, "{x\n\r");
		arena_message(buf);

		// send everyone to lounge
		for (i = 0; i < (2 * TEAM_MAX); i++)
		{
			if (cur_chal->team1[i] == NULL)
				continue;

			pplayer = get_char_anyone(NULL, cur_chal->team1[i]);

			if (pplayer == NULL)
				continue;

			for (rch = pplayer->in_room->people; rch != NULL; rch
					= rch->next_in_room)
			{
				if (rch->fighting != NULL)
				{
					stop_fighting(rch, TRUE );
				}
			}
			//Dusk
			affect_strip(pplayer, skill_lookup("cripple"));
			affect_strip(pplayer, skill_lookup("siphon energy"));
			affect_strip(pplayer, skill_lookup("siphon life"));
			//JLR
			affect_strip(pplayer, skill_lookup("poison"));
			affect_strip(pplayer, skill_lookup("chill touch"));
			affect_strip(pplayer, skill_lookup("fire breath"));
			affect_strip(pplayer, skill_lookup("slow"));
			affect_strip(pplayer, skill_lookup("weaken"));
			affect_strip(pplayer, skill_lookup("curse"));
			affect_strip(pplayer, skill_lookup("wither"));
			affect_strip(pplayer, skill_lookup("blindness"));
			affect_strip(pplayer, skill_lookup("blindness dust"));
			affect_strip(pplayer, skill_lookup("plague"));
			affect_strip(pplayer, skill_lookup("shriek"));
			affect_strip(pplayer, skill_lookup("sleep"));
			//Tien
			affect_strip(pplayer, skill_lookup("feeble mind"));
			affect_strip(pplayer, skill_lookup("dirt kick"));
			affect_strip(pplayer, skill_lookup("faerie fire"));
			affect_strip(pplayer, skill_lookup("headache"));
			affect_strip(pplayer, skill_lookup("adamantium palm"));
			affect_strip(pplayer, skill_lookup("call lightning"));
			affect_strip(pplayer, skill_lookup("prismatic spray"));
			affect_strip(pplayer, skill_lookup("gouge"));
			affect_strip(pplayer, skill_lookup("charm person"));
			/* Stheno/Revye */
			affect_strip(pplayer, skill_lookup("nerve"));
			affect_strip(pplayer, skill_lookup("garrote"));
			affect_strip(pplayer, skill_lookup("fear aura"));
			affect_strip(pplayer, skill_lookup("burning skin"));
			affect_strip(pplayer, skill_lookup("smokebomb"));
			affect_strip(pplayer, skill_lookup("dirt kicking"));
			affect_strip(pplayer, skill_lookup("voodan curse"));
			affect_strip(pplayer, skill_lookup("sense vitality"));
			/* affect_strip(pplayer,skill_lookup(""));
			 */

			pplayer->hit = pplayer->max_hit;
			pplayer->mana = pplayer->max_mana;
			pplayer->move = pplayer->max_move;

			//Jair
			/* This is a hack job to get arena wins and losses working */
			/*					whichteam=0;


			 for( i = 0; (pchal->team1[i]) && (i<TEAM_MAX); i++ )
			 {
			 pplayercheck = get_char_anyone( NULL,pchal->team1[i] );
			 if (pplayercheck == pplayer)
			 whichteam=1;
			 }

			 for( i = 0; (pchal->team1[i]) && (i<TEAM_MAX); i++ )
			 {
			 pplayercheck = get_char_anyone( NULL,pchal->team1[i] );
			 if (pplayercheck == pplayer)
			 whichteam=2;
			 }

			 if (whichteam == winner)
			 pplayer->pcdata->awins += 1;
			 else
			 pplayer->pcdata->alosses += 1;

			 */

			char_from_room(pplayer);
			char_to_room(pplayer, get_room_index(ROOM_ARENA_LOUNGE));
			act("$n appears in the room.", pplayer, NULL, NULL, TO_ROOM);
			do_look(pplayer, "auto");
		}

		cleanup_challenge(cur_chal);
		cur_chal = (challenge_t *) NULL;
	}

	// if no challenge is going, look for a green one
	if (!cur_chal)
	{
		for (pchal = chal_head; (pchal) && (!pchal->green); pchal = pchal->next)
			;

		if (pchal)
		{
			// we have a green challenge.  do it
			start_duel(pchal);
		}
	}
}

/*=======================================================================* 
 * function: start_duel 
 * purpose: begin a duel.  transport everyone to the arena, do everything. 
 *=======================================================================*/
static void start_duel(challenge_t *pchal)
{
	challenge_t *pc;
	CHAR_DATA *pplayer;
	char buf[MAX_STRING_LENGTH];
	int i, start_pos;
	ROOM_INDEX_DATA *start1, *start2;

	assert(pchal);

	// take pchal off the challenge list
	if (chal_head == pchal)
	{ // first entry on list
		chal_head = pchal->next;
	}
	else
	{
		for (pc = chal_head; (pc->next) && (pc->next != pchal); pc = pc->next)
			;

		if (pc->next == pchal)
		{
			pc->next = pchal->next;
		}
		else
		{ // not on the list.  Bogus.  Just return.
			return;
		}
	}

	// write it to cur_chal
	cur_chal = pchal;

	// tell everyone about it
	sprintf(buf, "[{$ARENA{x] Challenge [%d]%s:  ", pchal->id, challenge_flags(
			pchal));
	strcat(buf, challenge_string(pchal, "VERSUS ", FALSE ));
	strcat(buf, "{Rhas begun!{x\n\r\n\r");
	arena_message(buf);

	start_pos = ROOM_ARENA_LO + 1 + (rand() % (ROOM_ARENA_HI - ROOM_ARENA_LO));
	start1 = get_room_index(start_pos);
	start_pos = ROOM_ARENA_LO + 1 + (rand() % (ROOM_ARENA_HI - ROOM_ARENA_LO));
	start2 = get_room_index(start_pos);

	if ((start1 == NULL) || (start2 == NULL))
	{
		printf_debug("Arena doesn't exist.");
		cleanup_challenge(pchal);
		cur_chal = NULL;
		return;
	}

	// here's where we'd send all the people to the arena and stuff
	for (i = 0; (pchal->team1[i]) && (i < TEAM_MAX); i++)
	{
		pplayer = get_char_anyone(NULL, pchal->team1[i]);
		//		pplayer_orig = pplayer;
		char_from_room(pplayer);
		char_to_room(pplayer, start1);
		affect_strip(pplayer, gsn_plague);
		affect_strip(pplayer, gsn_poison);
		affect_strip(pplayer, gsn_blindness);
		affect_strip(pplayer, gsn_sleep);
		affect_strip(pplayer, gsn_curse);
		pplayer->hit = pplayer->max_hit;
		pplayer->mana = pplayer->max_mana;
		pplayer->move = pplayer->max_move;
		update_pos(pplayer);
		send_to_char("You have been Pre-Arena Restored.\n", pplayer);
		act("$n appears in the room.", pplayer, NULL, NULL, TO_ROOM);
		do_look(pplayer, "auto");
		//              send_to_char( "If the arena were working, you'd be sent to the arena "
		//                      "now.\n\r", pplayer );
	}

	for (i = 0; (pchal->team2[i]) && (i < TEAM_MAX); i++)
	{
		pplayer = get_char_anyone(NULL, pchal->team2[i]);
		//                pplayer_orig = pplayer;
		char_from_room(pplayer);
		char_to_room(pplayer, start2);
		affect_strip(pplayer, gsn_plague);
		affect_strip(pplayer, gsn_poison);
		affect_strip(pplayer, gsn_blindness);
		affect_strip(pplayer, gsn_sleep);
		affect_strip(pplayer, gsn_curse);
		pplayer->hit = pplayer->max_hit;
		pplayer->mana = pplayer->max_mana;
		pplayer->move = pplayer->max_move;
		update_pos(pplayer);
		send_to_char("You have been Pre-Arena Restored.", pplayer);
		act("$n appears in the room.", pplayer, NULL, NULL, TO_ROOM);
		do_look(pplayer, "auto");

		//              send_to_char( "If the arena were working, you'd be sent to the arena "
		//                      "now.\n\r", pplayer );
	}
}

/*=======================================================================* 
 * function: do_challenge 
 * purpose: sends initial arena match query 
 *=======================================================================*/
void do_challenge(CHAR_DATA *ch, char *argument)
{
	CHAR_DATA *victim;
	char buf[MAX_STRING_LENGTH], arg[MAX_INPUT_LENGTH];
	int done = FALSE, i;
	int team = 0;
	int count = 0;
	int reuse = FALSE;
	int hilevel = 0, lowlevel = 0;
	challenge_t *pchal, *pc;

	if (IS_NPC(ch))
		return;

	if (arena == FIGHT_LOCK)
	{
		send_to_char("Sorry, the arena is currently locked from use.\n\r", ch);
		return;
	}

	if (ch->hit < ch->max_hit * 3 / 4)
	{
		send_to_char("You are not healthy enough to fight in the arena.\n\r",
				ch);
		return;
	}

	if (argument[0] == '{' && argument[1] == '\0')
		return;

	if (argument[0] == '-')
		return;

	if (argument[0] == '"')
		return;

	// challenge block?
	if (!strcasecmp(argument, "block"))
	{
		if (IS_SET(ch->comm,COMM_NOARENA))
		{
			REMOVE_BIT(ch->comm,COMM_NOARENA);
			send_to_char(
					"You will now receive arena updates and challenges.\n\r",
					ch);
		}
		else
		{
			SET_BIT(ch->comm,COMM_NOARENA);
			send_to_char(
					"You will no longer receive arena updates and challenges.\n\r",
					ch);
		}

		return;
	}

	// challenge list?
	if (!strcasecmp(argument, "list"))
	{
		if (cur_chal)
		{
			sprintf(buf, "Current challenge [%d]%s:  ", cur_chal->id,
					challenge_flags(cur_chal));
			strcat(buf, challenge_string(cur_chal, "VERSUS ", FALSE));
			strcat(buf, "\n\r");
			send_to_char(buf, ch);
		}

		if (chal_head)
		{
			for (pc = chal_head; pc; pc = pc->next)
			{
				sprintf(buf, "{%cChallenge [%d]%s:{x  ", pc->green ? 'G' : 'R',
						pc->id, challenge_flags(pc));
				strcat(buf, challenge_string(pc, "VERSUS ", FALSE));
				strcat(buf, "\n\r");
				send_to_char(buf, ch);
			}
		}
		else
			send_to_char("No pending challenges.\n\r", ch);
		return;
	}
	// challenge status
	if (!strncasecmp(argument, "status", 6))
	{
		argument = one_argument(argument, arg);
		argument = one_argument(argument, arg);

		if (arg[0] == 0)
		{
			send_to_char("Give a challenge number.\n\r", ch);
			return;
		}

		challenge_status(atoi(arg), ch);
		return;

	}

	if (IS_SET(ch->comm,COMM_NOARENA))
	{
		send_to_char("You're configured to ignore the arena.\n\r", ch);
		return;
	}

	if (ch->in_room->vnum == ROOM_VNUM_CORNER)
		return;

	if (argument[0] == '\0')
	{
		send_to_char(
				"Usage: challenge [-conditions] <opponent1> [opponent2] ... [with] [teammate1] [teammate2] ....\n\r",
				ch);
		return;
	}

	// so far so good.  allocate.
	pchal = (challenge_t *) calloc(1L, sizeof(challenge_t));

	if (pchal == NULL)
		return;

	// write our name to the head of team1
	pchal->team1[0] = strdup(ch->name);
	count = 0;

	// set challeger accepted
	pchal->status1[0] |= CS_ACCEPT;

	// initialize hilevel and lowlevel
	hilevel = ch->level;
	lowlevel = ch->level;

	// now peel off the names, one at a time
	do
	{
		argument = one_argument(argument, arg);

		// end of args?
		if (arg[0] == 0)
		{
			done = TRUE;
			continue;
		}

		// look for the team separator
		if ((!strcmp(arg, "with")) && (team == 0))
		{
			if (count == 0)
			{
				send_to_char("You need at least one player on the other t"
					"eam.\n\r", ch);
				cleanup_challenge(pchal);
				return;
			}

			team++;
			count = 1;
			continue;
		}

		// condition flag?
		if (arg[0] == '-')
		{
			for (i = 0; cond_table[i].keyword; i++)
			{
				if (!strcasecmp(&arg[1], cond_table[i].keyword))
					pchal->flags |= cond_table[i].bit;
			}

			continue;
		}

		// match name
		victim = get_char_world(ch, arg);

		// not found?
		if (victim == NULL)
		{
			sprintf(buf, "Bad target: %s\n\r", arg);
			send_to_char(buf, ch);
			cleanup_challenge(pchal);
			return;
		}

		// npc, immortal, or self?
		if (IS_NPC(victim) || victim == ch || victim == NULL)
		{
			send_to_char("You cannot challenge NPC's, or yourself.\n\r", ch);
			cleanup_challenge(pchal);
			return;
		}

		if (victim->hit < victim->max_hit * 2 / 3)
		{
			send_to_char(
					"They are not healthy enough to fight in the arena.\n\r",
					ch);
			return;
		}

		if (victim->fight_timer > 0)
		{
			send_to_char("They cannot fight in the arena right now.\n\r", ch);
			return;
		}

		if (ch->in_room->area->continent != victim->in_room->area->continent)
		{
			send_to_char("They are too far away to arena.\n\r", ch);
			return;
		}

		for (i = 0; (pchal->team1[i]) && (i < TEAM_MAX); i++)
			if (!strcmp(victim->name, pchal->team1[i]))
				reuse = TRUE;

		for (i = 0; (pchal->team2[i]) && (i < TEAM_MAX); i++)
			if (!strcmp(victim->name, pchal->team2[i]))
				reuse = TRUE;

		// already named?
		if (reuse)
		{
			send_to_char( "Each player can be named only once in a given challenge.\n\r", ch);
			cleanup_challenge(pchal);
			return;
		}

		// noarena?
		if (IS_SET(victim->comm,COMM_NOARENA))
		{
			sprintf(buf, "%s is blocking all challenges.\n\r", victim->name);
			send_to_char(buf, ch);
			cleanup_challenge(pchal);
			return;
		}

		// named in too many?
		if (player_challenges(victim->name) >= 4)
		{
			sprintf(buf, "%s is already named in four challenges.  That's"
				" the limit.\n\r", victim->name);
			send_to_char(buf, ch);
			return;
		}

		// team full?
		if (count >= TEAM_MAX)
		{
			send_to_char("Team size limit exceeded.\n\r", ch);
			cleanup_challenge(pchal);
			return;
		}

		hilevel = GREATER(hilevel,victim->level);
		lowlevel = LESSER(hilevel,victim->level);

		// level range
		if ((hilevel - lowlevel > 20) && (!IS_IMMORTAL(ch)))
		{
			send_to_char(
					"All players in a challenge must be within 20 levels of one another.\n\r",
					ch);
			cleanup_challenge(pchal);
			return;
		}

		if (team)
		{
			pchal->status1[count] = CS_INIT;
			pchal->team1[count++] = strdup(victim->name);
		}
		else
		{
			pchal->status2[count] = CS_INIT;
			pchal->team2[count++] = strdup(victim->name);
		}

	} while (!done);

	// valid challenge.  Move on.

	// get an available challenge number
	pchal->id = ++challenge_count;

	// add challenge to the queue
	if (chal_head)
	{
		// walk list, append to end
		for (pc = chal_head; pc->next; pc = pc->next)
			;
		pc->next = pchal;
	}
	else
	{
		// no entries on list
		chal_head = pchal;
	}

	// announce it
	sprintf(buf, "[{$ARENA{x] Challenge [%d] issued!  ", pchal->id);
	strcat(buf, challenge_string(pchal, "VERSUS ", FALSE));
	strcat(buf, "\n\r");
	strcat(buf, "Special Conditions:");
	if (pchal->flags)
	{
		for (i = 0; cond_table[i].keyword; i++)
		{
			if (pchal->flags & cond_table[i].bit)
			{
				strcat(buf, " ");
				strcat(buf, cond_table[i].name);
			}
		}
	}
	else
	{
		strcat(buf, " None");
	}
	strcat(buf, "\n\r");
	arena_message(buf);

}

/*=======================================================================* 
 * function: do_accept                                                   * 
 * purpose: to accept the arena match, and move the players to the arena * 
 *=======================================================================*/
void do_accept(CHAR_DATA *ch, char *argument)
{
	int cnum, index;
	char buf[MAX_STRING_LENGTH];
	challenge_t *pc;

	if (argument[0] == '\0')
	{
		send_to_char("Accept challenges by number.\n\r", ch);
		return;
	}

	if (ch->fight_timer > 0)
	{
		send_to_char(
				"You cannot fight in the arena until your fight timer expires.\n\r",
				ch);
		return;
	}

	cnum = atoi(argument);

	pc = find_challenge(cnum);

	if (!pc)
	{
		send_to_char("No such challenge.\n\r", ch);
		return;
	}

	if (!(index = named_in_challenge(ch->name, pc)))
	{
		send_to_char("You're not named in that challenge.\n\r", ch);
		return;
	}

	// already accepted?
	if (pc->status1[index - 1] & CS_ACCEPT)
	{
		send_to_char("You've already accepted that challenge.\n\r", ch);
		return;
	}

	sprintf(buf, "[{$ARENA{x] Challenge %d accepted by %s.\n\r", cnum, ch->name);
	arena_message(buf);
	pc->status1[index - 1] |= CS_ACCEPT;

	// if this results in the challenge going green, do an update, so that
	// the challenge may launch immediately.
	if (challenge_update(pc))
	{
		arena_update();
	}

	return;
}

/*=======================================================================* 
 * function: do_decline                                                  * 
 * purpose: to chicken out from a sent arena challenge                   * 
 *=======================================================================*/
void do_decline(CHAR_DATA *ch, char *argument)
{
	int cnum;
	char buf[MAX_STRING_LENGTH];
	challenge_t *pc;

	if (argument[0] == '\0')
	{
		send_to_char("Decline challenges by number.\n\r", ch);
		return;
	}

	cnum = atoi(argument);

	pc = find_challenge(cnum);

	if (!pc)
	{
		send_to_char("No such challenge.\n\r", ch);
		return;
	}

	if ((!named_in_challenge(ch->name, pc)) && (!IS_IMMORTAL(ch)))
	{
		send_to_char("You're not named in that challenge.\n\r", ch);
		return;
	}

	sprintf(buf, "[{$ARENA{x] Challenge %d declined by %s.\n\r", cnum, ch->name);
	arena_message(buf);
	cleanup_challenge(pc);

	return;
}

/*======================================================================* 
 * function: do_bet                                                     * 
 * purpose: to allow players to wager on the outcome of arena battles   * 
 *======================================================================*/
void do_bet(CHAR_DATA *ch, char *argument)
{
	return;
}

/*=======================================================================* 
 * function: in_current_challenge 
 * purpose: returns 0 if player isn't in current challenge (or if there 
 * isn't one.)  Returns 1 if he's on team 1, 2 if he's on team 2 
 *=======================================================================*/
int in_current_challenge(char *pname)
{
	int index;

	index = named_in_challenge(pname, cur_chal);

	if (index > TEAM_MAX)
		return 2;

	if (index)
		return 1;

	return 0;
}

/*=======================================================================* 
 * function: named_in_challenge                                          * 
 * purpose: returns TRUE if pname is named in challenge pchal            * 
 *=======================================================================*/
static int named_in_challenge(char *pname, challenge_t *pchal)
{
	int i;

	if (pchal == (challenge_t *) NULL)
		return 0;

	for (i = 0; (pchal->team1[i]) && (i < TEAM_MAX); i++)
		if (!strcasecmp(pname, pchal->team1[i]))
			return i + 1;

	for (i = 0; (pchal->team2[i]) && (i < TEAM_MAX); i++)
		if (!strcasecmp(pname, pchal->team2[i]))
			return i + 1 + TEAM_MAX;

	return FALSE;
}

/*=======================================================================* 
 * function: player_challenges 
 * purpose: counts the number of challenges in which a player is named 
 *=======================================================================*/
static int player_challenges(char *pname)
{
	challenge_t *pc;
	int count = 0;

	for (pc = chal_head; pc; pc = pc->next)
	{
		if (named_in_challenge(pname, pc))
			count++;
	}

	return count;
}

/*=======================================================================* 
 * function: find_challenge 
 * purpose: returns a pointer to the challenge with id cnum, NULL if none 
 *=======================================================================*/
static challenge_t *find_challenge(int cnum)
{
	challenge_t *pc;

	for (pc = chal_head; pc; pc = pc->next)
		if (pc->id == cnum)
			return pc;

	return (challenge_t *) NULL;
}

/*=======================================================================* 
 * function: arena_message 
 * purpose: send message to everyone who isn't NO_ARENA 
 *=======================================================================*/
static void arena_message(char *message)
{
	DESCRIPTOR_DATA *d;

	for (d = descriptor_list; d; d = d->next)
	{
		if ((d->connected == CON_PLAYING)
				&& (!IS_SET(d->character->comm,COMM_NOARENA)))
			send_to_char(message, d->character);
	}
}

/*=======================================================================* 
 * function: challenge_message 
 * purpose: send message to everyone named in a given challenge 
 *=======================================================================*/
static void challenge_message(char *message, challenge_t *pchal)
{
	int team, count;
	CHAR_DATA *pplayer;
	char **names;
//	int *status;

	// loop through the players
	for (team = 0; team < 2; team++)
	{
		names = (team) ? pchal->team2 : pchal->team1;
//		status = (team) ? pchal->status2 : pchal->status1;

		for (count = 0; (count < TEAM_MAX) && (names[count]); count++)
		{
			// match name
			pplayer = get_char_anyone(NULL, names[count]);

			// connected?
			if (pplayer != NULL)
				send_to_char(message, pplayer);
		}
	}
}

/*=======================================================================* 
 * function: challenge_status 
 * purpose: send status of challenge number cnum to player ch 
 *=======================================================================*/
static void challenge_status(int cnum, CHAR_DATA *ch)
{
	challenge_t *pc;
	char buf[MAX_STRING_LENGTH];
	CHAR_DATA *pplayer;
	int i;

	pc = find_challenge(cnum);

	if (pc == NULL)
	{
		send_to_char("No such challenge.\n\r", ch);
		return;
	}

	// print the title line
	sprintf(buf, "{%cChallenge [%d]:{x  ", pc->green ? 'G' : 'R', pc->id);
	strcat(buf, challenge_string(pc, "VERSUS ", FALSE));
	strcat(buf, "\n\r");
	strcat(buf, "Special Conditions:");
	if (pc->flags)
	{
		for (i = 0; cond_table[i].keyword; i++)
		{
			if (pc->flags & cond_table[i].bit)
			{
				strcat(buf, " ");
				strcat(buf, cond_table[i].name);
			}
		}
	}
	else
	{
		strcat(buf, " None");
	}
	strcat(buf, "\n\r");
	send_to_char(buf, ch);

	if (pc->green)
	{
		send_to_char("Challenge is {GREADY{x.\n\r", ch);
		return;
	}

	send_to_char("Challenge is {RBLOCKED{x because:\n\r", ch);

	// now list each problem
	for (i = 0; i < 2 * TEAM_MAX; i++)
	{
		if (!(pc->team1[i]))
			continue;

		// match the player
		pplayer = get_char_anyone(NULL, pc->team1[i]);

		// player found? (should never happen)
		if (pplayer == NULL)
		{
			sprintf(buf, "> %s isn't connected.\n\r", pplayer->name);
			send_to_char(buf, ch);
		}

		// hasn't accepted?
		if (!(pc->status1[i] & CS_ACCEPT))
		{
			sprintf(buf, "> %s hasn't accepted yet.\n\r", pplayer->name);
			send_to_char(buf, ch);
		}

		// cursed?
		if (pc->status1[i] & CS_CURSE)
		{
			sprintf(buf, "> %s hasn't accepted yet.\n\r", pplayer->name);
			send_to_char(buf, ch);
		}

		// afk?
		if (pc->status1[i] & CS_AFK)
		{
			sprintf(buf, "> %s is AFK.\n\r", pplayer->name);
			send_to_char(buf, ch);
		}

		// no-recall?
		if (pc->status1[i] & CS_NORECALL)
		{
			sprintf(buf, "> %s is in a no-recall area.\n\r", pplayer->name);
			send_to_char(buf, ch);
		}
	}
}

/*=======================================================================* 
 * function: cleanup_challenge 
 * purpose: frees all memory associated with a given challenge and 
 * removes it from the queue 
 *=======================================================================*/
static void cleanup_challenge(challenge_t *pchal)
{
	int i;
	challenge_t *pc;
	printf_debug( "Arena check 3");
	// free up names
	for (i = 0; (pchal->team1[i]) && (i < TEAM_MAX); i++)
	{
		free(pchal->team1[i]);
		pchal->team1[i] = NULL;
	}

	for (i = 0; (pchal->team2[i]) && (i < TEAM_MAX); i++)
	{
		free(pchal->team2[i]);
		pchal->team2[i] = NULL;
	}

	// take it off the list (if it's on)
	if (chal_head == pchal)
	{ // first entry on list
		chal_head = pchal->next;
	}
	else if (chal_head)
	{
		for (pc = chal_head; (pc->next) && (pc->next != pchal); pc = pc->next)
			;

		if (pc->next == pchal)
			pc->next = pchal->next;
	}

	// now free the challenge itself
	free(pchal);
	return;
}

/*=======================================================================* 
 * function: challenge_string 
 * purpose: write a brief description of a challenge to a static buffer 
 * and return a pointer thereto. 
 *=======================================================================*/
static char *challenge_string(challenge_t *pchal, char *msg, int reverse)
{
	static char buf[MAX_STRING_LENGTH];
	int i;
	char **first, **second;

	if (reverse)
	{
		first = pchal->team2;
		second = pchal->team1;
	}
	else
	{
		first = pchal->team1;
		second = pchal->team2;
	}

	buf[0] = '\0';

	// announce it
	for (i = 0; (first[i]) && (i < TEAM_MAX); i++)
	{
		strcat(buf, first[i]);
		strcat(buf, " ");
	}

	strcat(buf, msg);

	for (i = 0; (second[i]) && (i < TEAM_MAX); i++)
	{
		strcat(buf, second[i]);
		strcat(buf, " ");
	}

	return buf;
}

/*=======================================================================* 
 * function: challenge_flags 
 * purpose: write a short string containing challenge flags 
 *=======================================================================*/
static char *challenge_flags(challenge_t *pchal)
{
	static char buf[MAX_STRING_LENGTH];
	int i = 0, j;

	sprintf(buf, "{x(");
	for (j = 0; cond_table[j].keyword; j++)
	{
		if (pchal->flags & cond_table[j].bit)
		{
			strcat(buf, cond_table[j].flag);
			i++;
		}
	}
	strcat(buf, ")");

	if (i == 0)
		buf[0] = '\0';

	return buf;
}

/*=======================================================================* 
 * function: challenge_update 
 * purpose: updates the status bits of a given challenge 
 * returns TRUE if the challenge goes green as of this update 
 *=======================================================================*/
static int challenge_update(challenge_t *pchal)
{
	static char buf[MAX_STRING_LENGTH];
	int team, count;
	CHAR_DATA *pplayer;
	char **names;
	int *status;
	int green, retval = FALSE;

	green = TRUE;

	// loop through the players
	for (team = 0; team < 2; team++)
	{
		names = (team) ? pchal->team2 : pchal->team1;
		status = (team) ? pchal->status2 : pchal->status1;

		for (count = 0; (count < TEAM_MAX) && (names[count]); count++)
		{
			// match name
			pplayer = get_char_anyone(NULL, names[count]);

			// connected?
			if (pplayer == NULL)
			{
				sprintf(buf, "Challenge [%d] expires - %s has quit.\n",
						pchal->id, names[count]);
				challenge_message(buf, pchal);
				cleanup_challenge(pchal);
				return FALSE;
			}

			// no recall?
			if (IS_SET(pplayer->in_room->room_flags,ROOM_NO_RECALL))
				status[count] |= CS_NORECALL;
			else
				status[count] &= ~CS_NORECALL;

			// afk?
			if (IS_SET(pplayer->comm,COMM_AFK))
				status[count] |= CS_AFK;
			else
				status[count] &= ~CS_AFK;

			// curse?
			if (IS_AFFECTED(pplayer,AFF_CURSE))
				status[count] |= CS_CURSE;
			else
				status[count] &= ~CS_CURSE;

			// still green?
			if (status[count] != CS_GO)
				green = FALSE;
		}
	}

	if (green != pchal->green)
	{
		sprintf(buf, "Challenge [%d] is now %s.\n", pchal->id,
				green ? "{GREADY{x" : "{RBLOCKED{x");
		challenge_message(buf, pchal);
		if (green)
			retval = TRUE;
	}

	pchal->green = green;

	return retval;
}

/*=======================================================================* 
 * function: challenge_finished 
 * purpose: returns 0 if challenge isn't done yet, 1 if team 1 won, 2 if 
 * team 2 won. 
 *=======================================================================*/
static int challenge_finished(challenge_t *pchal)
{
	int i, found;
	CHAR_DATA *pc;

	found = FALSE;

	// is team 1 entirely absent from rooms ARENA_LO to ARENA_HI?
	for (i = 0; (pchal->team1[i]) && (i < TEAM_MAX) && (!found); i++)
	{
		pc = get_char_anyone(NULL, pchal->team1[i]);

		if ((pc) && (pc->in_room->vnum >= ROOM_ARENA_LO) && (pc->in_room->vnum
				<= ROOM_ARENA_HI))
			found = TRUE;
	}

	if (!found)
		return 2;

	found = FALSE;

	// is team 2 entirely absent from rooms ARENA_LO to ARENA_HI?
	for (i = 0; (pchal->team2[i]) && (i < TEAM_MAX) && (!found); i++)
	{
		pc = get_char_anyone(NULL, pchal->team2[i]);

		if ((pc) && (pc->in_room->vnum >= ROOM_ARENA_LO) && (pc->in_room->vnum
				<= ROOM_ARENA_HI))
			found = TRUE;
	}

	if (!found)
		return 1;

	return 0;

}

/*=======================================================================* 
 * function: noncom_check 
 * purpose: scan arena for mortals who shouldn't be there & remove them. 
 *=======================================================================*/
static void noncom_check(void)
{
	DESCRIPTOR_DATA *d;

	for (d = descriptor_list; d; d = d->next)
	{
		// ignore them if they're disconnected
		if (d->connected != CON_PLAYING)
			continue;

		// ignore them if they're not in the arena
		if ((d->character->in_room->vnum < ROOM_ARENA_LO)
				|| (d->character->in_room->vnum > ROOM_ARENA_MORGUE))
			continue;

		// ignore if they're in the arena lounge
		if (d->character->in_room->vnum == ROOM_ARENA_LOUNGE)
			continue;

		// ignore them if they're named in the current challenge
		if ((cur_chal) && (named_in_challenge(d->character->name, cur_chal)))
			continue;

		// let immortals hang around if they want
		if (IS_IMMORTAL(d->character))
			continue;

		// boot 'em to lounge
		send_to_char("You've been auto-removed from the arena.\n\r",
				d->character);
		char_from_room(d->character);
		char_to_room(d->character, get_room_index(ROOM_ARENA_LOUNGE));
		act("$n appears in the room.", d->character, NULL, NULL, TO_ROOM);
		do_look(d->character, "auto");

	}
}

/*=======================================================================* 
 * function: arena_can_quaff 
 * purpose: returns FALSE if ch is in arena during a challenge that forbids 
 * use of potions, true otherwise 
 *=======================================================================*/
int arena_can_quaff(CHAR_DATA *ch)
{
	if (!cur_chal)
		return TRUE;

	if (!IN_ARENA(ch))
		return TRUE;

	if (!(cur_chal->flags & CC_NOPOTIONS))
		return TRUE;

	send_to_char("No potions may be used during this duel.\n\r", ch);

	return FALSE;
}

/*=======================================================================* 
 * function: arena_can_recite 
 * purpose: returns FALSE if ch is in arena during a challenge that forbids 
 * use of scrolls, true otherwise 
 *=======================================================================*/
int arena_can_recite(CHAR_DATA *ch)
{
	if (!cur_chal)
		return TRUE;

	if (!IN_ARENA(ch))
		return TRUE;

	if (!(cur_chal->flags & CC_NOSCROLLS))
		return TRUE;

	send_to_char("No scrolls may be used during this duel.\n\r", ch);

	return FALSE;
}

/*=======================================================================* 
 * function: arena_can_wimpy 
 * purpose: returns FALSE if ch is in arena during a challenge that forbids 
 * use of wimpy, true otherwise 
 *=======================================================================*/
int arena_can_wimpy(CHAR_DATA *ch)
{
	if (!cur_chal)
		return TRUE;

	if (!IN_ARENA(ch))
		return TRUE;

	if (!(cur_chal->flags & CC_NOWIMPY))
		return TRUE;

	return FALSE;
}

/*=======================================================================*/