/* * Created by Ian "Goober5000" Warfield for the FreeSpace2 Source Code Project. * You may not sell or otherwise commercially exploit the source or things you * create based on the source. */ #include "def_files/def_files.h" #include "hud/hud.h" #include "iff_defs/iff_defs.h" #include "io/timer.h" #include "mission/missionparse.h" #include "parse/parselo.h" #include "ship/ship.h" extern int radar_target_id_flags; int Num_iffs; iff_info Iff_info[MAX_IFFS]; int Iff_traitor; int radar_iff_color[5][2][4]; int iff_bright_delta; int *iff_color_brightness = &iff_bright_delta; // global only to file color Iff_colors[MAX_IFF_COLORS][2]; // AL 1-2-97: Create two IFF colors, regular and bright flag_def_list rti_flags[] = { { "crosshairs", RTIF_CROSSHAIRS, 0 }, { "blink", RTIF_BLINK, 0 }, { "pulsate", RTIF_PULSATE, 0 }, { "enlarge", RTIF_ENLARGE, 0 } }; int Num_rti_flags = sizeof(rti_flags)/sizeof(flag_def_list); /** * Borrowed from ship.cpp, ship_iff_init_colors * * @param is_bright Whether set to bright */ int iff_get_alpha_value(bool is_bright) { if (is_bright == false) return (HUD_COLOR_ALPHA_MAX - iff_bright_delta) * 16; else return HUD_COLOR_ALPHA_MAX * 16; } /** * Init a color and add it to the ::Iff_colors array * * @param r Red * @param g Green * @param b Blue * * @return The new IFF colour slot in ::Iff_colors array */ int iff_init_color(int r, int g, int b) { typedef struct { int r; int g; int b; } temp_color_t; int i, idx; temp_color_t *c; static int num_iff_colors = 0; static temp_color_t temp_colors[MAX_IFF_COLORS]; Assert(r >= 0 && r <= 255); Assert(g >= 0 && g <= 255); Assert(b >= 0 && b <= 255); // make sure we're under the limit if (num_iff_colors >= MAX_IFF_COLORS) { Warning(LOCATION, "Too many iff colors! Ignoring the rest...\n"); return 0; } // find out if this color is in use for (i = 0; i < num_iff_colors; i++) { c = &temp_colors[i]; if (c->r == r && c->g == g && c->b == b) return i; } // not in use, so add a new slot idx = num_iff_colors; num_iff_colors++; // save the values c = &temp_colors[idx]; c->r = r; c->g = g; c->b = b; // init it gr_init_alphacolor(&Iff_colors[idx][0], r, g, b, iff_get_alpha_value(false)); gr_init_alphacolor(&Iff_colors[idx][1], r, g, b, iff_get_alpha_value(true)); // return the new slot return idx; } /** * Parse the table */ void iff_init() { char traitor_name[NAME_LENGTH]; char attack_names[MAX_IFFS][MAX_IFFS][NAME_LENGTH]; struct { char iff_name[NAME_LENGTH]; int color_index; } observed_color_table[MAX_IFFS][MAX_IFFS]; int num_attack_names[MAX_IFFS]; int num_observed_colors[MAX_IFFS]; int i, j, k; int string_idx; try { // Goober5000 - if table doesn't exist, use the default table if (cf_exists_full("iff_defs.tbl", CF_TYPE_TABLES)) read_file_text("iff_defs.tbl", CF_TYPE_TABLES); else read_file_text_from_default(defaults_get_file("iff_defs.tbl")); reset_parse(); // parse the table -------------------------------------------------------- required_string("#IFFs"); // get the traitor required_string("$Traitor IFF:"); stuff_string(traitor_name, F_NAME, NAME_LENGTH); int rgb[3]; // check if alternate colours are wanted to be used for these // Marks various stuff... like asteroids if ((optional_string("$Selection Color:")) || (optional_string("$Selection Colour:"))) { stuff_int_list(rgb, 3, RAW_INTEGER_TYPE); iff_init_color(rgb[0], rgb[1], rgb[2]); } else iff_init_color(0xff, 0xff, 0xff); // Marks the ship currently saying something if ((optional_string("$Message Color:")) || (optional_string("$Message Colour:"))) { stuff_int_list(rgb, 3, RAW_INTEGER_TYPE); iff_init_color(rgb[0], rgb[1], rgb[2]); } else iff_init_color(0x7f, 0x7f, 0x7f); // Marks the tagged ships if ((optional_string("$Tagged Color:")) || (optional_string("$Tagged Colour:"))) { stuff_int_list(rgb, 3, RAW_INTEGER_TYPE); iff_init_color(rgb[0], rgb[1], rgb[2]); } else iff_init_color(0xff, 0xff, 0x00); // init radar blips colour table int a_bright, a_dim; bool alternate_blip_color = false; for (i = 0; i < 5; i++) { for (j = 0; j < 2; j++) { for (k = 0; k < 3; k++) { radar_iff_color[i][j][k] = -1; } } } // if the bright/dim scaling is wanted to be changed if (optional_string("$Dimmed IFF brightness:")) { int dim_iff_brightness; stuff_int(&dim_iff_brightness); Assert(dim_iff_brightness >= 0 && dim_iff_brightness <= HUD_COLOR_ALPHA_MAX); *iff_color_brightness = dim_iff_brightness; } else *iff_color_brightness = 4; // alternate = use same method as with ship blips // retail = use 1/2 intensities if (optional_string("$Use Alternate Blip Coloring:") || optional_string("$Use Alternate Blip Colouring:")) { stuff_boolean(&alternate_blip_color); } // Parse blip colours, their order is hardcoded. if ((optional_string("$Missile Blip Color:")) || (optional_string("$Missile Blip Colour:"))) { stuff_int_list(rgb, 3, RAW_INTEGER_TYPE); for (i = 0; i < 3; i++) { Assert(rgb[i] >= 0 && rgb[i] <= 255); radar_iff_color[0][1][i] = rgb[i]; radar_iff_color[0][0][i] = rgb[i] / 2; } } if ((optional_string("$Navbuoy Blip Color:")) || (optional_string("$Navbuoy Blip Colour:"))) { stuff_int_list(rgb, 3, RAW_INTEGER_TYPE); for (i = 0; i < 3; i++) { Assert(rgb[i] >= 0 && rgb[i] <= 255); radar_iff_color[1][1][i] = rgb[i]; radar_iff_color[1][0][i] = rgb[i] / 2; } } if ((optional_string("$Warping Blip Color:")) || (optional_string("$Warping Blip Colour:"))) { stuff_int_list(rgb, 3, RAW_INTEGER_TYPE); for (i = 0; i < 3; i++) { Assert(rgb[i] >= 0 && rgb[i] <= 255); radar_iff_color[2][1][i] = rgb[i]; radar_iff_color[2][0][i] = rgb[i] / 2; } } if ((optional_string("$Node Blip Color:")) || (optional_string("$Node Blip Colour:"))) { stuff_int_list(rgb, 3, RAW_INTEGER_TYPE); for (i = 0; i < 3; i++) { Assert(rgb[i] >= 0 && rgb[i] <= 255); radar_iff_color[3][1][i] = rgb[i]; radar_iff_color[3][0][i] = rgb[i] / 2; } } if ((optional_string("$Tagged Blip Color:")) || (optional_string("$Tagged Blip Colour:"))) { stuff_int_list(rgb, 3, RAW_INTEGER_TYPE); for (i = 0; i < 3; i++) { Assert(rgb[i] >= 0 && rgb[i] <= 255); radar_iff_color[4][1][i] = rgb[i]; radar_iff_color[4][0][i] = rgb[i] / 2; } } if (alternate_blip_color == true) { a_bright = iff_get_alpha_value(true); a_dim = iff_get_alpha_value(false); for (i = 0; i < 5; i++) { if (radar_iff_color[i][0][0] >= 0) { for (j = 0; j < 3; j++) { radar_iff_color[i][0][j] = radar_iff_color[i][1][j]; } radar_iff_color[i][1][3] = a_bright; radar_iff_color[i][0][3] = a_dim; } } } else { for (i = 0; i < 5; i++) { if (radar_iff_color[i][0][0] >= 0) { radar_iff_color[i][0][3] = 255; radar_iff_color[i][1][3] = 255; } } } if (optional_string("$Radar Target ID Flags:")) { parse_string_flag_list((int*)&radar_target_id_flags, rti_flags, Num_rti_flags); if (optional_string("+reset")) radar_target_id_flags = 0; } // begin reading data Num_iffs = 0; while (required_string_either("#End", "$IFF Name:")) { iff_info *iff; int cur_iff; // make sure we're under the limit if (Num_iffs >= MAX_IFFS) { Warning(LOCATION, "Too many iffs in iffs_defs.tbl! Max is %d.\n", MAX_IFFS); skip_to_start_of_string("#End", NULL); break; } // add new IFF iff = &Iff_info[Num_iffs]; cur_iff = Num_iffs; Num_iffs++; // get required IFF info ---------------------------------------------- // get the iff name required_string("$IFF Name:"); stuff_string(iff->iff_name, F_NAME, NAME_LENGTH); // get the iff color if (check_for_string("$Colour:")) required_string("$Colour:"); else required_string("$Color:"); stuff_int_list(rgb, 3, RAW_INTEGER_TYPE); iff->color_index = iff_init_color(rgb[0], rgb[1], rgb[2]); // get relationships between IFFs ------------------------------------- // get the list of iffs attacked if (optional_string("$Attacks:")) num_attack_names[cur_iff] = stuff_string_list(attack_names[cur_iff], MAX_IFFS); else num_attack_names[cur_iff] = 0; // get the list of observed colors num_observed_colors[cur_iff] = 0; while (optional_string("+Sees")) { // get iff observed stuff_string_until(observed_color_table[cur_iff][num_observed_colors[cur_iff]].iff_name, "As:", NAME_LENGTH); required_string("As:"); // get color observed stuff_int_list(rgb, 3, RAW_INTEGER_TYPE); observed_color_table[cur_iff][num_observed_colors[cur_iff]].color_index = iff_init_color(rgb[0], rgb[1], rgb[2]); // increment num_observed_colors[cur_iff]++; } // get F3 override iff->hotkey_team = IFF_hotkey_team::Default; if (optional_string("+Hotkey Team:")) { char temp[NAME_LENGTH]; stuff_string(temp, F_NAME, NAME_LENGTH); if (!stricmp(temp, "Friendly")) iff->hotkey_team = IFF_hotkey_team::Friendly; else if (!stricmp(temp, "Hostile") || !stricmp(temp, "Enemy")) iff->hotkey_team = IFF_hotkey_team::Hostile; else if (!stricmp(temp, "None")) iff->hotkey_team = IFF_hotkey_team::None; else Warning(LOCATION, "Unrecognized +Hotkey Tean: %s\n", temp); } // get flags ---------------------------------------------------------- // get iff flags iff->flags = 0; if (optional_string("$Flags:")) { char flag_strings[MAX_IFF_FLAGS][NAME_LENGTH]; int num_strings = stuff_string_list(flag_strings, MAX_IFF_FLAGS); for (string_idx = 0; string_idx < num_strings; string_idx++) { if (!stricmp(NOX("support allowed"), flag_strings[string_idx])) iff->flags |= IFFF_SUPPORT_ALLOWED; else if (!stricmp(NOX("exempt from all teams at war"), flag_strings[string_idx])) iff->flags |= IFFF_EXEMPT_FROM_ALL_TEAMS_AT_WAR; else if (!stricmp(NOX("orders hidden"), flag_strings[string_idx])) iff->flags |= IFFF_ORDERS_HIDDEN; else if (!stricmp(NOX("orders shown"), flag_strings[string_idx])) iff->flags |= IFFF_ORDERS_SHOWN; else if (!stricmp(NOX("wing name hidden"), flag_strings[string_idx])) iff->flags |= IFFF_WING_NAME_HIDDEN; else Warning(LOCATION, "Bogus string in iff flags: %s\n", flag_strings[string_idx]); } } // get default ship flags iff->default_parse_flags.reset(); if (optional_string("$Default Ship Flags:")) { parse_string_flag_list(iff->default_parse_flags, Parse_object_flags, num_parse_object_flags, NULL); } // again, for compatibility reasons if (optional_string("$Default Ship Flags2:")) { parse_string_flag_list(iff->default_parse_flags, Parse_object_flags, num_parse_object_flags, NULL); } // this is cleared between each level but let's just set it here for thoroughness iff->ai_rearm_timestamp = timestamp(-1); } required_string("#End"); // now resolve the relationships ------------------------------------------ // first get the traitor Iff_traitor = iff_lookup(traitor_name); if (Iff_traitor < 0) { Iff_traitor = 0; Warning(LOCATION, "Traitor IFF %s not found in iff_defs.tbl! Defaulting to %s.\n", traitor_name, Iff_info[Iff_traitor].iff_name); } // next get the attackees and colors for (int cur_iff = 0; cur_iff < Num_iffs; cur_iff++) { iff_info *iff = &Iff_info[cur_iff]; // clear the iffs to be attacked iff->attackee_bitmask = 0; iff->attackee_bitmask_all_teams_at_war = 0; // clear the observed colors for (j = 0; j < MAX_IFFS; j++) iff->observed_color_index[j] = -1; // resolve the list names for (int list_index = 0; list_index < MAX_IFFS; list_index++) { // are we within the number of attackees listed? if (list_index < num_attack_names[cur_iff]) { // find out who int target_iff = iff_lookup(attack_names[cur_iff][list_index]); // valid? if (target_iff >= 0) iff->attackee_bitmask |= iff_get_mask(target_iff); else Warning(LOCATION, "Attack target IFF %s not found for IFF %s in iff_defs.tbl!\n", attack_names[cur_iff][list_index], iff->iff_name); } // are we within the number of colors listed? if (list_index < num_observed_colors[cur_iff]) { // find out who int target_iff = iff_lookup(observed_color_table[cur_iff][list_index].iff_name); // valid? if (target_iff >= 0) iff->observed_color_index[target_iff] = observed_color_table[cur_iff][list_index].color_index; else Warning(LOCATION, "Observed color IFF %s not found for IFF %s in iff_defs.tbl!\n", observed_color_table[cur_iff][list_index].iff_name, iff->iff_name); } } // resolve the all teams at war relationships if (iff->flags & IFFF_EXEMPT_FROM_ALL_TEAMS_AT_WAR) { // exempt, so use standard attacks iff->attackee_bitmask_all_teams_at_war = iff->attackee_bitmask; } else { // nonexempt, so build bitmask of all other nonexempt teams for (int other_iff = 0; other_iff < Num_iffs; other_iff++) { // skip myself (unless I attack myself normally) if ((other_iff == cur_iff) && !iff_x_attacks_y(cur_iff, cur_iff)) continue; // skip anyone exempt if (Iff_info[other_iff].flags & IFFF_EXEMPT_FROM_ALL_TEAMS_AT_WAR) continue; // add everyone else iff->attackee_bitmask_all_teams_at_war |= iff_get_mask(other_iff); } } } } catch (const parse::ParseException& e) { mprintf(("TABLES: Unable to parse '%s'! Error message = %s.\n", "iff_defs.tbl", e.what())); return; } } /** * Find the iff name * * @param iff_name Pointer to name as a string * @return Index into ::Iff_info array */ int iff_lookup(const char *iff_name) { // bogus Assert(iff_name); if(iff_name == NULL) return -1; for (int i = 0; i < Num_iffs; i++) if (!stricmp(iff_name, Iff_info[i].iff_name)) return i; return -1; } /** * Get the mask, taking All Teams At War into account * * @param attacker_team Team of attacker * @return Bitmask */ int iff_get_attackee_mask(int attacker_team) { Assert(attacker_team >= 0 && attacker_team < Num_iffs); // All teams attack all other teams. if (Mission_all_attack) { return Iff_info[attacker_team].attackee_bitmask_all_teams_at_war; } // normal else { return Iff_info[attacker_team].attackee_bitmask; } } /** * Rather slower, since it has to construct a mask * * @param attackee_team Team of attacker * @return Bitmask */ int iff_get_attacker_mask(int attackee_team) { Assert(attackee_team >= 0 && attackee_team < Num_iffs); int i, attacker_bitmask = 0; for (i = 0; i < Num_iffs; i++) { if (iff_x_attacks_y(i, attackee_team)) attacker_bitmask |= iff_get_mask(i); } return attacker_bitmask; } /** * Similar to above * * @param team_x Team of attacker * @param team_y Team of attackee */ bool iff_x_attacks_y(int team_x, int team_y) { return iff_matches_mask(team_y, iff_get_attackee_mask(team_x)); } /** * Generate a mask for a team * * @param team Team to generate mask for */ int iff_get_mask(int team) { return (1 << team); } /** * See if the mask contains the team * * @param team Team to test * @param mask Mask */ bool iff_matches_mask(int team, int mask) { return (iff_get_mask(team) & mask) != 0; } /** * Get the color from the color index */ color *iff_get_color(int color_index, int is_bright) { return &Iff_colors[color_index][is_bright]; } /** * Get the color index, taking objective vs. subjective into account */ color *iff_get_color_by_team(int team, int seen_from_team, int is_bright) { Assert(team >= 0 && team < Num_iffs); Assert(seen_from_team < Num_iffs); Assert(is_bright == 0 || is_bright == 1); // is this guy being seen by anyone? if (seen_from_team < 0) return &Iff_colors[Iff_info[team].color_index][is_bright]; // Goober5000 - base the following on "sees X as" from iff code // c.f. AL's comment: // AL 12-26-97: it seems IFF color needs to be set relative to the player team. If // the team in question is the same as the player, then it should be // drawn friendly. If the team is different than the player's, then draw the // appropriate IFF. // assume an observed color is defined; if not, use normal color int color_index = Iff_info[seen_from_team].observed_color_index[team]; if (color_index < 0) color_index = Iff_info[team].color_index; return &Iff_colors[color_index][is_bright]; } /** * Get the color index, taking objective vs. subjective into account * * this one for the function calls that include some - any - of object */ color *iff_get_color_by_team_and_object(int team, int seen_from_team, int is_bright, object *objp) { Assert(team >= 0 && team < Num_iffs); Assert(seen_from_team < Num_iffs); Assert(is_bright == 0 || is_bright == 1); int alt_color_index = -1; // is this guy being seen by anyone? if (seen_from_team < 0) return &Iff_colors[Iff_info[team].color_index][is_bright]; int color_index = Iff_info[seen_from_team].observed_color_index[team]; // switch incase some sort of parent iff color inheritance for example for bombs is wanted... switch(objp->type) { case OBJ_SHIP: if (Ships[objp->instance].ship_iff_color[seen_from_team][team] >= 0) { alt_color_index = Ships[objp->instance].ship_iff_color[seen_from_team][team]; } else { alt_color_index = Ship_info[Ships[objp->instance].ship_info_index].ship_iff_info[seen_from_team][team]; } break; default: break; } // temporary solution.... if (alt_color_index >= 0) color_index = alt_color_index; if (color_index < 0) color_index = Iff_info[team].color_index; return &Iff_colors[color_index][is_bright]; }