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