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 // CampaignEditorDlg.cpp : implementation file
11 //
12 
13 #include <setjmp.h>
14 #include "stdafx.h"
15 #include "FRED.h"
16 #include "CampaignEditorDlg.h"
17 #include "CampaignTreeView.h"
18 #include "CampaignTreeWnd.h"
19 #include "Management.h"
20 #include "cfile/cfile.h"
21 #include "FREDDoc.h"
22 #include "parse/parselo.h"
23 #include "mission/missiongoals.h"
24 
25 #ifdef _DEBUG
26 #undef THIS_FILE
27 static char THIS_FILE[] = __FILE__;
28 #endif
29 
30 int Cur_campaign_mission = -1;
31 int Cur_campaign_link = -1;
32 
33 /////////////////////////////////////////////////////////////////////////////
34 // campaign_editor dialog
35 
36 IMPLEMENT_DYNCREATE(campaign_editor, CFormView)
37 
38 campaign_editor *Campaign_tree_formp;
39 
campaign_editor()40 campaign_editor::campaign_editor()
41 	: CFormView(campaign_editor::IDD)
42 {
43 	//{{AFX_DATA_INIT(campaign_editor)
44 	m_name = _T("");
45 	m_type = -1;
46 	m_num_players = _T("");
47 	m_desc = _T("");
48 	m_branch_desc = _T("");
49 	m_branch_brief_anim = _T("");
50 	m_branch_brief_sound = _T("");
51 	m_custom_tech_db = FALSE;
52 	//}}AFX_DATA_INIT
53 
54 	m_tree.m_mode = MODE_CAMPAIGN;
55 	m_num_links = 0;
56 	m_tree.link_modified(&Campaign_modified);
57 	m_last_mission = -1;
58 }
59 
~campaign_editor()60 campaign_editor::~campaign_editor()
61 {
62 }
63 
DoDataExchange(CDataExchange * pDX)64 void campaign_editor::DoDataExchange(CDataExchange* pDX)
65 {
66 	CFormView::DoDataExchange(pDX);
67 	//{{AFX_DATA_MAP(campaign_editor)
68 	DDX_Control(pDX, IDC_SEXP_TREE, m_tree);
69 	DDX_Control(pDX, IDC_FILELIST, m_filelist);
70 	DDX_Text(pDX, IDC_NAME, m_name);
71 	DDX_CBIndex(pDX, IDC_CAMPAIGN_TYPE, m_type);
72 	DDX_Text(pDX, IDC_NUM_PLAYERS, m_num_players);
73 	DDX_Text(pDX, IDC_DESC2, m_desc);
74 	DDX_Text(pDX, IDC_MISSISON_LOOP_DESC, m_branch_desc);
75 	DDX_Text(pDX, IDC_LOOP_BRIEF_ANIM, m_branch_brief_anim);
76 	DDX_Text(pDX, IDC_LOOP_BRIEF_SOUND, m_branch_brief_sound);
77 	DDX_Check(pDX, IDC_CUSTOM_TECH_DB, m_custom_tech_db);
78 	//}}AFX_DATA_MAP
79 
80 	DDV_MaxChars(pDX, m_desc, MISSION_DESC_LENGTH - 1);
81 	DDV_MaxChars(pDX, m_branch_desc, MISSION_DESC_LENGTH - 1);
82 }
83 
BEGIN_MESSAGE_MAP(campaign_editor,CFormView)84 BEGIN_MESSAGE_MAP(campaign_editor, CFormView)
85 	//{{AFX_MSG_MAP(campaign_editor)
86 	ON_BN_CLICKED(ID_LOAD, OnLoad)
87 	ON_BN_CLICKED(IDC_ALIGN, OnAlign)
88 	ON_BN_CLICKED(ID_CPGN_CLOSE, OnCpgnClose)
89 	ON_NOTIFY(NM_RCLICK, IDC_SEXP_TREE, OnRclickTree)
90 	ON_NOTIFY(TVN_BEGINLABELEDIT, IDC_SEXP_TREE, OnBeginlabeleditSexpTree)
91 	ON_NOTIFY(TVN_ENDLABELEDIT, IDC_SEXP_TREE, OnEndlabeleditSexpTree)
92 	ON_NOTIFY(TVN_SELCHANGED, IDC_SEXP_TREE, OnSelchangedSexpTree)
93 	ON_BN_CLICKED(IDC_MOVE_UP, OnMoveUp)
94 	ON_BN_CLICKED(IDC_MOVE_DOWN, OnMoveDown)
95 	ON_COMMAND(ID_END_EDIT, OnEndEdit)
96 	ON_EN_CHANGE(IDC_BRIEFING_CUTSCENE, OnChangeBriefingCutscene)
97 	ON_CBN_SELCHANGE(IDC_CAMPAIGN_TYPE, OnSelchangeType)
98 	ON_BN_CLICKED(IDC_TOGGLE_LOOP, OnToggleLoop)
99 	ON_BN_CLICKED(IDC_LOOP_BRIEF_BROWSE, OnBrowseLoopAni)
100 	ON_BN_CLICKED(IDC_LOOP_BRIEF_SOUND_BROWSE, OnBrowseLoopSound)
101 	ON_EN_CHANGE(IDC_MAIN_HALL, OnChangeMainHall)
102 	ON_EN_CHANGE(IDC_DEBRIEFING_PERSONA, OnChangeDebriefingPersona)
103 	ON_BN_CLICKED(IDC_CUSTOM_TECH_DB, OnCustomTechDB)
104 	//}}AFX_MSG_MAP
105 END_MESSAGE_MAP()
106 
107 /////////////////////////////////////////////////////////////////////////////
108 // campaign_editor diagnostics
109 
110 #ifdef _DEBUG
111 void campaign_editor::AssertValid() const
112 {
113 	CFormView::AssertValid();
114 }
115 
Dump(CDumpContext & dc) const116 void campaign_editor::Dump(CDumpContext& dc) const
117 {
118 	CFormView::Dump(dc);
119 }
120 #endif //_DEBUG
121 
122 /////////////////////////////////////////////////////////////////////////////
123 // campaign_editor message handlers
124 
OnUpdate(CView * pSender,LPARAM lHint,CObject * pHint)125 void campaign_editor::OnUpdate(CView* pSender, LPARAM lHint, CObject* pHint)
126 {
127 	UpdateData(FALSE);
128 }
129 
OnLoad()130 void campaign_editor::OnLoad()
131 {
132 	if (Cur_campaign_mission < 0){
133 		return;
134 	}
135 
136 	if (Campaign_modified){
137 		if (Campaign_wnd->save_modified()){
138 			return;
139 		}
140 	}
141 
142 	auto res = cf_find_file_location(Campaign.missions[Cur_campaign_mission].name, CF_TYPE_MISSIONS, false);
143 
144 	if (res.found) {
145 		FREDDoc_ptr->SetPathName(res.full_name.c_str());
146 	}
147 	Campaign_wnd->DestroyWindow();
148 }
149 
Create(LPCTSTR lpszClassName,LPCTSTR lpszWindowName,DWORD dwStyle,const RECT & rect,CWnd * pParentWnd,UINT nID,CCreateContext * pContext)150 BOOL campaign_editor::Create(LPCTSTR lpszClassName, LPCTSTR lpszWindowName, DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID, CCreateContext* pContext)
151 {
152 	int i;
153 	BOOL r;
154 	CComboBox *box;
155 
156 	r = CFormView::Create(lpszClassName, lpszWindowName, dwStyle, rect, pParentWnd, nID, pContext);
157 	if (r) {
158 		box = (CComboBox *) GetDlgItem(IDC_CAMPAIGN_TYPE);
159 		box->ResetContent();
160 		for (i=0; i<MAX_CAMPAIGN_TYPES; i++){
161 			box->AddString(campaign_types[i]);
162 		}
163 	}
164 
165 	return r;
166 }
167 
load_campaign()168 void campaign_editor::load_campaign()
169 {
170 	Cur_campaign_mission = Cur_campaign_link = -1;
171 	load_tree(0);
172 
173 	if (!strlen(Campaign.filename))
174 		strcpy_s(Campaign.filename, Default_campaign_file_name);
175 
176 	if (mission_campaign_load(Campaign.filename, NULL, 0)) {
177 		MessageBox("Couldn't open Campaign file!", "Error");
178 		Campaign_wnd->OnCpgnFileNew();
179 		return;
180 	}
181 
182 	Campaign_modified = 0;
183 	Campaign_tree_viewp->construct_tree();
184 	initialize();
185 }
186 
OnAlign()187 void campaign_editor::OnAlign()
188 {
189 	Campaign_tree_viewp->sort_elements();
190 	Campaign_tree_viewp->realign_tree();
191 	Campaign_tree_viewp->Invalidate();
192 }
193 
initialize(int init_files)194 void campaign_editor::initialize( int init_files )
195 {
196 	Cur_campaign_mission = Cur_campaign_link = -1;
197 	m_tree.setup((CEdit *) GetDlgItem(IDC_HELP_BOX));
198 	load_tree(0);
199 	Campaign_tree_viewp->initialize();
200 
201 	// only initialize the file dialog box when the parameter says to.  This should
202 	// only happen when a campaign type changes
203 	if ( init_files ){
204 		m_filelist.initialize();
205 	}
206 
207 	m_name = Campaign.name;
208 	m_type = Campaign.type;
209 	m_num_players.Format("%d", Campaign.num_players);
210 
211 	if (Campaign.desc) {
212 		convert_multiline_string(m_desc, Campaign.desc);
213 	} else {
214 		m_desc = _T("");
215 	}
216 
217 	m_branch_desc = _T("");
218 
219 	m_branch_brief_anim = _T("");
220 	m_branch_brief_sound = _T("");
221 
222 	// single player should hide the two dialog items about number of players allowed
223 	if ( m_type == CAMPAIGN_TYPE_SINGLE ) {
224 		GetDlgItem(IDC_NUM_PLAYERS)->ShowWindow( SW_HIDE );
225 		GetDlgItem(IDC_PLAYERS_LABEL)->ShowWindow( SW_HIDE );
226 	} else {
227 		GetDlgItem(IDC_NUM_PLAYERS)->ShowWindow( SW_SHOW );
228 		GetDlgItem(IDC_PLAYERS_LABEL)->ShowWindow( SW_SHOW );
229 	}
230 
231 	// set up the tech db checkbox
232 	m_custom_tech_db = (BOOL)(Campaign.flags & CF_CUSTOM_TECH_DATABASE);
233 
234 	UpdateData(FALSE);
235 }
236 
mission_selected(int num)237 void campaign_editor::mission_selected(int num)
238 {
239 	CEdit *bc_dialog, *bc_hall, *bc_persona;
240 	char personatext[10];
241 
242 	bc_dialog = (CEdit *) GetDlgItem(IDC_BRIEFING_CUTSCENE);
243 	bc_hall = (CEdit *) GetDlgItem(IDC_MAIN_HALL);
244 	bc_persona = (CEdit *) GetDlgItem(IDC_DEBRIEFING_PERSONA);
245 
246 	// clear out the text for the briefing cutscene, and put in new text if specified
247 	bc_dialog->SetWindowText("");
248 	if ( strlen(Campaign.missions[num].briefing_cutscene) )
249 		bc_dialog->SetWindowText(Campaign.missions[num].briefing_cutscene);
250 
251 	// new main hall stuff
252 	bc_hall->SetWindowText(Campaign.missions[num].main_hall.c_str());
253 
254 	// new debriefing persona stuff
255 	sprintf(personatext, "%d", Campaign.missions[num].debrief_persona_index);
256 	bc_persona->SetWindowText(CString(personatext));
257 }
258 
update()259 void campaign_editor::update()
260 {
261 	char buf[MISSION_DESC_LENGTH];
262 
263 	// get data from dlog box
264 	UpdateData(TRUE);
265 
266 	// update campaign name
267 	string_copy(Campaign.name, m_name, NAME_LENGTH);
268 	Campaign.type = m_type;
269 
270 	// update campaign desc
271 	deconvert_multiline_string(buf, m_desc, MISSION_DESC_LENGTH);
272 	if (Campaign.desc) {
273 		free(Campaign.desc);
274 	}
275 
276 	Campaign.desc = NULL;
277 	if (strlen(buf)) {
278 		Campaign.desc = strdup(buf);
279 	}
280 
281 	// update flags
282 	Campaign.flags = CF_DEFAULT_VALUE;
283 	if (m_custom_tech_db)
284 		Campaign.flags |= CF_CUSTOM_TECH_DATABASE;
285 
286 	// maybe update mission loop text
287 	save_loop_desc_window();
288 
289 	// set the number of players in a multiplayer mission equal to the number of players in the first mission
290 	if ( Campaign.type != CAMPAIGN_TYPE_SINGLE ) {
291 		if ( Campaign.num_missions == 0 ) {
292 			Campaign.num_players = 0;
293 		} else {
294 			mission a_mission;
295 
296 			get_mission_info(Campaign.missions[0].name, &a_mission);
297 			Campaign.num_players = a_mission.num_players;
298 		}
299 	}
300 }
301 
OnCpgnClose()302 void campaign_editor::OnCpgnClose()
303 {
304 	Campaign_wnd->OnClose();
305 }
306 
load_tree(int save_first)307 void campaign_editor::load_tree(int save_first)
308 {
309 	char text[80];
310 	int i;
311 	HTREEITEM h;
312 
313 	if (save_first)
314 		save_tree();
315 
316 	m_tree.clear_tree();
317 	m_tree.DeleteAllItems();
318 	m_num_links = 0;
319 	UpdateData(TRUE);
320 	update_loop_desc_window();
321 
322 	m_last_mission = Cur_campaign_mission;
323 	if (Cur_campaign_mission < 0) {
324 		GetDlgItem(IDC_SEXP_TREE)->EnableWindow(FALSE);
325 		GetDlgItem(IDC_BRIEFING_CUTSCENE)->EnableWindow(FALSE);
326 		GetDlgItem(IDC_MAIN_HALL)->EnableWindow(FALSE);
327 		return;
328 	}
329 
330 	GetDlgItem(IDC_SEXP_TREE)->EnableWindow(TRUE);
331 	GetDlgItem(IDC_BRIEFING_CUTSCENE)->EnableWindow(TRUE);
332 	GetDlgItem(IDC_MAIN_HALL)->EnableWindow(TRUE);
333 
334 	GetDlgItem(IDC_MISSISON_LOOP_DESC)->EnableWindow(FALSE);
335 	GetDlgItem(IDC_LOOP_BRIEF_ANIM)->EnableWindow(FALSE);
336 	GetDlgItem(IDC_LOOP_BRIEF_SOUND)->EnableWindow(FALSE);
337 	GetDlgItem(IDC_LOOP_BRIEF_BROWSE)->EnableWindow(FALSE);
338 	GetDlgItem(IDC_LOOP_BRIEF_SOUND_BROWSE)->EnableWindow(FALSE);
339 
340 	for (i=0; i<Total_links; i++) {
341 		if (Links[i].from == Cur_campaign_mission) {
342 			Links[i].node = m_tree.load_sub_tree(Links[i].sexp, true, "do-nothing");
343 			m_num_links++;
344 
345 			if (Links[i].from == Links[i].to) {
346 				strcpy_s(text, "Repeat mission");
347 			} else if ( (Links[i].to == -1) && (Links[i].from != -1) ) {
348 				strcpy_s(text, "End of Campaign");
349 			} else {
350 				sprintf(text, "Branch to %s", Campaign.missions[Links[i].to].name);
351 			}
352 
353 			// insert item into tree
354 			int image, sel_image;
355 			if (Links[i].is_mission_loop || Links[i].is_mission_fork) {
356 				image = BITMAP_BLUE_DOT;
357 				sel_image = BITMAP_GREEN_DOT;
358 			} else {
359 				image = BITMAP_BLACK_DOT;
360 				sel_image = BITMAP_RED_DOT;
361 			}
362 
363 			h = m_tree.insert(text, image, sel_image);
364 
365 			m_tree.SetItemData(h, Links[i].node);
366 			m_tree.add_sub_tree(Links[i].node, h);
367 		}
368 	}
369 }
370 
OnRclickTree(NMHDR * pNMHDR,LRESULT * pResult)371 void campaign_editor::OnRclickTree(NMHDR* pNMHDR, LRESULT* pResult)
372 {
373 	m_tree.right_clicked(MODE_CAMPAIGN);
374 	*pResult = 0;
375 }
376 
OnBeginlabeleditSexpTree(NMHDR * pNMHDR,LRESULT * pResult)377 void campaign_editor::OnBeginlabeleditSexpTree(NMHDR* pNMHDR, LRESULT* pResult)
378 {
379 	TV_DISPINFO* pTVDispInfo = (TV_DISPINFO*)pNMHDR;
380 
381 	if (m_tree.edit_label(pTVDispInfo->item.hItem) == 1)	{
382 		*pResult = 0;
383 		Campaign_modified = 1;
384 	} else {
385 		*pResult = 1;
386 	}
387 }
388 
OnEndlabeleditSexpTree(NMHDR * pNMHDR,LRESULT * pResult)389 void campaign_editor::OnEndlabeleditSexpTree(NMHDR* pNMHDR, LRESULT* pResult)
390 {
391 	TV_DISPINFO* pTVDispInfo = (TV_DISPINFO*)pNMHDR;
392 
393 	*pResult = m_tree.end_label_edit(pTVDispInfo->item);
394 }
395 
handler(int code,int node,char * str)396 int campaign_editor::handler(int code, int node, char *str)
397 {
398 	int i;
399 
400 	switch (code) {
401 	case ROOT_DELETED:
402 		for (i=0; i<Total_links; i++){
403 			if ((Links[i].from == Cur_campaign_mission) && (Links[i].node == node)){
404 				break;
405 			}
406 		}
407 
408 		Campaign_tree_viewp->delete_link(i);
409 		m_num_links--;
410 		return node;
411 
412 	default:
413 		Int3();
414 	}
415 
416 	return -1;
417 }
418 
save_tree(int clear)419 void campaign_editor::save_tree(int clear)
420 {
421 	int i;
422 
423 	if (m_last_mission < 0){
424 		return;  // nothing to save
425 	}
426 
427 	for (i=0; i<Total_links; i++){
428 		if (Links[i].from == m_last_mission) {
429 			sexp_unmark_persistent(Links[i].sexp);
430 			free_sexp2(Links[i].sexp);
431 			Links[i].sexp = m_tree.save_tree(Links[i].node);
432 			sexp_mark_persistent(Links[i].sexp);
433 		}
434 	}
435 
436 	if (clear){
437 		m_last_mission = -1;
438 	}
439 }
440 
OnSelchangedSexpTree(NMHDR * pNMHDR,LRESULT * pResult)441 void campaign_editor::OnSelchangedSexpTree(NMHDR* pNMHDR, LRESULT* pResult)
442 {
443 	int i, node;
444 	HTREEITEM h, h2;
445 
446 	// get handle of selected item
447 	NM_TREEVIEW* pNMTreeView = (NM_TREEVIEW*)pNMHDR;
448 	h = pNMTreeView->itemNew.hItem;
449 	Assert(h);
450 
451 	// update help on sexp
452 	m_tree.update_help(h);
453 
454 	// get handle of parent
455 	while ((h2 = m_tree.GetParentItem(h))>0){
456 		h = h2;
457 	}
458 
459 	// get identifier of parent
460 	node = (int)m_tree.GetItemData(h);
461 	for (i=0; i<Total_links; i++){
462 		if ((Links[i].from == Cur_campaign_mission) && (Links[i].node == node)){
463 			break;
464 		}
465 	}
466 
467 	if (i == Total_links) {
468 		Cur_campaign_link = -1;
469 		return;
470 	}
471 
472 	// update mission loop text
473 	UpdateData(TRUE);
474 	save_loop_desc_window();
475 
476 	Cur_campaign_link = i;
477 	update_loop_desc_window();
478 	Campaign_tree_viewp->Invalidate();
479 	*pResult = 0;
480 }
481 
OnMoveUp()482 void campaign_editor::OnMoveUp()
483 {
484 	int i, last = -1;
485 	campaign_tree_link temp;
486 	HTREEITEM h1, h2;
487 
488 	if (Cur_campaign_link >= 0) {
489 		save_tree();
490 		for (i=0; i<Total_links; i++){
491 			if (Links[i].from == Cur_campaign_mission) {
492 				if (i == Cur_campaign_link){
493 					break;
494 				}
495 
496 				last = i;
497 			}
498 		}
499 
500 		if ((last != -1) && (i < Total_links)) {
501 			h1 = m_tree.GetParentItem(m_tree.handle(Links[i].node));
502 			h2 = m_tree.GetParentItem(m_tree.handle(Links[last].node));
503 			m_tree.swap_roots(h1, h2);
504 			m_tree.SelectItem(m_tree.GetParentItem(m_tree.handle(Links[i].node)));
505 
506 			temp = Links[last];
507 			Links[last] = Links[i];
508 			Links[i] = temp;
509 			Cur_campaign_link = last;
510 		}
511 	}
512 
513 	GetDlgItem(IDC_SEXP_TREE)->SetFocus();
514 }
515 
OnMoveDown()516 void campaign_editor::OnMoveDown()
517 {
518 	int i, j;
519 	campaign_tree_link temp;
520 	HTREEITEM h1, h2;
521 
522 	if (Cur_campaign_link >= 0) {
523 		save_tree();
524 		for (i=0; i<Total_links; i++)
525 			if (Links[i].from == Cur_campaign_mission)
526 				if (i == Cur_campaign_link)
527 					break;
528 
529 		for (j=i+1; j<Total_links; j++)
530 			if (Links[j].from == Cur_campaign_mission)
531 				break;
532 
533 		if (j < Total_links) {
534 			h1 = m_tree.GetParentItem(m_tree.handle(Links[i].node));
535 			h2 = m_tree.GetParentItem(m_tree.handle(Links[j].node));
536 			m_tree.swap_roots(h1, h2);
537 			m_tree.SelectItem(m_tree.GetParentItem(m_tree.handle(Links[i].node)));
538 
539 			temp = Links[j];
540 			Links[j] = Links[i];
541 			Links[i] = temp;
542 			Cur_campaign_link = j;
543 		}
544 	}
545 
546 	GetDlgItem(IDC_SEXP_TREE)->SetFocus();
547 }
548 
swap_handler(int node1,int node2)549 void campaign_editor::swap_handler(int node1, int node2)
550 {
551 	int index1, index2;
552 	campaign_tree_link temp;
553 
554 	for (index1=0; index1<Total_links; index1++){
555 		if ((Links[index1].from == Cur_campaign_mission) && (Links[index1].node == node1)){
556 			break;
557 		}
558 	}
559 
560 	Assert(index1 < Total_links);
561 	for (index2=0; index2<Total_links; index2++){
562 		if ((Links[index2].from == Cur_campaign_mission) && (Links[index2].node == node2)){
563 			break;
564 		}
565 	}
566 
567 	Assert(index2 < Total_links);
568 	temp = Links[index1];
569 //	Links[index1] = Links[index2];
570 	while (index1 < index2) {
571 		Links[index1] = Links[index1 + 1];
572 		index1++;
573 	}
574 
575 	while (index1 > index2 + 1) {
576 		Links[index1] = Links[index1 - 1];
577 		index1--;
578 	}
579 
580 	// update Cur_campaign_link
581 	Cur_campaign_link = index1;
582 
583 	Links[index1] = temp;
584 }
585 
insert_handler(int old,int node)586 void campaign_editor::insert_handler(int old, int node)
587 {
588 	int i;
589 
590 	for (i=0; i<Total_links; i++){
591 		if ((Links[i].from == Cur_campaign_mission) && (Links[i].node == old)){
592 			break;
593 		}
594 	}
595 
596 	Assert(i < Total_links);
597 	Links[i].node = node;
598 	return;
599 }
600 
OnEndEdit()601 void campaign_editor::OnEndEdit()
602 {
603 	HWND h;
604 	CWnd *w;
605 
606 	w = GetFocus();
607 	if (w) {
608 		h = w->m_hWnd;
609 		GetDlgItem(IDC_SEXP_TREE)->SetFocus();
610 		::SetFocus(h);
611 	}
612 }
613 
OnChangeBriefingCutscene()614 void campaign_editor::OnChangeBriefingCutscene()
615 {
616 	CEdit *bc_dialog;
617 
618 	bc_dialog = (CEdit *) GetDlgItem(IDC_BRIEFING_CUTSCENE);
619 
620 	// maybe save off the current cutscene names.
621 	if ( Cur_campaign_mission != -1 ) {
622 
623 		// see if the contents of the edit box have changed.  Luckily, this returns 0 when the edit box is
624 		// cleared.
625 		if ( bc_dialog->GetModify() ) {
626 			bc_dialog->GetWindowText( Campaign.missions[Cur_campaign_mission].briefing_cutscene, NAME_LENGTH );
627 			Campaign_modified = 1;
628 		}
629 	}
630 
631 }
632 
633 // what to do when changing campaign type
OnSelchangeType()634 void campaign_editor::OnSelchangeType()
635 {
636 	// if campaign type is single player, then disable the multiplayer items
637 	update();
638 	initialize();
639 	Campaign_modified = 1;
640 }
641 
642 
643 // update the loop mission text
save_loop_desc_window()644 void campaign_editor::save_loop_desc_window()
645 {
646 	char buffer[MISSION_DESC_LENGTH];
647 
648 	// update only if active link and there is a mission has mission loop or fork
649 	if ( (Cur_campaign_link >= 0) && (Links[Cur_campaign_link].is_mission_loop || Links[Cur_campaign_link].is_mission_fork) ) {
650 		deconvert_multiline_string(buffer, m_branch_desc, MISSION_DESC_LENGTH);
651 		if (Links[Cur_campaign_link].mission_branch_txt) {
652 			free(Links[Cur_campaign_link].mission_branch_txt);
653 		}
654 		if (Links[Cur_campaign_link].mission_branch_brief_anim) {
655 			free(Links[Cur_campaign_link].mission_branch_brief_anim);
656 		}
657 		if (Links[Cur_campaign_link].mission_branch_brief_sound) {
658 			free(Links[Cur_campaign_link].mission_branch_brief_sound);
659 		}
660 
661 		if (strlen(buffer)) {
662 			Links[Cur_campaign_link].mission_branch_txt = strdup(buffer);
663 		} else {
664 			Links[Cur_campaign_link].mission_branch_txt = NULL;
665 		}
666 
667 		deconvert_multiline_string(buffer, m_branch_brief_anim, MAX_FILENAME_LEN);
668 		if(strlen(buffer)){
669 			Links[Cur_campaign_link].mission_branch_brief_anim = strdup(buffer);
670 		} else {
671 			Links[Cur_campaign_link].mission_branch_brief_anim = NULL;
672 		}
673 
674 		deconvert_multiline_string(buffer, m_branch_brief_sound, MAX_FILENAME_LEN);
675 		if(strlen(buffer)){
676 			Links[Cur_campaign_link].mission_branch_brief_sound = strdup(buffer);
677 		} else {
678 			Links[Cur_campaign_link].mission_branch_brief_sound = NULL;
679 		}
680 	}
681 }
682 
update_loop_desc_window()683 void campaign_editor::update_loop_desc_window()
684 {
685 	bool enable_branch_desc_window;
686 
687 	enable_branch_desc_window = false;
688 	if ((Cur_campaign_link >= 0) && (Links[Cur_campaign_link].is_mission_loop || Links[Cur_campaign_link].is_mission_fork)) {
689 		enable_branch_desc_window = true;
690 	}
691 
692 	// maybe enable description window
693 	GetDlgItem(IDC_MISSISON_LOOP_DESC)->EnableWindow(enable_branch_desc_window);
694 	GetDlgItem(IDC_LOOP_BRIEF_ANIM)->EnableWindow(enable_branch_desc_window);
695 	GetDlgItem(IDC_LOOP_BRIEF_SOUND)->EnableWindow(enable_branch_desc_window);
696 	GetDlgItem(IDC_LOOP_BRIEF_BROWSE)->EnableWindow(enable_branch_desc_window);
697 	GetDlgItem(IDC_LOOP_BRIEF_SOUND_BROWSE)->EnableWindow(enable_branch_desc_window);
698 
699 	// set new text
700 	if ((Cur_campaign_link >= 0) && Links[Cur_campaign_link].mission_branch_txt && enable_branch_desc_window) {
701 		convert_multiline_string(m_branch_desc, Links[Cur_campaign_link].mission_branch_txt);
702 	} else {
703 		m_branch_desc = _T("");
704 	}
705 
706 	// set new text
707 	if ((Cur_campaign_link >= 0) && Links[Cur_campaign_link].mission_branch_brief_anim && enable_branch_desc_window) {
708 		convert_multiline_string(m_branch_brief_anim, Links[Cur_campaign_link].mission_branch_brief_anim);
709 	} else {
710 		m_branch_brief_anim = _T("");
711 	}
712 
713 	// set new text
714 	if ((Cur_campaign_link >= 0) && Links[Cur_campaign_link].mission_branch_brief_sound && enable_branch_desc_window) {
715 		convert_multiline_string(m_branch_brief_sound, Links[Cur_campaign_link].mission_branch_brief_sound);
716 	} else {
717 		m_branch_brief_sound = _T("");
718 	}
719 
720 	// reset text
721 	UpdateData(FALSE);
722 }
723 
OnToggleLoop()724 void campaign_editor::OnToggleLoop()
725 {
726 	// check if branch selected
727 	if (Cur_campaign_link == -1) {
728 		return;
729 	}
730 
731 	// store mission loop text
732 	UpdateData(TRUE);
733 
734 	if ( (Cur_campaign_link >= 0) && (Links[Cur_campaign_link].is_mission_loop || Links[Cur_campaign_link].is_mission_fork) ) {
735 		if (Links[Cur_campaign_link].mission_branch_txt) {
736 			free(Links[Cur_campaign_link].mission_branch_txt);
737 		}
738 
739 		if (Links[Cur_campaign_link].mission_branch_brief_anim) {
740 			free(Links[Cur_campaign_link].mission_branch_brief_anim);
741 		}
742 
743 		if (Links[Cur_campaign_link].mission_branch_brief_sound) {
744 			free(Links[Cur_campaign_link].mission_branch_brief_sound);
745 		}
746 
747 		char buffer[MISSION_DESC_LENGTH];
748 
749 		deconvert_multiline_string(buffer, m_branch_desc, MISSION_DESC_LENGTH);
750 		if (m_branch_desc && strlen(buffer)) {
751 			Links[Cur_campaign_link].mission_branch_txt = strdup(buffer);
752 		} else {
753 			Links[Cur_campaign_link].mission_branch_txt = NULL;
754 		}
755 
756 		deconvert_multiline_string(buffer, m_branch_brief_anim, MISSION_DESC_LENGTH);
757 		if (m_branch_brief_anim && strlen(buffer)) {
758 			Links[Cur_campaign_link].mission_branch_brief_anim = strdup(buffer);
759 		} else {
760 			Links[Cur_campaign_link].mission_branch_brief_anim = NULL;
761 		}
762 
763 		deconvert_multiline_string(buffer, m_branch_brief_sound, MISSION_DESC_LENGTH);
764 		if (m_branch_brief_sound && strlen(buffer)) {
765 			Links[Cur_campaign_link].mission_branch_brief_sound = strdup(buffer);
766 		} else {
767 			Links[Cur_campaign_link].mission_branch_brief_sound = NULL;
768 		}
769 	}
770 
771 	// toggle to mission_loop setting
772 	Links[Cur_campaign_link].is_mission_loop = !Links[Cur_campaign_link].is_mission_loop;
773 	Links[Cur_campaign_link].is_mission_fork = false;
774 
775 	// reset loop desc window (gray if inactive)
776 	update_loop_desc_window();
777 
778 	// set root icon
779 	int bitmap1, bitmap2;
780 	if (Links[Cur_campaign_link].is_mission_loop || Links[Cur_campaign_link].is_mission_fork) {
781 		bitmap2 = BITMAP_GREEN_DOT;
782 		bitmap1 = BITMAP_BLUE_DOT;
783 	} else {
784 		bitmap1 = BITMAP_BLACK_DOT;
785 		bitmap2 = BITMAP_RED_DOT;
786 	}
787 
788 	// Search for item and update bitmap
789 	HTREEITEM h = m_tree.GetRootItem();
790 	while (h) {
791 		if ((int) m_tree.GetItemData(h) == Links[Cur_campaign_link].node) {
792 			m_tree.SetItemImage(h, bitmap1, bitmap2);
793 			break;
794 		}
795 
796 		h = m_tree.GetNextSiblingItem(h);
797 	}
798 
799 	// set to redraw
800 	Campaign_tree_viewp->Invalidate();
801 }
802 
OnBrowseLoopAni()803 void campaign_editor::OnBrowseLoopAni()
804 {
805 	int pushed_dir;
806 
807 	UpdateData(TRUE);
808 
809 	// switch directories
810 	pushed_dir = cfile_push_chdir(CF_TYPE_INTERFACE);
811 	CFileDialog dlg(TRUE, "ani", NULL, OFN_HIDEREADONLY | OFN_FILEMUSTEXIST | OFN_NOCHANGEDIR,
812 		"Ani Files (*.ani)|*.ani|Eff File (*.eff)|*.eff|APNG Files (*.png)|*.png|All Anims (*.ani, *.eff, *.png)|*.ani;*.eff;*.png");
813 
814 	if (dlg.DoModal() == IDOK) {
815 		m_branch_brief_anim = dlg.GetFileName();
816 		UpdateData(FALSE);
817 	}
818 
819 	// move back to the proper directory
820 	if (!pushed_dir){
821 		cfile_pop_dir();
822 	}
823 }
824 
OnBrowseLoopSound()825 void campaign_editor::OnBrowseLoopSound()
826 {
827 	int pushed_dir;
828 
829 	UpdateData(TRUE);
830 
831 	// switch directories
832 	pushed_dir = cfile_push_chdir(CF_TYPE_VOICE_CMD_BRIEF);
833 	CFileDialog dlg(TRUE, "wav", NULL, OFN_HIDEREADONLY | OFN_FILEMUSTEXIST | OFN_NOCHANGEDIR,
834 		"Voice Files (*.ogg, *.wav)|*.ogg;*.wav|Ogg Vorbis Files (*.ogg)|*.ogg|Wave Files (*.wav)|*.wav||");
835 
836 	if (dlg.DoModal() == IDOK) {
837 		m_branch_brief_sound = dlg.GetFileName();
838 		UpdateData(FALSE);
839 	}
840 
841 	// move back to the proper directory
842 	if (!pushed_dir){
843 		cfile_pop_dir();
844 	}
845 }
846 
OnChangeMainHall()847 void campaign_editor::OnChangeMainHall()
848 {
849 	CString str;
850 	UpdateData(TRUE);
851 
852 	if (Cur_campaign_mission >= 0)
853 	{
854 		GetDlgItem(IDC_MAIN_HALL)->GetWindowText(str);
855 		Campaign.missions[Cur_campaign_mission].main_hall = SCP_string((LPCTSTR)(str));
856 	}
857 }
858 
OnChangeDebriefingPersona()859 void campaign_editor::OnChangeDebriefingPersona()
860 {
861 	CString str;
862 	int persona;
863 
864 	UpdateData(TRUE);
865 
866 	if (Cur_campaign_mission >= 0)
867 	{
868 		GetDlgItem(IDC_DEBRIEFING_PERSONA)->GetWindowText(str);
869 		persona = atoi(str);
870 
871 		if (persona < 0 || persona > 0xff)
872 			persona = 0;
873 
874 		Campaign.missions[Cur_campaign_mission].debrief_persona_index = (ubyte) persona;
875 	}
876 }
877 
OnCustomTechDB()878 void campaign_editor::OnCustomTechDB()
879 {
880 	UpdateData(TRUE);
881 
882 	if (m_custom_tech_db)
883 		Campaign.flags |= CF_CUSTOM_TECH_DATABASE;
884 	else
885 		Campaign.flags &= ~CF_CUSTOM_TECH_DATABASE;
886 }
887