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