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