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 "DebriefingEditorDlg.h"
15 #include "FREDDoc.h"
16 #include "mission/missionbriefcommon.h"
17 #include "mission/missionparse.h"
18 #include "globalincs/linklist.h"
19 #include "parse/sexp.h"
20 #include "gamesnd/eventmusic.h"
21 #include "cfile/cfile.h"
22 #include "sound/audiostr.h"
23 #include "localization/localize.h"
24 #include "jumpnode/jumpnode.h"
25 
26 
27 #ifdef _DEBUG
28 #undef THIS_FILE
29 static char THIS_FILE[] = __FILE__;
30 #endif
31 
32 /////////////////////////////////////////////////////////////////////////////
33 // debriefing_editor_dlg dialog
34 
debriefing_editor_dlg(CWnd * pParent)35 debriefing_editor_dlg::debriefing_editor_dlg(CWnd* pParent /*=NULL*/)
36 	: CDialog(debriefing_editor_dlg::IDD, pParent)
37 {
38 	//{{AFX_DATA_INIT(debriefing_editor_dlg)
39 	m_text = _T("");
40 	m_voice = _T("");
41 	m_stage_title = _T("");
42 	m_rec_text = _T("");
43 	m_debriefPass_music = 0;
44 	m_debriefAvg_music = 0;
45 	m_debriefFail_music = 0;
46 	m_current_debriefing = -1;
47 	//}}AFX_DATA_INIT
48 
49 	modified = 0;
50 	m_cur_stage = 0;
51 	m_last_stage = -1;
52 	m_voice_id = -1;
53 	select_sexp_node = -1;
54 }
55 
DoDataExchange(CDataExchange * pDX)56 void debriefing_editor_dlg::DoDataExchange(CDataExchange* pDX)
57 {
58 	CDialog::DoDataExchange(pDX);
59 	//{{AFX_DATA_MAP(debriefing_editor_dlg)
60 	DDX_Control(pDX, IDC_TREE, m_tree);
61 	DDX_Text(pDX, IDC_TEXT, m_text);
62 	DDX_Text(pDX, IDC_VOICE, m_voice);
63 	DDX_Text(pDX, IDC_STAGE_TITLE, m_stage_title);
64 	DDX_Text(pDX, IDC_REC_TEXT, m_rec_text);
65 	DDX_CBIndex(pDX, IDC_SUCCESSFUL_MISSION_TRACK, m_debriefPass_music);
66 	DDX_CBIndex(pDX, IDC_DEBRIEFING_TRACK, m_debriefAvg_music);
67 	DDX_CBIndex(pDX, IDC_FAILED_MISSION_TRACK, m_debriefFail_music);
68 	//}}AFX_DATA_MAP
69 
70 	DDV_MaxChars(pDX, m_voice, MAX_FILENAME_LEN - 1);
71 }
72 
BEGIN_MESSAGE_MAP(debriefing_editor_dlg,CDialog)73 BEGIN_MESSAGE_MAP(debriefing_editor_dlg, CDialog)
74 	//{{AFX_MSG_MAP(debriefing_editor_dlg)
75 	ON_BN_CLICKED(IDC_NEXT, OnNext)
76 	ON_BN_CLICKED(IDC_PREV, OnPrev)
77 	ON_BN_CLICKED(IDC_BROWSE, OnBrowse)
78 	ON_BN_CLICKED(IDC_ADD_STAGE, OnAddStage)
79 	ON_BN_CLICKED(IDC_DELETE_STAGE, OnDeleteStage)
80 	ON_BN_CLICKED(IDC_INSERT_STAGE, OnInsertStage)
81 	ON_NOTIFY(NM_RCLICK, IDC_TREE, OnRclickTree)
82 	ON_NOTIFY(TVN_BEGINLABELEDIT, IDC_TREE, OnBeginlabeleditTree)
83 	ON_NOTIFY(TVN_ENDLABELEDIT, IDC_TREE, OnEndlabeleditTree)
84 	ON_WM_CLOSE()
85 	ON_WM_INITMENU()
86 	ON_BN_CLICKED(IDC_PLAY, OnPlay)
87 	//}}AFX_MSG_MAP
88 END_MESSAGE_MAP()
89 
90 /////////////////////////////////////////////////////////////////////////////
91 // debriefing_editor_dlg message handlers
92 
93 void debriefing_editor_dlg::OnInitMenu(CMenu* pMenu)
94 {
95 	int i;
96 	CMenu *m;
97 
98 	// disable any items we should disable
99 	m = pMenu->GetSubMenu(0);
100 
101 	// uncheck all menu items
102 	for (i = 0; i < Num_teams; i++ )
103 		m->CheckMenuItem( i, MF_BYPOSITION | MF_UNCHECKED );
104 
105 	for ( i = Num_teams; i < MAX_TVT_TEAMS; i++ )
106 		m->EnableMenuItem(i, MF_BYPOSITION | MF_GRAYED);
107 
108 
109 	// put a check next to the currently selected item
110 	m->CheckMenuItem(m_current_debriefing, MF_BYPOSITION | MF_CHECKED );
111 
112 	// Karajorma - it might be nice to autobalance the briefings and debriefings but I'm not doing anything till I
113 	// understand how the how system works better. disabling the option for now.
114 	m = pMenu->GetSubMenu(1);
115 	m->EnableMenuItem(ID_AUTOBALANCE, MF_GRAYED);
116 
117 	CDialog::OnInitMenu(pMenu);
118 }
119 
OnInitDialog()120 BOOL debriefing_editor_dlg::OnInitDialog()
121 {
122 	int i, n;
123 
124 	CDialog::OnInitDialog();
125 	m_play_bm.LoadBitmap(IDB_PLAY);
126 	((CButton *) GetDlgItem(IDC_PLAY)) -> SetBitmap(m_play_bm);
127 	CComboBox *box;
128 	box = (CComboBox *) GetDlgItem(IDC_ICON_IMAGE);
129 
130 	m_current_debriefing = 0;
131 	UpdateData(FALSE);
132 
133 	Debriefing = &Debriefings[m_current_debriefing];
134 
135 	box = (CComboBox *) GetDlgItem(IDC_SUCCESSFUL_MISSION_TRACK);
136 	box->AddString("None");
137 	for (i=0; i<Num_music_files; i++)
138 		box->AddString(Spooled_music[i].name);
139 
140 	box = (CComboBox *) GetDlgItem(IDC_DEBRIEFING_TRACK);
141 	box->AddString("None");
142 	for (i=0; i<Num_music_files; i++)
143 		box->AddString(Spooled_music[i].name);
144 
145 	box = (CComboBox *) GetDlgItem(IDC_FAILED_MISSION_TRACK);
146 	box->AddString("None");
147 	for (i=0; i<Num_music_files; i++)
148 		box->AddString(Spooled_music[i].name);
149 
150 	m_debriefPass_music = Mission_music[SCORE_DEBRIEF_SUCCESS] + 1;
151 	m_debriefAvg_music = Mission_music[SCORE_DEBRIEF_AVERAGE] + 1;
152 	m_debriefFail_music = Mission_music[SCORE_DEBRIEF_FAIL] + 1;
153 
154 	m_tree.link_modified(&modified);  // provide way to indicate trees are modified in dialog
155 	n = m_tree.select_sexp_node = select_sexp_node;
156 	select_sexp_node = -1;
157 	if (n != -1) {
158 		for (i=0; i<Debriefing->num_stages; i++)
159 			if (query_node_in_sexp(n, Debriefing->stages[i].formula))
160 				break;
161 
162 		if (i < Debriefing->num_stages) {
163 			m_cur_stage = i;
164 			update_data();
165 			GetDlgItem(IDC_TREE) -> SetFocus();
166 			m_tree.hilite_item(m_tree.select_sexp_node);
167 			set_modified();
168 			return FALSE;
169 		}
170 	}
171 
172 	CDialog::OnInitDialog();
173 	update_data();
174 	set_modified();
175 
176 	// hard coded stuff to deal with the multiple briefings per mission.
177 
178 	return TRUE;
179 }
180 
update_data(int update)181 void debriefing_editor_dlg::update_data(int update)
182 {
183 	int enable, save_debriefing;
184 	debrief_stage *ptr;
185 
186 	save_debriefing = m_current_debriefing;
187 
188 	if (update)
189 		UpdateData(TRUE);
190 
191 	// based on the game type, enable the multiple briefings combo box (or disable it)
192 
193 	// set up the pointer to the briefing that we are editing
194 	if ( save_debriefing != m_current_debriefing )
195 		Debriefing = &Debriefings[save_debriefing];
196 	else
197 		Debriefing = &Debriefings[m_current_debriefing];
198 
199 	if (m_last_stage >= 0) {
200 		ptr = &Debriefing->stages[m_last_stage];
201 		if (ptr->formula >= 0)
202 			free_sexp2(ptr->formula);
203 
204 		ptr->formula = m_tree.save_tree();
205 		deconvert_multiline_string(ptr->text, m_text);
206 		lcl_fred_replace_stuff(ptr->text);
207 		deconvert_multiline_string(ptr->recommendation_text, m_rec_text);
208 		lcl_fred_replace_stuff(ptr->recommendation_text);
209 		string_copy(ptr->voice, m_voice, MAX_FILENAME_LEN);
210 	}
211 
212 	// now get new stage data
213 	if ((m_cur_stage >= 0) && (m_cur_stage < Debriefing->num_stages)) {
214 		ptr = &Debriefing->stages[m_cur_stage];
215 		m_stage_title.Format("Stage %d of %d", m_cur_stage + 1, Debriefing->num_stages);
216 		m_tree.load_tree(ptr->formula);
217 		convert_multiline_string(m_text, ptr->text);
218 		convert_multiline_string(m_rec_text, ptr->recommendation_text);
219 		m_voice = ptr->voice;
220 		enable = TRUE;
221 
222 	} else {
223 		m_stage_title = _T("No stages");
224 		m_tree.clear_tree();
225 		m_text = _T("");
226 		m_rec_text = _T("");
227 		m_voice = _T("");
228 		enable = FALSE;
229 		m_cur_stage = -1;
230 	}
231 
232 	if (m_cur_stage == Debriefing->num_stages - 1)
233 		GetDlgItem(IDC_NEXT) -> EnableWindow(FALSE);
234 	else
235 		GetDlgItem(IDC_NEXT) -> EnableWindow(enable);
236 
237 	if (m_cur_stage)
238 		GetDlgItem(IDC_PREV) -> EnableWindow(enable);
239 	else
240 		GetDlgItem(IDC_PREV) -> EnableWindow(FALSE);
241 
242 	if (Debriefing->num_stages >= MAX_DEBRIEF_STAGES)
243 		GetDlgItem(IDC_ADD_STAGE) -> EnableWindow(FALSE);
244 	else
245 		GetDlgItem(IDC_ADD_STAGE) -> EnableWindow(TRUE);
246 
247 	if (Debriefing->num_stages) {
248 		GetDlgItem(IDC_DELETE_STAGE) -> EnableWindow(enable);
249 		GetDlgItem(IDC_INSERT_STAGE) -> EnableWindow(enable);
250 
251 	} else {
252 		GetDlgItem(IDC_DELETE_STAGE) -> EnableWindow(FALSE);
253 		GetDlgItem(IDC_INSERT_STAGE) -> EnableWindow(FALSE);
254 	}
255 
256 	GetDlgItem(IDC_VOICE) -> EnableWindow(enable);
257 	GetDlgItem(IDC_BROWSE) -> EnableWindow(enable);
258 	GetDlgItem(IDC_TEXT) -> EnableWindow(enable);
259 	GetDlgItem(IDC_REC_TEXT) -> EnableWindow(enable);
260 	GetDlgItem(IDC_TREE) -> EnableWindow(enable);
261 
262 	m_last_stage = m_cur_stage;
263 	UpdateData(FALSE);
264 
265 	#ifndef NDEBUG
266 	count_free_sexp_nodes();
267 	#endif
268 }
269 
OnNext()270 void debriefing_editor_dlg::OnNext()
271 {
272 	audiostream_close_file(m_voice_id, 0);
273 	m_voice_id = -1;
274 
275 	m_cur_stage++;
276 	update_data();
277 }
278 
OnPrev()279 void debriefing_editor_dlg::OnPrev()
280 {
281 	audiostream_close_file(m_voice_id, 0);
282 	m_voice_id = -1;
283 
284 	m_cur_stage--;
285 	update_data();
286 }
287 
OnBrowse()288 void debriefing_editor_dlg::OnBrowse()
289 {
290 	int z;
291 	CString name;
292 
293 	audiostream_close_file(m_voice_id, 0);
294 	m_voice_id = -1;
295 
296 	UpdateData(TRUE);
297 
298 	if (The_mission.game_type & MISSION_TYPE_TRAINING)
299 		z = cfile_push_chdir(CF_TYPE_VOICE_TRAINING);
300 	else
301 		z = cfile_push_chdir(CF_TYPE_VOICE_DEBRIEFINGS);
302 
303 	CFileDialog dlg(TRUE, "wav", NULL, OFN_HIDEREADONLY | OFN_FILEMUSTEXIST | OFN_NOCHANGEDIR,
304 		"Voice Files (*.ogg, *.wav)|*.ogg;*.wav|Ogg Vorbis Files (*.ogg)|*.ogg|Wave Files (*.wav)|*.wav||");
305 
306 	if (dlg.DoModal() == IDOK) {
307 		m_voice = dlg.GetFileName();
308 		UpdateData(FALSE);
309 	}
310 
311 	if (!z)
312 		cfile_pop_dir();
313 }
314 
OnAddStage()315 void debriefing_editor_dlg::OnAddStage()
316 {
317 	int i;
318 
319 	if (Debriefing->num_stages >= MAX_DEBRIEF_STAGES)
320 		return;
321 
322 	audiostream_close_file(m_voice_id, 0);
323 	m_voice_id = -1;
324 
325 	m_cur_stage = i = Debriefing->num_stages++;
326 	copy_stage(i - 1, i, 1);
327 	update_data(1);
328 }
329 
OnDeleteStage()330 void debriefing_editor_dlg::OnDeleteStage()
331 {
332 	int i, z;
333 
334 	if (m_cur_stage < 0)
335 		return;
336 
337 	audiostream_close_file(m_voice_id, 0);
338 	m_voice_id = -1;
339 
340 	Assert(Debriefing->num_stages);
341 	z = m_cur_stage;
342 	m_cur_stage = -1;
343 	update_data(1);
344 	for (i=z+1; i<Debriefing->num_stages; i++) {
345 		copy_stage(i, i - 1);
346 	}
347 
348 	Debriefing->num_stages--;
349 	m_cur_stage = z;
350 	if (m_cur_stage >= Debriefing->num_stages)
351 		m_cur_stage = Debriefing->num_stages - 1;
352 
353 	update_data(0);
354 }
355 
OnInsertStage()356 void debriefing_editor_dlg::OnInsertStage()
357 {
358 	int i, z;
359 
360 	if (Debriefing->num_stages >= MAX_DEBRIEF_STAGES)
361 		return;
362 
363 	if (!Debriefing->num_stages) {
364 		OnAddStage();
365 		return;
366 	}
367 
368 	audiostream_close_file(m_voice_id, 0);
369 	m_voice_id = -1;
370 
371 	z = m_cur_stage;
372 	m_cur_stage = -1;
373 	update_data(1);
374 	for (i=Debriefing->num_stages; i>z; i--) {
375 		copy_stage(i - 1, i);
376 	}
377 
378 	Debriefing->num_stages++;
379 	copy_stage(z, z + 1);
380 	Debriefing->stages[z].formula = -1;
381 	m_cur_stage = z;
382 	update_data(0);
383 }
384 
copy_stage(int from,int to,int clear_formula)385 void debriefing_editor_dlg::copy_stage(int from, int to, int clear_formula)
386 {
387 	if ((from < 0) || (from >= Debriefing->num_stages)) {
388 		Debriefing->stages[to].text = "<Text here>";
389 		strcpy_s(Debriefing->stages[to].voice, "none.wav");
390 		Debriefing->stages[to].formula = -1;
391 		return;
392 	}
393 
394 	if (clear_formula)
395 		Debriefing->stages[to].formula = -1;
396 	else
397 		Debriefing->stages[to].formula = Debriefing->stages[from].formula;
398 
399 	Debriefing->stages[to].text = Debriefing->stages[from].text;
400 	strcpy_s( Debriefing->stages[to].voice, Debriefing->stages[from].voice );
401 	Debriefing->stages[to].recommendation_text = Debriefing->stages[from].recommendation_text;
402 }
403 
OnRclickTree(NMHDR * pNMHDR,LRESULT * pResult)404 void debriefing_editor_dlg::OnRclickTree(NMHDR* pNMHDR, LRESULT* pResult)
405 {
406 	m_tree.right_clicked();
407 	*pResult = 0;
408 }
409 
OnBeginlabeleditTree(NMHDR * pNMHDR,LRESULT * pResult)410 void debriefing_editor_dlg::OnBeginlabeleditTree(NMHDR* pNMHDR, LRESULT* pResult)
411 {
412 	TV_DISPINFO* pTVDispInfo = (TV_DISPINFO*)pNMHDR;
413 
414 	if (m_tree.edit_label(pTVDispInfo->item.hItem) == 1)	{
415 		*pResult = 0;
416 		modified = 1;
417 
418 	} else
419 		*pResult = 1;
420 }
421 
OnEndlabeleditTree(NMHDR * pNMHDR,LRESULT * pResult)422 void debriefing_editor_dlg::OnEndlabeleditTree(NMHDR* pNMHDR, LRESULT* pResult)
423 {
424 	TV_DISPINFO* pTVDispInfo = (TV_DISPINFO*)pNMHDR;
425 
426 	*pResult = m_tree.end_label_edit(pTVDispInfo->item);
427 }
428 
OnClose()429 void debriefing_editor_dlg::OnClose()
430 {
431 	audiostream_close_file(m_voice_id, 0);
432 	m_voice_id = -1;
433 	m_cur_stage = -1;
434 	update_data(1);
435 
436 	Mission_music[SCORE_DEBRIEF_SUCCESS] = m_debriefPass_music - 1;
437 	Mission_music[SCORE_DEBRIEF_AVERAGE] = m_debriefAvg_music - 1;
438 	Mission_music[SCORE_DEBRIEF_FAIL] = m_debriefFail_music - 1;
439 
440 	CDialog::OnClose();
441 }
442 
OnOK()443 void debriefing_editor_dlg::OnOK()
444 {
445 }
446 
OnCommand(WPARAM wParam,LPARAM lParam)447 BOOL debriefing_editor_dlg::OnCommand(WPARAM wParam, LPARAM lParam)
448 {
449 	int id;
450 
451 	// deal with figuring out menu stuff
452 	id = LOWORD(wParam);
453 	if ( (id >= ID_TEAM_1) && (id < ID_TEAM_3) ) {
454 		update_data(1);
455 
456 		// set the current debriefing
457 		m_current_debriefing = id - ID_TEAM_1;
458 
459 		// put user back at first stage for this team (or no current stage is there are none).
460 		Debriefing = &Debriefings[m_current_debriefing];
461 		if ( Debriefing->num_stages > 0 )
462 			m_cur_stage = 0;
463 		else
464 			m_cur_stage = -1;
465 
466 		m_last_stage = -1;
467 		update_data(0);
468 	}
469 
470 	return CDialog::OnCommand(wParam, lParam);
471 }
472 
DestroyWindow()473 BOOL debriefing_editor_dlg::DestroyWindow()
474 {
475 	audiostream_close_file(m_voice_id, 0);
476 	m_play_bm.DeleteObject();
477 	return CDialog::DestroyWindow();
478 }
479 
OnPlay()480 void debriefing_editor_dlg::OnPlay()
481 {
482 	GetDlgItem(IDC_VOICE)->GetWindowText(m_voice);
483 
484 	if (m_voice_id >= 0) {
485 		audiostream_close_file(m_voice_id, 0);
486 		m_voice_id = -1;
487 		return;
488 	}
489 
490 	// we use ASF_EVENTMUSIC here so that it will keep the extension in place
491 	m_voice_id = audiostream_open((char *)(LPCSTR) m_voice, ASF_EVENTMUSIC);
492 
493 	if (m_voice_id >= 0) {
494 		audiostream_play(m_voice_id, 1.0f, 0);
495 	}
496 }
497