FS2_Open
Open source remastering of the Freespace 2 engine
sexp_tree.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (C) Volition, Inc. 1999. All rights reserved.
3  *
4  * All source code herein is the property of Volition, Inc. You may not sell
5  * or otherwise commercially exploit the source or things you created based on the
6  * source.
7  *
8 */
9 
10 
11 
12 #include "stdafx.h"
13 #include "Sexp_tree.h"
14 #include "FRED.h"
15 #include "FREDDoc.h"
16 #include "Management.h"
17 #include "parse/sexp.h"
18 #include "OperatorArgTypeSelect.h"
19 #include "globalincs/linklist.h"
20 #include "EventEditor.h"
21 #include "MissionGoalsDlg.h"
22 #include "ai/aigoals.h"
23 #include "mission/missionmessage.h"
25 #include "CampaignEditorDlg.h"
26 #include "hud/hudsquadmsg.h"
27 #include "IgnoreOrdersDlg.h"
28 #include "stats/medals.h"
30 #include "hud/hudgauges.h"
31 #include "starfield/starfield.h"
32 #include "nebula/neb.h"
33 #include "nebula/neblightning.h"
34 #include "jumpnode/jumpnode.h"
35 #include "AddVariableDlg.h"
36 #include "ModifyVariableDlg.h"
37 #include "gamesnd/eventmusic.h" // for change-soundtrack
38 #include "menuui/techmenu.h" // for intel stuff
39 #include "weapon/emp.h"
40 #include "gamesnd/gamesnd.h"
41 #include "weapon/weapon.h"
42 #include "hud/hudartillery.h"
43 #include "iff_defs/iff_defs.h"
44 #include "mission/missionmessage.h"
47 #include "sound/ds.h"
48 #include "globalincs/alphacolors.h"
49 #include "localization/localize.h"
50 
51 #define TREE_NODE_INCREMENT 100
52 
53 #define MAX_OP_MENUS 30
54 #define MAX_SUBMENUS (MAX_OP_MENUS * MAX_OP_MENUS)
55 
56 #define ID_VARIABLE_MENU 0xda00
57 #define ID_ADD_MENU 0xdc00
58 #define ID_REPLACE_MENU 0xde00
59 // note: stay below 0xe000 so we don't collide with MFC defines..
60 
61 #ifdef _DEBUG
62 #undef THIS_FILE
63 static char THIS_FILE[] = __FILE__;
64 #endif
65 
66 //********************sexp_tree********************
67 
68 BEGIN_MESSAGE_MAP(sexp_tree, CTreeCtrl)
69  //{{AFX_MSG_MAP(sexp_tree)
70  ON_NOTIFY_REFLECT(TVN_BEGINDRAG, OnBegindrag)
71  ON_WM_MOUSEMOVE()
72  ON_WM_LBUTTONUP()
73  ON_WM_DESTROY()
74  ON_WM_LBUTTONDOWN()
75  ON_NOTIFY_REFLECT(TVN_KEYDOWN, OnKeydown)
76  //}}AFX_MSG_MAP
77 END_MESSAGE_MAP()
78 
79 static int Add_count, Replace_count;
80 static int Modify_variable;
81 
83 extern op_menu_struct op_menu[];
84 extern op_menu_struct op_submenu[];
85 
86 // constructor
88 {
89  select_sexp_node = -1;
90  root_item = -1;
91  m_mode = 0;
92  m_dragging = FALSE;
93  m_p_image_list = NULL;
94  help_box = NULL;
95  clear_tree();
96 }
97 
98 // clears out the tree, so all the nodes are unused.
99 void sexp_tree::clear_tree(const char *op)
100 {
101  mprintf(("Resetting dynamic tree node limit from %d to %d...\n", tree_nodes.size(), 0));
102 
103  total_nodes = flag = 0;
104  tree_nodes.clear();
105 
106  if (op) {
107  DeleteAllItems();
108  if (strlen(op)) {
110  build_tree();
111  }
112  }
113 }
114 
116 {
117  uint i;
118 
119  for (i=0; i<tree_nodes.size(); i++)
120  tree_nodes[i].handle = NULL;
121 }
122 
123 // initializes and creates a tree from a given sexp startpoint.
124 void sexp_tree::load_tree(int index, const char *deflt)
125 {
126  int cur;
127 
128  clear_tree();
129  root_item = 0;
130  if (index < 0) {
131  cur = allocate_node(-1);
132  set_node(cur, (SEXPT_OPERATOR | SEXPT_VALID), deflt); // setup a default tree if none
133  build_tree();
134  return;
135  }
136 
137  if (Sexp_nodes[index].subtype == SEXP_ATOM_NUMBER) { // handle numbers allender likes to use so much..
138  cur = allocate_node(-1);
139  if (atoi(Sexp_nodes[index].text))
140  set_node(cur, (SEXPT_OPERATOR | SEXPT_VALID), "true");
141  else
142  set_node(cur, (SEXPT_OPERATOR | SEXPT_VALID), "false");
143 
144  build_tree();
145  return;
146  }
147 
148  // assumption: first token is an operator. I require this because it would cause problems
149  // with child/parent relations otherwise, and it should be this way anyway, since the
150  // return type of the whole sexp is boolean, and only operators can satisfy this.
152  load_branch(index, -1);
153  build_tree();
154 }
155 
156 void get_combined_variable_name(char *combined_name, const char *sexp_var_name)
157 {
158  int sexp_var_index = get_index_sexp_variable_name(sexp_var_name);
159  Assert(sexp_var_index > -1);
160 
161  sprintf(combined_name, "%s(%s)", Sexp_variables[sexp_var_index].variable_name, Sexp_variables[sexp_var_index].text);
162 }
163 
164 // creates a tree from a given Sexp_nodes[] point under a given parent. Recursive.
165 // Returns the allocated current node.
166 int sexp_tree::load_branch(int index, int parent)
167 {
168  int cur = -1;
169  char combined_var_name[2*TOKEN_LENGTH + 2];
170 
171  while (index != -1) {
172  Assert(Sexp_nodes[index].type != SEXP_NOT_USED);
173  if (Sexp_nodes[index].subtype == SEXP_ATOM_LIST) {
174  load_branch(Sexp_nodes[index].first, parent); // do the sublist and continue
175 
176  } else if (Sexp_nodes[index].subtype == SEXP_ATOM_OPERATOR) {
177  cur = allocate_node(parent);
178  if ((index == select_sexp_node) && !flag) { // translate sexp node to our node
179  select_sexp_node = cur;
180  flag = 1;
181  }
182 
183  set_node(cur, (SEXPT_OPERATOR | SEXPT_VALID), Sexp_nodes[index].text);
184  load_branch(Sexp_nodes[index].rest, cur); // operator is new parent now
185  return cur; // 'rest' was just used, so nothing left to use.
186 
187  } else if (Sexp_nodes[index].subtype == SEXP_ATOM_NUMBER) {
188  cur = allocate_node(parent);
189  if (Sexp_nodes[index].type & SEXP_FLAG_VARIABLE) {
190  get_combined_variable_name(combined_var_name, Sexp_nodes[index].text);
191  set_node(cur, (SEXPT_VARIABLE | SEXPT_NUMBER | SEXPT_VALID), combined_var_name);
192  } else {
193  set_node(cur, (SEXPT_NUMBER | SEXPT_VALID), Sexp_nodes[index].text);
194  }
195 
196  } else if (Sexp_nodes[index].subtype == SEXP_ATOM_STRING) {
197  cur = allocate_node(parent);
198  if (Sexp_nodes[index].type & SEXP_FLAG_VARIABLE) {
199  get_combined_variable_name(combined_var_name, Sexp_nodes[index].text);
200  set_node(cur, (SEXPT_VARIABLE | SEXPT_STRING | SEXPT_VALID), combined_var_name);
201  } else {
202  set_node(cur, (SEXPT_STRING | SEXPT_VALID), Sexp_nodes[index].text);
203  }
204 
205  } else
206  Assert(0); // unknown and/or invalid sexp type
207 
208  if ((index == select_sexp_node) && !flag) { // translate sexp node to our node
209  select_sexp_node = cur;
210  flag = 1;
211  }
212 
213  index = Sexp_nodes[index].rest;
214  if (index == -1)
215  return cur;
216  }
217 
218  return cur;
219 }
220 
222 {
223  if (node < 0)
224  node = root_item;
225 
226  Assert(node >= 0);
228  Assert(tree_nodes[node].next == -1); // must make this assumption or else it will confuse code!
229  if (get_operator_const(tree_nodes[node].text) == OP_FALSE){
230  return TRUE;
231  }
232 
233  return FALSE;
234 }
235 
236 // builds an sexp of the tree and returns the index of it. This allocates sexp nodes.
237 int sexp_tree::save_tree(int node)
238 {
239  if (node < 0)
240  node = root_item;
241 
242  Assert(node >= 0);
244  Assert(tree_nodes[node].next == -1); // must make this assumption or else it will confuse code!
245  return save_branch(node);
246 }
247 
248 // get variable name from sexp_tree node .text
249 void var_name_from_sexp_tree_text(char *var_name, const char *text)
250 {
251  int var_name_length = strcspn(text, "(");
252  Assert(var_name_length < TOKEN_LENGTH - 1);
253 
254  strncpy(var_name, text, var_name_length);
255  var_name[var_name_length] = '\0';
256 }
257 
258 #define NO_PREVIOUS_NODE -9
259 // called recursively to save a tree branch and everything under it
260 int sexp_tree::save_branch(int cur, int at_root)
261 {
262  int start, node = -1, last = NO_PREVIOUS_NODE;
263  char var_name_text[TOKEN_LENGTH];
264 
265  start = -1;
266  while (cur != -1) {
267  if (tree_nodes[cur].type & SEXPT_OPERATOR) {
268  node = alloc_sexp(tree_nodes[cur].text, SEXP_ATOM, SEXP_ATOM_OPERATOR, -1, save_branch(tree_nodes[cur].child));
269 
270  if ((tree_nodes[cur].parent >= 0) && !at_root) {
271  node = alloc_sexp("", SEXP_LIST, SEXP_ATOM_LIST, node, -1);
272  }
273  } else if (tree_nodes[cur].type & SEXPT_NUMBER) {
274  // allocate number, maybe variable
275  if (tree_nodes[cur].type & SEXPT_VARIABLE) {
276  var_name_from_sexp_tree_text(var_name_text, tree_nodes[cur].text);
277  node = alloc_sexp(var_name_text, (SEXP_ATOM | SEXP_FLAG_VARIABLE), SEXP_ATOM_NUMBER, -1, -1);
278  } else {
279  node = alloc_sexp(tree_nodes[cur].text, SEXP_ATOM, SEXP_ATOM_NUMBER, -1, -1);
280  }
281  } else if (tree_nodes[cur].type & SEXPT_STRING) {
282  // allocate string, maybe variable
283  if (tree_nodes[cur].type & SEXPT_VARIABLE) {
284  var_name_from_sexp_tree_text(var_name_text, tree_nodes[cur].text);
285  node = alloc_sexp(var_name_text, (SEXP_ATOM | SEXP_FLAG_VARIABLE), SEXP_ATOM_STRING, -1, -1);
286  } else {
287  node = alloc_sexp(tree_nodes[cur].text, SEXP_ATOM, SEXP_ATOM_STRING, -1, -1);
288  }
289  } else {
290  Assert(0); // unknown and/or invalid type
291  }
292 
293  if (last == NO_PREVIOUS_NODE){
294  start = node;
295  } else if (last >= 0){
296  Sexp_nodes[last].rest = node;
297  }
298 
299  last = node;
300  Assert(last != NO_PREVIOUS_NODE); // should be impossible
301  cur = tree_nodes[cur].next;
302  if (at_root){
303  return start;
304  }
305  }
306 
307  return start;
308 }
309 
310 // find the next free tree node and return its index.
312 {
313  int i;
314 
315  for (i = 0; i < (int)tree_nodes.size(); i++)
316  {
317  if (tree_nodes[i].type == SEXPT_UNUSED)
318  return i;
319  }
320 
321  return -1;
322 }
323 
324 // allocate a node. Remains used until freed.
326 {
327  int node = find_free_node();
328 
329  // need more tree nodes?
330  if (node < 0)
331  {
332  int old_size = tree_nodes.size();
333 
335 
336  // allocate in blocks of TREE_NODE_INCREMENT
337  tree_nodes.resize(tree_nodes.size() + TREE_NODE_INCREMENT);
338 
339  mprintf(("Bumping dynamic tree node limit from %d to %d...\n", old_size, tree_nodes.size()));
340 
341 #ifndef NDEBUG
342  for (int i = old_size; i < (int)tree_nodes.size(); i++)
343  {
344  sexp_tree_item *item = &tree_nodes[i];
345  Assert(item->type == SEXPT_UNUSED);
346  }
347 #endif
348 
349  // our new sexp is the first out of the ones we just created
350  node = old_size;
351  }
352 
353  // reset the new node
354  tree_nodes[node].type = SEXPT_UNINIT;
355  tree_nodes[node].parent = -1;
356  tree_nodes[node].child = -1;
357  tree_nodes[node].next = -1;
358  tree_nodes[node].flags = 0;
359  strcpy_s(tree_nodes[node].text, "<uninitialized tree node>");
360  tree_nodes[node].handle = NULL;
361 
362  total_nodes++;
363  return node;
364 }
365 
366 // allocate a child node under 'parent'. Appends to end of list.
367 int sexp_tree::allocate_node(int parent, int after)
368 {
369  int i, index = allocate_node();
370 
371  if (parent != -1) {
372  i = tree_nodes[parent].child;
373  if (i == -1) {
374  tree_nodes[parent].child = index;
375 
376  } else {
377  while ((i != after) && (tree_nodes[i].next != -1))
378  i = tree_nodes[i].next;
379 
380  tree_nodes[index].next = tree_nodes[i].next;
381  tree_nodes[i].next = index;
382  }
383  }
384 
385  tree_nodes[index].parent = parent;
386  return index;
387 }
388 
389 // free a node and all its children. Also clears pointers to it, if any.
390 // node = node chain to free
391 // cascade = 0: free just this node and children under it. (default)
392 // !0: free this node and all siblings after it.
393 //
394 void sexp_tree::free_node(int node, int cascade)
395 {
396  int i;
397 
398  // clear the pointer to node
399  i = tree_nodes[node].parent;
400  Assert(i != -1);
401  if (tree_nodes[i].child == node)
402  tree_nodes[i].child = tree_nodes[node].next;
403 
404  else {
405  i = tree_nodes[i].child;
406  while (tree_nodes[i].next != -1) {
407  if (tree_nodes[i].next == node) {
408  tree_nodes[i].next = tree_nodes[node].next;
409  break;
410  }
411 
412  i = tree_nodes[i].next;
413  }
414  }
415 
416  if (!cascade)
417  tree_nodes[node].next = -1;
418 
419  // now free up the node and its children
420  free_node2(node);
421 }
422 
423 // more simple node freer, which works recursively. It frees the given node and all siblings
424 // that come after it, as well as all children of these. Doesn't clear any links to any of
425 // these freed nodes, so make sure all links are broken first. (i.e. use free_node() if you can)
426 //
427 void sexp_tree::free_node2(int node)
428 {
429  Assert(node != -1);
431  Assert(total_nodes > 0);
432  *modified = 1;
433  tree_nodes[node].type = SEXPT_UNUSED;
434  total_nodes--;
435  if (tree_nodes[node].child != -1)
436  free_node2(tree_nodes[node].child);
437 
438  if (tree_nodes[node].next != -1)
439  free_node2(tree_nodes[node].next);
440 }
441 
442 // initialize the data for a node. Should be called right after a new node is allocated.
443 void sexp_tree::set_node(int node, int type, const char *text)
444 {
445  Assert(type != SEXPT_UNUSED);
446  Assert(tree_nodes[node].type != SEXPT_UNUSED);
447  tree_nodes[node].type = type;
448  size_t max_length;
449  if (type & SEXPT_VARIABLE) {
450  max_length = 2 * TOKEN_LENGTH + 2;
451  } else {
452  max_length = TOKEN_LENGTH;
453  }
454  Assert(strlen(text) < max_length);
455  strcpy_s(tree_nodes[node].text, text);
456 }
457 
459 {
460  if (!flag)
461  select_sexp_node = -1;
462 }
463 
464 // build or rebuild a CTreeCtrl object with the current tree data
466 {
467  if (!flag)
468  select_sexp_node = -1;
469 
470  DeleteAllItems();
471  add_sub_tree(0, TVI_ROOT);
472 }
473 
474 // Create the CTreeCtrl tree from the tree data. The tree data should already be setup by
475 // this point.
476 void sexp_tree::add_sub_tree(int node, HTREEITEM root)
477 {
478 // char str[80];
479  int node2;
480 
481  Assert(node >= 0 && node < (int)tree_nodes.size());
482  node2 = tree_nodes[node].child;
483 
484  // check for single argument operator case (prints as one line)
485 /* if (node2 != -1 && tree_nodes[node2].child == -1 && tree_nodes[node2].next == -1) {
486  sprintf(str, "%s %s", tree_nodes[node].text, tree_nodes[node2].text);
487  tree_nodes[node].handle = insert(str, root);
488  tree_nodes[node].flags = OPERAND | EDITABLE;
489  tree_nodes[node2].flags = COMBINED;
490  return;
491  }*/
492 
493  // bitmap to draw in tree
494  int bitmap;
495 
496  if (tree_nodes[node].type & SEXPT_OPERATOR) {
497  tree_nodes[node].flags = OPERAND;
498  bitmap = BITMAP_OPERATOR;
499  } else {
500  if (tree_nodes[node].type & SEXPT_VARIABLE) {
501  tree_nodes[node].flags = NOT_EDITABLE;
502  bitmap = BITMAP_VARIABLE;
503  } else {
504  tree_nodes[node].flags = EDITABLE;
505  bitmap = get_data_image(node);
506  }
507  }
508 
509  root = tree_nodes[node].handle = insert(tree_nodes[node].text, bitmap, bitmap, root);
510 
511  node = node2;
512  while (node != -1) {
513  Assert(node >= 0 && node < (int)tree_nodes.size());
515  if (tree_nodes[node].type & SEXPT_OPERATOR) {
516  add_sub_tree(node, root);
517 
518  } else {
519  Assert(tree_nodes[node].child == -1);
520  if (tree_nodes[node].type & SEXPT_VARIABLE) {
521  tree_nodes[node].handle = insert(tree_nodes[node].text, BITMAP_VARIABLE, BITMAP_VARIABLE, root);
522  tree_nodes[node].flags = NOT_EDITABLE;
523  } else {
524  int bmap = get_data_image(node);
525  tree_nodes[node].handle = insert(tree_nodes[node].text, bmap, bmap, root);
526  tree_nodes[node].flags = EDITABLE;
527  }
528  }
529 
530  node = tree_nodes[node].next;
531  }
532 }
533 
534 // construct tree nodes for an sexp, adding them to the list and returning first node
535 int sexp_tree::load_sub_tree(int index, bool valid, const char *text)
536 {
537  int cur;
538 
539  if (index < 0) {
540  cur = allocate_node(-1);
541  set_node(cur, (SEXPT_OPERATOR | (valid ? SEXPT_VALID : 0)), text); // setup a default tree if none
542  return cur;
543  }
544 
545  // assumption: first token is an operator. I require this because it would cause problems
546  // with child/parent relations otherwise, and it should be this way anyway, since the
547  // return type of the whole sexp is boolean, and only operators can satisfy this.
549  cur = load_branch(index, -1);
550  return cur;
551 }
552 
554 {
555  uint i;
556 
557  item_index = -1;
558  item_handle = h;
559  if (!h)
560  item_handle = GetSelectedItem();
561 
562  for (i=0; i<tree_nodes.size(); i++)
563  if (tree_nodes[i].handle == item_handle) {
564  item_index = i;
565  break;
566  }
567 }
568 
569 // handler for right mouse button clicks.
571 {
572  char buf[256];
573  int i, j, z, count, op, add_type, replace_type, type, subcategory_id;
574  sexp_list_item *list;
575  UINT _flags;
576  HTREEITEM h;
577  POINT click_point, mouse;
578  CMenu menu, *mptr, *popup_menu, *add_data_menu = NULL, *replace_data_menu = NULL;
579  CMenu *add_op_menu, add_op_submenu[MAX_OP_MENUS];
580  CMenu *replace_op_menu, replace_op_submenu[MAX_OP_MENUS];
581  CMenu *insert_op_menu, insert_op_submenu[MAX_OP_MENUS];
582  CMenu *replace_variable_menu = NULL;
583  CMenu add_op_subcategory_menu[MAX_SUBMENUS];
584  CMenu replace_op_subcategory_menu[MAX_SUBMENUS];
585  CMenu insert_op_subcategory_menu[MAX_SUBMENUS];
586 
587  m_mode = mode;
592 
593  GetCursorPos(&mouse);
594  click_point = mouse;
595  ScreenToClient(&click_point);
596  h = HitTest(CPoint(click_point), &_flags); // find out what they clicked on
597 
598  if (h && menu.LoadMenu(IDR_MENU_EDIT_SEXP_TREE)) {
599  update_help(h);
600  popup_menu = menu.GetSubMenu(0);
601  ASSERT(popup_menu != NULL);
602  //SelectDropTarget(h); // WTF: Why was this here???
603 
604  add_op_menu = replace_op_menu = insert_op_menu = NULL;
605 
606  // get pointers to several key popup menus we'll need to modify
607  i = popup_menu->GetMenuItemCount();
608  while (i--) {
609  if ( (mptr = popup_menu->GetSubMenu(i)) > 0 ) {
610  popup_menu->GetMenuString(i, buf, sizeof(buf), MF_BYPOSITION);
611 
612  if (!stricmp(buf, "add operator")) {
613  add_op_menu = mptr;
614 
615  } else if (!stricmp(buf, "replace operator")) {
616  replace_op_menu = mptr;
617 
618  } else if (!stricmp(buf, "add data")) {
619  add_data_menu = mptr;
620 
621  } else if (!stricmp(buf, "replace data")) {
622  replace_data_menu = mptr;
623 
624  } else if (!stricmp(buf, "insert operator")) {
625  insert_op_menu = mptr;
626 
627  } else if (!stricmp(buf, "replace variable")) {
628  replace_variable_menu = mptr;
629  }
630  }
631  }
632 
633  // add popup menus for all the operator categories
634  for (i=0; i<Num_op_menus; i++)
635  {
636  add_op_submenu[i].CreatePopupMenu();
637  replace_op_submenu[i].CreatePopupMenu();
638  insert_op_submenu[i].CreatePopupMenu();
639 
640  add_op_menu->AppendMenu(MF_POPUP, (UINT) add_op_submenu[i].m_hMenu, op_menu[i].name);
641  replace_op_menu->AppendMenu(MF_POPUP, (UINT) replace_op_submenu[i].m_hMenu, op_menu[i].name);
642  insert_op_menu->AppendMenu(MF_POPUP, (UINT) insert_op_submenu[i].m_hMenu, op_menu[i].name);
643  }
644 
645  // get rid of the placeholders we needed to ensure popup menus stayed popup menus,
646  // i.e. MSDEV will convert empty popup menus into normal menu items.
647  add_op_menu->DeleteMenu(ID_PLACEHOLDER, MF_BYCOMMAND);
648  replace_op_menu->DeleteMenu(ID_PLACEHOLDER, MF_BYCOMMAND);
649  insert_op_menu->DeleteMenu(ID_PLACEHOLDER, MF_BYCOMMAND);
650  replace_variable_menu->DeleteMenu(ID_PLACEHOLDER, MF_BYCOMMAND);
651 
652  // get item_index
653  item_index = -1;
654  for (i=0; i<(int)tree_nodes.size(); i++) {
655  if (tree_nodes[i].handle == h) {
656  item_index = i;
657  break;
658  }
659  }
660 
661  /*
662  Goober5000 - allow variables in all modes;
663  the restriction seems unnecessary IMHO
664 
665  // Do SEXP_VARIABLE stuff here.
666  if (m_mode != MODE_EVENTS)
667  {
668  // only allow variables in event mode
669  menu.EnableMenuItem(ID_SEXP_TREE_ADD_VARIABLE, MF_GRAYED);
670  menu.EnableMenuItem(ID_SEXP_TREE_MODIFY_VARIABLE, MF_GRAYED);
671  }
672  else
673  */
674  {
675  menu.EnableMenuItem(ID_SEXP_TREE_ADD_VARIABLE, MF_ENABLED);
676  menu.EnableMenuItem(ID_SEXP_TREE_MODIFY_VARIABLE, MF_ENABLED);
677 
678  // check not root (-1)
679  if (item_index >= 0) {
680  // get type of sexp_tree item clicked on
681  int type = get_type(h);
682 
683  int parent = tree_nodes[item_index].parent;
684  if (parent >= 0) {
685  op = get_operator_index(tree_nodes[parent].text);
686  Assert(op >= 0);
687  int first_arg = tree_nodes[parent].child;
688 
689  // get arg count of item to replace
690  Replace_count = 0;
691  int temp = first_arg;
692  while (temp != item_index) {
693  Replace_count++;
694  temp = tree_nodes[temp].next;
695 
696  // DB - added 3/4/99
697  if(temp == -1){
698  break;
699  }
700  }
701 
702  int op_type = query_operator_argument_type(op, Replace_count); // check argument type at this position
703 
704  // special case don't allow replace data for variable names
705  // Goober5000 - why? the only place this happens is when replacing the ambiguous argument in
706  // modify-variable with a variable, which seems legal enough.
707  //if (op_type != OPF_AMBIGUOUS) {
708 
709  // Goober5000 - given the above, we have to figure out what type this stands for
710  if (op_type == OPF_AMBIGUOUS)
711  {
712  int modify_type = get_modify_variable_type(parent);
713  if (modify_type == OPF_NUMBER) {
714  type = SEXPT_NUMBER;
715  } else if (modify_type == OPF_AMBIGUOUS) {
716  type = SEXPT_STRING;
717  } else {
718  Int3();
719  type = tree_nodes[first_arg].type;
720  }
721  }
722 
723  // Goober5000 - certain types accept both integers and a list of strings
724  if (op_type == OPF_GAME_SND || op_type == OPF_WEAPON_BANK_NUMBER)
725  {
726  type = SEXPT_NUMBER | SEXPT_STRING;
727  }
728 
729  if ( (type & SEXPT_STRING) || (type & SEXPT_NUMBER) ) {
730 
731  int max_sexp_vars = MAX_SEXP_VARIABLES;
732  // prevent collisions in id numbers: ID_VARIABLE_MENU + 512 = ID_ADD_MENU
733  Assert(max_sexp_vars < 512);
734 
735  for (int idx=0; idx<max_sexp_vars; idx++) {
736  if (Sexp_variables[idx].type & SEXP_VARIABLE_SET) {
737  // skip block variables
738  if (Sexp_variables[idx].type & SEXP_VARIABLE_BLOCK) {
739  continue;
740  }
741 
742  UINT flag = MF_STRING | MF_GRAYED;
743  // maybe gray flag MF_GRAYED
744 
745  // get type -- gray "string" or number accordingly
746  if ( type & SEXPT_STRING ) {
747  if ( Sexp_variables[idx].type & SEXP_VARIABLE_STRING ) {
748  flag &= ~MF_GRAYED;
749  }
750  }
751  if ( type & SEXPT_NUMBER ) {
752  if ( Sexp_variables[idx].type & SEXP_VARIABLE_NUMBER ) {
753  flag &= ~MF_GRAYED;
754  }
755  }
756 
757  // if modify-variable and changing variable, enable all variables
758  if (op_type == OPF_VARIABLE_NAME) {
759  Modify_variable = 1;
760  flag &= ~MF_GRAYED;
761  } else {
762  Modify_variable = 0;
763  }
764 
765  // enable navsystem always
766  if (op_type == OPF_NAV_POINT)
767  flag &= ~MF_GRAYED;
768 
769  if (!( (idx + 3) % 30)) {
770  flag |= MF_MENUBARBREAK;
771  }
772 
773  char buf[128];
774  // append list of variable names and values
775  // set id as ID_VARIABLE_MENU + idx
776  sprintf(buf, "%s (%s)", Sexp_variables[idx].variable_name, Sexp_variables[idx].text);
777 
778  replace_variable_menu->AppendMenu(flag, (ID_VARIABLE_MENU + idx), buf);
779  }
780  }
781  }
782  //}
783  }
784  }
785 
786  // can't modify if no variables
787  if (sexp_variable_count() == 0) {
788  menu.EnableMenuItem(ID_SEXP_TREE_MODIFY_VARIABLE, MF_GRAYED);
789  }
790  }
791 
792  // add all the submenu items first
793  for (i=0; i<Num_submenus; i++)
794  {
795  add_op_subcategory_menu[i].CreatePopupMenu();
796  replace_op_subcategory_menu[i].CreatePopupMenu();
797  insert_op_subcategory_menu[i].CreatePopupMenu();
798 
799  for (j=0; j<Num_op_menus; j++)
800  {
801  if (op_menu[j].id == category_of_subcategory(op_submenu[i].id))
802  {
803  add_op_submenu[j].AppendMenu(MF_POPUP, (UINT) add_op_subcategory_menu[i].m_hMenu, op_submenu[i].name);
804  replace_op_submenu[j].AppendMenu(MF_POPUP, (UINT) replace_op_subcategory_menu[i].m_hMenu, op_submenu[i].name);
805  insert_op_submenu[j].AppendMenu(MF_POPUP, (UINT) insert_op_subcategory_menu[i].m_hMenu, op_submenu[i].name);
806  break; // only 1 category valid
807  }
808  }
809  }
810 
811  // add operator menu items to the various CATEGORY submenus they belong in
812  for (i=0; i<Num_operators; i++)
813  {
814  // add only if it is not in a subcategory
815  subcategory_id = get_subcategory(Operators[i].value);
816  if (subcategory_id == -1)
817  {
818  // put it in the appropriate menu
819  for (j=0; j<Num_op_menus; j++)
820  {
821  if (op_menu[j].id == get_category(Operators[i].value))
822  {
823  switch (Operators[i].value) {
824 // Commented out by Goober5000 to allow these operators to be selectable
825 /*#ifdef NDEBUG
826  // various campaign operators
827  case OP_WAS_PROMOTION_GRANTED:
828  case OP_WAS_MEDAL_GRANTED:
829  case OP_GRANT_PROMOTION:
830  case OP_GRANT_MEDAL:
831  case OP_TECH_ADD_SHIP:
832  case OP_TECH_ADD_WEAPON:
833  case OP_TECH_ADD_INTEL_XSTR:
834  case OP_TECH_RESET_TO_DEFAULT:
835 #endif*/
836  // unlike the above operators, these are deprecated
839  case OP_ORDER:
840  case OP_TECH_ADD_INTEL:
843  j = Num_op_menus; // don't allow these operators to be visible
844  break;
845  }
846 
847  if (j < Num_op_menus) {
848  add_op_submenu[j].AppendMenu(MF_STRING | MF_GRAYED, Operators[i].value, Operators[i].text);
849  replace_op_submenu[j].AppendMenu(MF_STRING | MF_GRAYED, Operators[i].value | OP_REPLACE_FLAG, Operators[i].text);
850  insert_op_submenu[j].AppendMenu(MF_STRING, Operators[i].value | OP_INSERT_FLAG, Operators[i].text);
851  }
852 
853  break; // only 1 category valid
854  }
855  }
856  }
857  // if it is in a subcategory, handle it
858  else
859  {
860  // put it in the appropriate submenu
861  for (j=0; j<Num_submenus; j++)
862  {
863  if (op_submenu[j].id == subcategory_id)
864  {
865  switch (Operators[i].value) {
866 // Commented out by Goober5000 to allow these operators to be selectable
867 /*#ifdef NDEBUG
868  // various campaign operators
869  case OP_WAS_PROMOTION_GRANTED:
870  case OP_WAS_MEDAL_GRANTED:
871  case OP_GRANT_PROMOTION:
872  case OP_GRANT_MEDAL:
873  case OP_TECH_ADD_SHIP:
874  case OP_TECH_ADD_WEAPON:
875  case OP_TECH_ADD_INTEL_XSTR:
876  case OP_TECH_RESET_TO_DEFAULT:
877 #endif*/
878  // unlike the above operators, these are deprecated
881  case OP_ORDER:
882  case OP_TECH_ADD_INTEL:
885  j = Num_submenus; // don't allow these operators to be visible
886  break;
887  }
888 
889  if (j < Num_submenus) {
890  add_op_subcategory_menu[j].AppendMenu(MF_STRING | MF_GRAYED, Operators[i].value, Operators[i].text);
891  replace_op_subcategory_menu[j].AppendMenu(MF_STRING | MF_GRAYED, Operators[i].value | OP_REPLACE_FLAG, Operators[i].text);
892  insert_op_subcategory_menu[j].AppendMenu(MF_STRING, Operators[i].value | OP_INSERT_FLAG, Operators[i].text);
893  }
894 
895  break; // only 1 subcategory valid
896  }
897  }
898  }
899  }
900 
901  // find local index (i) of current item (from its handle)
902  SelectItem(item_handle = h);
903  for (i=0; i<(int)tree_nodes.size(); i++) {
904  if (tree_nodes[i].handle == h) {
905  break;
906  }
907  }
908 
909  // special case: item is a ROOT node, and a label that can be edited (not an item in the sexp tree)
910  if ((item_index == -1) && (m_mode & ST_LABELED_ROOT)) {
911  if (m_mode & ST_ROOT_EDITABLE) {
912  menu.EnableMenuItem(ID_EDIT_TEXT, MF_ENABLED);
913  } else {
914  menu.EnableMenuItem(ID_EDIT_TEXT, MF_GRAYED);
915  }
916 
917  // disable copy, insert op
918  menu.EnableMenuItem(ID_EDIT_COPY, MF_GRAYED);
919  for (j=0; j<Num_operators; j++) {
920  menu.EnableMenuItem(Operators[j].value | OP_INSERT_FLAG, MF_GRAYED);
921  }
922 
923  gray_menu_tree(popup_menu);
924  popup_menu->TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON, mouse.x, mouse.y, this);
925  return;
926  }
927 
928  Assert(item_index != -1); // handle not found, which should be impossible.
929  if (!(tree_nodes[item_index].flags & EDITABLE)) {
930  menu.EnableMenuItem(ID_EDIT_TEXT, MF_GRAYED);
931  }
932 
933  if (tree_nodes[item_index].parent == -1) { // root node
934  menu.EnableMenuItem(ID_DELETE, MF_GRAYED); // can't delete the root item.
935  }
936 
937 /* if ((tree_nodes[item_index].flags & OPERAND) && (tree_nodes[item_index].flags & EDITABLE)) // expandable?
938  menu.EnableMenuItem(ID_SPLIT_LINE, MF_ENABLED);
939 
940  z = tree_nodes[item_index].child;
941  if (z != -1 && tree_nodes[z].next == -1 && tree_nodes[z].child == -1)
942  menu.EnableMenuItem(ID_SPLIT_LINE, MF_ENABLED);
943 
944  z = tree_nodes[tree_nodes[item_index].parent].child;
945  if (z != -1 && tree_nodes[z].next == -1 && tree_nodes[z].child == -1)
946  menu.EnableMenuItem(ID_SPLIT_LINE, MF_ENABLED);*/
947 
948  // change enabled status of 'add' type menu options.
949  add_type = 0;
951  add_type = OPR_STRING;
952  int child = tree_nodes[item_index].child;
953  Add_count = count_args(child);
955  Assert(op >= 0);
956 
957  // get listing of valid argument values and add to menus
958  type = query_operator_argument_type(op, Add_count);
959  list = get_listing_opf(type, item_index, Add_count);
960  if (list) {
961  sexp_list_item *ptr;
962 
963  int data_idx = 0;
964  ptr = list;
965  while (ptr) {
966  if (ptr->op >= 0) {
967  // enable operators with correct return type
968  menu.EnableMenuItem(Operators[ptr->op].value, MF_ENABLED);
969 
970  } else {
971  // add data
972  if ( (data_idx + 3) % 30) {
973  add_data_menu->AppendMenu(MF_STRING | MF_ENABLED, ID_ADD_MENU + data_idx, ptr->text);
974  } else {
975  add_data_menu->AppendMenu(MF_MENUBARBREAK | MF_STRING | MF_ENABLED, ID_ADD_MENU + data_idx, ptr->text);
976  }
977  }
978 
979  data_idx++;
980  ptr = ptr->next;
981  }
982  }
983 
984  // special handling for the non-string formats
985  if (type == OPF_NONE) { // an argument can't be added
986  add_type = 0;
987 
988  } else if (type == OPF_NULL) { // arguments with no return values
989  add_type = OPR_NULL;
990 
991  // Goober5000
992  } else if (type == OPF_FLEXIBLE_ARGUMENT) {
993  add_type = OPR_FLEXIBLE_ARGUMENT;
994 
995  } else if (type == OPF_NUMBER) { // takes numbers
996  add_type = OPR_NUMBER;
997  menu.EnableMenuItem(ID_ADD_NUMBER, MF_ENABLED);
998 
999  } else if (type == OPF_POSITIVE) { // takes non-negative numbers
1000  add_type = OPR_POSITIVE;
1001  menu.EnableMenuItem(ID_ADD_NUMBER, MF_ENABLED);
1002 
1003  } else if (type == OPF_BOOL) { // takes true/false bool values
1004  add_type = OPR_BOOL;
1005 
1006  } else if (type == OPF_AI_GOAL) {
1007  add_type = OPR_AI_GOAL;
1008  }
1009 
1010  // add_type unchanged from above
1011  if (add_type == OPR_STRING) {
1012  menu.EnableMenuItem(ID_ADD_STRING, MF_ENABLED);
1013  }
1014 
1015  list->destroy();
1016  }
1017 
1018  // disable operators that do not have arguments available
1019  for (j=0; j<Num_operators; j++) {
1021  menu.EnableMenuItem(Operators[j].value, MF_GRAYED);
1022  }
1023  }
1024 
1025 
1026  // change enabled status of 'replace' type menu options.
1027  replace_type = 0;
1028  int parent = tree_nodes[item_index].parent;
1029  if (parent >= 0) {
1030  replace_type = OPR_STRING;
1031  op = get_operator_index(tree_nodes[parent].text);
1032  Assert(op >= 0);
1033  int first_arg = tree_nodes[parent].child;
1034  count = count_args(tree_nodes[parent].child);
1035 
1036  // already at minimum number of arguments?
1037  if (count <= Operators[op].min) {
1038  menu.EnableMenuItem(ID_DELETE, MF_GRAYED);
1039  }
1040 
1041  // get arg count of item to replace
1042  Replace_count = 0;
1043  int temp = first_arg;
1044  while (temp != item_index) {
1045  Replace_count++;
1046  temp = tree_nodes[temp].next;
1047 
1048  // DB - added 3/4/99
1049  if(temp == -1){
1050  break;
1051  }
1052  }
1053 
1054  // maybe gray delete
1055  for (i=Replace_count+1; i<count; i++) {
1057  menu.EnableMenuItem(ID_DELETE, MF_GRAYED);
1058  break;
1059  }
1060  }
1061 
1062  type = query_operator_argument_type(op, Replace_count); // check argument type at this position
1063 
1064  // special case reset type for ambiguous
1065  if (type == OPF_AMBIGUOUS) {
1066  type = get_modify_variable_type(parent);
1067  }
1068 
1069  list = get_listing_opf(type, parent, Replace_count);
1070 
1071  // special case don't allow replace data for variable names
1072  if ( (type != OPF_VARIABLE_NAME) && list) {
1073  sexp_list_item *ptr;
1074 
1075  int data_idx = 0;
1076  ptr = list;
1077  while (ptr) {
1078  if (ptr->op >= 0) {
1079  menu.EnableMenuItem(Operators[ptr->op].value | OP_REPLACE_FLAG, MF_ENABLED);
1080 
1081  } else {
1082  if ( (data_idx + 3) % 30)
1083  replace_data_menu->AppendMenu(MF_STRING | MF_ENABLED, ID_REPLACE_MENU + data_idx, ptr->text);
1084  else
1085  replace_data_menu->AppendMenu(MF_MENUBARBREAK | MF_STRING | MF_ENABLED, ID_REPLACE_MENU + data_idx, ptr->text);
1086  }
1087 
1088  data_idx++;
1089  ptr = ptr->next;
1090  }
1091  }
1092 
1093  if (type == OPF_NONE) { // takes no arguments
1094  replace_type = 0;
1095 
1096  } else if (type == OPF_NUMBER) { // takes numbers
1097  replace_type = OPR_NUMBER;
1098  menu.EnableMenuItem(ID_REPLACE_NUMBER, MF_ENABLED);
1099 
1100  } else if (type == OPF_POSITIVE) { // takes non-negative numbers
1101  replace_type = OPR_POSITIVE;
1102  menu.EnableMenuItem(ID_REPLACE_NUMBER, MF_ENABLED);
1103 
1104  } else if (type == OPF_BOOL) { // takes true/false bool values
1105  replace_type = OPR_BOOL;
1106 
1107  } else if (type == OPF_NULL) { // takes operator that doesn't return a value
1108  replace_type = OPR_NULL;
1109  } else if (type == OPF_AI_GOAL) {
1110  replace_type = OPR_AI_GOAL;
1111  }
1112 
1113  // Goober5000
1114  else if (type == OPF_FLEXIBLE_ARGUMENT) {
1115  replace_type = OPR_FLEXIBLE_ARGUMENT;
1116  }
1117  // Goober5000
1118  else if (type == OPF_GAME_SND || type == OPF_WEAPON_BANK_NUMBER) {
1119  // enable number even though we are also going to default to string
1120  menu.EnableMenuItem(ID_REPLACE_NUMBER, MF_ENABLED);
1121  }
1122 
1123  // default to string
1124  if (replace_type == OPR_STRING) {
1125  menu.EnableMenuItem(ID_REPLACE_STRING, MF_ENABLED);
1126  }
1127 
1128  // modify string or number if (modify_variable)
1129  if ( Operators[op].value == OP_MODIFY_VARIABLE ) {
1130  int modify_type = get_modify_variable_type(parent);
1131 
1132  if (modify_type == OPF_NUMBER) {
1133  menu.EnableMenuItem(ID_REPLACE_NUMBER, MF_ENABLED);
1134  menu.EnableMenuItem(ID_REPLACE_STRING, MF_GRAYED);
1135  }
1136  // no change for string type
1137  }
1138  else if ( Operators[op].value == OP_SET_VARIABLE_BY_INDEX ) {
1139  // it depends on which argument we are modifying
1140  // first argument is always a number
1141  if (Replace_count == 0) {
1142  menu.EnableMenuItem(ID_REPLACE_NUMBER, MF_ENABLED);
1143  menu.EnableMenuItem(ID_REPLACE_STRING, MF_GRAYED);
1144  }
1145  // second argument could be anything
1146  else {
1147  int modify_type = get_modify_variable_type(parent);
1148 
1149  if (modify_type == OPF_NUMBER) {
1150  menu.EnableMenuItem(ID_REPLACE_NUMBER, MF_ENABLED);
1151  menu.EnableMenuItem(ID_REPLACE_STRING, MF_GRAYED);
1152  }
1153  // no change for string type
1154  }
1155  }
1156 
1157  list->destroy();
1158 
1159  } else { // top node, so should be a Boolean type.
1160  if (m_mode == MODE_EVENTS) { // return type should be null
1161  replace_type = OPR_NULL;
1162  for (j=0; j<Num_operators; j++)
1164  menu.EnableMenuItem(Operators[j].value | OP_REPLACE_FLAG, MF_ENABLED);
1165 
1166  } else {
1167  replace_type = OPR_BOOL;
1168  for (j=0; j<Num_operators; j++)
1170  menu.EnableMenuItem(Operators[j].value | OP_REPLACE_FLAG, MF_ENABLED);
1171  }
1172  }
1173 
1174  // disable operators that do not have arguments available
1175  for (j=0; j<Num_operators; j++) {
1177  menu.EnableMenuItem(Operators[j].value | OP_REPLACE_FLAG, MF_GRAYED);
1178  }
1179  }
1180 
1181 
1182  // change enabled status of 'insert' type menu options.
1183  z = tree_nodes[item_index].parent;
1184  Assert(z >= -1);
1185  if (z != -1) {
1186  op = get_operator_index(tree_nodes[z].text);
1187  Assert(op != -1);
1188  j = tree_nodes[z].child;
1189  count = 0;
1190  while (j != item_index) {
1191  count++;
1192  j = tree_nodes[j].next;
1193  }
1194 
1195  type = query_operator_argument_type(op, count); // check argument type at this position
1196 
1197  } else {
1198  if (m_mode == MODE_EVENTS)
1199  type = OPF_NULL;
1200  else
1201  type = OPF_BOOL;
1202  }
1203 
1204  for (j=0; j<Num_operators; j++) {
1206  if (!sexp_query_type_match(type, z) || (Operators[j].min < 1))
1207  menu.EnableMenuItem(Operators[j].value | OP_INSERT_FLAG, MF_GRAYED);
1208 
1209  z = query_operator_argument_type(j, 0);
1210  if ((type == OPF_NUMBER) && (z == OPF_POSITIVE))
1211  z = OPF_NUMBER;
1212 
1213  // Goober5000's number hack
1214  if ((type == OPF_POSITIVE) && (z == OPF_NUMBER))
1215  z = OPF_POSITIVE;
1216 
1217  if (z != type)
1218  menu.EnableMenuItem(Operators[j].value | OP_INSERT_FLAG, MF_GRAYED);
1219  }
1220 
1221  // disable operators that do not have arguments available
1222  for (j=0; j<Num_operators; j++) {
1224  menu.EnableMenuItem(Operators[j].value | OP_INSERT_FLAG, MF_GRAYED);
1225  }
1226  }
1227 
1228 
1229  // disable non campaign operators if in campaign mode
1230  for (j=0; j<Num_operators; j++) {
1231  z = 0;
1232  if (m_mode == MODE_CAMPAIGN) {
1234  z = 1;
1235 
1236  } else {
1238  z = 1;
1239  }
1240 
1241  if (z) {
1242  menu.EnableMenuItem(Operators[j].value, MF_GRAYED);
1243  menu.EnableMenuItem(Operators[j].value | OP_REPLACE_FLAG, MF_GRAYED);
1244  menu.EnableMenuItem(Operators[j].value | OP_INSERT_FLAG, MF_GRAYED);
1245  }
1246  }
1247 
1248  if ((Sexp_clipboard > -1) && (Sexp_nodes[Sexp_clipboard].type != SEXP_NOT_USED)) {
1250 
1253  Assert(j);
1255 
1256  if ((z == OPR_POSITIVE) && (replace_type == OPR_NUMBER))
1257  z = OPR_NUMBER;
1258 
1259  // Goober5000's number hack
1260  if ((z == OPR_NUMBER) && (replace_type == OPR_POSITIVE))
1261  z = OPR_POSITIVE;
1262 
1263  if (replace_type == z)
1264  menu.EnableMenuItem(ID_EDIT_PASTE, MF_ENABLED);
1265 
1267  if ((z == OPR_POSITIVE) && (add_type == OPR_NUMBER))
1268  z = OPR_NUMBER;
1269 
1270  if (add_type == z)
1271  menu.EnableMenuItem(ID_EDIT_PASTE_SPECIAL, MF_ENABLED);
1272 
1274  if ((replace_type == OPR_POSITIVE) && (atoi(CTEXT(Sexp_clipboard)) > -1))
1275  menu.EnableMenuItem(ID_EDIT_PASTE, MF_ENABLED);
1276 
1277  else if (replace_type == OPR_NUMBER)
1278  menu.EnableMenuItem(ID_EDIT_PASTE, MF_ENABLED);
1279 
1280  if ((add_type == OPR_POSITIVE) && (atoi(CTEXT(Sexp_clipboard)) > -1))
1281  menu.EnableMenuItem(ID_EDIT_PASTE_SPECIAL, MF_ENABLED);
1282 
1283  else if (add_type == OPR_NUMBER)
1284  menu.EnableMenuItem(ID_EDIT_PASTE_SPECIAL, MF_ENABLED);
1285 
1287  if (replace_type == OPR_STRING)
1288  menu.EnableMenuItem(ID_EDIT_PASTE, MF_ENABLED);
1289 
1290  if (add_type == OPR_STRING)
1291  menu.EnableMenuItem(ID_EDIT_PASTE_SPECIAL, MF_ENABLED);
1292 
1293  } else
1294  Int3(); // unknown and/or invalid sexp type
1295  }
1296 
1297  if (!(menu.GetMenuState(ID_DELETE, MF_BYCOMMAND) & MF_GRAYED))
1298  menu.EnableMenuItem(ID_EDIT_CUT, MF_ENABLED);
1299 
1300  gray_menu_tree(popup_menu);
1301  popup_menu->TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON, mouse.x, mouse.y, this);
1302  }
1303 }
1304 
1305 // counts the number of arguments an operator has. Call this with the node of the first
1306 // argument of the operator
1308 {
1309  int count = 0;
1310 
1311  while (node != -1) {
1312  count++;
1313  node = tree_nodes[node].next;
1314  }
1315 
1316  return count;
1317 }
1318 
1319 // identify what type of argument this is. You call it with the node of the first argument
1320 // of an operator. It will search through enough of the arguments to determine what type of
1321 // data they are.
1323 {
1324  int type = -1;
1325 
1326  while (node != -1) {
1327  Assert(tree_nodes[node].type & SEXPT_VALID);
1328  switch (SEXPT_TYPE(tree_nodes[node].type)) {
1329  case SEXPT_OPERATOR:
1330  type = get_operator_const(tree_nodes[node].text);
1331  Assert(type);
1332  return query_operator_return_type(type);
1333 
1334  case SEXPT_NUMBER:
1335  return OPR_NUMBER;
1336 
1337  case SEXPT_STRING: // either a ship or a wing
1338  type = SEXP_ATOM_STRING;
1339  break; // don't return, because maybe we can narrow selection down more.
1340  }
1341 
1342  node = tree_nodes[node].next;
1343  }
1344 
1345  return type;
1346 }
1347 
1348 // determine if an item should be editable. This doesn't actually edit the label.
1349 int sexp_tree::edit_label(HTREEITEM h)
1350 {
1351  uint i;
1352 
1353  for (i=0; i<tree_nodes.size(); i++) {
1354  if (tree_nodes[i].handle == h) {
1355  break;
1356  }
1357  }
1358 
1359  // Check if tree root
1360  if (i == tree_nodes.size()) {
1361  if (m_mode & ST_ROOT_EDITABLE) {
1362  return 1;
1363  }
1364 
1365  return 0;
1366  }
1367 
1368  // Operators are editable
1369  if (tree_nodes[i].type & SEXPT_OPERATOR) {
1370  return 1;
1371  }
1372 
1373  // Variables must be edited through dialog box
1374  if (tree_nodes[i].type & SEXPT_VARIABLE) {
1375  return 0;
1376  }
1377 
1378  // Don't edit if not flaged as editable
1379  if (!(tree_nodes[i].flags & EDITABLE)) {
1380  return 0;
1381  }
1382 
1383  // Otherwise, allow editing
1384  return 1;
1385 
1386 /*
1387  if (tree_nodes[i].flags & OPERAND) {
1388  data = tree_nodes[i].child;
1389 
1390  SetItemText(h, tree_nodes[i].text);
1391  tree_nodes[i].flags = OPERAND;
1392  item_handle = tree_nodes[data].handle = insert(tree_nodes[data].text, tree_nodes[data].type, tree_nodes[data].flags, h);
1393  tree_nodes[data].flags = EDITABLE;
1394  Expand(h, TVE_EXPAND);
1395  SelectItem(item_handle);
1396  return 2;
1397  }
1398 */
1399 }
1400 
1401 // given a tree node, returns the argument type it should be.
1403 {
1404  int argnum = 0;
1405  int parent_node = tree_nodes[node].parent;
1406  Assert(parent_node >= 0);
1407  argnum = find_argument_number(parent_node, node);
1408  int op_num = get_operator_index(tree_nodes[parent_node].text);
1409  return query_operator_argument_type(op_num, argnum);
1410 }
1411 
1412 int sexp_tree::end_label_edit(TVITEMA &item)
1413 {
1414  HTREEITEM h = item.hItem;
1415  char *str = item.pszText;
1416  int len, r = 1;
1417  bool update_node = true;
1418  uint node;
1419 
1420  *modified = 1;
1421  if (!str)
1422  return 0;
1423 
1424  // let's make sure we aren't introducing any invalid characters, per Mantis #2893
1426 
1427  for (node=0; node<tree_nodes.size(); node++)
1428  if (tree_nodes[node].handle == h)
1429  break;
1430 
1431  if (node == tree_nodes.size()) {
1432  if (m_mode == MODE_EVENTS) {
1433  item_index = GetItemData(h);
1436  return 1;
1437 
1438  } else
1439  Int3(); // root labels shouldn't have been editable!
1440  }
1441 
1442  Assert(node < tree_nodes.size());
1443  if (tree_nodes[node].type & SEXPT_OPERATOR) {
1444  const char *op = match_closest_operator(str, node);
1445  if (!op) return 0; // Goober5000 - avoids crashing
1446 
1447  SetItemText(h, op);
1448  item_index = node;
1449  int op_num = get_operator_index(op);
1450  if (op_num >= 0 ) {
1451  add_or_replace_operator(op_num, 1);
1452  }
1453  else {
1454  update_node = false;
1455  }
1456  r = 0;
1457  }
1458 
1459  // gotta sidestep Goober5000's number hack and check entries are actually positive.
1460  else if (tree_nodes[node].type & SEXPT_NUMBER) {
1461  if (query_node_argument_type(node) == OPF_POSITIVE) {
1462  int val = atoi(str);
1463  if (val < 0) {
1464  MessageBox("Can not enter a negative value", "Invalid Number", MB_ICONEXCLAMATION);
1465  update_node = false;
1466  }
1467  }
1468  }
1469 
1470  // Error checking would not hurt here
1471  len = strlen(str);
1472  if (len >= TOKEN_LENGTH)
1473  len = TOKEN_LENGTH - 1;
1474 
1475  if (update_node) {
1476  strncpy(tree_nodes[node].text, str, len);
1477  tree_nodes[node].text[len] = 0;
1478  }
1479  else {
1480  strncpy(str, tree_nodes[node].text, len);
1481  return 1;
1482  }
1483 
1484 
1485 /* node = tree_nodes[node].parent;
1486  if (node != -1) {
1487  child = tree_nodes[node].child;
1488  if (child != -1 && tree_nodes[child].next == -1 && tree_nodes[child].child == -1) {
1489  merge_operator(child);
1490  return 0;
1491  }
1492  }*/
1493 
1494  return r;
1495 }
1496 
1497 // Look for the valid operator that is the closest match for 'str' and return the operator
1498 // number of it. What operators are valid is determined by 'node', and an operator is valid
1499 // if it is allowed to fit at position 'node'
1500 //
1501 const char *sexp_tree::match_closest_operator(const char *str, int node)
1502 {
1503  int z, i, op, arg_num, opf, opr;
1504  const char *sub_best = NULL, *best = NULL;
1505 
1506  z = tree_nodes[node].parent;
1507  if (z < 0) {
1508  return str;
1509  }
1510 
1511  op = get_operator_index(tree_nodes[z].text);
1512  if (op < 0)
1513  return str;
1514 
1515  // determine which argument we are of the parent
1516  arg_num = find_argument_number(z, node);
1517 
1518  opf = query_operator_argument_type(op, arg_num); // check argument type at this position
1519  opr = query_operator_return_type(op);
1520  for (i=0; i<Num_operators; i++) {
1521  if (sexp_query_type_match(opf, opr)) {
1522  if ( (stricmp(str, Operators[i].text) <= 0) && (!best || (stricmp(str, best) < 0)) )
1523  best = Operators[i].text;
1524 
1525  if ( !sub_best || (stricmp(Operators[i].text, sub_best) > 0) )
1526  sub_best = Operators[i].text;
1527  }
1528  }
1529 
1530  if (!best)
1531  best = sub_best; // no best found, use our plan #2 best found.
1532 
1533  Assert(best); // we better have some valid operator at this point.
1534  return best;
1535 
1536 /* char buf[256];
1537  int child;
1538 
1539  if (tree_nodes[node].flags == EDITABLE) // data
1540  node = tree_nodes[node].parent;
1541 
1542  if (node != -1) {
1543  child = tree_nodes[node].child;
1544  if (child != -1 && tree_nodes[child].next == -1 && tree_nodes[child].child == -1) {
1545  sprintf(buf, "%s %s", tree_nodes[node].text, tree_nodes[child].text);
1546  SetItemText(tree_nodes[node].handle, buf);
1547  tree_nodes[node].flags = OPERAND | EDITABLE;
1548  tree_nodes[child].flags = COMBINED;
1549  DeleteItem(tree_nodes[child].handle);
1550  tree_nodes[child].handle = NULL;
1551  return;
1552  }
1553  }*/
1554 }
1555 
1556 // this really only handles messages generated by the right click popup menu
1558 {
1559  int i, z, id, node, op, type;
1560  sexp_list_item *list, *ptr;
1561  HTREEITEM h;
1562 
1563  if ((item_index >= 0) && (item_index < total_nodes))
1564  item_handle = tree_nodes[item_index].handle;
1565 
1566  id = LOWORD(wParam);
1567 
1568 
1569  // Add variable
1570  if (id == ID_SEXP_TREE_ADD_VARIABLE) {
1571  CAddVariableDlg dlg;
1572  dlg.DoModal();
1573 
1574  if ( dlg.m_create ) {
1575 
1576  // set type
1577  int type;
1578  if ( dlg.m_type_number ) {
1579  type = SEXP_VARIABLE_NUMBER;
1580  } else {
1581  type = SEXP_VARIABLE_STRING;
1582  }
1583 
1584  if ( dlg.m_type_network_variable ) {
1585  type |= SEXP_VARIABLE_NETWORK;
1586  }
1587 
1588  if ( dlg.m_type_campaign_persistent ) {
1590  } else if ( dlg.m_type_player_persistent ) {
1592  }
1593 
1594  // add variable
1596 
1597  // sort variable
1599  }
1600  return 1;
1601  }
1602 
1603  // Modify variable
1604  if (id == ID_SEXP_TREE_MODIFY_VARIABLE) {
1605  CModifyVariableDlg dlg;
1606 
1607  // get sexp_variable index for item index
1609 
1610  // get pointer to tree
1611  dlg.m_p_sexp_tree = this;
1612 
1613  dlg.DoModal();
1614 
1615  Assert( !(dlg.m_deleted && dlg.m_do_modify) );
1616 
1617  if (dlg.m_deleted) {
1618  // find index in sexp_variable list
1619  int sexp_var_index = get_index_sexp_variable_name(dlg.m_cur_variable_name);
1620  Assert(sexp_var_index != -1);
1621 
1622  // delete from list
1623  sexp_variable_delete(sexp_var_index);
1624 
1625  // sort list
1627 
1628  // delete from sexp_tree, replacing with "number" or "string" as needed
1629  // further error checking from add_data()
1631 
1632  return 1;
1633  }
1634 
1635  if (dlg.m_do_modify) {
1636  // check sexp_tree -- warn on type
1637  // find index and change either (1) name, (2) type, (3) value
1638  int sexp_var_index = get_index_sexp_variable_name(dlg.m_old_var_name);
1639  Assert(sexp_var_index != -1);
1640 
1641  // save old name, since name may be modified
1642  char old_name[TOKEN_LENGTH];
1643  strcpy_s(old_name, Sexp_variables[sexp_var_index].variable_name);
1644 
1645  // set type
1646  int type;
1647  if (dlg.m_type_number) {
1648  type = SEXP_VARIABLE_NUMBER;
1649  } else {
1650  type = SEXP_VARIABLE_STRING;
1651  }
1652 
1653  if ( dlg.m_type_network_variable ) {
1654  type |= SEXP_VARIABLE_NETWORK;
1655  }
1656 
1657  if ( dlg.m_type_campaign_persistent ) {
1659  } else if ( dlg.m_type_player_persistent ) {
1661  }
1662 
1663  // update sexp_variable
1664  sexp_fred_modify_variable(dlg.m_default_value, dlg.m_cur_variable_name, sexp_var_index, type);
1665 
1666  // modify sexp_tree
1667  modify_sexp_tree_variable(old_name, sexp_var_index);
1668 
1669  // Don't sort until after modify, since modify uses index
1670  if (dlg.m_modified_name) {
1672  }
1673 
1674  return 1;
1675  }
1676 
1677  // no change
1678  return 1;
1679  }
1680 
1681 
1682  // check if REPLACE_VARIABLE_MENU
1683  if ( (id >= ID_VARIABLE_MENU) && (id < ID_VARIABLE_MENU + 511)) {
1684 
1685  Assert(item_index >= 0);
1686 
1687  // get index into list of type valid variables
1688  int var_idx = id - ID_VARIABLE_MENU;
1689  Assert( (var_idx >= 0) && (var_idx < MAX_SEXP_VARIABLES) );
1690 
1691  int type = get_type(item_handle);
1692  Assert( (type & SEXPT_NUMBER) || (type & SEXPT_STRING) );
1693 
1694  // don't do type check for modify-variable
1695  if (Modify_variable) {
1696  if (Sexp_variables[var_idx].type & SEXP_VARIABLE_NUMBER) {
1697  type = SEXPT_NUMBER;
1698  } else if (Sexp_variables[var_idx].type & SEXP_VARIABLE_STRING) {
1699  type = SEXPT_STRING;
1700  } else {
1701  Int3(); // unknown type
1702  }
1703 
1704  } else {
1705  // verify type in tree is same as type in Sexp_variables array
1706  if (type & SEXPT_NUMBER) {
1707  Assert(Sexp_variables[var_idx].type & SEXP_VARIABLE_NUMBER);
1708  }
1709 
1710  if (type & SEXPT_STRING) {
1711  Assert( (Sexp_variables[var_idx].type & SEXP_VARIABLE_STRING) );
1712  }
1713  }
1714 
1715  // Replace data
1716  replace_variable_data(var_idx, (type | SEXPT_VARIABLE));
1717 
1718  return 1;
1719  }
1720 
1721 
1722  if ((id >= ID_ADD_MENU) && (id < ID_ADD_MENU + 511)) {
1723  Assert(item_index >= 0);
1725  Assert(op >= 0);
1726 
1727  type = query_operator_argument_type(op, Add_count);
1728  list = get_listing_opf(type, item_index, Add_count);
1729  Assert(list);
1730 
1731  id -= ID_ADD_MENU;
1732  ptr = list;
1733  while (id) {
1734  id--;
1735  ptr = ptr->next;
1736  Assert(ptr);
1737  }
1738 
1739  Assert((SEXPT_TYPE(ptr->type) != SEXPT_OPERATOR) && (ptr->op < 0));
1741  add_data(ptr->text, ptr->type);
1742  list->destroy();
1743  return 1;
1744  }
1745 
1746  if ((id >= ID_REPLACE_MENU) && (id < ID_REPLACE_MENU + 511)) {
1747  Assert(item_index >= 0);
1748  Assert(tree_nodes[item_index].parent >= 0);
1749  op = get_operator_index(tree_nodes[tree_nodes[item_index].parent].text);
1750  Assert(op >= 0);
1751 
1752  type = query_operator_argument_type(op, Replace_count); // check argument type at this position
1753  list = get_listing_opf(type, tree_nodes[item_index].parent, Replace_count);
1754  Assert(list);
1755 
1756  id -= ID_REPLACE_MENU;
1757  ptr = list;
1758  while (id) {
1759  id--;
1760  ptr = ptr->next;
1761  Assert(ptr);
1762  }
1763 
1764  Assert((SEXPT_TYPE(ptr->type) != SEXPT_OPERATOR) && (ptr->op < 0));
1766  replace_data(ptr->text, ptr->type);
1767  list->destroy();
1768  return 1;
1769  }
1770 
1771  for (op=0; op<Num_operators; op++) {
1772  if (id == Operators[op].value) {
1774  return 1;
1775  }
1776 
1777  if (id == (Operators[op].value | OP_REPLACE_FLAG)) {
1778  add_or_replace_operator(op, 1);
1780  return 1;
1781  }
1782 
1783  if (id == (Operators[op].value | OP_INSERT_FLAG)) {
1784  int flags;
1785 
1786  z = tree_nodes[item_index].parent;
1787  flags = tree_nodes[item_index].flags;
1788  node = allocate_node(z, item_index);
1789  set_node(node, (SEXPT_OPERATOR | SEXPT_VALID), Operators[op].text);
1790  tree_nodes[node].flags = flags;
1791  if (z >= 0)
1792  h = tree_nodes[z].handle;
1793 
1794  else {
1795  h = GetParentItem(tree_nodes[item_index].handle);
1796  if (m_mode == MODE_GOALS) {
1799  SetItemData(h, node);
1800 
1801  } else if (m_mode == MODE_EVENTS) {
1804  SetItemData(h, node);
1805 
1806  } else if (m_mode == MODE_CAMPAIGN) {
1808  SetItemData(h, node);
1809 
1810  } else {
1811  h = TVI_ROOT;
1812  root_item = node;
1813  }
1814  }
1815 
1817  move_branch(item_index, node);
1818 
1819  item_index = node;
1820  for (i=1; i<Operators[op].min; i++)
1821  add_default_operator(op, i);
1822 
1823  Expand(item_handle, TVE_EXPAND);
1824  *modified = 1;
1825  return 1;
1826  }
1827  }
1828 
1829  switch (id) {
1830  case ID_EDIT_COPY:
1831  // If a clipboard already exist, unmark it as persistent and free old clipboard
1832  if (Sexp_clipboard != -1) {
1835  }
1836 
1837  // Allocate new clipboard and mark persistent
1840  return 1;
1841 
1842  case ID_EDIT_PASTE:
1843  // the following assumptions are made..
1846 
1850  if (Sexp_nodes[Sexp_clipboard].rest != -1) {
1852  i = tree_nodes[item_index].child;
1853  while (i != -1) {
1855  i = tree_nodes[i].next;
1856  }
1857  }
1858 
1860  Assert(Sexp_nodes[Sexp_clipboard].rest == -1);
1863  Assert(var_idx > -1);
1865  }
1866  else {
1869  }
1870 
1872  Assert(Sexp_nodes[Sexp_clipboard].rest == -1);
1875  Assert(var_idx > -1);
1877  }
1878  else {
1881  }
1882 
1883  } else
1884  Assert(0); // unknown and/or invalid sexp type
1885 
1887 
1888  return 1;
1889 
1890  case ID_EDIT_PASTE_SPECIAL: // add paste, instead of replace.
1891  // the following assumptions are made..
1894 
1898  if (Sexp_nodes[Sexp_clipboard].rest != -1) {
1900  i = tree_nodes[item_index].child;
1901  while (i != -1) {
1903  i = tree_nodes[i].next;
1904  }
1905  }
1906 
1908  Assert(Sexp_nodes[Sexp_clipboard].rest == -1);
1911 
1913  Assert(Sexp_nodes[Sexp_clipboard].rest == -1);
1916 
1917  } else
1918  Assert(0); // unknown and/or invalid sexp type
1919 
1921 
1922  return 1;
1923 
1924 /* case ID_SPLIT_LINE:
1925  if ((tree_nodes[item_index].flags & OPERAND) && (tree_nodes[item_index].flags & EDITABLE)) // expandable?
1926  expand_operator(item_index);
1927  else
1928  merge_operator(item_index);
1929 
1930  return 1;*/
1931 
1932  case ID_EXPAND_ALL:
1934  return 1;
1935 
1936  case ID_EDIT_TEXT:
1937  if (edit_label(item_handle)) {
1938  *modified = 1;
1939  EditLabel(item_handle);
1940  }
1941 
1942  return 1;
1943 
1944  case ID_REPLACE_NUMBER:
1946  replace_data("number", (SEXPT_NUMBER | SEXPT_VALID));
1947  EditLabel(tree_nodes[item_index].handle);
1948  return 1;
1949 
1950  case ID_REPLACE_STRING:
1952  replace_data("string", (SEXPT_STRING | SEXPT_VALID));
1953  EditLabel(tree_nodes[item_index].handle);
1954  return 1;
1955 
1956  case ID_ADD_STRING: {
1957  int theNode;
1958 
1959  theNode = add_data("string", (SEXPT_STRING | SEXPT_VALID));
1960  EditLabel(tree_nodes[theNode].handle);
1961  return 1;
1962  }
1963 
1964  case ID_ADD_NUMBER: {
1965  int theNode;
1966 
1967  theNode = add_data("number", (SEXPT_NUMBER | SEXPT_VALID));
1968  EditLabel(tree_nodes[theNode].handle);
1969  return 1;
1970  }
1971 
1972  case ID_EDIT_CUT:
1973  if (Sexp_clipboard != -1) {
1976  }
1977 
1980  // fall through to ID_DELETE case.
1981 
1982  case ID_DELETE: {
1983  int parent, theNode;
1984  HTREEITEM h_parent;
1985 
1986  if ((m_mode & ST_ROOT_DELETABLE) && (item_index == -1)) {
1987  item_index = GetItemData(item_handle);
1988  if (m_mode == MODE_GOALS) {
1991 
1992  } else if (m_mode == MODE_EVENTS) {
1995 
1996  } else {
1999  }
2000 
2001  Assert(theNode >= 0);
2002  free_node2(theNode);
2003  DeleteItem(item_handle);
2004  *modified = 1;
2005  return 1;
2006  }
2007 
2008  Assert(item_index >= 0);
2009  h_parent = GetParentItem(item_handle);
2010  parent = tree_nodes[item_index].parent;
2011  if ((parent == -1) && (m_mode == MODE_EVENTS))
2012  Int3(); // no longer used, temporary to check if called still.
2013 
2014  Assert(parent != -1 && tree_nodes[parent].handle == h_parent);
2016  DeleteItem(item_handle);
2017 
2018  theNode = tree_nodes[parent].child;
2019 /* if (node != -1 && tree_nodes[node].next == -1 && tree_nodes[node].child == -1) {
2020  sprintf(buf, "%s %s", tree_nodes[parent].text, tree_nodes[node].text);
2021  SetItem(h_parent, TVIF_TEXT, buf, 0, 0, 0, 0, 0);
2022  tree_nodes[parent].flags = OPERAND | EDITABLE;
2023  tree_nodes[node].flags = COMBINED;
2024  DeleteItem(tree_nodes[node].handle);
2025  }*/
2026 
2027  *modified = 1;
2028  return 1;
2029  }
2030  }
2031 
2032  return CTreeCtrl::OnCommand(wParam, lParam);
2033 }
2034 
2035 // adds to or replaces (based on passed in flag) the current operator
2036 void sexp_tree::add_or_replace_operator(int op, int replace_flag)
2037 {
2038  int i, op_index, op2;
2039 
2040  op_index = item_index;
2041  if (replace_flag) {
2042  if (tree_nodes[item_index].flags & OPERAND) { // are both operators?
2044  Assert(op2 >= 0);
2045  i = count_args(tree_nodes[item_index].child);
2046  if ((i >= Operators[op].min) && (i <= Operators[op].max)) { // are old num args valid?
2047  while (i--)
2048  if (query_operator_argument_type(op2, i) != query_operator_argument_type(op, i)) // does each arg match expected type?
2049  break;
2050 
2051  if (i < 0) { // everything is ok, so we can keep old arguments with new operator
2053  SetItemText(tree_nodes[item_index].handle, Operators[op].text);
2054  tree_nodes[item_index].flags = OPERAND;
2055  return;
2056  }
2057  }
2058  }
2059 
2060  replace_operator(Operators[op].text);
2061 
2062  } else
2063  add_operator(Operators[op].text);
2064 
2065  // fill in all the required (minimum) arguments with default values
2066  for (i=0; i<Operators[op].min; i++)
2067  add_default_operator(op, i);
2068 
2069  Expand(item_handle, TVE_EXPAND);
2070 }
2071 
2072 // initialize node, type operator
2073 //
2075 {
2076  int i;
2077 
2078  if (op_num >= FIRST_OP) { // do we have an op value instead of an op number (index)?
2079  for (i=0; i<Num_operators; i++)
2080  if (op_num == Operators[i].value)
2081  op_num = i; // convert op value to op number
2082  }
2083 
2084  op = op_num;
2085  text = Operators[op].text;
2087 }
2088 
2089 // initialize node, type data
2090 // Defaults: t = SEXPT_STRING
2091 //
2092 void sexp_list_item::set_data(const char *str, int t)
2093 {
2094  op = -1;
2095  text = str;
2096  type = t;
2097 }
2098 
2099 // initialize node, type data, allocating memory for the text
2100 // Defaults: t = SEXPT_STRING
2101 //
2102 void sexp_list_item::set_data_dup(const char *str, int t)
2103 {
2104  op = -1;
2105  text = strdup(str);
2107  type = t;
2108 }
2109 
2110 // add a node to end of list
2111 //
2113 {
2114  sexp_list_item *item, *ptr;
2115 
2116  item = new sexp_list_item;
2117  ptr = this;
2118  while (ptr->next)
2119  ptr = ptr->next;
2120 
2121  ptr->next = item;
2122  item->set_op(op_num);
2123 }
2124 
2125 // add a node to end of list
2126 // Defaults: t = SEXPT_STRING
2127 //
2128 void sexp_list_item::add_data(const char *str, int t)
2129 {
2130  sexp_list_item *item, *ptr;
2131 
2132  item = new sexp_list_item;
2133  ptr = this;
2134  while (ptr->next)
2135  ptr = ptr->next;
2136 
2137  ptr->next = item;
2138  item->set_data(str, t);
2139 }
2140 
2141 // add a node to end of list, allocating memory for the text
2142 // Defaults: t = SEXPT_STRING
2143 //
2144 void sexp_list_item::add_data_dup(const char *str, int t)
2145 {
2146  sexp_list_item *item, *ptr;
2147 
2148  item = new sexp_list_item;
2149  ptr = this;
2150  while (ptr->next)
2151  ptr = ptr->next;
2152 
2153  ptr->next = item;
2154  item->set_data(strdup(str), t);
2155  item->flags |= SEXP_ITEM_F_DUP;
2156 }
2157 
2158 // add an sexp list to end of another list (join lists)
2159 //
2161 {
2162  sexp_list_item *ptr;
2163 
2164  ptr = this;
2165  while (ptr->next)
2166  ptr = ptr->next;
2167 
2168  ptr->next = list;
2169 }
2170 
2171 // free all nodes of list
2172 //
2174 {
2175  sexp_list_item *ptr, *ptr2;
2176 
2177  ptr = this;
2178  while (ptr) {
2179  ptr2 = ptr->next;
2180  if (ptr->flags & SEXP_ITEM_F_DUP)
2181  free((void *) ptr->text);
2182 
2183  delete ptr;
2184  ptr = ptr2;
2185  }
2186 }
2187 
2188 int sexp_tree::add_default_operator(int op, int argnum)
2189 {
2190  char buf[256];
2191  int index;
2192  sexp_list_item item;
2193  HTREEITEM h;
2194 
2195  h = item_handle;
2196  index = item_index;
2197  if (get_default_value(&item, buf, op, argnum))
2198  return -1;
2199 
2200  if (item.type & SEXPT_OPERATOR) {
2201  Assert((item.op >= 0) && (item.op < Num_operators));
2203  item_index = index;
2204  item_handle = h;
2205 
2206  } else {
2207  // special case for modify-variable (data added 1st arg is variable)
2208  //if ( !stricmp(Operators[op].text, "modify-variable") ) {
2209  if ( query_operator_argument_type(op, argnum) == OPF_VARIABLE_NAME)
2210  {
2211  if ((argnum == 0 && Operators[op].value == OP_MODIFY_VARIABLE) ||
2212  (argnum == 8 && Operators[op].value == OP_ADD_BACKGROUND_BITMAP) ||
2213  (argnum == 5 && Operators[op].value == OP_ADD_SUN_BITMAP) ||
2214  (argnum == 2 && Operators[op].value == OP_STRING_CONCATENATE) ||
2215  (argnum == 1 && Operators[op].value == OP_INT_TO_STRING) ||
2216  (argnum == 3 && Operators[op].value == OP_STRING_GET_SUBSTRING) ||
2217  (argnum == 4 && Operators[op].value == OP_STRING_SET_SUBSTRING) ||
2218  (argnum == 1 && Operators[op].value == OP_COPY_VARIABLE_FROM_INDEX) ||
2219  (argnum == 1 && Operators[op].value == OP_SCRIPT_EVAL_STRING))
2220  {
2221 
2222  int sexp_var_index = get_index_sexp_variable_name(item.text);
2223  Assert(sexp_var_index != -1);
2225  if (Sexp_variables[sexp_var_index].type & SEXP_VARIABLE_STRING) {
2226  type |= SEXPT_STRING;
2227  } else if (Sexp_variables[sexp_var_index].type & SEXP_VARIABLE_NUMBER) {
2228  type |= SEXPT_NUMBER;
2229  } else {
2230  Int3();
2231  }
2232 
2233  char node_text[2*TOKEN_LENGTH + 2];
2234  sprintf(node_text, "%s(%s)", item.text, Sexp_variables[sexp_var_index].text);
2235  add_variable_data(node_text, type);
2236  } else {
2237  // the the variable name
2238  char buf2[256];
2239  Assert(argnum == 1);
2240  sexp_list_item temp_item;
2241  get_default_value(&temp_item, buf2, op, 0);
2242  int sexp_var_index = get_index_sexp_variable_name(temp_item.text);
2243  Assert(sexp_var_index != -1);
2244 
2245  // from name get type
2246  int temp_type = Sexp_variables[sexp_var_index].type;
2247  int type = 0;
2248  if (temp_type & SEXP_VARIABLE_NUMBER) {
2249  type = SEXPT_VALID | SEXPT_NUMBER;
2250  } else if (temp_type & SEXP_VARIABLE_STRING) {
2251  type = SEXPT_VALID | SEXPT_STRING;
2252  } else {
2253  Int3();
2254  }
2255  add_data(item.text, type);
2256  }
2257  } else {
2258  add_data(item.text, item.type);
2259  }
2260  }
2261 
2262  return 0;
2263 }
2264 
2265 int sexp_tree::get_default_value(sexp_list_item *item, char *text_buf, int op, int i)
2266 {
2267  char *str = NULL;
2268  int type, index;
2269  sexp_list_item *list;
2270  HTREEITEM h;
2271 
2272  h = item_handle;
2273  index = item_index;
2274  type = query_operator_argument_type(op, i);
2275  switch (type) {
2276  case OPF_NULL:
2277  item->set_op(OP_NOP);
2278  return 0;
2279 
2280  case OPF_BOOL:
2281  item->set_op(OP_TRUE);
2282  return 0;
2283 
2284  case OPF_ANYTHING:
2285  item->set_data("<any data>");
2286  return 0;
2287 
2288  case OPF_NUMBER:
2289  case OPF_POSITIVE:
2290  case OPF_AMBIGUOUS:
2291  // if the top level operators is an AI goal, and we are adding the last number required,
2292  // assume that this number is a priority and make it 89 instead of 1.
2293  if ((query_operator_return_type(op) == OPR_AI_GOAL) && (i == (Operators[op].min - 1)))
2294  {
2295  item->set_data("89", (SEXPT_NUMBER | SEXPT_VALID));
2296  }
2297  else if (((Operators[op].value == OP_HAS_DOCKED_DELAY) || (Operators[op].value == OP_HAS_UNDOCKED_DELAY)) && (i == 2))
2298  {
2299  item->set_data("1", (SEXPT_NUMBER | SEXPT_VALID));
2300  }
2302  {
2303  item->set_data("100", (SEXPT_NUMBER | SEXPT_VALID));
2304  }
2305  else if ( (Operators[op].value == OP_SET_SUPPORT_SHIP) )
2306  {
2307  item->set_data("-1", (SEXPT_NUMBER | SEXPT_VALID));
2308  }
2309  else if ( (Operators[op].value == OP_SHIP_TAG) && (i == 1) || (Operators[op].value == OP_TRIGGER_SUBMODEL_ANIMATION) && (i == 3) )
2310  {
2311  item->set_data("1", (SEXPT_NUMBER | SEXPT_VALID));
2312  }
2313  else if ( (Operators[op].value == OP_EXPLOSION_EFFECT) )
2314  {
2315  int temp;
2316  char sexp_str_token[TOKEN_LENGTH];
2317 
2318  switch (i)
2319  {
2320  case 3:
2321  temp = 10;
2322  break;
2323  case 4:
2324  temp = 10;
2325  break;
2326  case 5:
2327  temp = 100;
2328  break;
2329  case 6:
2330  temp = 10;
2331  break;
2332  case 7:
2333  temp = 100;
2334  break;
2335  case 11:
2336  temp = (int)EMP_DEFAULT_INTENSITY;
2337  break;
2338  case 12:
2339  temp = (int)EMP_DEFAULT_TIME;
2340  break;
2341  default:
2342  temp = 0;
2343  break;
2344  }
2345 
2346  // Goober5000 - set_data_dup is required if we're passing a variable
2347  sprintf(sexp_str_token, "%d", temp);
2348  item->set_data_dup(sexp_str_token, (SEXPT_NUMBER | SEXPT_VALID));
2349  }
2350  else if ( (Operators[op].value == OP_WARP_EFFECT) )
2351  {
2352  int temp;
2353  char sexp_str_token[TOKEN_LENGTH];
2354 
2355  switch (i)
2356  {
2357  case 6:
2358  temp = 100;
2359  break;
2360  case 7:
2361  temp = 10;
2362  break;
2363  default:
2364  temp = 0;
2365  break;
2366  }
2367 
2368  // Goober5000 - set_data_dup is required if we're passing a variable
2369  sprintf(sexp_str_token, "%d", temp);
2370  item->set_data_dup(sexp_str_token, (SEXPT_NUMBER | SEXPT_VALID));
2371  }
2372  else if ((Operators[op].value == OP_ADD_BACKGROUND_BITMAP))
2373  {
2374  int temp = 0;
2375  char sexp_str_token[TOKEN_LENGTH];
2376 
2377  switch (i)
2378  {
2379  case 4:
2380  case 5:
2381  temp = 100;
2382  break;
2383 
2384  case 6:
2385  case 7:
2386  temp = 1;
2387  break;
2388  }
2389 
2390  sprintf(sexp_str_token, "%d", temp);
2391  item->set_data_dup(sexp_str_token, (SEXPT_NUMBER | SEXPT_VALID));
2392  }
2393  else if ((Operators[op].value == OP_ADD_SUN_BITMAP))
2394  {
2395  int temp = 0;
2396  char sexp_str_token[TOKEN_LENGTH];
2397 
2398  if (i==4) temp = 100;
2399 
2400  sprintf(sexp_str_token, "%d", temp);
2401  item->set_data_dup(sexp_str_token, (SEXPT_NUMBER | SEXPT_VALID));
2402  }
2403  else if ((Operators[op].value == OP_MODIFY_VARIABLE)) {
2404  if (get_modify_variable_type(index) == OPF_NUMBER) {
2405  item->set_data("0", (SEXPT_NUMBER | SEXPT_VALID));
2406  }
2407  else {
2408  item->set_data("<any data>", (SEXPT_STRING | SEXPT_VALID));
2409  }
2410  }
2411  else if ((Operators[op].value == OP_SET_VARIABLE_BY_INDEX)) {
2412  if (i == 0) {
2413  item->set_data("0", (SEXPT_NUMBER | SEXPT_VALID));
2414  }
2415  else {
2416  item->set_data("<any data>", (SEXPT_STRING | SEXPT_VALID));
2417  }
2418  }
2419  else
2420  {
2421  item->set_data("0", (SEXPT_NUMBER | SEXPT_VALID));
2422  }
2423 
2424  return 0;
2425 
2426  // Goober5000 - special cases that used to be numbers but are now hybrids
2427  case OPF_GAME_SND:
2428  int sound_index = -1;
2429 
2430  if ( (Operators[op].value == OP_EXPLOSION_EFFECT) )
2431  {
2432  sound_index = SND_SHIP_EXPLODE_1;
2433  }
2434  else if ( (Operators[op].value == OP_WARP_EFFECT) )
2435  {
2436  sound_index = (i == 8) ? SND_CAPITAL_WARP_IN : SND_CAPITAL_WARP_OUT;
2437  }
2438 
2439  if (sound_index >= 0)
2440  {
2441  game_snd *snd = &Snds[sound_index];
2442  if (can_construe_as_integer(snd->name.c_str()))
2443  item->set_data(snd->name.c_str(), (SEXPT_NUMBER | SEXPT_VALID));
2444  else
2445  item->set_data(snd->name.c_str(), (SEXPT_STRING | SEXPT_VALID));
2446  return 0;
2447  }
2448 
2449  // if no hardcoded default, just use the listing default
2450  break;
2451  }
2452 
2453  list = get_listing_opf(type, index, i);
2454 
2455  // Goober5000 - the way this is done is really stupid, so stupid hacks are needed to deal with it
2456  // this particular hack is necessary because the argument string should never be a default
2457  if (list && !strcmp(list->text, SEXP_ARGUMENT_STRING))
2458  {
2459  sexp_list_item *first_ptr;
2460 
2461  first_ptr = list;
2462  list = list->next;
2463 
2464  if (first_ptr->flags & SEXP_ITEM_F_DUP)
2465  free((void *) first_ptr->text);
2466 
2467  delete first_ptr;
2468  }
2469 
2470  if (list)
2471  {
2472  // copy the information from the list to the passed-in item
2473  *item = *list;
2474 
2475  // but use the provided text buffer
2476  strcpy(text_buf, list->text);
2477  item->text = text_buf;
2478 
2479  // get rid of the list, since we're done with it
2480  list->destroy();
2481  item->next = NULL;
2482 
2483  return 0;
2484  }
2485 
2486  // catch anything that doesn't have a default value. Just describe what should be here instead
2487  switch (type) {
2488  case OPF_SHIP:
2489  case OPF_SHIP_NOT_PLAYER:
2490  case OPF_SHIP_POINT:
2491  case OPF_SHIP_WING:
2494  case OPF_SHIP_WING_POINT:
2495  str = "<name of ship here>";
2496  break;
2497 
2498  case OPF_ORDER_RECIPIENT:
2499  str = "<all fighters>";
2500  break;
2501 
2502  case OPF_SHIP_OR_NONE:
2503  case OPF_SUBSYSTEM_OR_NONE:
2505  str = SEXP_NONE_STRING;
2506  break;
2507 
2508  case OPF_WING:
2509  str = "<name of wing here>";
2510  break;
2511 
2512  case OPF_DOCKER_POINT:
2513  str = "<docker point>";
2514  break;
2515 
2516  case OPF_DOCKEE_POINT:
2517  str = "<dockee point>";
2518  break;
2519 
2520  case OPF_SUBSYSTEM:
2521  case OPF_AWACS_SUBSYSTEM:
2523  case OPF_SUBSYS_OR_GENERIC:
2524  str = "<name of subsystem>";
2525  break;
2526 
2527  case OPF_SUBSYSTEM_TYPE:
2529  break;
2530 
2531  case OPF_POINT:
2532  str = "<waypoint>";
2533  break;
2534 
2535  case OPF_MESSAGE:
2536  str = "<Message>";
2537  break;
2538 
2539  case OPF_WHO_FROM:
2540  //str = "<any allied>";
2541  str = "<any wingman>";
2542  break;
2543 
2544  case OPF_WAYPOINT_PATH:
2545  str = "<waypoint path>";
2546  break;
2547 
2548  case OPF_MISSION_NAME:
2549  str = "<mission name>";
2550  break;
2551 
2552  case OPF_GOAL_NAME:
2553  str = "<goal name>";
2554  break;
2555 
2556  case OPF_SHIP_TYPE:
2557  str = "<ship type here>";
2558  break;
2559 
2560  case OPF_EVENT_NAME:
2561  str = "<event name>";
2562  break;
2563 
2564  case OPF_HUGE_WEAPON:
2565  str = "<huge weapon type>";
2566  break;
2567 
2568  case OPF_JUMP_NODE_NAME:
2569  str = "<Jump node name>";
2570  break;
2571 
2572  case OPF_NAV_POINT:
2573  str = "<Nav 1>";
2574  break;
2575 
2576  case OPF_ANYTHING:
2577  str = "<any data>";
2578  break;
2579 
2580  case OPF_PERSONA:
2581  str = "<persona name>";
2582  break;
2583 
2584  case OPF_FONT:
2585  str = Fonts[0].filename;
2586  break;
2587 
2589  str = "Music";
2590  break;
2591 
2592  case OPF_POST_EFFECT:
2593  str = "<Effect Name>";
2594  break;
2595 
2596  case OPF_HUD_GAUGE:
2597  str = "Messages";
2598  break;
2599 
2600  default:
2601  str = "<new default required!>";
2602  break;
2603  }
2604 
2605  item->set_data(str, (SEXPT_STRING | SEXPT_VALID));
2606  return 0;
2607 }
2608 
2610 {
2611  int i;
2612 
2613  Assert(op >= 0);
2614  for (i=0; i<Operators[op].min; i++)
2616  return 0;
2617 
2618  return 1;
2619 }
2620 
2622 {
2623  int j, type;
2624  object *ptr;
2625 
2626  type = query_operator_argument_type(op, i);
2627  switch (type) {
2628  case OPF_NONE:
2629  case OPF_NULL:
2630  case OPF_BOOL:
2631  case OPF_NUMBER:
2632  case OPF_POSITIVE:
2633  case OPF_IFF:
2634  case OPF_AI_CLASS:
2635  case OPF_WHO_FROM:
2636  case OPF_PRIORITY:
2637  case OPF_SHIP_TYPE:
2638  case OPF_SUBSYSTEM:
2639  case OPF_AWACS_SUBSYSTEM:
2641  case OPF_SUBSYSTEM_TYPE:
2642  case OPF_DOCKER_POINT:
2643  case OPF_DOCKEE_POINT:
2644  case OPF_AI_GOAL:
2645  case OPF_KEYPRESS:
2646  case OPF_AI_ORDER:
2647  case OPF_SKILL_LEVEL:
2648  case OPF_MEDAL_NAME:
2649  case OPF_WEAPON_NAME:
2650  case OPF_INTEL_NAME:
2651  case OPF_SHIP_CLASS_NAME:
2652  case OPF_HUD_GAUGE_NAME:
2653  case OPF_HUGE_WEAPON:
2654  case OPF_JUMP_NODE_NAME:
2655  case OPF_AMBIGUOUS:
2656  case OPF_CARGO:
2657  case OPF_ARRIVAL_LOCATION:
2661  case OPF_SHIP_WITH_BAY:
2662  case OPF_SOUNDTRACK_NAME:
2663  case OPF_STRING:
2664  case OPF_FLEXIBLE_ARGUMENT:
2665  case OPF_ANYTHING:
2666  case OPF_SKYBOX_MODEL_NAME:
2667  case OPF_SKYBOX_FLAGS:
2668  case OPF_SHIP_OR_NONE:
2669  case OPF_SUBSYSTEM_OR_NONE:
2671  case OPF_SUBSYS_OR_GENERIC:
2672  case OPF_BACKGROUND_BITMAP:
2673  case OPF_SUN_BITMAP:
2674  case OPF_NEBULA_STORM_TYPE:
2675  case OPF_NEBULA_POOF:
2677  case OPF_POST_EFFECT:
2678  case OPF_TARGET_PRIORITIES:
2679  case OPF_ARMOR_TYPE:
2680  case OPF_DAMAGE_TYPE:
2681  case OPF_FONT:
2682  case OPF_HUD_ELEMENT:
2683  case OPF_SOUND_ENVIRONMENT:
2685  case OPF_EXPLOSION_OPTION:
2688  case OPF_MESSAGE_OR_STRING:
2689  case OPF_HUD_GAUGE:
2690  case OPF_SHIP_EFFECT:
2691  case OPF_ANIMATION_TYPE:
2692  case OPF_SHIP_FLAG:
2693  case OPF_NEBULA_PATTERN:
2694  case OPF_NAV_POINT:
2695  case OPF_TEAM_COLOR:
2696  case OPF_GAME_SND:
2697  return 1;
2698 
2699  case OPF_SHIP:
2700  case OPF_SHIP_WING:
2701  case OPF_SHIP_POINT:
2702  case OPF_SHIP_WING_POINT:
2705  ptr = GET_FIRST(&obj_used_list);
2706  while (ptr != END_OF_LIST(&obj_used_list)) {
2707  if (ptr->type == OBJ_SHIP || ptr->type == OBJ_START)
2708  return 1;
2709 
2710  ptr = GET_NEXT(ptr);
2711  }
2712 
2713  return 0;
2714 
2715  case OPF_SHIP_NOT_PLAYER:
2716  case OPF_ORDER_RECIPIENT:
2717  ptr = GET_FIRST(&obj_used_list);
2718  while (ptr != END_OF_LIST(&obj_used_list)) {
2719  if (ptr->type == OBJ_SHIP)
2720  return 1;
2721 
2722  ptr = GET_NEXT(ptr);
2723  }
2724 
2725  return 0;
2726 
2727  case OPF_WING:
2728  for (j=0; j<MAX_WINGS; j++)
2729  if (Wings[j].wave_count)
2730  return 1;
2731 
2732  return 0;
2733 
2734  case OPF_PERSONA:
2735  return (Num_personas > 0) ? 1 : 0;
2736 
2737  case OPF_POINT:
2738  case OPF_WAYPOINT_PATH:
2739  return Waypoint_lists.empty() ? 0 : 1;
2740 
2741  case OPF_MISSION_NAME:
2742  if (m_mode != MODE_CAMPAIGN) {
2743  if (!(*Mission_filename))
2744  return 0;
2745 
2746  return 1;
2747  }
2748 
2749  if (Campaign.num_missions > 0)
2750  return 1;
2751 
2752  return 0;
2753 
2754  case OPF_GOAL_NAME: {
2755  int value;
2756 
2757  value = Operators[op].value;
2758 
2759  if (m_mode == MODE_CAMPAIGN)
2760  return 1;
2761 
2762  // need to be sure that previous-goal functions are available. (i.e. we are providing a default argument for them)
2763  else if ((value == OP_PREVIOUS_GOAL_TRUE) || (value == OP_PREVIOUS_GOAL_FALSE) || (value == OP_PREVIOUS_GOAL_INCOMPLETE) || Num_goals)
2764  return 1;
2765 
2766  return 0;
2767  }
2768 
2769  case OPF_EVENT_NAME: {
2770  int value;
2771 
2772  value = Operators[op].value;
2773  if (m_mode == MODE_CAMPAIGN)
2774  return 1;
2775 
2776  // need to be sure that previous-event functions are available. (i.e. we are providing a default argument for them)
2777  else if ((value == OP_PREVIOUS_EVENT_TRUE) || (value == OP_PREVIOUS_EVENT_FALSE) || (value == OP_PREVIOUS_EVENT_INCOMPLETE) || Num_mission_events)
2778  return 1;
2779 
2780  return 0;
2781  }
2782 
2783  case OPF_MESSAGE:
2784  if (m_mode == MODE_EVENTS) {
2787  return 1;
2788 
2789  } else {
2791  return 1;
2792  }
2793 
2794  return 0;
2795 
2796  case OPF_VARIABLE_NAME:
2797  return (sexp_variable_count() > 0) ? 1 : 0;
2798 
2799  case OPF_SSM_CLASS:
2800  return (Ssm_info.size() > 0) ? 1 : 0;
2801 
2802  case OPF_MISSION_MOOD:
2803  return Builtin_moods.empty() ? 0 : 1;
2804 
2805  default:
2806  Int3();
2807 
2808  }
2809 
2810  return 0;
2811 }
2812 
2813 // expand a combined line (one with an operator and its one argument on the same line) into
2814 // 2 lines.
2816 {
2817  int data;
2818  HTREEITEM h;
2819 
2820  if (tree_nodes[node].flags & COMBINED) {
2821  node = tree_nodes[node].parent;
2822  Assert((tree_nodes[node].flags & OPERAND) && (tree_nodes[node].flags & EDITABLE));
2823  }
2824 
2825  if ((tree_nodes[node].flags & OPERAND) && (tree_nodes[node].flags & EDITABLE)) { // expandable?
2827  h = tree_nodes[node].handle;
2828  data = tree_nodes[node].child;
2829  Assert(data != -1 && tree_nodes[data].next == -1 && tree_nodes[data].child == -1);
2830 
2831  SetItem(h, TVIF_TEXT, tree_nodes[node].text, 0, 0, 0, 0, 0);
2832  tree_nodes[node].flags = OPERAND;
2833  int bmap = get_data_image(data);
2834  tree_nodes[data].handle = insert(tree_nodes[data].text, bmap, bmap, h);
2835  tree_nodes[data].flags = EDITABLE;
2836  Expand(h, TVE_EXPAND);
2837  }
2838 }
2839 
2840 // expand a CTreeCtrl branch and all of its children
2842 {
2843  Expand(h, TVE_EXPAND);
2844  h = GetChildItem(h);
2845  while (h) {
2846  expand_branch(h);
2847  h = GetNextSiblingItem(h);
2848  }
2849 }
2850 
2852 {
2853 /* char buf[256];
2854  int child;
2855 
2856  if (tree_nodes[node].flags == EDITABLE) // data
2857  node = tree_nodes[node].parent;
2858 
2859  if (node != -1) {
2860  child = tree_nodes[node].child;
2861  if (child != -1 && tree_nodes[child].next == -1 && tree_nodes[child].child == -1) {
2862  sprintf(buf, "%s %s", tree_nodes[node].text, tree_nodes[child].text);
2863  SetItemText(tree_nodes[node].handle, buf);
2864  tree_nodes[node].flags = OPERAND | EDITABLE;
2865  tree_nodes[child].flags = COMBINED;
2866  DeleteItem(tree_nodes[child].handle);
2867  tree_nodes[child].handle = NULL;
2868  return;
2869  }
2870  }*/
2871 }
2872 
2873 // add a data node under operator pointed to by item_index
2874 int sexp_tree::add_data(const char *data, int type)
2875 {
2876  int node;
2877 
2879  node = allocate_node(item_index);
2880  set_node(node, type, data);
2881  int bmap = get_data_image(node);
2882  tree_nodes[node].handle = insert(data, bmap, bmap, tree_nodes[item_index].handle);
2883  tree_nodes[node].flags = EDITABLE;
2884  *modified = 1;
2885  return node;
2886 }
2887 
2888 // add a (variable) data node under operator pointed to by item_index
2890 {
2891  int node;
2892 
2893  Assert(type & SEXPT_VARIABLE);
2894 
2896  node = allocate_node(item_index);
2897  set_node(node, type, data);
2899  tree_nodes[node].flags = NOT_EDITABLE;
2900  *modified = 1;
2901  return node;
2902 }
2903 
2904 // add an operator under operator pointed to by item_index. Updates item_index to point
2905 // to this new operator.
2906 void sexp_tree::add_operator(const char *op, HTREEITEM h)
2907 {
2908  int node;
2909 
2910  if (item_index == -1) {
2911  node = allocate_node(-1);
2912  set_node(node, (SEXPT_OPERATOR | SEXPT_VALID), op);
2913  item_handle = tree_nodes[node].handle = insert(op, BITMAP_OPERATOR, BITMAP_OPERATOR, h);
2914 
2915  } else {
2917  node = allocate_node(item_index);
2918  set_node(node, (SEXPT_OPERATOR | SEXPT_VALID), op);
2920  }
2921 
2922  tree_nodes[node].flags = OPERAND;
2923  item_index = node;
2924  *modified = 1;
2925 }
2926 
2927 // add an operator with one argument under operator pointed to by item_index. This function
2928 // exists because the one arg case is a special case. The operator and argument is
2929 // displayed on the same line.
2930 /*void sexp_tree::add_one_arg_operator(char *op, char *data, int type)
2931 {
2932  char str[80];
2933  int node1, node2;
2934 
2935  expand_operator(item_index);
2936  node1 = allocate_node(item_index);
2937  node2 = allocate_node(node1);
2938  set_node(node1, SEXPT_OPERATOR, op);
2939  set_node(node2, type, data);
2940  sprintf(str, "%s %s", op, data);
2941  tree_nodes[node1].handle = insert(str, tree_nodes[item_index].handle);
2942  tree_nodes[node1].flags = OPERAND | EDITABLE;
2943  tree_nodes[node2].flags = COMBINED;
2944  *modified = 1;
2945 }*/
2946 
2947 /*
2948 int sexp_tree::verify_tree(int *bypass)
2949 {
2950  return verify_tree(0, bypass);
2951 }
2952 
2953 // check the sexp tree for errors. Return -1 if error, or 0 if no errors. If an error
2954 // is found, item_index = node of error.
2955 int sexp_tree::verify_tree(int node, int *bypass)
2956 {
2957  int i, type, count, op, type2, op2, argnum = 0;
2958 
2959  if (!total_nodes)
2960  return 0; // nothing to check
2961 
2962  Assert(node >= 0 && node < tree_nodes.size());
2963  Assert(tree_nodes[node].type == SEXPT_OPERATOR);
2964 
2965  op = get_operator_index(tree_nodes[node].text);
2966  if (op == -1)
2967  return node_error(node, "Unknown operator", bypass);
2968 
2969  count = count_args(tree_nodes[node].child);
2970  if (count < Operators[op].min)
2971  return node_error(node, "Too few arguments for operator", bypass);
2972  if (count > Operators[op].max)
2973  return node_error(node, "Too many arguments for operator", bypass);
2974 
2975  node = tree_nodes[node].child; // get first argument
2976  while (node != -1) {
2977  type = query_operator_argument_type(op, argnum);
2978  Assert(tree_nodes[node].type & SEXPT_VALID);
2979  if (tree_nodes[node].type == SEXPT_OPERATOR) {
2980  if (verify_tree(node) == -1)
2981  return -1;
2982 
2983  op2 = get_operator_index(tree_nodes[node].text); // no error checking, because it was done in the call above.
2984  type2 = query_operator_return_type(op2);
2985 
2986  } else if (tree_nodes[node].type == SEXPT_NUMBER) {
2987  char *ptr;
2988 
2989  type2 = OPR_NUMBER;
2990  ptr = tree_nodes[node].text;
2991  while (*ptr)
2992  if (!isdigit(*ptr++))
2993  return node_error(node, "Number is invalid", bypass);
2994 
2995  } else if (tree_nodes[node].type == SEXPT_STRING) {
2996  type2 = SEXP_ATOM_STRING;
2997 
2998  } else
2999  Assert(0); // unknown and invalid sexp node type.
3000 
3001  switch (type) {
3002  case OPF_NUMBER:
3003  if (type2 != OPR_NUMBER)
3004  return node_error(node, "Number or number return type expected here", bypass);
3005 
3006  break;
3007 
3008  case OPF_SHIP:
3009  if (type2 == SEXP_ATOM_STRING)
3010  if (ship_name_lookup(tree_nodes[node].text, 1) == -1)
3011  type2 = 0;
3012 
3013  if (type2 != SEXP_ATOM_STRING)
3014  return node_error(node, "Ship name expected here", bypass);
3015 
3016  break;
3017 
3018  case OPF_WING:
3019  if (type2 == SEXP_ATOM_STRING)
3020  if (wing_name_lookup(tree_nodes[node].text) == -1)
3021  type2 = 0;
3022 
3023  if (type2 != SEXP_ATOM_STRING)
3024  return node_error(node, "Wing name expected here", bypass);
3025 
3026  break;
3027 
3028  case OPF_SHIP_WING:
3029  if (type2 == SEXP_ATOM_STRING)
3030  if (ship_name_lookup(tree_nodes[node].text, 1) == -1)
3031  if (wing_name_lookup(tree_nodes[node].text) == -1)
3032  type2 = 0;
3033 
3034  if (type2 != SEXP_ATOM_STRING)
3035  return node_error(node, "Ship or wing name expected here", bypass);
3036 
3037  break;
3038 
3039  case OPF_BOOL:
3040  if (type2 != OPR_BOOL)
3041  return node_error(node, "Boolean return type expected here", bypass);
3042 
3043  break;
3044 
3045  case OPF_NULL:
3046  if (type2 != OPR_NULL)
3047  return node_error(node, "No return type operator expected here", bypass);
3048 
3049  break;
3050 
3051  case OPF_POINT:
3052  if (type2 != SEXP_ATOM_STRING || verify_vector(tree_nodes[node].text))
3053  return node_error(node, "3d coordinate expected here", bypass);
3054 
3055  break;
3056 
3057  case OPF_SUBSYSTEM:
3058  case OPF_AWACS_SUBSYSTEM:
3059  case OPF_ROTATING_SUBSYSTEM:
3060  if (type2 == SEXP_ATOM_STRING)
3061  if (ai_get_subsystem_type(tree_nodes[node].text) == SUBSYSTEM_UNKNOWN)
3062  type2 = 0;
3063 
3064  if (type2 != SEXP_ATOM_STRING)
3065  return node_error(node, "Subsystem name expected here", bypass);
3066 
3067  break;
3068 
3069  case OPF_IFF:
3070  if (type2 == SEXP_ATOM_STRING) {
3071  for (i=0; i<Num_iffs; i++)
3072  if (!stricmp(Team_names[i], tree_nodes[node].text))
3073  break;
3074  }
3075 
3076  if (i == Num_iffs)
3077  return node_error(node, "Iff team type expected here", bypass);
3078 
3079  break;
3080 
3081  case OPF_AI_GOAL:
3082  if (type2 != OPR_AI_GOAL)
3083  return node_error(node, "Ai goal return type expected here", bypass);
3084 
3085  break;
3086 
3087  case OPF_FLEXIBLE_ARGUMENT:
3088  if (type2 != OPR_FLEXIBLE_ARGUMENT)
3089  return node_error(node, "Flexible argument return type expected here", bypass);
3090 
3091  break;
3092 
3093  case OPF_ANYTHING:
3094  break;
3095 
3096  case OPF_DOCKER_POINT:
3097  if (type2 != SEXP_ATOM_STRING)
3098  return node_error(node, "Docker docking point name expected here", bypass);
3099 
3100  break;
3101 
3102  case OPF_DOCKEE_POINT:
3103  if (type2 != SEXP_ATOM_STRING)
3104  return node_error(node, "Dockee docking point name expected here", bypass);
3105 
3106  break;
3107  }
3108 
3109  node = tree_nodes[node].next;
3110  argnum++;
3111  }
3112 
3113  return 0;
3114 }
3115 */
3116 
3117 // display an error message and position to point of error (a node)
3118 int sexp_tree::node_error(int node, const char *msg, int *bypass)
3119 {
3120  char text[512];
3121 
3122  if (bypass)
3123  *bypass = 1;
3124 
3125  item_index = node;
3126  item_handle = tree_nodes[node].handle;
3127  if (tree_nodes[node].flags & COMBINED)
3128  item_handle = tree_nodes[tree_nodes[node].parent].handle;
3129 
3130  ensure_visible(node);
3131  SelectItem(item_handle);
3132  sprintf(text, "%s\n\nContinue checking for more errors?", msg);
3133  if (MessageBox(text, "Sexp error", MB_YESNO | MB_ICONEXCLAMATION) == IDNO)
3134  return -1;
3135  else
3136  return 0;
3137 }
3138 
3140 {
3141  ensure_visible(node);
3142  SelectItem(tree_nodes[node].handle);
3143 }
3144 
3145 // because the MFC function EnsureVisible() doesn't do what it says it does, I wrote this.
3147 {
3148  Assert(node != -1);
3149  if (tree_nodes[node].parent != -1)
3150  ensure_visible(tree_nodes[node].parent); // expand all parents first
3151 
3152  if (tree_nodes[node].child != -1) // expandable?
3153  Expand(tree_nodes[node].handle, TVE_EXPAND); // expand this item
3154 }
3155 
3157 {
3158  modified = ptr;
3159 }
3160 
3161 void get_variable_default_text_from_variable_text(char *text, char *default_text)
3162 {
3163  int len;
3164  char *start;
3165 
3166  // find '('
3167  start = strstr(text, "(");
3168  Assert(start);
3169  start++;
3170 
3171  // get length and copy all but last char ")"
3172  len = strlen(start);
3173  strncpy(default_text, start, len-1);
3174 
3175  // add null termination
3176  default_text[len-1] = '\0';
3177 }
3178 
3179 void get_variable_name_from_sexp_tree_node_text(const char *text, char *var_name)
3180 {
3181  int length;
3182  length = strcspn(text, "(");
3183 
3184  strncpy(var_name, text, length);
3185  var_name[length] = '\0';
3186 }
3187 
3189 {
3190  int sexp_var_index = -1;
3191 
3192  Assert(parent >= 0);
3193  int op_const = get_operator_const(tree_nodes[parent].text);
3194 
3195  Assert(tree_nodes[parent].child >= 0);
3196  char *node_text = tree_nodes[tree_nodes[parent].child].text;
3197 
3198  if ( op_const == OP_MODIFY_VARIABLE ) {
3199  sexp_var_index = get_tree_name_to_sexp_variable_index(node_text);
3200  Assert(sexp_var_index >= 0);
3201  }
3202  else if ( op_const == OP_SET_VARIABLE_BY_INDEX ) {
3203  if (can_construe_as_integer(node_text)) {
3204  sexp_var_index = atoi(node_text);
3205  Assert(sexp_var_index >= 0);
3206  }
3207  else if (strchr(node_text, '(') && strchr(node_text, ')')) {
3208  // the variable index is itself a variable!
3209  return OPF_AMBIGUOUS;
3210  }
3211  } else {
3212  Int3(); // should not be called otherwise
3213  }
3214 
3215  if (sexp_var_index < 0 || Sexp_variables[sexp_var_index].type & SEXP_VARIABLE_BLOCK || Sexp_variables[sexp_var_index].type & SEXP_VARIABLE_NOT_USED) {
3216  // assume number so that we can allow tree display of number operators
3217  return OPF_NUMBER;
3218  } else if (Sexp_variables[sexp_var_index].type & SEXP_VARIABLE_NUMBER) {
3219  return OPF_NUMBER;
3220  } else if (Sexp_variables[sexp_var_index].type & SEXP_VARIABLE_STRING) {
3221  return OPF_AMBIGUOUS;
3222  } else {
3223  Int3();
3224  return 0;
3225  }
3226 }
3227 
3228 
3230 {
3231  int op, arg_num, type, tmp;
3232  static int flag = 0;
3233  sexp_list_item *list, *ptr;
3234  bool is_variable_arg = false;
3235 
3236  if (flag)
3237  return;
3238 
3239  flag++;
3240  op = get_operator_index(tree_nodes[node].text);
3241  if (op < 0)
3242  return;
3243 
3244  tmp = item_index;
3245 
3246  arg_num = 0;
3247  item_index = tree_nodes[node].child;
3248  while (item_index >= 0) {
3249  // get listing of valid argument values for node item_index
3250  type = query_operator_argument_type(op, arg_num);
3251  // special case for modify-variable
3252  if (type == OPF_AMBIGUOUS) {
3253  is_variable_arg = true;
3254  type = get_modify_variable_type(node);
3255  }
3256  if (query_restricted_opf_range(type)) {
3257  list = get_listing_opf(type, node, arg_num);
3258  if (!list && (arg_num >= Operators[op].min)) {
3259  free_node(item_index, 1);
3260  item_index = tmp;
3261  flag--;
3262  return;
3263  }
3264 
3265  if (list) {
3266  // get a pointer to tree_nodes[item_index].text for normal value
3267  // or default variable value if variable
3268  char *text_ptr;
3269  char default_variable_text[TOKEN_LENGTH];
3270  if (tree_nodes[item_index].type & SEXPT_VARIABLE) {
3271  // special case for SEXPs which can modify a variable
3272  if ((arg_num == 0 && Operators[op].value == OP_MODIFY_VARIABLE) ||
3273  (arg_num == 8 && Operators[op].value == OP_ADD_BACKGROUND_BITMAP) ||
3274  (arg_num == 5 && Operators[op].value == OP_ADD_SUN_BITMAP) ||
3275  (arg_num == 2 && Operators[op].value == OP_STRING_CONCATENATE) ||
3276  (arg_num == 1 && Operators[op].value == OP_INT_TO_STRING) ||
3277  (arg_num == 3 && Operators[op].value == OP_STRING_GET_SUBSTRING) ||
3278  (arg_num == 4 && Operators[op].value == OP_STRING_SET_SUBSTRING) ||
3279  (arg_num == 1 && Operators[op].value == OP_COPY_VARIABLE_FROM_INDEX))
3280  {
3281  // make text_ptr to start - before '('
3283  text_ptr = default_variable_text;
3284  } else {
3285  // only the type needs checking for variables. It's up the to the FREDder to ensure the value is valid
3286  get_variable_name_from_sexp_tree_node_text(tree_nodes[item_index].text, default_variable_text);
3287  int sexp_var_index = get_index_sexp_variable_name(default_variable_text);
3288  bool types_match = false;
3289  Assert(sexp_var_index != -1);
3290 
3291  switch (type) {
3292  case OPF_NUMBER:
3293  case OPF_POSITIVE:
3294  if (Sexp_variables[sexp_var_index].type & SEXP_VARIABLE_NUMBER) {
3295  types_match = true;
3296  }
3297  break;
3298 
3299  default:
3300  if (Sexp_variables[sexp_var_index].type & SEXP_VARIABLE_STRING) {
3301  types_match = true;
3302  }
3303  }
3304 
3305  if (types_match) {
3306  // on to the next argument
3308  arg_num++;
3309  continue;
3310  }
3311  else {
3312  // shouldn't really be getting here unless someone has been hacking the mission in a text editor
3314  text_ptr = default_variable_text;
3315  }
3316  }
3317  } else {
3318  text_ptr = tree_nodes[item_index].text;
3319  }
3320 
3321  ptr = list;
3322  while (ptr) {
3323 
3324  if (ptr->text != NULL) {
3325  // make sure text is not NULL
3326  // check that proposed text is valid for operator
3327  if ( !stricmp(ptr->text, text_ptr) )
3328  break;
3329 
3330  ptr = ptr->next;
3331  } else {
3332  // text is NULL, so set ptr to NULL to end loop
3333  ptr = NULL;
3334  }
3335  }
3336 
3337  if (!ptr) { // argument isn't in list of valid choices,
3338  if (list->op >= 0) {
3339  replace_operator(list->text);
3340  } else {
3341  replace_data(list->text, list->type);
3342  }
3343  }
3344 
3345  } else {
3346  bool invalid = false;
3347  if (type == OPF_AMBIGUOUS) {
3349  invalid = true;
3350  }
3351  } else {
3353  invalid = true;
3354  }
3355  }
3356 
3357  if (invalid) {
3358  replace_data("<Invalid>", (SEXPT_STRING | SEXPT_VALID));
3359  }
3360  }
3361 
3362  if (tree_nodes[item_index].type & SEXPT_OPERATOR)
3364 
3365  }
3366 
3367  //fix the node if it is the argument for modify-variable
3368  if (is_variable_arg //&&
3369  // !(tree_nodes[item_index].type & SEXPT_OPERATOR || tree_nodes[item_index].type & SEXPT_VARIABLE )
3370  ) {
3371  switch (type) {
3372  case OPF_AMBIGUOUS:
3375  break;
3376 
3377  case OPF_NUMBER:
3380  break;
3381 
3382  default:
3383  Int3();
3384  }
3385  }
3386 
3388  arg_num++;
3389  }
3390 
3391  item_index = tmp;
3392  flag--;
3393 }
3394 
3395 void sexp_tree::replace_data(const char *data, int type)
3396 {
3397  int node;
3398  HTREEITEM h;
3399 
3400  node = tree_nodes[item_index].child;
3401  if (node != -1)
3402  free_node2(node);
3403 
3404  tree_nodes[item_index].child = -1;
3405  h = tree_nodes[item_index].handle;
3406  while (ItemHasChildren(h))
3407  DeleteItem(GetChildItem(h));
3408 
3409  set_node(item_index, type, data);
3410  SetItemText(h, data);
3411  int bmap = get_data_image(item_index);
3412  SetItemImage(h, bmap, bmap);
3413  tree_nodes[item_index].flags = EDITABLE;
3414 
3415  // check remaining data beyond replaced data for validity (in case any of it is dependent on data just replaced)
3417 
3418  *modified = 1;
3419  update_help(GetSelectedItem());
3420 }
3421 
3422 
3423 // Replaces data with sexp_variable type data
3425 {
3426  int node;
3427  HTREEITEM h;
3428  char buf[128];
3429 
3430  Assert(type & SEXPT_VARIABLE);
3431 
3432  node = tree_nodes[item_index].child;
3433  if (node != -1)
3434  free_node2(node);
3435 
3436  tree_nodes[item_index].child = -1;
3437  h = tree_nodes[item_index].handle;
3438  while (ItemHasChildren(h)) {
3439  DeleteItem(GetChildItem(h));
3440  }
3441 
3442  // Assemble name
3443  sprintf(buf, "%s(%s)", Sexp_variables[var_idx].variable_name, Sexp_variables[var_idx].text);
3444 
3445  set_node(item_index, type, buf);
3446  SetItemText(h, buf);
3447  SetItemImage(h, BITMAP_VARIABLE, BITMAP_VARIABLE);
3449 
3450  // check remaining data beyond replaced data for validity (in case any of it is dependent on data just replaced)
3452 
3453  *modified = 1;
3454  update_help(GetSelectedItem());
3455 }
3456 
3457 
3458 
3459 void sexp_tree::replace_operator(const char *op)
3460 {
3461  int node;
3462  HTREEITEM h;
3463 
3464  node = tree_nodes[item_index].child;
3465  if (node != -1)
3466  free_node2(node);
3467 
3468  tree_nodes[item_index].child = -1;
3469  h = tree_nodes[item_index].handle;
3470  while (ItemHasChildren(h))
3471  DeleteItem(GetChildItem(h));
3472 
3474  SetItemText(h, op);
3475  tree_nodes[item_index].flags = OPERAND;
3476  *modified = 1;
3477  update_help(GetSelectedItem());
3478 
3479  // hack added at Allender's request. If changing ship in an ai-dock operator, re-default
3480  // docking point.
3481 }
3482 
3483 /*void sexp_tree::replace_one_arg_operator(char *op, char *data, int type)
3484 {
3485  char str[80];
3486  int node;
3487  HTREEITEM h;
3488 
3489  node = tree_nodes[item_index].child;
3490  if (node != -1)
3491  free_node2(node);
3492 
3493  tree_nodes[item_index].child = -1;
3494  h = tree_nodes[item_index].handle;
3495  while (ItemHasChildren(h))
3496  DeleteItem(GetChildItem(h));
3497 
3498  node = allocate_node(item_index);
3499  set_node(item_index, SEXPT_OPERATOR, op);
3500  set_node(node, type, data);
3501  sprintf(str, "%s %s", op, data);
3502  SetItemText(h, str);
3503  tree_nodes[item_index].flags = OPERAND | EDITABLE;
3504  tree_nodes[node].flags = COMBINED;
3505  *modified = 1;
3506  update_help(GetSelectedItem());
3507 }*/
3508 
3509 // moves a whole sexp tree branch to a new position under 'parent' and after 'after'.
3510 // The expansion state is preserved, and node handles are updated.
3511 void sexp_tree::move_branch(int source, int parent)
3512 {
3513  int node;
3514 
3515  // if no source, skip everything
3516  if (source != -1) {
3517  node = tree_nodes[source].parent;
3518  if (node != -1) {
3519  if (tree_nodes[node].child == source)
3520  tree_nodes[node].child = tree_nodes[source].next;
3521  else {
3522  node = tree_nodes[node].child;
3523  while (tree_nodes[node].next != source) {
3524  node = tree_nodes[node].next;
3525  Assert(node != -1);
3526  }
3527 
3528  tree_nodes[node].next = tree_nodes[source].next;
3529  }
3530  }
3531 
3532  tree_nodes[source].parent = parent;
3533  tree_nodes[source].next = -1;
3534  if (parent) {
3535  if (tree_nodes[parent].child == -1)
3536  tree_nodes[parent].child = source;
3537  else {
3538  node = tree_nodes[parent].child;
3539  while (tree_nodes[node].next != -1)
3540  node = tree_nodes[node].next;
3541 
3542  tree_nodes[node].next = source;
3543  }
3544 
3545  move_branch(tree_nodes[source].handle, tree_nodes[parent].handle);
3546 
3547  } else
3548  move_branch(tree_nodes[source].handle);
3549  }
3550 }
3551 
3552 HTREEITEM sexp_tree::move_branch(HTREEITEM source, HTREEITEM parent, HTREEITEM after)
3553 {
3554  uint i;
3555  int image1, image2;
3556  HTREEITEM h = 0, child, next;
3557 
3558  if (source) {
3559  for (i=0; i<tree_nodes.size(); i++)
3560  if (tree_nodes[i].handle == source)
3561  break;
3562 
3563  if (i < tree_nodes.size()) {
3564  GetItemImage(source, image1, image2);
3565  h = insert(GetItemText(source), image1, image2, parent, after);
3566  tree_nodes[i].handle = h;
3567 
3568  } else {
3569  GetItemImage(source, image1, image2);
3570  h = insert(GetItemText(source), image1, image2, parent, after);
3571  }
3572 
3573  SetItemData(h, GetItemData(source));
3574  child = GetChildItem(source);
3575  while (child) {
3576  next = GetNextSiblingItem(child);
3577  move_branch(child, h);
3578  child = next;
3579  }
3580 
3581  if (GetItemState(source, TVIS_EXPANDED) & TVIS_EXPANDED)
3582  Expand(h, TVE_EXPAND);
3583 
3584  DeleteItem(source);
3585  }
3586 
3587  return h;
3588 }
3589 
3590 void sexp_tree::copy_branch(HTREEITEM source, HTREEITEM parent, HTREEITEM after)
3591 {
3592  uint i;
3593  int image1, image2;
3594  HTREEITEM h, child;
3595 
3596  if (source) {
3597  for (i=0; i<tree_nodes.size(); i++)
3598  if (tree_nodes[i].handle == source)
3599  break;
3600 
3601  if (i < tree_nodes.size()) {
3602  GetItemImage(source, image1, image2);
3603  h = insert(GetItemText(source), image1, image2, parent, after);
3604  tree_nodes[i].handle = h;
3605 
3606  } else {
3607  GetItemImage(source, image1, image2);
3608  h = insert(GetItemText(source), image1, image2, parent, after);
3609  }
3610 
3611  SetItemData(h, GetItemData(source));
3612  child = GetChildItem(source);
3613  while (child) {
3614  copy_branch(child, h);
3615  child = GetNextSiblingItem(child);
3616  }
3617 
3618  if (GetItemState(source, TVIS_EXPANDED) & TVIS_EXPANDED)
3619  Expand(h, TVE_EXPAND);
3620  }
3621 }
3622 
3623 void sexp_tree::swap_roots(HTREEITEM one, HTREEITEM two)
3624 {
3625  HTREEITEM h;
3626 
3627  Assert(!GetParentItem(one));
3628  Assert(!GetParentItem(two));
3629 // copy_branch(one, TVI_ROOT, two);
3630 // move_branch(two, TVI_ROOT, one);
3631 // DeleteItem(one);
3632  h = move_branch(one, TVI_ROOT, two);
3633  SelectItem(h);
3634  SelectItem(h);
3635  *modified = 1;
3636 }
3637 
3638 void sexp_tree::OnBegindrag(NMHDR* pNMHDR, LRESULT* pResult)
3639 {
3640  UINT flags = 0;
3641 
3642 // ScreenToClient(&m_pt);
3643  ASSERT(!m_dragging);
3644  m_h_drag = HitTest(m_pt, &flags);
3645  m_h_drop = NULL;
3646 
3647  if (!m_mode || GetParentItem(m_h_drag))
3648  return;
3649 
3650  ASSERT(m_p_image_list == NULL);
3651  m_p_image_list = CreateDragImage(m_h_drag); // get the image list for dragging
3652  if (!m_p_image_list)
3653  return;
3654 
3655  m_p_image_list->DragShowNolock(TRUE);
3656  m_p_image_list->SetDragCursorImage(0, CPoint(0, 0));
3657  m_p_image_list->BeginDrag(0, CPoint(0,0));
3658  m_p_image_list->DragMove(m_pt);
3659  m_p_image_list->DragEnter(this, m_pt);
3660  SetCapture();
3661  m_dragging = TRUE;
3662 }
3663 
3664 void sexp_tree::OnLButtonDown(UINT nFlags, CPoint point)
3665 {
3666  m_pt = point;
3667  CTreeCtrl::OnLButtonDown(nFlags, point);
3668 }
3669 
3670 void sexp_tree::OnMouseMove(UINT nFlags, CPoint point)
3671 {
3672  HTREEITEM hitem = NULL;
3673  UINT flags = 0;
3674 
3675  if (m_dragging) {
3676  ASSERT(m_p_image_list != NULL);
3677  m_p_image_list->DragMove(point);
3678  if ((hitem = HitTest(point, &flags)) != NULL)
3679  if (!GetParentItem(hitem)) {
3680  m_p_image_list->DragLeave(this);
3681  SelectDropTarget(hitem);
3682  m_h_drop = hitem;
3683  m_p_image_list->DragEnter(this, point);
3684  }
3685  }
3686 
3687  CTreeCtrl::OnMouseMove(nFlags, point);
3688 }
3689 
3690 void sexp_tree::OnLButtonUp(UINT nFlags, CPoint point)
3691 {
3692  int index1, index2;
3693 
3694  if (m_dragging) {
3695  ASSERT(m_p_image_list != NULL);
3696  m_p_image_list->DragLeave(this);
3697  m_p_image_list->EndDrag();
3698  delete m_p_image_list;
3699  m_p_image_list = NULL;
3700 
3701  if (m_h_drop && m_h_drag != m_h_drop) {
3702  Assert(m_h_drag);
3703  index1 = GetItemData(m_h_drag);
3704  index2 = GetItemData(m_h_drop);
3706  if (m_mode == MODE_GOALS) {
3708  Goal_editor_dlg->swap_handler(index1, index2);
3709 
3710  } else if (m_mode == MODE_EVENTS) {
3712  Event_editor_dlg->swap_handler(index1, index2);
3713 
3714  } else if (m_mode == MODE_CAMPAIGN) {
3715  Campaign_tree_formp->swap_handler(index1, index2);
3716 
3717  } else
3718  Assert(0);
3719 
3720  } else
3721  MessageBeep(0);
3722 
3723  ReleaseCapture();
3724  m_dragging = FALSE;
3725  SelectDropTarget(NULL);
3726  }
3727 
3728  CTreeCtrl::OnLButtonUp(nFlags, point);
3729 }
3730 
3731 const static UINT Numbered_data_bitmaps[] = {
3732  IDB_DATA_00,
3733  IDB_DATA_05,
3734  IDB_DATA_10,
3735  IDB_DATA_15,
3736  IDB_DATA_20,
3737  IDB_DATA_25,
3738  IDB_DATA_30,
3739  IDB_DATA_35,
3740  IDB_DATA_40,
3741  IDB_DATA_45,
3742  IDB_DATA_50,
3743  IDB_DATA_55,
3744  IDB_DATA_60,
3745  IDB_DATA_65,
3746  IDB_DATA_70,
3747  IDB_DATA_75,
3748  IDB_DATA_80,
3749  IDB_DATA_85,
3750  IDB_DATA_90,
3751  IDB_DATA_95
3752 };
3753 
3754 void sexp_tree::setup(CEdit *ptr)
3755 {
3756  CImageList *pimagelist;
3757  CBitmap bitmap;
3758 
3759  help_box = ptr;
3760  if (help_box) {
3761  int stops[2] = { 10, 30 };
3762 
3763  help_box -> SetTabStops(2, (LPINT) stops);
3764  }
3765 
3766  pimagelist = GetImageList(TVSIL_NORMAL);
3767  if (!pimagelist) {
3768  pimagelist = new CImageList();
3769  pimagelist->Create(16, 16, TRUE/*bMask*/, 2, 22);
3770 
3771  //*****Add generic images
3772  bitmap.LoadBitmap(IDB_OPERATOR);
3773  pimagelist->Add(&bitmap, (COLORREF) 0xFFFFFF);
3774  bitmap.DeleteObject();
3775 
3776  bitmap.LoadBitmap(IDB_DATA);
3777  pimagelist->Add(&bitmap, (COLORREF) 0xFF00FF);
3778  bitmap.DeleteObject();
3779 
3780  bitmap.LoadBitmap(IDB_VARIABLE);
3781  pimagelist->Add(&bitmap, (COLORREF) 0xFF00FF);
3782  bitmap.DeleteObject();
3783 
3784  bitmap.LoadBitmap(IDB_ROOT);
3785  pimagelist->Add(&bitmap, (COLORREF) 0xFF00FF);
3786  bitmap.DeleteObject();
3787 
3788  bitmap.LoadBitmap(IDB_ROOT_DIRECTIVE);
3789  pimagelist->Add(&bitmap, (COLORREF) 0xFFFFFF);
3790  bitmap.DeleteObject();
3791 
3792  bitmap.LoadBitmap(IDB_CHAINED);
3793  pimagelist->Add(&bitmap, (COLORREF) 0xFF00FF);
3794  bitmap.DeleteObject();
3795 
3796  bitmap.LoadBitmap(IDB_CHAINED_DIRECTIVE);
3797  pimagelist->Add(&bitmap, (COLORREF) 0xFFFFFF);
3798  bitmap.DeleteObject();
3799 
3800  bitmap.LoadBitmap(IDB_GREEN_DOT);
3801  pimagelist->Add(&bitmap, (COLORREF) 0xFFFFFF);
3802  bitmap.DeleteObject();
3803 
3804  bitmap.LoadBitmap(IDB_BLACK_DOT);
3805  pimagelist->Add(&bitmap, (COLORREF) 0xFFFFFF);
3806  bitmap.DeleteObject();
3807 
3808  //*****Add numbered data entries
3809  int num = sizeof(Numbered_data_bitmaps)/sizeof(UINT);
3810  int i = 0;
3811  for(i = 0; i < num; i++)
3812  {
3813  bitmap.LoadBitmap(Numbered_data_bitmaps[i]);
3814  pimagelist->Add(&bitmap, (COLORREF) 0xFF00FF);
3815  bitmap.DeleteObject();
3816  }
3817 
3818  SetImageList(pimagelist, TVSIL_NORMAL);
3819  }
3820 }
3821 //#define BITMAP_OPERATOR 0
3822 //#define BITMAP_DATA 1
3823 //#define BITMAP_VARIABLE 2
3824 //#define BITMAP_ROOT 3
3825 //#define BITMAP_ROOT_DIRECTIVE 4
3826 //#define BITMAP_CHAIN 5
3827 //#define BITMAP_CHAIN_DIRECTIVE 6
3828 //#define BITMAP_GREEN_DOT 7
3829 //#define BITMAP_BLACK_DOT 8
3830 
3831 
3832 HTREEITEM sexp_tree::insert(LPCTSTR lpszItem, int image, int sel_image, HTREEITEM hParent, HTREEITEM hInsertAfter)
3833 {
3834  return InsertItem(lpszItem, image, sel_image, hParent, hInsertAfter);
3835 
3836 }
3837 
3839 {
3840  CImageList *pimagelist;
3841 
3842  pimagelist = GetImageList(TVSIL_NORMAL);
3843  if (pimagelist) {
3844  pimagelist->DeleteImageList();
3845  delete pimagelist;
3846  }
3847 
3848  CTreeCtrl::OnDestroy();
3849 }
3850 
3851 HTREEITEM sexp_tree::handle(int node)
3852 {
3853  return tree_nodes[node].handle;
3854 }
3855 
3856 const char *sexp_tree::help(int code)
3857 {
3858  int i;
3859 
3860  i = Num_sexp_help;
3861  while (i--) {
3862  if (Sexp_help[i].id == code)
3863  break;
3864  }
3865 
3866  if (i >= 0)
3867  return Sexp_help[i].help;
3868 
3869  return NULL;
3870 }
3871 
3872 // get type of item clicked on
3873 int sexp_tree::get_type(HTREEITEM h)
3874 {
3875  uint i;
3876 
3877  // get index into sexp_tree
3878  for (i=0; i<tree_nodes.size(); i++)
3879  if (tree_nodes[i].handle == h)
3880  break;
3881 
3882  if ( (i >= tree_nodes.size()) ) {
3883  // Int3(); // This would be the root of the tree -- ie, event name
3884  return -1;
3885  }
3886 
3887  return tree_nodes[i].type;
3888 }
3889 
3890 
3891 void sexp_tree::update_help(HTREEITEM h)
3892 {
3893  const char *str;
3894  int i, j, z, c, code, index, sibling_place;
3895  CString text;
3896 
3897  for (i=0; i<Num_operators; i++) {
3898  for (j=0; j<Num_op_menus; j++) {
3899  if (get_category(Operators[i].value) == op_menu[j].id) {
3900  if (!help(Operators[i].value)) {
3901  mprintf(("Allender! If you add new sexp operators, add help for them too! :)\n"));
3902  }
3903  }
3904  }
3905  }
3906 
3907  help_box = (CEdit *) GetParent()->GetDlgItem(IDC_HELP_BOX);
3908  if (!help_box || !::IsWindow(help_box->m_hWnd))
3909  return;
3910 
3911  mini_help_box = (CEdit *) GetParent()->GetDlgItem(IDC_MINI_HELP_BOX);
3912  if (!mini_help_box || !::IsWindow(mini_help_box->m_hWnd))
3913  return;
3914 
3915  for (i=0; i<(int)tree_nodes.size(); i++)
3916  if (tree_nodes[i].handle == h)
3917  break;
3918 
3919  if ((i >= (int)tree_nodes.size()) || !tree_nodes[i].type) {
3920  help_box->SetWindowText("");
3921  mini_help_box->SetWindowText("");
3922  return;
3923  }
3924 
3925  if (SEXPT_TYPE(tree_nodes[i].type) == SEXPT_OPERATOR)
3926  {
3927  mini_help_box->SetWindowText("");
3928  }
3929  else
3930  {
3931  z = tree_nodes[i].parent;
3932  if (z < 0) {
3933  Warning(LOCATION, "Sexp data \"%s\" has no parent!", tree_nodes[i].text);
3934  return;
3935  }
3936 
3937  code = get_operator_const(tree_nodes[z].text);
3938  index = get_operator_index(tree_nodes[z].text);
3939  sibling_place = get_sibling_place(i) + 1; //We want it to start at 1
3940 
3941  //*****Minihelp box
3942  if((SEXPT_TYPE(tree_nodes[i].type) == SEXPT_NUMBER) || (SEXPT_TYPE(tree_nodes[i].type) == SEXPT_STRING) && sibling_place > 0)
3943  {
3944  char buffer[10240] = {""};
3945 
3946  //Get the help for the current operator
3947  const char *helpstr = help(code);
3948  bool display_number = true;
3949 
3950  //If a help string exists, try to display it
3951  if(helpstr != NULL)
3952  {
3953  char searchstr[32];
3954  const char *loc=NULL, *loc2=NULL;
3955 
3956  if(loc == NULL)
3957  {
3958  sprintf(searchstr, "\n%d:", sibling_place);
3959  loc = strstr(helpstr, searchstr);
3960  }
3961 
3962  if(loc == NULL)
3963  {
3964  sprintf(searchstr, "\t%d:", sibling_place);
3965  loc = strstr(helpstr, searchstr);
3966  }
3967  if(loc == NULL)
3968  {
3969  sprintf(searchstr, " %d:", sibling_place);
3970  loc = strstr(helpstr, searchstr);
3971  }
3972  if(loc == NULL)
3973  {
3974  sprintf(searchstr, "%d:", sibling_place);
3975  loc = strstr(helpstr, searchstr);
3976  }
3977  if(loc == NULL)
3978  {
3979  loc = strstr(helpstr, "Rest:");
3980  }
3981  if(loc == NULL)
3982  {
3983  loc = strstr(helpstr, "All:");
3984  }
3985 
3986  if(loc != NULL)
3987  {
3988  //Skip whitespace
3989  while(*loc=='\r' || *loc == '\n' || *loc == ' ' || *loc == '\t') loc++;
3990 
3991  //Find EOL
3992  loc2 = strpbrk(loc, "\r\n");
3993  if(loc2 != NULL)
3994  {
3995  size_t size = loc2-loc;
3996  strncpy(buffer, loc, size);
3997  if(size < sizeof(buffer))
3998  {
3999  buffer[size] = '\0';
4000  }
4001  display_number = false;
4002  }
4003  else
4004  {
4005  strcpy_s(buffer, loc);
4006  display_number = false;
4007  }
4008  }
4009  }
4010 
4011  //Display argument number
4012  if(display_number)
4013  {
4014  sprintf(buffer, "%d:", sibling_place);
4015  }
4016 
4017  mini_help_box->SetWindowText(buffer);
4018  }
4019 
4020  if (index >= 0) {
4021  c = 0;
4022  j = tree_nodes[z].child;
4023  while ((j >= 0) && (j != i)) {
4024  j = tree_nodes[j].next;
4025  c++;
4026  }
4027 
4028  Assert(j >= 0);
4029  if (query_operator_argument_type(index, c) == OPF_MESSAGE) {
4030  for (j=0; j<Num_messages; j++)
4031  if (!stricmp(Messages[j].name, tree_nodes[i].text)) {
4032  text.Format("Message Text:\r\n%s", Messages[j].message);
4033  help_box->SetWindowText(text);
4034  return;
4035  }
4036  }
4037  }
4038 
4039  i = z;
4040  }
4041 
4042  code = get_operator_const(tree_nodes[i].text);
4043  str = help(code);
4044  if (!str)
4045  str = "No help available";
4046 
4047  help_box->SetWindowText(str);
4048 }
4049 
4050 // find list of sexp_tree nodes with text
4051 // stuff node indices into find[]
4052 int sexp_tree::find_text(const char *text, int *find)
4053 {
4054  uint i;
4055  int find_count;
4056 
4057  // initialize find
4058  for (i=0; i<MAX_SEARCH_MESSAGE_DEPTH; i++) {
4059  find[i] = -1;
4060  }
4061 
4062  find_count = 0;
4063 
4064  for (i=0; i<tree_nodes.size(); i++) {
4065  // only look at used and editable nodes
4066  if ((tree_nodes[i].flags & EDITABLE && (tree_nodes[i].type != SEXPT_UNUSED))) {
4067  // find the text
4068  if ( !stricmp(tree_nodes[i].text, text) ) {
4069  find[find_count++] = i;
4070 
4071  // don't exceed max count - array bounds
4072  if (find_count == MAX_SEARCH_MESSAGE_DEPTH) {
4073  break;
4074  }
4075  }
4076  }
4077  }
4078 
4079  return find_count;
4080 }
4081 
4082 
4083 void sexp_tree::OnKeydown(NMHDR *pNMHDR, LRESULT *pResult)
4084 {
4085  int key;
4086  TV_KEYDOWN *pTVKeyDown = (TV_KEYDOWN *) pNMHDR;
4087 
4088  key = pTVKeyDown->wVKey;
4089  if (key == VK_SPACE)
4090  EditLabel(GetSelectedItem());
4091 
4092  *pResult = 0;
4093 }
4094 
4095 // Determine if a given opf code has a restricted argument range (i.e. has a specific, limited
4096 // set of argument values, or has virtually unlimited possibilities. For example, boolean values
4097 // only have true or false, so it is restricted, but a number could be anything, so it's not.
4098 //
4100 {
4101  switch (opf) {
4102  case OPF_NUMBER:
4103  case OPF_POSITIVE:
4104  case OPF_WHO_FROM:
4105 
4106  // Goober5000 - these are needed too (otherwise the arguments revert to their defaults)
4107  case OPF_STRING:
4108  case OPF_ANYTHING:
4109  return 0;
4110  }
4111 
4112  return 1;
4113 }
4114 
4115 // generate listing of valid argument values.
4116 // opf = operator format to generate list for
4117 // parent_node = the parent node we are generating list for
4118 // arg_index = argument number of parent this argument will go at
4119 //
4120 // Goober5000 - add the listing from get_listing_opf_sub to the end of a new list containing
4121 // the special argument item, but only if it's a child of a when-argument (or similar) sexp.
4122 // Also only do this if the list has at least one item, because otherwise the argument code
4123 // would have nothing to select from.
4124 sexp_list_item *sexp_tree::get_listing_opf(int opf, int parent_node, int arg_index)
4125 {
4126  sexp_list_item head;
4127  sexp_list_item *list = NULL;
4128  int w_arg, e_arg;
4129 
4130  switch (opf) {
4131  case OPF_NONE:
4132  list = NULL;
4133  break;
4134 
4135  case OPF_NULL:
4136  list = get_listing_opf_null();
4137  break;
4138 
4139  case OPF_BOOL:
4140  list = get_listing_opf_bool(parent_node);
4141  break;
4142 
4143  case OPF_NUMBER:
4144  list = get_listing_opf_number();
4145  break;
4146 
4147  case OPF_SHIP:
4148  list = get_listing_opf_ship(parent_node);
4149  break;
4150 
4151  case OPF_WING:
4152  list = get_listing_opf_wing();
4153  break;
4154 
4155  case OPF_AWACS_SUBSYSTEM:
4157  case OPF_SUBSYSTEM:
4158  list = get_listing_opf_subsystem(parent_node, arg_index);
4159  break;
4160 
4161  case OPF_SUBSYSTEM_TYPE:
4162  list = get_listing_opf_subsystem_type(parent_node);
4163  break;
4164 
4165  case OPF_POINT:
4166  list = get_listing_opf_point();
4167  break;
4168 
4169  case OPF_IFF:
4170  list = get_listing_opf_iff();
4171  break;
4172 
4173  case OPF_AI_CLASS:
4174  list = get_listing_opf_ai_class();
4175  break;
4176 
4179  break;
4180 
4181  case OPF_SSM_CLASS:
4182  list = get_listing_opf_ssm_class();
4183  break;
4184 
4185  case OPF_ARRIVAL_LOCATION:
4187  break;
4188 
4191  break;
4192 
4195  break;
4196 
4197  case OPF_SHIP_WITH_BAY:
4199  break;
4200 
4201  case OPF_SOUNDTRACK_NAME:
4203  break;
4204 
4205  case OPF_AI_GOAL:
4206  list = get_listing_opf_ai_goal(parent_node);
4207  break;
4208 
4209  case OPF_FLEXIBLE_ARGUMENT:
4211  break;
4212 
4213  case OPF_DOCKER_POINT:
4214  list = get_listing_opf_docker_point(parent_node);
4215  break;
4216 
4217  case OPF_DOCKEE_POINT:
4218  list = get_listing_opf_dockee_point(parent_node);
4219  break;
4220 
4221  case OPF_MESSAGE:
4222  list = get_listing_opf_message();
4223  break;
4224 
4225  case OPF_WHO_FROM:
4226  list = get_listing_opf_who_from();
4227  break;
4228 
4229  case OPF_PRIORITY:
4230  list = get_listing_opf_priority();
4231  break;
4232 
4233  case OPF_WAYPOINT_PATH:
4235  break;
4236 
4237  case OPF_POSITIVE:
4238  list = get_listing_opf_positive();
4239  break;
4240 
4241  case OPF_MISSION_NAME:
4243  break;
4244 
4245  case OPF_SHIP_POINT:
4246  list = get_listing_opf_ship_point();
4247  break;
4248 
4249  case OPF_GOAL_NAME:
4250  list = get_listing_opf_goal_name(parent_node);
4251  break;
4252 
4253  case OPF_SHIP_WING:
4254  list = get_listing_opf_ship_wing();
4255  break;
4256 
4259  break;
4260 
4263  break;
4264 
4265  case OPF_SHIP_WING_POINT:
4267  break;
4268 
4271  break;
4272 
4273  case OPF_ORDER_RECIPIENT:
4275  break;
4276 
4277  case OPF_SHIP_TYPE:
4278  list = get_listing_opf_ship_type();
4279  break;
4280 
4281  case OPF_KEYPRESS:
4282  list = get_listing_opf_keypress();
4283  break;
4284 
4285  case OPF_EVENT_NAME:
4286  list = get_listing_opf_event_name(parent_node);
4287  break;
4288 
4289  case OPF_AI_ORDER:
4290  list = get_listing_opf_ai_order();
4291  break;
4292 
4293  case OPF_SKILL_LEVEL:
4294  list = get_listing_opf_skill_level();
4295  break;
4296 
4297  case OPF_CARGO:
4298  list = get_listing_opf_cargo();
4299  break;
4300 
4301  case OPF_STRING:
4302  list = get_listing_opf_string();
4303  break;
4304 
4305  case OPF_MEDAL_NAME:
4306  list = get_listing_opf_medal_name();
4307  break;
4308 
4309  case OPF_WEAPON_NAME:
4310  list = get_listing_opf_weapon_name();
4311  break;
4312 
4313  case OPF_INTEL_NAME:
4314  list = get_listing_opf_intel_name();
4315  break;
4316 
4317  case OPF_SHIP_CLASS_NAME:
4319  break;
4320 
4321  case OPF_HUD_GAUGE_NAME:
4323  break;
4324 
4325  case OPF_HUGE_WEAPON:
4326  list = get_listing_opf_huge_weapon();
4327  break;
4328 
4329  case OPF_SHIP_NOT_PLAYER:
4331  break;
4332 
4333  case OPF_SHIP_OR_NONE:
4335  break;
4336 
4337  case OPF_SUBSYSTEM_OR_NONE:
4338  list = get_listing_opf_subsystem_or_none(parent_node, arg_index);
4339  break;
4340 
4341  case OPF_SUBSYS_OR_GENERIC:
4342  list = get_listing_opf_subsys_or_generic(parent_node, arg_index);
4343  break;
4344 
4345  case OPF_JUMP_NODE_NAME:
4346  list = get_listing_opf_jump_nodes();
4347  break;
4348 
4349  case OPF_VARIABLE_NAME:
4351  break;
4352 
4353  case OPF_AMBIGUOUS:
4354  list = NULL;
4355  break;
4356 
4357  case OPF_ANYTHING:
4358  list = NULL;
4359  break;
4360 
4361  case OPF_SKYBOX_MODEL_NAME:
4363  break;
4364 
4365  case OPF_SKYBOX_FLAGS:
4367  break;
4368 
4369  case OPF_BACKGROUND_BITMAP:
4371  break;
4372 
4373  case OPF_SUN_BITMAP:
4374  list = get_listing_opf_sun_bitmap();
4375  break;
4376 
4377  case OPF_NEBULA_STORM_TYPE:
4379  break;
4380 
4381  case OPF_NEBULA_POOF:
4382  list = get_listing_opf_nebula_poof();
4383  break;
4384 
4387  break;
4388 
4389  case OPF_TARGET_PRIORITIES:
4391  break;
4392 
4393  case OPF_ARMOR_TYPE:
4394  list = get_listing_opf_armor_type();
4395  break;
4396 
4397  case OPF_DAMAGE_TYPE:
4398  list = get_listing_opf_damage_type();
4399  break;
4400 
4401  case OPF_ANIMATION_TYPE:
4403  break;
4404 
4405  case OPF_PERSONA:
4406  list = get_listing_opf_persona();
4407  break;
4408 
4409  case OPF_POST_EFFECT:
4410  list = get_listing_opf_post_effect();
4411  break;
4412 
4413  case OPF_FONT:
4414  list = get_listing_opf_font();
4415  break;
4416 
4417  case OPF_HUD_ELEMENT:
4419  break;
4420 
4421  case OPF_SOUND_ENVIRONMENT:
4423  break;
4424 
4427  break;
4428 
4431  break;
4432 
4433  case OPF_EXPLOSION_OPTION:
4435  break;
4436 
4439  break;
4440 
4441  case OPF_MESSAGE_OR_STRING:
4442  list = get_listing_opf_message();
4443  break;
4444 
4445  case OPF_HUD_GAUGE:
4446  list = get_listing_opf_hud_gauge();
4447  break;
4448 
4449  case OPF_SHIP_EFFECT:
4450  list = get_listing_opf_ship_effect();
4451  break;
4452 
4453  case OPF_MISSION_MOOD:
4455  break;
4456 
4457  case OPF_SHIP_FLAG:
4458  list = get_listing_opf_ship_flags();
4459  break;
4460 
4461  case OPF_TEAM_COLOR:
4462  list = get_listing_opf_team_colors();
4463  break;
4464 
4465  case OPF_NEBULA_PATTERN:
4467  break;
4468 
4469  case OPF_GAME_SND:
4470  list = get_listing_opf_game_snds();
4471  break;
4472 
4473  default:
4474  Int3(); // unknown OPF code
4475  list = NULL;
4476  break;
4477  }
4478 
4479 
4480  // skip OPF_NONE, also skip for OPF_NULL, because it takes no data (though it can take plenty of operators)
4481  if (opf == OPF_NULL || opf == OPF_NONE) {
4482  return list;
4483  }
4484 
4485  // skip the special argument if we aren't at the right spot in when-argument or
4486  // every-time-argument... meaning if either w_arg or e_arg is >= 1, we can continue
4487  w_arg = find_ancestral_argument_number(OP_WHEN_ARGUMENT, parent_node);
4489  if (w_arg < 1 && e_arg < 1 /* && the same for any future _ARGUMENT sexps */ ) {
4490  return list;
4491  }
4492 
4493  // the special item is a string and should not be added for numeric lists
4494  if (opf != OPF_NUMBER && opf != OPF_POSITIVE) {
4496  }
4497 
4498  if (list != NULL) {
4499  // append other list
4500  head.add_list(list);
4501  }
4502 
4503  // return listing
4504  return head.next;
4505 }
4506 
4507 // Goober5000
4508 int sexp_tree::find_argument_number(int parent_node, int child_node)
4509 {
4510  int arg_num, current_node;
4511 
4512  // code moved/adapted from match_closest_operator
4513  arg_num = 0;
4514  current_node = tree_nodes[parent_node].child;
4515  while (current_node >= 0)
4516  {
4517  // found?
4518  if (current_node == child_node)
4519  return arg_num;
4520 
4521  // continue iterating
4522  arg_num++;
4523  current_node = tree_nodes[current_node].next;
4524  }
4525 
4526  // not found
4527  return -1;
4528 }
4529 
4530 // Goober5000
4531 // backtrack through parents until we find the operator matching
4532 // parent_op, then find the argument we went through
4533 int sexp_tree::find_ancestral_argument_number(int parent_op, int child_node)
4534 {
4535  if(child_node == -1)
4536  return -1;
4537 
4538  int parent_node;
4539  int current_node;
4540 
4541  current_node = child_node;
4542  parent_node = tree_nodes[current_node].parent;
4543 
4544  while (parent_node >= 0)
4545  {
4546  // check if the parent operator is the one we're looking for
4547  if (get_operator_const(tree_nodes[parent_node].text) == parent_op)
4548  return find_argument_number(parent_node, current_node);
4549 
4550  // continue iterating up the tree
4551  current_node = parent_node;
4552  parent_node = tree_nodes[current_node].parent;
4553  }
4554 
4555  // not found
4556  return -1;
4557 }
4558 
4564 {
4565  int count = get_sibling_place(node) + 1;
4566 
4567  if (count <= 0) {
4568  return BITMAP_DATA;
4569  }
4570 
4571  if (count % 5) {
4572  return BITMAP_DATA;
4573  }
4574 
4575  int idx = (count % 100) / 5;
4576 
4577  int num = sizeof(Numbered_data_bitmaps)/sizeof(UINT);
4578 
4579  if (idx > num) {
4580  return BITMAP_DATA;
4581  }
4582 
4583  return BITMAP_NUMBERED_DATA + idx;
4584 }
4585 
4587 {
4588  if(tree_nodes[node].parent < 0 || tree_nodes[node].parent > (int)tree_nodes.size())
4589  return -1;
4590 
4591  sexp_tree_item *myparent = &tree_nodes[tree_nodes[node].parent];
4592 
4593  if(myparent->child == -1)
4594  return -1;
4595 
4596  sexp_tree_item *mysibling = &tree_nodes[myparent->child];
4597 
4598  int count = 0;
4599  while(true)
4600  {
4601  if(mysibling == &tree_nodes[node])
4602  break;
4603 
4604  if(mysibling->next == -1)
4605  break;
4606 
4607  count++;
4608  mysibling = &tree_nodes[mysibling->next];
4609  }
4610 
4611  return count;
4612 }
4613 
4614 
4616 {
4617  int i;
4618  sexp_list_item head;
4619 
4620  for (i=0; i<Num_operators; i++)
4622  head.add_op(i);
4623 
4624  return head.next;
4625 }
4626 
4628 {
4629  int i;
4630  sexp_list_item head;
4631 
4632  for (i=0; i<Num_operators; i++)
4634  head.add_op(i);
4635 
4636  return head.next;
4637 }
4638 
4640 {
4641  int i, only_basic;
4642  sexp_list_item head;
4643 
4644  // search for the previous goal/event operators. If found, only add the true/false
4645  // sexpressions to the list
4646  only_basic = 0;
4647  if ( parent_node != -1 ) {
4648  int op;
4649 
4650  op = get_operator_const(tree_nodes[parent_node].text);
4651  if ( (op == OP_PREVIOUS_GOAL_TRUE) || (op == OP_PREVIOUS_GOAL_FALSE) || (op == OP_PREVIOUS_EVENT_TRUE) || (op == OP_PREVIOUS_EVENT_FALSE) )
4652  only_basic = 1;
4653 
4654  }
4655 
4656  for (i=0; i<Num_operators; i++) {
4658  if ( !only_basic || (only_basic && ((Operators[i].value == OP_TRUE) || (Operators[i].value == OP_FALSE))) ) {
4659  head.add_op(i);
4660  }
4661  }
4662  }
4663 
4664  return head.next;
4665 }
4666 
4668 {
4669  int i, z;
4670  sexp_list_item head;
4671 
4672  for (i=0; i<Num_operators; i++) {
4674  // Goober5000's number hack
4675  if ((z == OPR_NUMBER) || (z == OPR_POSITIVE))
4676  head.add_op(i);
4677  }
4678 
4679  return head.next;
4680 }
4681 
4683 {
4684  int i, z;
4685  sexp_list_item head;
4686 
4687  for (i=0; i<Num_operators; i++) {
4689  if ((z == OPR_NUMBER) || (z == OPR_POSITIVE))
4690  head.add_op(i);
4691  }
4692 
4693  return head.next;
4694 }
4695 
4697 {
4698  object *ptr;
4699  sexp_list_item head;
4700  int op = 0, dock_ship = -1, require_cap_ship = 0;
4701 
4702  // look at the parent node and get the operator. Some ship lists should be filtered based
4703  // on what the parent operator is
4704  if ( parent_node >= 0 ) {
4705  op = get_operator_const(tree_nodes[parent_node].text);
4706 
4707  // get the dock_ship number of if this goal is an ai dock goal. used to prune out unwanted ships out
4708  // of the generated ship list
4709  dock_ship = -1;
4710  if ( op == OP_AI_DOCK ) {
4711  int z;
4712 
4713  z = tree_nodes[parent_node].parent;
4714  Assert(z >= 0);
4715  Assert(!stricmp(tree_nodes[z].text, "add-ship-goal") || !stricmp(tree_nodes[z].text, "add-wing-goal") || !stricmp(tree_nodes[z].text, "add-goal"));
4716 
4717  z = tree_nodes[z].child;
4718  Assert(z >= 0);
4719 
4720  dock_ship = ship_name_lookup(tree_nodes[z].text, 1);
4721  Assert( dock_ship != -1 );
4722  }
4723  }
4724 
4725  ptr = GET_FIRST(&obj_used_list);
4726  while (ptr != END_OF_LIST(&obj_used_list)) {
4727  if ((ptr->type == OBJ_SHIP) || (ptr->type == OBJ_START)) {
4728  if ( op == OP_AI_DOCK ) {
4729  // only include those ships in the list which the given ship can dock with.
4730  if ( (dock_ship != ptr->instance) && ship_docking_valid(dock_ship , ptr->instance) )
4731  head.add_data(Ships[ptr->instance].ship_name );
4732 
4733  }
4734  else if (op == OP_CAP_SUBSYS_CARGO_KNOWN_DELAY) {
4735  if ( ((Ship_info[Ships[ptr->instance].ship_info_index].flags & SIF_HUGE_SHIP) && // big ship
4736  !(Ships[ptr->instance].flags2 & SF2_TOGGLE_SUBSYSTEM_SCANNING) )|| // which is not flagged OR
4737  ((!(Ship_info[Ships[ptr->instance].ship_info_index].flags & SIF_HUGE_SHIP)) && // small ship
4738  (Ships[ptr->instance].flags2 & SF2_TOGGLE_SUBSYSTEM_SCANNING) ) ) { // which is flagged
4739 
4740  head.add_data(Ships[ptr->instance].ship_name);
4741  }
4742  }
4743  else {
4744  if ( !require_cap_ship || (Ship_info[Ships[ptr->instance].ship_info_index].flags & SIF_HUGE_SHIP) ) {
4745  head.add_data(Ships[ptr->instance].ship_name);
4746  }
4747  }
4748  }
4749 
4750  ptr = GET_NEXT(ptr);
4751  }
4752 
4753  return head.next;
4754 }
4755 
4757 {
4758  int i;
4759  sexp_list_item head;
4760 
4761  for (i=0; i<MAX_WINGS; i++){
4762  if (Wings[i].wave_count){
4763  head.add_data(Wings[i].name);
4764  }
4765  }
4766 
4767  return head.next;
4768 }
4769 
4770 // specific types of subsystems we're looking for
4771 #define OPS_CAP_CARGO 1
4772 #define OPS_STRENGTH 2
4773 #define OPS_BEAM_TURRET 3
4774 #define OPS_AWACS 4
4775 #define OPS_ROTATE 5
4776 #define OPS_ARMOR 6
4778 {
4779  int op, child, sh;
4780  int special_subsys = 0;
4781  sexp_list_item head;
4782  ship_subsys *subsys;
4783 
4784  // determine if the parent is one of the set subsystem strength items. If so,
4785  // we want to append the "Hull" name onto the end of the menu
4786  Assert(parent_node >= 0);
4787 
4788  // get the operator type of the node
4789  op = get_operator_const(tree_nodes[parent_node].text);
4790 
4791  // first child node
4792  child = tree_nodes[parent_node].child;
4793  Assert(child >= 0);
4794 
4795  switch(op)
4796  {
4797  // where we care about hull strength
4798  case OP_REPAIR_SUBSYSTEM:
4799  case OP_SABOTAGE_SUBSYSTEM:
4801  special_subsys = OPS_STRENGTH;
4802  break;
4803 
4804  // Armor types need Hull and Shields but not Simulated Hull
4805  case OP_SET_ARMOR_TYPE:
4806  special_subsys = OPS_ARMOR;
4807  break;
4808 
4809  // awacs subsystems
4810  case OP_AWACS_SET_RADIUS:
4811  special_subsys = OPS_AWACS;
4812  break;
4813 
4814  // rotating
4819  special_subsys = OPS_ROTATE;
4820  break;
4821 
4822  // where we care about capital ship subsystem cargo
4824  special_subsys = OPS_CAP_CARGO;
4825 
4826  // get the next sibling
4827  child = tree_nodes[child].next;
4828  break;
4829 
4830  // where we care about turrets carrying beam weapons
4831  case OP_BEAM_FIRE:
4832  special_subsys = OPS_BEAM_TURRET;
4833 
4834  // if this is arg index 3 (targeted ship)
4835  if(arg_index == 3)
4836  {
4837  special_subsys = OPS_STRENGTH;
4838 
4839  child = tree_nodes[child].next;
4840  Assert(child >= 0);
4841  child = tree_nodes[child].next;
4842  }
4843  else
4844  {
4845  Assert(arg_index == 1);
4846  }
4847  break;
4848 
4849  case OP_BEAM_FIRE_COORDS:
4850  special_subsys = OPS_BEAM_TURRET;
4851  break;
4852 
4853  // these sexps check the subsystem of the *second entry* on the list, not the first
4854  case OP_DISTANCE_SUBSYSTEM:
4855  case OP_SET_CARGO:
4856  case OP_IS_CARGO:
4857  case OP_CHANGE_AI_CLASS:
4858  case OP_IS_AI_CLASS:
4859  case OP_MISSILE_LOCKED:
4861  // iterate to the next field
4862  child = tree_nodes[child].next;
4863  break;
4864 
4865  // this sexp checks the subsystem of the *fourth entry* on the list
4866  case OP_QUERY_ORDERS:
4867  child = tree_nodes[child].next;
4868  Assert(child >= 0);
4869  child = tree_nodes[child].next;
4870  Assert(child >= 0);
4871  child = tree_nodes[child].next;
4872  break;
4873 
4874  // this sexp checks the subsystem of the *ninth entry* on the list
4875  case OP_WEAPON_CREATE:
4876  // iterate to the next field eight times
4877  child = tree_nodes[child].next;
4878  Assert(child >= 0);
4879  child = tree_nodes[child].next;
4880  Assert(child >= 0);
4881  child = tree_nodes[child].next;
4882  Assert(child >= 0);
4883  child = tree_nodes[child].next;
4884  Assert(child >= 0);
4885  child = tree_nodes[child].next;
4886  Assert(child >= 0);
4887  child = tree_nodes[child].next;
4888  Assert(child >= 0);
4889  child = tree_nodes[child].next;
4890  Assert(child >= 0);
4891  child = tree_nodes[child].next;
4892  break;
4893  }
4894 
4895  // now find the ship and add all relevant subsystems
4896  Assert(child >= 0);
4897  sh = ship_name_lookup(tree_nodes[child].text, 1);
4898  if (sh >= 0) {
4899  subsys = GET_FIRST(&Ships[sh].subsys_list);
4900  while (subsys != END_OF_LIST(&Ships[sh].subsys_list)) {
4901  // add stuff
4902  switch(special_subsys){
4903  // subsystem cargo
4904  case OPS_CAP_CARGO:
4906  head.add_data(subsys->system_info->subobj_name);
4907  }
4908  break;
4909 
4910  // beam fire
4911  case OPS_BEAM_TURRET:
4912  head.add_data(subsys->system_info->subobj_name);
4913  break;
4914 
4915  // awacs level
4916  case OPS_AWACS:
4917  if (subsys->system_info->flags & MSS_FLAG_AWACS) {
4918  head.add_data(subsys->system_info->subobj_name);
4919  }
4920  break;
4921 
4922  // rotating
4923  case OPS_ROTATE:
4924  if (subsys->system_info->flags & MSS_FLAG_ROTATES) {
4925  head.add_data(subsys->system_info->subobj_name);
4926  }
4927  break;
4928 
4929  // everything else
4930  default:
4931  head.add_data(subsys->system_info->subobj_name);
4932  break;
4933  }
4934 
4935  // next subsystem
4936  subsys = GET_NEXT(subsys);
4937  }
4938  }
4939 
4940  // if one of the subsystem strength operators, append the Hull string and the Simulated Hull string
4941  if(special_subsys == OPS_STRENGTH){
4942  head.add_data(SEXP_HULL_STRING);
4944  }
4945  // if setting armor type we only need Hull and Shields
4946  if(special_subsys == OPS_ARMOR){
4947  head.add_data(SEXP_HULL_STRING);
4949  }
4950 
4951  return head.next;
4952 }
4953 
4955 {
4956  int i, child, shipnum, num_added = 0;
4957  sexp_list_item head;
4958  ship_subsys *subsys;
4959 
4960  // first child node
4961  child = tree_nodes[parent_node].child;
4962  Assert(child >= 0);
4963 
4964  // now find the ship
4965  shipnum = ship_name_lookup(tree_nodes[child].text, 1);
4966  if (shipnum < 0) {
4967  return head.next;
4968  }
4969 
4970  // add all relevant subsystem types
4971  for (i = 0; i < SUBSYSTEM_MAX; i++) {
4972  // don't allow these two
4973  if (i == SUBSYSTEM_NONE || i == SUBSYSTEM_UNKNOWN)
4974  continue;
4975 
4976  // loop through all ship subsystems
4977  subsys = GET_FIRST(&Ships[shipnum].subsys_list);
4978  while (subsys != END_OF_LIST(&Ships[shipnum].subsys_list)) {
4979  // check if this subsystem is of this type
4980  if (i == subsys->system_info->type) {
4981  // subsystem type is applicable, so add it
4982  head.add_data(Subsystem_types[i]);
4983  num_added++;
4984  break;
4985  }
4986 
4987  // next subsystem
4988  subsys = GET_NEXT(subsys);
4989  }
4990  }
4991 
4992  // if no subsystem types, go ahead and add NONE (even though it won't be checked)
4993  if (num_added == 0) {
4995  }
4996 
4997  return head.next;
4998 }
4999 
5001 {
5002  char buf[NAME_LENGTH+8];
5004  int j;
5005  sexp_list_item head;
5006 
5007  for (ii = Waypoint_lists.begin(); ii != Waypoint_lists.end(); ++ii)
5008  {
5009  for (j = 0; (uint) j < ii->get_waypoints().size(); ++j)
5010  {
5011  sprintf(buf, "%s:%d", ii->get_name(), j + 1);
5012  head.add_data_dup(buf);
5013  }
5014  }
5015 
5016  return head.next;
5017 }
5018 
5020 {
5021  int i;
5022  sexp_list_item head;
5023 
5024  for (i=0; i<Num_iffs; i++)
5025  head.add_data(Iff_info[i].iff_name);
5026 
5027  return head.next;
5028 }
5029 
5031 {
5032  int i;
5033  sexp_list_item head;
5034 
5035  for (i=0; i<Num_ai_classes; i++)
5036  head.add_data(Ai_class_names[i]);
5037 
5038  return head.next;
5039 }
5040 
5042 {
5043  sexp_list_item head;
5044 
5045  head.add_data("<species support ship class>");
5046 
5047  for (auto it = Ship_info.cbegin(); it != Ship_info.cend(); ++it)
5048  {
5049  if (it->flags & SIF_SUPPORT)
5050  {
5051  head.add_data(it->name);
5052  }
5053  }
5054 
5055  return head.next;
5056 }
5057 
5059 {
5060  sexp_list_item head;
5061 
5062  for (auto it = Ssm_info.cbegin(); it != Ssm_info.cend(); ++it)
5063  {
5064  head.add_data(it->name);
5065  }
5066 
5067  return head.next;
5068 }
5069 
5071 {
5072  object *objp;
5073  sexp_list_item head;
5074 
5075  head.add_data("<no anchor>");
5076 
5077  for ( objp = GET_FIRST(&obj_used_list); objp != END_OF_LIST(&obj_used_list); objp = GET_NEXT(objp) )
5078  {
5079  if ( (objp->type == OBJ_SHIP) || (objp->type == OBJ_START) )
5080  {
5081  // determine if this ship has a docking bay
5082  if (ship_has_dock_bay(objp->instance))
5083  {
5084  head.add_data(Ships[objp->instance].ship_name);
5085  }
5086  }
5087  }
5088 
5089  return head.next;
5090 }
5091 
5093 {
5094  int i;
5095  sexp_list_item head;
5096 
5097  head.add_data("<No Music>");
5098 
5099  for (i=0; i<Num_soundtracks; i++)
5100  {
5101  head.add_data(Soundtracks[i].name);
5102  }
5103 
5104  return head.next;
5105 }
5106 
5108 {
5109  int i;
5110  sexp_list_item head;
5111 
5112  for (i=0; i<MAX_ARRIVAL_NAMES; i++)
5114 
5115  return head.next;
5116 }
5117 
5119 {
5120  int i;
5121  sexp_list_item head;
5122 
5123  for (i=0; i<MAX_DEPARTURE_NAMES; i++)
5125 
5126  return head.next;
5127 }
5128 
5130 {
5131  int i, restrict_to_players;
5132  object *objp;
5133  sexp_list_item head;
5134 
5135  for (restrict_to_players = 0; restrict_to_players < 2; restrict_to_players++)
5136  {
5137  for (i = 0; i < Num_iffs; i++)
5138  {
5139  char tmp[NAME_LENGTH + 15];
5140  stuff_special_arrival_anchor_name(tmp, i, restrict_to_players, 0);
5141 
5142  head.add_data_dup(tmp);
5143  }
5144  }
5145 
5146  for ( objp = GET_FIRST(&obj_used_list); objp != END_OF_LIST(&obj_used_list); objp = GET_NEXT(objp) )
5147  {
5148  if ( (objp->type == OBJ_SHIP) || (objp->type == OBJ_START) )
5149  {
5150  head.add_data(Ships[objp->instance].ship_name);
5151  }
5152  }
5153 
5154  return head.next;
5155 }
5156 
5158 {
5159  int i, n, w, z, child;
5160  sexp_list_item head;
5161 
5162  Assert(parent_node >= 0);
5163  child = tree_nodes[parent_node].child;
5164  Assert(child >= 0);
5165  n = ship_name_lookup(tree_nodes[child].text, 1);
5166  if (n >= 0) {
5167  // add operators if it's an ai-goal and ai-goal is allowed for that ship
5168  for (i=0; i<Num_operators; i++) {
5170  head.add_op(i);
5171  }
5172 
5173  } else {
5174  z = wing_name_lookup(tree_nodes[child].text);
5175  if (z >= 0) {
5176  for (w=0; w<Wings[z].wave_count; w++) {
5177  n = Wings[z].ship_index[w];
5178  // add operators if it's an ai-goal and ai-goal is allowed for that ship
5179  for (i=0; i<Num_operators; i++) {
5181  head.add_op(i);
5182  }
5183  }
5184  // when dealing with the special argument add them all. It's up to the FREDder to ensure invalid orders aren't given
5185  } else if (!strcmp(tree_nodes[child].text, SEXP_ARGUMENT_STRING)) {
5186  for (i=0; i<Num_operators; i++) {
5188  head.add_op(i);
5189  }
5190  }
5191  } else
5192  return NULL; // no valid ship or wing to check against, make nothing available
5193  }
5194 
5195  return head.next;
5196 }
5197 
5199 {
5200  int i, z;
5201  sexp_list_item head;
5202  int sh = -1;
5203 
5204  Assert(parent_node >= 0);
5205  Assert(!stricmp(tree_nodes[parent_node].text, "ai-dock") || !stricmp(tree_nodes[parent_node].text, "set-docked"));
5206 
5207  if (!stricmp(tree_nodes[parent_node].text, "ai-dock"))
5208  {
5209  z = tree_nodes[parent_node].parent;
5210  Assert(z >= 0);
5211  Assert(!stricmp(tree_nodes[z].text, "add-ship-goal") || !stricmp(tree_nodes[z].text, "add-wing-goal") || !stricmp(tree_nodes[z].text, "add-goal"));
5212 
5213  z = tree_nodes[z].child;
5214  Assert(z >= 0);
5215 
5216  sh = ship_name_lookup(tree_nodes[z].text, 1);
5217  }
5218  else if (!stricmp(tree_nodes[parent_node].text, "set-docked"))
5219  {
5220  //Docker ship should be the first child node
5221  z = tree_nodes[parent_node].child;
5222  sh = ship_name_lookup(tree_nodes[z].text, 1);
5223  }
5224 
5225  if (sh >= 0)
5226  {
5227  z = get_docking_list(Ship_info[Ships[sh].ship_info_index].model_num);
5228  for (i=0; i<z; i++)
5229  head.add_data(Docking_bay_list[i]);
5230  }
5231 
5232  return head.next;
5233 }
5234 
5236 {
5237  int i, z;
5238  sexp_list_item head;
5239  int sh = -1;
5240 
5241  Assert(parent_node >= 0);
5242  Assert(!stricmp(tree_nodes[parent_node].text, "ai-dock") || !stricmp(tree_nodes[parent_node].text, "set-docked"));
5243 
5244  if (!stricmp(tree_nodes[parent_node].text, "ai-dock"))
5245  {
5246  z = tree_nodes[parent_node].child;
5247  Assert(z >= 0);
5248 
5249  sh = ship_name_lookup(tree_nodes[z].text, 1);
5250  }
5251  else if (!stricmp(tree_nodes[parent_node].text, "set-docked"))
5252  {
5253  //Dockee ship should be the third child node
5254  z = tree_nodes[parent_node].child; // 1
5255  z = tree_nodes[z].next; // 2
5256  z = tree_nodes[z].next; // 3
5257 
5258  sh = ship_name_lookup(tree_nodes[z].text, 1);
5259  }
5260 
5261  if (sh >= 0)
5262 {
5263  z = get_docking_list(Ship_info[Ships[sh].ship_info_index].model_num);
5264  for (i=0; i<z; i++)
5265  head.add_data(Docking_bay_list[i]);
5266  }
5267 
5268  return head.next;
5269 }
5270 
5272 {
5273  char *str;
5274  int i;
5275  sexp_list_item head;
5276 
5277  if (m_mode == MODE_EVENTS) {
5279  // this for looks a litle strange, but had to do it get rid of a warning. Conditional
5280  //uses last statement is sequence, i.e. same as for (i=0, str, i++)
5281  for (i=0; str = Event_editor_dlg->current_message_name(i), str; i++)
5282  head.add_data(str);
5283 
5284  } else {
5285  for (i=Num_builtin_messages; i<Num_messages; i++)
5286  head.add_data(Messages[i].name);
5287  }
5288 
5289  return head.next;
5290 }
5291 
5293 {
5294  int i;
5295  sexp_list_item head;
5296 
5297  for (i = 0; i < Num_personas; i++) {
5298  if (Personas[i].flags & PERSONA_FLAG_WINGMAN) {
5299  head.add_data(Personas[i].name);
5300  }
5301  }
5302 
5303  return head.next;
5304 }
5305 
5307 {
5308  int i;
5309  sexp_list_item head;
5310 
5311  for (i = 0; i < Num_fonts; i++) {
5312  head.add_data(Fonts[i].filename);
5313  }
5314 
5315  return head.next;
5316 }
5317 
5319 {
5320  object *ptr;
5321  sexp_list_item head;
5322 
5323  //head.add_data("<any allied>");
5324  head.add_data("#Command");
5325  head.add_data("<any wingman>");
5326  head.add_data("<none>");
5327 
5328  ptr = GET_FIRST(&obj_used_list);
5329  while (ptr != END_OF_LIST(&obj_used_list)) {
5330  if ((ptr->type == OBJ_SHIP) || (ptr->type == OBJ_START))
5331  if (!(Ship_info[Ships[get_ship_from_obj(ptr)].ship_info_index].flags & SIF_NOT_FLYABLE))
5332  head.add_data(Ships[ptr->instance].ship_name);
5333 
5334  ptr = GET_NEXT(ptr);
5335  }
5336 
5337  return head.next;
5338 }
5339 
5341 {
5342  sexp_list_item head;
5343 
5344  head.add_data("High");
5345  head.add_data("Normal");
5346  head.add_data("Low");
5347  return head.next;
5348 }
5349 
5351 {
5352  sexp_list_item head;
5353 </