2 * $Logfile: /Freespace2/code/Fred2/Sexp_tree.cpp $
7 * Sexp tree handler class. Almost everything is handled by this class.
10 * Revision 1.2 2002/05/07 03:16:44 theoddone33
11 * The Great Newline Fix
13 * Revision 1.1.1.1 2002/05/03 03:28:08 root
17 * 43 9/07/99 9:22p Jefff
18 * added 2 more assignable medals
20 * 42 9/07/99 1:05a Andsager
21 * Added team-score sexp for multi team vs team missions
23 * 41 8/27/99 4:07p Andsager
24 * Add is-ship-visible sexp. Make ship-vanish sexp SINGLE player only
26 * 40 8/24/99 4:25p Andsager
27 * Add ship-vanish sexp
29 * 39 8/16/99 10:04p Andsager
30 * Add special-warp-dist and special-warpout-name sexp for Knossos device
33 * 38 8/09/99 2:00p Dave
36 * 37 8/02/99 4:26p Dave
37 * Added 2 new sexpressions.
39 * 36 8/02/99 1:43p Andsager
42 * 35 7/28/99 1:36p Andsager
43 * Modify cargo1 to include flag CARGO_NO_DEPLETE. Add sexp
44 * cargo-no-deplete (only for BIG / HUGE). Modify ship struct to pack
47 * 34 7/24/99 4:56p Dave
48 * Added 3 new sexpressions.
50 * 33 7/21/99 8:10p Dave
51 * First run of supernova effect.
53 * 32 7/20/99 9:19p Andsager
54 * Added facing waypoint sexp
56 * 31 7/20/99 9:54a Andsager
57 * Add subsys-set-random sexp
59 * 30 7/19/99 12:02p Andsager
60 * Allow AWACS on any ship subsystem. Fix sexp_set_subsystem_strength to
61 * only blow up subsystem if its strength is > 0
63 * 29 7/13/99 3:37p Andsager
64 * Add secondaries-depleted sexp
66 * 28 7/12/99 12:01p Andsager
67 * Make message by default come from command.
69 * 27 7/08/99 12:06p Andsager
70 * Add turret-tagged-only and turret-tagged-clear sexp.
72 * 26 6/29/99 10:08a Andsager
75 * 25 6/23/99 5:51p Andsager
76 * Add waypoint-cap-speed. Checkin stealth ai - inactive.
78 * 24 6/16/99 10:21a Dave
79 * Added send-message-list sexpression.
81 * 23 6/01/99 8:35p Dave
82 * Finished lockarm weapons. Added proper supercap weapons/damage. Added
83 * awacs-set-radius sexpression.
85 * 22 5/24/99 11:28a Dave
86 * Sexpression for adding/removing ships from the hud escort list.
88 * 21 5/20/99 1:40p Andsager
89 * Fix find_text() to only look at nodes that are used.
91 * 20 5/04/99 5:21p Andsager
93 * 19 4/28/99 9:33a Andsager
94 * Add turret-free and turret-lock (and -all) sexp. Stargger start time
95 * of beam weapons beam-free and beam-free-all.
97 * 18 4/26/99 2:14p Andsager
98 * Add beam-protect-ship and beam-unprotect-ship sexp.
100 * 17 4/23/99 12:01p Johnson
101 * Added SIF_HUGE_SHIP
103 * 16 4/02/99 9:54a Dave
104 * Added a few more options in the weapons.tbl for beam weapons. Attempt
105 * at putting "pain" packets into multiplayer.
107 * 15 3/20/99 3:46p Dave
108 * Added support for model-based background nebulae. Added 3 new
111 * 14 3/04/99 6:09p Dave
112 * Added in sexpressions for firing beams and checking for if a ship is
115 * 13 3/01/99 10:00a Dave
116 * Fxied several dogfight related stats bugs.
118 * 12 2/26/99 6:01p Andsager
119 * Add sexp has-been-tagged-delay and cap-subsys-cargo-known-delay
121 * 11 1/26/99 10:09a Andsager
122 * Better checking for modifying/deleting variables
124 * 10 1/25/99 5:16p Andsager
125 * Handle change of variable type on modify-variable
127 * 9 1/25/99 8:10a Andsager
128 * Add sexp_modify_variable(). Changed syntax checking to allow, adding
129 * operator return type ambiguous
131 * 8 1/24/99 11:37p Dave
132 * First full rev of beam weapons. Very customizable. Removed some bogus
133 * Int3()'s in low level net code.
135 * 7 1/20/99 9:02a Andsager
136 * Fix bug in verify_and_fix_arguments, where list of strings can have
139 * 6 1/19/99 3:57p Andsager
140 * Round 2 of variables
142 * 5 12/17/98 2:39p Andsager
143 * Added bitmaps for campaign editor. Changed input into insert() to
146 * 4 12/17/98 2:34p Andsager
147 * new bitmap and dialog for campaign editor
149 * 3 10/13/98 9:27a Dave
150 * Started neatening up freespace.h
152 * 2 10/07/98 6:28p Dave
153 * Initial checkin. Renamed all relevant stuff to be Fred2 instead of
154 * Fred. Globalized mission and campaign file extensions. Removed Silent
155 * Threat specific code.
157 * 1 10/07/98 3:02p Dave
159 * 1 10/07/98 3:00p Dave
161 * 179 9/25/98 1:33p Andsager
162 * Add color to event editor (root and chain) indicating mission directive
164 * 178 9/15/98 4:26p Allender
165 * added sexpression help for some sexpressions
167 * 177 6/09/98 5:15p Lawrance
168 * French/German localization
170 * 176 5/21/98 12:58a Hoffoss
171 * Fixed warnings optimized build turned up.
173 * 175 5/15/98 6:45p Hoffoss
174 * Made some things not appear in the release version of Fred.
176 * 174 5/14/98 10:15a Allender
177 * add optional argument to prevous-goal/event operators to specify what
178 * sexpression should return when being played as a single mission
180 * 173 5/04/98 10:57a Johnson
181 * Fixed bug with labeled roots allowing insert.
183 * 172 4/25/98 7:39p Allender
184 * fixd some small hotkey stuff. Worked on turret orientation being
185 * correct for multiplayer. new sexpression called end-campaign will will
186 * end the main campaign
188 * 171 4/23/98 5:49p Hoffoss
189 * Added tracking of techroom database list info in pilot files, added
190 * sexp to add more to list, made mouse usable on ship listing in tech
193 * 170 4/15/98 3:46p Hoffoss
194 * Fixed bug with getting a default argument value from an opf listing was
195 * utilizing temporary memory that was being destroyed before we were
198 * 169 4/14/98 5:46p Hoffoss
199 * Added special-check operator.
201 * 168 4/14/98 5:24p Hoffoss
202 * Added a custom operator for training handling for Mike K.
204 * 167 4/14/98 4:19p Jim
205 * Fixed bug with deleting an argument to an operator that you shouldn't
208 * 166 4/07/98 10:51a Allender
209 * remove any allied from message senders. Make heads for mission
210 * specific messages play appropriately
212 * 165 4/03/98 12:17a Allender
213 * new sexpression to detect departed or destroyed. optionally disallow
214 * support ships. Allow docking with escape pods
216 * 164 3/30/98 2:57p Hoffoss
217 * Fixed event listing in campaign editor mode.
219 * 163 3/26/98 3:13p Duncan
220 * Fixed bug in goal name listing generation function. Allender forgot
221 * about an assumption being made with them when he used it for
224 * 162 3/23/98 2:46p Hoffoss
225 * Fixed bug with default argument available for OPF_MESSAGE even when
226 * there were no messages, and added "#Command" as a message source to
229 * 161 3/21/98 7:36p Lawrance
230 * Move jump nodes to own lib.
237 #include "sexp_tree.h"
240 #include "management.h"
242 #include "operatorargtypeselect.h"
243 #include "linklist.h"
244 #include "eventeditor.h"
245 #include "missiongoalsdlg.h"
247 #include "missionmessage.h"
248 #include "missioncampaign.h"
249 #include "campaigneditordlg.h"
250 #include "hudsquadmsg.h"
251 #include "ignoreordersdlg.h"
253 #include "controlsconfig.h"
254 #include "hudgauges.h"
255 #include "starfield.h"
256 #include "jumpnode.h"
257 #include "addvariabledlg.h"
258 #include "modifyvariabledlg.h"
260 #define MAX_OP_MENUS 30
262 #define ID_VARIABLE_MENU 0xda00
263 #define ID_ADD_MENU 0xdc00
264 #define ID_REPLACE_MENU 0xde00
265 // note: stay below 0xe000 so we don't collide with MFC defines..
268 #define new DEBUG_NEW
270 static char THIS_FILE[] = __FILE__;
273 BEGIN_MESSAGE_MAP(sexp_tree, CTreeCtrl)
274 //{{AFX_MSG_MAP(sexp_tree)
275 ON_NOTIFY_REFLECT(TVN_BEGINDRAG, OnBegindrag)
280 ON_NOTIFY_REFLECT(TVN_KEYDOWN, OnKeydown)
284 static int Add_count, Replace_count;
285 static int Modify_variable;
287 struct sexp_help_struct {
292 { OP_PLUS, "Plus (Arithmetic operator)\r\n"
293 "\tAdds numbers and returns results.\r\n\r\n"
294 "Returns a number; Takes 2 or more numeric arguments." },
296 { OP_MINUS, "Minus (Arithmetic operator)\r\n"
297 "\tSubtracts numbers and returns results.\r\n\r\n"
298 "Returns a number; Takes 2 or more numeric arguments." },
300 { OP_MOD, "Mod (Arithmetic operator)\r\n"
301 "\tDivides numbers and returns the remainer.\r\n\r\n"
302 "Returns a number; Takes 2 or more numeric arguments." },
304 { OP_MUL, "Multiply (Arithmetic operator)\r\n"
305 "\tMultiplies numbers and returns results.\r\n\r\n"
306 "Returns a number; Takes 2 or more numeric arguments." },
308 { OP_DIV, "Divide (Arithmetic operator)\r\n"
309 "\tDivides numbers and returns results.\r\n\r\n"
310 "Returns a number; Takes 2 or more numeric arguments." },
312 { OP_RAND, "Random number (Arithmetic operator)\r\n"
313 "\tGets a random number and returns result.\r\n\r\n"
314 "Returns a number; Takes 2 numeric arguments.\r\n"
315 "\t1:\tLow range of random number.\r\n"
316 "\t2:\tHigh range of random number." },
318 { OP_TRUE, "True (Boolean operator)\r\n"
319 "\tA true boolean state\r\n\r\n"
320 "Returns a boolean value." },
322 { OP_FALSE, "False (Boolean operator)\r\n"
323 "\tA false boolean state\r\n\r\n"
324 "Returns a boolean value." },
326 { OP_AND, "And (Boolean operator)\r\n"
327 "\tAnd is true if all of it's arguments are true.\r\n\r\n"
328 "Returns a boolean value; Takes 2 or more boolean arguments." },
330 { OP_OR, "Or (Boolean operator)\r\n"
331 "\tOr is true if any of it's arguments are true.\r\n\r\n"
332 "Returns a boolean value; Takes 2 or more boolean arguments." },
334 { OP_EQUALS, "Equals (Boolean operator)\r\n"
335 "\tIs true if all of it's arguments are equal.\r\n\r\n"
336 "Returns a boolean value; Takes 2 or more numeric arguments." },
338 { OP_GREATER_THAN, "Greater than (Boolean operator)\r\n"
339 "\tTrue if first argument is greater than the second argument.\r\n\r\n"
340 "Returns a boolean value; Takes 2 numeric arguments." },
342 { OP_LESS_THAN, "Less than (Boolean operator)\r\n"
343 "\tTrue if first argument is less than the second argument.\r\n\r\n"
344 "Returns a boolean value; Takes 2 numeric arguments." },
346 { OP_IS_IFF, "Is IFF (Boolean operator)\r\n"
347 "\tTrue if ship{s} are all of the specified team.\r\n\r\n"
348 "Returns a boolean value; Takes 2 or more arguments:\r\n"
349 "/t1:\tTeam (\"friendly\", \"hostile\" or \"unknown\").\r\n"
350 "\tRest:\tName of ship to check." },
352 { OP_HAS_TIME_ELAPSED, "Has time elapsed (Boolean operator)\r\n"
353 "\tBecomes true when the specified amount of time has elapsed (Mission time "
354 "becomes greater than the specified time).\r\n"
355 "Returns a boolean value; Takes 1 numeric argument:\r\n"
356 "\t1:\tThe amount of time in seconds." },
358 { OP_NOT, "Not (Boolean operator)\r\n"
359 "\tReturns opposite boolean value of argument (True becomes false, and "
360 "false becomes true).\r\n\r\n"
361 "Returns a boolean value; Takes 1 boolean argument." },
363 { OP_PREVIOUS_GOAL_TRUE, "Previous Mission Goal True (Boolean operator)\r\n"
364 "\tReturns true if the specified goal in the specified mission is true "
365 "(or succeeded). It returns false otherwise.\r\n\r\n"
366 "Returns a boolean value; Takes 2 arguments + 1 optional:\r\n"
367 "\t1:\tName of the mission.\r\n"
368 "\t2:\tName of the goal in the mission.\r\n"
369 "\t3:\t(Optional) True/False which signifies what this sexpession should return when "
370 "this mission is played as a single mission." },
372 { OP_PREVIOUS_GOAL_FALSE, "Previous Mission Goal False (Boolean operator)\r\n"
373 "\tReturns true if the specified goal in the specified mission "
374 "is false (or failed). It returns false otherwise.\r\n\r\n"
375 "Returns a boolean value; Takes 2 arguments + 1 optional:\r\n"
376 "\t1:\tName of the mission.\r\n"
377 "\t2:\tName of the goal in the mission.\r\n"
378 "\t3:\t(Optional) True/False which signifies what this sexpession should return when "
379 "this mission is played as a single mission." },
381 { OP_PREVIOUS_GOAL_INCOMPLETE, "Previous Mission Goal Incomplete (Boolean operator)\r\n"
382 "\tReturns true if the specified goal in the specified mission "
383 "is incomplete (not true or false). It returns false otherwise.\r\n\r\n"
384 "Returns a boolean value; Takes 2 arguments + 1 optional:\r\n"
385 "\t1:\tName of the mission.\r\n"
386 "\t2:\tName of the goal in the mission.\r\n"
387 "\t3:\t(Optional) True/False which signifies what this sexpession should return when "
388 "this mission is played as a single mission." },
390 { OP_PREVIOUS_EVENT_TRUE, "Previous Mission Event True (Boolean operator)\r\n"
391 "\tReturns true if the specified event in the specified mission is true "
392 "(or succeeded). It returns false otherwise.\r\n\r\n"
393 "Returns a boolean value; Takes 2 arguments + 1 optional:\r\n"
394 "\t1:\tName of the mission.\r\n"
395 "\t2:\tName of the event in the mission.\r\n"
396 "\t3:\t(Optional) True/False which signifies what this sexpession should return when "
397 "this mission is played as a single mission." },
399 { OP_PREVIOUS_EVENT_FALSE, "Previous Mission Event False (Boolean operator)\r\n"
400 "\tReturns true if the specified event in the specified mission "
401 "is false (or failed). It returns false otherwise.\r\n\r\n"
402 "Returns a boolean value; Takes 2 arguments + 1 optional:\r\n"
403 "\t1:\tName of the mission.\r\n"
404 "\t2:\tName of the event in the mission.\r\n"
405 "\t3:\t(Optional) True/False which signifies what this sexpession should return when "
406 "this mission is played as a single mission." },
408 { OP_PREVIOUS_EVENT_INCOMPLETE, "Previous Mission Event Incomplete (Boolean operator)\r\n"
409 "\tReturns true if the specified event in the specified mission "
410 "is incomplete (not true or false). It returns false otherwise.\r\n\r\n"
411 "Returns a boolean value; Takes 2 arguments + 1 optional:\r\n"
412 "\t1:\tName of the mission.\r\n"
413 "\t2:\tName of the event in the mission.\r\n"
414 "\t3:\t(Optional) True/False which signifies what this sexpession should return when "
415 "this mission is played as a single mission." },
417 { OP_GOAL_TRUE_DELAY, "Mission Goal True (Boolean operator)\r\n"
418 "\tReturns true N seconds after the specified goal in the this mission is true "
419 "(or succeeded). It returns false otherwise.\r\n\r\n"
420 "Returns a boolean value; Takes 2 arguments:\r\n"
421 "\t1:\tName of the event in the mission.\r\n"
422 "\t2:\tNumber of seconds to delay before returning true."},
424 { OP_GOAL_FALSE_DELAY, "Mission Goal False (Boolean operator)\r\n"
425 "\tReturns true N seconds after the specified goal in the this mission is false "
426 "(or failed). It returns false otherwise.\r\n\r\n"
427 "Returns a boolean value; Takes 2 arguments:\r\n"
428 "\t1:\tName of the event in the mission.\r\n"
429 "\t2:\tNumber of seconds to delay before returning true."},
431 { OP_GOAL_INCOMPLETE, "Mission Goal Incomplete (Boolean operator)\r\n"
432 "\tReturns true if the specified goal in the this mission is incomplete. This "
433 "sexpression will only be useful in conjunction with another sexpression like"
434 "has-time-elapsed. Used alone, it will return true upon misison startup."
435 "Returns a boolean value; Takes 1 argument:\r\n"
436 "\t1:\tName of the event in the mission."},
438 { OP_EVENT_TRUE_DELAY, "Mission Event True (Boolean operator)\r\n"
439 "\tReturns true N seconds after the specified event in the this mission is true "
440 "(or succeeded). It returns false otherwise.\r\n\r\n"
441 "Returns a boolean value; Takes 2 arguments:\r\n"
442 "\t1:\tName of the event in the mission.\r\n"
443 "\t2:\tNumber of seconds to delay before returning true."},
445 { OP_EVENT_FALSE_DELAY, "Mission Event False (Boolean operator)\r\n"
446 "\tReturns true N seconds after the specified event in the this mission is false "
447 "(or failed). It returns false otherwise.\r\n\r\n"
448 "Returns a boolean value; Takes 2 arguments:\r\n"
449 "\t1:\tName of the event in the mission.\r\n"
450 "\t2:\tNumber of seconds to delay before returning true."},
452 { OP_EVENT_INCOMPLETE, "Mission Event Incomplete (Boolean operator)\r\n"
453 "\tReturns true if the specified event in the this mission is incomplete. This "
454 "sexpression will only be useful in conjunction with another sexpression like"
455 "has-time-elapsed. Used alone, it will return true upon misison startup."
456 "Returns a boolean value; Takes 1 argument:\r\n"
457 "\t1:\tName of the event in the mission."},
459 { OP_IS_DESTROYED_DELAY, "Is destroyed delay (Boolean operator)\r\n"
460 "\tBecomes true <delay> seconds after all specified ships have been destroyed.\r\n\r\n"
461 "Returns a boolean value; Takes 2 or more arguments:\r\n"
462 "\t1:\tTime delay in seconds (see above).\r\n"
463 "\tRest:\tName of ship (or wing) to check status of." },
465 { OP_IS_SUBSYSTEM_DESTROYED_DELAY, "Is subsystem destroyed delay (Boolean operator)\r\n"
466 "\tBecomes true <delay> seconds after the specified subsystem of the specified "
467 "ship is destroyed.\r\n\r\n"
468 "Returns a boolean value; Takes 3 arguments:\r\n"
469 "\t1:\tName of ship the subsystem we are checking is on.\r\n"
470 "\t2:\tThe name of the subsystem we are checking status of.\r\n"
471 "\t3:\tTime delay in seconds (see above)." },
473 { OP_IS_DISABLED_DELAY, "Is disabled delay (Boolean operator)\r\n"
474 "\tBecomes true <delay> seconds after the specified ship(s) are disabled. A "
475 "ship is disabled when all of it's engine subsystems are destroyed. All "
476 "ships must be diabled for this function to return true.\r\n\r\n"
477 "Returns a boolean value; Takes 2 or more arguments:\r\n"
478 "\t1:\tTime delay is seconds (see above).\r\n"
479 "\tRest:\tNames of ships to check disabled status of." },
481 { OP_IS_DISARMED_DELAY, "Is disarmed delay (Boolean operator)\r\n"
482 "\tBecomes true <delay> seconds after the specified ship(s) are disarmed. A "
483 "ship is disarmed when all of it's turret subsystems are destroyed. All "
484 "ships must be disarmed for this function to return true.\r\n\r\n"
485 "Returns a boolean value; Takes 2 or more arguments:\r\n"
486 "\t1:\tTime delay is seconds (see above).\r\n"
487 "\tRest:\tNames of ships to check disarmed status of." },
489 { OP_HAS_DOCKED_DELAY, "Has docked delay (Boolean operator)\r\n"
490 "\tBecomes true <delay> seconds after the specified ships have docked the "
491 "specified number of times.\r\n\r\n"
492 "Returns a boolean value; Takes 4 arguments:\r\n"
493 "\t1:\tThe name of the docker ship\r\n"
494 "\t2:\tThe name of the dockee ship\r\n"
495 "\t3:\tThe number of times they have to have docked\r\n"
496 "\t4:\tTime delay in seconds (see above)." },
498 { OP_HAS_UNDOCKED_DELAY, "Has undocked delay (Boolean operator)\r\n"
499 "\tBecomes true <delay> seconds after the specified ships have undocked the "
500 "specified number of times.\r\n\r\n"
501 "Returns a boolean value; Takes 4 arguments:\r\n"
502 "\t1:\tThe name of the docker ship\r\n"
503 "\t2:\tThe name of the dockee ship\r\n"
504 "\t3:\tThe number of times they have to have undocked\r\n"
505 "\t4:\tTime delay in seconds (see above)." },
507 { OP_HAS_ARRIVED_DELAY, "Has arrived delay (Boolean operator)\r\n"
508 "\tBecomes true <delay> seconds after the specified ship(s) have arrived into the mission\r\n\r\n"
509 "Returns a boolean value; Takes 2 or more arguments:\r\n"
510 "\t1:\tTime delay in seconds (see above).\r\n"
511 "\tRest:\tName of ship (or wing) we want to check has arrived." },
513 { OP_HAS_DEPARTED_DELAY, "Has departed delay (Boolean operator)\r\n"
514 "\tBecomes true <delay> seconds after the specified ship(s) or wing(s) have departed "
515 "from the mission by warping out. If any ship was destroyed, this operator will "
516 "never be true.\r\n\r\n"
517 "Returns a boolean value; Takes 2 or more arguments:\r\n"
518 "\t1:\tTime delay in seconds (see above).\r\n"
519 "\tRest:\tName of ship (or wing) we want to check has departed." },
521 { OP_WAYPOINTS_DONE_DELAY, "Waypoints done delay (Boolean operator)\r\n"
522 "\tBecomes true <delay> seconds after the specified ship has completed flying the "
523 "specified waypoint path.\r\n\r\n"
524 "Returns a boolean value; Takes 3 arguments:\r\n"
525 "\t1:\tName of ship we are checking.\r\n"
526 "\t2:\tWaypoint path we want to check if ship has flown.\r\n"
527 "\t3:\tTime delay in seconds (see above)." },
529 { OP_SHIP_TYPE_DESTROYED, "Ship Type Destroyed (Boolean operator)\r\n"
530 "\tBecomes true when the specified percentage of ship types in this mission "
531 "have been destroyed. The ship type is a generic type such as fighter/bomber, "
532 "transport, etc. Fighters and bombers count as the same type.\r\n\r\n"
533 "Returns a boolean value; Takes 2 arguments:\r\n"
534 "\t1:\tPercentage of ships that must be destroyed.\r\n"
535 "\t2:\tShip type to check for." },
537 { OP_TIME_SHIP_DESTROYED, "Time ship destroyed (Time operator)\r\n"
538 "\tReturns the time the specified ship was destroy.\r\n\r\n"
539 "Returns a numeric value; Takes 1 argument:\r\n"
540 "\t1:\tName of ship we want to check." },
542 { OP_TIME_SHIP_ARRIVED, "Time ship arrived (Time operator)\r\n"
543 "\tReturns the time the specified ship arrived into the mission.\r\n\r\n"
544 "Returns a numeric value; Takes 1 argument:\r\n"
545 "\t1:\tName of ship we want to check." },
547 { OP_TIME_SHIP_DEPARTED, "Time ship departed (Time operator)\r\n"
548 "\tReturns the time the specified ship departed the mission by warping out. Being "
549 "destroyed doesn't count departed.\r\n\r\n"
550 "Returns a numeric value; Takes 1 argument:\r\n"
551 "\t1:\tName of ship we want to check." },
553 { OP_TIME_WING_DESTROYED, "Time wing destroyed (Time operator)\r\n"
554 "\tReturns the time the specified wing was destroy.\r\n\r\n"
555 "Returns a numeric value; Takes 1 argument:\r\n"
556 "\t1:\tName of wing we want to check." },
558 { OP_TIME_WING_ARRIVED, "Time wing arrived (Time operator)\r\n"
559 "\tReturns the time the specified wing arrived into the mission.\r\n\r\n"
560 "Returns a numeric value; Takes 1 argument:\r\n"
561 "\t1:\tName of wing we want to check." },
563 { OP_TIME_WING_DEPARTED, "Time wing departed (Time operator)\r\n"
564 "\tReturns the time the specified wing departed the mission by warping out. All "
565 "ships in the wing have to have warped out. If any are destroyed, the wing can "
566 "never be considered departed.\r\n\r\n"
567 "Returns a numeric value; Takes 1 argument:\r\n"
568 "\t1:\tName of ship we want to check." },
570 { OP_MISSION_TIME, "Mission time (Time operator)\r\n"
571 "\tReturns the current time into the mission.\r\n\r\n"
572 "Returns a numeric value." },
574 { OP_TIME_DOCKED, "Time docked (Time operator)\r\n"
575 "\tReturns the time the specified ships docked.\r\n\r\n"
576 "Returns a numeric value; Takes 3 arguments:\r\n"
577 "\t1:\tThe name of the docker ship.\r\n"
578 "\t2:\tThe name of the dockee ship.\r\n"
579 "\t3:\tThe number of times they must have docked to be true." },
581 { OP_TIME_UNDOCKED, "Time undocked (Time operator)\r\n"
582 "\tReturns the time the specified ships undocked.\r\n\r\n"
583 "Returns a numeric value; Takes 3 arguments:\r\n"
584 "\t1:\tThe name of the docker ship.\r\n"
585 "\t2:\tThe name of the dockee ship.\r\n"
586 "\t3:\tThe number of times they must have undocked to be true." },
588 { OP_SHIELDS_LEFT, "Sheilds left (Status operator)\r\n"
589 "\tReturns the current level of the specified ship's shields as a percentage.\r\n\r\n"
590 "Returns a numeric value; Takes 1 argument:\r\n"
591 "\t1:\tName of ship to check." },
593 { OP_HITS_LEFT, "Hits left (Status operator)\r\n"
594 "\tReturns the current level of the specified ship's hull as a percentage.\r\n\r\n"
595 "Returns a numeric value; Takes 1 argument:\r\n"
596 "\t1:\tName of ship to check." },
598 { OP_HITS_LEFT_SUBSYSTEM, "Hits left subsystem (Status operator)\r\n"
599 "\tReturns the current level of the specified ship's subsystem integrity as a percentage.\r\n\r\n"
600 "Returns a numeric value; Takes 1 argument:\r\n"
601 "\t1:\tName of ship to check.\r\n"
602 "\t2:\tName of subsystem on ship to check." },
604 { OP_DISTANCE, "Distance (Misc. Operator)\r\n"
605 "\tReturns the distance between 2 objects. These objects can be either a ship, "
606 "a wing, or a waypoint.\r\n\r\n"
607 "Returns a numeric value; Takes 2 arguments:\r\n"
608 "\t1:\tThe name of one of the objects.\r\n"
609 "\t2:\tThe name of the other object." },
611 { OP_LAST_ORDER_TIME, "Last order time (Status operator)\r\n"
612 "\tReturns true if <count> seconds have elapsed since one or more ships have received "
613 "a meaningful order from the player. A meaningful order is currently any order that "
614 "is not the warp out order.\r\n\r\n"
615 "Returns a boolean value; Takes 2 or more arguments:\r\n"
616 "\t1:\tTime in seconds that must elapse.\r\n"
617 "\tRest:\tName of ship or wing to check for having received orders." },
619 { OP_WHEN, "When (Conditional operator)\r\n"
620 "\tPerforms specified actions when a condition becomes true\r\n\r\n"
621 "Takes 2 or more arguments:\r\n"
622 "\t1:\tBoolean expression that must be true for actions to take place.\r\n"
623 "\tRest:\tActions to take when boolean expression becomes true." },
627 { OP_CHANGE_IFF, "Change IFF (Action operator)\r\n"
628 "\tSets the specified ship(s) to the specified team.\r\n"
629 "Takes 2 or more arguments:\r\n"
630 "\t1:\tTeam to change to (\"friendly\", \"hostile\" or \"unknown\").\r\n"
631 "\tRest:\tName of ship to change team status of." },
633 { OP_MODIFY_VARIABLE, "Modify-variable (Misc. operator)\r\n"
634 "\tModifies variable to specified value\r\n\r\n"
635 "Takes 2 arguments:\r\n"
636 "\t1:\tName of Variable.\r\n"
637 "\t2:\tValue to be set." },
639 { OP_PROTECT_SHIP, "Protect ship (Action operator)\r\n"
640 "\tProtects a ship from being attacked by any enemy ship. Any ship"
641 "that is protected will not come under enemy fire.\r\n\r\n"
642 "Takes 1 or more arguments:\r\n"
643 "\tAll:\tName of ship(s) to protect." },
645 { OP_UNPROTECT_SHIP, "Unprotect ship (Action operator)\r\n"
646 "\tUnprotects a ship from being attacked by any enemy ship. Any ship"
647 "that is not protected can come under enemy fire. This function is the opposite"
648 "of protect-ship.\r\n\r\n"
649 "Takes 1 or more arguments:\r\n"
650 "\tAll:\tName of ship(s) to protect." },
652 { OP_BEAM_PROTECT_SHIP, "Beam Protect ship (Action operator)\r\n"
653 "\tProtects a ship from being attacked with beam weapon. Any ship"
654 "that is beam protected will not come under enemy beam fire.\r\n\r\n"
655 "Takes 1 or more arguments:\r\n"
656 "\tAll:\tName of ship(s) to protect." },
658 { OP_BEAM_UNPROTECT_SHIP, "Beam Unprotect ship (Action operator)\r\n"
659 "\tUnprotects a ship from being attacked with beam weapon. Any ship"
660 "that is not beam protected can come under enemy beam fire. This function is the opposite"
661 "of beam-protect-ship.\r\n\r\n"
662 "Takes 1 or more arguments:\r\n"
663 "\tAll:\tName of ship(s) to protect." },
665 { OP_SEND_MESSAGE, "Send message (Action operator)\r\n"
666 "\tSends a message to the player. Can be send by a ship, wing, or special "
667 "source. To send it from a special source, make the first character of the first "
668 "argument a \"#\".\r\n\r\n"
669 "Takes 3 arguments:\r\n"
670 "\t1:\tName of who the message is from.\r\n"
671 "\t2:\tPriority of message (\"Low\", \"Normal\" or \"High\").\r\n"
672 "\t3:\tName of message (from message editor)." },
674 { OP_SELF_DESTRUCT, "Self destruct (Action operator)\r\n"
675 "\tCauses the specified ship(s) to self destruct.\r\n\r\n"
676 "Takes 1 or more arguments:\r\n"
677 "\tAll:\tName of ship to self destruct." },
679 { OP_NEXT_MISSION, "Next Mission (Action operator)\r\n"
680 "\tThe next mission operator is used for campaign branching in the campaign editor. "
681 "It specifies which mission should played be next in the campaign. This operator "
682 "generally follows a 'when' or 'cond' statment in the campaign file.\r\n\r\n"
683 "Takes 1 argument:\r\n"
684 "\t1:\tName of mission (filename) to proceed to." },
686 { OP_CLEAR_GOALS, "Clear goals (Action operator)\r\n"
687 "\tClears the goals for the specified ships and/or wings.\r\n\r\n"
688 "Takes 1 or more arguments:\r\n"
689 "\tAll:\tName of ship or wing." },
691 { OP_ADD_GOAL, "Add goal (Action operator)\r\n"
692 "\tAdds a goal to a ship or wing.\r\n\r\n"
693 "Takes 2 arguments:\r\n"
694 "\t1:\tName of ship or wing to all goal to.\r\n"
695 "\t2:\tGoal to add." },
697 { OP_SABOTAGE_SUBSYSTEM, "Sabotage subystem (Action operator)\r\n"
698 "\tReduces the specified subsystem integrity by the specified percentage."
699 "If the percntage strength of the subsystem (after completion) is less than 0%,"
700 "subsystem strength is set to 0%.\r\n\r\n"
701 "Takes 3 arguments:\r\n"
702 "\t1:\tName of ship subsystem is on.\r\n"
703 "\t2:\tName of subsystem to sabotage.\r\n"
704 "\t3:\tPercentage to reduce subsystem integrity by." },
706 { OP_REPAIR_SUBSYSTEM, "Repair Subystem (Action operator)\r\n"
707 "\tIncreases the specified subsystem integrity by the specified percentage."
708 "If the percntage strength of the subsystem (after completion) is greater than 100%,"
709 "subsystem strength is set to 100%.\r\n\r\n"
710 "Takes 3 arguments:\r\n"
711 "\t1:\tName of ship subsystem is on.\r\n"
712 "\t2:\tName of subsystem to repair.\r\n"
713 "\t3:\tPercentage to increase subsystem integrity by." },
715 { OP_SET_SUBSYSTEM_STRNGTH, "Set Subsystem Strength (Action operator)\r\n"
716 "\tSets the specified subsystem to the the specified percentage."
717 "If the percentage specified is < 0, strength is set to 0. If the percentage is "
718 "> 100 % the subsystem strength is set to 100%.\r\n\r\n"
719 "Takes 3 arguments:\r\n"
720 "\t1:\tName of ship subsystem is on.\r\n"
721 "\t2:\tName of subsystem to set strength.\r\n"
722 "\t3:\tPercentage to set subsystem integrity to." },
724 { OP_INVALIDATE_GOAL, "Invalidate goal (Action operator)\r\n"
725 "\tMakes a mission goal invalid, which causes it to now show up on mission goals "
726 "screen, or be evaluated.\r\n"
727 "Takes 1 or more arguments:\r\n"
728 "\tAll:\tName of mission goal to invalidate." },
730 { OP_VALIDATE_GOAL, "Validate goal (Action operator)\r\n"
731 "\tMakes a mission goal valid again, so it shows up on mission goals screen.\r\n"
732 "Takes 1 or more arguments:\r\n"
733 "\tAll:\tName of mission goal to validate." },
735 { OP_SEND_RANDOM_MESSAGE, "Send random message (Action operator)\r\n"
736 "\tSends a random message to the player from those supplied. Can be send by a "
737 "ship, wing, or special source. To send it from a special source, make the first "
738 "character of the first argument a \"#\".\r\n\r\n"
739 "Takes 3 or more arguments:\r\n"
740 "\t1:\tName of who the message is from.\r\n"
741 "\t2:\tPriority of message (\"Low\", \"Normal\" or \"High\")."
742 "\tRest:\tName of message (from message editor)." },
744 { OP_TRANSFER_CARGO, "Transfer Cargo (Action operator)\r\n"
745 "\tTransfers the cargo from one ship to another ship.\r\n\r\n"
746 "Takes 2 arguments:\r\n"
747 "\t1:\tName of ship that cargo is being transferred from.\r\n"
748 "\t2:\tName of ship that cargo is being transferred to." },
750 { OP_EXCHANGE_CARGO, "Exchange Cargo (Action operator)"
751 "\tExchanges the cargos of two ships. If one of the two ships contains no cargo, "
752 "the cargo is transferred instead.\r\n"
753 "Takes 2 arguments:\r\n"
754 "\t1:\tName of one of the ships.\r\n"
755 "\t2:\tName of the other ship." },
757 { OP_INT3, "Error (Debug directive)\r\n"
758 "Causes the game to halt with an error." },
760 { OP_AI_CHASE, "Ai-chase (Ship goal)\r\n"
761 "\tCauses the specified ship to chase and attack the specified target.\r\n\r\n"
762 "Takes 2 arguments:\r\n"
763 "\t1:\tName of ship to chase.\r\n"
764 "\t2:\tGoal priority (number between 0 and 89)." },
766 { OP_AI_DOCK, "Ai-dock (Ship goal)\r\n"
767 "\tCauses one ship to dock with another ship.\r\n\r\n"
768 "Takes 4 arguments:\r\n"
769 "\t1:\tName of dockee ship (The ship that \"docker\" will dock with).\r\n"
770 "\t2:\tDocker's docking point - Which dock point docker uses to dock.\r\n"
771 "\t3:\tDockee's docking point - Which dock point on dockee docker will move to.\r\n"
772 "\t4:\tGoal priority (number between 0 and 89)." },
774 { OP_AI_UNDOCK, "Ai-undock (Ship goal)\r\n"
775 "\tCauses the specified ship to undock from who it is currently docked with.\r\n\r\n"
776 "Takes 1 arguments:\r\n"
777 "\t1:\tGoal priority (number between 0 and 89)." },
779 { OP_AI_WARP_OUT, "Ai-warp-out (Ship/Wing Goal)\r\n"
780 "\tCauses the specified ship/wing to warp out of the mission. Currently, the ship will "
781 "warp out at it's current location. This behavior will change. Currently, the first "
782 "argument means nothing.\r\n\r\n"
783 "Takes 2 arguments:\r\n"
784 "\t1:\tName of waypoint path to follow to warp out (not used).\r\n"
785 "\t2:\tGoal priority (number between 0 and 89)." },
787 { OP_AI_WAYPOINTS, "Ai-waypoints (Ship goal)\r\n"
788 "\tCauses the specified ship to fly a waypoint path continuously.\r\n\r\n"
789 "Takes 2 arguments:\r\n"
790 "\t1:\tName of waypoint path to fly.\r\n"
791 "\t2:\tGoal priority (number between 0 and 89)." },
793 { OP_AI_WAYPOINTS_ONCE, "Ai-waypoints once (Ship goal)\r\n"
794 "\tCauses the specified ship to fly a waypoint path.\r\n\r\n"
795 "Takes 2 arguments:\r\n"
796 "\t1:\tName of waypoint path to fly.\r\n"
797 "\t2:\tGoal priority (number between 0 and 89)." },
799 { OP_AI_DESTROY_SUBSYS, "Ai-destroy subsys (Ship goal)\r\n"
800 "\tCauses the specified ship to attack and try and destroy the specified subsystem "
801 "on the specified ship.\r\n\r\n"
802 "Takes 3 arguments:\r\n"
803 "\t1:\tName of ship subsystem is on.\r\n"
804 "\t2:\tName of subsystem on the ship to attack and destroy.\r\n"
805 "\t3:\tGoal priority (number between 0 and 89)." },
807 { OP_AI_CHASE_WING, "Ai-chase wing (Ship goal)\r\n"
808 "\tCauses the specified ship to chase and attack the specified target.\r\n\r\n"
809 "Takes 2 arguments:\r\n"
810 "\t1:\tName of wing to chase.\r\n"
811 "\t2:\tGoal priority (number between 0 and 89)." },
813 { OP_AI_DISABLE_SHIP, "Ai-disable-ship (Ship/wing goal)\r\n"
814 "\tThis AI goal causes a ship/wing to destroy all of the engine subsystems on "
815 "the specified ship. This goal is different than ai-destroy-subsystem since a ship "
816 "may have multiple engine subsystems requiring the use of > 1 ai-destroy-subsystem "
818 "Takes 2 arguments:\r\n"
819 "\t1:\tName of ship whose engine subsystems should be destroyed\r\n"
820 "\t2:\tGoal priority (number between 0 and 89)." },
822 { OP_AI_DISARM_SHIP, "Ai-disarm-ship (Ship/wing goal)\r\n"
823 "\tThis AI goal causes a ship/wing to destroy all of the turret subsystems on "
824 "the specified ship. This goal is different than ai-destroy-subsystem since a ship "
825 "may have multiple turret subsystems requiring the use of > 1 ai-destroy-subsystem "
827 "Takes 2 arguments:\r\n"
828 "\t1:\tName of ship whose turret subsystems should be destroyed\r\n"
829 "\t2:\tGoal priority (number between 0 and 89)." },
831 { OP_AI_GUARD, "Ai-guard (Ship goal)\r\n"
832 "\tCauses the specified ship to guard a ship from other ships not on the same team.\r\n\r\n"
833 "Takes 2 arguments:\r\n"
834 "\t1:\tName of ship to guard.\r\n"
835 "\t2:\tGoal priority (number between 0 and 89)." },
837 { OP_AI_CHASE_ANY, "Ai-chase any (Ship goal)\r\n"
838 "\tCauses the specified ship to chase and attack any ship on the opposite team.\r\n\r\n"
839 "Takes 1 arguments:\r\n"
840 "\t1:\tGoal priority (number between 0 and 89)." },
842 { OP_AI_GUARD_WING, "Ai-guard wing (Ship goal)\r\n"
843 "\tCauses the specified ship to guard a wing of ships from other ships not on the "
845 "Takes 2 arguments:\r\n"
846 "\t1:\tName of wing to guard.\r\n"
847 "\t2:\tGoal priority (number between 0 and 89)." },
849 { OP_NOP, "Do-nothing (Action operator)\r\n"
850 "\tDoes nothing. This is used as the default for any required action arguments "
853 { OP_KEY_PRESSED, "Key-pressed (Boolean training operator)\r\n"
854 "\tBecomes true when the specified default key has been pressed. Default key "
855 "refers to the what the key normally is when not remapped. FreeSpace will "
856 "automatically account for any keys that have been remapped. If the optional "
857 "delay is specified, becomes true that many seconds after the key has been pressed.\r\n\r\n"
858 "Returns a boolean value; Takes 1 or 2 arguments:\r\n"
859 "\t1:\tDefault key to check for.\r\n"
860 "\t2:\tDelay before operator registers as true (optional).\r\n" },
862 { OP_KEY_RESET, "Key-reset (Training operator)\r\n"
863 "\tMarks the specified default key as having not been pressed, so key-pressed will be false "
864 "until the player presses it again. See key-pressed help for more information about "
865 "what a default key is.\r\n\r\n"
866 "Returns a boolean value; Takes 1 argument:\r\n"
867 "\t1:\tDefault key to reset." },
869 { OP_TARGETED, "Targeted (Boolean training operator)\r\n"
870 "\tIs true as long as the player has the specified ship (or ship's subsystem) targeted, "
871 "or has been targeted for the specified amount of time.\r\n\r\n"
872 "Returns a boolean value; Takes 1 to 3 arguments (first required, rest optional):\r\n"
873 "\t1:\tName of ship to check if targeted by player.\r\n"
874 "\t2:\tLength of time target should have been kept for (optional).\r\n"
875 "\t3:\tName of subsystem on ship to check if targeted (optional)." },
877 { OP_SPEED, "Speed (Boolean training operator)\r\n"
878 "\tBecomes true when the player has been within the specified speed range set by "
879 "set-training-context-speed for the specified amount of time.\r\n\r\n"
880 "Returns a boolean value; Takes 1 argument:\r\n"
881 "\t1:\tTime in seconds." },
883 { OP_FACING, "Facing (Boolean training operator)\r\n"
884 "\tIs true as long as the specified ship is within the player's specified "
885 "forward cone. A forward cone is defined as any point that the angle between the "
886 "vector of the point and the player, and the forward facing vector is within the "
887 "given angle.\r\n\r\n"
888 "Returns a boolean value; Takes 2 argument:\r\n"
889 "\t1:\tShip to check is withing forward cone.\r\n"
890 "\t2:\tAngle in degrees of the forward cone." },
892 { OP_FACING2, "Facing Waypoint(Boolean training operator)\r\n"
893 "\tIs true as long as the specified first waypoint is within the player's specified "
894 "forward cone. A forward cone is defined as any point that the angle between the "
895 "vector of the point and the player, and the forward facing vector is within the "
896 "given angle.\r\n\r\n"
897 "Returns a boolean value; Takes 2 argument:\r\n"
898 "\t1:\tName of waypoint path whose first point is withing forward cone.\r\n"
899 "\t2:\tAngle in degrees of the forward cone." },
901 { OP_ORDER, "Order (Boolean training operator)\r\n"
902 "\tBecomes true when the player had given the specified ship or wing the specified order.\r\n\r\n"
903 "Returns a boolean value; Takes 2 arguments:\r\n"
904 "\t1:\tName of ship or wing to check if given order to.\r\n"
905 "\t2:\tName of order to check if player has given." },
907 { OP_WAYPOINT_MISSED, "Waypoint-missed (Boolean training operator)\r\n"
908 "\tBecomes true when a waypoint is flown, but the waypoint is ahead of the one "
909 "they are supposed to be flying. The one they are supposed to be flying is the "
910 "next one in sequence in the path after the last one they have hit.\r\n\r\n"
911 "Returns a boolean value; Takes no arguments." },
913 { OP_PATH_FLOWN, "Path-flown (Boolean training operator)\r\n"
914 "\tBecomes true when all the waypoints in the path have been flown, in sequence.\r\n\r\n"
915 "Returns a boolean value; Takes no arguments." },
917 { OP_WAYPOINT_TWICE, "Waypoint-twice (Boolean training operator)\r\n"
918 "\tBecomes true when a waypoint is hit that is before the last one hit, which "
919 "indicates they have flown a waypoint twice.\r\n\r\n"
920 "Returns a boolean value; Takes no arguments." },
922 { OP_TRAINING_MSG, "Training-msg (Action training operator)\r\n"
923 "\tSends the player a training message. Uses the same messages as normal messages, "
924 "only they get displayed differently using this operator. If a secondary message "
925 "is specified, it is sent the last time, while the primary message is sent all other "
926 "times (event should have a repeat count greater than 1).\r\n\r\n"
927 "Takes 1-3 arguments:\r\n"
928 "\t1:\tName of primary message to send.\r\n"
929 "\t2:\tName of secondary message to send (optional).\r\n"
930 "\t3:\tDelay (in seconds) to wait before sending message. (optional)\r\n"
931 "\t4:\tAmount of Time (in seconds) to display message (optional)." },
933 { OP_SET_TRAINING_CONTEXT_FLY_PATH, "Set-training-context-fly-path (Training context operator)\r\n"
934 "\tTells FreeSpace that the player is expected to fly a waypoint path. This must be "
935 "executed before waypoint-missed, waypoint-twice and path-flown operators become valid.\r\n\r\n"
936 "Takes 2 arguments:\r\n"
937 "\t1:\tName of waypoint path player should fly.\r\n"
938 "\t2:\tDistance away a player needs to be from a waypoint for it to be registered as flown." },
940 { OP_SET_TRAINING_CONTEXT_SPEED, "Set-training-context-speed (Training context operator)\r\n"
941 "\tTells FreeSpace that the player is expected to fly within a certain speed range. Once "
942 "this operator has been executed, you can measure how long they have been within this "
943 "speed range with the speed operator.\r\n\r\n"
944 "Takes 2 arguments:\r\n"
945 "\t1:\tMinimum speed of range player is to fly between.\r\n"
946 "\t2:\tMaximum speed of range player is to fly between." },
948 { OP_GRANT_PROMOTION, "Grant promotion (Action operator)\r\n"
949 "\tIn a single player game, this function grants a player an automatic promotion to the "
950 "next rank which the player can obtain. If he is already at the highest rank, this "
951 "operator has no effect. It takes no arguments." },
953 { OP_GRANT_MEDAL, "Grant medal (Action operator)\r\n"
954 "\tIn single player missions, this function grants the given medal to the player. "
955 "Currently, only 1 medal will be allowed to be given per mission.\r\n\r\n"
956 "Takes 1 argument:\r\n"
957 "\t1:\tName of medal to grant to player." },
959 { OP_GOOD_SECONDARY_TIME, "Set prefered secondary weapons\r\n"
960 "\tThis sexpression is used to inform the AI about prefered secondary weapons to "
961 "fire during combat. When this expression is evaulated, any AI ships of the given "
962 "team prefer to fire the given weapon at the given ship. (Prefered over other "
963 "secondary weapons)\r\n\r\n"
964 "Takes 4 argument:\r\n"
965 "\t1:\tTeam name which will prefer firing given weapon\r\n"
966 "\t2:\tMaximum number of this type of weapon above team can fire.\r\n"
967 "\t3:\tWeapon name (list includes only the valid weapons for this expression\r\n"
968 "\t4:\tShip name at which the above named team should fire the above named weapon." },
970 { OP_AND_IN_SEQUENCE, "And in sequence (Boolean operator)\r\n"
971 "\tReturns true if all of it's arguments have become true in the order they are "
973 "Returns a boolean value; Takes 2 or more boolean arguments." },
975 { OP_SKILL_LEVEL_AT_LEAST, "Skill level at least (Boolean operator)\r\n"
976 "\tReturns true if the player has selected the given skill level or higher.\r\n\r\n"
977 "Returns a boolean value; Takes 1 arguments:\r\n"
978 "\t1:\tName of the skill level to check." },
980 { OP_NUM_PLAYERS, "Num players (Status operator)\r\n"
981 "\tReturns the current number of players (multiplayer) playing in the current mission.\r\n\r\n"
982 "Returns a numeric value; Takes no arguments." },
984 { OP_IS_CARGO_KNOWN, "Is cargo known (Boolean operator)\r\n"
985 "\tReturns true if all of the specified objects' cargo is known by the player (i.e. they "
986 "have scanned each one.\r\n\r\n"
987 "Returns a boolean value; Takes 1 or more arguments:\r\n"
988 "\tAll:\tName of ship to check if it's cargo is known." },
990 { OP_HAS_BEEN_TAGGED_DELAY, "Has ship been tagged (delay) (Boolean operator)\r\n"
991 "\tReturns true if all of the specified ships have been tagged.\r\n\r\n"
992 "Returns a boolean value after <delay> seconds when all ships have been tagged; Takes 2 or more arguments:\r\n"
993 "\t1:\tDelay in seconds after which sexpression will return true when all cargo scanned."
994 "\tRest:\tNames of ships to check if tagged.." },
996 { OP_CAP_SUBSYS_CARGO_KNOWN_DELAY, "Is capital ship subsystem cargo known (delay) (Boolean operator)\r\n"
997 "\tReturns true if all of the specified subsystem cargo is known by the player.\r\n"
998 "\tNote: Cargo must be explicitly named.\r\n\r\n"
999 "Returns a boolean value after <delay> seconds when all cargo is known; Takes 3 or more arguments:\r\n"
1000 "\t1:\tDelay in seconds after which sexpression will return true when all cargo scanned.\r\n"
1001 "\t2:\tName of Captial ship\r\n"
1002 "\tRest:\tNames of subsystems to check for cargo known.." },
1004 { OP_CARGO_KNOWN_DELAY, "Is cargo known (delay) (Boolean operator)\r\n"
1005 "\tReturns true if all of the specified objects' cargo is known by the player (i.e. they "
1006 "have scanned each one.\r\n\r\n"
1007 "Returns a boolean value after <delay> seconds when all cargo is known; Takes 2 or more arguments:\r\n"
1008 "\t1:\tDelay in seconds after which sexpression will return true when all cargo scanned."
1009 "\tRest:\tNames of ships/cargo to check for cargo known.." },
1011 { OP_WAS_PROMOTION_GRANTED, "Was promotion granted (Boolean operator)\r\n"
1012 "\tReturns true if a promotion was granted via the 'Grant promotion' operator in the mission.\r\n\r\n"
1013 "Returns a boolean value; Takes no arguments." },
1015 { OP_WAS_MEDAL_GRANTED, "Was medal granted (Boolean operator)\r\n"
1016 "\tReturns true if a medal was granted via via the 'Grant medal' operator in the mission. "
1017 "If you provide the optional argument to this operator, then true is only returned if the "
1018 "specified medal was granted.\r\n\r\n"
1019 "Returns a boolean value; Takes 0 or 1 arguments:\r\n"
1020 "\t1:\tName of medal to specifically check for (optional)." },
1022 { OP_GOOD_REARM_TIME, "Good rearm time (Action operator)\r\n"
1023 "\tInforms the game logic that right now is a good time for a given team to attempt to "
1024 "rearm their ships. The time parameter specified how long the \"good time\" will last.\r\n\r\n"
1025 "Takes 2 arguments:\r\n"
1026 "\t1:\tTeam Name\r\n"
1027 "\t2:\tTime in seconds rearm window should last" },
1029 { OP_ALLOW_SHIP, "Allow ship (Action operator)\r\n"
1030 "\tThis operator makes the given ship type available to the Terran team. Players will be "
1031 "able to have ships of this type in their starting wings in all future missions of this "
1033 "Takes 1 arguments:\r\n"
1034 "\t1:\tName of ship type (or ship class) to allow." },
1036 { OP_ALLOW_WEAPON, "Allow weapon (Action operator)\r\n"
1037 "\tThis operator makes the given weapon available to the Terran team. Players will be "
1038 "able to equip ships with in all future missions of this campaign.\r\n\r\n"
1039 "Takes 1 arguments:\r\n"
1040 "\t1:\tName of weapon (primary or secondary) to allow." },
1042 { OP_TECH_ADD_SHIP, "Tech add ship (Action operator)\r\n"
1043 "\tThis operator makes the given ship type available in the techroom database. Players will "
1044 "then be able to view this ship's specs there.\r\n\r\n"
1045 "Takes 1 or more arguments:\r\n"
1046 "\tAll:\tName of ship type (or ship class) to add." },
1048 { OP_TECH_ADD_WEAPON, "Tech add weapon (Action operator)\r\n"
1049 "\tThis operator makes the given weapon available in the techroom database. Players will "
1050 "then be able to view this weapon's specs there.\r\n\r\n"
1051 "Takes 1 or more arguments:\r\n"
1052 "\tAll:\tName of weapon (primary or secondary) to add." },
1054 { OP_AI_EVADE_SHIP, "Ai-evade ship (Ship goal)\r\n"
1055 "\tCauses the specified ship to go into evade mode and run away like the weak "
1056 "sally-boy it is.\r\n\r\n"
1057 "Takes 2 arguments:\r\n"
1058 "\t1:\tName of ship to evade from.\r\n"
1059 "\t2:\tGoal priority (number between 0 and 89)." },
1061 { OP_AI_STAY_NEAR_SHIP, "Ai-stay near ship (Ship goal)\r\n"
1062 "\tCauses the specified ship to keep itself near the given ship and not stray too far "
1063 "away from it.\r\n\r\n"
1064 "Takes 2 arguments:\r\n"
1065 "\t1:\tName of ship to stay near.\r\n"
1066 "\t2:\tGoal priority (number between 0 and 89)." },
1068 { OP_AI_KEEP_SAFE_DISTANCE, "Ai-keep safe distance (Ship goal)\r\n"
1069 "\tTells the specified ship to stay a safe distance away from any ship that isn't on the "
1070 "same team as it.\r\n\r\n"
1071 "Takes 1 argument:\r\n"
1072 "\t1:\tGoal priority (number between 0 and 89)." },
1074 { OP_AI_IGNORE, "Ai-ignore (Ship goal)\r\n"
1075 "\tTells the specified ship to ignore the given ship and not consider it as a valid "
1076 "target to attack.\r\n\r\n"
1077 "Takes 2 arguments:\r\n"
1078 "\t1:\tName of ship to ignore.\r\n"
1079 "\t2:\tGoal priority (number between 0 and 89)." },
1081 { OP_AI_STAY_STILL, "Ai-stay still (Ship goal)\r\n"
1082 "\tCauses the specified ship to stay still. The ship will do nothing until attacked at "
1083 "which time the ship will come to life and defend itself.\r\n\r\n"
1084 "Takes 2 arguments:\r\n"
1085 "\t1:\tShip or waypoint the ship staying still will directly face (currently not implemented)\r\n"
1086 "\t2:\tGoal priority (number between 0 and 89)." },
1088 { OP_AI_PLAY_DEAD, "Ai-play dead (Ship goal)\r\n"
1089 "\tCauses the specified ship to pretend that it is dead and not do anything. This "
1090 "expression should be used to indicate that a ship has no pilot and cannot respond "
1091 "to any enemy threats. A ship playing dead will not respond to any attack. This "
1092 "should really be named ai-is-dead\r\n\r\n"
1093 "Takes 1 argument:\r\n"
1094 "\t1:\tGoal priority (number between 0 and 89)." },
1096 { OP_FLASH_HUD_GAUGE, "Ai-flash hud gauge (Training goal)\r\n"
1097 "\tCauses the specified hud gauge to flash to draw the player's attention to it.\r\n\r\n"
1098 "Takes 1 argument:\r\n"
1099 "\t1:\tName of hud gauge to flash." },
1101 { OP_SHIP_VISIBLE, "ship-visible\r\n"
1102 "\tCauses the ships listed in this sexpression to be visible with player sensors.\r\n\r\n"
1103 "Takes 1 or more arguments:\r\n"
1104 "\t1+:\tName of ships to make visible to sensors." },
1106 { OP_SHIP_INVISIBLE, "ship-invisible\r\n"
1107 "\tCauses the ships listed in this sexpression to be invisible to player sensors.\r\n\r\n"
1108 "Takes 1 or more arguments:\r\n"
1109 "\t1+:\tName of ships to make invisible to sensors." },
1111 { OP_SHIP_VULNERABLE, "ship-vulnerable\r\n"
1112 "\tCauses the ship listed in this sexpression to be vulnerable to weapons.\r\n\r\n"
1113 "Takes 1 or more arguments:\r\n"
1114 "\t1+:\tName of ships to make vulnerable to weapons." },
1116 { OP_SHIP_INVULNERABLE, "ship-invulnerable\r\n"
1117 "\tCauses the ships listed in this sexpression to be invulnerable to weapons. Use with caution!!!!\r\n\r\n"
1118 "Takes 1 or more arguments:\r\n"
1119 "\t1+:\tName of ships to make invulnerable to weapons." },
1121 { OP_SHIP_GUARDIAN, "ship-guardian\r\n"
1122 "\tCauses the ships listed in this sexpression to not be killable by weapons. Use with caution!!!!\r\n\r\n"
1123 "Takes 1 or more arguments:\r\n"
1124 "\t1+:\tName of ships to make invulnerable to weapons." },
1126 { OP_SHIP_NO_GUARDIAN, "ship-no-guardian\r\n"
1127 "\tCauses the ships listed in this sexpression to be killable by weapons, if not invulnerable.\r\n\r\n"
1128 "Takes 1 or more arguments:\r\n"
1129 "\t1+:\tName of ships to make vulnerable to weapons." },
1131 { OP_PERCENT_SHIPS_DEPARTED, "percent-ships-departed\r\n"
1132 "\tBoolean function which returns true if the percentage of ships in the listed ships and wings "
1133 "which have departed is greater or equal to the given percentage. For wings, all ships of all waves "
1134 "are used for calculation for the total possible ships to depart.\r\n\r\n"
1135 "Takes 2 or more arguments:\r\n"
1136 "\t1:\tPercentge of departed ships at which this function will return true.\r\n"
1137 "\t2+:\tList of ships/wing whose departure status should be determined." },
1139 { OP_PERCENT_SHIPS_DESTROYED, "percent-ships-destroyed\r\n"
1140 "\tBoolean function which returns true if the percentage of ships in the listed ships and wings "
1141 "which have been destroyed is greater or equal to the given percentage. For wings, all ships of all waves "
1142 "are used for calculation for the total possible ships to be destroyed.\r\n\r\n"
1143 "Takes 2 or more arguments:\r\n"
1144 "\t1:\tPercentge of destroyed ships at which this function will return true.\r\n"
1145 "\t2+:\tList of ships/wing whose destroyed status should be determined." },
1147 { OP_RED_ALERT, "red-alert\r\n"
1148 "\tCauses Red Alert status in a mission. This function ends the current mission, and moves to "
1149 "the next mission in the campaign under red alert status. There should only be one branch from "
1150 "a mission that uses this expression\r\n\r\n"
1151 "Takes no arguments."},
1153 { OP_DEPART_NODE_DELAY, "depart-node-delay\r\n"
1154 "\tReturns true N seconds after the listed ships depart, if those ships depart within the "
1155 "radius of the given jump node. The delay value is given in seconds.\r\n\r\n"
1156 "Takes 3 or more arguments:r\n"
1157 "\t1:\tDelay in seconds after the last ship listed departe before this expression can return true.\r\n"
1158 "\t2:\tName of a jump node\r\n"
1159 "\t3+:\tList of ships to check for departure within radius of the jump node." },
1161 { OP_DESTROYED_DEPARTED_DELAY, "destroyed-or-departed-delay\r\n"
1162 "\tReturns true N seconds after all the listed ships or wings have been destroyed or have "
1164 "Takes 2 or more arguments:\r\n"
1165 "\t1:\tDelay in seconda after the last ship/wing is destroyed or departerd this expression can return true.\r\n"
1166 "\t2+:\tName of a ship or wing" },
1168 { OP_SPECIAL_CHECK, "Special-check\r\n"
1169 "\tDo some special check in training. Ask Mike K. about how it works.\r\n\r\n"
1170 "Returns a boolean value; Takes 1 argument:\r\n"
1171 "\t1:\tExtra special number (tm)" },
1173 { OP_END_CAMPAIGN, "end-campaign\r\n"
1174 "\tEnds the builtin campaign. Should only be used by the main FreeSpace campaign\r\n" },
1176 { OP_WARP_BROKEN, "break-warp\r\n"
1177 "\tBreak the warp drive on the specified ship. A broken warp drive can be repaired by "
1178 "a repair ship. Takes 1 or more arguments:\r\n"
1179 "\t1:\tList of ships to break the warp drive on" },
1180 { OP_WARP_NOT_BROKEN, "fix-warp\r\n"
1181 "\tFixes a broken warp drive instantaneously. This option applies to warp drives broken with "
1182 "the break-warp sepxression. Takes 1 or more arguments:\r\n"
1183 "\t1:\tList of ships whose warp drive should be fixed"},
1184 { OP_WARP_NEVER, "never-warp\r\n"
1185 "\tNever allows a ship to warp out. When this sexpression is used, the given ships will "
1186 "never be able to warp out. The warp drive cannot be repaired. Takes 1 or more arguments:\r\n"
1187 "\t1:\tList of ships whose are not allowed to warp out under any condition"},
1188 { OP_WARP_ALLOWED, "allow-warp\r\n"
1189 "\tAllows a ship which was previously not allowed to warp out to do so. When this sexpression is "
1190 "used, the given ships will be able to warp out again. Takes 1 or more arguments:\r\n"
1191 "\t1:\tList of ships whose are allowed to warp out"},
1192 { OP_JETTISON_CARGO, "jettison-cargo-delay\r\n"
1193 "\tCauses a cargo carrying ship to jettison its cargo without the undocking procedure. Takes 2 arguments"},
1194 { OP_BEAM_FIRE, "beam-fire\r\n"
1195 "\tFire a beam weapon from a specified subsystem\r\n"
1196 "\t1:\tShip which will be firing\r\n"
1197 "\t2:\tTurret which will fire the beam (note, this turret must have at least 1 beam weapon on it)\r\n"
1198 "\t3:\tShip which will be targeted\r\n"
1199 "Use add-data to add a specific subsystem to target on the specified target ship"},
1200 { OP_IS_TAGGED, "is-tagged\r\n"
1201 "\tReturns whether a given ship is tagged or not\r\n"},
1202 { OP_NUM_KILLS, "num-kills\r\n"
1203 "\tReturns the # of kills a player has. The ship specified in the first field should be the ship the player is in.\r\n"
1204 "\tSo, for single player, this would be alpha 1. For multiplayer, it can be any ship with a player in it. If, at any\r\n"
1205 "\ttime there is no player in a given ship, this sexpression will return 0"},
1206 { OP_NUM_TYPE_KILLS, "num-type-kills\r\n"
1207 "\tReturns the # of kills a player has on a given ship type (fighter, bomber, cruiser, etc).\r\n"
1208 "The ship specified in the first field should be the ship the player is in.\r\n"
1209 "\tSo, for single player, this would be alpha 1. For multiplayer, it can be any ship with a player in it. If, at any\r\n"
1210 "\ttime there is no player in a given ship, this sexpression will return 0"},
1211 { OP_NUM_CLASS_KILLS, "num-class-kills\r\n"
1212 "\tReturns the # of kills a player has on a specific ship class (Ulysses, Hercules, etc).\r\n"
1213 "The ship specified in the first field should be the ship the player is in.\r\n"
1214 "\tSo, for single player, this would be alpha 1. For multiplayer, it can be any ship with a player in it. If, at any\r\n"
1215 "\ttime there is no player in a given ship, this sexpression will return 0"},
1216 { OP_BEAM_FREE, "beam-free\r\n"
1217 "\tSets one or more beam weapons to allow firing for a given ship\r\n"
1218 "\t1: Ship to be operated on\r\n"
1219 "\t2, 3, etc : List of turrets to activate\r\n"},
1220 { OP_BEAM_FREE_ALL, "beam-free-all\r\n"
1221 "\tSets all beam weapons on the specified ship to be active\r\n"},
1222 { OP_BEAM_LOCK, "beam-lock\r\n"
1223 "\tSets one or more beam weapons to NOT allow firing for a given ship\r\n"
1224 "\t1: Ship to be operated on\r\n"
1225 "\t2, 3, etc : List of turrets to deactivate\r\n"},
1226 { OP_BEAM_LOCK_ALL, "beam-lock-all\r\n"
1227 "\tSets all beam weapons on the specified ship to be deactivated\r\n"},
1228 { OP_TURRET_FREE, "turret-free\r\n"
1229 "\tSets one or more turret weapons to allow firing for a given ship\r\n"
1230 "\t1: Ship to be operated on\r\n"
1231 "\t2, 3, etc : List of turrets to activate\r\n"},
1232 { OP_TURRET_FREE_ALL, "turret-free-all\r\n"
1233 "\tSets all turret weapons on the specified ship to be active\r\n"},
1234 { OP_TURRET_LOCK, "turret-lock\r\n"
1235 "\tSets one or more turret weapons to NOT allow firing for a given ship\r\n"
1236 "\t1: Ship to be operated on\r\n"
1237 "\t2, 3, etc : List of turrets to deactivate\r\n"},
1238 { OP_TURRET_LOCK_ALL, "turret-lock-all\r\n"
1239 "\tSets all turret weapons on the specified ship to be deactivated\r\n"},
1240 { OP_ADD_REMOVE_ESCORT, "add-remove-escort\r\n"
1241 "\tAdds or removes a ship from an escort list.\r\n"
1242 "\t1: Ship to be added or removed\r\n"
1243 "\t2: 0 to remove from the list, any positive value to add to the list\r\n"
1244 "NOTE : it _IS_ safe to add a ship which may already be on the list or remove\r\n"
1245 "a ship which is not on the list\r\n"},
1246 { OP_AWACS_SET_RADIUS, "awacs-set-radius\r\n"
1247 "\tSets the awacs radius for a given ship subsystem. NOTE : does not work properly in multiplayer\r\n"
1248 "\t1: Ship which has the awacs subsystem\r\n"
1249 "\t2: Awacs subsystem\r\n"
1250 "\t3: New radius\r\n"},
1251 { OP_SEND_MESSAGE_LIST, "send-message-list\r\n"
1252 "\tSends a series of delayed messages. All times are accumulated"
1253 "\t1:\tName of who the message is from.\r\n"
1254 "\t2:\tPriority of message (\"Low\", \"Normal\" or \"High\").\r\n"
1255 "\t3:\tName of message (from message editor).\r\n"
1256 "\t4:\tDelay in ms\r\n"
1257 "Use Add-Data for multiple messages"
1258 "IMPORTANT : each additional message in the list MUST HAVE 4 entries\r\n"
1259 "any message without the 4 proper fields will be ignore, as will any\r\n"
1260 "successive messages"},
1261 { OP_CAP_WAYPOINT_SPEED, "cap-waypoint-speed\r\n"
1262 "\tSets the maximum speed of a ship while flying waypoints.\r\n"
1263 "\t1: Ship name\r\n"
1264 "\t2: Maximum speed while flying waypoints\r\n"
1265 "\tNOTE: This will only work if the ship is already in the game\r\n"
1266 "\tNOTE: Set to -1 to reset\r\n"},
1267 { OP_TURRET_TAGGED_ONLY_ALL, "turret-tagged-only\r\n"
1268 "\tMakes turrets target and hence fire strictly at tagged objects\r\n"
1269 "\t1: Ship name\r\n"
1270 "\tNOTE: Will not stop a turret already firing at an untagged ship\r\n"},
1271 { OP_TURRET_TAGGED_CLEAR_ALL, "turret-tagged-clear\r\n"
1272 "\tRelaxes restriction on turrets targeting only tagged ships\r\n"
1273 "\t1: Ship name\r\n"},
1274 { OP_SECONDARIES_DEPLETED, "secondaries-depleted\r\n"
1275 "\tReturns true if ship is out secondary weapons\r\n"
1276 "\t1: Ship name\r\n"},
1277 { OP_SUBSYS_SET_RANDOM, "subsys-set-random\r\n"
1278 "\tSets ship subsystem strength in a given range\r\n"
1279 "\t1: Ship name\r\n"
1280 "\t2: Low range\r\n"
1281 "\t3: High range\r\n"
1282 "\t4: List of subsys names not to be randomized\r\n"},
1283 { OP_SUPERNOVA_START, "supernova-start\r\n"
1284 "\t1: Time in seconds until the supernova shockwave hits the player\r\n"},
1285 { OP_SHIELD_RECHARGE_PCT, "shield-recharge-pct\r\n"
1286 "\tReturns a percentage from 0 to 100\r\n"
1287 "\t1: Ship name\r\n" },
1288 { OP_ENGINE_RECHARGE_PCT, "engine-recharge-pct\r\n"
1289 "\tReturns a percentage from 0 to 100\r\n"
1290 "\t1: Ship name\r\n" },
1291 { OP_WEAPON_RECHARGE_PCT, "weapon-recharge-pct\r\n"
1292 "\tReturns a percentage from 0 to 100\r\n"
1293 "\t1: Ship name\r\n" },
1294 { OP_CARGO_NO_DEPLETE, "cargo-no-deplete\r\n"
1295 "\tCauses the named ship to have unlimited cargo.\r\n"
1296 "\tNote: only applies to BIG or HUGE ships\r\n"
1297 "Takes 1 or more arguments:\r\n"
1298 "\t1:\tName of one of the ships.\r\n"
1299 "\t2:\toptional: 1 disallow depletion, 0 allow depletion." },
1300 { OP_SHIELD_QUAD_LOW, "shield-quad-low\r\n"
1301 "\tReturns true if the specified ship has a shield quadrant below\r\n"
1302 "\tthe specified threshold percentage\r\n"
1303 "\t1: Ship name\r\n"
1304 "\t2: Percentage\r\n" },
1305 { OP_SECONDARY_AMMO_PCT, "secondary-ammo-pct\r\n"
1306 "\tReturns the percentage of ammo remaining in the specified bank (0 to 100)\r\n"
1307 "\t1: Ship name\r\n"
1308 "\t2: Bank to check (0, 1, 2 are legal banks. 3 will return the cumulative average for all banks" },
1309 { OP_IS_SECONDARY_SELECTED, "is-secondary-selected\r\n"
1310 "\tReturns true if the specified bank is selected (0 .. num_banks - 1)\r\n"
1311 "\t1: Ship name\r\n"
1312 "\t2: Bank to check (0 .. num_banks - 1)\r\n"},
1313 { OP_IS_PRIMARY_SELECTED, "is-primary-selected\r\n"
1314 "\tReturns true if the specified bank is selected (0 .. num_banks - 1)\r\n"
1315 "\t1: Ship name\r\n"
1316 "\t2: Bank to check (0 .. num_banks - 1)\r\n"},
1317 { OP_SPECIAL_WARP_DISTANCE, "special-warp-dist\r\n"
1318 "\tReturns distance to the plane of the knossos device in percent length of ship\r\n"
1319 "\t(ie, 100 means front of ship is 1 ship length from plane of knossos device)\r\n"
1320 "\t1: Ship name\r\n"},
1321 { OP_SET_SPECIAL_WARPOUT_NAME, "special-warpout-name\r\n"
1322 "\tSets the name of the knossos device to be used for warpout\r\n"
1323 "\t1: Ship name to exit\r\n"
1324 "\t2: Name of knossos device\r\n"},
1325 { OP_SHIP_VANISH, "ship-vanish\r\n"
1326 "\tMakes the named ship vanish (no log and vanish)\r\n"
1327 "\tSingle Player Only! Warning: This will cause ship exit not to be logged, so 'has-departed', etc. will not work\r\n"
1328 "\t1: List of ship names to vanish\r\n"},
1329 { OP_IS_SHIP_VISIBLE, "is-ship-visible\r\n"
1330 "\tCheck whether ship is visible on Player's radar\r\n"
1331 "\tSingle Player Only! Returns 0 - not visible, 1 - partially visible, 2 - fully visible.\r\n"
1332 "\t1: Name of ship to check\r\n"},
1333 { OP_TEAM_SCORE, "team-score\r\n"
1334 "\tGet the score of a multi team vs team game.\r\n"
1335 "\t1: Team index (1 for team 1 and 2 for team 2)\r\n"},
1339 struct op_menu_struct {
1344 { "Objectives", OP_CATAGORY_OBJECTIVE },
1345 { "Time", OP_CATAGORY_TIME },
1346 { "Logical", OP_CATAGORY_LOGICAL },
1347 { "Arithmetic", OP_CATAGORY_ARITHMETIC },
1348 { "Status", OP_CATAGORY_STATUS },
1349 { "Change", OP_CATAGORY_CHANGE },
1350 { "Conditionals", OP_CATAGORY_CONDITIONAL },
1351 { "Debugging", OP_CATAGORY_DEBUG },
1352 { "Ai goals", OP_CATAGORY_AI },
1353 { "Event/Goals", OP_CATAGORY_GOAL_EVENT },
1354 { "Training", OP_CATAGORY_TRAINING },
1357 int Num_op_menus = sizeof(op_menu) / sizeof(op_menu_struct);
1360 sexp_tree::sexp_tree()
1362 select_sexp_node = -1;
1366 m_p_image_list = NULL;
1371 // clears out the tree, so all the nodes are unused.
1372 void sexp_tree::clear_tree(char *op)
1377 for (i=0; i<MAX_SEXP_TREE_SIZE; i++)
1378 nodes[i].type = SEXPT_UNUSED;
1383 set_node(allocate_node(-1), (SEXPT_OPERATOR | SEXPT_VALID), op);
1389 void sexp_tree::reset_handles()
1393 for (i=0; i<MAX_SEXP_TREE_SIZE; i++)
1394 nodes[i].handle = NULL;
1397 // initializes and creates a tree from a given sexp startpoint.
1398 void sexp_tree::load_tree(int index, char *deflt)
1405 cur = allocate_node(-1);
1406 set_node(cur, (SEXPT_OPERATOR | SEXPT_VALID), deflt); // setup a default tree if none
1411 if (Sexp_nodes[index].subtype == SEXP_ATOM_NUMBER) { // handle numbers allender likes to use so much..
1412 cur = allocate_node(-1);
1413 if (atoi(Sexp_nodes[index].text))
1414 set_node(cur, (SEXPT_OPERATOR | SEXPT_VALID), "true");
1416 set_node(cur, (SEXPT_OPERATOR | SEXPT_VALID), "false");
1422 // assumption: first token is an operator. I require this because it would cause problems
1423 // with child/parent relations otherwise, and it should be this way anyway, since the
1424 // return type of the whole sexp is boolean, and only operators can satisfy this.
1425 Assert(Sexp_nodes[index].subtype == SEXP_ATOM_OPERATOR);
1426 load_branch(index, -1);
1430 void get_combined_variable_name(char *combined_name, const char *sexp_var_name)
1432 int sexp_var_index = get_index_sexp_variable_name(sexp_var_name);
1433 Assert(sexp_var_index > -1);
1435 sprintf(combined_name, "%s(%s)", Sexp_variables[sexp_var_index].variable_name, Sexp_variables[sexp_var_index].text);
1440 // creates a tree from a given Sexp_nodes[] point under a given parent. Recursive.
1441 void sexp_tree::load_branch(int index, int parent)
1444 char combined_var_name[2*TOKEN_LENGTH + 2];
1446 while (index != -1) {
1447 Assert(Sexp_nodes[index].type != SEXP_NOT_USED);
1448 if (Sexp_nodes[index].subtype == SEXP_ATOM_LIST) {
1449 load_branch(Sexp_nodes[index].first, parent); // do the sublist and continue
1451 } else if (Sexp_nodes[index].subtype == SEXP_ATOM_OPERATOR) {
1452 cur = allocate_node(parent);
1453 if ((index == select_sexp_node) && !flag) { // translate sexp node to our node
1454 select_sexp_node = cur;
1458 set_node(cur, (SEXPT_OPERATOR | SEXPT_VALID), Sexp_nodes[index].text);
1459 load_branch(Sexp_nodes[index].rest, cur); // operator is new parent now
1460 return; // 'rest' was just used, so nothing left to use.
1462 } else if (Sexp_nodes[index].subtype == SEXP_ATOM_NUMBER) {
1463 cur = allocate_node(parent);
1464 if (Sexp_nodes[index].type & SEXP_FLAG_VARIABLE) {
1465 get_combined_variable_name(combined_var_name, Sexp_nodes[index].text);
1466 set_node(cur, (SEXPT_VARIABLE | SEXPT_NUMBER | SEXPT_VALID), combined_var_name);
1468 set_node(cur, (SEXPT_NUMBER | SEXPT_VALID), Sexp_nodes[index].text);
1471 } else if (Sexp_nodes[index].subtype == SEXP_ATOM_STRING) {
1472 cur = allocate_node(parent);
1473 if (Sexp_nodes[index].type & SEXP_FLAG_VARIABLE) {
1474 get_combined_variable_name(combined_var_name, Sexp_nodes[index].text);
1475 set_node(cur, (SEXPT_VARIABLE | SEXPT_STRING | SEXPT_VALID), combined_var_name);
1477 set_node(cur, (SEXPT_STRING | SEXPT_VALID), Sexp_nodes[index].text);
1481 Assert(0); // unknown and/or invalid sexp type
1483 if ((index == select_sexp_node) && !flag) { // translate sexp node to our node
1484 select_sexp_node = cur;
1488 index = Sexp_nodes[index].rest;
1494 int sexp_tree::query_false(int node)
1500 Assert(nodes[node].type == (SEXPT_OPERATOR | SEXPT_VALID));
1501 Assert(nodes[node].next == -1); // must make this assumption or else it will confuse code!
1502 if (find_operator(nodes[node].text) == OP_FALSE){
1509 // builds an sexp of the tree and returns the index of it. This allocates sexp nodes.
1510 int sexp_tree::save_tree(int node)
1516 Assert(nodes[node].type == (SEXPT_OPERATOR | SEXPT_VALID));
1517 Assert(nodes[node].next == -1); // must make this assumption or else it will confuse code!
1518 return save_branch(node);
1521 // get variable name from sexp_tree node .text
1522 void var_name_from_sexp_tree_text(char *var_name, const char *text)
1524 int var_name_length = strcspn(text, "(");
1525 Assert(var_name_length < TOKEN_LENGTH - 1);
1527 strncpy(var_name, text, var_name_length);
1528 var_name[var_name_length] = '\0';
1531 #define NO_PREVIOUS_NODE -9
1532 // called recursively to save a tree branch and everything under it
1533 int sexp_tree::save_branch(int cur, int at_root)
1535 int start, node = -1, last = NO_PREVIOUS_NODE;
1536 char var_name_text[TOKEN_LENGTH];
1540 if (nodes[cur].type & SEXPT_OPERATOR) {
1541 node = alloc_sexp(nodes[cur].text, SEXP_ATOM, SEXP_ATOM_OPERATOR, -1, save_branch(nodes[cur].child));
1543 if ((nodes[cur].parent >= 0) && !at_root) {
1544 node = alloc_sexp("", SEXP_LIST, SEXP_ATOM_LIST, node, -1);
1546 } else if (nodes[cur].type & SEXPT_NUMBER) {
1547 // allocate number, maybe variable
1548 if (nodes[cur].type & SEXPT_VARIABLE) {
1549 var_name_from_sexp_tree_text(var_name_text, nodes[cur].text);
1550 node = alloc_sexp(var_name_text, (SEXP_ATOM | SEXP_FLAG_VARIABLE), SEXP_ATOM_NUMBER, -1, -1);
1552 node = alloc_sexp(nodes[cur].text, SEXP_ATOM, SEXP_ATOM_NUMBER, -1, -1);
1554 } else if (nodes[cur].type & SEXPT_STRING) {
1555 // allocate string, maybe variable
1556 if (nodes[cur].type & SEXPT_VARIABLE) {
1557 var_name_from_sexp_tree_text(var_name_text, nodes[cur].text);
1558 node = alloc_sexp(var_name_text, (SEXP_ATOM | SEXP_FLAG_VARIABLE), SEXP_ATOM_STRING, -1, -1);
1560 node = alloc_sexp(nodes[cur].text, SEXP_ATOM, SEXP_ATOM_STRING, -1, -1);
1562 } else if (nodes[cur].type & SEXPT_STRING) {
1563 Assert( !(nodes[cur].type & SEXPT_VARIABLE) );
1566 Assert(0); // unknown and/or invalid type
1569 if (last == NO_PREVIOUS_NODE){
1571 } else if (last >= 0){
1572 Sexp_nodes[last].rest = node;
1576 Assert(last != NO_PREVIOUS_NODE); // should be impossible
1577 cur = nodes[cur].next;
1586 // allocate a node. Remains used until freed.
1587 int sexp_tree::allocate_node()
1591 for (i=0; i<MAX_SEXP_TREE_SIZE; i++)
1592 if (nodes[i].type == SEXPT_UNUSED) {
1593 nodes[i].type = SEXPT_UNINIT;
1594 nodes[i].parent = -1;
1595 nodes[i].child = -1;
1601 Error(LOCATION, "Out of sexp tree nodes!");
1605 // allocate a child node under 'parent'. Appends to end of list.
1606 int sexp_tree::allocate_node(int parent, int after)
1608 int i, index = allocate_node();
1611 i = nodes[parent].child;
1613 nodes[parent].child = index;
1616 while ((i != after) && (nodes[i].next != -1))
1619 nodes[index].next = nodes[i].next;
1620 nodes[i].next = index;
1624 nodes[index].parent = parent;
1628 // free a node and all it's children. Also clears pointers to it, if any.
1629 // node = node chain to free
1630 // cascade = 0: free just this node and children under it. (default)
1631 // !0: free this node and all siblings after it.
1633 void sexp_tree::free_node(int node, int cascade)
1637 // clear the pointer to node
1638 i = nodes[node].parent;
1640 if (nodes[i].child == node)
1641 nodes[i].child = nodes[node].next;
1645 while (nodes[i].next != -1) {
1646 if (nodes[i].next == node) {
1647 nodes[i].next = nodes[node].next;
1656 nodes[node].next = -1;
1658 // now free up the node and it's children
1662 // more simple node freer, which works recursively. It frees the given node and all siblings
1663 // that come after it, as well as all children of these. Doesn't clear any links to any of
1664 // these freed nodes, so make sure all links are broken first. (i.e. use free_node() if you can)
1666 void sexp_tree::free_node2(int node)
1669 Assert(nodes[node].type != SEXPT_UNUSED);
1672 nodes[node].type = SEXPT_UNUSED;
1674 if (nodes[node].child != -1)
1675 free_node2(nodes[node].child);
1677 if (nodes[node].next != -1)
1678 free_node2(nodes[node].next);
1681 // initialize the data for a node. Should be called right after a new node is allocated.
1682 void sexp_tree::set_node(int node, int type, char *text)
1684 Assert(type != SEXPT_UNUSED);
1685 Assert(nodes[node].type != SEXPT_UNUSED);
1686 nodes[node].type = type;
1688 if (type & SEXPT_VARIABLE) {
1689 max_length = 2 * TOKEN_LENGTH + 2;
1691 max_length = TOKEN_LENGTH;
1693 Assert(strlen(text) < max_length);
1694 strcpy(nodes[node].text, text);
1697 void sexp_tree::post_load()
1700 select_sexp_node = -1;
1703 // build or rebuild a CTreeCtrl object with the current tree data
1704 void sexp_tree::build_tree()
1707 select_sexp_node = -1;
1710 add_sub_tree(0, TVI_ROOT);
1713 // Create the CTreeCtrl tree from the tree data. The tree data should already be setup by
1715 void sexp_tree::add_sub_tree(int node, HTREEITEM root)
1720 Assert(node >= 0 && node < MAX_SEXP_TREE_SIZE);
1721 node2 = nodes[node].child;
1723 // check for single argument operator case (prints as one line)
1724 /* if (node2 != -1 && nodes[node2].child == -1 && nodes[node2].next == -1) {
1725 sprintf(str, "%s %s", nodes[node].text, nodes[node2].text);
1726 nodes[node].handle = insert(str, root);
1727 nodes[node].flags = OPERAND | EDITABLE;
1728 nodes[node2].flags = COMBINED;
1732 // bitmap to draw in tree
1735 if (nodes[node].type & SEXPT_OPERATOR) {
1736 nodes[node].flags = OPERAND;
1737 bitmap = BITMAP_OPERATOR;
1739 if (nodes[node].type & SEXPT_VARIABLE) {
1740 nodes[node].flags = NOT_EDITABLE;
1741 bitmap = BITMAP_VARIABLE;
1743 nodes[node].flags = EDITABLE;
1744 bitmap = BITMAP_DATA;
1748 root = nodes[node].handle = insert(nodes[node].text, bitmap, bitmap, root);
1751 while (node != -1) {
1752 Assert(node >= 0 && node < MAX_SEXP_TREE_SIZE);
1753 Assert(nodes[node].type & SEXPT_VALID);
1754 if (nodes[node].type & SEXPT_OPERATOR) {
1755 add_sub_tree(node, root);
1758 Assert(nodes[node].child == -1);
1759 if (nodes[node].type & SEXPT_VARIABLE) {
1760 nodes[node].handle = insert(nodes[node].text, BITMAP_VARIABLE, BITMAP_VARIABLE, root);
1761 nodes[node].flags = NOT_EDITABLE;
1763 nodes[node].handle = insert(nodes[node].text, BITMAP_DATA, BITMAP_DATA, root);
1764 nodes[node].flags = EDITABLE;
1768 node = nodes[node].next;
1772 void sexp_tree::setup_selected(HTREEITEM h)
1779 item_handle = GetSelectedItem();
1781 for (i=0; i<MAX_SEXP_TREE_SIZE; i++)
1782 if (nodes[i].handle == item_handle) {
1788 int sexp_tree::get_ambiguous_type(int parent)
1791 int first_arg_index = get_modify_variable_first_arg_index();
1792 int sexp_var_index = get_tree_name_to_sexp_variable_index(nodes[first_arg_index].text);
1793 Assert(sexp_var_index != -1);
1795 if (Sexp_variables[sexp_var_index].type & SEXP_VARIABLE_NUMBER) {
1798 return OPF_AMBIGUOUS;
1803 int sexp_tree::get_modify_variable_first_arg_index()
1807 Assert( item_index >= 0);
1809 // get parent and check "modify-variable"
1810 index = nodes[item_index].parent;
1811 Assert( index != -1 );
1812 Assert( !(stricmp(nodes[index].text, "modify-variable")) );
1814 // get first child and verify type variable
1815 index = nodes[index].child;
1816 Assert( index != -1 );
1817 Assert( nodes[index].type & SEXPT_VARIABLE);
1822 // handler for right mouse button clicks.
1823 void sexp_tree::right_clicked(int mode)
1826 int i, j, z, count, op, add_type, replace_type, type;
1827 sexp_list_item *list;
1830 POINT click_point, mouse;
1831 CMenu menu, *mptr, *popup_menu, *add_data_menu = NULL, *replace_data_menu = NULL;
1832 CMenu *add_op_menu, add_op_submenu[MAX_OP_MENUS];
1833 CMenu *replace_op_menu, replace_op_submenu[MAX_OP_MENUS];
1834 CMenu *insert_op_menu, insert_op_submenu[MAX_OP_MENUS];
1835 CMenu *replace_variable_menu = NULL;
1838 add_instance = replace_instance = -1;
1839 Assert(Num_operators <= MAX_OPERATORS);
1840 Assert(Num_op_menus < MAX_OP_MENUS);
1841 GetCursorPos(&mouse);
1842 click_point = mouse;
1843 ScreenToClient(&click_point);
1844 h = HitTest(CPoint(click_point), &_flags); // find out what they clicked on
1845 if (h && menu.LoadMenu(IDR_MENU_EDIT_SEXP_TREE)) {
1847 popup_menu = menu.GetSubMenu(0);
1848 ASSERT(popup_menu != NULL);
1849 SelectDropTarget(h);
1851 add_op_menu = replace_op_menu = insert_op_menu = NULL;
1853 // get pointers to several key popup menus we'll need to modify
1854 i = popup_menu->GetMenuItemCount();
1856 if ( (mptr = popup_menu->GetSubMenu(i)) > 0 ) {
1857 popup_menu->GetMenuString(i, buf, sizeof(buf), MF_BYPOSITION);
1859 if (!stricmp(buf, "add operator")) {
1862 } else if (!stricmp(buf, "replace operator")) {
1863 replace_op_menu = mptr;
1865 } else if (!stricmp(buf, "add data")) {
1866 add_data_menu = mptr;
1868 } else if (!stricmp(buf, "replace data")) {
1869 replace_data_menu = mptr;
1871 } else if (!stricmp(buf, "insert operator")) {
1872 insert_op_menu = mptr;
1874 } else if (!stricmp(buf, "replace variable")) {
1875 replace_variable_menu = mptr;
1880 // add popup menus for all the operator catagories
1881 for (i=0; i<Num_op_menus; i++) {
1882 add_op_submenu[i].CreatePopupMenu();
1883 replace_op_submenu[i].CreatePopupMenu();
1884 insert_op_submenu[i].CreatePopupMenu();
1885 add_op_menu->AppendMenu(MF_POPUP, (UINT) add_op_submenu[i].m_hMenu, op_menu[i].name);
1886 replace_op_menu->AppendMenu(MF_POPUP, (UINT) replace_op_submenu[i].m_hMenu, op_menu[i].name);
1887 insert_op_menu->AppendMenu(MF_POPUP, (UINT) insert_op_submenu[i].m_hMenu, op_menu[i].name);
1890 // get rid of the placeholders we needed to ensure popup menus stayed popup menus,
1891 // i.e. MSDEV will convert empty popup menus into normal menu items.
1892 add_op_menu->DeleteMenu(ID_PLACEHOLDER, MF_BYCOMMAND);
1893 replace_op_menu->DeleteMenu(ID_PLACEHOLDER, MF_BYCOMMAND);
1894 insert_op_menu->DeleteMenu(ID_PLACEHOLDER, MF_BYCOMMAND);
1895 replace_variable_menu->DeleteMenu(ID_PLACEHOLDER, MF_BYCOMMAND);
1900 for (i=0; i<MAX_SEXP_TREE_SIZE; i++) {
1901 if (nodes[i].handle == h) {
1907 // Do SEXP_VARIABLE stuff here.
1908 if (m_mode != MODE_EVENTS) {
1909 // only allow variables in event mode
1910 menu.EnableMenuItem(ID_SEXP_TREE_ADD_VARIABLE, MF_GRAYED);
1911 menu.EnableMenuItem(ID_SEXP_TREE_MODIFY_VARIABLE, MF_GRAYED);
1913 menu.EnableMenuItem(ID_SEXP_TREE_ADD_VARIABLE, MF_ENABLED);
1914 menu.EnableMenuItem(ID_SEXP_TREE_MODIFY_VARIABLE, MF_ENABLED);
1916 // check not root (-1)
1917 if (item_index >= 0) {
1918 // get type of sexp_tree item clicked on
1919 int type = get_type(h);
1921 int parent = nodes[item_index].parent;
1923 op = identify_operator(nodes[parent].text);
1925 int first_arg = nodes[parent].child;
1927 // get arg count of item to replace
1929 int temp = first_arg;
1930 while (temp != item_index) {
1932 temp = nodes[temp].next;
1934 // DB - added 3/4/99
1940 int op_type = query_operator_argument_type(op, Replace_count); // check argument type at this position
1942 // special case dont allow replace data for variable names
1943 if (op_type != OPF_AMBIGUOUS) {
1945 if ( (type & SEXPT_STRING) || (type & SEXPT_NUMBER) ) {
1947 int max_sexp_vars = MAX_SEXP_VARIABLES;
1948 // prevent collisions in id numbers: ID_VARIABLE_MENU + 512 = ID_ADD_MENU
1949 Assert(max_sexp_vars < 512);
1951 for (int idx=0; idx<max_sexp_vars; idx++) {
1952 if (Sexp_variables[idx].type & SEXP_VARIABLE_SET) {
1953 UINT flag = MF_STRING | MF_GRAYED;
1954 // maybe gray flag MF_GRAYED
1956 // get type -- gray "string" or number accordingly
1957 if ( type & SEXPT_STRING ) {
1958 if ( Sexp_variables[idx].type & SEXP_VARIABLE_STRING ) {
1961 } else if ( type & SEXPT_NUMBER ) {
1962 if ( Sexp_variables[idx].type & SEXP_VARIABLE_NUMBER ) {
1967 // if modify-variable and changing variable, enable all variables
1968 if (op_type == OPF_VARIABLE_NAME) {
1969 Modify_variable = 1;
1972 Modify_variable = 0;
1976 // append list of variable names and values
1977 // set id as ID_VARIABLE_MENU + idx
1978 sprintf(buf, "%s(%s)", Sexp_variables[idx].variable_name, Sexp_variables[idx].text);
1980 replace_variable_menu->AppendMenu(flag, (ID_VARIABLE_MENU + idx), buf);
1988 // cant modify if no variables
1989 if (sexp_variable_count() == 0) {
1990 menu.EnableMenuItem(ID_SEXP_TREE_MODIFY_VARIABLE, MF_GRAYED);
1994 // add operator menu items to the various catagory submenus they belong in
1995 for (i=0; i<Num_operators; i++) {
1996 for (j=0; j<Num_op_menus; j++) {
1997 if (op_menu[j].id == (Operators[i].value & OP_CATAGORY_MASK)) {
2000 switch (Operators[i].value) {
2001 case OP_WAS_PROMOTION_GRANTED:
2002 case OP_WAS_MEDAL_GRANTED:
2003 case OP_GRANT_PROMOTION:
2004 case OP_GRANT_MEDAL:
2005 case OP_TECH_ADD_SHIP:
2006 case OP_TECH_ADD_WEAPON:
2007 j = Num_op_menus; // don't allow these operators in final release
2010 if (j < Num_op_menus) {
2011 add_op_submenu[j].AppendMenu(MF_STRING | MF_GRAYED, Operators[i].value, Operators[i].text);
2012 replace_op_submenu[j].AppendMenu(MF_STRING | MF_GRAYED, Operators[i].value | OP_REPLACE_FLAG, Operators[i].text);
2013 insert_op_submenu[j].AppendMenu(MF_STRING, Operators[i].value | OP_INSERT_FLAG, Operators[i].text);
2016 break; // only 1 category valid
2021 // find local index (i) of current item (from it's handle)
2022 SelectItem(item_handle = h);
2023 for (i=0; i<MAX_SEXP_TREE_SIZE; i++) {
2024 if (nodes[i].handle == h) {
2029 // special case: item is a ROOT node, and a label that can be edited (not an item in the sexp tree)
2030 if ((item_index == -1) && (m_mode & ST_LABELED_ROOT)) {
2031 if (m_mode & ST_ROOT_EDITABLE) {
2032 menu.EnableMenuItem(ID_EDIT_TEXT, MF_ENABLED);
2034 menu.EnableMenuItem(ID_EDIT_TEXT, MF_GRAYED);
2037 // disable copy, insert op
2038 menu.EnableMenuItem(ID_EDIT_COPY, MF_GRAYED);
2039 for (j=0; j<Num_operators; j++) {
2040 menu.EnableMenuItem(Operators[j].value | OP_INSERT_FLAG, MF_GRAYED);
2043 gray_menu_tree(popup_menu);
2044 popup_menu->TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON, mouse.x, mouse.y, this);
2048 Assert(item_index != -1); // handle not found, which should be impossible.
2049 if (!(nodes[item_index].flags & EDITABLE)) {
2050 menu.EnableMenuItem(ID_EDIT_TEXT, MF_GRAYED);
2053 if (nodes[item_index].parent == -1) { // root node
2054 menu.EnableMenuItem(ID_DELETE, MF_GRAYED); // can't delete the root item.
2057 /* if ((nodes[item_index].flags & OPERAND) && (nodes[item_index].flags & EDITABLE)) // expandable?
2058 menu.EnableMenuItem(ID_SPLIT_LINE, MF_ENABLED);
2060 z = nodes[item_index].child;
2061 if (z != -1 && nodes[z].next == -1 && nodes[z].child == -1)
2062 menu.EnableMenuItem(ID_SPLIT_LINE, MF_ENABLED);
2064 z = nodes[nodes[item_index].parent].child;
2065 if (z != -1 && nodes[z].next == -1 && nodes[z].child == -1)
2066 menu.EnableMenuItem(ID_SPLIT_LINE, MF_ENABLED);*/
2068 // change enabled status of 'add' type menu options.
2070 if (nodes[item_index].flags & OPERAND) {
2071 add_type = OPR_STRING;
2072 int child = nodes[item_index].child;
2073 Add_count = count_args(child);
2074 op = identify_operator(nodes[item_index].text);
2077 // get listing of valid argument values and add to menus
2078 type = query_operator_argument_type(op, Add_count);
2079 list = get_listing_opf(type, item_index, Add_count);
2081 sexp_list_item *ptr;
2087 // enable operators with correct return type
2088 menu.EnableMenuItem(Operators[ptr->op].value, MF_ENABLED);
2092 if ( (data_idx + 3) % 30) {
2093 add_data_menu->AppendMenu(MF_STRING | MF_ENABLED, ID_ADD_MENU + data_idx, ptr->text);
2095 add_data_menu->AppendMenu(MF_MENUBARBREAK | MF_STRING | MF_ENABLED, ID_ADD_MENU + data_idx, ptr->text);
2104 // special handling for the non-string formats
2105 if (type == OPF_NONE) { // an argument can't be added
2108 } else if (type == OPF_NULL) { // arguments with no return values
2109 add_type = OPR_NULL;
2111 } else if (type == OPF_NUMBER) { // takes numbers
2112 add_type = OPR_NUMBER;
2113 menu.EnableMenuItem(ID_ADD_NUMBER, MF_ENABLED);
2115 } else if (type == OPF_POSITIVE) { // takes non-negative numbers
2116 add_type = OPR_POSITIVE;
2117 menu.EnableMenuItem(ID_ADD_NUMBER, MF_ENABLED);
2119 } else if (type == OPF_BOOL) { // takes true/false bool values
2120 add_type = OPR_BOOL;
2122 } else if (type == OPF_AI_GOAL) {
2123 add_type = OPR_AI_GOAL;
2126 // add_type unchanged from above
2127 if (add_type == OPR_STRING) {
2128 menu.EnableMenuItem(ID_ADD_STRING, MF_ENABLED);
2134 // disable operators that do not have arguments available
2135 for (j=0; j<Num_operators; j++) {
2136 if (!query_default_argument_available(j)) {
2137 menu.EnableMenuItem(Operators[j].value, MF_GRAYED);
2142 // change enabled status of 'replace' type menu options.
2144 int parent = nodes[item_index].parent;
2146 replace_type = OPR_STRING;
2147 op = identify_operator(nodes[parent].text);
2149 int first_arg = nodes[parent].child;
2150 count = count_args(nodes[parent].child);
2152 // already at minimum number of arguments?
2153 if (count <= Operators[op].min) {
2154 menu.EnableMenuItem(ID_DELETE, MF_GRAYED);
2157 // get arg count of item to replace
2159 int temp = first_arg;
2160 while (temp != item_index) {
2162 temp = nodes[temp].next;
2164 // DB - added 3/4/99
2170 // maybe gray delete
2171 for (i=Replace_count+1; i<count; i++) {
2172 if (query_operator_argument_type(op, i-1) != query_operator_argument_type(op, i)) {
2173 menu.EnableMenuItem(ID_DELETE, MF_GRAYED);
2178 type = query_operator_argument_type(op, Replace_count); // check argument type at this position
2180 // special case reset type for ambiguous
2181 if (type == OPF_AMBIGUOUS) {
2182 type = get_ambiguous_type(parent);
2185 list = get_listing_opf(type, parent, Replace_count);
2187 // special case dont allow replace data for variable names
2188 if ( (type != OPF_VARIABLE_NAME) && list) {
2189 sexp_list_item *ptr;
2195 menu.EnableMenuItem(Operators[ptr->op].value | OP_REPLACE_FLAG, MF_ENABLED);
2198 if ( (data_idx + 3) % 30)
2199 replace_data_menu->AppendMenu(MF_STRING | MF_ENABLED, ID_REPLACE_MENU + data_idx, ptr->text);
2201 replace_data_menu->AppendMenu(MF_MENUBARBREAK | MF_STRING | MF_ENABLED, ID_REPLACE_MENU + data_idx, ptr->text);
2209 if (type == OPF_NONE) { // takes no arguments
2212 } else if (type == OPF_NUMBER) { // takes numbers
2213 replace_type = OPR_NUMBER;
2214 menu.EnableMenuItem(ID_REPLACE_NUMBER, MF_ENABLED);
2216 } else if (type == OPF_POSITIVE) { // takes non-negative numbers
2217 replace_type = OPR_POSITIVE;
2218 menu.EnableMenuItem(ID_REPLACE_NUMBER, MF_ENABLED);
2220 } else if (type == OPF_BOOL) { // takes true/false bool values
2221 replace_type = OPR_BOOL;
2223 } else if (type == OPF_NULL) { // takes operator that doesn't return a value
2224 replace_type = OPR_NULL;
2226 } else if (type == OPF_AI_GOAL) {
2227 replace_type = OPR_AI_GOAL;
2230 // default to string
2231 if (replace_type == OPR_STRING) {
2232 menu.EnableMenuItem(ID_REPLACE_STRING, MF_ENABLED);
2235 // modify string or number if (modify_variable)
2236 if ( !stricmp(Operators[op].text, "modify-variable") ) {
2237 int modify_type = get_modify_variable_type();
2239 if (modify_type == OPF_NUMBER) {
2240 menu.EnableMenuItem(ID_REPLACE_NUMBER, MF_ENABLED);
2241 menu.EnableMenuItem(ID_REPLACE_STRING, MF_GRAYED);
2243 // no change for string type
2248 } else { // top node, so should be a Boolean type.
2249 if (m_mode == MODE_EVENTS) { // return type should be null
2250 replace_type = OPR_NULL;
2251 for (j=0; j<Num_operators; j++)
2252 if (query_operator_return_type(j) == OPR_NULL)
2253 menu.EnableMenuItem(Operators[j].value | OP_REPLACE_FLAG, MF_ENABLED);
2256 replace_type = OPR_BOOL;
2257 for (j=0; j<Num_operators; j++)
2258 if (query_operator_return_type(j) == OPR_BOOL)
2259 menu.EnableMenuItem(Operators[j].value | OP_REPLACE_FLAG, MF_ENABLED);
2263 for (j=0; j<Num_operators; j++)
2264 if (!query_default_argument_available(j))
2265 menu.EnableMenuItem(Operators[j].value | OP_REPLACE_FLAG, MF_GRAYED);
2267 // change enabled status of 'insert' type menu options.
2268 z = nodes[item_index].parent;
2271 op = identify_operator(nodes[z].text);
2275 while (j != item_index) {
2280 type = query_operator_argument_type(op, count); // check argument type at this position
2283 if (m_mode == MODE_EVENTS)
2289 for (j=0; j<Num_operators; j++) {
2290 z = query_operator_return_type(j);
2291 if (!sexp_query_type_match(type, z) || (Operators[j].min < 1))
2292 menu.EnableMenuItem(Operators[j].value | OP_INSERT_FLAG, MF_GRAYED);
2294 z = query_operator_argument_type(j, 0);
2295 if ((type == OPF_NUMBER) && (z == OPF_POSITIVE))
2299 menu.EnableMenuItem(Operators[j].value | OP_INSERT_FLAG, MF_GRAYED);
2302 for (j=0; j<Num_operators; j++)
2303 if (!query_default_argument_available(j))
2304 menu.EnableMenuItem(Operators[j].value | OP_INSERT_FLAG, MF_GRAYED);
2306 // disable non campaign operators if in campaign mode
2307 for (j=0; j<Num_operators; j++) {
2309 if (m_mode == MODE_CAMPAIGN) {
2310 if (Operators[j].value & OP_NONCAMPAIGN_FLAG)
2314 if (Operators[j].value & OP_CAMPAIGN_ONLY_FLAG)
2319 menu.EnableMenuItem(Operators[j].value, MF_GRAYED);
2320 menu.EnableMenuItem(Operators[j].value | OP_REPLACE_FLAG, MF_GRAYED);
2321 menu.EnableMenuItem(Operators[j].value | OP_INSERT_FLAG, MF_GRAYED);
2325 if ((Sexp_clipboard > -1) && (Sexp_nodes[Sexp_clipboard].type != SEXP_NOT_USED)) {
2326 Assert(Sexp_nodes[Sexp_clipboard].subtype != SEXP_ATOM_LIST);
2328 if (Sexp_nodes[Sexp_clipboard].subtype == SEXP_ATOM_OPERATOR) {
2329 j = find_operator(CTEXT(Sexp_clipboard));
2331 z = query_operator_return_type(j);
2333 if ((z == OPR_POSITIVE) && (replace_type == OPR_NUMBER))
2336 if (replace_type == z)
2337 menu.EnableMenuItem(ID_EDIT_PASTE, MF_ENABLED);
2339 z = query_operator_return_type(j);
2340 if ((z == OPR_POSITIVE) && (add_type == OPR_NUMBER))
2344 menu.EnableMenuItem(ID_EDIT_PASTE_SPECIAL, MF_ENABLED);
2346 } else if (Sexp_nodes[Sexp_clipboard].subtype == SEXP_ATOM_NUMBER) {
2347 if ((replace_type == OPR_POSITIVE) && (atoi(CTEXT(Sexp_clipboard)) > -1))
2348 menu.EnableMenuItem(ID_EDIT_PASTE, MF_ENABLED);
2350 else if (replace_type == OPR_NUMBER)
2351 menu.EnableMenuItem(ID_EDIT_PASTE, MF_ENABLED);
2353 if ((add_type == OPR_POSITIVE) && (atoi(CTEXT(Sexp_clipboard)) > -1))
2354 menu.EnableMenuItem(ID_EDIT_PASTE_SPECIAL, MF_ENABLED);
2356 else if (add_type == OPR_NUMBER)
2357 menu.EnableMenuItem(ID_EDIT_PASTE_SPECIAL, MF_ENABLED);
2359 } else if (Sexp_nodes[Sexp_clipboard].subtype == SEXP_ATOM_STRING) {
2360 if (replace_type == OPR_STRING)
2361 menu.EnableMenuItem(ID_EDIT_PASTE, MF_ENABLED);
2363 if (add_type == OPR_STRING)
2364 menu.EnableMenuItem(ID_EDIT_PASTE_SPECIAL, MF_ENABLED);
2367 Int3(); // unknown and/or invalid sexp type
2370 if (!(menu.GetMenuState(ID_DELETE, MF_BYCOMMAND) & MF_GRAYED))
2371 menu.EnableMenuItem(ID_EDIT_CUT, MF_ENABLED);
2373 gray_menu_tree(popup_menu);
2374 popup_menu->TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON, mouse.x, mouse.y, this);
2378 // counts the number of arguments an operator has. Call this with the node of the first
2379 // argument of the operator
2380 int sexp_tree::count_args(int node)
2384 while (node != -1) {
2386 node = nodes[node].next;
2392 // identify what type of argument this is. You call it with the node of the first argument
2393 // of an operator. It will search through enough of the arguments to determine what type of
2395 int sexp_tree::identify_arg_type(int node)
2399 while (node != -1) {
2400 Assert(nodes[node].type & SEXPT_VALID);
2401 switch (SEXPT_TYPE(nodes[node].type)) {
2402 case SEXPT_OPERATOR:
2403 type = find_operator(nodes[node].text);
2405 return query_operator_return_type(type);
2410 case SEXPT_STRING: // either a ship or a wing
2411 type = SEXP_ATOM_STRING;
2412 break; // don't return, because maybe we can narrow selection down more.
2415 node = nodes[node].next;
2421 // determine if an item should be editable. This doesn't actually edit the label.
2422 int sexp_tree::edit_label(HTREEITEM h)
2426 for (i=0; i<MAX_SEXP_TREE_SIZE; i++) {
2427 if (nodes[i].handle == h) {
2432 // Check if tree root
2433 if (i == MAX_SEXP_TREE_SIZE) {
2434 if (m_mode & ST_ROOT_EDITABLE) {
2441 // Operators are editable
2442 if (nodes[i].type & SEXPT_OPERATOR) {
2446 // Variables must be edited through dialog box
2447 if (nodes[i].type & SEXPT_VARIABLE) {
2451 // Don't edit if not flaged as editable
2452 if (!(nodes[i].flags & EDITABLE)) {
2456 // Otherwise, allow editing
2460 if (nodes[i].flags & OPERAND) {
2461 data = nodes[i].child;
2463 SetItemText(h, nodes[i].text);
2464 nodes[i].flags = OPERAND;
2465 item_handle = nodes[data].handle = insert(nodes[data].text, nodes[data].type, nodes[data].flags, h);
2466 nodes[data].flags = EDITABLE;
2467 Expand(h, TVE_EXPAND);
2468 SelectItem(item_handle);
2474 int sexp_tree::end_label_edit(HTREEITEM h, char *str)
2476 int len, node, r = 1;
2482 for (node=0; node<MAX_SEXP_TREE_SIZE; node++)
2483 if (nodes[node].handle == h)
2486 if (node == MAX_SEXP_TREE_SIZE) {
2487 if (m_mode == MODE_EVENTS) {
2488 item_index = GetItemData(h);
2489 Assert(Event_editor_dlg);
2490 node = Event_editor_dlg->handler(ROOT_RENAMED, item_index, str);
2494 Int3(); // root labels shouldn't have been editable!
2497 Assert(node < MAX_SEXP_TREE_SIZE);
2498 if (nodes[node].type & SEXPT_OPERATOR) {
2499 str = match_closest_operator(str, node);
2500 SetItemText(h, str);
2502 add_or_replace_operator(identify_operator(str), 1);
2506 // Error checking would not hurt here
2508 if (len >= TOKEN_LENGTH)
2509 len = TOKEN_LENGTH - 1;
2511 strncpy(nodes[node].text, str, len);
2512 nodes[node].text[len] = 0;
2514 /* node = nodes[node].parent;
2516 child = nodes[node].child;
2517 if (child != -1 && nodes[child].next == -1 && nodes[child].child == -1) {
2518 merge_operator(child);
2526 // Check if 'op' is a valid operator match for operator format (OPF) 'type'. Returns true if it
2527 // is, false if not.
2529 int sexp_tree::check_operator_validity(int op, int type)
2533 rtype = query_operator_return_type(op);
2536 return ( (rtype == OPR_NUMBER) || (rtype == OPR_POSITIVE) );
2539 return (rtype == OPR_POSITIVE);
2542 return (rtype == OPR_BOOL);
2545 return (rtype == OPR_NULL);
2548 return (rtype == OPR_AI_GOAL);
2554 // Look for the valid operator that is the closest match for 'str' and return the operator
2555 // number of it. What operators are valid is determined by 'node', and an operator is valid
2556 // if it is allowed to fit at position 'node'
2558 char *sexp_tree::match_closest_operator(char *str, int node)
2560 int z, n, i, op, arg_num, type;
2561 char *sub_best = NULL, *best = NULL;
2563 z = nodes[node].parent;
2568 op = identify_operator(nodes[z].text);
2572 // determine which argument we are of the parent
2581 type = query_operator_argument_type(op, arg_num); // check argument type at this position
2582 for (i=0; i<Num_operators; i++) {
2583 if (check_operator_validity(i, type)) {
2584 if ( (stricmp(str, Operators[i].text) <= 0) && (!best || (stricmp(str, best) < 0)) )
2585 best = Operators[i].text;
2587 if ( !sub_best || (stricmp(Operators[i].text, sub_best) > 0) )
2588 sub_best = Operators[i].text;
2593 best = sub_best; // no best found, use our plan #2 best found.
2595 Assert(best); // we better have some valid operator at this point.
2601 if (nodes[node].flags == EDITABLE) // data
2602 node = nodes[node].parent;
2605 child = nodes[node].child;
2606 if (child != -1 && nodes[child].next == -1 && nodes[child].child == -1) {
2607 sprintf(buf, "%s %s", nodes[node].text, nodes[child].text);
2608 SetItemText(nodes[node].handle, buf);
2609 nodes[node].flags = OPERAND | EDITABLE;
2610 nodes[child].flags = COMBINED;
2611 DeleteItem(nodes[child].handle);
2612 nodes[child].handle = NULL;
2618 // this really only handles messages generated by the right click popup menu
2619 BOOL sexp_tree::OnCommand(WPARAM wParam, LPARAM lParam)
2621 int i, z, id, node, op, type;
2622 sexp_list_item *list, *ptr;
2625 if ((item_index >= 0) && (item_index < total))
2626 item_handle = nodes[item_index].handle;
2628 id = LOWORD(wParam);
2632 if (id == ID_SEXP_TREE_ADD_VARIABLE) {
2633 CAddVariableDlg dlg;
2636 if ( dlg.m_create ) {
2640 if ( dlg.m_type_number ) {
2641 type = SEXP_VARIABLE_NUMBER;
2643 type = SEXP_VARIABLE_STRING;
2647 sexp_add_variable(dlg.m_default_value, dlg.m_variable_name, type);
2650 sexp_variable_sort();
2656 if (id == ID_SEXP_TREE_MODIFY_VARIABLE) {
2657 CModifyVariableDlg dlg;
2659 // get sexp_variable index for item index
2660 dlg.m_start_index = get_item_index_to_var_index();
2662 // get pointer to tree
2663 dlg.m_p_sexp_tree = this;
2667 Assert( !(dlg.m_deleted && dlg.m_do_modify) );
2669 if (dlg.m_deleted) {
2670 // find index in sexp_variable list
2671 int sexp_var_index = get_index_sexp_variable_name(dlg.m_cur_variable_name);
2672 Assert(sexp_var_index != -1);
2675 sexp_variable_delete(sexp_var_index);
2678 sexp_variable_sort();
2680 // delete from sexp_tree, replacing with "number" or "string" as needed
2681 // further error checking from add_data()
2682 delete_sexp_tree_variable(dlg.m_cur_variable_name);
2687 if (dlg.m_do_modify) {
2688 // check sexp_tree -- warn on type
2689 // find index and change either (1) name, (2) type, (3) value
2690 int sexp_var_index = get_index_sexp_variable_name(dlg.m_old_var_name);
2691 Assert(sexp_var_index != -1);
2693 // save old name, since name may be modified
2694 char old_name[TOKEN_LENGTH];
2695 strcpy(old_name, Sexp_variables[sexp_var_index].variable_name);
2699 if (dlg.m_type_number) {
2700 type = SEXP_VARIABLE_NUMBER;
2702 type = SEXP_VARIABLE_STRING;
2705 // update sexp_variable
2706 sexp_fred_modify_variable(dlg.m_default_value, dlg.m_cur_variable_name, sexp_var_index, type);
2709 modify_sexp_tree_variable(old_name, sexp_var_index);
2711 // Don't sort until after modify, since modify uses index
2712 if (dlg.m_modified_name) {
2713 sexp_variable_sort();
2724 // check if REPLACE_VARIABLE_MENU
2725 if ( (id >= ID_VARIABLE_MENU) && (id < ID_VARIABLE_MENU + 511)) {
2727 Assert(item_index >= 0);
2729 // get index into list of type valid variables
2730 int var_idx = id - ID_VARIABLE_MENU;
2731 Assert( (var_idx >= 0) && (var_idx < MAX_SEXP_VARIABLES) );
2733 int type = get_type(item_handle);
2734 Assert( (type & SEXPT_NUMBER) || (type & SEXPT_STRING) );
2736 // dont do type check for modify-variable
2737 if (Modify_variable) {
2738 if (Sexp_variables[var_idx].type & SEXP_VARIABLE_NUMBER) {
2739 type = SEXPT_NUMBER;
2740 } else if (Sexp_variables[var_idx].type & SEXP_VARIABLE_STRING) {
2741 type = SEXPT_STRING;
2743 Int3(); // unknown type
2748 // verify type in tree is same as type in Sexp_variables array
2749 if (type & SEXPT_NUMBER) {
2750 Assert(Sexp_variables[var_idx].type & SEXP_VARIABLE_NUMBER);
2753 if (type & SEXPT_STRING) {
2754 Assert( (Sexp_variables[var_idx].type & SEXP_VARIABLE_STRING) );
2759 replace_variable_data(var_idx, (type | SEXPT_VARIABLE));
2765 if ((id >= ID_ADD_MENU) && (id < ID_ADD_MENU + 511)) {
2766 Assert(item_index >= 0);
2767 op = identify_operator(nodes[item_index].text);
2770 type = query_operator_argument_type(op, Add_count);
2771 list = get_listing_opf(type, item_index, Add_count);
2782 Assert((SEXPT_TYPE(ptr->type) != SEXPT_OPERATOR) && (ptr->op < 0));
2783 expand_operator(item_index);
2784 add_data(ptr->text, ptr->type);
2789 if ((id >= ID_REPLACE_MENU) && (id < ID_REPLACE_MENU + 511)) {
2790 Assert(item_index >= 0);
2791 Assert(nodes[item_index].parent >= 0);
2792 op = identify_operator(nodes[nodes[item_index].parent].text);
2795 type = query_operator_argument_type(op, Replace_count); // check argument type at this position
2796 list = get_listing_opf(type, nodes[item_index].parent, Replace_count);
2799 id -= ID_REPLACE_MENU;
2807 Assert((SEXPT_TYPE(ptr->type) != SEXPT_OPERATOR) && (ptr->op < 0));
2808 expand_operator(item_index);
2809 replace_data(ptr->text, ptr->type);
2814 for (op=0; op<Num_operators; op++) {
2815 if (id == Operators[op].value) {
2816 add_or_replace_operator(op);
2820 if (id == (Operators[op].value | OP_REPLACE_FLAG)) {
2821 add_or_replace_operator(op, 1);
2825 if (id == (Operators[op].value | OP_INSERT_FLAG)) {
2828 z = nodes[item_index].parent;
2829 flags = nodes[item_index].flags;
2830 node = allocate_node(z, item_index);
2831 set_node(node, (SEXPT_OPERATOR | SEXPT_VALID), Operators[op].text);
2832 nodes[node].flags = flags;
2834 h = nodes[z].handle;
2837 h = GetParentItem(nodes[item_index].handle);
2838 if (m_mode == MODE_GOALS) {
2839 Assert(Goal_editor_dlg);
2840 Goal_editor_dlg->insert_handler(item_index, node);
2841 SetItemData(h, node);
2843 } else if (m_mode == MODE_EVENTS) {
2844 Assert(Event_editor_dlg);
2845 Event_editor_dlg->insert_handler(item_index, node);
2846 SetItemData(h, node);
2848 } else if (m_mode == MODE_CAMPAIGN) {
2849 Campaign_tree_formp->insert_handler(item_index, node);
2850 SetItemData(h, node);
2858 item_handle = nodes[node].handle = insert(Operators[op].text, BITMAP_OPERATOR, BITMAP_OPERATOR, h, nodes[item_index].handle);
2859 move_branch(item_index, node);
2862 for (i=1; i<Operators[op].min; i++)
2863 add_default_operator(op, i);
2865 Expand(item_handle, TVE_EXPAND);
2873 // If a clipboard already exist, unmark it as persistent and free old clipboard
2874 if (Sexp_clipboard != -1) {
2875 sexp_unmark_persistent(Sexp_clipboard);
2876 free_sexp2(Sexp_clipboard);
2879 // Allocate new clipboard and mark persistent
2880 Sexp_clipboard = save_branch(item_index, 1);
2881 sexp_mark_persistent(Sexp_clipboard);
2885 // the following assumptions are made..
2886 Assert((Sexp_clipboard > -1) && (Sexp_nodes[Sexp_clipboard].type != SEXP_NOT_USED));
2887 Assert(Sexp_nodes[Sexp_clipboard].subtype != SEXP_ATOM_LIST);
2889 if (Sexp_nodes[Sexp_clipboard].subtype == SEXP_ATOM_OPERATOR) {
2890 expand_operator(item_index);
2891 replace_operator(CTEXT(Sexp_clipboard));
2892 if (Sexp_nodes[Sexp_clipboard].rest != -1) {
2893 load_branch(Sexp_nodes[Sexp_clipboard].rest, item_index);
2894 i = nodes[item_index].child;
2896 add_sub_tree(i, nodes[item_index].handle);
2901 } else if (Sexp_nodes[Sexp_clipboard].subtype == SEXP_ATOM_NUMBER) {
2902 Assert(Sexp_nodes[Sexp_clipboard].rest == -1);
2903 expand_operator(item_index);
2904 replace_data(CTEXT(Sexp_clipboard), (SEXPT_NUMBER | SEXPT_VALID));
2906 } else if (Sexp_nodes[Sexp_clipboard].subtype == SEXP_ATOM_STRING) {
2907 Assert(Sexp_nodes[Sexp_clipboard].rest == -1);
2908 expand_operator(item_index);
2909 replace_data(CTEXT(Sexp_clipboard), (SEXPT_STRING | SEXPT_VALID));
2912 Assert(0); // unknown and/or invalid sexp type
2916 case ID_EDIT_PASTE_SPECIAL: // add paste, instead of replace.
2917 // the following assumptions are made..
2918 Assert((Sexp_clipboard > -1) && (Sexp_nodes[Sexp_clipboard].type != SEXP_NOT_USED));
2919 Assert(Sexp_nodes[Sexp_clipboard].subtype != SEXP_ATOM_LIST);
2921 if (Sexp_nodes[Sexp_clipboard].subtype == SEXP_ATOM_OPERATOR) {
2922 expand_operator(item_index);
2923 add_operator(CTEXT(Sexp_clipboard));
2924 if (Sexp_nodes[Sexp_clipboard].rest != -1) {
2925 load_branch(Sexp_nodes[Sexp_clipboard].rest, item_index);
2926 i = nodes[item_index].child;
2928 add_sub_tree(i, nodes[item_index].handle);
2933 } else if (Sexp_nodes[Sexp_clipboard].subtype == SEXP_ATOM_NUMBER) {
2934 Assert(Sexp_nodes[Sexp_clipboard].rest == -1);
2935 expand_operator(item_index);
2936 add_data(CTEXT(Sexp_clipboard), (SEXPT_NUMBER | SEXPT_VALID));
2938 } else if (Sexp_nodes[Sexp_clipboard].subtype == SEXP_ATOM_STRING) {
2939 Assert(Sexp_nodes[Sexp_clipboard].rest == -1);
2940 expand_operator(item_index);
2941 add_data(CTEXT(Sexp_clipboard), (SEXPT_STRING | SEXPT_VALID));
2944 Assert(0); // unknown and/or invalid sexp type
2948 /* case ID_SPLIT_LINE:
2949 if ((nodes[item_index].flags & OPERAND) && (nodes[item_index].flags & EDITABLE)) // expandable?
2950 expand_operator(item_index);
2952 merge_operator(item_index);
2957 expand_branch(item_handle);
2961 if (edit_label(item_handle)) {
2963 EditLabel(item_handle);
2968 case ID_ADD_STRING: {
2971 node = add_data("string", (SEXPT_STRING | SEXPT_VALID));
2972 EditLabel(nodes[node].handle);
2976 case ID_ADD_NUMBER: {
2979 node = add_data("number", (SEXPT_NUMBER | SEXPT_VALID));
2980 EditLabel(nodes[node].handle);
2985 if (Sexp_clipboard != -1) {
2986 sexp_unmark_persistent(Sexp_clipboard);
2987 free_sexp2(Sexp_clipboard);
2990 Sexp_clipboard = save_branch(item_index, 1);
2991 sexp_mark_persistent(Sexp_clipboard);
2992 // fall through to ID_DELETE case.
2998 if ((m_mode & ST_ROOT_DELETABLE) && (item_index == -1)) {
2999 item_index = GetItemData(item_handle);
3000 if (m_mode == MODE_GOALS) {
3001 Assert(Goal_editor_dlg);
3002 node = Goal_editor_dlg->handler(ROOT_DELETED, item_index);
3004 } else if (m_mode == MODE_EVENTS) {
3005 Assert(Event_editor_dlg);
3006 node = Event_editor_dlg->handler(ROOT_DELETED, item_index);
3009 Assert(m_mode == MODE_CAMPAIGN);
3010 node = Campaign_tree_formp->handler(ROOT_DELETED, item_index);
3015 DeleteItem(item_handle);
3020 Assert(item_index >= 0);
3021 h = GetParentItem(item_handle);
3022 parent = nodes[item_index].parent;
3023 if ((parent == -1) && (m_mode == MODE_EVENTS))
3024 Int3(); // no longer used, temporary to check if called still.
3026 Assert(parent != -1 && nodes[parent].handle == h);
3027 free_node(item_index);
3028 DeleteItem(item_handle);
3030 node = nodes[parent].child;
3031 /* if (node != -1 && nodes[node].next == -1 && nodes[node].child == -1) {
3032 sprintf(buf, "%s %s", nodes[parent].text, nodes[node].text);
3033 SetItem(h, TVIF_TEXT, buf, 0, 0, 0, 0, 0);
3034 nodes[parent].flags = OPERAND | EDITABLE;
3035 nodes[node].flags = COMBINED;
3036 DeleteItem(nodes[node].handle);
3044 return CTreeCtrl::OnCommand(wParam, lParam);
3047 // adds to or replaces (based on passed in flag) the current operator
3048 void sexp_tree::add_or_replace_operator(int op, int replace_flag)
3050 int i, op_index, op2;
3052 op_index = item_index;
3054 if (nodes[item_index].flags & OPERAND) { // are both operators?
3055 op2 = identify_operator(nodes[item_index].text);
3057 i = count_args(nodes[item_index].child);
3058 if ((i >= Operators[op].min) && (i <= Operators[op].max)) { // are old num args valid?
3060 if (query_operator_argument_type(op2, i) != query_operator_argument_type(op, i)) // does each arg match expected type?
3063 if (i < 0) { // everything is ok, so we can keep old arguments with new operator
3064 set_node(item_index, (SEXPT_OPERATOR | SEXPT_VALID), Operators[op].text);
3065 SetItemText(nodes[item_index].handle, Operators[op].text);
3066 nodes[item_index].flags = OPERAND;
3072 replace_operator(Operators[op].text);
3075 add_operator(Operators[op].text);
3077 // fill in all the required (minimum) arguments with default values
3078 for (i=0; i<Operators[op].min; i++)
3079 add_default_operator(op, i);
3081 Expand(item_handle, TVE_EXPAND);
3084 // initialize node, type operator
3086 void sexp_list_item::set_op(int op_num)
3090 if (op_num >= FIRST_OP) { // do we have an op value instead of an op number (index)?
3091 for (i=0; i<Num_operators; i++)
3092 if (op_num == Operators[i].value)
3093 op_num = i; // convert op value to op number
3097 text = Operators[op].text;
3098 type = (SEXPT_OPERATOR | SEXPT_VALID);
3101 // initialize node, type data
3102 // Defaults: t = SEXPT_STRING
3104 void sexp_list_item::set_data(char *str, int t)
3111 // add a node to end of list
3113 void sexp_list_item::add_op(int op_num)
3115 sexp_list_item *item, *ptr;
3117 item = new sexp_list_item;
3123 item->set_op(op_num);
3126 // add a node to end of list
3127 // Defaults: t = SEXPT_STRING
3129 void sexp_list_item::add_data(char *str, int t)
3131 sexp_list_item *item, *ptr;
3133 item = new sexp_list_item;
3139 item->set_data(str, t);
3142 // add a node to end of list, allocating memory for the text
3143 // Defaults: t = SEXPT_STRING
3145 void sexp_list_item::add_data_dup(char *str, int t)
3147 sexp_list_item *item, *ptr;
3149 item = new sexp_list_item;
3155 item->set_data(strdup(str), t);
3156 item->flags |= SEXP_ITEM_F_DUP;
3159 // add an sexp list to end of another list (join lists)
3161 void sexp_list_item::add_list(sexp_list_item *list)
3163 sexp_list_item *ptr;
3172 // free all nodes of list
3174 void sexp_list_item::destroy()
3176 sexp_list_item *ptr, *ptr2;
3181 if (ptr->flags & SEXP_ITEM_F_DUP)
3189 int sexp_tree::add_default_operator(int op, int argnum)
3193 sexp_list_item item;
3199 if (get_default_value(&item, op, argnum))
3202 if (item.type & SEXPT_OPERATOR) {
3203 Assert((item.op >= 0) && (item.op < Num_operators));
3204 add_or_replace_operator(item.op);
3209 // special case for modify-variable (data added 1st arg is variable)
3210 if ( !stricmp(Operators[op].text, "modify-variable") ) {
3213 int sexp_var_index = get_index_sexp_variable_name(item.text);
3214 Assert(sexp_var_index != -1);
3215 int type = SEXPT_VALID | SEXPT_VARIABLE;
3216 if (Sexp_variables[sexp_var_index].type & SEXP_VARIABLE_STRING) {
3217 type |= SEXPT_STRING;
3218 } else if (Sexp_variables[sexp_var_index].type & SEXP_VARIABLE_NUMBER) {
3219 type |= SEXPT_NUMBER;
3224 char node_text[2*TOKEN_LENGTH + 2];
3225 sprintf(node_text, "%s(%s)", item.text, Sexp_variables[sexp_var_index].text);
3226 add_variable_data(node_text, type);
3228 // the the variable name
3230 Assert(argnum == 1);
3231 sexp_list_item temp_item;
3232 temp_item.text = buf2;
3233 get_default_value(&temp_item, op, 0);
3234 int sexp_var_index = get_index_sexp_variable_name(temp_item.text);
3235 Assert(sexp_var_index != -1);
3237 // from name get type
3238 int temp_type = Sexp_variables[sexp_var_index].type;
3240 if (temp_type & SEXP_VARIABLE_NUMBER) {
3241 type = SEXPT_VALID | SEXPT_NUMBER;
3242 } else if (temp_type & SEXP_VARIABLE_STRING) {
3243 type = SEXPT_VALID | SEXPT_STRING;
3247 add_data(item.text, type);
3250 add_data(item.text, item.type);
3257 int sexp_tree::get_default_value(sexp_list_item *item, int op, int i)
3261 sexp_list_item *list;
3266 type = query_operator_argument_type(op, i);
3269 item->set_op(OP_NOP);
3273 item->set_op(OP_TRUE);
3279 // if the top level operators is an AI goal, and we are adding the last number required,
3280 // assume that this number is a priority and make it 89 instead of 1.
3281 if ((query_operator_return_type(op) == OPR_AI_GOAL) && (i == (Operators[op].min - 1)))
3282 item->set_data("89", (SEXPT_NUMBER | SEXPT_VALID));
3283 else if (((Operators[op].value == OP_HAS_DOCKED_DELAY) || (Operators[op].value == OP_HAS_UNDOCKED_DELAY)) && (i == 2))
3284 item->set_data("1", (SEXPT_NUMBER | SEXPT_VALID));
3285 else if ( (Operators[op].value == OP_SHIP_TYPE_DESTROYED) || (Operators[op].value == OP_GOOD_SECONDARY_TIME) )
3286 item->set_data("100", (SEXPT_NUMBER | SEXPT_VALID));
3288 item->set_data("0", (SEXPT_NUMBER | SEXPT_VALID));
3293 list = get_listing_opf(type, index, i);
3300 strcpy(item->text, list->text);
3306 // catch anything that doesn't have a default value. Just describe what should be here instead
3309 case OPF_SHIP_NOT_PLAYER:
3311 case OPF_SHIP_POINT:
3312 case OPF_SHIP_WING_POINT:
3313 str = "<name of ship here>";
3317 str = "<name of wing here>";
3320 case OPF_DOCKER_POINT:
3321 str = "<docker point>";
3324 case OPF_DOCKEE_POINT:
3325 str = "<dockee point>";
3329 case OPF_AWACS_SUBSYSTEM:
3330 str = "<name of subsystem>";
3342 //str = "<any allied>";
3343 str = "<any wingman>";
3346 case OPF_WAYPOINT_PATH:
3347 str = "<waypoint path>";
3350 case OPF_MISSION_NAME:
3351 str = "<mission name>";
3355 str = "<goal name>";
3359 str = "<ship type here>";
3362 case OPF_EVENT_NAME:
3363 str = "<event name>";
3366 case OPF_HUGE_WEAPON:
3367 str = "<huge weapon type>";
3370 case OPF_JUMP_NODE_NAME:
3371 str = "<Jump node name>";
3375 str = "<new default required!>";
3379 item->set_data(str, (SEXPT_STRING | SEXPT_VALID));
3383 int sexp_tree::query_default_argument_available(int op)
3388 for (i=0; i<Operators[op].min; i++)
3389 if (!query_default_argument_available(op, i))
3395 int sexp_tree::query_default_argument_available(int op, int i)
3400 type = query_operator_argument_type(op, i);
3411 case OPF_AWACS_SUBSYSTEM:
3412 case OPF_DOCKER_POINT:
3413 case OPF_DOCKEE_POINT:
3417 case OPF_SKILL_LEVEL:
3418 case OPF_MEDAL_NAME:
3419 case OPF_WEAPON_NAME:
3420 case OPF_SHIP_CLASS_NAME:
3421 case OPF_HUD_GAUGE_NAME:
3422 case OPF_HUGE_WEAPON:
3423 case OPF_JUMP_NODE_NAME:
3428 case OPF_SHIP_NOT_PLAYER:
3430 case OPF_SHIP_POINT:
3431 case OPF_SHIP_WING_POINT:
3432 ptr = GET_FIRST(&obj_used_list);
3433 while (ptr != END_OF_LIST(&obj_used_list)) {
3434 if (ptr->type == OBJ_SHIP)
3437 ptr = GET_NEXT(ptr);
3443 for (j=0; j<MAX_WINGS; j++)
3444 if (Wings[j].wave_count)
3450 case OPF_WAYPOINT_PATH:
3451 if (Num_waypoint_lists)
3456 case OPF_MISSION_NAME:
3457 if (m_mode != MODE_CAMPAIGN) {
3458 if (!(*Mission_filename))
3464 if (Campaign.num_missions > 0)
3469 case OPF_GOAL_NAME: {
3472 value = Operators[op].value;
3474 if (m_mode == MODE_CAMPAIGN)
3477 // need to be sure that previous-goal functions are available. (i.e. we are providing a default argument for them)
3478 else if ((value == OP_PREVIOUS_GOAL_TRUE) || (value == OP_PREVIOUS_GOAL_FALSE) || (value == OP_PREVIOUS_GOAL_INCOMPLETE) || Num_goals)
3484 case OPF_EVENT_NAME: {
3487 value = Operators[op].value;
3488 if (m_mode == MODE_CAMPAIGN)
3491 // need to be sure that previous-event functions are available. (i.e. we are providing a default argument for them)
3492 else if ((value == OP_PREVIOUS_EVENT_TRUE) || (value == OP_PREVIOUS_EVENT_FALSE) || (value == OP_PREVIOUS_EVENT_INCOMPLETE) || Num_mission_events)
3499 if (m_mode == MODE_EVENTS) {
3500 Assert(Event_editor_dlg);
3501 if (Event_editor_dlg->current_message_name(0))
3505 if (Num_messages > Num_builtin_messages)
3511 case OPF_VARIABLE_NAME:
3512 if (sexp_variable_count() > 0) {
3526 // expand a combined line (one with an operator and it's one argument on the same line) into
3528 void sexp_tree::expand_operator(int node)
3533 if (nodes[node].flags & COMBINED) {
3534 node = nodes[node].parent;
3535 Assert((nodes[node].flags & OPERAND) && (nodes[node].flags & EDITABLE));
3538 if ((nodes[node].flags & OPERAND) && (nodes[node].flags & EDITABLE)) { // expandable?
3539 Assert(nodes[node].type & SEXPT_OPERATOR);
3540 h = nodes[node].handle;
3541 data = nodes[node].child;
3542 Assert(data != -1 && nodes[data].next == -1 && nodes[data].child == -1);
3544 SetItem(h, TVIF_TEXT, nodes[node].text, 0, 0, 0, 0, 0);
3545 nodes[node].flags = OPERAND;
3546 nodes[data].handle = insert(nodes[data].text, BITMAP_DATA, BITMAP_DATA, h);
3547 nodes[data].flags = EDITABLE;
3548 Expand(h, TVE_EXPAND);
3552 // expand a CTreeCtrl branch and all of it's children
3553 void sexp_tree::expand_branch(HTREEITEM h)
3555 Expand(h, TVE_EXPAND);
3556 h = GetChildItem(h);
3559 h = GetNextSiblingItem(h);
3563 void sexp_tree::merge_operator(int node)
3568 if (nodes[node].flags == EDITABLE) // data
3569 node = nodes[node].parent;
3572 child = nodes[node].child;
3573 if (child != -1 && nodes[child].next == -1 && nodes[child].child == -1) {
3574 sprintf(buf, "%s %s", nodes[node].text, nodes[child].text);
3575 SetItemText(nodes[node].handle, buf);
3576 nodes[node].flags = OPERAND | EDITABLE;
3577 nodes[child].flags = COMBINED;
3578 DeleteItem(nodes[child].handle);
3579 nodes[child].handle = NULL;
3585 // add a data node under operator pointed to by item_index
3586 int sexp_tree::add_data(char *data, int type)
3590 expand_operator(item_index);
3591 node = allocate_node(item_index);
3592 set_node(node, type, data);
3593 nodes[node].handle = insert(data, BITMAP_DATA, BITMAP_DATA, nodes[item_index].handle);
3594 nodes[node].flags = EDITABLE;
3599 // add a (variable) data node under operator pointed to by item_index
3600 int sexp_tree::add_variable_data(char *data, int type)
3604 Assert(type & SEXPT_VARIABLE);
3606 expand_operator(item_index);
3607 node = allocate_node(item_index);
3608 set_node(node, type, data);
3609 nodes[node].handle = insert(data, BITMAP_VARIABLE, BITMAP_VARIABLE, nodes[item_index].handle);
3610 nodes[node].flags = NOT_EDITABLE;
3615 // add an operator under operator pointed to by item_index. Updates item_index to point
3616 // to this new operator.
3617 void sexp_tree::add_operator(char *op, HTREEITEM h)
3621 if (item_index == -1) {
3622 node = allocate_node(-1);
3623 set_node(node, (SEXPT_OPERATOR | SEXPT_VALID), op);
3624 item_handle = nodes[node].handle = insert(op, BITMAP_OPERATOR, BITMAP_OPERATOR, h);
3627 expand_operator(item_index);
3628 node = allocate_node(item_index);
3629 set_node(node, (SEXPT_OPERATOR | SEXPT_VALID), op);
3630 item_handle = nodes[node].handle = insert(op, BITMAP_OPERATOR, BITMAP_OPERATOR, nodes[item_index].handle);
3633 nodes[node].flags = OPERAND;
3638 // add an operator with one argument under operator pointed to by item_index. This function
3639 // exists because the one arg case is a special case. The operator and argument is
3640 // displayed on the same line.
3641 /*void sexp_tree::add_one_arg_operator(char *op, char *data, int type)
3646 expand_operator(item_index);
3647 node1 = allocate_node(item_index);
3648 node2 = allocate_node(node1);
3649 set_node(node1, SEXPT_OPERATOR, op);
3650 set_node(node2, type, data);
3651 sprintf(str, "%s %s", op, data);
3652 nodes[node1].handle = insert(str, nodes[item_index].handle);
3653 nodes[node1].flags = OPERAND | EDITABLE;
3654 nodes[node2].flags = COMBINED;
3659 int sexp_tree::verify_tree(int *bypass)
3661 return verify_tree(0, bypass);
3664 // check the sexp tree for errors. Return -1 if error, or 0 if no errors. If an error
3665 // is found, item_index = node of error.
3666 int sexp_tree::verify_tree(int node, int *bypass)
3668 int i, type, count, op, type2, op2, argnum = 0;
3671 return 0; // nothing to check
3673 Assert(node >= 0 && node < MAX_SEXP_TREE_SIZE);
3674 Assert(nodes[node].type == SEXPT_OPERATOR);
3676 op = identify_operator(nodes[node].text);
3678 return node_error(node, "Unknown operator", bypass);
3680 count = count_args(nodes[node].child);
3681 if (count < Operators[op].min)
3682 return node_error(node, "Too few arguments for operator", bypass);
3683 if (count > Operators[op].max)
3684 return node_error(node, "Too many arguments for operator", bypass);
3686 node = nodes[node].child; // get first argument
3687 while (node != -1) {
3688 type = query_operator_argument_type(op, argnum);
3689 Assert(nodes[node].type & SEXPT_VALID);
3690 if (nodes[node].type == SEXPT_OPERATOR) {
3691 if (verify_tree(node) == -1)
3694 op2 = identify_operator(nodes[node].text); // no error checking, because it was done in the call above.
3695 type2 = query_operator_return_type(op2);
3697 } else if (nodes[node].type == SEXPT_NUMBER) {
3701 ptr = nodes[node].text;
3703 if (!isdigit(*ptr++))
3704 return node_error(node, "Number is invalid", bypass);
3706 } else if (nodes[node].type == SEXPT_STRING) {
3707 type2 = SEXP_ATOM_STRING;
3710 Assert(0); // unknown and invalid sexp node type.
3714 if (type2 != OPR_NUMBER)
3715 return node_error(node, "Number or number return type expected here", bypass);
3720 if (type2 == SEXP_ATOM_STRING)
3721 if (ship_name_lookup(nodes[node].text) == -1)
3724 if (type2 != SEXP_ATOM_STRING)
3725 return node_error(node, "Ship name expected here", bypass);
3730 if (type2 == SEXP_ATOM_STRING)
3731 if (wing_name_lookup(nodes[node].text) == -1)
3734 if (type2 != SEXP_ATOM_STRING)
3735 return node_error(node, "Wing name expected here", bypass);
3740 if (type2 == SEXP_ATOM_STRING)
3741 if (ship_name_lookup(nodes[node].text) == -1)
3742 if (wing_name_lookup(nodes[node].text) == -1)
3745 if (type2 != SEXP_ATOM_STRING)
3746 return node_error(node, "Ship or wing name expected here", bypass);
3751 if (type2 != OPR_BOOL)
3752 return node_error(node, "Boolean return type expected here", bypass);
3757 if (type2 != OPR_NULL)
3758 return node_error(node, "No return type operator expected here", bypass);
3763 if (type2 != SEXP_ATOM_STRING || verify_vector(nodes[node].text))
3764 return node_error(node, "3d coordinate expected here", bypass);
3769 if (type2 == SEXP_ATOM_STRING)
3770 if (ai_get_subsystem_type(nodes[node].text) == SUBSYSTEM_UNKNOWN)
3773 if (type2 != SEXP_ATOM_STRING)
3774 return node_error(node, "Subsystem name expected here", bypass);
3779 if (type2 == SEXP_ATOM_STRING) {
3780 for (i=0; i<Num_team_names; i++)
3781 if (!stricmp(Team_names[i], nodes[node].text))
3785 if (i == Num_team_names)
3786 return node_error(node, "Iff team type expected here", bypass);
3791 if (type2 != OPR_AI_GOAL)
3792 return node_error(node, "Ai goal return type expected here", bypass);
3796 case OPF_DOCKER_POINT:
3797 if (type2 != SEXP_ATOM_STRING)
3798 return node_error(node, "Docker docking point name expected here", bypass);
3802 case OPF_DOCKEE_POINT:
3803 if (type2 != SEXP_ATOM_STRING)
3804 return node_error(node, "Dockee docking point name expected here", bypass);
3809 node = nodes[node].next;
3817 // display an error message and position to point of error (a node)
3818 int sexp_tree::node_error(int node, char *msg, int *bypass)
3826 item_handle = nodes[node].handle;
3827 if (nodes[node].flags & COMBINED)
3828 item_handle = nodes[nodes[node].parent].handle;
3830 ensure_visible(node);
3831 SelectItem(item_handle);
3832 sprintf(text, "%s\n\nContinue checking for more errors?", msg);
3833 if (MessageBox(text, "Sexp error", MB_YESNO | MB_ICONEXCLAMATION) == IDNO)
3839 void sexp_tree::hilite_item(int node)
3841 ensure_visible(node);
3842 SelectItem(nodes[node].handle);
3845 // because the MFC function EnsureVisible() doesn't do what it says it does, I wrote this.
3846 void sexp_tree::ensure_visible(int node)
3849 if (nodes[node].parent != -1)
3850 ensure_visible(nodes[node].parent); // expand all parents first
3852 if (nodes[node].child != -1) // expandable?
3853 Expand(nodes[node].handle, TVE_EXPAND); // expand this item
3856 void sexp_tree::link_modified(int *ptr)
3861 void get_variable_default_text_from_variable_text(char *text, char *default_text)
3867 start = strstr(text, "(");
3871 // get length and copy all but last char ")"
3872 len = strlen(start);
3873 strncpy(default_text, start, len-1);
3875 // add null termination
3876 default_text[len-1] = '\0';
3879 void get_variable_name_from_sexp_tree_node_text(const char *text, char *var_name)
3882 length = strcspn(text, "(");
3884 strncpy(var_name, text, length);
3885 var_name[length] = '\0';
3888 int sexp_tree::get_modify_variable_type()
3890 Assert(item_index > -1);
3894 int parent = nodes[item_index].parent;
3895 Assert(parent != -1);
3897 if ( !stricmp(nodes[parent].text, "modify-variable") ) {
3898 Assert(nodes[parent].child != -1);
3899 sexp_var_index = get_tree_name_to_sexp_variable_index(nodes[nodes[parent].child].text);
3900 Assert(sexp_var_index != -1);
3902 Int3(); // should not be called otherwise
3905 if (Sexp_variables[sexp_var_index].type & SEXP_VARIABLE_NUMBER) {
3907 } else if (Sexp_variables[sexp_var_index].type & SEXP_VARIABLE_STRING) {
3908 return OPF_AMBIGUOUS;
3916 void sexp_tree::verify_and_fix_arguments(int node)
3918 int op, arg_num, type, tmp;
3919 static int flag = 0;
3920 sexp_list_item *list, *ptr;
3926 op = identify_operator(nodes[node].text);
3934 item_index = nodes[node].child;
3935 while (item_index >= 0) {
3936 // get listing of valid argument values for node item_index
3937 type = query_operator_argument_type(op, arg_num);
3938 // special case for modify-variable
3939 if (type == OPF_AMBIGUOUS) {
3940 // check if parent variable type is number, returns OPF_NUMBER or OPF_AMBIGUOUS
3941 type = get_modify_variable_type();
3943 if (query_restricted_opf_range(type)) {
3944 list = get_listing_opf(type, node, arg_num);
3945 if (!list && (arg_num >= Operators[op].min)) {
3946 free_node(item_index, 1);
3953 // get a pointer to nodes[item_index].text for normal value
3954 // or default variable value if variable
3956 char default_variable_text[TOKEN_LENGTH];
3957 if (nodes[item_index].type & SEXPT_VARIABLE) {
3958 // special case for modify-variable
3959 if ( !stricmp(Operators[op].text, "modify-variable") ) {
3960 // make text_ptr to start - before '('
3961 get_variable_name_from_sexp_tree_node_text(nodes[item_index].text, default_variable_text);
3962 text_ptr = default_variable_text;
3964 get_variable_default_text_from_variable_text(nodes[item_index].text, default_variable_text);
3965 text_ptr = default_variable_text;
3968 text_ptr = nodes[item_index].text;
3974 if (ptr->text != NULL) {
3975 // make sure text is not NULL
3976 // check that proposed text is valid for operator
3977 if ( !stricmp(ptr->text, text_ptr) )
3982 // text is NULL, so set ptr to NULL to end loop
3987 if (!ptr) { // argument isn't in list of valid choices,
3988 if (list->op >= 0) {
3989 replace_operator(list->text);
3991 replace_data(list->text, list->type);
3996 bool invalid = false;
3997 if (type == OPF_AMBIGUOUS) {
3998 if (SEXPT_TYPE(nodes[item_index].type) == SEXPT_OPERATOR) {
4002 if (SEXPT_TYPE(nodes[item_index].type) != SEXPT_OPERATOR) {
4008 replace_data("<Invalid>", (SEXPT_STRING | SEXPT_VALID));
4012 if (nodes[item_index].type & SEXPT_OPERATOR)
4013 verify_and_fix_arguments(item_index);
4016 item_index = nodes[item_index].next;
4024 void sexp_tree::replace_data(char *data, int type)
4029 node = nodes[item_index].child;
4033 nodes[item_index].child = -1;
4034 h = nodes[item_index].handle;
4035 while (ItemHasChildren(h))
4036 DeleteItem(GetChildItem(h));
4038 set_node(item_index, type, data);
4039 SetItemText(h, data);
4040 SetItemImage(h, BITMAP_DATA, BITMAP_DATA);
4041 nodes[item_index].flags = EDITABLE;
4043 // check remaining data beyond replaced data for validity (in case any of it is dependent on data just replaced)
4044 verify_and_fix_arguments(nodes[item_index].parent);
4047 update_help(GetSelectedItem());
4051 // Replaces data with sexp_variable type data
4052 void sexp_tree::replace_variable_data(int var_idx, int type)
4058 Assert(type & SEXPT_VARIABLE);
4060 node = nodes[item_index].child;
4064 nodes[item_index].child = -1;
4065 h = nodes[item_index].handle;
4066 while (ItemHasChildren(h)) {
4067 DeleteItem(GetChildItem(h));
4071 sprintf(buf, "%s(%s)", Sexp_variables[var_idx].variable_name, Sexp_variables[var_idx].text);
4073 set_node(item_index, type, buf);
4074 SetItemText(h, buf);
4075 SetItemImage(h, BITMAP_VARIABLE, BITMAP_VARIABLE);
4076 nodes[item_index].flags = NOT_EDITABLE;
4078 // check remaining data beyond replaced data for validity (in case any of it is dependent on data just replaced)
4079 verify_and_fix_arguments(nodes[item_index].parent);
4082 update_help(GetSelectedItem());
4087 void sexp_tree::replace_operator(char *op)
4092 node = nodes[item_index].child;
4096 nodes[item_index].child = -1;
4097 h = nodes[item_index].handle;
4098 while (ItemHasChildren(h))
4099 DeleteItem(GetChildItem(h));
4101 set_node(item_index, (SEXPT_OPERATOR | SEXPT_VALID), op);
4103 nodes[item_index].flags = OPERAND;
4105 update_help(GetSelectedItem());
4107 // hack added at Allender's request. If changing ship in an ai-dock operator, re-default
4111 /*void sexp_tree::replace_one_arg_operator(char *op, char *data, int type)
4117 node = nodes[item_index].child;
4121 nodes[item_index].child = -1;
4122 h = nodes[item_index].handle;
4123 while (ItemHasChildren(h))
4124 DeleteItem(GetChildItem(h));
4126 node = allocate_node(item_index);
4127 set_node(item_index, SEXPT_OPERATOR, op);
4128 set_node(node, type, data);
4129 sprintf(str, "%s %s", op, data);
4130 SetItemText(h, str);
4131 nodes[item_index].flags = OPERAND | EDITABLE;
4132 nodes[node].flags = COMBINED;
4134 update_help(GetSelectedItem());
4137 // moves a whole sexp tree branch to a new position under 'parent' and after 'after'.
4138 // The expansion state is preserved, and node handles are updated.
4139 void sexp_tree::move_branch(int source, int parent)
4143 // if no source, skip everything
4145 node = nodes[source].parent;
4147 if (nodes[node].child == source)
4148 nodes[node].child = nodes[source].next;
4150 node = nodes[node].child;
4151 while (nodes[node].next != source) {
4152 node = nodes[node].next;
4156 nodes[node].next = nodes[source].next;
4160 nodes[source].parent = parent;
4161 nodes[source].next = -1;
4163 if (nodes[parent].child == -1)
4164 nodes[parent].child = source;
4166 node = nodes[parent].child;
4167 while (nodes[node].next != -1)
4168 node = nodes[node].next;
4170 nodes[node].next = source;
4173 move_branch(nodes[source].handle, nodes[parent].handle);
4176 move_branch(nodes[source].handle);
4180 HTREEITEM sexp_tree::move_branch(HTREEITEM source, HTREEITEM parent, HTREEITEM after)
4182 int i, image1, image2;
4183 HTREEITEM h = 0, child, next;
4186 for (i=0; i<MAX_SEXP_TREE_SIZE; i++)
4187 if (nodes[i].handle == source)
4190 if (i < MAX_SEXP_TREE_SIZE) {
4191 GetItemImage(source, image1, image2);
4192 h = insert(GetItemText(source), image1, image2, parent, after);
4193 nodes[i].handle = h;
4196 GetItemImage(source, image1, image2);
4197 h = insert(GetItemText(source), image1, image2, parent, after);
4200 SetItemData(h, GetItemData(source));
4201 child = GetChildItem(source);
4203 next = GetNextSiblingItem(child);
4204 move_branch(child, h);
4208 if (GetItemState(source, TVIS_EXPANDED) & TVIS_EXPANDED)
4209 Expand(h, TVE_EXPAND);
4217 void sexp_tree::copy_branch(HTREEITEM source, HTREEITEM parent, HTREEITEM after)
4219 int i, image1, image2;
4223 for (i=0; i<MAX_SEXP_TREE_SIZE; i++)
4224 if (nodes[i].handle == source)
4227 if (i < MAX_SEXP_TREE_SIZE) {
4228 GetItemImage(source, image1, image2);
4229 h = insert(GetItemText(source), image1, image2, parent, after);
4230 nodes[i].handle = h;
4233 GetItemImage(source, image1, image2);
4234 h = insert(GetItemText(source), image1, image2, parent, after);
4237 SetItemData(h, GetItemData(source));
4238 child = GetChildItem(source);
4240 copy_branch(child, h);
4241 child = GetNextSiblingItem(child);
4244 if (GetItemState(source, TVIS_EXPANDED) & TVIS_EXPANDED)
4245 Expand(h, TVE_EXPAND);
4249 void sexp_tree::swap_roots(HTREEITEM one, HTREEITEM two)
4253 Assert(!GetParentItem(one));
4254 Assert(!GetParentItem(two));
4255 // copy_branch(one, TVI_ROOT, two);
4256 // move_branch(two, TVI_ROOT, one);
4258 h = move_branch(one, TVI_ROOT, two);
4264 void sexp_tree::OnBegindrag(NMHDR* pNMHDR, LRESULT* pResult)
4268 // ScreenToClient(&m_pt);
4269 ASSERT(!m_dragging);
4270 m_h_drag = HitTest(m_pt, &flags);
4273 if (!m_mode || GetParentItem(m_h_drag))
4276 ASSERT(m_p_image_list == NULL);
4277 m_p_image_list = CreateDragImage(m_h_drag); // get the image list for dragging
4278 if (!m_p_image_list)
4281 m_p_image_list->DragShowNolock(TRUE);
4282 m_p_image_list->SetDragCursorImage(0, CPoint(0, 0));
4283 m_p_image_list->BeginDrag(0, CPoint(0,0));
4284 m_p_image_list->DragMove(m_pt);
4285 m_p_image_list->DragEnter(this, m_pt);
4290 void sexp_tree::OnLButtonDown(UINT nFlags, CPoint point)
4293 CTreeCtrl::OnLButtonDown(nFlags, point);
4296 void sexp_tree::OnMouseMove(UINT nFlags, CPoint point)
4302 ASSERT(m_p_image_list != NULL);
4303 m_p_image_list->DragMove(point);
4304 if ((hitem = HitTest(point, &flags)) != NULL)
4305 if (!GetParentItem(hitem)) {
4306 m_p_image_list->DragLeave(this);
4307 SelectDropTarget(hitem);
4309 m_p_image_list->DragEnter(this, point);
4313 CTreeCtrl::OnMouseMove(nFlags, point);
4316 void sexp_tree::OnLButtonUp(UINT nFlags, CPoint point)
4321 ASSERT(m_p_image_list != NULL);
4322 m_p_image_list->DragLeave(this);
4323 m_p_image_list->EndDrag();
4324 delete m_p_image_list;
4325 m_p_image_list = NULL;
4327 if (m_h_drop && m_h_drag != m_h_drop) {
4329 index1 = GetItemData(m_h_drag);
4330 index2 = GetItemData(m_h_drop);
4331 swap_roots(m_h_drag, m_h_drop);
4332 if (m_mode == MODE_GOALS) {
4333 Assert(Goal_editor_dlg);
4334 Goal_editor_dlg->swap_handler(index1, index2);
4336 } else if (m_mode == MODE_EVENTS) {
4337 Assert(Event_editor_dlg);
4338 Event_editor_dlg->swap_handler(index1, index2);
4340 } else if (m_mode == MODE_CAMPAIGN) {
4341 Campaign_tree_formp->swap_handler(index1, index2);
4351 SelectDropTarget(NULL);
4354 CTreeCtrl::OnLButtonUp(nFlags, point);
4357 void sexp_tree::setup(CEdit *ptr)
4359 CImageList *pimagelist;
4364 int stops[2] = { 10, 30 };
4366 help_box -> SetTabStops(2, (LPINT) stops);
4369 pimagelist = GetImageList(TVSIL_NORMAL);
4371 pimagelist = new CImageList();
4372 pimagelist->Create(16, 16, TRUE/*bMask*/, 2, 9);
4374 bitmap.LoadBitmap(IDB_OPERATOR);
4375 pimagelist->Add(&bitmap, (COLORREF) 0xFFFFFF);
4376 bitmap.DeleteObject();
4378 bitmap.LoadBitmap(IDB_DATA);
4379 pimagelist->Add(&bitmap, (COLORREF) 0xFF00FF);
4380 bitmap.DeleteObject();
4382 bitmap.LoadBitmap(IDB_VARIABLE);
4383 pimagelist->Add(&bitmap, (COLORREF) 0xFF00FF);
4384 bitmap.DeleteObject();
4386 bitmap.LoadBitmap(IDB_ROOT);
4387 pimagelist->Add(&bitmap, (COLORREF) 0xFF00FF);
4388 bitmap.DeleteObject();
4390 bitmap.LoadBitmap(IDB_ROOT_DIRECTIVE);
4391 pimagelist->Add(&bitmap, (COLORREF) 0xFFFFFF);
4392 bitmap.DeleteObject();
4394 bitmap.LoadBitmap(IDB_CHAINED);
4395 pimagelist->Add(&bitmap, (COLORREF) 0xFF00FF);
4396 bitmap.DeleteObject();
4398 bitmap.LoadBitmap(IDB_CHAINED_DIRECTIVE);
4399 pimagelist->Add(&bitmap, (COLORREF) 0xFFFFFF);
4400 bitmap.DeleteObject();
4402 bitmap.LoadBitmap(IDB_GREEN_DOT);
4403 pimagelist->Add(&bitmap, (COLORREF) 0xFFFFFF);
4404 bitmap.DeleteObject();
4406 bitmap.LoadBitmap(IDB_BLACK_DOT);
4407 pimagelist->Add(&bitmap, (COLORREF) 0xFFFFFF);
4408 bitmap.DeleteObject();
4410 SetImageList(pimagelist, TVSIL_NORMAL);
4413 //#define BITMAP_OPERATOR 0
4414 //#define BITMAP_DATA 1
4415 //#define BITMAP_VARIABLE 2
4416 //#define BITMAP_ROOT 3
4417 //#define BITMAP_ROOT_DIRECTIVE 4
4418 //#define BITMAP_CHAIN 5
4419 //#define BITMAP_CHAIN_DIRECTIVE 6
4420 //#define BITMAP_GREEN_DOT 7
4421 //#define BITMAP_BLACK_DOT 8
4424 HTREEITEM sexp_tree::insert(LPCTSTR lpszItem, int image, int sel_image, HTREEITEM hParent, HTREEITEM hInsertAfter)
4426 return InsertItem(lpszItem, image, sel_image, hParent, hInsertAfter);
4430 void sexp_tree::OnDestroy()
4432 CImageList *pimagelist;
4434 pimagelist = GetImageList(TVSIL_NORMAL);
4436 pimagelist->DeleteImageList();
4440 CTreeCtrl::OnDestroy();
4443 HTREEITEM sexp_tree::handle(int node)
4445 return nodes[node].handle;
4448 char *sexp_tree::help(int code)
4452 i = sizeof(Sexp_help) / sizeof(sexp_help_struct);
4454 if (Sexp_help[i].id == code)
4459 return Sexp_help[i].help;
4464 // get type of item clicked on
4465 int sexp_tree::get_type(HTREEITEM h)
4469 // get index into sexp_tree
4470 for (i=0; i<MAX_SEXP_TREE_SIZE; i++)
4471 if (nodes[i].handle == h)
4474 if ( (i >= MAX_SEXP_TREE_SIZE) ) {
4475 // Int3(); // This would be the root of the tree -- ie, event name
4479 return nodes[i].type;
4483 void sexp_tree::update_help(HTREEITEM h)
4486 int i, j, z, c, code;
4489 for (i=0; i<Num_operators; i++)
4490 for (j=0; j<Num_op_menus; j++)
4491 if ((Operators[i].value & OP_CATAGORY_MASK) == op_menu[j].id) {
4492 if (!help(Operators[i].value))
4493 Int3(); // Damn you, Allender! If you add new sexp operators, add help for them too! :)
4496 help_box = (CEdit *) GetParent()->GetDlgItem(IDC_HELP_BOX);
4497 if (!help_box || !::IsWindow(help_box->m_hWnd))
4500 for (i=0; i<MAX_SEXP_TREE_SIZE; i++)
4501 if (nodes[i].handle == h)
4504 if ((i >= MAX_SEXP_TREE_SIZE) || !nodes[i].type) {
4505 help_box->SetWindowText("");
4509 if (SEXPT_TYPE(nodes[i].type) != SEXPT_OPERATOR) {
4510 z = nodes[i].parent;
4512 Warning(LOCATION, "Sexp data \"%s\" has no parent!", nodes[i].text);
4516 code = identify_operator(nodes[z].text);
4520 while ((j >= 0) && (j != i)) {
4526 if (query_operator_argument_type(code, c) == OPF_MESSAGE) {
4527 for (j=0; j<Num_messages; j++)
4528 if (!stricmp(Messages[j].name, nodes[i].text)) {
4529 text.Format("Message Text:\r\n%s", Messages[j].message);
4530 help_box->SetWindowText(text);
4539 code = find_operator(nodes[i].text);
4542 str = "No help available";
4544 help_box->SetWindowText(str);
4547 // find list of sexp_tree nodes with text
4548 // stuff node indices into find[]
4549 int sexp_tree::find_text(char *text, int *find)
4554 for (i=0; i<MAX_SEARCH_MESSAGE_DEPTH; i++) {
4560 for (i=0; i<MAX_SEXP_TREE_SIZE; i++) {
4561 // only look at used and editable nodes
4562 if ((nodes[i].flags & EDITABLE && (nodes[i].type != SEXPT_UNUSED))) {
4564 if ( !stricmp(nodes[i].text, text) ) {
4565 find[find_count++] = i;
4567 // don't exceed max count - array bounds
4568 if (find_count == MAX_SEARCH_MESSAGE_DEPTH) {
4579 void sexp_tree::OnKeydown(NMHDR *pNMHDR, LRESULT *pResult)
4582 TV_KEYDOWN *pTVKeyDown = (TV_KEYDOWN *) pNMHDR;
4584 key = pTVKeyDown->wVKey;
4585 if (key == VK_SPACE)
4586 EditLabel(GetSelectedItem());
4591 // Determine if a given opf code has a restricted argument range (i.e. has a specific, limited
4592 // set of argument values, or has virtually unlimited possibilities. For example, boolean values
4593 // only have true or false, so it is restricted, but a number could be anything, so it's not.
4595 int sexp_tree::query_restricted_opf_range(int opf)
4607 // generate listing of valid argument values.
4608 // opf = operator format to generate list for
4609 // parent_node = the parent node we are generating list for
4610 // arg_index = argument number of parent this argument will go at
4612 sexp_list_item *sexp_tree::get_listing_opf(int opf, int parent_node, int arg_index)
4619 return get_listing_opf_null();
4622 return get_listing_opf_bool(parent_node);
4625 return get_listing_opf_number();
4628 return get_listing_opf_ship(parent_node);
4631 return get_listing_opf_wing();
4633 case OPF_AWACS_SUBSYSTEM:
4635 return get_listing_opf_subsystem(parent_node, arg_index);
4638 return get_listing_opf_point();
4641 return get_listing_opf_iff();
4644 return get_listing_opf_ai_goal(parent_node);
4646 case OPF_DOCKER_POINT:
4647 return get_listing_opf_docker_point(parent_node);
4649 case OPF_DOCKEE_POINT:
4650 return get_listing_opf_dockee_point(parent_node);
4653 return get_listing_opf_message();
4656 return get_listing_opf_who_from();
4659 return get_listing_opf_priority();
4661 case OPF_WAYPOINT_PATH:
4662 return get_listing_opf_waypoint_path();
4665 return get_listing_opf_positive();
4667 case OPF_MISSION_NAME:
4668 return get_listing_opf_mission_name();
4670 case OPF_SHIP_POINT:
4671 return get_listing_opf_ship_point();
4674 return get_listing_opf_goal_name(parent_node);
4677 return get_listing_opf_ship_wing();
4679 case OPF_SHIP_WING_POINT:
4680 return get_listing_opf_ship_wing_point();
4683 return get_listing_opf_ship_type();
4686 return get_listing_opf_keypress();
4688 case OPF_EVENT_NAME:
4689 return get_listing_opf_event_name(parent_node);
4692 return get_listing_opf_ai_order();
4694 case OPF_SKILL_LEVEL:
4695 return get_listing_opf_skill_level();
4697 case OPF_MEDAL_NAME:
4698 return get_listing_opf_medal_name();
4700 case OPF_WEAPON_NAME:
4701 return get_listing_opf_weapon_name();
4703 case OPF_SHIP_CLASS_NAME:
4704 return get_listing_opf_ship_class_name();
4706 case OPF_HUD_GAUGE_NAME:
4707 return get_listing_opf_hud_gauge_name();
4709 case OPF_HUGE_WEAPON:
4710 return get_listing_opf_huge_weapon();
4712 case OPF_SHIP_NOT_PLAYER:
4713 return get_listing_opf_ship_not_player();
4715 case OPF_JUMP_NODE_NAME:
4716 return get_listing_opf_jump_nodes();
4718 case OPF_VARIABLE_NAME:
4719 return get_listing_opf_variable_names();
4725 Int3(); // unknown OPF code
4731 sexp_list_item *sexp_tree::get_listing_opf_null()
4734 sexp_list_item head;
4736 for (i=0; i<Num_operators; i++)
4737 if (query_operator_return_type(i) == OPR_NULL)
4743 sexp_list_item *sexp_tree::get_listing_opf_bool(int parent_node)
4746 sexp_list_item head;
4748 // search for the previous goal/event operators. If found, only add the true/false
4749 // sexpressions to the list
4751 if ( parent_node != -1 ) {
4754 op = find_operator(nodes[parent_node].text);
4755 if ( (op == OP_PREVIOUS_GOAL_TRUE) || (op == OP_PREVIOUS_GOAL_FALSE) || (op == OP_PREVIOUS_EVENT_TRUE) || (op == OP_PREVIOUS_EVENT_FALSE) )
4760 for (i=0; i<Num_operators; i++) {
4761 if (query_operator_return_type(i) == OPR_BOOL) {
4762 if ( !only_basic || (only_basic && ((Operators[i].value == OP_TRUE) || (Operators[i].value == OP_FALSE))) ) {
4771 sexp_list_item *sexp_tree::get_listing_opf_positive()
4774 sexp_list_item head;
4776 for (i=0; i<Num_operators; i++)
4777 if (query_operator_return_type(i) == OPR_POSITIVE)
4783 sexp_list_item *sexp_tree::get_listing_opf_number()
4786 sexp_list_item head;
4788 for (i=0; i<Num_operators; i++) {
4789 z = query_operator_return_type(i);
4790 if ((z == OPR_NUMBER) || (z == OPR_POSITIVE))
4797 sexp_list_item *sexp_tree::get_listing_opf_ship(int parent_node)
4800 sexp_list_item head;
4801 int op = 0, dock_ship = -1, require_cap_ship = 0;
4803 // look at the parent node and get the operator. Some ship lists should be filtered based
4804 // on what the parent operator is
4805 if ( parent_node >= 0 ) {
4806 op = find_operator(nodes[parent_node].text);
4808 // prune out to only capital ships
4809 if (!stricmp(nodes[parent_node].text, "cap-subsys-cargo-known-delay")) {
4810 require_cap_ship = 1;
4813 // get the dock_ship number of if this goal is an ai dock goal. used to prune out unwanted ships out
4814 // of the generated ship list
4816 if ( op == OP_AI_DOCK ) {
4819 z = nodes[parent_node].parent;
4821 Assert(!stricmp(nodes[z].text, "add-ship-goal") || !stricmp(nodes[z].text, "add-wing-goal") || !stricmp(nodes[z].text, "add-goal"));
4826 dock_ship = ship_name_lookup(nodes[z].text, 1);
4827 Assert( dock_ship != -1 );
4831 ptr = GET_FIRST(&obj_used_list);
4832 while (ptr != END_OF_LIST(&obj_used_list)) {
4833 if ((ptr->type == OBJ_SHIP) || (ptr->type == OBJ_START)) {
4834 if ( op == OP_AI_DOCK ) {
4835 // only include those ships in the list which the given ship can dock with.
4836 if ( (dock_ship != ptr->instance) && ship_docking_valid(dock_ship , ptr->instance) )
4837 head.add_data(Ships[ptr->instance].ship_name );
4840 if ( !require_cap_ship || (Ship_info[Ships[ptr->instance].ship_info_index].flags & SIF_HUGE_SHIP) ) {
4841 head.add_data(Ships[ptr->instance].ship_name);
4846 ptr = GET_NEXT(ptr);
4852 sexp_list_item *sexp_tree::get_listing_opf_wing()
4855 sexp_list_item head;
4857 for (i=0; i<MAX_WINGS; i++){
4858 if (Wings[i].wave_count){
4859 head.add_data(Wings[i].name);
4866 // specific types of subsystems we're looking for
4867 #define OPS_CAP_CARGO 1
4868 #define OPS_STRENGTH 2
4869 #define OPS_BEAM_TURRET 3
4871 sexp_list_item *sexp_tree::get_listing_opf_subsystem(int parent_node, int arg_index)
4874 int special_subsys = 0;
4875 sexp_list_item head;
4876 ship_subsys *subsys;
4878 // determine if the parent is one of the set subsystem strength items. If so,
4879 // we want to append the "Hull" name onto the end of the menu
4880 Assert(parent_node >= 0);
4882 // get the operator type of the node
4883 op = find_operator(nodes[parent_node].text);
4886 child = nodes[parent_node].child;
4890 // where we care about hull strength
4891 case OP_REPAIR_SUBSYSTEM:
4892 case OP_SABOTAGE_SUBSYSTEM:
4893 case OP_SET_SUBSYSTEM_STRNGTH:
4894 special_subsys = OPS_STRENGTH;
4898 case OP_AWACS_SET_RADIUS:
4899 special_subsys = OPS_AWACS;
4902 // where we care about capital ship subsystem cargo
4903 case OP_CAP_SUBSYS_CARGO_KNOWN_DELAY:
4904 special_subsys = OPS_CAP_CARGO;
4906 // get the next sibling
4907 child = nodes[child].next;
4910 // where we care about turrets carrying beam weapons
4912 special_subsys = OPS_BEAM_TURRET;
4914 // if this is arg index 3 (targeted ship)
4916 Assert(arg_index == 3);
4917 child = nodes[child].next;
4919 child = nodes[child].next;
4921 Assert(arg_index == 1);
4926 // now find the ship and add all relevant subsystems
4928 sh = ship_name_lookup(nodes[child].text, 1);
4930 subsys = GET_FIRST(&Ships[sh].subsys_list);
4931 while (subsys != END_OF_LIST(&Ships[sh].subsys_list)) {
4933 switch(special_subsys){
4936 if (valid_cap_subsys_cargo_list(subsys->system_info->subobj_name) ) {
4937 head.add_data(subsys->system_info->subobj_name);
4942 case OPS_BEAM_TURRET:
4943 head.add_data(subsys->system_info->subobj_name);
4948 if (subsys->system_info->flags & MSS_FLAG_AWACS) {
4949 head.add_data(subsys->system_info->subobj_name);
4955 head.add_data(subsys->system_info->subobj_name);
4960 subsys = GET_NEXT(subsys);
4964 // if one of the subsystem strength operators, append the Hull string
4965 if(special_subsys == OPS_STRENGTH){
4966 head.add_data(SEXP_HULL_STRING);
4972 sexp_list_item *sexp_tree::get_listing_opf_point()
4974 char buf[NAME_LENGTH+8];
4976 sexp_list_item head;
4978 for (i=0; i<Num_waypoint_lists; i++)
4979 for (j=0; j<Waypoint_lists[i].count; j++) {
4980 sprintf(buf, "%s:%d", Waypoint_lists[i].name, j + 1);
4981 head.add_data_dup(buf);
4987 sexp_list_item *sexp_tree::get_listing_opf_iff()
4990 sexp_list_item head;
4992 for (i=0; i<Num_team_names; i++)
4993 head.add_data(Team_names[i]);
4998 sexp_list_item *sexp_tree::get_listing_opf_ai_goal(int parent_node)
5000 int i, n, w, z, child;
5001 sexp_list_item head;
5003 Assert(parent_node >= 0);
5004 child = nodes[parent_node].child;
5006 n = ship_name_lookup(nodes[child].text, 1);
5008 // add operators if it's an ai-goal and ai-goal is allowed for that ship
5009 for (i=0; i<Num_operators; i++) {
5010 if ( (query_operator_return_type(i) == OPR_AI_GOAL) && query_sexp_ai_goal_valid(Operators[i].value, n) )
5015 z = wing_name_lookup(nodes[child].text);
5017 for (w=0; w<Wings[z].wave_count; w++) {
5018 n = Wings[z].ship_index[w];
5019 // add operators if it's an ai-goal and ai-goal is allowed for that ship
5020 for (i=0; i<Num_operators; i++) {
5021 if ( (query_operator_return_type(i) == OPR_AI_GOAL) && query_sexp_ai_goal_valid(Operators[i].value, n) )
5027 return NULL; // no valid ship or wing to check against, make nothing available
5033 sexp_list_item *sexp_tree::get_listing_opf_docker_point(int parent_node)
5036 sexp_list_item head;
5038 Assert(parent_node >= 0);
5039 Assert(!stricmp(nodes[parent_node].text, "ai-dock"));
5041 z = nodes[parent_node].parent;
5043 Assert(!stricmp(nodes[z].text, "add-ship-goal") || !stricmp(nodes[z].text, "add-wing-goal") || !stricmp(nodes[z].text, "add-goal"));
5048 sh = ship_name_lookup(nodes[z].text, 1);
5050 z = get_docking_list(Ships[sh].modelnum);
5052 head.add_data(Docking_bay_list[i]);
5058 sexp_list_item *sexp_tree::get_listing_opf_dockee_point(int parent_node)
5061 sexp_list_item head;
5063 Assert(parent_node >= 0);
5064 Assert(!stricmp(nodes[parent_node].text, "ai-dock"));
5066 z = nodes[parent_node].child;
5069 sh = ship_name_lookup(nodes[z].text, 1);
5071 z = get_docking_list(Ships[sh].modelnum);
5073 head.add_data(Docking_bay_list[i]);
5079 sexp_list_item *sexp_tree::get_listing_opf_message()
5083 sexp_list_item head;
5085 if (m_mode == MODE_EVENTS) {
5086 Assert(Event_editor_dlg);
5087 // this for looks a litle strange, but had to do it get rid of a warning. Conditional
5088 //uses last statement is sequence, i.e. same as for (i=0, str, i++)
5089 for (i=0; str = Event_editor_dlg->current_message_name(i), str; i++)
5093 for (i=Num_builtin_messages; i<Num_messages; i++)
5094 head.add_data(Messages[i].name);
5100 sexp_list_item *sexp_tree::get_listing_opf_who_from()
5103 sexp_list_item head;
5105 //head.add_data("<any allied>");
5106 head.add_data("#Command");
5107 head.add_data("<any wingman>");
5109 ptr = GET_FIRST(&obj_used_list);
5110 while (ptr != END_OF_LIST(&obj_used_list)) {
5111 if ((ptr->type == OBJ_SHIP) || (ptr->type == OBJ_START))
5112 if (!(Ship_info[Ships[get_ship_from_obj(ptr)].ship_info_index].flags & SIF_NOT_FLYABLE))
5113 head.add_data(Ships[ptr->instance].ship_name);
5115 ptr = GET_NEXT(ptr);
5121 sexp_list_item *sexp_tree::get_listing_opf_priority()
5123 sexp_list_item head;
5125 head.add_data("High");
5126 head.add_data("Normal");
5127 head.add_data("Low");
5131 sexp_list_item *sexp_tree::get_listing_opf_waypoint_path()
5134 sexp_list_item head;
5136 for (i=0; i<Num_waypoint_lists; i++)
5137 head.add_data(Waypoint_lists[i].name);
5142 sexp_list_item *sexp_tree::get_listing_opf_ship_point()
5144 sexp_list_item head;
5146 head.add_list(get_listing_opf_ship());
5147 head.add_list(get_listing_opf_point());
5151 sexp_list_item *sexp_tree::get_listing_opf_ship_wing_point()
5153 sexp_list_item head;
5155 head.add_data("<any friendly>");
5156 head.add_data("<any hostile>");
5157 head.add_data("<any neutral>");
5158 head.add_data("<any unknown>");
5159 head.add_list(get_listing_opf_ship());
5160 head.add_list(get_listing_opf_wing());
5161 head.add_list(get_listing_opf_point());
5165 sexp_list_item *sexp_tree::get_listing_opf_mission_name()
5168 sexp_list_item head;
5170 if ((m_mode == MODE_CAMPAIGN) && (Cur_campaign_mission >= 0)) {
5171 for (i=0; i<Campaign.num_missions; i++)
5172 if ( (i == Cur_campaign_mission) || (Campaign.missions[i].level < Campaign.missions[Cur_campaign_mission].level) )
5173 head.add_data(Campaign.missions[i].name);
5176 head.add_data(Mission_filename);
5181 sexp_list_item *sexp_tree::get_listing_opf_goal_name(int parent_node)
5184 sexp_list_item head;
5186 if (m_mode == MODE_CAMPAIGN) {
5189 Assert(parent_node >= 0);
5190 child = nodes[parent_node].child;
5193 for (m=0; m<Campaign.num_missions; m++)
5194 if (!stricmp(Campaign.missions[m].name, nodes[child].text))
5197 if (m < Campaign.num_missions) {
5198 if (Campaign.missions[m].num_goals < 0) // haven't loaded goal names yet.
5199 read_mission_goal_list(m);
5201 for (i=0; i<Campaign.missions[m].num_goals; i++)
5202 head.add_data(Campaign.missions[m].goals[i].name);
5206 for (i=0; i<Num_goals; i++)
5207 head.add_data(Mission_goals[i].name);
5213 sexp_list_item *sexp_tree::get_listing_opf_ship_wing()
5215 sexp_list_item head;
5217 head.add_list(get_listing_opf_ship());
5218 head.add_list(get_listing_opf_wing());
5222 sexp_list_item *sexp_tree::get_listing_opf_ship_type()
5225 sexp_list_item head;
5227 for (i=0; i<MAX_SHIP_TYPE_COUNTS; i++){
5228 head.add_data(Ship_type_names[i]);
5234 sexp_list_item *sexp_tree::get_listing_opf_keypress()
5237 sexp_list_item head;
5239 for (i=0; i<CCFG_MAX; i++) {
5240 if (Control_config[i].key_default > 0) {
5241 head.add_data_dup(textify_scancode(Control_config[i].key_default));
5248 sexp_list_item *sexp_tree::get_listing_opf_event_name(int parent_node)
5251 sexp_list_item head;
5254 if (m_mode == MODE_CAMPAIGN) {
5257 Assert(parent_node >= 0);
5258 child = nodes[parent_node].child;
5261 for (m=0; m<Campaign.num_missions; m++)
5262 if (!stricmp(Campaign.missions[m].name, nodes[child].text))
5265 if (m < Campaign.num_missions) {
5266 if (Campaign.missions[m].num_events < 0) // haven't loaded goal names yet.
5267 read_mission_goal_list(m);
5269 for (i=0; i<Campaign.missions[m].num_events; i++)
5270 head.add_data(Campaign.missions[m].events[i].name);
5274 for (i=0; i<Num_mission_events; i++)
5275 head.add_data(Mission_events[i].name);
5281 sexp_list_item *sexp_tree::get_listing_opf_ai_order()
5284 sexp_list_item head;
5286 for (i=0; i<Fred_comm_orders_max; i++)
5287 head.add_data(Fred_comm_orders[i].menu_text);
5292 sexp_list_item *sexp_tree::get_listing_opf_skill_level()
5295 sexp_list_item head;
5297 for (i=0; i<NUM_SKILL_LEVELS; i++)
5298 head.add_data(Skill_level_names(i, 0));
5303 sexp_list_item *sexp_tree::get_listing_opf_medal_name()
5306 sexp_list_item head;
5308 for (i=0; i<MAX_ASSIGNABLE_MEDALS; i++)
5309 head.add_data(Medals[i].name);
5311 // also add SOC crest (index 17) and Wings (index 13)
5312 head.add_data(Medals[13].name);
5313 head.add_data(Medals[17].name);
5318 sexp_list_item *sexp_tree::get_listing_opf_weapon_name()
5321 sexp_list_item head;
5323 for (i=0; i<Num_weapon_types; i++)
5324 head.add_data(Weapon_info[i].name);
5329 sexp_list_item *sexp_tree::get_listing_opf_ship_class_name()
5332 sexp_list_item head;
5334 for (i=0; i<Num_ship_types; i++)
5335 head.add_data(Ship_info[i].name);
5340 sexp_list_item *sexp_tree::get_listing_opf_hud_gauge_name()
5343 sexp_list_item head;
5345 for (i=0; i<NUM_HUD_GAUGES; i++)
5346 head.add_data(HUD_gauge_text[i]);
5351 sexp_list_item *sexp_tree::get_listing_opf_huge_weapon()
5354 sexp_list_item head;
5356 for (i=0; i<Num_weapon_types; i++) {
5357 if (Weapon_info[i].wi_flags & WIF_HUGE)
5358 head.add_data(Weapon_info[i].name);
5364 sexp_list_item *sexp_tree::get_listing_opf_ship_not_player()
5367 sexp_list_item head;
5369 ptr = GET_FIRST(&obj_used_list);
5370 while (ptr != END_OF_LIST(&obj_used_list)) {
5371 if (ptr->type == OBJ_SHIP)
5372 head.add_data(Ships[ptr->instance].ship_name);
5374 ptr = GET_NEXT(ptr);
5380 sexp_list_item *sexp_tree::get_listing_opf_jump_nodes()
5383 sexp_list_item head;
5385 for (i = 0; i < Num_jump_nodes; i++ )
5386 head.add_data( Jump_nodes[i].name );
5391 // creates list of Sexp_variables
5392 sexp_list_item *sexp_tree::get_listing_opf_variable_names()
5395 sexp_list_item head;
5397 for (i=0; i<MAX_SEXP_VARIABLES; i++) {
5398 if (Sexp_variables[i].type & SEXP_VARIABLE_SET) {
5399 head.add_data( Sexp_variables[i].variable_name );
5407 // Deletes sexp_variable from sexp_tree.
5408 // resets tree to not include given variable, and resets text and type
5409 void sexp_tree::delete_sexp_tree_variable(const char *var_name)
5411 char search_str[64];
5412 char replace_text[TOKEN_LENGTH];
5414 sprintf(search_str, "%s(", var_name);
5416 // store old item index
5417 int old_item_index = item_index;
5419 for (int idx=0; idx<MAX_SEXP_TREE_SIZE; idx++) {
5420 if (nodes[idx].type & SEXPT_VARIABLE) {
5421 if ( strstr(nodes[idx].text, search_str) != NULL ) {
5423 // check type is number or string
5424 Assert( (nodes[idx].type & SEXPT_NUMBER) || (nodes[idx].type & SEXPT_STRING) );
5426 // reset type as not variable
5427 int type = nodes[idx].type &= ~SEXPT_VARIABLE;
5430 if (nodes[idx].type & SEXPT_NUMBER) {
5431 strcpy(replace_text, "number");
5433 strcpy(replace_text, "string");
5436 // set item_index and replace data
5438 replace_data(replace_text, type);
5443 // restore item_index
5444 item_index = old_item_index;
5448 // Modify sexp_tree for a change in sexp_variable (name, type, or default value)
5449 void sexp_tree::modify_sexp_tree_variable(const char *old_name, int sexp_var_index)
5451 char search_str[64];
5454 Assert(Sexp_variables[sexp_var_index].type & SEXP_VARIABLE_SET);
5455 Assert( (Sexp_variables[sexp_var_index].type & SEXP_VARIABLE_NUMBER) || (Sexp_variables[sexp_var_index].type & SEXP_VARIABLE_STRING) );
5457 // Get type for sexp_tree node
5458 if (Sexp_variables[sexp_var_index].type & SEXP_VARIABLE_NUMBER) {
5459 type = (SEXPT_NUMBER | SEXPT_VALID);
5461 type = (SEXPT_STRING | SEXPT_VALID);
5464 // store item index;
5465 int old_item_index = item_index;
5467 // Search string in sexp_tree nodes
5468 sprintf(search_str, "%s(", old_name);
5470 for (int idx=0; idx<MAX_SEXP_TREE_SIZE; idx++) {
5471 if (nodes[idx].type & SEXPT_VARIABLE) {
5472 if ( strstr(nodes[idx].text, search_str) != NULL ) {
5473 // temp set item_index
5476 // replace variable data
5477 replace_variable_data(sexp_var_index, (type | SEXPT_VARIABLE));
5482 // restore item_index
5483 item_index = old_item_index;
5487 // convert from item_index to sexp_variable index, -1 if not
5488 int sexp_tree::get_item_index_to_var_index()
5490 // check valid item index and node is a variable
5491 if ( (item_index > 0) && (nodes[item_index].type & SEXPT_VARIABLE) ) {
5493 return get_tree_name_to_sexp_variable_index(nodes[item_index].text);
5499 int sexp_tree::get_tree_name_to_sexp_variable_index(const char *tree_name)
5501 char var_name[TOKEN_LENGTH];
5503 int chars_to_copy = strcspn(tree_name, "(");
5504 Assert(chars_to_copy < TOKEN_LENGTH - 1);
5506 // Copy up to '(' and add null termination
5507 strncpy(var_name, tree_name, chars_to_copy);
5508 var_name[chars_to_copy] = '\0';
5511 return get_index_sexp_variable_name(var_name);
5514 int sexp_tree::get_variable_count(const char *var_name)
5518 char compare_name[64];
5520 // get name to compare
5521 strcpy(compare_name, var_name);
5522 strcat(compare_name, "(");
5524 // look for compare name
5525 for (idx=0; idx<MAX_SEXP_TREE_SIZE; idx++) {
5526 if (nodes[idx].type & SEXPT_VARIABLE) {
5527 if ( strstr(nodes[idx].text, compare_name) ) {