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 "BriefingEditorDlg.h"
15 #include "FREDDoc.h"
16 #include "mission/missionbriefcommon.h"
17 #include "mission/missionparse.h"
18 #include "FredRender.h"
19 #include "Management.h"
20 #include "globalincs/linklist.h"
21 #include "MainFrm.h"
22 #include "bmpman/bmpman.h"
23 #include "gamesnd/eventmusic.h"
24 #include "starfield/starfield.h"
25 #include "jumpnode/jumpnode.h"
26 #include "cfile/cfile.h"
27 #include "object/objectdock.h"
28 #include "iff_defs/iff_defs.h"
29 #include "sound/audiostr.h"
30 #include "localization/localize.h"
31 
32 #ifdef _DEBUG
33 #undef THIS_FILE
34 static char THIS_FILE[] = __FILE__;
35 #endif
36 
37 #define ID_MENU		9000
38 
39 static int Max_icons_for_lines;
40 
41 /////////////////////////////////////////////////////////////////////////////
42 // briefing_editor_dlg dialog
43 
briefing_editor_dlg(CWnd * pParent)44 briefing_editor_dlg::briefing_editor_dlg(CWnd* pParent /*=NULL*/)
45 	: CDialog(briefing_editor_dlg::IDD, pParent)
46 {
47 	int i, z;
48 
49 	// figure out max icons we can have with lines to each other less than max allowed lines.
50 	// Basically, # lines = (# icons - 1) factorial
51 	Max_icons_for_lines = 0;
52 	do {
53 		i = ++Max_icons_for_lines + 1;
54 		z = 0;
55 		while (--i)
56 			z += i;
57 
58 	} while (z < MAX_BRIEF_STAGE_LINES);
59 
60 	//{{AFX_DATA_INIT(briefing_editor_dlg)
61 	m_hilight = FALSE;
62 	m_icon_image = -1;
63 	m_icon_label = _T("");
64 	m_stage_title = _T("");
65 	m_text = _T("");
66 	m_time = _T("");
67 	m_voice = _T("");
68 	m_icon_text = _T("");
69 	m_icon_team = -1;
70 	m_ship_type = -1;
71 	m_change_local = FALSE;
72 	m_id = 0;
73 	m_briefing_music = -1;
74 	m_substitute_briefing_music = _T("");
75 	m_cut_next = FALSE;
76 	m_cut_prev = FALSE;
77 	m_current_briefing = -1;
78 	m_flipicon = FALSE;
79 	m_use_wing = FALSE;
80 	m_use_cargo = FALSE;
81 	//}}AFX_DATA_INIT
82 	m_voice_id = -1;
83 	m_cur_stage = 0;
84 	m_last_stage = m_cur_icon = m_last_icon = -1;
85 	m_tree.link_modified(&modified);  // provide way to indicate trees are modified in dialog
86 
87 	// copy view initialization
88 	m_copy_view_set = 0;
89 }
90 
DoDataExchange(CDataExchange * pDX)91 void briefing_editor_dlg::DoDataExchange(CDataExchange* pDX)
92 {
93 	CDialog::DoDataExchange(pDX);
94 	//{{AFX_DATA_MAP(briefing_editor_dlg)
95 	DDX_Control(pDX, IDC_TREE, m_tree);
96 	DDX_Control(pDX, IDC_LINES, m_lines);
97 	DDX_Check(pDX, IDC_HILIGHT, m_hilight);
98 	DDX_CBIndex(pDX, IDC_ICON_IMAGE, m_icon_image);
99 	DDX_Text(pDX, IDC_ICON_LABEL, m_icon_label);
100 	DDX_Text(pDX, IDC_STAGE_TITLE, m_stage_title);
101 	DDX_Text(pDX, IDC_TEXT, m_text);
102 	DDX_Text(pDX, IDC_TIME, m_time);
103 	DDX_Text(pDX, IDC_VOICE, m_voice);
104 	DDX_Text(pDX, IDC_ICON_TEXT, m_icon_text);
105 	DDX_CBIndex(pDX, IDC_TEAM, m_icon_team);
106 	DDX_CBIndex(pDX, IDC_SHIP_TYPE, m_ship_type);
107 	DDX_Check(pDX, IDC_LOCAL, m_change_local);
108 	DDX_Text(pDX, IDC_ID, m_id);
109 	DDX_CBIndex(pDX, IDC_BRIEFING_MUSIC, m_briefing_music);
110 	DDX_Text(pDX, IDC_SUBSTITUTE_BRIEFING_MUSIC, m_substitute_briefing_music);
111 	DDX_Check(pDX, IDC_CUT_NEXT, m_cut_next);
112 	DDX_Check(pDX, IDC_CUT_PREV, m_cut_prev);
113 	DDX_Check(pDX, IDC_FLIP_ICON, m_flipicon);
114 	DDX_Check(pDX, IDC_USE_WING_ICON, m_use_wing);
115 	DDX_Check(pDX, IDC_USE_CARGO_ICON, m_use_cargo);
116 	//}}AFX_DATA_MAP
117 
118 	DDV_MaxChars(pDX, m_voice, MAX_FILENAME_LEN - 1);
119 	DDV_MaxChars(pDX, m_icon_label, MAX_LABEL_LEN - 1);
120 	DDV_MaxChars(pDX, m_icon_text, MAX_ICON_TEXT_LEN - 1);
121 }
122 
BEGIN_MESSAGE_MAP(briefing_editor_dlg,CDialog)123 BEGIN_MESSAGE_MAP(briefing_editor_dlg, CDialog)
124 	//{{AFX_MSG_MAP(briefing_editor_dlg)
125 	ON_WM_CLOSE()
126 	ON_BN_CLICKED(IDC_NEXT, OnNext)
127 	ON_BN_CLICKED(IDC_PREV, OnPrev)
128 	ON_BN_CLICKED(IDC_BROWSE, OnBrowse)
129 	ON_BN_CLICKED(IDC_ADD_STAGE, OnAddStage)
130 	ON_BN_CLICKED(IDC_DELETE_STAGE, OnDeleteStage)
131 	ON_BN_CLICKED(IDC_INSERT_STAGE, OnInsertStage)
132 	ON_BN_CLICKED(IDC_MAKE_ICON, OnMakeIcon)
133 	ON_BN_CLICKED(IDC_DELETE_ICON, OnDeleteIcon)
134 	ON_BN_CLICKED(IDC_GOTO_VIEW, OnGotoView)
135 	ON_BN_CLICKED(IDC_SAVE_VIEW, OnSaveView)
136 	ON_CBN_SELCHANGE(IDC_ICON_IMAGE, OnSelchangeIconImage)
137 	ON_CBN_SELCHANGE(IDC_TEAM, OnSelchangeTeam)
138 	ON_CBN_SELCHANGE(IDC_SHIP_TYPE, OnSelchangeShipType)
139 	ON_BN_CLICKED(IDC_PROPAGATE_ICONS, OnPropagateIcons)
140 	ON_WM_INITMENU()
141 	ON_BN_CLICKED(IDC_LINES, OnLines)
142 	ON_NOTIFY(NM_RCLICK, IDC_TREE, OnRclickTree)
143 	ON_NOTIFY(TVN_BEGINLABELEDIT, IDC_TREE, OnBeginlabeleditTree)
144 	ON_NOTIFY(TVN_ENDLABELEDIT, IDC_TREE, OnEndlabeleditTree)
145 	ON_BN_CLICKED(IDC_PLAY, OnPlay)
146 	ON_BN_CLICKED(IDC_COPY_VIEW, OnCopyView)
147 	ON_BN_CLICKED(IDC_PASTE_VIEW, OnPasteView)
148 	ON_BN_CLICKED(IDC_FLIP_ICON, OnFlipIcon)
149 	ON_BN_CLICKED(IDC_USE_WING_ICON, OnWingIcon)
150 	ON_BN_CLICKED(IDC_USE_CARGO_ICON, OnCargoIcon)
151 	//}}AFX_MSG_MAP
152 END_MESSAGE_MAP()
153 
154 /////////////////////////////////////////////////////////////////////////////
155 // briefing_editor_dlg message handlers
156 
157 void briefing_editor_dlg::OnInitMenu(CMenu* pMenu)
158 {
159 	int i;
160 	CMenu *m;
161 
162 	// disable any items we should disable
163 	m = pMenu->GetSubMenu(0);
164 
165 	// uncheck all menu items
166 	for (i=0; i<Num_teams; i++)
167 		m->CheckMenuItem(i, MF_BYPOSITION | MF_UNCHECKED);
168 
169 	for (i=Num_teams; i<MAX_TVT_TEAMS; i++)
170 		m->EnableMenuItem(i, MF_BYPOSITION | MF_GRAYED);
171 
172 	// put a check next to the currently selected item
173 	m->CheckMenuItem(m_current_briefing, MF_BYPOSITION | MF_CHECKED );
174 
175 	// Karajorma - it might be nice to autobalance the briefings and debriefings but I'm not doing anything till I
176 	// understand how the how system works better. disabling the option for now.
177 	m = pMenu->GetSubMenu(1);
178 	m->EnableMenuItem(ID_AUTOBALANCE, MF_GRAYED);
179 
180 	CDialog::OnInitMenu(pMenu);
181 }
182 
create()183 void briefing_editor_dlg::create()
184 {
185 	int i;
186 	CComboBox *box;
187 
188 	CDialog::Create(IDD);
189 	theApp.init_window(&Briefing_wnd_data, this);
190 	box = (CComboBox *) GetDlgItem(IDC_ICON_IMAGE);
191 	for (i=0; i<MIN_BRIEF_ICONS; i++)
192 		box->AddString(Icon_names[i]);
193 
194 	box = (CComboBox *) GetDlgItem(IDC_TEAM);
195 	for (i=0; i<Num_iffs; i++)
196 		box->AddString(Iff_info[i].iff_name);
197 
198 	box = (CComboBox *) GetDlgItem(IDC_SHIP_TYPE);
199 	for (auto it = Ship_info.cbegin(); it != Ship_info.cend(); ++it)
200 		box->AddString(it->name);
201 
202 	box = (CComboBox *) GetDlgItem(IDC_BRIEFING_MUSIC);
203 	box->AddString("None");
204 	for (i=0; i<Num_music_files; i++)
205 		box->AddString(Spooled_music[i].name);
206 
207 	box = (CComboBox *) GetDlgItem(IDC_SUBSTITUTE_BRIEFING_MUSIC);
208 	box->AddString("None");
209 	for (i=0; i<Num_music_files; i++)
210 		box->AddString(Spooled_music[i].name);
211 
212 	m_play_bm.LoadBitmap(IDB_PLAY);
213 	((CButton *) GetDlgItem(IDC_PLAY)) -> SetBitmap(m_play_bm);
214 
215 	m_current_briefing = 0;
216 	Briefing = &Briefings[m_current_briefing];
217 	m_briefing_music = Mission_music[SCORE_BRIEFING] + 1;
218 	m_substitute_briefing_music = The_mission.substitute_briefing_music_name;
219 
220 	UpdateData(FALSE);
221 	update_data();
222 	OnGotoView();
223 	update_map_window();
224 }
225 
focus_sexp(int select_sexp_node)226 void briefing_editor_dlg::focus_sexp(int select_sexp_node)
227 {
228 	int i, n;
229 
230 	n = m_tree.select_sexp_node = select_sexp_node;
231 	if (n != -1) {
232 		for (i=0; i<Briefing->num_stages; i++)
233 			if (query_node_in_sexp(n, Briefing->stages[i].formula))
234 				break;
235 
236 		if (i < Briefing->num_stages) {
237 			m_cur_stage = i;
238 			update_data();
239 			GetDlgItem(IDC_TREE) -> SetFocus();
240 			m_tree.hilite_item(m_tree.select_sexp_node);
241 		}
242 	}
243 }
244 
OnOK()245 void briefing_editor_dlg::OnOK()
246 {
247 }
248 
OnCancel()249 void briefing_editor_dlg::OnCancel()
250 {
251 	OnClose();
252 }
253 
OnClose()254 void briefing_editor_dlg::OnClose()
255 {
256 	int bs, i, j, s, t, dup = 0;
257 	briefing_editor_dlg *ptr;
258 	brief_stage *sp;
259 
260 	m_cur_stage = -1;
261 	update_data(1);
262 
263 	audiostream_close_file(m_voice_id, 0);
264 
265 	for ( bs = 0; bs < Num_teams; bs++ ) {
266 		for (s=0; s<Briefing[bs].num_stages; s++) {
267 			sp = &Briefing[bs].stages[s];
268 			t = sp->num_icons;
269 			for (i=0; i<t-1; i++)
270 				for (j=i+1; j<t; j++) {
271 					if ((sp->icons[i].id >= 0) && (sp->icons[i].id == sp->icons[j].id))
272 						dup = 1;
273 				}
274 		}
275 	}
276 
277 	if (dup)
278 		MessageBox("You have duplicate icons names.  You should resolve these.", "Warning");
279 
280 	theApp.record_window_data(&Briefing_wnd_data, this);
281 	ptr = Briefing_dialog;
282 	Briefing_dialog = NULL;
283 	delete ptr;
284 }
285 
reset_editor()286 void briefing_editor_dlg::reset_editor()
287 {
288 	audiostream_close_file(m_voice_id, 0);
289 	m_voice_id = -1;
290 
291 	m_cur_stage = -1;
292 	update_data(0);
293 }
294 
295 // some kind of hackish code to get around the problem if saving (which implicitly loads,
296 // which implicitly clears all mission info) not affecting the editor's state at save.
save_editor_state()297 void briefing_editor_dlg::save_editor_state()
298 {
299 	stage_saved = m_cur_stage;
300 	icon_saved = m_cur_icon;
301 }
302 
restore_editor_state()303 void briefing_editor_dlg::restore_editor_state()
304 {
305 	m_cur_stage = stage_saved;
306 	m_cur_icon = icon_saved;
307 }
308 
update_data(int update)309 void briefing_editor_dlg::update_data(int update)
310 {
311 	char buf[MAX_LABEL_LEN], buf2[MAX_ICON_TEXT_LEN];
312 	SCP_string buf3;
313 	int i, j, l, lines, count, enable = TRUE, valid = 0, invalid = 0;
314 	object *objp;
315 	brief_stage *ptr = NULL;
316 
317 	if (update)
318 		UpdateData(TRUE);
319 
320 	// save off current data before we update over it with new briefing stage/team stuff
321 	Briefing = save_briefing;
322 
323 	Mission_music[SCORE_BRIEFING] = m_briefing_music - 1;
324 	strcpy_s(The_mission.substitute_briefing_music_name, m_substitute_briefing_music);
325 	if (m_last_stage >= 0) {
326 		ptr = &Briefing->stages[m_last_stage];
327 		deconvert_multiline_string(buf3, m_text);
328 		lcl_fred_replace_stuff(buf3);
329 		if (ptr->text != buf3)
330 			set_modified();
331 
332 		ptr->text = buf3;
333 		MODIFY(ptr->camera_time, atoi(m_time));
334 		string_copy(ptr->voice, m_voice, MAX_FILENAME_LEN, 1);
335 		i = ptr->flags;
336 		if (m_cut_prev)
337 			i |= BS_BACKWARD_CUT;
338 		else
339 			i &= ~BS_BACKWARD_CUT;
340 
341 		if (m_cut_next)
342 			i |= BS_FORWARD_CUT;
343 		else
344 			i &= ~BS_FORWARD_CUT;
345 
346 		MODIFY(ptr->flags, i);
347 		ptr->formula = m_tree.save_tree();
348 		switch (m_lines.GetCheck()) {
349 			case 1:
350 				// add lines between every pair of 2 marked icons if there isn't one already.
351 				for (i=0; i<ptr->num_icons - 1; i++)
352 					for (j=i+1; j<ptr->num_icons; j++) {
353 						if ( icon_marked[i] && icon_marked[j] ) {
354 							for (l=0; l<ptr->num_lines; l++)
355 								if ( ((ptr->lines[l].start_icon == i) && (ptr->lines[l].end_icon == j)) || ((ptr->lines[l].start_icon == j) && (ptr->lines[l].end_icon == i)) )
356 									break;
357 
358 							if ((l == ptr->num_lines) && (l < MAX_BRIEF_STAGE_LINES)) {
359 								ptr->lines[l].start_icon = i;
360 								ptr->lines[l].end_icon = j;
361 								ptr->num_lines++;
362 							}
363 						}
364 					}
365 
366 				break;
367 
368 			case 0:
369 				// remove all existing lines between any 2 marked icons
370 				i = ptr->num_lines;
371 				while (i--)
372 					if ( icon_marked[ptr->lines[i].start_icon] && icon_marked[ptr->lines[i].end_icon] ) {
373 						ptr->num_lines--;
374 						for (l=i; l<ptr->num_lines; l++)
375 							ptr->lines[l] = ptr->lines[l + 1];
376 					}
377 
378 				break;
379 		}
380 
381 		//WMC - Safeguard against broken lines
382 		i = ptr->num_lines;
383 		while(i--)
384 		{
385 			if(ptr->lines[i].start_icon < 0 || ptr->lines[i].end_icon < 0 || ptr->lines[i].start_icon >= ptr->num_icons || ptr->lines[i].end_icon >= ptr->num_icons)
386 			{
387 				ptr->num_lines--;
388 				for(l = i; l < ptr->num_lines; l++)
389 					ptr->lines[l] = ptr->lines[l+1];
390 			}
391 		}
392 
393 		if (m_last_icon >= 0) {
394 			valid = (m_id != ptr->icons[m_last_icon].id);
395 			if (m_id >= 0) {
396 				if (valid && !m_change_local) {
397 					for (i=m_last_stage+1; i<Briefing->num_stages; i++) {
398 						if (find_icon(m_id, i) >= 0) {
399 							char msg[1024];
400 
401 							valid = 0;
402 							sprintf(msg, "Icon ID #%d is already used in a later stage.  You can only\n"
403 								"change to that ID locally.  Icon ID has been reset back to %d", m_id, ptr->icons[m_last_icon].id);
404 
405 							m_id = ptr->icons[m_last_icon].id;
406 							MessageBox(msg);
407 							break;
408 						}
409 					}
410 				}
411 
412 				for (i=0; i<ptr->num_icons; i++)
413 					if ((i != m_last_icon) && (ptr->icons[i].id == m_id)) {
414 						char msg[1024];
415 
416 						sprintf(msg, "Icon ID #%d is already used in this stage.  Icon ID has been reset back to %d",
417 							m_id, ptr->icons[m_last_icon].id);
418 
419 						m_id = ptr->icons[m_last_icon].id;
420 						MessageBox(msg);
421 						break;
422 					}
423 
424 				if (valid && !m_change_local) {
425 					set_modified();
426 					reset_icon_loop(m_last_stage);
427 					while (get_next_icon(ptr->icons[m_last_icon].id))
428 						iconp->id = m_id;
429 				}
430 			}
431 
432 			ptr->icons[m_last_icon].id = m_id;
433 			string_copy(buf, m_icon_label, MAX_LABEL_LEN);
434 			if (stricmp(ptr->icons[m_last_icon].label, buf) && !m_change_local) {
435 				set_modified();
436 				reset_icon_loop(m_last_stage);
437 				while (get_next_icon(m_id))
438 					strcpy_s(iconp->label, buf);
439 			}
440 
441 			strcpy_s(ptr->icons[m_last_icon].label, buf);
442 
443 			if ( m_hilight )
444 				ptr->icons[m_last_icon].flags |= BI_HIGHLIGHT;
445 			else
446 				ptr->icons[m_last_icon].flags &= ~BI_HIGHLIGHT;
447 
448 			if (m_flipicon)
449 				ptr->icons[m_last_icon].flags |= BI_MIRROR_ICON;
450 			else
451 				ptr->icons[m_last_icon].flags &= ~BI_MIRROR_ICON;
452 
453 			if (m_use_wing)
454 				ptr->icons[m_last_icon].flags |= BI_USE_WING_ICON;
455 			else
456 				ptr->icons[m_last_icon].flags &= ~BI_USE_WING_ICON;
457 
458 			if (m_use_cargo)
459 				ptr->icons[m_last_icon].flags |= BI_USE_CARGO_ICON;
460 			else
461 				ptr->icons[m_last_icon].flags &= ~BI_USE_CARGO_ICON;
462 
463 			if ((ptr->icons[m_last_icon].type != m_icon_image) && !m_change_local) {
464 				set_modified();
465 				reset_icon_loop(m_last_stage);
466 				while (get_next_icon(m_id))
467 					iconp->type = m_icon_image;
468 			}
469 			ptr->icons[m_last_icon].type = m_icon_image;
470 
471 			if ((ptr->icons[m_last_icon].team != m_icon_team) && !m_change_local) {
472 				set_modified();
473 				reset_icon_loop(m_last_stage);
474 				while (get_next_icon(m_id))
475 					iconp->team = m_icon_team;
476 			}
477 			ptr->icons[m_last_icon].team = m_icon_team;
478 
479 			if ((ptr->icons[m_last_icon].ship_class != m_ship_type) && !m_change_local) {
480 				set_modified();
481 				reset_icon_loop(m_last_stage);
482 				while (get_next_icon(m_id))
483 					iconp->ship_class = m_ship_type;
484 			}
485 			MODIFY(ptr->icons[m_last_icon].ship_class, m_ship_type);
486 
487 			deconvert_multiline_string(buf2, m_icon_text, MAX_ICON_TEXT_LEN);
488 /*
489 			if (stricmp(ptr->icons[m_last_icon].text, buf2) && !m_change_local) {
490 				set_modified();
491 				reset_icon_loop(m_last_stage);
492 				while (get_next_icon(m_id))
493 					strcpy_s(iconp->text, buf2);
494 			}
495 
496 			strcpy_s(ptr->icons[m_last_icon].text, buf2);
497 */
498 		}
499 	}
500 
501 	if (!::IsWindow(m_hWnd))
502 		return;
503 
504 	// set briefing pointer to correct team
505 	Briefing = &Briefings[m_current_briefing];
506 
507 	if ((m_cur_stage >= 0) && (m_cur_stage < Briefing->num_stages)) {
508 		ptr = &Briefing->stages[m_cur_stage];
509 		m_stage_title.Format("Stage %d of %d", m_cur_stage + 1, Briefing->num_stages);
510 		convert_multiline_string(m_text, ptr->text);
511 		m_time.Format("%d", ptr->camera_time);
512 		m_voice = ptr->voice;
513 		m_cut_prev = (ptr->flags & BS_BACKWARD_CUT) ? 1 : 0;
514 		m_cut_next = (ptr->flags & BS_FORWARD_CUT) ? 1 : 0;
515 		m_tree.load_tree(ptr->formula);
516 
517 	} else {
518 		m_stage_title = _T("No stages");
519 		m_text = _T("");
520 		m_time = _T("");
521 		m_voice = _T("");
522 		m_cut_prev = m_cut_next = 0;
523 		m_tree.clear_tree();
524 		enable = FALSE;
525 		m_cur_stage = -1;
526 	}
527 
528 	if (m_cur_stage == Briefing->num_stages - 1)
529 		GetDlgItem(IDC_NEXT) -> EnableWindow(FALSE);
530 	else
531 		GetDlgItem(IDC_NEXT) -> EnableWindow(enable);
532 
533 	if (m_cur_stage)
534 		GetDlgItem(IDC_PREV) -> EnableWindow(enable);
535 	else
536 		GetDlgItem(IDC_PREV) -> EnableWindow(FALSE);
537 
538 	if (Briefing->num_stages >= MAX_BRIEF_STAGES)
539 		GetDlgItem(IDC_ADD_STAGE) -> EnableWindow(FALSE);
540 	else
541 		GetDlgItem(IDC_ADD_STAGE) -> EnableWindow(TRUE);
542 
543 	if (Briefing->num_stages) {
544 		GetDlgItem(IDC_DELETE_STAGE) -> EnableWindow(enable);
545 		GetDlgItem(IDC_INSERT_STAGE) -> EnableWindow(enable);
546 	} else {
547 		GetDlgItem(IDC_DELETE_STAGE) -> EnableWindow(FALSE);
548 		GetDlgItem(IDC_INSERT_STAGE) -> EnableWindow(FALSE);
549 	}
550 
551 	GetDlgItem(IDC_TIME) -> EnableWindow(enable);
552 	GetDlgItem(IDC_VOICE) -> EnableWindow(enable);
553 	GetDlgItem(IDC_BROWSE) -> EnableWindow(enable);
554 	GetDlgItem(IDC_TEXT) -> EnableWindow(enable);
555 	GetDlgItem(IDC_SAVE_VIEW) -> EnableWindow(enable);
556 	GetDlgItem(IDC_GOTO_VIEW) -> EnableWindow(enable);
557 	GetDlgItem(IDC_CUT_PREV) -> EnableWindow(enable);
558 	GetDlgItem(IDC_CUT_NEXT) -> EnableWindow(enable);
559 	GetDlgItem(IDC_TREE) -> EnableWindow(enable);
560 	GetDlgItem(IDC_PLAY) -> EnableWindow(enable);
561 
562 	if ((m_cur_stage >= 0) && (m_cur_icon >= 0) && (m_cur_icon < ptr->num_icons)) {
563 		m_hilight = (ptr->icons[m_cur_icon].flags & BI_HIGHLIGHT)?1:0;
564 		m_flipicon = (ptr->icons[m_cur_icon].flags & BI_MIRROR_ICON)?1:0;
565 		m_use_wing = (ptr->icons[m_cur_icon].flags & BI_USE_WING_ICON)?1:0;
566 		m_use_cargo = (ptr->icons[m_cur_icon].flags & BI_USE_CARGO_ICON)?1:0;
567 		m_icon_image = ptr->icons[m_cur_icon].type;
568 		m_icon_team = ptr->icons[m_cur_icon].team;
569 		m_icon_label = ptr->icons[m_cur_icon].label;
570 		m_ship_type = ptr->icons[m_cur_icon].ship_class;
571 //		m_icon_text = convert_multiline_string(ptr->icons[m_cur_icon].text);
572 		m_id = ptr->icons[m_cur_icon].id;
573 		enable = TRUE;
574 
575 	} else {
576 		m_flipicon = FALSE;
577 		m_hilight = FALSE;
578 		m_use_wing = FALSE;
579 		m_use_cargo = FALSE;
580 		m_icon_image = -1;
581 		m_icon_team = -1;
582 		m_ship_type = -1;
583 		m_icon_label = _T("");
584 		m_cur_icon = -1;
585 		m_id = 0;
586 		enable = FALSE;
587 	}
588 
589 	// see if icon is overridden by ships.tbl
590 	// if so, disable the icon type box
591 	int sip_bii_ship = (m_ship_type >= 0) ? Ship_info[m_ship_type].bii_index_ship : -1;
592 	int sip_bii_wing = (sip_bii_ship >= 0) ? Ship_info[m_ship_type].bii_index_wing : -1;
593 	int sip_bii_cargo = (sip_bii_ship >= 0) ? Ship_info[m_ship_type].bii_index_ship_with_cargo : -1;
594 
595 	GetDlgItem(IDC_USE_WING_ICON) -> ShowWindow(sip_bii_wing >= 0);
596 	GetDlgItem(IDC_USE_CARGO_ICON) -> ShowWindow(sip_bii_cargo >= 0);
597 
598 	GetDlgItem(IDC_ICON_TEXT) -> EnableWindow(enable);
599 	GetDlgItem(IDC_ICON_LABEL) -> EnableWindow(enable);
600 	GetDlgItem(IDC_ICON_IMAGE) -> EnableWindow(enable && (sip_bii_ship < 0));
601 	GetDlgItem(IDC_SHIP_TYPE) -> EnableWindow(enable);
602 	GetDlgItem(IDC_HILIGHT) -> EnableWindow(enable);
603 	GetDlgItem(IDC_FLIP_ICON) -> EnableWindow(enable);
604 	GetDlgItem(IDC_USE_WING_ICON) -> EnableWindow(enable);
605 	GetDlgItem(IDC_USE_CARGO_ICON) -> EnableWindow(enable);
606 	GetDlgItem(IDC_LOCAL) -> EnableWindow(enable);
607 	GetDlgItem(IDC_TEAM) -> EnableWindow(enable);
608 	GetDlgItem(IDC_ID) -> EnableWindow(enable);
609 	GetDlgItem(IDC_DELETE_ICON) -> EnableWindow(enable);
610 
611 	valid = invalid = 0;
612 	objp = GET_FIRST(&obj_used_list);
613 	while (objp != END_OF_LIST(&obj_used_list)) {
614 		if (objp->flags[Object::Object_Flags::Marked]) {
615 			if ((objp->type == OBJ_SHIP) || (objp->type == OBJ_START) || (objp->type == OBJ_WAYPOINT) || (objp->type == OBJ_JUMP_NODE))
616 				valid = 1;
617 			else
618 				invalid = 1;
619 		}
620 
621 		objp = GET_NEXT(objp);
622 	}
623 
624 	if (m_cur_stage >= 0)
625 		ptr = &Briefing->stages[m_cur_stage];
626 
627 	if (valid && !invalid && (m_cur_stage >= 0) && (ptr->num_icons < MAX_STAGE_ICONS))
628 		GetDlgItem(IDC_MAKE_ICON) -> EnableWindow(TRUE);
629 	else
630 		GetDlgItem(IDC_MAKE_ICON) -> EnableWindow(FALSE);
631 
632 	if (m_cur_stage >= 0)
633 		for (i=0; i<ptr->num_icons; i++)
634 			icon_marked[i] = 0;
635 
636 	valid = invalid = 0;
637 	objp = GET_FIRST(&obj_used_list);
638 	while (objp != END_OF_LIST(&obj_used_list)) {
639 		if (objp->flags[Object::Object_Flags::Marked]) {
640 			if (objp->type == OBJ_POINT) {
641 				valid++;
642 				icon_marked[objp->instance] = 1;
643 
644 			} else
645 				invalid++;
646 		}
647 
648 		objp = GET_NEXT(objp);
649 	}
650 
651 	if (valid && !invalid && (m_cur_stage >= 0))
652 		GetDlgItem(IDC_PROPAGATE_ICONS) -> EnableWindow(TRUE);
653 	else
654 		GetDlgItem(IDC_PROPAGATE_ICONS) -> EnableWindow(FALSE);
655 
656 	count = 0;
657 	lines = 1;  // default lines checkbox to checked
658 
659 	if (m_cur_stage >= 0) {
660 		for (i=0; i<ptr->num_lines; i++)
661 			line_marked[i] = 0;
662 
663 		// go through and locate all lines between marked icons
664 		for (i=0; i<ptr->num_icons - 1; i++)
665 			for (j=i+1; j<ptr->num_icons; j++) {
666 				if ( icon_marked[i] && icon_marked[j] ) {
667 					for (l=0; l<ptr->num_lines; l++)
668 						if ( ((ptr->lines[l].start_icon == i) && (ptr->lines[l].end_icon == j)) || ((ptr->lines[l].start_icon == j) && (ptr->lines[l].end_icon == i)) ) {
669 							line_marked[l] = 1;
670 							count++;  // track number of marked lines (lines between 2 icons that are both marked)
671 							break;
672 						}
673 
674 					// at least 1 line missing between 2 marked icons, so use mixed state
675 					if (l == ptr->num_lines)
676 						lines = 2;
677 				}
678 			}
679 	}
680 
681 	// not even 1 line between any 2 marked icons?  Set checkbox to unchecked.
682 	if (!count)
683 		lines = 0;
684 
685 	i = 0;
686 	if (m_cur_stage >= 0){
687 		i = calc_num_lines_for_icons(valid) + ptr->num_lines - count;
688 	}
689 
690 	if ((valid > 1) && !invalid && (m_cur_stage >= 0) && (i <= MAX_BRIEF_STAGE_LINES))
691 		GetDlgItem(IDC_LINES) -> EnableWindow(TRUE);
692 	else
693 		GetDlgItem(IDC_LINES) -> EnableWindow(FALSE);
694 
695 	m_lines.SetCheck(lines);
696 
697 	UpdateData(FALSE);
698 	if ((m_last_stage != m_cur_stage) || (Briefing != save_briefing)) {
699 		if (m_last_stage >= 0) {
700 			for (i=0; i<save_briefing->stages[m_last_stage].num_icons; i++) {
701 				// save positions of all icons, in case they have moved
702 				save_briefing->stages[m_last_stage].icons[i].pos = Objects[icon_obj[i]].pos;
703 				// release objects being used by last stage
704 				obj_delete(icon_obj[i]);
705 			}
706 		}
707 
708 		if (m_cur_stage >= 0) {
709             flagset<Object::Object_Flags> default_flags;
710 			for (i=0; i<ptr->num_icons; i++) {
711 				// create an object for each icon for display/manipulation purposes
712 				icon_obj[i] = obj_create(OBJ_POINT, -1, i, NULL, &ptr->icons[i].pos, 0.0f, default_flags + Object::Object_Flags::Renders);
713 			}
714 
715 			obj_merge_created_list();
716 		}
717 
718 		m_last_stage = m_cur_stage;
719 	}
720 
721 	m_last_icon = m_cur_icon;
722 	Update_window = 1;
723 	save_briefing = Briefing;
724 }
725 
726 // Given a number of icons, figure out how many lines would be required to connect each one
727 // with all of the others.
728 //
calc_num_lines_for_icons(int num)729 int briefing_editor_dlg::calc_num_lines_for_icons(int num)
730 {
731 	int lines = 0;
732 
733 	if (num < 2)
734 		return 0;
735 
736 	// Basically, this is # lines = (# icons - 1) factorial
737 	while (--num)
738 		lines += num;
739 
740 	return lines;
741 }
742 
OnNext()743 void briefing_editor_dlg::OnNext()
744 {
745 	m_cur_stage++;
746 	m_cur_icon = -1;
747 	audiostream_close_file(m_voice_id, 0);
748 	m_voice_id = -1;
749 	update_data();
750 	OnGotoView();
751 }
752 
OnPrev()753 void briefing_editor_dlg::OnPrev()
754 {
755 	m_cur_stage--;
756 	m_cur_icon = -1;
757 	audiostream_close_file(m_voice_id, 0);
758 	m_voice_id = -1;
759 	update_data();
760 	OnGotoView();
761 }
762 
OnBrowse()763 void briefing_editor_dlg::OnBrowse()
764 {
765 	int z;
766 	CString name;
767 
768 	UpdateData(TRUE);
769 
770 	audiostream_close_file(m_voice_id, 0);
771 	m_voice_id = -1;
772 
773 	if (The_mission.game_type & MISSION_TYPE_TRAINING)
774 		z = cfile_push_chdir(CF_TYPE_VOICE_TRAINING);
775 	else
776 		z = cfile_push_chdir(CF_TYPE_VOICE_BRIEFINGS);
777 
778 	CFileDialog dlg(TRUE, "wav", NULL, OFN_HIDEREADONLY | OFN_FILEMUSTEXIST | OFN_NOCHANGEDIR,
779 		"Voice Files (*.ogg, *.wav)|*.ogg;*.wav|Ogg Vorbis Files (*.ogg)|*.ogg|Wave Files (*.wav)|*.wav||");
780 
781 	if (dlg.DoModal() == IDOK) {
782 		m_voice = dlg.GetFileName();
783 		UpdateData(FALSE);
784 	}
785 
786 	if (!z)
787 		cfile_pop_dir();
788 }
789 
OnAddStage()790 void briefing_editor_dlg::OnAddStage()
791 {
792 	int i;
793 
794 	if (Briefing->num_stages >= MAX_BRIEF_STAGES)
795 		return;
796 
797 	m_cur_stage = i = Briefing->num_stages++;
798 	copy_stage(i - 1, i);
799 	audiostream_close_file(m_voice_id, 0);
800 	m_voice_id = -1;
801 	update_data(1);
802 }
803 
OnDeleteStage()804 void briefing_editor_dlg::OnDeleteStage()
805 {
806 	int i, z;
807 
808 	if (m_cur_stage < 0)
809 		return;
810 
811 	audiostream_close_file(m_voice_id, 0);
812 	m_voice_id = -1;
813 
814 	Assert(Briefing->num_stages);
815 	z = m_cur_stage;
816 	m_cur_stage = -1;
817 	update_data(1);
818 	for (i=z+1; i<Briefing->num_stages; i++)	{
819 		copy_stage(i, i-1);
820 	}
821 
822 	Briefing->num_stages--;
823 	m_cur_stage = z;
824 	if (m_cur_stage >= Briefing->num_stages)
825 		m_cur_stage = Briefing->num_stages - 1;
826 
827 	update_data(0);
828 }
829 
draw_icon(object * objp)830 void briefing_editor_dlg::draw_icon(object *objp)
831 {
832 	if (m_cur_stage < 0)
833 		return;
834 
835 	brief_render_icon(m_cur_stage, objp->instance, 1.0f/30.0f, objp->flags[Object::Object_Flags::Marked],
836 		(float) True_rw / BRIEF_GRID_W, (float) True_rh / BRIEF_GRID_H);
837 }
838 
batch_render()839 void briefing_editor_dlg::batch_render()
840 {
841 	int i, num_lines;
842 
843 
844 	if (m_cur_stage < 0)
845 		return;
846 
847 	num_lines = Briefing->stages[m_cur_stage].num_lines;
848 	for (i=0; i<num_lines; i++)
849 		brief_render_icon_line(m_cur_stage, i);
850 }
851 
icon_select(int num)852 void briefing_editor_dlg::icon_select(int num)
853 {
854 	m_cur_icon = num;
855 	update_data(1);
856 }
857 
OnInsertStage()858 void briefing_editor_dlg::OnInsertStage()
859 {
860 	int i, z;
861 
862 	if (Briefing->num_stages >= MAX_BRIEF_STAGES)
863 		return;
864 
865 	if (!Briefing->num_stages) {
866 		OnAddStage();
867 		return;
868 	}
869 
870 	audiostream_close_file(m_voice_id, 0);
871 	m_voice_id = -1;
872 
873 	z = m_cur_stage;
874 	m_cur_stage = -1;
875 	update_data(1);
876 	for (i=Briefing->num_stages; i>z; i--)	{
877 		copy_stage(i-1, i);
878 	}
879 
880 	Briefing->num_stages++;
881 	copy_stage(z, z + 1);
882 	m_cur_stage = z;
883 	update_data(0);
884 }
885 
copy_stage(int from,int to)886 void briefing_editor_dlg::copy_stage(int from, int to)
887 {
888 	if ((from < 0) || (from >= Briefing->num_stages)) {
889 		Briefing->stages[to].text = "<Text here>";
890 		strcpy_s(Briefing->stages[to].voice, "none.wav");
891 		Briefing->stages[to].camera_pos = view_pos;
892 		Briefing->stages[to].camera_orient = view_orient;
893 		Briefing->stages[to].camera_time = 500;
894 		Briefing->stages[to].num_icons = 0;
895 		Briefing->stages[to].formula = Locked_sexp_true;
896 		return;
897 	}
898 
899 	// Copy all the data in the stage structure.
900 	Briefing->stages[to].text = Briefing->stages[from].text;
901 	strcpy_s( Briefing->stages[to].voice, Briefing->stages[from].voice );
902 	Briefing->stages[to].camera_pos = Briefing->stages[from].camera_pos;
903 	Briefing->stages[to].camera_orient = Briefing->stages[from].camera_orient;
904 	Briefing->stages[to].camera_time = Briefing->stages[from].camera_time;
905 	Briefing->stages[to].flags = Briefing->stages[from].flags;
906 	Briefing->stages[to].num_icons = Briefing->stages[from].num_icons;
907 	Briefing->stages[to].num_lines = Briefing->stages[from].num_lines;
908 	Briefing->stages[to].formula = Briefing->stages[from].formula;
909 
910 	memmove( Briefing->stages[to].icons, Briefing->stages[from].icons, sizeof(brief_icon)*MAX_STAGE_ICONS );
911 	memmove( Briefing->stages[to].lines, Briefing->stages[from].lines, sizeof(brief_line)*MAX_BRIEF_STAGE_LINES );
912 }
913 
update_positions()914 void briefing_editor_dlg::update_positions()
915 {
916 	int i, s, z;
917 	vec3d v1, v2;
918 
919 	if (m_cur_stage < 0)
920 		return;
921 
922 	for (i=0; i<Briefing->stages[m_cur_stage].num_icons; i++) {
923 		v1 = Briefing->stages[m_cur_stage].icons[i].pos;
924 		v2 = Objects[icon_obj[i]].pos;
925 		if ((v1.xyz.x != v2.xyz.x) || (v1.xyz.y != v2.xyz.y) || (v1.xyz.z != v2.xyz.z)) {
926 			Briefing->stages[m_cur_stage].icons[i].pos = Objects[icon_obj[i]].pos;
927 			if (!m_change_local)  // propagate changes through rest of stages..
928 				for (s=m_cur_stage+1; s<Briefing->num_stages; s++) {
929 					z = find_icon(Briefing->stages[m_cur_stage].icons[i].id, s);
930 					if (z >= 0)
931 						Briefing->stages[s].icons[z].pos = Objects[icon_obj[i]].pos;
932 				}
933 		}
934 	}
935 }
936 
OnMakeIcon()937 void briefing_editor_dlg::OnMakeIcon()
938 {
939 	const char *name;
940 	int z, team, ship, waypoint, count = -1;
941 	int cargo = 0, cargo_count = 0, freighter_count = 0;
942 	object *ptr;
943 	vec3d min, max, pos;
944 	brief_icon *biconp;
945 	CJumpNode *jnp = nullptr;
946 
947 	if (Briefing->stages[m_cur_stage].num_icons >= MAX_STAGE_ICONS)
948 		return;
949 
950 	m_cur_icon = Briefing->stages[m_cur_stage].num_icons++;
951 	biconp = &Briefing->stages[m_cur_stage].icons[m_cur_icon];
952 	ship = waypoint = -1;
953 	team = 0;
954 
955 	vm_vec_make(&min, 9e19f, 9e19f, 9e19f);
956 	vm_vec_make(&max, -9e19f, -9e19f, -9e19f);
957 	ptr = GET_FIRST(&obj_used_list);
958 	while (ptr != END_OF_LIST(&obj_used_list)) {
959 		if (ptr->flags[Object::Object_Flags::Marked]) {
960 			if (ptr->pos.xyz.x < min.xyz.x)
961 				min.xyz.x = ptr->pos.xyz.x;
962 			if (ptr->pos.xyz.x > max.xyz.x)
963 				max.xyz.x = ptr->pos.xyz.x;
964 			if (ptr->pos.xyz.y < min.xyz.y)
965 				min.xyz.y = ptr->pos.xyz.y;
966 			if (ptr->pos.xyz.y > max.xyz.y)
967 				max.xyz.y = ptr->pos.xyz.y;
968 			if (ptr->pos.xyz.z < min.xyz.z)
969 				min.xyz.z = ptr->pos.xyz.z;
970 			if (ptr->pos.xyz.z > max.xyz.z)
971 				max.xyz.z = ptr->pos.xyz.z;
972 
973 			switch (ptr->type) {
974 				case OBJ_SHIP:
975 				case OBJ_START:
976 					ship = ptr->instance;
977 					break;
978 
979 				case OBJ_WAYPOINT:
980 					waypoint = ptr->instance;
981 					break;
982 				case OBJ_JUMP_NODE:
983 				    jnp = jumpnode_get_by_objnum(OBJ_INDEX(ptr));
984 					break;
985 
986 				default:
987 					Int3();
988 			}
989 
990 			if (ship >= 0) {
991 				team = Ships[ship].team;
992 
993 				z = ship_query_general_type(ship);
994 				if (Ship_info[Ships[ship].ship_info_index].flags[Ship::Info_Flags::Cargo])
995 					cargo_count++;
996 
997 				if (Ship_info[Ships[ship].ship_info_index].flags[Ship::Info_Flags::Freighter])
998 				{
999 					// direct docked with any marked cargo?
1000 					for (dock_instance *dock_ptr = ptr->dock_list; dock_ptr != NULL; dock_ptr = dock_ptr->next)
1001 					{
1002 						if (dock_ptr->docked_objp->flags[Object::Object_Flags::Marked])
1003 						{
1004 							if (Ship_info[Ships[dock_ptr->docked_objp->instance].ship_info_index].flags[Ship::Info_Flags::Cargo])
1005 								freighter_count++;
1006 						}
1007 					}
1008 				}
1009 			}
1010 
1011 			count++;
1012 		}
1013 
1014 		ptr = GET_NEXT(ptr);
1015 	}
1016 
1017 	if (cargo_count && cargo_count == freighter_count)
1018 		cargo = 1;
1019 
1020 	vm_vec_avg(&pos, &min, &max);
1021 	if (ship >= 0)
1022 		name = Ships[ship].ship_name;
1023 	else if (waypoint >= 0)
1024 	{
1025 		waypoint_list *wp_list = find_waypoint_list_with_instance(waypoint);
1026 		Assert(wp_list != NULL);
1027 		name = wp_list->get_name();
1028 	}
1029 	else if (jnp != nullptr)
1030 		name = jnp->GetName();
1031 	// Return if we didn't find anything somehow
1032 	else
1033 		return;
1034 
1035 	auto len = strlen(name);
1036 	if (len >= MAX_LABEL_LEN - 1)
1037 		len = MAX_LABEL_LEN - 1;
1038 
1039 	strncpy(biconp->label, name, len);
1040 	biconp->label[len] = 0;
1041 //	iconp->text[0] = 0;
1042 	biconp->type = 0;
1043 	biconp->team = team;
1044 	biconp->pos = pos;
1045 	biconp->flags = 0;
1046 	biconp->id = Cur_brief_id++;
1047 
1048 	biconp->modelnum = -1;
1049 	biconp->model_instance_num = -1;
1050 
1051 	if (ship >= 0) {
1052 		biconp->ship_class = Ships[ship].ship_info_index;
1053         ship_info* sip = &Ship_info[Ships[ship].ship_info_index];
1054 
1055         if (sip->flags[Ship::Info_Flags::Knossos_device])
1056             biconp->type = ICON_KNOSSOS_DEVICE;
1057         else if (sip->flags[Ship::Info_Flags::Corvette])
1058             biconp->type = ICON_CORVETTE;
1059         else if (sip->flags[Ship::Info_Flags::Gas_miner])
1060             biconp->type = ICON_GAS_MINER;
1061         else if (sip->flags[Ship::Info_Flags::Supercap])
1062             biconp->type = ICON_SUPERCAP;
1063         else if (sip->flags[Ship::Info_Flags::Sentrygun])
1064             biconp->type = ICON_SENTRYGUN;
1065         else if (sip->flags[Ship::Info_Flags::Awacs])
1066             biconp->type = ICON_AWACS;
1067         else if (sip->flags[Ship::Info_Flags::Cargo])
1068             if (cargo)
1069                 biconp->type = (count == 1) ? ICON_FREIGHTER_WITH_CARGO : ICON_FREIGHTER_WING_WITH_CARGO;
1070             else
1071                 biconp->type = count ? ICON_CARGO_WING : ICON_CARGO;
1072         else if (sip->flags[Ship::Info_Flags::Support])
1073             biconp->type = ICON_SUPPORT_SHIP;
1074         else if (sip->flags[Ship::Info_Flags::Fighter])
1075             biconp->type = count ? ICON_FIGHTER_WING : ICON_FIGHTER;
1076         else if (sip->flags[Ship::Info_Flags::Bomber])
1077             biconp->type = count ? ICON_BOMBER_WING : ICON_BOMBER;
1078         else if (sip->flags[Ship::Info_Flags::Freighter])
1079             if (cargo)
1080                 biconp->type = (count == 1) ? ICON_FREIGHTER_WITH_CARGO : ICON_FREIGHTER_WING_WITH_CARGO;
1081             else
1082                 biconp->type = count ? ICON_FREIGHTER_WING_NO_CARGO : ICON_FREIGHTER_NO_CARGO;
1083         else if (sip->flags[Ship::Info_Flags::Cruiser])
1084             biconp->type = count ? ICON_CRUISER_WING : ICON_CRUISER;
1085         else if (sip->flags[Ship::Info_Flags::Transport])
1086             biconp->type = count ? ICON_TRANSPORT_WING : ICON_TRANSPORT;
1087         else if (sip->flags[Ship::Info_Flags::Capital] || sip->flags[Ship::Info_Flags::Drydock])
1088             biconp->type = ICON_CAPITAL;
1089         else if (sip->flags[Ship::Info_Flags::Navbuoy])
1090             biconp->type = ICON_WAYPOINT;
1091         else
1092             biconp->type = ICON_ASTEROID_FIELD;
1093 	}
1094 	// jumpnodes -- have to use Navbuoy if clicked on during briefing because jump nodes are not in ship_info
1095 	else if (jnp != nullptr) {
1096 		// find the first navbuoy, by iterating through the ship classes
1097 		biconp->ship_class = -1;
1098 
1099 		for (auto it = Ship_info.cbegin(); it != Ship_info.cend(); ++it)
1100 		{
1101 			if (it->flags[Ship::Info_Flags::Navbuoy])
1102 			{
1103                 biconp->ship_class = (int)std::distance(Ship_info.cbegin(), it);
1104                 break;
1105             }
1106         }
1107 
1108 		biconp->type = ICON_JUMP_NODE;
1109 	}
1110 	// everything else -- have to use Navbuoy if clicked on during briefing because waypoints etc. are not in ship_info
1111 	else {
1112 		// find the first navbuoy
1113 		biconp->ship_class = -1;
1114 
1115 		for (auto it = Ship_info.cbegin(); it != Ship_info.cend(); ++it)
1116 		{
1117             if (it->flags[Ship::Info_Flags::Navbuoy])
1118             {
1119 				biconp->ship_class = (int)std::distance(Ship_info.cbegin(), it);
1120                 break;
1121             }
1122         }
1123 		biconp->type = ICON_WAYPOINT;
1124 	}
1125 
1126 	if (!m_change_local){
1127 		propagate_icon(m_cur_icon);
1128 	}
1129 
1130     flagset<Object::Object_Flags> default_flags;
1131     default_flags.set(Object::Object_Flags::Renders);
1132 	icon_obj[m_cur_icon] = obj_create(OBJ_POINT, -1, m_cur_icon, NULL, &pos, 0.0f, default_flags);
1133 	Assert(icon_obj[m_cur_icon] >= 0);
1134 	obj_merge_created_list();
1135 	unmark_all();
1136 	set_cur_object_index(icon_obj[m_cur_icon]);
1137 	GetDlgItem(IDC_MAKE_ICON) -> EnableWindow(FALSE);
1138 	GetDlgItem(IDC_PROPAGATE_ICONS) -> EnableWindow(TRUE);
1139 	update_data(1);
1140 }
1141 
OnDeleteIcon()1142 void briefing_editor_dlg::OnDeleteIcon()
1143 {
1144 	delete_icon(m_cur_icon);
1145 }
1146 
delete_icon(int num)1147 void briefing_editor_dlg::delete_icon(int num)
1148 {
1149 	int i, z;
1150 
1151 	if (num < 0)
1152 		num = m_cur_icon;
1153 
1154 	if (num < 0)
1155 		return;
1156 
1157 	Assert(m_cur_stage >= 0);
1158 	Assert(Briefing->stages[m_cur_stage].num_icons);
1159 	z = m_cur_icon;
1160 	if (z == num)
1161 		z = -1;
1162 	if (z > num)
1163 		z--;
1164 
1165 	m_cur_icon = -1;
1166 	update_data(1);
1167 	obj_delete(icon_obj[num]);
1168 	for (i=num+1; i<Briefing->stages[m_cur_stage].num_icons; i++) {
1169 		Briefing->stages[m_cur_stage].icons[i-1] = Briefing->stages[m_cur_stage].icons[i];
1170 		icon_obj[i-1] = icon_obj[i];
1171 		Objects[icon_obj[i-1]].instance = i - 1;
1172 	}
1173 
1174 	Briefing->stages[m_cur_stage].num_icons--;
1175 	if (z >= 0) {
1176 		m_cur_icon = z;
1177 		update_data(0);
1178 	}
1179 }
1180 
OnGotoView()1181 void briefing_editor_dlg::OnGotoView()
1182 {
1183 	if (m_cur_stage < 0)
1184 		return;
1185 
1186 	view_pos = Briefing->stages[m_cur_stage].camera_pos;
1187 	view_orient = Briefing->stages[m_cur_stage].camera_orient;
1188 	Update_window = 1;
1189 }
1190 
OnSaveView()1191 void briefing_editor_dlg::OnSaveView()
1192 {
1193 	if (m_cur_stage < 0)
1194 		return;
1195 
1196 	Briefing->stages[m_cur_stage].camera_pos = view_pos;
1197 	Briefing->stages[m_cur_stage].camera_orient = view_orient;
1198 }
1199 
OnSelchangeIconImage()1200 void briefing_editor_dlg::OnSelchangeIconImage()
1201 {
1202 	update_data(1);
1203 }
1204 
OnSelchangeTeam()1205 void briefing_editor_dlg::OnSelchangeTeam()
1206 {
1207 	update_data(1);
1208 }
1209 
OnSelchangeShipType()1210 void briefing_editor_dlg::OnSelchangeShipType()
1211 {
1212 	update_data(1);
1213 }
1214 
check_mouse_hit(int x,int y)1215 int briefing_editor_dlg::check_mouse_hit(int x, int y)
1216 {
1217 	int i;
1218 	brief_icon *ptr;
1219 
1220 	if (m_cur_stage < 0)
1221 		return -1;
1222 
1223 	for (i=0; i<Briefing->stages[m_cur_stage].num_icons; i++) {
1224 		ptr = &Briefing->stages[m_cur_stage].icons[i];
1225 		if ((x > ptr->x) && (x < ptr->x + ptr->w) && (y > ptr->y) && (y < ptr->y + ptr->h))	{
1226 			return icon_obj[i];
1227 		}
1228 	}
1229 
1230 	return -1;
1231 }
1232 
OnPropagateIcons()1233 void briefing_editor_dlg::OnPropagateIcons()
1234 {
1235 	object *ptr;
1236 
1237 	ptr = GET_FIRST(&obj_used_list);
1238 	while (ptr != END_OF_LIST(&obj_used_list)) {
1239 		if ((ptr->type == OBJ_POINT) && (ptr->flags[Object::Object_Flags::Marked])) {
1240 			propagate_icon(ptr->instance);
1241 		}
1242 
1243 		ptr = GET_NEXT(ptr);
1244 	}
1245 }
1246 
propagate_icon(int num)1247 void briefing_editor_dlg::propagate_icon(int num)
1248 {
1249 	int i, s;
1250 
1251 	for (s=m_cur_stage+1; s<Briefing->num_stages; s++) {
1252 		i = Briefing->stages[s].num_icons;
1253 		if (i >= MAX_STAGE_ICONS)
1254 			continue;
1255 
1256 		if (find_icon(Briefing->stages[m_cur_stage].icons[num].id, s) >= 0)
1257 			continue;  // don't change if icon exists here already.
1258 
1259 		Briefing->stages[s].icons[i] = Briefing->stages[m_cur_stage].icons[num];
1260 		Briefing->stages[s].num_icons++;
1261 	}
1262 }
1263 
find_icon(int id,int stage)1264 int briefing_editor_dlg::find_icon(int id, int stage)
1265 {
1266 	int i;
1267 
1268 	if (id >= 0)
1269 		for (i=0; i<Briefing->stages[stage].num_icons; i++)
1270 			if (Briefing->stages[stage].icons[i].id == id)
1271 				return i;
1272 
1273 	return -1;
1274 }
1275 
reset_icon_loop(int stage)1276 void briefing_editor_dlg::reset_icon_loop(int stage)
1277 {
1278 	stage_loop = stage + 1;
1279 	icon_loop = -1;
1280 }
1281 
get_next_icon(int id)1282 int briefing_editor_dlg::get_next_icon(int id)
1283 {
1284 	while (1) {
1285 		icon_loop++;
1286 		if (icon_loop >= Briefing->stages[stage_loop].num_icons) {
1287 			stage_loop++;
1288 			if (stage_loop > Briefing->num_stages)
1289 				return 0;
1290 
1291 			icon_loop = -1;
1292 			continue;
1293 		}
1294 
1295 		iconp = &Briefing->stages[stage_loop].icons[icon_loop];
1296 		if ((id >= 0) && (iconp->id == id))
1297 			return 1;
1298 	}
1299 }
1300 
OnCommand(WPARAM wParam,LPARAM lParam)1301 BOOL briefing_editor_dlg::OnCommand(WPARAM wParam, LPARAM lParam)
1302 {
1303 	int id;
1304 
1305 	// deal with figuring out menu stuff
1306 	id = LOWORD(wParam);
1307 	if ( (id >= ID_TEAM_1) && (id < ID_TEAM_3) ) {
1308 		m_current_briefing = id - ID_TEAM_1;
1309 
1310 		// put user back at first stage for this team (or no current stage is there are none).
1311 		Briefing = &Briefings[m_current_briefing];
1312 		if ( Briefing->num_stages > 0 )
1313 			m_cur_stage = 0;
1314 		else
1315 			m_cur_stage = -1;
1316 
1317 		update_data(1);
1318 		OnGotoView();
1319 		return 1;
1320 	}
1321 
1322 	return CDialog::OnCommand(wParam, lParam);
1323 }
1324 
OnLines()1325 void briefing_editor_dlg::OnLines()
1326 {
1327 	if (m_lines.GetCheck() == 1)
1328 		m_lines.SetCheck(0);
1329 	else
1330 		m_lines.SetCheck(1);
1331 
1332 	update_data(1);
1333 }
1334 
OnRclickTree(NMHDR * pNMHDR,LRESULT * pResult)1335 void briefing_editor_dlg::OnRclickTree(NMHDR* pNMHDR, LRESULT* pResult)
1336 {
1337 	m_tree.right_clicked();
1338 	*pResult = 0;
1339 }
1340 
OnBeginlabeleditTree(NMHDR * pNMHDR,LRESULT * pResult)1341 void briefing_editor_dlg::OnBeginlabeleditTree(NMHDR* pNMHDR, LRESULT* pResult)
1342 {
1343 	TV_DISPINFO* pTVDispInfo = (TV_DISPINFO*)pNMHDR;
1344 
1345 	if (m_tree.edit_label(pTVDispInfo->item.hItem) == 1)	{
1346 		*pResult = 0;
1347 		modified = 1;
1348 
1349 	} else
1350 		*pResult = 1;
1351 }
1352 
OnEndlabeleditTree(NMHDR * pNMHDR,LRESULT * pResult)1353 void briefing_editor_dlg::OnEndlabeleditTree(NMHDR* pNMHDR, LRESULT* pResult)
1354 {
1355 	TV_DISPINFO* pTVDispInfo = (TV_DISPINFO*)pNMHDR;
1356 
1357 	*pResult = m_tree.end_label_edit(pTVDispInfo->item);
1358 }
1359 
DestroyWindow()1360 BOOL briefing_editor_dlg::DestroyWindow()
1361 {
1362 	m_play_bm.DeleteObject();
1363 	audiostream_close_file(m_voice_id, 0);
1364 	return CDialog::DestroyWindow();
1365 }
1366 
OnPlay()1367 void briefing_editor_dlg::OnPlay()
1368 {
1369 	GetDlgItem(IDC_VOICE)->GetWindowText(m_voice);
1370 
1371 	if (m_voice_id >= 0) {
1372 		audiostream_close_file(m_voice_id, 0);
1373 		m_voice_id = -1;
1374 		return;
1375 	}
1376 
1377 	// we use ASF_EVENTMUSIC here so that it will keep the extension in place
1378 	m_voice_id = audiostream_open((char *)(LPCSTR) m_voice, ASF_EVENTMUSIC);
1379 
1380 	if (m_voice_id >= 0) {
1381 		audiostream_play(m_voice_id, 1.0f, 0);
1382 	}
1383 }
1384 
OnCopyView()1385 void briefing_editor_dlg::OnCopyView()
1386 {
1387 	// TODO: Add your control notification handler code here
1388 
1389 	m_copy_view_set = 1;
1390 	m_copy_view_pos = view_pos;
1391 	m_copy_view_orient = view_orient;
1392 }
1393 
OnPasteView()1394 void briefing_editor_dlg::OnPasteView()
1395 {
1396 	// TODO: Add your control notification handler code here
1397 	if (m_cur_stage < 0)
1398 		return;
1399 
1400 	if (m_copy_view_set == 0) {
1401 		MessageBox("No view set", "Unable to copy view");
1402 	} else {
1403 		Briefing->stages[m_cur_stage].camera_pos = m_copy_view_pos;
1404 		Briefing->stages[m_cur_stage].camera_orient = m_copy_view_orient;
1405 
1406 		update_data(1);
1407 		OnGotoView();
1408 	}
1409 }
1410 
OnFlipIcon()1411 void briefing_editor_dlg::OnFlipIcon()
1412 {
1413 	update_data(1);
1414 }
1415 
OnWingIcon()1416 void briefing_editor_dlg::OnWingIcon()
1417 {
1418 	update_data(1);
1419 }
1420 
OnCargoIcon()1421 void briefing_editor_dlg::OnCargoIcon()
1422 {
1423 	update_data(1);
1424 }