empiremud/cnf/
empiremud/doc/
empiremud/lib/boards/
empiremud/lib/etc/
empiremud/lib/misc/
empiremud/lib/plralias/F-J/
empiremud/lib/plralias/K-O/
empiremud/lib/plralias/P-T/
empiremud/lib/plralias/U-Z/
empiremud/lib/plrobjs/
empiremud/lib/plrobjs/F-J/
empiremud/lib/plrobjs/K-O/
empiremud/lib/plrobjs/P-T/
empiremud/lib/plrobjs/U-Z/
empiremud/lib/world/
empiremud/lib/world/mob/
empiremud/lib/world/obj/
empiremud/log/
/* ************************************************************************
*   File: empire.c                                       EmpireMUD AD 1.0 *
*  Usage: stores all of the empire-related code and commands              *
*                                                                         *
*  All rights reserved.  See license.doc for complete information.        *
*                                                                         *
*  Code base by Paul Clarke.  EmpireMUD Project, a tbgMUD Production.     *
*  Based upon CircleMUD 3.0, beta patch level 17, by Jeremy Elson.        *
*                                                                         *
*  Copyright (C) 1993, 94 by the Trustees of the Johns Hopkins University *
*  CircleMUD is based on DikuMUD, Copyright (C) 1990, 1991.               *
************************************************************************ */

#include "conf.h"
#include "sysdep.h"

#include "structs.h"
#include "utils.h"
#include "interpreter.h"
#include "comm.h"
#include "handler.h"
#include "db.h"
#include "empire.h"
#include "skills.h"
#include "vnums.h"

extern int top_of_p_table;
void save_char_file_u(struct char_file_u st);
extern struct player_index_element *player_table;


/* Constants stored here for practical purposes */
const char *priv[] = {
	"claim",
	"build",
	"harvest",
	"promote",
	"chop",
	"cede",
	"enroll",
	"withdraw",
	"diplomacy",
	"\n"
	};


/* Local Data */
struct empire_data *empire = NULL;
int top_of_empiret = -1;


int real_empire(long id) {
	int i;

	if (top_of_empiret < 0)
		return -1;

	for (i = 0; i <= top_of_empiret; i++)
		if (empire[i].leader == ABSOLUTE(id))
			return i;
	return -1;
	}


int get_empire_by_name(char *name) {
	int e, num = -1;

	if (isdigit(*name))
		num = atoi(name) - 1;

	for (e = 0; e <= top_of_empiret; e++)
		if (is_abbrev(name, empire[e].name) || e == num)
			return e;
	return -1;
	}


/* This function sets up empire territory at startup and when two empires merge */
void read_empire_territory(void) {
	extern struct empire_storage_data *find_stored_resource(int emp, obj_vnum vnum);
	extern const int open_monument_fame[];
	extern const int closed_monument_fame[];

	struct empire_storage_data *store;
	int i, j, e;

	/* Init empires */
	for (i = 0; i <= top_of_empiret; i++) {
		empire[i].territory = 0;
		for (j = 0; j < 6; j++)
			empire[i].vault[j] = 0;

		if ((store = find_stored_resource(i, o_SILVER)))
			empire[i].vault[0] = store->amount;
		if ((store = find_stored_resource(i, o_SILVER_DISC)))
			empire[i].vault[1] = store->amount;
		if ((store = find_stored_resource(i, o_SILVER_BAR)))
			empire[i].vault[2] = store->amount;
		if ((store = find_stored_resource(i, o_GOLD)))
			empire[i].vault[3] = store->amount;
		if ((store = find_stored_resource(i, o_GOLD_DISC)))
			empire[i].vault[4] = store->amount;
		if ((store = find_stored_resource(i, o_GOLD_BAR)))
			empire[i].vault[5] = store->amount;

		empire[i].haven = NOWHERE;
		empire[i].fame = 0;
		}

	for (i = 0; i <= top_of_world; i++)
		if (world[i].owner) {
			if ((e = real_empire(world[i].owner)) != -1 && world[i].owner != -1) {

				/* 1 haven per empire */
				if (IS_HAVEN(i)) {
					if (empire[e].haven != NOWHERE) {
						world[i].owner = 0;
						continue;
						}
					else
						empire[e].haven = world[i].number;
					}

				if (world[i].home_room == NOWHERE)
					empire[e].territory += 1;

				if (SECT(i) == SECT_MONUMENT_OPEN && IS_COMPLETE(i))
					empire[e].fame += open_monument_fame[(int) world[i].type];
				if (SECT(i) == SECT_MONUMENT_CLOSED && IS_COMPLETE(i))
					empire[e].fame += closed_monument_fame[(int) world[i].type];
				}
			else if (world[i].owner != -1)
				world[i].owner = 0;
			}
	}


int calculate_wealth(int e) {
	int val = 0;

	val += empire[e].vault[0];
	val += 3 * empire[e].vault[1];
	val += 6 * empire[e].vault[2];
	val += 2 * empire[e].vault[3];
	val += 5 * empire[e].vault[4];
	val += 10 * empire[e].vault[5];

	return val;
	}


void read_empire_members(void) {
	extern int top_of_p_table;
	extern struct player_index_element *player_table;
	struct char_file_u chdata;
	int j, e;

	for (j = 0; j <= top_of_empiret; j++)
		empire[j].members = 0;

	for (j = 0; j <= top_of_p_table; j++) {
		load_char((player_table + j)->name, &chdata);
		if ((e = real_empire(chdata.player_specials_saved.loyalty)) >= 0)
			if (!IS_SET(chdata.char_specials_saved.act, PLR_DELETED | PLR_SLAIN)) {
				empire[e].members += 1;
				if (chdata.level >= LVL_GOD)
					empire[e].imm_only = 1;
				}
		}

	for (j = 0; j <= top_of_empiret; j++)
		if (empire[j].members == 0) {
			delete_empire(j);
			j--;
			}
	}


/* Find a relationship between two given empires */
struct empire_political_data *find_relation(int a, int b) {
	struct empire_political_data *emp_pol;

	if (a == -1 || b == -1)
		return NULL;

	for (emp_pol = empire[a].diplomacy; emp_pol && emp_pol->id != empire[b].leader; emp_pol = emp_pol->next);

	return emp_pol;
	}


void log_to_empire(int e, const char *str, ...) {
	char output[MAX_STRING_LENGTH];
	Descr i;
	va_list tArgList;

	if (!str)
		return;

	va_start(tArgList, str);
	vsprintf(output, str, tArgList);

	for (i = descriptor_list; i; i = i->next) {
		if (STATE(i) != CON_PLAYING || IS_NPC(i->character))
			continue;
		if (PLR_FLAGGED(i->character, PLR_WRITING | PLR_MAILING))
			continue;
		if (real_empire(GET_LOYALTY(i->character)) != e)
			continue;

		msg_to_char(i->character, "%s[ %s ]&0\r\n", empire[e].banner, output);
		}
	va_end(tArgList);
	}


void save_empire_index(void) {
	FILE *index;
	char fname[30];
	int i;

	sprintf(fname, "%s%s", LIB_EMPIRE, INDEX_FILE);
	if (!(index = fopen(fname, "w"))) {
		log("SYSERR: Unable to create %s", fname);
		return;
		}

	for (i = 0; i <= top_of_empiret; i++)
		fprintf(index, "%ld.%s\n", empire[i].leader, SUF_EMPIRE);
	fprintf(index, "$~\n");
	fclose(index);
	}


void save_empire(int e) {
	struct empire_political_data *emp_pol;
	struct empire_storage_data *store;
	FILE *fl;
	char fname[30];
	int j;

	if (e < 0 || e > top_of_empiret)
		return;

	sprintf(fname, "%s%ld.%s", LIB_EMPIRE, empire[e].leader, SUF_EMPIRE);
	if (!(fl = fopen(fname, "w"))) {
		log("SYSERR: Unable to write %s", fname);
		return;
		}
	fprintf(fl, "#%ld\n", empire[e].leader);
	fprintf(fl, "%s~\n", empire[e].name);
	fprintf(fl, "%s~\n", empire[e].banner);
	fprintf(fl, "%d\n", empire[e].num_ranks);

	for (j = 0; j < empire[e].num_ranks; j++)
		fprintf(fl, "R%d\n%s~\n", j, empire[e].rank[j]);

	for (j = 0; j < NUM_PRIVILEGES; j++)
		fprintf(fl, "P%d\n%d\n", j, empire[e].priv[j]);

	for (emp_pol = empire[e].diplomacy; emp_pol; emp_pol = emp_pol->next)
		fprintf(fl, "D\n%ld %d %d\n", emp_pol->id, emp_pol->type, emp_pol->offer);

	for (store = empire[e].store; store; store = store->next)
		fprintf(fl, "O\n%d %d\n", store->vnum, store->amount);

	fprintf(fl, "S\n$~\n");
	fclose(fl);

	save_empire_index();
	}

void save_all_empires(void) {
	int i;

	if (top_of_empiret < 0)
		return;

	for (i = 0; i <= top_of_empiret; i++)
		save_empire(i);
	save_empire_index();
	}


void parse_empire(FILE *fl, int virtual_nr) {
	static int real_nr = 0;
	int t[6], j;
	char line[1024];
	struct empire_political_data *emp_pol;
	struct empire_storage_data *store;

	sprintf(buf2, "empire #%d", virtual_nr);

	empire[real_nr].leader = virtual_nr;
	empire[real_nr].name = fread_string(fl, buf2);
	empire[real_nr].banner = fread_string(fl, buf2);

	if (!get_line(fl, line)) {
		log("SYSERR: Expecting ranks type of empire #%d but file ended!", virtual_nr);
		exit(1);
		}

	if (sscanf(line, "%d", t) != 1) {
		log("SYSERR: Format error in ranks of empire #%d", virtual_nr);
		exit(1);
		}

	empire[real_nr].num_ranks = t[0];
	empire[real_nr].territory = 0;
	empire[real_nr].members = 0;

	sprintf(buf,"SYSERR: Format error in empire #%d (expecting letter, got %s)", virtual_nr, line);

	for (;;) {
		if (!get_line(fl, line)) {
			log(buf);
			exit(1);
			}
		switch (*line) {
			case 'R':
				j = atoi(line+1);
				if (j < 20)
					empire[real_nr].rank[j] = fread_string(fl, buf2);
				else {
					log("Invalid rank %d in empire #%d", j, virtual_nr);
					exit(1);
					}
				break;
			case 'P':
				j = atoi(line+1);
				if (!get_line(fl, line)) {
					log("SYSERR: Expecting privilege number for empire %d, but file ended!", virtual_nr);
					exit(1);
					}
				sscanf(line, "%d", t);
				empire[real_nr].priv[j] = t[0];
				break;
			case 'D':
				if (!get_line(fl, line)) {
					log("SYSERR: Expecting political data for empire %d, but file ended!", virtual_nr);
					exit(1);
					}
				sscanf(line, "%d %d %d", t, t + 1, t + 2);
				CREATE(emp_pol, struct empire_political_data, 1);
				emp_pol->id = t[0];
				emp_pol->type = t[1];
				emp_pol->offer = t[2];
				emp_pol->next = empire[real_nr].diplomacy;
				empire[real_nr].diplomacy = emp_pol;
				break;
			case 'O':
				if (!get_line(fl, line)) {
					log("SYSERR: Expecting store data for empire %d, but file ended!", virtual_nr);
					exit(1);
					}
				sscanf(line, "%d %d", t, t + 1);

				CREATE(store, struct empire_storage_data, 1);
				store->vnum = t[0];
				store->amount = t[1];

				store->next = empire[real_nr].store;
				empire[real_nr].store = store;
				break;
			case 'S':			/* end of empire */
				top_of_empiret = real_nr++;
				return;
			default:
				log(buf);
				exit(1);
			}
		}
	}


int create_empire(Creature ch) {
	struct empire_data *new_empire;
	int i, j = 0, k;
	bool found = FALSE;

	if (IS_NPC(ch))
		return -1;

	CREATE(new_empire, struct empire_data, top_of_empiret + 2);

	for (i = 0; i <= top_of_empiret; i++)
		if (found)
			new_empire[i+1] = empire[i];
		else if (empire[i].leader > GET_IDNUM(ch)) {
			new_empire[i+1] = empire[i];
			found = TRUE;

			new_empire[i].name = str_dup(PERS(ch, ch, 1));
			new_empire[i].leader = GET_IDNUM(ch);
			new_empire[i].banner = str_dup("&0");
			new_empire[i].num_ranks = 2;
			new_empire[i].rank[0] = str_dup("Follower");
			new_empire[i].rank[1] = str_dup("Leader");
			new_empire[i].territory = 0;
			new_empire[i].members = 1;
			new_empire[i].diplomacy = NULL;
			if (GET_LEVEL(ch) >= LVL_GOD)
				new_empire[i].imm_only = 1;
			for (k = 0; k < 6; k++)
				new_empire[i].vault[k] = 0;
			for (k = 0; k < NUM_PRIVILEGES; k++)
				new_empire[i].priv[k] = 2;
			j = i;
			}
		else
			new_empire[i] = empire[i];

	top_of_empiret += 1;

	if (!found) {
		new_empire[top_of_empiret].name = str_dup(PERS(ch, ch, 1));
		new_empire[top_of_empiret].leader = GET_IDNUM(ch);
		new_empire[top_of_empiret].banner = str_dup("&0");
		new_empire[top_of_empiret].num_ranks = 2;
		new_empire[top_of_empiret].rank[0] = str_dup("Follower");
		new_empire[top_of_empiret].rank[1] = str_dup("Leader");
		new_empire[top_of_empiret].territory = 0;
		new_empire[top_of_empiret].members = 1;
		new_empire[top_of_empiret].diplomacy = NULL;
		if (GET_LEVEL(ch) >= LVL_GOD)
			new_empire[top_of_empiret].imm_only = 1;
		for (k = 0; k < 6; k++)
			new_empire[top_of_empiret].vault[k] = 0;
		for (k = 0; k < NUM_PRIVILEGES; k++)
			new_empire[top_of_empiret].priv[k] = 2;
		j = top_of_empiret;
		}

	if (empire)
		free(empire);
	empire = new_empire;

	GET_LOYALTY(ch) = GET_IDNUM(ch);
	GET_RANK(ch) = 2;

	save_empire(j);
	SAVE_CHAR(ch);

	add_lore(ch, LORE_FOUND_EMPIRE, GET_IDNUM(ch));

	return j;
	}


Creature is_playing(long id) {
	Creature ch;

	for (ch = character_list; ch; ch = ch->next)
		if (!IS_NPC(ch) && GET_IDNUM(ch) == id)
			return ch;
	return NULL;
	}


void delete_empire(int rnum) {
	void save_char_file_u(struct char_file_u st);
	extern int top_of_p_table;
	extern struct player_index_element *player_table;
	struct char_file_u chdata;
	struct empire_data *new_empire;
	struct empire_political_data *emp_pol, *temp;
	Creature victim;
	int i, j;
	long idnum;
	room_rnum room;

	if (rnum < 0 || rnum > top_of_empiret)
		return;

	log_to_empire(rnum, "This empire has been deleted");

	idnum = empire[rnum].leader;

	CREATE(new_empire, struct empire_data, top_of_empiret);

	for (i = 0; i <= top_of_empiret; i++) {
		/* Delete their diplomacy */
		if (i != rnum)
			if ((emp_pol = find_relation(i, rnum))) {
				REMOVE_FROM_LIST(emp_pol, empire[i].diplomacy, next);
				free(emp_pol);
				save_empire(i);
				}

		if (i == rnum) {
			if (empire[i].name)
				free(empire[i].name);
			if (empire[i].banner)
				free(empire[i].banner);
			for (j = 0; j < 20; j++)
				if (empire[i].rank[j])
					free(empire[i].rank[j]);
			if (i != top_of_empiret)
				new_empire[i] = empire[i+1];
			}
		else if (i == top_of_empiret)
			break;
		else if (i > rnum)
			new_empire[i] = empire[i+1];
		else if (i < rnum)
			new_empire[i] = empire[i];
		}
	if (empire)
		free(empire);
	empire = new_empire;
	top_of_empiret--;
	save_empire_index();

	for (j = 0; j <= top_of_p_table && empire[rnum].members; j++) {
		if ((victim = is_playing((player_table + j)->id))) {
			if (GET_LOYALTY(victim) == idnum) {
				msg_to_char(victim, "Your empire has been destroyed.  You are no longer a member.\r\n");
				GET_LOYALTY(victim) = 0;
				GET_RANK(victim) = 0;
				SAVE_CHAR(victim);
				empire[rnum].members -= 1;
				}
			}
		else {
			load_char((player_table + j)->name, &chdata);
			if (chdata.player_specials_saved.loyalty == idnum) {
				chdata.player_specials_saved.loyalty = 0;
				chdata.player_specials_saved.rank = 0;
				save_char_file_u(chdata);
				empire[rnum].members -= 1;
				}
			}
		}

	for (room = 0; room <= top_of_world; room++)
		if (world[room].owner == idnum || (-1 * world[room].owner) == idnum)
			if (world[room].owner != -1)
				world[room].owner = 0;

	sprintf(buf, "rm %s%ld.%s", LIB_EMPIRE, idnum, SUF_EMPIRE);
	system(buf);
	save_empire_index();
	}


/*
 * For acquiring territory.  If ch is loyal to someone, he claims it for them (even if it's
 * himself).  Otherwise, it creates an empire.
 */
long get_id_by_empire(Creature ch) {
	if (GET_LOYALTY(ch) && real_empire(GET_LOYALTY(ch)) != -1)
		return GET_LOYALTY(ch);

	if (GET_IDNUM(ch) != 0)
		create_empire(ch);

	return GET_IDNUM(ch);
	}


bool can_claim(Creature ch) {
	int e;

	if (GET_IDNUM(ch) == 0 && GET_LOYALTY(ch) <= 0)
		return FALSE;
	if ((e = real_empire(GET_LOYALTY(ch))) == -1)
		return TRUE;
	if (empire[e].territory >= LAND_CAN_CLAIM(e))
		return FALSE;
	if (GET_RANK(ch) < empire[e].priv[PRIV_CLAIM])
		return FALSE;
	return TRUE;
	}

/* Allows a leader to change over command */
ACMD(do_chgleader) {
	if (IS_NPC(ch))
		return;

	msg_to_char(ch, "Report this message to an admin %s", GET_NAME(ch));
	
	}

ACMD(do_claim) {
	int e, i;
	long l;

	if (IS_NPC(ch))
		return;

	e = real_empire((l = get_id_by_empire(ch)));

	if (e == NOTHING)
		msg_to_char(ch, "You don't belong to any empire.\r\n");
	else if (world[ch->in_room].owner == l)
		msg_to_char(ch, "Your empire already owns this acre.\r\n");
	else if (SECT(ch->in_room) == SECT_WASTELAND || SECT(ch->in_room) == SECT_TOWER_OF_SOULS)
		msg_to_char(ch, "You can't claim this.\r\n");
	else if (GET_RANK(ch) < empire[e].priv[PRIV_CLAIM])
		msg_to_char(ch, "You don't have permission to claim land for the empire.\r\n");
	else if (world[ch->in_room].owner == -1)
		msg_to_char(ch, "This acre can't be claimed.\r\n");
	else if (world[ch->in_room].owner)
		msg_to_char(ch, "This acre is already claimed.\r\n");
	else if (SECT(ch->in_room) == SECT_OCEAN || SECT(ch->in_room) == SECT_RIVER)
		msg_to_char(ch, "You can't claim the water.\r\n");
	else if (HOME_ROOM(ch->in_room) != ch->in_room)
		msg_to_char(ch, "Just claim the main room for the building.\r\n");
	else if (empire[e].haven != NOWHERE && IS_HAVEN(ch->in_room))
		msg_to_char(ch, "The empire already has a haven, you can't claim another.\r\n");
	else if (!can_claim(ch))
		msg_to_char(ch, "You can't claim any more land.\r\n");
	else {
		msg_to_char(ch, OK);
		world[ch->in_room].owner = l;
		empire[e].territory += 1;
		if (SECT(ch->in_room) == SECT_MULTI || SECT(ch->in_room) == SECT_BUILDING || SECT(ch->in_room) == SECT_MONUMENT_CLOSED) {
			for (i = 0; i <= top_of_world; i++)
				if (world[i].home_room == world[ch->in_room].number)
					world[i].owner = l;

			/* Transfers wealth, etc */
			read_empire_territory();
			}

		save_empire(e);
		}
	}


ACMD(do_abandon) {
	int e, i;

	if ((e = real_empire(world[ch->in_room].owner)) != real_empire(GET_LOYALTY(ch)))
		msg_to_char(ch, "You don't own this acre.\r\n");
	else if (GET_RANK(ch) < empire[e].priv[PRIV_CEDE])
		msg_to_char(ch, "You don't have permission to abandon land.\r\n");
	else {
		if (IS_HAVEN(ch->in_room))
			empire[e].haven = NOWHERE;
		empire[e].territory--;
		if (SECT(ch->in_room) == SECT_MULTI || SECT(ch->in_room) == SECT_BUILDING || SECT(ch->in_room) == SECT_MONUMENT_CLOSED) {
			for (i = 0; i <= top_of_world; i++)
				if (world[i].home_room == world[ch->in_room].number)
					world[i].owner = 0;

			/* Transfers wealth, etc */
			read_empire_territory();
			}

		msg_to_char(ch, "Territory abandoned.\r\n");
		world[ch->in_room].owner = 0;
		}
	}


ACMD(do_cede) {
	extern room_rnum find_target_room(Creature ch, char *rawroomstr);

	int e = 0, f, i, rnum;
	long l = 0, to;
	Creature targ;

	if (IS_NPC(ch))
		return;

	if (GET_LOYALTY(ch) > 0)
		e = real_empire((l = get_id_by_empire(ch)));

	argument = one_argument(argument, arg);

	if (GET_LOYALTY(ch) <= 0 || e < 0 || l < 0)
		msg_to_char(ch, "You own no territory.\r\n");
	else if (!*arg)
		msg_to_char(ch, "Usage: cede <person> (x, y)\r\n");
	else if (!(targ = get_char_vis(ch, arg, FIND_CHAR_WORLD | FIND_NO_DARK)))
		msg_to_char(ch, NOPERSON);
	else if (IS_NPC(targ))
		msg_to_char(ch, "You can't cede land to animals!\r\n");
	else if (ch == targ)
		msg_to_char(ch, "You can't cede land to yourself!\r\n");
	else if (*argument && (!strstr(argument, ",") || !strstr(argument, "(") || !strstr(argument, ")")))
		msg_to_char(ch, "Usage: cede <person> (x, y)\r\n");
	else if ((*argument ? (rnum = HOME_ROOM(find_target_room(ch, argument))) : (rnum = HOME_ROOM(ch->in_room))) == NOWHERE)
		msg_to_char(ch, "Invalid grid.\r\n");
	else if (GET_RANK(ch) < empire[e].priv[PRIV_CEDE])
		msg_to_char(ch, "You don't have permission to cede.\r\n");
	else if (ABSOLUTE(world[rnum].owner) != l)
		msg_to_char(ch, "You don't even own %s acre.\r\n", rnum == ch->in_room ? "this" : "that");
	else if ((f = real_empire((to = get_id_by_empire(targ)))) < 0)
		msg_to_char(ch, "You can't seem to cede land to %s.\r\n", HMHR(targ));
	else if (f == e)
		msg_to_char(ch, "You can't seed land to your own empire!\r\n");
	else if (empire[f].territory >= LAND_CAN_CLAIM(f) + 15)	/* Extra 15 land */
		msg_to_char(ch, "You can't cede land to %s, %s empire can't own any more land.\r\n", HMHR(targ), HSHR(targ));
	else if (empire[f].haven != NOWHERE && IS_HAVEN(rnum))
		msg_to_char(ch, "That empire already has a haven, you can't give them another.\r\n");
	else {
		empire[e].territory -= 1;
		empire[f].territory += 1;
		world[rnum].owner = to;

		if (IS_HAVEN(rnum)) {
			empire[e].haven = NOWHERE;
			empire[f].haven = world[rnum].number;
			}

		save_empire(e);
		save_empire(f);
		if (SECT(rnum) == SECT_MULTI || SECT(rnum) == SECT_BUILDING || SECT(rnum) == SECT_MONUMENT_CLOSED) {
			for (i = 0; i <= top_of_world; i++)
				if (world[i].home_room == world[rnum].number)
					world[i].owner = to;

			/* Transfers wealth, etc */
			read_empire_territory();
			}

		log_to_empire(e, "%s has ceded (%d, %d) to %s", PERS(ch, ch, 1), X_COORD(rnum), Y_COORD(rnum), empire[f].name);
		log_to_empire(f, "%s has ceded (%d, %d) to this empire", PERS(ch, ch, 1), X_COORD(rnum), Y_COORD(rnum));
		msg_to_char(ch, OK);
		}
	}


ACMD(do_pledge) {
	int e;

	if (IS_NPC(ch))
		return;

	one_argument(argument, arg);

	if (GET_LOYALTY(ch) != GET_IDNUM(ch) && GET_LOYALTY(ch) > 0)
		msg_to_char(ch, "You're already a member of an empire.\r\n");
	else if ((e = get_empire_by_name(arg)) < 0)
		msg_to_char(ch, "There is no empire by that name.\r\n");
	else if (GET_DEFECT_TIMER(ch))
		msg_to_char(ch, "You can't pledge again yet.\r\n");
	else if ((IS_GOD(ch) || IS_IMMORTAL(ch)) && !empire[e].imm_only)
		msg_to_char(ch, "You may not join an empire.\r\n");
	else if (empire[e].imm_only && !IS_GOD(ch) && !IS_IMMORTAL(ch))
		msg_to_char(ch, "You can't join that empire.\r\n");
	else {
		GET_PLEDGE(ch) = empire[e].leader;
		log_to_empire(e, "%s has offered %s pledge to this empire", PERS(ch, ch, 1), HSHR(ch));
		msg_to_char(ch, "You offer your pledge to %s.\r\n", empire[e].name);
		SAVE_CHAR(ch);
		}
	}


ACMD(do_enroll) {
	void save_char_file_u(struct char_file_u st);
	extern struct empire_storage_data *find_stored_resource(int emp, obj_vnum vnum);
	extern int top_of_p_table;
	extern struct player_index_element *player_table;
	struct char_file_u chdata;
	struct empire_storage_data *store, *store2;
	int e, i, j;
	long l;
	Creature targ, victim;

	if (IS_NPC(ch))
		return;

	if (GET_LOYALTY(ch) <= 0) {
		msg_to_char(ch, "You don't belong to any empire.\r\n");
		return;
		}

	one_argument(argument, arg);
	e = real_empire((l = get_id_by_empire(ch)));

	if (e == NOTHING)
		msg_to_char(ch, "You don't belong to any empire.\r\n");
	else if (GET_RANK(ch) < empire[e].priv[PRIV_ENROLL])
		msg_to_char(ch, "You don't have the authority to enroll followers.\r\n");
	else if (!*arg)
		msg_to_char(ch, "Whom did you want to enroll?\r\n");
	else if (!(targ = get_char_vis(ch, arg, FIND_CHAR_WORLD | FIND_NO_DARK)))
		msg_to_char(ch, NOPERSON);
	else if (IS_NPC(targ))
		msg_to_char(ch, "You can't enroll animals!\r\n");
	else if (ch == targ)
		msg_to_char(ch, "You're already in the empire!\r\n");
	else if (GET_PLEDGE(targ) != l)
		act("$E has not pledged $mself to your empire.", FALSE, ch, 0, targ, TO_CHAR | TO_SLEEP);
	else if (GET_LOYALTY(targ) != GET_IDNUM(targ) && GET_LOYALTY(targ) > 0)
		act("$E is already loyal to another empire.", FALSE, ch, 0, targ, TO_CHAR | TO_SLEEP);
	else {
		log_to_empire(e, "%s has been enrolled in the empire", PERS(targ, targ, 1));
		msg_to_char(targ, "You have been enrolled in %s.\r\n", empire[e].name);
		msg_to_char(ch, OK);
		GET_LOYALTY(targ) = l;
		GET_RANK(targ) = 1;
		GET_PLEDGE(targ) = 0;
		empire[e].members += 1;

		add_lore(targ, LORE_JOIN_EMPIRE, l);

		/* Transfer land: checks for a duplicate haven */
		if (real_empire(GET_IDNUM(targ)) != -1) {
			for (store = empire[real_empire(GET_IDNUM(targ))].store; store; store = store->next) {
				if (!(store2 = find_stored_resource(e, store->vnum))) {
					CREATE(store2, struct empire_storage_data, 1);
					store2->next = empire[e].store;
					empire[e].store = store2;
					store2->vnum = store->vnum;
					}
				store2->amount += store->amount;
				}

			for (i = 0; i <= top_of_world; i++)
				if (world[i].owner == GET_IDNUM(targ) || world[i].owner == -1 * GET_IDNUM(targ)) {
					if (IS_HAVEN(i) && empire[e].haven != NOWHERE)
						world[i].owner = 0;
					else {
						if (IS_HAVEN(i))
							empire[e].haven = world[i].number;
						world[i].owner = l;
						if (world[i].home_room == NOWHERE)
							empire[e].territory += 1;
						}
					}
			}

		if (real_empire(GET_IDNUM(targ)) != -1) {
			/* The leader already joined.. */
			empire[real_empire(GET_IDNUM(targ))].members -= 1;

			for (j = 0; j <= top_of_p_table && empire[real_empire(GET_IDNUM(targ))].members; j++) {
				if ((victim = is_playing((player_table + j)->id))) {
					if (GET_LOYALTY(victim) == GET_IDNUM(targ)) {
						msg_to_char(victim, "Your empire has merged with %s.\r\n", empire[e].name);
						add_lore(victim, LORE_JOIN_EMPIRE, l);
						GET_LOYALTY(victim) = l;
						GET_RANK(victim) = 1;
						SAVE_CHAR(victim);
						empire[e].members += 1;
						empire[real_empire(GET_IDNUM(targ))].members -= 1;
						}
					}
				else {
					load_char((player_table + j)->name, &chdata);
					if (chdata.player_specials_saved.loyalty == GET_IDNUM(targ)) {
						chdata.player_specials_saved.loyalty = l;
						chdata.player_specials_saved.rank = 1;
						save_char_file_u(chdata);
						empire[e].members += 1;
						empire[real_empire(GET_IDNUM(targ))].members -= 1;
						}
					}
				}

			/* Delete the old empire */
			delete_empire(real_empire(GET_IDNUM(targ)));

			/* This will PROPERLY reset wealth and land */
			read_empire_territory();
			}
		}
	}


ACMD(do_defect) {
	int e;

	if (IS_NPC(ch))
		return;
	else if (GET_LOYALTY(ch) <= 0)
		msg_to_char(ch, "You don't belong to any empire.\r\n");
	else if ((e = real_empire(GET_LOYALTY(ch))) < 0)
		msg_to_char(ch, "You don't seem to belong to any empire.\r\n");
	else if (GET_IDNUM(ch) == empire[e].leader)
		msg_to_char(ch, "The leader can't defect!\r\n");
	else {
		GET_LOYALTY(ch) = 0;
		GET_DEFECT_TIMER(ch) = 96;
		empire[e].members -= 1;
		log_to_empire(e, "%s has defected from the empire", PERS(ch, ch, 1));
		msg_to_char(ch, "You defect from the empire!\r\n");
		add_lore(ch, LORE_DEFECT_EMPIRE, empire[e].leader);
		SAVE_CHAR(ch);

		if (empire[e].members == 0)
			delete_empire(e);
		}
	}


ACMD(do_expell) {
	int e;
	long l;
	Creature targ;

	if (IS_NPC(ch))
		return;

	if (GET_LOYALTY(ch) <= 0) {
		msg_to_char(ch, "You don't belong to any empire.\r\n");
		return;
		}

	one_argument(argument, arg);
	e = real_empire((l = get_id_by_empire(ch)));

	if (e == NOTHING)
		msg_to_char(ch, "You don't belong to any empire.\r\n");
	else if (GET_RANK(ch) != empire[e].num_ranks)
		msg_to_char(ch, "You don't have the authority to expell followers.\r\n");
	else if (!*arg)
		msg_to_char(ch, "Whom do you wish to expell?\r\n");
	else if (!(targ = get_char_vis(ch, arg, FIND_CHAR_WORLD | FIND_NO_DARK)))
		msg_to_char(ch, NOPERSON);
	else if (IS_NPC(targ) || GET_LOYALTY(targ) != l)
		msg_to_char(ch, "That person is not a member of this empire.\r\n");
	else if (targ == ch)
		msg_to_char(ch, "You can't expell yourself.\r\n");
	else if (empire[e].leader == GET_IDNUM(targ))
		msg_to_char(ch, "You can't expell the leader!\r\n");
	else {
		GET_LOYALTY(targ) = 0;
		GET_DEFECT_TIMER(targ) = 96;
		empire[e].members -= 1;
		log_to_empire(e, "%s has been expelled from the empire", PERS(targ, targ, 1));
		msg_to_char(ch, OK);
		msg_to_char(targ, "You have been expelled from the empire.\r\n");
		add_lore(targ, LORE_KICKED_EMPIRE, l);
		SAVE_CHAR(targ);
		}
	}


void show_detailed_empire(Creature ch, int e) {
	struct empire_political_data *emp_pol;
	int i;

	msg_to_char(ch, "%s%s&0\r\n", empire[e].banner, empire[e].name);
	msg_to_char(ch, "Led by %s\r\n", get_name_by_id(empire[e].leader) ? CAP(get_name_by_id(empire[e].leader)) : "(Unknown)");
	msg_to_char(ch, "Ranks:\r\n");

	for (i = 0; i < empire[e].num_ranks; i++)
		msg_to_char(ch, " %2d. %s\r\n", i+1, empire[e].rank[i]);

	msg_to_char(ch, "Privileges:\r\n");
	for (i = 0; i < NUM_PRIVILEGES; i++)
		msg_to_char(ch, "%-12.12s %2d%s", priv[i], empire[e].priv[i], !((i+1)%4) ? "\r\n" : " ");
	if (i % 4)
		msg_to_char(ch, "\r\n");

	msg_to_char(ch, "Territory: %d/%d\r\nMembers: %d\r\nWealth: %d\r\nFame: %d\r\n", empire[e].territory, LAND_CAN_CLAIM(e), empire[e].members, calculate_wealth(e), empire[e].fame);

	*buf1 = '\0';

	for (emp_pol = empire[e].diplomacy; emp_pol; emp_pol = emp_pol->next) {
		if (!IS_IMMORTAL(ch) && GET_LOYALTY(ch) != emp_pol->id && GET_LOYALTY(ch) != empire[e].leader)
			continue;

		/* Sanity checking */
		if (real_empire(emp_pol->id) < 0)
			continue;

		*buf = '\0';
		if (IS_SET(emp_pol->type, DIPL_PEACE))
			sprintf(buf + strlen(buf), "Peaceful relations with %s", empire[real_empire(emp_pol->id)].name);
		if (IS_SET(emp_pol->type, DIPL_WAR))
			sprintf(buf + strlen(buf), "At war with %s", empire[real_empire(emp_pol->id)].name);
		if (IS_SET(emp_pol->type, DIPL_ALLIED))
			sprintf(buf + strlen(buf), "Allied with %s", empire[real_empire(emp_pol->id)].name);
		if (IS_SET(emp_pol->type, DIPL_NONAGGR))
			sprintf(buf + strlen(buf), "In a non-aggression pact with %s", empire[real_empire(emp_pol->id)].name);
		if (IS_SET(emp_pol->type, DIPL_DISTRUST))
			sprintf(buf + strlen(buf), "Distrustful of %s", empire[real_empire(emp_pol->id)].name);
		if (IS_SET(emp_pol->type, DIPL_TRADE))
			sprintf(buf + strlen(buf), " with trade relations");
		if (IS_SET(emp_pol->offer, DIPL_ALLIED)) {
			sprintf(buf + strlen(buf), "%sroposing an alliance", *buf ? ", and are p" : "P");
			if (*buf == 'P')
				sprintf(buf + strlen(buf), " with %s", empire[real_empire(emp_pol->id)].name);
			}
		if (IS_SET(emp_pol->offer, DIPL_PEACE)) {
			sprintf(buf + strlen(buf), "%sroposing peace", *buf ? ", and are p" : "P");
			if (*buf == 'P')
				sprintf(buf + strlen(buf), " to %s", empire[real_empire(emp_pol->id)].name);
			}
		if (IS_SET(emp_pol->offer, DIPL_NONAGGR)) {
			sprintf(buf + strlen(buf), "%sroposing a non-aggression pact", *buf ? ", and are p" : "P");
			if (*buf == 'P')
				sprintf(buf + strlen(buf), " to %s", empire[real_empire(emp_pol->id)].name);
			}
		if (IS_SET(emp_pol->offer, DIPL_TRADE)) {
			sprintf(buf + strlen(buf), "%sroposing a trade pact", *buf ? ", and are p" : "P");
			if (*buf == 'P')
				sprintf(buf + strlen(buf), " with %s", empire[real_empire(emp_pol->id)].name);
			}
		if (*buf) {
			strcat(buf, ".\r\n");
			strcat(buf1, buf);
			}
		}
	if (*buf1)
		msg_to_char(ch, "Current relations:\r\n%s", buf1);
	}


ACMD(do_empires) {
	int e, min = 1;

	skip_spaces(&argument);

	if (top_of_empiret < 0) {
		msg_to_char(ch, "No empires have been formed.\r\n");
		return;
		}

	if (*argument == '-')
		min = atoi((argument + 1));
	else if (*argument) {
		if ((e = get_empire_by_name(argument)) != -1)
			show_detailed_empire(ch, e);
		else
			msg_to_char(ch, "There is no empire by that name or number.\r\n");
		return;
		}

	for (e = 0; e <= top_of_empiret; e++)
		if (empire[e].members >= min)
			msg_to_char(ch, "%3d. %s%-30.30s&0  Members: %2d Territory: %2d\r\n", e+1, empire[e].banner, empire[e].name, empire[e].members, empire[e].territory);
	}


ACMD(do_edelete) {
	Descr d;
	int e;

	skip_spaces(&argument);
	if (!*argument)
		msg_to_char(ch, "What empire do you want to delete?\r\n");
	else if ((e = get_empire_by_name(argument)) < 0)
		msg_to_char(ch, "Invalid empire.\r\n");
	else {
		for (d = descriptor_list; d; d = d->next)
			if (STATE(d) == CON_EMPIRE_EDIT && real_empire(GET_LOYALTY(d->character)) == e) {
				msg_to_char(ch, "You can't delete an empire which is being edited.\r\n");
				return;
				}
		delete_empire(e);
		msg_to_char(ch, "Empire deleted.\r\n");
		}
	}


bool check_pact(int a, int b) {
	struct empire_political_data *emp_pol;

	if (a == -1 || b == -1)
		return FALSE;

	if ((emp_pol = find_relation(a, b)))
		if (IS_SET(emp_pol->type, DIPL_NONAGGR))
			return TRUE;
	return FALSE;
	}


/*
 * Returns true if it hit someone
 */
bool shoot_at_room(room_rnum from_room, room_rnum to_room) {
	Creature ch, next_ch, m;
	int to_hit, to_dodge = 0, dam, type;

	/* Rooms we can't hit */
	if (SECT(to_room) == SECT_MOUNTAIN || SECT(to_room) == SECT_MONUMENT_CLOSED || SECT(to_room) == SECT_BUILDING || SECT(to_room) == SECT_MULTI || (SECT(to_room) == SECT_FOREST_4 && BUILDING_TYPE(from_room) != BUILDING_WATCH_TOWER))
		if (IS_COMPLETE(to_room) || ALWAYS_CLOSED(to_room))
			return FALSE;

	if (ROOM_AFF_FLAGGED(from_room, ROOM_AFF_DARK))
		return FALSE;
	if (ROOM_AFF_FLAGGED(to_room, ROOM_AFF_DARK))
		return FALSE;

	for (ch = world[to_room].people; ch; ch = next_ch) {
		next_ch = ch->next_in_room;

		if (IS_IMMORTAL(ch) || IS_GOD(ch))
			continue;

		/* Special handling for mobs */
		if (IS_NPC(ch)) {
			if (!GET_PULLING(ch) || !GET_OBJ_VAL(GET_PULLING(ch), 2))
				continue;
			}

		if (!((m = ch->master) && AFF_FLAGGED(ch, AFF_PARTY) && AFF_FLAGGED(m, AFF_PARTY)))
			m = ch;
		if ((!IS_NPC(ch) && CAN_USE_ROOM(ch, from_room, 1)) || (!IS_NPC(m) && CAN_USE_ROOM(m, from_room, 1)))
			continue;
		if (!IS_NPC(ch) && check_pact(real_empire(GET_LOYALTY(ch)), real_empire(world[from_room].owner)))
			continue;
		if (!IS_NPC(m) && check_pact(real_empire(GET_LOYALTY(m)), real_empire(world[from_room].owner)))
			continue;
		if ((AFF_FLAGGED(ch, AFF_HIDE) || DSC_FLAGGED(ch, DSC_UNSEEN_PRESENCE)) && !GET_LEADING(ch))
			continue;
		if (AFF_FLAGGED(ch, AFF_INVISIBLE) && !GET_LEADING(ch))
			continue;
		if (DSC_FLAGGED(ch, DSC_MAJESTY))
			continue;
		if (AFF_FLAGGED(ch, AFF_NO_TARGET_IN_ROOM | AFF_NO_ATTACK))
			continue;
		if (MORPH_FLAGGED(ch, MORPH_FLAG_NPC))
			continue;

		if (!number(0, 1) && next_ch)
			continue;

		to_hit = ww_dice(6, 6);

		if (AWAKE(ch))
			to_dodge = ww_dice(GET_DEXTERITY(ch) + GET_DODGE(ch), 6);

		/* Now we're sure we can hit this person */
		if (to_hit > to_dodge)
			dam = ww_dice(4 + (BUILDING_TYPE(from_room) == BUILDING_GUARD_TOWER3) + to_hit - to_dodge, 6);
		else
			dam = 0;

		type = ATTACK_GUARD_TOWER;

		if (real_empire(world[from_room].owner) != -1 && damage(ch, ch, dam, type, DAM_LETHAL) != 0) {
			log_to_empire(real_empire(world[from_room].owner), "%s at (%d, %d) is shooting at an infiltrator at (%d, %d)", SECT(from_room) == SECT_MULTI ? "Fort" : "Guard tower", X_COORD(from_room), Y_COORD(from_room), X_COORD(to_room), Y_COORD(to_room));
			if (GET_PULLING(ch) && GET_OBJ_VAL(GET_PULLING(ch), 2))
				log_to_empire(real_empire(world[from_room].owner), "A catapult has been spotted!");
			}

		return TRUE;
		}
	return FALSE;
	}


void process_tower(room_rnum room, bool fort) {
	extern int side_dir[][2];
	int dir = 0, count = 0;
	room_rnum to_room;

	if (real_empire(world[room].owner) < 0)
		return;

	for (count = 0; count < 3; count++) {
		/* Pick a direction */
		dir = number(0, NUM_2D_DIRS - 1);

		/* Radius 1 */
			to_room = real_shift(room, shift_dir[dir][0], shift_dir[dir][1]);

			/* shoot */
			if (shoot_at_room(room, to_room))
				break;

			/* check for blocking sects */
			if (SECT(to_room) == SECT_MOUNTAIN || (SECT(to_room) == SECT_BARRIER && world[to_room].type))
				continue;


		/* Radius 2 */
			to_room = real_shift(room, shift_dir[dir][0]*2, shift_dir[dir][1]*2);

			/* shoot */
			if (shoot_at_room(room, to_room))
				break;

			/* check for blocking sects */
			if (SECT(to_room) == SECT_MOUNTAIN || (SECT(to_room) == SECT_BARRIER && world[to_room].type))
				continue;

		/* Radius 1, to one side */
			to_room = real_shift(room, shift_dir[side_dir[dir][0]][0], shift_dir[side_dir[dir][0]][1]);
			if (shoot_at_room(room, to_room))
				break;

		/* Radius 1, to the other side */
			to_room = real_shift(room, shift_dir[side_dir[dir][1]][0], shift_dir[side_dir[dir][1]][1]);
			if (shoot_at_room(room, to_room))
				break;

		/* Radius 3 */
			/* Make sure we can shoot this far */
			if (!fort && BUILDING_TYPE(room) != BUILDING_GUARD_TOWER2 && BUILDING_TYPE(room) != BUILDING_GUARD_TOWER3 && BUILDING_TYPE(room) != BUILDING_WATCH_TOWER)
				continue;

			to_room = real_shift(room, shift_dir[dir][0]*2, shift_dir[dir][1]*2);

			/* shoot */
			if (shoot_at_room(room, to_room))
				break;


		/* Radius 2, to one side */
			to_room = real_shift(room, shift_dir[dir][0] + shift_dir[side_dir[dir][0]][0], shift_dir[dir][1] + shift_dir[side_dir[dir][0]][1]);
			if (shoot_at_room(room, to_room))
				break;

		/* Radius 2, to the other side */
			to_room = real_shift(room, shift_dir[dir][0] + shift_dir[side_dir[dir][1]][0], shift_dir[dir][1] + shift_dir[side_dir[dir][1]][1]);
			if (shoot_at_room(room, to_room))
				break;
		}
	}


void update_guard_towers(void) {
	room_rnum i;

	for (i = 0; i <= top_of_world; i++)
		if (SECT(i) == SECT_BUILDING && IS_COMPLETE(i) && (world[i].type == BUILDING_GUARD_TOWER || world[i].type == BUILDING_GUARD_TOWER2 || world[i].type == BUILDING_GUARD_TOWER3 || world[i].type == BUILDING_WATCH_TOWER))
			process_tower(i, FALSE);
		else if (SECT(i) == SECT_MULTI && IS_COMPLETE(i) && (world[i].type == MULTI_UL_DOOR || world[i].type == MULTI_LR_DOOR))
			process_tower(i, TRUE);
	}


ACMD(do_esay) {
	Descr d;
	Creature tch;
	int level = 0, e, i;
	bool emote = FALSE;
	char lstring[MAX_INPUT_LENGTH];
	long l;

	if (IS_NPC(ch))
		return;

	if ((e = real_empire(l = GET_LOYALTY(ch))) == -1) {
		msg_to_char(ch, "You don't belong to any empire.\r\n");
		return;
		}

	if (PLR_FLAGGED(ch, PLR_MUTED)) {
		msg_to_char(ch, "You can't use the empire channel while muted.\r\n");
		return;
		}

	skip_spaces(&argument);

	if (!*argument) {
		msg_to_char(ch, "What would you like to tell your empire?\r\n");
		return;
		}
	if (*argument == '*') {
		argument++;
		emote = TRUE;
		}
	if (*argument == '#') {
		half_chop(argument, arg, buf);
		strcpy(argument, buf);
		for (i = 0; i < strlen(arg); i++)
			if (arg[i] == '_')
				arg[i] = ' ';
		for (i = 0; i < empire[e].num_ranks; i++)
			if (is_abbrev(arg+1, empire[e].rank[i]))
				break;
		if (i < empire[e].num_ranks)
			level = i;
		}
	if (*argument == '*') {
		argument++;
		emote = TRUE;
		}

	level++;

	if (level > 1)
		sprintf(lstring, " <%s&0>", empire[e].rank[level-1]);
	else
		*lstring = '\0';

	/* Since we cut up the string, we have to check again */
	if (!*argument) {
		msg_to_char(ch, "What would you like to tell your empire?\r\n");
		return;
		}

	if (emote)
		sprintf(buf, "[%sEMPIRE&0%s] $n %s", empire[e].banner, lstring, argument);
	else
		/* Changed display to always show your name instead of your form. */
		sprintf(buf, "[%sEMPIRE&0 $o %s] %s", empire[e].banner, lstring, argument);

	if (PRF_FLAGGED(ch, PRF_NOREPEAT))
		msg_to_char(ch, OK);
	else
		act(buf, FALSE, ch, 0, 0, TO_CHAR | TO_SLEEP);

	for (d = descriptor_list; d; d = d->next)
		if (STATE(d) != CON_PLAYING || !(tch = d->character) || IS_NPC(tch)	|| GET_LOYALTY(tch) != l || GET_RANK(tch) < level || tch == ch)
			continue;
		else if (PRF_FLAGGED(tch, PRF_RP))
			continue;
		else
			act(buf, FALSE, ch, 0, tch, TO_VICT | TO_SLEEP);
	}


ACMD(do_promote) {
	int e, i;
	long l;
	Creature victim;

	if (IS_NPC(ch))
		return;

	argument = one_argument(argument, arg);
	skip_spaces(&argument);

	e = real_empire(l = GET_LOYALTY(ch));

	if (*argument && e != -1) {
		for (i = 0; i < strlen(argument); i++)
			if (argument[i] == '_')
				argument[i] = ' ';
		for (i = 0; i < empire[e].num_ranks; i++)
			if (is_abbrev(argument, empire[e].rank[i]))
				break;
		if (i == empire[e].num_ranks) {
			msg_to_char(ch, "Invalid rank.\r\n");
			return;
			}
		i++;
		}
	else
		i = -1;

	if (e == NOTHING)
		msg_to_char(ch, "You don't belong to any empire.\r\n");
	else if (GET_RANK(ch) < empire[e].priv[PRIV_PROMOTE])
		msg_to_char(ch, "You can't promote anybody!\r\n");
	else if (!*arg)
		msg_to_char(ch, "Promote whom?\r\n");
	else if (!(victim = get_char_vis(ch, arg, FIND_CHAR_WORLD | FIND_NO_DARK)))
		msg_to_char(ch, NOPERSON);
	else if (victim == ch)
		msg_to_char(ch, "You can't promote yourself!\r\n");
	else if (IS_NPC(victim) || GET_LOYALTY(victim) != l)
		msg_to_char(ch, "That person is not in your empire.\r\n");
	else if ((i != -1 ? i : (i = GET_RANK(victim) + 1)) < GET_RANK(victim))
		msg_to_char(ch, "Use demote for that.\r\n");
	else if (i == GET_RANK(victim))
		act("$E is already that rank.", FALSE, ch, 0, victim, TO_CHAR);
	else if (i > empire[e].num_ranks)
		msg_to_char(ch, "You can't promote someone over the top level.\r\n");
	else if (i >= GET_RANK(ch) && GET_RANK(ch) < empire[e].num_ranks)
		msg_to_char(ch, "You can't promote someone to that level.\r\n");
	else {
		GET_RANK(victim) = i;
		SAVE_CHAR(victim);
		log_to_empire(e, "%s has been promoted to %s%s!", PERS(victim, victim, 1), empire[e].rank[i-1], empire[e].banner);
		msg_to_char(ch, OK);
		}
	}


ACMD(do_demote) {
	int e, i;
	long l;
	Creature victim;

	if (IS_NPC(ch))
		return;

	argument = one_argument(argument, arg);
	skip_spaces(&argument);

	e = real_empire(l = GET_LOYALTY(ch));

	if (*argument && e != -1) {
		for (i = 0; i < strlen(argument); i++)
			if (argument[i] == '_')
				argument[i] = ' ';
		for (i = 0; i < empire[e].num_ranks && !is_abbrev(argument, empire[e].rank[i]); i++);
		if (i == empire[e].num_ranks) {
			msg_to_char(ch, "Invalid rank.\r\n");
			return;
			}
		i++;
		}
	else
		i = -1;

	if (e == NOTHING)
		msg_to_char(ch, "You don't belong to any empire.\r\n");
	else if (GET_RANK(ch) < empire[e].priv[PRIV_PROMOTE])
		msg_to_char(ch, "You can't demote anybody!\r\n");
	else if (!*arg)
		msg_to_char(ch, "Demote whom?\r\n");
	else if (!(victim = get_char_vis(ch, arg, FIND_CHAR_WORLD | FIND_NO_DARK)))
		msg_to_char(ch, NOPERSON);
	else if (ch == victim)
		msg_to_char(ch, "You can't demote yourself!\r\n");
	else if (IS_NPC(victim) || GET_LOYALTY(victim) != l)
		msg_to_char(ch, "That person is not in your empire.\r\n");
	else if ((i != -1 ? i : (i = GET_RANK(victim) - 1)) > GET_RANK(victim))
		msg_to_char(ch, "Use promote for that.\r\n");
	else if (i == GET_RANK(victim))
		act("$E is already that rank.", FALSE, ch, 0, victim, TO_CHAR);
	else if (i < 1)
		act("You can't demote $M THAT low!", FALSE, ch, 0, victim, TO_CHAR);
	else {
		GET_RANK(victim) = i;
		SAVE_CHAR(victim);
		log_to_empire(e, "%s has been demoted to %s%s", PERS(victim, victim, 1), empire[e].rank[i-1], empire[e].banner);
		msg_to_char(ch, OK);
		}
	}


ACMD(do_roster) {
	extern int top_of_p_table;
	extern struct player_index_element *player_table;

	struct char_file_u chdata;
	int j, e;
	long l;
	Creature tmp;

	skip_spaces(&argument);

	if (!*argument || !IS_IMMORTAL(ch)) {
		if ((e = real_empire(GET_LOYALTY(ch))) == -1) {
			msg_to_char(ch, "You don't belong to any empire!\r\n");
			return;
			}
		}
	else if ((e = get_empire_by_name(argument)) == -1) {
		send_to_char("Unknown empire.\r\n", ch);
		return;
		}

	l = empire[e].leader;

	*buf = '\0';

	for (j = 0; j <= top_of_p_table; j++) {
		load_char((player_table + j)->name, &chdata);
		if (!IS_SET(chdata.char_specials_saved.act, PLR_DELETED | PLR_SLAIN))
			if (chdata.player_specials_saved.loyalty == l) {
				sprintf(buf + strlen(buf), "<%s> %s", empire[e].rank[chdata.player_specials_saved.rank - 1], chdata.name);
				if ((tmp = get_player_vis(ch, chdata.name, FIND_CHAR_WORLD | FIND_NO_DARK))) {
					strcat(buf, "  - &6online&0");
					if (IS_AFK(tmp))
						strcat(buf, " - &1afk&0");
					}
				strcat(buf, "\r\n");
				}
		}

	sprintf(buf1, "Roster of %s%s&0:\r\n%s", empire[e].banner, empire[e].name, buf);
	page_string(ch->desc, buf1, 1);
	}


#define DIPLOMACY_FORMAT		\
	"Usage: diplomacy <action> <empire>\r\n"	\
	"Actions are:\r\n"	\
	" peace    - end a war or begin a relationship with a neutral empire\r\n"	\
	" war      - declare war on an empire\r\n"	\
	" ally     - propose or accept a full alliance\r\n" \
	" pact     - propose or accept a pact of non-aggression\r\n" \
	" trade    - propose or accept a trade agreement\r\n" \
	" distrust - declare that your empire distrusts, but is not at war with, another\r\n"

ACMD(do_diplomacy) {
	struct empire_political_data *pol_a, *pol_b;
	int e, f, i;

	char *dipl_commands[] = {
		"peace",	"war",		"ally",		"pact",
		"trade",	"distrust",	"\n"
		};

	argument = one_argument(argument, arg);
	skip_spaces(&argument);

	if (!*arg || !*argument) {
		msg_to_char(ch, DIPLOMACY_FORMAT);
		return;
		}

	if ((e = real_empire(GET_LOYALTY(ch))) == -1) {
		msg_to_char(ch, "You don't belong to any empire!\r\n");
		return;
		}
	if (GET_RANK(ch) < empire[e].priv[PRIV_DIPLOMACY]) {
		msg_to_char(ch, "You don't have the authority to make diplomatic relations.\r\n");
		return;
		}
	if ((f = get_empire_by_name(argument)) == -1) {
		msg_to_char(ch, "Unknown empire.\r\n");
		return;
		}

	/* Find the command */
	for (i = 0; str_cmp(dipl_commands[i], "\n") && !is_abbrev(arg, dipl_commands[i]); i++);

	if (!str_cmp(dipl_commands[i], "\n")) {
		msg_to_char(ch, DIPLOMACY_FORMAT);
		return;
		}

	if (!(pol_a = find_relation(e, f))) {
		CREATE(pol_a, struct empire_political_data, 1);
		pol_a->id = empire[f].leader;
		pol_a->next = empire[e].diplomacy;
		empire[e].diplomacy = pol_a;
		}
	if (!(pol_b = find_relation(f, e))) {
		CREATE(pol_b, struct empire_political_data, 1);
		pol_b->id = empire[e].leader;
		pol_b->next = empire[f].diplomacy;
		empire[f].diplomacy =pol_b;
		}

	switch (i) {
		case 0:		/* Peace */
			if (IS_SET(pol_b->offer, DIPL_PEACE)) {
				REMOVE_BIT(pol_b->offer, DIPL_PEACE);
				pol_a->type = pol_b->type = DIPL_PEACE;
				log_to_empire(e, "The empire is now at peace with %s", empire[f].name);
				log_to_empire(f, "The empire is now at peace with %s", empire[e].name);
				msg_to_char(ch, OK);
				}
			else if (IS_SET(pol_a->type, DIPL_WAR | DIPL_DISTRUST) || !pol_a->type) {
				SET_BIT(pol_a->offer, DIPL_PEACE);
				log_to_empire(e, "The empire has offered peace to %s", empire[f].name);
				log_to_empire(f, "%s has offered peace to the empire", empire[e].name);
				msg_to_char(ch, OK);
				}
			else if (IS_SET(pol_a->type, DIPL_ALLIED))
				msg_to_char(ch, "But you're already allied!\r\n");
			else if (IS_SET(pol_a->type, DIPL_NONAGGR))
				msg_to_char(ch, "But you've already got a non-aggression pact!\r\n");
			else
				msg_to_char(ch, "But you already have better relations!\r\n");
			break;
		case 1:		/* War */
			if (IS_SET(pol_a->type, DIPL_WAR))
				msg_to_char(ch, "You're already at war!\r\n");
			else {
				pol_a->offer = pol_b->offer = 0;
				pol_a->type = pol_b->type = DIPL_WAR;
				log_to_empire(e, "War has been declared upon %s!", empire[f].name);
				log_to_empire(f, "%s has declared war!", empire[e].name);
				msg_to_char(ch, OK);
				}
			break;
		case 2:		/* Ally */
			if (IS_SET(pol_b->offer, DIPL_ALLIED)) {
				REMOVE_BIT(pol_b->offer, DIPL_ALLIED | DIPL_NONAGGR | DIPL_PEACE);
				REMOVE_BIT(pol_a->offer, DIPL_ALLIED | DIPL_NONAGGR | DIPL_PEACE);
				REMOVE_BIT(pol_b->type, DIPL_NONAGGR | DIPL_PEACE);
				REMOVE_BIT(pol_a->type, DIPL_NONAGGR | DIPL_PEACE);
				SET_BIT(pol_a->type, DIPL_ALLIED);
				SET_BIT(pol_b->type, DIPL_ALLIED);
				log_to_empire(e, "An alliance has been established with %s!", empire[f].name);
				log_to_empire(f, "%s has accepted the offer of alliance!", empire[e].name);
				msg_to_char(ch, OK);
				}
			else if (IS_SET(pol_a->type, DIPL_WAR | DIPL_DISTRUST))
				msg_to_char(ch, "You'll have to establish peace first.\r\n");
			else if (IS_SET(pol_a->type, DIPL_ALLIED))
				msg_to_char(ch, "You're already allied!\r\n");
			else {
				SET_BIT(pol_a->offer, DIPL_ALLIED);
				log_to_empire(e, "The empire has suggested an alliance to %s", empire[f].name);
				log_to_empire(f, "%s has suggested an alliance", empire[e].name);
				msg_to_char(ch, OK);
				}
			break;
		case 3:		/* Pact */
			if (IS_SET(pol_b->offer, DIPL_NONAGGR)) {
				REMOVE_BIT(pol_b->offer, DIPL_NONAGGR | DIPL_PEACE);
				REMOVE_BIT(pol_a->offer, DIPL_NONAGGR | DIPL_PEACE);
				REMOVE_BIT(pol_b->type, DIPL_PEACE);
				REMOVE_BIT(pol_a->type, DIPL_PEACE);
				SET_BIT(pol_a->type, DIPL_NONAGGR);
				SET_BIT(pol_b->type, DIPL_NONAGGR);
				log_to_empire(e, "A non-aggression pact has been established with %s!", empire[f].name);
				log_to_empire(f, "%s has accepted the offer of a non-aggression pact!", empire[e].name);
				msg_to_char(ch, OK);
				}
			else if (IS_SET(pol_a->type, DIPL_WAR | DIPL_DISTRUST))
				msg_to_char(ch, "You'll have to establish peace first.\r\n");
			else if (IS_SET(pol_a->type, DIPL_ALLIED))
				msg_to_char(ch, "You're already allied!\r\n");
			else if (IS_SET(pol_a->type, DIPL_NONAGGR))
				msg_to_char(ch, "You've already got a pact!\r\n");
			else {
				SET_BIT(pol_a->offer, DIPL_NONAGGR);
				log_to_empire(e, "The empire has offered a non-aggression pact to %s", empire[f].name);
				log_to_empire(f, "%s has offered a non-aggression pact", empire[e].name);
				msg_to_char(ch, OK);
				}
			break;
		case 4:		/* Trade */
			if (IS_SET(pol_b->offer, DIPL_TRADE)) {
				REMOVE_BIT(pol_b->offer, DIPL_TRADE);
				SET_BIT(pol_a->type, DIPL_TRADE);
				SET_BIT(pol_b->type, DIPL_TRADE);
				log_to_empire(e, "A trade agreement been established with %s!", empire[f].name);
				log_to_empire(f, "%s has accepted the offer of a trade agreement!", empire[e].name);
				msg_to_char(ch, OK);
				}
			else if (IS_SET(pol_a->type, DIPL_WAR | DIPL_DISTRUST) || !pol_a->type)
				msg_to_char(ch, "You'll have to establish peace first.\r\n");
			else if (IS_SET(pol_a->type, DIPL_TRADE))
				msg_to_char(ch, "You're already trading with them!\r\n");
			else {
				SET_BIT(pol_a->offer, DIPL_TRADE);
				log_to_empire(e, "The empire has offered a trade agreement to %s", empire[f].name);
				log_to_empire(f, "%s has offered a trade agreement", empire[e].name);
				msg_to_char(ch, OK);
				}
			break;
		case 5:		/* Distrust */
			if (IS_SET(pol_a->type, DIPL_WAR))
				msg_to_char(ch, "You're already at war!\r\n");
			else if (IS_SET(pol_a->type, DIPL_DISTRUST))
				msg_to_char(ch, "You already distrust them!\r\n");
			else {
				pol_a->offer = pol_b->offer = 0;
				pol_a->type = pol_b->type = DIPL_DISTRUST;
				log_to_empire(e, "The empire now officially distrusts %s", empire[f].name);
				log_to_empire(f, "%s has declared that they official distrust the empire", empire[e].name);
				msg_to_char(ch, OK);
				}
			break;
		}

	save_empire(e);
	save_empire(f);
	}


/* Determines whether or not a person can use a room */
bool CAN_USE_ROOM(Creature ch, room_rnum room, bool mode) {
	struct empire_political_data *emp_pol;

	if (IS_NPC(ch))
		return FALSE;
	if (world[HOME_ROOM(room)].owner == 0)
		return TRUE;
	if (world[HOME_ROOM(room)].owner < 0 && !mode)
		return TRUE;
	if (real_empire(world[HOME_ROOM(room)].owner) == real_empire(GET_LOYALTY(ch)))
		return TRUE;
	if (world[HOME_ROOM(room)].owner == -1 * GET_LOYALTY(ch))
		return TRUE;

	if ((emp_pol = find_relation(real_empire(GET_LOYALTY(ch)), real_empire(world[HOME_ROOM(room)].owner))))
		if (IS_SET(emp_pol->type, DIPL_ALLIED))
			return TRUE;
	return FALSE;
	}


ACMD(do_publicize) {
	if (world[ch->in_room].home_room != NOWHERE)
		msg_to_char(ch, "You can't do that here.\r\n");
	else if (real_empire(GET_LOYALTY(ch)) < 0)
		msg_to_char(ch, "You're not in an empire.\r\n");
	else if (GET_RANK(ch) < empire[real_empire(GET_LOYALTY(ch))].priv[PRIV_CLAIM])
		msg_to_char(ch, "You don't have permission to do that.\r\n");
	else if (world[ch->in_room].owner == -1 * GET_LOYALTY(ch)) {
		world[ch->in_room].owner = GET_LOYALTY(ch);
		msg_to_char(ch, "This area is no longer public.\r\n");
		}
	else if (world[ch->in_room].owner == GET_LOYALTY(ch)) {
		world[ch->in_room].owner = -1 * GET_LOYALTY(ch);
		msg_to_char(ch, "This room is now public.\r\n");
		}
	else
		msg_to_char(ch, "Your empire doesn't own this area.\r\n");
	}


ACMD(do_unpublicize) {
	int e;
	room_rnum r;

	if ((e = real_empire(GET_LOYALTY(ch))) < 0)
		msg_to_char(ch, "You're not in an empire.\r\n");
	else if (GET_RANK(ch) < empire[e].num_ranks)
		msg_to_char(ch, "You're of insufficient rank to remove all public status for the empire.\r\n");
	else {
		for (r = 0; r <= top_of_world; r++)
			if (world[r].owner < -1 && real_empire(world[r].owner) == e)
				world[r].owner *= -1;
		msg_to_char(ch, "All public status for this empire's buildings has been renounced.\r\n");
		}
	}


/* ********** EMPIRE EDITOR *********************************************** */

const char *color_names[] = {
	"normal",
	"red",
	"green",
	"yellow",
	"blue",
	"magenta",
	"cyan",
	"\n"
	};


/* code works fine as long as you don't duplicate entries in these strings */
const char color_numbers[] = {	'0', '1', '2', '3', '4', '5', '6', '\n' };
const char color_flag_letters[] = {	'b', 'u', '\n' };

const char *color_mod_bits[] = {
	"bold",
	"underlined",
	"\n"
	};

const char *color_mod_flag_bits[] = {
	"&b",
	"&u",
	"\n"
	};


#define COLOR_MOD_BOLD			(1 << 0)
#define COLOR_MOD_UNDERLINE		(1 << 1)


/* Utilities */

/* extracts color codes and flags from a string */
char *extract_color_codes(char *input, sh_int *color, sh_int *flags, bool ignore_trailing_white_code) {
	static char output[MAX_STRING_LENGTH];
	int f, i, j;

	*color = 0;
	*flags = 0;
	*output = '\0';

	for (i = 0, j = 0; i <= strlen(input); i++) {
		/* handle a color code unless it's a trailing &0 */
		if (input[i] == '&' && (i < strlen(input) - 2 || !ignore_trailing_white_code)) {
			i++;

			for (f = 0; color_numbers[f] != '\n'; f++)
				if (input[i] == color_numbers[f])
					*color = f;
			for (f = 0; color_flag_letters[f] != '\n'; f++)
				if (input[i] == color_flag_letters[f])
					*flags |= (1 << f);
			}
		/* skipped color code */
		else if (input[i] == '&')
			i++;
		/* copy the letter */
		else {
			output[j] = input[i];
			j++;
			}
		}

	return output;
	}


/* creates a color code string from color flags */
char *make_color_string(sh_int color, sh_int flags) {
	static char output[MAX_STRING_LENGTH];
	char buffer[MAX_INPUT_LENGTH];

	sprintbit(flags, color_mod_flag_bits, buffer, FALSE);
	sprintf(output, "&%d%s", color, buffer);

	return output;
	}

/* creates color codes as a phrase */
char *make_color_phrase(sh_int color, sh_int flags) {
	static char output[MAX_STRING_LENGTH];
	char buffer[MAX_INPUT_LENGTH];

	sprintbit(flags, color_mod_bits, buffer, FALSE);
	sprintf(output, "%s%s%s", buffer, *buffer ? " " : "", color_names[color]);

	return output;
	}


/* setup the editor */
struct empire_edit_data *create_empire_editor(int emp) {
	struct empire_edit_data *editor;
	int i;

	CREATE(editor, struct empire_edit_data, 1);

	editor->mode = EEDIT_MENU;
	editor->submode = 0;
	editor->changed = FALSE;

	editor->leader = empire[emp].leader;
	editor->name = str_dup(empire[emp].name);

	extract_color_codes(empire[emp].banner, &editor->banner_color, &editor->banner_flags, FALSE);

	editor->num_ranks = empire[emp].num_ranks;
	for (i = 0; i < MAX_RANKS; i++)
		if (i >= editor->num_ranks) {
			editor->rank_colors[i] = 0;
			editor->rank_color_flags[i] = 0;
			}
		else
			editor->rank[i] = str_dup(extract_color_codes(empire[emp].rank[i], &editor->rank_colors[i], &editor->rank_color_flags[i], TRUE));

	for (i = 0; i < NUM_PRIVILEGES; i++)
		editor->priv[i] = empire[emp].priv[i];

	return editor;
	}


/* copy the editor back to the empire */
void save_empire_editor(struct empire_edit_data *editor, int emp) {
	int i;

	/* reset changed in case this is a mid-way save */
	editor->changed = FALSE;

	empire[emp].leader = editor->leader;

	if (empire[emp].name)
		free(empire[emp].name);
	empire[emp].name = str_dup(editor->name);

	if (empire[emp].banner)
		free(empire[emp].banner);
	empire[emp].banner = str_dup(make_color_string(editor->banner_color, editor->banner_flags));

	empire[emp].num_ranks = editor->num_ranks;
	for (i = 0; i < editor->num_ranks; i++) {
		if (editor->rank_colors[i] || editor->rank_color_flags[i])
			sprintf(buf, "%s%s&0", make_color_string(editor->rank_colors[i], editor->rank_color_flags[i]), editor->rank[i]);
		else
			strcpy(buf, editor->rank[i]);
		if (empire[emp].rank[i])
			free (empire[emp].rank[i]);
		empire[emp].rank[i] = str_dup(buf);
		}

	for (i = 0; i < NUM_PRIVILEGES; i++)
		empire[emp].priv[i] = editor->priv[i];

	save_empire(emp);
	}


/* get out of the editor */
void exit_empire_editor(Descr d) {
	extern char *MENU;
	int i;

	if (d->empire->name)
		free (d->empire->name);
	for (i = 0; i < MAX_RANKS; i++)
		if (d->empire->rank[i])
			free (d->empire->rank[i]);
	free(d->empire);

	STATE(d) = CON_MENU;
	SEND_TO_Q(MENU, d);
	}


/* Displays */

/* main menu */
void send_empire_edit_menu(Descr d) {
	int i;

	/* header */
	sprintf(buf, "\r\n                   --- Empire Editor Alpha ---\r\n");

	/* name and banner */
	sprintf(buf + strlen(buf), " 1. Name: %-30.30s  2. Banner: %s%s&0\r\n", d->empire->name, make_color_string(d->empire->banner_color, d->empire->banner_flags), make_color_phrase(d->empire->banner_color, d->empire->banner_flags));

	/* ranks */
	sprintf(buf + strlen(buf), " 3. Ranks:\r\n");
	for (i = 0; i < d->empire->num_ranks; i++)
		sprintf(buf + strlen(buf), "  r%-2d. %s%-25.25s&0%s", i + 1, make_color_string(d->empire->rank_colors[i], d->empire->rank_color_flags[i]), d->empire->rank[i], (!((i + 1) % 2)) ? "\r\n" : "  ");
	if (i % 2)
		strcat(buf, "\r\n");

	strcat(buf, "\r\n");

	/* privileges */
	sprintf(buf + strlen(buf), " Privileges:\r\n");
	for (i = 0; i < NUM_PRIVILEGES; i++)
		sprintf(buf + strlen(buf), "  p%-2d. %-12.12s %2d%s", i + 1, priv[i], d->empire->priv[i], !((i + 1) % 3) ? "\r\n" : "");
	if (i % 3)
		strcat(buf, "\r\n");

	strcat(buf, "\r\n");

	/* misc menus */
	sprintf(buf + strlen(buf), " 4. Resource Viewer\r\n");
	/*sprintf(buf + strlen(buf), " 5. Territory Viewer - coming soon\r\n");
	sprintf(buf + strlen(buf), " 6. Member Editor    - coming soon\r\n");
	sprintf(buf + strlen(buf), " 7. Change Leader    - coming soon\r\n");*/

	strcat(buf, "\r\n");

	/* usage */
	strcat(buf, " Usage: <option letter and number>\r\n");
	strcat(buf, " Examples: r3, 1, 6\r\n");
	strcat(buf, " Type 'exit' to finish.\r\n");
	strcat(buf, "> ");

	msg_to_char(d->character, buf);
	}


/* the banner menu */
void send_banner_menu(Descr d) {
	int i;

	/* header */
	sprintf(buf, "\r\n Banner Menu\r\n");

	/* colors */
	for (i = 0; color_numbers[i] != '\n'; i++)
		sprintf(buf + strlen(buf), " %c. %-15.15s%s", color_numbers[i], color_names[i], (!((i + 1) % 2)) ? "\r\n" : " ");
	if (i % 2)
		strcat(buf, "\r\n");

	strcat(buf, "\r\n");

	/* color flags */
	for (i = 0; color_flag_letters[i] != '\n'; i++)
		sprintf(buf + strlen(buf), " %c. %-15.15s\r\n", color_flag_letters[i], color_mod_bits[i]);

	/* current */
	sprintf(buf + strlen(buf), "\r\nYour current banner color is %s%s&0.\r\n", make_color_string(d->empire->banner_color, d->empire->banner_flags), make_color_phrase(d->empire->banner_color, d->empire->banner_flags));

	/* prompt */
	strcat(buf, "Enter a color code or type 'done' to exit.\r\n> ");

	msg_to_char(d->character, buf);
	}


/* the rank menu */
void send_rank_menu(Descr d) {
	/* header */
	sprintf(buf, "\r\n Rank %d\r\n", d->empire->submode + 1);

	/* name */
	sprintf(buf + strlen(buf), "1. Name: %s\r\n", d->empire->rank[d->empire->submode]);

	/* color */
	sprintf(buf + strlen(buf), "2. Color: %s%s&0\r\n", make_color_string(d->empire->rank_colors[d->empire->submode], d->empire->rank_color_flags[d->empire->submode]), make_color_phrase(d->empire->rank_colors[d->empire->submode], d->empire->rank_color_flags[d->empire->submode]));

	/* result */
	sprintf(buf + strlen(buf), "\r\nType a number to edit, or 'done' to exit.\r\n> ");

	msg_to_char(d->character, buf);
	}


/* the rank color menu */
void send_rank_color_menu(Descr d) {
	int i;

	/* header */
	sprintf(buf, "\r\n Rank Color Menu\r\n");

	/* colors */
	for (i = 0; color_numbers[i] != '\n'; i++)
		sprintf(buf + strlen(buf), " %c. %-15.15s%s", color_numbers[i], color_names[i], (!((i + 1) % 2)) ? "\r\n" : " ");
	if (i % 2)
		strcat(buf, "\r\n");

	strcat(buf, "\r\n");

	/* color flags */
	for (i = 0; color_flag_letters[i] != '\n'; i++)
		sprintf(buf + strlen(buf), " %c. %-15.15s\r\n", color_flag_letters[i], color_mod_bits[i]);

	/* current */
	sprintf(buf + strlen(buf), "\r\nYour current rank color is %s%s&0.\r\n", make_color_string(d->empire->rank_colors[d->empire->submode], d->empire->rank_color_flags[d->empire->submode]), make_color_phrase(d->empire->rank_colors[d->empire->submode], d->empire->rank_color_flags[d->empire->submode]));

	/* prompt */
	strcat(buf, "Enter a color code or type 'done' to exit.\r\n> ");

	msg_to_char(d->character, buf);
	}


/* shows all stored resources for the empire */
void display_resources(Descr d) {
	struct empire_storage_data *store;
	int e = real_empire(GET_LOYALTY(d->character)), c = 0;

	msg_to_char(d->character, "\r\n");

	/* idiot-proofing */
	if (e < 0) {
		msg_to_char(d->character, "Error: unable to locate empire!\r\n");
		return;
		}

	*buf = '\0';

	for (store = empire[e].store; store; store = store->next) {
		sprintf(buf1, "%.20s: %d", GET_OBJ_NAME_BY_PROTO(real_object(store->vnum)), store->amount);
		sprintf(buf + strlen(buf), " %-25.25s%s", buf1, !(++c % 3) ? "\r\n" : "");
		}

	if (c % 3)
		strcat(buf, "\r\n");

	if (*buf)
		msg_to_char(d->character, "Resources owned:\r\n%s", buf);
	else
		msg_to_char(d->character, "Your empire has no resources stored.\r\n");
	}


/* Parsers */

/* the menu parser */
void parse_empire_edit_menu(Descr d, char *arg) {
	int i;

	if (!str_cmp(arg, "exit")) {
		if (d->empire->changed) {
			d->empire->mode = EEDIT_EXIT;
			msg_to_char(d->character, "\r\nDo you want to save changes (y/n) ? ");
			}
		else
			exit_empire_editor(d);
		return;
		}

	switch (LOWER(*arg)) {
		case '1':
			d->empire->mode = EEDIT_NAME;
			msg_to_char(d->character, "Enter a new empire name: ");
			break;
		case '2':
			d->empire->mode = EEDIT_BANNER;
			send_banner_menu(d);
			break;
		case '3':
			d->empire->mode = EEDIT_RANK_NUMBER_CONFIRM;
			msg_to_char(d->character, "This will require saving your progress up to this point.  Continue (y/N) ? ");
			break;
		case '4':
			display_resources(d);
			d->empire->mode = EEDIT_TO_MENU;
			msg_to_char(d->character, "Press ENTER to continue: ");
			break;
		case '5':
			msg_to_char(d->character, "Feature not available.\r\n> ");
			break;
		case '6':
			msg_to_char(d->character, "Feature not available.\r\n> ");
			break;
		case '7':
			msg_to_char(d->character, "Feature not available.\r\n> ");
			break;

		case 'r':
			if ((i = atoi(arg + 1)) < 1 || i > d->empire->num_ranks) {
				msg_to_char(d->character, "Invalid rank option %d!\r\n> ", i);
				break;
				}

			d->empire->mode = EEDIT_RANK_MENU;
			d->empire->submode = i - 1;
			send_rank_menu(d);
			break;
		case 'p':
			if ((i = atoi(arg + 1)) < 1 || i > NUM_PRIVILEGES) {
				msg_to_char(d->character, "Invalid privilege option %d!\r\n> ", i);
				break;
				}

			d->empire->mode = EEDIT_PRIVILEGE;
			d->empire->submode = i - 1;
			msg_to_char(d->character, "Enter a new rank number for %s: ", priv[d->empire->submode]);
			break;

		default:
			msg_to_char(d->character, "Unknown command '%c'!\r\n> ", *arg);
			return;
		}
	}


/* the empire editor */
void parse_empire_edit(Descr d, char *arg) {
	int e, i, j, num, members;
	long l;
	bool found;
	Creature victim;
	struct char_file_u chdata;

	skip_spaces(&arg);

	switch (d->empire->mode) {
		case EEDIT_MENU:
			parse_empire_edit_menu(d, arg);
			break;

		case EEDIT_TO_MENU:
			d->empire->mode = EEDIT_MENU;
			send_empire_edit_menu(d);
			break;

		case EEDIT_NAME:
			if (strstr(arg, "&"))
				msg_to_char(d->character, "Empire names may not contain color codes.\r\n");
			else if (strstr(arg, "%"))
				msg_to_char(d->character, "Empire names may not contain per cent signs.\r\n");
			else {
				if (d->empire->name)
					free(d->empire->name);
				d->empire->name = str_dup(arg);

				d->empire->changed = TRUE;
				d->empire->mode = EEDIT_MENU;
				send_empire_edit_menu(d);
				break;
			}

			d->empire->mode = EEDIT_TO_MENU;
			msg_to_char(d->character, "Press ENTER to continue: ");
			break;

		case EEDIT_BANNER:
			if (!str_cmp(arg, "done")) {
				d->empire->mode = EEDIT_MENU;
				send_empire_edit_menu(d);
				break;
				}

			found = FALSE;

			for (i = 0; color_numbers[i] != '\n'; i++)
				if (*arg == color_numbers[i]) {
					found = TRUE;
					d->empire->banner_color = i;
					}
			for (i = 0; color_flag_letters[i] != '\n'; i++)
				if (*arg == color_flag_letters[i]) {
					if (d->empire->banner_flags & (1 << i))
						d->empire->banner_flags &= ~(1 << i);
					else
						d->empire->banner_flags |= (1 << i);
					found = TRUE;
					}

			if (!found) {
				msg_to_char(d->character, "Invalid choice!\r\n> ");
				break;
				}

			d->empire->changed = TRUE;
			send_banner_menu(d);
			break;

		case EEDIT_RANK_NUMBER:
			if ((num = atoi(arg)) < 2 || num > MAX_RANKS)
				msg_to_char(d->character, "You can have between 2 and %d ranks.\r\n", MAX_RANKS);
			else if (num == d->empire->num_ranks) {
				d->empire->mode = EEDIT_MENU;
				send_empire_edit_menu(d);
				break;
				}
			else {
				l = d->empire->leader;
				e = real_empire(d->empire->leader);

				/* fix old peoples */
				if (num < d->empire->num_ranks) {
					for (j = 0, members = empire[e].members; j <= top_of_p_table && members; j++) {
						if ((victim = is_playing((player_table + j)->id))) {
							if (GET_LOYALTY(victim) == l) {
								if (GET_RANK(victim) >= num)
									GET_RANK(victim) = num - 1;
								SAVE_CHAR(victim);
								members--;
								}
							}
						else {
							load_char((player_table + j)->name, &chdata);
							if (chdata.player_specials_saved.loyalty == l) {
								if (chdata.player_specials_saved.rank >= num)
									chdata.player_specials_saved.rank = num - 1;
								save_char_file_u(chdata);
								members--;
								}
							}
						}
					}

				GET_RANK(d->character) = num;
				SAVE_CHAR(d->character);

				if (num < d->empire->num_ranks)
					for (j = 0; j < NUM_PRIVILEGES; j++)
						d->empire->priv[j] = num;

				for (j = 0; j < MAX_RANKS; j++)
					if (j < num && !d->empire->rank[j]) {
						d->empire->rank[j] = str_dup("Follower");
						d->empire->rank_colors[j] = 0;
						d->empire->rank_color_flags[j] = 0;
						}

				d->empire->num_ranks = num;

				/* must save now */
				save_empire_editor(d->empire, e);

				d->empire->changed = TRUE;
				d->empire->mode = EEDIT_MENU;
				send_empire_edit_menu(d);
				break;
				}

			d->empire->mode = EEDIT_TO_MENU;
			msg_to_char(d->character, "Press ENTER to continue: ");
			break;

		case EEDIT_RANK_NUMBER_CONFIRM:
			switch (LOWER(*arg)) {
				case 'y':
					d->empire->mode = EEDIT_RANK_NUMBER;
					msg_to_char(d->character, "How many ranks would you like (2-20): ");
					break;
				default:
					d->empire->mode = EEDIT_MENU;
					send_empire_edit_menu(d);
					break;
				}
			break;

		case EEDIT_RANK_MENU:
			if (!str_cmp(arg, "done")) {
				d->empire->mode = EEDIT_MENU;
				send_empire_edit_menu(d);
				break;
				}

			switch (LOWER(*arg)) {
				case '1':
					d->empire->mode = EEDIT_RANK_NAME;
					msg_to_char(d->character, "Enter a new name for this rank: ");
					break;
				case '2':
					d->empire->mode = EEDIT_RANK_COLOR;
					send_rank_color_menu(d);
					break;
				default:
					msg_to_char(d->character, "Invalid choice!\r\n> ");
					break;
				}

			break;

		case EEDIT_RANK_NAME:
			if (strstr(arg, "&"))
				msg_to_char(d->character, "Rank names may not contain color codes.\r\n> ");
			else {
				if (d->empire->rank[d->empire->submode])
					free (d->empire->rank[d->empire->submode]);
				d->empire->rank[d->empire->submode] = str_dup(arg);

				d->empire->changed = TRUE;
				d->empire->mode = EEDIT_RANK_MENU;
				send_rank_menu(d);
				}

			break;

		case EEDIT_RANK_COLOR:
			if (!str_cmp(arg, "done")) {
				d->empire->mode = EEDIT_RANK_MENU;
				send_rank_menu(d);
				break;
				}

			found = FALSE;

			for (i = 0; color_numbers[i] != '\n'; i++)
				if (*arg == color_numbers[i]) {
					found = TRUE;
					d->empire->rank_colors[d->empire->submode] = i;
					}
			for (i = 0; color_flag_letters[i] != '\n'; i++)
				if (*arg == color_flag_letters[i]) {
					if (d->empire->rank_color_flags[d->empire->submode] & (1 << i))
						d->empire->rank_color_flags[d->empire->submode] &= ~(1 << i);
					else
						d->empire->rank_color_flags[d->empire->submode] |= (1 << i);
					found = TRUE;
					}

			if (!found) {
				msg_to_char(d->character, "Invalid choice!\r\n> ");
				break;
				}

			d->empire->changed = TRUE;
			send_rank_color_menu(d);
			break;

		case EEDIT_PRIVILEGE:
			if ((num = atoi(arg)) < 1 || num > d->empire->num_ranks) {
				msg_to_char(d->character, "The rank number must be between 1 and %d.\r\n", d->empire->num_ranks);
				d->empire->mode = EEDIT_TO_MENU;
				msg_to_char(d->character, "Press ENTER to continue: ");
				break;
				}

			d->empire->priv[d->empire->submode] = num;

			d->empire->changed = TRUE;
			d->empire->mode = EEDIT_MENU;
			send_empire_edit_menu(d);
			break;

		case EEDIT_EXIT:
			switch (LOWER(*arg)) {
				case 'y':
					save_empire_editor(d->empire, real_empire(GET_LOYALTY(d->character)));
					log_to_empire(real_empire(GET_LOYALTY(d->character)), "%s has edited the empire", PERS(d->character, d->character, 1));
					syslog(GET_INVIS_LEV(d->character), FALSE, "%s has edited %s empire", PERS(d->character, d->character, 1), HSHR(d->character));
				case 'n':
					exit_empire_editor(d);
					break;
				default:
					msg_to_char(d->character, "\r\nYou must type 'yes' or 'no': ");
					break;
				}
			break;

		default:
			syslog(0, TRUE, "SYSERR: Unknown editor mode!");
			d->empire->mode = EEDIT_MENU;
			send_empire_edit_menu(d);
			return;
		}
	}