Index: code/fred2/management.cpp
===================================================================
--- code/fred2/management.cpp	(revision 10925)
+++ code/fred2/management.cpp	(working copy)
@@ -397,9 +397,7 @@
 
 	hud_init_comm_orders();		// Goober5000
 
-	if (!new_alpha_colors_init()) {
-		old_alpha_colors_init();
-	}
+	alpha_colors_init();
 	
 	gamesnd_parse_soundstbl();		// needs to be loaded after species stuff but before interface/weapon/ship stuff - taylor
 	mission_brief_common_init();	
Index: code/freespace2/freespace.cpp
===================================================================
--- code/freespace2/freespace.cpp	(revision 10925)
+++ code/freespace2/freespace.cpp	(working copy)
@@ -2001,9 +2001,7 @@
 
 	// initialize alpha colors
 	// CommanderDJ: try with colors.tbl first, then use the old way if that doesn't work
-	if (!new_alpha_colors_init()) {
-		old_alpha_colors_init();
-	}
+	alpha_colors_init();
 
 	obj_init();	
 	mflash_game_init();	
Index: code/globalincs/alphacolors.cpp
===================================================================
--- code/globalincs/alphacolors.cpp	(revision 10925)
+++ code/globalincs/alphacolors.cpp	(working copy)
@@ -15,10 +15,17 @@
 SCP_map<SCP_string, team_color> Team_Colors;
 SCP_vector<SCP_string> Team_Names;
 
+SCP_map<char, color> Tagged_Colors;
+SCP_vector<char> Color_Tags;
+
 // -----------------------------------------------------------------------------------
 // ALPHA DEFINES/VARS
 //
 
+color Color_text_normal, Color_text_subselected, Color_text_selected;
+color Color_text_error, Color_text_error_hi, Color_text_active, Color_text_active_hi;
+color Color_text_heading, Color_more_indicator, Color_more_bright, Color_bright, Color_normal;
+
 color Color_blue, Color_bright_blue, Color_green, Color_bright_green;
 color Color_black, Color_grey, Color_silver, Color_white, Color_bright_white;
 color Color_violet_gray, Color_violet, Color_pink, Color_light_pink;
@@ -26,6 +33,170 @@
 color Color_ui_light_green, Color_ui_green;
 color Color_ui_light_pink, Color_ui_pink;
 
+color Brief_color_red, Brief_color_green, Brief_color_legacy_neutral;
+
+char default_fiction_viewer_color = 'w';
+char default_command_briefing_color = 'w';
+char default_briefing_color = 'w';
+char default_redalert_briefing_color = 'w';
+char default_debriefing_color = 'w';
+char default_recommendation_color = 'r';
+char default_loop_briefing_color = 'w';
+
+color *COLOR_LIST[TOTAL_COLORS] = {
+	&Color_blue,
+	&Color_bright_blue,
+	&Color_green,
+	&Color_bright_green,
+	&Color_black,
+	&Color_grey,
+	&Color_silver,
+	&Color_white,
+	&Color_bright_white,
+	&Color_violet_gray,
+	&Color_violet,
+	&Color_dim_red,
+	&Color_red,
+	&Color_bright_red,
+	&Color_pink,
+	&Color_light_pink,
+	&Color_yellow,
+	&Color_bright_yellow,
+	&Color_ui_light_green,
+	&Color_ui_green,
+	&Color_ui_light_pink,
+	&Color_ui_pink,
+};
+
+const char *COLOR_NAMES[TOTAL_COLORS] = {
+	"Blue",
+	"Bright Blue",
+	"Green",
+	"Bright Green",
+	"Black",
+	"Grey",
+	"Silver",
+	"White",
+	"Bright White",
+	"Violet Gray",
+	"Violet",
+	"Dim Red",
+	"Red",
+	"Bright Red",
+	"Pink",
+	"Light Pink",
+	"Yellow",
+	"Bright Yellow",
+	"UI Light Green",
+	"UI Green",
+	"UI Light Pink",
+	"UI Pink"
+};
+
+const int rgba_defaults[TOTAL_COLORS][4] = {
+	{93, 93, 128, 255},
+	{185, 185, 255, 255},
+	{0, 120, 0, 255},
+	{50, 190, 50, 255},
+	{0, 0, 0, 255},
+	{65, 65, 65, 255},
+	{160, 160, 160, 255},
+	{105, 105, 105, 255},
+	{255, 255, 255 ,255},
+	{160, 144, 160, 255},
+	{192, 104, 192, 255},
+	{80, 6, 6, 255},
+	{126, 6, 6, 255},
+	{200, 0, 0, 255},
+	{185, 150, 150, 255},
+	{230, 190, 190, 255},
+	{255, 255, 122, 255},
+	{255, 255, 0, 255},
+	{161, 184, 161, 255},
+	{190, 228, 190, 255},
+	{184, 161, 161, 255},
+	{228, 190, 190, 255}
+};
+
+color *interface_colors[INTERFACE_COLORS] = {
+	&Color_text_normal,
+	&Color_text_subselected,
+	&Color_text_selected,
+	&Color_text_error,
+	&Color_text_error_hi,
+	&Color_text_active,
+	&Color_text_active_hi,
+	&Color_text_heading,
+	&Color_more_indicator,
+	&Color_more_bright,
+	&Color_bright,
+	&Color_normal,
+};
+
+const int interface_defaults[INTERFACE_COLORS] = {
+	7,	//"White"
+	0,	//"Blue"
+	1,	//"Bright Blue"
+	12,	//"Red"
+	13,	//"Bright Red"
+	8,	//"Bright White"
+	8,	//"Bright White"
+	9,	//"Violet Gray"
+	12,	//"Red"
+	13,	//"Bright Red"
+	1,	//"Bright Blue"
+	7,	//"White"
+};
+
+#define DEFAULT_TAGS	20
+
+const char DEFAULT_TAG_LIST[DEFAULT_TAGS] = {
+	'w',
+	'W',
+	'r',
+	'g',
+	'y',
+	'b',
+	'f',
+	'h',
+	'n',
+	'B',
+	'G',
+	'R',
+	'Y',
+	'k',
+	'e',
+	'E',
+	'v',
+	'V',
+	'p',
+	'P',
+};
+
+color* DEFAULT_TAG_COLORS[DEFAULT_TAGS] = {
+	&Color_white,
+	&Color_bright_white,
+	&Color_red,
+	&Color_green,
+	&Color_yellow,
+	&Color_blue,
+	&Brief_color_green,
+	&Brief_color_red,
+	&Brief_color_legacy_neutral,
+	&Color_bright_blue,
+	&Color_bright_green,
+	&Color_bright_red,
+	&Color_bright_yellow,
+	&Color_black,
+	&Color_grey,
+	&Color_silver,
+	&Color_violet_gray,
+	&Color_violet,
+	&Color_pink,
+	&Color_light_pink,
+};
+
+
 // netplayer colors
 color *Color_netplayer[NETPLAYER_COLORS] = {
 
@@ -52,6 +223,9 @@
 };
 
 
+void parse_colors(const char *filename);
+void parse_everything_else(const char *filename);	// needs a better name
+
 // -----------------------------------------------------------------------------------
 // ALPHA FUNCTIONS
 //
@@ -58,63 +232,60 @@
 
 /**
 * CommanderDJ: initialise alpha colors based on colors.tbl
-* \return 1 if successful, 0 if init fails for any reason
+* Made modular and given a wider range of features by MageKing17
 */
-int new_alpha_colors_init()
+void alpha_colors_init()
 {
-	int err_code;
-	if ((err_code = setjmp(parse_abort)) != 0)
-	{
-		mprintf(("Unable to parse 'colors.tbl'!  Error code = %d.\n", err_code));
-		return 0;
+	// Set our default colors.
+	int i;
+	for (i = 0; i < TOTAL_COLORS; i++) {
+		gr_init_alphacolor(COLOR_LIST[i], rgba_defaults[i][0], rgba_defaults[i][1], rgba_defaults[i][2], rgba_defaults[i][3]);
 	}
 
-	if (cf_exists_full("colors.tbl", CF_TYPE_TABLES))
-	{
-		read_file_text("colors.tbl", CF_TYPE_TABLES);
-		mprintf(("TABLES => Starting parse of 'colors.tbl'...\n"));
+	if (cf_exists_full("colors.tbl", CF_TYPE_TABLES)) {
+		mprintf(("TABLES => Starting parse of 'colors.tbl' (checking '#Start Colors' section only)...\n"));
+		parse_colors("colors.tbl");
 	}
-	else
-	{
-		//colors.tbl doesn't exist
-		mprintf(("TABLES => Unable to find 'colors.tbl'. Initialising colors with default values.\n"));
-		return 0;
+	parse_modular_table(NOX("*-clr.tbm"), parse_colors);
+
+	// Set defaults for interface colors and color tags (must be done after the above because they're generally just copies of above-defined colors).
+	for (i = 0; i < INTERFACE_COLORS; i++) {
+		memcpy(interface_colors[i], COLOR_LIST[interface_defaults[i]], sizeof(color));
 	}
 
+	for (i = 0; i < DEFAULT_TAGS; i++) {
+		Tagged_Colors[DEFAULT_TAG_LIST[i]] = *DEFAULT_TAG_COLORS[i];
+		Color_Tags.push_back(DEFAULT_TAG_LIST[i]);
+	}
+
+	if (cf_exists_full("colors.tbl", CF_TYPE_TABLES)) {
+		mprintf(("TABLES => Starting parse of 'colors.tbl' (skipping '#Start Colors' section)...\n"));
+		parse_everything_else("colors.tbl");
+	}
+	parse_modular_table(NOX("*-clr.tbm"), parse_everything_else);
+}
+
+void parse_colors(const char *filename)
+{
+	Assertion(filename != NULL, "parse_colors() called on NULL; get a coder!\n");
+	read_file_text(filename, CF_TYPE_TABLES);
+
+	int err_code;
+	if ((err_code = setjmp(parse_abort)) != 0) {
+		mprintf(("TABLES: Unable to parse '%s'!  Error code = %d.\n", filename, err_code));
+		return;
+	}
+
 	reset_parse();
 
 	// we search for the colors based on their order of definition above
 	// if the color isn't found, we just use default values
-	if (required_string("#Start Colors"))
+	if (optional_string("#Start Colors"))
 	{
 		// vars for holding rgba values
 		int rgba[4] = {0,0,0,0};
+		int i, j;
 
-		color *colors[TOTAL_COLORS] = {
-			&Color_blue,
-			&Color_bright_blue,
-			&Color_green,
-			&Color_bright_green,
-			&Color_black,
-			&Color_grey,
-			&Color_silver,
-			&Color_white,
-			&Color_bright_white,
-			&Color_violet_gray,
-			&Color_violet,
-			&Color_dim_red,
-			&Color_red,
-			&Color_bright_red,
-			&Color_pink,
-			&Color_light_pink,
-			&Color_yellow,
-			&Color_bright_yellow,
-			&Color_ui_light_green,
-			&Color_ui_green,
-			&Color_ui_light_pink,
-			&Color_ui_pink,
-		};
-
 		char* color_names[TOTAL_COLORS] = {
 			"$Blue:",
 			"$Bright Blue:",
@@ -140,72 +311,66 @@
 			"$UI Pink:"
 		};
 
-		int rgba_defaults[TOTAL_COLORS][4] = {
-			{93, 93, 128, 255},
-			{185, 185, 255, 255},
-			{0, 120, 0, 255},
-			{50, 190, 50, 255},
-			{0, 0, 0, 255},
-			{65, 65, 65, 255},
-			{160, 160, 160, 255},
-			{105, 105, 105, 255},
-			{255, 255, 255 ,255},
-			{160, 144, 160, 255},
-			{192, 104, 192, 255},
-			{80, 6, 6, 255},
-			{126, 6, 6, 255},
-			{200, 0, 0, 255},
-			{185, 150, 150, 255},
-			{230, 190, 190, 255},
-			{255, 255, 122, 255},
-			{255, 255, 0, 255},
-			{161, 184, 161, 255},
-			{190, 228, 190, 255},
-			{184, 161, 161, 255},
-            {228, 190, 190, 255}
-		};
-
 		// now for each color, check if it's corresponding string is there
-		for (int i=0; i<TOTAL_COLORS; i++)
-		{
-			if (optional_string(color_names[i]))
-			{
+		for (i = 0; i < TOTAL_COLORS; i++) {
+			if (optional_string(color_names[i])) {
 				// if so, get its rgba values and initialise it using them
 				mprintf(("'%s' has been redefined.\n", color_names[i]));
-				stuff_int_list(rgba, 4, RAW_INTEGER_TYPE);
-				for (int j=0; j<4; j++)
-				{
-					if (rgba[j] < 0)
-					{
-						Warning(LOCATION, "RGBA value for '%s' in colors.tbl too low (%d), capping to 0.", color_names[i], rgba[j]);
-						rgba[j] = 0;
+				//if (check_for_string("(")) {
+					stuff_int_list(rgba, 4, RAW_INTEGER_TYPE);
+					for (j = 0; j < 4; j++) {
+						if (rgba[j] < 0) {
+							Warning(LOCATION, "RGBA value for '%s' in %s too low (%d), capping to 0.\n", color_names[i], filename, rgba[j]);
+							rgba[j] = 0;
+						} else if (rgba[j] > 255) {
+							Warning(LOCATION, "RGBA value for '%s' in %s too high (%d), capping to 255.\n", color_names[i], filename, rgba[j]);
+							rgba[j] = 255;
+						}
 					}
-					else if (rgba[j] > 255)
-					{
-						Warning(LOCATION, "RGBA value for '%s' in colors.tbl too high (%d), capping to 255.", color_names[i], rgba[j]);
-						rgba[j] = 255;
-					}
-				}
-				gr_init_alphacolor(colors[i], rgba[0], rgba[1], rgba[2], rgba[3]);
+				//} else {
+				//	stuff_hex_list(rgba, 4);
+				//}
+				gr_init_alphacolor(COLOR_LIST[i], rgba[0], rgba[1], rgba[2], rgba[3]);
 			}
-			// otherwise use its defaults
-			else
-				gr_init_alphacolor(colors[i], rgba_defaults[i][0], rgba_defaults[i][1], rgba_defaults[i][2], rgba_defaults[i][3]);
 		}
 		required_string("#End");
 	}
+}
 
+void parse_everything_else(const char *filename)
+{
+	Assertion(filename != NULL, "parse_everything_else() called on NULL; get a coder!\n");
+	read_file_text(filename, CF_TYPE_TABLES);
+
+	int err_code;
+	if ((err_code = setjmp(parse_abort)) != 0) {
+		mprintf(("TABLES: Unable to parse '%s'!  Error code = %d.\n", filename, err_code));
+		return;
+	}
+
+	reset_parse();
+
+	int rgba[4] = {0,0,0,0};
+
+	// reusable temp vars
+	int i, j;
+	SCP_string temp;
+
+	if (optional_string("#Start Colors")) {
+		// Skip this section; we already parsed it in every file.
+		skip_to_string("#End", NULL);
+	}
+
 	//Team coloring
 	if (optional_string("#Team Colors")) {
 
 		while (required_string_either("#End", "$Team Name:")) {
 			required_string("$Team Name:"); // required to move the parse pointer forward
-			char temp[NAME_LENGTH];
-			SCP_string temp2;
 			team_color temp_color;
 
-			stuff_string(temp, F_NAME, NAME_LENGTH);
-			temp2 = SCP_string(temp);
+			char temp2[NAME_LENGTH];
+			stuff_string(temp2, F_NAME, NAME_LENGTH);
+			temp = temp2;
 
 			if (required_string("$Team Stripe Color:")) {
 				int rgb[3];
@@ -231,46 +396,218 @@
 				temp_color.base.b = rgb[2] / 255.0f;
 			}
 
-			Team_Colors[temp2] = temp_color;
-			Team_Names.push_back(temp2);
+			Team_Colors[temp] = temp_color;
+			Team_Names.push_back(temp);
 		}
+		required_string("#End");
 	}
 
-	return 1;
-}
+	// Previously-hardcoded interface colors
+	if (optional_string("#Interface Colors")) {
+		char *color_names[INTERFACE_COLORS] = {
+			"$Text Normal:",
+			"$Text Subselected:",
+			"$Text Selected:",
+			"$Text Error:",
+			"$Text Error Highlighted:",
+			"$Text Active:",
+			"$Text Active Highlighted:",
+			"$Text Heading:",
+			"$More Indicator:",
+			"$Bright More Indicator:",
+			"$Bright:",
+			"$Normal:",
+		};
 
-// initialize all alpha colors (call at startup)
-void old_alpha_colors_init()
-{
-	// See the variable declarations above for color usage
-	gr_init_alphacolor( &Color_blue, 93, 93, 128, 255 );
-	gr_init_alphacolor( &Color_bright_blue, 185, 185, 255, 255 );
+		// now for each color, check if its corresponding string is there
+		for (i = 0; i < INTERFACE_COLORS; i++) {
+			if (optional_string(color_names[i])) {
+				// if so, get its rgba values and initialise it using them
+				mprintf(("'%s' has been redefined.\n", color_names[i]));
+				if ( check_for_string("(") ) {
+					// If we have a list of integers, use them.
+					stuff_int_list(rgba, 4, RAW_INTEGER_TYPE);
+					for (j = 0; j < 4; j++) {
+						if (rgba[j] < 0)
+						{
+							Warning(LOCATION, "RGBA value for '%s' in %s too low (%d), capping to 0.\n", color_names[i], filename, rgba[j]);
+							rgba[j] = 0;
+						}
+						else if (rgba[j] > 255)
+						{
+							Warning(LOCATION, "RGBA value for '%s' in %s too high (%d), capping to 255.\n", color_names[i], filename, rgba[j]);
+							rgba[j] = 255;
+						}
+					}
+					gr_init_alphacolor(interface_colors[i], rgba[0], rgba[1], rgba[2], rgba[3]);
+				//} else if (check_for_string("#")) {
+				//	stuff_hex_list(rgba, 4);
+				//	gr_init_alphacolor(interface_colors[i], rgba[0], rgba[1], rgba[2], rgba[3]);
+				} else {
+					// We have a string; it should be the name of a color to use.
+					stuff_string(temp, F_NAME);
+					for (j = 0; j < TOTAL_COLORS; j++) {
+						if ( !temp.compare(COLOR_NAMES[j]) ) {
+							break;
+						}
+					}
+					if ( j == TOTAL_COLORS ) {
+						Warning(LOCATION, "Unknown color '%s' in %s, for definition of '%s'; using default ('%s').\n", temp.c_str(), filename, color_names[i], COLOR_NAMES[interface_defaults[i]]);
+					} else {
+						Assertion(j >= 0 && j < TOTAL_COLORS, "Attempting to copy nonexistant color (%d out of 0-%d)!\n", j, TOTAL_COLORS-1);
+						memcpy(interface_colors[i], COLOR_LIST[j], sizeof(color));
+					}
+				}
+			}
+		}
+		required_string("#End");
+	}
 
-	gr_init_alphacolor( &Color_green, 0, 120, 0, 255 );
-	gr_init_alphacolor( &Color_bright_green, 50, 190, 50, 255 );
+	// Text color tags; for briefings, command briefings, debriefings, and the fiction viewer
+	if (optional_string("#Color Tags")) {
+		while (required_string_either("$Tag:", "#End") < 1) {
+			required_string("$Tag:");
+			color temp_color;
+			char tag;
 
-	gr_init_alphacolor( &Color_black, 0, 0, 0, 255 );
-	gr_init_alphacolor( &Color_grey, 65, 65, 65, 255 );
-	gr_init_alphacolor( &Color_silver, 160, 160, 160, 255 );
-	gr_init_alphacolor( &Color_white, 105, 105, 105, 255 );
-	gr_init_alphacolor( &Color_bright_white, 255, 255, 255, 255 );
+			stuff_string(temp, F_RAW);
+			if (temp[0] == '$') {
+				if (temp[1] == '\0') {
+					Error(LOCATION, "%s - found a '$Tag:' entry with a solitary '$'.\n", filename);
+				}
+				tag = temp[1];
+				if (temp[2] != '\0') {
+					Warning(LOCATION, "%s - tag '$%c' has extra text in its definition.\n", filename, tag);
+				}
+			} else if (temp[0] == '\0') {
+				Error(LOCATION, "%s - found a '$Tag:' entry with no tag.\n", filename);
+			} else {
+				tag = temp[0];
+				if (temp[1] != '\0') {
+					Warning(LOCATION, "%s - tag '$%c' has extra text in its definition.\n", filename, tag);
+				}
+			}
 
-	gr_init_alphacolor( &Color_violet_gray, 160, 144, 160, 255 );
-	gr_init_alphacolor( &Color_violet, 192, 104, 192, 255 );
+			if (Tagged_Colors.find(tag) == Tagged_Colors.end()) {	// Only push the tag to our list of tags if it's actually new, not just a redefinition.
+				Color_Tags.push_back(tag);
+			}
 
-	gr_init_alphacolor( &Color_dim_red, 80, 6, 6, 255 );
-	gr_init_alphacolor( &Color_red, 126, 6, 6, 255 );
-	gr_init_alphacolor( &Color_bright_red, 200, 0, 0, 255 );
+			switch(required_string_one_of(4, "+Color:", "+Friendly", "+Hostile", "+Neutral")) {
+			case 0:	// +Color
+				required_string("+Color:");
 
-	gr_init_alphacolor( &Color_pink, 185, 150, 150, 255 );
-	gr_init_alphacolor( &Color_light_pink, 230, 190, 190, 255 );
+				rgba[0] = rgba[1] = rgba[2] = 0;
+				rgba[3] = 255;	// Odds are pretty high you want it to have full alpha...
 
-	gr_init_alphacolor( &Color_yellow, 255, 255, 122, 255 );
-	gr_init_alphacolor( &Color_bright_yellow, 255, 255, 0, 255 );
+				if ( check_for_string("(") ) {
+					stuff_int_list(rgba, 4, RAW_INTEGER_TYPE);
+					for (j = 0; j < 4; j++) {
+						if (rgba[j] < 0)
+						{
+							Warning(LOCATION, "RGBA value for '$%c' in %s too low (%d), capping to 0.\n", tag, filename, rgba[j]);
+							rgba[j] = 0;
+						}
+						else if (rgba[j] > 255)
+						{
+							Warning(LOCATION, "RGBA value for '$%c' in %s too high (%d), capping to 255.\n", tag, filename, rgba[j]);
+							rgba[j] = 255;
+						}
+					}
+					gr_init_alphacolor(&temp_color, rgba[0], rgba[1], rgba[2], rgba[3]);
+					Tagged_Colors[tag] = temp_color;
+				//} else if ( check_for_string ("#") ) {
+				//	stuff_hex_list(rgba, 4);
+				//	gr_init_alphacolor(&temp_color, rgba[0], rgba[1], rgba[2], rgba[3]);
+				//	Tagged_Colors[tag] = temp_color;
+				} else {
+					// We have a string; it should be the name of a color to use.
+					stuff_string(temp, F_NAME);
+					for (j = 0; j < TOTAL_COLORS; j++) {
+						if ( !temp.compare(COLOR_NAMES[j]) ) {
+							break;
+						}
+					}
+					if ( j == TOTAL_COLORS ) {
+						Error(LOCATION, "Unknown color '%s' in %s, for definition of tag '$%c'.\n", temp.c_str(), filename, tag);
+					}
+					Tagged_Colors[tag] = *COLOR_LIST[j];
+				}
+				break;
+			case 1:	// +Friendly
+				required_string("+Friendly");
+				Tagged_Colors[tag] = Brief_color_green;
+				break;
+			case 2:	// +Hostile
+				required_string("+Hostile");
+				Tagged_Colors[tag] = Brief_color_red;
+				break;
+			case 3:	// +Neutral
+				required_string("+Neutral");
+				Tagged_Colors[tag] = Brief_color_legacy_neutral;
+				break;
+			case -1:
+				// -noparseerrors is set
+				if (Tagged_Colors.find(tag) == Tagged_Colors.end()) {	// It was a new color, but since we haven't actually defined it...
+					Color_Tags.pop_back();
+				}
+				break;
+			default:
+				Assertion(false, "MageKing17 made a coding error somewhere, and you should laugh at him (and report this error).\n");
+				break;
+			}
+		}
 
-	gr_init_alphacolor( &Color_ui_light_green, 161, 184, 161, 255 );
-	gr_init_alphacolor( &Color_ui_green, 190, 228, 190, 255 );
+		required_string("#End");
+	}
+	Assertion(Color_Tags.size() == Tagged_Colors.size(), "Color_Tags and Tagged_Colors size mismatch; get a coder!\n");
 
-	gr_init_alphacolor( &Color_ui_light_pink, 184, 161, 161, 255 );
-	gr_init_alphacolor( &Color_ui_pink, 228, 190, 190, 255 );
-}
+	if (optional_string("#Default Text Colors")) {
+
+		char* color_names[MAX_DEFAULT_TEXT_COLORS] = {
+			"$Fiction Viewer:",
+			"$Command Briefing:",
+			"$Briefing:",
+			"$Redalert Briefing:",
+			"$Debriefing:",
+			"$Recommendation:",
+			"$Loop Briefing:",
+		};
+
+		char *color_value[MAX_DEFAULT_TEXT_COLORS] = {
+			&default_fiction_viewer_color,
+			&default_command_briefing_color,
+			&default_briefing_color,
+			&default_redalert_briefing_color,
+			&default_debriefing_color,
+			&default_recommendation_color,
+			&default_loop_briefing_color,
+		};
+
+		for (i = 0; i < MAX_DEFAULT_TEXT_COLORS; i++) {
+			if ( optional_string(color_names[i]) ) {
+				stuff_string(temp, F_RAW);
+				if (temp[0] == '$') {
+					if (temp[1] == '\0') {
+						Error(LOCATION, "%s - default text color '%s' entry with a solitary '$'.\n", filename, color_names[i]);
+					}
+					*color_value[i] = temp[1];
+					if (temp[2] != '\0') {
+						Warning(LOCATION, "%s - default text color '%s' has extra text after the tag '$%c'.\n", filename, color_names[i], *color_value[i]);
+					}
+				} else if (temp[0] == '\0') {
+					Error(LOCATION, "%s - default text color '%s' entry with no tag.\n", filename, color_names[i]);
+				} else {
+					*color_value[i] = temp[0];
+					if (temp[1] != '\0') {
+						Warning(LOCATION, "%s - default text color '%s' has extra text after the tag '$%c'.\n", filename, color_names[i], *color_value[i]);
+					}
+				}
+				if (Tagged_Colors.find(*color_value[i]) == Tagged_Colors.end()) {
+					// Just mprintf() this information instead of complaining with a Warning(); the tag might be defined in a later-loading TBM, and if it isn't, nothing too terrible will happen.
+					mprintf(("%s - default text color '%s' set to non-existant tag '$%c'.\n", filename, color_names[i], *color_value[i]));
+				}
+			}
+		}
+		required_string("#End");
+	}
+}
\ No newline at end of file
Index: code/globalincs/alphacolors.h
===================================================================
--- code/globalincs/alphacolors.h	(revision 10925)
+++ code/globalincs/alphacolors.h	(working copy)
@@ -21,28 +21,11 @@
 // Colors for UI
 // See FreeSpace.cpp for usage
 
-// The following colors are for text drawing:
-// normal text
-#define Color_text_normal		Color_white
-// text highlighted while down still down on a line
-#define Color_text_subselected	Color_blue
-// text highlighted as the selected line
-#define Color_text_selected		Color_bright_blue
-// text that indicates an error
-#define Color_text_error		Color_red
-// text that indicates an error, and line is selected or subselected
-#define Color_text_error_hi		Color_bright_red
-// text that indicates line is active item
-#define Color_text_active		Color_bright_white
-// text that indicates line is active item, and line is selected or subselected
-#define Color_text_active_hi	Color_bright_white
-// text drawn as a heading for a section or title, etc
-#define Color_text_heading		Color_violet_gray
-// color of the "more" indicator in briefings/command briefings/debrefings
-#define Color_more_indicator	Color_red
+#define INTERFACE_COLORS 12
+extern color Color_text_normal, Color_text_subselected, Color_text_selected;
+extern color Color_text_error, Color_text_error_hi, Color_text_active, Color_text_active_hi;
+extern color Color_text_heading, Color_more_indicator, Color_more_bright, Color_bright, Color_normal;
 
-#define Color_bright Color_bright_blue
-#define Color_normal Color_white
 #define TOTAL_COLORS 22
 extern color Color_blue, Color_bright_blue, Color_green, Color_bright_green;
 extern color Color_black, Color_grey, Color_silver, Color_white, Color_bright_white;
@@ -52,6 +35,9 @@
 extern color Color_ui_light_green, Color_ui_green;
 extern color Color_ui_light_pink, Color_ui_pink;
 
+// briefing hostile/friendly/neutral colors
+extern color Brief_color_red, Brief_color_green, Brief_color_legacy_neutral;
+
 // netplayer colors
 #define NETPLAYER_COLORS 20
 extern color *Color_netplayer[NETPLAYER_COLORS];
@@ -60,14 +46,23 @@
 extern SCP_map<SCP_string, team_color> Team_Colors;
 extern SCP_vector<SCP_string> Team_Names;
 
+extern SCP_map<char, color> Tagged_Colors;
+extern SCP_vector<char> Color_Tags;
+
+#define MAX_DEFAULT_TEXT_COLORS	7
+extern char default_fiction_viewer_color;
+extern char default_command_briefing_color;
+extern char default_briefing_color;
+extern char default_redalert_briefing_color;
+extern char default_debriefing_color;
+extern char default_recommendation_color;
+extern char default_loop_briefing_color;
+
 // -----------------------------------------------------------------------------------
 // ALPHA FUNCTIONS
 //
 
 //initialize alpha colors based on colors.tbl
-int new_alpha_colors_init();
+void alpha_colors_init();
 
-//initialize alpha colors based on hardcoded values
-void old_alpha_colors_init();
-
 #endif
Index: code/mission/missionbriefcommon.cpp
===================================================================
--- code/mission/missionbriefcommon.cpp	(revision 10925)
+++ code/mission/missionbriefcommon.cpp	(working copy)
@@ -117,6 +117,8 @@
 #define LOOKAT_DIST	500.0f
 #define STAGE_ADVANCE_DELAY	1000		// time in ms to wait after voice stops before advancing stage
 
+extern const float		BRIEF_TEXT_WIPE_TIME	= 1.5f;		// time in seconds for wipe to occur
+
 // --------------------------------------------------------------------------------------
 // Game-wide global data
 // --------------------------------------------------------------------------------------
@@ -181,7 +183,7 @@
 typedef struct colored_char
 {
 	char	letter;
-	ubyte	color;		// index into Brief_text_colors[]
+	char	color;		// tag to look up in Tagged_Colors
 } colored_char;
 
 typedef SCP_vector<colored_char> briefing_line; 
@@ -188,32 +190,6 @@
 typedef SCP_vector<briefing_line> briefing_stream; 
 static briefing_stream Colored_stream[MAX_TEXT_STREAMS];
 
-color Brief_color_red, Brief_color_green, Brief_color_legacy_neutral;
-
-color *Brief_text_colors[MAX_BRIEF_TEXT_COLORS] = 
-{
-	&Color_white,
-	&Color_bright_white,
-	&Color_red,
-	&Color_green,
-	&Color_yellow,
-	&Color_blue,
-	&Brief_color_green,
-	&Brief_color_red,
-	&Brief_color_legacy_neutral,
-	&Color_bright_blue,
-	&Color_bright_green,
-	&Color_bright_red,
-	&Color_bright_yellow,
-	&Color_black,
-	&Color_grey,
-	&Color_silver,
-	&Color_violet_gray,
-	&Color_violet,
-	&Color_pink,
-	&Color_light_pink,
-};
-
 #define BRIGHTEN_LEAD	2
 
 float Brief_text_wipe_time_elapsed;
@@ -288,7 +264,7 @@
 void	brief_render_grid(grid *gridp);
 void	brief_modify_grid(grid *gridp);
 void	brief_rpd_line(vec3d *v0, vec3d *v1);
-void	brief_set_text_color(int color_index);
+void	brief_set_text_color(char color_tag);
 extern void get_camera_limits(matrix *start_camera, matrix *end_camera, float time, vec3d *acc_max, vec3d *w_max);
 int brief_text_wipe_finished();
 
@@ -1216,7 +1192,7 @@
 	// The following algorithm builds a character sequence of color 'last_color' into 'line' buffer.
 	// When the color changes, the buffer is drawn and reset.
 	{
-		int last_color = src->at(0).color;    
+		char last_color = src->at(0).color;
 		for (int current_pos=0; current_pos<truncate_len; current_pos++) {
 			colored_char &current_char=src->at(current_pos);		
 			//when the current color changes, the accumulated character sequence is drawn.
@@ -1428,81 +1404,18 @@
 }
 
 
-ubyte brief_return_color_index(char c)
+bool brief_verify_color_tag(char color_tag)
 {
-	switch (c) {
-		case 'f':
-			return BRIEF_TEXT_FRIENDLY;
-
-		case 'h':
-			return BRIEF_TEXT_HOSTILE;
-
-		case 'n':
-			return BRIEF_TEXT_NEUTRAL;
-
-		case 'r':
-			return BRIEF_TEXT_RED;
-
-		case 'g':
-			return BRIEF_TEXT_GREEN;
-
-		case 'b':
-			return BRIEF_TEXT_BLUE;
-
-//The following added by Zacam for expanded BRIEF colors
- 		case 'w':
- 			return BRIEF_TEXT_WHITE;
-
- 		case 'y':
- 			return BRIEF_TEXT_YELLOW;
-
-		case 'W':
-			return BRIEF_TEXT_BRIGHT_WHITE;
-
-		case 'B':
-			return BRIEF_TEXT_BRIGHT_BLUE;
-
-		case 'G':
-			return BRIEF_TEXT_BRIGHT_GREEN;
-
-		case 'R':
-			return BRIEF_TEXT_BRIGHT_RED;
-
-		case 'Y':
-			return BRIEF_TEXT_BRIGHT_YELLOW;
-
-		case 'k':
-			return BRIEF_TEXT_BLACK;
-
-		case 'e':
-			return BRIEF_TEXT_GREY;
-
-		case 'E':
-			return BRIEF_TEXT_SILVER;
-
-		case 'v':
-			return BRIEF_TEXT_VIOLET_GRAY;
-
-		case 'V':
-			return BRIEF_TEXT_VIOLET;
-
-		case 'p':
-			return BRIEF_TEXT_PINK;
-
-		case 'P':
-			return BRIEF_TEXT_LIGHT_PINK;
-
-		default:	//Zacam: Changed fron an Int3() in order to provide better feedback while still allowing play.
-			Warning(LOCATION, "Unrecognized or undefined case character: '$%c' used in Briefing in mission: '%s'. Tell Zacam.", c, Mission_filename);
-	} // end switch
-
-	return BRIEF_TEXT_WHITE;
+	if ( Tagged_Colors.find(color_tag) == Tagged_Colors.end() ) {
+		Warning(LOCATION, "Invalid text color tag '$%c' used in mission: '%s'.\n", color_tag, Mission_filename);
+		return false;
+	}
+	return true;
 }
 
-void brief_set_text_color(int color_index)
+void brief_set_text_color(char color_tag)
 {
-	Assert(color_index < MAX_BRIEF_TEXT_COLORS);
-	gr_set_color_fast(Brief_text_colors[color_index]);
+	gr_set_color_fast(&Tagged_Colors[color_tag]);
 }
 
 /**
@@ -1529,13 +1442,13 @@
  * @param[in,out] color_stack_index pointer to the current index in the above stack
  * @return number of character of the resulting sequence.
  */
-int brief_text_colorize(char *src, int instance, ubyte default_color_stack[], int &color_stack_index)
+int brief_text_colorize(char *src, int instance, char default_color_stack[], int &color_stack_index)
 {
 	Assert(src);
 	Assert((0 <= instance) && (instance < (int)(sizeof(Colored_stream) / sizeof(*Colored_stream))));
 
 	briefing_line dest_line;	//the resulting vector of colored character
-	ubyte active_color_index;	//the current drawing color
+	char active_color_index;	//the current drawing color
 
 	active_color_index = default_color_stack[color_stack_index];
 
@@ -1567,7 +1480,7 @@
 			// normal $c or $c{
 			else
 			{
-				active_color_index = brief_return_color_index(src[i]);
+				if (brief_verify_color_tag(src[i])) active_color_index = src[i];
 				i++; // Consume the color identifier and focus on the white character (if any)
 
 				// special case: color spans (different default color within braces)
@@ -1612,12 +1525,12 @@
  *
  * @param src paragraph of text to process
  * @param w	max width of line in pixels
+ * @param[in] default_color optional, default color for this text (defaults to '\0', which gets converted to the first defined color tag (should be 'w'))
  * @param instance optional parameter, used when multiple text streams are required (default value is 0)
  * @param max_lines maximum number of lines
- * @param[in] default_color default color for this text (defaults to BRIEF_TEXT_WHITE)
  * @param[in] append add on to the existing lines instead of replacing them (defaults to false)
  */
-int brief_color_text_init(const char* src, int w, int instance, int max_lines, const ubyte default_color, const bool append)
+int brief_color_text_init(const char* src, int w, const char default_color, int instance, int max_lines, const bool append)
 {
 	int i, n_lines, len;
 	SCP_vector<int> n_chars;
@@ -1625,11 +1538,15 @@
 	char brief_line[MAX_BRIEF_LINE_LEN];
 
 	// manage different default colors (don't use a SCP_ stack because eh)
-	ubyte default_color_stack[HIGHEST_COLOR_STACK_INDEX + 1];
+	char default_color_stack[HIGHEST_COLOR_STACK_INDEX + 1];
 	int color_stack_index = 0;
 
 	// start off with white, or whatever our default is
-	default_color_stack[0] = default_color;
+	if (default_color == '\0' || !brief_verify_color_tag(default_color)) {	// call brief_verify_color_tag() to make sure our default is actually a defined color tag
+		default_color_stack[0] = Color_Tags[0];
+	} else {
+		default_color_stack[0] = default_color;
+	}
 
 	Assert(src != NULL);
 	n_lines = split_str(src, w, n_chars, p_str, BRIEF_META_CHAR);
@@ -1845,10 +1762,10 @@
 
 	if (gr_screen.res == GR_640) {
 		// GR_640
-		Num_brief_text_lines[0] = brief_color_text_init(msg, MAX_BRIEF_LINE_W_640);
+		Num_brief_text_lines[0] = brief_color_text_init(msg, MAX_BRIEF_LINE_W_640, default_briefing_color);
 	} else {
 		// GR_1024
-		Num_brief_text_lines[0] = brief_color_text_init(msg, MAX_BRIEF_LINE_W_1024);		
+		Num_brief_text_lines[0] = brief_color_text_init(msg, MAX_BRIEF_LINE_W_1024, default_briefing_color);
 	}
 
 	Top_brief_text_line = 0;
Index: code/mission/missionbriefcommon.h
===================================================================
--- code/mission/missionbriefcommon.h	(revision 10925)
+++ code/mission/missionbriefcommon.h	(working copy)
@@ -74,36 +74,9 @@
 
 
 
-// Moving briefing color definitions out of missionbriefcommon.cpp so they can be referenced elsewhere -MageKing17
+// Moving out of missionbriefcommon.cpp so it can be referenced elsewhere -MageKing17
+extern const float		BRIEF_TEXT_WIPE_TIME;		// time in seconds for wipe to occur
 
-enum
-{
-	BRIEF_TEXT_WHITE = 0,
-	BRIEF_TEXT_BRIGHT_WHITE,
-	BRIEF_TEXT_RED,
-	BRIEF_TEXT_GREEN,
-	BRIEF_TEXT_YELLOW,
-	BRIEF_TEXT_BLUE,
-	BRIEF_TEXT_FRIENDLY,
-	BRIEF_TEXT_HOSTILE,
-	BRIEF_TEXT_NEUTRAL,
-	BRIEF_TEXT_BRIGHT_BLUE,
-	BRIEF_TEXT_BRIGHT_GREEN,
-	BRIEF_TEXT_BRIGHT_RED,
-	BRIEF_TEXT_BRIGHT_YELLOW,
-	BRIEF_TEXT_BLACK,
-	BRIEF_TEXT_GREY,
-	BRIEF_TEXT_SILVER,
-	BRIEF_TEXT_VIOLET_GRAY,
-	BRIEF_TEXT_VIOLET,
-	BRIEF_TEXT_PINK,
-	BRIEF_TEXT_LIGHT_PINK,
-	MAX_BRIEF_TEXT_COLORS
-};
-
-// And the same here
-const float		BRIEF_TEXT_WIPE_TIME	= 1.5f;		// time in seconds for wipe to occur
-
 // ------------------------------------------------------------------------
 // Structures to hold briefing data
 // ------------------------------------------------------------------------
@@ -335,7 +308,7 @@
 void brief_voice_unpause(int stage_num);
 
 // fancy briefing style text functions for use in other modules.
-int brief_color_text_init(const char *src, int w, int instance = 0, int max_lines = MAX_BRIEF_LINES, const ubyte default_color = BRIEF_TEXT_WHITE, const bool append = false);
+int brief_color_text_init(const char *src, int w, const char default_color = '\0', int instance = 0, int max_lines = MAX_BRIEF_LINES, const bool append = false);
 int brief_render_text(int line_offset, int x, int y, int h, float frametime, int instance = 0, int line_spacing = 0);
 
 void cmd_brief_reset();
Index: code/missionui/fictionviewer.cpp
===================================================================
--- code/missionui/fictionviewer.cpp	(revision 10925)
+++ code/missionui/fictionviewer.cpp	(working copy)
@@ -320,7 +320,7 @@
 	Fiction_viewer_buttons[Fiction_viewer_ui][gr_screen.res][FVW_BUTTON_SCROLL_DOWN].button.set_hotkey(KEY_DOWN);
 
 	// init brief text
-	brief_color_text_init(Fiction_viewer_text, Fiction_viewer_text_coordinates[Fiction_viewer_ui][gr_screen.res][2], 0, 0);
+	brief_color_text_init(Fiction_viewer_text, Fiction_viewer_text_coordinates[Fiction_viewer_ui][gr_screen.res][2], default_fiction_viewer_color, 0, 0);
 
 	// if the story is going to overflow the screen, add a slider
 	if (Num_brief_text_lines[0] > Fiction_viewer_text_max_lines)
Index: code/missionui/missioncmdbrief.cpp
===================================================================
--- code/missionui/missioncmdbrief.cpp	(revision 10925)
+++ code/missionui/missioncmdbrief.cpp	(working copy)
@@ -351,7 +351,7 @@
 	}
 
 	Cur_stage = stage;
-	brief_color_text_init(Cur_cmd_brief->stage[stage].text.c_str(), Cmd_text_wnd_coords[Uses_scroll_buttons][gr_screen.res][CMD_W_COORD]);
+	brief_color_text_init(Cur_cmd_brief->stage[stage].text.c_str(), Cmd_text_wnd_coords[Uses_scroll_buttons][gr_screen.res][CMD_W_COORD], default_command_briefing_color);
 
 	// load a new animation if it's different to what's already playing
 	if (strcmp(Cur_anim_filename, Cur_cmd_brief->stage[stage].ani_filename) != 0) {
Index: code/missionui/missiondebrief.cpp
===================================================================
--- code/missionui/missiondebrief.cpp	(revision 10925)
+++ code/missionui/missiondebrief.cpp	(working copy)
@@ -1831,7 +1831,7 @@
 		}
 	}
 
-	Num_text_lines = Text_offset = brief_color_text_init("", Debrief_text_wnd_coords[gr_screen.res][2], 0, 0);	// Initialize color stuff -MageKing17
+	Num_text_lines = Text_offset = brief_color_text_init("", Debrief_text_wnd_coords[gr_screen.res][2], default_debriefing_color, 0, 0);	// Initialize color stuff -MageKing17
 
 	fsspeech_start_buffer();
 
@@ -1839,12 +1839,12 @@
 		for (i=0; i<Num_debrief_stages; i++) {
 			if (i)
 				// add a blank line between stages
-				Num_text_lines += brief_color_text_init("\n", Debrief_text_wnd_coords[gr_screen.res][2], 0, MAX_DEBRIEF_LINES, BRIEF_TEXT_WHITE, true);
+				Num_text_lines += brief_color_text_init("\n", Debrief_text_wnd_coords[gr_screen.res][2], default_debriefing_color, 0, MAX_DEBRIEF_LINES, true);
 
 			src = Debrief_stages[i]->text.c_str();
 
 			if (*src) {
-				Num_text_lines += brief_color_text_init(src, Debrief_text_wnd_coords[gr_screen.res][2], 0, MAX_DEBRIEF_LINES, BRIEF_TEXT_WHITE, true);
+				Num_text_lines += brief_color_text_init(src, Debrief_text_wnd_coords[gr_screen.res][2], default_debriefing_color, 0, MAX_DEBRIEF_LINES, true);
 
 				if (use_sim_speech && !Recommend_active) {
 					fsspeech_stuff_buffer(src);
@@ -1859,9 +1859,9 @@
 					src = XSTR( "We have no recommendations for you.", 1054);
 
 				if (*src) {
-					Num_text_lines += brief_color_text_init("\n", Debrief_text_wnd_coords[gr_screen.res][2], 0, MAX_DEBRIEF_LINES, BRIEF_TEXT_RED, true);
+					Num_text_lines += brief_color_text_init("\n", Debrief_text_wnd_coords[gr_screen.res][2], default_recommendation_color, 0, MAX_DEBRIEF_LINES, true);
 
-					Num_text_lines += brief_color_text_init(src, Debrief_text_wnd_coords[gr_screen.res][2], 0, MAX_DEBRIEF_LINES, BRIEF_TEXT_RED, true);
+					Num_text_lines += brief_color_text_init(src, Debrief_text_wnd_coords[gr_screen.res][2], default_recommendation_color, 0, MAX_DEBRIEF_LINES, true);
 					r_count++;
 
 					if (use_sim_speech) {
Index: code/missionui/missionloopbrief.cpp
===================================================================
--- code/missionui/missionloopbrief.cpp	(revision 10925)
+++ code/missionui/missionloopbrief.cpp	(working copy)
@@ -25,7 +25,9 @@
 
 #include "graphics/generic.h"
 
+extern char default_loop_briefing_color;	// Doesn't seem worth including alphacolors.h for -MageKing17
 
+
 // ---------------------------------------------------------------------------------------------------------------------------------------
 // MISSION LOOP BRIEF DEFINES/VARS
 //
@@ -169,7 +171,7 @@
 
 	// init brief text
 	if(Campaign.missions[Campaign.current_mission].mission_branch_desc != NULL){
-		brief_color_text_init(Campaign.missions[Campaign.current_mission].mission_branch_desc, Loop_brief_text_coords[gr_screen.res][2]);
+		brief_color_text_init(Campaign.missions[Campaign.current_mission].mission_branch_desc, Loop_brief_text_coords[gr_screen.res][2], default_loop_briefing_color);
 	}
 
 	bool sound_played = false;
Index: code/missionui/redalert.cpp
===================================================================
--- code/missionui/redalert.cpp	(revision 10925)
+++ code/missionui/redalert.cpp	(working copy)
@@ -337,7 +337,7 @@
 	}
 
 	if ( Briefing->num_stages > 0 ) {
-		brief_color_text_init(Briefing->stages[0].text.c_str(), Ra_brief_text_wnd_coords[gr_screen.res][RA_W_COORD], 0);
+		brief_color_text_init(Briefing->stages[0].text.c_str(), Ra_brief_text_wnd_coords[gr_screen.res][RA_W_COORD], default_redalert_briefing_color, 0);
 	}
 
 	red_alert_voice_load();
Index: code/network/multiui.cpp
===================================================================
--- code/network/multiui.cpp	(revision 10925)
+++ code/network/multiui.cpp	(working copy)
@@ -222,7 +222,7 @@
 	}
 
 	if ( (Multi_common_num_text_lines - Multi_common_top_text_line) > Multi_common_text_max_display[gr_screen.res] ) {
-		gr_set_color_fast(&Color_bright_red);
+		gr_set_color_fast(&Color_more_bright);
 		gr_string(Multi_common_text_coords[gr_screen.res][0], (Multi_common_text_coords[gr_screen.res][1] + Multi_common_text_coords[gr_screen.res][3])-5, XSTR("more",755), GR_RESIZE_MENU);
 	}
 }
Index: code/popup/popup.cpp
===================================================================
--- code/popup/popup.cpp	(revision 10925)
+++ code/popup/popup.cpp	(working copy)
@@ -731,7 +731,7 @@
 	// maybe draw "more"
 	h = 10;
 	if(i < pi->nlines){
-		gr_set_color_fast(&Color_bright_red);
+		gr_set_color_fast(&Color_more_bright);
 		gr_string(Title_coords[gr_screen.res][4], sy + (Popup_max_display[gr_screen.res]) * h, XSTR("More", 459), GR_RESIZE_MENU);
 	}
 
