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