]> icculus.org git repositories - taylor/freespace2.git/blob - src/fred2/eventeditor.cpp
Initial revision
[taylor/freespace2.git] / src / fred2 / eventeditor.cpp
1 /*
2  * $Logfile: /Freespace2/code/fred2/EventEditor.cpp $
3  * $Revision$
4  * $Date$
5  * $Author$
6  *
7  * Event editor dialog box class and event tree class
8  *
9  * $Log$
10  * Revision 1.1  2002/05/03 03:28:08  root
11  * Initial revision
12  *
13  * 
14  * 12    9/13/99 8:03a Andsager
15  * Add command heads 3,4,5 as allowable head animations.
16  * 
17  * 11    9/09/99 5:07a Andsager
18  * Make sure TP2 is available in FRED head ani
19  * 
20  * 10    9/01/99 2:52p Andsager
21  * Add new heads to FRED and some debug code for playing heads
22  * 
23  * 9     8/28/99 7:29p Dave
24  * Fixed wingmen persona messaging. Make sure locked turrets don't count
25  * towards the # attacking a player.
26  * 
27  * 8     8/26/99 8:52p Dave
28  * Gave multiplayer TvT messaging a heavy dose of sanity. Cheat codes.
29  * 
30  * 7     5/04/99 5:21p Andsager
31  * 
32  * 6     2/17/99 2:11p Dave
33  * First full run of squad war. All freespace and tracker side stuff
34  * works.
35  * 
36  * 5     1/21/99 9:29a Andsager
37  * 
38  * 4     12/17/98 2:41p Andsager
39  * Changed input into sexp_tree::insert() to include bitmaps
40  * 
41  * 3     11/06/98 11:21a Johnson
42  * Put in handling code for wacky event editor Assert().
43  * 
44  * 2     10/07/98 6:28p Dave
45  * Initial checkin. Renamed all relevant stuff to be Fred2 instead of
46  * Fred. Globalized mission and campaign file extensions. Removed Silent
47  * Threat specific code.
48  * 
49  * 1     10/07/98 3:02p Dave
50  * 
51  * 1     10/07/98 3:00p Dave
52  * 
53  * 55    9/25/98 1:33p Andsager
54  * Add color to event editor (root and chain) indicating mission directive
55  * 
56  * 54    7/09/98 10:57a Hoffoss
57  * Fixed bug where the 'update stuff' button was reverting changes made to
58  * various message fields.
59  * 
60  * 53    5/15/98 5:51p Hoffoss
61  * Fixed escape key and cancel button bugs.
62  * 
63  * 52    5/12/98 11:44a Hoffoss
64  * Made escape key not close dialog (and lose changes made).
65  * 
66  * 51    4/30/98 9:53p Hoffoss
67  * Added "Head-VC" to ani list at Sandeep's request.
68  * 
69  * 50    4/30/98 8:23p John
70  * Fixed some bugs with Fred caused by my new cfile code.
71  * 
72  * 49    4/22/98 9:56a Sandeep
73  * 
74  * 48    4/20/98 4:40p Hoffoss
75  * Added a button to 4 editors to play the chosen wave file.
76  * 
77  * 47    4/03/98 5:20p Hoffoss
78  * Changed code so that changing a message's wave file will update the
79  * persona as well, if the wave file has the proper prefix.
80  * 
81  * 46    4/03/98 12:39p Hoffoss
82  * Changed starting directory for browse buttons in several editors.
83  * 
84  * 45    3/10/98 4:06p Hoffoss
85  * Fixed browse button blues.
86  * 
87  * 44    3/06/98 2:24p Hoffoss
88  * Fixed bug with going to reference with deleting a entity referenced by
89  * an sexp tree.
90  * 
91  * 43    2/16/98 6:25p Hoffoss
92  * Did major rework of the whole right_clicked() handler to simplify it
93  * all, break it down and make it more flexible.  Should be a lot easier
94  * to work with from now on.
95  * 
96  * 42    2/16/98 2:42p Hoffoss
97  * Added new code in preparation to simplify the sexp_tree monster.
98  * Checking in code now as a good foundation point that I can revert back
99  * to if needed.
100  * 
101  * 41    1/23/98 3:06p Hoffoss
102  * Added an explicit <none> item to the filename combo boxes at designers
103  * request.
104  * 
105  * 40    1/09/98 3:41p Hoffoss
106  * Fixed bug with event moving not updating fields properly.
107  * 
108  * 39    1/08/98 11:18a Hoffoss
109  * Fixed several bugs in new Event Editor.
110  * 
111  * 38    1/08/98 10:24a Johnson
112  * Fixed bug with null strings for filenames.
113  * 
114  * 37    1/07/98 5:58p Hoffoss
115  * Combined message editor into event editor.
116  * 
117  * 36    1/06/98 8:25p Hoffoss
118  * Added insert event functionality to event editor.
119  * 
120  * 35    1/06/98 3:31p Hoffoss
121  * Added image to indicate chained events, and added code to support it.
122  * 
123  * 34    10/20/97 5:13p Allender
124  * new subsystem sabotage/repair/set sexpressions.  Added new event/goal
125  * status checking sexpressions (not fully implemented yet).  Change
126  * campaign save files to save all events as well as goals
127  * 
128  * 33    10/10/97 6:21p Hoffoss
129  * Put in Fred support for training object list editing.
130  * 
131  * 32    10/10/97 2:53p Johnson
132  * Fixed bug with new items being selected before they are fully
133  * registered as added.
134  * 
135  * $NoKeywords: $
136  */
137
138 #include "stdafx.h"
139 #include <mmsystem.h>
140 #include "fred.h"
141 #include "freddoc.h"
142 #include "eventeditor.h"
143 #include "fredview.h"
144 #include "management.h"
145 #include "sexp_tree.h"
146 #include "missionmessage.h"
147 #include "cfile.h"
148
149 #ifdef _DEBUG
150 #define new DEBUG_NEW
151 #undef THIS_FILE
152 static char THIS_FILE[] = __FILE__;
153 #endif
154
155 event_editor *Event_editor_dlg = NULL; // global reference needed by event tree class
156
157 // determine the node number that would be allocated without actually allocating it yet.
158 int sexp_event_tree::get_new_node_position()
159 {
160         int i;
161
162         for (i=0; i<MAX_SEXP_TREE_SIZE; i++)
163                 if (nodes[i].type == SEXPT_UNUSED)
164                         return i;
165
166         return -1;
167 }
168
169 // construct tree nodes for an sexp, adding them to the list and returning first node
170 int sexp_event_tree::load_sub_tree(int index)
171 {
172         int cur;
173
174         if (index < 0) {
175                 cur = allocate_node(-1);
176                 set_node(cur, SEXPT_OPERATOR, "do-nothing");  // setup a default tree if none
177                 return cur;
178         }
179
180         // assumption: first token is an operator.  I require this because it would cause problems
181         // with child/parent relations otherwise, and it should be this way anyway, since the
182         // return type of the whole sexp is boolean, and only operators can satisfy this.
183         Assert(Sexp_nodes[index].subtype == SEXP_ATOM_OPERATOR);
184         cur = get_new_node_position();
185         load_branch(index, -1);
186         return cur;
187 }
188
189 /////////////////////////////////////////////////////////////////////////////
190 // event_editor dialog
191
192 event_editor::event_editor(CWnd* pParent /*=NULL*/)
193         : CDialog(event_editor::IDD, pParent)
194 {
195         //{{AFX_DATA_INIT(event_editor)
196         m_repeat_count = 0;
197         m_interval = 0;
198         m_event_score = 0;
199         m_chain_delay = 0;
200         m_chained = FALSE;
201         m_obj_text = _T("");
202         m_obj_key_text = _T("");
203         m_avi_filename = _T("");
204         m_message_name = _T("");
205         m_message_text = _T("");
206         m_persona = -1;
207         m_wave_filename = _T("");
208         m_cur_msg = -1;
209         m_team = -1;
210         m_message_team = -1;
211         m_last_message_node = -1;
212         //}}AFX_DATA_INIT
213         m_event_tree.m_mode = MODE_EVENTS;
214         m_num_events = 0;
215         m_event_tree.link_modified(&modified);
216         modified = 0;
217         select_sexp_node = -1;
218 }
219
220 void event_editor::DoDataExchange(CDataExchange* pDX)
221 {
222         CDialog::DoDataExchange(pDX);
223         //{{AFX_DATA_MAP(event_editor)
224         DDX_Control(pDX, IDC_EVENT_TREE, m_event_tree);
225         DDX_Text(pDX, IDC_REPEAT_COUNT, m_repeat_count);
226         DDX_Text(pDX, IDC_INTERVAL_TIME, m_interval);
227         DDX_Text(pDX, IDC_EVENT_SCORE, m_event_score);
228         DDX_Text(pDX, IDC_CHAIN_DELAY, m_chain_delay);
229         DDX_Check(pDX, IDC_CHAINED, m_chained);
230         DDX_Text(pDX, IDC_OBJ_TEXT, m_obj_text);
231         DDX_Text(pDX, IDC_OBJ_KEY_TEXT, m_obj_key_text);
232         DDX_CBString(pDX, IDC_AVI_FILENAME, m_avi_filename);
233         DDX_Text(pDX, IDC_MESSAGE_NAME, m_message_name);
234         DDX_Text(pDX, IDC_MESSAGE_TEXT, m_message_text);
235         DDX_CBIndex(pDX, IDC_PERSONA_NAME, m_persona);
236         DDX_CBString(pDX, IDC_WAVE_FILENAME, m_wave_filename);
237         DDX_LBIndex(pDX, IDC_MESSAGE_LIST, m_cur_msg);
238
239         // m_team == -1 maps to 2
240         if(m_team == -1){
241                 m_team = 2;
242         }
243         DDX_CBIndex(pDX, IDC_EVENT_TEAM, m_team);
244
245         // m_message_team == -1 maps to 2
246         if(m_message_team == -1){
247                 m_message_team = 2;
248         }
249         DDX_CBIndex(pDX, IDC_MESSAGE_TEAM, m_message_team);
250         //}}AFX_DATA_MAP
251
252         DDV_MaxChars(pDX, m_obj_text, NAME_LENGTH - 1);
253         DDV_MaxChars(pDX, m_obj_key_text, NAME_LENGTH - 1);
254         DDV_MaxChars(pDX, m_message_name, NAME_LENGTH - 1);
255         DDV_MaxChars(pDX, m_message_text, MESSAGE_LENGTH - 1);
256         DDV_MaxChars(pDX, m_avi_filename, MAX_FILENAME_LEN - 1);
257         DDV_MaxChars(pDX, m_wave_filename, MAX_FILENAME_LEN - 1);
258 }
259
260 BEGIN_MESSAGE_MAP(event_editor, CDialog)
261         //{{AFX_MSG_MAP(event_editor)
262         ON_NOTIFY(NM_RCLICK, IDC_EVENT_TREE, OnRclickEventTree)
263         ON_NOTIFY(TVN_BEGINLABELEDIT, IDC_EVENT_TREE, OnBeginlabeleditEventTree)
264         ON_NOTIFY(TVN_ENDLABELEDIT, IDC_EVENT_TREE, OnEndlabeleditEventTree)
265         ON_BN_CLICKED(IDC_BUTTON_NEW_EVENT, OnButtonNewEvent)
266         ON_BN_CLICKED(IDC_DELETE, OnDelete)
267         ON_BN_CLICKED(ID_OK, OnOk)
268         ON_WM_CLOSE()
269         ON_NOTIFY(TVN_SELCHANGED, IDC_EVENT_TREE, OnSelchangedEventTree)
270         ON_EN_UPDATE(IDC_REPEAT_COUNT, OnUpdateRepeatCount)
271         ON_BN_CLICKED(IDC_CHAINED, OnChained)
272         ON_BN_CLICKED(IDC_INSERT, OnInsert)
273         ON_LBN_SELCHANGE(IDC_MESSAGE_LIST, OnSelchangeMessageList)
274         ON_BN_CLICKED(IDC_NEW_MSG, OnNewMsg)
275         ON_BN_CLICKED(IDC_DELETE_MSG, OnDeleteMsg)
276         ON_BN_CLICKED(IDC_BROWSE_AVI, OnBrowseAvi)
277         ON_BN_CLICKED(IDC_BROWSE_WAVE, OnBrowseWave)
278         ON_CBN_SELCHANGE(IDC_WAVE_FILENAME, OnSelchangeWaveFilename)
279         ON_BN_CLICKED(IDC_PLAY, OnPlay)
280         ON_BN_CLICKED(IDC_UPDATE, OnUpdate)
281         ON_BN_CLICKED(ID_CANCEL, On_Cancel)
282         ON_CBN_SELCHANGE(IDC_EVENT_TEAM, OnSelchangeTeam)
283         ON_CBN_SELCHANGE(IDC_MESSAGE_TEAM, OnSelchangeMessageTeam)
284         ON_LBN_DBLCLK(IDC_MESSAGE_LIST, OnDblclkMessageList)
285         //}}AFX_MSG_MAP
286 END_MESSAGE_MAP()
287
288 /////////////////////////////////////////////////////////////////////////////
289 // event_editor message handlers
290
291 void maybe_add_head(CComboBox *box, char* name)
292 {
293         if (box->FindStringExact(-1, name) == CB_ERR) {
294                 box->AddString(name);
295         }
296 }
297
298 BOOL event_editor::OnInitDialog() 
299 {
300         int i, adjust = 0;
301         BOOL r = TRUE;
302         CListBox *list;
303         CComboBox *box;
304
305         CDialog::OnInitDialog();  // let the base class do the default work
306         m_play_bm.LoadBitmap(IDB_PLAY);
307         ((CButton *) GetDlgItem(IDC_PLAY)) -> SetBitmap(m_play_bm);
308
309         if (!Show_sexp_help)
310                 adjust = -SEXP_HELP_BOX_SIZE;
311
312         theApp.init_window(&Events_wnd_data, this, adjust);
313         m_event_tree.setup((CEdit *) GetDlgItem(IDC_HELP_BOX));
314         load_tree();
315         create_tree();
316         if (m_num_events >= MAX_MISSION_EVENTS){
317                 GetDlgItem(IDC_BUTTON_NEW_EVENT)->EnableWindow(FALSE);
318         }
319
320         update_cur_event();
321         i = m_event_tree.select_sexp_node;
322         if (i != -1) {
323                 GetDlgItem(IDC_EVENT_TREE) -> SetFocus();
324                 m_event_tree.hilite_item(i);
325                 r = FALSE;
326         }
327
328         m_num_messages = Num_messages - Num_builtin_messages;
329         for (i=0; i<m_num_messages; i++) {
330                 m_messages[i] = Messages[i + Num_builtin_messages];
331                 if (m_messages[i].avi_info.name){
332                         m_messages[i].avi_info.name = strdup(m_messages[i].avi_info.name);
333                 }
334                 if (m_messages[i].wave_info.name){
335                         m_messages[i].wave_info.name = strdup(m_messages[i].wave_info.name);
336                 }
337                 m_msg_sig[i] = i + Num_builtin_messages;
338         }
339
340         ((CEdit *) GetDlgItem(IDC_MESSAGE_NAME))->LimitText(NAME_LENGTH - 1);
341         ((CEdit *) GetDlgItem(IDC_MESSAGE_TEXT))->LimitText(MESSAGE_LENGTH - 1);
342         ((CComboBox *) GetDlgItem(IDC_AVI_FILENAME))->LimitText(MAX_FILENAME_LEN - 1);
343         ((CComboBox *) GetDlgItem(IDC_WAVE_FILENAME))->LimitText(MAX_FILENAME_LEN - 1);
344
345         list = (CListBox *) GetDlgItem(IDC_MESSAGE_LIST);
346         list->ResetContent();
347         for (i=0; i<m_num_messages; i++) {
348                 list->AddString(m_messages[i].name);
349         }
350
351         box = (CComboBox *) GetDlgItem(IDC_AVI_FILENAME);
352         box->ResetContent();
353         box->AddString("<None>");
354         for (i=0; i<Num_messages; i++) {
355                 if (Messages[i].avi_info.name) {
356                         maybe_add_head(box, Messages[i].avi_info.name);
357                 }
358         }
359
360         // add new heads, if not already in
361         maybe_add_head(box, "Head-TP2");
362         maybe_add_head(box, "Head-VC2");
363         maybe_add_head(box, "Head-TP4");
364         maybe_add_head(box, "Head-TP5");
365         maybe_add_head(box, "Head-TP6");
366         maybe_add_head(box, "Head-TP7");
367         maybe_add_head(box, "Head-TP8");
368         maybe_add_head(box, "Head-VP2");
369         maybe_add_head(box, "Head-VP2");
370         maybe_add_head(box, "Head-CM2");
371         maybe_add_head(box, "Head-CM3");
372         maybe_add_head(box, "Head-CM4");
373         maybe_add_head(box, "Head-CM5");
374         maybe_add_head(box, "Head-BSH");
375
376 /*
377         box->AddString("Head-VC");  // force it in, since Sandeep wants it and it's not used in built-in messages
378         box->AddString("Head-VC2");
379
380         // add terran pilot heads
381         box->AddString("Head-TP4");
382         box->AddString("Head-TP5");
383         box->AddString("Head-TP6");
384         box->AddString("Head-TP7");
385         box->AddString("Head-TP8");
386
387         // add vasudan pilot heads
388         box->AddString("Head-VP2");
389
390         // BSH and CM2
391         box->AddString("Head-CM2");
392         box->AddString("Head-BSH");
393         */
394
395         box = (CComboBox *) GetDlgItem(IDC_WAVE_FILENAME);
396         box->ResetContent();
397         box->AddString("<None>");
398         for (i=0; i<Num_messages; i++){
399                 if (Messages[i].wave_info.name){
400                         if (box->FindStringExact(i, Messages[i].wave_info.name) == CB_ERR){
401                                 box->AddString(Messages[i].wave_info.name);
402                         }
403                 }
404         }
405
406         // add the persona names into the combo box
407         box = (CComboBox *) GetDlgItem(IDC_PERSONA_NAME);
408         box->ResetContent();
409         box->AddString("<None>");
410         for (i = 0; i < Num_personas; i++ ){
411                 box->AddString( Personas[i].name );
412         }
413
414         // set the first message to be the first non-builtin message (if it exists)
415         if ( Num_messages > Num_builtin_messages ){
416                 m_cur_msg = 0;
417         } else {
418                 m_cur_msg = -1;
419         }
420
421         if (Num_messages >= MAX_MISSION_MESSAGES){
422                 GetDlgItem(IDC_NEW_MSG)->EnableWindow(FALSE);
423         }
424
425         update_cur_message();
426         return r;
427 }
428
429 void event_editor::load_tree()
430 {
431         int i;
432
433         m_event_tree.select_sexp_node = select_sexp_node;
434         select_sexp_node = -1;
435
436         m_event_tree.clear_tree();
437         m_num_events = Num_mission_events;
438         for (i=0; i<m_num_events; i++) {
439                 m_events[i] = Mission_events[i];
440                 if (Mission_events[i].objective_text){
441                         m_events[i].objective_text = strdup(Mission_events[i].objective_text);
442                 } else {
443                         m_events[i].objective_text = NULL;
444                 }
445
446                 if (Mission_events[i].objective_key_text){
447                         m_events[i].objective_key_text = strdup(Mission_events[i].objective_key_text);
448                 } else {
449                         m_events[i].objective_key_text = NULL;
450                 }
451                 
452                 m_sig[i] = i;
453                 if (!(*m_events[i].name)){
454                         strcpy(m_events[i].name, "<Unnamed>");
455                 }
456
457                 m_events[i].formula = m_event_tree.load_sub_tree(Mission_events[i].formula);
458
459                 // we must check for the case of the repeat count being 0.  This would happen if the repeat
460                 // count is not specified in a mission
461                 if ( m_events[i].repeat_count <= 0 ){
462                         m_events[i].repeat_count = 1;
463                 }
464         }
465
466         m_event_tree.post_load();
467         cur_event = -1;
468 }
469
470 void event_editor::create_tree()
471 {
472         int i;
473         HTREEITEM h;
474
475         m_event_tree.DeleteAllItems();
476         for (i=0; i<m_num_events; i++) {
477
478                 // set the proper bitmap
479                 int image;
480                 if (m_events[i].chain_delay >= 0) {
481                         image = BITMAP_CHAIN;
482                         if (m_events[i].objective_text) {
483                                 image = BITMAP_CHAIN_DIRECTIVE;
484                         }
485                 } else {
486                         image = BITMAP_ROOT;
487                         if (m_events[i].objective_text) {
488                                 image = BITMAP_ROOT_DIRECTIVE;
489                         }
490                 }
491
492                 h = m_event_tree.insert(m_events[i].name, image, image);
493
494                 m_event_tree.SetItemData(h, m_events[i].formula);
495                 m_event_tree.add_sub_tree(m_events[i].formula, h);
496         }
497
498         cur_event = -1;
499 }
500
501 void event_editor::OnRclickEventTree(NMHDR* pNMHDR, LRESULT* pResult) 
502 {
503         save();
504         m_event_tree.right_clicked(MODE_EVENTS);
505         *pResult = 0;
506 }
507
508 void event_editor::OnBeginlabeleditEventTree(NMHDR* pNMHDR, LRESULT* pResult) 
509 {
510         TV_DISPINFO* pTVDispInfo = (TV_DISPINFO*)pNMHDR;
511         CEdit *edit;
512
513         if (m_event_tree.edit_label(pTVDispInfo->item.hItem) == 1)      {
514                 *pResult = 0;
515                 modified = 1;
516                 edit = m_event_tree.GetEditControl();
517                 Assert(edit);
518                 edit->SetLimitText(NAME_LENGTH - 1);
519
520         } else
521                 *pResult = 1;
522 }
523
524 void event_editor::OnEndlabeleditEventTree(NMHDR* pNMHDR, LRESULT* pResult) 
525 {
526         TV_DISPINFO* pTVDispInfo = (TV_DISPINFO*)pNMHDR;
527
528         *pResult = m_event_tree.end_label_edit(pTVDispInfo->item.hItem, pTVDispInfo->item.pszText);
529 }
530
531 // This is needed as a HACK around default MFC standard
532 // It is not required, but overrides default MFC and links no errors without.
533 void event_editor::OnOK()
534 {
535         HWND h;
536         CWnd *w;
537
538         save();
539         w = GetFocus();
540         if (w) {
541                 h = w->m_hWnd;
542                 GetDlgItem(IDC_EVENT_TREE)->SetFocus();
543                 ::SetFocus(h);
544         }
545 }
546
547 int event_editor::query_modified()
548 {
549         int i;
550         char *ptr, buf[MESSAGE_LENGTH];
551
552         UpdateData(TRUE);
553         if (modified)
554                 return 1;
555
556         if (Num_mission_events != m_num_events)
557                 return 1;
558
559         for (i=0; i<m_num_events; i++) {
560                 if (stricmp(m_events[i].name, Mission_events[i].name))
561                         return 1;
562                 if (m_events[i].repeat_count != Mission_events[i].repeat_count)
563                         return 1;
564                 if (m_events[i].interval != Mission_events[i].interval)
565                         return 1;
566                 if (m_events[i].score != Mission_events[i].score)
567                         return 1;
568                 if (m_events[i].chain_delay != Mission_events[i].chain_delay)
569                         return 1;
570                 if (advanced_stricmp(m_events[i].objective_text, Mission_events[i].objective_text))
571                         return 1;
572                 if (advanced_stricmp(m_events[i].objective_key_text, Mission_events[i].objective_key_text))
573                         return 1;
574         }
575
576         if (m_cur_msg < 0)
577                 return 0;
578
579         if (m_num_messages != Num_messages)
580                 return 1;
581
582         ptr = (char *) (LPCTSTR) m_message_name;
583         for (i=0; i<Num_builtin_messages; i++)
584                 if (!stricmp(ptr, Messages[i].name))
585                         return 1;
586
587         for (i=0; i<m_num_messages; i++) {
588                 if (m_msg_sig[i] < 0)
589                         return 1;
590
591                 if ((i != m_cur_msg) && (!stricmp(ptr, m_messages[m_cur_msg].name)))
592                         return 1;
593         }
594
595         if (stricmp(ptr, m_messages[m_cur_msg].name))
596                 return 1;  // name is different and allowed to update
597
598         string_copy(buf, m_message_text, MESSAGE_LENGTH - 1);
599         if (stricmp(buf, m_messages[m_cur_msg].message))
600                 return 1;
601
602         ptr = (char *) (LPCTSTR) m_avi_filename;
603         if (advanced_stricmp(ptr, m_messages[m_cur_msg].avi_info.name))
604                 return 1;
605
606         ptr = (char *) (LPCTSTR) m_wave_filename;
607         if (advanced_stricmp(ptr, m_messages[m_cur_msg].wave_info.name))
608                 return 1;
609
610         // check to see if persona changed.  use -1 since we stuck a "None" for persona
611         // at the beginning of the list.
612         if ( (m_persona - 1 ) != m_messages[m_cur_msg].persona_index )
613                 return 1;
614
615         return 0;
616 }
617
618 void event_editor::OnOk()
619 {
620         char buf[256], names[2][MAX_MISSION_EVENTS][NAME_LENGTH];
621         int i, count;
622
623         save();
624         if (query_modified())
625                 set_modified();
626
627         for (i=0; i<Num_mission_events; i++) {
628                 free_sexp2(Mission_events[i].formula);
629                 if (Mission_events[i].objective_text)
630                         free(Mission_events[i].objective_text);
631                 if (Mission_events[i].objective_key_text)
632                         free(Mission_events[i].objective_key_text);
633         }
634
635         count = 0;
636         for (i=0; i<Num_mission_events; i++)
637                 Mission_events[i].result = 0;  // use this as a processed flag
638         
639         // rename all sexp references to old events
640         for (i=0; i<m_num_events; i++)
641                 if (m_sig[i] >= 0) {
642                         strcpy(names[0][count], Mission_events[m_sig[i]].name);
643                         strcpy(names[1][count], m_events[i].name);
644                         count++;
645                         Mission_events[m_sig[i]].result = 1;
646                 }
647
648         // invalidate all sexp references to deleted events.
649         for (i=0; i<Num_mission_events; i++)
650                 if (!Mission_events[i].result) {
651                         sprintf(buf, "<%s>", Mission_events[i].name);
652                         strcpy(buf + NAME_LENGTH - 2, ">");  // force it to be not too long
653                         strcpy(names[0][count], Mission_events[i].name);
654                         strcpy(names[1][count], buf);
655                         count++;
656                 }
657
658         Num_mission_events = m_num_events;
659         for (i=0; i<m_num_events; i++) {
660                 Mission_events[i] = m_events[i];
661                 Mission_events[i].formula = m_event_tree.save_tree(m_events[i].formula);
662                 Mission_events[i].objective_text = m_events[i].objective_text;
663                 Mission_events[i].objective_key_text = m_events[i].objective_key_text;
664         }
665
666         // now update all sexp references
667         while (count--)
668                 update_sexp_references(names[0][count], names[1][count], OPF_EVENT_NAME);
669
670         for (i=Num_builtin_messages; i<Num_messages; i++) {
671                 if (Messages[i].avi_info.name)
672                         free(Messages[i].avi_info.name);
673
674                 if (Messages[i].wave_info.name)
675                         free(Messages[i].wave_info.name);
676         }
677
678         Num_messages = m_num_messages + Num_builtin_messages;
679         for (i=0; i<m_num_messages; i++)
680                 Messages[i + Num_builtin_messages] = m_messages[i];
681
682         theApp.record_window_data(&Events_wnd_data, this);
683         delete Event_editor_dlg;
684         Event_editor_dlg = NULL;
685 }
686
687 // load controls with structure data
688 void event_editor::update_cur_message()
689 {
690         int enable = TRUE;
691
692         if (m_cur_msg < 0) {
693                 enable = FALSE;
694                 m_message_name = _T("");
695                 m_message_text = _T("");
696                 m_avi_filename = _T("");
697                 m_wave_filename = _T("");
698                 m_persona = 0;
699                 m_message_team = -1;
700         } else {
701                 m_message_name = m_messages[m_cur_msg].name;
702                 m_message_text = m_messages[m_cur_msg].message;
703                 if (m_messages[m_cur_msg].avi_info.name){
704                         m_avi_filename = _T(m_messages[m_cur_msg].avi_info.name);
705                 } else {
706                         m_avi_filename = _T("<None>");
707                 }
708
709                 if (m_messages[m_cur_msg].wave_info.name){
710                         m_wave_filename = _T(m_messages[m_cur_msg].wave_info.name);
711                 } else {
712                         m_wave_filename = _T("<None>");
713                 }
714
715                 // add persona id
716                 if ( m_messages[m_cur_msg].persona_index != -1 ){
717                         m_persona = m_messages[m_cur_msg].persona_index + 1;  // add one for the "none" at the beginning of the list
718                 } else {
719                         m_persona = 0;
720                 }
721
722                 if(m_messages[m_cur_msg].multi_team >= 2){
723                         m_message_team = -1;
724                         m_messages[m_cur_msg].multi_team = -1;
725                 } else {
726                         m_message_team = m_messages[m_cur_msg].multi_team;
727                 }
728 /*
729                 m_event_num = find_event();
730                 if (m_event_num < 0) {
731                         node = -1;
732                         m_sender = m_priority = 0;
733
734                 } else
735                         node = CADR(Mission_events[m_event_num].formula);
736 */      }
737
738         GetDlgItem(IDC_MESSAGE_NAME)->EnableWindow(enable);
739         GetDlgItem(IDC_MESSAGE_TEXT)->EnableWindow(enable);
740         GetDlgItem(IDC_AVI_FILENAME)->EnableWindow(enable);
741         GetDlgItem(IDC_BROWSE_AVI)->EnableWindow(enable);
742         GetDlgItem(IDC_BROWSE_WAVE)->EnableWindow(enable);
743         GetDlgItem(IDC_WAVE_FILENAME)->EnableWindow(enable);
744         GetDlgItem(IDC_DELETE_MSG)->EnableWindow(enable);
745         GetDlgItem(IDC_PERSONA_NAME)->EnableWindow(enable);
746         GetDlgItem(IDC_MESSAGE_TEAM)->EnableWindow(enable);
747         UpdateData(FALSE);
748 }
749
750 int event_editor::handler(int code, int node, char *str)
751 {
752         int i;
753
754         switch (code) {
755                 case ROOT_DELETED:
756                         for (i=0; i<m_num_events; i++)
757                                 if (m_events[i].formula == node)
758                                         break;
759
760                         Assert(i < m_num_events);
761                         while (i < m_num_events - 1) {
762                                 m_events[i] = m_events[i + 1];
763                                 m_sig[i] = m_sig[i + 1];
764                                 i++;
765                         }
766
767                         m_num_events--;
768                         GetDlgItem(IDC_BUTTON_NEW_EVENT)->EnableWindow(TRUE);
769                         return node;
770
771                 case ROOT_RENAMED:
772                         for (i=0; i<m_num_events; i++)
773                                 if (m_events[i].formula == node)
774                                         break;
775
776                         Assert(i < m_num_events);
777                         Assert(strlen(str) < NAME_LENGTH);
778                         strcpy(m_events[i].name, str);
779                         return node;
780
781                 default:
782                         Int3();
783         }
784
785         return -1;
786 }
787
788 void event_editor::OnButtonNewEvent() 
789 {
790         if (m_num_events == MAX_MISSION_EVENTS) {
791                 MessageBox("You have reached the limit on mission events.\n"
792                         "Can't add any more.");
793                 return;
794         }
795
796         reset_event(m_num_events++, TVI_LAST);
797 }
798
799 void event_editor::OnInsert() 
800 {
801         int i;
802
803         if (m_num_events == MAX_MISSION_EVENTS) {
804                 MessageBox("You have reached the limit on mission events.\n"
805                         "Can't add any more.");
806                 return;
807         }
808
809         for (i=m_num_events; i>cur_event; i--) {
810                 m_events[i] = m_events[i - 1];
811                 m_sig[i] = m_sig[i - 1];
812         }
813
814         if (cur_event){
815                 reset_event(cur_event, get_event_handle(cur_event - 1));
816         } else {
817                 reset_event(cur_event, TVI_FIRST);
818         }
819
820         m_num_events++;
821 }
822
823 HTREEITEM event_editor::get_event_handle(int num)
824 {
825         HTREEITEM h;
826
827         h = m_event_tree.GetRootItem();
828         while (h) {
829                 if ((int) m_event_tree.GetItemData(h) == m_events[num].formula){
830                         return h;
831                 }
832
833                 h = m_event_tree.GetNextSiblingItem(h);
834         }
835
836         return 0;
837 }
838
839 void event_editor::reset_event(int num, HTREEITEM after)
840 {
841         int index;
842         HTREEITEM h;
843
844         strcpy(m_events[num].name, "Event name");
845         h = m_event_tree.insert(m_events[num].name, BITMAP_ROOT, BITMAP_ROOT, TVI_ROOT, after);
846
847         m_events[num].repeat_count = 1;
848         m_events[num].interval = 1;
849         m_events[num].score = 0;
850         m_events[num].chain_delay = -1;
851         m_events[num].objective_text = NULL;
852         m_events[num].objective_key_text = NULL;
853         m_sig[num] = -1;
854
855         m_event_tree.item_index = -1;
856         m_event_tree.add_operator("when", h);
857         index = m_events[num].formula = m_event_tree.item_index;
858         m_event_tree.SetItemData(h, index);
859         m_event_tree.add_operator("true");
860         m_event_tree.item_index = index;
861         m_event_tree.add_operator("do-nothing");
862
863         m_event_tree.SelectItem(h);
864 //      GetDlgItem(IDC_CHAIN_DELAY) -> EnableWindow(FALSE);
865         if (num >= MAX_MISSION_EVENTS){
866                 GetDlgItem(IDC_BUTTON_NEW_EVENT)->EnableWindow(FALSE);
867         }
868 }
869
870 void event_editor::OnDelete() 
871 {
872         HTREEITEM h;
873
874         // call update_cur_event to clean up local class variables so that we can correctly
875         // set up the newly selected item.
876         cur_event = -1;
877         update_cur_event();
878
879         h = m_event_tree.GetSelectedItem();
880         if (h) {
881                 while (m_event_tree.GetParentItem(h))
882                         h = m_event_tree.GetParentItem(h);
883
884                 m_event_tree.setup_selected(h);
885                 m_event_tree.OnCommand(ID_DELETE, 0);
886         }
887 }
888
889 // this is called when you hit the escape key..
890 void event_editor::OnCancel()
891 {
892 }
893
894 // this is called the clicking the ID_CANCEL button
895 void event_editor::On_Cancel()
896 {
897         theApp.record_window_data(&Events_wnd_data, this);
898         delete Event_editor_dlg;
899         Event_editor_dlg = NULL;
900 }
901
902 void event_editor::OnClose() 
903 {
904         int z;
905
906         if (query_modified()) {
907                 z = MessageBox("Do you want to keep your changes?", "Close", MB_ICONQUESTION | MB_YESNOCANCEL);
908                 if (z == IDCANCEL){
909                         return;
910                 }
911
912                 if (z == IDYES) {
913                         OnOk();
914                         return;
915                 }
916         }
917         
918         theApp.record_window_data(&Events_wnd_data, this);
919         delete Event_editor_dlg;
920         Event_editor_dlg = NULL;
921 }
922
923 void event_editor::insert_handler(int old, int node)
924 {
925         int i;
926
927         for (i=0; i<m_num_events; i++){
928                 if (m_events[i].formula == old){
929                         break;
930                 }
931         }
932
933         Assert(i < m_num_events);
934         m_events[i].formula = node;
935         return;
936 }
937
938 void event_editor::save()
939 {
940         int m = m_cur_msg;
941
942         save_event(cur_event);
943         save_message(m);
944 }
945
946 void event_editor::save_event(int e)
947 {
948         if (e < 0) {
949                 return;
950         }
951
952         UpdateData(TRUE);
953         m_events[e].repeat_count = m_repeat_count;
954         m_events[e].interval = m_interval;
955         m_events[e].score = m_event_score;
956
957         // handle chain
958         if (m_chained) {
959                 m_events[e].chain_delay = m_chain_delay;
960         } else {
961                 m_events[e].chain_delay = -1;
962         }
963
964         // handle objective text
965         if (m_events[e].objective_text) {
966                 free(m_events[e].objective_text);
967         }
968
969         if (m_obj_text.IsEmpty()) {
970                 m_events[e].objective_text = NULL;
971         } else {
972                 m_events[e].objective_text = strdup(m_obj_text);
973         }
974
975         // handle objective key text
976         if (m_events[e].objective_key_text) {
977                 free(m_events[e].objective_key_text);
978         }
979
980         if (m_obj_key_text.IsEmpty()) {
981                 m_events[e].objective_key_text = NULL;
982         } else {
983                 m_events[e].objective_key_text = strdup(m_obj_key_text);
984         }
985
986         // update bitmap
987         int bitmap;
988         if (m_chained) {
989                 if (m_obj_text.IsEmpty()) {
990                         bitmap = BITMAP_CHAIN;
991                 } else {
992                         bitmap = BITMAP_CHAIN_DIRECTIVE;
993                 }
994         } else {
995                 // not chained
996                 if (m_obj_text.IsEmpty()) {
997                         bitmap = BITMAP_ROOT;
998                 } else {
999                         bitmap = BITMAP_ROOT_DIRECTIVE;
1000                 }
1001         }
1002
1003         // Search for item to update
1004         HTREEITEM h = m_event_tree.GetRootItem();
1005         while (h) {
1006                 if ((int) m_event_tree.GetItemData(h) == m_events[e].formula) {
1007                         m_event_tree.SetItemImage(h, bitmap, bitmap);
1008                         return;
1009                 }
1010
1011                 h = m_event_tree.GetNextSiblingItem(h);
1012         }
1013
1014 }
1015
1016 // this function deals with the left click on an event in the tree view.  We get into this
1017 // function so that we may update the other data on the screen (i.e repeat count and interval
1018 // count)
1019 void event_editor::OnSelchangedEventTree(NMHDR* pNMHDR, LRESULT* pResult) 
1020 {
1021         int i, z;
1022         NM_TREEVIEW* pNMTreeView = (NM_TREEVIEW*)pNMHDR;
1023         HTREEITEM h, h2;
1024
1025         // before we do anything, we must check and save off any data from the current event (i.e.
1026         // the repeat count and interval count)
1027         save();
1028         h = pNMTreeView->itemNew.hItem;
1029         if (!h){
1030                 return;
1031         }
1032
1033         m_event_tree.update_help(h);
1034         while ((h2 = m_event_tree.GetParentItem(h))>0){
1035                 h = h2;
1036         }
1037
1038         z = m_event_tree.GetItemData(h);
1039         for (i=0; i<m_num_events; i++){
1040                 if (m_events[i].formula == z){
1041                         break;
1042                 }
1043         }
1044
1045         Assert(i < m_num_events);
1046         cur_event = i;
1047         update_cur_event();
1048         
1049         *pResult = 0;
1050 }
1051
1052 void event_editor::update_cur_event()
1053 {
1054         if (cur_event < 0) {
1055                 m_repeat_count = 1;
1056                 m_interval = 1;
1057                 m_chain_delay = 0;
1058                 m_team = -1;
1059                 m_obj_text.Empty();
1060                 m_obj_key_text.Empty();
1061                 GetDlgItem(IDC_INTERVAL_TIME) -> EnableWindow(FALSE);
1062                 GetDlgItem(IDC_REPEAT_COUNT) -> EnableWindow(FALSE);
1063                 GetDlgItem(IDC_EVENT_SCORE) -> EnableWindow(FALSE);
1064                 GetDlgItem(IDC_CHAINED) -> EnableWindow(FALSE);
1065                 GetDlgItem(IDC_CHAIN_DELAY) -> EnableWindow(FALSE);
1066                 GetDlgItem(IDC_OBJ_TEXT) -> EnableWindow(FALSE);
1067                 GetDlgItem(IDC_OBJ_KEY_TEXT) -> EnableWindow(FALSE);
1068                 GetDlgItem(IDC_EVENT_TEAM)->EnableWindow(FALSE);
1069                 return;
1070         }
1071
1072         m_team = m_events[cur_event].team;
1073
1074         m_repeat_count = m_events[cur_event].repeat_count;
1075         m_interval = m_events[cur_event].interval;
1076         m_event_score = m_events[cur_event].score;
1077         if (m_events[cur_event].chain_delay >= 0) {
1078                 m_chained = TRUE;
1079                 m_chain_delay = m_events[cur_event].chain_delay;
1080                 GetDlgItem(IDC_CHAIN_DELAY) -> EnableWindow(TRUE);
1081
1082         } else {
1083                 m_chained = FALSE;
1084                 m_chain_delay = 0;
1085                 GetDlgItem(IDC_CHAIN_DELAY) -> EnableWindow(FALSE);
1086         }
1087
1088         if (m_events[cur_event].objective_text){
1089                 m_obj_text = m_events[cur_event].objective_text;
1090         } else {
1091                 m_obj_text.Empty();
1092         }
1093
1094         if (m_events[cur_event].objective_key_text){
1095                 m_obj_key_text = m_events[cur_event].objective_key_text;
1096         } else {
1097                 m_obj_key_text.Empty();
1098         }
1099
1100         GetDlgItem(IDC_REPEAT_COUNT)->EnableWindow(TRUE);
1101         if ( m_repeat_count <= 1 ) {
1102                 m_interval = 1;
1103                 GetDlgItem(IDC_INTERVAL_TIME) -> EnableWindow(FALSE);
1104         } else {
1105                 GetDlgItem(IDC_INTERVAL_TIME) -> EnableWindow(TRUE);
1106         }
1107
1108         GetDlgItem(IDC_EVENT_SCORE) -> EnableWindow(TRUE);
1109         GetDlgItem(IDC_CHAINED) -> EnableWindow(TRUE);
1110         GetDlgItem(IDC_OBJ_TEXT) -> EnableWindow(TRUE);
1111         GetDlgItem(IDC_OBJ_KEY_TEXT) -> EnableWindow(TRUE);
1112         GetDlgItem(IDC_EVENT_TEAM)->EnableWindow(FALSE);
1113         if ( The_mission.game_type & MISSION_TYPE_MULTI_TEAMS ){
1114                 GetDlgItem(IDC_EVENT_TEAM)->EnableWindow(TRUE);
1115         }
1116         UpdateData(FALSE);
1117 }
1118
1119 void event_editor::OnUpdateRepeatCount()
1120 {
1121         char buf[128];
1122         int count;
1123
1124         count = 128;
1125         GetDlgItem(IDC_REPEAT_COUNT)->GetWindowText(buf, count);
1126         m_repeat_count = atoi(buf);
1127
1128         if ( m_repeat_count <= 1 ){
1129                 GetDlgItem(IDC_INTERVAL_TIME)->EnableWindow(FALSE);
1130         } else {
1131                 GetDlgItem(IDC_INTERVAL_TIME)->EnableWindow(TRUE);
1132         }
1133 }
1134
1135 void event_editor::swap_handler(int node1, int node2)
1136 {
1137         int index1, index2;
1138         mission_event m;
1139
1140         save();
1141         for (index1=0; index1<m_num_events; index1++){
1142                 if (m_events[index1].formula == node1){
1143                         break;
1144                 }
1145         }
1146
1147         Assert(index1 < m_num_events);
1148         for (index2=0; index2<m_num_events; index2++){
1149                 if (m_events[index2].formula == node2){
1150                         break;
1151                 }
1152         }
1153
1154         Assert(index2 < m_num_events);
1155         m = m_events[index1];
1156 //      m_events[index1] = m_events[index2];
1157         while (index1 < index2) {
1158                 m_events[index1] = m_events[index1 + 1];
1159                 m_sig[index1] = m_sig[index1 + 1];
1160                 index1++;
1161         }
1162
1163         while (index1 > index2 + 1) {
1164                 m_events[index1] = m_events[index1 - 1];
1165                 m_sig[index1] = m_sig[index1 - 1];
1166                 index1--;
1167         }
1168
1169         m_events[index1] = m;
1170         cur_event = index1;
1171         update_cur_event();
1172 }
1173
1174 void event_editor::OnChained() 
1175 {
1176         int image;
1177         HTREEITEM h;
1178
1179         UpdateData(TRUE);
1180         if (m_chained) {
1181                 GetDlgItem(IDC_CHAIN_DELAY) -> EnableWindow(TRUE);
1182                 if (m_obj_text.IsEmpty()) {
1183                         image = BITMAP_CHAIN;
1184                 } else {
1185                         image = BITMAP_CHAIN_DIRECTIVE;
1186                 }
1187         } else {
1188                 GetDlgItem(IDC_CHAIN_DELAY) -> EnableWindow(FALSE);
1189                 if (m_obj_text.IsEmpty()) {
1190                         image = BITMAP_ROOT;
1191                 } else {
1192                         image = BITMAP_ROOT_DIRECTIVE;
1193                 }
1194         }
1195
1196         h = m_event_tree.GetRootItem();
1197         while (h) {
1198                 if ((int) m_event_tree.GetItemData(h) == m_events[cur_event].formula) {
1199                         m_event_tree.SetItemImage(h, image, image);
1200                         return;
1201                 }
1202
1203                 h = m_event_tree.GetNextSiblingItem(h);
1204         }
1205 }
1206
1207 void event_editor::OnSelchangeMessageList() 
1208 {       
1209         static flag = 0;
1210
1211         if (flag)
1212                 return;
1213 /*
1214         if (save_message(m_cur_msg)) {
1215                 flag = 1;
1216                 ((CListBox *) GetDlgItem(IDC_MESSAGE_LIST)) -> SetCurSel(old);
1217                 m_cur_msg = old;
1218                 flag = 0;
1219                 return;
1220         }*/
1221
1222         save();
1223         update_cur_message();
1224 }
1225
1226 int event_editor::save_message(int num)
1227 {
1228         char *ptr;
1229         int i, conflict = 0;
1230         CListBox *list;
1231
1232         UpdateData(TRUE);
1233         if (num >= 0) {
1234                 ptr = (char *) (LPCTSTR) m_message_name;
1235                 for (i=0; i<Num_builtin_messages; i++){
1236                         if (!stricmp(m_message_name, Messages[i].name)) {
1237                                 conflict = 1;
1238                                 break;
1239                         }
1240                 }
1241
1242                 for (i=0; i<m_num_messages; i++){
1243                         if ((i != num) && (!stricmp(m_message_name, m_messages[i].name))) {
1244                                 conflict = 1;
1245                                 break;
1246                         }
1247                 }
1248
1249                 if (!conflict) {  // update name if no conflicts, otherwise keep old name
1250                         string_copy(m_messages[num].name, m_message_name, NAME_LENGTH - 1);
1251
1252                         list = (CListBox *) GetDlgItem(IDC_MESSAGE_LIST);
1253                         list->DeleteString(num);
1254                         list->InsertString(num, m_message_name);
1255                 }
1256
1257                 string_copy(m_messages[num].message, m_message_text, MESSAGE_LENGTH - 1);
1258                 if (m_messages[num].avi_info.name){
1259                         free(m_messages[num].avi_info.name);
1260                 }
1261
1262                 ptr = (char *) (LPCTSTR) m_avi_filename;
1263                 if (!ptr || !strlen(ptr) || !stricmp(ptr, "none") || !stricmp(ptr, "<none>")){
1264                         m_messages[num].avi_info.name = NULL;
1265                 } else {
1266                         m_messages[num].avi_info.name = strdup(ptr);
1267                 }
1268
1269                 if (m_messages[num].wave_info.name){
1270                         free(m_messages[num].wave_info.name);
1271                 }
1272
1273                 ptr = (char *) (LPCTSTR) m_wave_filename;
1274                 if (!ptr || !strlen(ptr) || !stricmp(ptr, "none") || !stricmp(ptr, "<none>")){
1275                         m_messages[num].wave_info.name = NULL;
1276                 } else {
1277                         m_messages[num].wave_info.name = strdup(ptr);
1278                 }
1279
1280                 // update the persona to the message.  We subtract 1 for the "None" at the beginning of the combo
1281                 // box list.
1282                 m_messages[num].persona_index = m_persona - 1;
1283
1284                 if(m_message_team >= 2){
1285                         m_messages[num].multi_team = -1;
1286                         m_message_team = -1;
1287                 } else {
1288                         m_messages[num].multi_team = m_message_team;
1289                 }
1290
1291                 // possible TODO: auto-update event tree references to this message if we renamed it.
1292         }
1293
1294         return 0;
1295 }
1296
1297 void event_editor::OnNewMsg() 
1298 {
1299 //      if (save_message(m_cur_msg))
1300 //              return;
1301
1302         save();
1303         Assert(m_num_messages + Num_builtin_messages < MAX_MISSION_MESSAGES);
1304         strcpy(m_messages[m_num_messages].name, "<new message>");
1305         ((CListBox *) GetDlgItem(IDC_MESSAGE_LIST))->AddString("<new message>");
1306
1307         strcpy(m_messages[m_num_messages].message, "<put description here>");
1308         m_messages[m_num_messages].avi_info.name = NULL;
1309         m_messages[m_num_messages].wave_info.name = NULL;
1310         m_messages[m_num_messages].persona_index = -1;
1311         m_messages[m_num_messages].multi_team = -1;
1312         m_cur_msg = m_num_messages++;
1313         if (m_num_messages + Num_builtin_messages >= MAX_MISSION_MESSAGES){
1314                 GetDlgItem(IDC_NEW_MSG)->EnableWindow(FALSE);
1315         }
1316
1317         modified = 1;
1318         update_cur_message();
1319 }
1320
1321 void event_editor::OnDeleteMsg() 
1322 {
1323         char buf[256];
1324         int i;
1325
1326         // handle this case somewhat gracefully
1327         Assert((m_cur_msg >= 0) && (m_cur_msg < m_num_messages));
1328         if((m_cur_msg < 0) || (m_cur_msg >= m_num_messages)){
1329                 return;
1330         }
1331         
1332         if (m_messages[m_cur_msg].avi_info.name){
1333                 free(m_messages[m_cur_msg].avi_info.name);
1334         }
1335         if (m_messages[m_cur_msg].wave_info.name){
1336                 free(m_messages[m_cur_msg].wave_info.name);
1337         }
1338
1339         ((CListBox *) GetDlgItem(IDC_MESSAGE_LIST))->DeleteString(m_cur_msg);
1340         sprintf(buf, "<%s>", m_messages[m_cur_msg].name);
1341         update_sexp_references(m_messages[m_cur_msg].name, buf, OPF_MESSAGE);
1342
1343         for (i=m_cur_msg; i<m_num_messages-1; i++){
1344                 m_messages[i] = m_messages[i + 1];
1345         }
1346
1347         m_num_messages--;
1348         if (m_cur_msg >= m_num_messages){
1349                 m_cur_msg = m_num_messages - 1;
1350         }
1351
1352         GetDlgItem(IDC_NEW_MSG)->EnableWindow(TRUE);
1353         modified = 1;
1354         update_cur_message();
1355 }
1356
1357 void event_editor::OnBrowseAvi() 
1358 {
1359         int z;
1360         CString name;   
1361
1362         UpdateData(TRUE);
1363         if (!stricmp(m_avi_filename, "<None>"))
1364                 m_avi_filename = _T("");
1365
1366         z = cfile_push_chdir(CF_TYPE_INTERFACE);
1367         CFileDialog dlg(TRUE, "ani", m_avi_filename, OFN_HIDEREADONLY | OFN_FILEMUSTEXIST | OFN_NOCHANGEDIR,
1368                 "Ani Files (*.ani)|*.ani|Avi Files (*.avi)|*.avi|Both (*.ani, *.avi)|*.ani;*.avi||");
1369
1370         if (dlg.DoModal() == IDOK) {
1371                 m_avi_filename = dlg.GetFileName();
1372                 UpdateData(FALSE);
1373                 modified = 1;
1374         }
1375
1376         if (!z)
1377                 cfile_pop_dir();
1378 }
1379
1380 void event_editor::OnBrowseWave() 
1381 {
1382         int z;
1383         CString name;
1384
1385         UpdateData(TRUE);
1386         if (!stricmp(m_wave_filename, "<None>"))
1387                 m_wave_filename = _T("");
1388
1389         if (The_mission.game_type & MISSION_TYPE_TRAINING)
1390                 z = cfile_push_chdir(CF_TYPE_VOICE_TRAINING);
1391         else
1392                 z = cfile_push_chdir(CF_TYPE_VOICE_SPECIAL);
1393
1394         CFileDialog dlg(TRUE, "wav", m_wave_filename, OFN_HIDEREADONLY | OFN_FILEMUSTEXIST | OFN_NOCHANGEDIR,
1395                 "Wave Files (*.wav)|*.wav||");
1396
1397         if (dlg.DoModal() == IDOK) {
1398                 m_wave_filename = dlg.GetFileName();
1399                 update_persona();
1400         }
1401
1402         if (!z){
1403                 cfile_pop_dir();
1404         }
1405 }
1406
1407 char *event_editor::current_message_name(int i)
1408 {
1409         if ( (i < 0) || (i >= m_num_messages) ){
1410                 return NULL;
1411         }
1412
1413         return m_messages[i].name;
1414 }
1415
1416 char *event_editor::get_message_list_item(int i)
1417 {
1418         return m_messages[i].name;
1419 }
1420
1421 void event_editor::update_persona()
1422 {
1423         int i, mask;
1424
1425         if ((m_wave_filename[0] >= '1') && (m_wave_filename[0] <= '9') && (m_wave_filename[1] == '_') ) {
1426                 i = m_wave_filename[0] - '1';
1427                 if ( (i < Num_personas) && (Personas[i].flags & PERSONA_FLAG_WINGMAN) ) {
1428                         m_persona = i + 1;
1429                         if ((m_persona==1) || (m_persona==2)) 
1430                                 m_avi_filename = "HEAD-TP1";
1431                         else if ((m_persona==3) || (m_persona==4))
1432                                 m_avi_filename = "HEAD-TP2";
1433                         else if ((m_persona==5))
1434                                 m_avi_filename = "HEAD-TP3";
1435                         else if ((m_persona==6))
1436                                 m_avi_filename = "HEAD-VP1";
1437                 }
1438         } else {
1439                 mask = 0;
1440                 if (!strnicmp(m_wave_filename, "S_", 2)) {
1441                         mask = PERSONA_FLAG_SUPPORT;
1442                         m_avi_filename = "HEAD-CM1";
1443                 }
1444                 else if (!strnicmp(m_wave_filename, "L_", 2)) {
1445                         mask = PERSONA_FLAG_LARGE;
1446                         m_avi_filename = "HEAD-CM1";
1447                 }
1448                 else if (!strnicmp(m_wave_filename, "TC_", 3)) {
1449                         mask = PERSONA_FLAG_COMMAND;
1450                         m_avi_filename = "HEAD-CM1";
1451                 }
1452
1453                 for (i=0; i<Num_personas; i++)
1454                         if (Personas[i].flags & mask)
1455                                 m_persona = i + 1;
1456         }
1457         //GetDlgItem(IDC_ANI_FILENAME)->SetWindowText(m_avi_filename);
1458         UpdateData(FALSE);
1459         modified = 1;
1460 }
1461
1462 void event_editor::OnSelchangeWaveFilename() 
1463 {
1464         int z;
1465         CComboBox *box;
1466
1467         box = (CComboBox *) GetDlgItem(IDC_WAVE_FILENAME);
1468         z = box -> GetCurSel();
1469         UpdateData(TRUE);
1470         UpdateData(TRUE);
1471
1472         box -> GetLBText(z, m_wave_filename);
1473         UpdateData(FALSE);
1474         update_persona();
1475 }
1476
1477 BOOL event_editor::DestroyWindow() 
1478 {
1479         m_play_bm.DeleteObject();
1480         return CDialog::DestroyWindow();
1481 }
1482
1483 void event_editor::OnPlay() 
1484 {
1485         char path[MAX_PATH_LEN + 1];
1486         GetDlgItem(IDC_WAVE_FILENAME)->GetWindowText(m_wave_filename);
1487
1488         int size, offset;
1489         cf_find_file_location((char *)(LPCSTR)m_wave_filename, CF_TYPE_ANY, path, &size, &offset );
1490
1491         PlaySound(path, NULL, SND_ASYNC | SND_FILENAME);
1492 }
1493
1494 void event_editor::OnUpdate() 
1495 {
1496 //      GetDlgItem(IDC_WAVE_FILENAME)->GetWindowText(m_wave_filename);
1497         UpdateData(TRUE);
1498         update_persona();
1499 }
1500
1501 // code when the "team" selection in the combo box changes
1502 void event_editor::OnSelchangeTeam() 
1503 {
1504         if ( cur_event < 0 ){
1505                 return;
1506         }
1507
1508         UpdateData(TRUE);
1509
1510         // team == 2, means no team
1511         if(m_team == 2){
1512                 m_team = -1;
1513         }
1514
1515         m_events[cur_event].team = m_team;
1516 }
1517
1518 // code when the "team" selection in the combo box changes
1519 void event_editor::OnSelchangeMessageTeam() 
1520 {
1521         if ( m_cur_msg < 0 ){
1522                 return;
1523         }
1524
1525         UpdateData(TRUE);
1526
1527         // team == 2, means no team
1528         if(m_message_team == 2){
1529                 m_message_team = -1;
1530         }
1531
1532         m_messages[m_cur_msg].multi_team = m_message_team;
1533 }
1534
1535 // Cycles among sexp nodes with message text
1536 void event_editor::OnDblclkMessageList() 
1537 {
1538         CListBox *list = (CListBox*) GetDlgItem(IDC_MESSAGE_LIST);
1539         int num_messages;
1540         int message_nodes[MAX_SEARCH_MESSAGE_DEPTH];
1541
1542         // get current message index and message name
1543         int cur_index = list->GetCurSel();
1544
1545         // check if message name is in event tree
1546         char buffer[256];
1547         list->GetText(cur_index, buffer);
1548
1549
1550         num_messages = m_event_tree.find_text(buffer, message_nodes);
1551
1552         if (num_messages == 0) {
1553                 char message[256];
1554                 sprintf(message, "No events using message '%s'", buffer);
1555                 MessageBox(message);
1556         } else {
1557                 // find last message_node
1558                 if (m_last_message_node == -1) {
1559                         m_last_message_node = message_nodes[0];
1560                 } else {
1561
1562                         if (num_messages == 1) {
1563                                 // only 1 message
1564                                 m_last_message_node = message_nodes[0];
1565                         } else {
1566                                 // find which message and go to next message
1567                                 int found_pos = -1;
1568                                 for (int i=0; i<num_messages; i++) {
1569                                         if (message_nodes[i] == m_last_message_node) {
1570                                                 found_pos = i;
1571                                                 break;
1572                                         }
1573                                 }
1574
1575                                 if (found_pos == -1) {
1576                                         // no previous message
1577                                         m_last_message_node = message_nodes[0];
1578                                 } else if (found_pos == num_messages-1) {
1579                                         // cycle back to start
1580                                         m_last_message_node = message_nodes[0];
1581                                 } else {
1582                                         // go to next
1583                                         m_last_message_node = message_nodes[found_pos+1];
1584                                 }
1585                         }
1586                 }
1587
1588                 // highlight next
1589                 m_event_tree.hilite_item(m_last_message_node);
1590         }
1591 }