void parse_ai_profiles_tbl(const char *filename) { int i; char profile_name[NAME_LENGTH]; ai_profile_t dummy_profile; char *saved_Mp = NULL; char buf[NAME_LENGTH]; try { if (filename == NULL) read_file_text_from_default(defaults_get_file("ai_profiles.tbl")); else read_file_text(filename, CF_TYPE_TABLES); reset_parse(); // start parsing required_string("#AI Profiles"); // new default? if (optional_string("$Default Profile:")) stuff_string(Default_profile_name, F_NAME, NAME_LENGTH); // begin reading data while (required_string_either("#End", "$Profile Name:")) { ai_profile_t *profile = &dummy_profile; ai_profile_t *previous_profile = NULL; bool no_create = false; // get the name required_string("$Profile Name:"); stuff_string(profile_name, F_NAME, NAME_LENGTH); // see if it exists for (i = 0; i < Num_ai_profiles; i++) { if (!stricmp(Ai_profiles[i].profile_name, profile_name)) { previous_profile = &Ai_profiles[i]; break; } } // modular table stuff if (optional_string("+nocreate")) { no_create = true; // use the previous one if possible, // otherwise continue to use the dummy one if (previous_profile != NULL) profile = previous_profile; } else { // don't create multiple profiles with the same name if (previous_profile != NULL) { Warning(LOCATION, "An ai profile named '%s' already exists! The new one will not be created.\n", profile_name); } else { // make sure we're under the limit if (Num_ai_profiles >= MAX_AI_PROFILES) { Warning(LOCATION, "Too many profiles in ai_profiles.tbl! Max is %d.\n", MAX_AI_PROFILES - 1); // -1 because one is built-in skip_to_string("#End", NULL); break; } profile = &Ai_profiles[Num_ai_profiles]; Num_ai_profiles++; } } // initialize profile if we're not building from a previously parsed one if (!no_create) { // base profile, so zero it out if (profile == &Ai_profiles[0]) { profile->reset(); } // brand new profile, so set it to the base defaults else { *profile = Ai_profiles[0]; } } // set the name strcpy_s(profile->profile_name, profile_name); // fill in any and all settings; they're all optional and can be in any order while (!check_for_string("$Profile Name:") && !check_for_string("#End")) { if (optional_string("$Player Afterburner Recharge Scale:")) parse_float_list(profile->afterburner_recharge_scale, NUM_SKILL_LEVELS); if (optional_string("$Max Beam Friendly Fire Damage:")) parse_float_list(profile->beam_friendly_damage_cap, NUM_SKILL_LEVELS); if (optional_string("$Player Countermeasure Life Scale:")) parse_float_list(profile->cmeasure_life_scale, NUM_SKILL_LEVELS); if (optional_string("$AI Countermeasure Firing Chance:")) parse_float_list(profile->cmeasure_fire_chance, NUM_SKILL_LEVELS); if (optional_string("$AI In Range Time:")) parse_float_list(profile->in_range_time, NUM_SKILL_LEVELS); if (optional_string("$AI Always Links Ammo Weapons:")) parse_float_list(profile->link_ammo_levels_always, NUM_SKILL_LEVELS); if (optional_string("$AI Maybe Links Ammo Weapons:")) parse_float_list(profile->link_ammo_levels_maybe, NUM_SKILL_LEVELS); if (optional_string("$Primary Ammo Burst Multiplier:")) parse_float_list(profile->primary_ammo_burst_mult, NUM_SKILL_LEVELS); if (optional_string("$AI Always Links Energy Weapons:")) parse_float_list(profile->link_energy_levels_always, NUM_SKILL_LEVELS); if (optional_string("$AI Maybe Links Energy Weapons:")) parse_float_list(profile->link_energy_levels_maybe, NUM_SKILL_LEVELS); if (optional_string("$Max Missles Locked on Player:") || optional_string("$Max Missiles Locked on Player:")) parse_int_list(profile->max_allowed_player_homers, NUM_SKILL_LEVELS); if (optional_string("$Max Player Attackers:")) parse_int_list(profile->max_attackers, NUM_SKILL_LEVELS); if (optional_string("$Max Incoming Asteroids:")) parse_int_list(profile->max_incoming_asteroids, NUM_SKILL_LEVELS); if (optional_string("$Player Damage Factor:") || optional_string("$AI Damage Reduction to Player Hull:")) parse_float_list(profile->player_damage_scale, NUM_SKILL_LEVELS); if (optional_string("$Player Subsys Damage Factor:") || optional_string("$AI Damage Reduction to Player Subsys:")) parse_float_list(profile->subsys_damage_scale, NUM_SKILL_LEVELS); // represented in fractions of F1_0 if (optional_string("$Predict Position Delay:")) { int iLoop; float temp_list[NUM_SKILL_LEVELS]; parse_float_list(temp_list, NUM_SKILL_LEVELS); for (iLoop = 0; iLoop < NUM_SKILL_LEVELS; iLoop++) profile->predict_position_delay[iLoop] = fl2f(temp_list[iLoop]); } if (optional_string("$AI Shield Manage Delay:") || optional_string("$AI Shield Manage Delays:")) parse_float_list(profile->shield_manage_delay, NUM_SKILL_LEVELS); if (optional_string("$Friendly AI Fire Delay Scale:")) parse_float_list(profile->ship_fire_delay_scale_friendly, NUM_SKILL_LEVELS); if (optional_string("$Hostile AI Fire Delay Scale:")) parse_float_list(profile->ship_fire_delay_scale_hostile, NUM_SKILL_LEVELS); if (optional_string("$Friendly AI Secondary Fire Delay Scale:")) parse_float_list(profile->ship_fire_secondary_delay_scale_friendly, NUM_SKILL_LEVELS); if (optional_string("$Hostile AI Secondary Fire Delay Scale:")) parse_float_list(profile->ship_fire_secondary_delay_scale_hostile, NUM_SKILL_LEVELS); if (optional_string("$AI Turn Time Scale:")) parse_float_list(profile->turn_time_scale, NUM_SKILL_LEVELS); if (optional_string("$Glide Attack Percent:")) { parse_float_list(profile->glide_attack_percent, NUM_SKILL_LEVELS); //Percent is nice for modders, but here in the code we want it betwwen 0 and 1.0 //While we're at it, verify the range for (i = 0; i < NUM_SKILL_LEVELS; i++) { if (profile->glide_attack_percent[i] < 0.0f || profile->glide_attack_percent[i] > 100.0f) { Warning(LOCATION, "$Glide Attack Percent should be between 0 and 100.0 (read %f). Setting to 0.", profile->glide_attack_percent[i]); profile->glide_attack_percent[i] = 0.0f; } profile->glide_attack_percent[i] /= 100.0; } } if (optional_string("$Circle Strafe Percent:")) { parse_float_list(profile->circle_strafe_percent, NUM_SKILL_LEVELS); //Percent is nice for modders, but here in the code we want it betwwen 0 and 1.0 //While we're at it, verify the range for (i = 0; i < NUM_SKILL_LEVELS; i++) { if (profile->circle_strafe_percent[i] < 0.0f || profile->circle_strafe_percent[i] > 100.0f) { Warning(LOCATION, "$Circle Strafe Percent should be between 0 and 100.0 (read %f). Setting to 0.", profile->circle_strafe_percent[i]); profile->circle_strafe_percent[i] = 0.0f; } profile->circle_strafe_percent[i] /= 100.0; } } if (optional_string("$Glide Strafe Percent:")) { parse_float_list(profile->glide_strafe_percent, NUM_SKILL_LEVELS); //Percent is nice for modders, but here in the code we want it betwwen 0 and 1.0 //While we're at it, verify the range for (i = 0; i < NUM_SKILL_LEVELS; i++) { if (profile->glide_strafe_percent[i] < 0.0f || profile->glide_strafe_percent[i] > 100.0f) { Warning(LOCATION, "$Glide Strafe Percent should be between 0 and 100.0 (read %f). Setting to 0.", profile->glide_strafe_percent[i]); profile->glide_strafe_percent[i] = 0.0f; } profile->glide_strafe_percent[i] /= 100.0; } } if (optional_string("$Random Sidethrust Percent:")) { parse_float_list(profile->random_sidethrust_percent, NUM_SKILL_LEVELS); //Percent is nice for modders, but here in the code we want it betwwen 0 and 1.0 //While we're at it, verify the range for (i = 0; i < NUM_SKILL_LEVELS; i++) { if (profile->random_sidethrust_percent[i] < 0.0f || profile->random_sidethrust_percent[i] > 100.0f) { Warning(LOCATION, "$Random Sidethrust Percent should be between 0 and 100.0 (read %f). Setting to 0.", profile->random_sidethrust_percent[i]); profile->random_sidethrust_percent[i] = 0.0f; } profile->random_sidethrust_percent[i] /= 100.0; } } if (optional_string("$Stalemate Time Threshold:")) parse_float_list(profile->stalemate_time_thresh, NUM_SKILL_LEVELS); if (optional_string("$Stalemate Distance Threshold:")) parse_float_list(profile->stalemate_dist_thresh, NUM_SKILL_LEVELS); if (optional_string("$Player Shield Recharge Scale:")) parse_float_list(profile->shield_energy_scale, NUM_SKILL_LEVELS); if (optional_string("$Player Weapon Recharge Scale:")) parse_float_list(profile->weapon_energy_scale, NUM_SKILL_LEVELS); if (optional_string("$Max Turret Target Ownage:")) parse_int_list(profile->max_turret_ownage_target, NUM_SKILL_LEVELS); if (optional_string("$Max Turret Player Ownage:")) parse_int_list(profile->max_turret_ownage_player, NUM_SKILL_LEVELS); if (optional_string("$Percentage Required For Kill Scale:")) parse_float_list(profile->kill_percentage_scale, NUM_SKILL_LEVELS); if (optional_string("$Percentage Required For Assist Scale:")) parse_float_list(profile->assist_percentage_scale, NUM_SKILL_LEVELS); if (optional_string("$Percentage Awarded For Capship Assist:")) parse_float_list(profile->assist_award_percentage_scale, NUM_SKILL_LEVELS); if (optional_string("$Repair Penalty:")) parse_int_list(profile->repair_penalty, NUM_SKILL_LEVELS); if (optional_string("$Delay Before Allowing Bombs to Be Shot Down:")) parse_float_list(profile->delay_bomb_arm_timer, NUM_SKILL_LEVELS); if (optional_string("$Chance AI Has to Fire Missiles at Player:")) parse_int_list(profile->chance_to_use_missiles_on_plr, NUM_SKILL_LEVELS); if (optional_string("$Max Aim Update Delay:")) parse_float_list(profile->max_aim_update_delay, NUM_SKILL_LEVELS); if (optional_string("$Turret Max Aim Update Delay:")) parse_float_list(profile->turret_max_aim_update_delay, NUM_SKILL_LEVELS); if (optional_string("$Player Autoaim FOV:")) { float fov_list[NUM_SKILL_LEVELS]; parse_float_list(fov_list, NUM_SKILL_LEVELS); for (i = 0; i < NUM_SKILL_LEVELS; i++) { //Enforce range if (fov_list[i] < 0.0f || fov_list[i] >= 360.0f) { Warning(LOCATION, "$Player Autoaim FOV should be >= 0 and < 360.0 (read %f). Setting to 0.", fov_list[i]); fov_list[i] = 0.0f; } //Convert units profile->player_autoaim_fov[i] = fov_list[i] * PI / 180.0f; } } if (optional_string("$Detail Distance Multiplier:")) parse_float_list(profile->detail_distance_mult, MAX_DETAIL_LEVEL + 1); set_flag(profile, "$big ships can attack beam turrets on untargeted ships:", AI::Profile_Flags::Big_ships_can_attack_beam_turrets_on_untargeted_ships); set_flag(profile, "$smart primary weapon selection:", AI::Profile_Flags::Smart_primary_weapon_selection); set_flag(profile, "$smart secondary weapon selection:", AI::Profile_Flags::Smart_secondary_weapon_selection); set_flag(profile, "$smart shield management:", AI::Profile_Flags::Smart_shield_management); set_flag(profile, "$smart afterburner management:", AI::Profile_Flags::Smart_afterburner_management); set_flag(profile, "$free afterburner use:", AI::Profile_Flags::Free_afterburner_use); set_flag(profile, "$allow rapid secondary dumbfire:", AI::Profile_Flags::Allow_rapid_secondary_dumbfire); set_flag(profile, "$huge turret weapons ignore bombs:", AI::Profile_Flags::Huge_turret_weapons_ignore_bombs); set_flag(profile, "$don't insert random turret fire delay:", AI::Profile_Flags::Dont_insert_random_turret_fire_delay); set_flag(profile, "$hack improve non-homing swarm turret fire accuracy:", AI::Profile_Flags::Hack_improve_non_homing_swarm_turret_fire_accuracy); set_flag(profile, "$shockwaves damage small ship subsystems:", AI::Profile_Flags::Shockwaves_damage_small_ship_subsystems); set_flag(profile, "$navigation subsystem governs warpout capability:", AI::Profile_Flags::Navigation_subsys_governs_warp); set_flag(profile, "$ignore lower bound for minimum speed of docked ship:", AI::Profile_Flags::No_min_dock_speed_cap); set_flag(profile, "$disable linked fire penalty:", AI::Profile_Flags::Disable_linked_fire_penalty); set_flag(profile, "$disable weapon damage scaling:", AI::Profile_Flags::Disable_weapon_damage_scaling); set_flag(profile, "$use additive weapon velocity:", AI::Profile_Flags::Use_additive_weapon_velocity); set_flag(profile, "$use newtonian dampening:", AI::Profile_Flags::Use_newtonian_dampening); set_flag(profile, "$include beams for kills and assists:", AI::Profile_Flags::Include_beams_in_stat_calcs); set_flag(profile, "$score kills based on damage caused:", AI::Profile_Flags::Kill_scoring_scales_with_damage); set_flag(profile, "$score assists based on damage caused:", AI::Profile_Flags::Assist_scoring_scales_with_damage); set_flag(profile, "$allow event and goal scoring in multiplayer:", AI::Profile_Flags::Allow_multi_event_scoring); set_flag(profile, "$fix linked primary weapon decision bug:", AI::Profile_Flags::Fix_linked_primary_bug); set_flag(profile, "$prevent turrets targeting too distant bombs:", AI::Profile_Flags::Prevent_targeting_bombs_beyond_range); set_flag(profile, "$smart subsystem targeting for turrets:", AI::Profile_Flags::Smart_subsystem_targeting_for_turrets); set_flag(profile, "$fix heat seekers homing on stealth ships bug:", AI::Profile_Flags::Fix_heat_seeker_stealth_bug); set_flag(profile, "$multi allow empty primaries:", AI::Profile_Flags::Multi_allow_empty_primaries); set_flag(profile, "$multi allow empty secondaries:", AI::Profile_Flags::Multi_allow_empty_secondaries); set_flag(profile, "$allow turrets target weapons freely:", AI::Profile_Flags::Allow_turrets_target_weapons_freely); set_flag(profile, "$use only single fov for turrets:", AI::Profile_Flags::Use_only_single_fov_for_turrets); set_flag(profile, "$allow vertical dodge:", AI::Profile_Flags::Allow_vertical_dodge); set_flag(profile, "$force beam turrets to use normal fov:", AI::Profile_Flags::Force_beam_turret_fov); set_flag(profile, "$fix ai class bug:", AI::Profile_Flags::Fix_ai_class_bug); set_flag(profile, "$turrets ignore targets radius in range checks:", AI::Profile_Flags::Turrets_ignore_target_radius); set_flag(profile, "$no extra collision avoidance vs player:", AI::Profile_Flags::No_special_player_avoid); set_flag(profile, "$perform fewer checks for death screams:", AI::Profile_Flags::Perform_fewer_scream_checks); set_flag(profile, "$advanced turret fov edge checks:", AI::Profile_Flags::Advanced_turret_fov_edge_checks); set_flag(profile, "$require turrets to have target in fov:", AI::Profile_Flags::Require_turret_to_have_target_in_fov); set_flag(profile, "$all ships manage shields:", AI::Profile_Flags::All_ships_manage_shields); set_flag(profile, "$ai aims from ship center:", AI::Profile_Flags::Ai_aims_from_ship_center); set_flag(profile, "$allow primary link at mission start:", AI::Profile_Flags::Allow_primary_link_at_start); set_flag(profile, "$allow beams to damage bombs:", AI::Profile_Flags::Beams_damage_weapons); set_flag(profile, "$disable weapon damage scaling for player:", AI::Profile_Flags::Player_weapon_scale_fix); set_flag(profile, "$countermeasures affect aspect seekers:", AI::Profile_Flags::Aspect_lock_countermeasure); set_flag(profile, "$ai guards specific ship in wing:", AI::Profile_Flags::Ai_guards_specific_ship_in_wing); profile->ai_path_mode = AI_PATH_MODE_NORMAL; if (optional_string("$ai path mode:")) { stuff_string(buf, F_NAME, NAME_LENGTH); int j = ai_path_type_match(buf); if (j >= 0) { profile->ai_path_mode = j; } else { Warning(LOCATION, "Invalid ai path mode '%s' specified", buf); } } set_flag(profile, "$no warp camera:", AI::Profile_Flags::No_warp_camera); set_flag(profile, "$fix ai path order bug:", AI::Profile_Flags::Fix_ai_path_order_bug); set_flag(profile, "$strict turret-tagged-only targeting:", AI::Profile_Flags::Strict_turred_tagged_only_targeting); set_flag(profile, "$aspect bomb invulnerability fix:", AI::Profile_Flags::Aspect_invulnerability_fix); set_flag(profile, "$glide decay requires thrust:", AI::Profile_Flags::Glide_decay_requires_thrust); set_flag(profile, "$ai can slow down when attacking big ships:", AI::Profile_Flags::Ai_can_slow_down_attacking_big_ships); set_flag(profile, "$use actual primary range:", AI::Profile_Flags::Use_actual_primary_range); profile->bay_arrive_speed_mult = 1.0f; profile->bay_depart_speed_mult = 1.0f; if (optional_string("$bay arrive speed multiplier:")) { stuff_float(&profile->bay_arrive_speed_mult); } if (optional_string("$bay depart speed multiplier:")) { stuff_float(&profile->bay_depart_speed_mult); } // ---------- // compatibility if (optional_string("$perform less checks for death screams:")) { mprintf(("Warning: \"$perform less checks for death screams\" flag is deprecated in favor of \"$perform fewer checks for death screams\"\n")); bool temp; stuff_boolean(&temp); profile->flags.set(AI::Profile_Flags::Perform_fewer_scream_checks, temp); } if (optional_string("$allow primary link delay:")) { mprintf(("Warning: \"$allow primary link delay\" flag is deprecated in favor of \"$allow primary link at mission start\"\n")); bool temp; stuff_boolean(&temp); profile->flags.set(AI::Profile_Flags::Allow_primary_link_at_start, !temp); } // if we've been through once already and are at the same place, force a move if (saved_Mp && (saved_Mp == Mp)) { char tmp[60]; memset(tmp, 0, 60); strncpy(tmp, Mp, 59); mprintf(("WARNING: Unrecognized parameter in ai_profiles: %s\n", tmp)); Mp++; } // find next valid option if (!skip_to_start_of_string_either("$", "#")) break; saved_Mp = Mp; } } required_string("#End"); } catch (const parse::ParseException& e) { mprintf(("TABLES: Unable to parse '%s'! Error message = %s.\n", (filename) ? filename : "<default ai_profiles.tbl>", e.what())); return; } // add tbl/tbm to multiplayer validation list extern void fs2netd_add_table_validation(const char *tblname); fs2netd_add_table_validation(filename); }
void parse_species_tbl(const char *filename) { int i, rval; char species_name[NAME_LENGTH]; if ((rval = setjmp(parse_abort)) != 0) { mprintf(("TABLES: Unable to parse '%s'! Error code = %i.\n", (filename) ? filename : NOX("<default species_defs.tbl>"), rval)); return; } if (filename == NULL) read_file_text_from_array(defaults_get_file("species_defs.tbl")); else read_file_text(filename, CF_TYPE_TABLES); reset_parse(); // start parsing required_string("#SPECIES DEFS"); // no longer required: counted automatically if (optional_string("$NumSpecies:")) { int temp; stuff_int(&temp); } // begin reading data while (required_string_either("#END","$Species_Name:")) { bool no_create = false; species_info *species, new_species; species = &new_species; // Start Species - Get its name required_string("$Species_Name:"); stuff_string(species_name, F_NAME, NAME_LENGTH); if (optional_string("+nocreate")) { no_create = true; for (i = 0; i < (int)Species_info.size(); i++) { if (!stricmp(Species_info[i].species_name, species_name)) { species = &Species_info[i]; break; } } } else { strcpy_s(species->species_name, species_name); } // Goober5000 - IFF if (optional_string("$Default IFF:")) { bool iff_found = false; char temp_name[NAME_LENGTH]; stuff_string(temp_name, F_NAME, NAME_LENGTH); // search for it in iffs for (int iLoop = 0; iLoop < Num_iffs; iLoop++) { if (!stricmp(Iff_info[iLoop].iff_name, temp_name)) { species->default_iff = iLoop; iff_found = true; } } if (!iff_found) { species->default_iff = 0; Warning(LOCATION, "Species %s default IFF %s not found in iff_defs.tbl! Defaulting to %s.\n", species->species_name, temp_name, Iff_info[species->default_iff].iff_name); } } else if (!no_create) { // we have no idea which it could be, so default to 0 species->default_iff = 0; // let them know Warning(LOCATION, "$Default IFF not specified for species %s in species_defs.tbl! Defaulting to %s.\n", species->species_name, Iff_info[species->default_iff].iff_name); } // Goober5000 - FRED color if (optional_string("$FRED Color:") || optional_string("$FRED Colour:")) { stuff_int_list(species->fred_color.a1d, 3, RAW_INTEGER_TYPE); } else if (!no_create) { // set defaults to Volition's originals if (!stricmp(species->species_name, "Terran")) { species->fred_color.rgb.r = 0; species->fred_color.rgb.g = 0; species->fred_color.rgb.b = 192; } else if (!stricmp(species->species_name, "Vasudan")) { species->fred_color.rgb.r = 0; species->fred_color.rgb.g = 128; species->fred_color.rgb.b = 0; } else if (!stricmp(species->species_name, "Shivan")) { species->fred_color.rgb.r = 192; species->fred_color.rgb.g = 0; species->fred_color.rgb.b = 0; } else if (!stricmp(species->species_name, "Ancients") || !stricmp(species->species_name, "Ancient")) { species->fred_color.rgb.r = 192; species->fred_color.rgb.g = 0; species->fred_color.rgb.b = 192; } else { species->fred_color.rgb.r = 0; species->fred_color.rgb.g = 0; species->fred_color.rgb.b = 0; } // let them know Warning(LOCATION, "$FRED Color not specified for species %s in species_defs.tbl! Defaulting to (%d, %d, %d).\n", species->species_name, species->fred_color.rgb.r, species->fred_color.rgb.g, species->fred_color.rgb.b); } // stuff optional_string("$MiscAnims:"); // Get its Debris Texture if ((!no_create && required_string("+Debris_Texture:")) || optional_string("+Debris_Texture:")) { generic_bitmap_init(&species->debris_texture, NULL); stuff_string(species->debris_texture.filename, F_NAME, MAX_FILENAME_LEN); } // Shield Hit Animation if (optional_string("+Shield_Hit_ani:")) // Shouldn't be required -- LPine { generic_anim_init(&species->shield_anim, NULL); stuff_string(species->shield_anim.filename, F_NAME, MAX_FILENAME_LEN); } else if (!no_create) { species->shield_anim.filename[0] = '\0'; species->shield_anim.first_frame = -1; // Landmine to trip up anyone who does end up using this } // Thruster Anims parse_thrust_anims(species, no_create); // Thruster Glow Anims parse_thrust_glows(species, no_create); // Goober5000 - AWACS multiplier if (optional_string("$AwacsMultiplier:")) { stuff_float(&species->awacs_multiplier); } else if (!no_create) { // set defaults to Volition's originals if (!stricmp(species->species_name, "Vasudan")) species->awacs_multiplier = 1.25f; else if (!stricmp(species->species_name, "Shivan")) species->awacs_multiplier = 1.50f; else species->awacs_multiplier = 1.0f; // let them know Warning(LOCATION, "$AwacsMultiplier not specified for species %s in species_defs.tbl! Defaulting to %.2d.\n", species->species_name, species->awacs_multiplier); } // Goober5000 - countermeasure type // (we won't be able to resolve it until after we've parsed the weapons table) if (optional_string("$Countermeasure type:")) stuff_string(species->cmeasure_name, F_NAME, NAME_LENGTH); // don't add new entry if this is just a modified one if ( !no_create ) Species_info.push_back( new_species ); } required_string("#END"); // add tbl/tbm to multiplayer validation list extern void fs2netd_add_table_validation(const char *tblname); fs2netd_add_table_validation(filename); }
/** * 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_array(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 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 = 0; if (optional_string("$Default Ship Flags:")) { i = 0; j = 0; char flag_strings[MAX_PARSE_OBJECT_FLAGS][NAME_LENGTH]; int num_strings = stuff_string_list(flag_strings, MAX_PARSE_OBJECT_FLAGS); for (i = 0; i < num_strings; i++) { for (j = 0; j < MAX_PARSE_OBJECT_FLAGS; j++) { if (!stricmp(flag_strings[i], Parse_object_flags[j])) { iff->default_parse_flags |= (1 << j); break; } } } if (j == MAX_PARSE_OBJECT_FLAGS) Warning(LOCATION, "Bogus string in iff default ship flags: %s\n", flag_strings[i]); } // again iff->default_parse_flags2 = 0; if (optional_string("$Default Ship Flags2:")) { i = 0; j = 0; char flag_strings[MAX_PARSE_OBJECT_FLAGS_2][NAME_LENGTH]; int num_strings = stuff_string_list(flag_strings, MAX_PARSE_OBJECT_FLAGS_2); for (i = 0; i < num_strings; i++) { for (j = 0; j < MAX_PARSE_OBJECT_FLAGS_2; j++) { if (!stricmp(flag_strings[i], Parse_object_flags_2[j])) { iff->default_parse_flags2 |= (1 << j); break; } } } if (j == MAX_PARSE_OBJECT_FLAGS_2) Warning(LOCATION, "Bogus string in iff default ship flags2: %s\n", flag_strings[i]); } // 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); } } } // add tbl/tbm to multiplayer validation list extern void fs2netd_add_table_validation(const char *tblname); fs2netd_add_table_validation("iff_defs.tbl"); } catch (const parse::ParseException& e) { mprintf(("TABLES: Unable to parse '%s'! Error message = %s.\n", "iff_defs.tbl", e.what())); return; } }
void script_parse_table(char* filename) { script_state* st = &Script_system; int rval; if ((rval = setjmp(parse_abort)) != 0) { mprintf(("TABLES: Unable to parse '%s'! Error code = %i.\n", filename, rval)); return; } read_file_text(filename, CF_TYPE_TABLES); reset_parse(); if (optional_string("#Global Hooks")) { //int num = 42; //Script_system.SetHookVar("Version", 'i', &num); if (optional_string("$Global:")) { Script_globalhook = st->ParseChunk("Global"); } if (optional_string("$Splash:")) { Script_splashhook = st->ParseChunk("Splash"); } if (optional_string("$GameInit:")) { Script_gameinithook = st->ParseChunk("GameInit"); } if (optional_string("$Simulation:")) { Script_simulationhook = st->ParseChunk("Simulation"); } if (optional_string("$HUD:")) { Script_hudhook = st->ParseChunk("HUD"); } required_string("#End"); } /* if(optional_string("#State Hooks")) { while(optional_string("$State:")) { char buf[NAME_LENGTH]; int idx; stuff_string(buf, F_NAME, sizeof(buf)); idx = gameseq_get_state_idx(buf); if(optional_string("$Hook:")) { if(idx > -1) { GS_state_hooks[idx] = st->ParseChunk(buf); } else { st->ParseChunk(buf); } } } required_string("#End"); } */ if (optional_string("#Conditional Hooks")) { while (st->ParseCondition(filename)); required_string("#End"); } // add tbl/tbm to multiplayer validation list extern void fs2netd_add_table_validation(char* tblname); fs2netd_add_table_validation(filename); }
void parse_ai_profiles_tbl(const char *filename) { int i; char profile_name[NAME_LENGTH]; ai_profile_t dummy_profile; char *saved_Mp = NULL; char buf[NAME_LENGTH]; try { if (filename == NULL) read_file_text_from_default(defaults_get_file("ai_profiles.tbl")); else read_file_text(filename, CF_TYPE_TABLES); reset_parse(); // start parsing required_string("#AI Profiles"); // new default? if (optional_string("$Default Profile:")) stuff_string(Default_profile_name, F_NAME, NAME_LENGTH); // begin reading data while (required_string_either("#End", "$Profile Name:")) { ai_profile_t *profile = &dummy_profile; ai_profile_t *previous_profile = NULL; bool no_create = false; // get the name required_string("$Profile Name:"); stuff_string(profile_name, F_NAME, NAME_LENGTH); // see if it exists for (i = 0; i < Num_ai_profiles; i++) { if (!stricmp(Ai_profiles[i].profile_name, profile_name)) { previous_profile = &Ai_profiles[i]; break; } } // modular table stuff if (optional_string("+nocreate")) { no_create = true; // use the previous one if possible, // otherwise continue to use the dummy one if (previous_profile != NULL) profile = previous_profile; } else { // don't create multiple profiles with the same name if (previous_profile != NULL) { Warning(LOCATION, "An ai profile named '%s' already exists! The new one will not be created.\n", profile_name); } else { // make sure we're under the limit if (Num_ai_profiles >= MAX_AI_PROFILES) { Warning(LOCATION, "Too many profiles in ai_profiles.tbl! Max is %d.\n", MAX_AI_PROFILES - 1); // -1 because one is built-in skip_to_string("#End", NULL); break; } profile = &Ai_profiles[Num_ai_profiles]; Num_ai_profiles++; } } // initialize profile if we're not building from a previously parsed one if (!no_create) { // base profile, so zero it out if (profile == &Ai_profiles[0]) { memset(profile, 0, sizeof(ai_profile_t)); } // brand new profile, so set it to the base defaults else { memcpy(profile, &Ai_profiles[0], sizeof(ai_profile_t)); } } // set the name strcpy_s(profile->profile_name, profile_name); // fill in any and all settings; they're all optional and can be in any order while (!check_for_string("$Profile Name:") && !check_for_string("#End")) { if (optional_string("$Player Afterburner Recharge Scale:")) parse_float_list(profile->afterburner_recharge_scale, NUM_SKILL_LEVELS); if (optional_string("$Max Beam Friendly Fire Damage:")) parse_float_list(profile->beam_friendly_damage_cap, NUM_SKILL_LEVELS); if (optional_string("$Player Countermeasure Life Scale:")) parse_float_list(profile->cmeasure_life_scale, NUM_SKILL_LEVELS); if (optional_string("$AI Countermeasure Firing Chance:")) parse_float_list(profile->cmeasure_fire_chance, NUM_SKILL_LEVELS); if (optional_string("$AI In Range Time:")) parse_float_list(profile->in_range_time, NUM_SKILL_LEVELS); if (optional_string("$AI Always Links Ammo Weapons:")) parse_float_list(profile->link_ammo_levels_always, NUM_SKILL_LEVELS); if (optional_string("$AI Maybe Links Ammo Weapons:")) parse_float_list(profile->link_ammo_levels_maybe, NUM_SKILL_LEVELS); if (optional_string("$Primary Ammo Burst Multiplier:")) parse_float_list(profile->primary_ammo_burst_mult, NUM_SKILL_LEVELS); if (optional_string("$AI Always Links Energy Weapons:")) parse_float_list(profile->link_energy_levels_always, NUM_SKILL_LEVELS); if (optional_string("$AI Maybe Links Energy Weapons:")) parse_float_list(profile->link_energy_levels_maybe, NUM_SKILL_LEVELS); if (optional_string("$Max Missles Locked on Player:") || optional_string("$Max Missiles Locked on Player:")) parse_int_list(profile->max_allowed_player_homers, NUM_SKILL_LEVELS); if (optional_string("$Max Player Attackers:")) parse_int_list(profile->max_attackers, NUM_SKILL_LEVELS); if (optional_string("$Max Incoming Asteroids:")) parse_int_list(profile->max_incoming_asteroids, NUM_SKILL_LEVELS); if (optional_string("$Player Damage Factor:") || optional_string("$AI Damage Reduction to Player Hull:")) parse_float_list(profile->player_damage_scale, NUM_SKILL_LEVELS); if (optional_string("$Player Subsys Damage Factor:") || optional_string("$AI Damage Reduction to Player Subsys:")) parse_float_list(profile->subsys_damage_scale, NUM_SKILL_LEVELS); // represented in fractions of F1_0 if (optional_string("$Predict Position Delay:")) { int iLoop; float temp_list[NUM_SKILL_LEVELS]; parse_float_list(temp_list, NUM_SKILL_LEVELS); for (iLoop = 0; iLoop < NUM_SKILL_LEVELS; iLoop++) profile->predict_position_delay[iLoop] = fl2f(temp_list[iLoop]); } if (optional_string("$AI Shield Manage Delay:") || optional_string("$AI Shield Manage Delays:")) parse_float_list(profile->shield_manage_delay, NUM_SKILL_LEVELS); if (optional_string("$Friendly AI Fire Delay Scale:")) parse_float_list(profile->ship_fire_delay_scale_friendly, NUM_SKILL_LEVELS); if (optional_string("$Hostile AI Fire Delay Scale:")) parse_float_list(profile->ship_fire_delay_scale_hostile, NUM_SKILL_LEVELS); if (optional_string("$Friendly AI Secondary Fire Delay Scale:")) parse_float_list(profile->ship_fire_secondary_delay_scale_friendly, NUM_SKILL_LEVELS); if (optional_string("$Hostile AI Secondary Fire Delay Scale:")) parse_float_list(profile->ship_fire_secondary_delay_scale_hostile, NUM_SKILL_LEVELS); if (optional_string("$AI Turn Time Scale:")) parse_float_list(profile->turn_time_scale, NUM_SKILL_LEVELS); if (optional_string("$Glide Attack Percent:")) { parse_float_list(profile->glide_attack_percent, NUM_SKILL_LEVELS); //Percent is nice for modders, but here in the code we want it betwwen 0 and 1.0 //While we're at it, verify the range for (i = 0; i < NUM_SKILL_LEVELS; i++) { if (profile->glide_attack_percent[i] < 0.0f || profile->glide_attack_percent[i] > 100.0f) { Warning(LOCATION, "$Glide Attack Percent should be between 0 and 100.0 (read %f). Setting to 0.", profile->glide_attack_percent[i]); profile->glide_attack_percent[i] = 0.0f; } profile->glide_attack_percent[i] /= 100.0; } } if (optional_string("$Circle Strafe Percent:")) { parse_float_list(profile->circle_strafe_percent, NUM_SKILL_LEVELS); //Percent is nice for modders, but here in the code we want it betwwen 0 and 1.0 //While we're at it, verify the range for (i = 0; i < NUM_SKILL_LEVELS; i++) { if (profile->circle_strafe_percent[i] < 0.0f || profile->circle_strafe_percent[i] > 100.0f) { Warning(LOCATION, "$Circle Strafe Percent should be between 0 and 100.0 (read %f). Setting to 0.", profile->circle_strafe_percent[i]); profile->circle_strafe_percent[i] = 0.0f; } profile->circle_strafe_percent[i] /= 100.0; } } if (optional_string("$Glide Strafe Percent:")) { parse_float_list(profile->glide_strafe_percent, NUM_SKILL_LEVELS); //Percent is nice for modders, but here in the code we want it betwwen 0 and 1.0 //While we're at it, verify the range for (i = 0; i < NUM_SKILL_LEVELS; i++) { if (profile->glide_strafe_percent[i] < 0.0f || profile->glide_strafe_percent[i] > 100.0f) { Warning(LOCATION, "$Glide Strafe Percent should be between 0 and 100.0 (read %f). Setting to 0.", profile->glide_strafe_percent[i]); profile->glide_strafe_percent[i] = 0.0f; } profile->glide_strafe_percent[i] /= 100.0; } } if (optional_string("$Random Sidethrust Percent:")) { parse_float_list(profile->random_sidethrust_percent, NUM_SKILL_LEVELS); //Percent is nice for modders, but here in the code we want it betwwen 0 and 1.0 //While we're at it, verify the range for (i = 0; i < NUM_SKILL_LEVELS; i++) { if (profile->random_sidethrust_percent[i] < 0.0f || profile->random_sidethrust_percent[i] > 100.0f) { Warning(LOCATION, "$Random Sidethrust Percent should be between 0 and 100.0 (read %f). Setting to 0.", profile->random_sidethrust_percent[i]); profile->random_sidethrust_percent[i] = 0.0f; } profile->random_sidethrust_percent[i] /= 100.0; } } if (optional_string("$Stalemate Time Threshold:")) parse_float_list(profile->stalemate_time_thresh, NUM_SKILL_LEVELS); if (optional_string("$Stalemate Distance Threshold:")) parse_float_list(profile->stalemate_dist_thresh, NUM_SKILL_LEVELS); if (optional_string("$Player Shield Recharge Scale:")) parse_float_list(profile->shield_energy_scale, NUM_SKILL_LEVELS); if (optional_string("$Player Weapon Recharge Scale:")) parse_float_list(profile->weapon_energy_scale, NUM_SKILL_LEVELS); if (optional_string("$Max Turret Target Ownage:")) parse_int_list(profile->max_turret_ownage_target, NUM_SKILL_LEVELS); if (optional_string("$Max Turret Player Ownage:")) parse_int_list(profile->max_turret_ownage_player, NUM_SKILL_LEVELS); if (optional_string("$Percentage Required For Kill Scale:")) parse_float_list(profile->kill_percentage_scale, NUM_SKILL_LEVELS); if (optional_string("$Percentage Required For Assist Scale:")) parse_float_list(profile->assist_percentage_scale, NUM_SKILL_LEVELS); if (optional_string("$Percentage Awarded For Capship Assist:")) parse_float_list(profile->assist_award_percentage_scale, NUM_SKILL_LEVELS); if (optional_string("$Repair Penalty:")) parse_int_list(profile->repair_penalty, NUM_SKILL_LEVELS); if (optional_string("$Delay Before Allowing Bombs to Be Shot Down:")) parse_float_list(profile->delay_bomb_arm_timer, NUM_SKILL_LEVELS); if (optional_string("$Chance AI Has to Fire Missiles at Player:")) parse_int_list(profile->chance_to_use_missiles_on_plr, NUM_SKILL_LEVELS); if (optional_string("$Max Aim Update Delay:")) parse_float_list(profile->max_aim_update_delay, NUM_SKILL_LEVELS); if (optional_string("$Turret Max Aim Update Delay:")) parse_float_list(profile->turret_max_aim_update_delay, NUM_SKILL_LEVELS); if (optional_string("$Player Autoaim FOV:")) { float fov_list[NUM_SKILL_LEVELS]; parse_float_list(fov_list, NUM_SKILL_LEVELS); for (i = 0; i < NUM_SKILL_LEVELS; i++) { //Enforce range if (fov_list[i] < 0.0f || fov_list[i] >= 360.0f) { Warning(LOCATION, "$Player Autoaim FOV should be >= 0 and < 360.0 (read %f). Setting to 0.", fov_list[i]); fov_list[i] = 0.0f; } //Convert units profile->player_autoaim_fov[i] = fov_list[i] * PI / 180.0f; } } if (optional_string("$Detail Distance Multiplier:")) parse_float_list(profile->detail_distance_mult, NUM_SKILL_LEVELS); set_flag(profile, "$big ships can attack beam turrets on untargeted ships:", AIPF_BIG_SHIPS_CAN_ATTACK_BEAM_TURRETS_ON_UNTARGETED_SHIPS, AIP_FLAG); set_flag(profile, "$smart primary weapon selection:", AIPF_SMART_PRIMARY_WEAPON_SELECTION, AIP_FLAG); set_flag(profile, "$smart secondary weapon selection:", AIPF_SMART_SECONDARY_WEAPON_SELECTION, AIP_FLAG); set_flag(profile, "$smart shield management:", AIPF_SMART_SHIELD_MANAGEMENT, AIP_FLAG); set_flag(profile, "$smart afterburner management:", AIPF_SMART_AFTERBURNER_MANAGEMENT, AIP_FLAG); set_flag(profile, "$allow rapid secondary dumbfire:", AIPF_ALLOW_RAPID_SECONDARY_DUMBFIRE, AIP_FLAG); set_flag(profile, "$huge turret weapons ignore bombs:", AIPF_HUGE_TURRET_WEAPONS_IGNORE_BOMBS, AIP_FLAG); set_flag(profile, "$don't insert random turret fire delay:", AIPF_DONT_INSERT_RANDOM_TURRET_FIRE_DELAY, AIP_FLAG); set_flag(profile, "$hack improve non-homing swarm turret fire accuracy:", AIPF_HACK_IMPROVE_NON_HOMING_SWARM_TURRET_FIRE_ACCURACY, AIP_FLAG); set_flag(profile, "$shockwaves damage small ship subsystems:", AIPF_SHOCKWAVES_DAMAGE_SMALL_SHIP_SUBSYSTEMS, AIP_FLAG); set_flag(profile, "$navigation subsystem governs warpout capability:", AIPF_NAVIGATION_SUBSYS_GOVERNS_WARP, AIP_FLAG); set_flag(profile, "$ignore lower bound for minimum speed of docked ship:", AIPF_NO_MIN_DOCK_SPEED_CAP, AIP_FLAG); set_flag(profile, "$disable linked fire penalty:", AIPF_DISABLE_LINKED_FIRE_PENALTY, AIP_FLAG); set_flag(profile, "$disable weapon damage scaling:", AIPF_DISABLE_WEAPON_DAMAGE_SCALING, AIP_FLAG); set_flag(profile, "$use additive weapon velocity:", AIPF_USE_ADDITIVE_WEAPON_VELOCITY, AIP_FLAG); set_flag(profile, "$use newtonian dampening:", AIPF_USE_NEWTONIAN_DAMPENING, AIP_FLAG); set_flag(profile, "$include beams for kills and assists:", AIPF_INCLUDE_BEAMS_IN_STAT_CALCS, AIP_FLAG); set_flag(profile, "$score kills based on damage caused:", AIPF_KILL_SCORING_SCALES_WITH_DAMAGE, AIP_FLAG); set_flag(profile, "$score assists based on damage caused:", AIPF_ASSIST_SCORING_SCALES_WITH_DAMAGE, AIP_FLAG); set_flag(profile, "$allow event and goal scoring in multiplayer:", AIPF_ALLOW_MULTI_EVENT_SCORING, AIP_FLAG); set_flag(profile, "$fix linked primary weapon decision bug:", AIPF_FIX_LINKED_PRIMARY_BUG, AIP_FLAG); set_flag(profile, "$prevent turrets targeting too distant bombs:", AIPF_PREVENT_TARGETING_BOMBS_BEYOND_RANGE, AIP_FLAG); set_flag(profile, "$smart subsystem targeting for turrets:", AIPF_SMART_SUBSYSTEM_TARGETING_FOR_TURRETS, AIP_FLAG); set_flag(profile, "$fix heat seekers homing on stealth ships bug:", AIPF_FIX_HEAT_SEEKER_STEALTH_BUG, AIP_FLAG); set_flag(profile, "$multi allow empty primaries:", AIPF_MULTI_ALLOW_EMPTY_PRIMARIES, AIP_FLAG); set_flag(profile, "$multi allow empty secondaries:", AIPF_MULTI_ALLOW_EMPTY_SECONDARIES, AIP_FLAG); set_flag(profile, "$allow turrets target weapons freely:", AIPF_ALLOW_TURRETS_TARGET_WEAPONS_FREELY, AIP_FLAG); set_flag(profile, "$use only single fov for turrets:", AIPF_USE_ONLY_SINGLE_FOV_FOR_TURRETS, AIP_FLAG); set_flag(profile, "$allow vertical dodge:", AIPF_ALLOW_VERTICAL_DODGE, AIP_FLAG); set_flag(profile, "$force beam turrets to use normal fov:", AIPF_FORCE_BEAM_TURRET_FOV, AIP_FLAG); set_flag(profile, "$fix ai class bug:", AIPF_FIX_AI_CLASS_BUG, AIP_FLAG); set_flag(profile, "$turrets ignore targets radius in range checks:", AIPF2_TURRETS_IGNORE_TARGET_RADIUS, AIP_FLAG2); set_flag(profile, "$no extra collision avoidance vs player:", AIPF2_NO_SPECIAL_PLAYER_AVOID, AIP_FLAG2); set_flag(profile, "$perform fewer checks for death screams:", AIPF2_PERFORM_FEWER_SCREAM_CHECKS, AIP_FLAG2); set_flag(profile, "$advanced turret fov edge checks:", AIPF2_ADVANCED_TURRET_FOV_EDGE_CHECKS, AIP_FLAG2); set_flag(profile, "$require turrets to have target in fov:", AIPF2_REQUIRE_TURRET_TO_HAVE_TARGET_IN_FOV, AIP_FLAG2); set_flag(profile, "$all ships manage shields:", AIPF2_ALL_SHIPS_MANAGE_SHIELDS, AIP_FLAG2); set_flag(profile, "$ai aims from ship center:", AIPF2_AI_AIMS_FROM_SHIP_CENTER, AIP_FLAG2); set_flag(profile, "$allow primary link at mission start:", AIPF2_ALLOW_PRIMARY_LINK_AT_START, AIP_FLAG2); set_flag(profile, "$allow beams to damage bombs:", AIPF2_BEAMS_DAMAGE_WEAPONS, AIP_FLAG2); set_flag(profile, "$disable weapon damage scaling for player:", AIPF2_PLAYER_WEAPON_SCALE_FIX, AIP_FLAG2); set_flag(profile, "$countermeasures affect aspect seekers:", AIPF2_ASPECT_LOCK_COUNTERMEASURE, AIP_FLAG2); set_flag(profile, "$ai guards specific ship in wing:", AIPF2_AI_GUARDS_SPECIFIC_SHIP_IN_WING, AIP_FLAG2); profile->ai_path_mode = AI_PATH_MODE_NORMAL; if (optional_string("$ai path mode:")) { stuff_string(buf, F_NAME, NAME_LENGTH); int j = ai_path_type_match(buf); if (j >= 0) { profile->ai_path_mode = j; } else { Warning(LOCATION, "Invalid ai path mode '%s' specified", buf); } } set_flag(profile, "$no warp camera:", AIPF2_NO_WARP_CAMERA, AIP_FLAG2); set_flag(profile, "$fix ai path order bug:", AIPF2_FIX_AI_PATH_ORDER_BUG, AIP_FLAG2); set_flag(profile, "$strict turret-tagged-only targeting:", AIPF2_STRICT_TURRET_TAGGED_ONLY_TARGETING, AIP_FLAG2); set_flag(profile, "$aspect bomb invulnerability fix:", AIPF2_ASPECT_INVULNERABILITY_FIX, AIP_FLAG2); set_flag(profile, "$glide decay requires thrust:", AIPF2_GLIDE_DECAY_REQUIRES_THRUST, AIP_FLAG2); set_flag(profile, "$ai can slow down when attacking big ships:", AIPF2_AI_CAN_SLOW_DOWN_ATTACKING_BIG_SHIPS, AIP_FLAG2); profile->bay_arrive_speed_mult = 1.0f; profile->bay_depart_speed_mult = 1.0f; if (optional_string("$bay arrive speed multiplier:")) { stuff_float(&profile->bay_arrive_speed_mult); } if (optional_string("$bay depart speed multiplier:")) { stuff_float(&profile->bay_depart_speed_mult); } // ---------- // compatibility if (optional_string("$perform less checks for death screams:")) { mprintf(("Warning: \"$perform less checks for death screams\" flag is deprecated in favor of \"$perform fewer checks for death screams\"\n")); bool temp; stuff_boolean(&temp); if (temp) profile->flags2 |= AIPF2_PERFORM_FEWER_SCREAM_CHECKS; else profile->flags2 &= ~AIPF2_PERFORM_FEWER_SCREAM_CHECKS; } if (optional_string("$allow primary link delay:")) { mprintf(("Warning: \"$allow primary link delay\" flag is deprecated in favor of \"$allow primary link at mission start\"\n")); bool temp; stuff_boolean(&temp); if (temp) profile->flags2 &= ~AIPF2_ALLOW_PRIMARY_LINK_AT_START; else profile->flags2 |= AIPF2_ALLOW_PRIMARY_LINK_AT_START; } // if we've been through once already and are at the same place, force a move if (saved_Mp && (saved_Mp == Mp)) { char tmp[60]; memset(tmp, 0, 60); strncpy(tmp, Mp, 59); mprintf(("WARNING: Unrecognized parameter in ai_profiles: %s\n", tmp)); Mp++; } // find next valid option skip_to_start_of_string_either("$", "#"); saved_Mp = Mp; } } required_string("#End"); } catch (const parse::ParseException& e) { mprintf(("TABLES: Unable to parse '%s'! Error message = %s.\n", (filename) ? filename : "<default ai_profiles.tbl>", e.what())); return; } // add tbl/tbm to multiplayer validation list extern void fs2netd_add_table_validation(const char *tblname); fs2netd_add_table_validation(filename); }