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