2 * Copyright (C) Volition, Inc. 1999. All rights reserved.
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
10 * $Logfile: /Freespace2/code/fred2/EventEditor.cpp $
15 * Event editor dialog box class and event tree class
18 * Revision 1.3 2002/06/09 04:41:16 relnev
19 * added copyright header
21 * Revision 1.2 2002/05/07 03:16:44 theoddone33
22 * The Great Newline Fix
24 * Revision 1.1.1.1 2002/05/03 03:28:08 root
28 * 12 9/13/99 8:03a Andsager
29 * Add command heads 3,4,5 as allowable head animations.
31 * 11 9/09/99 5:07a Andsager
32 * Make sure TP2 is available in FRED head ani
34 * 10 9/01/99 2:52p Andsager
35 * Add new heads to FRED and some debug code for playing heads
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.
41 * 8 8/26/99 8:52p Dave
42 * Gave multiplayer TvT messaging a heavy dose of sanity. Cheat codes.
44 * 7 5/04/99 5:21p Andsager
46 * 6 2/17/99 2:11p Dave
47 * First full run of squad war. All freespace and tracker side stuff
50 * 5 1/21/99 9:29a Andsager
52 * 4 12/17/98 2:41p Andsager
53 * Changed input into sexp_tree::insert() to include bitmaps
55 * 3 11/06/98 11:21a Johnson
56 * Put in handling code for wacky event editor SDL_assert().
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.
63 * 1 10/07/98 3:02p Dave
65 * 1 10/07/98 3:00p Dave
67 * 55 9/25/98 1:33p Andsager
68 * Add color to event editor (root and chain) indicating mission directive
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.
74 * 53 5/15/98 5:51p Hoffoss
75 * Fixed escape key and cancel button bugs.
77 * 52 5/12/98 11:44a Hoffoss
78 * Made escape key not close dialog (and lose changes made).
80 * 51 4/30/98 9:53p Hoffoss
81 * Added "Head-VC" to ani list at Sandeep's request.
83 * 50 4/30/98 8:23p John
84 * Fixed some bugs with Fred caused by my new cfile code.
86 * 49 4/22/98 9:56a Sandeep
88 * 48 4/20/98 4:40p Hoffoss
89 * Added a button to 4 editors to play the chosen wave file.
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.
95 * 46 4/03/98 12:39p Hoffoss
96 * Changed starting directory for browse buttons in several editors.
98 * 45 3/10/98 4:06p Hoffoss
99 * Fixed browse button blues.
101 * 44 3/06/98 2:24p Hoffoss
102 * Fixed bug with going to reference with deleting a entity referenced by
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.
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
115 * 41 1/23/98 3:06p Hoffoss
116 * Added an explicit <none> item to the filename combo boxes at designers
119 * 40 1/09/98 3:41p Hoffoss
120 * Fixed bug with event moving not updating fields properly.
122 * 39 1/08/98 11:18a Hoffoss
123 * Fixed several bugs in new Event Editor.
125 * 38 1/08/98 10:24a Johnson
126 * Fixed bug with null strings for filenames.
128 * 37 1/07/98 5:58p Hoffoss
129 * Combined message editor into event editor.
131 * 36 1/06/98 8:25p Hoffoss
132 * Added insert event functionality to event editor.
134 * 35 1/06/98 3:31p Hoffoss
135 * Added image to indicate chained events, and added code to support it.
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
142 * 33 10/10/97 6:21p Hoffoss
143 * Put in Fred support for training object list editing.
145 * 32 10/10/97 2:53p Johnson
146 * Fixed bug with new items being selected before they are fully
147 * registered as added.
153 #include <mmsystem.h>
156 #include "eventeditor.h"
157 #include "fredview.h"
158 #include "management.h"
159 #include "sexp_tree.h"
160 #include "missionmessage.h"
164 #define new DEBUG_NEW
166 static char THIS_FILE[] = __FILE__;
169 event_editor *Event_editor_dlg = NULL; // global reference needed by event tree class
171 // determine the node number that would be allocated without actually allocating it yet.
172 int sexp_event_tree::get_new_node_position()
176 for (i=0; i<MAX_SEXP_TREE_SIZE; i++)
177 if (nodes[i].type == SEXPT_UNUSED)
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)
189 cur = allocate_node(-1);
190 set_node(cur, SEXPT_OPERATOR, "do-nothing"); // setup a default tree if none
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);
203 /////////////////////////////////////////////////////////////////////////////
204 // event_editor dialog
206 event_editor::event_editor(CWnd* pParent /*=NULL*/)
207 : CDialog(event_editor::IDD, pParent)
209 //{{AFX_DATA_INIT(event_editor)
216 m_obj_key_text = _T("");
217 m_avi_filename = _T("");
218 m_message_name = _T("");
219 m_message_text = _T("");
221 m_wave_filename = _T("");
225 m_last_message_node = -1;
227 m_event_tree.m_mode = MODE_EVENTS;
229 m_event_tree.link_modified(&modified);
231 select_sexp_node = -1;
234 void event_editor::DoDataExchange(CDataExchange* pDX)
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);
253 // m_team == -1 maps to 2
257 DDX_CBIndex(pDX, IDC_EVENT_TEAM, m_team);
259 // m_message_team == -1 maps to 2
260 if(m_message_team == -1){
263 DDX_CBIndex(pDX, IDC_MESSAGE_TEAM, m_message_team);
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);
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)
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)
302 /////////////////////////////////////////////////////////////////////////////
303 // event_editor message handlers
305 void maybe_add_head(CComboBox *box, char* name)
307 if (box->FindStringExact(-1, name) == CB_ERR) {
308 box->AddString(name);
312 BOOL event_editor::OnInitDialog()
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);
324 adjust = -SEXP_HELP_BOX_SIZE;
326 theApp.init_window(&Events_wnd_data, this, adjust);
327 m_event_tree.setup((CEdit *) GetDlgItem(IDC_HELP_BOX));
330 if (m_num_events >= MAX_MISSION_EVENTS){
331 GetDlgItem(IDC_BUTTON_NEW_EVENT)->EnableWindow(FALSE);
335 i = m_event_tree.select_sexp_node;
337 GetDlgItem(IDC_EVENT_TREE) -> SetFocus();
338 m_event_tree.hilite_item(i);
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);
348 if (m_messages[i].wave_info.name){
349 m_messages[i].wave_info.name = strdup(m_messages[i].wave_info.name);
351 m_msg_sig[i] = i + Num_builtin_messages;
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);
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);
365 box = (CComboBox *) GetDlgItem(IDC_AVI_FILENAME);
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);
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");
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");
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");
401 // add vasudan pilot heads
402 box->AddString("Head-VP2");
405 box->AddString("Head-CM2");
406 box->AddString("Head-BSH");
409 box = (CComboBox *) GetDlgItem(IDC_WAVE_FILENAME);
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);
420 // add the persona names into the combo box
421 box = (CComboBox *) GetDlgItem(IDC_PERSONA_NAME);
423 box->AddString("<None>");
424 for (i = 0; i < Num_personas; i++ ){
425 box->AddString( Personas[i].name );
428 // set the first message to be the first non-builtin message (if it exists)
429 if ( Num_messages > Num_builtin_messages ){
435 if (Num_messages >= MAX_MISSION_MESSAGES){
436 GetDlgItem(IDC_NEW_MSG)->EnableWindow(FALSE);
439 update_cur_message();
443 void event_editor::load_tree()
447 m_event_tree.select_sexp_node = select_sexp_node;
448 select_sexp_node = -1;
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);
457 m_events[i].objective_text = NULL;
460 if (Mission_events[i].objective_key_text){
461 m_events[i].objective_key_text = strdup(Mission_events[i].objective_key_text);
463 m_events[i].objective_key_text = NULL;
467 if (!(*m_events[i].name)){
468 strcpy(m_events[i].name, "<Unnamed>");
471 m_events[i].formula = m_event_tree.load_sub_tree(Mission_events[i].formula);
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;
480 m_event_tree.post_load();
484 void event_editor::create_tree()
489 m_event_tree.DeleteAllItems();
490 for (i=0; i<m_num_events; i++) {
492 // set the proper bitmap
494 if (m_events[i].chain_delay >= 0) {
495 image = BITMAP_CHAIN;
496 if (m_events[i].objective_text) {
497 image = BITMAP_CHAIN_DIRECTIVE;
501 if (m_events[i].objective_text) {
502 image = BITMAP_ROOT_DIRECTIVE;
506 h = m_event_tree.insert(m_events[i].name, image, image);
508 m_event_tree.SetItemData(h, m_events[i].formula);
509 m_event_tree.add_sub_tree(m_events[i].formula, h);
515 void event_editor::OnRclickEventTree(NMHDR* pNMHDR, LRESULT* pResult)
518 m_event_tree.right_clicked(MODE_EVENTS);
522 void event_editor::OnBeginlabeleditEventTree(NMHDR* pNMHDR, LRESULT* pResult)
524 TV_DISPINFO* pTVDispInfo = (TV_DISPINFO*)pNMHDR;
527 if (m_event_tree.edit_label(pTVDispInfo->item.hItem) == 1) {
530 edit = m_event_tree.GetEditControl();
532 edit->SetLimitText(NAME_LENGTH - 1);
538 void event_editor::OnEndlabeleditEventTree(NMHDR* pNMHDR, LRESULT* pResult)
540 TV_DISPINFO* pTVDispInfo = (TV_DISPINFO*)pNMHDR;
542 *pResult = m_event_tree.end_label_edit(pTVDispInfo->item.hItem, pTVDispInfo->item.pszText);
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()
556 GetDlgItem(IDC_EVENT_TREE)->SetFocus();
561 int event_editor::query_modified()
564 char *ptr, buf[MESSAGE_LENGTH];
570 if (Num_mission_events != m_num_events)
573 for (i=0; i<m_num_events; i++) {
574 if (stricmp(m_events[i].name, Mission_events[i].name))
576 if (m_events[i].repeat_count != Mission_events[i].repeat_count)
578 if (m_events[i].interval != Mission_events[i].interval)
580 if (m_events[i].score != Mission_events[i].score)
582 if (m_events[i].chain_delay != Mission_events[i].chain_delay)
584 if (advanced_stricmp(m_events[i].objective_text, Mission_events[i].objective_text))
586 if (advanced_stricmp(m_events[i].objective_key_text, Mission_events[i].objective_key_text))
593 if (m_num_messages != Num_messages)
596 ptr = (char *) (LPCTSTR) m_message_name;
597 for (i=0; i<Num_builtin_messages; i++)
598 if (!stricmp(ptr, Messages[i].name))
601 for (i=0; i<m_num_messages; i++) {
602 if (m_msg_sig[i] < 0)
605 if ((i != m_cur_msg) && (!stricmp(ptr, m_messages[m_cur_msg].name)))
609 if (stricmp(ptr, m_messages[m_cur_msg].name))
610 return 1; // name is different and allowed to update
612 string_copy(buf, m_message_text, MESSAGE_LENGTH - 1);
613 if (stricmp(buf, m_messages[m_cur_msg].message))
616 ptr = (char *) (LPCTSTR) m_avi_filename;
617 if (advanced_stricmp(ptr, m_messages[m_cur_msg].avi_info.name))
620 ptr = (char *) (LPCTSTR) m_wave_filename;
621 if (advanced_stricmp(ptr, m_messages[m_cur_msg].wave_info.name))
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 )
632 void event_editor::OnOk()
634 char buf[256], names[2][MAX_MISSION_EVENTS][NAME_LENGTH];
638 if (query_modified())
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);
650 for (i=0; i<Num_mission_events; i++)
651 Mission_events[i].result = 0; // use this as a processed flag
653 // rename all sexp references to old events
654 for (i=0; i<m_num_events; i++)
656 strcpy(names[0][count], Mission_events[m_sig[i]].name);
657 strcpy(names[1][count], m_events[i].name);
659 Mission_events[m_sig[i]].result = 1;
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);
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;
680 // now update all sexp references
682 update_sexp_references(names[0][count], names[1][count], OPF_EVENT_NAME);
684 for (i=Num_builtin_messages; i<Num_messages; i++) {
685 if (Messages[i].avi_info.name)
686 free(Messages[i].avi_info.name);
688 if (Messages[i].wave_info.name)
689 free(Messages[i].wave_info.name);
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];
696 theApp.record_window_data(&Events_wnd_data, this);
697 delete Event_editor_dlg;
698 Event_editor_dlg = NULL;
701 // load controls with structure data
702 void event_editor::update_cur_message()
708 m_message_name = _T("");
709 m_message_text = _T("");
710 m_avi_filename = _T("");
711 m_wave_filename = _T("");
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);
720 m_avi_filename = _T("<None>");
723 if (m_messages[m_cur_msg].wave_info.name){
724 m_wave_filename = _T(m_messages[m_cur_msg].wave_info.name);
726 m_wave_filename = _T("<None>");
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
736 if(m_messages[m_cur_msg].multi_team >= 2){
738 m_messages[m_cur_msg].multi_team = -1;
740 m_message_team = m_messages[m_cur_msg].multi_team;
743 m_event_num = find_event();
744 if (m_event_num < 0) {
746 m_sender = m_priority = 0;
749 node = CADR(Mission_events[m_event_num].formula);
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);
764 int event_editor::handler(int code, int node, char *str)
770 for (i=0; i<m_num_events; i++)
771 if (m_events[i].formula == node)
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];
782 GetDlgItem(IDC_BUTTON_NEW_EVENT)->EnableWindow(TRUE);
786 for (i=0; i<m_num_events; i++)
787 if (m_events[i].formula == node)
790 SDL_assert(i < m_num_events);
791 SDL_assert(strlen(str) < NAME_LENGTH);
792 strcpy(m_events[i].name, str);
802 void event_editor::OnButtonNewEvent()
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.");
810 reset_event(m_num_events++, TVI_LAST);
813 void event_editor::OnInsert()
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.");
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];
829 reset_event(cur_event, get_event_handle(cur_event - 1));
831 reset_event(cur_event, TVI_FIRST);
837 HTREEITEM event_editor::get_event_handle(int num)
841 h = m_event_tree.GetRootItem();
843 if ((int) m_event_tree.GetItemData(h) == m_events[num].formula){
847 h = m_event_tree.GetNextSiblingItem(h);
853 void event_editor::reset_event(int num, HTREEITEM after)
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);
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;
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");
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);
884 void event_editor::OnDelete()
888 // call update_cur_event to clean up local class variables so that we can correctly
889 // set up the newly selected item.
893 h = m_event_tree.GetSelectedItem();
895 while (m_event_tree.GetParentItem(h))
896 h = m_event_tree.GetParentItem(h);
898 m_event_tree.setup_selected(h);
899 m_event_tree.OnCommand(ID_DELETE, 0);
903 // this is called when you hit the escape key..
904 void event_editor::OnCancel()
908 // this is called the clicking the ID_CANCEL button
909 void event_editor::On_Cancel()
911 theApp.record_window_data(&Events_wnd_data, this);
912 delete Event_editor_dlg;
913 Event_editor_dlg = NULL;
916 void event_editor::OnClose()
920 if (query_modified()) {
921 z = MessageBox("Do you want to keep your changes?", "Close", MB_ICONQUESTION | MB_YESNOCANCEL);
932 theApp.record_window_data(&Events_wnd_data, this);
933 delete Event_editor_dlg;
934 Event_editor_dlg = NULL;
937 void event_editor::insert_handler(int old, int node)
941 for (i=0; i<m_num_events; i++){
942 if (m_events[i].formula == old){
947 SDL_assert(i < m_num_events);
948 m_events[i].formula = node;
952 void event_editor::save()
956 save_event(cur_event);
960 void event_editor::save_event(int e)
967 m_events[e].repeat_count = m_repeat_count;
968 m_events[e].interval = m_interval;
969 m_events[e].score = m_event_score;
973 m_events[e].chain_delay = m_chain_delay;
975 m_events[e].chain_delay = -1;
978 // handle objective text
979 if (m_events[e].objective_text) {
980 free(m_events[e].objective_text);
983 if (m_obj_text.IsEmpty()) {
984 m_events[e].objective_text = NULL;
986 m_events[e].objective_text = strdup(m_obj_text);
989 // handle objective key text
990 if (m_events[e].objective_key_text) {
991 free(m_events[e].objective_key_text);
994 if (m_obj_key_text.IsEmpty()) {
995 m_events[e].objective_key_text = NULL;
997 m_events[e].objective_key_text = strdup(m_obj_key_text);
1003 if (m_obj_text.IsEmpty()) {
1004 bitmap = BITMAP_CHAIN;
1006 bitmap = BITMAP_CHAIN_DIRECTIVE;
1010 if (m_obj_text.IsEmpty()) {
1011 bitmap = BITMAP_ROOT;
1013 bitmap = BITMAP_ROOT_DIRECTIVE;
1017 // Search for item to update
1018 HTREEITEM h = m_event_tree.GetRootItem();
1020 if ((int) m_event_tree.GetItemData(h) == m_events[e].formula) {
1021 m_event_tree.SetItemImage(h, bitmap, bitmap);
1025 h = m_event_tree.GetNextSiblingItem(h);
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
1033 void event_editor::OnSelchangedEventTree(NMHDR* pNMHDR, LRESULT* pResult)
1036 NM_TREEVIEW* pNMTreeView = (NM_TREEVIEW*)pNMHDR;
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)
1042 h = pNMTreeView->itemNew.hItem;
1047 m_event_tree.update_help(h);
1048 while ((h2 = m_event_tree.GetParentItem(h))>0){
1052 z = m_event_tree.GetItemData(h);
1053 for (i=0; i<m_num_events; i++){
1054 if (m_events[i].formula == z){
1059 SDL_assert(i < m_num_events);
1066 void event_editor::update_cur_event()
1068 if (cur_event < 0) {
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);
1086 m_team = m_events[cur_event].team;
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) {
1093 m_chain_delay = m_events[cur_event].chain_delay;
1094 GetDlgItem(IDC_CHAIN_DELAY) -> EnableWindow(TRUE);
1099 GetDlgItem(IDC_CHAIN_DELAY) -> EnableWindow(FALSE);
1102 if (m_events[cur_event].objective_text){
1103 m_obj_text = m_events[cur_event].objective_text;
1108 if (m_events[cur_event].objective_key_text){
1109 m_obj_key_text = m_events[cur_event].objective_key_text;
1111 m_obj_key_text.Empty();
1114 GetDlgItem(IDC_REPEAT_COUNT)->EnableWindow(TRUE);
1115 if ( m_repeat_count <= 1 ) {
1117 GetDlgItem(IDC_INTERVAL_TIME) -> EnableWindow(FALSE);
1119 GetDlgItem(IDC_INTERVAL_TIME) -> EnableWindow(TRUE);
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);
1133 void event_editor::OnUpdateRepeatCount()
1139 GetDlgItem(IDC_REPEAT_COUNT)->GetWindowText(buf, count);
1140 m_repeat_count = atoi(buf);
1142 if ( m_repeat_count <= 1 ){
1143 GetDlgItem(IDC_INTERVAL_TIME)->EnableWindow(FALSE);
1145 GetDlgItem(IDC_INTERVAL_TIME)->EnableWindow(TRUE);
1149 void event_editor::swap_handler(int node1, int node2)
1155 for (index1=0; index1<m_num_events; index1++){
1156 if (m_events[index1].formula == node1){
1161 SDL_assert(index1 < m_num_events);
1162 for (index2=0; index2<m_num_events; index2++){
1163 if (m_events[index2].formula == node2){
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];
1177 while (index1 > index2 + 1) {
1178 m_events[index1] = m_events[index1 - 1];
1179 m_sig[index1] = m_sig[index1 - 1];
1183 m_events[index1] = m;
1188 void event_editor::OnChained()
1195 GetDlgItem(IDC_CHAIN_DELAY) -> EnableWindow(TRUE);
1196 if (m_obj_text.IsEmpty()) {
1197 image = BITMAP_CHAIN;
1199 image = BITMAP_CHAIN_DIRECTIVE;
1202 GetDlgItem(IDC_CHAIN_DELAY) -> EnableWindow(FALSE);
1203 if (m_obj_text.IsEmpty()) {
1204 image = BITMAP_ROOT;
1206 image = BITMAP_ROOT_DIRECTIVE;
1210 h = m_event_tree.GetRootItem();
1212 if ((int) m_event_tree.GetItemData(h) == m_events[cur_event].formula) {
1213 m_event_tree.SetItemImage(h, image, image);
1217 h = m_event_tree.GetNextSiblingItem(h);
1221 void event_editor::OnSelchangeMessageList()
1228 if (save_message(m_cur_msg)) {
1230 ((CListBox *) GetDlgItem(IDC_MESSAGE_LIST)) -> SetCurSel(old);
1237 update_cur_message();
1240 int event_editor::save_message(int num)
1243 int i, conflict = 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)) {
1256 for (i=0; i<m_num_messages; i++){
1257 if ((i != num) && (!stricmp(m_message_name, m_messages[i].name))) {
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);
1266 list = (CListBox *) GetDlgItem(IDC_MESSAGE_LIST);
1267 list->DeleteString(num);
1268 list->InsertString(num, m_message_name);
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);
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;
1280 m_messages[num].avi_info.name = strdup(ptr);
1283 if (m_messages[num].wave_info.name){
1284 free(m_messages[num].wave_info.name);
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;
1291 m_messages[num].wave_info.name = strdup(ptr);
1294 // update the persona to the message. We subtract 1 for the "None" at the beginning of the combo
1296 m_messages[num].persona_index = m_persona - 1;
1298 if(m_message_team >= 2){
1299 m_messages[num].multi_team = -1;
1300 m_message_team = -1;
1302 m_messages[num].multi_team = m_message_team;
1305 // possible TODO: auto-update event tree references to this message if we renamed it.
1311 void event_editor::OnNewMsg()
1313 // if (save_message(m_cur_msg))
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>");
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);
1332 update_cur_message();
1335 void event_editor::OnDeleteMsg()
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)){
1346 if (m_messages[m_cur_msg].avi_info.name){
1347 free(m_messages[m_cur_msg].avi_info.name);
1349 if (m_messages[m_cur_msg].wave_info.name){
1350 free(m_messages[m_cur_msg].wave_info.name);
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);
1357 for (i=m_cur_msg; i<m_num_messages-1; i++){
1358 m_messages[i] = m_messages[i + 1];
1362 if (m_cur_msg >= m_num_messages){
1363 m_cur_msg = m_num_messages - 1;
1366 GetDlgItem(IDC_NEW_MSG)->EnableWindow(TRUE);
1368 update_cur_message();
1371 void event_editor::OnBrowseAvi()
1377 if (!stricmp(m_avi_filename, "<None>"))
1378 m_avi_filename = _T("");
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||");
1384 if (dlg.DoModal() == IDOK) {
1385 m_avi_filename = dlg.GetFileName();
1394 void event_editor::OnBrowseWave()
1400 if (!stricmp(m_wave_filename, "<None>"))
1401 m_wave_filename = _T("");
1403 if (The_mission.game_type & MISSION_TYPE_TRAINING)
1404 z = cfile_push_chdir(CF_TYPE_VOICE_TRAINING);
1406 z = cfile_push_chdir(CF_TYPE_VOICE_SPECIAL);
1408 CFileDialog dlg(TRUE, "wav", m_wave_filename, OFN_HIDEREADONLY | OFN_FILEMUSTEXIST | OFN_NOCHANGEDIR,
1409 "Wave Files (*.wav)|*.wav||");
1411 if (dlg.DoModal() == IDOK) {
1412 m_wave_filename = dlg.GetFileName();
1421 char *event_editor::current_message_name(int i)
1423 if ( (i < 0) || (i >= m_num_messages) ){
1427 return m_messages[i].name;
1430 char *event_editor::get_message_list_item(int i)
1432 return m_messages[i].name;
1435 void event_editor::update_persona()
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) ) {
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";
1454 if (!strnicmp(m_wave_filename, "S_", 2)) {
1455 mask = PERSONA_FLAG_SUPPORT;
1456 m_avi_filename = "HEAD-CM1";
1458 else if (!strnicmp(m_wave_filename, "L_", 2)) {
1459 mask = PERSONA_FLAG_LARGE;
1460 m_avi_filename = "HEAD-CM1";
1462 else if (!strnicmp(m_wave_filename, "TC_", 3)) {
1463 mask = PERSONA_FLAG_COMMAND;
1464 m_avi_filename = "HEAD-CM1";
1467 for (i=0; i<Num_personas; i++)
1468 if (Personas[i].flags & mask)
1471 //GetDlgItem(IDC_ANI_FILENAME)->SetWindowText(m_avi_filename);
1476 void event_editor::OnSelchangeWaveFilename()
1481 box = (CComboBox *) GetDlgItem(IDC_WAVE_FILENAME);
1482 z = box -> GetCurSel();
1486 box -> GetLBText(z, m_wave_filename);
1491 BOOL event_editor::DestroyWindow()
1493 m_play_bm.DeleteObject();
1494 return CDialog::DestroyWindow();
1497 void event_editor::OnPlay()
1499 char path[MAX_PATH_LEN + 1];
1500 GetDlgItem(IDC_WAVE_FILENAME)->GetWindowText(m_wave_filename);
1503 cf_find_file_location((char *)(LPCSTR)m_wave_filename, CF_TYPE_ANY, path, &size, &offset );
1505 PlaySound(path, NULL, SND_ASYNC | SND_FILENAME);
1508 void event_editor::OnUpdate()
1510 // GetDlgItem(IDC_WAVE_FILENAME)->GetWindowText(m_wave_filename);
1515 // code when the "team" selection in the combo box changes
1516 void event_editor::OnSelchangeTeam()
1518 if ( cur_event < 0 ){
1524 // team == 2, means no team
1529 m_events[cur_event].team = m_team;
1532 // code when the "team" selection in the combo box changes
1533 void event_editor::OnSelchangeMessageTeam()
1535 if ( m_cur_msg < 0 ){
1541 // team == 2, means no team
1542 if(m_message_team == 2){
1543 m_message_team = -1;
1546 m_messages[m_cur_msg].multi_team = m_message_team;
1549 // Cycles among sexp nodes with message text
1550 void event_editor::OnDblclkMessageList()
1552 CListBox *list = (CListBox*) GetDlgItem(IDC_MESSAGE_LIST);
1554 int message_nodes[MAX_SEARCH_MESSAGE_DEPTH];
1556 // get current message index and message name
1557 int cur_index = list->GetCurSel();
1559 // check if message name is in event tree
1561 list->GetText(cur_index, buffer);
1564 num_messages = m_event_tree.find_text(buffer, message_nodes);
1566 if (num_messages == 0) {
1568 sprintf(message, "No events using message '%s'", buffer);
1569 MessageBox(message);
1571 // find last message_node
1572 if (m_last_message_node == -1) {
1573 m_last_message_node = message_nodes[0];
1576 if (num_messages == 1) {
1578 m_last_message_node = message_nodes[0];
1580 // find which message and go to next message
1582 for (int i=0; i<num_messages; i++) {
1583 if (message_nodes[i] == m_last_message_node) {
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];
1597 m_last_message_node = message_nodes[found_pos+1];
1603 m_event_tree.hilite_item(m_last_message_node);