void opengl_tnl_set_material(material* material_info, bool set_base_map, bool set_clipping) { int shader_handle = material_info->get_shader_handle(); int base_map = material_info->get_texture_map(TM_BASE_TYPE); vec4 clr = material_info->get_color(); Assert(shader_handle >= 0); opengl_shader_set_current(shader_handle); if (material_info->has_buffer_blend_modes()) { Assertion(GLAD_GL_ARB_draw_buffers_blend != 0, "Buffer blend modes are not supported at the moment! Query the capability before using this feature."); auto enable_blend = false; for (auto i = 0; i < (int) material::NUM_BUFFER_BLENDS; ++i) { auto mode = material_info->get_blend_mode(i); GL_state.SetAlphaBlendModei(i, mode); enable_blend = enable_blend || mode != ALPHA_BLEND_NONE; } GL_state.Blend(enable_blend ? GL_TRUE : GL_FALSE); } else { GL_state.SetAlphaBlendMode(material_info->get_blend_mode()); } GL_state.SetZbufferType(material_info->get_depth_mode()); gr_set_cull(material_info->get_cull_mode() ? 1 : 0); gr_zbias(material_info->get_depth_bias()); gr_set_fill_mode(material_info->get_fill_mode()); gr_set_texture_addressing(material_info->get_texture_addressing()); if (set_clipping) { // Only set the clipping state if explicitly requested by the caller to avoid unnecessary state changes auto& clip_params = material_info->get_clip_plane(); if (!clip_params.enabled) { GL_state.ClipDistance(0, false); } else { Assertion(Current_shader != NULL && (Current_shader->shader == SDR_TYPE_MODEL || Current_shader->shader == SDR_TYPE_PASSTHROUGH_RENDER || Current_shader->shader == SDR_TYPE_DEFAULT_MATERIAL), "Clip planes are not supported by this shader!"); GL_state.ClipDistance(0, true); } } GL_state.StencilMask(material_info->get_stencil_mask()); auto& stencilFunc = material_info->get_stencil_func(); GL_state.StencilFunc(convertComparisionFunction(stencilFunc.compare), stencilFunc.ref, stencilFunc.mask); auto& frontStencilOp = material_info->get_front_stencil_op(); GL_state.StencilOpSeparate(GL_FRONT, convertStencilOp(frontStencilOp.stencilFailOperation), convertStencilOp(frontStencilOp.depthFailOperation), convertStencilOp(frontStencilOp.successOperation)); auto& backStencilOp = material_info->get_back_stencil_op(); GL_state.StencilOpSeparate(GL_BACK, convertStencilOp(backStencilOp.stencilFailOperation), convertStencilOp(backStencilOp.depthFailOperation), convertStencilOp(backStencilOp.successOperation)); GL_state.StencilTest(material_info->is_stencil_enabled() ? GL_TRUE : GL_FALSE); auto& color_mask = material_info->get_color_mask(); GL_state.ColorMask(color_mask.x, color_mask.y, color_mask.z, color_mask.w); // This is only needed for the passthrough shader uint32_t array_index = 0; if ( set_base_map && base_map >= 0 ) { float u_scale, v_scale; if ( !gr_opengl_tcache_set(base_map, material_info->get_texture_type(), &u_scale, &v_scale, &array_index) ) { mprintf(("WARNING: Error setting bitmap texture (%i)!\n", base_map)); } } if ( Current_shader->shader == SDR_TYPE_DEFAULT_MATERIAL ) { opengl_shader_set_default_material(base_map >= 0, material_info->get_texture_type() == TCACHE_TYPE_AABITMAP, &clr, material_info->get_color_scale(), array_index, material_info->get_clip_plane()); } }
void opengl_tnl_set_model_material(model_material *material_info) { float u_scale, v_scale; int render_pass = 0; opengl_tnl_set_material(material_info, false, false); if ( GL_state.CullFace() ) { GL_state.FrontFaceValue(GL_CW); } gr_set_center_alpha(material_info->get_center_alpha()); Assert( Current_shader->shader == SDR_TYPE_MODEL ); GL_state.Texture.SetShaderMode(GL_TRUE); if (GL_workaround_clipping_planes) { if (Current_shader->flags & SDR_FLAG_MODEL_CLIP) { GL_state.ClipDistance(0, true); } else { GL_state.ClipDistance(0, false); } } else { if (Current_shader->flags & SDR_FLAG_MODEL_CLIP || Current_shader->flags & SDR_FLAG_MODEL_TRANSFORM) { GL_state.ClipDistance(0, true); } else { GL_state.ClipDistance(0, false); } } uint32_t array_index; if ( Current_shader->flags & SDR_FLAG_MODEL_DIFFUSE_MAP ) { Current_shader->program->Uniforms.setUniformi("sBasemap", render_pass); gr_opengl_tcache_set(material_info->get_texture_map(TM_BASE_TYPE), TCACHE_TYPE_NORMAL, &u_scale, &v_scale, &array_index, render_pass); ++render_pass; } if ( Current_shader->flags & SDR_FLAG_MODEL_GLOW_MAP ) { Current_shader->program->Uniforms.setUniformi("sGlowmap", render_pass); gr_opengl_tcache_set(material_info->get_texture_map(TM_GLOW_TYPE), TCACHE_TYPE_NORMAL, &u_scale, &v_scale, &array_index, render_pass); ++render_pass; } if ( Current_shader->flags & SDR_FLAG_MODEL_SPEC_MAP ) { Current_shader->program->Uniforms.setUniformi("sSpecmap", render_pass); if ( material_info->get_texture_map(TM_SPEC_GLOSS_TYPE) > 0 ) { gr_opengl_tcache_set(material_info->get_texture_map(TM_SPEC_GLOSS_TYPE), TCACHE_TYPE_NORMAL, &u_scale, &v_scale, &array_index, render_pass); } else { gr_opengl_tcache_set(material_info->get_texture_map(TM_SPECULAR_TYPE), TCACHE_TYPE_NORMAL, &u_scale, &v_scale, &array_index, render_pass); } ++render_pass; if ( Current_shader->flags & SDR_FLAG_MODEL_ENV_MAP ) { Current_shader->program->Uniforms.setUniformi("sEnvmap", render_pass); gr_opengl_tcache_set(ENVMAP, TCACHE_TYPE_CUBEMAP, &u_scale, &v_scale, &array_index, render_pass); Assertion(array_index == 0, "Cube map arrays are not supported yet!"); ++render_pass; } } if ( Current_shader->flags & SDR_FLAG_MODEL_NORMAL_MAP ) { Current_shader->program->Uniforms.setUniformi("sNormalmap", render_pass); gr_opengl_tcache_set(material_info->get_texture_map(TM_NORMAL_TYPE), TCACHE_TYPE_NORMAL, &u_scale, &v_scale, &array_index, render_pass); ++render_pass; } if ( Current_shader->flags & SDR_FLAG_MODEL_HEIGHT_MAP ) { Current_shader->program->Uniforms.setUniformi("sHeightmap", render_pass); gr_opengl_tcache_set(material_info->get_texture_map(TM_HEIGHT_TYPE), TCACHE_TYPE_NORMAL, &u_scale, &v_scale, &array_index, render_pass); ++render_pass; } if ( Current_shader->flags & SDR_FLAG_MODEL_AMBIENT_MAP ) { Current_shader->program->Uniforms.setUniformi("sAmbientmap", render_pass); gr_opengl_tcache_set(material_info->get_texture_map(TM_AMBIENT_TYPE), TCACHE_TYPE_NORMAL, &u_scale, &v_scale, &array_index, render_pass); ++render_pass; } if ( Current_shader->flags & SDR_FLAG_MODEL_MISC_MAP ) { Current_shader->program->Uniforms.setUniformi("sMiscmap", render_pass); gr_opengl_tcache_set(material_info->get_texture_map(TM_MISC_TYPE), TCACHE_TYPE_NORMAL, &u_scale, &v_scale, &array_index, render_pass); ++render_pass; } if ( Current_shader->flags & SDR_FLAG_MODEL_SHADOWS ) { Current_shader->program->Uniforms.setUniformi("shadow_map", render_pass); GL_state.Texture.Enable(render_pass, GL_TEXTURE_2D_ARRAY, Shadow_map_texture); ++render_pass; // bump! } if ( Current_shader->flags & SDR_FLAG_MODEL_ANIMATED ) { Current_shader->program->Uniforms.setUniformi("sFramebuffer", render_pass); if ( Scene_framebuffer_in_frame ) { GL_state.Texture.Enable(render_pass, GL_TEXTURE_2D, Scene_effect_texture); glDrawBuffer(GL_COLOR_ATTACHMENT0); } else { GL_state.Texture.Enable(render_pass, GL_TEXTURE_2D, Framebuffer_fallback_texture_id); } ++render_pass; } if ( Current_shader->flags & SDR_FLAG_MODEL_TRANSFORM ) { Current_shader->program->Uniforms.setUniformi("transform_tex", render_pass); GL_state.Texture.Enable(render_pass, GL_TEXTURE_BUFFER, opengl_get_transform_buffer_texture()); ++render_pass; } if ( Deferred_lighting ) { // don't blend if we're drawing to the g-buffers GL_state.SetAlphaBlendMode(ALPHA_BLEND_NONE); } }
bool pilotfile::save_savefile() { char base[_MAX_FNAME] = { '\0' }; std::ostringstream buf; if (Game_mode & GM_MULTIPLAYER) { return false; } // set player ptr first thing Assert( (Player_num >= 0) && (Player_num < MAX_PLAYERS) ); p = &Players[Player_num]; if ( !strlen(Campaign.filename) ) { return false; } // build up filename for the savefile... _splitpath(Campaign.filename, NULL, NULL, base, NULL); buf << p->callsign << "." << base << ".csg"; filename = buf.str().c_str(); // make sure that we can actually save this safely if (m_data_invalid) { mprintf(("CSG => Skipping save of '%s' due to invalid data check!\n", filename.c_str())); return false; } // validate the number of red alert entries // assertion before writing so that we don't corrupt the .csg by asserting halfway through writing // assertion should also prevent loss of major campaign progress // i.e. lose one mission, not several missions worth (in theory) Assertion(Red_alert_wingman_status.size() <= MAX_SHIPS, "Invalid number of Red_alert_wingman_status entries: %u\n", Red_alert_wingman_status.size()); // open it, hopefully... cfp = cfopen((char*)filename.c_str(), "wb", CFILE_NORMAL, CF_TYPE_PLAYERS); if ( !cfp ) { mprintf(("CSG => Unable to open '%s' for saving!\n", filename.c_str())); return false; } // header and version cfwrite_int(CSG_FILE_ID, cfp); cfwrite_ubyte(CSG_VERSION, cfp); mprintf(("CSG => Saving '%s' with version %d...\n", filename.c_str(), (int)CSG_VERSION)); // flags and info sections go first mprintf(("CSG => Saving: Flags...\n")); csg_write_flags(); mprintf(("CSG => Saving: Info...\n")); csg_write_info(); // everything else is next, not order specific mprintf(("CSG => Saving: Missions...\n")); csg_write_missions(); mprintf(("CSG => Saving: Techroom...\n")); csg_write_techroom(); mprintf(("CSG => Saving: Loadout...\n")); csg_write_loadout(); mprintf(("CSG => Saving: Scoring...\n")); csg_write_stats(); mprintf(("CSG => Saving: RedAlert...\n")); csg_write_redalert(); mprintf(("CSG => Saving: HUD...\n")); csg_write_hud(); mprintf(("CSG => Saving: Variables...\n")); csg_write_variables(); mprintf(("CSG => Saving: Settings...\n")); csg_write_settings(); mprintf(("CSG => Saving: Controls...\n")); csg_write_controls(); mprintf(("CSG => Saving: Cutscenes...\n")); csg_write_cutscenes(); mprintf(("CSG => Saving: Last Missions...\n")); csg_write_lastmissions(); // Done! mprintf(("CSG => Saving complete!\n")); csg_close(); return true; }
void pilotfile::csg_read_info() { char t_string[NAME_LENGTH+1] = { '\0' }; index_list_t ilist; int idx, list_size = 0; ubyte allowed = 0; if ( !m_have_flags ) { throw "Info before Flags!"; } // // NOTE: lists may contain missing/invalid entries for current data // this is not necessarily fatal // // ship list (NOTE: may contain more than MAX_SHIP_CLASSES) list_size = cfread_int(cfp); for (idx = 0; idx < list_size; idx++) { cfread_string_len(t_string, NAME_LENGTH, cfp); ilist.name = t_string; ilist.index = ship_info_lookup(t_string); ship_list.push_back(ilist); } // weapon list (NOTE: may contain more than MAX_WEAPON_TYPES) list_size = cfread_int(cfp); for (idx = 0; idx < list_size; idx++) { cfread_string_len(t_string, NAME_LENGTH, cfp); ilist.name = t_string; ilist.index = weapon_info_lookup(t_string); weapon_list.push_back(ilist); } // intel list (NOTE: may contain more than MAX_INTEL_ENTRIES) list_size = cfread_int(cfp); for (idx = 0; idx < list_size; idx++) { cfread_string_len(t_string, NAME_LENGTH, cfp); ilist.name = t_string; ilist.index = intel_info_lookup(t_string); intel_list.push_back(ilist); } // medals list (NOTE: may contain more than Num_medals) list_size = cfread_int(cfp); for (idx = 0; idx < list_size; idx++) { cfread_string_len(t_string, NAME_LENGTH, cfp); ilist.name = t_string; ilist.index = medals_info_lookup(t_string); medals_list.push_back(ilist); } // last ship flown (index into ship_list) idx = cfread_int(cfp); // check the idx is within bounds Assertion ((idx < (int)ship_list.size()), "Campaign file contains an incorrect value for the last flown ship class. No data in ship_list for ship number %d.", idx); if (idx >= (int)ship_list.size()) idx = -1; else if (idx != -1) p->last_ship_flown_si_index = ship_list[idx].index; else p->last_ship_flown_si_index = -1; // progression state Campaign.prev_mission = cfread_int(cfp); Campaign.next_mission = cfread_int(cfp); // loop state Campaign.loop_reentry = cfread_int(cfp); Campaign.loop_enabled = cfread_int(cfp); // missions completed Campaign.num_missions_completed = cfread_int(cfp); // allowed ships list_size = (int)ship_list.size(); for (idx = 0; idx < list_size; idx++) { allowed = cfread_ubyte(cfp); if (allowed) { if (ship_list[idx].index >= 0) { Campaign.ships_allowed[ship_list[idx].index] = 1; } else { m_data_invalid = true; } } } // allowed weapons list_size = (int)weapon_list.size(); for (idx = 0; idx < list_size; idx++) { allowed = cfread_ubyte(cfp); if (allowed) { if (weapon_list[idx].index >= 0) { Campaign.weapons_allowed[weapon_list[idx].index] = 1; } else { m_data_invalid = true; } } } if (csg_ver >= 2) { // single/campaign squad name & image cfread_string_len(p->s_squad_name, NAME_LENGTH, cfp); cfread_string_len(p->s_squad_filename, MAX_FILENAME_LEN, cfp); } // if anything we need/use was missing then it should be considered fatal if (m_data_invalid) { throw "Invalid data for CSG!"; } }
SCP_string get_frame_profile_output() { Assertion(frameProfiler, "Frame profiling must be enabled for this function!"); return frameProfiler->getContent(); }
/*! * \brief Throw a DataTransferKit::Assertion. * * \param cond A string containing the assertion condition that failed. * * \param field A string containing the file name in which the assertion * failed. * * \param line The line number at which the assertion failed. */ void throwAssertion( const std::string& cond, const std::string& file, const int line ) { throw Assertion( cond, file, line ); }
int batch_add_polygon(int texture, int tmap_flags, vec3d *pos, matrix *orient, float width, float height, float alpha) { //idiot-proof if(width == 0 || height == 0) return 0; Assert(pos != NULL); Assert(orient != NULL); //Let's begin. const int NUM_VERTICES = 4; vec3d p[NUM_VERTICES] = { ZERO_VECTOR }; vertex v[NUM_VERTICES] = { vertex() }; p[0].xyz.x = width; p[0].xyz.y = height; p[1].xyz.x = -width; p[1].xyz.y = height; p[2].xyz.x = -width; p[2].xyz.y = -height; p[3].xyz.x = width; p[3].xyz.y = -height; for(int i = 0; i < NUM_VERTICES; i++) { vec3d tmp = vmd_zero_vector; //Rotate correctly vm_vec_unrotate(&tmp, &p[i], orient); //Move to point in space vm_vec_add2(&tmp, pos); //Convert to vertex g3_transfer_vertex(&v[i], &tmp); } v[0].texture_position.u = 1.0f; v[0].texture_position.v = 0.0f; v[1].texture_position.u = 0.0f; v[1].texture_position.v = 0.0f; v[2].texture_position.u = 0.0f; v[2].texture_position.v = 1.0f; v[3].texture_position.u = 1.0f; v[3].texture_position.v = 1.0f; if (texture < 0) { Int3(); return 1; } batch_item *item = NULL; SCP_map<int, batch_item>::iterator it = geometry_map.find(texture); if ( !geometry_map.empty() && it != geometry_map.end() ) { item = &it->second; } else { item = &geometry_map[texture]; item->texture = texture; } Assertion( (item->laser == false), "Particle effect %s used as laser glow or laser bitmap\n", bm_get_filename(texture) ); item->tmap_flags = tmap_flags; item->alpha = alpha; item->batch.add_allocate(1); item->batch.draw_quad(v); return 0; }
void frame_profile_process_frame() { Assertion(frameProfiler, "Frame profiling must be enabled for this function!"); return frameProfiler->processFrame(); }
// loads nframes bitmaps, starting at index start_frame. // anything < start_frame will not be loaded. // this keeps the loading code from trying to load bitmaps which don't exist // and taking an unnecessary disk hit. int UI_GADGET::set_bmaps(char *ani_fname, int nframes, int start_frame) { int first_frame, i; char full_name[MAX_FILENAME_LEN] = ""; char tmp[33]; int idx, s_idx; int num_digits; int its_all_good = 0; // clear out all frames for(idx=0; idx<MAX_BMAPS_PER_GADGET; idx++){ bmap_ids[idx] = -1; } // load all the bitmaps bm_filename = ani_fname; Assertion(nframes < MAX_BMAPS_PER_GADGET, "Too many frames specified (%d), must be less than MAX_BMAPS_PER_GADGET", nframes); m_num_frames = nframes; for(idx=start_frame; idx<nframes; idx++){ // clear the string strcpy_s(full_name, ""); // get the # of digits for this index num_digits = (idx < 10) ? 1 : (idx < 100) ? 2 : (idx < 1000) ? 3 : 4; // build the actual filename strcpy_s(full_name, ani_fname); for(s_idx=0; s_idx<(4-num_digits); s_idx++){ strcat_s(full_name, NOX("0")); } sprintf(tmp, "%d", idx); strcat_s(full_name, tmp); // try and load the bitmap bmap_ids[idx] = bm_load(full_name); if(bmap_ids[idx] != -1){ its_all_good = 1; } } // done if(its_all_good){ uses_bmaps = 1; return 0; } // no go, so try and load as an ani. try and load as an .ani first_frame = bm_load_animation(ani_fname, &m_num_frames); if((first_frame >= 0) && (m_num_frames <= MAX_BMAPS_PER_GADGET)){ // seems pretty stupid that we didn't just use a variable for the first frame and access all // other frames offset from it instead of accessing this bmap_ids[] array, but probably too // much trouble to go through and change this anymore. How sad.. for ( i=0; i<m_num_frames; i++ ) { bmap_ids[i] = first_frame + i; } } // flag that this control is using bitmaps for art uses_bmaps = 1; return 0; }
void credits_parse_table(const char* filename) { try { read_file_text(filename, CF_TYPE_TABLES); reset_parse(); // any metadata? if (optional_string("$Music:")) { stuff_string(Credits_music_name, F_NAME, NAME_LENGTH); } if (optional_string("$Number of Images:")) { int temp; stuff_int(&temp); if (temp > 0) Credits_num_images = temp; } if (optional_string("$Start Image Index:")) { stuff_int(&Credits_artwork_index); // bounds check if (Credits_artwork_index < 0) { Credits_artwork_index = 0; } else if (Credits_artwork_index >= Credits_num_images) { Credits_artwork_index = Credits_num_images - 1; } } if (optional_string("$Text scroll rate:")) { stuff_float(&Credits_scroll_rate); if (Credits_scroll_rate < 0.01f) Credits_scroll_rate = 0.01f; } if (optional_string("$Artworks display time:")) { stuff_float(&Credits_artwork_display_time); if (Credits_artwork_display_time < 0.01f) Credits_artwork_display_time = 0.01f; } if (optional_string("$Artworks fade time:")) { stuff_float(&Credits_artwork_fade_time); if (Credits_artwork_fade_time < 0.01f) Credits_artwork_fade_time = 0.01f; } if (optional_string("$SCP Credits position:")) { char mode[NAME_LENGTH]; stuff_string(mode, F_NAME, NAME_LENGTH); if (!stricmp(mode, "Start")) SCP_credits_position = START; else if (!stricmp(mode, "End")) SCP_credits_position = END; else Warning(LOCATION, "Unknown credits position mode \"%s\".", mode); } ignore_white_space(); SCP_string credits_text; SCP_string line; SCP_vector<int> charNum; SCP_vector<const char*> lines; int numLines = -1; bool first_run = true; while (!check_for_eof_raw() && !check_for_string_raw("#end")) { // Read in a line of text stuff_string_line(line); // This is a bit odd but it means if a total conversion uses different credits the // Volition credit won't happen // Also don't append the default credits anymore when there was already a parsed table if (first_run && !Credits_parsed && !line.compare(mod_check)) { credits_text.append(unmodified_credits); } first_run = false; if (line.empty()) { // If the line is empty then just append a newline, don't bother with splitting it first credits_text.append("\n"); } else { // split_str doesn't take care of this. charNum.clear(); // Split the string into multiple lines if it's too long numLines = split_str(line.c_str(), Credits_text_coords[gr_screen.res][2], charNum, lines, -1); // Make sure that we have valid data Assertion(lines.size() == (size_t)numLines, "split_str reported %d lines but vector contains " SIZE_T_ARG " entries!", numLines, lines.size()); Assertion(lines.size() <= charNum.size(), "Something has gone wrong while splitting strings. Got " SIZE_T_ARG " lines but only " SIZE_T_ARG " chacter lengths.", lines.size(), charNum.size()); // Now add all splitted lines to the credit text and append a newline to the end for (int i = 0; i < numLines; i++) { credits_text.append(SCP_string(lines[i], charNum[i])); credits_text.append("\n"); } } } Credit_text_parts.push_back(credits_text); Credits_parsed = true; } catch (const parse::ParseException& e) { mprintf(("TABLES: Unable to parse '%s'! Error message = %s.\n", filename, e.what())); return; } }
// ******************************************************************************************** // Engages autopilot // This does: // * Control switched from player to AI // * Time compression to 32x // * Lock time compression -WMC // * Tell AI to fly to targeted Nav Point (for all nav-status wings/ships) // * Sets max waypoint speed to the best-speed of the slowest ship tagged bool StartAutopilot() { // Check for support ship and dismiss it if it is not doing anything. // If the support ship is doing something then tell the user such. for ( object *objp = GET_FIRST(&obj_used_list); objp !=END_OF_LIST(&obj_used_list); objp = GET_NEXT(objp) ) { if ((objp->type == OBJ_SHIP) && !(objp->flags & OF_SHOULD_BE_DEAD)) { Assertion((objp->instance >= 0) && (objp->instance < MAX_SHIPS), "objp does not have a valid pointer to a ship. Pointer is %d, which is smaller than 0 or bigger than %d", objp->instance, MAX_SHIPS); ship *shipp = &Ships[objp->instance]; if (shipp->team != Player_ship->team) continue; Assertion((shipp->ship_info_index >= 0) && (shipp->ship_info_index < MAX_SHIP_CLASSES), "Ship '%s' does not have a valid pointer to a ship class. Pointer is %d, which is smaller than 0 or bigger than %d", shipp->ship_name, shipp->ship_info_index, MAX_SHIP_CLASSES); ship_info *sip = &Ship_info[shipp->ship_info_index]; if ( !(sip->flags & SIF_SUPPORT) ) continue; // don't deal with dying or departing support ships if ( shipp->flags & (SF_DYING | SF_DEPARTING) ) continue; Assert(shipp->ai_index != -1); ai_info* support_ship_aip = &(Ai_info[Ships[objp->instance].ai_index]); // is support ship trying to rearm-repair if ( ai_find_goal_index( support_ship_aip->goals, AI_GOAL_REARM_REPAIR ) == -1 ) { // no, so tell it to depart ai_add_ship_goal_player( AIG_TYPE_PLAYER_SHIP, AI_GOAL_WARP, -1, NULL, support_ship_aip ); } else { // yes send_autopilot_msgID(NP_MSG_FAIL_SUPPORT_WORKING); return false; } } } if (!CanAutopilot()) return false; AutoPilotEngaged = true; // find the ship that is "leading" all of the ships when the player starts // autopilot // by default the ship that is leading the autopilot session the player's // wing leader (if the player is the wing leader then it will be the // player). // TODO:implement a way to allow a FREDer to say a different ship is leader Autopilot_flight_leader = get_wing_leader(Player_ship->wingnum); if ( Autopilot_flight_leader == NULL ) { // force player to be the leader if he doesn't have a wing Autopilot_flight_leader = Player_obj; } if (The_mission.flags & MISSION_FLAG_USE_AP_CINEMATICS) LockAPConv = timestamp(); // lock convergence instantly else LockAPConv = timestamp(3000); // 3 seconds before we lock convergence Player_use_ai = 1; set_time_compression(1); lock_time_compression(true); // determine speed cap int i,j, wcount=1, tc_factor=1; float speed_cap = 1000000.0; // 1m is a safe starting point float radius = Player_obj->radius, distance = 0.0f, ftemp; bool capshipPresent = false; int capship_counts[3]; // three size classes capship_counts[0] = 0; capship_counts[1] = 0; capship_counts[2] = 0; int capship_placed[3]; // three size classes capship_placed[0] = 0; capship_placed[1] = 0; capship_placed[2] = 0; float capship_spreads[3]; capship_spreads[0] = 0.0f; capship_spreads[1] = 0.0f; capship_spreads[2] = 0.0f; SCP_vector<int> capIndexes; // empty the autopilot wings map autopilot_wings.clear(); // vars for usage w/ cinematic vec3d pos, norm1, perp, tpos, rpos = Player_obj->pos, zero; memset(&zero, 0, sizeof(vec3d)); // instantly turn player toward tpos if (The_mission.flags & MISSION_FLAG_USE_AP_CINEMATICS) { vm_vec_sub(&norm1, Navs[CurrentNav].GetPosition(), &Player_obj->pos); vm_vector_2_matrix(&Player_obj->orient, &norm1, NULL, NULL); } for (i = 0; i < MAX_SHIPS; i++) { if (Ships[i].objnum != -1 && (Ships[i].flags2 & SF2_NAVPOINT_CARRY || (Ships[i].wingnum != -1 && Wings[Ships[i].wingnum].flags & WF_NAV_CARRY) ) ) { if (speed_cap > vm_vec_mag(&Ship_info[Ships[i].ship_info_index].max_vel)) speed_cap = vm_vec_mag(&Ship_info[Ships[i].ship_info_index].max_vel); } } // damp speed_cap to 90% of actual -- to make sure ships stay in formation if (The_mission.flags & MISSION_FLAG_USE_AP_CINEMATICS) speed_cap = 0.90f * speed_cap; if ( speed_cap < 1.0f ) { /* We need to deal with this so that incorrectly flagged ships will not cause the engine to fail to limit all the ships speeds correctly. */ Warning(LOCATION, "Ship speed cap is way too small (%f)!\n" "This is normally caused by a ship that has nav-carry-status set, but cannot move itself (like a Cargo container).\n" "Speed cap has been set to 1.0 m/s.", speed_cap); speed_cap = 1.0f; } ramp_bias = speed_cap/50.0f; // assign ship goals // when assigning goals to individual ships only do so if Ships[shipnum].wingnum != -1 // we will assign wing goals below for (i = 0; i < MAX_SHIPS; i++) { if (Ships[i].objnum != -1 && (Ships[i].flags2 & SF2_NAVPOINT_CARRY || (Ships[i].wingnum != -1 && Wings[Ships[i].wingnum].flags & WF_NAV_CARRY) ) ) { // do we have capital ships in the area? if (Ship_info[Ships[i].ship_info_index].flags & ( SIF_CRUISER | SIF_CAPITAL | SIF_SUPERCAP | SIF_CORVETTE | SIF_AWACS | SIF_GAS_MINER | SIF_FREIGHTER | SIF_TRANSPORT)) { capshipPresent = true; capIndexes.push_back(i); // ok.. what size class if (Ship_info[Ships[i].ship_info_index].flags & (SIF_CAPITAL | SIF_SUPERCAP)) { capship_counts[0]++; if (capship_spreads[0] < Objects[Ships[i].objnum].radius) capship_spreads[0] = Objects[Ships[i].objnum].radius; } else if (Ship_info[Ships[i].ship_info_index].flags & (SIF_CORVETTE)) { capship_counts[1]++; if (capship_spreads[1] < Objects[Ships[i].objnum].radius) capship_spreads[1] = Objects[Ships[i].objnum].radius; } else { capship_counts[2]++; if (capship_spreads[2] < Objects[Ships[i].objnum].radius) capship_spreads[2] = Objects[Ships[i].objnum].radius; } } // check for bigger radius for usage later /*if (!vm_vec_cmp(&rpos, &Player_obj->pos)) // want to make sure rpos isn't player pos - we can worry about it being largest object's later { rpos = Objects[Ships[i].objnum].pos; }*/ if (Objects[Ships[i].objnum].radius > radius) { rpos = Objects[Ships[i].objnum].pos; radius = Objects[Ships[i].objnum].radius; } if (The_mission.flags & MISSION_FLAG_USE_AP_CINEMATICS) {// instantly turn the ship to match the direction player is looking //vm_vec_sub(&norm1, Navs[CurrentNav].GetPosition(), &Player_obj->pos); vm_vector_2_matrix(&Objects[Ships[i].objnum].orient, &norm1, NULL, NULL); } // snap wings into formation if (The_mission.flags & MISSION_FLAG_USE_AP_CINEMATICS && // only if using cinematics (Ships[i].wingnum != -1 && Wings[Ships[i].wingnum].flags & WF_NAV_CARRY) // only if in a wing && Autopilot_flight_leader != &Objects[Ships[i].objnum]) //only if not flight leader's object { ai_info *aip = &Ai_info[Ships[i].ai_index]; int wingnum = aip->wing, wing_index = get_wing_index(&Objects[Ships[i].objnum], wingnum); vec3d goal_point; object *leader_objp = get_wing_leader(wingnum); if (leader_objp != &Objects[Ships[i].objnum]) { // not leader.. get our position relative to leader get_absolute_wing_pos_autopilot(&goal_point, leader_objp, wing_index, aip->ai_flags & AIF_FORMATION_OBJECT); } else { ai_clear_wing_goals(wingnum); j = 1+int( (float)floor(double(wcount-1)/2.0) ); switch (wcount % 2) { case 1: // back-left vm_vec_add(&perp, &zero, &Autopilot_flight_leader->orient.vec.rvec); //vm_vec_sub(&perp, &perp, &Player_obj->orient.vec.fvec); vm_vec_normalize(&perp); vm_vec_scale(&perp, -166.0f*j); // 166m is supposedly the optimal range according to tolwyn vm_vec_add(&goal_point, &Autopilot_flight_leader->pos, &perp); break; default: //back-right case 0: vm_vec_add(&perp, &zero, &Autopilot_flight_leader->orient.vec.rvec); //vm_vec_sub(&perp, &perp, &Player_obj->orient.vec.fvec); vm_vec_normalize(&perp); vm_vec_scale(&perp, 166.0f*j); vm_vec_add(&goal_point, &Autopilot_flight_leader->pos, &perp); break; } autopilot_wings[wingnum] = wcount; wcount++; } Objects[Ships[i].objnum].pos = goal_point; if (vm_vec_dist_quick(&Autopilot_flight_leader->pos, &Objects[Ships[i].objnum].pos) > distance) { distance = vm_vec_dist_quick(&Autopilot_flight_leader->pos, &Objects[Ships[i].objnum].pos); } } // lock primary and secondary weapons if ( LockWeaponsDuringAutopilot ) Ships[i].flags2 |= (SF2_PRIMARIES_LOCKED | SF2_SECONDARIES_LOCKED); // clear the ship goals and cap the waypoint speed ai_clear_ship_goals(&Ai_info[Ships[i].ai_index]); Ai_info[Ships[i].ai_index].waypoint_speed_cap = (int)speed_cap; // if they're not part of a wing set their goal if (Ships[i].wingnum == -1 || The_mission.flags & MISSION_FLAG_USE_AP_CINEMATICS) { if (Navs[CurrentNav].flags & NP_WAYPOINT) { ai_add_ship_goal_player( AIG_TYPE_PLAYER_SHIP, AI_GOAL_WAYPOINTS_ONCE, 0, ((waypoint_list*)Navs[CurrentNav].target_obj)->get_name(), &Ai_info[Ships[i].ai_index] ); //fixup has to wait until after wing goals } else { ai_add_ship_goal_player( AIG_TYPE_PLAYER_SHIP, AI_GOAL_FLY_TO_SHIP, 0, ((ship*)Navs[CurrentNav].target_obj)->ship_name, &Ai_info[Ships[i].ai_index] ); } } } } // assign wing goals if (!(The_mission.flags & MISSION_FLAG_USE_AP_CINEMATICS)) { for (i = 0; i < MAX_WINGS; i++) { if (Wings[i].flags & WF_NAV_CARRY ) { //ai_add_ship_goal_player( int type, int mode, int submode, char *shipname, ai_info *aip ); //ai_add_wing_goal_player( AIG_TYPE_PLAYER_WING, AI_GOAL_STAY_NEAR_SHIP, 0, target_shipname, wingnum ); //ai_add_wing_goal_player( AIG_TYPE_PLAYER_WING, AI_GOAL_WAYPOINTS_ONCE, 0, target_shipname, wingnum ); //ai_clear_ship_goals( &(Ai_info[Ships[num].ai_index]) ); ai_clear_wing_goals( i ); if (Navs[CurrentNav].flags & NP_WAYPOINT) { ai_add_wing_goal_player( AIG_TYPE_PLAYER_WING, AI_GOAL_WAYPOINTS_ONCE, 0, ((waypoint_list*)Navs[CurrentNav].target_obj)->get_name(), i ); // "fix up" the goal for (j = 0; j < MAX_AI_GOALS; j++) { if (Wings[i].ai_goals[j].ai_mode == AI_GOAL_WAYPOINTS_ONCE || Wings[i].ai_goals[j].ai_mode == AIM_WAYPOINTS ) { autopilot_ai_waypoint_goal_fixup(&(Wings[i].ai_goals[j])); } } } else { ai_add_wing_goal_player( AIG_TYPE_PLAYER_WING, AI_GOAL_FLY_TO_SHIP, 0, ((ship*)Navs[CurrentNav].target_obj)->ship_name, i ); } } } } // fixup has to go down here because ships are assigned goals during wing goals as well for (i = 0; i < MAX_SHIPS; i++) { if (Ships[i].objnum != -1) { if (Ships[i].flags2 & SF2_NAVPOINT_CARRY || (Ships[i].wingnum != -1 && Wings[Ships[i].wingnum].flags & WF_NAV_CARRY)) for (j = 0; j < MAX_AI_GOALS; j++) { if (Ai_info[Ships[i].ai_index].goals[j].ai_mode == AI_GOAL_WAYPOINTS_ONCE || Ai_info[Ships[i].ai_index].goals[j].ai_mode == AIM_WAYPOINTS) { autopilot_ai_waypoint_goal_fixup( &(Ai_info[Ships[i].ai_index].goals[j]) ); // formation fixup //ai_form_on_wing(&Objects[Ships[i].objnum], &Objects[Player_ship->objnum]); } } } } start_dist = DistanceTo(CurrentNav); // ----------------------------- setup cinematic ----------------------------- if (The_mission.flags & MISSION_FLAG_USE_AP_CINEMATICS) { if (capshipPresent) { // position capships vec3d right, front, up, offset; for (SCP_vector<int>::iterator idx = capIndexes.begin(); idx != capIndexes.end(); ++idx) { vm_vec_add(&right, &Autopilot_flight_leader->orient.vec.rvec, &zero); vm_vec_add(&front, &Autopilot_flight_leader->orient.vec.fvec, &zero); vm_vec_add(&up, &Autopilot_flight_leader->orient.vec.uvec, &zero); vm_vec_add(&offset, &zero, &zero); if (Ship_info[Ships[*idx].ship_info_index].flags & (SIF_CAPITAL | SIF_SUPERCAP)) { //0 - below - three lines of position // front/back to zero vm_vec_add(&front, &zero, &zero); // position below vm_vec_scale(&up, capship_spreads[0]); // scale the up vector by the radius of the largest ship in this formation part switch (capship_placed[0] % 3) { case 1: // right vm_vec_scale(&right, capship_spreads[0]); break; case 2: // left vm_vec_scale(&right, -capship_spreads[0]); break; default: // straight case 0: vm_vec_add(&right, &zero, &zero); vm_vec_scale(&up, 1.5); // add an extra half-radius break; } // scale by row vm_vec_scale(&right, (1+((float)floor((float)capship_placed[0]/3)))); vm_vec_scale(&up, -(1+((float)floor((float)capship_placed[0]/3)))); capship_placed[0]++; } else if (Ship_info[Ships[*idx].ship_info_index].flags & SIF_CORVETTE) { //1 above - 3 lines of position // front/back to zero vm_vec_add(&front, &zero, &zero); // position below vm_vec_scale(&up, capship_spreads[1]); // scale the up vector by the radius of the largest ship in this formation part switch (capship_placed[1] % 3) { case 1: // right vm_vec_scale(&right, capship_spreads[1]); break; case 2: // left vm_vec_scale(&right, -capship_spreads[1]); break; default: // straight case 0: vm_vec_add(&right, &zero, &zero); vm_vec_scale(&up, 1.5); // add an extra half-radius break; } // scale by row vm_vec_scale(&right, (1+((float)floor((float)capship_placed[1]/3)))); vm_vec_scale(&up, (1+((float)floor((float)capship_placed[1]/3)))); // move ourselves up and out of the way of the smaller ships vm_vec_add(&perp, &Autopilot_flight_leader->orient.vec.uvec, &zero); vm_vec_scale(&perp, capship_spreads[2]); vm_vec_add(&up, &up, &perp); capship_placed[1]++; } else { //2 either side - 6 lines of position (right (dir, front, back), left (dir, front, back) ) // placing pattern: right, left, front right, front left, rear right, rear left // up/down to zero vm_vec_add(&up, &zero, &zero); switch (capship_placed[2] % 6) { case 5: // rear left vm_vec_scale(&right, -capship_spreads[2]); vm_vec_scale(&front, -capship_spreads[2]); break; case 4: // rear right vm_vec_scale(&right, capship_spreads[2]); vm_vec_scale(&front, -capship_spreads[2]); break; case 3: // front left vm_vec_scale(&right, -capship_spreads[2]); vm_vec_scale(&front, capship_spreads[2]); break; case 2: // front right vm_vec_scale(&right, capship_spreads[2]); vm_vec_scale(&front, capship_spreads[2]); break; case 1: // straight left vm_vec_scale(&right, 1.5); vm_vec_scale(&right, -capship_spreads[2]); vm_vec_add(&front, &zero, &zero); break; default: // straight right case 0: vm_vec_scale(&right, 1.5); vm_vec_scale(&right, capship_spreads[2]); vm_vec_add(&front, &zero, &zero); break; } // these ships seem to pack a little too tightly vm_vec_scale(&right, 2*(1+((float)floor((float)capship_placed[2]/3)))); vm_vec_scale(&front, 2*(1+((float)floor((float)capship_placed[2]/3)))); // move "out" by 166*(wcount-1) so we don't bump into fighters vm_vec_add(&perp, &Autopilot_flight_leader->orient.vec.rvec, &zero); vm_vec_scale(&perp, 166.0f*float(wcount-1)); if ( (capship_placed[2] % 2) == 0) vm_vec_add(&right, &right, &perp); else vm_vec_sub(&right, &right, &perp); capship_placed[2]++; } // integrate the up/down componant vm_vec_add(&offset, &offset, &up); //integrate the left/right componant vm_vec_add(&offset, &offset, &right); //integrate the left/right componant vm_vec_add(&offset, &offset, &front); // global scale the position by 50% //vm_vec_scale(&offset, 1.5); vm_vec_add(&Objects[Ships[*idx].objnum].pos, &Autopilot_flight_leader->pos, &offset); if (vm_vec_dist_quick(&Autopilot_flight_leader->pos, &Objects[Ships[*idx].objnum].pos) > distance) { distance = vm_vec_dist_quick(&Autopilot_flight_leader->pos, &Objects[Ships[*idx].objnum].pos); } } } ftemp = floor(Autopilot_flight_leader->phys_info.max_vel.xyz.z/speed_cap); if (ftemp >= 2.0f && ftemp < 4.0f) tc_factor = 2; else if (ftemp >= 4.0f && ftemp < 8.0f) tc_factor = 4; else if (ftemp >= 8.0f) tc_factor = 8; tpos = *Navs[CurrentNav].GetPosition(); // determine distance toward nav at which camera will be vm_vec_sub(&pos, &tpos, &Autopilot_flight_leader->pos); vm_vec_normalize(&pos); // pos is now a unit vector in the direction we will be moving the camera //norm1 = pos; vm_vec_scale(&pos, 5*speed_cap*tc_factor); // pos is now scaled by 5 times the speed (5 seconds ahead) vm_vec_add(&pos, &pos, &Autopilot_flight_leader->pos); // pos is now 5*speed cap in front of player ship switch (myrand()%24) // 8 different ways of getting perp points // 4 of which will not be used when capships are present (anything below, or straight above) { case 1: // down case 9: case 16: if (capship_placed[0] == 0) vm_vec_sub(&perp, &zero, &Autopilot_flight_leader->orient.vec.uvec); else { // become up-left vm_vec_add(&perp, &zero, &Autopilot_flight_leader->orient.vec.uvec); vm_vec_sub(&perp, &perp, &Autopilot_flight_leader->orient.vec.rvec); } break; case 2: // up case 10: case 23: vm_vec_add(&perp, &perp, &Autopilot_flight_leader->orient.vec.uvec); if (capshipPresent) // become up-right vm_vec_add(&perp, &perp, &Autopilot_flight_leader->orient.vec.rvec); break; case 3: // left case 11: case 22: vm_vec_sub(&perp, &zero, &Autopilot_flight_leader->orient.vec.rvec); break; case 4: // up-left case 12: case 21: vm_vec_sub(&perp, &zero, &Autopilot_flight_leader->orient.vec.rvec); vm_vec_add(&perp, &perp, &Autopilot_flight_leader->orient.vec.uvec); break; case 5: // up-right case 13: case 20: vm_vec_add(&perp, &zero, &Autopilot_flight_leader->orient.vec.rvec); vm_vec_add(&perp, &perp, &Autopilot_flight_leader->orient.vec.uvec); break; case 6: // down-left case 14: case 19: vm_vec_sub(&perp, &zero, &Autopilot_flight_leader->orient.vec.rvec); if (capship_placed[0] < 2) vm_vec_sub(&perp, &perp, &Autopilot_flight_leader->orient.vec.uvec); else vm_vec_add(&perp, &perp, &Autopilot_flight_leader->orient.vec.uvec); break; case 7: // down-right case 15: case 18: vm_vec_add(&perp, &zero, &Autopilot_flight_leader->orient.vec.rvec); if (capship_placed[0] < 1) vm_vec_sub(&perp, &perp, &Autopilot_flight_leader->orient.vec.uvec); else vm_vec_add(&perp, &perp, &Autopilot_flight_leader->orient.vec.uvec); break; default: case 0: // right case 8: case 17: perp = Autopilot_flight_leader->orient.vec.rvec; break; } vm_vec_normalize(&perp); //vm_vec_scale(&perp, 2*radius+distance); vm_vec_scale(&perp, Autopilot_flight_leader->radius+radius); // randomly scale up/down by up to 20% j = 20-myrand()%40; // [-20,20] vm_vec_scale(&perp, 1.0f+(float(j)/100.0f)); vm_vec_add(&cameraPos, &pos, &perp); if (capshipPresent) { vm_vec_normalize(&perp); // place it behind //vm_vec_copy_scale(&norm1, &Player_obj->orient.vec.fvec, -2*(Player_obj->radius+radius*(1.0f+(float(j)/100.0f)))); //vm_vec_add(&cameraTarget, &cameraTarget, &norm1); vm_vec_copy_scale(&cameraTarget,&perp, radius/5.0f); //vm_vec_scale(&cameraTarget, Player_obj->radius+radius*(1.0f+(float(j)/100.0f))); //vm_vec_add(&cameraTarget, &pos, &cameraTarget); //CameraSpeed = (radius+distance)/25; //vm_vec_add(&cameraTarget, &zero, &perp); //vm_vec_scale(&CameraVelocity, (radius+distance/100.f)); //vm_vec_scale(&CameraVelocity, 1.0f/float(NPS_TICKRATE*tc_factor)); } else { vm_vec_add(&cameraTarget, &zero, &zero); //CameraSpeed = 0; } //CameraMoving = false; EndAPCinematic = timestamp((10000*tc_factor)+NPS_TICKRATE); // 10 objective seconds before end of cinematic MoveCamera = timestamp((5500*tc_factor)+NPS_TICKRATE); camMovingTime = int(4.5*float(tc_factor)); set_time_compression((float)tc_factor); } return true; }
void fs2netd_options_config_init() { if (PXO_options_loaded) { return; } if ( !strlen(Multi_options_g.game_tracker_ip) ) { ml_printf("NOTICE: Address for game tracker not specified, using default instead (%s).", FS2NETD_DEFAULT_SERVER); strncpy( Multi_options_g.game_tracker_ip, FS2NETD_DEFAULT_SERVER, MULTI_OPTIONS_STRING_LEN ); } else if ( !strcmp("gt.pxo.net", Multi_options_g.game_tracker_ip) ) { ml_printf("NOTICE: Incompatible game tracker IP detected (gt.pxo.net), using default instead (%s)!", FS2NETD_DEFAULT_SERVER); strncpy( Multi_options_g.game_tracker_ip, FS2NETD_DEFAULT_SERVER, MULTI_OPTIONS_STRING_LEN ); } if ( !strlen(Multi_options_g.user_tracker_ip) ) { ml_printf("NOTICE: Address for user tracker not specified, using default instead (%s).", FS2NETD_DEFAULT_SERVER); strncpy( Multi_options_g.user_tracker_ip, FS2NETD_DEFAULT_SERVER, MULTI_OPTIONS_STRING_LEN ); } else if ( !strcmp("ut.pxo.net", Multi_options_g.user_tracker_ip) ) { ml_printf("NOTICE: Incompatible user tracker IP detected (ut.pxo.net), using default instead (%s)!", FS2NETD_DEFAULT_SERVER); strncpy( Multi_options_g.user_tracker_ip, FS2NETD_DEFAULT_SERVER, MULTI_OPTIONS_STRING_LEN ); } if ( !strlen(Multi_options_g.tracker_port) ) { if ( FS2NetD_port >= 1024 && FS2NetD_port <= USHRT_MAX ) { ml_printf("NOTICE: User override for game/user tracker port not specified, using game_settings.tbl override (%i).", FS2NetD_port); int result; result = sprintf(Multi_options_g.tracker_port, "%i", FS2NetD_port); Assertion( result > 0, "Copying port %i to tracker_port failed\n", FS2NetD_port ); } else { if ( FS2NetD_port != 0 ) { ml_printf("ERROR: game_settings.tbl override for game/user tracker port '%i' must be between %i and %i.", FS2NetD_port, 1024, USHRT_MAX); } ml_printf("NOTICE: Port for game/user trackers not specified, using default instead (%s).", FS2NETD_DEFAULT_PORT); strncpy( Multi_options_g.tracker_port, FS2NETD_DEFAULT_PORT, STD_NAME_LEN ); } } else { long port_tmp = strtol(Multi_options_g.tracker_port, (char**)NULL, 10); if ( (port_tmp < 1024) || (port_tmp > USHRT_MAX) ) { ml_printf("NOTICE: The port specified for game/user trackers, '%ld', is outside of the required range, %i through %i!", port_tmp, 1024, USHRT_MAX); ml_printf("NOTICE: Port for game/user trackers is invalid, using default instead (%s).", FS2NETD_DEFAULT_PORT); strncpy( Multi_options_g.tracker_port, FS2NETD_DEFAULT_PORT, STD_NAME_LEN ); } } if ( !strlen(Multi_options_g.pxo_ip) ) { ml_printf("NOTICE: Address for chat server not specified, using default instead (%s).", FS2NETD_DEFAULT_CHAT_SERVER); strncpy( Multi_options_g.pxo_ip, FS2NETD_DEFAULT_CHAT_SERVER, MULTI_OPTIONS_STRING_LEN ); } else if ( !strcmp("chat.pxo.net", Multi_options_g.pxo_ip) ) { ml_printf("NOTICE: Incompatible chat server IP detected (chat.pxo.net), using default instead (%s)!", FS2NETD_DEFAULT_CHAT_SERVER); strncpy( Multi_options_g.pxo_ip, FS2NETD_DEFAULT_CHAT_SERVER, MULTI_OPTIONS_STRING_LEN ); } if ( !strlen(Multi_options_g.pxo_banner_url) ) { ml_printf("NOTICE: URL for banners not specified, using default instead (%s).", FS2NETD_DEFAULT_BANNER_URL); strncpy( Multi_options_g.pxo_banner_url, FS2NETD_DEFAULT_BANNER_URL, MULTI_OPTIONS_STRING_LEN ); } else if ( !strcmp("http://www.pxo.net/files/banners", Multi_options_g.pxo_banner_url) ) { ml_printf("NOTICE: Incompatible banner URL detected (chat.pxo.net), using default instead (%s)!", FS2NETD_DEFAULT_BANNER_URL); strncpy( Multi_options_g.pxo_banner_url, FS2NETD_DEFAULT_BANNER_URL, MULTI_OPTIONS_STRING_LEN ); } PXO_options_loaded = true; }
void pilotfile::update_stats_backout(scoring_struct *stats, bool training) { int i, j; uint idx; size_t list_size; index_list_t ilist; scoring_special_t *p_stats = NULL; if (Game_mode & GM_MULTIPLAYER) { p_stats = &multi_stats; } else { p_stats = &all_time_stats; } // medals if (stats->m_medal_earned >= 0) { list_size = p_stats->medals_earned.size(); j = -1; for (idx = 0; idx < list_size; idx++) { if ( p_stats->medals_earned[idx].name.compare(Medals[stats->m_medal_earned].name) == 0 ) { j = idx; break; } } if (j >= 0) { p_stats->medals_earned[j].val = MAX(0,p_stats->medals_earned[j].val--); } else { Assertion(true, "Medal '%s' not found, should have been added by pilotfile::update_stats.", Medals[stats->m_medal_earned].name); } } // only medals can be awarded in training missions if (training) { return; } p_stats->score -= stats->m_score; p_stats->assists -= stats->m_assists; p_stats->kill_count -= stats->m_kill_count; p_stats->kill_count_ok -= stats->m_kill_count_ok; p_stats->bonehead_kills -= stats->m_bonehead_kills; p_stats->p_shots_fired -= stats->mp_shots_fired; p_stats->p_shots_hit -= stats->mp_shots_hit; p_stats->p_bonehead_hits -= stats->mp_bonehead_hits; p_stats->s_shots_fired -= stats->ms_shots_fired; p_stats->s_shots_hit -= stats->ms_shots_hit; p_stats->s_bonehead_hits -= stats->ms_bonehead_hits; p_stats->flight_time -= (unsigned int)f2i(Missiontime); p_stats->last_flown = p_stats->last_backup; p_stats->missions_flown--; if (stats->m_promotion_earned >= 0) { // deal with a multi-rank promotion mission for (i = 0; i < MAX_FREESPACE2_RANK; ++i) { if (p_stats->score <= Ranks[i].points) { p_stats->rank = i-1; break; } } Assertion (p_stats->rank >= 0, "Rank became negative."); } // badges if (stats->m_badge_earned.size()) { list_size = p_stats->medals_earned.size(); for (size_t medal = 0; medal < stats->m_badge_earned.size(); medal++) { j = -1; for (idx = 0; idx < list_size; idx++) { if ( p_stats->medals_earned[idx].name.compare(Medals[stats->m_badge_earned[medal]].name) == 0 ) { j = idx; break; } } if (j >= 0) { p_stats->medals_earned[j].val = 0; } else { Assertion (false, "Badge '%s' not found, should have been added by pilotfile::update_stats.", Medals[stats->m_badge_earned[medal]].name); } } } // ship kills i = 0; for (auto it = Ship_info.cbegin(); it != Ship_info.cend(); i++, ++it) { if (stats->m_okKills[i] > 0) { list_size = p_stats->ship_kills.size(); j = -1; for (idx = 0; idx < list_size; idx++) { if ( p_stats->ship_kills[idx].name.compare(it->name) == 0 ) { j = idx; break; } } if (j >= 0) { p_stats->ship_kills[j].val -= stats->m_okKills[i]; } else { Assertion(false, "Ship kills of '%s' not found, should have been added by pilotfile::update_stats.", Ship_info[i].name); } } } }