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