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