]> icculus.org git repositories - taylor/freespace2.git/blob - src/fred2/missiongoalsdlg.cpp
Initial revision
[taylor/freespace2.git] / src / fred2 / missiongoalsdlg.cpp
1 /*
2  * $Logfile: /Freespace2/code/fred2/MissionGoalsDlg.cpp $
3  * $Revision$
4  * $Date$
5  * $Author$
6  *
7  * Mission goals editor dialog box handling code
8  *
9  * $Log$
10  * Revision 1.1  2002/05/03 03:28:08  root
11  * Initial revision
12  *
13  * 
14  * 4     2/17/99 2:11p Dave
15  * First full run of squad war. All freespace and tracker side stuff
16  * works.
17  * 
18  * 3     1/19/99 3:57p Andsager
19  * Round 2 of variables
20  * 
21  * 2     10/07/98 6:28p Dave
22  * Initial checkin. Renamed all relevant stuff to be Fred2 instead of
23  * Fred. Globalized mission and campaign file extensions. Removed Silent
24  * Threat specific code.
25  * 
26  * 1     10/07/98 3:01p Dave
27  * 
28  * 1     10/07/98 3:00p Dave
29  * 
30  * 47    5/23/98 12:20p Sandeep
31  * 
32  * 46    5/22/98 1:06a Hoffoss
33  * Made Fred not use OLE.
34  * 
35  * 45    5/20/98 1:04p Hoffoss
36  * Made credits screen use new artwork and removed rating field usage from
37  * Fred (a goal struct member).
38  * 
39  * 44    3/31/98 12:23a Allender
40  * changed macro names of campaign types to be more descriptive.  Added
41  * "team" to objectives dialog for team v. team missions.  Added two
42  * distinct multiplayer campaign types
43  * 
44  * 43    12/08/97 2:03p Hoffoss
45  * Added Fred support for MGF_NO_MUSIC flag in objectives.
46  * 
47  * 42    11/11/97 4:13p Duncan
48  * changed assert to abort operation instead.
49  * 
50  * 41    10/10/97 6:21p Hoffoss
51  * Put in Fred support for training object list editing.
52  * 
53  * 40    10/10/97 2:53p Johnson
54  * Fixed bug with new items being selected before they are fully
55  * registered as added.
56  * 
57  * 39    10/09/97 1:03p Hoffoss
58  * Renaming events or goals now updates sexp references as well.
59  * 
60  * 38    9/30/97 12:30p Hoffoss
61  * Drag and drop reordering of sexp tree roots now does an insert after
62  * rather than a swap operation.
63  * 
64  * 37    9/09/97 3:39p Sandeep
65  * warning level 4 bugs
66  * 
67  * 36    8/12/97 3:33p Hoffoss
68  * Fixed the "press cancel to go to reference" code to work properly.
69  * 
70  * 35    8/12/97 2:39p Johnson
71  * fixed bug with sexp tree goal name problem.
72  * 
73  * 34    8/01/97 3:10p Hoffoss
74  * Made Sexp help hidable.
75  * 
76  * 33    7/30/97 5:23p Hoffoss
77  * Removed Sexp tree verification code, since it duplicates normal sexp
78  * verification, and is just another set of code to keep maintained.
79  * 
80  * 32    7/25/97 3:05p Allender
81  * added score field to goals and events editor
82  * 
83  * 31    7/25/97 2:40p Hoffoss
84  * Fixed bug in sexp tree selection updating handling.
85  * 
86  * 30    7/24/97 12:45p Hoffoss
87  * Added sexp help system to sexp trees and some dialog boxes.
88  * 
89  * 29    7/17/97 4:10p Hoffoss
90  * Added drag and drop to sexp trees for reordering root items.
91  * 
92  * 28    7/16/97 6:30p Hoffoss
93  * Added icons to sexp trees, mainly because I think they will be required
94  * for drag n drop.
95  * 
96  * 27    7/07/97 12:04p Allender
97  * mission goal validation.
98  * 
99  * 26    6/02/97 8:47p Hoffoss
100  * Fixed bug with inserting an operator at root position, but under a
101  * label.
102  * 
103  * 25    5/20/97 2:28p Hoffoss
104  * Added message box queries for close window operation on all modal
105  * dialog boxes.
106  * 
107  * 24    5/01/97 4:12p Hoffoss
108  * Added return handling to dialogs.
109  * 
110  * 23    4/25/97 12:50p Allender
111  * change globals to new naming conventions
112  * 
113  * 22    4/23/97 11:55a Hoffoss
114  * Fixed many bugs uncovered while trying to create Mission 6.
115  * 
116  * 21    4/17/97 2:01p Hoffoss
117  * All dialog box window states are saved between sessions now.
118  * 
119  * 20    4/11/97 4:22p Hoffoss
120  * Fixed bug in Sexp trees, moved Show starfield option to view menu and
121  * removed preferences dialog box.
122  * 
123  * 19    4/11/97 10:11a Hoffoss
124  * Name fields supported by Fred for Events and Mission Goals.
125  * 
126  * 18    4/10/97 3:20p Mike
127  * Change hull damage to be like shields.
128  * 
129  * 17    4/01/97 5:15p Hoffoss
130  * Fixed errors in max length checks, renaming a wing now renames the
131  * ships in the wing as well, as it should.
132  * 
133  * 16    2/21/97 5:34p Hoffoss
134  * Added extensive modification detection and fixed a bug in initial
135  * orders editor.
136  * 
137  * 15    2/17/97 5:28p Hoffoss
138  * Checked RCS headers, added them were missing, changing description to
139  * something better, etc where needed.
140  *
141  * $NoKeywords: $
142  */
143
144 #include "stdafx.h"
145 #include "fred.h"
146 #include "freddoc.h"
147 #include "fredview.h"
148 #include "linklist.h"
149 #include "sexp.h"
150 #include "missiongoalsdlg.h"
151 #include "management.h"
152 #include "operatorargtypeselect.h"
153
154 #define ID_ADD_SHIPS                    9000
155 #define ID_REPLACE_SHIPS        11000
156 #define ID_ADD_WINGS                    13000
157 #define ID_REPLACE_WINGS        15000
158
159 #ifdef _DEBUG
160 #define new DEBUG_NEW
161 #undef THIS_FILE
162 static char THIS_FILE[] = __FILE__;
163 #endif
164
165 CMissionGoalsDlg *Goal_editor_dlg; // global reference needed by sexp_tree class
166
167 /////////////////////////////////////////////////////////////////////////////
168 // sexp_goal_tree class member functions
169
170 // determine the node number that would be allocated without actually allocating it yet.
171 int sexp_goal_tree::get_new_node_position()
172 {
173         int i;
174
175         for (i=0; i<MAX_SEXP_TREE_SIZE; i++)
176                 if (nodes[i].type == SEXPT_UNUSED)
177                         return i;
178
179         return -1;
180 }
181
182 // construct tree nodes for an sexp, adding them to the list and returning first node
183 int sexp_goal_tree::load_sub_tree(int index)
184 {
185         int cur;
186
187         if (index < 0) {
188                 cur = allocate_node(-1);
189                 set_node(cur, (SEXPT_OPERATOR | SEXPT_VALID), "true");  // setup a default tree if none
190                 return cur;
191         }
192
193         // assumption: first token is an operator.  I require this because it would cause problems
194         // with child/parent relations otherwise, and it should be this way anyway, since the
195         // return type of the whole sexp is boolean, and only operators can satisfy this.
196         Assert(Sexp_nodes[index].subtype == SEXP_ATOM_OPERATOR);
197         cur = get_new_node_position();
198         load_branch(index, -1);
199         return cur;
200 }
201
202 /////////////////////////////////////////////////////////////////////////////
203 // CMissionGoalsDlg dialog class member functions
204
205 CMissionGoalsDlg::CMissionGoalsDlg(CWnd* pParent /*=NULL*/)
206         : CDialog(CMissionGoalsDlg::IDD, pParent)
207 {
208         //{{AFX_DATA_INIT(CMissionGoalsDlg)
209         m_goal_desc = _T("");
210         m_goal_type = -1;
211         m_display_goal_types = 0;
212         m_name = _T("");
213         m_goal_invalid = FALSE;
214         m_goal_score = 0;
215         m_no_music = FALSE;
216         m_team = -1;
217         //}}AFX_DATA_INIT
218         m_goals_tree.m_mode = MODE_GOALS;
219         m_num_goals = 0;
220         m_goals_tree.link_modified(&modified);
221         modified = 0;
222         select_sexp_node = -1;
223 }
224
225 BOOL CMissionGoalsDlg::OnInitDialog()
226 {
227         int i, adjust = 0;
228
229         CDialog::OnInitDialog();  // let the base class do the default work
230         if (!Show_sexp_help)
231                 adjust = -SEXP_HELP_BOX_SIZE;
232
233         theApp.init_window(&Mission_goals_wnd_data, this, adjust);
234         m_goals_tree.setup((CEdit *) GetDlgItem(IDC_HELP_BOX));
235         load_tree();
236         create_tree();
237         if (m_num_goals >= MAX_GOALS)
238                 GetDlgItem(IDC_BUTTON_NEW_GOAL)->EnableWindow(FALSE);
239
240         Goal_editor_dlg = this;
241         i = m_goals_tree.select_sexp_node;
242         if (i != -1) {
243                 GetDlgItem(IDC_GOALS_TREE) -> SetFocus();
244                 m_goals_tree.hilite_item(i);
245                 return FALSE;
246         }
247
248         return TRUE;
249 }
250
251 void CMissionGoalsDlg::DoDataExchange(CDataExchange* pDX)
252 {
253         CDialog::DoDataExchange(pDX);
254         //{{AFX_DATA_MAP(CMissionGoalsDlg)
255         DDX_Control(pDX, IDC_GOALS_TREE, m_goals_tree);
256         DDX_Text(pDX, IDC_GOAL_DESC, m_goal_desc);
257         DDX_CBIndex(pDX, IDC_GOAL_TYPE_DROP, m_goal_type);
258         DDX_CBIndex(pDX, IDC_DISPLAY_GOAL_TYPES_DROP, m_display_goal_types);
259         DDX_Text(pDX, IDC_GOAL_NAME, m_name);
260         DDX_Check(pDX, IDC_GOAL_INVALID, m_goal_invalid);
261         DDX_Text(pDX, IDC_GOAL_SCORE, m_goal_score);
262         DDX_Check(pDX, IDC_NO_MUSIC, m_no_music);
263         DDX_CBIndex(pDX, IDC_OBJ_TEAM, m_team);
264         //}}AFX_DATA_MAP
265         DDV_MaxChars(pDX, m_goal_desc, MAX_GOAL_TEXT - 1);
266         DDV_MaxChars(pDX, m_name, NAME_LENGTH - 1);
267 }
268
269 BEGIN_MESSAGE_MAP(CMissionGoalsDlg, CDialog)
270         //{{AFX_MSG_MAP(CMissionGoalsDlg)
271         ON_CBN_SELCHANGE(IDC_DISPLAY_GOAL_TYPES_DROP, OnSelchangeDisplayGoalTypesDrop)
272         ON_NOTIFY(TVN_SELCHANGED, IDC_GOALS_TREE, OnSelchangedGoalsTree)
273         ON_NOTIFY(NM_RCLICK, IDC_GOALS_TREE, OnRclickGoalsTree)
274         ON_NOTIFY(TVN_ENDLABELEDIT, IDC_GOALS_TREE, OnEndlabeleditGoalsTree)
275         ON_NOTIFY(TVN_BEGINLABELEDIT, IDC_GOALS_TREE, OnBeginlabeleditGoalsTree)
276         ON_BN_CLICKED(IDC_BUTTON_NEW_GOAL, OnButtonNewGoal)
277         ON_EN_CHANGE(IDC_GOAL_DESC, OnChangeGoalDesc)
278         ON_EN_CHANGE(IDC_GOAL_RATING, OnChangeGoalRating)
279         ON_CBN_SELCHANGE(IDC_GOAL_TYPE_DROP, OnSelchangeGoalTypeDrop)
280         ON_EN_CHANGE(IDC_GOAL_NAME, OnChangeGoalName)
281         ON_BN_CLICKED(ID_OK, OnOk)
282         ON_WM_CLOSE()
283         ON_BN_CLICKED(IDC_GOAL_INVALID, OnGoalInvalid)
284         ON_EN_CHANGE(IDC_GOAL_SCORE, OnChangeGoalScore)
285         ON_BN_CLICKED(IDC_NO_MUSIC, OnNoMusic)
286         ON_CBN_SELCHANGE(IDC_OBJ_TEAM, OnSelchangeTeam)
287         //}}AFX_MSG_MAP
288 END_MESSAGE_MAP()
289
290 /////////////////////////////////////////////////////////////////////////////
291 // CMissionGoalsDlg message handlers
292
293 // Initialization: sets up internal working copy of mission goals and goal trees.
294 void CMissionGoalsDlg::load_tree()
295 {
296         int i;
297
298         m_goals_tree.select_sexp_node = select_sexp_node;
299         select_sexp_node = -1;
300
301         m_goals_tree.clear_tree();
302         m_num_goals = Num_goals;
303         for (i=0; i<Num_goals; i++) {
304                 m_goals[i] = Mission_goals[i];
305                 m_sig[i] = i;
306                 if (!(*m_goals[i].name))
307                         strcpy(m_goals[i].name, "<unnamed>");
308
309                 m_goals[i].formula = m_goals_tree.load_sub_tree(Mission_goals[i].formula);
310         }
311
312         m_goals_tree.post_load();
313         cur_goal = -1;
314         update_cur_goal();
315 }
316
317 // create the CTreeCtrl tree from the goal tree, filtering based on m_display_goal_types
318 void CMissionGoalsDlg::create_tree()
319 {
320         int i;
321         HTREEITEM h;
322
323         m_goals_tree.DeleteAllItems();
324         m_goals_tree.reset_handles();
325         for (i=0; i<m_num_goals; i++) {
326                 if ( (m_goals[i].type & GOAL_TYPE_MASK) != m_display_goal_types)
327                         continue;
328
329                 h = m_goals_tree.insert(m_goals[i].name);
330                 m_goals_tree.SetItemData(h, m_goals[i].formula);
331                 m_goals_tree.add_sub_tree(m_goals[i].formula, h);
332         }
333
334         cur_goal = -1;
335         update_cur_goal();
336 }
337
338 // Display goal types selection changed, so update the display
339 void CMissionGoalsDlg::OnSelchangeDisplayGoalTypesDrop() 
340 {
341         UpdateData(TRUE);
342         create_tree();
343 }
344
345 // New tree item selected.  Because goal info is displayed for the selected tree item,
346 // we need to update the display when this occurs.
347 void CMissionGoalsDlg::OnSelchangedGoalsTree(NMHDR* pNMHDR, LRESULT* pResult) 
348 {
349         int i, z;
350         NM_TREEVIEW* pNMTreeView = (NM_TREEVIEW*)pNMHDR;
351         HTREEITEM h, h2;
352
353         h = pNMTreeView->itemNew.hItem;
354         if (!h)
355                 return;
356
357         m_goals_tree.update_help(h);
358         while ((h2 = m_goals_tree.GetParentItem(h)) != 0)
359                 h = h2;
360
361         z = m_goals_tree.GetItemData(h);
362         for (i=0; i<m_num_goals; i++)
363                 if (m_goals[i].formula == z)
364                         break;
365
366         Assert(i < m_num_goals);
367         cur_goal = i;
368         update_cur_goal();
369         *pResult = 0;
370 }
371
372 // update display info to reflect the currently selected goal.
373 void CMissionGoalsDlg::update_cur_goal()
374 {
375         if (cur_goal < 0) {
376                 m_name = _T("");
377                 m_goal_desc = _T("");
378                 m_goal_type = -1;
379                 m_team = 0;
380                 UpdateData(FALSE);
381                 GetDlgItem(IDC_GOAL_TYPE_DROP) -> EnableWindow(FALSE);
382                 GetDlgItem(IDC_GOAL_NAME) -> EnableWindow(FALSE);
383                 GetDlgItem(IDC_GOAL_DESC) -> EnableWindow(FALSE);
384                 GetDlgItem(IDC_GOAL_INVALID)->EnableWindow(FALSE);
385                 GetDlgItem(IDC_GOAL_SCORE)->EnableWindow(FALSE);
386                 GetDlgItem(IDC_NO_MUSIC)->EnableWindow(FALSE);
387                 GetDlgItem(IDC_OBJ_TEAM)->EnableWindow(FALSE);
388                 return;
389         }
390
391         m_name = _T(m_goals[cur_goal].name);
392         m_goal_desc = _T(m_goals[cur_goal].message);
393         m_goal_type = m_goals[cur_goal].type & GOAL_TYPE_MASK;
394         if ( m_goals[cur_goal].type & INVALID_GOAL ){
395                 m_goal_invalid = 1;
396         } else {
397                 m_goal_invalid = 0;
398         }
399
400         if ( m_goals[cur_goal].flags & MGF_NO_MUSIC ){
401                 m_no_music = 1;
402         } else {
403                 m_no_music = 0;
404         }
405
406         m_goal_score = m_goals[cur_goal].score;
407
408         m_team = m_goals[cur_goal].team;
409
410         UpdateData(FALSE);
411         GetDlgItem(IDC_GOAL_TYPE_DROP) -> EnableWindow(TRUE);
412         GetDlgItem(IDC_GOAL_NAME) -> EnableWindow(TRUE);
413         GetDlgItem(IDC_GOAL_DESC) -> EnableWindow(TRUE);
414 //      GetDlgItem(IDC_GOAL_RATING) -> EnableWindow(TRUE);
415         GetDlgItem(IDC_GOAL_INVALID)->EnableWindow(TRUE);
416         GetDlgItem(IDC_GOAL_SCORE)->EnableWindow(TRUE);
417         GetDlgItem(IDC_NO_MUSIC)->EnableWindow(TRUE);
418         GetDlgItem(IDC_OBJ_TEAM)->EnableWindow(FALSE);
419         if ( The_mission.game_type & MISSION_TYPE_MULTI_TEAMS ){
420                 GetDlgItem(IDC_OBJ_TEAM)->EnableWindow(TRUE);
421         }
422 }
423
424 // handler for context menu (i.e. a right mouse button click).
425 void CMissionGoalsDlg::OnRclickGoalsTree(NMHDR* pNMHDR, LRESULT* pResult) 
426 {
427         m_goals_tree.right_clicked(MODE_GOALS);
428         *pResult = 0;
429 }
430
431 // goal tree item label editing is requested.  Determine if it should be allowed.
432 void CMissionGoalsDlg::OnBeginlabeleditGoalsTree(NMHDR* pNMHDR, LRESULT* pResult)
433 {
434         TV_DISPINFO* pTVDispInfo = (TV_DISPINFO*)pNMHDR;
435
436         if (m_goals_tree.edit_label(pTVDispInfo->item.hItem) == 1)      {
437                 *pResult = 0;
438                 modified = 1;
439         } else {
440                 *pResult = 1;
441         }
442 }
443
444 // Once we finish editing, we need to clean up, which we do here.
445 void CMissionGoalsDlg::OnEndlabeleditGoalsTree(NMHDR* pNMHDR, LRESULT* pResult) 
446 {
447         TV_DISPINFO* pTVDispInfo = (TV_DISPINFO*)pNMHDR;
448
449         *pResult = m_goals_tree.end_label_edit(pTVDispInfo->item.hItem, pTVDispInfo->item.pszText);
450 }
451
452 void CMissionGoalsDlg::OnOK()
453 {
454         HWND h;
455         CWnd *w;
456
457         w = GetFocus();
458         if (w) {
459                 h = w->m_hWnd;
460                 GetDlgItem(IDC_GOALS_TREE)->SetFocus();
461                 ::SetFocus(h);
462         }
463 }
464
465 int CMissionGoalsDlg::query_modified()
466 {
467         int i;
468
469         if (modified)
470                 return 1;
471
472         if (Num_goals != m_num_goals)
473                 return 1;
474
475         for (i=0; i<Num_goals; i++) {
476                 if (stricmp(Mission_goals[i].name, m_goals[i].name))
477                         return 1;
478                 if (stricmp(Mission_goals[i].message, m_goals[i].message))
479                         return 1;
480                 if (Mission_goals[i].type != m_goals[i].type)
481                         return 1;
482                 if ( Mission_goals[i].score != m_goals[i].score )
483                         return 1;
484                 if ( Mission_goals[i].team != m_goals[i].team )
485                         return 1;
486         }
487
488         return 0;
489 }
490
491 void CMissionGoalsDlg::OnOk()
492 {
493         char buf[256], names[2][MAX_GOALS][NAME_LENGTH];
494         int i, count;
495
496         for (i=0; i<Num_goals; i++)
497                 free_sexp2(Mission_goals[i].formula);
498
499         UpdateData(TRUE);
500         if (query_modified())
501                 set_modified();
502
503         count = 0;
504         for (i=0; i<Num_goals; i++)
505                 Mission_goals[i].satisfied = 0;  // use this as a processed flag
506         
507         // rename all sexp references to old events
508         for (i=0; i<m_num_goals; i++)
509                 if (m_sig[i] >= 0) {
510                         strcpy(names[0][count], Mission_goals[m_sig[i]].name);
511                         strcpy(names[1][count], m_goals[i].name);
512                         count++;
513                         Mission_goals[m_sig[i]].satisfied = 1;
514                 }
515
516         // invalidate all sexp references to deleted events.
517         for (i=0; i<Num_goals; i++)
518                 if (!Mission_goals[i].satisfied) {
519                         sprintf(buf, "<%s>", Mission_goals[i].name);
520                         strcpy(buf + NAME_LENGTH - 2, ">");  // force it to be not too long
521                         strcpy(names[0][count], Mission_goals[i].name);
522                         strcpy(names[1][count], buf);
523                         count++;
524                 }
525
526         Num_goals = m_num_goals;
527         for (i=0; i<Num_goals; i++) {
528                 Mission_goals[i] = m_goals[i];
529                 Mission_goals[i].formula = m_goals_tree.save_tree(Mission_goals[i].formula);
530                 if ( The_mission.game_type & MISSION_TYPE_MULTI_TEAMS ) {
531                         Assert( Mission_goals[i].team != -1 );
532                 }
533         }
534
535         // now update all sexp references
536         while (count--)
537                 update_sexp_references(names[0][count], names[1][count], OPF_GOAL_NAME);
538
539         theApp.record_window_data(&Mission_goals_wnd_data, this);
540         CDialog::OnOK();
541 }
542
543 void CMissionGoalsDlg::OnButtonNewGoal() 
544 {
545         int index;
546         HTREEITEM h;
547
548         Assert(m_num_goals < MAX_GOALS);
549         m_goals[m_num_goals].type = m_display_goal_types;                       // this also marks the goal as valid since bit not set
550         m_sig[m_num_goals] = -1;
551         strcpy(m_goals[m_num_goals].name, "Goal name");
552         strcpy(m_goals[m_num_goals].message, "Mission goal text");
553         h = m_goals_tree.insert(m_goals[m_num_goals].name);
554
555         m_goals_tree.item_index = -1;
556         m_goals_tree.add_operator("true", h);
557         m_goals[m_num_goals].score = 0;
558         index = m_goals[m_num_goals].formula = m_goals_tree.item_index;
559         m_goals_tree.SetItemData(h, index);
560
561         // team defaults to the first team.
562         m_goals[m_num_goals].team = 0;
563
564         m_num_goals++;
565
566         if (m_num_goals >= MAX_GOALS){
567                 GetDlgItem(IDC_BUTTON_NEW_GOAL)->EnableWindow(FALSE);
568         }
569
570         m_goals_tree.SelectItem(h);
571 }
572
573 int CMissionGoalsDlg::handler(int code, int node)
574 {
575         int goal;
576
577         switch (code) {
578         case ROOT_DELETED:
579                 for (goal=0; goal<m_num_goals; goal++){
580                         if (m_goals[goal].formula == node){
581                                 break;
582                         }
583                 }
584
585                 Assert(goal < m_num_goals);
586                 while (goal < m_num_goals - 1) {
587                         m_goals[goal] = m_goals[goal + 1];
588                         m_sig[goal] = m_sig[goal + 1];
589                         goal++;
590                 }
591                 m_num_goals--;
592                 GetDlgItem(IDC_BUTTON_NEW_GOAL)->EnableWindow(TRUE);
593                 return node;
594
595         default:
596                 Int3();
597         }
598
599         return -1;
600 }
601
602 void CMissionGoalsDlg::OnChangeGoalDesc() 
603 {
604         if (cur_goal < 0){
605                 return;
606         }
607
608         UpdateData(TRUE);
609         string_copy(m_goals[cur_goal].message, m_goal_desc, MAX_GOAL_TEXT);
610 }
611
612 void CMissionGoalsDlg::OnChangeGoalRating() 
613 {
614         if (cur_goal < 0){
615                 return;
616         }
617
618         UpdateData(TRUE);
619 }
620
621 void CMissionGoalsDlg::OnSelchangeGoalTypeDrop() 
622 {
623         HTREEITEM h, h2;
624         int otype;
625
626         if (cur_goal < 0){
627                 return;
628         }
629
630         UpdateData(TRUE);
631         UpdateData(TRUE);  // doesn't seem to update unless we do it twice..
632
633         // change the type being sure to keep the invalid bit if set
634         otype = m_goals[cur_goal].type;
635         m_goals[cur_goal].type = m_goal_type;
636         if ( otype & INVALID_GOAL ){
637                 m_goals[cur_goal].type |= INVALID_GOAL;
638         }
639
640         h = m_goals_tree.GetSelectedItem();
641         Assert(h);
642         while ((h2 = m_goals_tree.GetParentItem(h)) != 0){
643                 h = h2;
644         }
645
646         m_goals_tree.DeleteItem(h);
647         cur_goal = -1;
648         update_cur_goal();
649 }
650
651 void CMissionGoalsDlg::OnChangeGoalName() 
652 {
653         HTREEITEM h, h2;
654
655         if (cur_goal < 0){
656                 return;
657         }
658
659         UpdateData(TRUE);
660         h = m_goals_tree.GetSelectedItem();
661         if (!h){
662                 return;
663         }
664
665         while ((h2 = m_goals_tree.GetParentItem(h)) != 0){
666                 h = h2;
667         }
668
669         m_goals_tree.SetItemText(h, m_name);
670         string_copy(m_goals[cur_goal].name, m_name, NAME_LENGTH);
671 }
672
673 void CMissionGoalsDlg::OnCancel()
674 {
675         theApp.record_window_data(&Messages_wnd_data, this);
676         CDialog::OnCancel();
677 }
678
679 void CMissionGoalsDlg::OnClose() 
680 {
681         int z;
682
683         if (query_modified()) {
684                 z = MessageBox("Do you want to keep your changes?", "Close", MB_ICONQUESTION | MB_YESNOCANCEL);
685                 if (z == IDCANCEL)
686                         return;
687
688                 if (z == IDYES) {
689                         OnOk();
690                         return;
691                 }
692         }
693         
694         CDialog::OnClose();
695 }
696
697 void CMissionGoalsDlg::insert_handler(int old, int node)
698 {
699         int i;
700
701         for (i=0; i<m_num_goals; i++){
702                 if (m_goals[i].formula == old){
703                         break;
704                 }
705         }
706
707         Assert(i < m_num_goals);
708         m_goals[i].formula = node;
709         return;
710 }
711
712 void CMissionGoalsDlg::OnGoalInvalid() 
713 {
714         if ( cur_goal < 0 ){
715                 return;
716         }
717
718         m_goal_invalid = !m_goal_invalid;
719         m_goals[cur_goal].type ^= INVALID_GOAL;
720         UpdateData(TRUE);
721 }
722
723 void CMissionGoalsDlg::OnNoMusic() 
724 {
725         if (cur_goal < 0){
726                 return;
727         }
728
729         m_no_music = !m_no_music;
730         m_goals[cur_goal].flags ^= MGF_NO_MUSIC;
731         UpdateData(TRUE);
732 }
733
734 void CMissionGoalsDlg::swap_handler(int node1, int node2)
735 {
736         int index1, index2;
737         mission_goal m;
738
739         for (index1=0; index1<m_num_goals; index1++){
740                 if (m_goals[index1].formula == node1){
741                         break;
742                 }
743         }
744
745         Assert(index1 < m_num_goals);
746         for (index2=0; index2<m_num_goals; index2++){
747                 if (m_goals[index2].formula == node2){
748                         break;
749                 }
750         }
751
752         Assert(index2 < m_num_goals);
753         m = m_goals[index1];
754 //      m_goals[index1] = m_goals[index2];
755         while (index1 < index2) {
756                 m_goals[index1] = m_goals[index1 + 1];
757                 m_sig[index1] = m_sig[index1 + 1];
758                 index1++;
759         }
760
761         while (index1 > index2 + 1) {
762                 m_goals[index1] = m_goals[index1 - 1];
763                 m_sig[index1] = m_sig[index1 - 1];
764                 index1--;
765         }
766
767         m_goals[index1] = m;
768 }
769
770 void CMissionGoalsDlg::OnChangeGoalScore() 
771 {
772         if (cur_goal < 0){
773                 return;
774         }
775
776         UpdateData(TRUE);
777         m_goals[cur_goal].score = m_goal_score;
778 }
779
780
781 // code when the "team" selection in the combo box changes
782 void CMissionGoalsDlg::OnSelchangeTeam() 
783 {
784         if ( cur_goal < 0 ){
785                 return;
786         }
787
788         UpdateData(TRUE);
789         m_goals[cur_goal].team = m_team;
790 }