]> icculus.org git repositories - taylor/freespace2.git/blob - src/fred2/sexp_tree.cpp
The Great Newline Fix
[taylor/freespace2.git] / src / fred2 / sexp_tree.cpp
1 /*
2  * $Logfile: /Freespace2/code/Fred2/Sexp_tree.cpp $
3  * $Revision$
4  * $Date$
5  * $Author$
6  *
7  * Sexp tree handler class.  Almost everything is handled by this class.
8  *
9  * $Log$
10  * Revision 1.2  2002/05/07 03:16:44  theoddone33
11  * The Great Newline Fix
12  *
13  * Revision 1.1.1.1  2002/05/03 03:28:08  root
14  * Initial import.
15  *
16  * 
17  * 43    9/07/99 9:22p Jefff
18  * added 2 more assignable medals
19  * 
20  * 42    9/07/99 1:05a Andsager
21  * Added team-score sexp for multi team vs team missions
22  * 
23  * 41    8/27/99 4:07p Andsager
24  * Add is-ship-visible sexp.  Make ship-vanish sexp SINGLE player only
25  * 
26  * 40    8/24/99 4:25p Andsager
27  * Add ship-vanish sexp
28  * 
29  * 39    8/16/99 10:04p Andsager
30  * Add special-warp-dist and special-warpout-name sexp for Knossos device
31  * warpout.
32  * 
33  * 38    8/09/99 2:00p Dave
34  * 2 new sexpressions.
35  * 
36  * 37    8/02/99 4:26p Dave
37  * Added 2 new sexpressions.
38  * 
39  * 36    8/02/99 1:43p Andsager
40  * format fix
41  * 
42  * 35    7/28/99 1:36p Andsager
43  * Modify cargo1 to include flag CARGO_NO_DEPLETE.  Add sexp
44  * cargo-no-deplete (only for BIG / HUGE).  Modify ship struct to pack
45  * better.
46  * 
47  * 34    7/24/99 4:56p Dave
48  * Added 3 new sexpressions.
49  * 
50  * 33    7/21/99 8:10p Dave
51  * First run of supernova effect.
52  * 
53  * 32    7/20/99 9:19p Andsager
54  * Added facing waypoint sexp
55  * 
56  * 31    7/20/99 9:54a Andsager
57  * Add subsys-set-random sexp
58  * 
59  * 30    7/19/99 12:02p Andsager
60  * Allow AWACS on any ship subsystem. Fix sexp_set_subsystem_strength to
61  * only blow up subsystem if its strength is > 0
62  * 
63  * 29    7/13/99 3:37p Andsager
64  * Add secondaries-depleted sexp
65  * 
66  * 28    7/12/99 12:01p Andsager
67  * Make message by default come from command.
68  * 
69  * 27    7/08/99 12:06p Andsager
70  * Add turret-tagged-only and turret-tagged-clear sexp.
71  * 
72  * 26    6/29/99 10:08a Andsager
73  * Add guardian sexp
74  * 
75  * 25    6/23/99 5:51p Andsager
76  * Add waypoint-cap-speed.  Checkin stealth ai - inactive.
77  * 
78  * 24    6/16/99 10:21a Dave
79  * Added send-message-list sexpression.
80  * 
81  * 23    6/01/99 8:35p Dave
82  * Finished lockarm weapons. Added proper supercap weapons/damage. Added
83  * awacs-set-radius sexpression.
84  * 
85  * 22    5/24/99 11:28a Dave
86  * Sexpression for adding/removing ships from the hud escort list.
87  * 
88  * 21    5/20/99 1:40p Andsager
89  * Fix find_text() to only look at nodes that are used.
90  * 
91  * 20    5/04/99 5:21p Andsager
92  * 
93  * 19    4/28/99 9:33a Andsager
94  * Add turret-free and turret-lock (and -all) sexp.  Stargger start time
95  * of beam weapons beam-free and beam-free-all.
96  * 
97  * 18    4/26/99 2:14p Andsager
98  * Add beam-protect-ship and beam-unprotect-ship sexp.
99  * 
100  * 17    4/23/99 12:01p Johnson
101  * Added SIF_HUGE_SHIP
102  * 
103  * 16    4/02/99 9:54a Dave
104  * Added a few more options in the weapons.tbl for beam weapons. Attempt
105  * at putting "pain" packets into multiplayer.
106  * 
107  * 15    3/20/99 3:46p Dave
108  * Added support for model-based background nebulae. Added 3 new
109  * sexpressions.
110  * 
111  * 14    3/04/99 6:09p Dave
112  * Added in sexpressions for firing beams and checking for if a ship is
113  * tagged.
114  * 
115  * 13    3/01/99 10:00a Dave
116  * Fxied several dogfight related stats bugs.
117  * 
118  * 12    2/26/99 6:01p Andsager
119  * Add sexp has-been-tagged-delay and cap-subsys-cargo-known-delay
120  * 
121  * 11    1/26/99 10:09a Andsager
122  * Better checking for modifying/deleting variables
123  * 
124  * 10    1/25/99 5:16p Andsager
125  * Handle change of variable type on modify-variable
126  * 
127  * 9     1/25/99 8:10a Andsager
128  * Add sexp_modify_variable().  Changed syntax checking to allow, adding
129  * operator return type ambiguous
130  * 
131  * 8     1/24/99 11:37p Dave
132  * First full rev of beam weapons. Very customizable. Removed some bogus
133  * Int3()'s in low level net code.
134  * 
135  * 7     1/20/99 9:02a Andsager
136  * Fix bug in verify_and_fix_arguments, where list of strings can have
137  * NULL strings.
138  * 
139  * 6     1/19/99 3:57p Andsager
140  * Round 2 of variables
141  * 
142  * 5     12/17/98 2:39p Andsager
143  * Added bitmaps for campaign editor.  Changed input into insert() to
144  * include bitmaps
145  * 
146  * 4     12/17/98 2:34p Andsager
147  * new bitmap and dialog for campaign editor
148  * 
149  * 3     10/13/98 9:27a Dave
150  * Started neatening up freespace.h
151  * 
152  * 2     10/07/98 6:28p Dave
153  * Initial checkin. Renamed all relevant stuff to be Fred2 instead of
154  * Fred. Globalized mission and campaign file extensions. Removed Silent
155  * Threat specific code.
156  * 
157  * 1     10/07/98 3:02p Dave
158  * 
159  * 1     10/07/98 3:00p Dave
160  * 
161  * 179   9/25/98 1:33p Andsager
162  * Add color to event editor (root and chain) indicating mission directive
163  * 
164  * 178   9/15/98 4:26p Allender
165  * added sexpression help for some sexpressions
166  * 
167  * 177   6/09/98 5:15p Lawrance
168  * French/German localization
169  * 
170  * 176   5/21/98 12:58a Hoffoss
171  * Fixed warnings optimized build turned up.
172  * 
173  * 175   5/15/98 6:45p Hoffoss
174  * Made some things not appear in the release version of Fred.
175  * 
176  * 174   5/14/98 10:15a Allender
177  * add optional argument to prevous-goal/event operators to specify what
178  * sexpression should return when being played as a single mission
179  * 
180  * 173   5/04/98 10:57a Johnson
181  * Fixed bug with labeled roots allowing insert.
182  * 
183  * 172   4/25/98 7:39p Allender
184  * fixd some small hotkey stuff.  Worked on turret orientation being
185  * correct for multiplayer.  new sexpression called end-campaign will will
186  * end the main campaign
187  * 
188  * 171   4/23/98 5:49p Hoffoss
189  * Added tracking of techroom database list info in pilot files, added
190  * sexp to add more to list, made mouse usable on ship listing in tech
191  * room.
192  * 
193  * 170   4/15/98 3:46p Hoffoss
194  * Fixed bug with getting a default argument value from an opf listing was
195  * utilizing temporary memory that was being destroyed before we were
196  * finished with it.
197  * 
198  * 169   4/14/98 5:46p Hoffoss
199  * Added special-check operator.
200  * 
201  * 168   4/14/98 5:24p Hoffoss
202  * Added a custom operator for training handling for Mike K.
203  * 
204  * 167   4/14/98 4:19p Jim
205  * Fixed bug with deleting an argument to an operator that you shouldn't
206  * be allowed to.
207  * 
208  * 166   4/07/98 10:51a Allender
209  * remove any allied from message senders.  Make heads for mission
210  * specific messages play appropriately
211  * 
212  * 165   4/03/98 12:17a Allender
213  * new sexpression to detect departed or destroyed.  optionally disallow
214  * support ships.  Allow docking with escape pods 
215  * 
216  * 164   3/30/98 2:57p Hoffoss
217  * Fixed event listing in campaign editor mode.
218  * 
219  * 163   3/26/98 3:13p Duncan
220  * Fixed bug in goal name listing generation function.  Allender forgot
221  * about an assumption being made with them when he used it for
222  * invalidate-goal.
223  * 
224  * 162   3/23/98 2:46p Hoffoss
225  * Fixed bug with default argument available for OPF_MESSAGE even when
226  * there were no messages, and added "#Command" as a message source to
227  * listing.
228  * 
229  * 161   3/21/98 7:36p Lawrance
230  * Move jump nodes to own lib.
231  * 
232  *
233  * $NoKeywords: $
234  */
235
236 #include "stdafx.h"
237 #include "sexp_tree.h"
238 #include "fred.h"
239 #include "freddoc.h"
240 #include "management.h"
241 #include "sexp.h"
242 #include "operatorargtypeselect.h"
243 #include "linklist.h"
244 #include "eventeditor.h"
245 #include "missiongoalsdlg.h"
246 #include "aigoals.h"
247 #include "missionmessage.h"
248 #include "missioncampaign.h"
249 #include "campaigneditordlg.h"
250 #include "hudsquadmsg.h"
251 #include "ignoreordersdlg.h"
252 #include "medals.h"
253 #include "controlsconfig.h"
254 #include "hudgauges.h"
255 #include "starfield.h"
256 #include "jumpnode.h"
257 #include "addvariabledlg.h"
258 #include "modifyvariabledlg.h"
259
260 #define MAX_OP_MENUS    30
261
262 #define ID_VARIABLE_MENU        0xda00
263 #define ID_ADD_MENU                     0xdc00
264 #define ID_REPLACE_MENU         0xde00
265 // note: stay below 0xe000 so we don't collide with MFC defines..
266
267 #ifdef _DEBUG
268 #define new DEBUG_NEW
269 #undef THIS_FILE
270 static char THIS_FILE[] = __FILE__;
271 #endif
272
273 BEGIN_MESSAGE_MAP(sexp_tree, CTreeCtrl)
274         //{{AFX_MSG_MAP(sexp_tree)
275         ON_NOTIFY_REFLECT(TVN_BEGINDRAG, OnBegindrag)
276         ON_WM_MOUSEMOVE()
277         ON_WM_LBUTTONUP()
278         ON_WM_DESTROY()
279         ON_WM_LBUTTONDOWN()
280         ON_NOTIFY_REFLECT(TVN_KEYDOWN, OnKeydown)
281         //}}AFX_MSG_MAP
282 END_MESSAGE_MAP()
283
284 static int Add_count, Replace_count;
285 static int Modify_variable;
286
287 struct sexp_help_struct {
288         int id;
289         char *help;
290
291 } Sexp_help[] = {
292         { OP_PLUS, "Plus (Arithmetic operator)\r\n"
293                 "\tAdds numbers and returns results.\r\n\r\n"
294                 "Returns a number; Takes 2 or more numeric arguments." },
295
296         { OP_MINUS, "Minus (Arithmetic operator)\r\n"
297                 "\tSubtracts numbers and returns results.\r\n\r\n"
298                 "Returns a number; Takes 2 or more numeric arguments." },
299
300         { OP_MOD, "Mod (Arithmetic operator)\r\n"
301                 "\tDivides numbers and returns the remainer.\r\n\r\n"
302                 "Returns a number; Takes 2 or more numeric arguments." },
303
304         { OP_MUL, "Multiply (Arithmetic operator)\r\n"
305                 "\tMultiplies numbers and returns results.\r\n\r\n"
306                 "Returns a number; Takes 2 or more numeric arguments." },
307
308         { OP_DIV, "Divide (Arithmetic operator)\r\n"
309                 "\tDivides numbers and returns results.\r\n\r\n"
310                 "Returns a number; Takes 2 or more numeric arguments." },
311
312         { OP_RAND, "Random number (Arithmetic operator)\r\n"
313                 "\tGets a random number and returns result.\r\n\r\n"
314                 "Returns a number; Takes 2 numeric arguments.\r\n"
315                 "\t1:\tLow range of random number.\r\n"
316                 "\t2:\tHigh range of random number." },
317
318         { OP_TRUE, "True (Boolean operator)\r\n"
319                 "\tA true boolean state\r\n\r\n"
320                 "Returns a boolean value." },
321
322         { OP_FALSE, "False (Boolean operator)\r\n"
323                 "\tA false boolean state\r\n\r\n"
324                 "Returns a boolean value." },
325
326         { OP_AND, "And (Boolean operator)\r\n"
327                 "\tAnd is true if all of it's arguments are true.\r\n\r\n"
328                 "Returns a boolean value; Takes 2 or more boolean arguments." },
329
330         { OP_OR, "Or (Boolean operator)\r\n"
331                 "\tOr is true if any of it's arguments are true.\r\n\r\n"
332                 "Returns a boolean value; Takes 2 or more boolean arguments." },
333
334         { OP_EQUALS, "Equals (Boolean operator)\r\n"
335                 "\tIs true if all of it's arguments are equal.\r\n\r\n"
336                 "Returns a boolean value; Takes 2 or more numeric arguments." },
337
338         { OP_GREATER_THAN, "Greater than (Boolean operator)\r\n"
339                 "\tTrue if first argument is greater than the second argument.\r\n\r\n"
340                 "Returns a boolean value; Takes 2 numeric arguments." },
341
342         { OP_LESS_THAN, "Less than (Boolean operator)\r\n"
343                 "\tTrue if first argument is less than the second argument.\r\n\r\n"
344                 "Returns a boolean value; Takes 2 numeric arguments." },
345
346         { OP_IS_IFF, "Is IFF (Boolean operator)\r\n"
347                 "\tTrue if ship{s} are all of the specified team.\r\n\r\n"
348                 "Returns a boolean value; Takes 2 or more arguments:\r\n"
349                 "/t1:\tTeam (\"friendly\", \"hostile\" or \"unknown\").\r\n"
350                 "\tRest:\tName of ship to check." },
351
352         { OP_HAS_TIME_ELAPSED, "Has time elapsed (Boolean operator)\r\n"
353                 "\tBecomes true when the specified amount of time has elapsed (Mission time "
354                 "becomes greater than the specified time).\r\n"
355                 "Returns a boolean value; Takes 1 numeric argument:\r\n"
356                 "\t1:\tThe amount of time in seconds." },
357
358         { OP_NOT, "Not (Boolean operator)\r\n"
359                 "\tReturns opposite boolean value of argument (True becomes false, and "
360                 "false becomes true).\r\n\r\n"
361                 "Returns a boolean value; Takes 1 boolean argument." },
362
363         { OP_PREVIOUS_GOAL_TRUE, "Previous Mission Goal True (Boolean operator)\r\n"
364                 "\tReturns true if the specified goal in the specified mission is true "
365                 "(or succeeded).  It returns false otherwise.\r\n\r\n"
366                 "Returns a boolean value; Takes 2 arguments + 1 optional:\r\n"
367                 "\t1:\tName of the mission.\r\n"
368                 "\t2:\tName of the goal in the mission.\r\n"
369                 "\t3:\t(Optional) True/False which signifies what this sexpession should return when "
370                 "this mission is played as a single mission." },
371
372         { OP_PREVIOUS_GOAL_FALSE, "Previous Mission Goal False (Boolean operator)\r\n"
373                 "\tReturns true if the specified goal in the specified mission "
374                 "is false (or failed).  It returns false otherwise.\r\n\r\n"
375                 "Returns a boolean value; Takes 2 arguments + 1 optional:\r\n"
376                 "\t1:\tName of the mission.\r\n"
377                 "\t2:\tName of the goal in the mission.\r\n"
378                 "\t3:\t(Optional) True/False which signifies what this sexpession should return when "
379                 "this mission is played as a single mission." },
380
381         { OP_PREVIOUS_GOAL_INCOMPLETE, "Previous Mission Goal Incomplete (Boolean operator)\r\n"
382                 "\tReturns true if the specified goal in the specified mission "
383                 "is incomplete (not true or false).  It returns false otherwise.\r\n\r\n"
384                 "Returns a boolean value; Takes 2 arguments + 1 optional:\r\n"
385                 "\t1:\tName of the mission.\r\n"
386                 "\t2:\tName of the goal in the mission.\r\n"
387                 "\t3:\t(Optional) True/False which signifies what this sexpession should return when "
388                 "this mission is played as a single mission." },
389
390         { OP_PREVIOUS_EVENT_TRUE, "Previous Mission Event True (Boolean operator)\r\n"
391                 "\tReturns true if the specified event in the specified mission is true "
392                 "(or succeeded).  It returns false otherwise.\r\n\r\n"
393                 "Returns a boolean value; Takes 2 arguments + 1 optional:\r\n"
394                 "\t1:\tName of the mission.\r\n"
395                 "\t2:\tName of the event in the mission.\r\n"
396                 "\t3:\t(Optional) True/False which signifies what this sexpession should return when "
397                 "this mission is played as a single mission." },
398
399         { OP_PREVIOUS_EVENT_FALSE, "Previous Mission Event False (Boolean operator)\r\n"
400                 "\tReturns true if the specified event in the specified mission "
401                 "is false (or failed).  It returns false otherwise.\r\n\r\n"
402                 "Returns a boolean value; Takes 2 arguments + 1 optional:\r\n"
403                 "\t1:\tName of the mission.\r\n"
404                 "\t2:\tName of the event in the mission.\r\n"
405                 "\t3:\t(Optional) True/False which signifies what this sexpession should return when "
406                 "this mission is played as a single mission." },
407
408         { OP_PREVIOUS_EVENT_INCOMPLETE, "Previous Mission Event Incomplete (Boolean operator)\r\n"
409                 "\tReturns true if the specified event in the specified mission "
410                 "is incomplete (not true or false).  It returns false otherwise.\r\n\r\n"
411                 "Returns a boolean value; Takes 2 arguments + 1 optional:\r\n"
412                 "\t1:\tName of the mission.\r\n"
413                 "\t2:\tName of the event in the mission.\r\n"
414                 "\t3:\t(Optional) True/False which signifies what this sexpession should return when "
415                 "this mission is played as a single mission." },
416
417         { OP_GOAL_TRUE_DELAY, "Mission Goal True (Boolean operator)\r\n"
418                 "\tReturns true N seconds after the specified goal in the this mission is true "
419                 "(or succeeded).  It returns false otherwise.\r\n\r\n"
420                 "Returns a boolean value; Takes 2 arguments:\r\n"
421                 "\t1:\tName of the event in the mission.\r\n"
422                 "\t2:\tNumber of seconds to delay before returning true."},
423
424         { OP_GOAL_FALSE_DELAY, "Mission Goal False (Boolean operator)\r\n"
425                 "\tReturns true N seconds after the specified goal in the this mission is false "
426                 "(or failed).  It returns false otherwise.\r\n\r\n"
427                 "Returns a boolean value; Takes 2 arguments:\r\n"
428                 "\t1:\tName of the event in the mission.\r\n"
429                 "\t2:\tNumber of seconds to delay before returning true."},
430
431         { OP_GOAL_INCOMPLETE, "Mission Goal Incomplete (Boolean operator)\r\n"
432                 "\tReturns true if the specified goal in the this mission is incomplete.  This "
433                 "sexpression will only be useful in conjunction with another sexpression like"
434                 "has-time-elapsed.  Used alone, it will return true upon misison startup."
435                 "Returns a boolean value; Takes 1 argument:\r\n"
436                 "\t1:\tName of the event in the mission."},
437
438         { OP_EVENT_TRUE_DELAY, "Mission Event True (Boolean operator)\r\n"
439                 "\tReturns true N seconds after the specified event in the this mission is true "
440                 "(or succeeded).  It returns false otherwise.\r\n\r\n"
441                 "Returns a boolean value; Takes 2 arguments:\r\n"
442                 "\t1:\tName of the event in the mission.\r\n"
443                 "\t2:\tNumber of seconds to delay before returning true."},
444
445         { OP_EVENT_FALSE_DELAY, "Mission Event False (Boolean operator)\r\n"
446                 "\tReturns true N seconds after the specified event in the this mission is false "
447                 "(or failed).  It returns false otherwise.\r\n\r\n"
448                 "Returns a boolean value; Takes 2 arguments:\r\n"
449                 "\t1:\tName of the event in the mission.\r\n"
450                 "\t2:\tNumber of seconds to delay before returning true."},
451
452         { OP_EVENT_INCOMPLETE, "Mission Event Incomplete (Boolean operator)\r\n"
453                 "\tReturns true if the specified event in the this mission is incomplete.  This "
454                 "sexpression will only be useful in conjunction with another sexpression like"
455                 "has-time-elapsed.  Used alone, it will return true upon misison startup."
456                 "Returns a boolean value; Takes 1 argument:\r\n"
457                 "\t1:\tName of the event in the mission."},
458
459         { OP_IS_DESTROYED_DELAY, "Is destroyed delay (Boolean operator)\r\n"
460                 "\tBecomes true <delay> seconds after all specified ships have been destroyed.\r\n\r\n"
461                 "Returns a boolean value; Takes 2 or more arguments:\r\n"
462                 "\t1:\tTime delay in seconds (see above).\r\n"
463                 "\tRest:\tName of ship (or wing) to check status of." },
464
465         { OP_IS_SUBSYSTEM_DESTROYED_DELAY, "Is subsystem destroyed delay (Boolean operator)\r\n"
466                 "\tBecomes true <delay> seconds after the specified subsystem of the specified "
467                 "ship is destroyed.\r\n\r\n"
468                 "Returns a boolean value; Takes 3 arguments:\r\n"
469                 "\t1:\tName of ship the subsystem we are checking is on.\r\n"
470                 "\t2:\tThe name of the subsystem we are checking status of.\r\n"
471                 "\t3:\tTime delay in seconds (see above)." },
472
473         { OP_IS_DISABLED_DELAY, "Is disabled delay (Boolean operator)\r\n"
474                 "\tBecomes true <delay> seconds after the specified ship(s) are disabled.  A "
475                 "ship is disabled when all of it's engine subsystems are destroyed.  All "
476                 "ships must be diabled for this function to return true.\r\n\r\n"
477                 "Returns a boolean value; Takes 2 or more arguments:\r\n"
478                 "\t1:\tTime delay is seconds (see above).\r\n"
479                 "\tRest:\tNames of ships to check disabled status of." },
480
481         { OP_IS_DISARMED_DELAY, "Is disarmed delay (Boolean operator)\r\n"
482                 "\tBecomes true <delay> seconds after the specified ship(s) are disarmed.  A "
483                 "ship is disarmed when all of it's turret subsystems are destroyed.  All "
484                 "ships must be disarmed for this function to return true.\r\n\r\n"
485                 "Returns a boolean value; Takes 2 or more arguments:\r\n"
486                 "\t1:\tTime delay is seconds (see above).\r\n"
487                 "\tRest:\tNames of ships to check disarmed status of." },
488
489         { OP_HAS_DOCKED_DELAY, "Has docked delay (Boolean operator)\r\n"
490                 "\tBecomes true <delay> seconds after the specified ships have docked the "
491                 "specified number of times.\r\n\r\n"
492                 "Returns a boolean value; Takes 4 arguments:\r\n"
493                 "\t1:\tThe name of the docker ship\r\n"
494                 "\t2:\tThe name of the dockee ship\r\n"
495                 "\t3:\tThe number of times they have to have docked\r\n"
496                 "\t4:\tTime delay in seconds (see above)." },
497
498         { OP_HAS_UNDOCKED_DELAY, "Has undocked delay (Boolean operator)\r\n"
499                 "\tBecomes true <delay> seconds after the specified ships have undocked the "
500                 "specified number of times.\r\n\r\n"
501                 "Returns a boolean value; Takes 4 arguments:\r\n"
502                 "\t1:\tThe name of the docker ship\r\n"
503                 "\t2:\tThe name of the dockee ship\r\n"
504                 "\t3:\tThe number of times they have to have undocked\r\n"
505                 "\t4:\tTime delay in seconds (see above)." },
506
507         { OP_HAS_ARRIVED_DELAY, "Has arrived delay (Boolean operator)\r\n"
508                 "\tBecomes true <delay> seconds after the specified ship(s) have arrived into the mission\r\n\r\n"
509                 "Returns a boolean value; Takes 2 or more arguments:\r\n"
510                 "\t1:\tTime delay in seconds (see above).\r\n"
511                 "\tRest:\tName of ship (or wing) we want to check has arrived." },
512
513         { OP_HAS_DEPARTED_DELAY, "Has departed delay (Boolean operator)\r\n"
514                 "\tBecomes true <delay> seconds after the specified ship(s) or wing(s) have departed "
515                 "from the mission by warping out.  If any ship was destroyed, this operator will "
516                 "never be true.\r\n\r\n"
517                 "Returns a boolean value; Takes 2 or more arguments:\r\n"
518                 "\t1:\tTime delay in seconds (see above).\r\n"
519                 "\tRest:\tName of ship (or wing) we want to check has departed." },
520
521         { OP_WAYPOINTS_DONE_DELAY, "Waypoints done delay (Boolean operator)\r\n"
522                 "\tBecomes true <delay> seconds after the specified ship has completed flying the "
523                 "specified waypoint path.\r\n\r\n"
524                 "Returns a boolean value; Takes 3 arguments:\r\n"
525                 "\t1:\tName of ship we are checking.\r\n"
526                 "\t2:\tWaypoint path we want to check if ship has flown.\r\n"
527                 "\t3:\tTime delay in seconds (see above)." },
528
529         { OP_SHIP_TYPE_DESTROYED, "Ship Type Destroyed (Boolean operator)\r\n"
530                 "\tBecomes true when the specified percentage of ship types in this mission "
531                 "have been destroyed.  The ship type is a generic type such as fighter/bomber, "
532                 "transport, etc.  Fighters and bombers count as the same type.\r\n\r\n"
533                 "Returns a boolean value; Takes 2 arguments:\r\n"
534                 "\t1:\tPercentage of ships that must be destroyed.\r\n"
535                 "\t2:\tShip type to check for." },
536
537         { OP_TIME_SHIP_DESTROYED, "Time ship destroyed (Time operator)\r\n"
538                 "\tReturns the time the specified ship was destroy.\r\n\r\n"
539                 "Returns a numeric value; Takes 1 argument:\r\n"
540                 "\t1:\tName of ship we want to check." },
541
542         { OP_TIME_SHIP_ARRIVED, "Time ship arrived (Time operator)\r\n"
543                 "\tReturns the time the specified ship arrived into the mission.\r\n\r\n"
544                 "Returns a numeric value; Takes 1 argument:\r\n"
545                 "\t1:\tName of ship we want to check." },
546
547         { OP_TIME_SHIP_DEPARTED, "Time ship departed (Time operator)\r\n"
548                 "\tReturns the time the specified ship departed the mission by warping out.  Being "
549                 "destroyed doesn't count departed.\r\n\r\n"
550                 "Returns a numeric value; Takes 1 argument:\r\n"
551                 "\t1:\tName of ship we want to check." },
552
553         { OP_TIME_WING_DESTROYED, "Time wing destroyed (Time operator)\r\n"
554                 "\tReturns the time the specified wing was destroy.\r\n\r\n"
555                 "Returns a numeric value; Takes 1 argument:\r\n"
556                 "\t1:\tName of wing we want to check." },
557
558         { OP_TIME_WING_ARRIVED, "Time wing arrived (Time operator)\r\n"
559                 "\tReturns the time the specified wing arrived into the mission.\r\n\r\n"
560                 "Returns a numeric value; Takes 1 argument:\r\n"
561                 "\t1:\tName of wing we want to check." },
562
563         { OP_TIME_WING_DEPARTED, "Time wing departed (Time operator)\r\n"
564                 "\tReturns the time the specified wing departed the mission by warping out.  All "
565                 "ships in the wing have to have warped out.  If any are destroyed, the wing can "
566                 "never be considered departed.\r\n\r\n"
567                 "Returns a numeric value; Takes 1 argument:\r\n"
568                 "\t1:\tName of ship we want to check." },
569
570         { OP_MISSION_TIME, "Mission time (Time operator)\r\n"
571                 "\tReturns the current time into the mission.\r\n\r\n"
572                 "Returns a numeric value." },
573
574         { OP_TIME_DOCKED, "Time docked (Time operator)\r\n"
575                 "\tReturns the time the specified ships docked.\r\n\r\n"
576                 "Returns a numeric value; Takes 3 arguments:\r\n"
577                 "\t1:\tThe name of the docker ship.\r\n"
578                 "\t2:\tThe name of the dockee ship.\r\n"
579                 "\t3:\tThe number of times they must have docked to be true." },
580
581         { OP_TIME_UNDOCKED, "Time undocked (Time operator)\r\n"
582                 "\tReturns the time the specified ships undocked.\r\n\r\n"
583                 "Returns a numeric value; Takes 3 arguments:\r\n"
584                 "\t1:\tThe name of the docker ship.\r\n"
585                 "\t2:\tThe name of the dockee ship.\r\n"
586                 "\t3:\tThe number of times they must have undocked to be true." },
587
588         { OP_SHIELDS_LEFT, "Sheilds left (Status operator)\r\n"
589                 "\tReturns the current level of the specified ship's shields as a percentage.\r\n\r\n"
590                 "Returns a numeric value; Takes 1 argument:\r\n"
591                 "\t1:\tName of ship to check." },
592
593         { OP_HITS_LEFT, "Hits left (Status operator)\r\n"
594                 "\tReturns the current level of the specified ship's hull as a percentage.\r\n\r\n"
595                 "Returns a numeric value; Takes 1 argument:\r\n"
596                 "\t1:\tName of ship to check." },
597
598         { OP_HITS_LEFT_SUBSYSTEM, "Hits left subsystem (Status operator)\r\n"
599                 "\tReturns the current level of the specified ship's subsystem integrity as a percentage.\r\n\r\n"
600                 "Returns a numeric value; Takes 1 argument:\r\n"
601                 "\t1:\tName of ship to check.\r\n"
602                 "\t2:\tName of subsystem on ship to check." },
603
604         { OP_DISTANCE, "Distance (Misc. Operator)\r\n"
605                 "\tReturns the distance between 2 objects.  These objects can be either a ship, "
606                 "a wing, or a waypoint.\r\n\r\n"
607                 "Returns a numeric value; Takes 2 arguments:\r\n"
608                 "\t1:\tThe name of one of the objects.\r\n"
609                 "\t2:\tThe name of the other object." },
610
611         { OP_LAST_ORDER_TIME, "Last order time (Status operator)\r\n"
612                 "\tReturns true if <count> seconds have elapsed since one or more ships have received "
613                 "a meaningful order from the player.  A meaningful order is currently any order that "
614                 "is not the warp out order.\r\n\r\n"
615                 "Returns a boolean value; Takes 2 or more arguments:\r\n"
616                 "\t1:\tTime in seconds that must elapse.\r\n"
617                 "\tRest:\tName of ship or wing to check for having received orders." },
618
619         { OP_WHEN, "When (Conditional operator)\r\n"
620                 "\tPerforms specified actions when a condition becomes true\r\n\r\n"
621                 "Takes 2 or more arguments:\r\n"
622                 "\t1:\tBoolean expression that must be true for actions to take place.\r\n"
623                 "\tRest:\tActions to take when boolean expression becomes true." },
624
625         { OP_COND, "Blah" },
626
627         { OP_CHANGE_IFF, "Change IFF (Action operator)\r\n"
628                 "\tSets the specified ship(s) to the specified team.\r\n"
629                 "Takes 2 or more arguments:\r\n"
630                 "\t1:\tTeam to change to (\"friendly\", \"hostile\" or \"unknown\").\r\n"
631                 "\tRest:\tName of ship to change team status of." },
632
633         { OP_MODIFY_VARIABLE, "Modify-variable (Misc. operator)\r\n"
634                 "\tModifies variable to specified value\r\n\r\n"
635                 "Takes 2 arguments:\r\n"
636                 "\t1:\tName of Variable.\r\n"
637                 "\t2:\tValue to be set." },
638
639         { OP_PROTECT_SHIP, "Protect ship (Action operator)\r\n"
640                 "\tProtects a ship from being attacked by any enemy ship.  Any ship"
641                 "that is protected will not come under enemy fire.\r\n\r\n"
642                 "Takes 1 or more arguments:\r\n"
643                 "\tAll:\tName of ship(s) to protect." },
644
645         { OP_UNPROTECT_SHIP, "Unprotect ship (Action operator)\r\n"
646                 "\tUnprotects a ship from being attacked by any enemy ship.  Any ship"
647                 "that is not protected can come under enemy fire.  This function is the opposite"
648                 "of protect-ship.\r\n\r\n"
649                 "Takes 1 or more arguments:\r\n"
650                 "\tAll:\tName of ship(s) to protect." },
651
652         { OP_BEAM_PROTECT_SHIP, "Beam Protect ship (Action operator)\r\n"
653                 "\tProtects a ship from being attacked with beam weapon.  Any ship"
654                 "that is beam protected will not come under enemy beam fire.\r\n\r\n"
655                 "Takes 1 or more arguments:\r\n"
656                 "\tAll:\tName of ship(s) to protect." },
657
658         { OP_BEAM_UNPROTECT_SHIP, "Beam Unprotect ship (Action operator)\r\n"
659                 "\tUnprotects a ship from being attacked with beam weapon.  Any ship"
660                 "that is not beam protected can come under enemy beam fire.  This function is the opposite"
661                 "of beam-protect-ship.\r\n\r\n"
662                 "Takes 1 or more arguments:\r\n"
663                 "\tAll:\tName of ship(s) to protect." },
664
665         { OP_SEND_MESSAGE, "Send message (Action operator)\r\n"
666                 "\tSends a message to the player.  Can be send by a ship, wing, or special "
667                 "source.  To send it from a special source, make the first character of the first "
668                 "argument a \"#\".\r\n\r\n"
669                 "Takes 3 arguments:\r\n"
670                 "\t1:\tName of who the message is from.\r\n"
671                 "\t2:\tPriority of message (\"Low\", \"Normal\" or \"High\").\r\n"
672                 "\t3:\tName of message (from message editor)." },
673
674         { OP_SELF_DESTRUCT, "Self destruct (Action operator)\r\n"
675                 "\tCauses the specified ship(s) to self destruct.\r\n\r\n"
676                 "Takes 1 or more arguments:\r\n"
677                 "\tAll:\tName of ship to self destruct." },
678
679         { OP_NEXT_MISSION, "Next Mission (Action operator)\r\n"
680                 "\tThe next mission operator is used for campaign branching in the campaign editor.  "
681                 "It specifies which mission should played be next in the campaign.  This operator "
682                 "generally follows a 'when' or 'cond' statment in the campaign file.\r\n\r\n"
683                 "Takes 1 argument:\r\n"
684                 "\t1:\tName of mission (filename) to proceed to." },
685
686         { OP_CLEAR_GOALS, "Clear goals (Action operator)\r\n"
687                 "\tClears the goals for the specified ships and/or wings.\r\n\r\n"
688                 "Takes 1 or more arguments:\r\n"
689                 "\tAll:\tName of ship or wing." },
690
691         { OP_ADD_GOAL, "Add goal (Action operator)\r\n"
692                 "\tAdds a goal to a ship or wing.\r\n\r\n"
693                 "Takes 2 arguments:\r\n"
694                 "\t1:\tName of ship or wing to all goal to.\r\n"
695                 "\t2:\tGoal to add." },
696
697         { OP_SABOTAGE_SUBSYSTEM, "Sabotage subystem (Action operator)\r\n"
698                 "\tReduces the specified subsystem integrity by the specified percentage."
699                 "If the percntage strength of the subsystem (after completion) is less than 0%,"
700                 "subsystem strength is set to 0%.\r\n\r\n"
701                 "Takes 3 arguments:\r\n"
702                 "\t1:\tName of ship subsystem is on.\r\n"
703                 "\t2:\tName of subsystem to sabotage.\r\n"
704                 "\t3:\tPercentage to reduce subsystem integrity by." },
705
706         { OP_REPAIR_SUBSYSTEM, "Repair Subystem (Action operator)\r\n"
707                 "\tIncreases the specified subsystem integrity by the specified percentage."
708                 "If the percntage strength of the subsystem (after completion) is greater than 100%,"
709                 "subsystem strength is set to 100%.\r\n\r\n"
710                 "Takes 3 arguments:\r\n"
711                 "\t1:\tName of ship subsystem is on.\r\n"
712                 "\t2:\tName of subsystem to repair.\r\n"
713                 "\t3:\tPercentage to increase subsystem integrity by." },
714
715         { OP_SET_SUBSYSTEM_STRNGTH, "Set Subsystem Strength (Action operator)\r\n"
716                 "\tSets the specified subsystem to the the specified percentage."
717                 "If the percentage specified is < 0, strength is set to 0.  If the percentage is "
718                 "> 100 % the subsystem strength is set to 100%.\r\n\r\n"
719                 "Takes 3 arguments:\r\n"
720                 "\t1:\tName of ship subsystem is on.\r\n"
721                 "\t2:\tName of subsystem to set strength.\r\n"
722                 "\t3:\tPercentage to set subsystem integrity to." },
723
724         { OP_INVALIDATE_GOAL, "Invalidate goal (Action operator)\r\n"
725                 "\tMakes a mission goal invalid, which causes it to now show up on mission goals "
726                 "screen, or be evaluated.\r\n"
727                 "Takes 1 or more arguments:\r\n"
728                 "\tAll:\tName of mission goal to invalidate." },
729
730         { OP_VALIDATE_GOAL, "Validate goal (Action operator)\r\n"
731                 "\tMakes a mission goal valid again, so it shows up on mission goals screen.\r\n"
732                 "Takes 1 or more arguments:\r\n"
733                 "\tAll:\tName of mission goal to validate." },
734
735         { OP_SEND_RANDOM_MESSAGE, "Send random message (Action operator)\r\n"
736                 "\tSends a random message to the player from those supplied.  Can be send by a "
737                 "ship, wing, or special source.  To send it from a special source, make the first "
738                 "character of the first argument a \"#\".\r\n\r\n"
739                 "Takes 3 or more arguments:\r\n"
740                 "\t1:\tName of who the message is from.\r\n"
741                 "\t2:\tPriority of message (\"Low\", \"Normal\" or \"High\")."
742                 "\tRest:\tName of message (from message editor)." },
743
744         { OP_TRANSFER_CARGO, "Transfer Cargo (Action operator)\r\n"
745                 "\tTransfers the cargo from one ship to another ship.\r\n\r\n"
746                 "Takes 2 arguments:\r\n"
747                 "\t1:\tName of ship that cargo is being transferred from.\r\n"
748                 "\t2:\tName of ship that cargo is being transferred to." },
749
750         { OP_EXCHANGE_CARGO, "Exchange Cargo (Action operator)"
751                 "\tExchanges the cargos of two ships.  If one of the two ships contains no cargo, "
752                 "the cargo is transferred instead.\r\n"
753                 "Takes 2 arguments:\r\n"
754                 "\t1:\tName of one of the ships.\r\n"
755                 "\t2:\tName of the other ship." },
756
757         { OP_INT3, "Error (Debug directive)\r\n"
758                 "Causes the game to halt with an error." },
759
760         { OP_AI_CHASE, "Ai-chase (Ship goal)\r\n"
761                 "\tCauses the specified ship to chase and attack the specified target.\r\n\r\n"
762                 "Takes 2 arguments:\r\n"
763                 "\t1:\tName of ship to chase.\r\n"
764                 "\t2:\tGoal priority (number between 0 and 89)." },
765
766         { OP_AI_DOCK, "Ai-dock (Ship goal)\r\n"
767                 "\tCauses one ship to dock with another ship.\r\n\r\n"
768                 "Takes 4 arguments:\r\n"
769                 "\t1:\tName of dockee ship (The ship that \"docker\" will dock with).\r\n"
770                 "\t2:\tDocker's docking point - Which dock point docker uses to dock.\r\n"
771                 "\t3:\tDockee's docking point - Which dock point on dockee docker will move to.\r\n"
772                 "\t4:\tGoal priority (number between 0 and 89)." },
773
774         { OP_AI_UNDOCK, "Ai-undock (Ship goal)\r\n"
775                 "\tCauses the specified ship to undock from who it is currently docked with.\r\n\r\n"
776                 "Takes 1 arguments:\r\n"
777                 "\t1:\tGoal priority (number between 0 and 89)." },
778
779         { OP_AI_WARP_OUT, "Ai-warp-out (Ship/Wing Goal)\r\n"
780                 "\tCauses the specified ship/wing to warp out of the mission.  Currently, the ship will "
781                 "warp out at it's current location.  This behavior will change.  Currently, the first "
782                 "argument means nothing.\r\n\r\n"
783                 "Takes 2 arguments:\r\n"
784                 "\t1:\tName of waypoint path to follow to warp out (not used).\r\n"
785                 "\t2:\tGoal priority (number between 0 and 89)." },
786
787         { OP_AI_WAYPOINTS, "Ai-waypoints (Ship goal)\r\n"
788                 "\tCauses the specified ship to fly a waypoint path continuously.\r\n\r\n"
789                 "Takes 2 arguments:\r\n"
790                 "\t1:\tName of waypoint path to fly.\r\n"
791                 "\t2:\tGoal priority (number between 0 and 89)." },
792
793         { OP_AI_WAYPOINTS_ONCE, "Ai-waypoints once (Ship goal)\r\n"
794                 "\tCauses the specified ship to fly a waypoint path.\r\n\r\n"
795                 "Takes 2 arguments:\r\n"
796                 "\t1:\tName of waypoint path to fly.\r\n"
797                 "\t2:\tGoal priority (number between 0 and 89)." },
798
799         { OP_AI_DESTROY_SUBSYS, "Ai-destroy subsys (Ship goal)\r\n"
800                 "\tCauses the specified ship to attack and try and destroy the specified subsystem "
801                 "on the specified ship.\r\n\r\n"
802                 "Takes 3 arguments:\r\n"
803                 "\t1:\tName of ship subsystem is on.\r\n"
804                 "\t2:\tName of subsystem on the ship to attack and destroy.\r\n"
805                 "\t3:\tGoal priority (number between 0 and 89)." },
806
807         { OP_AI_CHASE_WING, "Ai-chase wing (Ship goal)\r\n"
808                 "\tCauses the specified ship to chase and attack the specified target.\r\n\r\n"
809                 "Takes 2 arguments:\r\n"
810                 "\t1:\tName of wing to chase.\r\n"
811                 "\t2:\tGoal priority (number between 0 and 89)." },
812
813         { OP_AI_DISABLE_SHIP, "Ai-disable-ship (Ship/wing goal)\r\n"
814                 "\tThis AI goal causes a ship/wing to destroy all of the engine subsystems on "
815                 "the specified ship.  This goal is different than ai-destroy-subsystem since a ship "
816                 "may have multiple engine subsystems requiring the use of > 1 ai-destroy-subsystem "
817                 "goals.\r\n\r\n"
818                 "Takes 2 arguments:\r\n"
819                 "\t1:\tName of ship whose engine subsystems should be destroyed\r\n"
820                 "\t2:\tGoal priority (number between 0 and 89)." },
821
822         { OP_AI_DISARM_SHIP, "Ai-disarm-ship (Ship/wing goal)\r\n"
823                 "\tThis AI goal causes a ship/wing to destroy all of the turret subsystems on "
824                 "the specified ship.  This goal is different than ai-destroy-subsystem since a ship "
825                 "may have multiple turret subsystems requiring the use of > 1 ai-destroy-subsystem "
826                 "goals.\r\n\r\n"
827                 "Takes 2 arguments:\r\n"
828                 "\t1:\tName of ship whose turret subsystems should be destroyed\r\n"
829                 "\t2:\tGoal priority (number between 0 and 89)." },
830
831         { OP_AI_GUARD, "Ai-guard (Ship goal)\r\n"
832                 "\tCauses the specified ship to guard a ship from other ships not on the same team.\r\n\r\n"
833                 "Takes 2 arguments:\r\n"
834                 "\t1:\tName of ship to guard.\r\n"
835                 "\t2:\tGoal priority (number between 0 and 89)." },
836
837         { OP_AI_CHASE_ANY, "Ai-chase any (Ship goal)\r\n"
838                 "\tCauses the specified ship to chase and attack any ship on the opposite team.\r\n\r\n"
839                 "Takes 1 arguments:\r\n"
840                 "\t1:\tGoal priority (number between 0 and 89)." },
841
842         { OP_AI_GUARD_WING, "Ai-guard wing (Ship goal)\r\n"
843                 "\tCauses the specified ship to guard a wing of ships from other ships not on the "
844                 "same team.\r\n\r\n"
845                 "Takes 2 arguments:\r\n"
846                 "\t1:\tName of wing to guard.\r\n"
847                 "\t2:\tGoal priority (number between 0 and 89)." },
848
849         { OP_NOP, "Do-nothing (Action operator)\r\n"
850                 "\tDoes nothing.  This is used as the default for any required action arguments "
851                 "of an operator." },
852
853         { OP_KEY_PRESSED, "Key-pressed (Boolean training operator)\r\n"
854                 "\tBecomes true when the specified default key has been pressed.  Default key "
855                 "refers to the what the key normally is when not remapped.  FreeSpace will "
856                 "automatically account for any keys that have been remapped.  If the optional "
857                 "delay is specified, becomes true that many seconds after the key has been pressed.\r\n\r\n"
858                 "Returns a boolean value; Takes 1 or 2 arguments:\r\n"
859                 "\t1:\tDefault key to check for.\r\n"
860                 "\t2:\tDelay before operator registers as true (optional).\r\n" },
861
862         { OP_KEY_RESET, "Key-reset (Training operator)\r\n"
863                 "\tMarks the specified default key as having not been pressed, so key-pressed will be false "
864                 "until the player presses it again.  See key-pressed help for more information about "
865                 "what a default key is.\r\n\r\n"
866                 "Returns a boolean value; Takes 1 argument:\r\n"
867                 "\t1:\tDefault key to reset." },
868
869         { OP_TARGETED, "Targeted (Boolean training operator)\r\n"
870                 "\tIs true as long as the player has the specified ship (or ship's subsystem) targeted, "
871                 "or has been targeted for the specified amount of time.\r\n\r\n"
872                 "Returns a boolean value; Takes 1 to 3 arguments (first required, rest optional):\r\n"
873                 "\t1:\tName of ship to check if targeted by player.\r\n"
874                 "\t2:\tLength of time target should have been kept for (optional).\r\n"
875                 "\t3:\tName of subsystem on ship to check if targeted (optional)." },
876
877         { OP_SPEED, "Speed (Boolean training operator)\r\n"
878                 "\tBecomes true when the player has been within the specified speed range set by "
879                 "set-training-context-speed for the specified amount of time.\r\n\r\n"
880                 "Returns a boolean value; Takes 1 argument:\r\n"
881                 "\t1:\tTime in seconds." },
882
883         { OP_FACING, "Facing (Boolean training operator)\r\n"
884                 "\tIs true as long as the specified ship is within the player's specified "
885                 "forward cone.  A forward cone is defined as any point that the angle between the "
886                 "vector of the point and the player, and the forward facing vector is within the "
887                 "given angle.\r\n\r\n"
888                 "Returns a boolean value; Takes 2 argument:\r\n"
889                 "\t1:\tShip to check is withing forward cone.\r\n"
890                 "\t2:\tAngle in degrees of the forward cone." },
891
892         { OP_FACING2, "Facing Waypoint(Boolean training operator)\r\n"
893                 "\tIs true as long as the specified first waypoint is within the player's specified "
894                 "forward cone.  A forward cone is defined as any point that the angle between the "
895                 "vector of the point and the player, and the forward facing vector is within the "
896                 "given angle.\r\n\r\n"
897                 "Returns a boolean value; Takes 2 argument:\r\n"
898                 "\t1:\tName of waypoint path whose first point is withing forward cone.\r\n"
899                 "\t2:\tAngle in degrees of the forward cone." },
900
901         { OP_ORDER, "Order (Boolean training operator)\r\n"
902                 "\tBecomes true when the player had given the specified ship or wing the specified order.\r\n\r\n"
903                 "Returns a boolean value; Takes 2 arguments:\r\n"
904                 "\t1:\tName of ship or wing to check if given order to.\r\n"
905                 "\t2:\tName of order to check if player has given." },
906
907         { OP_WAYPOINT_MISSED, "Waypoint-missed (Boolean training operator)\r\n"
908                 "\tBecomes true when a waypoint is flown, but the waypoint is ahead of the one "
909                 "they are supposed to be flying.  The one they are supposed to be flying is the "
910                 "next one in sequence in the path after the last one they have hit.\r\n\r\n"
911                 "Returns a boolean value; Takes no arguments." },
912
913         { OP_PATH_FLOWN, "Path-flown (Boolean training operator)\r\n"
914                 "\tBecomes true when all the waypoints in the path have been flown, in sequence.\r\n\r\n"
915                 "Returns a boolean value; Takes no arguments." },
916
917         { OP_WAYPOINT_TWICE, "Waypoint-twice (Boolean training operator)\r\n"
918                 "\tBecomes true when a waypoint is hit that is before the last one hit, which "
919                 "indicates they have flown a waypoint twice.\r\n\r\n"
920                 "Returns a boolean value; Takes no arguments." },
921
922         { OP_TRAINING_MSG, "Training-msg (Action training operator)\r\n"
923                 "\tSends the player a training message.  Uses the same messages as normal messages, "
924                 "only they get displayed differently using this operator.  If a secondary message "
925                 "is specified, it is sent the last time, while the primary message is sent all other "
926                 "times (event should have a repeat count greater than 1).\r\n\r\n"
927                 "Takes 1-3 arguments:\r\n"
928                 "\t1:\tName of primary message to send.\r\n"
929                 "\t2:\tName of secondary message to send (optional).\r\n"
930                 "\t3:\tDelay (in seconds) to wait before sending message. (optional)\r\n"
931                 "\t4:\tAmount of Time (in seconds) to display message (optional)." },
932
933         { OP_SET_TRAINING_CONTEXT_FLY_PATH, "Set-training-context-fly-path (Training context operator)\r\n"
934                 "\tTells FreeSpace that the player is expected to fly a waypoint path.  This must be "
935                 "executed before waypoint-missed, waypoint-twice and path-flown operators become valid.\r\n\r\n"
936                 "Takes 2 arguments:\r\n"
937                 "\t1:\tName of waypoint path player should fly.\r\n"
938                 "\t2:\tDistance away a player needs to be from a waypoint for it to be registered as flown." },
939
940         { OP_SET_TRAINING_CONTEXT_SPEED, "Set-training-context-speed (Training context operator)\r\n"
941                 "\tTells FreeSpace that the player is expected to fly within a certain speed range.  Once "
942                 "this operator has been executed, you can measure how long they have been within this "
943                 "speed range with the speed operator.\r\n\r\n"
944                 "Takes 2 arguments:\r\n"
945                 "\t1:\tMinimum speed of range player is to fly between.\r\n"
946                 "\t2:\tMaximum speed of range player is to fly between." },
947
948         { OP_GRANT_PROMOTION, "Grant promotion (Action operator)\r\n"
949                 "\tIn a single player game, this function grants a player an automatic promotion to the "
950                 "next rank which the player can obtain.  If he is already at the highest rank, this "
951                 "operator has no effect.  It takes no arguments." },
952
953         { OP_GRANT_MEDAL, "Grant medal (Action operator)\r\n"
954                 "\tIn single player missions, this function grants the given medal to the player.  "
955                 "Currently, only 1 medal will be allowed to be given per mission.\r\n\r\n"
956                 "Takes 1 argument:\r\n"
957                 "\t1:\tName of medal to grant to player." },
958
959         { OP_GOOD_SECONDARY_TIME, "Set prefered secondary weapons\r\n"
960                 "\tThis sexpression is used to inform the AI about prefered secondary weapons to "
961                 "fire during combat.  When this expression is evaulated, any AI ships of the given "
962                 "team prefer to fire the given weapon at the given ship. (Prefered over other "
963                 "secondary weapons)\r\n\r\n"
964                 "Takes 4 argument:\r\n"
965                 "\t1:\tTeam name which will prefer firing given weapon\r\n"
966                 "\t2:\tMaximum number of this type of weapon above team can fire.\r\n"
967                 "\t3:\tWeapon name (list includes only the valid weapons for this expression\r\n"
968                 "\t4:\tShip name at which the above named team should fire the above named weapon." },
969
970         { OP_AND_IN_SEQUENCE, "And in sequence (Boolean operator)\r\n"
971                 "\tReturns true if all of it's arguments have become true in the order they are "
972                 "listed in.\r\n\r\n"
973                 "Returns a boolean value; Takes 2 or more boolean arguments." },
974
975         { OP_SKILL_LEVEL_AT_LEAST, "Skill level at least (Boolean operator)\r\n"
976                 "\tReturns true if the player has selected the given skill level or higher.\r\n\r\n"
977                 "Returns a boolean value; Takes 1 arguments:\r\n"
978                 "\t1:\tName of the skill level to check." },
979
980         { OP_NUM_PLAYERS, "Num players (Status operator)\r\n"
981                 "\tReturns the current number of players (multiplayer) playing in the current mission.\r\n\r\n"
982                 "Returns a numeric value; Takes no arguments." },
983
984         { OP_IS_CARGO_KNOWN, "Is cargo known (Boolean operator)\r\n"
985                 "\tReturns true if all of the specified objects' cargo is known by the player (i.e. they "
986                 "have scanned each one.\r\n\r\n"
987                 "Returns a boolean value; Takes 1 or more arguments:\r\n"
988                 "\tAll:\tName of ship to check if it's cargo is known." },
989
990         { OP_HAS_BEEN_TAGGED_DELAY, "Has ship been tagged (delay) (Boolean operator)\r\n"
991                 "\tReturns true if all of the specified ships have been tagged.\r\n\r\n"
992                 "Returns a boolean value after <delay> seconds when all ships have been tagged; Takes 2 or more arguments:\r\n"
993                 "\t1:\tDelay in seconds after which sexpression will return true when all cargo scanned."
994                 "\tRest:\tNames of ships to check if tagged.." },
995
996         { OP_CAP_SUBSYS_CARGO_KNOWN_DELAY, "Is capital ship subsystem cargo known (delay) (Boolean operator)\r\n"
997                 "\tReturns true if all of the specified subsystem cargo is known by the player.\r\n"
998                 "\tNote: Cargo must be explicitly named.\r\n\r\n"
999                 "Returns a boolean value after <delay> seconds when all cargo is known; Takes 3 or more arguments:\r\n"
1000                 "\t1:\tDelay in seconds after which sexpression will return true when all cargo scanned.\r\n"
1001                 "\t2:\tName of Captial ship\r\n"
1002                 "\tRest:\tNames of subsystems to check for cargo known.." },
1003
1004         { OP_CARGO_KNOWN_DELAY, "Is cargo known (delay) (Boolean operator)\r\n"
1005                 "\tReturns true if all of the specified objects' cargo is known by the player (i.e. they "
1006                 "have scanned each one.\r\n\r\n"
1007                 "Returns a boolean value after <delay> seconds when all cargo is known; Takes 2 or more arguments:\r\n"
1008                 "\t1:\tDelay in seconds after which sexpression will return true when all cargo scanned."
1009                 "\tRest:\tNames of ships/cargo to check for cargo known.." },
1010
1011         { OP_WAS_PROMOTION_GRANTED, "Was promotion granted (Boolean operator)\r\n"
1012                 "\tReturns true if a promotion was granted via the 'Grant promotion' operator in the mission.\r\n\r\n"
1013                 "Returns a boolean value; Takes no arguments." },
1014
1015         { OP_WAS_MEDAL_GRANTED, "Was medal granted (Boolean operator)\r\n"
1016                 "\tReturns true if a medal was granted via via the 'Grant medal' operator in the mission.  "
1017                 "If you provide the optional argument to this operator, then true is only returned if the "
1018                 "specified medal was granted.\r\n\r\n"
1019                 "Returns a boolean value; Takes 0 or 1 arguments:\r\n"
1020                 "\t1:\tName of medal to specifically check for (optional)." },
1021
1022         { OP_GOOD_REARM_TIME, "Good rearm time (Action operator)\r\n"
1023                 "\tInforms the game logic that right now is a good time for a given team to attempt to "
1024                 "rearm their ships.  The time parameter specified how long the \"good time\" will last.\r\n\r\n"
1025                 "Takes 2 arguments:\r\n"
1026                 "\t1:\tTeam Name\r\n"
1027                 "\t2:\tTime in seconds rearm window should last" },
1028
1029         { OP_ALLOW_SHIP, "Allow ship (Action operator)\r\n"
1030                 "\tThis operator makes the given ship type available to the Terran team.  Players will be "
1031                 "able to have ships of this type in their starting wings in all future missions of this "
1032                 "campaign.\r\n\r\n"
1033                 "Takes 1 arguments:\r\n"
1034                 "\t1:\tName of ship type (or ship class) to allow." },
1035
1036         { OP_ALLOW_WEAPON, "Allow weapon (Action operator)\r\n"
1037                 "\tThis operator makes the given weapon available to the Terran team.  Players will be "
1038                 "able to equip ships with in all future missions of this campaign.\r\n\r\n"
1039                 "Takes 1 arguments:\r\n"
1040                 "\t1:\tName of weapon (primary or secondary) to allow." },
1041
1042         { OP_TECH_ADD_SHIP, "Tech add ship (Action operator)\r\n"
1043                 "\tThis operator makes the given ship type available in the techroom database.  Players will "
1044                 "then be able to view this ship's specs there.\r\n\r\n"
1045                 "Takes 1 or more arguments:\r\n"
1046                 "\tAll:\tName of ship type (or ship class) to add." },
1047
1048         { OP_TECH_ADD_WEAPON, "Tech add weapon (Action operator)\r\n"
1049                 "\tThis operator makes the given weapon available in the techroom database.  Players will "
1050                 "then be able to view this weapon's specs there.\r\n\r\n"
1051                 "Takes 1 or more arguments:\r\n"
1052                 "\tAll:\tName of weapon (primary or secondary) to add." },
1053
1054         { OP_AI_EVADE_SHIP, "Ai-evade ship (Ship goal)\r\n"
1055                 "\tCauses the specified ship to go into evade mode and run away like the weak "
1056                 "sally-boy it is.\r\n\r\n"
1057                 "Takes 2 arguments:\r\n"
1058                 "\t1:\tName of ship to evade from.\r\n"
1059                 "\t2:\tGoal priority (number between 0 and 89)." },
1060
1061         { OP_AI_STAY_NEAR_SHIP, "Ai-stay near ship (Ship goal)\r\n"
1062                 "\tCauses the specified ship to keep itself near the given ship and not stray too far "
1063                 "away from it.\r\n\r\n"
1064                 "Takes 2 arguments:\r\n"
1065                 "\t1:\tName of ship to stay near.\r\n"
1066                 "\t2:\tGoal priority (number between 0 and 89)." },
1067
1068         { OP_AI_KEEP_SAFE_DISTANCE, "Ai-keep safe distance (Ship goal)\r\n"
1069                 "\tTells the specified ship to stay a safe distance away from any ship that isn't on the "
1070                 "same team as it.\r\n\r\n"
1071                 "Takes 1 argument:\r\n"
1072                 "\t1:\tGoal priority (number between 0 and 89)." },
1073
1074         { OP_AI_IGNORE, "Ai-ignore (Ship goal)\r\n"
1075                 "\tTells the specified ship to ignore the given ship and not consider it as a valid "
1076                 "target to attack.\r\n\r\n"
1077                 "Takes 2 arguments:\r\n"
1078                 "\t1:\tName of ship to ignore.\r\n"
1079                 "\t2:\tGoal priority (number between 0 and 89)." },
1080
1081         { OP_AI_STAY_STILL, "Ai-stay still (Ship goal)\r\n"
1082                 "\tCauses the specified ship to stay still.  The ship will do nothing until attacked at "
1083                 "which time the ship will come to life and defend itself.\r\n\r\n"
1084                 "Takes 2 arguments:\r\n"
1085                 "\t1:\tShip or waypoint the ship staying still will directly face (currently not implemented)\r\n"
1086                 "\t2:\tGoal priority (number between 0 and 89)." },
1087
1088         { OP_AI_PLAY_DEAD, "Ai-play dead (Ship goal)\r\n"
1089                 "\tCauses the specified ship to pretend that it is dead and not do anything.  This "
1090                 "expression should be used to indicate that a ship has no pilot and cannot respond "
1091                 "to any enemy threats.  A ship playing dead will not respond to any attack.  This "
1092                 "should really be named ai-is-dead\r\n\r\n"
1093                 "Takes 1 argument:\r\n"
1094                 "\t1:\tGoal priority (number between 0 and 89)." },
1095
1096         { OP_FLASH_HUD_GAUGE, "Ai-flash hud gauge (Training goal)\r\n"
1097                 "\tCauses the specified hud gauge to flash to draw the player's attention to it.\r\n\r\n"
1098                 "Takes 1 argument:\r\n"
1099                 "\t1:\tName of hud gauge to flash." },
1100
1101         { OP_SHIP_VISIBLE, "ship-visible\r\n"
1102                 "\tCauses the ships listed in this sexpression to be visible with player sensors.\r\n\r\n"
1103                 "Takes 1 or more arguments:\r\n"
1104                 "\t1+:\tName of ships to make visible to sensors." },
1105
1106         { OP_SHIP_INVISIBLE, "ship-invisible\r\n"
1107                 "\tCauses the ships listed in this sexpression to be invisible to player sensors.\r\n\r\n"
1108                 "Takes 1 or more arguments:\r\n"
1109                 "\t1+:\tName of ships to make invisible to sensors." },
1110
1111         { OP_SHIP_VULNERABLE, "ship-vulnerable\r\n"
1112                 "\tCauses the ship listed in this sexpression to be vulnerable to weapons.\r\n\r\n"
1113                 "Takes 1 or more arguments:\r\n"
1114                 "\t1+:\tName of ships to make vulnerable to weapons." },
1115
1116         { OP_SHIP_INVULNERABLE, "ship-invulnerable\r\n"
1117                 "\tCauses the ships listed in this sexpression to be invulnerable to weapons.  Use with caution!!!!\r\n\r\n"
1118                 "Takes 1 or more arguments:\r\n"
1119                 "\t1+:\tName of ships to make invulnerable to weapons." },
1120
1121         { OP_SHIP_GUARDIAN, "ship-guardian\r\n"
1122                 "\tCauses the ships listed in this sexpression to not be killable by weapons.  Use with caution!!!!\r\n\r\n"
1123                 "Takes 1 or more arguments:\r\n"
1124                 "\t1+:\tName of ships to make invulnerable to weapons." },
1125
1126         { OP_SHIP_NO_GUARDIAN, "ship-no-guardian\r\n"
1127                 "\tCauses the ships listed in this sexpression to be killable by weapons, if not invulnerable.\r\n\r\n"
1128                 "Takes 1 or more arguments:\r\n"
1129                 "\t1+:\tName of ships to make vulnerable to weapons." },
1130
1131         { OP_PERCENT_SHIPS_DEPARTED, "percent-ships-departed\r\n"
1132                 "\tBoolean function which returns true if the percentage of ships in the listed ships and wings "
1133                 "which have departed is greater or equal to the given percentage.  For wings, all ships of all waves "
1134                 "are used for calculation for the total possible ships to depart.\r\n\r\n"
1135                 "Takes 2 or more arguments:\r\n"
1136                 "\t1:\tPercentge of departed ships at which this function will return true.\r\n"
1137                 "\t2+:\tList of ships/wing whose departure status should be determined." },
1138
1139         { OP_PERCENT_SHIPS_DESTROYED, "percent-ships-destroyed\r\n"
1140                 "\tBoolean function which returns true if the percentage of ships in the listed ships and wings "
1141                 "which have been destroyed is greater or equal to the given percentage.  For wings, all ships of all waves "
1142                 "are used for calculation for the total possible ships to be destroyed.\r\n\r\n"
1143                 "Takes 2 or more arguments:\r\n"
1144                 "\t1:\tPercentge of destroyed ships at which this function will return true.\r\n"
1145                 "\t2+:\tList of ships/wing whose destroyed status should be determined." },
1146
1147         { OP_RED_ALERT, "red-alert\r\n"
1148                 "\tCauses Red Alert status in a mission.  This function ends the current mission, and moves to "
1149                 "the next mission in the campaign under red alert status.  There should only be one branch from "
1150                 "a mission that uses this expression\r\n\r\n"
1151                 "Takes no arguments."},
1152
1153         { OP_DEPART_NODE_DELAY, "depart-node-delay\r\n"
1154                 "\tReturns true N seconds after the listed ships depart, if those ships depart within the "
1155                 "radius of the given jump node.  The delay value is given in seconds.\r\n\r\n"
1156                 "Takes 3 or more arguments:r\n"
1157                 "\t1:\tDelay in seconds after the last ship listed departe before this expression can return true.\r\n"
1158                 "\t2:\tName of a jump node\r\n"
1159                 "\t3+:\tList of ships to check for departure within radius of the jump node." },
1160
1161         { OP_DESTROYED_DEPARTED_DELAY, "destroyed-or-departed-delay\r\n"
1162                 "\tReturns true N seconds after all the listed ships or wings have been destroyed or have "
1163                 "departed.\r\n\r\n"
1164                 "Takes 2 or more arguments:\r\n"
1165                 "\t1:\tDelay in seconda after the last ship/wing is destroyed or departerd this expression can return true.\r\n"
1166                 "\t2+:\tName of a ship or wing" },
1167
1168         { OP_SPECIAL_CHECK, "Special-check\r\n"
1169                 "\tDo some special check in training.  Ask Mike K. about how it works.\r\n\r\n"
1170                 "Returns a boolean value; Takes 1 argument:\r\n"
1171                 "\t1:\tExtra special number (tm)" },
1172
1173         { OP_END_CAMPAIGN, "end-campaign\r\n"
1174                 "\tEnds the builtin campaign.  Should only be used by the main FreeSpace campaign\r\n" },
1175
1176         { OP_WARP_BROKEN, "break-warp\r\n"
1177                 "\tBreak the warp drive on the specified ship.  A broken warp drive can be repaired by "
1178                 "a repair ship.  Takes 1 or more arguments:\r\n"
1179                 "\t1:\tList of ships to break the warp drive on" },
1180         { OP_WARP_NOT_BROKEN, "fix-warp\r\n"
1181                 "\tFixes a broken warp drive instantaneously.  This option applies to warp drives broken with "
1182                 "the break-warp sepxression.  Takes 1 or more arguments:\r\n"
1183                 "\t1:\tList of ships whose warp drive should be fixed"},
1184         { OP_WARP_NEVER, "never-warp\r\n"
1185                 "\tNever allows a ship to warp out.  When this sexpression is used, the given ships will "
1186                 "never be able to warp out.  The warp drive cannot be repaired.  Takes 1 or more arguments:\r\n"
1187                 "\t1:\tList of ships whose are not allowed to warp out under any condition"},
1188         { OP_WARP_ALLOWED, "allow-warp\r\n"
1189                 "\tAllows a ship which was previously not allowed to warp out to do so.  When this sexpression is "
1190                 "used, the given ships will be able to warp out again.  Takes 1 or more arguments:\r\n"
1191                 "\t1:\tList of ships whose are allowed to warp out"},
1192         { OP_JETTISON_CARGO, "jettison-cargo-delay\r\n"
1193                 "\tCauses a cargo carrying ship to jettison its cargo without the undocking procedure. Takes 2 arguments"},
1194         { OP_BEAM_FIRE, "beam-fire\r\n"
1195                 "\tFire a beam weapon from a specified subsystem\r\n"
1196                 "\t1:\tShip which will be firing\r\n"
1197                 "\t2:\tTurret which will fire the beam (note, this turret must have at least 1 beam weapon on it)\r\n"
1198                 "\t3:\tShip which will be targeted\r\n"
1199                 "Use add-data to add a specific subsystem to target on the specified target ship"},
1200         { OP_IS_TAGGED, "is-tagged\r\n"
1201                 "\tReturns whether a given ship is tagged or not\r\n"},
1202         { OP_NUM_KILLS, "num-kills\r\n"
1203                 "\tReturns the # of kills a player has. The ship specified in the first field should be the ship the player is in.\r\n"
1204                 "\tSo, for single player, this would be alpha 1. For multiplayer, it can be any ship with a player in it. If, at any\r\n"
1205                 "\ttime there is no player in a given ship, this sexpression will return 0"},
1206         { OP_NUM_TYPE_KILLS, "num-type-kills\r\n"
1207                 "\tReturns the # of kills a player has on a given ship type (fighter, bomber, cruiser, etc).\r\n"
1208                 "The ship specified in the first field should be the ship the player is in.\r\n"
1209                 "\tSo, for single player, this would be alpha 1. For multiplayer, it can be any ship with a player in it. If, at any\r\n"
1210                 "\ttime there is no player in a given ship, this sexpression will return 0"},
1211         { OP_NUM_CLASS_KILLS, "num-class-kills\r\n"
1212                 "\tReturns the # of kills a player has on a specific ship class (Ulysses, Hercules, etc).\r\n"
1213                 "The ship specified in the first field should be the ship the player is in.\r\n"
1214                 "\tSo, for single player, this would be alpha 1. For multiplayer, it can be any ship with a player in it. If, at any\r\n"
1215                 "\ttime there is no player in a given ship, this sexpression will return 0"},   
1216         { OP_BEAM_FREE, "beam-free\r\n"
1217                 "\tSets one or more beam weapons to allow firing for a given ship\r\n"
1218                 "\t1: Ship to be operated on\r\n"
1219                 "\t2, 3, etc : List of turrets to activate\r\n"},
1220         { OP_BEAM_FREE_ALL, "beam-free-all\r\n"
1221                 "\tSets all beam weapons on the specified ship to be active\r\n"},
1222         { OP_BEAM_LOCK, "beam-lock\r\n"
1223                 "\tSets one or more beam weapons to NOT allow firing for a given ship\r\n"
1224                 "\t1: Ship to be operated on\r\n"
1225                 "\t2, 3, etc : List of turrets to deactivate\r\n"},
1226         { OP_BEAM_LOCK_ALL, "beam-lock-all\r\n"
1227                 "\tSets all beam weapons on the specified ship to be deactivated\r\n"},
1228         { OP_TURRET_FREE, "turret-free\r\n"
1229                 "\tSets one or more turret weapons to allow firing for a given ship\r\n"
1230                 "\t1: Ship to be operated on\r\n"
1231                 "\t2, 3, etc : List of turrets to activate\r\n"},
1232         { OP_TURRET_FREE_ALL, "turret-free-all\r\n"
1233                 "\tSets all turret weapons on the specified ship to be active\r\n"},
1234         { OP_TURRET_LOCK, "turret-lock\r\n"
1235                 "\tSets one or more turret weapons to NOT allow firing for a given ship\r\n"
1236                 "\t1: Ship to be operated on\r\n"
1237                 "\t2, 3, etc : List of turrets to deactivate\r\n"},
1238         { OP_TURRET_LOCK_ALL, "turret-lock-all\r\n"
1239                 "\tSets all turret weapons on the specified ship to be deactivated\r\n"},
1240         { OP_ADD_REMOVE_ESCORT, "add-remove-escort\r\n"
1241                 "\tAdds or removes a ship from an escort list.\r\n"
1242                 "\t1: Ship to be added or removed\r\n"
1243                 "\t2: 0 to remove from the list, any positive value to add to the list\r\n"
1244                 "NOTE : it _IS_ safe to add a ship which may already be on the list or remove\r\n"
1245                 "a ship which is not on the list\r\n"},
1246         { OP_AWACS_SET_RADIUS, "awacs-set-radius\r\n"
1247                 "\tSets the awacs radius for a given ship subsystem. NOTE : does not work properly in multiplayer\r\n"
1248                 "\t1: Ship which has the awacs subsystem\r\n"
1249                 "\t2: Awacs subsystem\r\n"
1250                 "\t3: New radius\r\n"},
1251         { OP_SEND_MESSAGE_LIST, "send-message-list\r\n"
1252                 "\tSends a series of delayed messages. All times are accumulated"
1253                 "\t1:\tName of who the message is from.\r\n"
1254                 "\t2:\tPriority of message (\"Low\", \"Normal\" or \"High\").\r\n"
1255                 "\t3:\tName of message (from message editor).\r\n"
1256                 "\t4:\tDelay in ms\r\n"
1257                 "Use Add-Data for multiple messages"
1258                 "IMPORTANT : each additional message in the list MUST HAVE 4 entries\r\n"
1259                 "any message without the 4 proper fields will be ignore, as will any\r\n"
1260                 "successive messages"},
1261         { OP_CAP_WAYPOINT_SPEED, "cap-waypoint-speed\r\n"
1262                 "\tSets the maximum speed of a ship while flying waypoints.\r\n"
1263                 "\t1: Ship name\r\n"
1264                 "\t2: Maximum speed while flying waypoints\r\n"
1265                 "\tNOTE: This will only work if the ship is already in the game\r\n"
1266                 "\tNOTE: Set to -1 to reset\r\n"},
1267         { OP_TURRET_TAGGED_ONLY_ALL, "turret-tagged-only\r\n"
1268                 "\tMakes turrets target and hence fire strictly at tagged objects\r\n"
1269                 "\t1: Ship name\r\n"
1270                 "\tNOTE: Will not stop a turret already firing at an untagged ship\r\n"},
1271         { OP_TURRET_TAGGED_CLEAR_ALL, "turret-tagged-clear\r\n"
1272                 "\tRelaxes restriction on turrets targeting only tagged ships\r\n"
1273                 "\t1: Ship name\r\n"},
1274         { OP_SECONDARIES_DEPLETED, "secondaries-depleted\r\n"
1275                 "\tReturns true if ship is out secondary weapons\r\n"
1276                 "\t1: Ship name\r\n"},
1277         { OP_SUBSYS_SET_RANDOM, "subsys-set-random\r\n"
1278                 "\tSets ship subsystem strength in a given range\r\n"
1279                 "\t1: Ship name\r\n"
1280                 "\t2: Low range\r\n"
1281                 "\t3: High range\r\n"
1282                 "\t4: List of subsys names not to be randomized\r\n"},
1283         { OP_SUPERNOVA_START, "supernova-start\r\n"
1284                 "\t1: Time in seconds until the supernova shockwave hits the player\r\n"},
1285         { OP_SHIELD_RECHARGE_PCT, "shield-recharge-pct\r\n"
1286                 "\tReturns a percentage from 0 to 100\r\n"
1287                 "\t1: Ship name\r\n" },
1288         { OP_ENGINE_RECHARGE_PCT, "engine-recharge-pct\r\n"
1289                 "\tReturns a percentage from 0 to 100\r\n"
1290                 "\t1: Ship name\r\n" },
1291         { OP_WEAPON_RECHARGE_PCT, "weapon-recharge-pct\r\n"
1292                 "\tReturns a percentage from 0 to 100\r\n"
1293                 "\t1: Ship name\r\n" },
1294         { OP_CARGO_NO_DEPLETE, "cargo-no-deplete\r\n"
1295                 "\tCauses the named ship to have unlimited cargo.\r\n"
1296                 "\tNote:  only applies to BIG or HUGE ships\r\n"
1297                 "Takes 1 or more arguments:\r\n"
1298                 "\t1:\tName of one of the ships.\r\n"
1299                 "\t2:\toptional: 1 disallow depletion, 0 allow depletion." },
1300         { OP_SHIELD_QUAD_LOW, "shield-quad-low\r\n"
1301                 "\tReturns true if the specified ship has a shield quadrant below\r\n"
1302                 "\tthe specified threshold percentage\r\n"
1303                 "\t1: Ship name\r\n"
1304                 "\t2: Percentage\r\n" },
1305         { OP_SECONDARY_AMMO_PCT, "secondary-ammo-pct\r\n"
1306                 "\tReturns the percentage of ammo remaining in the specified bank (0 to 100)\r\n"
1307                 "\t1: Ship name\r\n"
1308                 "\t2: Bank to check (0, 1, 2 are legal banks. 3 will return the cumulative average for all banks" },
1309         { OP_IS_SECONDARY_SELECTED, "is-secondary-selected\r\n"
1310                 "\tReturns true if the specified bank is selected (0 .. num_banks - 1)\r\n"
1311                 "\t1: Ship name\r\n"
1312                 "\t2: Bank to check (0 .. num_banks - 1)\r\n"},
1313         { OP_IS_PRIMARY_SELECTED, "is-primary-selected\r\n"
1314                 "\tReturns true if the specified bank is selected (0 .. num_banks - 1)\r\n"
1315                 "\t1: Ship name\r\n"
1316                 "\t2: Bank to check (0 .. num_banks - 1)\r\n"},
1317         { OP_SPECIAL_WARP_DISTANCE, "special-warp-dist\r\n"
1318                 "\tReturns distance to the plane of the knossos device in percent length of ship\r\n"
1319                 "\t(ie, 100 means front of ship is 1 ship length from plane of knossos device)\r\n"
1320                 "\t1: Ship name\r\n"}, 
1321         { OP_SET_SPECIAL_WARPOUT_NAME, "special-warpout-name\r\n"
1322                 "\tSets the name of the knossos device to be used for warpout\r\n"
1323                 "\t1: Ship name to exit\r\n"
1324                 "\t2: Name of knossos device\r\n"},
1325         { OP_SHIP_VANISH, "ship-vanish\r\n"
1326                 "\tMakes the named ship vanish (no log and vanish)\r\n"
1327                 "\tSingle Player Only!  Warning: This will cause ship exit not to be logged, so 'has-departed', etc. will not work\r\n"
1328                 "\t1: List of ship names to vanish\r\n"},
1329         { OP_IS_SHIP_VISIBLE, "is-ship-visible\r\n"
1330                 "\tCheck whether ship is visible on Player's radar\r\n"
1331                 "\tSingle Player Only!  Returns 0 - not visible, 1 - partially visible, 2 - fully visible.\r\n"
1332                 "\t1: Name of ship to check\r\n"},
1333         { OP_TEAM_SCORE, "team-score\r\n"
1334                 "\tGet the score of a multi team vs team game.\r\n"
1335                 "\t1: Team index (1 for team 1 and 2 for team 2)\r\n"},
1336
1337 };
1338
1339 struct op_menu_struct {
1340         char *name;
1341         int id;
1342
1343 } op_menu[] = {
1344         { "Objectives", OP_CATAGORY_OBJECTIVE },
1345         { "Time",                       OP_CATAGORY_TIME },
1346         { "Logical",            OP_CATAGORY_LOGICAL },
1347         { "Arithmetic", OP_CATAGORY_ARITHMETIC },
1348         { "Status",                     OP_CATAGORY_STATUS },
1349         { "Change",                     OP_CATAGORY_CHANGE },
1350         { "Conditionals",       OP_CATAGORY_CONDITIONAL },
1351         { "Debugging",          OP_CATAGORY_DEBUG },
1352         { "Ai goals",           OP_CATAGORY_AI },
1353         { "Event/Goals",        OP_CATAGORY_GOAL_EVENT },
1354         { "Training",           OP_CATAGORY_TRAINING },
1355 };
1356
1357 int Num_op_menus = sizeof(op_menu) / sizeof(op_menu_struct);
1358
1359 // constructor
1360 sexp_tree::sexp_tree()
1361 {
1362         select_sexp_node = -1;
1363         root_item = -1;
1364         m_mode = 0;
1365         m_dragging = FALSE;
1366         m_p_image_list = NULL;
1367         help_box = NULL;
1368         clear_tree();
1369 }
1370
1371 // clears out the tree, so all the nodes are unused.
1372 void sexp_tree::clear_tree(char *op)
1373 {
1374         int i;
1375
1376         total = flag = 0;
1377         for (i=0; i<MAX_SEXP_TREE_SIZE; i++)
1378                 nodes[i].type = SEXPT_UNUSED;
1379
1380         if (op) {
1381                 DeleteAllItems();
1382                 if (strlen(op)) {
1383                         set_node(allocate_node(-1), (SEXPT_OPERATOR | SEXPT_VALID), op);
1384                         build_tree();
1385                 }
1386         }
1387 }
1388
1389 void sexp_tree::reset_handles()
1390 {
1391         int i;
1392
1393         for (i=0; i<MAX_SEXP_TREE_SIZE; i++)
1394                 nodes[i].handle = NULL;
1395 }
1396
1397 // initializes and creates a tree from a given sexp startpoint.
1398 void sexp_tree::load_tree(int index, char *deflt)
1399 {
1400         int cur;
1401
1402         clear_tree();
1403         root_item = 0;
1404         if (index < 0) {
1405                 cur = allocate_node(-1);
1406                 set_node(cur, (SEXPT_OPERATOR | SEXPT_VALID), deflt);  // setup a default tree if none
1407                 build_tree();
1408                 return;
1409         }
1410
1411         if (Sexp_nodes[index].subtype == SEXP_ATOM_NUMBER) {  // handle numbers allender likes to use so much..
1412                 cur = allocate_node(-1);
1413                 if (atoi(Sexp_nodes[index].text))
1414                         set_node(cur, (SEXPT_OPERATOR | SEXPT_VALID), "true");
1415                 else
1416                         set_node(cur, (SEXPT_OPERATOR | SEXPT_VALID), "false");
1417
1418                 build_tree();
1419                 return;
1420         }
1421
1422         // assumption: first token is an operator.  I require this because it would cause problems
1423         // with child/parent relations otherwise, and it should be this way anyway, since the
1424         // return type of the whole sexp is boolean, and only operators can satisfy this.
1425         Assert(Sexp_nodes[index].subtype == SEXP_ATOM_OPERATOR);
1426         load_branch(index, -1);
1427         build_tree();
1428 }
1429
1430 void get_combined_variable_name(char *combined_name, const char *sexp_var_name)
1431 {
1432         int sexp_var_index = get_index_sexp_variable_name(sexp_var_name);
1433         Assert(sexp_var_index > -1);
1434
1435         sprintf(combined_name, "%s(%s)", Sexp_variables[sexp_var_index].variable_name, Sexp_variables[sexp_var_index].text);
1436 }
1437
1438
1439
1440 // creates a tree from a given Sexp_nodes[] point under a given parent.  Recursive.
1441 void sexp_tree::load_branch(int index, int parent)
1442 {
1443         int cur = -1;
1444         char combined_var_name[2*TOKEN_LENGTH + 2];
1445
1446         while (index != -1) {
1447                 Assert(Sexp_nodes[index].type != SEXP_NOT_USED);
1448                 if (Sexp_nodes[index].subtype == SEXP_ATOM_LIST) {
1449                         load_branch(Sexp_nodes[index].first, parent);  // do the sublist and continue
1450
1451                 } else if (Sexp_nodes[index].subtype == SEXP_ATOM_OPERATOR) {
1452                         cur = allocate_node(parent);
1453                         if ((index == select_sexp_node) && !flag) {  // translate sexp node to our node
1454                                 select_sexp_node = cur;
1455                                 flag = 1;
1456                         }
1457
1458                         set_node(cur, (SEXPT_OPERATOR | SEXPT_VALID), Sexp_nodes[index].text);
1459                         load_branch(Sexp_nodes[index].rest, cur);  // operator is new parent now
1460                         return;  // 'rest' was just used, so nothing left to use.
1461
1462                 } else if (Sexp_nodes[index].subtype == SEXP_ATOM_NUMBER) {
1463                         cur = allocate_node(parent);
1464                         if (Sexp_nodes[index].type & SEXP_FLAG_VARIABLE) {
1465                                 get_combined_variable_name(combined_var_name, Sexp_nodes[index].text);
1466                                 set_node(cur, (SEXPT_VARIABLE | SEXPT_NUMBER | SEXPT_VALID), combined_var_name);
1467                         } else {
1468                                 set_node(cur, (SEXPT_NUMBER | SEXPT_VALID), Sexp_nodes[index].text);
1469                         }
1470
1471                 } else if (Sexp_nodes[index].subtype == SEXP_ATOM_STRING) {
1472                         cur = allocate_node(parent);
1473                         if (Sexp_nodes[index].type & SEXP_FLAG_VARIABLE) {
1474                                 get_combined_variable_name(combined_var_name, Sexp_nodes[index].text);
1475                                 set_node(cur, (SEXPT_VARIABLE | SEXPT_STRING | SEXPT_VALID), combined_var_name);
1476                         } else {
1477                                 set_node(cur, (SEXPT_STRING | SEXPT_VALID), Sexp_nodes[index].text);
1478                         }
1479
1480                 } else
1481                         Assert(0);  // unknown and/or invalid sexp type
1482
1483                 if ((index == select_sexp_node) && !flag) {  // translate sexp node to our node
1484                         select_sexp_node = cur;
1485                         flag = 1;
1486                 }
1487
1488                 index = Sexp_nodes[index].rest;
1489                 if (index == -1)
1490                         return;
1491         }
1492 }
1493
1494 int sexp_tree::query_false(int node)
1495 {
1496         if (node < 0)
1497                 node = root_item;
1498
1499         Assert(node >= 0);
1500         Assert(nodes[node].type == (SEXPT_OPERATOR | SEXPT_VALID));
1501         Assert(nodes[node].next == -1);  // must make this assumption or else it will confuse code!
1502         if (find_operator(nodes[node].text) == OP_FALSE){
1503                 return TRUE;
1504         }
1505
1506         return FALSE;
1507 }
1508
1509 // builds an sexp of the tree and returns the index of it.  This allocates sexp nodes.
1510 int sexp_tree::save_tree(int node)
1511 {
1512         if (node < 0)
1513                 node = root_item;
1514
1515         Assert(node >= 0);
1516         Assert(nodes[node].type == (SEXPT_OPERATOR | SEXPT_VALID));
1517         Assert(nodes[node].next == -1);  // must make this assumption or else it will confuse code!
1518         return save_branch(node);
1519 }
1520
1521 // get variable name from sexp_tree node .text
1522 void var_name_from_sexp_tree_text(char *var_name, const char *text)
1523 {
1524         int var_name_length = strcspn(text, "(");
1525         Assert(var_name_length < TOKEN_LENGTH - 1);
1526
1527         strncpy(var_name, text, var_name_length);
1528         var_name[var_name_length] = '\0';
1529 }
1530
1531 #define NO_PREVIOUS_NODE -9
1532 // called recursively to save a tree branch and everything under it
1533 int sexp_tree::save_branch(int cur, int at_root)
1534 {
1535         int start, node = -1, last = NO_PREVIOUS_NODE;
1536         char var_name_text[TOKEN_LENGTH];
1537
1538         start = -1;
1539         while (cur != -1) {
1540                 if (nodes[cur].type & SEXPT_OPERATOR) {
1541                         node = alloc_sexp(nodes[cur].text, SEXP_ATOM, SEXP_ATOM_OPERATOR, -1, save_branch(nodes[cur].child));
1542
1543                         if ((nodes[cur].parent >= 0) && !at_root) {
1544                                 node = alloc_sexp("", SEXP_LIST, SEXP_ATOM_LIST, node, -1);
1545                         }
1546                 } else if (nodes[cur].type & SEXPT_NUMBER) {
1547                         // allocate number, maybe variable
1548                         if (nodes[cur].type & SEXPT_VARIABLE) {
1549                                 var_name_from_sexp_tree_text(var_name_text, nodes[cur].text);
1550                                 node = alloc_sexp(var_name_text, (SEXP_ATOM | SEXP_FLAG_VARIABLE), SEXP_ATOM_NUMBER, -1, -1);
1551                         } else {
1552                                 node = alloc_sexp(nodes[cur].text, SEXP_ATOM, SEXP_ATOM_NUMBER, -1, -1);
1553                         }
1554                 } else if (nodes[cur].type & SEXPT_STRING) {
1555                         // allocate string, maybe variable
1556                         if (nodes[cur].type & SEXPT_VARIABLE) {
1557                                 var_name_from_sexp_tree_text(var_name_text, nodes[cur].text);
1558                                 node = alloc_sexp(var_name_text, (SEXP_ATOM | SEXP_FLAG_VARIABLE), SEXP_ATOM_STRING, -1, -1);
1559                         } else {
1560                                 node = alloc_sexp(nodes[cur].text, SEXP_ATOM, SEXP_ATOM_STRING, -1, -1);
1561                         }
1562                 } else if (nodes[cur].type & SEXPT_STRING) {
1563                         Assert( !(nodes[cur].type & SEXPT_VARIABLE) );
1564                         Int3();
1565                 } else {
1566                         Assert(0); // unknown and/or invalid type
1567                 }
1568
1569                 if (last == NO_PREVIOUS_NODE){
1570                         start = node;
1571                 } else if (last >= 0){
1572                         Sexp_nodes[last].rest = node;
1573                 }
1574
1575                 last = node;
1576                 Assert(last != NO_PREVIOUS_NODE);  // should be impossible
1577                 cur = nodes[cur].next;
1578                 if (at_root){
1579                         return start;
1580                 }
1581         }
1582
1583         return start;
1584 }
1585
1586 // allocate a node.  Remains used until freed.
1587 int sexp_tree::allocate_node()
1588 {
1589         int i;
1590
1591         for (i=0; i<MAX_SEXP_TREE_SIZE; i++)
1592                 if (nodes[i].type == SEXPT_UNUSED) {
1593                         nodes[i].type = SEXPT_UNINIT;
1594                         nodes[i].parent = -1;
1595                         nodes[i].child = -1;
1596                         nodes[i].next = -1;
1597                         total++;
1598                         return i;
1599                 }
1600
1601         Error(LOCATION, "Out of sexp tree nodes!");
1602         return -1;
1603 }
1604
1605 // allocate a child node under 'parent'.  Appends to end of list.
1606 int sexp_tree::allocate_node(int parent, int after)
1607 {
1608         int i, index = allocate_node();
1609
1610         if (parent != -1) {
1611                 i = nodes[parent].child;
1612                 if (i == -1) {
1613                         nodes[parent].child = index;
1614
1615                 } else {
1616                         while ((i != after) && (nodes[i].next != -1))
1617                                 i = nodes[i].next;
1618
1619                         nodes[index].next = nodes[i].next;
1620                         nodes[i].next = index;
1621                 }
1622         }
1623
1624         nodes[index].parent = parent;
1625         return index;
1626 }
1627
1628 // free a node and all it's children.  Also clears pointers to it, if any.
1629 //   node = node chain to free
1630 //   cascade =  0: free just this node and children under it. (default)
1631 //             !0: free this node and all siblings after it.
1632 //
1633 void sexp_tree::free_node(int node, int cascade)
1634 {
1635         int i;
1636
1637         // clear the pointer to node
1638         i = nodes[node].parent;
1639         Assert(i != -1);
1640         if (nodes[i].child == node)
1641                 nodes[i].child = nodes[node].next;
1642
1643         else {
1644                 i = nodes[i].child;
1645                 while (nodes[i].next != -1) {
1646                         if (nodes[i].next == node) {
1647                                 nodes[i].next = nodes[node].next;
1648                                 break;
1649                         }
1650
1651                         i = nodes[i].next;
1652                 }
1653         }
1654
1655         if (!cascade)
1656                 nodes[node].next = -1;
1657
1658         // now free up the node and it's children
1659         free_node2(node);
1660 }
1661
1662 // more simple node freer, which works recursively.  It frees the given node and all siblings
1663 // that come after it, as well as all children of these.  Doesn't clear any links to any of
1664 // these freed nodes, so make sure all links are broken first. (i.e. use free_node() if you can)
1665 //
1666 void sexp_tree::free_node2(int node)
1667 {
1668         Assert(node != -1);
1669         Assert(nodes[node].type != SEXPT_UNUSED);
1670         Assert(total > 0);
1671         *modified = 1;
1672         nodes[node].type = SEXPT_UNUSED;
1673         total--;
1674         if (nodes[node].child != -1)
1675                 free_node2(nodes[node].child);
1676
1677         if (nodes[node].next != -1)
1678                 free_node2(nodes[node].next);
1679 }
1680
1681 // initialize the data for a node.  Should be called right after a new node is allocated.
1682 void sexp_tree::set_node(int node, int type, char *text)
1683 {
1684         Assert(type != SEXPT_UNUSED);
1685         Assert(nodes[node].type != SEXPT_UNUSED);
1686         nodes[node].type = type;
1687         size_t max_length;
1688         if (type & SEXPT_VARIABLE) {
1689                 max_length = 2 * TOKEN_LENGTH + 2;
1690         } else {
1691                 max_length = TOKEN_LENGTH;
1692         }
1693         Assert(strlen(text) < max_length);
1694         strcpy(nodes[node].text, text);
1695 }
1696
1697 void sexp_tree::post_load()
1698 {
1699         if (!flag)
1700                 select_sexp_node = -1;
1701 }
1702
1703 // build or rebuild a CTreeCtrl object with the current tree data
1704 void sexp_tree::build_tree()
1705 {
1706         if (!flag)
1707                 select_sexp_node = -1;
1708
1709         DeleteAllItems();
1710         add_sub_tree(0, TVI_ROOT);
1711 }
1712
1713 // Create the CTreeCtrl tree from the tree data.  The tree data should already be setup by
1714 // this point.
1715 void sexp_tree::add_sub_tree(int node, HTREEITEM root)
1716 {
1717 //      char str[80];
1718         int node2;
1719
1720         Assert(node >= 0 && node < MAX_SEXP_TREE_SIZE);
1721         node2 = nodes[node].child;
1722
1723         // check for single argument operator case (prints as one line)
1724 /*      if (node2 != -1 && nodes[node2].child == -1 && nodes[node2].next == -1) {
1725                 sprintf(str, "%s %s", nodes[node].text, nodes[node2].text);
1726                 nodes[node].handle = insert(str, root);
1727                 nodes[node].flags = OPERAND | EDITABLE;
1728                 nodes[node2].flags = COMBINED;
1729                 return;
1730         }*/
1731
1732         // bitmap to draw in tree
1733         int bitmap;
1734
1735         if (nodes[node].type & SEXPT_OPERATOR) {
1736                 nodes[node].flags = OPERAND;
1737                 bitmap = BITMAP_OPERATOR;
1738         } else {
1739                 if (nodes[node].type & SEXPT_VARIABLE) {
1740                         nodes[node].flags = NOT_EDITABLE;
1741                         bitmap = BITMAP_VARIABLE;
1742                 } else {
1743                         nodes[node].flags = EDITABLE;
1744                         bitmap = BITMAP_DATA;
1745                 }
1746         }
1747
1748         root = nodes[node].handle = insert(nodes[node].text, bitmap, bitmap, root);
1749
1750         node = node2;
1751         while (node != -1) {
1752                 Assert(node >= 0 && node < MAX_SEXP_TREE_SIZE);
1753                 Assert(nodes[node].type & SEXPT_VALID);
1754                 if (nodes[node].type & SEXPT_OPERATOR)  {
1755                         add_sub_tree(node, root);
1756
1757                 } else {
1758                         Assert(nodes[node].child == -1);
1759                         if (nodes[node].type & SEXPT_VARIABLE) {
1760                                 nodes[node].handle = insert(nodes[node].text, BITMAP_VARIABLE, BITMAP_VARIABLE, root);
1761                                 nodes[node].flags = NOT_EDITABLE;
1762                         } else {
1763                                 nodes[node].handle = insert(nodes[node].text, BITMAP_DATA, BITMAP_DATA, root);
1764                                 nodes[node].flags = EDITABLE;
1765                         }
1766                 }
1767
1768                 node = nodes[node].next;
1769         }
1770 }
1771
1772 void sexp_tree::setup_selected(HTREEITEM h)
1773 {
1774         int i;
1775
1776         item_index = -1;
1777         item_handle = h;
1778         if (!h)
1779                 item_handle = GetSelectedItem();
1780
1781         for (i=0; i<MAX_SEXP_TREE_SIZE; i++)
1782                 if (nodes[i].handle == item_handle) {
1783                         item_index = i;
1784                         break;
1785                 }
1786 }
1787
1788 int sexp_tree::get_ambiguous_type(int parent)
1789 {
1790
1791         int first_arg_index = get_modify_variable_first_arg_index();
1792         int sexp_var_index = get_tree_name_to_sexp_variable_index(nodes[first_arg_index].text);
1793         Assert(sexp_var_index != -1);
1794
1795         if (Sexp_variables[sexp_var_index].type & SEXP_VARIABLE_NUMBER) {
1796                 return OPF_NUMBER;
1797         } else {
1798                 return OPF_AMBIGUOUS;
1799         }
1800 }
1801
1802
1803 int sexp_tree::get_modify_variable_first_arg_index()
1804 {
1805         int index;
1806
1807         Assert( item_index >= 0);
1808
1809         // get parent and check "modify-variable"
1810         index = nodes[item_index].parent;
1811         Assert( index != -1 );
1812         Assert( !(stricmp(nodes[index].text, "modify-variable")) );
1813
1814         // get first child and verify type variable
1815         index = nodes[index].child;
1816         Assert( index != -1 );
1817         Assert( nodes[index].type & SEXPT_VARIABLE);
1818
1819         return index;
1820 }
1821
1822 // handler for right mouse button clicks.
1823 void sexp_tree::right_clicked(int mode)
1824 {
1825         char buf[256];
1826         int i, j, z, count, op, add_type, replace_type, type;
1827         sexp_list_item *list;
1828         UINT _flags;
1829         HTREEITEM h;
1830         POINT click_point, mouse;
1831         CMenu menu, *mptr, *popup_menu, *add_data_menu = NULL, *replace_data_menu = NULL;
1832         CMenu *add_op_menu, add_op_submenu[MAX_OP_MENUS];
1833         CMenu *replace_op_menu, replace_op_submenu[MAX_OP_MENUS];
1834         CMenu *insert_op_menu, insert_op_submenu[MAX_OP_MENUS];
1835         CMenu *replace_variable_menu = NULL;
1836
1837         m_mode = mode;
1838         add_instance = replace_instance = -1;
1839         Assert(Num_operators <= MAX_OPERATORS);
1840         Assert(Num_op_menus < MAX_OP_MENUS);
1841         GetCursorPos(&mouse);
1842         click_point = mouse;
1843         ScreenToClient(&click_point);
1844         h = HitTest(CPoint(click_point), &_flags);  // find out what they clicked on
1845         if (h && menu.LoadMenu(IDR_MENU_EDIT_SEXP_TREE)) {
1846                 update_help(h);
1847                 popup_menu = menu.GetSubMenu(0);
1848                 ASSERT(popup_menu != NULL);
1849                 SelectDropTarget(h);
1850
1851                 add_op_menu = replace_op_menu = insert_op_menu = NULL;
1852
1853                 // get pointers to several key popup menus we'll need to modify
1854                 i = popup_menu->GetMenuItemCount();
1855                 while (i--) {
1856                         if ( (mptr = popup_menu->GetSubMenu(i)) > 0 ) {
1857                                 popup_menu->GetMenuString(i, buf, sizeof(buf), MF_BYPOSITION);
1858
1859                                 if (!stricmp(buf, "add operator")) {
1860                                         add_op_menu = mptr;
1861
1862                                 } else if (!stricmp(buf, "replace operator")) {
1863                                         replace_op_menu = mptr;
1864
1865                                 } else if (!stricmp(buf, "add data")) {
1866                                         add_data_menu = mptr;
1867
1868                                 } else if (!stricmp(buf, "replace data")) {
1869                                         replace_data_menu = mptr;
1870
1871                                 } else if (!stricmp(buf, "insert operator")) {
1872                                         insert_op_menu = mptr;
1873
1874                                 } else if (!stricmp(buf, "replace variable")) {
1875                                         replace_variable_menu = mptr;
1876                                 }
1877                         }
1878                 }
1879
1880                 // add popup menus for all the operator catagories
1881                 for (i=0; i<Num_op_menus; i++) {
1882                         add_op_submenu[i].CreatePopupMenu();
1883                         replace_op_submenu[i].CreatePopupMenu();
1884                         insert_op_submenu[i].CreatePopupMenu();
1885                         add_op_menu->AppendMenu(MF_POPUP, (UINT) add_op_submenu[i].m_hMenu, op_menu[i].name);
1886                         replace_op_menu->AppendMenu(MF_POPUP, (UINT) replace_op_submenu[i].m_hMenu, op_menu[i].name);
1887                         insert_op_menu->AppendMenu(MF_POPUP, (UINT) insert_op_submenu[i].m_hMenu, op_menu[i].name);
1888                 }
1889
1890                 // get rid of the placeholders we needed to ensure popup menus stayed popup menus,
1891                 // i.e. MSDEV will convert empty popup menus into normal menu items.
1892                 add_op_menu->DeleteMenu(ID_PLACEHOLDER, MF_BYCOMMAND);
1893                 replace_op_menu->DeleteMenu(ID_PLACEHOLDER, MF_BYCOMMAND);
1894                 insert_op_menu->DeleteMenu(ID_PLACEHOLDER, MF_BYCOMMAND);
1895                 replace_variable_menu->DeleteMenu(ID_PLACEHOLDER, MF_BYCOMMAND);
1896
1897
1898                 // get item_index
1899                 item_index = -1;
1900                 for (i=0; i<MAX_SEXP_TREE_SIZE; i++) {
1901                         if (nodes[i].handle == h) {
1902                                 item_index = i;
1903                                 break;
1904                         }
1905                 }
1906
1907                 // Do SEXP_VARIABLE stuff here.
1908                 if (m_mode != MODE_EVENTS) {
1909                         // only allow variables in event mode
1910                         menu.EnableMenuItem(ID_SEXP_TREE_ADD_VARIABLE, MF_GRAYED);
1911                         menu.EnableMenuItem(ID_SEXP_TREE_MODIFY_VARIABLE, MF_GRAYED);
1912                 } else {
1913                         menu.EnableMenuItem(ID_SEXP_TREE_ADD_VARIABLE, MF_ENABLED);
1914                         menu.EnableMenuItem(ID_SEXP_TREE_MODIFY_VARIABLE, MF_ENABLED);
1915                         
1916                         // check not root (-1)
1917                         if (item_index >= 0) {
1918                                 // get type of sexp_tree item clicked on
1919                                 int type = get_type(h);
1920
1921                                 int parent = nodes[item_index].parent;
1922                                 if (parent >= 0) {
1923                                         op = identify_operator(nodes[parent].text);
1924                                         Assert(op >= 0);
1925                                         int first_arg = nodes[parent].child;
1926
1927                                         // get arg count of item to replace
1928                                         Replace_count = 0;
1929                                         int temp = first_arg;
1930                                         while (temp != item_index) {
1931                                                 Replace_count++;
1932                                                 temp = nodes[temp].next;
1933
1934                                                 // DB - added 3/4/99
1935                                                 if(temp == -1){
1936                                                         break;
1937                                                 }
1938                                         }
1939
1940                                         int op_type = query_operator_argument_type(op, Replace_count); // check argument type at this position
1941
1942                                         // special case dont allow replace data for variable names
1943                                         if (op_type != OPF_AMBIGUOUS) {
1944
1945                                                 if ( (type & SEXPT_STRING) || (type & SEXPT_NUMBER) ) {
1946
1947                                                         int max_sexp_vars = MAX_SEXP_VARIABLES;
1948                                                         // prevent collisions in id numbers: ID_VARIABLE_MENU + 512 = ID_ADD_MENU
1949                                                         Assert(max_sexp_vars < 512);
1950
1951                                                         for (int idx=0; idx<max_sexp_vars; idx++) {
1952                                                                 if (Sexp_variables[idx].type & SEXP_VARIABLE_SET) {
1953                                                                         UINT flag = MF_STRING | MF_GRAYED;
1954                                                                         // maybe gray flag MF_GRAYED
1955
1956                                                                         // get type -- gray "string" or number accordingly
1957                                                                         if ( type & SEXPT_STRING ) {
1958                                                                                 if ( Sexp_variables[idx].type & SEXP_VARIABLE_STRING ) {
1959                                                                                         flag &= ~MF_GRAYED;
1960                                                                                 }
1961                                                                         } else if ( type & SEXPT_NUMBER ) {
1962                                                                                 if ( Sexp_variables[idx].type & SEXP_VARIABLE_NUMBER ) {
1963                                                                                         flag &= ~MF_GRAYED;
1964                                                                                 }
1965                                                                         }
1966
1967                                                                         // if modify-variable and changing variable, enable all variables
1968                                                                         if (op_type == OPF_VARIABLE_NAME) {
1969                                                                                 Modify_variable = 1;
1970                                                                                 flag &= ~MF_GRAYED;
1971                                                                         } else {
1972                                                                                 Modify_variable = 0;
1973                                                                         }
1974
1975                                                                         char buf[128];
1976                                                                         // append list of variable names and values
1977                                                                         // set id as ID_VARIABLE_MENU + idx
1978                                                                         sprintf(buf, "%s(%s)", Sexp_variables[idx].variable_name, Sexp_variables[idx].text);
1979
1980                                                                         replace_variable_menu->AppendMenu(flag, (ID_VARIABLE_MENU + idx), buf);
1981                                                                 }
1982                                                         }
1983                                                 }
1984                                         }
1985                                 }
1986                         }
1987
1988                         // cant modify if no variables
1989                         if (sexp_variable_count() == 0) {
1990                                 menu.EnableMenuItem(ID_SEXP_TREE_MODIFY_VARIABLE, MF_GRAYED);
1991                         }
1992                 }
1993
1994                 // add operator menu items to the various catagory submenus they belong in
1995                 for (i=0; i<Num_operators; i++) {
1996                         for (j=0; j<Num_op_menus; j++) {
1997                                 if (op_menu[j].id == (Operators[i].value & OP_CATAGORY_MASK)) {
1998
1999 #ifdef NDEBUG
2000                                         switch (Operators[i].value) {
2001                                                 case OP_WAS_PROMOTION_GRANTED:
2002                                                 case OP_WAS_MEDAL_GRANTED:
2003                                                 case OP_GRANT_PROMOTION:
2004                                                 case OP_GRANT_MEDAL:
2005                                                 case OP_TECH_ADD_SHIP:
2006                                                 case OP_TECH_ADD_WEAPON:
2007                                                         j = Num_op_menus;  // don't allow these operators in final release
2008                                         }
2009 #endif
2010                                         if (j < Num_op_menus) {
2011                                                 add_op_submenu[j].AppendMenu(MF_STRING | MF_GRAYED, Operators[i].value, Operators[i].text);
2012                                                 replace_op_submenu[j].AppendMenu(MF_STRING | MF_GRAYED, Operators[i].value | OP_REPLACE_FLAG, Operators[i].text);
2013                                                 insert_op_submenu[j].AppendMenu(MF_STRING, Operators[i].value | OP_INSERT_FLAG, Operators[i].text);
2014                                         }
2015
2016                                         break;  // only 1 category valid
2017                                 }
2018                         }
2019                 }
2020
2021                 // find local index (i) of current item (from it's handle)
2022                 SelectItem(item_handle = h);
2023                 for (i=0; i<MAX_SEXP_TREE_SIZE; i++) {
2024                         if (nodes[i].handle == h) {
2025                                 break;
2026                         }
2027                 }
2028
2029                 // special case: item is a ROOT node, and a label that can be edited (not an item in the sexp tree)
2030                 if ((item_index == -1) && (m_mode & ST_LABELED_ROOT)) {
2031                         if (m_mode & ST_ROOT_EDITABLE) {
2032                                 menu.EnableMenuItem(ID_EDIT_TEXT, MF_ENABLED);
2033                         } else {
2034                                 menu.EnableMenuItem(ID_EDIT_TEXT, MF_GRAYED);
2035                         }
2036
2037                         // disable copy, insert op
2038                         menu.EnableMenuItem(ID_EDIT_COPY, MF_GRAYED);
2039                         for (j=0; j<Num_operators; j++) {
2040                                 menu.EnableMenuItem(Operators[j].value | OP_INSERT_FLAG, MF_GRAYED);
2041                         }
2042
2043                         gray_menu_tree(popup_menu);
2044                         popup_menu->TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON, mouse.x, mouse.y, this);
2045                         return;
2046                 }
2047
2048                 Assert(item_index != -1);  // handle not found, which should be impossible.
2049                 if (!(nodes[item_index].flags & EDITABLE)) {
2050                         menu.EnableMenuItem(ID_EDIT_TEXT, MF_GRAYED);
2051                 }
2052
2053                 if (nodes[item_index].parent == -1) {  // root node
2054                         menu.EnableMenuItem(ID_DELETE, MF_GRAYED);  // can't delete the root item.
2055                 }
2056
2057 /*              if ((nodes[item_index].flags & OPERAND) && (nodes[item_index].flags & EDITABLE))  // expandable?
2058                         menu.EnableMenuItem(ID_SPLIT_LINE, MF_ENABLED);
2059
2060                 z = nodes[item_index].child;
2061                 if (z != -1 && nodes[z].next == -1 && nodes[z].child == -1)
2062                         menu.EnableMenuItem(ID_SPLIT_LINE, MF_ENABLED);
2063
2064                 z = nodes[nodes[item_index].parent].child;
2065                 if (z != -1 && nodes[z].next == -1 && nodes[z].child == -1)
2066                         menu.EnableMenuItem(ID_SPLIT_LINE, MF_ENABLED);*/
2067
2068                 // change enabled status of 'add' type menu options.
2069                 add_type = 0;
2070                 if (nodes[item_index].flags & OPERAND)  {
2071                         add_type = OPR_STRING;
2072                         int child = nodes[item_index].child;
2073                         Add_count = count_args(child);
2074                         op = identify_operator(nodes[item_index].text);
2075                         Assert(op >= 0);
2076
2077                         // get listing of valid argument values and add to menus
2078                         type = query_operator_argument_type(op, Add_count);
2079                         list = get_listing_opf(type, item_index, Add_count);
2080                         if (list) {
2081                                 sexp_list_item *ptr;
2082
2083                                 int data_idx = 0;
2084                                 ptr = list;
2085                                 while (ptr) {
2086                                         if (ptr->op >= 0) {
2087                                                 // enable operators with correct return type
2088                                                 menu.EnableMenuItem(Operators[ptr->op].value, MF_ENABLED);
2089
2090                                         } else {
2091                                                 // add data
2092                                                 if ( (data_idx + 3) % 30) {
2093                                                         add_data_menu->AppendMenu(MF_STRING | MF_ENABLED, ID_ADD_MENU + data_idx, ptr->text);
2094                                                 } else {
2095                                                         add_data_menu->AppendMenu(MF_MENUBARBREAK | MF_STRING | MF_ENABLED, ID_ADD_MENU + data_idx, ptr->text);
2096                                                 }
2097                                         }
2098
2099                                         data_idx++;
2100                                         ptr = ptr->next;
2101                                 }
2102                         }
2103
2104                         // special handling for the non-string formats
2105                         if (type == OPF_NONE) {  // an argument can't be added
2106                                 add_type = 0;
2107
2108                         } else if (type == OPF_NULL) {  // arguments with no return values
2109                                 add_type = OPR_NULL;
2110                         
2111                         } else if (type == OPF_NUMBER) {  // takes numbers
2112                                 add_type = OPR_NUMBER;
2113                                 menu.EnableMenuItem(ID_ADD_NUMBER, MF_ENABLED);
2114
2115                         } else if (type == OPF_POSITIVE) {  // takes non-negative numbers
2116                                 add_type = OPR_POSITIVE;
2117                                 menu.EnableMenuItem(ID_ADD_NUMBER, MF_ENABLED);
2118
2119                         } else if (type == OPF_BOOL) {  // takes true/false bool values
2120                                 add_type = OPR_BOOL;
2121
2122                         } else if (type == OPF_AI_GOAL) {
2123                                 add_type = OPR_AI_GOAL;
2124                         }
2125
2126                         // add_type unchanged from above
2127                         if (add_type == OPR_STRING) {
2128                                 menu.EnableMenuItem(ID_ADD_STRING, MF_ENABLED);
2129                         }
2130
2131                         list->destroy();
2132                 }
2133
2134                 // disable operators that do not have arguments available
2135                 for (j=0; j<Num_operators; j++) {
2136                         if (!query_default_argument_available(j)) {
2137                                 menu.EnableMenuItem(Operators[j].value, MF_GRAYED);
2138                         }
2139                 }
2140
2141
2142                 // change enabled status of 'replace' type menu options.
2143                 replace_type = 0;
2144                 int parent = nodes[item_index].parent;
2145                 if (parent >= 0) {
2146                         replace_type = OPR_STRING;
2147                         op = identify_operator(nodes[parent].text);
2148                         Assert(op >= 0);
2149                         int first_arg = nodes[parent].child;
2150                         count = count_args(nodes[parent].child);
2151
2152                         // already at minimum number of arguments?
2153                         if (count <= Operators[op].min) {
2154                                 menu.EnableMenuItem(ID_DELETE, MF_GRAYED);
2155                         }
2156
2157                         // get arg count of item to replace
2158                         Replace_count = 0;
2159                         int temp = first_arg;
2160                         while (temp != item_index) {
2161                                 Replace_count++;
2162                                 temp = nodes[temp].next;
2163
2164                                 // DB - added 3/4/99
2165                                 if(temp == -1){
2166                                         break;
2167                                 }
2168                         }
2169
2170                         // maybe gray delete
2171                         for (i=Replace_count+1; i<count; i++) {
2172                                 if (query_operator_argument_type(op, i-1) != query_operator_argument_type(op, i)) {
2173                                         menu.EnableMenuItem(ID_DELETE, MF_GRAYED);
2174                                         break;
2175                                 }
2176                         }
2177
2178                         type = query_operator_argument_type(op, Replace_count); // check argument type at this position
2179
2180                         // special case reset type for ambiguous
2181                         if (type == OPF_AMBIGUOUS) {
2182                                 type = get_ambiguous_type(parent);
2183                         }
2184
2185                         list = get_listing_opf(type, parent, Replace_count);
2186
2187                         // special case dont allow replace data for variable names
2188                         if ( (type != OPF_VARIABLE_NAME) && list) {
2189                                 sexp_list_item *ptr;
2190
2191                                 int data_idx = 0;
2192                                 ptr = list;
2193                                 while (ptr) {
2194                                         if (ptr->op >= 0) {
2195                                                 menu.EnableMenuItem(Operators[ptr->op].value | OP_REPLACE_FLAG, MF_ENABLED);
2196
2197                                         } else {
2198                                                 if ( (data_idx + 3) % 30)
2199                                                         replace_data_menu->AppendMenu(MF_STRING | MF_ENABLED, ID_REPLACE_MENU + data_idx, ptr->text);
2200                                                 else
2201                                                         replace_data_menu->AppendMenu(MF_MENUBARBREAK | MF_STRING | MF_ENABLED, ID_REPLACE_MENU + data_idx, ptr->text);
2202                                         }
2203
2204                                         data_idx++;
2205                                         ptr = ptr->next;
2206                                 }
2207                         }
2208
2209                         if (type == OPF_NONE) {  // takes no arguments
2210                                 replace_type = 0;
2211
2212                         } else if (type == OPF_NUMBER) {  // takes numbers
2213                                 replace_type = OPR_NUMBER;
2214                                 menu.EnableMenuItem(ID_REPLACE_NUMBER, MF_ENABLED);
2215
2216                         } else if (type == OPF_POSITIVE) {  // takes non-negative numbers
2217                                 replace_type = OPR_POSITIVE;
2218                                 menu.EnableMenuItem(ID_REPLACE_NUMBER, MF_ENABLED);
2219
2220                         } else if (type == OPF_BOOL) {  // takes true/false bool values
2221                                 replace_type = OPR_BOOL;
2222
2223                         } else if (type == OPF_NULL) {  // takes operator that doesn't return a value
2224                                 replace_type = OPR_NULL;
2225
2226                         } else if (type == OPF_AI_GOAL) {
2227                                 replace_type = OPR_AI_GOAL;
2228                         }
2229
2230                         // default to string
2231                         if (replace_type == OPR_STRING) {
2232                                 menu.EnableMenuItem(ID_REPLACE_STRING, MF_ENABLED);
2233                         }
2234
2235                         // modify string or number if (modify_variable)
2236                         if ( !stricmp(Operators[op].text, "modify-variable") ) {
2237                                 int modify_type = get_modify_variable_type();
2238
2239                                 if (modify_type == OPF_NUMBER) {
2240                                         menu.EnableMenuItem(ID_REPLACE_NUMBER, MF_ENABLED);
2241                                         menu.EnableMenuItem(ID_REPLACE_STRING, MF_GRAYED);
2242                                 }
2243                                 // no change for string type
2244                         }
2245
2246                         list->destroy();
2247
2248                 } else {  // top node, so should be a Boolean type.
2249                         if (m_mode == MODE_EVENTS) {  // return type should be null
2250                                 replace_type = OPR_NULL;
2251                                 for (j=0; j<Num_operators; j++)
2252                                         if (query_operator_return_type(j) == OPR_NULL)
2253                                                 menu.EnableMenuItem(Operators[j].value | OP_REPLACE_FLAG, MF_ENABLED);
2254
2255                         } else {
2256                                 replace_type = OPR_BOOL;
2257                                 for (j=0; j<Num_operators; j++)
2258                                         if (query_operator_return_type(j) == OPR_BOOL)
2259                                                 menu.EnableMenuItem(Operators[j].value | OP_REPLACE_FLAG, MF_ENABLED);
2260                         }
2261                 }
2262
2263                 for (j=0; j<Num_operators; j++)
2264                         if (!query_default_argument_available(j))
2265                                 menu.EnableMenuItem(Operators[j].value | OP_REPLACE_FLAG, MF_GRAYED);
2266
2267                 // change enabled status of 'insert' type menu options.
2268                 z = nodes[item_index].parent;
2269                 Assert(z >= -1);
2270                 if (z != -1) {
2271                         op = identify_operator(nodes[z].text);
2272                         Assert(op != -1);
2273                         j = nodes[z].child;
2274                         count = 0;
2275                         while (j != item_index) {
2276                                 count++;
2277                                 j = nodes[j].next;
2278                         }
2279
2280                         type = query_operator_argument_type(op, count); // check argument type at this position
2281
2282                 } else {
2283                         if (m_mode == MODE_EVENTS)
2284                                 type = OPF_NULL;
2285                         else
2286                                 type = OPF_BOOL;
2287                 }
2288
2289                 for (j=0; j<Num_operators; j++) {
2290                         z = query_operator_return_type(j);
2291                         if (!sexp_query_type_match(type, z) || (Operators[j].min < 1))
2292                                 menu.EnableMenuItem(Operators[j].value | OP_INSERT_FLAG, MF_GRAYED);
2293
2294                         z = query_operator_argument_type(j, 0);
2295                         if ((type == OPF_NUMBER) && (z == OPF_POSITIVE))
2296                                 z = OPF_NUMBER;
2297
2298                         if (z != type)
2299                                 menu.EnableMenuItem(Operators[j].value | OP_INSERT_FLAG, MF_GRAYED);
2300                 }
2301
2302                 for (j=0; j<Num_operators; j++)
2303                         if (!query_default_argument_available(j))
2304                                 menu.EnableMenuItem(Operators[j].value | OP_INSERT_FLAG, MF_GRAYED);
2305
2306                 // disable non campaign operators if in campaign mode
2307                 for (j=0; j<Num_operators; j++) {
2308                         z = 0;
2309                         if (m_mode == MODE_CAMPAIGN) {
2310                                 if (Operators[j].value & OP_NONCAMPAIGN_FLAG)
2311                                         z = 1;
2312
2313                         } else {
2314                                 if (Operators[j].value & OP_CAMPAIGN_ONLY_FLAG)
2315                                         z = 1;
2316                         }
2317
2318                         if (z) {
2319                                 menu.EnableMenuItem(Operators[j].value, MF_GRAYED);
2320                                 menu.EnableMenuItem(Operators[j].value | OP_REPLACE_FLAG, MF_GRAYED);
2321                                 menu.EnableMenuItem(Operators[j].value | OP_INSERT_FLAG, MF_GRAYED);
2322                         }
2323                 }
2324
2325                 if ((Sexp_clipboard > -1) && (Sexp_nodes[Sexp_clipboard].type != SEXP_NOT_USED)) {
2326                         Assert(Sexp_nodes[Sexp_clipboard].subtype != SEXP_ATOM_LIST);
2327
2328                         if (Sexp_nodes[Sexp_clipboard].subtype == SEXP_ATOM_OPERATOR) {
2329                                 j = find_operator(CTEXT(Sexp_clipboard));
2330                                 Assert(j);
2331                                 z = query_operator_return_type(j);
2332
2333                                 if ((z == OPR_POSITIVE) && (replace_type == OPR_NUMBER))
2334                                         z = OPR_NUMBER;
2335
2336                                 if (replace_type == z)
2337                                         menu.EnableMenuItem(ID_EDIT_PASTE, MF_ENABLED);
2338
2339                                 z = query_operator_return_type(j);
2340                                 if ((z == OPR_POSITIVE) && (add_type == OPR_NUMBER))
2341                                         z = OPR_NUMBER;
2342
2343                                 if (add_type == z)
2344                                         menu.EnableMenuItem(ID_EDIT_PASTE_SPECIAL, MF_ENABLED);
2345
2346                         } else if (Sexp_nodes[Sexp_clipboard].subtype == SEXP_ATOM_NUMBER) {
2347                                 if ((replace_type == OPR_POSITIVE) && (atoi(CTEXT(Sexp_clipboard)) > -1))
2348                                         menu.EnableMenuItem(ID_EDIT_PASTE, MF_ENABLED);
2349
2350                                 else if (replace_type == OPR_NUMBER)
2351                                         menu.EnableMenuItem(ID_EDIT_PASTE, MF_ENABLED);
2352
2353                                 if ((add_type == OPR_POSITIVE) && (atoi(CTEXT(Sexp_clipboard)) > -1))
2354                                         menu.EnableMenuItem(ID_EDIT_PASTE_SPECIAL, MF_ENABLED);
2355
2356                                 else if (add_type == OPR_NUMBER)
2357                                         menu.EnableMenuItem(ID_EDIT_PASTE_SPECIAL, MF_ENABLED);
2358
2359                         } else if (Sexp_nodes[Sexp_clipboard].subtype == SEXP_ATOM_STRING) {
2360                                 if (replace_type == OPR_STRING)
2361                                         menu.EnableMenuItem(ID_EDIT_PASTE, MF_ENABLED);
2362
2363                                 if (add_type == OPR_STRING)
2364                                         menu.EnableMenuItem(ID_EDIT_PASTE_SPECIAL, MF_ENABLED);
2365
2366                         } else
2367                                 Int3();  // unknown and/or invalid sexp type
2368                 }
2369
2370                 if (!(menu.GetMenuState(ID_DELETE, MF_BYCOMMAND) & MF_GRAYED))
2371                         menu.EnableMenuItem(ID_EDIT_CUT, MF_ENABLED);
2372
2373                 gray_menu_tree(popup_menu);
2374                 popup_menu->TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON, mouse.x, mouse.y, this);
2375         }
2376 }
2377
2378 // counts the number of arguments an operator has.  Call this with the node of the first
2379 // argument of the operator
2380 int sexp_tree::count_args(int node)
2381 {
2382         int count = 0;
2383
2384         while (node != -1) {
2385                 count++;
2386                 node = nodes[node].next;
2387         }
2388
2389         return count;
2390 }
2391
2392 // identify what type of argument this is.  You call it with the node of the first argument
2393 // of an operator.  It will search through enough of the arguments to determine what type of
2394 // data they are.
2395 int sexp_tree::identify_arg_type(int node)
2396 {
2397         int type = -1;
2398
2399         while (node != -1) {
2400                 Assert(nodes[node].type & SEXPT_VALID);
2401                 switch (SEXPT_TYPE(nodes[node].type)) {
2402                         case SEXPT_OPERATOR:
2403                                 type = find_operator(nodes[node].text);
2404                                 Assert(type);
2405                                 return query_operator_return_type(type);
2406
2407                         case SEXPT_NUMBER:
2408                                 return OPR_NUMBER;
2409
2410                         case SEXPT_STRING:  // either a ship or a wing
2411                                 type = SEXP_ATOM_STRING;
2412                                 break;  // don't return, because maybe we can narrow selection down more.
2413                 }
2414
2415                 node = nodes[node].next;
2416         }
2417
2418         return type;
2419 }
2420
2421 // determine if an item should be editable.  This doesn't actually edit the label.
2422 int sexp_tree::edit_label(HTREEITEM h)
2423 {
2424         int i;
2425
2426         for (i=0; i<MAX_SEXP_TREE_SIZE; i++) {
2427                 if (nodes[i].handle == h) {
2428                         break;
2429                 }
2430         }
2431
2432         // Check if tree root
2433         if (i == MAX_SEXP_TREE_SIZE) {
2434                 if (m_mode & ST_ROOT_EDITABLE) {
2435                         return 1;
2436                 }
2437
2438                 return 0;
2439         }
2440
2441         // Operators are editable
2442         if (nodes[i].type & SEXPT_OPERATOR) {
2443                 return 1;
2444         }
2445
2446         // Variables must be edited through dialog box
2447         if (nodes[i].type & SEXPT_VARIABLE) {
2448                 return 0;
2449         }
2450
2451         // Don't edit if not flaged as editable
2452         if (!(nodes[i].flags & EDITABLE)) {
2453                 return 0;
2454         }
2455
2456         // Otherwise, allow editing
2457         return 1;
2458
2459 /*
2460         if (nodes[i].flags & OPERAND) {
2461                 data = nodes[i].child;
2462
2463                 SetItemText(h, nodes[i].text);
2464                 nodes[i].flags = OPERAND;
2465                 item_handle = nodes[data].handle = insert(nodes[data].text, nodes[data].type, nodes[data].flags, h);
2466                 nodes[data].flags = EDITABLE;
2467                 Expand(h, TVE_EXPAND);
2468                 SelectItem(item_handle);
2469                 return 2;
2470         }
2471 */
2472 }
2473
2474 int sexp_tree::end_label_edit(HTREEITEM h, char *str)
2475 {
2476         int len, node, r = 1;
2477
2478         *modified = 1;
2479         if (!str)
2480                 return 0;
2481
2482         for (node=0; node<MAX_SEXP_TREE_SIZE; node++)
2483                 if (nodes[node].handle == h)
2484                         break;
2485
2486         if (node == MAX_SEXP_TREE_SIZE) {
2487                 if (m_mode == MODE_EVENTS) {
2488                         item_index = GetItemData(h);
2489                         Assert(Event_editor_dlg);
2490                         node = Event_editor_dlg->handler(ROOT_RENAMED, item_index, str);
2491                         return 1;
2492
2493                 } else
2494                         Int3();  // root labels shouldn't have been editable!
2495         }
2496
2497         Assert(node < MAX_SEXP_TREE_SIZE);
2498         if (nodes[node].type & SEXPT_OPERATOR) {
2499                 str = match_closest_operator(str, node);
2500                 SetItemText(h, str);
2501                 item_index = node;
2502                 add_or_replace_operator(identify_operator(str), 1);
2503                 r = 0;
2504         }
2505
2506         // Error checking would not hurt here
2507         len = strlen(str);
2508         if (len >= TOKEN_LENGTH)
2509                 len = TOKEN_LENGTH - 1;
2510
2511         strncpy(nodes[node].text, str, len);
2512         nodes[node].text[len] = 0;
2513
2514 /*      node = nodes[node].parent;
2515         if (node != -1) {
2516                 child = nodes[node].child;
2517                 if (child != -1 && nodes[child].next == -1 && nodes[child].child == -1) {
2518                         merge_operator(child);
2519                         return 0;
2520                 }
2521         }*/
2522
2523         return r;
2524 }
2525
2526 // Check if 'op' is a valid operator match for operator format (OPF) 'type'.  Returns true if it
2527 // is, false if not.
2528 //
2529 int sexp_tree::check_operator_validity(int op, int type)
2530 {
2531         int rtype;
2532
2533         rtype = query_operator_return_type(op);
2534         switch (type) {
2535                 case OPF_NUMBER:
2536                         return ( (rtype == OPR_NUMBER) || (rtype == OPR_POSITIVE) );
2537
2538                 case OPF_POSITIVE:
2539                         return (rtype == OPR_POSITIVE);
2540
2541                 case OPF_BOOL:
2542                         return (rtype == OPR_BOOL);
2543
2544                 case OPF_NULL:
2545                         return (rtype == OPR_NULL);
2546
2547                 case OPF_AI_GOAL:
2548                         return (rtype == OPR_AI_GOAL);
2549         }
2550
2551         return 0;
2552 }
2553
2554 // Look for the valid operator that is the closest match for 'str' and return the operator
2555 // number of it.  What operators are valid is determined by 'node', and an operator is valid
2556 // if it is allowed to fit at position 'node'
2557 //
2558 char *sexp_tree::match_closest_operator(char *str, int node)
2559 {
2560         int z, n, i, op, arg_num, type;
2561         char *sub_best = NULL, *best = NULL;
2562
2563         z = nodes[node].parent;
2564         if (z < 0) {
2565                 return str;
2566         }
2567
2568         op = identify_operator(nodes[z].text);
2569         if (op < 0)
2570                 return str;
2571
2572         // determine which argument we are of the parent
2573         arg_num = 0;
2574         n = nodes[z].child;
2575         while (n != node) {
2576                 Assert(n >= 0);
2577                 arg_num++;
2578                 n = nodes[n].next;
2579         }
2580
2581         type = query_operator_argument_type(op, arg_num); // check argument type at this position
2582         for (i=0; i<Num_operators; i++) {
2583                 if (check_operator_validity(i, type)) {
2584                         if ( (stricmp(str, Operators[i].text) <= 0) && (!best || (stricmp(str, best) < 0)) )
2585                                 best = Operators[i].text;
2586                         
2587                         if ( !sub_best || (stricmp(Operators[i].text, sub_best) > 0) )
2588                                 sub_best = Operators[i].text;
2589                 }
2590         }
2591
2592         if (!best)
2593                 best = sub_best;  // no best found, use our plan #2 best found.
2594
2595         Assert(best);  // we better have some valid operator at this point.
2596         return best;
2597
2598 /*      char buf[256];
2599         int child;
2600
2601         if (nodes[node].flags == EDITABLE)  // data
2602                 node = nodes[node].parent;
2603
2604         if (node != -1) {
2605                 child = nodes[node].child;
2606                 if (child != -1 && nodes[child].next == -1 && nodes[child].child == -1) {
2607                         sprintf(buf, "%s %s", nodes[node].text, nodes[child].text);
2608                         SetItemText(nodes[node].handle, buf);
2609                         nodes[node].flags = OPERAND | EDITABLE;
2610                         nodes[child].flags = COMBINED;
2611                         DeleteItem(nodes[child].handle);
2612                         nodes[child].handle = NULL;
2613                         return;
2614                 }
2615         }*/
2616 }
2617
2618 // this really only handles messages generated by the right click popup menu
2619 BOOL sexp_tree::OnCommand(WPARAM wParam, LPARAM lParam)
2620 {
2621         int i, z, id, node, op, type;
2622         sexp_list_item *list, *ptr;
2623         HTREEITEM h;
2624
2625         if ((item_index >= 0) && (item_index < total))
2626                 item_handle = nodes[item_index].handle;
2627
2628         id = LOWORD(wParam);
2629
2630
2631         // Add variable
2632         if (id == ID_SEXP_TREE_ADD_VARIABLE) {
2633                 CAddVariableDlg dlg;
2634                 dlg.DoModal();
2635
2636                 if ( dlg.m_create ) {
2637
2638                         // set type
2639                         int type;
2640                         if ( dlg.m_type_number ) {
2641                                 type = SEXP_VARIABLE_NUMBER;
2642                         } else {
2643                                 type = SEXP_VARIABLE_STRING;
2644                         }
2645
2646                         // add variable
2647                         sexp_add_variable(dlg.m_default_value, dlg.m_variable_name, type);
2648
2649                         // sort variable
2650                         sexp_variable_sort();
2651                 }
2652                 return 1;
2653         }
2654
2655         // Modify variable
2656         if (id == ID_SEXP_TREE_MODIFY_VARIABLE) {
2657                 CModifyVariableDlg dlg;
2658
2659                 // get sexp_variable index for item index
2660                 dlg.m_start_index = get_item_index_to_var_index();
2661
2662                 // get pointer to tree
2663                 dlg.m_p_sexp_tree = this;
2664
2665                 dlg.DoModal();
2666
2667                 Assert( !(dlg.m_deleted && dlg.m_do_modify) );
2668
2669                 if (dlg.m_deleted) {
2670                         // find index in sexp_variable list
2671                         int sexp_var_index = get_index_sexp_variable_name(dlg.m_cur_variable_name);
2672                         Assert(sexp_var_index != -1);
2673
2674                         // delete from list
2675                         sexp_variable_delete(sexp_var_index);
2676
2677                         // sort list
2678                         sexp_variable_sort();
2679
2680                         // delete from sexp_tree, replacing with "number" or "string" as needed
2681                         // further error checking from add_data()
2682                         delete_sexp_tree_variable(dlg.m_cur_variable_name);
2683
2684                         return 1;
2685                 }
2686
2687                 if (dlg.m_do_modify) {
2688                         // check sexp_tree -- warn on type
2689                         // find index and change either (1) name, (2) type, (3) value
2690                         int sexp_var_index = get_index_sexp_variable_name(dlg.m_old_var_name);
2691                         Assert(sexp_var_index != -1);
2692
2693                         // save old name, since name may be modified
2694                         char old_name[TOKEN_LENGTH];
2695                         strcpy(old_name, Sexp_variables[sexp_var_index].variable_name);
2696
2697                         // set type
2698                         int type;
2699                         if (dlg.m_type_number) {
2700                                 type = SEXP_VARIABLE_NUMBER;
2701                         } else {
2702                                 type = SEXP_VARIABLE_STRING;
2703                         }
2704
2705                         // update sexp_variable
2706                         sexp_fred_modify_variable(dlg.m_default_value, dlg.m_cur_variable_name, sexp_var_index, type);
2707
2708                         // modify sexp_tree
2709                         modify_sexp_tree_variable(old_name, sexp_var_index);
2710
2711                         // Don't sort until after modify, since modify uses index
2712                         if (dlg.m_modified_name) {
2713                                 sexp_variable_sort();
2714                         }
2715
2716                         return 1;
2717                 }
2718
2719                 // no change
2720                 return 1;
2721         }
2722
2723
2724         // check if REPLACE_VARIABLE_MENU
2725         if ( (id >= ID_VARIABLE_MENU) && (id < ID_VARIABLE_MENU + 511)) {
2726
2727                 Assert(item_index >= 0);
2728
2729                 // get index into list of type valid variables
2730                 int var_idx = id - ID_VARIABLE_MENU;
2731                 Assert( (var_idx >= 0) && (var_idx < MAX_SEXP_VARIABLES) );
2732
2733                 int type = get_type(item_handle);
2734                 Assert( (type & SEXPT_NUMBER) || (type & SEXPT_STRING) );
2735
2736                 // dont do type check for modify-variable
2737                 if (Modify_variable) {
2738                         if (Sexp_variables[var_idx].type & SEXP_VARIABLE_NUMBER) {
2739                                 type = SEXPT_NUMBER;
2740                         } else if (Sexp_variables[var_idx].type & SEXP_VARIABLE_STRING) {
2741                                 type = SEXPT_STRING;
2742                         } else {
2743                                 Int3(); // unknown type
2744                         }
2745
2746                 } else {
2747         
2748                         // verify type in tree is same as type in Sexp_variables array
2749                         if (type & SEXPT_NUMBER) {
2750                                 Assert(Sexp_variables[var_idx].type & SEXP_VARIABLE_NUMBER);
2751                         }
2752
2753                         if (type & SEXPT_STRING) {
2754                                 Assert( (Sexp_variables[var_idx].type & SEXP_VARIABLE_STRING) );
2755                         }
2756                 }
2757
2758                 // Replace data
2759                 replace_variable_data(var_idx, (type | SEXPT_VARIABLE));
2760
2761                 return 1;
2762         }
2763
2764
2765         if ((id >= ID_ADD_MENU) && (id < ID_ADD_MENU + 511)) {
2766                 Assert(item_index >= 0);
2767                 op = identify_operator(nodes[item_index].text);
2768                 Assert(op >= 0);
2769
2770                 type = query_operator_argument_type(op, Add_count);
2771                 list = get_listing_opf(type, item_index, Add_count);
2772                 Assert(list);
2773
2774                 id -= ID_ADD_MENU;
2775                 ptr = list;
2776                 while (id) {
2777                         id--;
2778                         ptr = ptr->next;
2779                         Assert(ptr);
2780                 }
2781
2782                 Assert((SEXPT_TYPE(ptr->type) != SEXPT_OPERATOR) && (ptr->op < 0));
2783                 expand_operator(item_index);
2784                 add_data(ptr->text, ptr->type);
2785                 list->destroy();
2786                 return 1;
2787         }
2788
2789         if ((id >= ID_REPLACE_MENU) && (id < ID_REPLACE_MENU + 511)) {
2790                 Assert(item_index >= 0);
2791                 Assert(nodes[item_index].parent >= 0);
2792                 op = identify_operator(nodes[nodes[item_index].parent].text);
2793                 Assert(op >= 0);
2794
2795                 type = query_operator_argument_type(op, Replace_count); // check argument type at this position
2796                 list = get_listing_opf(type, nodes[item_index].parent, Replace_count);
2797                 Assert(list);
2798
2799                 id -= ID_REPLACE_MENU;
2800                 ptr = list;
2801                 while (id) {
2802                         id--;
2803                         ptr = ptr->next;
2804                         Assert(ptr);
2805                 }
2806
2807                 Assert((SEXPT_TYPE(ptr->type) != SEXPT_OPERATOR) && (ptr->op < 0));
2808                 expand_operator(item_index);
2809                 replace_data(ptr->text, ptr->type);
2810                 list->destroy();
2811                 return 1;
2812         }
2813
2814         for (op=0; op<Num_operators; op++) {
2815                 if (id == Operators[op].value) {
2816                         add_or_replace_operator(op);
2817                         return 1;
2818                 }
2819
2820                 if (id == (Operators[op].value | OP_REPLACE_FLAG)) {
2821                         add_or_replace_operator(op, 1);
2822                         return 1;
2823                 }
2824
2825                 if (id == (Operators[op].value | OP_INSERT_FLAG)) {
2826                         int flags;
2827
2828                         z = nodes[item_index].parent;
2829                         flags = nodes[item_index].flags;
2830                         node = allocate_node(z, item_index);
2831                         set_node(node, (SEXPT_OPERATOR | SEXPT_VALID), Operators[op].text);
2832                         nodes[node].flags = flags;
2833                         if (z >= 0)
2834                                 h = nodes[z].handle;
2835
2836                         else {
2837                                 h = GetParentItem(nodes[item_index].handle);
2838                                 if (m_mode == MODE_GOALS) {
2839                                         Assert(Goal_editor_dlg);
2840                                         Goal_editor_dlg->insert_handler(item_index, node);
2841                                         SetItemData(h, node);
2842
2843                                 } else if (m_mode == MODE_EVENTS) {
2844                                         Assert(Event_editor_dlg);
2845                                         Event_editor_dlg->insert_handler(item_index, node);
2846                                         SetItemData(h, node);
2847
2848                                 } else if (m_mode == MODE_CAMPAIGN) {
2849                                         Campaign_tree_formp->insert_handler(item_index, node);
2850                                         SetItemData(h, node);
2851
2852                                 } else {
2853                                         h = TVI_ROOT;
2854                                         root_item = node;
2855                                 }
2856                         }
2857
2858                         item_handle = nodes[node].handle = insert(Operators[op].text, BITMAP_OPERATOR, BITMAP_OPERATOR, h, nodes[item_index].handle);
2859                         move_branch(item_index, node);
2860
2861                         item_index = node;
2862                         for (i=1; i<Operators[op].min; i++)
2863                                 add_default_operator(op, i);
2864
2865                         Expand(item_handle, TVE_EXPAND);
2866                         *modified = 1;
2867                         return 1;
2868                 }
2869         }
2870
2871         switch (id) {
2872                 case ID_EDIT_COPY:
2873                         // If a clipboard already exist, unmark it as persistent and free old clipboard
2874                         if (Sexp_clipboard != -1) {
2875                                 sexp_unmark_persistent(Sexp_clipboard);
2876                                 free_sexp2(Sexp_clipboard);
2877                         }
2878
2879                         // Allocate new clipboard and mark persistent
2880                         Sexp_clipboard = save_branch(item_index, 1);
2881                         sexp_mark_persistent(Sexp_clipboard);
2882                         return 1;
2883
2884                 case ID_EDIT_PASTE:
2885                         // the following assumptions are made..
2886                         Assert((Sexp_clipboard > -1) && (Sexp_nodes[Sexp_clipboard].type != SEXP_NOT_USED));
2887                         Assert(Sexp_nodes[Sexp_clipboard].subtype != SEXP_ATOM_LIST);
2888
2889                         if (Sexp_nodes[Sexp_clipboard].subtype == SEXP_ATOM_OPERATOR) {
2890                                 expand_operator(item_index);
2891                                 replace_operator(CTEXT(Sexp_clipboard));
2892                                 if (Sexp_nodes[Sexp_clipboard].rest != -1) {
2893                                         load_branch(Sexp_nodes[Sexp_clipboard].rest, item_index);
2894                                         i = nodes[item_index].child;
2895                                         while (i != -1) {
2896                                                 add_sub_tree(i, nodes[item_index].handle);
2897                                                 i = nodes[i].next;
2898                                         }
2899                                 }
2900
2901                         } else if (Sexp_nodes[Sexp_clipboard].subtype == SEXP_ATOM_NUMBER) {
2902                                 Assert(Sexp_nodes[Sexp_clipboard].rest == -1);
2903                                 expand_operator(item_index);
2904                                 replace_data(CTEXT(Sexp_clipboard), (SEXPT_NUMBER | SEXPT_VALID));
2905
2906                         } else if (Sexp_nodes[Sexp_clipboard].subtype == SEXP_ATOM_STRING) {
2907                                 Assert(Sexp_nodes[Sexp_clipboard].rest == -1);
2908                                 expand_operator(item_index);
2909                                 replace_data(CTEXT(Sexp_clipboard), (SEXPT_STRING | SEXPT_VALID));
2910
2911                         } else
2912                                 Assert(0);  // unknown and/or invalid sexp type
2913
2914                         return 1;
2915
2916                 case ID_EDIT_PASTE_SPECIAL:  // add paste, instead of replace.
2917                         // the following assumptions are made..
2918                         Assert((Sexp_clipboard > -1) && (Sexp_nodes[Sexp_clipboard].type != SEXP_NOT_USED));
2919                         Assert(Sexp_nodes[Sexp_clipboard].subtype != SEXP_ATOM_LIST);
2920
2921                         if (Sexp_nodes[Sexp_clipboard].subtype == SEXP_ATOM_OPERATOR) {
2922                                 expand_operator(item_index);
2923                                 add_operator(CTEXT(Sexp_clipboard));
2924                                 if (Sexp_nodes[Sexp_clipboard].rest != -1) {
2925                                         load_branch(Sexp_nodes[Sexp_clipboard].rest, item_index);
2926                                         i = nodes[item_index].child;
2927                                         while (i != -1) {
2928                                                 add_sub_tree(i, nodes[item_index].handle);
2929                                                 i = nodes[i].next;
2930                                         }
2931                                 }
2932
2933                         } else if (Sexp_nodes[Sexp_clipboard].subtype == SEXP_ATOM_NUMBER) {
2934                                 Assert(Sexp_nodes[Sexp_clipboard].rest == -1);
2935                                 expand_operator(item_index);
2936                                 add_data(CTEXT(Sexp_clipboard), (SEXPT_NUMBER | SEXPT_VALID));
2937
2938                         } else if (Sexp_nodes[Sexp_clipboard].subtype == SEXP_ATOM_STRING) {
2939                                 Assert(Sexp_nodes[Sexp_clipboard].rest == -1);
2940                                 expand_operator(item_index);
2941                                 add_data(CTEXT(Sexp_clipboard), (SEXPT_STRING | SEXPT_VALID));
2942
2943                         } else
2944                                 Assert(0);  // unknown and/or invalid sexp type
2945
2946                         return 1;
2947
2948 /*              case ID_SPLIT_LINE:
2949                         if ((nodes[item_index].flags & OPERAND) && (nodes[item_index].flags & EDITABLE))  // expandable?
2950                                 expand_operator(item_index);
2951                         else
2952                                 merge_operator(item_index);
2953
2954                         return 1;*/
2955
2956                 case ID_EXPAND_ALL:
2957                         expand_branch(item_handle);
2958                         return 1;
2959
2960                 case ID_EDIT_TEXT:
2961                         if (edit_label(item_handle)) {
2962                                 *modified = 1;
2963                                 EditLabel(item_handle);
2964                         }
2965
2966                         return 1;
2967
2968                 case ID_ADD_STRING:     {
2969                         int node;
2970                         
2971                         node = add_data("string", (SEXPT_STRING | SEXPT_VALID));
2972                         EditLabel(nodes[node].handle);
2973                         return 1;
2974                 }
2975
2976                 case ID_ADD_NUMBER:     {
2977                         int node;
2978
2979                         node = add_data("number", (SEXPT_NUMBER | SEXPT_VALID));
2980                         EditLabel(nodes[node].handle);
2981                         return 1;
2982                 }
2983
2984                 case ID_EDIT_CUT:
2985                         if (Sexp_clipboard != -1) {
2986                                 sexp_unmark_persistent(Sexp_clipboard);
2987                                 free_sexp2(Sexp_clipboard);
2988                         }
2989
2990                         Sexp_clipboard = save_branch(item_index, 1);
2991                         sexp_mark_persistent(Sexp_clipboard);
2992                         // fall through to ID_DELETE case.
2993
2994                 case ID_DELETE: {
2995                         int parent, node;
2996                         HTREEITEM h;
2997
2998                         if ((m_mode & ST_ROOT_DELETABLE) && (item_index == -1)) {
2999                                 item_index = GetItemData(item_handle);
3000                                 if (m_mode == MODE_GOALS) {
3001                                         Assert(Goal_editor_dlg);
3002                                         node = Goal_editor_dlg->handler(ROOT_DELETED, item_index);
3003
3004                                 } else if (m_mode == MODE_EVENTS) {
3005                                         Assert(Event_editor_dlg);
3006                                         node = Event_editor_dlg->handler(ROOT_DELETED, item_index);
3007
3008                                 } else {
3009                                         Assert(m_mode == MODE_CAMPAIGN);
3010                                         node = Campaign_tree_formp->handler(ROOT_DELETED, item_index);
3011                                 }
3012
3013                                 Assert(node >= 0);
3014                                 free_node2(node);
3015                                 DeleteItem(item_handle);
3016                                 *modified = 1;
3017                                 return 1;
3018                         }
3019
3020                         Assert(item_index >= 0);
3021                         h = GetParentItem(item_handle);
3022                         parent = nodes[item_index].parent;
3023                         if ((parent == -1) && (m_mode == MODE_EVENTS))
3024                                 Int3();  // no longer used, temporary to check if called still.
3025
3026                         Assert(parent != -1 && nodes[parent].handle == h);
3027                         free_node(item_index);
3028                         DeleteItem(item_handle);
3029
3030                         node = nodes[parent].child;
3031 /*                      if (node != -1 && nodes[node].next == -1 && nodes[node].child == -1) {
3032                                 sprintf(buf, "%s %s", nodes[parent].text, nodes[node].text);
3033                                 SetItem(h, TVIF_TEXT, buf, 0, 0, 0, 0, 0);
3034                                 nodes[parent].flags = OPERAND | EDITABLE;
3035                                 nodes[node].flags = COMBINED;
3036                                 DeleteItem(nodes[node].handle);
3037                         }*/
3038
3039                         *modified = 1;
3040                         return 1;
3041                 }
3042         }
3043         
3044         return CTreeCtrl::OnCommand(wParam, lParam);
3045 }
3046
3047 // adds to or replaces (based on passed in flag) the current operator
3048 void sexp_tree::add_or_replace_operator(int op, int replace_flag)
3049 {
3050         int i, op_index, op2;
3051
3052         op_index = item_index;
3053         if (replace_flag) {
3054                 if (nodes[item_index].flags & OPERAND) {  // are both operators?
3055                         op2 = identify_operator(nodes[item_index].text);
3056                         Assert(op2 >= 0);
3057                         i = count_args(nodes[item_index].child);
3058                         if ((i >= Operators[op].min) && (i <= Operators[op].max)) {  // are old num args valid?
3059                                 while (i--)
3060                                         if (query_operator_argument_type(op2, i) != query_operator_argument_type(op, i))  // does each arg match expected type?
3061                                                 break;
3062
3063                                 if (i < 0) {  // everything is ok, so we can keep old arguments with new operator
3064                                         set_node(item_index, (SEXPT_OPERATOR | SEXPT_VALID), Operators[op].text);
3065                                         SetItemText(nodes[item_index].handle, Operators[op].text);
3066                                         nodes[item_index].flags = OPERAND;
3067                                         return;
3068                                 }
3069                         }
3070                 }
3071
3072                 replace_operator(Operators[op].text);
3073
3074         } else
3075                 add_operator(Operators[op].text);
3076
3077         // fill in all the required (minimum) arguments with default values
3078         for (i=0; i<Operators[op].min; i++)
3079                 add_default_operator(op, i);
3080
3081         Expand(item_handle, TVE_EXPAND);
3082 }
3083
3084 // initialize node, type operator
3085 //
3086 void sexp_list_item::set_op(int op_num)
3087 {
3088         int i;
3089
3090         if (op_num >= FIRST_OP) {  // do we have an op value instead of an op number (index)?
3091                 for (i=0; i<Num_operators; i++)
3092                         if (op_num == Operators[i].value)
3093                                 op_num = i;  // convert op value to op number
3094         }
3095
3096         op = op_num;
3097         text = Operators[op].text;
3098         type = (SEXPT_OPERATOR | SEXPT_VALID);
3099 }
3100
3101 // initialize node, type data
3102 // Defaults: t = SEXPT_STRING
3103 //
3104 void sexp_list_item::set_data(char *str, int t)
3105 {
3106         op = -1;
3107         text = str;
3108         type = t;
3109 }
3110
3111 // add a node to end of list
3112 //
3113 void sexp_list_item::add_op(int op_num)
3114 {
3115         sexp_list_item *item, *ptr;
3116
3117         item = new sexp_list_item;
3118         ptr = this;
3119         while (ptr->next)
3120                 ptr = ptr->next;
3121
3122         ptr->next = item;
3123         item->set_op(op_num);
3124 }
3125
3126 // add a node to end of list
3127 // Defaults: t = SEXPT_STRING
3128 //
3129 void sexp_list_item::add_data(char *str, int t)
3130 {
3131         sexp_list_item *item, *ptr;
3132
3133         item = new sexp_list_item;
3134         ptr = this;
3135         while (ptr->next)
3136                 ptr = ptr->next;
3137
3138         ptr->next = item;
3139         item->set_data(str, t);
3140 }
3141
3142 // add a node to end of list, allocating memory for the text
3143 // Defaults: t = SEXPT_STRING
3144 //
3145 void sexp_list_item::add_data_dup(char *str, int t)
3146 {
3147         sexp_list_item *item, *ptr;
3148
3149         item = new sexp_list_item;
3150         ptr = this;
3151         while (ptr->next)
3152                 ptr = ptr->next;
3153
3154         ptr->next = item;
3155         item->set_data(strdup(str), t);
3156         item->flags |= SEXP_ITEM_F_DUP;
3157 }
3158
3159 // add an sexp list to end of another list (join lists)
3160 //
3161 void sexp_list_item::add_list(sexp_list_item *list)
3162 {
3163         sexp_list_item *ptr;
3164
3165         ptr = this;
3166         while (ptr->next)
3167                 ptr = ptr->next;
3168
3169         ptr->next = list;
3170 }
3171
3172 // free all nodes of list
3173 //
3174 void sexp_list_item::destroy()
3175 {
3176         sexp_list_item *ptr, *ptr2;
3177
3178         ptr = this;
3179         while (ptr) {
3180                 ptr2 = ptr->next;
3181                 if (ptr->flags & SEXP_ITEM_F_DUP)
3182                         free(ptr->text);
3183
3184                 delete ptr;
3185                 ptr = ptr2;
3186         }
3187 }
3188
3189 int sexp_tree::add_default_operator(int op, int argnum)
3190 {
3191         char buf[256];
3192         int index;
3193         sexp_list_item item;
3194         HTREEITEM h;
3195
3196         h = item_handle;
3197         index = item_index;
3198         item.text = buf;
3199         if (get_default_value(&item, op, argnum))
3200                 return -1;
3201
3202         if (item.type & SEXPT_OPERATOR) {
3203                 Assert((item.op >= 0) && (item.op < Num_operators));
3204                 add_or_replace_operator(item.op);
3205                 item_index = index;
3206                 item_handle = h;
3207
3208         } else {
3209                 // special case for modify-variable (data added 1st arg is variable)
3210                 if ( !stricmp(Operators[op].text, "modify-variable") ) {
3211                         if (argnum == 0) {
3212
3213                                 int sexp_var_index = get_index_sexp_variable_name(item.text);
3214                                 Assert(sexp_var_index != -1);
3215                                 int type = SEXPT_VALID | SEXPT_VARIABLE;
3216                                 if (Sexp_variables[sexp_var_index].type & SEXP_VARIABLE_STRING) {
3217                                         type |= SEXPT_STRING;
3218                                 } else if (Sexp_variables[sexp_var_index].type & SEXP_VARIABLE_NUMBER) {
3219                                         type |= SEXPT_NUMBER;
3220                                 } else {
3221                                         Int3();
3222                                 }
3223
3224                                 char node_text[2*TOKEN_LENGTH + 2];
3225                                 sprintf(node_text, "%s(%s)", item.text, Sexp_variables[sexp_var_index].text);
3226                                 add_variable_data(node_text, type);
3227                         } else {
3228                                 // the the variable name
3229                                 char buf2[256];
3230                                 Assert(argnum == 1);
3231                                 sexp_list_item temp_item;
3232                                 temp_item.text = buf2;
3233                                 get_default_value(&temp_item, op, 0);
3234                                 int sexp_var_index = get_index_sexp_variable_name(temp_item.text);
3235                                 Assert(sexp_var_index != -1);
3236
3237                                 // from name get type
3238                                 int temp_type = Sexp_variables[sexp_var_index].type;
3239                                 int type;
3240                                 if (temp_type & SEXP_VARIABLE_NUMBER) {
3241                                         type = SEXPT_VALID | SEXPT_NUMBER;
3242                                 } else if (temp_type & SEXP_VARIABLE_STRING) {
3243                                         type = SEXPT_VALID | SEXPT_STRING;
3244                                 } else {
3245                                         Int3();
3246                                 }
3247                                 add_data(item.text, type);
3248                         }
3249                 } else {
3250                         add_data(item.text, item.type);
3251                 }
3252         }
3253
3254         return 0;
3255 }
3256
3257 int sexp_tree::get_default_value(sexp_list_item *item, int op, int i)
3258 {
3259         char *str = NULL;
3260         int type, index;
3261         sexp_list_item *list;
3262         HTREEITEM h;
3263
3264         h = item_handle;
3265         index = item_index;
3266         type = query_operator_argument_type(op, i);
3267         switch (type) {
3268                 case OPF_NULL:
3269                         item->set_op(OP_NOP);
3270                         return 0;
3271
3272                 case OPF_BOOL:
3273                         item->set_op(OP_TRUE);
3274                         return 0;
3275
3276                 case OPF_NUMBER:
3277                 case OPF_POSITIVE:
3278                 case OPF_AMBIGUOUS:
3279                         // if the top level operators is an AI goal, and we are adding the last number required,
3280                         // assume that this number is a priority and make it 89 instead of 1.
3281                         if ((query_operator_return_type(op) == OPR_AI_GOAL) && (i == (Operators[op].min - 1)))
3282                                 item->set_data("89", (SEXPT_NUMBER | SEXPT_VALID));
3283                         else if (((Operators[op].value == OP_HAS_DOCKED_DELAY) || (Operators[op].value == OP_HAS_UNDOCKED_DELAY)) && (i == 2))
3284                                 item->set_data("1", (SEXPT_NUMBER | SEXPT_VALID));
3285                         else if ( (Operators[op].value == OP_SHIP_TYPE_DESTROYED) || (Operators[op].value == OP_GOOD_SECONDARY_TIME) )
3286                                 item->set_data("100", (SEXPT_NUMBER | SEXPT_VALID));
3287                         else
3288                                 item->set_data("0", (SEXPT_NUMBER | SEXPT_VALID));
3289
3290                         return 0;
3291         }
3292
3293         list = get_listing_opf(type, index, i);
3294         if (list) {
3295                 char *ptr;
3296
3297                 ptr = item->text;
3298                 *item = *list;
3299                 item->text = ptr;
3300                 strcpy(item->text, list->text);
3301
3302                 list->destroy();
3303                 return 0;
3304         }
3305
3306         // catch anything that doesn't have a default value.  Just describe what should be here instead
3307         switch (type) {
3308                 case OPF_SHIP:
3309                 case OPF_SHIP_NOT_PLAYER:
3310                 case OPF_SHIP_WING:
3311                 case OPF_SHIP_POINT:
3312                 case OPF_SHIP_WING_POINT:
3313                         str = "<name of ship here>";
3314                         break;
3315
3316                 case OPF_WING:
3317                         str = "<name of wing here>";
3318                         break;
3319
3320                 case OPF_DOCKER_POINT:
3321                         str = "<docker point>";
3322                         break;
3323
3324                 case OPF_DOCKEE_POINT:
3325                         str = "<dockee point>";
3326                         break;
3327
3328                 case OPF_SUBSYSTEM:
3329                 case OPF_AWACS_SUBSYSTEM:
3330                         str = "<name of subsystem>";
3331                         break;
3332
3333                 case OPF_POINT:
3334                         str = "<waypoint>";
3335                         break;
3336
3337                 case OPF_MESSAGE:
3338                         str = "<Message>";
3339                         break;
3340
3341                 case OPF_WHO_FROM:
3342                         //str = "<any allied>";
3343                         str = "<any wingman>";
3344                         break;
3345                         
3346                 case OPF_WAYPOINT_PATH:
3347                         str = "<waypoint path>";
3348                         break;
3349
3350                 case OPF_MISSION_NAME:
3351                         str = "<mission name>";
3352                         break;
3353
3354                 case OPF_GOAL_NAME:
3355                         str = "<goal name>";
3356                         break;
3357
3358                 case OPF_SHIP_TYPE:
3359                         str = "<ship type here>";
3360                         break;
3361
3362                 case OPF_EVENT_NAME:
3363                         str = "<event name>";
3364                         break;
3365
3366                 case OPF_HUGE_WEAPON:
3367                         str = "<huge weapon type>";
3368                         break;
3369
3370                 case OPF_JUMP_NODE_NAME:
3371                         str = "<Jump node name>";
3372                         break;
3373
3374                 default:
3375                         str = "<new default required!>";
3376                         break;
3377         }
3378
3379         item->set_data(str, (SEXPT_STRING | SEXPT_VALID));
3380         return 0;
3381 }
3382
3383 int sexp_tree::query_default_argument_available(int op)
3384 {
3385         int i;
3386
3387         Assert(op >= 0);
3388         for (i=0; i<Operators[op].min; i++)
3389                 if (!query_default_argument_available(op, i))
3390                         return 0;
3391
3392         return 1;
3393 }
3394
3395 int sexp_tree::query_default_argument_available(int op, int i)
3396 {       
3397         int j, type;
3398         object *ptr;
3399
3400         type = query_operator_argument_type(op, i);
3401         switch (type) {
3402                 case OPF_NULL:
3403                 case OPF_BOOL:
3404                 case OPF_NUMBER:
3405                 case OPF_POSITIVE:
3406                 case OPF_IFF:
3407                 case OPF_WHO_FROM:
3408                 case OPF_PRIORITY:
3409                 case OPF_SHIP_TYPE:
3410                 case OPF_SUBSYSTEM:             
3411                 case OPF_AWACS_SUBSYSTEM:
3412                 case OPF_DOCKER_POINT:
3413                 case OPF_DOCKEE_POINT:
3414                 case OPF_AI_GOAL:
3415                 case OPF_KEYPRESS:
3416                 case OPF_AI_ORDER:
3417                 case OPF_SKILL_LEVEL:
3418                 case OPF_MEDAL_NAME:
3419                 case OPF_WEAPON_NAME:
3420                 case OPF_SHIP_CLASS_NAME:
3421                 case OPF_HUD_GAUGE_NAME:
3422                 case OPF_HUGE_WEAPON:
3423                 case OPF_JUMP_NODE_NAME:
3424                 case OPF_AMBIGUOUS:
3425                         return 1;
3426
3427                 case OPF_SHIP:
3428                 case OPF_SHIP_NOT_PLAYER:
3429                 case OPF_SHIP_WING:
3430                 case OPF_SHIP_POINT:
3431                 case OPF_SHIP_WING_POINT:
3432                         ptr = GET_FIRST(&obj_used_list);
3433                         while (ptr != END_OF_LIST(&obj_used_list)) {
3434                                 if (ptr->type == OBJ_SHIP)
3435                                         return 1;
3436
3437                                 ptr = GET_NEXT(ptr);
3438                         }
3439
3440                         return 0;
3441
3442                 case OPF_WING:
3443                         for (j=0; j<MAX_WINGS; j++)
3444                                 if (Wings[j].wave_count)
3445                                         return 1;
3446
3447                         return 0;
3448
3449                 case OPF_POINT:
3450                 case OPF_WAYPOINT_PATH:
3451                         if (Num_waypoint_lists)
3452                                 return 1;
3453
3454                         return 0;
3455
3456                 case OPF_MISSION_NAME:
3457                         if (m_mode != MODE_CAMPAIGN) {
3458                                 if (!(*Mission_filename))
3459                                         return 0;
3460
3461                                 return 1;
3462                         }
3463
3464                         if (Campaign.num_missions > 0)
3465                                 return 1;
3466
3467                         return 0;
3468
3469                 case OPF_GOAL_NAME: {
3470                         int value;
3471
3472                         value = Operators[op].value;
3473
3474                         if (m_mode == MODE_CAMPAIGN)
3475                                 return 1;
3476
3477                         // need to be sure that previous-goal functions are available.  (i.e. we are providing a default argument for them)
3478                         else if ((value == OP_PREVIOUS_GOAL_TRUE) || (value == OP_PREVIOUS_GOAL_FALSE) || (value == OP_PREVIOUS_GOAL_INCOMPLETE) || Num_goals)
3479                                 return 1;
3480
3481                         return 0;
3482                 }
3483
3484                 case OPF_EVENT_NAME: {
3485                         int value;
3486
3487                         value = Operators[op].value;
3488                         if (m_mode == MODE_CAMPAIGN)
3489                                 return 1;
3490
3491                         // need to be sure that previous-event functions are available.  (i.e. we are providing a default argument for them)
3492                         else if ((value == OP_PREVIOUS_EVENT_TRUE) || (value == OP_PREVIOUS_EVENT_FALSE) || (value == OP_PREVIOUS_EVENT_INCOMPLETE) || Num_mission_events)
3493                                 return 1;
3494
3495                         return 0;
3496                 }
3497
3498                 case OPF_MESSAGE:
3499                         if (m_mode == MODE_EVENTS) {
3500                                 Assert(Event_editor_dlg);
3501                                 if (Event_editor_dlg->current_message_name(0))
3502                                         return 1;
3503
3504                         } else {
3505                                 if (Num_messages > Num_builtin_messages)
3506                                         return 1;
3507                         }
3508
3509                         return 0;
3510
3511                 case OPF_VARIABLE_NAME:
3512                         if (sexp_variable_count() > 0) {
3513                                 return 1;
3514                         } else {
3515                                 return 0;
3516                         }
3517
3518                 default:
3519                         Int3();
3520
3521         }
3522
3523         return 0;
3524 }
3525
3526 // expand a combined line (one with an operator and it's one argument on the same line) into
3527 // 2 lines.
3528 void sexp_tree::expand_operator(int node)
3529 {
3530         int data;
3531         HTREEITEM h;
3532
3533         if (nodes[node].flags & COMBINED) {
3534                 node = nodes[node].parent;
3535                 Assert((nodes[node].flags & OPERAND) && (nodes[node].flags & EDITABLE));
3536         }
3537
3538         if ((nodes[node].flags & OPERAND) && (nodes[node].flags & EDITABLE)) {  // expandable?
3539                 Assert(nodes[node].type & SEXPT_OPERATOR);
3540                 h = nodes[node].handle;
3541                 data = nodes[node].child;
3542                 Assert(data != -1 && nodes[data].next == -1 && nodes[data].child == -1);
3543
3544                 SetItem(h, TVIF_TEXT, nodes[node].text, 0, 0, 0, 0, 0);
3545                 nodes[node].flags = OPERAND;
3546                 nodes[data].handle = insert(nodes[data].text, BITMAP_DATA, BITMAP_DATA, h);
3547                 nodes[data].flags = EDITABLE;
3548                 Expand(h, TVE_EXPAND);
3549         }
3550 }
3551
3552 // expand a CTreeCtrl branch and all of it's children
3553 void sexp_tree::expand_branch(HTREEITEM h)
3554 {
3555         Expand(h, TVE_EXPAND);
3556         h = GetChildItem(h);
3557         while (h) {
3558                 expand_branch(h);
3559                 h = GetNextSiblingItem(h);
3560         }
3561 }
3562
3563 void sexp_tree::merge_operator(int node)
3564 {
3565 /*      char buf[256];
3566         int child;
3567
3568         if (nodes[node].flags == EDITABLE)  // data
3569                 node = nodes[node].parent;
3570
3571         if (node != -1) {
3572                 child = nodes[node].child;
3573                 if (child != -1 && nodes[child].next == -1 && nodes[child].child == -1) {
3574                         sprintf(buf, "%s %s", nodes[node].text, nodes[child].text);
3575                         SetItemText(nodes[node].handle, buf);
3576                         nodes[node].flags = OPERAND | EDITABLE;
3577                         nodes[child].flags = COMBINED;
3578                         DeleteItem(nodes[child].handle);
3579                         nodes[child].handle = NULL;
3580                         return;
3581                 }
3582         }*/
3583 }
3584
3585 // add a data node under operator pointed to by item_index
3586 int sexp_tree::add_data(char *data, int type)
3587 {
3588         int node;
3589
3590         expand_operator(item_index);
3591         node = allocate_node(item_index);
3592         set_node(node, type, data);
3593         nodes[node].handle = insert(data, BITMAP_DATA, BITMAP_DATA, nodes[item_index].handle);
3594         nodes[node].flags = EDITABLE;
3595         *modified = 1;
3596         return node;
3597 }
3598
3599 // add a (variable) data node under operator pointed to by item_index
3600 int sexp_tree::add_variable_data(char *data, int type)
3601 {
3602         int node;
3603
3604         Assert(type & SEXPT_VARIABLE);
3605
3606         expand_operator(item_index);
3607         node = allocate_node(item_index);
3608         set_node(node, type, data);
3609         nodes[node].handle = insert(data, BITMAP_VARIABLE, BITMAP_VARIABLE, nodes[item_index].handle);
3610         nodes[node].flags = NOT_EDITABLE;
3611         *modified = 1;
3612         return node;
3613 }
3614
3615 // add an operator under operator pointed to by item_index.  Updates item_index to point
3616 // to this new operator.
3617 void sexp_tree::add_operator(char *op, HTREEITEM h)
3618 {
3619         int node;
3620         
3621         if (item_index == -1) {
3622                 node = allocate_node(-1);
3623                 set_node(node, (SEXPT_OPERATOR | SEXPT_VALID), op);
3624                 item_handle = nodes[node].handle = insert(op, BITMAP_OPERATOR, BITMAP_OPERATOR, h);
3625
3626         } else {
3627                 expand_operator(item_index);
3628                 node = allocate_node(item_index);
3629                 set_node(node, (SEXPT_OPERATOR | SEXPT_VALID), op);
3630                 item_handle = nodes[node].handle = insert(op, BITMAP_OPERATOR, BITMAP_OPERATOR, nodes[item_index].handle);
3631         }
3632
3633         nodes[node].flags = OPERAND;
3634         item_index = node;
3635         *modified = 1;
3636 }
3637
3638 // add an operator with one argument under operator pointed to by item_index.  This function
3639 // exists because the one arg case is a special case.  The operator and argument is
3640 // displayed on the same line.
3641 /*void sexp_tree::add_one_arg_operator(char *op, char *data, int type)
3642 {
3643         char str[80];
3644         int node1, node2;
3645         
3646         expand_operator(item_index);
3647         node1 = allocate_node(item_index);
3648         node2 = allocate_node(node1);
3649         set_node(node1, SEXPT_OPERATOR, op);
3650         set_node(node2, type, data);
3651         sprintf(str, "%s %s", op, data);
3652         nodes[node1].handle = insert(str, nodes[item_index].handle);
3653         nodes[node1].flags = OPERAND | EDITABLE;
3654         nodes[node2].flags = COMBINED;
3655         *modified = 1;
3656 }*/
3657
3658 /*
3659 int sexp_tree::verify_tree(int *bypass)
3660 {
3661         return verify_tree(0, bypass);
3662 }
3663
3664 // check the sexp tree for errors.  Return -1 if error, or 0 if no errors.  If an error
3665 // is found, item_index = node of error.
3666 int sexp_tree::verify_tree(int node, int *bypass)
3667 {
3668         int i, type, count, op, type2, op2, argnum = 0;
3669
3670         if (!total)
3671                 return 0;  // nothing to check
3672
3673         Assert(node >= 0 && node < MAX_SEXP_TREE_SIZE);
3674         Assert(nodes[node].type == SEXPT_OPERATOR);
3675
3676         op = identify_operator(nodes[node].text);
3677         if (op == -1)
3678                 return node_error(node, "Unknown operator", bypass);
3679
3680         count = count_args(nodes[node].child);
3681         if (count < Operators[op].min)
3682                 return node_error(node, "Too few arguments for operator", bypass);
3683         if (count > Operators[op].max)
3684                 return node_error(node, "Too many arguments for operator", bypass);
3685
3686         node = nodes[node].child;  // get first argument
3687         while (node != -1) {
3688                 type = query_operator_argument_type(op, argnum);
3689                 Assert(nodes[node].type & SEXPT_VALID);
3690                 if (nodes[node].type == SEXPT_OPERATOR) {
3691                         if (verify_tree(node) == -1)
3692                                 return -1;
3693
3694                         op2 = identify_operator(nodes[node].text);  // no error checking, because it was done in the call above.
3695                         type2 = query_operator_return_type(op2);
3696
3697                 } else if (nodes[node].type == SEXPT_NUMBER) {
3698                         char *ptr;
3699
3700                         type2 = OPR_NUMBER;
3701                         ptr = nodes[node].text;
3702                         while (*ptr)
3703                                 if (!isdigit(*ptr++))
3704                                         return node_error(node, "Number is invalid", bypass);
3705
3706                 } else if (nodes[node].type == SEXPT_STRING) {
3707                         type2 = SEXP_ATOM_STRING;
3708
3709                 } else
3710                         Assert(0);  // unknown and invalid sexp node type.
3711
3712                 switch (type) {
3713                         case OPF_NUMBER:
3714                                 if (type2 != OPR_NUMBER)
3715                                         return node_error(node, "Number or number return type expected here", bypass);
3716
3717                                 break;
3718
3719                         case OPF_SHIP:
3720                                 if (type2 == SEXP_ATOM_STRING)
3721                                         if (ship_name_lookup(nodes[node].text) == -1)
3722                                                 type2 = 0;
3723
3724                                 if (type2 != SEXP_ATOM_STRING)
3725                                         return node_error(node, "Ship name expected here", bypass);
3726
3727                                 break;
3728
3729                         case OPF_WING:
3730                                 if (type2 == SEXP_ATOM_STRING)
3731                                         if (wing_name_lookup(nodes[node].text) == -1)
3732                                                 type2 = 0;
3733
3734                                 if (type2 != SEXP_ATOM_STRING)
3735                                         return node_error(node, "Wing name expected here", bypass);
3736
3737                                 break;
3738
3739                         case OPF_SHIP_WING:
3740                                 if (type2 == SEXP_ATOM_STRING)
3741                                         if (ship_name_lookup(nodes[node].text) == -1)
3742                                                 if (wing_name_lookup(nodes[node].text) == -1)
3743                                                         type2 = 0;
3744
3745                                 if (type2 != SEXP_ATOM_STRING)
3746                                         return node_error(node, "Ship or wing name expected here", bypass);
3747
3748                                 break;
3749
3750                         case OPF_BOOL:
3751                                 if (type2 != OPR_BOOL)
3752                                         return node_error(node, "Boolean return type expected here", bypass);
3753
3754                                 break;
3755
3756                         case OPF_NULL:
3757                                 if (type2 != OPR_NULL)
3758                                         return node_error(node, "No return type operator expected here", bypass);
3759
3760                                 break;
3761
3762                         case OPF_POINT:
3763                                 if (type2 != SEXP_ATOM_STRING || verify_vector(nodes[node].text))
3764                                         return node_error(node, "3d coordinate expected here", bypass);
3765
3766                                 break;
3767
3768                         case OPF_SUBSYSTEM:
3769                                 if (type2 == SEXP_ATOM_STRING)
3770                                         if (ai_get_subsystem_type(nodes[node].text) == SUBSYSTEM_UNKNOWN)
3771                                                 type2 = 0;
3772
3773                                 if (type2 != SEXP_ATOM_STRING)
3774                                         return node_error(node, "Subsystem name expected here", bypass);
3775
3776                                 break;
3777
3778                         case OPF_IFF:
3779                                 if (type2 == SEXP_ATOM_STRING) {
3780                                         for (i=0; i<Num_team_names; i++)
3781                                                 if (!stricmp(Team_names[i], nodes[node].text))
3782                                                         break;
3783                                 }
3784
3785                                 if (i == Num_team_names)
3786                                         return node_error(node, "Iff team type expected here", bypass);
3787
3788                                 break;
3789
3790                         case OPF_AI_GOAL:
3791                                 if (type2 != OPR_AI_GOAL)
3792                                         return node_error(node, "Ai goal return type expected here", bypass);
3793
3794                                 break;
3795
3796                         case OPF_DOCKER_POINT:
3797                                 if (type2 != SEXP_ATOM_STRING)
3798                                         return node_error(node, "Docker docking point name expected here", bypass);
3799
3800                                 break;
3801
3802                         case OPF_DOCKEE_POINT:
3803                                 if (type2 != SEXP_ATOM_STRING)
3804                                         return node_error(node, "Dockee docking point name expected here", bypass);
3805
3806                                 break;
3807                 }
3808
3809                 node = nodes[node].next;
3810                 argnum++;
3811         }
3812
3813         return 0;
3814 }
3815 */
3816
3817 // display an error message and position to point of error (a node)
3818 int sexp_tree::node_error(int node, char *msg, int *bypass)
3819 {
3820         char text[512];
3821
3822         if (bypass)
3823                 *bypass = 1;
3824
3825         item_index = node;
3826         item_handle = nodes[node].handle;
3827         if (nodes[node].flags & COMBINED)
3828                 item_handle = nodes[nodes[node].parent].handle;
3829
3830         ensure_visible(node);
3831         SelectItem(item_handle);
3832         sprintf(text, "%s\n\nContinue checking for more errors?", msg);
3833         if (MessageBox(text, "Sexp error", MB_YESNO | MB_ICONEXCLAMATION) == IDNO)
3834                 return -1;
3835         else
3836                 return 0;
3837 }
3838
3839 void sexp_tree::hilite_item(int node)
3840 {
3841         ensure_visible(node);
3842         SelectItem(nodes[node].handle);
3843 }
3844
3845 // because the MFC function EnsureVisible() doesn't do what it says it does, I wrote this.
3846 void sexp_tree::ensure_visible(int node)
3847 {
3848         Assert(node != -1);
3849         if (nodes[node].parent != -1)
3850                 ensure_visible(nodes[node].parent);  // expand all parents first
3851
3852         if (nodes[node].child != -1)  // expandable?
3853                 Expand(nodes[node].handle, TVE_EXPAND);  // expand this item
3854 }
3855
3856 void sexp_tree::link_modified(int *ptr)
3857 {
3858         modified = ptr;
3859 }
3860
3861 void get_variable_default_text_from_variable_text(char *text, char *default_text)
3862 {
3863         int len;
3864         char *start;
3865
3866         // find '('
3867         start = strstr(text, "(");
3868         Assert(start);
3869         start++;
3870
3871         // get length and copy all but last char ")"
3872         len = strlen(start);
3873         strncpy(default_text, start, len-1);
3874
3875         // add null termination
3876         default_text[len-1] = '\0';
3877 }
3878
3879 void get_variable_name_from_sexp_tree_node_text(const char *text, char *var_name)
3880 {
3881         int length;
3882         length = strcspn(text, "(");
3883
3884         strncpy(var_name, text, length);
3885         var_name[length] = '\0';
3886 }
3887
3888 int sexp_tree::get_modify_variable_type()
3889 {
3890         Assert(item_index > -1);
3891         int sexp_var_index;
3892
3893         // get arg
3894         int parent = nodes[item_index].parent;
3895         Assert(parent != -1);
3896
3897         if ( !stricmp(nodes[parent].text, "modify-variable") ) {
3898                 Assert(nodes[parent].child != -1);
3899                 sexp_var_index = get_tree_name_to_sexp_variable_index(nodes[nodes[parent].child].text);
3900                 Assert(sexp_var_index != -1);
3901         } else {
3902                 Int3();  // should not be called otherwise
3903         }
3904
3905         if (Sexp_variables[sexp_var_index].type & SEXP_VARIABLE_NUMBER) {
3906                 return OPF_NUMBER;
3907         } else if (Sexp_variables[sexp_var_index].type & SEXP_VARIABLE_STRING) {
3908                 return OPF_AMBIGUOUS;
3909         } else {
3910                 Int3();
3911                 return 0;
3912         }
3913 }
3914
3915
3916 void sexp_tree::verify_and_fix_arguments(int node)
3917 {
3918         int op, arg_num, type, tmp;
3919         static int flag = 0;
3920         sexp_list_item *list, *ptr;
3921
3922         if (flag)
3923                 return;
3924
3925         flag++;
3926         op = identify_operator(nodes[node].text);
3927         if (op < 0)
3928                 return;
3929
3930         tmp = item_index;
3931         item_index = node;
3932
3933         arg_num = 0;
3934         item_index = nodes[node].child;
3935         while (item_index >= 0) {
3936                 // get listing of valid argument values for node item_index
3937                 type = query_operator_argument_type(op, arg_num);
3938                 // special case for modify-variable
3939                 if (type == OPF_AMBIGUOUS) {
3940                         // check if parent variable type is number, returns OPF_NUMBER or OPF_AMBIGUOUS
3941                         type = get_modify_variable_type();
3942                 }
3943                 if (query_restricted_opf_range(type)) {
3944                         list = get_listing_opf(type, node, arg_num);
3945                         if (!list && (arg_num >= Operators[op].min)) {
3946                                 free_node(item_index, 1);
3947                                 item_index = tmp;
3948                                 flag--;
3949                                 return;
3950                         }
3951
3952                         if (list) {
3953                                 // get a pointer to nodes[item_index].text for normal value
3954                                 // or default variable value if variable
3955                                 char *text_ptr;
3956                                 char default_variable_text[TOKEN_LENGTH];
3957                                 if (nodes[item_index].type & SEXPT_VARIABLE) {
3958                                         // special case for modify-variable
3959                                         if ( !stricmp(Operators[op].text, "modify-variable") ) {
3960                                                 // make text_ptr to start - before '('
3961                                                 get_variable_name_from_sexp_tree_node_text(nodes[item_index].text, default_variable_text);
3962                                                 text_ptr = default_variable_text;
3963                                         } else {
3964                                                 get_variable_default_text_from_variable_text(nodes[item_index].text, default_variable_text);
3965                                                 text_ptr = default_variable_text;
3966                                         }
3967                                 } else {
3968                                         text_ptr = nodes[item_index].text;
3969                                 }
3970
3971                                 ptr = list;
3972                                 while (ptr) {
3973
3974                                         if (ptr->text != NULL) {
3975                                                 // make sure text is not NULL
3976                                                 // check that proposed text is valid for operator
3977                                                 if ( !stricmp(ptr->text, text_ptr) )
3978                                                         break;
3979
3980                                                 ptr = ptr->next;
3981                                         } else {
3982                                                 // text is NULL, so set ptr to NULL to end loop
3983                                                 ptr = NULL;
3984                                         }
3985                                 }
3986
3987                                 if (!ptr) {  // argument isn't in list of valid choices, 
3988                                         if (list->op >= 0) {
3989                                                 replace_operator(list->text);
3990                                         } else {
3991                                                 replace_data(list->text, list->type);
3992                                         }
3993                                 }
3994
3995                         } else {
3996                                 bool invalid = false;
3997                                 if (type == OPF_AMBIGUOUS) {
3998                                         if (SEXPT_TYPE(nodes[item_index].type) == SEXPT_OPERATOR) {
3999                                                 invalid = true;
4000                                         }
4001                                 } else {
4002                                         if (SEXPT_TYPE(nodes[item_index].type) != SEXPT_OPERATOR) {
4003                                                 invalid = true;
4004                                         }
4005                                 }
4006
4007                                 if (invalid) {
4008                                         replace_data("<Invalid>", (SEXPT_STRING | SEXPT_VALID));
4009                                 }
4010                         }
4011
4012                         if (nodes[item_index].type & SEXPT_OPERATOR)
4013                                 verify_and_fix_arguments(item_index);
4014                 }
4015
4016                 item_index = nodes[item_index].next;
4017                 arg_num++;
4018         }
4019
4020         item_index = tmp;
4021         flag--;
4022 }
4023
4024 void sexp_tree::replace_data(char *data, int type)
4025 {
4026         int node;
4027         HTREEITEM h;
4028
4029         node = nodes[item_index].child;
4030         if (node != -1)
4031                 free_node2(node);
4032
4033         nodes[item_index].child = -1;
4034         h = nodes[item_index].handle;
4035         while (ItemHasChildren(h))
4036                 DeleteItem(GetChildItem(h));
4037
4038         set_node(item_index, type, data);
4039         SetItemText(h, data);
4040         SetItemImage(h, BITMAP_DATA, BITMAP_DATA);
4041         nodes[item_index].flags = EDITABLE;
4042
4043         // check remaining data beyond replaced data for validity (in case any of it is dependent on data just replaced)
4044         verify_and_fix_arguments(nodes[item_index].parent);
4045
4046         *modified = 1;
4047         update_help(GetSelectedItem());
4048 }
4049
4050
4051 // Replaces data with sexp_variable type data
4052 void sexp_tree::replace_variable_data(int var_idx, int type)
4053 {
4054         int node;
4055         HTREEITEM h;
4056         char buf[128];
4057
4058         Assert(type & SEXPT_VARIABLE);
4059
4060         node = nodes[item_index].child;
4061         if (node != -1)
4062                 free_node2(node);
4063
4064         nodes[item_index].child = -1;
4065         h = nodes[item_index].handle;
4066         while (ItemHasChildren(h)) {
4067                 DeleteItem(GetChildItem(h));
4068         }
4069
4070         // Assemble name
4071         sprintf(buf, "%s(%s)", Sexp_variables[var_idx].variable_name, Sexp_variables[var_idx].text);
4072
4073         set_node(item_index, type, buf);
4074         SetItemText(h, buf);
4075         SetItemImage(h, BITMAP_VARIABLE, BITMAP_VARIABLE);
4076         nodes[item_index].flags = NOT_EDITABLE;
4077
4078         // check remaining data beyond replaced data for validity (in case any of it is dependent on data just replaced)
4079         verify_and_fix_arguments(nodes[item_index].parent);
4080
4081         *modified = 1;
4082         update_help(GetSelectedItem());
4083 }
4084
4085
4086
4087 void sexp_tree::replace_operator(char *op)
4088 {
4089         int node;
4090         HTREEITEM h;
4091
4092         node = nodes[item_index].child;
4093         if (node != -1)
4094                 free_node2(node);
4095
4096         nodes[item_index].child = -1;
4097         h = nodes[item_index].handle;
4098         while (ItemHasChildren(h))
4099                 DeleteItem(GetChildItem(h));
4100
4101         set_node(item_index, (SEXPT_OPERATOR | SEXPT_VALID), op);
4102         SetItemText(h, op);
4103         nodes[item_index].flags = OPERAND;
4104         *modified = 1;
4105         update_help(GetSelectedItem());
4106
4107         // hack added at Allender's request.  If changing ship in an ai-dock operator, re-default
4108         // docking point.
4109 }
4110
4111 /*void sexp_tree::replace_one_arg_operator(char *op, char *data, int type)
4112 {
4113         char str[80];
4114         int node;
4115         HTREEITEM h;
4116
4117         node = nodes[item_index].child;
4118         if (node != -1)
4119                 free_node2(node);
4120
4121         nodes[item_index].child = -1;
4122         h = nodes[item_index].handle;
4123         while (ItemHasChildren(h))
4124                 DeleteItem(GetChildItem(h));
4125         
4126         node = allocate_node(item_index);
4127         set_node(item_index, SEXPT_OPERATOR, op);
4128         set_node(node, type, data);
4129         sprintf(str, "%s %s", op, data);
4130         SetItemText(h, str);
4131         nodes[item_index].flags = OPERAND | EDITABLE;
4132         nodes[node].flags = COMBINED;
4133         *modified = 1;
4134         update_help(GetSelectedItem());
4135 }*/
4136
4137 // moves a whole sexp tree branch to a new position under 'parent' and after 'after'.
4138 // The expansion state is preserved, and node handles are updated.
4139 void sexp_tree::move_branch(int source, int parent)
4140 {
4141         int node;
4142
4143         // if no source, skip everything
4144         if (source != -1) {
4145                 node = nodes[source].parent;
4146                 if (node != -1) {
4147                         if (nodes[node].child == source)
4148                                 nodes[node].child = nodes[source].next;
4149                         else {
4150                                 node = nodes[node].child;
4151                                 while (nodes[node].next != source) {
4152                                         node = nodes[node].next;
4153                                         Assert(node != -1);
4154                                 }
4155
4156                                 nodes[node].next = nodes[source].next;
4157                         }
4158                 }
4159
4160                 nodes[source].parent = parent;
4161                 nodes[source].next = -1;
4162                 if (parent) {
4163                         if (nodes[parent].child == -1)
4164                                 nodes[parent].child = source;
4165                         else {
4166                                 node = nodes[parent].child;
4167                                 while (nodes[node].next != -1)
4168                                         node = nodes[node].next;
4169
4170                                 nodes[node].next = source;
4171                         }
4172
4173                         move_branch(nodes[source].handle, nodes[parent].handle);
4174
4175                 } else
4176                         move_branch(nodes[source].handle);
4177         }
4178 }
4179
4180 HTREEITEM sexp_tree::move_branch(HTREEITEM source, HTREEITEM parent, HTREEITEM after)
4181 {
4182         int i, image1, image2;
4183         HTREEITEM h = 0, child, next;
4184
4185         if (source) {
4186                 for (i=0; i<MAX_SEXP_TREE_SIZE; i++)
4187                         if (nodes[i].handle == source)
4188                                 break;
4189
4190                 if (i < MAX_SEXP_TREE_SIZE) {
4191                         GetItemImage(source, image1, image2);
4192                         h = insert(GetItemText(source), image1, image2, parent, after);
4193                         nodes[i].handle = h;
4194
4195                 } else {
4196                         GetItemImage(source, image1, image2);
4197                         h = insert(GetItemText(source), image1, image2, parent, after);
4198                 }
4199
4200                 SetItemData(h, GetItemData(source));
4201                 child = GetChildItem(source);
4202                 while (child) {
4203                         next = GetNextSiblingItem(child);
4204                         move_branch(child, h);
4205                         child = next;
4206                 }
4207
4208                 if (GetItemState(source, TVIS_EXPANDED) & TVIS_EXPANDED)
4209                         Expand(h, TVE_EXPAND);
4210
4211                 DeleteItem(source);
4212         }
4213
4214         return h;
4215 }
4216
4217 void sexp_tree::copy_branch(HTREEITEM source, HTREEITEM parent, HTREEITEM after)
4218 {
4219         int i, image1, image2;
4220         HTREEITEM h, child;
4221
4222         if (source) {
4223                 for (i=0; i<MAX_SEXP_TREE_SIZE; i++)
4224                         if (nodes[i].handle == source)
4225                                 break;
4226
4227                 if (i < MAX_SEXP_TREE_SIZE) {
4228                         GetItemImage(source, image1, image2);
4229                         h = insert(GetItemText(source), image1, image2, parent, after);
4230                         nodes[i].handle = h;
4231
4232                 } else {
4233                         GetItemImage(source, image1, image2);
4234                         h = insert(GetItemText(source), image1, image2, parent, after);
4235                 }
4236
4237                 SetItemData(h, GetItemData(source));
4238                 child = GetChildItem(source);
4239                 while (child) {
4240                         copy_branch(child, h);
4241                         child = GetNextSiblingItem(child);
4242                 }
4243
4244                 if (GetItemState(source, TVIS_EXPANDED) & TVIS_EXPANDED)
4245                         Expand(h, TVE_EXPAND);
4246         }
4247 }
4248
4249 void sexp_tree::swap_roots(HTREEITEM one, HTREEITEM two)
4250 {
4251         HTREEITEM h;
4252
4253         Assert(!GetParentItem(one));
4254         Assert(!GetParentItem(two));
4255 //      copy_branch(one, TVI_ROOT, two);
4256 //      move_branch(two, TVI_ROOT, one);
4257 //      DeleteItem(one);
4258         h = move_branch(one, TVI_ROOT, two);
4259         SelectItem(h);
4260         SelectItem(h);
4261         *modified = 1;
4262 }
4263
4264 void sexp_tree::OnBegindrag(NMHDR* pNMHDR, LRESULT* pResult) 
4265 {
4266         UINT flags;
4267
4268 //      ScreenToClient(&m_pt);
4269         ASSERT(!m_dragging);
4270         m_h_drag = HitTest(m_pt, &flags);
4271         m_h_drop = NULL;
4272
4273         if (!m_mode || GetParentItem(m_h_drag))
4274                 return;
4275
4276         ASSERT(m_p_image_list == NULL);
4277         m_p_image_list = CreateDragImage(m_h_drag);  // get the image list for dragging
4278         if (!m_p_image_list)
4279                 return;
4280
4281         m_p_image_list->DragShowNolock(TRUE);
4282         m_p_image_list->SetDragCursorImage(0, CPoint(0, 0));
4283         m_p_image_list->BeginDrag(0, CPoint(0,0));
4284         m_p_image_list->DragMove(m_pt);
4285         m_p_image_list->DragEnter(this, m_pt);
4286         SetCapture();
4287         m_dragging = TRUE;
4288 }
4289
4290 void sexp_tree::OnLButtonDown(UINT nFlags, CPoint point) 
4291 {
4292         m_pt = point;
4293         CTreeCtrl::OnLButtonDown(nFlags, point);
4294 }
4295
4296 void sexp_tree::OnMouseMove(UINT nFlags, CPoint point) 
4297 {
4298         HTREEITEM hitem;
4299         UINT flags;
4300
4301         if (m_dragging) {
4302                 ASSERT(m_p_image_list != NULL);
4303                 m_p_image_list->DragMove(point);
4304                 if ((hitem = HitTest(point, &flags)) != NULL)
4305                         if (!GetParentItem(hitem)) {
4306                                 m_p_image_list->DragLeave(this);
4307                                 SelectDropTarget(hitem);
4308                                 m_h_drop = hitem;
4309                                 m_p_image_list->DragEnter(this, point);
4310                         }
4311         }
4312
4313         CTreeCtrl::OnMouseMove(nFlags, point);
4314 }
4315
4316 void sexp_tree::OnLButtonUp(UINT nFlags, CPoint point) 
4317 {
4318         int index1, index2;
4319
4320         if (m_dragging) {
4321                 ASSERT(m_p_image_list != NULL);
4322                 m_p_image_list->DragLeave(this);
4323                 m_p_image_list->EndDrag();
4324                 delete m_p_image_list;
4325                 m_p_image_list = NULL;
4326
4327                 if (m_h_drop && m_h_drag != m_h_drop) {
4328                         Assert(m_h_drag);
4329                         index1 = GetItemData(m_h_drag);
4330                         index2 = GetItemData(m_h_drop);
4331                         swap_roots(m_h_drag, m_h_drop);
4332                         if (m_mode == MODE_GOALS) {
4333                                 Assert(Goal_editor_dlg);
4334                                 Goal_editor_dlg->swap_handler(index1, index2);
4335
4336                         } else if (m_mode == MODE_EVENTS) {
4337                                 Assert(Event_editor_dlg);
4338                                 Event_editor_dlg->swap_handler(index1, index2);
4339
4340                         } else if (m_mode == MODE_CAMPAIGN) {
4341                                 Campaign_tree_formp->swap_handler(index1, index2);
4342
4343                         } else
4344                                 Assert(0);
4345
4346                 } else
4347                         MessageBeep(0);
4348
4349                 ReleaseCapture();
4350                 m_dragging = FALSE;
4351                 SelectDropTarget(NULL);
4352         }
4353
4354         CTreeCtrl::OnLButtonUp(nFlags, point);
4355 }
4356
4357 void sexp_tree::setup(CEdit *ptr)
4358 {
4359         CImageList *pimagelist;
4360         CBitmap bitmap;
4361
4362         help_box = ptr;
4363         if (help_box) {
4364                 int stops[2] = { 10, 30 };
4365
4366                 help_box -> SetTabStops(2, (LPINT) stops);
4367         }
4368
4369         pimagelist = GetImageList(TVSIL_NORMAL);
4370         if (!pimagelist) {
4371                 pimagelist = new CImageList();
4372                 pimagelist->Create(16, 16, TRUE/*bMask*/, 2, 9);
4373
4374                 bitmap.LoadBitmap(IDB_OPERATOR);
4375                 pimagelist->Add(&bitmap, (COLORREF) 0xFFFFFF);
4376                 bitmap.DeleteObject();
4377
4378                 bitmap.LoadBitmap(IDB_DATA);
4379                 pimagelist->Add(&bitmap, (COLORREF) 0xFF00FF);
4380                 bitmap.DeleteObject();
4381
4382                 bitmap.LoadBitmap(IDB_VARIABLE);
4383                 pimagelist->Add(&bitmap, (COLORREF) 0xFF00FF);
4384                 bitmap.DeleteObject();
4385
4386                 bitmap.LoadBitmap(IDB_ROOT);
4387                 pimagelist->Add(&bitmap, (COLORREF) 0xFF00FF);
4388                 bitmap.DeleteObject();
4389
4390                 bitmap.LoadBitmap(IDB_ROOT_DIRECTIVE);
4391                 pimagelist->Add(&bitmap, (COLORREF) 0xFFFFFF);
4392                 bitmap.DeleteObject();
4393
4394                 bitmap.LoadBitmap(IDB_CHAINED);
4395                 pimagelist->Add(&bitmap, (COLORREF) 0xFF00FF);
4396                 bitmap.DeleteObject();
4397
4398                 bitmap.LoadBitmap(IDB_CHAINED_DIRECTIVE);
4399                 pimagelist->Add(&bitmap, (COLORREF) 0xFFFFFF);
4400                 bitmap.DeleteObject();
4401
4402                 bitmap.LoadBitmap(IDB_GREEN_DOT);
4403                 pimagelist->Add(&bitmap, (COLORREF) 0xFFFFFF);
4404                 bitmap.DeleteObject();
4405
4406                 bitmap.LoadBitmap(IDB_BLACK_DOT);
4407                 pimagelist->Add(&bitmap, (COLORREF) 0xFFFFFF);
4408                 bitmap.DeleteObject();
4409
4410                 SetImageList(pimagelist, TVSIL_NORMAL);
4411         }
4412 }
4413 //#define BITMAP_OPERATOR 0
4414 //#define BITMAP_DATA 1
4415 //#define BITMAP_VARIABLE 2
4416 //#define BITMAP_ROOT 3
4417 //#define BITMAP_ROOT_DIRECTIVE 4
4418 //#define BITMAP_CHAIN 5
4419 //#define BITMAP_CHAIN_DIRECTIVE 6
4420 //#define BITMAP_GREEN_DOT 7
4421 //#define BITMAP_BLACK_DOT 8
4422
4423
4424 HTREEITEM sexp_tree::insert(LPCTSTR lpszItem, int image, int sel_image, HTREEITEM hParent, HTREEITEM hInsertAfter)
4425 {
4426         return InsertItem(lpszItem, image, sel_image, hParent, hInsertAfter);
4427
4428 }
4429
4430 void sexp_tree::OnDestroy() 
4431 {
4432         CImageList *pimagelist;
4433
4434         pimagelist = GetImageList(TVSIL_NORMAL);
4435         if (pimagelist) {
4436                 pimagelist->DeleteImageList();
4437                 delete pimagelist;
4438         }
4439
4440         CTreeCtrl::OnDestroy();
4441 }
4442
4443 HTREEITEM sexp_tree::handle(int node)
4444 {
4445         return nodes[node].handle;
4446 }
4447
4448 char *sexp_tree::help(int code)
4449 {
4450         int i;
4451
4452         i = sizeof(Sexp_help) / sizeof(sexp_help_struct);
4453         while (i--) {
4454                 if (Sexp_help[i].id == code)
4455                         break;
4456         }
4457
4458         if (i >= 0)
4459                 return Sexp_help[i].help;
4460
4461         return NULL;
4462 }
4463
4464 // get type of item clicked on
4465 int sexp_tree::get_type(HTREEITEM h)
4466 {
4467         int i;
4468
4469         // get index into sexp_tree 
4470         for (i=0; i<MAX_SEXP_TREE_SIZE; i++)
4471                 if (nodes[i].handle == h)
4472                         break;
4473
4474         if ( (i >= MAX_SEXP_TREE_SIZE) ) {
4475                 // Int3();      // This would be the root of the tree  -- ie, event name
4476                 return -1;
4477         }
4478
4479         return nodes[i].type;
4480 }
4481
4482
4483 void sexp_tree::update_help(HTREEITEM h)
4484 {
4485         char *str;
4486         int i, j, z, c, code;
4487         CString text;
4488
4489         for (i=0; i<Num_operators; i++)
4490                 for (j=0; j<Num_op_menus; j++)
4491                         if ((Operators[i].value & OP_CATAGORY_MASK) == op_menu[j].id) {
4492                                 if (!help(Operators[i].value))
4493                                         Int3();  // Damn you, Allender!  If you add new sexp operators, add help for them too! :)
4494                         }
4495
4496         help_box = (CEdit *) GetParent()->GetDlgItem(IDC_HELP_BOX);
4497         if (!help_box || !::IsWindow(help_box->m_hWnd))
4498                 return;
4499
4500         for (i=0; i<MAX_SEXP_TREE_SIZE; i++)
4501                 if (nodes[i].handle == h)
4502                         break;
4503
4504         if ((i >= MAX_SEXP_TREE_SIZE) || !nodes[i].type) {
4505                 help_box->SetWindowText("");
4506                 return;
4507         }
4508
4509         if (SEXPT_TYPE(nodes[i].type) != SEXPT_OPERATOR) {
4510                 z = nodes[i].parent;
4511                 if (z < 0) {
4512                         Warning(LOCATION, "Sexp data \"%s\" has no parent!", nodes[i].text);
4513                         return;
4514                 }
4515
4516                 code = identify_operator(nodes[z].text);
4517                 if (code >= 0) {
4518                         c = 0;
4519                         j = nodes[z].child;
4520                         while ((j >= 0) && (j != i)) {
4521                                 j = nodes[j].next;
4522                                 c++;
4523                         }
4524
4525                         Assert(j >= 0);
4526                         if (query_operator_argument_type(code, c) == OPF_MESSAGE) {
4527                                 for (j=0; j<Num_messages; j++)
4528                                         if (!stricmp(Messages[j].name, nodes[i].text)) {
4529                                                 text.Format("Message Text:\r\n%s", Messages[j].message);
4530                                                 help_box->SetWindowText(text);
4531                                                 return;
4532                                         }
4533                         }
4534                 }
4535
4536                 i = z;
4537         }
4538
4539         code = find_operator(nodes[i].text);
4540         str = help(code);
4541         if (!str)
4542                 str = "No help available";
4543
4544         help_box->SetWindowText(str);
4545 }
4546
4547 // find list of sexp_tree nodes with text
4548 // stuff node indices into find[]
4549 int sexp_tree::find_text(char *text, int *find)
4550 {
4551         int i, find_count;
4552
4553         // initialize find
4554         for (i=0; i<MAX_SEARCH_MESSAGE_DEPTH; i++) {
4555                 find[i] = -1;
4556         }
4557
4558         find_count = 0;
4559
4560         for (i=0; i<MAX_SEXP_TREE_SIZE; i++) {
4561                 // only look at used and editable nodes
4562                 if ((nodes[i].flags & EDITABLE && (nodes[i].type != SEXPT_UNUSED))) {
4563                         // find the text
4564                         if ( !stricmp(nodes[i].text, text)  ) {
4565                                 find[find_count++] = i;
4566
4567                                 // don't exceed max count - array bounds
4568                                 if (find_count == MAX_SEARCH_MESSAGE_DEPTH) {
4569                                         break;
4570                                 }
4571                         }
4572                 }
4573         }
4574
4575         return find_count;
4576 }
4577                         
4578
4579 void sexp_tree::OnKeydown(NMHDR *pNMHDR, LRESULT *pResult) 
4580 {
4581         int key;
4582         TV_KEYDOWN *pTVKeyDown = (TV_KEYDOWN *) pNMHDR;
4583
4584         key = pTVKeyDown->wVKey;
4585         if (key == VK_SPACE)
4586                 EditLabel(GetSelectedItem());
4587
4588         *pResult = 0;
4589 }
4590
4591 // Determine if a given opf code has a restricted argument range (i.e. has a specific, limited
4592 // set of argument values, or has virtually unlimited possibilities.  For example, boolean values
4593 // only have true or false, so it is restricted, but a number could be anything, so it's not.
4594 //
4595 int sexp_tree::query_restricted_opf_range(int opf)
4596 {
4597         switch (opf) {
4598                 case OPF_NUMBER:
4599                 case OPF_POSITIVE:
4600                 case OPF_WHO_FROM:
4601                         return 0;
4602         }
4603
4604         return 1;
4605 }
4606
4607 // generate listing of valid argument values.
4608 // opf = operator format to generate list for
4609 // parent_node = the parent node we are generating list for
4610 // arg_index = argument number of parent this argument will go at
4611 //
4612 sexp_list_item *sexp_tree::get_listing_opf(int opf, int parent_node, int arg_index)
4613 {
4614         switch (opf) {
4615                 case OPF_NONE:
4616                         return NULL;
4617
4618                 case OPF_NULL:
4619                         return get_listing_opf_null();
4620
4621                 case OPF_BOOL:
4622                         return get_listing_opf_bool(parent_node);
4623
4624                 case OPF_NUMBER:
4625                         return get_listing_opf_number();
4626
4627                 case OPF_SHIP:
4628                         return get_listing_opf_ship(parent_node);
4629
4630                 case OPF_WING:
4631                         return get_listing_opf_wing();
4632                 
4633                 case OPF_AWACS_SUBSYSTEM:
4634                 case OPF_SUBSYSTEM:
4635                         return get_listing_opf_subsystem(parent_node, arg_index);                       
4636
4637                 case OPF_POINT:
4638                         return get_listing_opf_point();
4639
4640                 case OPF_IFF:
4641                         return get_listing_opf_iff();
4642
4643                 case OPF_AI_GOAL:
4644                         return get_listing_opf_ai_goal(parent_node);
4645
4646                 case OPF_DOCKER_POINT:
4647                         return get_listing_opf_docker_point(parent_node);
4648
4649                 case OPF_DOCKEE_POINT:
4650                         return get_listing_opf_dockee_point(parent_node);
4651
4652                 case OPF_MESSAGE:
4653                         return get_listing_opf_message();
4654
4655                 case OPF_WHO_FROM:
4656                         return get_listing_opf_who_from();
4657
4658                 case OPF_PRIORITY:
4659                         return get_listing_opf_priority();
4660
4661                 case OPF_WAYPOINT_PATH:
4662                         return get_listing_opf_waypoint_path();
4663
4664                 case OPF_POSITIVE:
4665                         return get_listing_opf_positive();
4666
4667                 case OPF_MISSION_NAME:
4668                         return get_listing_opf_mission_name();
4669
4670                 case OPF_SHIP_POINT:
4671                         return get_listing_opf_ship_point();
4672
4673                 case OPF_GOAL_NAME:
4674                         return get_listing_opf_goal_name(parent_node);
4675
4676                 case OPF_SHIP_WING:
4677                         return get_listing_opf_ship_wing();
4678
4679                 case OPF_SHIP_WING_POINT:
4680                         return get_listing_opf_ship_wing_point();
4681
4682                 case OPF_SHIP_TYPE:
4683                         return get_listing_opf_ship_type();
4684
4685                 case OPF_KEYPRESS:
4686                         return get_listing_opf_keypress();
4687
4688                 case OPF_EVENT_NAME:
4689                         return get_listing_opf_event_name(parent_node);
4690
4691                 case OPF_AI_ORDER:
4692                         return get_listing_opf_ai_order();
4693
4694                 case OPF_SKILL_LEVEL:
4695                         return get_listing_opf_skill_level();
4696
4697                 case OPF_MEDAL_NAME:
4698                         return get_listing_opf_medal_name();
4699
4700                 case OPF_WEAPON_NAME:
4701                         return get_listing_opf_weapon_name();
4702
4703                 case OPF_SHIP_CLASS_NAME:
4704                         return get_listing_opf_ship_class_name();
4705
4706                 case OPF_HUD_GAUGE_NAME:
4707                         return get_listing_opf_hud_gauge_name();
4708
4709                 case OPF_HUGE_WEAPON:
4710                         return get_listing_opf_huge_weapon();
4711
4712                 case OPF_SHIP_NOT_PLAYER:
4713                         return get_listing_opf_ship_not_player();
4714
4715                 case OPF_JUMP_NODE_NAME:
4716                         return get_listing_opf_jump_nodes();
4717
4718                 case OPF_VARIABLE_NAME:
4719                         return get_listing_opf_variable_names();
4720
4721                 case OPF_AMBIGUOUS:
4722                         return NULL();
4723
4724                 default:
4725                         Int3();  // unknown OPF code
4726         }
4727
4728         return NULL;
4729 }
4730
4731 sexp_list_item *sexp_tree::get_listing_opf_null()
4732 {
4733         int i;
4734         sexp_list_item head;
4735
4736         for (i=0; i<Num_operators; i++)
4737                 if (query_operator_return_type(i) == OPR_NULL)
4738                         head.add_op(i);
4739
4740         return head.next;
4741 }
4742
4743 sexp_list_item *sexp_tree::get_listing_opf_bool(int parent_node)
4744 {
4745         int i, only_basic;
4746         sexp_list_item head;
4747
4748         // search for the previous goal/event operators.  If found, only add the true/false
4749         // sexpressions to the list
4750         only_basic = 0;
4751         if ( parent_node != -1 ) {
4752                 int op;
4753
4754                 op = find_operator(nodes[parent_node].text);
4755                 if ( (op == OP_PREVIOUS_GOAL_TRUE) || (op == OP_PREVIOUS_GOAL_FALSE) || (op == OP_PREVIOUS_EVENT_TRUE) || (op == OP_PREVIOUS_EVENT_FALSE) )
4756                         only_basic = 1;
4757
4758         }
4759
4760         for (i=0; i<Num_operators; i++) {
4761                 if (query_operator_return_type(i) == OPR_BOOL) {
4762                         if ( !only_basic || (only_basic && ((Operators[i].value == OP_TRUE) || (Operators[i].value == OP_FALSE))) ) {
4763                                 head.add_op(i);
4764                         }
4765                 }
4766         }
4767
4768         return head.next;
4769 }
4770
4771 sexp_list_item *sexp_tree::get_listing_opf_positive()
4772 {
4773         int i;
4774         sexp_list_item head;
4775
4776         for (i=0; i<Num_operators; i++)
4777                 if (query_operator_return_type(i) == OPR_POSITIVE)
4778                         head.add_op(i);
4779
4780         return head.next;
4781 }
4782
4783 sexp_list_item *sexp_tree::get_listing_opf_number()
4784 {
4785         int i, z;
4786         sexp_list_item head;
4787
4788         for (i=0; i<Num_operators; i++) {
4789                 z = query_operator_return_type(i);
4790                 if ((z == OPR_NUMBER) || (z == OPR_POSITIVE))
4791                         head.add_op(i);
4792         }
4793
4794         return head.next;
4795 }
4796
4797 sexp_list_item *sexp_tree::get_listing_opf_ship(int parent_node)
4798 {
4799         object *ptr;
4800         sexp_list_item head;
4801         int op = 0, dock_ship = -1, require_cap_ship = 0;
4802
4803         // look at the parent node and get the operator.  Some ship lists should be filtered based
4804         // on what the parent operator is
4805         if ( parent_node >= 0 ) {
4806                 op = find_operator(nodes[parent_node].text);
4807
4808                 // prune out to only capital ships
4809                 if (!stricmp(nodes[parent_node].text, "cap-subsys-cargo-known-delay")) {
4810                         require_cap_ship = 1;
4811                 }
4812
4813                 // get the dock_ship number of if this goal is an ai dock goal.  used to prune out unwanted ships out
4814                 // of the generated ship list
4815                 dock_ship = -1;
4816                 if ( op == OP_AI_DOCK ) {
4817                         int z;
4818
4819                         z = nodes[parent_node].parent;
4820                         Assert(z >= 0);
4821                         Assert(!stricmp(nodes[z].text, "add-ship-goal") || !stricmp(nodes[z].text, "add-wing-goal") || !stricmp(nodes[z].text, "add-goal"));
4822
4823                         z = nodes[z].child;
4824                         Assert(z >= 0);
4825
4826                         dock_ship = ship_name_lookup(nodes[z].text, 1);
4827                         Assert( dock_ship != -1 );
4828                 }
4829         }
4830
4831         ptr = GET_FIRST(&obj_used_list);
4832         while (ptr != END_OF_LIST(&obj_used_list)) {
4833                 if ((ptr->type == OBJ_SHIP) || (ptr->type == OBJ_START)) {
4834                         if ( op == OP_AI_DOCK ) {
4835                                 // only include those ships in the list which the given ship can dock with.
4836                                 if ( (dock_ship != ptr->instance) && ship_docking_valid(dock_ship , ptr->instance) )
4837                                         head.add_data(Ships[ptr->instance].ship_name );
4838
4839                         } else {
4840                                 if ( !require_cap_ship || (Ship_info[Ships[ptr->instance].ship_info_index].flags & SIF_HUGE_SHIP) ) {
4841                                         head.add_data(Ships[ptr->instance].ship_name);
4842                                 }
4843                         }
4844                 }
4845
4846                 ptr = GET_NEXT(ptr);
4847         }
4848
4849         return head.next;
4850 }
4851
4852 sexp_list_item *sexp_tree::get_listing_opf_wing()
4853 {
4854         int i;
4855         sexp_list_item head;
4856
4857         for (i=0; i<MAX_WINGS; i++){
4858                 if (Wings[i].wave_count){
4859                         head.add_data(Wings[i].name);
4860                 }
4861         }
4862
4863         return head.next;
4864 }
4865
4866 // specific types of subsystems we're looking for
4867 #define OPS_CAP_CARGO           1       
4868 #define OPS_STRENGTH                    2
4869 #define OPS_BEAM_TURRET         3
4870 #define OPS_AWACS                               4
4871 sexp_list_item *sexp_tree::get_listing_opf_subsystem(int parent_node, int arg_index)
4872 {
4873         int op, child, sh;
4874         int special_subsys = 0;
4875         sexp_list_item head;
4876         ship_subsys *subsys;
4877
4878         // determine if the parent is one of the set subsystem strength items.  If so,
4879         // we want to append the "Hull" name onto the end of the menu
4880         Assert(parent_node >= 0);       
4881         
4882         // get the operator type of the node
4883         op = find_operator(nodes[parent_node].text);
4884
4885         // first child node
4886         child = nodes[parent_node].child;
4887         Assert(child >= 0);
4888
4889         switch(op){
4890         // where we care about hull strength
4891         case OP_REPAIR_SUBSYSTEM:
4892         case OP_SABOTAGE_SUBSYSTEM:
4893         case OP_SET_SUBSYSTEM_STRNGTH:
4894                 special_subsys = OPS_STRENGTH;
4895                 break;
4896
4897         // awacs subsystems
4898         case OP_AWACS_SET_RADIUS:
4899                 special_subsys = OPS_AWACS;
4900                 break;
4901
4902         // where we care about capital ship subsystem cargo
4903         case OP_CAP_SUBSYS_CARGO_KNOWN_DELAY:
4904                 special_subsys = OPS_CAP_CARGO;
4905                 
4906                 // get the next sibling
4907                 child = nodes[child].next;              
4908                 break;
4909
4910         // where we care about turrets carrying beam weapons
4911         case OP_BEAM_FIRE:
4912                 special_subsys = OPS_BEAM_TURRET;
4913
4914                 // if this is arg index 3 (targeted ship)
4915                 if(arg_index == 3){                     
4916                         Assert(arg_index == 3);
4917                         child = nodes[child].next;
4918                         Assert(child >= 0);                     
4919                         child = nodes[child].next;                      
4920                 } else {
4921                         Assert(arg_index == 1);
4922                 }
4923                 break;
4924         }                       
4925
4926         // now find the ship and add all relevant subsystems
4927         Assert(child >= 0);
4928         sh = ship_name_lookup(nodes[child].text, 1);
4929         if (sh >= 0) {
4930                 subsys = GET_FIRST(&Ships[sh].subsys_list);
4931                 while (subsys != END_OF_LIST(&Ships[sh].subsys_list)) {
4932                         // add stuff
4933                         switch(special_subsys){
4934                         // subsystem cargo
4935                         case OPS_CAP_CARGO:                                     
4936                                 if (valid_cap_subsys_cargo_list(subsys->system_info->subobj_name) ) {
4937                                         head.add_data(subsys->system_info->subobj_name);
4938                                 }
4939                                 break;
4940
4941                         // beam fire
4942                         case OPS_BEAM_TURRET:
4943                                 head.add_data(subsys->system_info->subobj_name);
4944                                 break;
4945
4946                         // awacs level
4947                         case OPS_AWACS:
4948                                 if (subsys->system_info->flags & MSS_FLAG_AWACS) {
4949                                         head.add_data(subsys->system_info->subobj_name);
4950                                 }
4951                                 break;
4952
4953                         // everything else
4954                         default:
4955                                 head.add_data(subsys->system_info->subobj_name);
4956                                 break;
4957                         }
4958
4959                         // next subsystem
4960                         subsys = GET_NEXT(subsys);
4961                 }
4962         }
4963         
4964         // if one of the subsystem strength operators, append the Hull string
4965         if(special_subsys == OPS_STRENGTH){
4966                 head.add_data(SEXP_HULL_STRING);
4967         }
4968
4969         return head.next;
4970 }
4971
4972 sexp_list_item *sexp_tree::get_listing_opf_point()
4973 {
4974         char buf[NAME_LENGTH+8];
4975         int i, j;
4976         sexp_list_item head;
4977
4978         for (i=0; i<Num_waypoint_lists; i++)
4979                 for (j=0; j<Waypoint_lists[i].count; j++) {
4980                         sprintf(buf, "%s:%d", Waypoint_lists[i].name, j + 1);
4981                         head.add_data_dup(buf);
4982                 }
4983
4984         return head.next;
4985 }
4986
4987 sexp_list_item *sexp_tree::get_listing_opf_iff()
4988 {
4989         int i;
4990         sexp_list_item head;
4991
4992         for (i=0; i<Num_team_names; i++)
4993                 head.add_data(Team_names[i]);
4994
4995         return head.next;
4996 }
4997
4998 sexp_list_item *sexp_tree::get_listing_opf_ai_goal(int parent_node)
4999 {
5000         int i, n, w, z, child;
5001         sexp_list_item head;
5002
5003         Assert(parent_node >= 0);
5004         child = nodes[parent_node].child;
5005         Assert(child >= 0);
5006         n = ship_name_lookup(nodes[child].text, 1);
5007         if (n >= 0) {
5008                 // add operators if it's an ai-goal and ai-goal is allowed for that ship
5009                 for (i=0; i<Num_operators; i++) {
5010                         if ( (query_operator_return_type(i) == OPR_AI_GOAL) && query_sexp_ai_goal_valid(Operators[i].value, n) )
5011                                 head.add_op(i);
5012                 }
5013
5014         } else {
5015                 z = wing_name_lookup(nodes[child].text);
5016                 if (z >= 0) {
5017                         for (w=0; w<Wings[z].wave_count; w++) {
5018                                 n = Wings[z].ship_index[w];
5019                                 // add operators if it's an ai-goal and ai-goal is allowed for that ship
5020                                 for (i=0; i<Num_operators; i++) {
5021                                         if ( (query_operator_return_type(i) == OPR_AI_GOAL) && query_sexp_ai_goal_valid(Operators[i].value, n) )
5022                                                 head.add_op(i);
5023                                 }
5024                         }
5025
5026                 } else
5027                         return NULL;  // no valid ship or wing to check against, make nothing available
5028         }
5029
5030         return head.next;
5031 }
5032
5033 sexp_list_item *sexp_tree::get_listing_opf_docker_point(int parent_node)
5034 {
5035         int i, z, sh;
5036         sexp_list_item head;
5037
5038         Assert(parent_node >= 0);
5039         Assert(!stricmp(nodes[parent_node].text, "ai-dock"));
5040
5041         z = nodes[parent_node].parent;
5042         Assert(z >= 0);
5043         Assert(!stricmp(nodes[z].text, "add-ship-goal") || !stricmp(nodes[z].text, "add-wing-goal") || !stricmp(nodes[z].text, "add-goal"));
5044
5045         z = nodes[z].child;
5046         Assert(z >= 0);
5047
5048         sh = ship_name_lookup(nodes[z].text, 1);
5049         if (sh >= 0) {
5050                 z = get_docking_list(Ships[sh].modelnum);
5051                 for (i=0; i<z; i++)
5052                         head.add_data(Docking_bay_list[i]);
5053         }
5054
5055         return head.next;
5056 }
5057
5058 sexp_list_item *sexp_tree::get_listing_opf_dockee_point(int parent_node)
5059 {
5060         int i, z, sh;
5061         sexp_list_item head;
5062
5063         Assert(parent_node >= 0);
5064         Assert(!stricmp(nodes[parent_node].text, "ai-dock"));
5065
5066         z = nodes[parent_node].child;
5067         Assert(z >= 0);
5068
5069         sh = ship_name_lookup(nodes[z].text, 1);
5070         if (sh >= 0) {
5071                 z = get_docking_list(Ships[sh].modelnum);
5072                 for (i=0; i<z; i++)
5073                         head.add_data(Docking_bay_list[i]);
5074         }
5075
5076         return head.next;
5077 }
5078
5079 sexp_list_item *sexp_tree::get_listing_opf_message()
5080 {
5081         char *str;
5082         int i;
5083         sexp_list_item head;
5084
5085         if (m_mode == MODE_EVENTS) {
5086                 Assert(Event_editor_dlg);
5087                 // this for looks a litle strange, but had to do it get rid of a warning.  Conditional
5088                 //uses last statement is sequence, i.e. same as for (i=0, str, i++)
5089                 for (i=0; str = Event_editor_dlg->current_message_name(i), str; i++)
5090                         head.add_data(str);
5091
5092         } else {
5093                 for (i=Num_builtin_messages; i<Num_messages; i++)
5094                         head.add_data(Messages[i].name);
5095         }
5096
5097         return head.next;
5098 }
5099
5100 sexp_list_item *sexp_tree::get_listing_opf_who_from()
5101 {
5102         object *ptr;
5103         sexp_list_item head;
5104
5105         //head.add_data("<any allied>");
5106         head.add_data("#Command");
5107         head.add_data("<any wingman>");
5108
5109         ptr = GET_FIRST(&obj_used_list);
5110         while (ptr != END_OF_LIST(&obj_used_list)) {
5111                 if ((ptr->type == OBJ_SHIP) || (ptr->type == OBJ_START))
5112                         if (!(Ship_info[Ships[get_ship_from_obj(ptr)].ship_info_index].flags & SIF_NOT_FLYABLE))
5113                                 head.add_data(Ships[ptr->instance].ship_name);
5114
5115                 ptr = GET_NEXT(ptr);
5116         }
5117
5118         return head.next;
5119 }
5120
5121 sexp_list_item *sexp_tree::get_listing_opf_priority()
5122 {
5123         sexp_list_item head;
5124
5125         head.add_data("High");
5126         head.add_data("Normal");
5127         head.add_data("Low");
5128         return head.next;
5129 }
5130
5131 sexp_list_item *sexp_tree::get_listing_opf_waypoint_path()
5132 {
5133         int i;
5134         sexp_list_item head;
5135
5136         for (i=0; i<Num_waypoint_lists; i++)
5137                 head.add_data(Waypoint_lists[i].name);
5138
5139         return head.next;
5140 }
5141
5142 sexp_list_item *sexp_tree::get_listing_opf_ship_point()
5143 {
5144         sexp_list_item head;
5145
5146         head.add_list(get_listing_opf_ship());
5147         head.add_list(get_listing_opf_point());
5148         return head.next;
5149 }
5150
5151 sexp_list_item *sexp_tree::get_listing_opf_ship_wing_point()
5152 {
5153         sexp_list_item head;
5154
5155         head.add_data("<any friendly>");
5156         head.add_data("<any hostile>");
5157         head.add_data("<any neutral>");
5158         head.add_data("<any unknown>");
5159         head.add_list(get_listing_opf_ship());
5160         head.add_list(get_listing_opf_wing());
5161         head.add_list(get_listing_opf_point());
5162         return head.next;
5163 }
5164
5165 sexp_list_item *sexp_tree::get_listing_opf_mission_name()
5166 {
5167         int i;
5168         sexp_list_item head;
5169
5170         if ((m_mode == MODE_CAMPAIGN) && (Cur_campaign_mission >= 0)) {
5171                 for (i=0; i<Campaign.num_missions; i++)
5172                         if ( (i == Cur_campaign_mission) || (Campaign.missions[i].level < Campaign.missions[Cur_campaign_mission].level) )
5173                                 head.add_data(Campaign.missions[i].name);
5174
5175         } else
5176                 head.add_data(Mission_filename);
5177
5178         return head.next;
5179 }
5180
5181 sexp_list_item *sexp_tree::get_listing_opf_goal_name(int parent_node)
5182 {
5183         int i, m;
5184         sexp_list_item head;
5185
5186         if (m_mode == MODE_CAMPAIGN) {
5187                 int child;
5188
5189                 Assert(parent_node >= 0);
5190                 child = nodes[parent_node].child;
5191                 Assert(child >= 0);
5192
5193                 for (m=0; m<Campaign.num_missions; m++)
5194                         if (!stricmp(Campaign.missions[m].name, nodes[child].text))
5195                                 break;
5196
5197                 if (m < Campaign.num_missions) {
5198                         if (Campaign.missions[m].num_goals < 0)  // haven't loaded goal names yet.
5199                                 read_mission_goal_list(m);
5200
5201                         for (i=0; i<Campaign.missions[m].num_goals; i++)
5202                                 head.add_data(Campaign.missions[m].goals[i].name);
5203                 }
5204
5205         } else {
5206                 for (i=0; i<Num_goals; i++)
5207                         head.add_data(Mission_goals[i].name);
5208         }
5209
5210         return head.next;
5211 }
5212
5213 sexp_list_item *sexp_tree::get_listing_opf_ship_wing()
5214 {
5215         sexp_list_item head;
5216
5217         head.add_list(get_listing_opf_ship());
5218         head.add_list(get_listing_opf_wing());
5219         return head.next;
5220 }
5221
5222 sexp_list_item *sexp_tree::get_listing_opf_ship_type()
5223 {
5224         int i;
5225         sexp_list_item head;
5226
5227         for (i=0; i<MAX_SHIP_TYPE_COUNTS; i++){
5228                 head.add_data(Ship_type_names[i]);
5229         }
5230
5231         return head.next;
5232 }
5233
5234 sexp_list_item *sexp_tree::get_listing_opf_keypress()
5235 {
5236         int i;
5237         sexp_list_item head;
5238
5239         for (i=0; i<CCFG_MAX; i++) {
5240                 if (Control_config[i].key_default > 0) {
5241                         head.add_data_dup(textify_scancode(Control_config[i].key_default));
5242                 }
5243         }
5244
5245         return head.next;
5246 }
5247
5248 sexp_list_item *sexp_tree::get_listing_opf_event_name(int parent_node)
5249 {
5250         int i, m;
5251         sexp_list_item head;
5252
5253
5254         if (m_mode == MODE_CAMPAIGN) {
5255                 int child;
5256
5257                 Assert(parent_node >= 0);
5258                 child = nodes[parent_node].child;
5259                 Assert(child >= 0);
5260
5261                 for (m=0; m<Campaign.num_missions; m++)
5262                         if (!stricmp(Campaign.missions[m].name, nodes[child].text))
5263                                 break;
5264
5265                 if (m < Campaign.num_missions) {
5266                         if (Campaign.missions[m].num_events < 0)  // haven't loaded goal names yet.
5267                                 read_mission_goal_list(m);
5268
5269                         for (i=0; i<Campaign.missions[m].num_events; i++)
5270                                 head.add_data(Campaign.missions[m].events[i].name);
5271                 }
5272
5273         } else {
5274                 for (i=0; i<Num_mission_events; i++)
5275                         head.add_data(Mission_events[i].name);
5276         }
5277
5278         return head.next;
5279 }
5280
5281 sexp_list_item *sexp_tree::get_listing_opf_ai_order()
5282 {
5283         int i;
5284         sexp_list_item head;
5285
5286         for (i=0; i<Fred_comm_orders_max; i++)
5287                 head.add_data(Fred_comm_orders[i].menu_text);
5288
5289         return head.next;
5290 }
5291
5292 sexp_list_item *sexp_tree::get_listing_opf_skill_level()
5293 {
5294         int i;
5295         sexp_list_item head;
5296
5297         for (i=0; i<NUM_SKILL_LEVELS; i++)
5298                 head.add_data(Skill_level_names(i, 0));
5299
5300         return head.next;
5301 }
5302
5303 sexp_list_item *sexp_tree::get_listing_opf_medal_name()
5304 {
5305         int i;
5306         sexp_list_item head;
5307
5308         for (i=0; i<MAX_ASSIGNABLE_MEDALS; i++)
5309                 head.add_data(Medals[i].name);
5310
5311         // also add SOC crest (index 17) and Wings (index 13)
5312         head.add_data(Medals[13].name);
5313         head.add_data(Medals[17].name);
5314
5315         return head.next;
5316 }
5317
5318 sexp_list_item *sexp_tree::get_listing_opf_weapon_name()
5319 {
5320         int i;
5321         sexp_list_item head;
5322
5323         for (i=0; i<Num_weapon_types; i++)
5324                 head.add_data(Weapon_info[i].name);
5325
5326         return head.next;
5327 }
5328
5329 sexp_list_item *sexp_tree::get_listing_opf_ship_class_name()
5330 {
5331         int i;
5332         sexp_list_item head;
5333
5334         for (i=0; i<Num_ship_types; i++)
5335                 head.add_data(Ship_info[i].name);
5336
5337         return head.next;
5338 }
5339
5340 sexp_list_item *sexp_tree::get_listing_opf_hud_gauge_name()
5341 {
5342         int i;
5343         sexp_list_item head;
5344
5345         for (i=0; i<NUM_HUD_GAUGES; i++)
5346                 head.add_data(HUD_gauge_text[i]);
5347
5348         return head.next;
5349 }
5350
5351 sexp_list_item *sexp_tree::get_listing_opf_huge_weapon()
5352 {
5353         int i;
5354         sexp_list_item head;
5355
5356         for (i=0; i<Num_weapon_types; i++) {
5357                 if (Weapon_info[i].wi_flags & WIF_HUGE)
5358                         head.add_data(Weapon_info[i].name);
5359         }
5360
5361         return head.next;
5362 }
5363
5364 sexp_list_item *sexp_tree::get_listing_opf_ship_not_player()
5365 {
5366         object *ptr;
5367         sexp_list_item head;
5368
5369         ptr = GET_FIRST(&obj_used_list);
5370         while (ptr != END_OF_LIST(&obj_used_list)) {
5371                 if (ptr->type == OBJ_SHIP)
5372                         head.add_data(Ships[ptr->instance].ship_name);
5373
5374                 ptr = GET_NEXT(ptr);
5375         }
5376
5377         return head.next;
5378 }
5379
5380 sexp_list_item *sexp_tree::get_listing_opf_jump_nodes()
5381 {
5382         int i;
5383         sexp_list_item head;
5384
5385         for (i = 0; i < Num_jump_nodes; i++ ) 
5386                 head.add_data( Jump_nodes[i].name );
5387
5388         return head.next;
5389 }
5390
5391 // creates list of Sexp_variables
5392 sexp_list_item *sexp_tree::get_listing_opf_variable_names()
5393 {
5394         int i;
5395         sexp_list_item head;
5396
5397         for (i=0; i<MAX_SEXP_VARIABLES; i++) {
5398                 if (Sexp_variables[i].type & SEXP_VARIABLE_SET) {
5399                         head.add_data( Sexp_variables[i].variable_name );
5400                 }
5401         }
5402
5403         return head.next;
5404 }
5405
5406
5407 // Deletes sexp_variable from sexp_tree.
5408 // resets tree to not include given variable, and resets text and type
5409 void sexp_tree::delete_sexp_tree_variable(const char *var_name)
5410 {
5411         char search_str[64];
5412         char replace_text[TOKEN_LENGTH];
5413         
5414         sprintf(search_str, "%s(", var_name);
5415
5416         // store old item index
5417         int old_item_index = item_index;
5418
5419         for (int idx=0; idx<MAX_SEXP_TREE_SIZE; idx++) {
5420                 if (nodes[idx].type & SEXPT_VARIABLE) {
5421                         if ( strstr(nodes[idx].text, search_str) != NULL ) {
5422
5423                                 // check type is number or string
5424                                 Assert( (nodes[idx].type & SEXPT_NUMBER) || (nodes[idx].type & SEXPT_STRING) );
5425
5426                                 // reset type as not variable
5427                                 int type = nodes[idx].type &= ~SEXPT_VARIABLE;
5428
5429                                 // reset text
5430                                 if (nodes[idx].type & SEXPT_NUMBER) {
5431                                         strcpy(replace_text, "number");
5432                                 } else {
5433                                         strcpy(replace_text, "string");
5434                                 }
5435
5436                                 // set item_index and replace data
5437                                 item_index = idx;
5438                                 replace_data(replace_text, type);
5439                         }
5440                 }
5441         }
5442
5443         // restore item_index
5444         item_index = old_item_index;
5445 }
5446
5447
5448 // Modify sexp_tree for a change in sexp_variable (name, type, or default value)
5449 void sexp_tree::modify_sexp_tree_variable(const char *old_name, int sexp_var_index)
5450 {
5451         char search_str[64];
5452         int type;
5453
5454         Assert(Sexp_variables[sexp_var_index].type & SEXP_VARIABLE_SET);
5455         Assert( (Sexp_variables[sexp_var_index].type & SEXP_VARIABLE_NUMBER) || (Sexp_variables[sexp_var_index].type & SEXP_VARIABLE_STRING) );
5456
5457         // Get type for sexp_tree node
5458         if (Sexp_variables[sexp_var_index].type & SEXP_VARIABLE_NUMBER) {
5459                 type = (SEXPT_NUMBER | SEXPT_VALID);
5460         } else {
5461                 type = (SEXPT_STRING | SEXPT_VALID);
5462         }
5463                                                                                                                         
5464         // store item index;
5465         int old_item_index = item_index;
5466
5467         // Search string in sexp_tree nodes
5468         sprintf(search_str, "%s(", old_name);
5469
5470         for (int idx=0; idx<MAX_SEXP_TREE_SIZE; idx++) {
5471                 if (nodes[idx].type & SEXPT_VARIABLE) {
5472                         if ( strstr(nodes[idx].text, search_str) != NULL ) {
5473                                 // temp set item_index
5474                                 item_index = idx;
5475
5476                                 // replace variable data
5477                                 replace_variable_data(sexp_var_index, (type | SEXPT_VARIABLE));
5478                         }
5479                 }
5480         }
5481
5482         // restore item_index
5483         item_index = old_item_index;
5484 }
5485
5486
5487 // convert from item_index to sexp_variable index, -1 if not
5488 int sexp_tree::get_item_index_to_var_index()
5489 {
5490         // check valid item index and node is a variable
5491         if ( (item_index > 0) && (nodes[item_index].type & SEXPT_VARIABLE) ) {
5492
5493                 return get_tree_name_to_sexp_variable_index(nodes[item_index].text);
5494         } else {
5495                 return -1;
5496         }
5497 }
5498
5499 int sexp_tree::get_tree_name_to_sexp_variable_index(const char *tree_name)
5500 {
5501         char var_name[TOKEN_LENGTH];
5502
5503         int chars_to_copy = strcspn(tree_name, "(");
5504         Assert(chars_to_copy < TOKEN_LENGTH - 1);
5505
5506         // Copy up to '(' and add null termination
5507         strncpy(var_name, tree_name, chars_to_copy);
5508         var_name[chars_to_copy] = '\0';
5509
5510         // Look up index
5511         return get_index_sexp_variable_name(var_name);
5512 }
5513
5514 int sexp_tree::get_variable_count(const char *var_name)
5515 {
5516         int idx;
5517         int count = 0;
5518         char compare_name[64];
5519
5520         // get name to compare
5521         strcpy(compare_name, var_name);
5522         strcat(compare_name, "(");
5523
5524         // look for compare name
5525         for (idx=0; idx<MAX_SEXP_TREE_SIZE; idx++) {
5526                 if (nodes[idx].type & SEXPT_VARIABLE) {
5527                         if ( strstr(nodes[idx].text, compare_name) ) {
5528                                 count++;
5529                         }
5530                 }
5531         }
5532
5533         return count;
5534 }