bool fs2netd_get_valid_missions()
{
	int rc = 0;

	if ( !Logged_in ) {
		return false;
	}

	FS2NetD_file_list.clear();

	do_full_packet = true;

	In_process = true;

	if (Is_standalone) {
		do { rc = fs2netd_get_valid_missions_do(); } while (!rc);
	} else {
		rc = popup_till_condition(fs2netd_get_valid_missions_do, XSTR("&Cancel", 779), XSTR("Starting mission validation", 1588));
	}

	In_process = false;
	Local_timeout = -1;

	FS2NetD_file_list.clear(); //-V586

	switch (rc) {
		// canceled by popup
		case 0:
			return false;

		// timed out
		case 1:
			if ( !Is_standalone ) {
				popup(PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, XSTR("Mission validation timed out!", 1589));
			}

			return false;

		// no missions
		case 2:
			if ( !Is_standalone ) {
				popup(PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, XSTR("No missions are available from the server for validation!", 1590));
			}

			return false;

		// out of memory
		case 3:
			if ( !Is_standalone ) {
				popup(PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, XSTR("Memory error during mission validation!", 1591));
			}

			return false;
	}

	return true;
}
void fs2netd_close()
{
	// make sure that a hosted games is de-listed
	fs2netd_gameserver_disconnect();

	FS2NetD_Disconnect();

	fs2netd_reset_state();
	PXO_options_loaded = false;

	Table_valid_status.clear();

	FS2NetD_file_list.clear();
	FS2NetD_ban_list.clear();
}
// Reset everything between levels
void particle_init()
{
	int fps;

	Particles_enabled = (Detail.num_particles > 0);

	Num_particles = 0;

	Particles.clear();

	// FIRE!!!
	if ( Anim_bitmap_id_fire == -1 )	{
		Anim_bitmap_id_fire = bm_load_animation( "particleexp01", &Anim_num_frames_fire, &fps, NULL, 0 );
	}

	// Cough, cough
	if ( Anim_bitmap_id_smoke == -1 )	{
		Anim_bitmap_id_smoke = bm_load_animation( "particlesmoke01", &Anim_num_frames_smoke, &fps, NULL, 0 );
	}

	// wheeze
	if ( Anim_bitmap_id_smoke2 == -1 )	{
		Anim_bitmap_id_smoke2 = bm_load_animation( "particlesmoke02", &Anim_num_frames_smoke2, &fps, NULL, 0 );
	}

	// grab a vertex buffer object
	if ( Particle_buffer_object < 0 ) {
		Particle_buffer_object = gr_create_stream_buffer();
	}
}
/**
 * Initializes the shader system. Creates a 1x1 texture that can be used as a fallback texture when framebuffer support is missing.
 * Also compiles the shaders used for particle rendering.
 */
void opengl_shader_init()
{
	glGenTextures(1,&Framebuffer_fallback_texture_id);
	GL_state.Texture.SetActiveUnit(0);
	GL_state.Texture.SetTarget(GL_TEXTURE_2D);
	GL_state.Texture.Enable(Framebuffer_fallback_texture_id);

	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
	GLuint pixels[4] = {0,0,0,0};
	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 1, 1, 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, &pixels);

	GL_shader.clear();

	// Reserve 32 shader slots. This should cover most use cases in real life.
	GL_shader.reserve(32);

	// compile effect shaders
	gr_opengl_maybe_create_shader(SDR_TYPE_EFFECT_PARTICLE, 0);
	gr_opengl_maybe_create_shader(SDR_TYPE_EFFECT_PARTICLE, SDR_FLAG_PARTICLE_POINT_GEN);
	gr_opengl_maybe_create_shader(SDR_TYPE_EFFECT_DISTORTION, 0);

	gr_opengl_maybe_create_shader(SDR_TYPE_SHIELD_DECAL, 0);

	// compile deferred lighting shaders
	opengl_shader_compile_deferred_light_shader();

	// compile passthrough shader
	opengl_shader_compile_passthrough_shader();

	mprintf(("\n"));
}
void snd_clear()
{
	Sounds.clear();

	// reset how much storage sounds are taking up in memory
	Snd_sram = 0;
}
/**
 * Initializes the shader system. Creates a 1x1 texture that can be used as a fallback texture when framebuffer support is missing.
 * Also compiles the shaders used for particle rendering.
 */
void opengl_shader_init()
{
	if ( !Use_GLSL ) {
		return;
	}

	glGenTextures(1,&Framebuffer_fallback_texture_id);
	GL_state.Texture.SetActiveUnit(0);
	GL_state.Texture.SetTarget(GL_TEXTURE_2D);
	GL_state.Texture.Enable(Framebuffer_fallback_texture_id);

	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
	GLuint pixels[4] = {0,0,0,0};
	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 1, 1, 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, &pixels);

	if (Cmdline_no_glsl_model_rendering) {
		Use_GLSL = 1;
	}

	GL_shader.clear();
	
	// Reserve 32 shader slots. This should cover most use cases in real life.
	GL_shader.reserve(32);

	// Compile the particle shaders, since these are most definitely going to be used
	opengl_compile_main_shader(SDR_FLAG_SOFT_QUAD);
	opengl_compile_main_shader(SDR_FLAG_SOFT_QUAD | SDR_FLAG_DISTORTION);

	mprintf(("\n"));
}
void opengl_post_process_shutdown()
{
	if ( !Post_initialized ) {
		return;
	}

	if (Post_framebuffer_id[0]) {
		glDeleteFramebuffers(1, &Post_framebuffer_id[0]);
		Post_framebuffer_id[0] = 0;

		if (Post_framebuffer_id[1]) {
			glDeleteFramebuffers(1, &Post_framebuffer_id[1]);
			Post_framebuffer_id[1] = 0;
		}
	}

	Post_effects.clear();

	opengl_post_process_shutdown_bloom();

	Post_in_frame = false;
	Post_active_shader_index = 0;

	Post_initialized = 0;
}
/**
 * Go through GL_shader and call glDeleteObject() for all created shaders, then clear GL_shader
 */
void opengl_shader_shutdown()
{
	size_t i;

	if ( !is_minimum_GLSL_version() ) {
		return;
	}

	for (i = 0; i < GL_shader.size(); i++) {
		if (GL_shader[i].program_id) {
			vglDeleteObjectARB(GL_shader[i].program_id);
			GL_shader[i].program_id = 0;
		}

		GL_shader[i].uniforms.clear();
		GL_shader[i].attributes.clear();
		GL_shader[i].uniform_blocks.clear();
	}

	GL_shader.clear();

	if (GLshader_info_log != NULL) {
		vm_free(GLshader_info_log);
		GLshader_info_log = NULL;
	}
}
/**
 * Builds the output text.
 */
void profile_dump_output()
{
    if (Cmdline_frame_profile) {
        end_profile_time = timer_get_microseconds();

        if (Cmdline_profile_write_file)
        {
            profiling_file << end_profile_time << ";" << (end_profile_time - start_profile_time) << std::endl;
        }

        profile_output.clear();
        profile_output += "  Avg :  Min :  Max :   # : Profile Name\n";
        profile_output += "----------------------------------------\n";

        for(int i = 0; i < (int)samples.size(); i++) {
            uint64_t sample_time;
            float percent_time, avg_time, min_time, max_time;
            uint64_t avg_micro_seconds, min_micro_seconds, max_micro_seconds;

            Assert(samples[i].open_profiles == 0);

            sample_time = samples[i].accumulator - samples[i].children_sample_time;

            if (end_profile_time == start_profile_time) {
                percent_time = 0.0f;
            } else {
                percent_time = (i2fl(sample_time) / i2fl(end_profile_time - start_profile_time)) *100.0f;
            }

            avg_micro_seconds = min_micro_seconds = max_micro_seconds = sample_time;
            avg_time = min_time = max_time = percent_time;

            // add new measurement into the history and get avg, min, and max
            store_profile_in_history(samples[i].name, percent_time, sample_time);
            get_profile_from_history(samples[i].name, &avg_time, &min_time, &max_time, &avg_micro_seconds, &min_micro_seconds, &max_micro_seconds);

            // format the data
            char avg[64], min[64], max[64], num[64];

            sprintf(avg, "%3.1f%% (%3.1fms)", avg_time, i2fl(avg_micro_seconds)*0.001f);
            sprintf(min, "%3.1f%% (%3.1fms)", min_time, i2fl(min_micro_seconds)*0.001f);
            sprintf(max, "%3.1f%% (%3.1fms)", max_time, i2fl(max_micro_seconds)*0.001f);
            sprintf(num, "%3d", samples[i].profile_instances);

            SCP_string indented_name(samples[i].name);

            for(uint indent = 0; indent < samples[i].num_parents; indent++) {
                indented_name = ">" + indented_name;
            }

            char line[256];
            sprintf(line, "%5s : %5s : %5s : %3s : ", avg, min, max, num);

            profile_output += line + indented_name + "\n";
        }

        samples.clear();
        start_profile_time = timer_get_microseconds();
    }
}
// initialization stuff for cutscenes
void cutscene_init()
{
	atexit(cutscene_close);
	char buf[MULTITEXT_LENGTH];
	int rval;
    cutscene_info cutinfo;

	if ((rval = setjmp(parse_abort)) != 0) {
		mprintf(("TABLES: Unable to parse '%s'!  Error code = %i.\n", "cutscenes.tbl", rval));
		return;
	}

	read_file_text("cutscenes.tbl", CF_TYPE_TABLES);
	reset_parse();

	// parse in all the cutscenes
	Cutscenes.clear();
	skip_to_string("#Cutscenes");
	ignore_white_space();

	bool isFirstCutscene = true;

	while ( required_string_either("#End", "$Filename:") ) 
    {
		required_string("$Filename:");
		stuff_string( cutinfo.filename, F_PATHNAME, MAX_FILENAME_LEN );

		required_string("$Name:");
		stuff_string( cutinfo.name, F_NAME, NAME_LENGTH );

		required_string("$Description:");
		stuff_string(buf, F_MULTITEXT, sizeof(buf));
		drop_white_space(buf);
		compact_multitext_string(buf);
		cutinfo.description = vm_strdup(buf);

		if (optional_string("$cd:"))
			stuff_int( &cutinfo.cd );
		else
			cutinfo.cd = 0;

		cutinfo.viewable = false;

		if (isFirstCutscene) {
			isFirstCutscene = false;
			// The original code assumes the first movie is the intro, so always viewable
			cutinfo.viewable = true;
		}

		if (optional_string("$Always Viewable:")) {
			stuff_boolean(&cutinfo.viewable);
		}

        Cutscenes.push_back(cutinfo);
	}

	required_string("#End");
}
// only call from game_shutdown()!!!
void particle_close()
{
	for (SCP_vector<particle*>::iterator p = Particles.begin(); p != Particles.end(); ++p)
	{
		(*p)->signature = 0;
		delete *p;
	}
	Particles.clear();
}
void cam_close()
{
	//Set Current_camera to nothing
	Current_camera = camid();
	for (auto ii = Cameras.begin(); ii != Cameras.end(); ++ii) {
		delete * ii;
	}
	Cameras.clear();
}
void obj_find_overlap_colliders(SCP_vector<int> *overlap_list_out, SCP_vector<int> *list, int axis, bool collide)
{
	size_t i, j;
	bool overlapped;
	bool first_not_added = true;
	SCP_vector<int> overlappers;

	float min;
	float max;
	float overlap_min;
	float overlap_max;
	
	overlappers.clear();

	for ( i = 0; i < (*list).size(); ++i ) {
		overlapped = false;

		min = obj_get_collider_endpoint((*list)[i], axis, true);
		max = obj_get_collider_endpoint((*list)[i], axis, false);

		for ( j = 0; j < overlappers.size(); ) {
			overlap_min = obj_get_collider_endpoint(overlappers[j], axis, true);
			overlap_max = obj_get_collider_endpoint(overlappers[j], axis, false);
			if ( min <= overlap_max ) {
				overlapped = true;

				if ( overlappers.size() == 1 && first_not_added ) {
					first_not_added = false;
					overlap_list_out->push_back(overlappers[j]);
				}
				
				if ( collide ) {
					obj_collide_pair(&Objects[(*list)[i]], &Objects[overlappers[j]]);
				}
			} else {
				overlappers[j] = overlappers.back();
				overlappers.pop_back();
				continue;
			}

			++j;
		}

		if ( overlappers.size() == 0 ) {
			first_not_added = true;
		}

		if ( overlapped ) {
			overlap_list_out->push_back((*list)[i]);
		}

		overlappers.push_back((*list)[i]);
	}

	overlapped = true;
}
void credits_close()
{	
	int i;

	for (i=0; i<Credits_num_images; i++) {
		if (Credits_bmps[i] >= 0){
			bm_release(Credits_bmps[i]);
		}
	}	
	Credits_bmps.clear();

	credits_stop_music(true);

	Credit_text_parts.clear();

	if (Background_bitmap){
		bm_release(Background_bitmap);
	}

	Ui_window.destroy();
	common_free_interface_palette();		// restore game palette
}
// kill all active particles
void particle_kill_all()
{
	// kill all active particles
	Num_particles = 0;
	Num_particles_hwm = 0;

	for (SCP_vector<particle*>::iterator p = Particles.begin(); p != Particles.end(); ++p)
	{
		(*p)->signature = 0;
		delete *p;
	}
	Particles.clear();
}
Exemple #16
0
void os_poll()
{
	// Replay the buffered events
	auto end = buffered_events.end();
	for (auto it = buffered_events.begin(); it != end; ++it) {
		handle_sdl_event(*it);
	}
	buffered_events.clear();

	SDL_Event event;

	while (SDL_PollEvent(&event)) {
		handle_sdl_event(event);
	}
}
void cutscenes_screen_init()
{
	int i;
	ui_button_info *b;

	Ui_window.create(0, 0, gr_screen.max_w_unscaled, gr_screen.max_h_unscaled, 0);
	Ui_window.set_mask_bmap(Cutscene_mask_name[gr_screen.res]);

	for (i=0; i<NUM_BUTTONS; i++) {
		b = &Buttons[gr_screen.res][i];

		b->button.create(&Ui_window, "", b->x, b->y, 60, 30, (i < 2), 1);
		// set up callback for when a mouse first goes over a button
		b->button.set_highlight_action(common_play_highlight_sound);
		b->button.set_bmaps(b->filename);
		b->button.link_hotspot(b->hotspot);
	}

	// add xstrs
	for(i=0; i<NUM_CUTSCENE_TEXT; i++){
		Ui_window.add_XSTR(&Cutscene_text[gr_screen.res][i]);
	}

	Buttons[gr_screen.res][EXIT_BUTTON].button.set_hotkey(KEY_CTRLED | KEY_ENTER);
	Buttons[gr_screen.res][SCROLL_UP_BUTTON].button.set_hotkey(KEY_PAGEUP);
	Buttons[gr_screen.res][SCROLL_DOWN_BUTTON].button.set_hotkey(KEY_PAGEDOWN);	

	List_region.create(&Ui_window, "", Cutscene_list_coords[gr_screen.res][0], Cutscene_list_coords[gr_screen.res][1], Cutscene_list_coords[gr_screen.res][2], Cutscene_list_coords[gr_screen.res][3], 0, 1);
	List_region.hide();

	// set up hotkeys for buttons so we draw the correct animation frame when a key is pressed
	Buttons[gr_screen.res][SCROLL_UP_BUTTON].button.set_hotkey(KEY_PAGEUP);
	Buttons[gr_screen.res][SCROLL_DOWN_BUTTON].button.set_hotkey(KEY_PAGEDOWN);

	Background_bitmap = bm_load(Cutscene_bitmap_name[gr_screen.res]);
	Scroll_offset = Selected_line = 0;
	Description_index = -1;

    Cutscene_list.clear();
	
	int u = 0;
	for (SCP_vector<cutscene_info>::iterator cut = Cutscenes.begin(); cut != Cutscenes.end(); ++cut, u++) {
		if ( (*cut).viewable ) {
			Cutscene_list.push_back(u);
		}
	}
}
/**
 * @brief Writes JSON tracing data to a file if the commandlinfe option is enabled
 */
void profile_dump_json_output() {
    if (Cmdline_json_profiling) {
        std::lock_guard<std::mutex> guard(json_mutex);

        // FIXME: This could be improved by using only a single thread and a synchronized bounded
        // queue. Boost has an implementation of that.
        tracing_frame_data frame_data;
        frame_data.data = current_frame_data;
        frame_data.frame_num = ++json_frame_num;

        pending_frame_data.push_back(std::move(frame_data));

        current_frame_data.clear();

        process_pending_data();
    }
}
void species_init()
{
	if (Species_initted)
		return;

	Species_info.clear();


	if (cf_exists_full("species_defs.tbl", CF_TYPE_TABLES))
		parse_species_tbl("species_defs.tbl");
	else
		parse_species_tbl(NULL);

	parse_modular_table("*-sdf.tbm", parse_species_tbl);


	Species_initted = 1;
}
/**
 * Parse the sounds.tbl file, and load the specified sounds.
 */
void gamesnd_parse_soundstbl()
{
	parse_sound_table("sounds.tbl");

	parse_modular_table("*-snd.tbm", parse_sound_table);

	// if we are missing any species then report
	if (!missingFlybySounds.empty())
	{
		SCP_string errorString;
		for (size_t i = 0; i < missingFlybySounds.size(); i++)
		{
			errorString.append(missingFlybySounds[i].species_name);
			errorString.append("\n");
		}

		Error(LOCATION, "The following species are missing flyby sounds in sounds.tbl:\n%s", errorString.c_str());
	}

	missingFlybySounds.clear();
}
void fs2netd_update_ban_list()
{
	int rc = 0;

	if ( !Logged_in ) {
		return;
	}

	// destroy the file prior to updating
	cf_delete( "banlist.cfg", CF_TYPE_DATA );

	do_full_packet = true;

	In_process = true;

	if (Is_standalone) {
		do { rc = fs2netd_update_ban_list_do(); } while (!rc);
	} else {
		rc = popup_till_condition(fs2netd_update_ban_list_do, XSTR("&Cancel", 779), XSTR("Requesting IP ban list", 1587));
	}

	In_process = false;
	Local_timeout = -1;

	if ( !FS2NetD_ban_list.empty() ) {
		CFILE *banlist_cfg = cfopen("banlist.cfg", "wt", CFILE_NORMAL, CF_TYPE_DATA);

		if (banlist_cfg != NULL) {
			for (SCP_vector<SCP_string>::iterator bl = FS2NetD_ban_list.begin(); bl != FS2NetD_ban_list.end(); ++bl) {
				cfputs( const_cast<char*>(bl->c_str()), banlist_cfg );
			}

			cfclose(banlist_cfg);
		}
	}

	FS2NetD_ban_list.clear();
}
void profile_deinit()
{
    if (Cmdline_profile_write_file)
    {
        if (profiling_file.is_open())
        {
            profiling_file.flush();
            profiling_file.close();
        }
    }
    if (Cmdline_json_profiling)
    {
        profile_dump_json_output();
        while (!pending_frame_data.empty()) {
            process_pending_data();
        }
    }

    for (auto obj : query_objects) {
        gr_delete_query_object(obj);
    }
    query_objects.clear();
    SCP_queue<int>().swap(free_query_objects);
}
/**
 * Go through GL_shader and call glDeleteObject() for all created shaders, then clear GL_shader
 */
void opengl_shader_shutdown()
{
	size_t i;

	if ( !Use_GLSL ) {
		return;
	}

	for (i = 0; i < GL_shader.size(); i++) {
		if (GL_shader[i].program_id) {
			vglDeleteObjectARB(GL_shader[i].program_id);
			GL_shader[i].program_id = 0;
		}

		GL_shader[i].uniforms.clear();
	}

	GL_shader.clear();

	if (GLshader_info_log != NULL) {
		vm_free(GLshader_info_log);
		GLshader_info_log = NULL;
	}
}
void cutscenes_screen_do_frame()
{
	int i, k, y, z;
	int font_height = gr_get_font_height();
	int select_tease_line = -1;

	k = Ui_window.process();
	switch (k) {
		case KEY_DOWN:  // select next line
			cutscenes_screen_scroll_line_down();
			break;

		case KEY_UP:  // select previous line
			cutscenes_screen_scroll_line_up();
			break;

		case KEY_TAB:
		case KEY_CTRLED | KEY_DOWN:
			cutscenes_screen_button_pressed(CREDITS_BUTTON);
			break;

		case KEY_SHIFTED | KEY_TAB:
		case KEY_CTRLED | KEY_UP:
			cutscenes_screen_button_pressed(SIMULATOR_BUTTON);
			break;

		case KEY_ENTER:
			cutscenes_screen_play();
			break;

		case KEY_ESC:  // cancel
			gameseq_post_event(GS_EVENT_MAIN_MENU);
			game_flush();
			break;

		case KEY_F1:  // show help overlay
			break;

		case KEY_F2:  // goto options screen
			gameseq_post_event(GS_EVENT_OPTIONS_MENU);
			break;

		// the "show-all" hotkey
		case KEY_CTRLED | KEY_SHIFTED | KEY_S:
		{
            Cutscene_list.clear();
			size_t size = Cutscenes.size();
			for (size_t t = 0; t < size; t++) {
                Cutscene_list.push_back((int)t);
			}

			break;
		}
	}	// end switch

	for (i=0; i<NUM_BUTTONS; i++){
		if (Buttons[gr_screen.res][i].button.pressed()){
			if (cutscenes_screen_button_pressed(i)){
				return;
			}
		}
	}

	if (List_region.button_down()) {
		List_region.get_mouse_pos(NULL, &y);
		z = Scroll_offset + y / font_height;
		if ((z >= 0) && (z < (int)Cutscene_list.size()))
			select_tease_line = z;
	}
	
	if (List_region.pressed()) {
		List_region.get_mouse_pos(NULL, &y);
		z = Scroll_offset + y / font_height;
		if ((z >= 0) && (z < (int)Cutscene_list.size()))
			Selected_line = z;
	}

	GR_MAYBE_CLEAR_RES(Background_bitmap);
	if (Background_bitmap >= 0) {
		gr_set_bitmap(Background_bitmap);
		gr_bitmap(0, 0);
	} 

	Ui_window.draw();

	for (i=TECH_DATABASE_BUTTON; i<=CREDITS_BUTTON; i++){
		if (Buttons[gr_screen.res][i].button.button_down()){
			break;
		}
	}

	if (i > CREDITS_BUTTON){
		Buttons[gr_screen.res][CUTSCENES_BUTTON].button.draw_forced(2);
	}

	y = 0;
	z = Scroll_offset;
	while (y + font_height <= Cutscene_list_coords[gr_screen.res][3]) {
		if (z >= (int)Cutscene_list.size()){
			break;
		}

		if (z == Selected_line){
			gr_set_color_fast(&Color_text_selected);
		} else if (z == select_tease_line) {
			gr_set_color_fast(&Color_text_subselected);
		} else {
			gr_set_color_fast(&Color_text_normal);
		}

		gr_printf(Cutscene_list_coords[gr_screen.res][0], Cutscene_list_coords[gr_screen.res][1] + y, Cutscenes[Cutscene_list[z]].name);

		y += font_height;
		z++;
	}

	if (Description_index != Selected_line) {
		char *src = NULL;

		Description_index = Selected_line;
		Text_size = 0;
		if ( Description_index < (int)Cutscene_list.size( ) &&
			 (int)Cutscene_list[ Description_index ] < (int)Cutscenes.size( ) ) {
			src = Cutscenes[Cutscene_list[Description_index]].description;
			if (src) {
				Text_size = split_str(src, Cutscene_desc_coords[gr_screen.res][2], Text_line_size, Text_lines, Cutscene_max_text_lines[gr_screen.res]);
				Assert(Text_size >= 0 && Text_size < Cutscene_max_text_lines[gr_screen.res]);
			}
		}
	}

	if (Description_index >= 0) {
		int len;
		char line[MAX_TEXT_LINE_LEN + 1];

		gr_set_color_fast(&Color_text_normal);

		y = 0;
		z = Text_offset;
		while (y + font_height <= Cutscene_desc_coords[gr_screen.res][3]) {
			if (z >= Text_size || z >= MAX_TEXT_LINES-1)
				break;

			len = Text_line_size[z];
			if (len > MAX_TEXT_LINE_LEN)
				len = MAX_TEXT_LINE_LEN;

			strncpy(line, Text_lines[z], len);
			line[len] = 0;
			gr_string(Cutscene_desc_coords[gr_screen.res][0], Cutscene_desc_coords[gr_screen.res][1] + y, line);

			y += font_height;
			z++;
		}
	}

	gr_flip();
}
/**
 * Go through GL_shader and call glDeleteObject() for all created shaders, then clear GL_shader
 */
void opengl_shader_shutdown()
{
	GL_shader.clear();
	GL_shader_map.clear();
}
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_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;
	}
}
void obj_reset_colliders()
{
	Collision_sort_list.clear();
	Collision_cached_pairs.clear();
}
/*
 * Record the current state of the players wingman & ships with the "red-alert-carry" flag
 * Wingmen without the red-alert-carry flag are only stored if they survive
 * dead wingmen must still be handled in red_alert_bash_wingman_status
 */
void red_alert_store_wingman_status()
{
	ship				*shipp;
	red_alert_ship_status	ras;
	ship_obj			*so;
	object			*ship_objp;

	// store the mission filename for the red alert precursor mission
	Red_alert_precursor_mission = Game_current_mission_filename;

	// Pyro3d - Clear list of stored red alert ships 
	// Probably not the best solution, but it prevents an assertion in change_ship_type()
	Red_alert_wingman_status.clear();

	// store status for all existing ships
	for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
		ship_objp = &Objects[so->objnum];
		Assert(ship_objp->type == OBJ_SHIP);
		shipp = &Ships[ship_objp->instance];

		if ( shipp->flags[Ship::Ship_Flags::Dying] ) {
			continue;
		}

		if ( !(shipp->flags[Ship::Ship_Flags::From_player_wing]) && !(shipp->flags[Ship::Ship_Flags::Red_alert_store_status]) ) {
			continue;
		}

		ras.name = shipp->ship_name;
		ras.hull = Objects[shipp->objnum].hull_strength;
		ras.ship_class = shipp->ship_info_index;
		red_alert_store_weapons(&ras, &shipp->weapons);
		red_alert_store_subsys_status(&ras, shipp);

		Red_alert_wingman_status.push_back( ras );
		// niffiwan: trying to track down red alert bug creating HUGE pilot files 
		Assert( (Red_alert_wingman_status.size() <= MAX_SHIPS) );
	}

	// store exited ships that did not die
	for (int idx=0; idx<(int)Ships_exited.size(); idx++) {
		if (Ships_exited[idx].flags[Ship::Exit_Flags::Red_alert_carry]) {
			ras.name = Ships_exited[idx].ship_name;
			ras.hull = float(Ships_exited[idx].hull_strength);

			// if a ship has been destroyed or removed manually by the player, then mark it as such ...
			if ( Ships_exited[idx].flags[Ship::Exit_Flags::Destroyed]) {
				ras.ship_class = RED_ALERT_DESTROYED_SHIP_CLASS;
			}
			else if (Ships_exited[idx].flags[Ship::Exit_Flags::Player_deleted]) {
				ras.ship_class = RED_ALERT_PLAYER_DEL_SHIP_CLASS;
			}
			// ... otherwise we want to make sure and carry over the ship class
			else {
				Assert( Ships_exited[idx].ship_class >= 0 );
				ras.ship_class = Ships_exited[idx].ship_class;
			}

			red_alert_store_weapons(&ras, NULL);
			red_alert_store_subsys_status(&ras, NULL);

			Red_alert_wingman_status.push_back( ras );
			// niffiwan: trying to track down red alert bug creating HUGE pilot files 
			Assert( (Red_alert_wingman_status.size() <= MAX_SHIPS) );
		}
	}

	Assert( !Red_alert_wingman_status.empty() );
}
/*
 * red_alert_clear()
 *
 * clear all red alert "wingman" data
 * Allows data to be cleared from outside REDALERT_INTERNAL code
 */
void red_alert_clear()
{
	Red_alert_wingman_status.clear();
}
/**
 * Compiles a new shader, and creates an opengl_shader_t that will be put into the GL_shader vector
 * if compilation is successful.
 * This function is used for main (i.e. model rendering) and particle shaders, post processing shaders use their own infrastructure
 *
 * @param flags		Combination of SDR_* flags
 */
void opengl_compile_main_shader(int flags) {
	char *vert = NULL, *frag = NULL;

	mprintf(("Compiling new shader:\n"));

	bool in_error = false;
	opengl_shader_t new_shader;

	// choose appropriate files
	char vert_name[NAME_LENGTH];
	char frag_name[NAME_LENGTH];

	if (flags & SDR_FLAG_SOFT_QUAD) {
		strcpy_s( vert_name, "soft-v.sdr");
		strcpy_s( frag_name, "soft-f.sdr");
	} else {
		strcpy_s( vert_name, "main-v.sdr");
		strcpy_s( frag_name, "main-f.sdr");
	}

	// read vertex shader
	if ( (vert = opengl_load_shader(vert_name, flags)) == NULL ) {
		in_error = true;
		goto Done;
	}

	// read fragment shader
	if ( (frag = opengl_load_shader(frag_name, flags)) == NULL ) {
		in_error = true;
		goto Done;
	}

	Verify( vert != NULL );
	Verify( frag != NULL );

	new_shader.program_id = opengl_shader_create(vert, frag);

	if ( !new_shader.program_id ) {
		in_error = true;
		goto Done;
	}

	new_shader.flags = flags;

	opengl_shader_set_current( &new_shader );
	
	mprintf(("Shader features:\n"));

	//Init all the uniforms
	if (new_shader.flags & SDR_FLAG_SOFT_QUAD) {
		for (int j = 0; j < Particle_shader_flag_references; j++) {
			if (new_shader.flags == GL_Uniform_Reference_Particle[j].flag) {
				int k;

			// Equality check needed because the combination of SDR_FLAG_SOFT_QUAD and SDR_FLAG_DISTORTION define something very different
			// than just SDR_FLAG_SOFT_QUAD alone
				for (k = 0; k < GL_Uniform_Reference_Particle[j].num_uniforms; k++) {
					opengl_shader_init_uniform( GL_Uniform_Reference_Particle[j].uniforms[k] );
				}

				for (k = 0; k < GL_Uniform_Reference_Particle[j].num_attributes; k++) {
					opengl_shader_init_attribute( GL_Uniform_Reference_Particle[j].attributes[k] );
				}

				mprintf(("   %s\n", GL_Uniform_Reference_Particle[j].name));
			}
		}
	} else {
		for (int j = 0; j < Main_shader_flag_references; j++) {
			if (new_shader.flags & GL_Uniform_Reference_Main[j].flag) {
				if (GL_Uniform_Reference_Main[j].num_uniforms > 0) {
					for (int k = 0; k < GL_Uniform_Reference_Main[j].num_uniforms; k++) {
						opengl_shader_init_uniform( GL_Uniform_Reference_Main[j].uniforms[k] );
					}
				}

				if (GL_Uniform_Reference_Main[j].num_attributes > 0) {
					for (int k = 0; k < GL_Uniform_Reference_Main[j].num_attributes; k++) {
						opengl_shader_init_attribute( GL_Uniform_Reference_Main[j].attributes[k] );
					}
				}

				mprintf(("   %s\n", GL_Uniform_Reference_Main[j].name));
			}
		}
	}

	opengl_shader_set_current();

	// add it to our list of embedded shaders
	GL_shader.push_back( new_shader );

Done:
	if (vert != NULL) {
		vm_free(vert);
		vert = NULL;
	}

	if (frag != NULL) {
		vm_free(frag);
		frag = NULL;
	}

	if (in_error) {
		// shut off relevant usage things ...
		bool dealt_with = false;

		if (flags & SDR_FLAG_HEIGHT_MAP) {
			mprintf(("  Shader in_error!  Disabling height maps!\n"));
			Cmdline_height = 0;
			dealt_with = true;
		}

		if (flags & SDR_FLAG_NORMAL_MAP) {
			mprintf(("  Shader in_error!  Disabling normal maps and height maps!\n"));
			Cmdline_height = 0;
			Cmdline_normal = 0;
			dealt_with = true;
		}

		if (!dealt_with) {
			if (flags == 0) {
				mprintf(("  Shader in_error!  Disabling GLSL!\n"));

				Use_GLSL = 0;
				Cmdline_height = 0;
				Cmdline_normal = 0;

				GL_shader.clear();
			} else {
				// We died on a lighting shader, probably due to instruction count.
				// Drop down to a special var that will use fixed-function rendering
				// but still allow for post-processing to work
				mprintf(("  Shader in_error!  Disabling GLSL model rendering!\n"));
				Use_GLSL = 1;
				Cmdline_height = 0;
				Cmdline_normal = 0;
			}
		}
	}
}