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