1 /*
2  * Copyright (C) Volition, Inc. 1999.  All rights reserved.
3  *
4  * All source code herein is the property of Volition, Inc. You may not sell
5  * or otherwise commercially exploit the source or things you created based on the
6  * source.
7  *
8 */
9 
10 
11 
12 #include "stdafx.h"
13 #include "FRED.h"
14 #include "MainFrm.h"
15 #include "FREDDoc.h"
16 #include "FREDView.h"
17 #include "FredRender.h"
18 #include "ai/aigoals.h"
19 #include "ship/ship.h"
20 #include "globalincs/linklist.h"
21 #include "globalincs/version.h"
22 #include "globalincs/alphacolors.h"
23 #include "mission/missiongrid.h"
24 #include "mission/missionparse.h"
25 #include "mission/missionmessage.h"
26 #include "mission/missiongoals.h"
27 #include "mission/missionbriefcommon.h"
28 #include "Management.h"
29 #include "cfile/cfile.h"
30 #include "graphics/2d.h"
31 #include "render/3d.h"
32 #include "weapon/weapon.h"
33 #include "io/key.h"
34 #include "parse/parselo.h"
35 #include "math/fvi.h"
36 #include "starfield/starfield.h"
37 #include "parse/sexp.h"
38 #include "parse/sexp/sexp_lookup.h"
39 #include "io/mouse.h"
40 #include "mission/missioncampaign.h"
41 #include "wing.h"
42 #include "MessageEditorDlg.h"
43 #include "EventEditor.h"
44 #include "MissionGoalsDlg.h"
45 #include "ShieldSysDlg.h"
46 #include "gamesnd/eventmusic.h"
47 #include "DebriefingEditorDlg.h"
48 #include "starfield/nebula.h"
49 #include "asteroid/asteroid.h"
50 #include "hud/hudsquadmsg.h"
51 #include "jumpnode/jumpnode.h"
52 #include "stats/medals.h"
53 #include "localization/localize.h"
54 #include "osapi/osregistry.h"
55 #include "localization/fhash.h"
56 #include "io/timer.h"
57 #include "nebula/neb.h"
58 #include "nebula/neblightning.h"
59 #include "species_defs/species_defs.h"
60 #include "osapi/osapi.h"
61 #include "graphics/font.h"
62 #include "object/objectdock.h"
63 #include "gamesnd/gamesnd.h"
64 #include "iff_defs/iff_defs.h"
65 #include "menuui/techmenu.h"
66 #include "missionui/fictionviewer.h"
67 #include "mod_table/mod_table.h"
68 #include "libs/ffmpeg/FFmpeg.h"
69 #include "scripting/scripting.h"
70 #include "utils/Random.h"
71 
72 #include <direct.h>
73 #include "cmdline/cmdline.h"
74 
75 #define SDL_MAIN_HANDLED
76 #include <SDL_main.h>
77 
78 #define MAX_DOCKS 1000
79 
80 #define UNKNOWN_USER		"Unknown"
81 
82 extern void ssm_init();	// Need this to populate Ssm_info so OPF_SSM_CLASS does something. -MageKing17
83 
84 int cur_wing = -1;
85 int cur_wing_index;
86 int cur_object_index = -1;
87 int cur_ship = -1;
88 int cur_model_index = 0;
89 waypoint *cur_waypoint = NULL;
90 waypoint_list *cur_waypoint_list = NULL;
91 int delete_flag;
92 int bypass_update = 0;
93 int Default_player_model = 0;
94 int Update_ship = 0;
95 int Update_wing = 0;
96 
97 char Fred_exe_dir[512] = "";
98 char Fred_base_dir[512] = "";
99 
100 char Fred_alt_names[MAX_SHIPS][NAME_LENGTH+1];
101 char Fred_callsigns[MAX_SHIPS][NAME_LENGTH+1];
102 
103 // object numbers for ships in a wing.
104 int wing_objects[MAX_WINGS][MAX_SHIPS_PER_WING];
105 
106 char *Docking_bay_list[MAX_DOCKS];
107 
108 // Goober5000
109 bool Show_iff[MAX_IFFS];
110 
111 CCriticalSection CS_cur_object_index;
112 
113 // Used in the FRED drop-down menu and in error_check_initial_orders
114 // NOTE: Certain goals (Form On Wing, Rearm, Chase Weapon, Fly To Ship) aren't listed here.  This may or may not be intentional,
115 // but if they are added in the future, it will be necessary to verify correct functionality in the various FRED dialog functions.
116 ai_goal_list Ai_goal_list[] = {
117 	{ "Waypoints",				AI_GOAL_WAYPOINTS,			0 },
118 	{ "Waypoints once",			AI_GOAL_WAYPOINTS_ONCE,		0 },
119 	{ "Attack",					AI_GOAL_CHASE | AI_GOAL_CHASE_WING,	0 },
120 	{ "Attack any ship",		AI_GOAL_CHASE_ANY,			0 },
121 	{ "Attack ship class",		AI_GOAL_CHASE_SHIP_CLASS,	0 },
122 	{ "Guard",					AI_GOAL_GUARD | AI_GOAL_GUARD_WING, 0 },
123 	{ "Disable ship",			AI_GOAL_DISABLE_SHIP,		0 },
124 	{ "Disarm ship",			AI_GOAL_DISARM_SHIP,		0 },
125 	{ "Destroy subsystem",		AI_GOAL_DESTROY_SUBSYSTEM,	0 },
126 	{ "Dock",					AI_GOAL_DOCK,				0 },
127 	{ "Undock",					AI_GOAL_UNDOCK,				0 },
128 	{ "Warp",					AI_GOAL_WARP,				0 },
129 	{ "Evade ship",				AI_GOAL_EVADE_SHIP,			0 },
130 	{ "Ignore ship",			AI_GOAL_IGNORE,				0 },
131 	{ "Ignore ship (new)",		AI_GOAL_IGNORE_NEW,			0 },
132 	{ "Stay near ship",			AI_GOAL_STAY_NEAR_SHIP,		0 },
133 	{ "Keep safe distance",		AI_GOAL_KEEP_SAFE_DISTANCE,	0 },
134 	{ "Stay still",				AI_GOAL_STAY_STILL,			0 },
135 	{ "Play dead",				AI_GOAL_PLAY_DEAD,			0 },
136 	{ "Play dead (persistent)",	AI_GOAL_PLAY_DEAD_PERSISTENT,		0 }
137 };
138 
139 int Ai_goal_list_size = sizeof(Ai_goal_list) / sizeof(ai_goal_list);
140 
141 // internal function prototypes
142 void set_cur_indices(int obj);
143 int common_object_delete(int obj);
144 int create_waypoint(vec3d *pos, int waypoint_instance);
145 int create_ship(matrix *orient, vec3d *pos, int ship_type);
146 int query_ship_name_duplicate(int ship);
147 char *reg_read_string( char *section, char *name, char *default_value );
148 
149 extern int Nmodel_num;
150 extern int Nmodel_instance_num;
151 extern matrix Nmodel_orient;
152 extern int Nmodel_bitmap;
153 
string_copy(char * dest,const CString & src,size_t max_len,int modify)154 void string_copy(char *dest, const CString &src, size_t max_len, int modify)
155 {
156 	if (modify)
157 		if (strcmp(src, dest))
158 			set_modified();
159 
160 	auto len = strlen(src);
161 	if (len >= max_len)
162 		len = max_len - 1;
163 
164 	strncpy(dest, src, len);
165 	dest[len] = 0;
166 }
167 
string_copy(SCP_string & dest,const CString & src,int modify)168 void string_copy(SCP_string &dest, const CString &src, int modify)
169 {
170 	if (modify)
171 		if (strcmp(src, dest.c_str()))
172 			set_modified();
173 
174 	dest = src;
175 }
176 
177 // converts a multiline string (one with newlines in it) into a windows format multiline
178 // string (newlines changed to '\r\n').
convert_multiline_string(CString & dest,const SCP_string & src)179 void convert_multiline_string(CString &dest, const SCP_string &src)
180 {
181 	dest = src.c_str();
182 	dest.Replace("\n", "\r\n");
183 }
184 
185 // converts a multiline string (one with newlines in it) into a windows format multiline
186 // string (newlines changed to '\r\n').
convert_multiline_string(CString & dest,const char * src)187 void convert_multiline_string(CString &dest, const char *src)
188 {
189 	dest = src;
190 	dest.Replace("\n", "\r\n");
191 }
192 
193 // Converts a windows format multiline CString back into a normal multiline string.
deconvert_multiline_string(char * dest,const CString & str,int max_len)194 void deconvert_multiline_string(char *dest, const CString &str, int max_len)
195 {
196 	// leave room for the null terminator
197 	memset(dest, 0, max_len);
198 	strncpy(dest, (LPCTSTR) str, max_len - 1);
199 
200 	replace_all(dest, "\r\n", "\n", max_len);
201 }
202 
203 // ditto for SCP_string
deconvert_multiline_string(SCP_string & dest,const CString & str)204 void deconvert_multiline_string(SCP_string &dest, const CString &str)
205 {
206 	dest = str;
207 	replace_all(dest, "\r\n", "\n");
208 }
209 
strip_quotation_marks(CString & str)210 void strip_quotation_marks(CString& str) { str.Remove('\"'); }
211 
pad_with_newline(CString & str,int max_size)212 void pad_with_newline(CString& str, int max_size) {
213 	int len = str.GetLength();
214 	if (!len) {
215 		len = 1;
216 	}
217 	if (str[len - 1] != '\n' && len < max_size) {
218 		str += _T("\n");
219 	}
220 }
221 
222 // medal_stuff Medals[NUM_MEDALS];
223 /*
224 void parse_medal_tbl()
225 {
226 	int rval, num_medals;
227 
228 	// open localization
229 	lcl_ext_open();
230 
231 	if ((rval = setjmp(parse_abort)) != 0) {
232 		mprintf(("TABLES: Unable to parse '%s'!  Error code = %i.\n", "medals.tbl", rval));
233 		lcl_ext_close();
234 		return;
235 	}
236 
237 	read_file_text("medals.tbl");
238 	reset_parse();
239 
240 	// parse in all the rank names
241 	num_medals = 0;
242 	required_string("#Medals");
243 	while ( required_string_either("#End", "$Name:") ) {
244 		Assert ( num_medals < NUM_MEDALS);
245 		required_string("$Name:");
246 		stuff_string( Medals[num_medals].name, F_NAME, NULL );
247 		required_string("$Bitmap:");
248 		stuff_string( Medals[num_medals].bitmap, F_NAME, NULL );
249 		required_string("$Num mods:");
250 		stuff_int( &Medals[num_medals].num_versions);
251 
252 		// some medals are based on kill counts.  When string +Num Kills: is present, we know that
253 		// this medal is a badge and should be treated specially
254 		Medals[num_medals].kills_needed = 0;
255 
256 		if ( optional_string("+Num Kills:") ) {
257 			char buf[MULTITEXT_LENGTH + 1];
258 
259 			stuff_int( &Medals[num_medals].kills_needed );
260 
261 			required_string("$Wavefile 1:");
262 			stuff_string(buf, F_NAME, NULL, MAX_FILENAME_LEN);
263 
264 			required_string("$Wavefile 2:");
265 			stuff_string(buf, F_NAME, NULL, MAX_FILENAME_LEN);
266 
267 			required_string("$Promotion Text:");
268 			stuff_string(buf, F_MULTITEXT, NULL);
269 		}
270 
271 		num_medals++;
272 	}
273 
274 	required_string("#End");
275 
276 	// close localization
277 	lcl_ext_close();
278 }
279 */
280 
281 void parse_init(bool basic = false);
282 void brief_init_colors();
283 
fred_preload_all_briefing_icons()284 void fred_preload_all_briefing_icons()
285 {
286 	for (SCP_vector<briefing_icon_info>::iterator ii = Briefing_icon_info.begin(); ii != Briefing_icon_info.end(); ++ii)
287 	{
288 		generic_anim_load(&ii->regular);
289 		hud_anim_load(&ii->fade);
290 		hud_anim_load(&ii->highlight);
291 	}
292 }
293 
fred_init(std::unique_ptr<os::GraphicsOperations> && graphicsOps)294 bool fred_init(std::unique_ptr<os::GraphicsOperations>&& graphicsOps)
295 {
296 	int i;
297 
298 	SDL_SetMainReady();
299 
300 	Random::seed(static_cast<unsigned int>(time(nullptr)));
301 	init_pending_messages();
302 
303 	os_init(Osreg_class_name, Osreg_app_name);
304 
305 	timer_init();
306 
307 	Assert(strlen(Fred_base_dir) > 0); //-V805
308 
309 	// sigh... this should enable proper reading of cmdline_fso.cfg - Goober5000
310 	cfile_chdir(Fred_base_dir);
311 
312 	// this should enable mods - Kazan
313 	if (!parse_cmdline(__argc, __argv)) {
314 		// Command line contained an option that terminates the program immediately
315 		exit(1);
316 	}
317 
318 #ifndef NDEBUG
319 	#if FS_VERSION_REVIS == 0
320 		mprintf(("Fred2 Open version: %i.%i.%i\n", FS_VERSION_MAJOR, FS_VERSION_MINOR, FS_VERSION_BUILD));
321 	#else
322 		mprintf(("Fred2 Open version: %i.%i.%i.%i\n", FS_VERSION_MAJOR, FS_VERSION_MINOR, FS_VERSION_BUILD, FS_VERSION_REVIS));
323 	#endif
324 
325 	extern void cmdline_debug_print_cmdline();
326 	cmdline_debug_print_cmdline();
327 #endif
328 
329 	// d'oh
330 	if(cfile_init(Fred_exe_dir)){
331 		exit(1);
332 	}
333 
334 	// Load game_settings.tbl
335 	mod_table_init();
336 
337 	// initialize localization module. Make sure this is done AFTER initializing OS.
338 	// NOTE: Fred should ALWAYS run without localization. Otherwise it might swap in another language
339 	// when saving - which would cause inconsistencies when externalizing to tstrings.tbl via Exstr
340 	// trust me on this :)
341 	lcl_init(LCL_UNTRANSLATED);
342 
343 	// Goober5000 - force init XSTRs (so they work, but only work untranslated, based on above comment)
344 	extern bool Xstr_inited;
345 	Xstr_inited = true;
346 
347 #ifndef NDEBUG
348 	load_filter_info();
349 #endif
350 
351 	//CFREDView *window = CFREDView::GetView();
352 	//HWND hwndApp = window->GetSafeHwnd();
353 	//os_set_window((uint) hwndApp);
354 
355 	snd_init();
356 
357 	// Not ready for this yet
358   //	Cmdline_nospec = 1;
359  // 	Cmdline_noglow = 1;
360  	Cmdline_window = 1;
361 
362 	gr_init(std::move(graphicsOps), GR_OPENGL, 640, 480, 32);
363 
364 	io::mouse::CursorManager::get()->showCursor(false);
365 
366 	// To avoid breaking current mods which do not support scripts in FRED we only initialize the scripting
367 	// system if a special mod_table option is set
368 	if (Enable_scripts_in_fred) {
369 		script_init();			//WMC
370 	}
371 
372 	font::init();					// loads up all fonts
373 
374 	gr_set_gamma(3.0f);
375 
376 	key_init();
377 	mouse_init();
378 
379 	particle::ParticleManager::init();
380 
381 	iff_init();			// Goober5000
382 	species_init();		// Kazan
383 
384 	brief_icons_init();
385 
386 	// for fred specific replacement texture stuff
387 	Fred_texture_replacements.clear();
388 
389 	// Goober5000
390 	for (i = 0; i < MAX_IFFS; i++)
391 		Show_iff[i] = true;
392 
393 	// Goober5000
394 	strcpy_s(Voice_abbrev_briefing, "");
395 	strcpy_s(Voice_abbrev_campaign, "");
396 	strcpy_s(Voice_abbrev_command_briefing, "");
397 	strcpy_s(Voice_abbrev_debriefing, "");
398 	strcpy_s(Voice_abbrev_message, "");
399 	strcpy_s(Voice_abbrev_mission, "");
400 	Voice_no_replace_filenames = false;
401 	strcpy_s(Voice_script_entry_format, "Sender: $sender\r\nPersona: $persona\r\nFile: $filename\r\nMessage: $message");
402 	Voice_export_selection = 0;
403 
404 	hud_init_comm_orders();		// Goober5000
405 
406 	alpha_colors_init();
407 
408 	gamesnd_parse_soundstbl();		// needs to be loaded after species stuff but before interface/weapon/ship stuff - taylor
409 	mission_brief_common_init();
410 	obj_init();
411 	model_free_all();				// Free all existing models
412 	ai_init();
413 	ai_profiles_init();
414 	armor_init();
415 	weapon_init();
416 	parse_medal_tbl();			// get medal names for sexpression usage
417 	glowpoint_init();
418 	ship_init();
419 	parse_init();
420 	techroom_intel_init();
421 	hud_positions_init();
422 	asteroid_init();
423 
424 	// get fireball IDs for sexpression usage
425 	// (we don't need to init the entire system via fireball_init, we just need the information)
426 	fireball_parse_tbl();
427 
428 	// initialize and activate external string hash table
429 	// make sure to do here so that we don't parse the table files into the hash table - waste of space
430 	fhash_init();
431 	fhash_activate();
432 
433 	neb2_init();						// fullneb stuff
434 	stars_init();
435 	ssm_init();		// The game calls this after stars_init(), and we need Ssm_info initialized for OPF_SSM_CLASS. -MageKing17
436 	brief_init_colors();
437 	fred_preload_all_briefing_icons(); //phreak.  This needs to be done or else the briefing icons won't show up
438 	event_music_init();
439 	fiction_viewer_reset();
440 	cmd_brief_reset();
441 	Show_waypoints = TRUE;
442 
443 	mission_campaign_clear();
444 	create_new_mission();
445 
446 	// neb lightning
447 	nebl_init();
448 
449 	libs::ffmpeg::initialize();
450 
451 	sexp::dynamic_sexp_init();
452 
453 	// wookieejedi
454 	// load in the controls and defaults including the controlconfigdefault.tbl
455 	// this allows the sexp tree in key-pressed to actually match what the game will use
456 	// especially useful when a custom Controlconfigdefaults.tbl is used
457 	control_config_common_init();
458 
459 	gr_reset_clip();
460 	g3_start_frame(0);
461 	g3_set_view_matrix(&eye_pos, &eye_orient, 0.5f);
462 
463 	// Get the default player ship
464 	Default_player_model = cur_model_index = get_default_player_ship_index();
465 
466 	Id_select_type_start = (int)(Ship_info.size() + 2);
467 	Id_select_type_jump_node = (int)(Ship_info.size() + 1);
468 	Id_select_type_waypoint = (int)(Ship_info.size());
469 	Fred_main_wnd -> init_tools();
470 
471 	Script_system.RunInitFunctions();
472 	Script_system.RunCondition(CHA_GAMEINIT);
473 
474 	return true;
475 }
476 
set_physics_controls()477 void set_physics_controls()
478 {
479 	physics_init(&view_physics);
480 	view_physics.max_vel.xyz.x *= physics_speed / 3.0f;
481 	view_physics.max_vel.xyz.y *= physics_speed / 3.0f;
482 	view_physics.max_vel.xyz.z *= physics_speed / 3.0f;
483 	view_physics.max_rear_vel *= physics_speed / 3.0f;
484 
485 	view_physics.max_rotvel.xyz.x *= physics_rot / 30.0f;
486 	view_physics.max_rotvel.xyz.y *= physics_rot / 30.0f;
487 	view_physics.max_rotvel.xyz.z *= physics_rot / 30.0f;
488 	view_physics.flags |= PF_ACCELERATES | PF_SLIDE_ENABLED;
489 	theApp.write_ini_file(1);
490 }
491 
create_object_on_grid(int waypoint_instance)492 int create_object_on_grid(int waypoint_instance)
493 {
494 	int obj = -1;
495 	float rval;
496 	vec3d dir,pos;
497 
498 	g3_point_to_vec_delayed(&dir, marking_box.x2, marking_box.y2);
499 
500 	rval = fvi_ray_plane(&pos, &The_grid->center, &The_grid->gmatrix.vec.uvec, &view_pos, &dir, 0.0f);
501 
502 	if (rval>=0.0f) {
503 		unmark_all();
504 		obj = create_object(&pos, waypoint_instance);
505 		if (obj >= 0) {
506 			mark_object(obj);
507 			FREDDoc_ptr->autosave("object create");
508 
509 		} else if (obj == -1)
510 			Fred_main_wnd->MessageBox("Maximum ship limit reached.  Can't add any more ships.");
511 	}
512 
513 	return obj;
514 }
515 
fix_ship_name(int ship)516 void fix_ship_name(int ship)
517 {
518 	int i = 1;
519 
520 	do {
521 		sprintf(Ships[ship].ship_name, "U.R.A. Moron %d", i++);
522 	} while (query_ship_name_duplicate(ship));
523 }
524 
create_ship(matrix * orient,vec3d * pos,int ship_type)525 int create_ship(matrix *orient, vec3d *pos, int ship_type)
526 {
527 	int obj, z1, z2;
528 	float temp_max_hull_strength;
529 	ship_info *sip;
530 
531 	// Save the Current Working dir to restore in a minute - fred is being stupid
532 	char pwd[MAX_PATH_LEN];
533 	getcwd(pwd, MAX_PATH_LEN); // get the present working dir - probably <fs2path>[/modpath]/data/missions/
534 
535 	// "pop" and cfile_chdirs off the stack
536 	chdir(Fred_base_dir);
537 
538 	obj = ship_create(orient, pos, ship_type);
539 	if (obj == -1)
540 		return -1;
541 
542 	// ok, done with file io, restore the pwd
543 	chdir(pwd);
544 
545 	Objects[obj].phys_info.speed = 33.0f;
546 
547 	ship *shipp = &Ships[Objects[obj].instance];
548 	sip = &Ship_info[shipp->ship_info_index];
549 
550 	if (query_ship_name_duplicate(Objects[obj].instance))
551 		fix_ship_name(Objects[obj].instance);
552 
553 	// set up model stuff - only needs to be done once, not every frame
554 	model_set_up_techroom_instance(sip, shipp->model_instance_num);
555 
556 	// default stuff according to species and IFF
557 	shipp->team = Species_info[Ship_info[shipp->ship_info_index].species].default_iff;
558 	resolve_parse_flags(&Objects[obj], Iff_info[shipp->team].default_parse_flags);
559 
560 	// default shield setting
561 	shipp->special_shield = -1;
562 	z1 = Shield_sys_teams[shipp->team];
563 	z2 = Shield_sys_types[ship_type];
564     if (((z1 == 1) && z2) || (z2 == 1))
565         Objects[obj].flags.set(Object::Object_Flags::No_shields);
566 
567 	// set orders according to whether the ship is on the player ship's team
568 	{
569 		object *temp_objp;
570 		ship *temp_shipp = NULL;
571 
572 		// find the first player ship
573 		for (temp_objp = GET_FIRST(&obj_used_list); temp_objp != END_OF_LIST(&obj_used_list); temp_objp = GET_NEXT(temp_objp))
574 		{
575 			if (temp_objp->type == OBJ_START)
576 			{
577 				temp_shipp = &Ships[temp_objp->instance];
578 				break;
579 			}
580 		}
581 
582 		// set orders if teams match, or if player couldn't be found
583 		if (temp_shipp == NULL || shipp->team == temp_shipp->team)
584 		{
585 			// if this ship is not a small ship, then make the orders be the default orders without
586 			// the depart item
587 			if (!(sip->is_small_ship()))
588 			{
589 				shipp->orders_accepted = ship_get_default_orders_accepted( sip );
590 				shipp->orders_accepted &= ~DEPART_ITEM;
591 			}
592 		}
593 		else
594 		{
595 			shipp->orders_accepted = 0;
596 		}
597 	}
598 
599 	// calc kamikaze stuff
600 	if (shipp->use_special_explosion)
601 	{
602 		temp_max_hull_strength = (float)shipp->special_exp_blast;
603 	}
604 	else
605 	{
606 		temp_max_hull_strength = sip->max_hull_strength;
607 	}
608 
609 	Ai_info[shipp->ai_index].kamikaze_damage = (int) std::min(1000.0f, 200.0f + (temp_max_hull_strength / 4.0f));
610 
611 	return obj;
612 }
613 
query_ship_name_duplicate(int ship)614 int query_ship_name_duplicate(int ship)
615 {
616 	int i;
617 
618 	for (i=0; i<MAX_SHIPS; i++)
619 		if ((i != ship) && (Ships[i].objnum != -1))
620 			if (!stricmp(Ships[i].ship_name, Ships[ship].ship_name))
621 				return 1;
622 
623 	return 0;
624 }
625 
copy_bits(int * dest,int src,int mask)626 void copy_bits(int *dest, int src, int mask)
627 {
628 	*dest &= ~mask;
629 	*dest |= src & mask;
630 }
631 
dup_object(object * objp)632 int dup_object(object *objp)
633 {
634 	int i, j, n, inst, obj = -1;
635 	ai_info *aip1, *aip2;
636 	object *objp1, *objp2;
637 	ship_subsys *subp1, *subp2;
638 	static int waypoint_instance(-1);
639 
640 	if (!objp) {
641 		waypoint_instance = -1;
642 		return 0;
643 	}
644 
645 	inst = objp->instance;
646 	if ((objp->type == OBJ_SHIP) || (objp->type == OBJ_START)) {
647 		obj = create_ship(&objp->orient, &objp->pos, Ships[inst].ship_info_index);
648 		if (obj == -1)
649 			return -1;
650 
651 		n = Objects[obj].instance;
652 		Ships[n].team = Ships[inst].team;
653 		Ships[n].arrival_cue = dup_sexp_chain(Ships[inst].arrival_cue);
654 		Ships[n].departure_cue = dup_sexp_chain(Ships[inst].departure_cue);
655 		Ships[n].cargo1 = Ships[inst].cargo1;
656 		Ships[n].arrival_location = Ships[inst].arrival_location;
657 		Ships[n].departure_location = Ships[inst].departure_location;
658 		Ships[n].arrival_delay = Ships[inst].arrival_delay;
659 		Ships[n].departure_delay = Ships[inst].departure_delay;
660 		Ships[n].weapons = Ships[inst].weapons;
661 		Ships[n].hotkey = Ships[inst].hotkey;
662 
663 		aip1 = &Ai_info[Ships[n].ai_index];
664 		aip2 = &Ai_info[Ships[inst].ai_index];
665 		aip1->behavior = aip2->behavior;
666 		aip1->ai_class = aip2->ai_class;
667 		for (i=0; i<MAX_AI_GOALS; i++)
668 			aip1->goals[i] = aip2->goals[i];
669 
670 		if (aip2->ai_flags[AI::AI_Flags::Kamikaze])
671 			aip1->ai_flags.set(AI::AI_Flags::Kamikaze);
672 		if (aip2->ai_flags[AI::AI_Flags::No_dynamic])
673 			aip2->ai_flags.set(AI::AI_Flags::No_dynamic);
674 
675 		aip1->kamikaze_damage = aip2->kamikaze_damage;
676 
677 		objp1 = &Objects[obj];
678 		objp2 = &Objects[Ships[inst].objnum];
679 		objp1->phys_info.speed = objp2->phys_info.speed;
680 		objp1->phys_info.fspeed = objp2->phys_info.fspeed;
681 		objp1->hull_strength = objp2->hull_strength;
682 		objp1->shield_quadrant[0] = objp2->shield_quadrant[0];
683 
684 		subp1 = GET_FIRST(&Ships[n].subsys_list);
685 		subp2 = GET_FIRST(&Ships[inst].subsys_list);
686 		while (subp1 != END_OF_LIST(&Ships[n].subsys_list)) {
687 			Assert(subp2 != END_OF_LIST(&Ships[inst].subsys_list));
688 			subp1 -> current_hits = subp2 -> current_hits;
689 			subp1 = GET_NEXT(subp1);
690 			subp2 = GET_NEXT(subp2);
691 		}
692 
693 		for (i=0; i<Num_reinforcements; i++)
694 			if (!stricmp(Reinforcements[i].name, Ships[inst].ship_name)) {
695 				if (Num_reinforcements < MAX_REINFORCEMENTS) {
696 					j = Num_reinforcements++;
697 					strcpy_s(Reinforcements[j].name, Ships[n].ship_name);
698 					Reinforcements[j].type = Reinforcements[i].type;
699 					Reinforcements[j].uses = Reinforcements[i].uses;
700 				}
701 
702 				break;
703 			}
704 
705 	} else if (objp->type == OBJ_WAYPOINT) {
706 		obj = create_waypoint(&objp->pos, waypoint_instance);
707 		waypoint_instance = Objects[obj].instance;
708 	}
709 
710 	if (obj == -1)
711 		return -1;
712 
713 	Objects[obj].pos = objp->pos;
714 	Objects[obj].orient = objp->orient;
715     Objects[obj].flags.set(Object::Object_Flags::Temp_marked);
716 	return obj;
717 }
718 
create_object(vec3d * pos,int waypoint_instance)719 int create_object(vec3d *pos, int waypoint_instance)
720 {
721 	int obj, n;
722 
723 	if (cur_model_index == Id_select_type_waypoint)
724 		obj = create_waypoint(pos, waypoint_instance);
725 
726 	else if (cur_model_index == Id_select_type_start) {
727 		if (Player_starts >= MAX_PLAYERS) {
728 			Fred_main_wnd->MessageBox("Unable to create new player start point.\n"
729 				"You have reached the maximum limit.", NULL, MB_OK | MB_ICONEXCLAMATION);
730 			obj = -2;
731 
732 		} else if (The_mission.game_type & MISSION_TYPE_SINGLE) {
733 			Fred_main_wnd->MessageBox("You can't have more than one player start in\n"
734 				"single player missions.\n", NULL, MB_OK | MB_ICONEXCLAMATION);
735 			obj = -2;
736 
737 		} else if (The_mission.game_type & MISSION_TYPE_TRAINING) {
738 			Fred_main_wnd->MessageBox("You can't have more than one player start in\n"
739 				"a training missions.\n", NULL, MB_OK | MB_ICONEXCLAMATION);
740 			obj = -2;
741 
742 		} else
743 			obj = create_player(Player_starts, pos, NULL, Default_player_model);
744 
745 	} else if (cur_model_index == Id_select_type_jump_node) {
746 		CJumpNode jnp(pos);
747 		obj = jnp.GetSCPObjectNumber();
748 		Jump_nodes.push_back(std::move(jnp));
749 	} else if(Ship_info[cur_model_index].flags[Ship::Info_Flags::No_fred]){
750 		obj = -1;
751 	} else {  // creating a ship
752 		obj = create_ship(NULL, pos, cur_model_index);
753 		if (obj == -1)
754 			return -1;
755 
756 		n = Objects[obj].instance;
757 		Ships[n].arrival_cue = alloc_sexp("true", SEXP_ATOM, SEXP_ATOM_OPERATOR, -1, -1);
758 		Ships[n].departure_cue = alloc_sexp("false", SEXP_ATOM, SEXP_ATOM_OPERATOR, -1, -1);
759 		Ships[n].cargo1 = 0;
760 	}
761 
762 	if (obj < 0)
763 		return obj;
764 
765 	obj_merge_created_list();
766 	set_modified();
767 	Update_window = 1;
768 	return obj;
769 }
770 
create_player(int num,vec3d * pos,matrix * orient,int type,int init)771 int create_player(int num, vec3d *pos, matrix *orient, int type, int init)
772 {
773 	int obj;
774 
775 	if (type == -1){
776 		type = Default_player_model;
777 	}
778 
779 	Assert(type >= 0);
780 	Assert(Player_starts < MAX_PLAYERS);
781 	Player_starts++;
782 	obj = create_ship(orient, pos, type);
783 	Objects[obj].type = OBJ_START;
784 
785 	// be sure arrival/departure cues are set
786 	Ships[Objects[obj].instance].arrival_cue = Locked_sexp_true;
787 	Ships[Objects[obj].instance].departure_cue = Locked_sexp_false;
788 	obj_merge_created_list();
789 	set_modified();
790 	return obj;
791 }
792 
create_waypoint(vec3d * pos,int waypoint_instance)793 int create_waypoint(vec3d *pos, int waypoint_instance)
794 {
795 	int obj = waypoint_add(pos, waypoint_instance);
796 	set_modified();
797 	return obj;
798 }
799 
create_new_mission()800 void create_new_mission()
801 {
802 	reset_mission();
803 	*Mission_filename = 0;
804 	FREDDoc_ptr->autosave("nothing");
805 	Undo_count = 0;
806 }
807 
reset_mission()808 void reset_mission()
809 {
810 	clear_mission();
811 
812 	player_start1 = create_player(0, &vmd_zero_vector, &vmd_identity_matrix);
813 	stars_post_level_init();
814 }
815 
clear_mission()816 void clear_mission()
817 {
818 	char *str;
819 	int i, j, count;
820 	CTime t;
821 
822 	// clean up everything we need to before we reset back to defaults.
823 	if (Briefing_dialog){
824 		Briefing_dialog->reset_editor();
825 	}
826 
827 	extern void allocate_parse_text(size_t size);
828 	allocate_parse_text( PARSE_TEXT_SIZE );
829 
830 	The_mission.cutscenes.clear();
831 	fiction_viewer_reset();
832 	cmd_brief_reset();
833 	mission_event_shutdown();
834 
835 	Asteroid_field.num_initial_asteroids = 0;  // disable asteroid field by default.
836 	Asteroid_field.speed = 0.0f;
837 	vm_vec_make(&Asteroid_field.min_bound, -1000.0f, -1000.0f, -1000.0f);
838 	vm_vec_make(&Asteroid_field.max_bound,  1000.0f,  1000.0f,  1000.0f);
839 	vm_vec_make(&Asteroid_field.inner_min_bound, -500.0f, -500.0f, -500.0f);
840 	vm_vec_make(&Asteroid_field.inner_max_bound,  500.0f,  500.0f,  500.0f);
841 	Asteroid_field.has_inner_bound = 0;
842 	Asteroid_field.field_type = FT_ACTIVE;
843 	Asteroid_field.debris_genre = DG_ASTEROID;
844 	Asteroid_field.field_debris_type[0] = -1;
845 	Asteroid_field.field_debris_type[1] = -1;
846 	Asteroid_field.field_debris_type[2] = -1;
847 
848 	strcpy_s(Mission_parse_storm_name, "none");
849 
850 	obj_init();
851 	model_free_all();				// Free all existing models
852 	ai_init();
853 	ship_init();
854 	jumpnode_level_close();
855 	waypoint_level_close();
856 
857 	Num_wings = 0;
858 	for (i=0; i<MAX_WINGS; i++){
859 		Wings[i].wave_count = 0;
860 		Wings[i].wing_squad_filename[0] = '\0';
861 		Wings[i].wing_insignia_texture = -1;
862 	}
863 
864 	for (i=0; i<MAX_IFFS; i++){
865 		Shield_sys_teams[i] = 0;
866 	}
867 
868 	for (i=0; i<MAX_SHIP_CLASSES; i++){
869 		Shield_sys_types[i] = 0;
870 	}
871 
872 	Num_ai_dock_names = 0;
873 	Num_reinforcements = 0;
874 	set_cur_indices(-1);
875 
876 	str = reg_read_string("SOFTWARE\\Microsoft\\Windows\\CurrentVersion", "RegisteredOwner", NULL);
877 	if (!str) {
878 		str = reg_read_string("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion", "RegisteredOwner", NULL);
879 		if (!str) {
880 			str = getenv("USERNAME");
881 			if (!str){
882 				str = UNKNOWN_USER;
883 			}
884 		}
885 	}
886 
887 	t = CTime::GetCurrentTime();
888 	strcpy_s(The_mission.name, "Untitled");
889 	strncpy(The_mission.author, str, NAME_LENGTH - 1);
890 	The_mission.author[NAME_LENGTH - 1] = 0;
891 	strcpy_s(The_mission.created, t.Format("%x at %X"));
892 	strcpy_s(The_mission.modified, The_mission.created);
893 	strcpy_s(The_mission.notes, "This is a FRED2_OPEN created mission.\n");
894 	strcpy_s(The_mission.mission_desc, "Put mission description here\n");
895 	The_mission.game_type = MISSION_TYPE_SINGLE;
896 	strcpy_s(The_mission.squad_name, "");
897 	strcpy_s(The_mission.squad_filename, "");
898 	The_mission.num_respawns = 3;
899 	The_mission.max_respawn_delay = -1;
900 
901 	Player_starts = 0;
902 	Num_teams = 1;
903 
904 	// reset alternate name & callsign stuff
905 	for(i=0; i<MAX_SHIPS; i++){
906 		strcpy_s(Fred_alt_names[i], "");
907 		strcpy_s(Fred_callsigns[i], "");
908 	}
909 
910 	// set up the default ship types for all teams.  For now, this is the same class
911 	// of ships for all teams
912 	for (i=0; i<MAX_TVT_TEAMS; i++) {
913 		count = 0;
914 		for ( j = 0; j < ship_info_size(); j++ ) {
915 			if (Ship_info[j].flags[Ship::Info_Flags::Default_player_ship]) {
916 				Team_data[i].ship_list[count] = j;
917 				strcpy_s(Team_data[i].ship_list_variables[count], "");
918 				Team_data[i].ship_count[count] = 5;
919 				strcpy_s(Team_data[i].ship_count_variables[count], "");
920 				count++;
921 			}
922 		}
923 		Team_data[i].num_ship_choices = count;
924 
925 		count = 0;
926 		for ( j = 0; j < weapon_info_size(); j++ ) {
927 			if (Weapon_info[j].wi_flags[Weapon::Info_Flags::Player_allowed]) {
928 				if (Weapon_info[j].subtype == WP_LASER) {
929 					Team_data[i].weaponry_count[count] = 16;
930 				} else {
931 					Team_data[i].weaponry_count[count] = 500;
932 				}
933 				Team_data[i].weaponry_pool[count] = j;
934 				strcpy_s(Team_data[i].weaponry_pool_variable[count], "");
935 				strcpy_s(Team_data[i].weaponry_amount_variable[count], "");
936 				count++;
937 			}
938 			Team_data[i].weapon_required[j] = false;
939 		}
940 		Team_data[i].num_weapon_choices = count;
941 	}
942 
943 	*Parse_text = *Parse_text_raw = '\0';
944 	Parse_text[1] = Parse_text_raw[1] = 0;
945 
946 	waypoint_parse_init();
947 	Num_mission_events = 0;
948 	Num_goals = 0;
949 	unmark_all();
950 	obj_init();
951 	model_free_all();				// Free all existing models
952 	fred_render_init();
953 	init_sexp();
954 	messages_init();
955 	brief_reset();
956 	debrief_reset();
957 	ship_init();
958 	event_music_reset_choices();
959 	clear_texture_replacements();
960 
961 	Event_annotations.clear();
962 
963 	mission_parse_reset_alt();		// alternate ship type names
964 	mission_parse_reset_callsign();
965 
966 	strcpy(Cargo_names[0], "Nothing");
967 	Num_cargo = 1;
968 	set_physics_controls();
969 
970 	// reset background bitmaps and suns
971 	stars_pre_level_init();
972 	Nebula_index = 0;
973 	Mission_palette = 1;
974 	Nebula_pitch = (int) ((float) (Random::next() & 0x0fff) * 360.0f / 4096.0f);
975 	Nebula_bank = (int) ((float) (Random::next() & 0x0fff) * 360.0f / 4096.0f);
976 	Nebula_heading = (int) ((float) (Random::next() & 0x0fff) * 360.0f / 4096.0f);
977 	Neb2_awacs = -1.0f;
978 	Neb2_poof_flags = 0;
979 	strcpy_s(Neb2_texture_name, "");
980 	for(i=0; i<(int)MAX_NEB2_POOFS; i++){
981 		Neb2_poof_flags |= (1<<i);
982 	}
983 
984 	Nmodel_flags = DEFAULT_NMODEL_FLAGS;
985 	Nmodel_num = -1;
986 	Nmodel_instance_num = -1;
987 	vm_set_identity(&Nmodel_orient);
988 	Nmodel_bitmap = -1;
989 
990 	The_mission.contrail_threshold = CONTRAIL_THRESHOLD_DEFAULT;
991 
992 	// Goober5000
993 	The_mission.command_persona = Default_command_persona;
994 	strcpy_s(The_mission.command_sender, DEFAULT_COMMAND);
995 
996 	// Goober5000: reset ALL mission flags, not just nebula!
997 	The_mission.flags.reset();
998 	The_mission.support_ships.max_support_ships = -1;	// negative means infinite
999 	The_mission.support_ships.max_hull_repair_val = 0.0f;
1000 	The_mission.support_ships.max_subsys_repair_val = 100.0f;
1001 	The_mission.ai_profile = &Ai_profiles[Default_ai_profile];
1002 
1003 	nebula_init(Nebula_filenames[Nebula_index], Nebula_pitch, Nebula_bank, Nebula_heading);
1004 
1005 	strcpy_s(The_mission.loading_screen[GR_640],"");
1006 	strcpy_s(The_mission.loading_screen[GR_1024],"");
1007 	strcpy_s(The_mission.skybox_model, "");
1008 	vm_set_identity(&The_mission.skybox_orientation);
1009 	strcpy_s(The_mission.envmap_name, "");
1010 	The_mission.skybox_flags = DEFAULT_NMODEL_FLAGS;
1011 
1012 	// no sound environment
1013 	The_mission.sound_environment.id = -1;
1014 
1015 	ENVMAP = -1;
1016 
1017 	set_modified(FALSE);
1018 	Update_window = 1;
1019 }
1020 
query_valid_object(int index)1021 int query_valid_object(int index)
1022 {
1023 	int obj_found = FALSE;
1024 	object *ptr;
1025 
1026 	if (index < 0 || index >= MAX_OBJECTS || Objects[index].type == OBJ_NONE)
1027 		return FALSE;
1028 
1029 	ptr = GET_FIRST(&obj_used_list);
1030 	while (ptr != END_OF_LIST(&obj_used_list)) {
1031 		Assert(ptr->type != OBJ_NONE);
1032 		if (OBJ_INDEX(ptr) == index)
1033 			obj_found = TRUE;
1034 
1035 		ptr = GET_NEXT(ptr);
1036 	}
1037 
1038 	Assert(obj_found);  // just to make sure it's in the list like it should be.
1039 	return TRUE;
1040 }
1041 
query_valid_ship(int index)1042 int query_valid_ship(int index)
1043 {
1044 	int obj_found = FALSE;
1045 	object *ptr;
1046 
1047 	if (index < 0 || index >= MAX_OBJECTS || Objects[index].type != OBJ_SHIP)
1048 		return FALSE;
1049 
1050 	ptr = GET_FIRST(&obj_used_list);
1051 	while (ptr != END_OF_LIST(&obj_used_list)) {
1052 		Assert(ptr->type != OBJ_NONE);
1053 		if (OBJ_INDEX(ptr) == index)
1054 			obj_found = TRUE;
1055 
1056 		ptr = GET_NEXT(ptr);
1057 	}
1058 
1059 	Assert(obj_found);  // just to make sure it's in the list like it should be.
1060 	return TRUE;
1061 }
1062 
query_valid_waypoint(int index)1063 int query_valid_waypoint(int index)
1064 {
1065 	int obj_found = FALSE;
1066 	object *ptr;
1067 
1068 	if (index < 0 || index >= MAX_OBJECTS || Objects[index].type != OBJ_WAYPOINT)
1069 		return FALSE;
1070 
1071 	ptr = GET_FIRST(&obj_used_list);
1072 	while (ptr != END_OF_LIST(&obj_used_list)) {
1073 		Assert(ptr->type != OBJ_NONE);
1074 		if (OBJ_INDEX(ptr) == index)
1075 			obj_found = TRUE;
1076 
1077 		ptr = GET_NEXT(ptr);
1078 	}
1079 
1080 	Assert(obj_found);  // just to make sure it's in the list like it should be.
1081 	return TRUE;
1082 }
1083 
1084 // Sets the current object to whatever is specified or advances to the next object
1085 // in the list if nothing is passed.
set_cur_object_index(int obj)1086 void set_cur_object_index(int obj)
1087 {
1088 	if (obj < 0)
1089 		unmark_all();
1090 	else
1091 		mark_object(obj);
1092 
1093 	set_cur_indices(obj);  // select the new object
1094 	Update_ship = Update_wing = 1;
1095 	Waypoint_editor_dialog.initialize_data(1);
1096 	Update_window = 1;
1097 }
1098 
1099 // changes the currently selected wing.  It is assumed that cur_wing == cur_ship's wing
1100 // number.  Don't call this if this won't be true, or else you'll screw things up.
set_cur_wing(int wing)1101 void set_cur_wing(int wing)
1102 {
1103 	cur_wing = wing;
1104 /*	if (cur_ship != -1)
1105 		Assert(cur_wing == Ships[cur_ship].wingnum);
1106 	if ((cur_object_index != -1) && (Objects[cur_object_index].type == OBJ_SHIP))
1107 		Assert(cur_wing == Ships[Objects[cur_object_index].instance].wingnum);*/
1108 	Update_wing = 1;
1109 	Update_window = 1;
1110 }
1111 
1112 // sets up the various cur_* global variables related to the selecting of an object.  This
1113 // is an internal function that shouldn't typically get called directly.  Use set_cur_object_index() instead.
set_cur_indices(int obj)1114 void set_cur_indices(int obj)
1115 {
1116 	int i;
1117 	object *ptr;
1118 	CSingleLock sync(&CS_cur_object_index);
1119 
1120 	sync.Lock();  // Don't modify until it's unlocked (if it's locked elsewhere).
1121 	if (query_valid_object(obj)) {
1122 		cur_object_index = obj;
1123 		cur_ship = cur_wing = -1;
1124 		cur_waypoint_list = NULL;
1125 		cur_waypoint = NULL;
1126 
1127 		if ((Objects[obj].type == OBJ_SHIP) || (Objects[obj].type == OBJ_START)) {
1128 			cur_ship = Objects[obj].instance;
1129 			cur_wing = Ships[cur_ship].wingnum;
1130 			if (cur_wing >= 0)
1131 				for (i=0; i<Wings[cur_wing].wave_count; i++)
1132 					if (wing_objects[cur_wing][i] == cur_object_index) {
1133 						cur_wing_index = i;
1134 						break;
1135 					}
1136 
1137 		} else if (Objects[obj].type == OBJ_WAYPOINT) {
1138 			cur_waypoint = find_waypoint_with_instance(Objects[obj].instance);
1139 			Assert(cur_waypoint != NULL);
1140 			cur_waypoint_list = cur_waypoint->get_parent_list();
1141 		}
1142 
1143 		return;
1144 	}
1145 
1146 	if (obj == -1 || !Num_objects) {
1147 		cur_object_index = cur_ship = cur_wing = -1;
1148 		cur_waypoint_list = NULL;
1149 		cur_waypoint = NULL;
1150 		return;
1151 	}
1152 
1153 	if (query_valid_object(cur_object_index))
1154 		ptr = Objects[cur_object_index].next;
1155 	else
1156 		ptr = GET_FIRST(&obj_used_list);
1157 
1158 	if (ptr == END_OF_LIST(&obj_used_list))
1159 		ptr = ptr->next;
1160 
1161 	Assert(ptr != END_OF_LIST(&obj_used_list));
1162 	cur_object_index = OBJ_INDEX(ptr);
1163 	Assert(ptr->type != OBJ_NONE);
1164 	cur_ship = cur_wing = -1;
1165 	cur_waypoint_list = NULL;
1166 	cur_waypoint = NULL;
1167 
1168 	if (ptr->type == OBJ_SHIP) {
1169 		cur_ship = ptr->instance;
1170 		cur_wing = Ships[cur_ship].wingnum;
1171 		for (i=0; i<Wings[cur_wing].wave_count; i++)
1172 			if (wing_objects[cur_wing][i] == cur_object_index) {
1173 				cur_wing_index = i;
1174 				break;
1175 			}
1176 
1177 	} else if (ptr->type == OBJ_WAYPOINT) {
1178 		cur_waypoint = find_waypoint_with_instance(ptr->instance);
1179 		Assert(cur_waypoint != NULL);
1180 		cur_waypoint_list = cur_waypoint->get_parent_list();
1181 	}
1182 }
1183 
update_dialog_boxes()1184 int update_dialog_boxes()
1185 {
1186 	int z;
1187 
1188 	nprintf(("Fred routing", "updating dialog boxes\n"));
1189 
1190 	// check wing first, since ships are dependent on wings, but not the reverse
1191 	z = Wing_editor_dialog.update_data(0);
1192 	if (z) {
1193 		nprintf(("Fred routing", "wing dialog save failed\n"));
1194 		Wing_editor_dialog.SetWindowPos(&Fred_main_wnd->wndTop, 0, 0, 0, 0,
1195 			SWP_SHOWWINDOW | SWP_NOMOVE | SWP_NOSIZE);
1196 
1197 		return z;
1198 	}
1199 
1200 	z = Ship_editor_dialog.update_data(0);
1201 	if (z) {
1202 		nprintf(("Fred routing", "ship dialog save failed\n"));
1203 		Ship_editor_dialog.SetWindowPos(&Fred_main_wnd->wndTop, 0, 0, 0, 0,
1204 			SWP_SHOWWINDOW | SWP_NOMOVE | SWP_NOSIZE);
1205 
1206 		return z;
1207 	}
1208 
1209 	z = Waypoint_editor_dialog.update_data(0);
1210 	if (z) {
1211 		nprintf(("Fred routing", "waypoint dialog save failed\n"));
1212 		Waypoint_editor_dialog.SetWindowPos(&Fred_main_wnd->wndTop, 0, 0, 0, 0,
1213 			SWP_SHOWWINDOW | SWP_NOMOVE | SWP_NOSIZE);
1214 
1215 		return z;
1216 	}
1217 
1218 	update_map_window();
1219 	return 0;
1220 }
1221 
delete_object(int obj)1222 int delete_object(int obj)
1223 {
1224 	int r;
1225 
1226 	Ship_editor_dialog.bypass_all++;
1227 	r = common_object_delete(obj);
1228 	Ship_editor_dialog.bypass_all--;
1229 	return r;
1230 }
1231 
delete_object(object * ptr)1232 int delete_object(object *ptr)
1233 {
1234 	int r;
1235 
1236 	Ship_editor_dialog.bypass_all++;
1237 	r = common_object_delete(OBJ_INDEX(ptr));
1238 	Ship_editor_dialog.bypass_all--;
1239 	return r;
1240 }
1241 
delete_ship(int ship)1242 int delete_ship(int ship)
1243 {
1244 	int r;
1245 
1246 	Ship_editor_dialog.bypass_all++;
1247 	r = common_object_delete(Ships[ship].objnum);
1248 	Ship_editor_dialog.bypass_all--;
1249 	return r;
1250 }
1251 
common_object_delete(int obj)1252 int common_object_delete(int obj)
1253 {
1254 	char msg[255], *name;
1255 	int i, z, r, type;
1256 	object *objp;
1257 	SCP_list<CJumpNode>::iterator jnp;
1258 
1259 	type = Objects[obj].type;
1260 	if (type == OBJ_START) {
1261 		i = Objects[obj].instance;
1262 		if (Player_starts < 2) {  // player 1 start
1263 			Fred_main_wnd->MessageBox("Must have at least 1 player starting point!",
1264 				NULL, MB_OK | MB_ICONEXCLAMATION);
1265 
1266 			unmark_object(obj);
1267 			return 1;
1268 		}
1269 
1270 		Assert((i >= 0) && (i < MAX_SHIPS));
1271 		sprintf(msg, "Player %d", i + 1);
1272 		name = msg;
1273 		r = reference_handler(name, REF_TYPE_PLAYER, obj);
1274 		if (r)
1275 			return r;
1276 
1277 		if (Ships[i].wingnum >= 0) {
1278 			r = delete_ship_from_wing(i);
1279 			if (r)
1280 				return r;
1281 		}
1282 
1283 		Objects[obj].type = OBJ_SHIP;  // was allocated as a ship originally, so remove as such.
1284 		invalidate_references(name, REF_TYPE_PLAYER);
1285 
1286 		// check if any ship is docked with this ship and break dock if so
1287 		while (object_is_docked(&Objects[obj]))
1288 		{
1289 			ai_do_objects_undocked_stuff(&Objects[obj], dock_get_first_docked_object(&Objects[obj]));
1290 		}
1291 
1292 		if (Player_start_shipnum == i) {  // need a new single player start.
1293 			objp = GET_FIRST(&obj_used_list);
1294 			while (objp != END_OF_LIST(&obj_used_list)) {
1295 				if (objp->type == OBJ_START) {
1296 					Player_start_shipnum = objp->instance;
1297 					break;
1298 				}
1299 
1300 				objp = GET_NEXT(objp);
1301 			}
1302 		}
1303 
1304 		Player_starts--;
1305 
1306 	} else if (type == OBJ_WAYPOINT) {
1307 		waypoint *wpt = find_waypoint_with_instance(Objects[obj].instance);
1308 		Assert(wpt != NULL);
1309 		waypoint_list *wp_list = wpt->get_parent_list();
1310 		int index = calc_waypoint_list_index(Objects[obj].instance);
1311 		int count = (int) wp_list->get_waypoints().size();
1312 
1313 		// we'll end up deleting the path, so check for path references
1314 		if (count == 1) {
1315 			name = wp_list->get_name();
1316 			r = reference_handler(name, REF_TYPE_PATH, obj);
1317 			if (r)
1318 				return r;
1319 		}
1320 
1321 		// check for waypoint references
1322 		sprintf(msg, "%s:%d", wp_list->get_name(), index + 1);
1323 		name = msg;
1324 		r = reference_handler(name, REF_TYPE_WAYPOINT, obj);
1325 		if (r)
1326 			return r;
1327 
1328 		// at this point we've confirmed we want to delete it
1329 
1330 		invalidate_references(name, REF_TYPE_WAYPOINT);
1331 		if (count == 1) {
1332 			invalidate_references(wp_list->get_name(), REF_TYPE_PATH);
1333 		}
1334 
1335 		// the actual removal code has been moved to this function in waypoints.cpp
1336 		waypoint_remove(wpt);
1337 
1338 	} else if (type == OBJ_SHIP) {
1339 		name = Ships[Objects[obj].instance].ship_name;
1340 		r = reference_handler(name, REF_TYPE_SHIP, obj);
1341 		if (r)
1342 			return r;
1343 
1344 		z = Objects[obj].instance;
1345 		if (Ships[z].wingnum >= 1) {
1346 			invalidate_references(name, REF_TYPE_SHIP);
1347 			r = delete_ship_from_wing(z);
1348 			if (r)
1349 				return r;
1350 
1351 		} else if (Ships[z].wingnum >= 0) {
1352 			r = delete_ship_from_wing(z);
1353 			if (r)
1354 				return r;
1355 
1356 			invalidate_references(name, REF_TYPE_SHIP);
1357 		}
1358 
1359 		for (i=0; i<Num_reinforcements; i++)
1360 			if (!stricmp(name, Reinforcements[i].name)) {
1361 				delete_reinforcement(i);
1362 				break;
1363 			}
1364 
1365 		// check if any ship is docked with this ship and break dock if so
1366 		while (object_is_docked(&Objects[obj]))
1367 		{
1368 			ai_do_objects_undocked_stuff(&Objects[obj], dock_get_first_docked_object(&Objects[obj]));
1369 		}
1370 
1371 	} else if (type == OBJ_POINT) {
1372 		Assert(Briefing_dialog);
1373 		Briefing_dialog->delete_icon(Objects[obj].instance);
1374 		Update_window = 1;
1375 		return 0;
1376 
1377 	} else if (type == OBJ_JUMP_NODE) {
1378 		for (jnp = Jump_nodes.begin(); jnp != Jump_nodes.end(); ++jnp) {
1379 			if(jnp->GetSCPObject() == &Objects[obj])
1380 				break;
1381 		}
1382 
1383 		// come on, WMC, we don't want to call obj_delete twice...
1384 		// fool the destructor into not calling obj_delete yet
1385 		Objects[obj].type = OBJ_NONE;
1386 
1387 		// now call the destructor
1388 		Jump_nodes.erase(jnp);
1389 
1390 		// now restore the jump node type so that the below unmark and obj_delete will work
1391 		Objects[obj].type = OBJ_JUMP_NODE;
1392 	}
1393 
1394 	unmark_object(obj);
1395 
1396 	//we need to call obj_delete() even if obj is a jump node
1397 	//the statement "delete Objects[obj].jnp" deletes the jnp object
1398 	//obj_delete() frees up the object slot where the node used to reside.
1399 	//if we don't call this then the node will still show up in fred and you can try to delete it twice
1400 	//this causes an ugly crash.
1401 	obj_delete(obj);
1402 	set_modified();
1403 	Update_window = 1;
1404 	return 0;
1405 }
1406 
delete_marked()1407 void delete_marked()
1408 {
1409 	object *ptr, *next;
1410 
1411 	delete_flag = 0;
1412 	ptr = GET_FIRST(&obj_used_list);
1413 	while (ptr != END_OF_LIST(&obj_used_list)) {
1414 		next = GET_NEXT(ptr);
1415 		if (ptr->flags[Object::Object_Flags::Marked])
1416 			if (delete_object(ptr) == 2)  // user went to a reference, so don't get in the way.
1417 				break;
1418 
1419 		ptr = next;
1420 	}
1421 
1422 	if (!delete_flag)
1423 		set_cur_object_index(-1);
1424 
1425 	Update_window = 1;
1426 }
1427 
delete_reinforcement(int num)1428 void delete_reinforcement(int num)
1429 {
1430 	int i;
1431 
1432 	for (i=num; i<Num_reinforcements-1; i++)
1433 		Reinforcements[i] = Reinforcements[i + 1];
1434 
1435 	Num_reinforcements--;
1436 	set_modified();
1437 }
1438 
1439 // delete ship, removing it from its wing if necessary.
delete_ship_from_wing(int ship)1440 int delete_ship_from_wing(int ship)
1441 {
1442 	char name[NAME_LENGTH];
1443 	int i, r, wing, end;
1444 
1445 	wing = Ships[ship].wingnum;
1446 	if (wing >= 0) {
1447 		if (Wings[wing].wave_count == 1) {
1448 			cur_wing = -1;
1449 			Update_wing = 1;
1450 			r = delete_wing(wing, 1);
1451 			if (r) {
1452 				if (r == 2){
1453 					delete_flag = 1;
1454 				}
1455 
1456 				return r;
1457 			}
1458 
1459 		} else {
1460 			i = Wings[wing].wave_count;
1461 			end = i - 1;
1462 			while (i--){
1463 				if (wing_objects[wing][i] == Ships[ship].objnum){
1464 					break;
1465 				}
1466 			}
1467 
1468 			Assert(i != -1);  // Error, object should be in wing.
1469 			if (Wings[wing].special_ship == i){
1470 				Wings[wing].special_ship = 0;
1471 			} else if (Wings[wing].special_ship > i) {
1472 				Wings[wing].special_ship--;
1473 			}
1474 
1475 			if (i != end) {
1476 				wing_objects[wing][i] = wing_objects[wing][end];
1477 				Wings[wing].ship_index[i] = Wings[wing].ship_index[end];
1478 				if (Objects[wing_objects[wing][i]].type == OBJ_SHIP) {
1479 					wing_bash_ship_name(name, Wings[wing].name, i + 1);
1480 					rename_ship(Wings[wing].ship_index[i], name);
1481 				}
1482 			}
1483 
1484 			if (Wings[wing].threshold >= Wings[wing].wave_count){
1485 				Wings[wing].threshold = Wings[wing].wave_count - 1;
1486 			}
1487 
1488 			Wings[wing].wave_count--;
1489 			if (Wings[wing].wave_count && (Wings[wing].threshold >= Wings[wing].wave_count)){
1490 				Wings[wing].threshold = Wings[wing].wave_count - 1;
1491 			}
1492 		}
1493 	}
1494 
1495 	set_modified();
1496 	return 0;
1497 }
1498 
1499 //	Return true if current object is valid and is in a wing.
1500 //	Else return false.
query_object_in_wing(int obj)1501 int query_object_in_wing(int obj)
1502 {
1503 	if (query_valid_object(obj)){
1504 		if (Ships[Objects[obj].instance].wingnum != -1){
1505 			return TRUE;
1506 		}
1507 	}
1508 
1509 	return FALSE;
1510 }
1511 
mark_object(int obj)1512 void mark_object(int obj)
1513 {
1514 	Assert(query_valid_object(obj));
1515 	if (!(Objects[obj].flags[Object::Object_Flags::Marked])) {
1516         Objects[obj].flags.set(Object::Object_Flags::Marked);  // set as marked
1517 		Marked++;
1518 		Update_window = 1;
1519 		if (cur_object_index == -1){
1520 			set_cur_object_index(obj);
1521 		}
1522 		Update_ship = Update_wing = 1;
1523 		Waypoint_editor_dialog.initialize_data(1);
1524 	}
1525 }
1526 
unmark_object(int obj)1527 void unmark_object(int obj)
1528 {
1529 	Assert(query_valid_object(obj));
1530 	if (Objects[obj].flags[Object::Object_Flags::Marked]) {
1531         Objects[obj].flags.remove(Object::Object_Flags::Marked);
1532 		Marked--;
1533 		Update_window = 1;
1534 		if (obj == cur_object_index) {  // need to find a new index
1535 			object *ptr;
1536 
1537 			ptr = GET_FIRST(&obj_used_list);
1538 			while (ptr != END_OF_LIST(&obj_used_list)) {
1539 				if (ptr->flags[Object::Object_Flags::Marked]) {
1540 					set_cur_object_index(OBJ_INDEX(ptr));  // found one
1541 					return;
1542 				}
1543 
1544 				ptr = GET_NEXT(ptr);
1545 			}
1546 
1547 			set_cur_object_index(-1);  // can't find one; nothing is marked.
1548 		}
1549 		Update_ship = Update_wing = 1;
1550 		Waypoint_editor_dialog.initialize_data(1);
1551 	}
1552 }
1553 
1554 // clears the marked flag of all objects (so nothing is marked)
unmark_all()1555 void unmark_all()
1556 {
1557 	int i;
1558 
1559 	if (Marked) {
1560 		for (i=0; i<MAX_OBJECTS; i++){
1561             Objects[i].flags.remove(Object::Object_Flags::Marked);
1562 		}
1563 
1564 		Marked = 0;
1565 		Update_window = 1;
1566 		set_cur_object_index(-1);
1567 	}
1568 }
1569 
clear_menu(CMenu * ptr)1570 void clear_menu(CMenu *ptr)
1571 {
1572 	int count;
1573 
1574 	count = ptr->GetMenuItemCount();
1575 	while (count--){
1576 		ptr->DeleteMenu(count, MF_BYPOSITION);
1577 	}
1578 }
1579 
generate_wing_popup_menu(CMenu * mptr,int first_id,int state)1580 void generate_wing_popup_menu(CMenu *mptr, int first_id, int state)
1581 {
1582 	int i, z, columns, rows, count;
1583 
1584 	columns = 1;
1585 	rows = Num_wings;
1586 	while (rows > 25) {
1587 		columns++;
1588 		rows = Num_wings / columns;
1589 	}
1590 
1591 	count = rows + 1;
1592 	for (i=0; i<MAX_WINGS; i++){
1593 		if (Wings[i].wave_count) {
1594 			z = state | MF_STRING;
1595 			if (!count--) {
1596 				count = rows;
1597 				z |= MF_MENUBARBREAK;
1598 			}
1599 
1600 			mptr->AppendMenu(z, first_id + i, Wings[i].name);
1601 		}
1602 	}
1603 
1604 	mptr->DeleteMenu(ID_PLACEHOLDER, MF_BYCOMMAND);
1605 }
1606 
generate_ship_popup_menu(CMenu * mptr,int first_id,int state,int filter)1607 void generate_ship_popup_menu(CMenu *mptr, int first_id, int state, int filter)
1608 {
1609 	int z, ship, columns, rows, count, num_ships;
1610 	object *ptr;
1611 
1612 	columns = 1;
1613 	num_ships = ship_get_num_ships();
1614 	rows = num_ships;
1615 	while (rows > 25) {
1616 		columns++;
1617 		rows = num_ships / columns;
1618 	}
1619 
1620 	count = rows + 1;
1621 	ptr = GET_FIRST(&obj_used_list);
1622 	while (ptr != END_OF_LIST(&obj_used_list)) {
1623 		if ((ptr->type == OBJ_SHIP) || ((ptr->type == OBJ_START) && (filter & SHIP_FILTER_PLAYERS))) {
1624 			z = 1;
1625 			if (filter & SHIP_FILTER_FLYABLE) {
1626 				if (!Ship_info[Ships[get_ship_from_obj(ptr)].ship_info_index].is_flyable()){
1627 					z = 0;
1628 				}
1629 			}
1630 
1631 			if (z) {
1632 				z = state | MF_STRING;
1633 				if (!count--) {
1634 					count = rows;
1635 					z |= MF_MENUBARBREAK;
1636 				}
1637 
1638 				ship = ptr->instance;
1639 				mptr->AppendMenu(z, first_id + ship, Ships[ship].ship_name);
1640 			}
1641 		}
1642 
1643 		ptr = GET_NEXT(ptr);
1644 	}
1645 
1646 	mptr->DeleteMenu(ID_PLACEHOLDER, MF_BYCOMMAND);
1647 }
1648 
1649 // Alternate string lookup function, taking a CString instead.  The reason that it's here,
1650 // instead of parselo.cpp, is because the class CString require an include of windows.h,
1651 // which everyone wants to avoid including in any freespace header files.  So..
string_lookup(const CString & str1,char * strlist[],int max)1652 int string_lookup(const CString &str1, char *strlist[], int max)
1653 {
1654 	int	i;
1655 
1656 	for (i=0; i<max; i++) {
1657 		Assert(strlen(strlist[i]));
1658 
1659 		if (!stricmp((LPCTSTR) str1, strlist[i])){
1660 			return i;
1661 		}
1662 	}
1663 
1664 	return -1;
1665 }
1666 
gray_menu_tree(CMenu * base)1667 int gray_menu_tree(CMenu *base)
1668 {
1669 	int i, z, count = 0;
1670 	CMenu *submenu;
1671 
1672 	i = base->GetMenuItemCount();
1673 	while (i--) {
1674 		if ((submenu = base->GetSubMenu(i))>0) {
1675 			if (gray_menu_tree(submenu)) {
1676 				count++;
1677 			} else {
1678 				base->EnableMenuItem(i, MF_GRAYED | MF_BYPOSITION);
1679 			}
1680 
1681 		} else {
1682 			z = base->GetMenuState(i, MF_BYPOSITION);
1683 			if (z == MF_ENABLED){
1684 				count++;
1685 			}
1686 		}
1687 	}
1688 
1689 	return count;
1690 }
1691 
query_initial_orders_conflict(int wing)1692 int query_initial_orders_conflict(int wing)
1693 {
1694 	int i, z;
1695 
1696 	Assert(wing != -1);
1697 	if (wing == -1){
1698 		return 0;
1699 	}
1700 
1701 	if (query_initial_orders_empty(Wings[wing].ai_goals)){
1702 		return 0;
1703 	}
1704 
1705 	i = Wings[wing].wave_count;  // wing has orders, now check ships.
1706 	while (i--) {
1707 		z = Ships[Objects[wing_objects[wing][i]].instance].ai_index;
1708 		if (!query_initial_orders_empty(Ai_info[z].goals)){  // ship also has orders
1709 			return 1;
1710 		}
1711 	}
1712 
1713 	return 0;
1714 }
1715 
query_initial_orders_empty(ai_goal * ai_goals)1716 int query_initial_orders_empty(ai_goal *ai_goals)
1717 {
1718 	int i;
1719 
1720 	for (i=0; i<MAX_AI_GOALS; i++){
1721 		if (ai_goals[i].ai_mode != AI_GOAL_NONE){
1722 			return 0;
1723 		}
1724 	}
1725 
1726 	return 1;
1727 }
1728 
set_reinforcement(char * name,int state)1729 int set_reinforcement(char *name, int state)
1730 {
1731 	int i, index, cur = -1;
1732 
1733 	for (i=0; i<Num_reinforcements; i++){
1734 		if (!stricmp(Reinforcements[i].name, name)){
1735 			cur = i;
1736 		}
1737 	}
1738 
1739 	if (!state && (cur != -1)) {
1740 		Num_reinforcements--;
1741 		Reinforcements[cur] = Reinforcements[Num_reinforcements];
1742 
1743 		// clear the ship/wing flag for this reinforcement
1744 		index = ship_name_lookup(name);
1745 		if ( index != -1 ){
1746             Ships[index].flags.remove(Ship::Ship_Flags::Reinforcement);
1747 		} else {
1748 			index = wing_name_lookup(name);
1749 			if ( index != -1 ){
1750                 Wings[index].flags.remove(Ship::Wing_Flags::Reinforcement);
1751 			}
1752 		}
1753 		if (index == -1 ){
1754 			Int3();				// get allender -- coudln't find ship/wing for clearing reinforcement flag
1755 		}
1756 
1757 		set_modified();
1758 		return -1;
1759 	}
1760 
1761 	if (state && (cur == -1) && (Num_reinforcements < MAX_REINFORCEMENTS)) {
1762 		Assert(strlen(name) < NAME_LENGTH);
1763 		strcpy_s(Reinforcements[Num_reinforcements].name, name);
1764 		Reinforcements[Num_reinforcements].uses = 1;
1765 		Reinforcements[Num_reinforcements].arrival_delay = 0;
1766 		memset( Reinforcements[Num_reinforcements].no_messages, 0, MAX_REINFORCEMENT_MESSAGES * NAME_LENGTH );
1767 		memset( Reinforcements[Num_reinforcements].yes_messages, 0, MAX_REINFORCEMENT_MESSAGES * NAME_LENGTH );
1768 		Num_reinforcements++;
1769 
1770 		// set the reinforcement flag on the ship or wing
1771 		index = ship_name_lookup(name);
1772 		if ( index != -1 ){
1773 			Ships[index].flags.set(Ship::Ship_Flags::Reinforcement);
1774 		} else {
1775 			index = wing_name_lookup(name);
1776 			if ( index != -1 ){
1777                 Wings[index].flags.set(Ship::Wing_Flags::Reinforcement);
1778 			}
1779 		}
1780 		if ( index == -1 ){
1781 			Int3();				// get allender -- coudln't find ship/wing for setting reinforcement flag
1782 		}
1783 
1784 		set_modified();
1785 		return 1;
1786 	}
1787 
1788 	// this code will take care of setting the bits for the ship/wing flags
1789 	if ( state && (cur != -1) ) {
1790 		// set the reinforcement flag on the ship or wing
1791 		index = ship_name_lookup(name);
1792 		if ( index != -1 ){
1793 			Ships[index].flags.set(Ship::Ship_Flags::Reinforcement);
1794 		} else {
1795 			index = wing_name_lookup(name);
1796 			if ( index != -1 ){
1797                 Wings[index].flags.set(Ship::Wing_Flags::Reinforcement);
1798 			}
1799 		}
1800 		if ( index == -1 ){
1801 			Int3();				// get allender -- coudln't find ship/wing for setting reinforcement flag
1802 		}
1803 	}
1804 
1805 	return 0;
1806 }
1807 
get_docking_list(int model_index)1808 int get_docking_list(int model_index)
1809 {
1810 	int i;
1811 	polymodel *pm;
1812 
1813 	pm = model_get(model_index);
1814 	Assert(pm->n_docks <= MAX_DOCKS);
1815 	for (i=0; i<pm->n_docks; i++)
1816 		Docking_bay_list[i] = pm->docking_bays[i].name;
1817 
1818 	return pm->n_docks;
1819 }
1820 
1821 // DA 1/7/99 These ship names are not variables
rename_ship(int ship,char * name)1822 int rename_ship(int ship, char *name)
1823 {
1824 	int i;
1825 
1826 	Assert(ship >= 0);
1827 	Assert(strlen(name) < NAME_LENGTH);
1828 
1829 	update_sexp_references(Ships[ship].ship_name, name);
1830 	ai_update_goal_references(REF_TYPE_SHIP, Ships[ship].ship_name, name);
1831 	update_texture_replacements(Ships[ship].ship_name, name);
1832 	for (i=0; i<Num_reinforcements; i++)
1833 		if (!stricmp(Ships[ship].ship_name, Reinforcements[i].name)) {
1834 			strcpy_s(Reinforcements[i].name, name);
1835 		}
1836 
1837 	strcpy_s(Ships[ship].ship_name, name);
1838 	if (ship == cur_ship)
1839 		Ship_editor_dialog.m_ship_name = _T(name);
1840 
1841 	return 0;
1842 }
1843 
invalidate_references(char * name,int type)1844 int invalidate_references(char *name, int type)
1845 {
1846 	char new_name[512];
1847 	int i;
1848 
1849 	sprintf(new_name, "<%s>", name);
1850 	update_sexp_references(name, new_name);
1851 	ai_update_goal_references(type, name, new_name);
1852 	update_texture_replacements(name, new_name);
1853 	for (i=0; i<Num_reinforcements; i++)
1854 		if (!stricmp(name, Reinforcements[i].name)) {
1855 			strcpy_s(Reinforcements[i].name, new_name);
1856 		}
1857 
1858 	return 0;
1859 }
1860 
internal_integrity_check()1861 int internal_integrity_check()
1862 {
1863 	int i;
1864 
1865 	for (i=0; i<Num_mission_events; i++)
1866 		verify_sexp_tree(Mission_events[i].formula);
1867 
1868 	for (i=0; i<Num_goals; i++)
1869 		verify_sexp_tree(Mission_goals[i].formula);
1870 
1871 	for (i=0; i<MAX_WINGS; i++)
1872 		if (Wings[i].wave_count) {
1873 			verify_sexp_tree(Wings[i].arrival_cue);
1874 			verify_sexp_tree(Wings[i].departure_cue);
1875 		}
1876 
1877 	for (i=0; i<MAX_SHIPS; i++)
1878 		if (Ships[i].objnum >= 0) {
1879 			verify_sexp_tree(Ships[i].arrival_cue);
1880 			verify_sexp_tree(Ships[i].departure_cue);
1881 			if (Ships[i].ai_index < 0)
1882 				Assert(0);
1883 			if (Ai_info[Ships[i].ai_index].shipnum != i)
1884 				Int3();
1885 		}
1886 
1887 	return 0;
1888 }
1889 
correct_marking()1890 void correct_marking()
1891 {
1892 	object *ptr;
1893 
1894 	ptr = GET_FIRST(&obj_used_list);
1895 	while (ptr != END_OF_LIST(&obj_used_list)) {
1896 		if (ptr->flags[Object::Object_Flags::Marked]) {
1897 			if (ptr->flags[Object::Object_Flags::Hidden])
1898 				unmark_object(OBJ_INDEX(ptr));
1899 
1900 			else switch (ptr->type) {
1901 				case OBJ_WAYPOINT:
1902 					if (!Show_waypoints)
1903 						unmark_object(OBJ_INDEX(ptr));
1904 					break;
1905 
1906 				case OBJ_START:
1907 					if (!Show_starts || !Show_ships)
1908 						unmark_object(OBJ_INDEX(ptr));
1909 					break;
1910 
1911 				case OBJ_SHIP:
1912 					if (!Show_ships)
1913 						unmark_object(OBJ_INDEX(ptr));
1914 
1915 					if (!Show_iff[Ships[ptr->instance].team])
1916 						unmark_object(OBJ_INDEX(ptr));
1917 
1918 					break;
1919 			}
1920 		}
1921 
1922 		ptr = GET_NEXT(ptr);
1923 	}
1924 }
1925 
1926 // Fills a combo box with a list of all docking points of type 'type' on ship 'ship'.
1927 // Item data is the actual docking point index.
set_valid_dock_points(int ship,int type,CComboBox * box)1928 void set_valid_dock_points(int ship, int type, CComboBox *box)
1929 {
1930 	int i, z, num, model;
1931 
1932 	model = Ship_info[Ships[ship].ship_info_index].model_num;
1933 	num = model_get_num_dock_points(model);
1934 	for (i=0; i<num; i++)
1935 		if (model_get_dock_index_type(model, i) & type) {
1936 			z = box->AddString(model_get_dock_name(model, i));
1937 			box->SetItemData(z, i);
1938 		}
1939 
1940 	Assert(box->GetCount());
1941 }
1942 
1943 // Given an object index, find the ship index for that object.
get_ship_from_obj(int obj)1944 int get_ship_from_obj(int obj)
1945 {
1946 	if ((Objects[obj].type == OBJ_SHIP) || (Objects[obj].type == OBJ_START))
1947 		return Objects[obj].instance;
1948 
1949 	Int3();
1950 	return 0;
1951 }
1952 
get_ship_from_obj(object * objp)1953 int get_ship_from_obj(object *objp)
1954 {
1955 	if ((objp->type == OBJ_SHIP) || (objp->type == OBJ_START))
1956 		return objp->instance;
1957 
1958 	Int3();
1959 	return 0;
1960 }
1961 
ai_update_goal_references(int type,const char * old_name,const char * new_name)1962 void ai_update_goal_references(int type, const char *old_name, const char *new_name)
1963 {
1964 	int i;
1965 
1966 	for (i=0; i<MAX_AI_INFO; i++)  // loop through all Ai_info entries
1967 		if (Ai_info[i].shipnum != -1)  // skip if unused
1968 			ai_update_goal_references(Ai_info[i].goals, type, old_name, new_name);
1969 
1970 	for (i=0; i<MAX_WINGS; i++)
1971 		if (Wings[i].wave_count)
1972 			ai_update_goal_references(Wings[i].ai_goals, type, old_name, new_name);
1973 }
1974 
query_referenced_in_ai_goals(int type,const char * name)1975 int query_referenced_in_ai_goals(int type, const char *name)
1976 {
1977 	int i;
1978 
1979 	for (i=0; i<MAX_AI_INFO; i++)  // loop through all Ai_info entries
1980 		if (Ai_info[i].shipnum >= 0)  // skip if unused
1981 			if (query_referenced_in_ai_goals(Ai_info[i].goals, type, name))
1982 				return Ai_info[i].shipnum | SRC_SHIP_ORDER;
1983 
1984 	for (i=0; i<MAX_WINGS; i++)
1985 		if (Wings[i].wave_count)
1986 			if (query_referenced_in_ai_goals(Wings[i].ai_goals, type, name))
1987 				return i | SRC_WING_ORDER;
1988 
1989 	return 0;
1990 }
1991 
advanced_stricmp(char * one,char * two)1992 int advanced_stricmp(char *one, char *two)
1993 {
1994 	if (!one && !two)
1995 		return 0;
1996 
1997 	if (!one)
1998 		return -1;
1999 
2000 	if (!two)
2001 		return 1;
2002 
2003 	return stricmp(one, two);
2004 }
2005 
2006 // returns 0: go ahead change object
2007 //			  1: don't change it
2008 //			  2: abort (they used cancel to go to reference)
reference_handler(char * name,int type,int obj)2009 int reference_handler(char *name, int type, int obj)
2010 {
2011 	char msg[2048], text[128], type_name[128];
2012 	int r, n, node;
2013 
2014 	switch (type) {
2015 		case REF_TYPE_SHIP:
2016 			sprintf(type_name, "Ship \"%s\"", name);
2017 			break;
2018 
2019 		case REF_TYPE_WING:
2020 			sprintf(type_name, "Wing \"%s\"", name);
2021 			break;
2022 
2023 		case REF_TYPE_PLAYER:
2024 			strcpy_s(type_name, name);
2025 			break;
2026 
2027 		case REF_TYPE_WAYPOINT:
2028 			sprintf(type_name, "Waypoint \"%s\"", name);
2029 			break;
2030 
2031 		case REF_TYPE_PATH:
2032 			sprintf(type_name, "Waypoint path \"%s\"", name);
2033 			break;
2034 
2035 		default:
2036 			Error(LOCATION, "Type unknown for object \"%s\".  Let Hoffos know now!", name);
2037 	}
2038 
2039 	r = query_referenced_in_sexp(type, name, &node);
2040 	if (r) {
2041 		n = r & SRC_DATA_MASK;
2042 		switch (r & SRC_MASK) {
2043 			case SRC_SHIP_ARRIVAL:
2044 				sprintf(text, "the arrival cue of ship \"%s\"", Ships[n].ship_name);
2045 				break;
2046 
2047 			case SRC_SHIP_DEPARTURE:
2048 				sprintf(text, "the departure cue of ship \"%s\"", Ships[n].ship_name);
2049 				break;
2050 
2051 			case SRC_WING_ARRIVAL:
2052 				sprintf(text, "the arrival cue of wing \"%s\"", Wings[n].name);
2053 				break;
2054 
2055 			case SRC_WING_DEPARTURE:
2056 				sprintf(text, "the departure cue of wing \"%s\"", Wings[n].name);
2057 				break;
2058 
2059 			case SRC_EVENT:
2060 				if (*Mission_events[n].name)
2061 					sprintf(text, "event \"%s\"", Mission_events[n].name);
2062 				else
2063 					sprintf(text, "event #%d", n);
2064 
2065 				break;
2066 
2067 			case SRC_MISSION_GOAL:
2068 				if (*Mission_goals[n].name)
2069 					sprintf(text, "mission goal \"%s\"", Mission_goals[n].name);
2070 				else
2071 					sprintf(text, "mission goal #%d", n);
2072 
2073 				break;
2074 
2075 			case SRC_DEBRIEFING:
2076 				sprintf(text, "debriefing #%d", n);
2077 				break;
2078 
2079 			case SRC_BRIEFING:
2080 				sprintf(text, "briefing #%d", n);
2081 				break;
2082 
2083 			default:  // very bad.  Someone added an sexp somewhere and didn't change this.
2084 				Warning(LOCATION, "\"%s\" referenced by an unknown sexp source!  "
2085 					"Run for the hills and let Hoffoss know right now!", name);
2086 
2087 				delete_flag = 1;
2088 				return 2;
2089 		}
2090 
2091 		sprintf(msg, "%s is referenced by %s (possibly more sexps).\n"
2092 			"Do you want to delete it anyway?\n\n"
2093 			"(click Cancel to go to the reference)", type_name, text);
2094 
2095 		r = sexp_reference_handler(node, r, msg);
2096 		if (r == 1) {
2097 			if (obj >= 0)
2098 				unmark_object(obj);
2099 
2100 			return 1;
2101 		}
2102 
2103 		if (r == 2) {
2104 			delete_flag = 1;
2105 			return 2;
2106 		}
2107 	}
2108 
2109 	r = query_referenced_in_ai_goals(type, name);
2110 	if (r) {
2111 		n = r & SRC_DATA_MASK;
2112 		switch (r & SRC_MASK) {
2113 			case SRC_SHIP_ORDER:
2114 				sprintf(text, "ship \"%s\"", Ships[n].ship_name);
2115 				break;
2116 
2117 			case SRC_WING_ORDER:
2118 				sprintf(text, "wing \"%s\"", Wings[n].name);
2119 				break;
2120 
2121 			default:  // very bad.  Someone added an sexp somewhere and didn't change this.
2122 				Error(LOCATION, "\"%s\" referenced by an unknown initial orders source!  "
2123 					"Run for the hills and let Hoffoss know right now!", name);
2124 		}
2125 
2126 		sprintf(msg, "%s is referenced by the initial orders of %s (possibly \n"
2127 			"more initial orders).  Do you want to delete it anyway?\n\n"
2128 			"(click Cancel to go to the reference)", type_name, text);
2129 
2130 		r = orders_reference_handler(r, msg);
2131 		if (r == 1) {
2132 			if (obj >= 0)
2133 				unmark_object(obj);
2134 
2135 			return 1;
2136 		}
2137 
2138 		if (r == 2) {
2139 			delete_flag = 1;
2140 			return 2;
2141 		}
2142 	}
2143 
2144 	if ((type != REF_TYPE_SHIP) && (type != REF_TYPE_WING))
2145 		return 0;
2146 
2147 	for (n=0; n<Num_reinforcements; n++)
2148 		if (!stricmp(name, Reinforcements[n].name))
2149 			break;
2150 
2151 	if (n < Num_reinforcements) {
2152 		sprintf(msg, "Ship \"%s\" is a reinforcement unit.\n"
2153 			"Do you want to delete it anyway?", name);
2154 
2155 		r = Fred_main_wnd->MessageBox(msg, NULL, MB_YESNO | MB_ICONEXCLAMATION);
2156 		if (r == IDNO) {
2157 			if (obj >= 0)
2158 				unmark_object(obj);
2159 
2160 			return 1;
2161 		}
2162 	}
2163 
2164 	return 0;
2165 }
2166 
orders_reference_handler(int code,char * msg)2167 int orders_reference_handler(int code, char *msg)
2168 {
2169 	int r, n;
2170 
2171 	r = Fred_main_wnd->MessageBox(msg, "Warning", MB_YESNOCANCEL | MB_ICONEXCLAMATION);
2172 	if (r == IDNO)
2173 		return 1;
2174 
2175 	if (r == IDYES)
2176 		return 0;
2177 
2178 	ShipGoalsDlg dlg_goals;
2179 
2180 	n = code & SRC_DATA_MASK;
2181 	switch (code & SRC_MASK) {
2182 		case SRC_SHIP_ORDER:
2183 			unmark_all();
2184 			mark_object(Ships[n].objnum);
2185 
2186 			dlg_goals.self_ship = n;
2187 			dlg_goals.DoModal();
2188 			if (!query_initial_orders_empty(Ai_info[Ships[n].ai_index].goals))
2189 				if ((Ships[n].wingnum >= 0) && (query_initial_orders_conflict(Ships[n].wingnum)))
2190 					Fred_main_wnd->MessageBox("This ship's wing also has initial orders", "Possible conflict");
2191 
2192 			break;
2193 
2194 		case SRC_WING_ORDER:
2195 			unmark_all();
2196 			mark_wing(n);
2197 
2198 			dlg_goals.self_wing = n;
2199 			dlg_goals.DoModal();
2200 			if (query_initial_orders_conflict(n))
2201 				Fred_main_wnd->MessageBox("One or more ships of this wing also has initial orders", "Possible conflict");
2202 
2203 			break;
2204 
2205 		default:  // very bad.  Someone added an sexp somewhere and didn't change this.
2206 			Error(LOCATION, "Unknown initial order reference source");
2207 	}
2208 
2209 	delete_flag = 1;
2210 	return 2;
2211 }
2212 
sexp_reference_handler(int node,int code,char * msg)2213 int sexp_reference_handler(int node, int code, char *msg)
2214 {
2215 	int r;
2216 
2217 	r = Fred_main_wnd->MessageBox(msg, "Warning", MB_YESNOCANCEL | MB_ICONEXCLAMATION);
2218 	if (r == IDNO)
2219 		return 1;
2220 
2221 	if (r == IDYES)
2222 		return 0;
2223 
2224 	switch (code & SRC_MASK) {
2225 		case SRC_SHIP_ARRIVAL:
2226 		case SRC_SHIP_DEPARTURE:
2227 			if (!Ship_editor_dialog.GetSafeHwnd())
2228 				Ship_editor_dialog.Create();
2229 
2230 			Ship_editor_dialog.SetWindowPos(&Fred_main_wnd->wndTop, 0, 0, 0, 0,
2231 				SWP_SHOWWINDOW | SWP_NOMOVE | SWP_NOSIZE);
2232 			Ship_editor_dialog.ShowWindow(SW_RESTORE);
2233 
2234 			Ship_editor_dialog.select_sexp_node = node;
2235 			unmark_all();
2236 			mark_object(Ships[code & SRC_DATA_MASK].objnum);
2237 			break;
2238 
2239 		case SRC_WING_ARRIVAL:
2240 		case SRC_WING_DEPARTURE:
2241 			if (!Wing_editor_dialog.GetSafeHwnd())
2242 				Wing_editor_dialog.Create();
2243 
2244 			Wing_editor_dialog.SetWindowPos(&Fred_main_wnd->wndTop, 0, 0, 0, 0,
2245 				SWP_SHOWWINDOW | SWP_NOMOVE | SWP_NOSIZE);
2246 			Wing_editor_dialog.ShowWindow(SW_RESTORE);
2247 
2248 			Wing_editor_dialog.select_sexp_node = node;
2249 			unmark_all();
2250 			mark_wing(code & SRC_DATA_MASK);
2251 			break;
2252 
2253 		case SRC_EVENT:
2254 			if (Message_editor_dlg) {
2255 				Fred_main_wnd->MessageBox("You must close the message editor before the event editor can be opened");
2256 				break;
2257 			}
2258 
2259 			if (!Event_editor_dlg) {
2260 				Event_editor_dlg = new event_editor;
2261 				Event_editor_dlg->select_sexp_node = node;
2262 				Event_editor_dlg->Create(event_editor::IDD);
2263 			}
2264 
2265 			Event_editor_dlg->SetWindowPos(&CWnd::wndTop, 0, 0, 0, 0, SWP_SHOWWINDOW | SWP_NOMOVE | SWP_NOSIZE);
2266 			Event_editor_dlg->ShowWindow(SW_RESTORE);
2267 			break;
2268 
2269 		case SRC_MISSION_GOAL: {
2270 			CMissionGoalsDlg dlg;
2271 
2272 			dlg.select_sexp_node = node;
2273 			dlg.DoModal();
2274 			break;
2275 		}
2276 
2277 		case SRC_DEBRIEFING: {
2278 			debriefing_editor_dlg dlg;
2279 
2280 			dlg.select_sexp_node = node;
2281 			dlg.DoModal();
2282 			break;
2283 		}
2284 
2285 		case SRC_BRIEFING: {
2286 			if (!Briefing_dialog) {
2287 				Briefing_dialog = new briefing_editor_dlg;
2288 				Briefing_dialog->create();
2289 			}
2290 
2291 			Briefing_dialog->SetWindowPos(&Briefing_dialog->wndTop, 0, 0, 0, 0,
2292 				SWP_SHOWWINDOW | SWP_NOMOVE | SWP_NOSIZE);
2293 			Briefing_dialog->ShowWindow(SW_RESTORE);
2294 			Briefing_dialog->focus_sexp(node);
2295 			break;
2296 		}
2297 
2298 		default:  // very bad.  Someone added an sexp somewhere and didn't change this.
2299 			Error(LOCATION, "Unknown sexp reference source");
2300 	}
2301 
2302 	delete_flag = 1;
2303 	return 2;
2304 }
2305 
object_name(int obj)2306 char *object_name(int obj)
2307 {
2308 	static char text[80];
2309 	waypoint_list *wp_list;
2310 	int waypoint_num;
2311 
2312 	if (!query_valid_object(obj))
2313 		return "*none*";
2314 
2315 	switch (Objects[obj].type) {
2316 		case OBJ_SHIP:
2317 		case OBJ_START:
2318 			return Ships[Objects[obj].instance].ship_name;
2319 
2320 		case OBJ_WAYPOINT:
2321 			wp_list = find_waypoint_list_with_instance(Objects[obj].instance, &waypoint_num);
2322 			Assert(wp_list != NULL);
2323 			sprintf(text, "%s:%d", wp_list->get_name(), waypoint_num + 1);
2324 			return text;
2325 
2326 		case OBJ_POINT:
2327 			return "Briefing icon";
2328 	}
2329 
2330 	return "*unknown*";
2331 }
2332 
get_order_name(int order)2333 const char *get_order_name(int order)
2334 {
2335 	int i;
2336 
2337 	if (order == AI_GOAL_NONE)  // special case
2338 		return "None";
2339 
2340 	for (i=0; i<Ai_goal_list_size; i++)
2341 		if (Ai_goal_list[i].def & order)
2342 			return Ai_goal_list[i].name;
2343 
2344 	return "???";
2345 }
2346 
object_moved(object * objp)2347 void object_moved(object *objp)
2348 {
2349 	if (objp->type == OBJ_WAYPOINT)
2350 	{
2351 		waypoint *wpt = find_waypoint_with_instance(objp->instance);
2352 		Assert(wpt != NULL);
2353 		wpt->set_pos(&objp->pos);
2354 	}
2355 
2356 	if ((objp->type == OBJ_SHIP) || (objp->type == OBJ_START)) // do we have a ship?
2357 	{
2358 		// reset the already-handled flag (inefficient, but it's FRED, so who cares)
2359         for (int i = 0; i < MAX_OBJECTS; i++)
2360             Objects[i].flags.remove(Object::Object_Flags::Docked_already_handled);
2361 
2362 		// move all docked objects docked to me
2363 		dock_move_docked_objects(objp);
2364 	}
2365 }
2366 
2367 // determine if all the ships in a given wing are all marked or not.
query_whole_wing_marked(int wing)2368 int query_whole_wing_marked(int wing)
2369 {
2370 	int count = 0;
2371 	object *ptr;
2372 
2373 	if (!Wings[wing].wave_count)
2374 		return 0;
2375 
2376 	ptr = GET_FIRST(&obj_used_list);
2377 	while (ptr != END_OF_LIST(&obj_used_list)) {
2378 		if (ptr->flags[Object::Object_Flags::Marked])
2379 			if ((ptr->type == OBJ_SHIP) || (ptr->type == OBJ_START))
2380 				if (Ships[get_ship_from_obj(ptr)].wingnum == wing)
2381 					count++;
2382 
2383 		ptr = GET_NEXT(ptr);
2384 	}
2385 
2386 	if (count == Wings[wing].wave_count)
2387 		return 1;
2388 
2389 	return 0;
2390 }
2391 
generate_ship_usage_list(int * arr,int wing)2392 void generate_ship_usage_list(int *arr, int wing)
2393 {
2394 	int i;
2395 
2396 	if (wing < 0) {
2397 		return;
2398 	}
2399 
2400 	i = Wings[wing].wave_count;
2401 	while (i--) {
2402 		arr[Ships[Wings[wing].ship_index[i]].ship_info_index]++;
2403 	}
2404 }
2405 
generate_weaponry_usage_list(int * arr,int wing)2406 void generate_weaponry_usage_list(int *arr, int wing)
2407 {
2408 	int i, j;
2409 	ship_weapon *swp;
2410 
2411 	if (wing < 0)
2412 		return;
2413 
2414 	i = Wings[wing].wave_count;
2415 	while (i--) {
2416 		swp = &Ships[Wings[wing].ship_index[i]].weapons;
2417 		j = swp->num_primary_banks;
2418 		while (j--) {
2419 			if (swp->primary_bank_weapons[j] >= 0 && swp->primary_bank_weapons[j] < weapon_info_size()) {
2420 				arr[swp->primary_bank_weapons[j]]++;
2421 			}
2422 		}
2423 
2424 		j = swp->num_secondary_banks;
2425 		while (j--) {
2426 			if (swp->secondary_bank_weapons[j] >=0 && swp->secondary_bank_weapons[j] < weapon_info_size()) {
2427 				arr[swp->secondary_bank_weapons[j]] += (int) floor((swp->secondary_bank_ammo[j] * swp->secondary_bank_capacity[j] / 100.0f / Weapon_info[swp->secondary_bank_weapons[j]].cargo_size) + 0.5f);
2428 			}
2429 		}
2430 	}
2431 }
2432 
generate_weaponry_usage_list(int team,int * arr)2433 void generate_weaponry_usage_list(int team, int *arr)
2434 {
2435 	int i;
2436 
2437 	for (i=0; i<MAX_WEAPON_TYPES; i++)
2438 		arr[i] = 0;
2439 
2440     if (The_mission.game_type & MISSION_TYPE_MULTI_TEAMS) {
2441 		Assert (team >= 0 && team < MAX_TVT_TEAMS);
2442 
2443 		for (i=0; i<MAX_TVT_WINGS_PER_TEAM; i++) {
2444 			generate_weaponry_usage_list(arr, TVT_wings[(team * MAX_TVT_WINGS_PER_TEAM) + i]);
2445 		}
2446 	}
2447 	else {
2448 		for (i=0; i<MAX_STARTING_WINGS; i++) {
2449 			generate_weaponry_usage_list(arr, Starting_wings[i]);
2450 		}
2451 	}
2452 }
2453 
jumpnode_get_by_name(const CString & name)2454 CJumpNode *jumpnode_get_by_name(const CString& name)
2455 {
2456 	CJumpNode *jnp = jumpnode_get_by_name((LPCTSTR) name);
2457 	return jnp;
2458 }
2459 
2460 // function which adds all current ships in the Fred mission to the passed in combo box.  useful for
2461 // building up ship lists for arrival/departure targets
management_add_ships_to_combo(CComboBox * box,int flags)2462 void management_add_ships_to_combo( CComboBox *box, int flags )
2463 {
2464 	object *objp;
2465 	int id, i, restrict_to_players;
2466 
2467 	box->ResetContent();
2468 
2469 	// add the "special" targets, i.e. any friendly, any hostile, etc.
2470 	if (flags & SHIPS_2_COMBO_SPECIAL)
2471 	{
2472 		for (restrict_to_players = 0; restrict_to_players < 2; restrict_to_players++)
2473 		{
2474 			for (i = 0; i < Num_iffs; i++)
2475 			{
2476 				char tmp[NAME_LENGTH + 15];
2477 				stuff_special_arrival_anchor_name(tmp, i, restrict_to_players, 0);
2478 
2479 				id = box->AddString(tmp);
2480 				box->SetItemData(id, get_special_anchor(tmp));
2481 			}
2482 		}
2483 	}
2484 
2485 	// either add all ships to the list, or only add ships with docking bays.
2486 	if ( flags & SHIPS_2_COMBO_ALL_SHIPS ) {
2487 		for ( objp = GET_FIRST(&obj_used_list); objp != END_OF_LIST(&obj_used_list); objp = GET_NEXT(objp) ) {
2488 			if ( ((objp->type == OBJ_SHIP) || (objp->type == OBJ_START)) && !(objp->flags[Object::Object_Flags::Marked]) ) {
2489 				id = box->AddString(Ships[get_ship_from_obj(objp)].ship_name);
2490 				box->SetItemData(id, get_ship_from_obj(objp));
2491 			}
2492 		}
2493 	} else if ( flags & SHIPS_2_COMBO_DOCKING_BAY_ONLY ) {
2494 		for ( objp = GET_FIRST(&obj_used_list); objp != END_OF_LIST(&obj_used_list); objp = GET_NEXT(objp) ) {
2495 			if ( ((objp->type == OBJ_SHIP) || (objp->type == OBJ_START)) && !(objp->flags[Object::Object_Flags::Marked]) ) {
2496 				polymodel *pm;
2497 
2498 				// determine if this ship has a docking bay
2499 				pm = model_get( Ship_info[Ships[objp->instance].ship_info_index].model_num );
2500 				Assert( pm );
2501 				if ( pm->ship_bay && (pm->ship_bay->num_paths > 0) ) {
2502 					id = box->AddString(Ships[get_ship_from_obj(objp)].ship_name);
2503 					box->SetItemData(id, get_ship_from_obj(objp));
2504 				}
2505 			}
2506 		}
2507 	}
2508 }
2509 
reg_read_string(char * section,char * name,char * default_value)2510 char *reg_read_string( char *section, char *name, char *default_value )
2511 {
2512 	HKEY hKey = NULL;
2513 	DWORD dwType, dwLen;
2514 	char keyname[1024];
2515 	static char tmp_string_data[1024];
2516 	LONG lResult;
2517 
2518 	strcpy_s( keyname, section );
2519 
2520 	lResult = RegOpenKeyEx( HKEY_LOCAL_MACHINE,							// Where it is
2521 											 keyname,								// name of key
2522 											 NULL,									// DWORD reserved
2523 											 KEY_QUERY_VALUE,						// Allows all changes
2524 											 &hKey );								// Location to store key
2525 
2526 	if ( lResult != ERROR_SUCCESS )	{
2527 		mprintf(( "Error opening registry key '%s'\n", keyname ));
2528 		goto Cleanup;
2529 	}
2530 
2531 	if ( !name )	 {
2532 		mprintf(( "No variable name passed\n" ));
2533 		goto Cleanup;
2534 	}
2535 
2536 	dwLen = 1024;
2537 	lResult = RegQueryValueEx( hKey,									// Handle to key
2538 									 name,											// The values name
2539 									 NULL,											// DWORD reserved
2540 	                         &dwType,										// What kind it is
2541 									 (ubyte *)&tmp_string_data,						// value to set
2542 									 &dwLen );								// How many bytes to set
2543 
2544 	if ( lResult != ERROR_SUCCESS )	{
2545 		mprintf(( "Error reading registry key '%s'\n", name ));
2546 		goto Cleanup;
2547 	}
2548 
2549 	default_value = tmp_string_data;
2550 
2551 Cleanup:
2552 	if ( hKey )
2553 		RegCloseKey(hKey);
2554 
2555 	return default_value;
2556 }
2557 
2558 // Goober5000
wing_is_player_wing(int wing)2559 int wing_is_player_wing(int wing)
2560 {
2561 	int i;
2562 
2563 	if (wing < 0)
2564 		return 0;
2565 
2566 	if (The_mission.game_type & MISSION_TYPE_MULTI_TEAMS)
2567 	{
2568 		for (i=0; i<MAX_TVT_WINGS; i++)
2569 		{
2570 			if (wing == TVT_wings[i])
2571 				return 1;
2572 		}
2573 	}
2574 	else
2575 	{
2576 		for (i=0; i<MAX_STARTING_WINGS; i++)
2577 		{
2578 			if (wing == Starting_wings[i])
2579 				return 1;
2580 		}
2581 	}
2582 
2583 	return 0;
2584 }
2585 
2586 // Goober5000
2587 // This must be done when either the wing name or the custom name is changed.
2588 // (It's also duplicated in FS2, in post_process_mission, for setting the indexes at mission load.)
update_custom_wing_indexes()2589 void update_custom_wing_indexes()
2590 {
2591 	int i;
2592 
2593 	for (i = 0; i < MAX_STARTING_WINGS; i++)
2594 	{
2595 		Starting_wings[i] = wing_name_lookup(Starting_wing_names[i], 1);
2596 	}
2597 
2598 	for (i = 0; i < MAX_SQUADRON_WINGS; i++)
2599 	{
2600 		Squadron_wings[i] = wing_name_lookup(Squadron_wing_names[i], 1);
2601 	}
2602 
2603 	for (i = 0; i < MAX_TVT_WINGS; i++)
2604 	{
2605 		TVT_wings[i] = wing_name_lookup(TVT_wing_names[i], 1);
2606 	}
2607 }
2608 
2609 // Goober5000
stuff_special_arrival_anchor_name(char * buf,int iff_index,int restrict_to_players,int retail_format)2610 void stuff_special_arrival_anchor_name(char *buf, int iff_index, int restrict_to_players, int retail_format)
2611 {
2612 	char *iff_name = Iff_info[iff_index].iff_name;
2613 
2614 	// stupid retail hack
2615 	if (retail_format && !stricmp(iff_name, "hostile") && !restrict_to_players)
2616 		iff_name = "enemy";
2617 
2618 	if (restrict_to_players)
2619 		sprintf(buf, "<any %s player>", iff_name);
2620 	else
2621 		sprintf(buf, "<any %s>", iff_name);
2622 
2623 	strlwr(buf);
2624 }
2625 
2626 // Goober5000
stuff_special_arrival_anchor_name(char * buf,int anchor_num,int retail_format)2627 void stuff_special_arrival_anchor_name(char *buf, int anchor_num, int retail_format)
2628 {
2629 	// filter out iff
2630 	int iff_index = anchor_num;
2631 	iff_index &= ~SPECIAL_ARRIVAL_ANCHOR_FLAG;
2632 	iff_index &= ~SPECIAL_ARRIVAL_ANCHOR_PLAYER_FLAG;
2633 
2634 	// filter players
2635 	int restrict_to_players = (anchor_num & SPECIAL_ARRIVAL_ANCHOR_PLAYER_FLAG);
2636 
2637 	// get name
2638 	stuff_special_arrival_anchor_name(buf, iff_index, restrict_to_players, retail_format);
2639 }
2640 
2641 // Goober5000
update_texture_replacements(const char * old_name,const char * new_name)2642 void update_texture_replacements(const char *old_name, const char *new_name)
2643 {
2644 	for (SCP_vector<texture_replace>::iterator ii = Fred_texture_replacements.begin(); ii != Fred_texture_replacements.end(); ++ii)
2645 	{
2646 		if (!stricmp(ii->ship_name, old_name))
2647 			strcpy_s(ii->ship_name, new_name);
2648 	}
2649 }
2650