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