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 "FREDDoc.h"
15 #include "FREDView.h"
16 #include "MainFrm.h"
17 #include "render/3d.h"
18 #include "physics/physics.h"
19 #include "ai/aigoals.h"
20 #include "parse/parselo.h"
21 #include "Management.h"
22 #include "globalincs/linklist.h"
23 #include "InitialStatus.h"
24 #include "WeaponEditorDlg.h"
25 #include "ship/ship.h"
26 #include "TextViewDlg.h"
27 #include "playerman/player.h"				// used for the max_keyed_target stuff
28 #include "IgnoreOrdersDlg.h"
29 #include "mission/missionparse.h"
30 #include "model/model.h"
31 #include "starfield/starfield.h"
32 #include "jumpnode/jumpnode.h"
33 #include "ShipFlagsDlg.h"
34 #include "mission/missionmessage.h"
35 #include "ShipSpecialDamage.h"
36 #include "ShipTexturesDlg.h"
37 #include "ShipSpecialHitpoints.h"
38 #include "altshipclassdlg.h"
39 #include "species_defs/species_defs.h"
40 #include "iff_defs/iff_defs.h"
41 #include "restrictpaths.h"
42 #include "warpparamsdlg.h"
43 
44 #define ID_SHIP_MENU 9000
45 
46 #define NO_PERSONA_INDEX	999
47 
48 #ifdef _DEBUG
49 #undef THIS_FILE
50 static char THIS_FILE[] = __FILE__;
51 #endif
52 
setup(int id,CWnd * wnd)53 void numeric_edit_control::setup(int id, CWnd *wnd)
54 {
55 	control_id = id;
56 	dlg = wnd;
57 }
58 
init(int n)59 void numeric_edit_control::init(int n)
60 {
61 	value = n;
62 	unique = 1;
63 }
64 
set(int n)65 void numeric_edit_control::set(int n)
66 {
67 	if (n != value){
68 		unique = 0;
69 	}
70 }
71 
display()72 void numeric_edit_control::display()
73 {
74 	CString str;
75 
76 	if (unique){
77 		str.Format("%d", value);
78 	}
79 
80 	dlg->GetDlgItem(control_id)->SetWindowText(str);
81 }
82 
save(int * n)83 void numeric_edit_control::save(int *n)
84 {
85 	CString str;
86 
87 	if (control_id) {
88 		dlg->GetDlgItem(control_id)->GetWindowText(str);
89 		if (!str.IsEmpty()){
90 			MODIFY(*n, atoi(str));
91 		}
92 	}
93 }
94 
fix(int n)95 void numeric_edit_control::fix(int n)
96 {
97 	if (unique) {
98 		CString str;
99 		CWnd *w;
100 
101 		value = n;
102 		str.Format("%d", n);
103 		w = dlg->GetDlgItem(control_id);
104 		dlg->GetDlgItem(control_id)->SetWindowText(str);
105 	}
106 }
107 
108 /////////////////////////////////////////////////////////////////////////////
109 // CShipEditorDlg dialog
110 
CShipEditorDlg(CWnd * pParent)111 CShipEditorDlg::CShipEditorDlg(CWnd* pParent /*=NULL*/)
112 	: CDialog(CShipEditorDlg::IDD, pParent)
113 {
114 	//{{AFX_DATA_INIT(CShipEditorDlg)
115 	m_ship_name = _T("");
116 	m_cargo1 = _T("");
117 	m_ship_class = -1;
118 	m_team = -1;
119 	m_arrival_location = -1;
120 	m_departure_location = -1;
121 	m_ai_class = -1;
122 	m_hotkey = -1;
123 	m_update_arrival = FALSE;
124 	m_update_departure = FALSE;
125 	m_arrival_target = -1;
126 	m_departure_target = -1;
127 	m_persona = -1;
128 	//}}AFX_DATA_INIT
129 
130 	m_pSEView = NULL;
131 	initialized = editing = multi_edit = 0;
132 	select_sexp_node = -1;
133 	bypass_errors = 0;
134 }
135 
136 //	Modeless constructor, MK
CShipEditorDlg(CView * pView)137 CShipEditorDlg::CShipEditorDlg(CView* pView)
138 {
139 	m_pSEView = pView;
140 	initialized = editing = 0;
141 	select_sexp_node = -1;
142 }
143 
DoDataExchange(CDataExchange * pDX)144 void CShipEditorDlg::DoDataExchange(CDataExchange* pDX)
145 {
146 	int n;
147 	CString str;
148 
149 	CDialog::DoDataExchange(pDX);
150 	//{{AFX_DATA_MAP(CShipEditorDlg)
151 	DDX_Control(pDX, IDC_NO_DEPARTURE_WARP, m_no_departure_warp);
152 	DDX_Control(pDX, IDC_NO_ARRIVAL_WARP, m_no_arrival_warp);
153 	DDX_Control(pDX, IDC_PLAYER_SHIP, m_player_ship);
154 	DDX_Control(pDX, IDC_DEPARTURE_DELAY_SPIN, m_departure_delay_spin);
155 	DDX_Control(pDX, IDC_ARRIVAL_DELAY_SPIN, m_arrival_delay_spin);
156 	DDX_Control(pDX, IDC_DEPARTURE_TREE, m_departure_tree);
157 	DDX_Control(pDX, IDC_ARRIVAL_TREE, m_arrival_tree);
158 	DDX_Text(pDX, IDC_SHIP_NAME, m_ship_name);
159 	DDX_CBString(pDX, IDC_SHIP_CARGO1, m_cargo1);
160 	DDX_CBIndex(pDX, IDC_SHIP_CLASS, m_ship_class);
161 	DDX_CBIndex(pDX, IDC_SHIP_TEAM, m_team);
162 	DDX_CBIndex(pDX, IDC_ARRIVAL_LOCATION, m_arrival_location);
163 	DDX_CBIndex(pDX, IDC_DEPARTURE_LOCATION, m_departure_location);
164 	DDX_CBIndex(pDX, IDC_AI_CLASS, m_ai_class);
165 	DDX_CBIndex(pDX, IDC_HOTKEY, m_hotkey);
166 	DDX_Check(pDX, IDC_UPDATE_ARRIVAL, m_update_arrival);
167 	DDX_Check(pDX, IDC_UPDATE_DEPARTURE, m_update_departure);
168 	DDX_CBIndex(pDX, IDC_ARRIVAL_TARGET, m_arrival_target);
169 	DDX_CBIndex(pDX, IDC_DEPARTURE_TARGET, m_departure_target);
170 	DDX_CBIndex(pDX, IDC_SHIP_PERSONA, m_persona);
171 	//}}AFX_DATA_MAP
172 	DDV_MaxChars(pDX, m_ship_name, NAME_LENGTH - 1);
173 	DDV_MaxChars(pDX, m_cargo1, NAME_LENGTH - 1);
174 
175 	if (pDX->m_bSaveAndValidate) {  // get dialog control values
176 		GetDlgItem(IDC_ARRIVAL_DELAY)->GetWindowText(str);
177 		n = atoi(str);
178 		if (n < 0){
179 			n = 0;
180 		}
181 
182 		m_arrival_delay.init(n);
183 
184 		GetDlgItem(IDC_ARRIVAL_DISTANCE)->GetWindowText(str);
185 		m_arrival_dist.init(atoi(str));
186 
187 		GetDlgItem(IDC_DEPARTURE_DELAY)->GetWindowText(str);
188 		n = atoi(str);
189 		if (n < 0)
190 			n = 0;
191 		m_departure_delay.init(n);
192 
193 		GetDlgItem(IDC_SCORE)->GetWindowText(str);
194 		m_score.init(atoi(str));
195 
196 		GetDlgItem(IDC_ASSIST_SCORE)->GetWindowText(str);
197 		m_assist_score.init(atoi(str));
198 	}
199 }
200 
BEGIN_MESSAGE_MAP(CShipEditorDlg,CDialog)201 BEGIN_MESSAGE_MAP(CShipEditorDlg, CDialog)
202 	//{{AFX_MSG_MAP(CShipEditorDlg)
203 	ON_WM_CLOSE()
204 	ON_NOTIFY(NM_RCLICK, IDC_ARRIVAL_TREE, OnRclickArrivalTree)
205 	ON_NOTIFY(NM_RCLICK, IDC_DEPARTURE_TREE, OnRclickDepartureTree)
206 	ON_NOTIFY(TVN_BEGINLABELEDIT, IDC_ARRIVAL_TREE, OnBeginlabeleditArrivalTree)
207 	ON_NOTIFY(TVN_BEGINLABELEDIT, IDC_DEPARTURE_TREE, OnBeginlabeleditDepartureTree)
208 	ON_NOTIFY(TVN_ENDLABELEDIT, IDC_ARRIVAL_TREE, OnEndlabeleditArrivalTree)
209 	ON_NOTIFY(TVN_ENDLABELEDIT, IDC_DEPARTURE_TREE, OnEndlabeleditDepartureTree)
210 	ON_BN_CLICKED(IDC_GOALS, OnGoals)
211 	ON_CBN_SELCHANGE(IDC_SHIP_CLASS, OnSelchangeShipClass)
212 	ON_BN_CLICKED(IDC_INITIAL_STATUS, OnInitialStatus)
213 	ON_BN_CLICKED(IDC_WEAPONS, OnWeapons)
214 	ON_BN_CLICKED(IDC_SHIP_RESET, OnShipReset)
215 	ON_BN_CLICKED(IDC_DELETE_SHIP, OnDeleteShip)
216 	ON_BN_CLICKED(IDC_SHIP_TBL, OnShipTbl)
217 	ON_BN_CLICKED(IDC_NEXT, OnNext)
218 	ON_NOTIFY(TVN_SELCHANGED, IDC_ARRIVAL_TREE, OnSelchangedArrivalTree)
219 	ON_NOTIFY(TVN_SELCHANGED, IDC_DEPARTURE_TREE, OnSelchangedDepartureTree)
220 	ON_BN_CLICKED(IDC_HIDE_CUES, OnHideCues)
221 	ON_BN_CLICKED(IDC_PREV, OnPrev)
222 	ON_CBN_SELCHANGE(IDC_ARRIVAL_LOCATION, OnSelchangeArrivalLocation)
223 	ON_BN_CLICKED(IDC_PLAYER_SHIP, OnPlayerShip)
224 	ON_BN_CLICKED(IDC_NO_ARRIVAL_WARP, OnNoArrivalWarp)
225 	ON_BN_CLICKED(IDC_NO_DEPARTURE_WARP, OnNoDepartureWarp)
226 	ON_CBN_SELCHANGE(IDC_DEPARTURE_LOCATION, OnSelchangeDepartureLocation)
227 	ON_CBN_SELCHANGE(IDC_HOTKEY, OnSelchangeHotkey)
228 	ON_BN_CLICKED(IDC_FLAGS, OnFlags)
229 	ON_BN_CLICKED(IDC_IGNORE_ORDERS, OnIgnoreOrders)
230 	ON_BN_CLICKED(IDC_SPECIAL_EXP, OnSpecialExp)
231 	ON_BN_CLICKED(IDC_TEXTURES, OnTextures)
232 	ON_BN_CLICKED(IDC_SPECIAL_HITPOINTS, OnSpecialHitpoints)
233 	ON_BN_CLICKED(IDC_ALT_SHIP_CLASS, OnAltShipClass)
234 	ON_BN_CLICKED(IDC_RESTRICT_ARRIVAL, OnRestrictArrival)
235 	ON_BN_CLICKED(IDC_RESTRICT_DEPARTURE, OnRestrictDeparture)
236 	ON_WM_INITMENU()
237 	ON_BN_CLICKED(IDC_SET_AS_PLAYER_SHIP, OnSetAsPlayerShip)
238 	ON_BN_CLICKED(IDC_CUSTOM_WARPIN_PARAMS, OnBnClickedCustomWarpinParams)
239 	ON_BN_CLICKED(IDC_CUSTOM_WARPOUT_PARAMS, OnBnClickedCustomWarpoutParams)
240 	//}}AFX_MSG_MAP
241 END_MESSAGE_MAP()
242 
243 /////////////////////////////////////////////////////////////////////////////
244 // CShipEditorDlg message handlers
245 
246 BOOL CShipEditorDlg::Create()
247 {
248 	int i;
249 	BOOL r;
250 	CComboBox *ptr;
251 
252 	r = CDialog::Create(IDD, Fred_main_wnd);
253 
254 	ptr = (CComboBox *) GetDlgItem(IDC_ARRIVAL_LOCATION);
255 	ptr->ResetContent();
256 	for (i=0; i<MAX_ARRIVAL_NAMES; i++){
257 		ptr->AddString(Arrival_location_names[i]);
258 	}
259 
260 	ptr = (CComboBox *) GetDlgItem(IDC_DEPARTURE_LOCATION);
261 	ptr->ResetContent();
262 	for (i=0; i<MAX_DEPARTURE_NAMES; i++){
263 		ptr->AddString(Departure_location_names[i]);
264 	}
265 
266 	ptr = (CComboBox *) GetDlgItem(IDC_SHIP_CLASS);
267 	ptr->ResetContent();
268 	for (auto it = Ship_info.cbegin(); it != Ship_info.end(); ++it){
269 		ptr->AddString(it->name);
270 	}
271 
272 	ptr = (CComboBox *) GetDlgItem(IDC_AI_CLASS);
273 	ptr->ResetContent();
274 	for (i=0; i<Num_ai_classes; i++){
275 		ptr->AddString(Ai_class_names[i]);
276 	}
277 
278 	// alternate ship name combobox
279 	ptr = (CComboBox *)GetDlgItem(IDC_SHIP_ALT);
280 	ptr->ResetContent();
281 	ptr->AddString("<none>");
282 	ptr->SetCurSel(0);
283 
284 	// deal with the persona dialog
285 	ptr = (CComboBox *)GetDlgItem(IDC_SHIP_PERSONA);
286 	ptr->ResetContent();
287 	auto index = ptr->AddString("<None>");
288 	if ( index >= 0 ){
289 		ptr->SetItemData(index, NO_PERSONA_INDEX);
290 	}
291 
292 	for ( i = 0; i < Num_personas; i++ ) {
293 		if ( Personas[i].flags & PERSONA_FLAG_WINGMAN ) {
294 			// don't bother putting any vasudan personas on the list -- done automatically by code
295 //			if ( Personas[i].flags & PERSONA_FLAG_VASUDAN ){
296 //				continue;
297 //			}
298 
299 			CString persona_name = Personas[i].name;
300 			if ( Personas[i].flags & PERSONA_FLAG_VASUDAN ){
301 				persona_name += " -Vas";
302 			}
303 
304 			index = ptr->AddString(persona_name);
305 			if ( index >= 0 ){
306 				ptr->SetItemData(index, i);
307 			}
308 		}
309 	}
310 
311 	m_score.setup(IDC_SCORE, this);
312 	m_assist_score.setup(IDC_ASSIST_SCORE, this);
313 	m_arrival_dist.setup(IDC_ARRIVAL_DISTANCE, this);
314 	m_arrival_delay.setup(IDC_ARRIVAL_DELAY, this);
315 	m_departure_delay.setup(IDC_DEPARTURE_DELAY, this);
316 
317 	m_hotkey = 0;
318 	m_arrival_tree.link_modified(&modified);  // provide way to indicate trees are modified in dialog
319 	m_arrival_tree.setup((CEdit *) GetDlgItem(IDC_HELP_BOX));
320 	m_departure_tree.link_modified(&modified);
321 	m_departure_tree.setup();
322 	m_arrival_delay_spin.SetRange(0, 999);
323 	m_departure_delay_spin.SetRange(0, 999);
324 	initialize_data(1);
325 
326 	return r;
327 }
328 
329 //	This gets called when you click on the "X" button.  Note that OnClose
330 //	does not destroy the window.  It only hides it.
OnClose()331 void CShipEditorDlg::OnClose()
332 {
333 	if (verify() && (!bypass_errors)) {
334 		SetWindowPos(&wndTop, 0, 0, 0, 0, SWP_SHOWWINDOW | SWP_NOMOVE | SWP_NOSIZE);
335 		bypass_errors = 0;
336 		return;
337 	}
338 
339 	if (update_data()) {
340 		SetWindowPos(&wndTop, 0, 0, 0, 0, SWP_SHOWWINDOW | SWP_NOMOVE | SWP_NOSIZE);
341 		bypass_errors = 0;
342 		return;
343 	}
344 
345 	SetWindowPos(Fred_main_wnd, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE | SWP_HIDEWINDOW);
346 	Fred_main_wnd->SetWindowPos(&wndTop, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
347 }
348 
Create(LPCTSTR lpszClassName,LPCTSTR lpszWindowName,DWORD dwStyle,const RECT & rect,CWnd * pParentWnd,UINT nID,CCreateContext * pContext)349 BOOL CShipEditorDlg::Create(LPCTSTR lpszClassName, LPCTSTR lpszWindowName, DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID, CCreateContext* pContext)
350 {
351 	BOOL r;
352 
353 	r = CDialog::Create(IDD, pParentWnd);
354 	return r;
355 }
356 
tristate_set(int val,int cur_state)357 int CShipEditorDlg::tristate_set(int val, int cur_state)
358 {
359 	if (val) {
360 		if (!cur_state){
361 			return 2;
362 		}
363 
364 	} else {
365 		if (cur_state){
366 			return 2;
367 		}
368 	}
369 
370 	return cur_state;
371 }
372 
373 // called to initialize the dialog box to reflect what ships we currently have marked.  Any
374 // time what we have marked changes, this should get called again.
375 //
376 // Notes: player_count is the number of player starts marked, when we are in a non-multiplayer
377 // mission (NMM).  In a multiplayer mission (MM), player_count will always be zero.
378 // ship_count in NMM is the number of ships (i.e. not player starts) that are marked.  In MM,
379 // ship_count is the number of ships and player starts.  Total_count is the sum of ship_count
380 // and player_count in all cases.  The reason player_count isn't used in MM, and ship_count
381 // is used instead to track player starts is because in MM, player starts can be edited as
382 // freely as AI ships, and are very likely to be AI ships sometimes.  Thus, treating them like
383 // AI ships instead of player starts simplifies processing.
384 //
initialize_data(int full_update)385 void CShipEditorDlg::initialize_data(int full_update)
386 {
387 	int type, ship_count, player_count, total_count, wing = -1, pvalid_count;
388 	int a_cue, d_cue, cue_init = 0, cargo = 0, base_ship, base_player, pship = -1;
389 	int no_arrival_warp = 0, no_departure_warp = 0, escort_count, ship_orders, current_orders;
390 	int pship_count;  // a total count of the player ships not marked
391 	object *objp;
392 	CWnd *w = NULL;
393 	CString str;
394 	CComboBox *box;
395 	CSingleLock sync(&CS_update);
396 
397 	nprintf(("Fred routing", "Ship dialog load\n"));
398 	if (!GetSafeHwnd() || bypass_all)
399 		return;
400 
401 	sync.Lock();  // don't initialize if we are still updating.  Wait until update is done.
402 
403 	box = (CComboBox *) GetDlgItem(IDC_ARRIVAL_TARGET);
404 	management_add_ships_to_combo( box, SHIPS_2_COMBO_SPECIAL | SHIPS_2_COMBO_ALL_SHIPS );
405 
406 	box = (CComboBox *)GetDlgItem(IDC_DEPARTURE_TARGET);
407 	management_add_ships_to_combo( box, SHIPS_2_COMBO_DOCKING_BAY_ONLY );
408 
409 	if (The_mission.game_type & MISSION_TYPE_MULTI){
410 		mission_type = 0;  // multi player mission
411 	} else {
412 		mission_type = 1;  // non-multiplayer mission (implies single player mission I guess)
413 	}
414 
415 	// figure out what all we are editing.
416 	ship_count = player_count = escort_count = pship_count = pvalid_count = 0;
417 	base_ship = base_player = -1;
418 	enable = p_enable = 1;
419 	objp = GET_FIRST(&obj_used_list);
420 	while (objp != END_OF_LIST(&obj_used_list)) {
421 		if ((objp->type == OBJ_SHIP) && (Ships[objp->instance].flags[Ship::Ship_Flags::Escort])){
422 			escort_count++;  // get a total count of escort ships
423 		}
424 
425 		if (objp->type == OBJ_START){
426 			pship_count++;  // count all player starts in mission
427 		}
428 
429 		if (objp->flags[Object::Object_Flags::Marked]) {
430 			type = objp->type;
431 			if ((type == OBJ_START) && !mission_type){  // in multiplayer missions, starts act like ships
432 				type = OBJ_SHIP;
433 			}
434 
435 			auto i = -1;
436 			if (type == OBJ_START) {
437 				player_count++;
438 				// if player_count is 1, base_player will be the one and only player
439 				i = base_player = objp->instance;
440 
441 			} else if (type == OBJ_SHIP) {
442 				ship_count++;
443 				// if ship_count is 1, base_ship will be the one and only ship
444 				i = base_ship = objp->instance;
445 			}
446 
447 			if (i >= 0){
448 				if (Ship_info[Ships[i].ship_info_index].flags[Ship::Info_Flags::Player_ship]){
449 					pvalid_count++;
450 				}
451 			}
452 		}
453 
454 		objp = GET_NEXT(objp);
455 	}
456 
457 	total_count = ship_count + player_count;  // get total number of objects being edited.
458 	if (total_count > 1){
459 		multi_edit = 1;
460 	} else {
461 		multi_edit = 0;
462 	}
463 
464 	a_cue = d_cue = -1;
465 	m_arrival_location = -1;
466 	m_arrival_dist.blank();
467 	m_arrival_target = -1;
468 	m_arrival_delay.blank();
469 	m_departure_location = -1;
470 	m_departure_target = -1;
471 	m_departure_delay.blank();
472 
473 	player_ship = single_ship = -1;
474 	m_arrival_tree.select_sexp_node = m_departure_tree.select_sexp_node = select_sexp_node;
475 	select_sexp_node = -1;
476 	ship_orders = 0;				// assume they are all the same type
477 	if (ship_count) {
478 		box = (CComboBox *) GetDlgItem(IDC_SHIP_CARGO1);
479 		box->ResetContent();
480 		for (auto i=0; i<Num_cargo; i++){
481 			box->AddString(Cargo_names[i]);
482 		}
483 
484 		if (!multi_edit) {
485 			Assert((ship_count == 1) && (base_ship >= 0));
486 			m_ship_name = Ships[base_ship].ship_name;
487 		} else {
488 			m_ship_name = _T("");
489 		}
490 
491 		m_update_arrival = m_update_departure = 1;
492 		base_player = 0;
493 		objp = GET_FIRST(&obj_used_list);
494 		while (objp != END_OF_LIST(&obj_used_list)) {
495 			if ((objp->type == OBJ_START) || (objp->type == OBJ_SHIP)) {
496 				if (objp->flags[Object::Object_Flags::Marked]) {
497 					// do processing for both ships and players
498 					auto i = get_ship_from_obj(objp);
499 					if (base_player >= 0) {
500 						m_ship_class = Ships[i].ship_info_index;
501 						m_team = Ships[i].team;
502 						pship = (objp->type == OBJ_START) ? 1 : 0;
503 						base_player = -1;
504 
505 					} else {
506 						if (Ships[i].ship_info_index != m_ship_class)
507 							m_ship_class = -1;
508 						if (Ships[i].team != m_team)
509 							m_team = -1;
510 
511 						pship = tristate_set(Objects[Ships[i].objnum].type == OBJ_START, pship);
512 					}
513 
514 					// 'and' in the ship type of this ship to our running bitfield
515 					current_orders = ship_get_default_orders_accepted( &Ship_info[Ships[i].ship_info_index] );
516 					if (!ship_orders){
517 						ship_orders = current_orders;
518 					} else if (ship_orders != current_orders){
519 						ship_orders = -1;
520 					}
521 
522 					if (Ships[i].flags[Ship::Ship_Flags::Escort]){
523 						escort_count--;  // remove marked escorts from count
524 					}
525 
526 					if (Objects[Ships[i].objnum].type == OBJ_START){
527 						pship_count--;  // removed marked starts from count
528 					}
529 
530 					// do processing only for ships (plus players if in a multiplayer mission
531 					if ((objp->type == OBJ_SHIP) || ((objp->type == OBJ_START) && !mission_type)) {
532 						// process this if ship not in a wing
533 						if (Ships[i].wingnum < 0) {
534 							if (!cue_init) {
535 								cue_init = 1;
536 								a_cue = Ships[i].arrival_cue;
537 								d_cue = Ships[i].departure_cue;
538 								m_arrival_location = Ships[i].arrival_location;
539 								m_arrival_dist.init(Ships[i].arrival_distance);
540 								m_arrival_target = Ships[i].arrival_anchor;
541 								m_arrival_delay.init(Ships[i].arrival_delay);
542 								m_departure_location = Ships[i].departure_location;
543 								m_departure_delay.init(Ships[i].departure_delay);
544 								m_departure_target = Ships[i].departure_anchor;
545 
546 							} else {
547 								cue_init++;
548 								if (Ships[i].arrival_location != m_arrival_location){
549 									m_arrival_location = -1;
550 								}
551 
552 								if (Ships[i].departure_location != m_departure_location){
553 									m_departure_location = -1;
554 								}
555 
556 								m_arrival_dist.set(Ships[i].arrival_distance);
557 								m_arrival_delay.set(Ships[i].arrival_delay);
558 								m_departure_delay.set(Ships[i].departure_delay);
559 
560 								if (Ships[i].arrival_anchor != m_arrival_target){
561 									m_arrival_target = -1;
562 								}
563 
564 								if (!cmp_sexp_chains(a_cue, Ships[i].arrival_cue)) {
565 									a_cue = -1;
566 									m_update_arrival = 0;
567 								}
568 
569 								if (!cmp_sexp_chains(d_cue, Ships[i].departure_cue)) {
570 									d_cue = -1;
571 									m_update_departure = 0;
572 								}
573 
574 								if ( Ships[i].departure_anchor != m_departure_target ){
575 									m_departure_target = -1;
576 								}
577 							}
578 						}
579 
580 						// process the first ship in group, else process the rest
581 						if (base_ship >= 0) {
582 							m_ai_class = Ships[i].weapons.ai_class;
583 							cargo = Ships[i].cargo1;
584 							m_cargo1 = Cargo_names[cargo];
585 							m_hotkey = Ships[i].hotkey + 1;
586 							m_score.init(Ships[i].score);
587 							m_assist_score.init((int)(Ships[i].assist_score_pct*100));
588 
589 							m_persona = Ships[i].persona_index;
590 
591 							// we use final_death_time member of ship structure for holding the amount of time before a mission
592 							// to destroy this ship
593 							wing = Ships[i].wingnum;
594 							if (wing < 0) {
595 								GetDlgItem(IDC_WING) -> SetWindowText("None");
596 
597 							} else {
598 								GetDlgItem(IDC_WING) -> SetWindowText(Wings[wing].name);
599 								if (!query_whole_wing_marked(wing))
600 									m_update_arrival = m_update_departure = 0;
601 							}
602 
603 							// set routine local varaiables for ship/object flags
604 							no_arrival_warp = (Ships[i].flags[Ship::Ship_Flags::No_arrival_warp]) ? 1 : 0;
605 							no_departure_warp = (Ships[i].flags[Ship::Ship_Flags::No_departure_warp]) ? 1 : 0;
606 
607 							base_ship = -1;
608 							if (!multi_edit)
609 								single_ship = i;
610 
611 						} else {
612 							if (Ships[i].weapons.ai_class != m_ai_class){
613 								m_ai_class = -1;
614 							}
615 
616 							if (Ships[i].cargo1 != cargo){
617 								m_cargo1 = _T("");
618 							}
619 
620 							m_score.set(Ships[i].score);
621 							m_assist_score.set((int)(Ships[i].assist_score_pct*100));
622 
623 							if (Ships[i].hotkey != m_hotkey - 1){
624 								m_hotkey = -1;
625 							}
626 
627 							if ( Ships[i].persona_index != m_persona ){
628 								m_persona = -2;
629 							}
630 
631 							if (Ships[i].wingnum != wing){
632 								GetDlgItem(IDC_WING) -> SetWindowText("");
633 							}
634 
635 							no_arrival_warp = tristate_set(Ships[i].flags[Ship::Ship_Flags::No_arrival_warp], no_arrival_warp);
636 							no_departure_warp = tristate_set(Ships[i].flags[Ship::Ship_Flags::No_departure_warp], no_departure_warp);
637 						}
638 					}
639 				}
640 			}
641 
642 			objp = GET_NEXT(objp);
643 		}
644 
645 		if (multi_edit) {
646 			m_arrival_tree.clear_tree("");
647 			m_departure_tree.clear_tree("");
648 		}
649 
650 		if (cue_init) {
651 			m_arrival_tree.load_tree(a_cue);
652 			m_departure_tree.load_tree(d_cue, "false");
653 
654 		} else {
655 			m_arrival_tree.clear_tree();
656 			m_arrival_tree.DeleteAllItems();
657 			m_departure_tree.clear_tree();
658 			m_departure_tree.DeleteAllItems();
659 		}
660 
661 		m_player_ship.SetCheck(pship);
662 		m_no_arrival_warp.SetCheck(no_arrival_warp);
663 		m_no_departure_warp.SetCheck(no_departure_warp);
664 
665 		if (!multi_edit) {
666 			auto i = m_arrival_tree.select_sexp_node;
667 			if (i != -1) {
668 				w = GetDlgItem(IDC_ARRIVAL_TREE);
669 				m_arrival_tree.hilite_item(i);
670 
671 			} else {
672 				i = m_departure_tree.select_sexp_node;
673 				if (i != -1) {
674 					w = GetDlgItem(IDC_DEPARTURE_TREE);
675 					m_departure_tree.hilite_item(i);
676 				}
677 			}
678 		}
679 
680 		m_persona++;
681 		if (m_persona > 0) {
682 			int persona_index = 0;
683 			for (int i = 0; i < m_persona; i++) {
684 				if (Personas[i].flags & PERSONA_FLAG_WINGMAN)
685 					persona_index++;
686 			}
687 			m_persona = persona_index;
688 		}
689 
690 	} else {  // no ships selected, 0 or more player ships selected
691 		if (player_count > 1) {  // multiple player ships selected
692 			Assert(base_player >= 0);
693 			m_ship_name = _T("");
694 			m_player_ship.SetCheck(TRUE);
695 			objp = GET_FIRST(&obj_used_list);
696 			while (objp != END_OF_LIST(&obj_used_list)) {
697 				if ((objp->type == OBJ_START) && (objp->flags[Object::Object_Flags::Marked])) {
698 					auto i = objp->instance;
699 					if (base_player >= 0) {
700 						m_ship_class = Ships[i].ship_info_index;
701 						m_team = Ships[i].team;
702 						base_player = -1;
703 
704 					} else {
705 						if (Ships[i].ship_info_index != m_ship_class)
706 							m_ship_class = -1;
707 						if (Ships[i].team != m_team)
708 							m_team = -1;
709 					}
710 				}
711 
712 				objp = GET_NEXT(objp);
713 			}
714 
715 		// only 1 player selected..
716 		} else if (query_valid_object() && (Objects[cur_object_index].type == OBJ_START)) {
717 			Assert((player_count == 1) && !multi_edit);
718 			player_ship = Objects[cur_object_index].instance;
719 			m_ship_name = Ships[player_ship].ship_name;
720 			m_ship_class = Ships[player_ship].ship_info_index;
721 			m_team = Ships[player_ship].team;
722 			m_player_ship.SetCheck(TRUE);
723 
724 		} else {  // no ships or players selected..
725 			m_ship_name = _T("");
726 			m_ship_class = -1;
727 			m_team = -1;
728 			m_persona = -1;
729 			m_player_ship.SetCheck(FALSE);
730 		}
731 
732 		m_ai_class = -1;
733 		m_cargo1 = _T("");
734 		m_hotkey = 0;
735 		m_score.blank();  // cause control to be blank
736 		m_assist_score.blank();
737 		m_arrival_location = -1;
738 		m_departure_location = -1;
739 		m_arrival_delay.blank();
740 		m_departure_delay.blank();
741 		m_arrival_dist.blank();
742 		m_arrival_target = -1;
743 		m_departure_target = -1;
744 		m_arrival_tree.clear_tree();
745 		m_arrival_tree.DeleteAllItems();
746 		m_departure_tree.clear_tree();
747 		m_departure_tree.DeleteAllItems();
748 		m_no_arrival_warp.SetCheck(0);
749 		m_no_departure_warp.SetCheck(0);
750 		enable = p_enable = 0;
751 		GetDlgItem(IDC_WING)->SetWindowText(_T("None"));
752 	}
753 
754 	box = (CComboBox *) GetDlgItem(IDC_ARRIVAL_TARGET);
755 	// must put the appropriate ships into the list depending on arrival location
756 	if ( m_arrival_location != ARRIVE_FROM_DOCK_BAY ){
757 		management_add_ships_to_combo( box, SHIPS_2_COMBO_SPECIAL | SHIPS_2_COMBO_ALL_SHIPS );
758 	} else {
759 		management_add_ships_to_combo( box, SHIPS_2_COMBO_DOCKING_BAY_ONLY );
760 	}
761 
762 	// set the internal variable appropriatly
763 	// Goober5000 - gah, this is ridiculous!  Prior to this point in the function (and only in this function),
764 	// m_arrival_target seems to refer to the arrival anchor.  The rest of the time, it refers to the index
765 	// of the drop-down list.
766 	if (m_arrival_target >= 0)
767 	{
768 		if (m_arrival_target & SPECIAL_ARRIVAL_ANCHOR_FLAG)
769 		{
770 			// figure out what the box represents this as
771 			char tmp[NAME_LENGTH + 15];
772 			stuff_special_arrival_anchor_name(tmp, m_arrival_target, 0);
773 
774 			// find it in the box
775 			m_arrival_target = box->FindStringExact(-1, tmp);
776 		}
777 		else
778 		{
779 			// find it in the box
780 			m_arrival_target = box->FindStringExact(-1, Ships[m_arrival_target].ship_name);
781 		}
782 	}
783 
784 	box = (CComboBox *)GetDlgItem(IDC_DEPARTURE_TARGET);
785 	// must put the appropriate ships into the list depending on departure location
786 	if ( m_departure_location == DEPART_AT_DOCK_BAY ){
787 		management_add_ships_to_combo( box, SHIPS_2_COMBO_DOCKING_BAY_ONLY );
788 	} else {
789 		box->ResetContent();
790 	}
791 
792 	if ( m_departure_target >= 0 ){
793 		m_departure_target = box->FindStringExact( -1, Ships[m_departure_target].ship_name );
794 	}
795 
796 	initialized = 1;
797 	if (player_count) {
798 		box = (CComboBox *) GetDlgItem(IDC_SHIP_TEAM);
799 		if (!mission_type){  // multiplayer mission
800 			box->EnableWindow(TRUE);
801 		}
802 
803 		else {
804 			box->EnableWindow(FALSE);
805 			m_team = -1;
806 		}
807 
808 		box->ResetContent();
809 		for (auto i=0; i<MAX_TVT_TEAMS; i++)
810 			box->AddString(Iff_info[i].iff_name);
811 	} else {
812 		box = (CComboBox *) GetDlgItem(IDC_SHIP_TEAM);
813 		box->EnableWindow(enable);
814 		box->ResetContent();
815 		for (auto i=0; i<Num_iffs; i++){
816 			box->AddString(Iff_info[i].iff_name);
817 		}
818 	}
819 
820 	m_score.display();
821 	m_assist_score.display();
822 	m_arrival_dist.display();
823 	m_arrival_delay.display();
824 	m_departure_delay.display();
825 
826 	if (full_update)
827 		UpdateData(FALSE);
828 
829 	if (!cue_init) {
830 		GetDlgItem(IDC_ARRIVAL_LOCATION)->EnableWindow(FALSE);
831 		GetDlgItem(IDC_ARRIVAL_DELAY)->EnableWindow(FALSE);
832 		GetDlgItem(IDC_ARRIVAL_DISTANCE)->EnableWindow(FALSE);
833 		GetDlgItem(IDC_ARRIVAL_TARGET)->EnableWindow(FALSE);
834 		GetDlgItem(IDC_ARRIVAL_DELAY_SPIN)->EnableWindow(FALSE);
835 		GetDlgItem(IDC_ARRIVAL_TREE)->EnableWindow(FALSE);
836 		GetDlgItem(IDC_DEPARTURE_LOCATION)->EnableWindow(FALSE);
837 		GetDlgItem(IDC_DEPARTURE_TARGET)->EnableWindow(FALSE);
838 		GetDlgItem(IDC_DEPARTURE_DELAY)->EnableWindow(FALSE);
839 		GetDlgItem(IDC_DEPARTURE_DELAY_SPIN)->EnableWindow(FALSE);
840 		GetDlgItem(IDC_DEPARTURE_TREE)->EnableWindow(FALSE);
841 		GetDlgItem(IDC_NO_ARRIVAL_WARP)->EnableWindow(FALSE);
842 		GetDlgItem(IDC_NO_DEPARTURE_WARP)->EnableWindow(FALSE);
843 
844 		GetDlgItem(IDC_RESTRICT_ARRIVAL)->EnableWindow(FALSE);
845 		GetDlgItem(IDC_RESTRICT_DEPARTURE)->EnableWindow(FALSE);
846 
847 	} else {
848 		GetDlgItem(IDC_ARRIVAL_LOCATION)->EnableWindow(enable);
849 		if (m_arrival_location) {
850 			GetDlgItem(IDC_ARRIVAL_DISTANCE)->EnableWindow(enable);
851 			GetDlgItem(IDC_ARRIVAL_TARGET)->EnableWindow(enable);
852 		} else {
853 			GetDlgItem(IDC_ARRIVAL_DISTANCE)->EnableWindow(FALSE);
854 			GetDlgItem(IDC_ARRIVAL_TARGET)->EnableWindow(FALSE);
855 		}
856 		if (m_arrival_location == ARRIVE_FROM_DOCK_BAY) {
857 			GetDlgItem(IDC_RESTRICT_ARRIVAL)->EnableWindow(enable);
858 			GetDlgItem(IDC_CUSTOM_WARPIN_PARAMS)->EnableWindow(FALSE);
859 		} else {
860 			GetDlgItem(IDC_RESTRICT_ARRIVAL)->EnableWindow(FALSE);
861 			GetDlgItem(IDC_CUSTOM_WARPIN_PARAMS)->EnableWindow(enable);
862 		}
863 
864 		GetDlgItem(IDC_DEPARTURE_LOCATION)->EnableWindow(enable);
865 		if ( m_departure_location ) {
866 			GetDlgItem(IDC_DEPARTURE_TARGET)->EnableWindow(enable);
867 		} else {
868 			GetDlgItem(IDC_DEPARTURE_TARGET)->EnableWindow(FALSE);
869 		}
870 		if (m_departure_location == DEPART_AT_DOCK_BAY) {
871 			GetDlgItem(IDC_RESTRICT_DEPARTURE)->EnableWindow(enable);
872 			GetDlgItem(IDC_CUSTOM_WARPOUT_PARAMS)->EnableWindow(FALSE);
873 		} else {
874 			GetDlgItem(IDC_RESTRICT_DEPARTURE)->EnableWindow(FALSE);
875 			GetDlgItem(IDC_CUSTOM_WARPOUT_PARAMS)->EnableWindow(enable);
876 		}
877 
878 		GetDlgItem(IDC_ARRIVAL_DELAY)->EnableWindow(enable);
879 		GetDlgItem(IDC_ARRIVAL_DELAY_SPIN)->EnableWindow(enable);
880 		GetDlgItem(IDC_ARRIVAL_TREE)->EnableWindow(enable);
881 		GetDlgItem(IDC_DEPARTURE_LOCATION)->EnableWindow(enable);
882 		GetDlgItem(IDC_DEPARTURE_DELAY)->EnableWindow(enable);
883 		GetDlgItem(IDC_DEPARTURE_DELAY_SPIN)->EnableWindow(enable);
884 		GetDlgItem(IDC_DEPARTURE_TREE)->EnableWindow(enable);
885 		GetDlgItem(IDC_NO_ARRIVAL_WARP)->EnableWindow(enable);
886 		GetDlgItem(IDC_NO_DEPARTURE_WARP)->EnableWindow(enable);
887 	}
888 
889 	if (total_count) {
890 		GetDlgItem(IDC_SHIP_NAME)->EnableWindow(!multi_edit);
891 		GetDlgItem(IDC_SHIP_CLASS)->EnableWindow(TRUE);
892 		GetDlgItem(IDC_SHIP_ALT)->EnableWindow(TRUE);
893 		GetDlgItem(IDC_INITIAL_STATUS)->EnableWindow(TRUE);
894 		GetDlgItem(IDC_WEAPONS)->EnableWindow(m_ship_class >= 0);
895 		GetDlgItem(IDC_FLAGS)->EnableWindow(TRUE);
896 		GetDlgItem(IDC_TEXTURES)->EnableWindow(TRUE);
897 		GetDlgItem(IDC_ALT_SHIP_CLASS)->EnableWindow(TRUE);
898 		GetDlgItem(IDC_SPECIAL_EXP)->EnableWindow(TRUE);
899 		GetDlgItem(IDC_SPECIAL_HITPOINTS)->EnableWindow(TRUE);
900 	} else {
901 		GetDlgItem(IDC_SHIP_NAME)->EnableWindow(FALSE);
902 		GetDlgItem(IDC_SHIP_CLASS)->EnableWindow(FALSE);
903 		GetDlgItem(IDC_SHIP_ALT)->EnableWindow(FALSE);
904 		GetDlgItem(IDC_INITIAL_STATUS)->EnableWindow(FALSE);
905 		GetDlgItem(IDC_WEAPONS)->EnableWindow(FALSE);
906 		GetDlgItem(IDC_FLAGS)->EnableWindow(FALSE);
907 		GetDlgItem(IDC_TEXTURES)->EnableWindow(FALSE);
908 		GetDlgItem(IDC_ALT_SHIP_CLASS)->EnableWindow(FALSE);
909 		GetDlgItem(IDC_SPECIAL_EXP)->EnableWindow(FALSE);
910 		GetDlgItem(IDC_SPECIAL_HITPOINTS)->EnableWindow(FALSE);
911 	}
912 
913 	// disable textures for multiple ships
914 	if (multi_edit)
915 	{
916 		GetDlgItem(IDC_TEXTURES)->EnableWindow(FALSE);
917 	}
918 
919 	GetDlgItem(IDC_AI_CLASS)->EnableWindow(enable);
920 	GetDlgItem(IDC_SHIP_CARGO1)->EnableWindow(enable);
921 	GetDlgItem(IDC_HOTKEY)->EnableWindow(enable);
922     if ((m_ship_class >= 0) && !(Ship_info[m_ship_class].flags[Ship::Info_Flags::Cargo]) && !(Ship_info[m_ship_class].flags[Ship::Info_Flags::No_ship_type]))
923 		GetDlgItem(IDC_GOALS)->EnableWindow(enable);
924 	else if (multi_edit)
925 		GetDlgItem(IDC_GOALS)->EnableWindow(enable);
926 	else
927 		GetDlgItem(IDC_GOALS)->EnableWindow(FALSE);
928 
929 	// !pship_count used because if allowed to clear, we would have no player starts
930 	// mission_type 0 = multi, 1 = single
931 	if (mission_type || !pship_count || (pship_count + total_count > MAX_PLAYERS) || (pvalid_count < total_count))
932 		m_player_ship.EnableWindow(FALSE);
933 	else
934 		m_player_ship.EnableWindow(TRUE);
935 
936 	// show the "set player" button only if single player
937 	if (mission_type)
938 		GetDlgItem(IDC_SET_AS_PLAYER_SHIP)->ShowWindow(TRUE);
939 	else
940 		GetDlgItem(IDC_SET_AS_PLAYER_SHIP)->ShowWindow(FALSE);
941 
942 	// enable the "set player" button only if single player, single edit, and ship is in player wing
943 	{
944 		int marked_ship = (player_ship >= 0) ? player_ship : single_ship;
945 
946 		if (mission_type && total_count && !multi_edit && wing_is_player_wing(Ships[marked_ship].wingnum))
947 			GetDlgItem(IDC_SET_AS_PLAYER_SHIP)->EnableWindow(TRUE);
948 		else
949 			GetDlgItem(IDC_SET_AS_PLAYER_SHIP)->EnableWindow(FALSE);
950 	}
951 
952 	GetDlgItem(IDC_DELETE_SHIP)->EnableWindow(enable);
953 	GetDlgItem(IDC_SHIP_RESET)->EnableWindow(enable);
954 	GetDlgItem(IDC_SCORE)->EnableWindow(enable);
955 	GetDlgItem(IDC_ASSIST_SCORE)->EnableWindow(enable);
956 
957 //#ifndef NDEBUG
958 	GetDlgItem(IDC_SHIP_TBL)->EnableWindow(m_ship_class >= 0);
959 //#else
960 //	GetDlgItem(IDC_SHIP_TBL)->EnableWindow(0);
961 //	GetDlgItem(IDC_SHIP_TBL)->ShowWindow(SW_HIDE);
962 //#endif
963 
964 	if (cue_init > 1) {  // more than one ship (players don't have cues to edit)
965 		GetDlgItem(IDC_UPDATE_ARRIVAL)->ShowWindow(SW_SHOW);
966 		GetDlgItem(IDC_UPDATE_DEPARTURE)->ShowWindow(SW_SHOW);
967 
968 	} else {
969 		GetDlgItem(IDC_UPDATE_ARRIVAL)->ShowWindow(SW_HIDE);
970 		GetDlgItem(IDC_UPDATE_DEPARTURE)->ShowWindow(SW_HIDE);
971 	}
972 
973 	if (multi_edit || (total_count > 1)) {
974 		// we will allow the ignore orders dialog to be multi edit if all selected
975 		// ships are the same type.  the ship_type (local) variable holds the ship types
976 		// for all ships.  Determine how may bits set and enable/diable window
977 		// as appropriate
978 		if ( /*(m_team == -1) ||*/ (ship_orders == -1) ){
979 			GetDlgItem(IDC_IGNORE_ORDERS)->EnableWindow(FALSE);
980 		} else {
981 			GetDlgItem(IDC_IGNORE_ORDERS)->EnableWindow(TRUE);
982 		}
983 	} else
984 		// always enabled when one ship is selected
985 		GetDlgItem(IDC_IGNORE_ORDERS)->EnableWindow(enable);
986 
987 	// always enabled if >= 1 ship selected
988 	GetDlgItem(IDC_SHIP_PERSONA)->EnableWindow(enable);
989 
990 	if (multi_edit){
991 		SetWindowText("Edit Marked Ships");
992 	} else if (player_count) {
993 		SetWindowText("Edit Player Ship");
994 	} else {
995 		SetWindowText("Edit Ship");
996 	}
997 
998 	// setup alternate name and callsign stuff
999 	if(player_ship >= 0){
1000 		ship_alt_name_init(player_ship);
1001 		ship_callsign_init(player_ship);
1002 	} else {
1003 		ship_alt_name_init(single_ship);
1004 		ship_callsign_init(single_ship);
1005 	}
1006 
1007 	modified = 0;
1008 	if (w){
1009 		w->SetFocus();
1010 	}
1011 }
1012 
1013 // update ship structure(s) with dialog data.  The data is first checked for errors.  If
1014 // no errors occur, returns 0.  If an error occurs, returns -1.  If the update is bypassed,
1015 // returns 1.  Bypass is necessary to avoid an infinite loop, and it doesn't actually
1016 // update the data.  Bypass only occurs if bypass mode is active and we still get an error.
1017 // Once the error no longer occurs, bypass mode is cleared and data is updated.
update_data(int redraw)1018 int CShipEditorDlg::update_data(int redraw)
1019 {
1020 	char *str, old_name[255];
1021 	object *ptr;
1022 	int i, z, wing;
1023 	CSingleLock sync(&CS_cur_object_index), sync2(&CS_update);
1024 
1025 	nprintf(("Fred routing", "Ship dialog save\n"));
1026 	if (!GetSafeHwnd() || !initialized || bypass_all)
1027 		return 0;
1028 
1029 	sync.Lock();  // don't allow cur_object_index to change while we are using it
1030 	sync2.Lock();  // don't allow reinitialization until we are done updating
1031 	UpdateData(TRUE);
1032 	UpdateData(TRUE);
1033 	Wing_editor_dialog.update_data_safe();
1034 	if (multi_edit) {  // editing multiple ships (ships and/or players)
1035 		ptr = GET_FIRST(&obj_used_list);
1036 		while (ptr != END_OF_LIST(&obj_used_list)) {
1037 			if (((ptr->type == OBJ_START) || (ptr->type == OBJ_SHIP)) && (ptr->flags[Object::Object_Flags::Marked]))
1038 				update_ship(get_ship_from_obj(ptr));
1039 
1040 			ptr = GET_NEXT(ptr);
1041 		}
1042 
1043 	} else if (player_ship >= 0) {  // editing a single player
1044 		update_ship(player_ship);
1045 
1046 	} else if (single_ship >= 0) {  // editing a single ship
1047 		m_ship_name.TrimLeft();
1048 		m_ship_name.TrimRight();
1049 		ptr = GET_FIRST(&obj_used_list);
1050 		while (ptr != END_OF_LIST(&obj_used_list)) {
1051 			if (((ptr->type == OBJ_SHIP) || (ptr->type == OBJ_START)) && (cur_object_index != OBJ_INDEX(ptr))) {
1052 				str = Ships[ptr->instance].ship_name;
1053 				if (!stricmp(m_ship_name, str)) {
1054 					if (bypass_errors)
1055 						return 1;
1056 
1057 					bypass_errors = 1;
1058 					z = MessageBox("This ship name is already being used by another ship\n"
1059 						"Press OK to restore old name", "Error", MB_ICONEXCLAMATION | MB_OKCANCEL);
1060 
1061 					if (z == IDCANCEL)
1062 						return -1;
1063 
1064 					m_ship_name = _T(Ships[single_ship].ship_name);
1065 					UpdateData(FALSE);
1066 				}
1067 			}
1068 
1069 			ptr = GET_NEXT(ptr);
1070 		}
1071 
1072 		for (i=0; i<MAX_WINGS; i++) {
1073 			if (Wings[i].wave_count && !stricmp(Wings[i].name, m_ship_name)) {
1074 				if (bypass_errors)
1075 					return 1;
1076 
1077 				bypass_errors = 1;
1078 				z = MessageBox("This ship name is already being used by a wing\n"
1079 					"Press OK to restore old name", "Error", MB_ICONEXCLAMATION | MB_OKCANCEL);
1080 
1081 				if (z == IDCANCEL)
1082 					return -1;
1083 
1084 				m_ship_name = _T(Ships[single_ship].ship_name);
1085 				UpdateData(FALSE);
1086 			}
1087 		}
1088 
1089 		// We don't need to check teams.  "Unknown" is a valid name and also an IFF.
1090 
1091 		for ( i=0; i < (int)Ai_tp_list.size(); i++) {
1092 			if (!stricmp(m_ship_name, Ai_tp_list[i].name))
1093 			{
1094 				if (bypass_errors)
1095 					return 1;
1096 
1097 				bypass_errors = 1;
1098 				z = MessageBox("This ship name is already being used by a target priority group.\n"
1099 					"Press OK to restore old name", "Error", MB_ICONEXCLAMATION | MB_OKCANCEL);
1100 
1101 				if (z == IDCANCEL)
1102 					return -1;
1103 
1104 				m_ship_name = _T(Ships[single_ship].ship_name);
1105 				UpdateData(FALSE);
1106 			}
1107 		}
1108 
1109 		if (find_matching_waypoint_list((LPCSTR) m_ship_name) != NULL)
1110 		{
1111 			if (bypass_errors)
1112 				return 0;
1113 
1114 			bypass_errors = 1;
1115 			z = MessageBox("This ship name is already being used by a waypoint path\n"
1116 				"Press OK to restore old name", "Error", MB_ICONEXCLAMATION | MB_OKCANCEL);
1117 
1118 			if (z == IDCANCEL)
1119 				return -1;
1120 
1121 			m_ship_name = _T(Ships[single_ship].ship_name);
1122 			UpdateData(FALSE);
1123 		}
1124 
1125 		if(jumpnode_get_by_name(m_ship_name) != NULL)
1126 		{
1127 			if (bypass_errors)
1128 				return 1;
1129 
1130 			bypass_errors = 1;
1131 
1132 			z = MessageBox("This ship name is already being used by a jump node\n"
1133 			"Press OK to restore old name", "Error", MB_ICONEXCLAMATION | MB_OKCANCEL);
1134 
1135 			if (z == IDCANCEL)
1136 			return -1;
1137 
1138 			m_ship_name = _T(Ships[single_ship].ship_name);
1139 			UpdateData(FALSE);
1140 		}
1141 
1142 		if (!stricmp(m_ship_name.Left(1), "<")) {
1143 			if (bypass_errors)
1144 				return 1;
1145 
1146 			bypass_errors = 1;
1147 			z = MessageBox("Ship names not allowed to begin with <\n"
1148 				"Press OK to restore old name", "Error", MB_ICONEXCLAMATION | MB_OKCANCEL);
1149 
1150 			if (z == IDCANCEL)
1151 				return -1;
1152 
1153 			m_ship_name = _T(Ships[single_ship].ship_name);
1154 			UpdateData(FALSE);
1155 		}
1156 
1157 		wing = Ships[single_ship].wingnum;
1158 		if (wing >= 0) {
1159 			Assert((wing < MAX_WINGS) && Wings[wing].wave_count);
1160 			for (i=0; i<Wings[wing].wave_count; i++)
1161 				if (wing_objects[wing][i] == Ships[single_ship].objnum)
1162 					break;
1163 
1164 			Assert(i < Wings[wing].wave_count);
1165 			wing_bash_ship_name(old_name, Wings[wing].name, i + 1);
1166 			if (strcmp(old_name, m_ship_name)) {
1167 				if (bypass_errors)
1168 					return 0;
1169 
1170 				if (MessageBox("This ship is part of a wing, and its name cannot be changed",
1171 					NULL, MB_OKCANCEL) == IDCANCEL)
1172 						return -1;
1173 
1174 				m_ship_name = _T(old_name);
1175 				UpdateData(FALSE);
1176 			}
1177 		}
1178 
1179 		z = update_ship(single_ship);
1180 		if (z)
1181 			return z;
1182 
1183 		strcpy_s(old_name, Ships[single_ship].ship_name);
1184 		string_copy(Ships[single_ship].ship_name, m_ship_name, NAME_LENGTH, 1);
1185 		str = Ships[single_ship].ship_name;
1186 		if (strcmp(old_name, str)) {
1187 			update_sexp_references(old_name, str);
1188 			ai_update_goal_references(REF_TYPE_SHIP, old_name, str);
1189 			update_texture_replacements(old_name, str);
1190 			for (i=0; i<Num_reinforcements; i++)
1191 				if (!strcmp(old_name, Reinforcements[i].name)) {
1192 					Assert(strlen(str) < NAME_LENGTH);
1193 					strcpy_s(Reinforcements[i].name, str);
1194 				}
1195 
1196 			Update_window = 1;
1197 		}
1198 	}
1199 
1200 	if (Player_start_shipnum < 0 || Objects[Ships[Player_start_shipnum].objnum].type != OBJ_START) {  // need a new single player start.
1201 		ptr = GET_FIRST(&obj_used_list);
1202 		while (ptr != END_OF_LIST(&obj_used_list)) {
1203 			if (ptr->type == OBJ_START) {
1204 				Player_start_shipnum = ptr->instance;
1205 				break;
1206 			}
1207 
1208 			ptr = GET_NEXT(ptr);
1209 		}
1210 	}
1211 
1212 	if (modified)
1213 		set_modified();
1214 
1215 	Wing_editor_dialog.initialize_data_safe(1);
1216 	bypass_errors = 0;
1217 	modified = 0;
1218 
1219 	if (redraw)
1220 		update_map_window();
1221 
1222 	return 0;
1223 }
1224 
update_ship(int ship)1225 int CShipEditorDlg::update_ship(int ship)
1226 {
1227 	int z, d;
1228 	CString str;
1229 	CComboBox *box;
1230 	int persona;
1231 
1232 	// THIS DIALOG IS THE SOME OF THE WORST CODE I HAVE EVER SEEN IN MY ENTIRE LIFE.
1233 	// IT TOOK A RIDICULOUSLY LONG AMOUNT OF TIME TO ADD 2 FUNCTIONS. OMG
1234 	ship_alt_name_close(ship);
1235 	ship_callsign_close(ship);
1236 
1237 	if ((Ships[ship].ship_info_index != m_ship_class) && (m_ship_class != -1)) {
1238 		change_ship_type(ship, m_ship_class);
1239 		set_modified();
1240 	}
1241 
1242 	if (m_team != -1)
1243 		MODIFY(Ships[ship].team, m_team);
1244 
1245 	if (Objects[Ships[ship].objnum].type != OBJ_SHIP){
1246 		if (mission_type || (Objects[Ships[ship].objnum].type != OBJ_START)){
1247 			return 0;
1248 		}
1249 	}
1250 
1251 	if (m_ai_class != -1){
1252 		MODIFY(Ships[ship].weapons.ai_class, m_ai_class);
1253 	}
1254 	if (strlen(m_cargo1)) {
1255 		z = string_lookup(m_cargo1, Cargo_names, Num_cargo);
1256 		if (z == -1) {
1257 			if (Num_cargo < MAX_CARGO) {
1258 				z = Num_cargo++;
1259 				strcpy(Cargo_names[z], m_cargo1);
1260 			}
1261 			else {
1262 				str.Format("Maximum number of cargo names (%d) reached.\nIgnoring new name.\n", MAX_CARGO);
1263 				MessageBox(str, "Error", MB_ICONEXCLAMATION);
1264 				z = 0;
1265 				m_cargo1 = Cargo_names[z];
1266 			}
1267 		}
1268 
1269 		MODIFY(Ships[ship].cargo1, (char)z);
1270 	}
1271 
1272 	m_score.save(&Ships[ship].score);
1273 	int temp_assist = -1;
1274 	m_assist_score.save(&temp_assist);
1275 	if (temp_assist != -1) {
1276 		Ships[ship].assist_score_pct = ((float)temp_assist)/100;
1277 		// value must be a percentage
1278 		if (Ships[ship].assist_score_pct < 0) {
1279 			Ships[ship].assist_score_pct = 0;
1280 			MessageBox("Assist Percentage too low. Set to 0. No score will be granted for an assist");
1281 		}
1282 		else if (Ships[ship].assist_score_pct > 1) {
1283 			Ships[ship].assist_score_pct = 1;
1284 			MessageBox("Assist Percentage too high. Set to 1. Assists will score as many points as a kill");
1285 		}
1286 	}
1287 
1288 	if (m_persona != -1)
1289 	{
1290 		// do the persona update
1291 		// m_persona holds the index into the list.  Get the item data associated with this index and then
1292 		// assign to the ship taking care that we check for the NO_PERSONA_INDEX id
1293 		box = (CComboBox *)GetDlgItem(IDC_SHIP_PERSONA);
1294 		persona = (int)box->GetItemData(m_persona);
1295 		if ( persona == NO_PERSONA_INDEX )
1296 			persona = -1;
1297 
1298 		MODIFY(Ships[ship].persona_index, persona);
1299 	}
1300 
1301 	if (Ships[ship].wingnum < 0) {
1302 
1303 		if (m_arrival_location != -1)
1304 			MODIFY(Ships[ship].arrival_location, m_arrival_location);
1305 		if (m_departure_location != -1)
1306 			MODIFY(Ships[ship].departure_location, m_departure_location);
1307 
1308 		if (!multi_edit || m_update_arrival) {  // should we update the arrival cue?
1309 			if (Ships[ship].arrival_cue >= 0)
1310 				free_sexp2(Ships[ship].arrival_cue);
1311 
1312 			Ships[ship].arrival_cue = m_arrival_tree.save_tree();
1313 		}
1314 
1315 		if (!multi_edit || m_update_departure) {
1316 			if (Ships[ship].departure_cue >= 0)
1317 				free_sexp2(Ships[ship].departure_cue);
1318 
1319 			Ships[ship].departure_cue = m_departure_tree.save_tree();
1320 		}
1321 
1322 		m_arrival_dist.save(&Ships[ship].arrival_distance);
1323 		m_arrival_delay.save(&Ships[ship].arrival_delay);
1324 		m_departure_delay.save(&Ships[ship].departure_delay);
1325 		if (m_arrival_target >= 0) {
1326 			z = (int)((CComboBox *) GetDlgItem(IDC_ARRIVAL_TARGET))->GetItemData(m_arrival_target);
1327 			MODIFY(Ships[ship].arrival_anchor, z);
1328 
1329 			// if the arrival is not hyperspace or docking bay -- force arrival distance to be
1330 			// greater than 2*radius of target.
1331 			if (((m_arrival_location != ARRIVE_FROM_DOCK_BAY) && (m_arrival_location != ARRIVE_AT_LOCATION)) && (z >= 0) && !(z & SPECIAL_ARRIVAL_ANCHOR_FLAG)) {
1332 			d = int(std::min(500.0f, 2.0f * Objects[Ships[ship].objnum].radius));
1333 				if ((Ships[ship].arrival_distance < d) && (Ships[ship].arrival_distance > -d)) {
1334 					str.Format("Ship must arrive at least %d meters away from target.\n"
1335 						"Value has been reset to this.  Use with caution!\r\n"
1336 						"Recommended distance is %d meters.\r\n", d, (int)(2.0f * Objects[Ships[ship].objnum].radius) );
1337 
1338 					MessageBox(str);
1339 					if (Ships[ship].arrival_distance < 0)
1340 						Ships[ship].arrival_distance = -d;
1341 					else
1342 						Ships[ship].arrival_distance = d;
1343 
1344 					m_arrival_dist.fix(Ships[ship].arrival_distance);
1345 				}
1346 			}
1347 		}
1348 		if (m_departure_target >= 0) {
1349 			z = (int)((CComboBox *) GetDlgItem(IDC_DEPARTURE_TARGET))->GetItemData(m_departure_target);
1350 			MODIFY(Ships[ship].departure_anchor, z );
1351 		}
1352 	}
1353 
1354 	if (m_hotkey != -1)
1355 		MODIFY(Ships[ship].hotkey, m_hotkey - 1);
1356 
1357 	switch( m_no_arrival_warp.GetCheck() ) {
1358 		case 0:
1359 			if (Ships[ship].flags[Ship::Ship_Flags::No_arrival_warp])
1360 				set_modified();
1361 
1362             Ships[ship].flags.remove(Ship::Ship_Flags::No_arrival_warp);
1363 			break;
1364 
1365 		case 1:
1366 			if (!(Ships[ship].flags[Ship::Ship_Flags::No_arrival_warp]))
1367 				set_modified();
1368 
1369             Ships[ship].flags.set(Ship::Ship_Flags::No_arrival_warp);
1370 			break;
1371 	}
1372 
1373 	switch( m_no_departure_warp.GetCheck() ) {
1374 		case 0:
1375 			if (Ships[ship].flags[Ship::Ship_Flags::No_departure_warp])
1376 				set_modified();
1377 
1378             Ships[ship].flags.remove(Ship::Ship_Flags::No_departure_warp);
1379 			break;
1380 
1381 		case 1:
1382 			if (!(Ships[ship].flags[Ship::Ship_Flags::No_departure_warp]))
1383 				set_modified();
1384 
1385             Ships[ship].flags.set(Ship::Ship_Flags::No_departure_warp);
1386 			break;
1387 	}
1388 
1389 	switch (m_player_ship.GetCheck()) {
1390 		case 1:
1391 			if (Objects[Ships[ship].objnum].type != OBJ_START) {
1392 				Player_starts++;
1393 				set_modified();
1394 			}
1395 
1396 			Objects[Ships[ship].objnum].type = OBJ_START;
1397 			break;
1398 
1399 		case 0:
1400 			if (Objects[Ships[ship].objnum].type == OBJ_START) {
1401 				Player_starts--;
1402 				set_modified();
1403 			}
1404 
1405 			Objects[Ships[ship].objnum].type = OBJ_SHIP;
1406 			break;
1407 	}
1408 
1409 	Update_ship = 1;
1410 	return 0;
1411 }
1412 
OnOK()1413 void CShipEditorDlg::OnOK()
1414 {
1415 	HWND h;
1416 	CWnd *w;
1417 
1418 	w = GetFocus();
1419 	if (w) {
1420 		h = w->m_hWnd;
1421 		GetDlgItem(IDC_ARRIVAL_TREE)->SetFocus();
1422 		::SetFocus(h);
1423 	}
1424 }
1425 
OnInitMenu(CMenu * pMenu)1426 void CShipEditorDlg::OnInitMenu(CMenu *pMenu)
1427 {
1428 	CMenu *m;
1429 
1430 	m = pMenu->GetSubMenu(0);
1431 	clear_menu(m);
1432 	generate_ship_popup_menu(m, ID_SHIP_MENU, MF_ENABLED, SHIP_FILTER_PLAYERS);
1433 	if (cur_ship != -1)
1434 		m->CheckMenuItem(ID_SHIP_MENU + cur_ship, MF_BYCOMMAND | MF_CHECKED);
1435 
1436 	CWnd::OnInitMenu(pMenu);
1437 }
1438 
OnCommand(WPARAM wParam,LPARAM lParam)1439 BOOL CShipEditorDlg::OnCommand(WPARAM wParam, LPARAM lParam)
1440 {
1441 	int id, ship;
1442 
1443 	id = LOWORD(wParam);
1444 	if (id >= ID_SHIP_MENU && id < ID_SHIP_MENU + MAX_SHIPS) {
1445 		if (!update_data()) {
1446 			ship = id - ID_SHIP_MENU;
1447 			unmark_all();
1448 			set_cur_object_index(Ships[ship].objnum);
1449 			return 1;
1450 		}
1451 	}
1452 
1453 	return CDialog::OnCommand(wParam, lParam);
1454 }
1455 
OnRclickArrivalTree(NMHDR * pNMHDR,LRESULT * pResult)1456 void CShipEditorDlg::OnRclickArrivalTree(NMHDR* pNMHDR, LRESULT* pResult)
1457 {
1458 	m_arrival_tree.right_clicked();
1459 	*pResult = 0;
1460 }
1461 
OnRclickDepartureTree(NMHDR * pNMHDR,LRESULT * pResult)1462 void CShipEditorDlg::OnRclickDepartureTree(NMHDR* pNMHDR, LRESULT* pResult)
1463 {
1464 	m_departure_tree.right_clicked();
1465 	*pResult = 0;
1466 }
1467 
OnBeginlabeleditArrivalTree(NMHDR * pNMHDR,LRESULT * pResult)1468 void CShipEditorDlg::OnBeginlabeleditArrivalTree(NMHDR* pNMHDR, LRESULT* pResult)
1469 {
1470 	TV_DISPINFO* pTVDispInfo = (TV_DISPINFO*)pNMHDR;
1471 
1472 	if (m_arrival_tree.edit_label(pTVDispInfo->item.hItem) == 1)	{
1473 		*pResult = 0;
1474 		modified = editing = 1;
1475 
1476 	} else
1477 		*pResult = 1;
1478 }
1479 
OnBeginlabeleditDepartureTree(NMHDR * pNMHDR,LRESULT * pResult)1480 void CShipEditorDlg::OnBeginlabeleditDepartureTree(NMHDR* pNMHDR, LRESULT* pResult)
1481 {
1482 	TV_DISPINFO* pTVDispInfo = (TV_DISPINFO*)pNMHDR;
1483 
1484 	if (m_departure_tree.edit_label(pTVDispInfo->item.hItem) == 1)	{
1485 		*pResult = 0;
1486 		modified = editing = 1;
1487 
1488 	} else
1489 		*pResult = 1;
1490 }
1491 
OnEndlabeleditArrivalTree(NMHDR * pNMHDR,LRESULT * pResult)1492 void CShipEditorDlg::OnEndlabeleditArrivalTree(NMHDR* pNMHDR, LRESULT* pResult)
1493 {
1494 	TV_DISPINFO* pTVDispInfo = (TV_DISPINFO*)pNMHDR;
1495 
1496 	*pResult = m_arrival_tree.end_label_edit(pTVDispInfo->item);
1497 	editing = 0;
1498 }
1499 
OnEndlabeleditDepartureTree(NMHDR * pNMHDR,LRESULT * pResult)1500 void CShipEditorDlg::OnEndlabeleditDepartureTree(NMHDR* pNMHDR, LRESULT* pResult)
1501 {
1502 	TV_DISPINFO* pTVDispInfo = (TV_DISPINFO*)pNMHDR;
1503 
1504 	*pResult = m_departure_tree.end_label_edit(pTVDispInfo->item);
1505 	editing = 0;
1506 }
1507 
verify()1508 int CShipEditorDlg::verify()
1509 {
1510 	nprintf(("Fred routing", "Ship dialog verify\n"));
1511 	if (!GetSafeHwnd() || !modified)
1512 		return 0;
1513 
1514 	if (bypass_errors)
1515 		return 1;
1516 
1517 	return 0;
1518 }
1519 
OnGoals()1520 void CShipEditorDlg::OnGoals()
1521 {
1522 	ShipGoalsDlg dlg_goals;
1523 
1524 	Assert(query_valid_object());
1525 //	if (multi_edit)
1526 //		dlg_goals.initialize_multi();
1527 //
1528 //	else {
1529 //		Assert(single_ship != -1);
1530 //		dlg_goals.self_ship = single_ship;
1531 //		dlg_goals.initialize(Ai_info[Ships[single_ship].ai_index].goals);
1532 //	}
1533 
1534 	if (!multi_edit) {
1535 		Assert(single_ship != -1);
1536 		dlg_goals.self_ship = single_ship;
1537 	}
1538 
1539 	dlg_goals.DoModal();
1540 	if (!multi_edit && !query_initial_orders_empty(Ai_info[Ships[single_ship].ai_index].goals))
1541 		if ((Ships[single_ship].wingnum >= 0) && (query_initial_orders_conflict(Ships[single_ship].wingnum)))
1542 			MessageBox("This ship's wing also has initial orders", "Possible conflict");
1543 }
1544 
OnSelchangeShipClass()1545 void CShipEditorDlg::OnSelchangeShipClass()
1546 {
1547 	object *ptr;
1548 
1549 	UpdateData(TRUE);
1550 	UpdateData(TRUE);
1551 	ptr = GET_FIRST(&obj_used_list);
1552 	while (ptr != END_OF_LIST(&obj_used_list)) {
1553 		if (((ptr->type == OBJ_SHIP) || (ptr->type == OBJ_START)) && (ptr->flags[Object::Object_Flags::Marked]))
1554 			if (Ships[ptr->instance].ship_info_index != m_ship_class) {
1555 				change_ship_type(ptr->instance, m_ship_class);
1556 				set_modified();
1557 			}
1558 
1559 		ptr = GET_NEXT(ptr);
1560 	}
1561 
1562 	update_map_window();
1563 }
1564 
OnInitialStatus()1565 void CShipEditorDlg::OnInitialStatus()
1566 {
1567 	initial_status dlg;
1568 
1569 	dlg.m_multi_edit = multi_edit;
1570 	dlg.DoModal();
1571 }
1572 
OnWeapons()1573 void CShipEditorDlg::OnWeapons()
1574 {
1575 	int i, ship = -1;
1576 	WeaponEditorDlg dlg;
1577 	object *objp;
1578 	CComboBox *box;
1579 
1580 	dlg.m_multi_edit = multi_edit;
1581 	dlg.DoModal();
1582 
1583 	if (multi_edit) {
1584 		objp = GET_FIRST(&obj_used_list);
1585 		while (objp != END_OF_LIST(&obj_used_list)) {
1586 			if (objp->flags[Object::Object_Flags::Marked])
1587 				if ((objp->type == OBJ_SHIP) || (objp->type == OBJ_START)) {
1588 					i = objp->instance;
1589 					if (ship) {
1590 						if (Ships[i].weapons.ai_class != Ships[ship].weapons.ai_class)
1591 							m_ai_class = -1;
1592 
1593 					} else {
1594 						ship = i;
1595 						m_ai_class = Ships[i].weapons.ai_class;
1596 					}
1597 				}
1598 
1599 			objp = GET_NEXT(objp);
1600 		}
1601 
1602 	} else {
1603 		ship = single_ship;
1604 		if (ship < 0)
1605 			ship = player_ship;
1606 
1607 		Assert(ship >= 0);
1608 		m_ai_class = Ships[ship].weapons.ai_class;
1609 	}
1610 
1611 	box = (CComboBox *) GetDlgItem(IDC_AI_CLASS);
1612 	box->SetCurSel(m_ai_class);
1613 }
1614 
OnShipReset()1615 void CShipEditorDlg::OnShipReset()
1616 {
1617 	int i, j, index, ship;
1618 	object *objp;
1619 	ship_info *sip;
1620 	ship_subsys *ptr;
1621 	ship_weapon *wp;
1622 	model_subsystem *sp;
1623 
1624 	m_cargo1 = "Nothing";
1625 	m_ai_class = AI_DEFAULT_CLASS;
1626 	if (m_ship_class) {
1627 		m_team = Species_info[Ship_info[m_ship_class].species].default_iff;
1628 	}
1629 
1630 	objp = GET_FIRST(&obj_used_list);
1631 	while (objp != END_OF_LIST(&obj_used_list)) {
1632 		if (((objp->type == OBJ_SHIP) || ((objp->type == OBJ_START) && !mission_type)) && (objp->flags[Object::Object_Flags::Marked])) {
1633 			ship = objp->instance;
1634 
1635 			// reset ship goals
1636 			for (i=0; i<MAX_AI_GOALS; i++){
1637 				Ai_info[Ships[ship].ai_index].goals[i].ai_mode = AI_GOAL_NONE;
1638 			}
1639 
1640 			objp->phys_info.speed = 0.0f;
1641 			objp->shield_quadrant[0] = 100.0f;
1642 			objp->hull_strength = 100.0f;
1643 
1644 			sip = &Ship_info[Ships[ship].ship_info_index];
1645 			for (i=0; i<sip->num_primary_banks; i++)
1646 				Ships[ship].weapons.primary_bank_weapons[i] = sip->primary_bank_weapons[i];
1647 
1648 			for (i=0; i<sip->num_secondary_banks; i++) {
1649 				Ships[ship].weapons.secondary_bank_weapons[i] = sip->secondary_bank_weapons[i];
1650 				Ships[ship].weapons.secondary_bank_capacity[i] = sip->secondary_bank_ammo_capacity[i];
1651 			}
1652 
1653 			index = 0;
1654 			ptr = GET_FIRST(&Ships[ship].subsys_list);
1655 			while (ptr != END_OF_LIST(&Ships[ship].subsys_list)) {
1656 				ptr->current_hits = 0.0f;
1657 				if (ptr->system_info->type == SUBSYSTEM_TURRET) {
1658 					wp = &ptr->weapons;
1659 					sp = &Ship_info[Ships[ship].ship_info_index].subsystems[index];
1660 
1661 					j = 0;
1662 					for (i=0; i<MAX_SHIP_PRIMARY_BANKS; i++){
1663 						if (sp->primary_banks[i] != -1){
1664 							wp->primary_bank_weapons[j++] = sp->primary_banks[i];
1665 						}
1666 					}
1667 
1668 					wp->num_primary_banks = j;
1669 					j = 0;
1670 					for (i=0; i<MAX_SHIP_SECONDARY_BANKS; i++){
1671 						if (sp->secondary_banks[i] != -1) {
1672 							wp->secondary_bank_weapons[j] = sp->secondary_banks[i];
1673 							wp->secondary_bank_capacity[j++] = sp->secondary_bank_capacity[i];
1674 						}
1675 					}
1676 
1677 					wp->num_secondary_banks = j;
1678 					for (i=0; i<MAX_SHIP_SECONDARY_BANKS; i++){
1679 						wp->secondary_bank_ammo[i] = 100;
1680 					}
1681 				}
1682 
1683 				index++;
1684 				ptr = GET_NEXT(ptr);
1685 			}
1686 		}
1687 
1688 		objp = GET_NEXT(objp);
1689 	}
1690 
1691 	UpdateData(FALSE);
1692 	if (multi_edit){
1693 		MessageBox("Ships reset to ship class defaults");
1694 	} else {
1695 		MessageBox("Ship reset to ship class defaults");
1696 	}
1697 }
1698 
OnDeleteShip()1699 void CShipEditorDlg::OnDeleteShip()
1700 {
1701 	delete_marked();
1702 	unmark_all();
1703 }
1704 
OnShipTbl()1705 void CShipEditorDlg::OnShipTbl()
1706 {
1707 	TextViewDlg dlg;
1708 
1709 	if (m_ship_class < 0)
1710 		return;
1711 	auto sip = &Ship_info[m_ship_class];
1712 
1713 	dlg.LoadShipsTblText(sip);
1714 	dlg.DoModal();
1715 }
1716 
make_ship_list(int * arr)1717 int CShipEditorDlg::make_ship_list(int *arr)
1718 {
1719 	int n = 0;
1720 	object *ptr;
1721 
1722 	ptr = GET_FIRST(&obj_used_list);
1723 	while (ptr != END_OF_LIST(&obj_used_list)) {
1724 		if ((ptr->type == OBJ_SHIP) || (ptr->type == OBJ_START)){
1725 			arr[n++] = OBJ_INDEX(ptr);
1726 		}
1727 
1728 		ptr = GET_NEXT(ptr);
1729 	}
1730 
1731 	return n;
1732 }
1733 
OnPrev()1734 void CShipEditorDlg::OnPrev()
1735 {
1736 	int i, n, arr[MAX_SHIPS];
1737 
1738 	if (!update_data()) {
1739 		n = make_ship_list(arr);
1740 		if (!n){
1741 			return;
1742 		}
1743 
1744 		if (cur_ship < 0){
1745 			i = n - 1;
1746 		}
1747 
1748 		else {
1749 			for (i=0; i<n; i++){
1750 				if (Ships[cur_ship].objnum == arr[i]){
1751 					break;
1752 				}
1753 			}
1754 
1755 			Assert(i < n);
1756 			i--;
1757 			if (i < 0){
1758 				i = n - 1;
1759 			}
1760 		}
1761 
1762 		unmark_all();
1763 		set_cur_object_index(arr[i]);
1764 		Ship_editor_dialog.initialize_data(1);
1765 		Update_ship = 0;
1766 	}
1767 
1768 	return;
1769 }
1770 
OnNext()1771 void CShipEditorDlg::OnNext()
1772 {
1773 	int i, n, arr[MAX_SHIPS];
1774 
1775 	if (!update_data()) {
1776 		n = make_ship_list(arr);
1777 		if (!n)
1778 			return;
1779 
1780 		if (cur_ship < 0)
1781 			i = 0;
1782 
1783 		else {
1784 			for (i=0; i<n; i++)
1785 				if (Ships[cur_ship].objnum == arr[i])
1786 					break;
1787 
1788 			Assert(i < n);
1789 			i++;
1790 			if (i == n)
1791 				i = 0;
1792 		}
1793 
1794 		unmark_all();
1795 		set_cur_object_index(arr[i]);
1796 		Ship_editor_dialog.initialize_data(1);
1797 		Update_ship = 0;
1798 	}
1799 
1800 	return;
1801 }
1802 
OnSelchangedArrivalTree(NMHDR * pNMHDR,LRESULT * pResult)1803 void CShipEditorDlg::OnSelchangedArrivalTree(NMHDR* pNMHDR, LRESULT* pResult)
1804 {
1805 	HTREEITEM h;
1806 
1807 	NM_TREEVIEW* pNMTreeView = (NM_TREEVIEW*)pNMHDR;
1808 	h = pNMTreeView->itemNew.hItem;
1809 	if (h){
1810 		m_arrival_tree.update_help(h);
1811 	}
1812 
1813 	*pResult = 0;
1814 }
1815 
OnSelchangedDepartureTree(NMHDR * pNMHDR,LRESULT * pResult)1816 void CShipEditorDlg::OnSelchangedDepartureTree(NMHDR* pNMHDR, LRESULT* pResult)
1817 {
1818 	HTREEITEM h;
1819 
1820 	NM_TREEVIEW* pNMTreeView = (NM_TREEVIEW*)pNMHDR;
1821 	h = pNMTreeView->itemNew.hItem;
1822 	if (h){
1823 		m_departure_tree.update_help(h);
1824 	}
1825 
1826 	*pResult = 0;
1827 }
1828 
calc_cue_height()1829 void CShipEditorDlg::calc_cue_height()
1830 {
1831 	CRect cue, help;
1832 
1833 	GetDlgItem(IDC_CUE_FRAME)->GetWindowRect(cue);
1834 	cue_height = (cue.bottom - cue.top)+20;
1835 	if (Show_sexp_help){
1836 		GetDlgItem(IDC_HELP_BOX)->GetWindowRect(help);
1837 		cue_height += (help.bottom - help.top);
1838 	}
1839 
1840 	if (Hide_ship_cues) {
1841 		((CButton *) GetDlgItem(IDC_HIDE_CUES)) -> SetCheck(1);
1842 		OnHideCues();
1843 	}
1844 }
1845 
show_hide_sexp_help()1846 void CShipEditorDlg::show_hide_sexp_help()
1847 {
1848 	CRect rect, help;
1849 	GetDlgItem(IDC_HELP_BOX)->GetWindowRect(help);
1850 	float box_size = (float)(help.bottom - help.top);
1851 
1852 	if (Show_sexp_help){
1853 		cue_height += (int)box_size;
1854 	} else {
1855 		cue_height -= (int)box_size;
1856 	}
1857 
1858 	if (((CButton *) GetDlgItem(IDC_HIDE_CUES)) -> GetCheck()){
1859 		return;
1860 	}
1861 
1862 	GetWindowRect(rect);
1863 
1864 	if (Show_sexp_help){
1865 		rect.bottom += (LONG)box_size;
1866 	} else {
1867 		rect.bottom -= (LONG)box_size;
1868 	}
1869 
1870 	MoveWindow(rect);
1871 }
1872 
OnHideCues()1873 void CShipEditorDlg::OnHideCues()
1874 {
1875 	CRect rect;
1876 
1877 	GetWindowRect(rect);
1878 	if (((CButton *) GetDlgItem(IDC_HIDE_CUES)) -> GetCheck()) {
1879 		rect.bottom -= cue_height;
1880 		Hide_ship_cues = 1;
1881 
1882 	} else {
1883 		rect.bottom += cue_height;
1884 		Hide_ship_cues = 0;
1885 	}
1886 
1887 	MoveWindow(rect);
1888 }
1889 
OnSelchangeArrivalLocation()1890 void CShipEditorDlg::OnSelchangeArrivalLocation()
1891 {
1892 	CComboBox *box;
1893 
1894 	UpdateData();
1895 	box = (CComboBox *)GetDlgItem( IDC_ARRIVAL_TARGET );
1896 	if (m_arrival_location) {
1897 		GetDlgItem(IDC_ARRIVAL_DISTANCE)->EnableWindow(TRUE);
1898 		GetDlgItem(IDC_ARRIVAL_TARGET)->EnableWindow(TRUE);
1899 		if (m_arrival_target < 0) {
1900 			m_arrival_target = 0;
1901 		}
1902 
1903 		// determine which items we should put into the arrival target combo box
1904 		if ( m_arrival_location == ARRIVE_FROM_DOCK_BAY ) {
1905 			management_add_ships_to_combo( box, SHIPS_2_COMBO_DOCKING_BAY_ONLY );
1906 		} else {
1907 			management_add_ships_to_combo( box, SHIPS_2_COMBO_SPECIAL | SHIPS_2_COMBO_ALL_SHIPS );
1908 		}
1909 	} else {
1910 		m_arrival_target = -1;
1911 		GetDlgItem(IDC_ARRIVAL_DISTANCE)->EnableWindow(FALSE);
1912 		GetDlgItem(IDC_ARRIVAL_TARGET)->EnableWindow(FALSE);
1913 	}
1914 
1915 	if (m_arrival_location == ARRIVE_FROM_DOCK_BAY)	{
1916 		GetDlgItem(IDC_RESTRICT_ARRIVAL)->EnableWindow(TRUE);
1917 		GetDlgItem(IDC_CUSTOM_WARPIN_PARAMS)->EnableWindow(FALSE);
1918 	} else {
1919 		GetDlgItem(IDC_RESTRICT_ARRIVAL)->EnableWindow(FALSE);
1920 		GetDlgItem(IDC_CUSTOM_WARPIN_PARAMS)->EnableWindow(TRUE);
1921 	}
1922 
1923 	UpdateData(FALSE);
1924 }
1925 
OnSelchangeDepartureLocation()1926 void CShipEditorDlg::OnSelchangeDepartureLocation()
1927 {
1928 	CComboBox *box;
1929 
1930 	UpdateData();
1931 	box = (CComboBox *)GetDlgItem(IDC_DEPARTURE_TARGET);
1932 	if ( m_departure_location ) {
1933 		box->EnableWindow(TRUE);
1934 		if ( m_departure_target < 0 ) {
1935 			m_departure_target = 0;
1936 		}
1937 
1938 		// we need to build up the list box content based on the departure type.  When
1939 		// from a docking bay, only show ships in the list which have them.  Show all ships otherwise
1940 		if ( m_departure_location == DEPART_AT_DOCK_BAY ) {
1941 			management_add_ships_to_combo( box, SHIPS_2_COMBO_DOCKING_BAY_ONLY );
1942 		} else {
1943 			// I think that this section is currently illegal
1944 			Int3();
1945 		}
1946 	} else {
1947 		m_departure_target = -1;
1948 		box->EnableWindow(FALSE);
1949 	}
1950 
1951 	if (m_departure_location == DEPART_AT_DOCK_BAY)	{
1952 		GetDlgItem(IDC_RESTRICT_DEPARTURE)->EnableWindow(TRUE);
1953 		GetDlgItem(IDC_CUSTOM_WARPOUT_PARAMS)->EnableWindow(FALSE);
1954 	} else {
1955 		GetDlgItem(IDC_RESTRICT_DEPARTURE)->EnableWindow(FALSE);
1956 		GetDlgItem(IDC_CUSTOM_WARPOUT_PARAMS)->EnableWindow(TRUE);
1957 	}
1958 
1959 	UpdateData(FALSE);
1960 }
1961 
1962 
OnPlayerShip()1963 void CShipEditorDlg::OnPlayerShip()
1964 {
1965 	if (m_player_ship.GetCheck() == 1)
1966 		m_player_ship.SetCheck(0);
1967 	else
1968 		m_player_ship.SetCheck(1);
1969 
1970 	update_map_window();
1971 }
1972 
OnNoArrivalWarp()1973 void CShipEditorDlg::OnNoArrivalWarp()
1974 {
1975 	if (m_no_arrival_warp.GetCheck() == 1)
1976 		m_no_arrival_warp.SetCheck(0);
1977 	else
1978 		m_no_arrival_warp.SetCheck(1);
1979 }
1980 
OnNoDepartureWarp()1981 void CShipEditorDlg::OnNoDepartureWarp()
1982 {
1983 	if (m_no_departure_warp.GetCheck() == 1)
1984 		m_no_departure_warp.SetCheck(0);
1985 	else
1986 		m_no_departure_warp.SetCheck(1);
1987 }
1988 
1989 
1990 // function to possibly warn user when he selects a hotkey which might be used for
1991 // a player wing.
OnSelchangeHotkey()1992 void CShipEditorDlg::OnSelchangeHotkey()
1993 {
1994 	int set_num;
1995 	char buf[256];
1996 
1997 	UpdateData(TRUE);
1998 	set_num = m_hotkey-1;			// use -1 since values associated with hotkey sets are 1 index based
1999 
2000 	// the first three sets are generally reserved for player starting wings.
2001 	if ( set_num >= 0 && set_num < MAX_STARTING_WINGS ) {
2002 		sprintf( buf, "This hotkey set should probably be reserved\nfor wing %s", Starting_wing_names[set_num] );
2003 		MessageBox(buf, NULL, MB_OK);
2004 	}
2005 }
2006 
OnFlags()2007 void CShipEditorDlg::OnFlags()
2008 {
2009 	ship_flags_dlg dlg;
2010 
2011 	dlg.setup(p_enable);
2012 	dlg.DoModal();
2013 }
2014 
OnIgnoreOrders()2015 void CShipEditorDlg::OnIgnoreOrders()
2016 {
2017 	// TODO: Add your control notification handler code here
2018 	ignore_orders_dlg player_order_dlg;
2019 
2020 	Assert(query_valid_object());
2021 
2022 	if (!multi_edit) {
2023 		if ( single_ship != -1 ){
2024 			player_order_dlg.m_ship = single_ship;
2025 		} else {
2026 			player_order_dlg.m_ship = player_ship;
2027 		}
2028 	} else {
2029 		player_order_dlg.m_ship = -1;
2030 	}
2031 
2032 	player_order_dlg.DoModal();
2033 }
2034 
OnSpecialExp()2035 void CShipEditorDlg::OnSpecialExp()
2036 {
2037 	// TODO: Add your control notification handler code here
2038 	ShipSpecialDamage dlg;
2039 	dlg.DoModal();
2040 }
2041 
2042 // alternate ship name stuff
ship_alt_name_init(int base_ship)2043 void CShipEditorDlg::ship_alt_name_init(int base_ship)
2044 {
2045 	int idx, sel_idx;
2046 	CComboBox *ptr = (CComboBox*)GetDlgItem(IDC_SHIP_ALT);
2047 	if(ptr == NULL){
2048 		Int3();
2049 		return;
2050 	}
2051 
2052 	// multi-edit. bah
2053 	if(multi_edit){
2054 		GetDlgItem(IDC_SHIP_ALT)->EnableWindow(FALSE);
2055 		return;
2056 	}
2057 	GetDlgItem(IDC_SHIP_ALT)->EnableWindow(TRUE);
2058 
2059 	// reset the combobox and add all relevant strings
2060 	ptr->ResetContent();
2061 	ptr->AddString("<none>");
2062 	sel_idx = (base_ship < 0 || !strlen(Fred_alt_names[base_ship])) ? -1 : -2;
2063 	for(idx=0; idx<Mission_alt_type_count; idx++){
2064 		ptr->AddString(Mission_alt_types[idx]);
2065 		if (sel_idx == -2 && !strcmp(Mission_alt_types[idx], Fred_alt_names[base_ship])) {
2066 			sel_idx = idx;
2067 		}
2068 	}
2069 	Assertion(sel_idx >= -1, "Alt name exists but can't be found in Mission_alt_types; get a coder!\n");
2070 
2071 	sel_idx += 1;
2072 	ptr->SetCurSel(sel_idx);
2073 }
2074 
ship_alt_name_close(int base_ship)2075 void CShipEditorDlg::ship_alt_name_close(int base_ship)
2076 {
2077 	CString cstr;
2078 	char str[NAME_LENGTH+2] = "";
2079 	char *p;
2080 	CComboBox *ptr = (CComboBox*)GetDlgItem(IDC_SHIP_ALT);
2081 
2082 	if(multi_edit){
2083 		return;
2084 	}
2085 
2086 	if(ptr == NULL){
2087 		Int3();
2088 		return;
2089 	}
2090 
2091 	ptr->GetWindowText(cstr);
2092 	cstr.TrimLeft();
2093 	cstr.TrimRight();
2094 	p = cstr.GetBuffer(0);
2095 	if(p == NULL){
2096 		return;
2097 	}
2098 	strcpy_s(str, p);
2099 
2100 	// do we have an empty string or "none" selected?
2101 	if(!*str || !stricmp(str, "<none>")) {
2102 		// if we currently have an entry, remove it -- but only if it's unused
2103 		if (*Fred_alt_names[base_ship]) {
2104 			bool used = false;
2105 			for (int i = 0; i < MAX_SHIPS; ++i) {
2106 				if (i != base_ship && !strcmp(Fred_alt_names[i], Fred_alt_names[base_ship])) {
2107 					used = true;
2108 					break;
2109 				}
2110 			}
2111 			if (!used) {
2112 				mission_parse_remove_alt(Fred_alt_names[base_ship]);
2113 			}
2114 
2115 			// zero the entry
2116 			strcpy_s(Fred_alt_names[base_ship], "");
2117 		}
2118 		return;
2119 	}
2120 
2121 	// otherwise see if it already exists
2122 	if(mission_parse_lookup_alt(str) >= 0){
2123 		strcpy_s(Fred_alt_names[base_ship], str);
2124 		return;
2125 	}
2126 
2127 	// otherwise try and add it
2128 	if(mission_parse_add_alt(str) >= 0){
2129 		strcpy_s(Fred_alt_names[base_ship], str);
2130 		return;
2131 	}
2132 
2133 	// bad - couldn't add
2134 	strcpy_s(Fred_alt_names[base_ship], "");
2135 	MessageBox("Couldn't add new alternate type name. Already using too many!");
2136 }
2137 
2138 // callsign stuff
ship_callsign_init(int base_ship)2139 void CShipEditorDlg::ship_callsign_init(int base_ship)
2140 {
2141 	int idx, sel_idx;
2142 	CComboBox *ptr = (CComboBox*)GetDlgItem(IDC_SHIP_CALLSIGN);
2143 	if(ptr == NULL){
2144 		Int3();
2145 		return;
2146 	}
2147 
2148 	// multi-edit. bah
2149 	if(multi_edit){
2150 		GetDlgItem(IDC_SHIP_CALLSIGN)->EnableWindow(FALSE);
2151 		return;
2152 	}
2153 	GetDlgItem(IDC_SHIP_CALLSIGN)->EnableWindow(TRUE);
2154 
2155 	// reset the combobox and add all relevant strings
2156 	ptr->ResetContent();
2157 	ptr->AddString("<none>");
2158 	sel_idx = (base_ship < 0 || !strlen(Fred_callsigns[base_ship])) ? -1 : -2;
2159 	for(idx=0; idx<Mission_callsign_count; idx++){
2160 		ptr->AddString(Mission_callsigns[idx]);
2161 		if (sel_idx == -2 && !strcmp(Mission_callsigns[idx], Fred_callsigns[base_ship])) {
2162 			sel_idx = idx;
2163 		}
2164 	}
2165 	Assertion(sel_idx >= -1, "Callsign exists but can't be found in Mission_callsigns; get a coder!\n");
2166 
2167 	sel_idx += 1;
2168 	ptr->SetCurSel(sel_idx);
2169 }
2170 
ship_callsign_close(int base_ship)2171 void CShipEditorDlg::ship_callsign_close(int base_ship)
2172 {
2173 	CString cstr;
2174 	char str[NAME_LENGTH+2] = "";
2175 	char *p;
2176 	CComboBox *ptr = (CComboBox*)GetDlgItem(IDC_SHIP_CALLSIGN);
2177 
2178 	if(multi_edit){
2179 		return;
2180 	}
2181 
2182 	if(ptr == NULL){
2183 		Int3();
2184 		return;
2185 	}
2186 
2187 	ptr->GetWindowText(cstr);
2188 	cstr.TrimLeft();
2189 	cstr.TrimRight();
2190 	p = cstr.GetBuffer(0);
2191 	if(p == NULL){
2192 		return;
2193 	}
2194 	strcpy_s(str, p);
2195 
2196 	// do we have an empty string or "none" selected?
2197 	if(!*str || !stricmp(str, "<none>")) {
2198 		// if we currently have an entry, remove it -- but only if it's unused
2199 		if (*Fred_callsigns[base_ship]) {
2200 			bool used = false;
2201 			for (int i = 0; i < MAX_SHIPS; ++i) {
2202 				if (i != base_ship && !strcmp(Fred_callsigns[i], Fred_callsigns[base_ship])) {
2203 					used = true;
2204 					break;
2205 				}
2206 			}
2207 			if (!used) {
2208 				mission_parse_remove_callsign(Fred_callsigns[base_ship]);
2209 			}
2210 
2211 			// zero the entry
2212 			strcpy_s(Fred_callsigns[base_ship], "");
2213 		}
2214 		return;
2215 	}
2216 
2217 	// otherwise see if it already exists
2218 	if(mission_parse_lookup_callsign(str) >= 0){
2219 		strcpy_s(Fred_callsigns[base_ship], str);
2220 		return;
2221 	}
2222 
2223 	// otherwise try and add it
2224 	if(mission_parse_add_callsign(str) >= 0){
2225 		strcpy_s(Fred_callsigns[base_ship], str);
2226 
2227 		return;
2228 	}
2229 
2230 	// bad - couldn't add
2231 	strcpy_s(Fred_callsigns[base_ship], "");
2232 	MessageBox("Couldn't add new callsign. Already using too many!");
2233 }
2234 
OnTextures()2235 void CShipEditorDlg::OnTextures()
2236 {
2237 	CShipTexturesDlg dlg_textures;
2238 
2239 	Assert(query_valid_object());
2240 
2241 	if (multi_edit)
2242 	{
2243 		MessageBox("Sorry, you can only edit textures for one ship at a time.", "Too many ships selected");
2244 		return;
2245 	}
2246 
2247 	// get the ship that's marked
2248 	int marked_ship = (player_ship >= 0) ? player_ship : single_ship;
2249 
2250 	dlg_textures.self_ship = marked_ship;
2251 	dlg_textures.DoModal();
2252 }
2253 
OnSpecialHitpoints()2254 void CShipEditorDlg::OnSpecialHitpoints()
2255 {
2256 	ShipSpecialHitpoints dlg;
2257 	dlg.DoModal();
2258 }
2259 
OnAltShipClass()2260 void CShipEditorDlg::OnAltShipClass()
2261 {
2262 	AltShipClassDlg dlg;
2263 	dlg.DoModal();
2264 }
2265 
2266 // Goober5000
OnSetAsPlayerShip()2267 void CShipEditorDlg::OnSetAsPlayerShip()
2268 {
2269 	Assert(query_valid_object());
2270 
2271 	if (multi_edit)
2272 	{
2273 		MessageBox("Please select only one ship.", "Too many ships selected");
2274 		return;
2275 	}
2276 
2277 	// since this is single player, clear all player ships and set only this one
2278 	object *objp = GET_FIRST(&obj_used_list);
2279 	while (objp != END_OF_LIST(&obj_used_list))
2280 	{
2281 		if ((objp->type == OBJ_SHIP) || (objp->type == OBJ_START))
2282 		{
2283 			if (objp->flags[Object::Object_Flags::Marked])	// there should only be one selected ship
2284 			{
2285 				// set as player ship
2286 				objp->type = OBJ_START;
2287 				objp->flags.set(Object::Object_Flags::Player_ship);
2288 			}
2289 			else
2290 			{
2291 				// set as regular ship
2292 				objp->type = OBJ_SHIP;
2293 				objp->flags.remove(Object::Object_Flags::Player_ship);
2294 			}
2295 		}
2296 		objp = GET_NEXT(objp);
2297 	}
2298 
2299 	// finally set editor dialog
2300 	m_player_ship.SetCheck(1);
2301 	update_map_window();
2302 }
2303 
2304 // Goober5000
OnRestrictArrival()2305 void CShipEditorDlg::OnRestrictArrival()
2306 {
2307 	int arrive_from_ship;
2308 	CComboBox *box;
2309 	restrict_paths dlg;
2310 
2311 	if (multi_edit)
2312 	{
2313 		MessageBox("Please select only one ship.", "Too many ships selected");
2314 		return;
2315 	}
2316 
2317 	// grab stuff from GUI
2318 	UpdateData(TRUE);
2319 
2320 	if (m_arrival_location != ARRIVE_FROM_DOCK_BAY)
2321 	{
2322 		Int3();
2323 		return;
2324 	}
2325 
2326 	box = (CComboBox *) GetDlgItem(IDC_ARRIVAL_TARGET);
2327 	if (box->GetCount() == 0)
2328 		return;
2329 
2330 	arrive_from_ship = (int)box->GetItemData(m_arrival_target);
2331 
2332 	if (!ship_has_dock_bay(arrive_from_ship))
2333 	{
2334 		Int3();
2335 		return;
2336 	}
2337 
2338 	// get the ship that's marked
2339 	int marked_ship = (player_ship >= 0) ? player_ship : single_ship;
2340 
2341 	dlg.m_arrival = true;
2342 	dlg.m_ship_class = Ships[arrive_from_ship].ship_info_index;
2343 	dlg.m_path_mask = &Ships[marked_ship].arrival_path_mask;
2344 
2345 	dlg.DoModal();
2346 }
2347 
2348 // Goober5000
OnRestrictDeparture()2349 void CShipEditorDlg::OnRestrictDeparture()
2350 {
2351 	int depart_to_ship;
2352 	CComboBox *box;
2353 	restrict_paths dlg;
2354 
2355 	if (multi_edit)
2356 	{
2357 		MessageBox("Please select only one ship.", "Too many ships selected");
2358 		return;
2359 	}
2360 
2361 	// grab stuff from GUI
2362 	UpdateData(TRUE);
2363 
2364 	if (m_departure_location != DEPART_AT_DOCK_BAY)
2365 	{
2366 		Int3();
2367 		return;
2368 	}
2369 
2370 	box = (CComboBox *) GetDlgItem(IDC_DEPARTURE_TARGET);
2371 	if (box->GetCount() == 0)
2372 		return;
2373 
2374 	depart_to_ship = (int)box->GetItemData(m_departure_target);
2375 
2376 	if (!ship_has_dock_bay(depart_to_ship))
2377 	{
2378 		Int3();
2379 		return;
2380 	}
2381 
2382 	// get the ship that's marked
2383 	int marked_ship = (player_ship >= 0) ? player_ship : single_ship;
2384 
2385 	dlg.m_arrival = false;
2386 	dlg.m_ship_class = Ships[depart_to_ship].ship_info_index;
2387 	dlg.m_path_mask = &Ships[marked_ship].departure_path_mask;
2388 
2389 	dlg.DoModal();
2390 }
2391 
OnBnClickedCustomWarpinParams()2392 void CShipEditorDlg::OnBnClickedCustomWarpinParams()
2393 {
2394 	warp_params_dlg dlg;
2395 	dlg.m_warp_in = true;
2396 	dlg.DoModal();
2397 }
2398 
OnBnClickedCustomWarpoutParams()2399 void CShipEditorDlg::OnBnClickedCustomWarpoutParams()
2400 {
2401 	warp_params_dlg dlg;
2402 	dlg.m_warp_in = false;
2403 	dlg.DoModal();
2404 }
2405